@osolmaz/pi-workflows 0.16.5 → 0.16.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/builtins/autoimplement.workflow.d.ts +5 -1
- package/dist/builtins/autoimplement.workflow.js +29 -10
- package/dist/builtins/autoimplement.workflow.js.map +1 -1
- package/dist/client/client.js +3 -1
- package/dist/client/client.js.map +1 -1
- package/dist/client/protocol.d.ts +1 -1
- package/dist/client/protocol.js +18 -1
- package/dist/client/protocol.js.map +1 -1
- package/dist/client/view.d.ts +7 -5
- package/dist/extension/index.d.ts +2 -1
- package/dist/extension/index.js +115 -165
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/recorder.d.ts +1 -1
- package/dist/extension/recorder.js +2 -2
- package/dist/extension/recorder.js.map +1 -1
- package/dist/extension/step-message.d.ts +1 -1
- package/dist/extension/step-message.js +4 -4
- package/dist/extension/step-message.js.map +1 -1
- package/dist/extension/workflow-message-coordinator.d.ts +12 -1
- package/dist/extension/workflow-message-coordinator.js +65 -54
- package/dist/extension/workflow-message-coordinator.js.map +1 -1
- package/dist/resource-managers/index.d.ts +1 -2
- package/dist/resource-managers/index.js +1 -2
- package/dist/resource-managers/index.js.map +1 -1
- package/dist/resource-managers/sqlite.d.ts +2 -295
- package/dist/resource-managers/sqlite.js +71 -1528
- package/dist/resource-managers/sqlite.js.map +1 -1
- package/dist/resource-managers/types.d.ts +2 -7
- package/dist/server/resource-runner-entry.js +2 -6
- package/dist/server/resource-runner-entry.js.map +1 -1
- package/dist/server/rpc-bridge.js +1 -1
- package/dist/server/rpc-bridge.js.map +1 -1
- package/dist/server/rpc-executor.js +2 -4
- package/dist/server/rpc-executor.js.map +1 -1
- package/dist/server/server-entry.js +3 -0
- package/dist/server/server-entry.js.map +1 -1
- package/dist/server/server.d.ts +9 -8
- package/dist/server/server.js +364 -541
- package/dist/server/server.js.map +1 -1
- package/dist/server/state.d.ts +16 -29
- package/dist/server/state.js +105 -319
- package/dist/server/state.js.map +1 -1
- package/dist/server/view.d.ts +4 -5
- package/dist/server/view.js +51 -51
- package/dist/server/view.js.map +1 -1
- package/dist/server/workflow-runner-entry.d.ts +1 -1
- package/dist/server/workflow-runner-entry.js +1 -49
- package/dist/server/workflow-runner-entry.js.map +1 -1
- package/dist/server/workflow-runner-protocol.d.ts +2 -7
- package/dist/server/workflow-runner-protocol.js +5 -10
- package/dist/server/workflow-runner-protocol.js.map +1 -1
- package/dist/server/workflow-runner-store.d.ts +5 -6
- package/dist/server/workflow-runner-store.js +7 -14
- package/dist/server/workflow-runner-store.js.map +1 -1
- package/dist/state/attempt-time.d.ts +21 -0
- package/dist/state/attempt-time.js +95 -0
- package/dist/state/attempt-time.js.map +1 -0
- package/dist/state/json.d.ts +2 -1
- package/dist/state/json.js.map +1 -1
- package/dist/state/project-store.d.ts +39 -0
- package/dist/state/project-store.js +154 -0
- package/dist/state/project-store.js.map +1 -0
- package/dist/state/prune.js +0 -7
- package/dist/state/prune.js.map +1 -1
- package/dist/state/schema.js +23 -23
- package/dist/state/workflow-messages.d.ts +1 -1
- package/dist/state/workflow-messages.js +3 -1
- package/dist/state/workflow-messages.js.map +1 -1
- package/dist/viewer/backup.js +5 -0
- package/dist/viewer/backup.js.map +1 -1
- package/dist/workflows/diagnostics.d.ts +8 -0
- package/dist/workflows/diagnostics.js +61 -0
- package/dist/workflows/diagnostics.js.map +1 -0
- package/dist/workflows/engine.d.ts +2 -15
- package/dist/workflows/engine.js +125 -245
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/human-decision.d.ts +3 -7
- package/dist/workflows/human-decision.js +2 -56
- package/dist/workflows/human-decision.js.map +1 -1
- package/dist/workflows/index.d.ts +2 -1
- package/dist/workflows/index.js +1 -0
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/queue.d.ts +267 -0
- package/dist/workflows/queue.js +1248 -0
- package/dist/workflows/queue.js.map +1 -0
- package/dist/workflows/requests.d.ts +47 -0
- package/dist/workflows/requests.js +158 -0
- package/dist/workflows/requests.js.map +1 -0
- package/dist/workflows/schema.js +2 -4
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.d.ts +11 -16
- package/dist/workflows/store.js +174 -169
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/tool-input.d.ts +6 -9
- package/dist/workflows/tool-input.js +15 -12
- package/dist/workflows/tool-input.js.map +1 -1
- package/dist/workflows/transitions.d.ts +38 -0
- package/dist/workflows/transitions.js +276 -0
- package/dist/workflows/transitions.js.map +1 -0
- package/dist/workflows/types.d.ts +9 -42
- package/dist/workflows/workflow-message-content.d.ts +7 -1
- package/dist/workflows/workflow-message-content.js +22 -4
- package/dist/workflows/workflow-message-content.js.map +1 -1
- package/docs/2026-08-25-workflow-follow-ups.md +5 -5
- package/docs/2026-09-02-unify-workflow-messages-plan.md +2 -0
- package/docs/2026-09-04-workflow-run-state-plan.md +2 -0
- package/docs/2026-09-06-durable-execution-plan.md +176 -0
- package/docs/DEFERRED_TURNS.md +84 -105
- package/docs/HUMAN_DECISIONS.md +17 -19
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +1 -1
- package/docs/SQLITE_STATE.md +68 -29
- package/docs/WORKFLOW_SERVER.md +21 -10
- package/docs/WORKFLOW_STEP_MESSAGES.md +72 -54
- package/docs/plans/2026-08-04-controller-runtime-plan.md +2 -0
- package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +2 -0
- package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +2 -0
- package/docs/workflows.md +94 -45
- package/examples/workflows/autoresearch.workflow.ts +0 -2
- package/examples/workflows/branch.workflow.ts +0 -4
- package/examples/workflows/echo.workflow.ts +0 -2
- package/examples/workflows/live-settings.workflow.ts +0 -2
- package/examples/workflows/two-turn.workflow.ts +0 -2
- package/herdr-plugin.toml +1 -1
- package/package.json +1 -1
- package/protocol/fixtures/client-v1.json +1 -1
- package/skills/pi-workflows/SKILL.md +8 -7
- package/src/builtins/autoimplement.workflow.ts +31 -10
- package/src/client/client.ts +3 -1
- package/src/client/protocol.ts +21 -1
- package/src/client/view.ts +9 -5
- package/src/extension/index.ts +117 -189
- package/src/extension/recorder.ts +2 -2
- package/src/extension/step-message.ts +4 -4
- package/src/extension/workflow-message-coordinator.ts +78 -50
- package/src/resource-managers/index.ts +1 -9
- package/src/resource-managers/sqlite.ts +330 -2655
- package/src/resource-managers/types.ts +2 -3
- package/src/server/resource-runner-entry.ts +2 -7
- package/src/server/rpc-bridge.ts +1 -1
- package/src/server/rpc-executor.ts +4 -9
- package/src/server/server-entry.ts +3 -0
- package/src/server/server.ts +416 -641
- package/src/server/state.ts +122 -466
- package/src/server/view.ts +56 -60
- package/src/server/workflow-runner-entry.ts +2 -55
- package/src/server/workflow-runner-protocol.ts +5 -15
- package/src/server/workflow-runner-store.ts +12 -27
- package/src/state/attempt-time.ts +122 -0
- package/src/state/json.ts +2 -1
- package/src/state/project-store.ts +218 -0
- package/src/state/prune.ts +0 -13
- package/src/state/schema.ts +23 -23
- package/src/state/workflow-messages.ts +4 -2
- package/src/viewer/backup.ts +7 -0
- package/src/workflows/diagnostics.ts +78 -0
- package/src/workflows/engine.ts +133 -291
- package/src/workflows/human-decision.ts +3 -81
- package/src/workflows/index.ts +8 -2
- package/src/workflows/queue.ts +2091 -0
- package/src/workflows/requests.ts +283 -0
- package/src/workflows/schema.ts +2 -6
- package/src/workflows/store.ts +223 -231
- package/src/workflows/tool-input.ts +20 -12
- package/src/workflows/transitions.ts +330 -0
- package/src/workflows/types.ts +9 -46
- package/src/workflows/workflow-message-content.ts +28 -4
- package/dist/resource-managers/runtime.d.ts +0 -59
- package/dist/resource-managers/runtime.js +0 -423
- package/dist/resource-managers/runtime.js.map +0 -1
- package/src/resource-managers/runtime.ts +0 -576
package/dist/extension/index.js
CHANGED
|
@@ -86,33 +86,22 @@ export function parseWorkflowArgs(args) {
|
|
|
86
86
|
return { kind: "status", runId };
|
|
87
87
|
}
|
|
88
88
|
if (trimmed === "answer" || trimmed.startsWith("answer ")) {
|
|
89
|
-
|
|
90
|
-
if (rest.length === 0) {
|
|
91
|
-
throw new Error('answer requires a JSON value or text, e.g. /workflow answer {"approved":true}');
|
|
92
|
-
}
|
|
93
|
-
let runId;
|
|
89
|
+
const rest = trimmed === "answer" ? "" : trimmed.slice("answer".length).trim();
|
|
94
90
|
const firstSpace = rest.search(/\s/);
|
|
95
|
-
if (firstSpace
|
|
96
|
-
|
|
97
|
-
const remainder = rest.slice(firstSpace).trim();
|
|
98
|
-
if (validRunId(candidate) &&
|
|
99
|
-
remainder.length > 0 &&
|
|
100
|
-
(remainder.startsWith("{") || remainder.startsWith("["))) {
|
|
101
|
-
runId = candidate;
|
|
102
|
-
rest = remainder;
|
|
103
|
-
}
|
|
91
|
+
if (firstSpace <= 0 || rest.slice(firstSpace).trim().length === 0) {
|
|
92
|
+
throw new Error('answer requires an exact request id and a response: /workflow answer REQUEST_ID {"choice":"approve"}');
|
|
104
93
|
}
|
|
94
|
+
const requestId = rest.slice(0, firstSpace);
|
|
95
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(requestId))
|
|
96
|
+
throw new Error("answer requires a valid request id");
|
|
97
|
+
const text = rest.slice(firstSpace).trim();
|
|
105
98
|
try {
|
|
106
|
-
return {
|
|
107
|
-
kind: "answer",
|
|
108
|
-
input: JSON.parse(rest),
|
|
109
|
-
...(runId === undefined ? {} : { runId }),
|
|
110
|
-
};
|
|
99
|
+
return { kind: "answer", requestId, input: JSON.parse(text) };
|
|
111
100
|
}
|
|
112
101
|
catch {
|
|
113
|
-
if (
|
|
114
|
-
throw new Error("answer
|
|
115
|
-
return { kind: "answer", input: { answer:
|
|
102
|
+
if (/^[{["]/.test(text))
|
|
103
|
+
throw new Error("answer contains invalid JSON");
|
|
104
|
+
return { kind: "answer", requestId, input: { answer: text } };
|
|
116
105
|
}
|
|
117
106
|
}
|
|
118
107
|
const spaceIndex = trimmed.search(/\s/);
|
|
@@ -143,6 +132,7 @@ export default function piWorkflows(pi) {
|
|
|
143
132
|
const sessionView = new SessionWorkflowView();
|
|
144
133
|
const sessionRecorders = new Map();
|
|
145
134
|
let agentRunning = false;
|
|
135
|
+
let lastStopReason = "completed";
|
|
146
136
|
let activeRecorder = null;
|
|
147
137
|
let activeRecorderMessageId = null;
|
|
148
138
|
const ensureRecorder = async (message, ctx) => {
|
|
@@ -163,7 +153,7 @@ export default function piWorkflows(pi) {
|
|
|
163
153
|
if (message === undefined || activeRecorderMessageId === message.workflowMessageId)
|
|
164
154
|
return;
|
|
165
155
|
const contract = agentContractForWorkflowMessage(message);
|
|
166
|
-
if (contract === undefined && message.kind !== "
|
|
156
|
+
if (contract === undefined && message.kind !== "followUp") {
|
|
167
157
|
return;
|
|
168
158
|
}
|
|
169
159
|
const recorder = await ensureRecorder(message, ctx);
|
|
@@ -172,7 +162,7 @@ export default function piWorkflows(pi) {
|
|
|
172
162
|
return;
|
|
173
163
|
}
|
|
174
164
|
if (contract === undefined) {
|
|
175
|
-
recorder.
|
|
165
|
+
recorder.beginFollowUp(message.workflowMessageId);
|
|
176
166
|
}
|
|
177
167
|
else {
|
|
178
168
|
recorder.beginAttempt(contract);
|
|
@@ -188,7 +178,28 @@ export default function piWorkflows(pi) {
|
|
|
188
178
|
});
|
|
189
179
|
await prior;
|
|
190
180
|
try {
|
|
191
|
-
|
|
181
|
+
const finishRecording = async (message) => {
|
|
182
|
+
const recorder = sessionRecorders.get(message.runId);
|
|
183
|
+
if (recorder === undefined)
|
|
184
|
+
return;
|
|
185
|
+
await recorder.record(ctx).catch((error) => {
|
|
186
|
+
ctx.ui.notify(`Workflow conversation recording failed: ${errorMessage(error)}`, "warning");
|
|
187
|
+
});
|
|
188
|
+
await recorder.finish();
|
|
189
|
+
sessionRecorders.delete(message.runId);
|
|
190
|
+
if (activeRecorder === recorder)
|
|
191
|
+
activeRecorder = null;
|
|
192
|
+
};
|
|
193
|
+
await workflowMessages.synchronize(pi, client, ctx, {
|
|
194
|
+
beforeTurnEnd: async (message, end) => {
|
|
195
|
+
if (end.stopReason === "completed") {
|
|
196
|
+
await submitVisibleAssistantResponse(client, ctx, message, end.responseSessionEntryId);
|
|
197
|
+
}
|
|
198
|
+
if (message.kind === "followUp")
|
|
199
|
+
await finishRecording(message);
|
|
200
|
+
},
|
|
201
|
+
terminalDelivered: finishRecording,
|
|
202
|
+
});
|
|
192
203
|
await activateRecorder(ctx);
|
|
193
204
|
}
|
|
194
205
|
finally {
|
|
@@ -412,7 +423,7 @@ export default function piWorkflows(pi) {
|
|
|
412
423
|
"List, start, restart, inspect, change settings, queue or remove follow-ups, pause, resume, cancel, answer ordinary checkpoints, update, or complete hosted workflow runs.",
|
|
413
424
|
"Protected human decisions cannot be answered with this model-facing tool.",
|
|
414
425
|
"When the user asks to continue or resume the active workflow, call workflow resume immediately.",
|
|
415
|
-
"Use update or submit only when a workflow step contract asks for it, and pass
|
|
426
|
+
"Use update or submit only when a workflow step contract asks for it, and pass its exact requestId.",
|
|
416
427
|
"Do not start repeated work without the user's request.",
|
|
417
428
|
].join(" "),
|
|
418
429
|
parameters: WorkflowToolParameters,
|
|
@@ -420,28 +431,16 @@ export default function piWorkflows(pi) {
|
|
|
420
431
|
return await runToolInOrder(async () => {
|
|
421
432
|
const params = parseWorkflowToolInput(rawParams);
|
|
422
433
|
if (params.action === "update" || params.action === "submit") {
|
|
423
|
-
const interaction = pendingInteractionForSession(ctx.sessionManager.getSessionId());
|
|
424
|
-
if (interaction === undefined)
|
|
425
|
-
throw new Error("No workflow step is waiting for output");
|
|
426
|
-
const contract = agentContract(interaction);
|
|
427
|
-
if (contract === undefined)
|
|
428
|
-
throw new Error("The pending interaction is not an agent step");
|
|
429
|
-
if (contract.nodeId !== params.step || contract.attemptId !== params.attempt) {
|
|
430
|
-
throw new Error("Workflow step or attempt does not match the durable request");
|
|
431
|
-
}
|
|
432
434
|
let response;
|
|
433
435
|
try {
|
|
434
436
|
response = await requestAccepted(client, {
|
|
435
437
|
operation: params.action === "update" ? "interaction.update" : "interaction.submit",
|
|
436
438
|
requestId: `${params.action}-${toolCallId}-${randomUUID()}`,
|
|
437
439
|
idempotencyKey: toolCallId,
|
|
438
|
-
runId: interaction.runId,
|
|
439
|
-
expectedRevision: interaction.revision,
|
|
440
440
|
payload: jsonValue({
|
|
441
|
-
|
|
441
|
+
...sessionCommandPayload(ctx),
|
|
442
|
+
requestId: params.requestId,
|
|
442
443
|
submissionId: toolCallId,
|
|
443
|
-
step: params.step,
|
|
444
|
-
attempt: params.attempt,
|
|
445
444
|
value: params.action === "update"
|
|
446
445
|
? { update: params.update }
|
|
447
446
|
: { output: params.output },
|
|
@@ -592,35 +591,16 @@ export default function piWorkflows(pi) {
|
|
|
592
591
|
});
|
|
593
592
|
pi.on("agent_start", async (_event, ctx) => {
|
|
594
593
|
agentRunning = true;
|
|
594
|
+
lastStopReason = "completed";
|
|
595
595
|
activeRecorder = null;
|
|
596
596
|
activeRecorderMessageId = null;
|
|
597
597
|
workflowMessages.startTurn();
|
|
598
598
|
await presentInOrder(ctx).catch(() => undefined);
|
|
599
599
|
});
|
|
600
|
-
pi.on("agent_end",
|
|
601
|
-
try {
|
|
602
|
-
await submitVisibleAssistantResponse(client, ctx);
|
|
603
|
-
}
|
|
604
|
-
catch (error) {
|
|
605
|
-
ctx.ui.notify(`Workflow response was rejected: ${errorMessage(error)}`, "error");
|
|
606
|
-
}
|
|
607
|
-
const finishedMessage = workflowMessages.activeTurnMessage();
|
|
600
|
+
pi.on("agent_end", (event, ctx) => {
|
|
608
601
|
agentRunning = false;
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
if (activeRecorder !== null &&
|
|
612
|
-
finishedMessage !== undefined &&
|
|
613
|
-
(finishedMessage.kind === "terminal" || finishedMessage.kind === "followUp")) {
|
|
614
|
-
const finishedRecorder = activeRecorder;
|
|
615
|
-
activeRecorder = null;
|
|
616
|
-
await finishedRecorder.finish();
|
|
617
|
-
if (sessionRecorders.get(finishedMessage.runId) === finishedRecorder) {
|
|
618
|
-
sessionRecorders.delete(finishedMessage.runId);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
await presentInOrder(ctx).catch((error) => {
|
|
622
|
-
ctx.ui.notify(`Could not record workflow model activity: ${errorMessage(error)}`, "warning");
|
|
623
|
-
});
|
|
602
|
+
lastStopReason = workflowTurnStopReason(event.messages, ctx.signal?.aborted === true);
|
|
603
|
+
// Pi can retry after agent_end. Only agent_settled completes the owned turn.
|
|
624
604
|
});
|
|
625
605
|
pi.on("turn_start", (event) => {
|
|
626
606
|
activeRecorder?.handleTurnStart(event);
|
|
@@ -648,13 +628,11 @@ export default function piWorkflows(pi) {
|
|
|
648
628
|
});
|
|
649
629
|
pi.on("agent_settled", async (_event, ctx) => {
|
|
650
630
|
activeRecorder?.settleAttempt();
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
}
|
|
657
|
-
await presentInOrder(ctx).catch(() => undefined);
|
|
631
|
+
workflowMessages.endTurn(lastStopReason, responseEntryId(ctx.sessionManager.getBranch()));
|
|
632
|
+
activeRecorderMessageId = null;
|
|
633
|
+
await presentInOrder(ctx).catch((error) => {
|
|
634
|
+
ctx.ui.notify(`Could not record workflow model activity: ${errorMessage(error)}`, "warning");
|
|
635
|
+
});
|
|
658
636
|
});
|
|
659
637
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
660
638
|
sessionGeneration += 1;
|
|
@@ -788,17 +766,16 @@ async function executeCommand(client, ctx, command, idempotencyKey = randomUUID(
|
|
|
788
766
|
if (runId === undefined)
|
|
789
767
|
throw new Error("No workflow terminal result is available to restart");
|
|
790
768
|
const session = sessionCommandPayload(ctx);
|
|
791
|
-
const
|
|
769
|
+
const expectedRevision = command.expectedRevision ?? (await client.getRun(runId))?.runRevision;
|
|
770
|
+
if (expectedRevision === undefined)
|
|
771
|
+
throw new Error("Workflow run revision is unavailable");
|
|
792
772
|
const response = await requestAccepted(client, {
|
|
793
773
|
operation: "run.restart",
|
|
794
774
|
requestId: `restart-${idempotencyKey}`,
|
|
795
775
|
idempotencyKey,
|
|
796
776
|
runId,
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
workflowMessageId: marker.workflowMessageId,
|
|
800
|
-
...(marker.workflowTurnId === undefined ? {} : { workflowTurnId: marker.workflowTurnId }),
|
|
801
|
-
},
|
|
777
|
+
expectedRevision,
|
|
778
|
+
payload: session,
|
|
802
779
|
});
|
|
803
780
|
return {
|
|
804
781
|
message: `Restarted workflow ${runId}.`,
|
|
@@ -906,11 +883,30 @@ async function executeCommand(client, ctx, command, idempotencyKey = randomUUID(
|
|
|
906
883
|
};
|
|
907
884
|
}
|
|
908
885
|
case "answer": {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
886
|
+
if (authority === "model") {
|
|
887
|
+
const response = await requestAccepted(client, {
|
|
888
|
+
operation: "checkpoint.answer",
|
|
889
|
+
requestId: `checkpoint-${idempotencyKey}`,
|
|
890
|
+
idempotencyKey,
|
|
891
|
+
payload: {
|
|
892
|
+
...sessionCommandPayload(ctx),
|
|
893
|
+
requestId: command.requestId,
|
|
894
|
+
submissionId: idempotencyKey,
|
|
895
|
+
input: jsonValue(command.input),
|
|
896
|
+
},
|
|
897
|
+
});
|
|
898
|
+
return {
|
|
899
|
+
message: `Answered checkpoint ${command.requestId}.`,
|
|
900
|
+
details: { action: "answer", response: response.receipt ?? null },
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
const interaction = sessionSnapshots
|
|
904
|
+
.get(ctx.sessionManager.getSessionId())
|
|
905
|
+
?.pendingInteractions.map(parseInteractiveRequest)
|
|
906
|
+
.find((request) => request?.requestId === command.requestId);
|
|
907
|
+
if (interaction === undefined)
|
|
908
|
+
throw new Error("No matching checkpoint request is waiting in this session");
|
|
909
|
+
if (interaction.kind === "decision") {
|
|
914
910
|
const response = await requestAccepted(client, {
|
|
915
911
|
operation: "decision.answer",
|
|
916
912
|
requestId: `answer-${idempotencyKey}`,
|
|
@@ -918,6 +914,7 @@ async function executeCommand(client, ctx, command, idempotencyKey = randomUUID(
|
|
|
918
914
|
runId: interaction.runId,
|
|
919
915
|
expectedRevision: interaction.revision,
|
|
920
916
|
payload: {
|
|
917
|
+
...sessionCommandPayload(ctx),
|
|
921
918
|
requestId: interaction.requestId,
|
|
922
919
|
submissionId: idempotencyKey,
|
|
923
920
|
response: decisionResponse(command.input),
|
|
@@ -932,32 +929,26 @@ async function executeCommand(client, ctx, command, idempotencyKey = randomUUID(
|
|
|
932
929
|
},
|
|
933
930
|
};
|
|
934
931
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
throw new Error("No checkpoint is waiting in this session");
|
|
938
|
-
const continuationRunId = createRunId(parent.workflowName);
|
|
932
|
+
if (interaction.kind !== "checkpoint")
|
|
933
|
+
throw new Error("Only an ordinary checkpoint accepts an answer");
|
|
939
934
|
const response = await requestAccepted(client, {
|
|
940
935
|
operation: "checkpoint.answer",
|
|
941
936
|
requestId: `checkpoint-${idempotencyKey}`,
|
|
942
937
|
idempotencyKey,
|
|
943
|
-
runId:
|
|
938
|
+
runId: interaction.runId,
|
|
939
|
+
expectedRevision: interaction.revision,
|
|
944
940
|
payload: {
|
|
945
|
-
|
|
941
|
+
...sessionCommandPayload(ctx),
|
|
942
|
+
requestId: interaction.requestId,
|
|
943
|
+
submissionId: idempotencyKey,
|
|
946
944
|
input: jsonValue(command.input),
|
|
947
945
|
},
|
|
948
946
|
});
|
|
949
|
-
const receipt = isRecord(response.receipt) ? response.receipt : undefined;
|
|
950
|
-
const actualRunId = receipt !== undefined && typeof receipt.runId === "string"
|
|
951
|
-
? receipt.runId
|
|
952
|
-
: continuationRunId;
|
|
953
947
|
return {
|
|
954
|
-
message:
|
|
955
|
-
? `Checkpoint ${parent.runId} was already answered; continuation ${actualRunId} exists.`
|
|
956
|
-
: `Answered checkpoint ${parent.runId}; continuation ${actualRunId} started.`,
|
|
948
|
+
message: `Answered checkpoint ${interaction.requestId}; run ${interaction.runId} can continue.`,
|
|
957
949
|
details: {
|
|
958
950
|
action: "answer",
|
|
959
|
-
|
|
960
|
-
runId: actualRunId,
|
|
951
|
+
runId: interaction.runId,
|
|
961
952
|
response: response.receipt ?? null,
|
|
962
953
|
},
|
|
963
954
|
};
|
|
@@ -1023,50 +1014,36 @@ async function executeResourceManagerCommand(client, ctx, command) {
|
|
|
1023
1014
|
details: { action: command.kind, resource: response.receipt ?? null },
|
|
1024
1015
|
};
|
|
1025
1016
|
}
|
|
1026
|
-
async function submitVisibleAssistantResponse(client, ctx) {
|
|
1027
|
-
const
|
|
1028
|
-
if (
|
|
1029
|
-
interaction.kind !== "assistant" ||
|
|
1030
|
-
workflowRunPaused(interaction.runId)) {
|
|
1017
|
+
async function submitVisibleAssistantResponse(client, ctx, message, settledResponseEntryId) {
|
|
1018
|
+
const contract = agentContractForWorkflowMessage(message);
|
|
1019
|
+
if (contract?.completion !== "assistant" || workflowRunPaused(message.runId))
|
|
1031
1020
|
return;
|
|
1032
|
-
|
|
1033
|
-
const contract = agentContract(interaction);
|
|
1034
|
-
if (contract === undefined)
|
|
1021
|
+
if (settledResponseEntryId === null)
|
|
1035
1022
|
return;
|
|
1036
|
-
const
|
|
1023
|
+
const branch = ctx.sessionManager.getBranch();
|
|
1024
|
+
const responseIndex = branch.findIndex((entry) => entry.id === settledResponseEntryId);
|
|
1025
|
+
if (responseIndex < 0)
|
|
1026
|
+
return;
|
|
1027
|
+
const submission = recoverAssistantStep(branch.slice(0, responseIndex + 1), contract);
|
|
1037
1028
|
if (submission === undefined)
|
|
1038
1029
|
return;
|
|
1039
1030
|
const responseId = submission.conversation?.lastEntryId ?? submission.assistantMessage?.entryId;
|
|
1040
|
-
if (responseId
|
|
1031
|
+
if (responseId !== settledResponseEntryId)
|
|
1041
1032
|
return;
|
|
1042
|
-
await
|
|
1043
|
-
operation: "interaction.
|
|
1044
|
-
requestId: `assistant-${
|
|
1045
|
-
idempotencyKey: `assistant-${
|
|
1046
|
-
runId: interaction.runId,
|
|
1047
|
-
expectedRevision: interaction.revision,
|
|
1033
|
+
const response = await client.request({
|
|
1034
|
+
operation: "interaction.assistant",
|
|
1035
|
+
requestId: `assistant-${message.sourceId}-${responseId}`,
|
|
1036
|
+
idempotencyKey: `assistant-${message.sourceId}-${responseId}`,
|
|
1048
1037
|
payload: {
|
|
1049
|
-
|
|
1038
|
+
...sessionCommandPayload(ctx),
|
|
1039
|
+
requestId: message.sourceId,
|
|
1050
1040
|
submissionId: `assistant-${responseId}`,
|
|
1051
|
-
step: contract.nodeId,
|
|
1052
|
-
attempt: contract.attemptId,
|
|
1053
1041
|
value: submission,
|
|
1054
1042
|
},
|
|
1055
1043
|
});
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
.get(sessionId)
|
|
1060
|
-
?.pendingInteractions.map(parseInteractiveRequest)
|
|
1061
|
-
.find((request) => request !== undefined);
|
|
1062
|
-
}
|
|
1063
|
-
function pendingDecision(sessionId, runId) {
|
|
1064
|
-
return sessionSnapshots
|
|
1065
|
-
.get(sessionId)
|
|
1066
|
-
?.pendingInteractions.map(parseInteractiveRequest)
|
|
1067
|
-
.find((request) => request !== undefined &&
|
|
1068
|
-
request.kind === "decision" &&
|
|
1069
|
-
(runId === undefined || request.runId === runId));
|
|
1044
|
+
if (response.outcome !== "accepted" && response.outcome !== "adopted") {
|
|
1045
|
+
ctx.ui.notify(`Workflow response was rejected: ${response.error ?? response.outcome}`, "error");
|
|
1046
|
+
}
|
|
1070
1047
|
}
|
|
1071
1048
|
function workflowRunPaused(runId) {
|
|
1072
1049
|
for (const session of sessionSnapshots.values()) {
|
|
@@ -1089,21 +1066,6 @@ function sessionCommandPayload(ctx) {
|
|
|
1089
1066
|
}
|
|
1090
1067
|
return { targetSessionId, coordinatorEpoch: session.coordinatorEpoch };
|
|
1091
1068
|
}
|
|
1092
|
-
function terminalMessageForSession(ctx, runId) {
|
|
1093
|
-
const session = sessionSnapshots.get(ctx.sessionManager.getSessionId());
|
|
1094
|
-
const message = session?.workflowMessages
|
|
1095
|
-
.filter((item) => item.kind === "terminal" && item.runId === runId && item.status === "sent")
|
|
1096
|
-
.sort((left, right) => right.order - left.order)[0];
|
|
1097
|
-
if (message === undefined)
|
|
1098
|
-
throw new Error(`Workflow run ${runId} has no current terminal result`);
|
|
1099
|
-
const turn = session?.openWorkflowTurn;
|
|
1100
|
-
return {
|
|
1101
|
-
workflowMessageId: message.workflowMessageId,
|
|
1102
|
-
...(turn?.workflowMessageId === message.workflowMessageId
|
|
1103
|
-
? { workflowTurnId: turn.workflowTurnId }
|
|
1104
|
-
: {}),
|
|
1105
|
-
};
|
|
1106
|
-
}
|
|
1107
1069
|
function sessionRun(ctx, runId) {
|
|
1108
1070
|
const session = sessionSnapshots.get(ctx.sessionManager.getSessionId());
|
|
1109
1071
|
if (session?.run === null || session?.run === undefined || !isRecord(session.run.queue)) {
|
|
@@ -1132,7 +1094,7 @@ function toolInputToCommand(params) {
|
|
|
1132
1094
|
case "start":
|
|
1133
1095
|
return { kind: "run", ref: params.workflow, input: params.input ?? {} };
|
|
1134
1096
|
case "restart":
|
|
1135
|
-
return { kind: "restart", runId: params.runId };
|
|
1097
|
+
return { kind: "restart", runId: params.runId, expectedRevision: params.expectedRevision };
|
|
1136
1098
|
case "change-settings":
|
|
1137
1099
|
return {
|
|
1138
1100
|
kind: "change-settings",
|
|
@@ -1168,8 +1130,8 @@ function toolInputToCommand(params) {
|
|
|
1168
1130
|
case "answer":
|
|
1169
1131
|
return {
|
|
1170
1132
|
kind: "answer",
|
|
1171
|
-
|
|
1172
|
-
|
|
1133
|
+
requestId: params.requestId,
|
|
1134
|
+
input: params.input,
|
|
1173
1135
|
};
|
|
1174
1136
|
case "update":
|
|
1175
1137
|
case "submit":
|
|
@@ -1186,28 +1148,13 @@ async function requestAccepted(client, options) {
|
|
|
1186
1148
|
}
|
|
1187
1149
|
return response;
|
|
1188
1150
|
}
|
|
1189
|
-
function interactionContract(interaction) {
|
|
1190
|
-
if (!isRecord(interaction.contract))
|
|
1191
|
-
throw new Error("Stored interaction contract is invalid");
|
|
1192
|
-
return interaction.contract;
|
|
1193
|
-
}
|
|
1194
|
-
function agentContract(interaction) {
|
|
1195
|
-
const value = interactionContract(interaction).contract;
|
|
1196
|
-
if (!isRecord(value) ||
|
|
1197
|
-
typeof value.runId !== "string" ||
|
|
1198
|
-
typeof value.workflowName !== "string" ||
|
|
1199
|
-
typeof value.nodeId !== "string" ||
|
|
1200
|
-
typeof value.attemptId !== "string" ||
|
|
1201
|
-
(value.completion !== "submit" && value.completion !== "assistant")) {
|
|
1202
|
-
return undefined;
|
|
1203
|
-
}
|
|
1204
|
-
return value;
|
|
1205
|
-
}
|
|
1206
1151
|
function agentContractForWorkflowMessage(message) {
|
|
1207
1152
|
if (message.kind !== "step" || !isRecord(message.content.details))
|
|
1208
1153
|
return undefined;
|
|
1209
1154
|
const value = message.content.details.contract;
|
|
1210
1155
|
if (!isRecord(value) ||
|
|
1156
|
+
value.requestId !== message.sourceId ||
|
|
1157
|
+
value.runId !== message.runId ||
|
|
1211
1158
|
typeof value.runId !== "string" ||
|
|
1212
1159
|
typeof value.workflowName !== "string" ||
|
|
1213
1160
|
typeof value.nodeId !== "string" ||
|
|
@@ -1303,7 +1250,10 @@ function parseInteractiveRequest(value) {
|
|
|
1303
1250
|
typeof value.runId !== "string" ||
|
|
1304
1251
|
typeof value.targetSessionId !== "string" ||
|
|
1305
1252
|
typeof value.revision !== "number" ||
|
|
1306
|
-
(value.kind !== "agent" &&
|
|
1253
|
+
(value.kind !== "agent" &&
|
|
1254
|
+
value.kind !== "assistant" &&
|
|
1255
|
+
value.kind !== "checkpoint" &&
|
|
1256
|
+
value.kind !== "decision")) {
|
|
1307
1257
|
return undefined;
|
|
1308
1258
|
}
|
|
1309
1259
|
return value;
|