@fragno-dev/pi-harness 0.0.0-stage → 0.0.3

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.
Files changed (140) hide show
  1. package/LICENSE.md +16 -0
  2. package/README.md +181 -2
  3. package/dist/browser/agent-harness-event-protocol-CuKubox1.js +666 -0
  4. package/dist/browser/agent-harness-event-protocol-CuKubox1.js.map +1 -0
  5. package/dist/browser/agent-harness-event-protocol-d6mH_9n3.d.ts +261 -0
  6. package/dist/browser/agent-harness-event-protocol-d6mH_9n3.d.ts.map +1 -0
  7. package/dist/browser/client/clients.d.ts +2 -0
  8. package/dist/browser/client/clients.js +2 -0
  9. package/dist/browser/client/pi-workflow-emission-stream.d.ts +13 -0
  10. package/dist/browser/client/pi-workflow-emission-stream.d.ts.map +1 -0
  11. package/dist/browser/client/pi-workflow-emission-stream.js +29 -0
  12. package/dist/browser/client/pi-workflow-emission-stream.js.map +1 -0
  13. package/dist/browser/client/react.d.ts +147 -0
  14. package/dist/browser/client/react.d.ts.map +1 -0
  15. package/dist/browser/client/react.js +187 -0
  16. package/dist/browser/client/react.js.map +1 -0
  17. package/dist/browser/client/solid.d.ts +150 -0
  18. package/dist/browser/client/solid.d.ts.map +1 -0
  19. package/dist/browser/client/solid.js +130 -0
  20. package/dist/browser/client/solid.js.map +1 -0
  21. package/dist/browser/client/svelte.d.ts +150 -0
  22. package/dist/browser/client/svelte.d.ts.map +1 -0
  23. package/dist/browser/client/svelte.js +132 -0
  24. package/dist/browser/client/svelte.js.map +1 -0
  25. package/dist/browser/client/vanilla.d.ts +177 -0
  26. package/dist/browser/client/vanilla.d.ts.map +1 -0
  27. package/dist/browser/client/vanilla.js +159 -0
  28. package/dist/browser/client/vanilla.js.map +1 -0
  29. package/dist/browser/client/vue.d.ts +150 -0
  30. package/dist/browser/client/vue.d.ts.map +1 -0
  31. package/dist/browser/client/vue.js +128 -0
  32. package/dist/browser/client/vue.js.map +1 -0
  33. package/dist/browser/client/workflow-lofi-session-projection.d.ts +17 -0
  34. package/dist/browser/client/workflow-lofi-session-projection.d.ts.map +1 -0
  35. package/dist/browser/client/workflow-lofi-session-projection.js +107 -0
  36. package/dist/browser/client/workflow-lofi-session-projection.js.map +1 -0
  37. package/dist/browser/clients-DYXj19je.d.ts +247 -0
  38. package/dist/browser/clients-DYXj19je.d.ts.map +1 -0
  39. package/dist/browser/clients-pIAk6ClP.js +1853 -0
  40. package/dist/browser/clients-pIAk6ClP.js.map +1 -0
  41. package/dist/browser/pi/context-usage.d.ts +20 -0
  42. package/dist/browser/pi/context-usage.d.ts.map +1 -0
  43. package/dist/browser/pi/context-usage.js +84 -0
  44. package/dist/browser/pi/context-usage.js.map +1 -0
  45. package/dist/browser/pi/harness/agent-harness-event-protocol.d.ts +2 -0
  46. package/dist/browser/pi/harness/agent-harness-event-protocol.js +2 -0
  47. package/dist/browser/pi/route-schemas.d.ts +2 -0
  48. package/dist/browser/pi/route-schemas.js +130 -0
  49. package/dist/browser/pi/route-schemas.js.map +1 -0
  50. package/dist/browser/pi/workflow-session-projection.d.ts +2 -0
  51. package/dist/browser/pi/workflow-session-projection.js +2 -0
  52. package/dist/browser/route-schemas-DQB5ZR1E.d.ts +109 -0
  53. package/dist/browser/route-schemas-DQB5ZR1E.d.ts.map +1 -0
  54. package/dist/browser/types-8q4RWFXT.js +23 -0
  55. package/dist/browser/types-8q4RWFXT.js.map +1 -0
  56. package/dist/browser/types-Dk856kV1.d.ts +527 -0
  57. package/dist/browser/types-Dk856kV1.d.ts.map +1 -0
  58. package/dist/browser/workflow-session-projection-CJ4MUkpJ.js +443 -0
  59. package/dist/browser/workflow-session-projection-CJ4MUkpJ.js.map +1 -0
  60. package/dist/browser/workflow-session-projection-Dj_a79GF.d.ts +93 -0
  61. package/dist/browser/workflow-session-projection-Dj_a79GF.d.ts.map +1 -0
  62. package/dist/node/pi/compact-command-outcome-projection.js +41 -0
  63. package/dist/node/pi/compact-command-outcome-projection.js.map +1 -0
  64. package/dist/node/pi/context-usage.d.ts +20 -0
  65. package/dist/node/pi/context-usage.d.ts.map +1 -0
  66. package/dist/node/pi/context-usage.js +84 -0
  67. package/dist/node/pi/context-usage.js.map +1 -0
  68. package/dist/node/pi/definition.d.ts +535 -0
  69. package/dist/node/pi/definition.d.ts.map +1 -0
  70. package/dist/node/pi/definition.js +71 -0
  71. package/dist/node/pi/definition.js.map +1 -0
  72. package/dist/node/pi/factory.d.ts +142 -0
  73. package/dist/node/pi/factory.d.ts.map +1 -0
  74. package/dist/node/pi/factory.js +22 -0
  75. package/dist/node/pi/factory.js.map +1 -0
  76. package/dist/node/pi/harness/agent-harness-event-protocol.d.ts +261 -0
  77. package/dist/node/pi/harness/agent-harness-event-protocol.d.ts.map +1 -0
  78. package/dist/node/pi/harness/agent-harness-event-protocol.js +588 -0
  79. package/dist/node/pi/harness/agent-harness-event-protocol.js.map +1 -0
  80. package/dist/node/pi/harness/json-value.js +82 -0
  81. package/dist/node/pi/harness/json-value.js.map +1 -0
  82. package/dist/node/pi/harness/pi-operation-completed.d.ts +16 -0
  83. package/dist/node/pi/harness/pi-operation-completed.d.ts.map +1 -0
  84. package/dist/node/pi/harness/pi-operation-completed.js +71 -0
  85. package/dist/node/pi/harness/pi-operation-completed.js.map +1 -0
  86. package/dist/node/pi/harness/session-storage.d.ts +62 -0
  87. package/dist/node/pi/harness/session-storage.d.ts.map +1 -0
  88. package/dist/node/pi/harness/session-storage.js +174 -0
  89. package/dist/node/pi/harness/session-storage.js.map +1 -0
  90. package/dist/node/pi/pi-jsonl-export.d.ts +11 -0
  91. package/dist/node/pi/pi-jsonl-export.d.ts.map +1 -0
  92. package/dist/node/pi/pi-jsonl-export.js +31 -0
  93. package/dist/node/pi/pi-jsonl-export.js.map +1 -0
  94. package/dist/node/pi/route-schemas.d.ts +109 -0
  95. package/dist/node/pi/route-schemas.d.ts.map +1 -0
  96. package/dist/node/pi/route-schemas.js +130 -0
  97. package/dist/node/pi/route-schemas.js.map +1 -0
  98. package/dist/node/pi/session-command-protocol.d.ts +14 -0
  99. package/dist/node/pi/session-command-protocol.d.ts.map +1 -0
  100. package/dist/node/pi/session-command-protocol.js +25 -0
  101. package/dist/node/pi/session-command-protocol.js.map +1 -0
  102. package/dist/node/pi/session-entry-projection.js +79 -0
  103. package/dist/node/pi/session-entry-projection.js.map +1 -0
  104. package/dist/node/pi/skills.d.ts +70 -0
  105. package/dist/node/pi/skills.d.ts.map +1 -0
  106. package/dist/node/pi/skills.js +150 -0
  107. package/dist/node/pi/skills.js.map +1 -0
  108. package/dist/node/pi/tools.d.ts +12 -0
  109. package/dist/node/pi/tools.d.ts.map +1 -0
  110. package/dist/node/pi/tools.js +8 -0
  111. package/dist/node/pi/tools.js.map +1 -0
  112. package/dist/node/pi/types.d.ts +165 -0
  113. package/dist/node/pi/types.d.ts.map +1 -0
  114. package/dist/node/pi/types.js +49 -0
  115. package/dist/node/pi/types.js.map +1 -0
  116. package/dist/node/pi/wait-for-command-step.js +46 -0
  117. package/dist/node/pi/wait-for-command-step.js.map +1 -0
  118. package/dist/node/pi/workflow-session-live-projection.js +220 -0
  119. package/dist/node/pi/workflow-session-live-projection.js.map +1 -0
  120. package/dist/node/pi/workflow-session-projection.d.ts +84 -0
  121. package/dist/node/pi/workflow-session-projection.d.ts.map +1 -0
  122. package/dist/node/pi/workflow-session-projection.js +100 -0
  123. package/dist/node/pi/workflow-session-projection.js.map +1 -0
  124. package/dist/node/pi/workflows/interactive-chat-workflow.d.ts +37 -0
  125. package/dist/node/pi/workflows/interactive-chat-workflow.d.ts.map +1 -0
  126. package/dist/node/pi/workflows/interactive-chat-workflow.js +219 -0
  127. package/dist/node/pi/workflows/interactive-chat-workflow.js.map +1 -0
  128. package/dist/node/pi/workflows/workflow-agent-harness.d.ts +142 -0
  129. package/dist/node/pi/workflows/workflow-agent-harness.d.ts.map +1 -0
  130. package/dist/node/pi/workflows/workflow-agent-harness.js +450 -0
  131. package/dist/node/pi/workflows/workflow-agent-harness.js.map +1 -0
  132. package/dist/node/routes.d.ts +631 -0
  133. package/dist/node/routes.d.ts.map +1 -0
  134. package/dist/node/routes.js +355 -0
  135. package/dist/node/routes.js.map +1 -0
  136. package/dist/node/schema.d.ts +5 -0
  137. package/dist/node/schema.d.ts.map +1 -0
  138. package/dist/node/schema.js +7 -0
  139. package/dist/node/schema.js.map +1 -0
  140. package/package.json +175 -3
@@ -0,0 +1,450 @@
1
+ import { PiHarnessEventEncoder } from "../harness/agent-harness-event-protocol.js";
2
+ import { WorkflowBackedSessionStorage, createWorkflowBackedSessionEntryIdAllocator, nextWorkflowBackedSessionEntryIndex, sessionEntriesLeafId } from "../harness/session-storage.js";
3
+ import { Session } from "@earendil-works/pi-agent-core";
4
+ //#region src/pi/workflows/workflow-agent-harness.ts
5
+ const createInitialMessageEntries = (messages) => {
6
+ if (!messages) return [];
7
+ let parentId = null;
8
+ return messages.map((message, index) => {
9
+ const id = `initial-${index}`;
10
+ const entry = {
11
+ type: "message",
12
+ id,
13
+ parentId,
14
+ timestamp: new Date(message.timestamp).toISOString(),
15
+ message: structuredClone(message)
16
+ };
17
+ parentId = id;
18
+ return entry;
19
+ });
20
+ };
21
+ const createPiHarnessSessionState = (options) => ({
22
+ metadata: { ...options.metadata },
23
+ entries: createInitialMessageEntries(options.initialMessages),
24
+ checkpointedEntryCount: 0
25
+ });
26
+ const harnessEmissionKinds = /* @__PURE__ */ new Set([
27
+ "harness-operation-start",
28
+ "harness-session-entry",
29
+ "harness-event",
30
+ "harness-operation-complete"
31
+ ]);
32
+ const readHarnessAttempt = (emissions, operationId) => {
33
+ let attemptIdentity;
34
+ let started = false;
35
+ let completion;
36
+ const sessionEntries = [];
37
+ for (const emission of emissions) {
38
+ const kind = emission.payload?.kind;
39
+ if (emission.actor !== "user" || !kind || !harnessEmissionKinds.has(kind)) continue;
40
+ const identity = `${emission.executionId}\0${emission.epoch}`;
41
+ if (attemptIdentity !== void 0 && attemptIdentity !== identity) throw new Error("WORKFLOW_AGENT_HARNESS_ATTEMPT_IDENTITY_MISMATCH");
42
+ attemptIdentity = identity;
43
+ if (completion) throw new Error("WORKFLOW_AGENT_HARNESS_EMISSION_AFTER_OPERATION_COMPLETE");
44
+ const harnessEmission = emission;
45
+ if (harnessEmission.payload.kind !== "harness-operation-start" && !started) throw new Error("WORKFLOW_AGENT_HARNESS_EMISSION_BEFORE_OPERATION_START");
46
+ switch (harnessEmission.payload.kind) {
47
+ case "harness-operation-start":
48
+ if (started) throw new Error("WORKFLOW_AGENT_HARNESS_DUPLICATE_OPERATION_START");
49
+ started = true;
50
+ break;
51
+ case "harness-session-entry":
52
+ sessionEntries.push(harnessEmission.payload.entry);
53
+ break;
54
+ case "harness-event": break;
55
+ case "harness-operation-complete":
56
+ completion = harnessEmission.payload.result;
57
+ break;
58
+ }
59
+ if ("operationId" in harnessEmission.payload && harnessEmission.payload.operationId !== operationId) throw new Error("WORKFLOW_AGENT_HARNESS_OPERATION_ID_MISMATCH");
60
+ }
61
+ return {
62
+ started,
63
+ sessionEntries,
64
+ completion
65
+ };
66
+ };
67
+ const appendUniqueSessionEntries = (entries, appendedEntries) => {
68
+ const entryIds = /* @__PURE__ */ new Set();
69
+ const combinedEntries = [];
70
+ for (const entry of entries) {
71
+ if (entryIds.has(entry.id)) throw new Error("WORKFLOW_AGENT_HARNESS_DUPLICATE_SESSION_ENTRY");
72
+ entryIds.add(entry.id);
73
+ combinedEntries.push(entry);
74
+ }
75
+ for (const entry of appendedEntries) {
76
+ if (entryIds.has(entry.id)) throw new Error(`WORKFLOW_AGENT_HARNESS_DUPLICATE_SESSION_ENTRY:${entry.id}`);
77
+ if (entry.parentId !== null && !entryIds.has(entry.parentId)) throw new Error(`WORKFLOW_AGENT_HARNESS_UNKNOWN_PARENT_ENTRY:${entry.parentId}`);
78
+ entryIds.add(entry.id);
79
+ combinedEntries.push(entry);
80
+ }
81
+ return combinedEntries;
82
+ };
83
+ /** Finds the last replay-safe leaf and unfinished tool calls in an interrupted operation branch. */
84
+ function analyzeInterruptedTranscript(baseLeafId, activeOperationBranch) {
85
+ let recoverableLeafId = baseLeafId;
86
+ let openToolCalls;
87
+ let completedToolCallIds = /* @__PURE__ */ new Set();
88
+ for (const entry of activeOperationBranch) {
89
+ if (entry.type !== "message") {
90
+ if (!openToolCalls) recoverableLeafId = entry.type === "leaf" ? entry.targetId : entry.id;
91
+ continue;
92
+ }
93
+ const message = entry.message;
94
+ if (message.role === "user") {
95
+ if (openToolCalls) return {
96
+ recoverableLeafId,
97
+ missingToolCalls: openToolCalls.filter((toolCall) => !completedToolCallIds.has(toolCall.id))
98
+ };
99
+ recoverableLeafId = entry.id;
100
+ continue;
101
+ }
102
+ if (message.role === "assistant") {
103
+ if (openToolCalls) return {
104
+ recoverableLeafId,
105
+ missingToolCalls: openToolCalls.filter((toolCall) => !completedToolCallIds.has(toolCall.id))
106
+ };
107
+ const toolCalls = message.content.filter((content) => content.type === "toolCall");
108
+ if (message.stopReason !== "toolUse" || toolCalls.length === 0) return {
109
+ recoverableLeafId,
110
+ missingToolCalls: []
111
+ };
112
+ openToolCalls = toolCalls;
113
+ completedToolCallIds = /* @__PURE__ */ new Set();
114
+ recoverableLeafId = entry.id;
115
+ continue;
116
+ }
117
+ if (message.role !== "toolResult") return {
118
+ recoverableLeafId,
119
+ missingToolCalls: openToolCalls?.filter((toolCall) => !completedToolCallIds.has(toolCall.id)) ?? []
120
+ };
121
+ const toolCallId = message.toolCallId;
122
+ if (!openToolCalls?.some((toolCall) => toolCall.id === toolCallId) || completedToolCallIds.has(toolCallId)) return {
123
+ recoverableLeafId,
124
+ missingToolCalls: openToolCalls?.filter((toolCall) => !completedToolCallIds.has(toolCall.id)) ?? []
125
+ };
126
+ completedToolCallIds.add(toolCallId);
127
+ recoverableLeafId = entry.id;
128
+ if (completedToolCallIds.size === openToolCalls.length) {
129
+ openToolCalls = void 0;
130
+ completedToolCallIds = /* @__PURE__ */ new Set();
131
+ }
132
+ }
133
+ return {
134
+ recoverableLeafId,
135
+ missingToolCalls: openToolCalls?.filter((toolCall) => !completedToolCallIds.has(toolCall.id)) ?? []
136
+ };
137
+ }
138
+ const assertCheckpointBoundary = (state) => {
139
+ if (!Number.isSafeInteger(state.checkpointedEntryCount) || state.checkpointedEntryCount < 0 || state.checkpointedEntryCount > state.entries.length) throw new Error(`WORKFLOW_AGENT_HARNESS_INVALID_CHECKPOINT_BOUNDARY:${state.checkpointedEntryCount}:${state.entries.length}`);
140
+ };
141
+ const thinkingLevels = /* @__PURE__ */ new Set([
142
+ "off",
143
+ "minimal",
144
+ "low",
145
+ "medium",
146
+ "high",
147
+ "xhigh",
148
+ "max"
149
+ ]);
150
+ const restoredThinkingLevel = (value) => {
151
+ if (!thinkingLevels.has(value)) throw new Error(`WORKFLOW_AGENT_HARNESS_UNKNOWN_THINKING_LEVEL:${value}`);
152
+ return value;
153
+ };
154
+ const sessionEntriesToRoot = (entries, leafId) => {
155
+ if (leafId === null) return [];
156
+ const entriesById = /* @__PURE__ */ new Map();
157
+ for (const entry of entries) entriesById.set(entry.id, entry);
158
+ const branch = [];
159
+ let entry = entriesById.get(leafId);
160
+ if (!entry) throw new Error(`WORKFLOW_AGENT_HARNESS_UNKNOWN_LEAF:${leafId}`);
161
+ while (entry) {
162
+ branch.push(entry);
163
+ if (entry.parentId === null) break;
164
+ const parentId = entry.parentId;
165
+ entry = entriesById.get(parentId);
166
+ if (!entry) throw new Error(`WORKFLOW_AGENT_HARNESS_UNKNOWN_PARENT_ENTRY:${parentId}`);
167
+ }
168
+ branch.reverse();
169
+ return branch;
170
+ };
171
+ const deriveAgentHarnessOptionsFromSessionEntries = (session, models, entries, leafId = sessionEntriesLeafId(entries)) => {
172
+ const branchEntries = sessionEntriesToRoot(entries, leafId);
173
+ let modelSelection;
174
+ let thinkingLevel;
175
+ let activeToolNames;
176
+ for (const entry of branchEntries) if (entry.type === "model_change") modelSelection = {
177
+ provider: entry.provider,
178
+ modelId: entry.modelId
179
+ };
180
+ else if (entry.type === "message" && entry.message.role === "assistant") modelSelection = {
181
+ provider: entry.message.provider,
182
+ modelId: entry.message.model
183
+ };
184
+ else if (entry.type === "thinking_level_change") thinkingLevel = entry.thinkingLevel;
185
+ else if (entry.type === "active_tools_change") activeToolNames = [...entry.activeToolNames];
186
+ const model = modelSelection ? models.getModel(modelSelection.provider, modelSelection.modelId) : void 0;
187
+ if (modelSelection && !model) throw new Error(`WORKFLOW_AGENT_HARNESS_MODEL_NOT_AVAILABLE:${modelSelection.provider}:${modelSelection.modelId}`);
188
+ return {
189
+ session,
190
+ ...model ? { model } : {},
191
+ ...thinkingLevel !== void 0 ? { thinkingLevel: restoredThinkingLevel(thinkingLevel) } : {},
192
+ ...activeToolNames !== void 0 ? { activeToolNames } : {}
193
+ };
194
+ };
195
+ const restoreWorkflowBackedSession = (options) => {
196
+ assertCheckpointBoundary(options.state);
197
+ const attempt = readHarnessAttempt(options.previousEmissions, options.operationId);
198
+ const operationEntryStart = options.state.entries.length;
199
+ const checkpointEntryStart = options.state.checkpointedEntryCount;
200
+ const storageEntries = appendUniqueSessionEntries(options.state.entries, attempt.sessionEntries);
201
+ let recovery = { kind: "execute" };
202
+ if (attempt.completion !== void 0) recovery = {
203
+ kind: "completed",
204
+ result: attempt.completion
205
+ };
206
+ else if (attempt.started) {
207
+ const operationEntryIds = /* @__PURE__ */ new Set();
208
+ for (const entry of attempt.sessionEntries) operationEntryIds.add(entry.id);
209
+ const activeOperationBranch = sessionEntriesToRoot(storageEntries, sessionEntriesLeafId(storageEntries)).filter((entry) => operationEntryIds.has(entry.id));
210
+ recovery = {
211
+ kind: "interrupted",
212
+ transcript: analyzeInterruptedTranscript(sessionEntriesLeafId(options.state.entries), activeOperationBranch),
213
+ replayedEntries: attempt.sessionEntries
214
+ };
215
+ }
216
+ const appendEntryListeners = /* @__PURE__ */ new Set();
217
+ const entryIdPrefix = `${options.operationId}:entry`;
218
+ const storage = new WorkflowBackedSessionStorage({
219
+ metadata: { ...options.state.metadata },
220
+ entries: storageEntries,
221
+ entryIds: createWorkflowBackedSessionEntryIdAllocator({
222
+ prefix: entryIdPrefix,
223
+ startIndex: nextWorkflowBackedSessionEntryIndex({
224
+ prefix: entryIdPrefix,
225
+ entries: storageEntries
226
+ })
227
+ }),
228
+ onAppendEntry: async (entry) => {
229
+ await Promise.all([...appendEntryListeners].map((listener) => Promise.resolve(listener(entry))));
230
+ }
231
+ });
232
+ const session = new Session(storage);
233
+ return {
234
+ session,
235
+ storage,
236
+ operationId: options.operationId,
237
+ operationEntryStart,
238
+ checkpointEntryStart,
239
+ recovery,
240
+ subscribeToAppendedEntries: (listener) => {
241
+ appendEntryListeners.add(listener);
242
+ return () => {
243
+ appendEntryListeners.delete(listener);
244
+ };
245
+ },
246
+ options: deriveAgentHarnessOptionsFromSessionEntries(session, options.models, storageEntries, recovery.kind === "interrupted" ? recovery.transcript.recoverableLeafId : sessionEntriesLeafId(storageEntries))
247
+ };
248
+ };
249
+ const latestAssistantMessage = (entries) => {
250
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
251
+ const entry = entries[index];
252
+ if (entry?.type === "message" && entry.message.role === "assistant") return entry.message;
253
+ }
254
+ };
255
+ const assertTerminalAssistantSucceeded = (entries) => {
256
+ const assistantMessage = latestAssistantMessage(entries);
257
+ if (assistantMessage?.stopReason !== "error") return;
258
+ throw new Error(assistantMessage.errorMessage ? `Pi harness agent stream failed: ${assistantMessage.errorMessage}` : "Pi harness agent stream failed.");
259
+ };
260
+ const createInterruptedToolResult = (toolCall) => ({
261
+ role: "toolResult",
262
+ toolCallId: toolCall.id,
263
+ toolName: toolCall.name,
264
+ content: [{
265
+ type: "text",
266
+ text: "Tool execution interrupted before completion."
267
+ }],
268
+ isError: true,
269
+ timestamp: Date.now()
270
+ });
271
+ const appendInterruptedToolResults = (session, toolCalls) => toolCalls.reduce((previousAppend, toolCall) => previousAppend.then(() => session.appendMessage(createInterruptedToolResult(toolCall))), Promise.resolve());
272
+ const hasSummarizableCompactionHistory = (preparation) => preparation.messagesToSummarize.length > 0 || preparation.turnPrefixMessages.length > 0;
273
+ async function readWorkflowAgentHarnessEntrySuffixes(options) {
274
+ const checkpointEntries = await options.storage.getEntries({ afterEntrySeq: options.checkpointEntryStart });
275
+ const operationOffset = options.operationEntryStart - options.checkpointEntryStart;
276
+ return {
277
+ checkpointEntries,
278
+ operationEntries: checkpointEntries.slice(operationOffset)
279
+ };
280
+ }
281
+ const withWorkflowAgentHarness = async ({ restored, harness, tx, observeLiveEvents, runDurableStep, checkpointTerminalAssistantError = false, onTerminalOutcome }) => {
282
+ const { session, storage, operationId, operationEntryStart, checkpointEntryStart, recovery, subscribeToAppendedEntries } = restored;
283
+ if (recovery.kind === "completed") {
284
+ const result = recovery.result;
285
+ const operationEntries = await storage.getEntries({ afterEntrySeq: operationEntryStart });
286
+ await onTerminalOutcome?.({
287
+ operationId,
288
+ operationEntries,
289
+ result
290
+ });
291
+ if (!checkpointTerminalAssistantError) assertTerminalAssistantSucceeded(operationEntries);
292
+ return result;
293
+ }
294
+ const unsubscribeEntries = subscribeToAppendedEntries((entry) => {
295
+ tx.emit({
296
+ kind: "harness-session-entry",
297
+ entry
298
+ });
299
+ });
300
+ let unsubscribeHarness = () => {};
301
+ const emitOperationStart = () => {
302
+ tx.emit({
303
+ kind: "harness-operation-start",
304
+ operationId,
305
+ replay: {
306
+ protocol: "pi-harness-operation",
307
+ version: 1
308
+ }
309
+ });
310
+ };
311
+ try {
312
+ if (recovery.kind === "interrupted") {
313
+ emitOperationStart();
314
+ for (const entry of recovery.replayedEntries) tx.emit({
315
+ kind: "harness-session-entry",
316
+ entry
317
+ });
318
+ if (await storage.getLeafId() !== recovery.transcript.recoverableLeafId) await session.moveTo(recovery.transcript.recoverableLeafId);
319
+ await appendInterruptedToolResults(session, recovery.transcript.missingToolCalls);
320
+ const { checkpointEntries, operationEntries } = await readWorkflowAgentHarnessEntrySuffixes({
321
+ storage,
322
+ checkpointEntryStart,
323
+ operationEntryStart
324
+ });
325
+ const result = {
326
+ type: "harness-run",
327
+ outcome: "aborted",
328
+ appendedEntries: checkpointEntries,
329
+ leafId: await storage.getLeafId()
330
+ };
331
+ await onTerminalOutcome?.({
332
+ operationId,
333
+ operationEntries,
334
+ result
335
+ });
336
+ tx.emit({
337
+ kind: "harness-operation-complete",
338
+ operationId,
339
+ result
340
+ });
341
+ return result;
342
+ }
343
+ const eventEncoder = new PiHarnessEventEncoder();
344
+ unsubscribeHarness = harness.subscribe((event) => {
345
+ tx.emit({
346
+ kind: "harness-event",
347
+ event: eventEncoder.encode(event)
348
+ });
349
+ });
350
+ emitOperationStart();
351
+ const activeEventHandlers = /* @__PURE__ */ new Set();
352
+ const eventHandlerErrors = [];
353
+ const unsubscribeLiveEvents = [];
354
+ let liveEventObservationOpen = true;
355
+ let liveEventDeliveriesOpen = false;
356
+ const trackEventHandler = (handling) => {
357
+ activeEventHandlers.add(handling);
358
+ handling.catch((error) => {
359
+ eventHandlerErrors.push(error instanceof Error ? error : new Error("Workflow AgentHarness event handler failed.", { cause: error }));
360
+ }).finally(() => {
361
+ activeEventHandlers.delete(handling);
362
+ });
363
+ return handling;
364
+ };
365
+ const onLiveEvent = (type, handler) => {
366
+ if (!liveEventObservationOpen) throw new Error("WORKFLOW_AGENT_HARNESS_LIVE_EVENT_OBSERVATION_CLOSED");
367
+ unsubscribeLiveEvents.push(tx.onEvent(type, (event) => {
368
+ if (!liveEventDeliveriesOpen) return Promise.resolve();
369
+ const executeHandler = async () => {
370
+ try {
371
+ await handler(event);
372
+ } catch (error) {
373
+ throw error instanceof Error ? error : new Error("Workflow AgentHarness event handler failed.", { cause: error });
374
+ }
375
+ };
376
+ return trackEventHandler(executeHandler());
377
+ }));
378
+ };
379
+ let value;
380
+ try {
381
+ try {
382
+ observeLiveEvents?.(onLiveEvent);
383
+ } finally {
384
+ liveEventObservationOpen = false;
385
+ }
386
+ liveEventDeliveriesOpen = true;
387
+ value = await runDurableStep();
388
+ } finally {
389
+ liveEventObservationOpen = false;
390
+ liveEventDeliveriesOpen = false;
391
+ for (const unsubscribeLiveEvent of unsubscribeLiveEvents) unsubscribeLiveEvent();
392
+ await Promise.allSettled(activeEventHandlers);
393
+ }
394
+ if (eventHandlerErrors.length > 0) throw eventHandlerErrors[0];
395
+ const { checkpointEntries, operationEntries } = await readWorkflowAgentHarnessEntrySuffixes({
396
+ storage,
397
+ checkpointEntryStart,
398
+ operationEntryStart
399
+ });
400
+ const result = latestAssistantMessage(operationEntries)?.stopReason === "aborted" ? {
401
+ type: "harness-run",
402
+ outcome: "aborted",
403
+ appendedEntries: checkpointEntries,
404
+ leafId: await storage.getLeafId()
405
+ } : {
406
+ type: "harness-run",
407
+ outcome: "completed",
408
+ value,
409
+ appendedEntries: checkpointEntries,
410
+ leafId: await storage.getLeafId()
411
+ };
412
+ await onTerminalOutcome?.({
413
+ operationId,
414
+ operationEntries,
415
+ result
416
+ });
417
+ if (!checkpointTerminalAssistantError) assertTerminalAssistantSucceeded(operationEntries);
418
+ tx.emit({
419
+ kind: "harness-operation-complete",
420
+ operationId,
421
+ result
422
+ });
423
+ return result;
424
+ } finally {
425
+ unsubscribeHarness();
426
+ unsubscribeEntries();
427
+ }
428
+ };
429
+ const sessionEntryPrefixMatches = (prefix, entries) => {
430
+ if (prefix.length > entries.length) return false;
431
+ for (const [index, entry] of prefix.entries()) if (JSON.stringify(entry) !== JSON.stringify(entries[index])) return false;
432
+ return true;
433
+ };
434
+ /** Append one completed checkpoint suffix and verify its derived active leaf. */
435
+ const applyWorkflowAgentHarnessStepResult = (state, result) => {
436
+ assertCheckpointBoundary(state);
437
+ const uncheckpointedEntries = state.entries.slice(state.checkpointedEntryCount);
438
+ if (!sessionEntryPrefixMatches(uncheckpointedEntries, result.appendedEntries)) throw new Error("WORKFLOW_AGENT_HARNESS_CHECKPOINT_PREFIX_MISMATCH");
439
+ const entries = appendUniqueSessionEntries(state.entries, result.appendedEntries.slice(uncheckpointedEntries.length));
440
+ if (sessionEntriesLeafId(entries) !== result.leafId) throw new Error("WORKFLOW_AGENT_HARNESS_LEAF_MISMATCH");
441
+ return {
442
+ metadata: { ...state.metadata },
443
+ entries,
444
+ checkpointedEntryCount: entries.length
445
+ };
446
+ };
447
+ //#endregion
448
+ export { applyWorkflowAgentHarnessStepResult, createPiHarnessSessionState, hasSummarizableCompactionHistory, restoreWorkflowBackedSession, withWorkflowAgentHarness };
449
+
450
+ //# sourceMappingURL=workflow-agent-harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-agent-harness.js","names":[],"sources":["../../../../src/pi/workflows/workflow-agent-harness.ts"],"sourcesContent":["import type {\n WorkflowStepEmission,\n WorkflowStepEventHandler,\n WorkflowStepTx,\n} from \"@fragno-dev/workflows/workflow\";\n\nimport {\n type AgentHarness,\n type AgentHarnessOptions,\n type AgentHarnessTool,\n type AgentMessage,\n type CompactionPreparation,\n type PromptTemplate,\n type SessionMetadata,\n type SessionTreeEntry,\n type Skill,\n type ThinkingLevel,\n} from \"@earendil-works/pi-agent-core\";\nimport { Session } from \"@earendil-works/pi-agent-core\";\nimport type { ToolCall, ToolResultMessage } from \"@earendil-works/pi-ai\";\n\nimport {\n PiHarnessEventEncoder,\n type PiHarnessEncodedEventEmission,\n type PiHarnessSubscribedEvent,\n} from \"../harness/agent-harness-event-protocol\";\nimport {\n createWorkflowBackedSessionEntryIdAllocator,\n nextWorkflowBackedSessionEntryIndex,\n sessionEntriesLeafId,\n WorkflowBackedSessionStorage,\n} from \"../harness/session-storage\";\n\nexport type PiHarnessOperation =\n | { kind: \"prompt\"; args: Parameters<AgentHarness[\"prompt\"]>; stopOnTools?: readonly string[] }\n | { kind: \"skill\"; args: Parameters<AgentHarness[\"skill\"]> }\n | { kind: \"promptFromTemplate\"; args: Parameters<AgentHarness[\"promptFromTemplate\"]> }\n | { kind: \"compact\"; args: Parameters<AgentHarness[\"compact\"]> }\n | { kind: \"navigateTree\"; args: Parameters<AgentHarness[\"navigateTree\"]> };\n\nexport type PiHarnessStepResult = {\n type: \"harness-run\";\n appendedEntries: SessionTreeEntry[];\n leafId: string | null;\n};\n\nexport type PiHarnessSessionEntryEmission = {\n kind: \"harness-session-entry\";\n entry: SessionTreeEntry;\n};\n\nexport type PiHarnessOperationStartEmission = {\n kind: \"harness-operation-start\";\n operationId: string;\n replay: {\n protocol: \"pi-harness-operation\";\n version: 1;\n };\n};\n\nexport type PiHarnessOperationCompleteEmission<TResult = PiHarnessStepResult> = {\n kind: \"harness-operation-complete\";\n operationId: string;\n result: TResult;\n};\n\nexport type PiHarnessEmission<TResult = PiHarnessStepResult> =\n | PiHarnessSessionEntryEmission\n | PiHarnessEncodedEventEmission\n | PiHarnessOperationStartEmission\n | PiHarnessOperationCompleteEmission<TResult>;\n\nexport type WorkflowAgentHarnessOptions<\n TContext extends object | undefined = undefined,\n TSkill extends Skill = Skill,\n TPromptTemplate extends PromptTemplate = PromptTemplate,\n TTool extends AgentHarnessTool<TContext> = AgentHarnessTool<TContext>,\n> = Omit<AgentHarnessOptions<TContext, TSkill, TPromptTemplate, TTool>, \"session\">;\n\nexport type PiHarnessSessionStepState = {\n metadata: SessionMetadata;\n entries: readonly SessionTreeEntry[];\n checkpointedEntryCount: number;\n};\n\nexport type CreatePiHarnessSessionStateOptions = {\n metadata: SessionMetadata;\n /** Messages must carry timestamps created outside workflow replay. */\n initialMessages?: readonly AgentMessage[];\n};\n\nconst createInitialMessageEntries = (\n messages: readonly AgentMessage[] | undefined,\n): SessionTreeEntry[] => {\n if (!messages) {\n return [];\n }\n\n let parentId: string | null = null;\n return messages.map((message, index) => {\n const id = `initial-${index}`;\n const entry: SessionTreeEntry = {\n type: \"message\",\n id,\n parentId,\n timestamp: new Date(message.timestamp).toISOString(),\n message: structuredClone(message),\n };\n parentId = id;\n return entry;\n });\n};\n\nexport const createPiHarnessSessionState = (\n options: CreatePiHarnessSessionStateOptions,\n): PiHarnessSessionStepState => ({\n metadata: { ...options.metadata },\n entries: createInitialMessageEntries(options.initialMessages),\n checkpointedEntryCount: 0,\n});\n\n/**\n * Restore one runner-selected attempt from emissions in persisted order. Session entries are an\n * immutable append log; repeated IDs or mixed attempt identities are corruption.\n */\n\ntype AppendEntryListener = (entry: SessionTreeEntry) => void | Promise<void>;\n\ntype WorkflowAgentHarnessEmission = PiHarnessEmission<WorkflowAgentHarnessStepResult>;\n\ntype TrustedWorkflowAgentHarnessEmission = WorkflowStepEmission<WorkflowAgentHarnessEmission>;\n\n/** Starts an AgentHarness operation when the selected workflow attempt has no prior journal. */\ntype ExecuteWorkflowAgentHarnessRecovery = { readonly kind: \"execute\" };\n\n/** Checkpoints durable transcript progress from an interrupted workflow attempt without rerunning it. */\ntype InterruptedWorkflowAgentHarnessRecovery = {\n readonly kind: \"interrupted\";\n readonly transcript: InterruptedTranscript;\n readonly replayedEntries: readonly SessionTreeEntry[];\n};\n\n/** Replays a finished AgentHarness operation whose entries and result were emitted before the process died, but whose workflow step had not committed. */\ntype CompletedWorkflowAgentHarnessRecovery = {\n readonly kind: \"completed\";\n readonly result: WorkflowAgentHarnessStepResult;\n};\n\n/** Tells withWorkflowAgentHarness whether to run, recover, or replay the operation. */\ntype WorkflowAgentHarnessRecovery =\n | ExecuteWorkflowAgentHarnessRecovery\n | InterruptedWorkflowAgentHarnessRecovery\n | CompletedWorkflowAgentHarnessRecovery;\n\ntype WorkflowAgentHarnessStorageMetadata = {\n readonly operationId: string;\n readonly operationEntryStart: number;\n readonly checkpointEntryStart: number;\n readonly recovery: WorkflowAgentHarnessRecovery;\n};\n\nexport type RestoredWorkflowAgentHarnessOptions = Pick<AgentHarnessOptions, \"session\"> &\n Partial<Pick<AgentHarnessOptions, \"model\" | \"thinkingLevel\" | \"activeToolNames\">>;\n\nexport type RestoredWorkflowBackedSession = WorkflowAgentHarnessStorageMetadata & {\n session: Session;\n storage: WorkflowBackedSessionStorage;\n options: RestoredWorkflowAgentHarnessOptions;\n subscribeToAppendedEntries: (listener: AppendEntryListener) => () => void;\n};\n\ntype RestoreWorkflowBackedSessionOptions = {\n operationId: string;\n state: PiHarnessSessionStepState;\n previousEmissions: readonly WorkflowStepEmission[];\n models: AgentHarnessOptions[\"models\"];\n};\n\ntype HarnessAttempt = {\n started: boolean;\n sessionEntries: SessionTreeEntry[];\n completion: WorkflowAgentHarnessStepResult | undefined;\n};\n\nconst harnessEmissionKinds = new Set<WorkflowAgentHarnessEmission[\"kind\"]>([\n \"harness-operation-start\",\n \"harness-session-entry\",\n \"harness-event\",\n \"harness-operation-complete\",\n]);\n\nconst readHarnessAttempt = (\n emissions: readonly WorkflowStepEmission[],\n operationId: string,\n): HarnessAttempt => {\n let attemptIdentity: string | undefined;\n let started = false;\n let completion: WorkflowAgentHarnessStepResult | undefined;\n const sessionEntries: SessionTreeEntry[] = [];\n\n for (const emission of emissions) {\n const kind = (emission.payload as { kind?: WorkflowAgentHarnessEmission[\"kind\"] } | null)?.kind;\n if (emission.actor !== \"user\" || !kind || !harnessEmissionKinds.has(kind)) {\n continue;\n }\n\n const identity = `${emission.executionId}\\0${emission.epoch}`;\n if (attemptIdentity !== undefined && attemptIdentity !== identity) {\n throw new Error(\"WORKFLOW_AGENT_HARNESS_ATTEMPT_IDENTITY_MISMATCH\");\n }\n attemptIdentity = identity;\n if (completion) {\n throw new Error(\"WORKFLOW_AGENT_HARNESS_EMISSION_AFTER_OPERATION_COMPLETE\");\n }\n\n const harnessEmission = emission as TrustedWorkflowAgentHarnessEmission;\n if (harnessEmission.payload.kind !== \"harness-operation-start\" && !started) {\n throw new Error(\"WORKFLOW_AGENT_HARNESS_EMISSION_BEFORE_OPERATION_START\");\n }\n\n switch (harnessEmission.payload.kind) {\n case \"harness-operation-start\":\n if (started) {\n throw new Error(\"WORKFLOW_AGENT_HARNESS_DUPLICATE_OPERATION_START\");\n }\n started = true;\n break;\n case \"harness-session-entry\":\n sessionEntries.push(harnessEmission.payload.entry);\n break;\n case \"harness-event\":\n break;\n case \"harness-operation-complete\":\n completion = harnessEmission.payload.result;\n break;\n }\n\n if (\n \"operationId\" in harnessEmission.payload &&\n harnessEmission.payload.operationId !== operationId\n ) {\n throw new Error(\"WORKFLOW_AGENT_HARNESS_OPERATION_ID_MISMATCH\");\n }\n }\n\n return { started, sessionEntries, completion };\n};\n\nconst appendUniqueSessionEntries = (\n entries: readonly SessionTreeEntry[],\n appendedEntries: readonly SessionTreeEntry[],\n): SessionTreeEntry[] => {\n const entryIds = new Set<string>();\n const combinedEntries: SessionTreeEntry[] = [];\n for (const entry of entries) {\n if (entryIds.has(entry.id)) {\n throw new Error(\"WORKFLOW_AGENT_HARNESS_DUPLICATE_SESSION_ENTRY\");\n }\n entryIds.add(entry.id);\n combinedEntries.push(entry);\n }\n for (const entry of appendedEntries) {\n if (entryIds.has(entry.id)) {\n throw new Error(`WORKFLOW_AGENT_HARNESS_DUPLICATE_SESSION_ENTRY:${entry.id}`);\n }\n if (entry.parentId !== null && !entryIds.has(entry.parentId)) {\n throw new Error(`WORKFLOW_AGENT_HARNESS_UNKNOWN_PARENT_ENTRY:${entry.parentId}`);\n }\n entryIds.add(entry.id);\n combinedEntries.push(entry);\n }\n return combinedEntries;\n};\n\ntype InterruptedTranscript = {\n recoverableLeafId: string | null;\n missingToolCalls: ToolCall[];\n};\n\n/** Finds the last replay-safe leaf and unfinished tool calls in an interrupted operation branch. */\nfunction analyzeInterruptedTranscript(\n baseLeafId: string | null,\n activeOperationBranch: readonly SessionTreeEntry[],\n): InterruptedTranscript {\n let recoverableLeafId = baseLeafId;\n let openToolCalls: ToolCall[] | undefined;\n let completedToolCallIds = new Set<string>();\n\n for (const entry of activeOperationBranch) {\n if (entry.type !== \"message\") {\n if (!openToolCalls) {\n recoverableLeafId = entry.type === \"leaf\" ? entry.targetId : entry.id;\n }\n continue;\n }\n\n const message = entry.message;\n if (message.role === \"user\") {\n if (openToolCalls) {\n return {\n recoverableLeafId,\n missingToolCalls: openToolCalls.filter(\n (toolCall) => !completedToolCallIds.has(toolCall.id),\n ),\n };\n }\n recoverableLeafId = entry.id;\n continue;\n }\n\n if (message.role === \"assistant\") {\n if (openToolCalls) {\n return {\n recoverableLeafId,\n missingToolCalls: openToolCalls.filter(\n (toolCall) => !completedToolCallIds.has(toolCall.id),\n ),\n };\n }\n const toolCalls = message.content.filter(\n (content): content is ToolCall => content.type === \"toolCall\",\n );\n if (message.stopReason !== \"toolUse\" || toolCalls.length === 0) {\n return { recoverableLeafId, missingToolCalls: [] };\n }\n openToolCalls = toolCalls;\n completedToolCallIds = new Set<string>();\n recoverableLeafId = entry.id;\n continue;\n }\n\n if (message.role !== \"toolResult\") {\n return {\n recoverableLeafId,\n missingToolCalls:\n openToolCalls?.filter((toolCall) => !completedToolCallIds.has(toolCall.id)) ?? [],\n };\n }\n const toolCallId = message.toolCallId;\n if (\n !openToolCalls?.some((toolCall) => toolCall.id === toolCallId) ||\n completedToolCallIds.has(toolCallId)\n ) {\n return {\n recoverableLeafId,\n missingToolCalls:\n openToolCalls?.filter((toolCall) => !completedToolCallIds.has(toolCall.id)) ?? [],\n };\n }\n\n completedToolCallIds.add(toolCallId);\n recoverableLeafId = entry.id;\n if (completedToolCallIds.size === openToolCalls.length) {\n openToolCalls = undefined;\n completedToolCallIds = new Set<string>();\n }\n }\n\n return {\n recoverableLeafId,\n missingToolCalls:\n openToolCalls?.filter((toolCall) => !completedToolCallIds.has(toolCall.id)) ?? [],\n };\n}\n\nconst assertCheckpointBoundary = (state: PiHarnessSessionStepState): void => {\n if (\n !Number.isSafeInteger(state.checkpointedEntryCount) ||\n state.checkpointedEntryCount < 0 ||\n state.checkpointedEntryCount > state.entries.length\n ) {\n throw new Error(\n `WORKFLOW_AGENT_HARNESS_INVALID_CHECKPOINT_BOUNDARY:${state.checkpointedEntryCount}:${state.entries.length}`,\n );\n }\n};\n\nconst thinkingLevels = new Set<ThinkingLevel>([\n \"off\",\n \"minimal\",\n \"low\",\n \"medium\",\n \"high\",\n \"xhigh\",\n \"max\",\n]);\n\nconst restoredThinkingLevel = (value: string): ThinkingLevel => {\n if (!thinkingLevels.has(value as ThinkingLevel)) {\n throw new Error(`WORKFLOW_AGENT_HARNESS_UNKNOWN_THINKING_LEVEL:${value}`);\n }\n return value as ThinkingLevel;\n};\n\nconst sessionEntriesToRoot = (\n entries: readonly SessionTreeEntry[],\n leafId: string | null,\n): SessionTreeEntry[] => {\n if (leafId === null) {\n return [];\n }\n\n const entriesById = new Map<string, SessionTreeEntry>();\n for (const entry of entries) {\n entriesById.set(entry.id, entry);\n }\n const branch: SessionTreeEntry[] = [];\n let entry = entriesById.get(leafId);\n if (!entry) {\n throw new Error(`WORKFLOW_AGENT_HARNESS_UNKNOWN_LEAF:${leafId}`);\n }\n\n while (entry) {\n branch.push(entry);\n if (entry.parentId === null) {\n break;\n }\n\n const parentId: string = entry.parentId;\n entry = entriesById.get(parentId);\n if (!entry) {\n throw new Error(`WORKFLOW_AGENT_HARNESS_UNKNOWN_PARENT_ENTRY:${parentId}`);\n }\n }\n\n branch.reverse();\n return branch;\n};\n\nconst deriveAgentHarnessOptionsFromSessionEntries = (\n session: Session,\n models: AgentHarnessOptions[\"models\"],\n entries: readonly SessionTreeEntry[],\n leafId = sessionEntriesLeafId(entries),\n): RestoredWorkflowAgentHarnessOptions => {\n const branchEntries = sessionEntriesToRoot(entries, leafId);\n let modelSelection: { provider: string; modelId: string } | undefined;\n let thinkingLevel: string | undefined;\n let activeToolNames: string[] | undefined;\n\n for (const entry of branchEntries) {\n if (entry.type === \"model_change\") {\n modelSelection = { provider: entry.provider, modelId: entry.modelId };\n } else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n modelSelection = { provider: entry.message.provider, modelId: entry.message.model };\n } else if (entry.type === \"thinking_level_change\") {\n thinkingLevel = entry.thinkingLevel;\n } else if (entry.type === \"active_tools_change\") {\n activeToolNames = [...entry.activeToolNames];\n }\n }\n\n const model = modelSelection\n ? models.getModel(modelSelection.provider, modelSelection.modelId)\n : undefined;\n if (modelSelection && !model) {\n throw new Error(\n `WORKFLOW_AGENT_HARNESS_MODEL_NOT_AVAILABLE:${modelSelection.provider}:${modelSelection.modelId}`,\n );\n }\n\n return {\n session,\n ...(model ? { model } : {}),\n ...(thinkingLevel !== undefined ? { thinkingLevel: restoredThinkingLevel(thinkingLevel) } : {}),\n ...(activeToolNames !== undefined ? { activeToolNames } : {}),\n };\n};\n\nexport const restoreWorkflowBackedSession = (\n options: RestoreWorkflowBackedSessionOptions,\n): RestoredWorkflowBackedSession => {\n assertCheckpointBoundary(options.state);\n\n const attempt = readHarnessAttempt(options.previousEmissions, options.operationId);\n const operationEntryStart = options.state.entries.length;\n const checkpointEntryStart = options.state.checkpointedEntryCount;\n const storageEntries = appendUniqueSessionEntries(options.state.entries, attempt.sessionEntries);\n let recovery: WorkflowAgentHarnessRecovery = { kind: \"execute\" };\n\n if (attempt.completion !== undefined) {\n recovery = { kind: \"completed\", result: attempt.completion };\n } else if (attempt.started) {\n const operationEntryIds = new Set<string>();\n for (const entry of attempt.sessionEntries) {\n operationEntryIds.add(entry.id);\n }\n const activeOperationBranch = sessionEntriesToRoot(\n storageEntries,\n sessionEntriesLeafId(storageEntries),\n ).filter((entry) => operationEntryIds.has(entry.id));\n recovery = {\n kind: \"interrupted\",\n transcript: analyzeInterruptedTranscript(\n sessionEntriesLeafId(options.state.entries),\n activeOperationBranch,\n ),\n replayedEntries: attempt.sessionEntries,\n };\n }\n\n const appendEntryListeners = new Set<AppendEntryListener>();\n const entryIdPrefix = `${options.operationId}:entry`;\n const storage = new WorkflowBackedSessionStorage({\n metadata: { ...options.state.metadata },\n entries: storageEntries,\n entryIds: createWorkflowBackedSessionEntryIdAllocator({\n prefix: entryIdPrefix,\n startIndex: nextWorkflowBackedSessionEntryIndex({\n prefix: entryIdPrefix,\n entries: storageEntries,\n }),\n }),\n onAppendEntry: async (entry) => {\n await Promise.all(\n [...appendEntryListeners].map((listener) => Promise.resolve(listener(entry))),\n );\n },\n });\n\n const session = new Session(storage);\n\n return {\n session,\n storage,\n operationId: options.operationId,\n operationEntryStart,\n checkpointEntryStart,\n recovery,\n subscribeToAppendedEntries: (listener) => {\n appendEntryListeners.add(listener);\n return () => {\n appendEntryListeners.delete(listener);\n };\n },\n options: deriveAgentHarnessOptionsFromSessionEntries(\n session,\n options.models,\n storageEntries,\n recovery.kind === \"interrupted\"\n ? recovery.transcript.recoverableLeafId\n : sessionEntriesLeafId(storageEntries),\n ),\n };\n};\n\n/**\n * Execute a fresh invocation, replay a completed result, or checkpoint interrupted progress as\n * aborted. Recovery never calls runDurableStep and never re-executes provider or tool work.\n */\n\ntype WorkflowAgentHarnessStepResultBase = Pick<\n PiHarnessStepResult,\n \"type\" | \"appendedEntries\" | \"leafId\"\n>;\n\nexport type WorkflowAgentHarnessStepResult<TResult = unknown> = WorkflowAgentHarnessStepResultBase &\n ({ readonly outcome: \"completed\"; readonly value: TResult } | { readonly outcome: \"aborted\" });\n\nexport type WorkflowAgentHarnessTerminalOutcome<TResult = unknown> = {\n operationId: string;\n operationEntries: readonly SessionTreeEntry[];\n result: WorkflowAgentHarnessStepResult<TResult>;\n};\n\nexport type WorkflowAgentHarnessOnLiveEvent = <TPayload = unknown>(\n type: string,\n handler: WorkflowStepEventHandler<TPayload>,\n) => void;\n\ntype WorkflowAgentHarnessTerminalOptions<TResult> = {\n /** Persist terminal provider errors as completed operations instead of failing the workflow step. */\n checkpointTerminalAssistantError?: boolean;\n /** May run more than once before the enclosing workflow step commits. */\n onTerminalOutcome?: (\n outcome: WorkflowAgentHarnessTerminalOutcome<TResult>,\n ) => Promise<void> | void;\n};\n\nexport type WithWorkflowAgentHarnessOptions<TResult = unknown> =\n WorkflowAgentHarnessTerminalOptions<TResult> & {\n restored: RestoredWorkflowBackedSession;\n harness: AgentHarness;\n tx: WorkflowAgentHarnessExecutionTx;\n observeLiveEvents?: (onLiveEvent: WorkflowAgentHarnessOnLiveEvent) => void;\n runDurableStep: () => Promise<TResult>;\n };\n\ntype WorkflowAgentHarnessExecutionTx = Pick<WorkflowStepTx, \"emit\" | \"onEvent\">;\n\nconst latestAssistantMessage = (\n entries: readonly SessionTreeEntry[],\n): Extract<AgentMessage, { role: \"assistant\" }> | undefined => {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (entry?.type === \"message\" && entry.message.role === \"assistant\") {\n return entry.message;\n }\n }\n\n return undefined;\n};\n\nconst assertTerminalAssistantSucceeded = (entries: readonly SessionTreeEntry[]): void => {\n const assistantMessage = latestAssistantMessage(entries);\n if (assistantMessage?.stopReason !== \"error\") {\n return;\n }\n\n throw new Error(\n assistantMessage.errorMessage\n ? `Pi harness agent stream failed: ${assistantMessage.errorMessage}`\n : \"Pi harness agent stream failed.\",\n );\n};\n\nconst createInterruptedToolResult = (toolCall: ToolCall): ToolResultMessage => ({\n role: \"toolResult\",\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n content: [{ type: \"text\", text: \"Tool execution interrupted before completion.\" }],\n isError: true,\n timestamp: Date.now(),\n});\n\nconst appendInterruptedToolResults = (\n session: Session,\n toolCalls: readonly ToolCall[],\n): Promise<unknown> =>\n toolCalls.reduce<Promise<unknown>>(\n (previousAppend, toolCall) =>\n previousAppend.then(() => session.appendMessage(createInterruptedToolResult(toolCall))),\n Promise.resolve(),\n );\n\nexport const hasSummarizableCompactionHistory = (preparation: CompactionPreparation): boolean =>\n preparation.messagesToSummarize.length > 0 || preparation.turnPrefixMessages.length > 0;\n\nasync function readWorkflowAgentHarnessEntrySuffixes(options: {\n storage: WorkflowBackedSessionStorage;\n checkpointEntryStart: number;\n operationEntryStart: number;\n}): Promise<{\n checkpointEntries: SessionTreeEntry[];\n operationEntries: SessionTreeEntry[];\n}> {\n const checkpointEntries = await options.storage.getEntries({\n afterEntrySeq: options.checkpointEntryStart,\n });\n const operationOffset = options.operationEntryStart - options.checkpointEntryStart;\n return {\n checkpointEntries,\n operationEntries: checkpointEntries.slice(operationOffset),\n };\n}\n\nexport const withWorkflowAgentHarness = async <TResult>({\n restored,\n harness,\n tx,\n observeLiveEvents,\n runDurableStep,\n checkpointTerminalAssistantError = false,\n onTerminalOutcome,\n}: WithWorkflowAgentHarnessOptions<TResult>): Promise<WorkflowAgentHarnessStepResult<TResult>> => {\n const {\n session,\n storage,\n operationId,\n operationEntryStart,\n checkpointEntryStart,\n recovery,\n subscribeToAppendedEntries,\n } = restored;\n\n if (recovery.kind === \"completed\") {\n const result = recovery.result as WorkflowAgentHarnessStepResult<TResult>;\n const operationEntries = await storage.getEntries({ afterEntrySeq: operationEntryStart });\n await onTerminalOutcome?.({ operationId, operationEntries, result });\n if (!checkpointTerminalAssistantError) {\n assertTerminalAssistantSucceeded(operationEntries);\n }\n return result;\n }\n\n const unsubscribeEntries = subscribeToAppendedEntries((entry) => {\n tx.emit({ kind: \"harness-session-entry\", entry } satisfies WorkflowAgentHarnessEmission);\n });\n let unsubscribeHarness = () => {};\n const emitOperationStart = () => {\n tx.emit({\n kind: \"harness-operation-start\",\n operationId,\n replay: { protocol: \"pi-harness-operation\", version: 1 },\n } satisfies WorkflowAgentHarnessEmission);\n };\n\n try {\n if (recovery.kind === \"interrupted\") {\n emitOperationStart();\n // Workflows replays only the selected attempt epoch. Carry these entries forward before the\n // first await so another interruption can restore the transcript from this epoch alone.\n for (const entry of recovery.replayedEntries) {\n tx.emit({ kind: \"harness-session-entry\", entry } satisfies WorkflowAgentHarnessEmission);\n }\n if ((await storage.getLeafId()) !== recovery.transcript.recoverableLeafId) {\n await session.moveTo(recovery.transcript.recoverableLeafId);\n }\n\n await appendInterruptedToolResults(session, recovery.transcript.missingToolCalls);\n\n const { checkpointEntries, operationEntries } = await readWorkflowAgentHarnessEntrySuffixes({\n storage,\n checkpointEntryStart,\n operationEntryStart,\n });\n const result = {\n type: \"harness-run\",\n outcome: \"aborted\",\n appendedEntries: checkpointEntries,\n leafId: await storage.getLeafId(),\n } satisfies WorkflowAgentHarnessStepResult<TResult>;\n\n await onTerminalOutcome?.({ operationId, operationEntries, result });\n\n tx.emit({\n kind: \"harness-operation-complete\",\n operationId,\n result,\n } satisfies WorkflowAgentHarnessEmission);\n\n return result;\n }\n\n const eventEncoder = new PiHarnessEventEncoder();\n unsubscribeHarness = harness.subscribe((event) => {\n tx.emit({\n kind: \"harness-event\",\n event: eventEncoder.encode(event as PiHarnessSubscribedEvent),\n } satisfies PiHarnessEncodedEventEmission);\n });\n emitOperationStart();\n\n const activeEventHandlers = new Set<Promise<void>>();\n const eventHandlerErrors: Error[] = [];\n const unsubscribeLiveEvents: Array<() => void> = [];\n let liveEventObservationOpen = true;\n let liveEventDeliveriesOpen = false;\n\n const trackEventHandler = (handling: Promise<void>): Promise<void> => {\n activeEventHandlers.add(handling);\n void handling\n .catch((error: unknown) => {\n eventHandlerErrors.push(\n error instanceof Error\n ? error\n : new Error(\"Workflow AgentHarness event handler failed.\", { cause: error }),\n );\n })\n .finally(() => {\n activeEventHandlers.delete(handling);\n });\n return handling;\n };\n\n const onLiveEvent: WorkflowAgentHarnessOnLiveEvent = (type, handler) => {\n if (!liveEventObservationOpen) {\n throw new Error(\"WORKFLOW_AGENT_HARNESS_LIVE_EVENT_OBSERVATION_CLOSED\");\n }\n\n unsubscribeLiveEvents.push(\n tx.onEvent(type, (event) => {\n if (!liveEventDeliveriesOpen) {\n return Promise.resolve();\n }\n\n const executeHandler = async () => {\n try {\n await (handler as WorkflowStepEventHandler)(event);\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error(\"Workflow AgentHarness event handler failed.\", { cause: error });\n }\n };\n\n return trackEventHandler(executeHandler());\n }),\n );\n };\n\n let value!: TResult;\n try {\n try {\n observeLiveEvents?.(onLiveEvent);\n } finally {\n liveEventObservationOpen = false;\n }\n\n // Live-event observation and step execution start in the same synchronous turn, so workflow\n // events cannot reach the shared harness before its direct operation starts.\n liveEventDeliveriesOpen = true;\n value = await runDurableStep();\n } finally {\n liveEventObservationOpen = false;\n liveEventDeliveriesOpen = false;\n for (const unsubscribeLiveEvent of unsubscribeLiveEvents) {\n unsubscribeLiveEvent();\n }\n await Promise.allSettled(activeEventHandlers);\n }\n if (eventHandlerErrors.length > 0) {\n throw eventHandlerErrors[0];\n }\n\n const { checkpointEntries, operationEntries } = await readWorkflowAgentHarnessEntrySuffixes({\n storage,\n checkpointEntryStart,\n operationEntryStart,\n });\n const terminalAssistant = latestAssistantMessage(operationEntries);\n const result: WorkflowAgentHarnessStepResult<TResult> =\n terminalAssistant?.stopReason === \"aborted\"\n ? {\n type: \"harness-run\",\n outcome: \"aborted\",\n appendedEntries: checkpointEntries,\n leafId: await storage.getLeafId(),\n }\n : {\n type: \"harness-run\",\n outcome: \"completed\",\n value,\n appendedEntries: checkpointEntries,\n leafId: await storage.getLeafId(),\n };\n\n await onTerminalOutcome?.({\n operationId,\n operationEntries,\n result,\n });\n\n if (!checkpointTerminalAssistantError) {\n assertTerminalAssistantSucceeded(operationEntries);\n }\n\n tx.emit({\n kind: \"harness-operation-complete\",\n operationId,\n result,\n } satisfies WorkflowAgentHarnessEmission);\n\n return result;\n } finally {\n unsubscribeHarness();\n unsubscribeEntries();\n }\n};\n\nconst sessionEntryPrefixMatches = (\n prefix: readonly SessionTreeEntry[],\n entries: readonly SessionTreeEntry[],\n): boolean => {\n if (prefix.length > entries.length) {\n return false;\n }\n for (const [index, entry] of prefix.entries()) {\n if (JSON.stringify(entry) !== JSON.stringify(entries[index])) {\n return false;\n }\n }\n return true;\n};\n\n/** Append one completed checkpoint suffix and verify its derived active leaf. */\n\nexport const applyWorkflowAgentHarnessStepResult = (\n state: PiHarnessSessionStepState,\n result: Pick<WorkflowAgentHarnessStepResult, \"appendedEntries\" | \"leafId\">,\n): PiHarnessSessionStepState => {\n assertCheckpointBoundary(state);\n\n const uncheckpointedEntries = state.entries.slice(state.checkpointedEntryCount);\n const checkpointPrefixMatches = sessionEntryPrefixMatches(\n uncheckpointedEntries,\n result.appendedEntries,\n );\n if (!checkpointPrefixMatches) {\n throw new Error(\"WORKFLOW_AGENT_HARNESS_CHECKPOINT_PREFIX_MISMATCH\");\n }\n\n const entries = appendUniqueSessionEntries(\n state.entries,\n result.appendedEntries.slice(uncheckpointedEntries.length),\n );\n const leafId = sessionEntriesLeafId(entries);\n if (leafId !== result.leafId) {\n throw new Error(\"WORKFLOW_AGENT_HARNESS_LEAF_MISMATCH\");\n }\n\n return {\n metadata: { ...state.metadata },\n entries,\n checkpointedEntryCount: entries.length,\n };\n};\n"],"mappings":"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"}