feat(protocol): add edge prompt to Transition + EvaluateResult (#402)
- Transition type gains prompt: string | null
- evaluate() returns EvaluateResult { role, prompt } instead of string
- normalizeGraph coerces prompt: undefined → null
- spawnAgent passes edge prompt via UWF_EDGE_PROMPT env
- AgentContext gains edgePrompt field
Refs #402
This commit is contained in:
@@ -624,13 +624,17 @@ function resolveAgentConfig(
|
|||||||
return agentConfig;
|
return agentConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
function spawnAgent(agent: AgentConfig, threadId: ThreadId, role: string): CasRef {
|
function spawnAgent(agent: AgentConfig, threadId: ThreadId, role: string, edgePrompt: string | null): CasRef {
|
||||||
const argv = [...agent.args, threadId, role];
|
const argv = [...agent.args, threadId, role];
|
||||||
|
const env = { ...process.env };
|
||||||
|
if (edgePrompt !== null) {
|
||||||
|
env.UWF_EDGE_PROMPT = edgePrompt;
|
||||||
|
}
|
||||||
let stdout: string;
|
let stdout: string;
|
||||||
try {
|
try {
|
||||||
stdout = execFileSync(agent.command, argv, {
|
stdout = execFileSync(agent.command, argv, {
|
||||||
encoding: "utf8",
|
encoding: "utf8",
|
||||||
env: process.env,
|
env,
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -712,7 +716,7 @@ async function cmdThreadStepOnce(
|
|||||||
fail(nextResult.error.message);
|
fail(nextResult.error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextResult.value === END_ROLE) {
|
if (nextResult.value.role === END_ROLE) {
|
||||||
await archiveThread(storageRoot, threadId, workflowHash, headHash);
|
await archiveThread(storageRoot, threadId, workflowHash, headHash);
|
||||||
return {
|
return {
|
||||||
workflow: workflowHash,
|
workflow: workflowHash,
|
||||||
@@ -722,12 +726,13 @@ async function cmdThreadStepOnce(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const role = nextResult.value;
|
const role = nextResult.value.role;
|
||||||
|
const edgePrompt = nextResult.value.prompt;
|
||||||
const config = await loadWorkflowConfig(storageRoot);
|
const config = await loadWorkflowConfig(storageRoot);
|
||||||
const agent = resolveAgentConfig(config, workflow, role, agentOverride);
|
const agent = resolveAgentConfig(config, workflow, role, agentOverride);
|
||||||
|
|
||||||
loadDotenv({ path: getEnvPath(storageRoot) });
|
loadDotenv({ path: getEnvPath(storageRoot) });
|
||||||
const newHead = spawnAgent(agent, threadId, role);
|
const newHead = spawnAgent(agent, threadId, role, edgePrompt);
|
||||||
|
|
||||||
// Re-create store to pick up nodes written by the agent subprocess
|
// Re-create store to pick up nodes written by the agent subprocess
|
||||||
const uwfAfter = await createUwfStore(storageRoot);
|
const uwfAfter = await createUwfStore(storageRoot);
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ function normalizeGraph(graph: Record<string, Transition[]>): Record<string, Tra
|
|||||||
result[node] = transitions.map((t) => ({
|
result[node] = transitions.map((t) => ({
|
||||||
role: t.role,
|
role: t.role,
|
||||||
condition: t.condition ?? null,
|
condition: t.condition ?? null,
|
||||||
|
prompt: t.prompt ?? null,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ export async function buildContext(threadId: ThreadId, role: string): Promise<Ag
|
|||||||
}
|
}
|
||||||
|
|
||||||
const steps = await buildHistory(store, chain.stepsNewestFirst);
|
const steps = await buildHistory(store, chain.stepsNewestFirst);
|
||||||
|
const edgePrompt = process.env.UWF_EDGE_PROMPT ?? null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
threadId,
|
threadId,
|
||||||
@@ -142,6 +143,7 @@ export async function buildContext(threadId: ThreadId, role: string): Promise<Ag
|
|||||||
workflow,
|
workflow,
|
||||||
store,
|
store,
|
||||||
outputFormatInstruction: "",
|
outputFormatInstruction: "",
|
||||||
|
edgePrompt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +180,7 @@ export async function buildContextWithMeta(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const steps = await buildHistory(store, chain.stepsNewestFirst);
|
const steps = await buildHistory(store, chain.stepsNewestFirst);
|
||||||
|
const edgePrompt = process.env.UWF_EDGE_PROMPT ?? null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
threadId,
|
threadId,
|
||||||
@@ -187,6 +190,7 @@ export async function buildContextWithMeta(
|
|||||||
workflow,
|
workflow,
|
||||||
store,
|
store,
|
||||||
outputFormatInstruction: "",
|
outputFormatInstruction: "",
|
||||||
|
edgePrompt,
|
||||||
meta: { storageRoot, store, schemas, headHash, chain },
|
meta: { storageRoot, store, schemas, headHash, chain },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ export type AgentContext = ModeratorContext & {
|
|||||||
* role's output schema. Populated by `createAgent` at run time.
|
* role's output schema. Populated by `createAgent` at run time.
|
||||||
*/
|
*/
|
||||||
outputFormatInstruction: string;
|
outputFormatInstruction: string;
|
||||||
|
/**
|
||||||
|
* Edge prompt from the graph transition that led to this role.
|
||||||
|
* null on first entry (use full role definition), non-null on re-entry
|
||||||
|
* (use as continuation instruction from moderator).
|
||||||
|
*/
|
||||||
|
edgePrompt: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AgentRunResult = {
|
export type AgentRunResult = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { ModeratorContext, WorkflowPayload } from "@uncaged/workflow-protocol";
|
import type { ModeratorContext, WorkflowPayload } from "@uncaged/workflow-protocol";
|
||||||
import jsonata from "jsonata";
|
import jsonata from "jsonata";
|
||||||
|
|
||||||
import type { Result } from "./types.js";
|
import type { EvaluateResult, Result } from "./types.js";
|
||||||
|
|
||||||
const START_ROLE = "$START";
|
const START_ROLE = "$START";
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ function currentRole(context: ModeratorContext): string {
|
|||||||
export async function evaluate(
|
export async function evaluate(
|
||||||
workflow: WorkflowPayload,
|
workflow: WorkflowPayload,
|
||||||
context: ModeratorContext,
|
context: ModeratorContext,
|
||||||
): Promise<Result<string, Error>> {
|
): Promise<Result<EvaluateResult, Error>> {
|
||||||
const role = currentRole(context);
|
const role = currentRole(context);
|
||||||
const transitions = workflow.graph[role];
|
const transitions = workflow.graph[role];
|
||||||
if (transitions === undefined) {
|
if (transitions === undefined) {
|
||||||
@@ -90,7 +90,7 @@ export async function evaluate(
|
|||||||
|
|
||||||
for (const transition of transitions) {
|
for (const transition of transitions) {
|
||||||
if (transition.condition === null) {
|
if (transition.condition === null) {
|
||||||
return { ok: true, value: transition.role };
|
return { ok: true, value: { role: transition.role, prompt: transition.prompt } };
|
||||||
}
|
}
|
||||||
|
|
||||||
const conditionDef = workflow.conditions[transition.condition];
|
const conditionDef = workflow.conditions[transition.condition];
|
||||||
@@ -106,7 +106,7 @@ export async function evaluate(
|
|||||||
return evalResult;
|
return evalResult;
|
||||||
}
|
}
|
||||||
if (isTruthy(evalResult.value)) {
|
if (isTruthy(evalResult.value)) {
|
||||||
return { ok: true, value: transition.role };
|
return { ok: true, value: { role: transition.role, prompt: transition.prompt } };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
export { evaluate } from "./evaluate.js";
|
export { evaluate } from "./evaluate.js";
|
||||||
|
export type { EvaluateResult } from "./types.js";
|
||||||
|
|||||||
@@ -1 +1,7 @@
|
|||||||
export type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };
|
export type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };
|
||||||
|
|
||||||
|
/** The result of moderator evaluation — which role to go to, and the edge prompt (if any). */
|
||||||
|
export type EvaluateResult = {
|
||||||
|
role: string;
|
||||||
|
prompt: string | null;
|
||||||
|
};
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export type RoleDefinition = {
|
|||||||
export type Transition = {
|
export type Transition = {
|
||||||
role: string;
|
role: string;
|
||||||
condition: string | null;
|
condition: string | null;
|
||||||
|
prompt: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ConditionDefinition = {
|
export type ConditionDefinition = {
|
||||||
|
|||||||
Reference in New Issue
Block a user