@fragno-dev/pi-fragment 0.0.1 → 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 (98) hide show
  1. package/README.md +39 -3
  2. package/dist/browser/client/react.d.ts +44 -36
  3. package/dist/browser/client/react.d.ts.map +1 -1
  4. package/dist/browser/client/react.js +105 -22
  5. package/dist/browser/client/react.js.map +1 -1
  6. package/dist/browser/client/solid.d.ts +42 -36
  7. package/dist/browser/client/solid.d.ts.map +1 -1
  8. package/dist/browser/client/solid.js +27 -13
  9. package/dist/browser/client/solid.js.map +1 -1
  10. package/dist/browser/client/svelte.d.ts +42 -36
  11. package/dist/browser/client/svelte.d.ts.map +1 -1
  12. package/dist/browser/client/svelte.js +14 -6
  13. package/dist/browser/client/svelte.js.map +1 -1
  14. package/dist/browser/client/vanilla.d.ts +99 -39
  15. package/dist/browser/client/vanilla.d.ts.map +1 -1
  16. package/dist/browser/client/vanilla.js +151 -3
  17. package/dist/browser/client/vanilla.js.map +1 -1
  18. package/dist/browser/client/vue.d.ts +54 -38
  19. package/dist/browser/client/vue.d.ts.map +1 -1
  20. package/dist/browser/client/vue.js +25 -17
  21. package/dist/browser/client/vue.js.map +1 -1
  22. package/dist/browser/{factory-DKoO_lRA.js → clients-BscY_HVe.js} +1051 -799
  23. package/dist/browser/clients-BscY_HVe.js.map +1 -0
  24. package/dist/browser/index.d.ts +3 -776
  25. package/dist/browser/index.js +801 -2
  26. package/dist/browser/index.js.map +1 -0
  27. package/dist/browser/routes-CpL_YGWK.d.ts +1560 -0
  28. package/dist/browser/routes-CpL_YGWK.d.ts.map +1 -0
  29. package/dist/cli/mod.d.ts.map +1 -1
  30. package/dist/cli/mod.js +245 -7
  31. package/dist/cli/mod.js.map +1 -1
  32. package/dist/node/{pi → client}/clients.d.ts +46 -36
  33. package/dist/node/client/clients.d.ts.map +1 -0
  34. package/dist/node/client/clients.js +54 -0
  35. package/dist/node/client/clients.js.map +1 -0
  36. package/dist/node/client/session-controller.d.ts +31 -0
  37. package/dist/node/client/session-controller.d.ts.map +1 -0
  38. package/dist/node/client/session-controller.js +33 -0
  39. package/dist/node/client/session-controller.js.map +1 -0
  40. package/dist/node/client/session-store.d.ts +71 -0
  41. package/dist/node/client/session-store.d.ts.map +1 -0
  42. package/dist/node/client/session-store.js +637 -0
  43. package/dist/node/client/session-store.js.map +1 -0
  44. package/dist/node/debug-log.d.ts +9 -0
  45. package/dist/node/debug-log.d.ts.map +1 -0
  46. package/dist/node/debug-log.js +58 -0
  47. package/dist/node/debug-log.js.map +1 -0
  48. package/dist/node/index.d.ts +5 -4
  49. package/dist/node/index.js +5 -3
  50. package/dist/node/pi/definition.d.ts +1 -1
  51. package/dist/node/pi/definition.d.ts.map +1 -1
  52. package/dist/node/pi/dsl.d.ts +5 -2
  53. package/dist/node/pi/dsl.d.ts.map +1 -1
  54. package/dist/node/pi/dsl.js +22 -3
  55. package/dist/node/pi/dsl.js.map +1 -1
  56. package/dist/node/pi/factory.d.ts +37 -34
  57. package/dist/node/pi/factory.d.ts.map +1 -1
  58. package/dist/node/pi/factory.js.map +1 -1
  59. package/dist/node/pi/mappers.js +0 -1
  60. package/dist/node/pi/mappers.js.map +1 -1
  61. package/dist/node/pi/route-schemas.js +42 -10
  62. package/dist/node/pi/route-schemas.js.map +1 -1
  63. package/dist/node/pi/types.d.ts +155 -7
  64. package/dist/node/pi/types.d.ts.map +1 -1
  65. package/dist/node/pi/types.js +6 -0
  66. package/dist/node/pi/types.js.map +1 -0
  67. package/dist/node/pi/workflow/active-session.d.ts +2 -0
  68. package/dist/node/pi/workflow/active-session.js +107 -0
  69. package/dist/node/pi/workflow/active-session.js.map +1 -0
  70. package/dist/node/pi/workflow/agent-runner.d.ts +13 -0
  71. package/dist/node/pi/workflow/agent-runner.d.ts.map +1 -0
  72. package/dist/node/pi/workflow/agent-runner.js +228 -0
  73. package/dist/node/pi/workflow/agent-runner.js.map +1 -0
  74. package/dist/node/pi/workflow/tool-journal.js +157 -0
  75. package/dist/node/pi/workflow/tool-journal.js.map +1 -0
  76. package/dist/node/pi/workflow/workflow.d.ts +29 -0
  77. package/dist/node/pi/workflow/workflow.d.ts.map +1 -0
  78. package/dist/node/pi/workflow/workflow.js +219 -0
  79. package/dist/node/pi/workflow/workflow.js.map +1 -0
  80. package/dist/node/routes.d.ts +38 -35
  81. package/dist/node/routes.d.ts.map +1 -1
  82. package/dist/node/routes.js +203 -132
  83. package/dist/node/routes.js.map +1 -1
  84. package/dist/node/schema.js +1 -1
  85. package/dist/node/schema.js.map +1 -1
  86. package/package.json +30 -29
  87. package/dist/browser/client-Bk-J98pf.d.ts +0 -679
  88. package/dist/browser/client-Bk-J98pf.d.ts.map +0 -1
  89. package/dist/browser/factory-DKoO_lRA.js.map +0 -1
  90. package/dist/browser/index.d.ts.map +0 -1
  91. package/dist/node/pi/clients.d.ts.map +0 -1
  92. package/dist/node/pi/clients.js +0 -18
  93. package/dist/node/pi/clients.js.map +0 -1
  94. package/dist/node/pi/workflow.d.ts +0 -31
  95. package/dist/node/pi/workflow.d.ts.map +0 -1
  96. package/dist/node/pi/workflow.js +0 -242
  97. package/dist/node/pi/workflow.js.map +0 -1
  98. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,228 @@
1
+ import { PiLogger } from "../../debug-log.js";
2
+ import { buildStableToolCallKey, buildToolErrorResult, clonePersistedToolCall, createPersistedToolCall, extractToolErrorMessage, takeNextReplaySequence } from "./tool-journal.js";
3
+ import { NonRetryableError } from "@fragno-dev/workflows";
4
+ import { Agent } from "@mariozechner/pi-agent-core";
5
+
6
+ //#region src/pi/workflow/agent-runner.ts
7
+ const isAssistantLikeMessage = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && value.role === "assistant" && Array.isArray(value.content) && typeof value.stopReason === "string";
8
+ const buildStreamErrorAssistantMessage = (model, error) => ({
9
+ role: "assistant",
10
+ content: [{
11
+ type: "text",
12
+ text: ""
13
+ }],
14
+ api: model.api,
15
+ provider: model.provider,
16
+ model: model.id,
17
+ usage: {
18
+ input: 0,
19
+ output: 0,
20
+ cacheRead: 0,
21
+ cacheWrite: 0,
22
+ totalTokens: 0,
23
+ cost: {
24
+ input: 0,
25
+ output: 0,
26
+ cacheRead: 0,
27
+ cacheWrite: 0,
28
+ total: 0
29
+ }
30
+ },
31
+ stopReason: "error",
32
+ errorMessage: error instanceof Error ? error.message : String(error),
33
+ timestamp: Date.now()
34
+ });
35
+ const wrapStreamFn = (streamFn) => streamFn ? async (...args) => {
36
+ const [model] = args;
37
+ const stream = await streamFn(...args);
38
+ if (typeof stream !== "object" || stream === null || Array.isArray(stream)) return stream;
39
+ const response = stream;
40
+ if (typeof response.result !== "function") return stream;
41
+ const originalResult = response.result.bind(stream);
42
+ let streamResultError;
43
+ response.result = async () => {
44
+ if (streamResultError) throw streamResultError;
45
+ try {
46
+ const result = await originalResult();
47
+ if (isAssistantLikeMessage(result)) return result;
48
+ streamResultError = /* @__PURE__ */ new Error("Stream result is not a valid assistant message.");
49
+ } catch (error) {
50
+ streamResultError = error;
51
+ }
52
+ const errorMessage = buildStreamErrorAssistantMessage(model, streamResultError);
53
+ if ("push" in stream && typeof stream.push === "function") stream.push({
54
+ type: "error",
55
+ reason: "error",
56
+ error: errorMessage
57
+ });
58
+ return errorMessage;
59
+ };
60
+ return stream;
61
+ } : void 0;
62
+ const resolveTool = async (name, factory, context) => {
63
+ if (!factory) throw new NonRetryableError(`Tool ${name} not found.`);
64
+ if (typeof factory === "function") {
65
+ const tool = await factory(context);
66
+ if (!tool) throw new NonRetryableError(`Tool ${name} returned no definition.`);
67
+ return tool;
68
+ }
69
+ return factory;
70
+ };
71
+ const wrapToolWithReplay = (options) => ({
72
+ ...options.tool,
73
+ execute: async (toolCallId, params, signal, onUpdate) => {
74
+ const sessionId = options.context.session.id;
75
+ const toolCallIdValue = String(toolCallId);
76
+ const key = buildStableToolCallKey(sessionId, options.context.turnId, toolCallIdValue);
77
+ const replayEntry = options.context.replay.cache.get(key);
78
+ if (replayEntry) {
79
+ options.context.replay.journal.push(clonePersistedToolCall({
80
+ ...replayEntry,
81
+ source: "replay",
82
+ seq: takeNextReplaySequence(options.context.replay)
83
+ }));
84
+ PiLogger.debug("tool replay hit", {
85
+ sessionId,
86
+ turnId: options.context.turnId,
87
+ toolName: replayEntry.toolName,
88
+ key
89
+ });
90
+ if (replayEntry.isError) throw new Error(extractToolErrorMessage(replayEntry.result));
91
+ return structuredClone(replayEntry.result);
92
+ }
93
+ const argsSnapshot = structuredClone(params);
94
+ const recordResult = (result, isError) => {
95
+ const entry = createPersistedToolCall({
96
+ sessionId,
97
+ turnId: options.context.turnId,
98
+ toolCallId: toolCallIdValue,
99
+ toolName: options.toolName,
100
+ args: argsSnapshot,
101
+ result,
102
+ isError,
103
+ source: "executed",
104
+ seq: takeNextReplaySequence(options.context.replay)
105
+ });
106
+ options.context.replay.cache.set(entry.key, clonePersistedToolCall(entry));
107
+ options.context.replay.journal.push(clonePersistedToolCall(entry));
108
+ return entry;
109
+ };
110
+ try {
111
+ const result = await options.tool.execute(toolCallId, params, signal, onUpdate);
112
+ recordResult(structuredClone(result), false);
113
+ return result;
114
+ } catch (error) {
115
+ recordResult(buildToolErrorResult(error), true);
116
+ throw error;
117
+ }
118
+ }
119
+ });
120
+ const resolveTools = async (options) => {
121
+ const toolNames = options.agent.tools ?? [];
122
+ if (toolNames.length === 0) return [];
123
+ const context = {
124
+ session: options.session,
125
+ turnId: options.turnId,
126
+ toolConfig: options.agent.toolConfig ?? null,
127
+ messages: options.messages,
128
+ replay: options.replay
129
+ };
130
+ const resolved = [];
131
+ for (const name of toolNames) {
132
+ const tool = await resolveTool(name, options.tools[name], context);
133
+ resolved.push(wrapToolWithReplay({
134
+ toolName: name,
135
+ tool,
136
+ context
137
+ }));
138
+ }
139
+ return resolved;
140
+ };
141
+ const findLastAssistantMessage = (messages) => messages.findLast((m) => m.role === "assistant") ?? null;
142
+ const createAgent = async (options) => {
143
+ const now = /* @__PURE__ */ new Date();
144
+ const session = {
145
+ id: options.params.sessionId,
146
+ name: null,
147
+ status: "active",
148
+ agent: options.params.agentName,
149
+ steeringMode: options.steeringMode,
150
+ metadata: null,
151
+ tags: [],
152
+ createdAt: now,
153
+ updatedAt: now
154
+ };
155
+ const agentTools = await resolveTools({
156
+ agent: options.agent,
157
+ tools: options.tools,
158
+ session,
159
+ turnId: options.turnId,
160
+ messages: options.messages,
161
+ replay: options.replay
162
+ });
163
+ const initialState = {
164
+ systemPrompt: options.params.systemPrompt ?? options.agent.systemPrompt,
165
+ model: options.agent.model,
166
+ tools: agentTools,
167
+ messages: options.messages
168
+ };
169
+ if (options.agent.thinkingLevel) initialState.thinkingLevel = options.agent.thinkingLevel;
170
+ const agent = new Agent({
171
+ initialState,
172
+ streamFn: wrapStreamFn(options.agent.streamFn),
173
+ convertToLlm: options.agent.convertToLlm,
174
+ transformContext: options.agent.transformContext,
175
+ getApiKey: options.agent.getApiKey,
176
+ thinkingBudgets: options.agent.thinkingBudgets,
177
+ maxRetryDelayMs: options.agent.maxRetryDelayMs,
178
+ sessionId: options.params.sessionId
179
+ });
180
+ agent.setSteeringMode(options.steeringMode);
181
+ const trace = [];
182
+ const unsubscribe = agent.subscribe((event) => {
183
+ trace.push(event);
184
+ options.onEvent?.(event);
185
+ if (!options.agent.onEvent) return;
186
+ try {
187
+ options.agent.onEvent(event, {
188
+ sessionId: options.params.sessionId,
189
+ turnId: options.turnId
190
+ });
191
+ } catch (error) {
192
+ console.warn("Agent onEvent hook failed.", {
193
+ error,
194
+ sessionId: options.params.sessionId,
195
+ turnId: options.turnId,
196
+ agent: options.agent.name
197
+ });
198
+ }
199
+ });
200
+ try {
201
+ await agent.continue();
202
+ } finally {
203
+ unsubscribe();
204
+ }
205
+ const assistant = findLastAssistantMessage(agent.state.messages);
206
+ const stateError = agent.state.error;
207
+ if (stateError) throw stateError instanceof Error ? stateError : new Error(String(stateError));
208
+ if (assistant && "errorMessage" in assistant && typeof assistant.errorMessage === "string") throw new Error(assistant.errorMessage);
209
+ return {
210
+ agent,
211
+ trace,
212
+ assistant,
213
+ toolJournal: options.replay.journal.map(clonePersistedToolCall)
214
+ };
215
+ };
216
+ const runAgentTurn = async (options) => {
217
+ const result = await createAgent(options);
218
+ return {
219
+ messages: result.agent.state.messages,
220
+ trace: result.trace,
221
+ assistant: result.assistant,
222
+ toolJournal: result.toolJournal
223
+ };
224
+ };
225
+
226
+ //#endregion
227
+ export { runAgentTurn };
228
+ //# sourceMappingURL=agent-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-runner.js","names":[],"sources":["../../../../src/pi/workflow/agent-runner.ts"],"sourcesContent":["import { NonRetryableError } from \"@fragno-dev/workflows\";\n\nimport {\n Agent,\n type AgentEvent,\n type AgentMessage,\n type AgentState,\n type AgentTool,\n} from \"@mariozechner/pi-agent-core\";\nimport type { AssistantMessage } from \"@mariozechner/pi-ai\";\n\nimport { PiLogger } from \"../../debug-log\";\nimport type {\n PiAgentDefinition,\n PiPersistedToolCall,\n PiPersistedToolResult,\n PiSession,\n PiToolFactory,\n PiToolFactoryContext,\n PiToolReplayContext,\n PiToolRegistry,\n} from \"../types\";\nimport {\n buildStableToolCallKey,\n buildToolErrorResult,\n clonePersistedToolCall,\n createPersistedToolCall,\n extractToolErrorMessage,\n takeNextReplaySequence,\n} from \"./tool-journal\";\n\nexport type AgentLoopParams = {\n sessionId: string;\n agentName: string;\n systemPrompt?: string;\n initialMessages?: AgentMessage[];\n};\n\n// --- Stream wrapping ---\n\ntype AgentStreamFn = NonNullable<PiAgentDefinition[\"streamFn\"]>;\ntype AgentStreamFnArgs = Parameters<AgentStreamFn>;\n\nconst isAssistantLikeMessage = (value: unknown): value is AssistantMessage =>\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n (value as { role?: unknown }).role === \"assistant\" &&\n Array.isArray((value as { content?: unknown }).content) &&\n typeof (value as { stopReason?: unknown }).stopReason === \"string\";\n\nconst buildStreamErrorAssistantMessage = (\n model: AgentStreamFnArgs[0],\n error: unknown,\n): AssistantMessage => ({\n role: \"assistant\",\n content: [{ type: \"text\", text: \"\" }],\n api: model.api,\n provider: model.provider,\n model: model.id,\n usage: {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n },\n stopReason: \"error\",\n errorMessage: error instanceof Error ? error.message : String(error),\n timestamp: Date.now(),\n});\n\nconst wrapStreamFn = (streamFn: PiAgentDefinition[\"streamFn\"]) =>\n streamFn\n ? async (...args: AgentStreamFnArgs) => {\n const [model] = args;\n const stream = await streamFn(...args);\n if (typeof stream !== \"object\" || stream === null || Array.isArray(stream)) {\n return stream;\n }\n\n const response = stream as { result?: unknown };\n if (typeof response.result !== \"function\") {\n return stream;\n }\n\n const originalResult = response.result.bind(stream) as () => Promise<unknown>;\n let streamResultError: unknown | undefined;\n response.result = async () => {\n if (streamResultError) {\n throw streamResultError;\n }\n\n try {\n const result = await originalResult();\n if (isAssistantLikeMessage(result)) {\n return result;\n }\n\n streamResultError = new Error(\"Stream result is not a valid assistant message.\");\n } catch (error) {\n streamResultError = error;\n }\n\n const errorMessage = buildStreamErrorAssistantMessage(model, streamResultError);\n if (\"push\" in stream && typeof stream.push === \"function\") {\n stream.push({ type: \"error\", reason: \"error\", error: errorMessage });\n }\n return errorMessage;\n };\n\n return stream;\n }\n : undefined;\n\n// --- Tool resolution ---\n\nconst resolveTool = async (\n name: string,\n factory: PiToolFactory | undefined,\n context: PiToolFactoryContext,\n): Promise<AgentTool> => {\n if (!factory) {\n throw new NonRetryableError(`Tool ${name} not found.`);\n }\n if (typeof factory === \"function\") {\n const tool = await factory(context);\n if (!tool) {\n throw new NonRetryableError(`Tool ${name} returned no definition.`);\n }\n return tool;\n }\n return factory;\n};\n\nconst wrapToolWithReplay = (options: {\n toolName: string;\n tool: AgentTool;\n context: PiToolFactoryContext;\n}): AgentTool => ({\n ...options.tool,\n execute: async (toolCallId, params, signal, onUpdate) => {\n const sessionId = options.context.session.id;\n const toolCallIdValue = String(toolCallId);\n const key = buildStableToolCallKey(sessionId, options.context.turnId, toolCallIdValue);\n const replayEntry = options.context.replay.cache.get(key);\n\n if (replayEntry) {\n options.context.replay.journal.push(\n clonePersistedToolCall({\n ...replayEntry,\n source: \"replay\",\n seq: takeNextReplaySequence(options.context.replay),\n }),\n );\n PiLogger.debug(\"tool replay hit\", {\n sessionId,\n turnId: options.context.turnId,\n toolName: replayEntry.toolName,\n key,\n });\n if (replayEntry.isError) {\n throw new Error(extractToolErrorMessage(replayEntry.result));\n }\n return structuredClone(replayEntry.result);\n }\n\n const argsSnapshot = structuredClone(params) as Record<string, unknown>;\n const recordResult = (result: PiPersistedToolResult, isError: boolean) => {\n const entry = createPersistedToolCall({\n sessionId,\n turnId: options.context.turnId,\n toolCallId: toolCallIdValue,\n toolName: options.toolName,\n args: argsSnapshot,\n result,\n isError,\n source: \"executed\",\n seq: takeNextReplaySequence(options.context.replay),\n });\n options.context.replay.cache.set(entry.key, clonePersistedToolCall(entry));\n options.context.replay.journal.push(clonePersistedToolCall(entry));\n return entry;\n };\n\n try {\n const result = await options.tool.execute(toolCallId, params, signal, onUpdate);\n recordResult(structuredClone(result) as PiPersistedToolResult, false);\n return result;\n } catch (error) {\n recordResult(buildToolErrorResult(error), true);\n throw error;\n }\n },\n});\n\nconst resolveTools = async (options: {\n agent: PiAgentDefinition;\n tools: PiToolRegistry;\n session: PiSession;\n turnId: string;\n messages: AgentMessage[];\n replay: PiToolReplayContext;\n}): Promise<AgentTool[]> => {\n const toolNames = options.agent.tools ?? [];\n if (toolNames.length === 0) {\n return [];\n }\n\n const context: PiToolFactoryContext = {\n session: options.session,\n turnId: options.turnId,\n toolConfig: options.agent.toolConfig ?? null,\n messages: options.messages,\n replay: options.replay,\n };\n\n const resolved: AgentTool[] = [];\n for (const name of toolNames) {\n const tool = await resolveTool(name, options.tools[name], context);\n resolved.push(wrapToolWithReplay({ toolName: name, tool, context }));\n }\n\n return resolved;\n};\n\n// --- Agent lifecycle ---\n\nconst findLastAssistantMessage = (messages: AgentMessage[]): AgentMessage | null =>\n messages.findLast((m) => m.role === \"assistant\") ?? null;\n\nconst createAgent = async (options: {\n agent: PiAgentDefinition;\n tools: PiToolRegistry;\n params: AgentLoopParams;\n messages: AgentMessage[];\n steeringMode: \"all\" | \"one-at-a-time\";\n turnId: string;\n replay: PiToolReplayContext;\n onEvent?: (event: AgentEvent) => void;\n}): Promise<{\n agent: Agent;\n trace: AgentEvent[];\n assistant: AgentMessage | null;\n toolJournal: PiPersistedToolCall[];\n}> => {\n const now = new Date();\n const session: PiSession = {\n id: options.params.sessionId,\n name: null,\n status: \"active\",\n agent: options.params.agentName,\n steeringMode: options.steeringMode,\n metadata: null,\n tags: [],\n createdAt: now,\n updatedAt: now,\n };\n\n const agentTools = await resolveTools({\n agent: options.agent,\n tools: options.tools,\n session,\n turnId: options.turnId,\n messages: options.messages,\n replay: options.replay,\n });\n\n const initialState: Partial<AgentState> = {\n systemPrompt: options.params.systemPrompt ?? options.agent.systemPrompt,\n model: options.agent.model,\n tools: agentTools,\n messages: options.messages,\n };\n\n if (options.agent.thinkingLevel) {\n initialState.thinkingLevel = options.agent.thinkingLevel;\n }\n\n const agent = new Agent({\n initialState,\n streamFn: wrapStreamFn(options.agent.streamFn),\n convertToLlm: options.agent.convertToLlm,\n transformContext: options.agent.transformContext,\n getApiKey: options.agent.getApiKey,\n thinkingBudgets: options.agent.thinkingBudgets,\n maxRetryDelayMs: options.agent.maxRetryDelayMs,\n sessionId: options.params.sessionId,\n });\n\n agent.setSteeringMode(options.steeringMode);\n\n const trace: AgentEvent[] = [];\n const unsubscribe = agent.subscribe((event) => {\n trace.push(event);\n options.onEvent?.(event);\n if (!options.agent.onEvent) {\n return;\n }\n try {\n options.agent.onEvent(event, { sessionId: options.params.sessionId, turnId: options.turnId });\n } catch (error) {\n console.warn(\"Agent onEvent hook failed.\", {\n error,\n sessionId: options.params.sessionId,\n turnId: options.turnId,\n agent: options.agent.name,\n });\n }\n });\n\n try {\n await agent.continue();\n } finally {\n unsubscribe();\n }\n\n const assistant = findLastAssistantMessage(agent.state.messages);\n const stateError = agent.state.error as unknown;\n if (stateError) {\n throw stateError instanceof Error ? stateError : new Error(String(stateError));\n }\n if (assistant && \"errorMessage\" in assistant && typeof assistant.errorMessage === \"string\") {\n throw new Error(assistant.errorMessage);\n }\n return {\n agent,\n trace,\n assistant,\n toolJournal: options.replay.journal.map(clonePersistedToolCall),\n };\n};\n\nexport const runAgentTurn = async (options: {\n params: AgentLoopParams;\n agent: PiAgentDefinition;\n tools: PiToolRegistry;\n messages: AgentMessage[];\n steeringMode: \"all\" | \"one-at-a-time\";\n turnId: string;\n replay: PiToolReplayContext;\n onEvent?: (event: AgentEvent) => void;\n}) => {\n const result = await createAgent(options);\n\n return {\n messages: result.agent.state.messages,\n trace: result.trace,\n assistant: result.assistant,\n toolJournal: result.toolJournal,\n };\n};\n"],"mappings":";;;;;;AA2CA,MAAM,0BAA0B,UAC9B,OAAO,UAAU,YACjB,UAAU,QACV,CAAC,MAAM,QAAQ,MAAM,IACpB,MAA6B,SAAS,eACvC,MAAM,QAAS,MAAgC,QAAQ,IACvD,OAAQ,MAAmC,eAAe;AAE5D,MAAM,oCACJ,OACA,WACsB;CACtB,MAAM;CACN,SAAS,CAAC;EAAE,MAAM;EAAQ,MAAM;EAAI,CAAC;CACrC,KAAK,MAAM;CACX,UAAU,MAAM;CAChB,OAAO,MAAM;CACb,OAAO;EACL,OAAO;EACP,QAAQ;EACR,WAAW;EACX,YAAY;EACZ,aAAa;EACb,MAAM;GAAE,OAAO;GAAG,QAAQ;GAAG,WAAW;GAAG,YAAY;GAAG,OAAO;GAAG;EACrE;CACD,YAAY;CACZ,cAAc,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;CACpE,WAAW,KAAK,KAAK;CACtB;AAED,MAAM,gBAAgB,aACpB,WACI,OAAO,GAAG,SAA4B;CACpC,MAAM,CAAC,SAAS;CAChB,MAAM,SAAS,MAAM,SAAS,GAAG,KAAK;AACtC,KAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,OAAO,CACxE,QAAO;CAGT,MAAM,WAAW;AACjB,KAAI,OAAO,SAAS,WAAW,WAC7B,QAAO;CAGT,MAAM,iBAAiB,SAAS,OAAO,KAAK,OAAO;CACnD,IAAI;AACJ,UAAS,SAAS,YAAY;AAC5B,MAAI,kBACF,OAAM;AAGR,MAAI;GACF,MAAM,SAAS,MAAM,gBAAgB;AACrC,OAAI,uBAAuB,OAAO,CAChC,QAAO;AAGT,uCAAoB,IAAI,MAAM,kDAAkD;WACzE,OAAO;AACd,uBAAoB;;EAGtB,MAAM,eAAe,iCAAiC,OAAO,kBAAkB;AAC/E,MAAI,UAAU,UAAU,OAAO,OAAO,SAAS,WAC7C,QAAO,KAAK;GAAE,MAAM;GAAS,QAAQ;GAAS,OAAO;GAAc,CAAC;AAEtE,SAAO;;AAGT,QAAO;IAET;AAIN,MAAM,cAAc,OAClB,MACA,SACA,YACuB;AACvB,KAAI,CAAC,QACH,OAAM,IAAI,kBAAkB,QAAQ,KAAK,aAAa;AAExD,KAAI,OAAO,YAAY,YAAY;EACjC,MAAM,OAAO,MAAM,QAAQ,QAAQ;AACnC,MAAI,CAAC,KACH,OAAM,IAAI,kBAAkB,QAAQ,KAAK,0BAA0B;AAErE,SAAO;;AAET,QAAO;;AAGT,MAAM,sBAAsB,aAIV;CAChB,GAAG,QAAQ;CACX,SAAS,OAAO,YAAY,QAAQ,QAAQ,aAAa;EACvD,MAAM,YAAY,QAAQ,QAAQ,QAAQ;EAC1C,MAAM,kBAAkB,OAAO,WAAW;EAC1C,MAAM,MAAM,uBAAuB,WAAW,QAAQ,QAAQ,QAAQ,gBAAgB;EACtF,MAAM,cAAc,QAAQ,QAAQ,OAAO,MAAM,IAAI,IAAI;AAEzD,MAAI,aAAa;AACf,WAAQ,QAAQ,OAAO,QAAQ,KAC7B,uBAAuB;IACrB,GAAG;IACH,QAAQ;IACR,KAAK,uBAAuB,QAAQ,QAAQ,OAAO;IACpD,CAAC,CACH;AACD,YAAS,MAAM,mBAAmB;IAChC;IACA,QAAQ,QAAQ,QAAQ;IACxB,UAAU,YAAY;IACtB;IACD,CAAC;AACF,OAAI,YAAY,QACd,OAAM,IAAI,MAAM,wBAAwB,YAAY,OAAO,CAAC;AAE9D,UAAO,gBAAgB,YAAY,OAAO;;EAG5C,MAAM,eAAe,gBAAgB,OAAO;EAC5C,MAAM,gBAAgB,QAA+B,YAAqB;GACxE,MAAM,QAAQ,wBAAwB;IACpC;IACA,QAAQ,QAAQ,QAAQ;IACxB,YAAY;IACZ,UAAU,QAAQ;IAClB,MAAM;IACN;IACA;IACA,QAAQ;IACR,KAAK,uBAAuB,QAAQ,QAAQ,OAAO;IACpD,CAAC;AACF,WAAQ,QAAQ,OAAO,MAAM,IAAI,MAAM,KAAK,uBAAuB,MAAM,CAAC;AAC1E,WAAQ,QAAQ,OAAO,QAAQ,KAAK,uBAAuB,MAAM,CAAC;AAClE,UAAO;;AAGT,MAAI;GACF,MAAM,SAAS,MAAM,QAAQ,KAAK,QAAQ,YAAY,QAAQ,QAAQ,SAAS;AAC/E,gBAAa,gBAAgB,OAAO,EAA2B,MAAM;AACrE,UAAO;WACA,OAAO;AACd,gBAAa,qBAAqB,MAAM,EAAE,KAAK;AAC/C,SAAM;;;CAGX;AAED,MAAM,eAAe,OAAO,YAOA;CAC1B,MAAM,YAAY,QAAQ,MAAM,SAAS,EAAE;AAC3C,KAAI,UAAU,WAAW,EACvB,QAAO,EAAE;CAGX,MAAM,UAAgC;EACpC,SAAS,QAAQ;EACjB,QAAQ,QAAQ;EAChB,YAAY,QAAQ,MAAM,cAAc;EACxC,UAAU,QAAQ;EAClB,QAAQ,QAAQ;EACjB;CAED,MAAM,WAAwB,EAAE;AAChC,MAAK,MAAM,QAAQ,WAAW;EAC5B,MAAM,OAAO,MAAM,YAAY,MAAM,QAAQ,MAAM,OAAO,QAAQ;AAClE,WAAS,KAAK,mBAAmB;GAAE,UAAU;GAAM;GAAM;GAAS,CAAC,CAAC;;AAGtE,QAAO;;AAKT,MAAM,4BAA4B,aAChC,SAAS,UAAU,MAAM,EAAE,SAAS,YAAY,IAAI;AAEtD,MAAM,cAAc,OAAO,YAcrB;CACJ,MAAM,sBAAM,IAAI,MAAM;CACtB,MAAM,UAAqB;EACzB,IAAI,QAAQ,OAAO;EACnB,MAAM;EACN,QAAQ;EACR,OAAO,QAAQ,OAAO;EACtB,cAAc,QAAQ;EACtB,UAAU;EACV,MAAM,EAAE;EACR,WAAW;EACX,WAAW;EACZ;CAED,MAAM,aAAa,MAAM,aAAa;EACpC,OAAO,QAAQ;EACf,OAAO,QAAQ;EACf;EACA,QAAQ,QAAQ;EAChB,UAAU,QAAQ;EAClB,QAAQ,QAAQ;EACjB,CAAC;CAEF,MAAM,eAAoC;EACxC,cAAc,QAAQ,OAAO,gBAAgB,QAAQ,MAAM;EAC3D,OAAO,QAAQ,MAAM;EACrB,OAAO;EACP,UAAU,QAAQ;EACnB;AAED,KAAI,QAAQ,MAAM,cAChB,cAAa,gBAAgB,QAAQ,MAAM;CAG7C,MAAM,QAAQ,IAAI,MAAM;EACtB;EACA,UAAU,aAAa,QAAQ,MAAM,SAAS;EAC9C,cAAc,QAAQ,MAAM;EAC5B,kBAAkB,QAAQ,MAAM;EAChC,WAAW,QAAQ,MAAM;EACzB,iBAAiB,QAAQ,MAAM;EAC/B,iBAAiB,QAAQ,MAAM;EAC/B,WAAW,QAAQ,OAAO;EAC3B,CAAC;AAEF,OAAM,gBAAgB,QAAQ,aAAa;CAE3C,MAAM,QAAsB,EAAE;CAC9B,MAAM,cAAc,MAAM,WAAW,UAAU;AAC7C,QAAM,KAAK,MAAM;AACjB,UAAQ,UAAU,MAAM;AACxB,MAAI,CAAC,QAAQ,MAAM,QACjB;AAEF,MAAI;AACF,WAAQ,MAAM,QAAQ,OAAO;IAAE,WAAW,QAAQ,OAAO;IAAW,QAAQ,QAAQ;IAAQ,CAAC;WACtF,OAAO;AACd,WAAQ,KAAK,8BAA8B;IACzC;IACA,WAAW,QAAQ,OAAO;IAC1B,QAAQ,QAAQ;IAChB,OAAO,QAAQ,MAAM;IACtB,CAAC;;GAEJ;AAEF,KAAI;AACF,QAAM,MAAM,UAAU;WACd;AACR,eAAa;;CAGf,MAAM,YAAY,yBAAyB,MAAM,MAAM,SAAS;CAChE,MAAM,aAAa,MAAM,MAAM;AAC/B,KAAI,WACF,OAAM,sBAAsB,QAAQ,aAAa,IAAI,MAAM,OAAO,WAAW,CAAC;AAEhF,KAAI,aAAa,kBAAkB,aAAa,OAAO,UAAU,iBAAiB,SAChF,OAAM,IAAI,MAAM,UAAU,aAAa;AAEzC,QAAO;EACL;EACA;EACA;EACA,aAAa,QAAQ,OAAO,QAAQ,IAAI,uBAAuB;EAChE;;AAGH,MAAa,eAAe,OAAO,YAS7B;CACJ,MAAM,SAAS,MAAM,YAAY,QAAQ;AAEzC,QAAO;EACL,UAAU,OAAO,MAAM,MAAM;EAC7B,OAAO,OAAO;EACd,WAAW,OAAO;EAClB,aAAa,OAAO;EACrB"}
@@ -0,0 +1,157 @@
1
+ import { PI_TOOL_JOURNAL_VERSION } from "../types.js";
2
+ import { z } from "zod";
3
+ import { NonRetryableError } from "@fragno-dev/workflows";
4
+
5
+ //#region src/pi/workflow/tool-journal.ts
6
+ const isRecord = (value) => typeof value === "object" && value !== null;
7
+ const TOOL_JOURNAL_FIELD = "toolJournal";
8
+ const persistedToolResultContentSchema = z.union([z.object({
9
+ type: z.literal("text"),
10
+ text: z.string(),
11
+ textSignature: z.string().optional()
12
+ }), z.object({
13
+ type: z.literal("image"),
14
+ data: z.string(),
15
+ mimeType: z.string()
16
+ })]);
17
+ const persistedToolResultSchema = z.object({
18
+ content: z.array(persistedToolResultContentSchema),
19
+ details: z.unknown()
20
+ });
21
+ const persistedToolCallSchema = z.object({
22
+ version: z.literal(PI_TOOL_JOURNAL_VERSION),
23
+ key: z.string(),
24
+ sessionId: z.string(),
25
+ turnId: z.string(),
26
+ toolCallId: z.string(),
27
+ toolName: z.string(),
28
+ args: z.record(z.string(), z.unknown()),
29
+ result: persistedToolResultSchema,
30
+ isError: z.boolean(),
31
+ source: z.enum(["executed", "replay"]),
32
+ capturedAt: z.number().finite(),
33
+ seq: z.number().int().nonnegative()
34
+ });
35
+ const formatToolJournalIssueLocation = (location, issue) => {
36
+ if (issue.path.length === 0) return location;
37
+ return `${location}.${issue.path.join(".")}`;
38
+ };
39
+ const buildStableToolCallKey = (sessionId, turnId, toolCallId) => `${sessionId}:${turnId}:${toolCallId}`;
40
+ const compareToolJournalEntries = (a, b) => a.seq !== b.seq ? a.seq - b.seq : a.capturedAt - b.capturedAt;
41
+ const clonePersistedToolCall = (entry) => structuredClone(entry);
42
+ const buildToolErrorResult = (error) => ({
43
+ content: [{
44
+ type: "text",
45
+ text: error instanceof Error ? error.message : String(error)
46
+ }],
47
+ details: {}
48
+ });
49
+ const extractToolErrorMessage = (result) => {
50
+ for (const block of result.content) if (block.type === "text") return block.text;
51
+ return "Tool execution failed";
52
+ };
53
+ const createPersistedToolCall = (options) => ({
54
+ version: PI_TOOL_JOURNAL_VERSION,
55
+ key: buildStableToolCallKey(options.sessionId, options.turnId, options.toolCallId),
56
+ sessionId: options.sessionId,
57
+ turnId: options.turnId,
58
+ toolCallId: options.toolCallId,
59
+ toolName: options.toolName,
60
+ args: options.args,
61
+ result: options.result,
62
+ isError: options.isError,
63
+ source: options.source,
64
+ capturedAt: Date.now(),
65
+ seq: options.seq
66
+ });
67
+ const parsePersistedToolCall = (value, location) => {
68
+ if (!isRecord(value) || Array.isArray(value)) throw new NonRetryableError(`Invalid tool journal entry at ${location}.`);
69
+ if (value["version"] !== PI_TOOL_JOURNAL_VERSION) throw new NonRetryableError(`Unsupported tool journal version at ${location}: ${String(value["version"])}`);
70
+ const parsed = persistedToolCallSchema.safeParse(value);
71
+ if (!parsed.success) {
72
+ const issue = parsed.error.issues[0];
73
+ throw new NonRetryableError(`Invalid tool journal entry at ${formatToolJournalIssueLocation(location, issue)}: ${issue.message}`);
74
+ }
75
+ return parsed.data;
76
+ };
77
+ const parsePersistedToolJournal = (assistantResult, stepName) => {
78
+ if (!isRecord(assistantResult) || Array.isArray(assistantResult)) throw new NonRetryableError(`Assistant step ${stepName} returned an invalid result object.`);
79
+ if (!(TOOL_JOURNAL_FIELD in assistantResult)) return [];
80
+ const raw = assistantResult[TOOL_JOURNAL_FIELD];
81
+ if (!Array.isArray(raw)) throw new NonRetryableError(`Assistant step ${stepName} contains an invalid tool journal.`);
82
+ return raw.map((entry, index) => parsePersistedToolCall(entry, `${stepName}[${index}]`));
83
+ };
84
+ const hydrateReplayCache = (cache, entries) => {
85
+ const sorted = [...entries].sort(compareToolJournalEntries);
86
+ for (const entry of sorted) {
87
+ if (entry.source === "replay" && cache.has(entry.key)) continue;
88
+ cache.set(entry.key, clonePersistedToolCall(entry));
89
+ }
90
+ };
91
+ const reduceBashSideEffects = (state, entry) => {
92
+ const details = isRecord(entry.result.details) ? entry.result.details : {};
93
+ const base = isRecord(state) ? state : {};
94
+ const next = {
95
+ cwd: typeof details.cwd === "string" ? details.cwd : typeof base.cwd === "string" ? base.cwd : "/",
96
+ files: isRecord(base.files) && !Array.isArray(base.files) ? { ...base.files } : {}
97
+ };
98
+ const detailsFiles = isRecord(details.files) && !Array.isArray(details.files) ? details.files : null;
99
+ if (detailsFiles) for (const [path, value] of Object.entries(detailsFiles)) next.files[path] = value;
100
+ if (Array.isArray(details.writes)) for (const item of details.writes) {
101
+ if (!isRecord(item)) continue;
102
+ const writeEntry = item;
103
+ const path = typeof writeEntry.path === "string" ? writeEntry.path : null;
104
+ if (!path) continue;
105
+ next.files[path] = typeof writeEntry.content === "string" ? writeEntry.content : "";
106
+ }
107
+ const deletes = Array.isArray(details.deletes) ? details.deletes : Array.isArray(details.deletedPaths) ? details.deletedPaths : [];
108
+ for (const path of deletes) if (typeof path === "string") delete next.files[path];
109
+ return next;
110
+ };
111
+ const buildSideEffectState = (options) => {
112
+ const reducers = {
113
+ bash: reduceBashSideEffects,
114
+ ...options.reducers
115
+ };
116
+ const state = {};
117
+ const journal = [...options.cache.values()].sort(compareToolJournalEntries);
118
+ for (const entry of journal) {
119
+ const reducer = reducers[entry.toolName];
120
+ if (!reducer) continue;
121
+ try {
122
+ state[entry.toolName] = reducer(state[entry.toolName], entry, {
123
+ key: entry.key,
124
+ sessionId: entry.sessionId,
125
+ turnId: entry.turnId
126
+ });
127
+ } catch (error) {
128
+ throw new NonRetryableError(`Tool side-effect reducer failed for ${entry.toolName}: ${error instanceof Error ? error.message : String(error)}`);
129
+ }
130
+ }
131
+ return state;
132
+ };
133
+ const replaySequenceByContext = /* @__PURE__ */ new WeakMap();
134
+ const takeNextReplaySequence = (replayContext) => {
135
+ const current = replaySequenceByContext.get(replayContext) ?? 0;
136
+ replaySequenceByContext.set(replayContext, current + 1);
137
+ return current;
138
+ };
139
+ const createReplayContext = (options) => {
140
+ const localCache = /* @__PURE__ */ new Map();
141
+ for (const [key, entry] of options.cache.entries()) localCache.set(key, clonePersistedToolCall(entry));
142
+ const replayContext = {
143
+ cache: localCache,
144
+ journal: [],
145
+ sideEffects: buildSideEffectState({
146
+ cache: localCache,
147
+ reducers: options.reducers
148
+ })
149
+ };
150
+ const maxSeq = [...localCache.values()].reduce((max, entry) => entry.seq > max ? entry.seq : max, -1);
151
+ replaySequenceByContext.set(replayContext, maxSeq + 1);
152
+ return replayContext;
153
+ };
154
+
155
+ //#endregion
156
+ export { buildStableToolCallKey, buildToolErrorResult, clonePersistedToolCall, createPersistedToolCall, createReplayContext, extractToolErrorMessage, hydrateReplayCache, parsePersistedToolJournal, takeNextReplaySequence };
157
+ //# sourceMappingURL=tool-journal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-journal.js","names":[],"sources":["../../../../src/pi/workflow/tool-journal.ts"],"sourcesContent":["import { z } from \"zod\";\n\nimport { NonRetryableError } from \"@fragno-dev/workflows\";\n\nimport {\n PI_TOOL_JOURNAL_VERSION,\n type PiPersistedToolCall,\n type PiPersistedToolResult,\n type PiToolReplayContext,\n type PiToolSideEffectReducerRegistry,\n} from \"../types\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst TOOL_JOURNAL_FIELD = \"toolJournal\";\n\nconst persistedToolResultContentSchema = z.union([\n z.object({\n type: z.literal(\"text\"),\n text: z.string(),\n textSignature: z.string().optional(),\n }),\n z.object({\n type: z.literal(\"image\"),\n data: z.string(),\n mimeType: z.string(),\n }),\n]);\n\nconst persistedToolResultSchema = z.object({\n content: z.array(persistedToolResultContentSchema),\n details: z.unknown(),\n});\n\nconst persistedToolCallSchema = z.object({\n version: z.literal(PI_TOOL_JOURNAL_VERSION),\n key: z.string(),\n sessionId: z.string(),\n turnId: z.string(),\n toolCallId: z.string(),\n toolName: z.string(),\n args: z.record(z.string(), z.unknown()),\n result: persistedToolResultSchema,\n isError: z.boolean(),\n source: z.enum([\"executed\", \"replay\"]),\n capturedAt: z.number().finite(),\n seq: z.number().int().nonnegative(),\n});\n\nconst formatToolJournalIssueLocation = (location: string, issue: z.ZodIssue): string => {\n if (issue.path.length === 0) {\n return location;\n }\n\n return `${location}.${issue.path.join(\".\")}`;\n};\n\nexport const buildStableToolCallKey = (sessionId: string, turnId: string, toolCallId: string) =>\n `${sessionId}:${turnId}:${toolCallId}`;\n\nexport const compareToolJournalEntries = (a: PiPersistedToolCall, b: PiPersistedToolCall) =>\n a.seq !== b.seq ? a.seq - b.seq : a.capturedAt - b.capturedAt;\n\nexport const clonePersistedToolCall = (entry: PiPersistedToolCall): PiPersistedToolCall =>\n structuredClone(entry);\n\nexport const buildToolErrorResult = (error: unknown): PiPersistedToolResult => ({\n content: [{ type: \"text\", text: error instanceof Error ? error.message : String(error) }],\n details: {},\n});\n\nexport const extractToolErrorMessage = (result: PiPersistedToolResult): string => {\n for (const block of result.content) {\n if (block.type === \"text\") {\n return block.text;\n }\n }\n return \"Tool execution failed\";\n};\n\nexport const createPersistedToolCall = (options: {\n sessionId: string;\n turnId: string;\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n result: PiPersistedToolResult;\n isError: boolean;\n source: PiPersistedToolCall[\"source\"];\n seq: number;\n}): PiPersistedToolCall => ({\n version: PI_TOOL_JOURNAL_VERSION,\n key: buildStableToolCallKey(options.sessionId, options.turnId, options.toolCallId),\n sessionId: options.sessionId,\n turnId: options.turnId,\n toolCallId: options.toolCallId,\n toolName: options.toolName,\n args: options.args,\n result: options.result,\n isError: options.isError,\n source: options.source,\n capturedAt: Date.now(),\n seq: options.seq,\n});\n\nconst parsePersistedToolCall = (value: unknown, location: string): PiPersistedToolCall => {\n if (!isRecord(value) || Array.isArray(value)) {\n throw new NonRetryableError(`Invalid tool journal entry at ${location}.`);\n }\n\n if (value[\"version\"] !== PI_TOOL_JOURNAL_VERSION) {\n throw new NonRetryableError(\n `Unsupported tool journal version at ${location}: ${String(value[\"version\"])}`,\n );\n }\n\n const parsed = persistedToolCallSchema.safeParse(value);\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n throw new NonRetryableError(\n `Invalid tool journal entry at ${formatToolJournalIssueLocation(location, issue)}: ${issue.message}`,\n );\n }\n\n return parsed.data as PiPersistedToolCall;\n};\n\nexport const parsePersistedToolJournal = (\n assistantResult: unknown,\n stepName: string,\n): PiPersistedToolCall[] => {\n if (!isRecord(assistantResult) || Array.isArray(assistantResult)) {\n throw new NonRetryableError(`Assistant step ${stepName} returned an invalid result object.`);\n }\n if (!(TOOL_JOURNAL_FIELD in assistantResult)) {\n return [];\n }\n const raw = assistantResult[TOOL_JOURNAL_FIELD];\n if (!Array.isArray(raw)) {\n throw new NonRetryableError(`Assistant step ${stepName} contains an invalid tool journal.`);\n }\n return raw.map((entry, index) => parsePersistedToolCall(entry, `${stepName}[${index}]`));\n};\n\nexport const hydrateReplayCache = (\n cache: PiToolReplayContext[\"cache\"],\n entries: PiPersistedToolCall[],\n): void => {\n const sorted = [...entries].sort(compareToolJournalEntries);\n for (const entry of sorted) {\n if (entry.source === \"replay\" && cache.has(entry.key)) {\n continue;\n }\n cache.set(entry.key, clonePersistedToolCall(entry));\n }\n};\n\n// --- Side-effect reducers ---\n\nconst reduceBashSideEffects = (state: unknown, entry: PiPersistedToolCall): unknown => {\n const details = isRecord(entry.result.details)\n ? (entry.result.details as {\n cwd?: unknown;\n files?: unknown;\n writes?: unknown;\n deletes?: unknown;\n deletedPaths?: unknown;\n })\n : {};\n const base = isRecord(state) ? (state as { cwd?: unknown; files?: unknown }) : {};\n\n const next = {\n cwd:\n typeof details.cwd === \"string\" ? details.cwd : typeof base.cwd === \"string\" ? base.cwd : \"/\",\n files:\n isRecord(base.files) && !Array.isArray(base.files)\n ? ({ ...(base.files as Record<string, unknown>) } as Record<string, unknown>)\n : ({} as Record<string, unknown>),\n };\n\n const detailsFiles =\n isRecord(details.files) && !Array.isArray(details.files) ? details.files : null;\n if (detailsFiles) {\n for (const [path, value] of Object.entries(detailsFiles)) {\n next.files[path] = value;\n }\n }\n\n if (Array.isArray(details.writes)) {\n for (const item of details.writes) {\n if (!isRecord(item)) {\n continue;\n }\n const writeEntry = item as { path?: unknown; content?: unknown };\n const path = typeof writeEntry.path === \"string\" ? writeEntry.path : null;\n if (!path) {\n continue;\n }\n next.files[path] = typeof writeEntry.content === \"string\" ? writeEntry.content : \"\";\n }\n }\n\n const deletes = Array.isArray(details.deletes)\n ? details.deletes\n : Array.isArray(details.deletedPaths)\n ? details.deletedPaths\n : [];\n for (const path of deletes) {\n if (typeof path === \"string\") {\n delete next.files[path];\n }\n }\n\n return next;\n};\n\nconst buildSideEffectState = (options: {\n cache: PiToolReplayContext[\"cache\"];\n reducers?: PiToolSideEffectReducerRegistry;\n}): Record<string, unknown> => {\n const reducers: PiToolSideEffectReducerRegistry = {\n bash: reduceBashSideEffects,\n ...options.reducers,\n };\n\n const state: Record<string, unknown> = {};\n const journal = [...options.cache.values()].sort(compareToolJournalEntries);\n\n for (const entry of journal) {\n const reducer = reducers[entry.toolName];\n if (!reducer) {\n continue;\n }\n\n try {\n state[entry.toolName] = reducer(state[entry.toolName], entry, {\n key: entry.key,\n sessionId: entry.sessionId,\n turnId: entry.turnId,\n });\n } catch (error) {\n throw new NonRetryableError(\n `Tool side-effect reducer failed for ${entry.toolName}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n return state;\n};\n\n// --- Replay context ---\n\nconst replaySequenceByContext = new WeakMap<PiToolReplayContext, number>();\n\nexport const takeNextReplaySequence = (replayContext: PiToolReplayContext): number => {\n const current = replaySequenceByContext.get(replayContext) ?? 0;\n replaySequenceByContext.set(replayContext, current + 1);\n return current;\n};\n\nexport const createReplayContext = (options: {\n cache: PiToolReplayContext[\"cache\"];\n reducers?: PiToolSideEffectReducerRegistry;\n}): PiToolReplayContext => {\n const localCache: PiToolReplayContext[\"cache\"] = new Map();\n for (const [key, entry] of options.cache.entries()) {\n localCache.set(key, clonePersistedToolCall(entry));\n }\n\n const replayContext: PiToolReplayContext = {\n cache: localCache,\n journal: [],\n sideEffects: buildSideEffectState({ cache: localCache, reducers: options.reducers }),\n };\n const maxSeq = [...localCache.values()].reduce(\n (max, entry) => (entry.seq > max ? entry.seq : max),\n -1,\n );\n replaySequenceByContext.set(replayContext, maxSeq + 1);\n return replayContext;\n};\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import "./active-session.js";
2
+ import { AgentLoopParams } from "./agent-runner.js";
3
+ import { PiAgentLoopState, PiAgentRegistry, PiFragmentConfig, PiToolRegistry, PiToolSideEffectReducerRegistry } from "../types.js";
4
+ import { z } from "zod";
5
+ import * as _fragno_dev_workflows0 from "@fragno-dev/workflows";
6
+ import { AgentMessage } from "@mariozechner/pi-agent-core";
7
+
8
+ //#region src/pi/workflow/workflow.d.ts
9
+ declare const PI_WORKFLOW_NAME = "agent-loop-workflow";
10
+ type WorkflowsOptions = {
11
+ agents: PiAgentRegistry;
12
+ tools: PiToolRegistry;
13
+ toolSideEffectReducers?: PiToolSideEffectReducerRegistry;
14
+ logging?: PiFragmentConfig["logging"];
15
+ };
16
+ declare const createPiAgentLoopWorkflow: (options: WorkflowsOptions) => _fragno_dev_workflows0.WorkflowDefinition<AgentLoopParams, {
17
+ messages: AgentMessage[];
18
+ }, z.ZodType<AgentLoopParams, unknown, z.core.$ZodTypeInternals<AgentLoopParams, unknown>>, undefined, PiAgentLoopState, "agent-loop-workflow">;
19
+ type PiWorkflowsRegistry = {
20
+ agentLoop: ReturnType<typeof createPiAgentLoopWorkflow>;
21
+ };
22
+ declare const createPiWorkflows: (options: WorkflowsOptions) => {
23
+ agentLoop: _fragno_dev_workflows0.WorkflowDefinition<AgentLoopParams, {
24
+ messages: AgentMessage[];
25
+ }, z.ZodType<AgentLoopParams, unknown, z.core.$ZodTypeInternals<AgentLoopParams, unknown>>, undefined, PiAgentLoopState, "agent-loop-workflow">;
26
+ };
27
+ //#endregion
28
+ export { PI_WORKFLOW_NAME, PiWorkflowsRegistry, createPiWorkflows };
29
+ //# sourceMappingURL=workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow.d.ts","names":[],"sources":["../../../../src/pi/workflow/workflow.ts"],"mappings":";;;;;;;;cAyCa,gBAAA;AAAA,KAER,gBAAA;EACH,MAAA,EAAQ,eAAA;EACR,KAAA,EAAO,cAAA;EACP,sBAAA,GAAyB,+BAAA;EACzB,OAAA,GAAU,gBAAA;AAAA;AAAA,cAmLN,yBAAA,GAA6B,OAAA,EAAS,gBAAA,4BAAgB,kBAAA,CAAA,eAAA;;;KAoHhD,mBAAA;EACV,SAAA,EAAW,UAAA,QAAkB,yBAAA;AAAA;AAAA,cAGlB,iBAAA,GAAqB,OAAA,EAAS,gBAAA"}