Slices 2-4: Work becomes executable
Domain primitives: versioned WorkDefinition/DesignPacket with risk, questions, and approval gates; lifecycle transition table with invalidation rules; Resolver (Run/Attempt/outcomes/CodingKitV0); provider-neutral HarnessRuntime with normalized events and a deterministic FakeHarnessLive that always reaches a terminal classification and never claims implementation. Convex durable model: workDefinitions, workQuestions, workApprovals, designPackets, workSlices, workRuns, workAttempts, workAttemptEvents tables; broadened works.status union; generalized workEvents with optional signalId, referenceId, and payloadJson. Authenticated commands for definition request/ save/revise/approve, question answer/withdraw, design save/revise/approve, and simulated execution start/cancel/retry with leased attempts, checkpointed events, and expired-lease reconciliation. Private work-planner FLUE agent with proposal-only tools (definition, design, question). Convex validates and stores every proposal; FLUE never approves or advances Work. Expanded Work card with Outcome, Design, and Build sections wired to the new mutations. Slice 1 conversation and provenance preserved.
This commit is contained in:
@@ -9,7 +9,8 @@
|
||||
"dev": "bun --env-file=../../.env flue dev",
|
||||
"dev:tailscale": "bun --env-file=../../.env flue dev",
|
||||
"run": "bun --env-file=../../.env flue run",
|
||||
"run:zopu": "bun --env-file=../../.env flue run zopu"
|
||||
"run:zopu": "bun --env-file=../../.env flue run zopu",
|
||||
"run:work-planner": "bun --env-file=../../.env flue run work-planner"
|
||||
},
|
||||
"dependencies": {
|
||||
"@code/backend": "workspace:*",
|
||||
|
||||
30
packages/agents/src/agents/work-planner.ts
Normal file
30
packages/agents/src/agents/work-planner.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { parseAgentEnv } from "@code/env/agent";
|
||||
import { defineAgent } from "@flue/runtime";
|
||||
|
||||
import { createWorkPlannerTools } from "../tools/work-planner";
|
||||
|
||||
const INSTRUCTIONS = `You are Zopu's private Work Planner.
|
||||
|
||||
You may only submit typed proposals through tools:
|
||||
- a Work Definition proposal;
|
||||
- a Design Packet proposal containing 1-4 observable, independently verifiable slices;
|
||||
- structured questions.
|
||||
|
||||
You must never approve a Definition or Design, change Work status, start execution, claim implementation, create Git changes, or claim that simulation implemented the Work. Convex validates and stores every proposal. Ask a focused structured question when a high-impact decision is unresolved.`;
|
||||
|
||||
export {
|
||||
convexAgentRoute as attachments,
|
||||
convexAgentRoute as route,
|
||||
} from "../auth";
|
||||
|
||||
export default defineAgent(({ env }) => {
|
||||
const { AGENT_MODEL_NAME, AGENT_MODEL_PROVIDER } = parseAgentEnv(env);
|
||||
return {
|
||||
description:
|
||||
"Proposes Work Definitions, Design Packets, and structured questions.",
|
||||
instructions: INSTRUCTIONS,
|
||||
model: `${AGENT_MODEL_PROVIDER}/${AGENT_MODEL_NAME}`,
|
||||
thinkingLevel: "medium",
|
||||
tools: createWorkPlannerTools(env),
|
||||
};
|
||||
});
|
||||
@@ -3,7 +3,7 @@ import { defineAgent } from "@flue/runtime";
|
||||
|
||||
import { createSliceOneTools } from "../tools/slice-one";
|
||||
|
||||
const INSTRUCTIONS = `You are Zopu for product Slice 1: Conversation to Signal to proposed Work.
|
||||
const INSTRUCTIONS = `You are Zopu for product Slice 1 and the Work planning handoff.
|
||||
|
||||
## Your role
|
||||
|
||||
@@ -46,7 +46,8 @@ When a user sends a message, follow this decision flow:
|
||||
- Repeated delivery of the same message must not create duplicate Signals or attachments. The backend is idempotent.
|
||||
- Never claim you created a Signal until the tool call returns successfully.
|
||||
- Do not start implementation, planning, sandboxes, Git, verification, or delivery. Those are explicitly outside Slice 1.
|
||||
- Proposed Work is the only Work status in this slice.`;
|
||||
- After creating proposed Work, the system may invoke the private work-planner. Never claim that a Definition, Design, approval, or implementation exists unless Convex reports it.
|
||||
- Proposed Work is the only Work status you directly create.`;
|
||||
|
||||
export {
|
||||
convexAgentRoute as attachments,
|
||||
|
||||
69
packages/agents/src/tools/work-planner.ts
Normal file
69
packages/agents/src/tools/work-planner.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { parseAgentEnv } from "@code/env/agent";
|
||||
import { defineTool } from "@flue/runtime";
|
||||
import { ConvexHttpClient } from "convex/browser";
|
||||
import { makeFunctionReference } from "convex/server";
|
||||
import * as v from "valibot";
|
||||
|
||||
const submitDefinitionRef = makeFunctionReference<
|
||||
"mutation",
|
||||
{ workId: string; payloadJson: string; token: string },
|
||||
{ version: number }
|
||||
>("workPlanning:submitDefinitionProposal");
|
||||
const submitDesignRef = makeFunctionReference<
|
||||
"mutation",
|
||||
{ workId: string; payloadJson: string; token: string },
|
||||
{ version: number }
|
||||
>("workPlanning:submitDesignProposal");
|
||||
const submitQuestionRef = makeFunctionReference<
|
||||
"mutation",
|
||||
{ workId: string; questionJson: string; token: string },
|
||||
{ accepted: boolean }
|
||||
>("workPlanning:submitQuestion");
|
||||
|
||||
export const createWorkPlannerTools = (
|
||||
runtimeEnv: Record<string, string | undefined>
|
||||
) => {
|
||||
const env = parseAgentEnv(runtimeEnv);
|
||||
const client = new ConvexHttpClient(env.CONVEX_URL);
|
||||
const token = env.FLUE_DB_TOKEN;
|
||||
return [
|
||||
defineTool({
|
||||
description:
|
||||
"Submit a versioned Work Definition proposal. This never approves the Definition.",
|
||||
input: v.object({ definitionJson: v.string(), workId: v.string() }),
|
||||
name: "submit_definition_proposal",
|
||||
async run({ input }) {
|
||||
return await client.mutation(submitDefinitionRef, {
|
||||
payloadJson: input.definitionJson,
|
||||
token,
|
||||
workId: input.workId,
|
||||
});
|
||||
},
|
||||
}),
|
||||
defineTool({
|
||||
description:
|
||||
"Submit a Design Packet with observable independently verifiable slices. This never approves the Design.",
|
||||
input: v.object({ designJson: v.string(), workId: v.string() }),
|
||||
name: "submit_design_proposal",
|
||||
async run({ input }) {
|
||||
return await client.mutation(submitDesignRef, {
|
||||
payloadJson: input.designJson,
|
||||
token,
|
||||
workId: input.workId,
|
||||
});
|
||||
},
|
||||
}),
|
||||
defineTool({
|
||||
description: "Submit one structured question for human attention.",
|
||||
input: v.object({ questionJson: v.string(), workId: v.string() }),
|
||||
name: "submit_question",
|
||||
async run({ input }) {
|
||||
return await client.mutation(submitQuestionRef, {
|
||||
questionJson: input.questionJson,
|
||||
token,
|
||||
workId: input.workId,
|
||||
});
|
||||
},
|
||||
}),
|
||||
];
|
||||
};
|
||||
Reference in New Issue
Block a user