@earendil-works/pi-durable 0.99.1 → 1.0.0

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 (203) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +557 -46
  3. package/dist/entries.d.ts +20 -2
  4. package/dist/entries.d.ts.map +1 -1
  5. package/dist/entries.js +14 -1
  6. package/dist/entries.js.map +1 -1
  7. package/dist/env/index.d.ts +18 -38
  8. package/dist/env/index.d.ts.map +1 -1
  9. package/dist/env/index.js +2 -0
  10. package/dist/env/index.js.map +1 -1
  11. package/dist/env/node.d.ts +2 -0
  12. package/dist/env/node.d.ts.map +1 -1
  13. package/dist/env/node.js +53 -58
  14. package/dist/env/node.js.map +1 -1
  15. package/dist/harness/agent.d.ts +31 -0
  16. package/dist/harness/agent.d.ts.map +1 -0
  17. package/dist/harness/agent.js +209 -0
  18. package/dist/harness/agent.js.map +1 -0
  19. package/dist/harness/compaction.d.ts +56 -0
  20. package/dist/harness/compaction.d.ts.map +1 -0
  21. package/dist/harness/compaction.js +382 -0
  22. package/dist/harness/compaction.js.map +1 -0
  23. package/dist/harness/context.d.ts +8 -0
  24. package/dist/harness/context.d.ts.map +1 -1
  25. package/dist/harness/context.js +32 -21
  26. package/dist/harness/context.js.map +1 -1
  27. package/dist/harness/define.d.ts +18 -0
  28. package/dist/harness/define.d.ts.map +1 -0
  29. package/dist/harness/define.js +25 -0
  30. package/dist/harness/define.js.map +1 -0
  31. package/dist/harness/events.d.ts +166 -0
  32. package/dist/harness/events.d.ts.map +1 -0
  33. package/dist/harness/events.js +319 -0
  34. package/dist/harness/events.js.map +1 -0
  35. package/dist/harness/generation.d.ts +36 -7
  36. package/dist/harness/generation.d.ts.map +1 -1
  37. package/dist/harness/generation.js +356 -82
  38. package/dist/harness/generation.js.map +1 -1
  39. package/dist/harness/harness.d.ts.map +1 -1
  40. package/dist/harness/harness.js +141 -106
  41. package/dist/harness/harness.js.map +1 -1
  42. package/dist/harness/inbox.d.ts +59 -0
  43. package/dist/harness/inbox.d.ts.map +1 -0
  44. package/dist/harness/inbox.js +97 -0
  45. package/dist/harness/inbox.js.map +1 -0
  46. package/dist/harness/json.d.ts +10 -0
  47. package/dist/harness/json.d.ts.map +1 -0
  48. package/dist/harness/json.js +33 -0
  49. package/dist/harness/json.js.map +1 -0
  50. package/dist/harness/live.d.ts +66 -7
  51. package/dist/harness/live.d.ts.map +1 -1
  52. package/dist/harness/live.js +71 -7
  53. package/dist/harness/live.js.map +1 -1
  54. package/dist/harness/output.d.ts +62 -0
  55. package/dist/harness/output.d.ts.map +1 -0
  56. package/dist/harness/output.js +261 -0
  57. package/dist/harness/output.js.map +1 -0
  58. package/dist/harness/prompt.d.ts +9 -6
  59. package/dist/harness/prompt.d.ts.map +1 -1
  60. package/dist/harness/prompt.js +57 -14
  61. package/dist/harness/prompt.js.map +1 -1
  62. package/dist/harness/registry.d.ts +2 -3
  63. package/dist/harness/registry.d.ts.map +1 -1
  64. package/dist/harness/registry.js +69 -277
  65. package/dist/harness/registry.js.map +1 -1
  66. package/dist/harness/scheduler.d.ts +42 -12
  67. package/dist/harness/scheduler.d.ts.map +1 -1
  68. package/dist/harness/scheduler.js +649 -75
  69. package/dist/harness/scheduler.js.map +1 -1
  70. package/dist/harness/submissions.d.ts +14 -9
  71. package/dist/harness/submissions.d.ts.map +1 -1
  72. package/dist/harness/submissions.js +76 -34
  73. package/dist/harness/submissions.js.map +1 -1
  74. package/dist/harness/task-graph.d.ts +49 -0
  75. package/dist/harness/task-graph.d.ts.map +1 -0
  76. package/dist/harness/task-graph.js +163 -0
  77. package/dist/harness/task-graph.js.map +1 -0
  78. package/dist/harness/tool.d.ts +36 -0
  79. package/dist/harness/tool.d.ts.map +1 -0
  80. package/dist/harness/tool.js +415 -0
  81. package/dist/harness/tool.js.map +1 -0
  82. package/dist/harness/types.d.ts +330 -125
  83. package/dist/harness/types.d.ts.map +1 -1
  84. package/dist/harness/types.js.map +1 -1
  85. package/dist/harness/usage.d.ts +18 -0
  86. package/dist/harness/usage.d.ts.map +1 -0
  87. package/dist/harness/usage.js +57 -0
  88. package/dist/harness/usage.js.map +1 -0
  89. package/dist/harness/view.d.ts +43 -0
  90. package/dist/harness/view.d.ts.map +1 -0
  91. package/dist/harness/view.js +189 -0
  92. package/dist/harness/view.js.map +1 -0
  93. package/dist/index.d.ts +13 -5
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +8 -2
  96. package/dist/index.js.map +1 -1
  97. package/dist/session/observation.d.ts +18 -11
  98. package/dist/session/observation.d.ts.map +1 -1
  99. package/dist/session/observation.js +24 -13
  100. package/dist/session/observation.js.map +1 -1
  101. package/dist/session/session.d.ts +10 -1
  102. package/dist/session/session.d.ts.map +1 -1
  103. package/dist/session/session.js +23 -5
  104. package/dist/session/session.js.map +1 -1
  105. package/dist/session/transaction.d.ts +12 -3
  106. package/dist/session/transaction.d.ts.map +1 -1
  107. package/dist/session/transaction.js +90 -31
  108. package/dist/session/transaction.js.map +1 -1
  109. package/dist/storage/jsonl/storage.d.ts +22 -2
  110. package/dist/storage/jsonl/storage.d.ts.map +1 -1
  111. package/dist/storage/jsonl/storage.js +1 -1
  112. package/dist/storage/jsonl/storage.js.map +1 -1
  113. package/dist/storage/memory.js +1 -1
  114. package/dist/storage/memory.js.map +1 -1
  115. package/dist/storage/sqlite/database.d.ts +26 -19
  116. package/dist/storage/sqlite/database.d.ts.map +1 -1
  117. package/dist/storage/sqlite/database.js.map +1 -1
  118. package/dist/storage/sqlite/index.d.ts +1 -1
  119. package/dist/storage/sqlite/index.d.ts.map +1 -1
  120. package/dist/storage/sqlite/index.js.map +1 -1
  121. package/dist/storage/sqlite/migrations.js +7 -7
  122. package/dist/storage/sqlite/migrations.js.map +1 -1
  123. package/dist/storage/sqlite/node.d.ts +23 -7
  124. package/dist/storage/sqlite/node.d.ts.map +1 -1
  125. package/dist/storage/sqlite/node.js +129 -47
  126. package/dist/storage/sqlite/node.js.map +1 -1
  127. package/dist/storage/sqlite/storage.d.ts +12 -0
  128. package/dist/storage/sqlite/storage.d.ts.map +1 -1
  129. package/dist/storage/sqlite/storage.js +121 -129
  130. package/dist/storage/sqlite/storage.js.map +1 -1
  131. package/dist/testing/storage-benchmark.d.ts.map +1 -1
  132. package/dist/testing/storage-benchmark.js +0 -1
  133. package/dist/testing/storage-benchmark.js.map +1 -1
  134. package/dist/testing/storage-conformance.d.ts.map +1 -1
  135. package/dist/testing/storage-conformance.js +34 -2
  136. package/dist/testing/storage-conformance.js.map +1 -1
  137. package/dist/tools/bash.d.ts +28 -0
  138. package/dist/tools/bash.d.ts.map +1 -0
  139. package/dist/tools/bash.js +70 -0
  140. package/dist/tools/bash.js.map +1 -0
  141. package/dist/tools/edit-diff.d.ts +89 -0
  142. package/dist/tools/edit-diff.d.ts.map +1 -0
  143. package/dist/tools/edit-diff.js +386 -0
  144. package/dist/tools/edit-diff.js.map +1 -0
  145. package/dist/tools/edit.d.ts +18 -0
  146. package/dist/tools/edit.d.ts.map +1 -0
  147. package/dist/tools/edit.js +117 -0
  148. package/dist/tools/edit.js.map +1 -0
  149. package/dist/tools/env.d.ts +5 -0
  150. package/dist/tools/env.d.ts.map +1 -0
  151. package/dist/tools/env.js +7 -0
  152. package/dist/tools/env.js.map +1 -0
  153. package/dist/tools/file-mutation-queue.d.ts +9 -0
  154. package/dist/tools/file-mutation-queue.d.ts.map +1 -0
  155. package/dist/tools/file-mutation-queue.js +52 -0
  156. package/dist/tools/file-mutation-queue.js.map +1 -0
  157. package/dist/tools/image.d.ts +2 -0
  158. package/dist/tools/image.d.ts.map +1 -0
  159. package/dist/tools/image.js +92 -0
  160. package/dist/tools/image.js.map +1 -0
  161. package/dist/tools/index.d.ts +23 -0
  162. package/dist/tools/index.d.ts.map +1 -0
  163. package/dist/tools/index.js +15 -0
  164. package/dist/tools/index.js.map +1 -0
  165. package/dist/tools/path-utils.d.ts +5 -0
  166. package/dist/tools/path-utils.d.ts.map +1 -0
  167. package/dist/tools/path-utils.js +26 -0
  168. package/dist/tools/path-utils.js.map +1 -0
  169. package/dist/tools/read.d.ts +17 -0
  170. package/dist/tools/read.d.ts.map +1 -0
  171. package/dist/tools/read.js +101 -0
  172. package/dist/tools/read.js.map +1 -0
  173. package/dist/tools/write.d.ts +10 -0
  174. package/dist/tools/write.d.ts.map +1 -0
  175. package/dist/tools/write.js +31 -0
  176. package/dist/tools/write.js.map +1 -0
  177. package/dist/{env/utils/truncate.d.ts → truncate.d.ts} +1 -17
  178. package/dist/truncate.d.ts.map +1 -0
  179. package/dist/truncate.js +146 -0
  180. package/dist/truncate.js.map +1 -0
  181. package/dist/types.d.ts +97 -19
  182. package/dist/types.d.ts.map +1 -1
  183. package/dist/types.js.map +1 -1
  184. package/package.json +32 -14
  185. package/dist/env/utils/adaptive-publisher.d.ts +0 -24
  186. package/dist/env/utils/adaptive-publisher.d.ts.map +0 -1
  187. package/dist/env/utils/adaptive-publisher.js +0 -79
  188. package/dist/env/utils/adaptive-publisher.js.map +0 -1
  189. package/dist/env/utils/output-capture.d.ts +0 -31
  190. package/dist/env/utils/output-capture.d.ts.map +0 -1
  191. package/dist/env/utils/output-capture.js +0 -238
  192. package/dist/env/utils/output-capture.js.map +0 -1
  193. package/dist/env/utils/truncate.d.ts.map +0 -1
  194. package/dist/env/utils/truncate.js +0 -294
  195. package/dist/env/utils/truncate.js.map +0 -1
  196. package/dist/harness/config.d.ts +0 -20
  197. package/dist/harness/config.d.ts.map +0 -1
  198. package/dist/harness/config.js +0 -18
  199. package/dist/harness/config.js.map +0 -1
  200. package/docs/chord-delta-findings.md +0 -358
  201. package/docs/pico-v5-chord-usage.md +0 -373
  202. package/docs/pico-v5-handoff.md +0 -581
  203. package/docs/pico-v5.md +0 -3084
@@ -0,0 +1,415 @@
1
+ import { copyJson } from "@earendil-works/chord";
2
+ import { awaitWithContext } from "@earendil-works/chord/context";
3
+ import { overlap } from "@earendil-works/chord/delta";
4
+ import { validateToolArguments } from "@earendil-works/pi-ai/utils/validation";
5
+ import { AssistantEntry, ToolResultEntry } from "../entries.js";
6
+ import { defineTask } from "../tasks.js";
7
+ import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, utf8ByteLength } from "../truncate.js";
8
+ import { assignJson } from "./json.js";
9
+ import { clearProgress, finishSlot, LiveDoc, toolSlot } from "./live.js";
10
+ import { boundOutput, OutputBuffer, Progress } from "./output.js";
11
+ import { recordUsage } from "./usage.js";
12
+ /**
13
+ * Built-in tool task: resolves the called tool among its phase agent's tools, validates, runs `beforeTool`, records intent,
14
+ * executes, runs `afterTool`, and appends the result, all in one `call` handler so nothing separates resolution from
15
+ * settlement. `execute` is reached only by recovery and applies the replay rule.
16
+ */
17
+ export const ToolTask = defineTask({
18
+ name: "pi.tool",
19
+ version: 1,
20
+ initial: () => ({ phase: "call" }),
21
+ phases: {
22
+ call: async (task, runtime, context) => {
23
+ const call = await readCall(runtime, task.input, context);
24
+ const tool = (await runtime.agent(context)).tools.find((each) => each.name === call.name);
25
+ if (tool === undefined) {
26
+ const error = harnessError("tool_unavailable", `Tool ${call.name} is not available`);
27
+ return settle(runtime, call, COMPLETED, () => error, context);
28
+ }
29
+ const prepared = prepare(tool, call.arguments);
30
+ const checked = "error" in prepared ? prepared : validate(tool, call, prepared.args);
31
+ if ("error" in checked)
32
+ return settle(runtime, call, COMPLETED, () => invalid(checked.error), context);
33
+ let args = checked.args;
34
+ let block;
35
+ await runtime.hooks.each("beforeTool", async (hook) => {
36
+ if (block !== undefined)
37
+ return;
38
+ try {
39
+ const decision = await hook({ ...call, arguments: args }, runtime, context);
40
+ if (decision?.block !== undefined)
41
+ block = decision.block;
42
+ else if (decision?.arguments !== undefined)
43
+ args = decision.arguments;
44
+ }
45
+ catch (error) {
46
+ if (runtime.signal.aborted)
47
+ throw error;
48
+ block = errorText(error);
49
+ }
50
+ });
51
+ if (block !== undefined) {
52
+ const blocked = harnessError("blocked", `Tool call blocked: ${block}`);
53
+ return settle(runtime, call, COMPLETED, () => blocked, context);
54
+ }
55
+ const validated = validate(tool, call, args);
56
+ if ("error" in validated)
57
+ return settle(runtime, call, COMPLETED, () => invalid(validated.error), context);
58
+ const final = validated.args;
59
+ await runtime.commit(async (tx) => {
60
+ const slot = toolSlot(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);
61
+ if (slot !== undefined)
62
+ slot.status = "running";
63
+ const intent = { phase: "execute", arguments: final, replay: tool.replay ?? "unsafe" };
64
+ return { status: "running", checkpoint: intent };
65
+ }, context);
66
+ await run(runtime, call, tool, final, context);
67
+ },
68
+ /** Recovery after intent: rerun only when the stored and the current policy both say `safe`. */
69
+ execute: async (task, runtime, context) => {
70
+ const { arguments: args, replay } = task.state.checkpoint;
71
+ const call = await readCall(runtime, task.input, context);
72
+ const tool = (await runtime.agent(context)).tools.find((each) => each.name === call.name);
73
+ if (replay === "safe" && tool?.replay === "safe") {
74
+ // The rerun reports from scratch; clear what the interrupted attempt published.
75
+ await runtime.commit(async (tx) => {
76
+ const slot = toolSlot(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);
77
+ if (slot !== undefined)
78
+ clearProgress(slot);
79
+ return undefined;
80
+ }, context);
81
+ return run(runtime, call, tool, args, context);
82
+ }
83
+ const message = `Tool ${call.name} was interrupted and may have partially run`;
84
+ // `failed` records cancellation intent, so the call's owned conversations, left unsupervised, are aborted.
85
+ const ending = { status: "failed", message };
86
+ await settle(runtime, call, ending, (slot) => fromSlot(slot, "interrupted", message), context);
87
+ },
88
+ },
89
+ abort: async (task, runtime, context) => {
90
+ const call = await readCall(runtime, task.input, context);
91
+ const message = `Tool ${call.name} was aborted`;
92
+ await settle(runtime, call, { status: "aborted" }, (slot) => fromSlot(slot, "aborted", message), context);
93
+ },
94
+ });
95
+ /** The tool call `callId` of the assistant entry. */
96
+ async function readCall(runtime, input, context) {
97
+ const entry = await runtime.entry(AssistantEntry, input.assistant, context);
98
+ const message = entry?.model?.[0];
99
+ const call = message?.role === "assistant"
100
+ ? message.content.find((content) => content.type === "toolCall" && content.id === input.callId)
101
+ : undefined;
102
+ if (call === undefined)
103
+ throw new Error(`Entry ${input.assistant} has no tool call ${input.callId}`);
104
+ return call;
105
+ }
106
+ /** The call's arguments as repaired by the tool; a throwing repair makes them invalid. */
107
+ function prepare(tool, args) {
108
+ if (tool.prepareArguments === undefined)
109
+ return { args };
110
+ try {
111
+ return { args: tool.prepareArguments(args) };
112
+ }
113
+ catch (error) {
114
+ return { error: errorText(error) };
115
+ }
116
+ }
117
+ /** Arguments validated and coerced against the implementation's schema. */
118
+ function validate(tool, call, args) {
119
+ try {
120
+ return { args: validateToolArguments(tool, { ...call, arguments: args }) };
121
+ }
122
+ catch (error) {
123
+ return { error: errorText(error) };
124
+ }
125
+ }
126
+ function invalid(message) {
127
+ return harnessError("invalid_arguments", message);
128
+ }
129
+ /** Execute with the resolved implementation, then settle its result. */
130
+ async function run(runtime, call, tool, args, context) {
131
+ const limits = {
132
+ maxBytes: tool.outputLimits?.maxBytes ?? DEFAULT_MAX_BYTES,
133
+ maxLines: tool.outputLimits?.maxLines ?? DEFAULT_MAX_LINES,
134
+ retain: tool.outputLimits?.retain ?? "head",
135
+ };
136
+ const reported = { output: new OutputBuffer(limits), limits, diagnostics: [], details: undefined };
137
+ const progress = publishProgress(runtime, reported, context);
138
+ let ended = false;
139
+ const assertLive = () => {
140
+ if (ended)
141
+ throw new Error(`Tool call ${call.id} has settled`);
142
+ };
143
+ const api = {
144
+ taskId: runtime.taskId,
145
+ conversationId: runtime.conversationId,
146
+ callId: call.id,
147
+ registry: runtime.registry,
148
+ agent: runtime.agent,
149
+ output: (chunk) => {
150
+ assertLive();
151
+ if (reported.output.push(chunk))
152
+ progress.mark();
153
+ },
154
+ diagnostic: (diagnostic) => {
155
+ assertLive();
156
+ reported.diagnostics.push(copyJson(diagnostic, { omitUndefinedProperties: true }));
157
+ progress.mark();
158
+ },
159
+ details: async (value, detailsContext) => {
160
+ assertLive();
161
+ detailsContext.abortSignal?.throwIfAborted();
162
+ reported.details = copyJson(value, { omitUndefinedProperties: true });
163
+ const committed = progress.markAndWait();
164
+ // Cancelling the wait leaves the update in place; the commit's own outcome stays observed.
165
+ committed.catch(() => { });
166
+ return awaitWithContext(committed, detailsContext);
167
+ },
168
+ commit: async (change, commitContext) => {
169
+ let result;
170
+ await runtime.commit(async (tx) => {
171
+ result = await change(tx);
172
+ return undefined;
173
+ }, commitContext);
174
+ return result;
175
+ },
176
+ memo: runtime.memo,
177
+ createTask: async (task, input, options, taskContext) => {
178
+ let id;
179
+ await runtime.commit(async (tx) => {
180
+ id = await tx.createTask(task, input, options);
181
+ return undefined;
182
+ }, taskContext);
183
+ return id;
184
+ },
185
+ getTask: runtime.getTask,
186
+ waitForTask: runtime.waitForTask,
187
+ conversation: runtime.conversation,
188
+ snapshot: runtime.snapshot,
189
+ snapshotAsOf: runtime.snapshotAsOf,
190
+ watchDoc: runtime.watchDoc,
191
+ };
192
+ let result;
193
+ let ending = COMPLETED;
194
+ try {
195
+ // Built for this call, so a rerun after recovery gets the conversation's environment at that time.
196
+ const env = await runtime.env(context);
197
+ result = await tool.execute(args, { ...api, env }, context);
198
+ }
199
+ catch (error) {
200
+ if (runtime.signal.aborted) {
201
+ ended = true;
202
+ for (const waiter of await progress.stop())
203
+ waiter.reject(error);
204
+ throw error;
205
+ }
206
+ result = { isError: true, diagnostics: [toolDiagnostic("tool_error", errorText(error))] };
207
+ // A throw, from `execute()` or from building the environment, ends the task `failed`, which cancels what the call owned; it no longer supervises it. The error text
208
+ // is already in the result entry.
209
+ ending = { status: "failed", message: `Tool ${call.name} threw` };
210
+ }
211
+ ended = true;
212
+ reported.output.end();
213
+ // Details still waiting for a progress commit settle with the terminal commit, the final flush.
214
+ const pending = await progress.stop();
215
+ try {
216
+ const settled = await finalResult(runtime, call, result, reported, context);
217
+ await settle(runtime, call, ending, () => settled, context);
218
+ }
219
+ catch (error) {
220
+ for (const waiter of pending)
221
+ waiter.reject(error);
222
+ throw error;
223
+ }
224
+ for (const waiter of pending)
225
+ waiter.resolve();
226
+ }
227
+ /**
228
+ * Throttled commits of what the tool reported into its `pi.live.tools` slot, each writing only what changed since the
229
+ * last one.
230
+ */
231
+ function publishProgress(runtime, reported, context) {
232
+ let written = { text: "", details: undefined, diagnostics: 0 };
233
+ return new Progress(async () => {
234
+ // Capture everything synchronously: the tool keeps reporting while the commit is in flight.
235
+ const snapshot = reported.output.snapshot();
236
+ const current = { text: snapshot.text, details: reported.details, diagnostics: reported.diagnostics.length };
237
+ const added = reported.diagnostics.slice(written.diagnostics, current.diagnostics);
238
+ const detailsChanged = current.details !== written.details;
239
+ // What the commit writes, as Chord diffs the string: an append, a trim plus an append of what follows the shared
240
+ // part, or the whole window when its bounded overlap search finds nothing.
241
+ let bytes = 0;
242
+ if (snapshot.text !== written.text) {
243
+ const shared = snapshot.text.startsWith(written.text)
244
+ ? written.text.length
245
+ : overlap(written.text, snapshot.text, 65_536);
246
+ bytes += utf8ByteLength(snapshot.text.slice(shared));
247
+ }
248
+ if (detailsChanged)
249
+ bytes += utf8ByteLength(JSON.stringify(current.details ?? null));
250
+ if (added.length > 0)
251
+ bytes += utf8ByteLength(JSON.stringify(added));
252
+ await runtime.commit(async (tx) => {
253
+ const slot = toolSlot(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);
254
+ if (slot === undefined)
255
+ return undefined;
256
+ // REMINDER: assign `output` as one string field. Chord then diffs it into an append, or a trim plus an
257
+ // append for a sliding tail; replacing the slot object would record the whole window on every commit.
258
+ if ((slot.output ?? "") !== snapshot.text)
259
+ slot.output = snapshot.text;
260
+ if (snapshot.droppedBytes > 0)
261
+ slot.droppedBytes = snapshot.droppedBytes;
262
+ if (snapshot.droppedLines > 0)
263
+ slot.droppedLines = snapshot.droppedLines;
264
+ // Diff details leaf by leaf and append new diagnostics, so each commit writes only what changed.
265
+ if (detailsChanged && current.details !== undefined) {
266
+ assignJson(slot, "details", current.details);
267
+ }
268
+ if (added.length > 0) {
269
+ if (slot.diagnostics === undefined)
270
+ slot.diagnostics = [];
271
+ for (const diagnostic of added)
272
+ slot.diagnostics.push(diagnostic);
273
+ }
274
+ return undefined;
275
+ }, context);
276
+ written = current;
277
+ return bytes;
278
+ }, (error) => {
279
+ // Rejections after an abort mark or close are expected; the committed state stays consistent.
280
+ if (!runtime.signal.aborted)
281
+ runtime.report(error);
282
+ });
283
+ }
284
+ /**
285
+ * The settled result: the tool's result with the retained output and last details as fallbacks, its diagnostics after
286
+ * those reported through the api, `afterTool` applied, and explicit text bounded, with the Harness's truncation
287
+ * diagnostic last.
288
+ */
289
+ async function finalResult(runtime, call, result, reported, context) {
290
+ const harness = [];
291
+ const retained = result.content === undefined ? reported.output.snapshot() : undefined;
292
+ const content = retained === undefined ? result.content : retained.text === "" ? [] : [{ type: "text", text: retained.text }];
293
+ let final = {
294
+ ...result,
295
+ content,
296
+ details: result.details === undefined ? reported.details : result.details,
297
+ diagnostics: [...reported.diagnostics, ...(result.diagnostics ?? [])],
298
+ };
299
+ await runtime.hooks.each("afterTool", async (hook) => {
300
+ final = (await hook(call, final, runtime, context)) ?? final;
301
+ });
302
+ // The retained output's truncation applies only while afterTool kept that content.
303
+ if (retained !== undefined && retained.droppedBytes > 0 && final.content === content) {
304
+ harness.push(truncated(retained, reported.limits.retain));
305
+ }
306
+ const bounded = boundContent(final.content ?? [], reported.limits);
307
+ if (bounded.droppedBytes > 0)
308
+ harness.push(truncated(bounded, reported.limits.retain));
309
+ return { ...final, content: bounded.content, diagnostics: [...(final.diagnostics ?? []), ...harness] };
310
+ }
311
+ /**
312
+ * Commit the tool's terminal state: append its result entry, mark its slot done, and complete or end aborted with the
313
+ * entry ID. `build` receives the slot so interruption and abort can report the durable partial output.
314
+ */
315
+ async function settle(runtime, call, ending, build, context) {
316
+ await runtime.commit(async (tx) => {
317
+ const slot = toolSlot(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);
318
+ const result = build(slot);
319
+ const entry = await appendToolResult(tx, runtime.conversationId, call, result, runtime.now());
320
+ if (slot !== undefined)
321
+ finishSlot(slot, entry.id);
322
+ const entryId = entry.id;
323
+ if (ending.status === "aborted")
324
+ return { status: "terminal", outcome: { status: "aborted", result: { entryId } } };
325
+ if (ending.status === "failed") {
326
+ const error = { message: ending.message };
327
+ return { status: "terminal", outcome: { status: "failed", error, result: { entryId } } };
328
+ }
329
+ // Tools build control objects freely; drop keys set to undefined so the task result is strict JSON.
330
+ const control = result.control === undefined
331
+ ? {}
332
+ : { control: copyJson(result.control, { omitUndefinedProperties: true }) };
333
+ return { status: "terminal", outcome: { status: "completed", result: { entryId, ...control } } };
334
+ }, context);
335
+ }
336
+ const COMPLETED = { status: "completed" };
337
+ /** An error result from the slot's durable partial output, details, and diagnostics. */
338
+ function fromSlot(slot, code, message) {
339
+ const diagnostics = [...(slot?.diagnostics ?? [])];
340
+ const droppedBytes = slot?.droppedBytes ?? 0;
341
+ if (droppedBytes > 0)
342
+ diagnostics.push(truncated({ droppedBytes, droppedLines: slot?.droppedLines ?? 0 }));
343
+ diagnostics.push(toolDiagnostic(code, message));
344
+ return {
345
+ content: slot?.output === undefined || slot.output === "" ? [] : [{ type: "text", text: slot.output }],
346
+ isError: true,
347
+ ...(slot?.details === undefined ? {} : { details: slot.details }),
348
+ diagnostics,
349
+ };
350
+ }
351
+ /** An error result the Harness writes itself: no content and one `error` diagnostic with `code`. */
352
+ export function harnessError(code, message) {
353
+ return { content: [], isError: true, diagnostics: [toolDiagnostic(code, message)] };
354
+ }
355
+ function toolDiagnostic(code, message) {
356
+ return { severity: "error", code, message };
357
+ }
358
+ /** The Harness's truncation diagnostic; `retain` is unknown when rebuilt from a slot after recovery. */
359
+ function truncated(dropped, retain) {
360
+ const kept = retain === undefined ? "" : ` to its ${retain === "head" ? "beginning" : "end"}`;
361
+ return {
362
+ severity: "warn",
363
+ code: "truncated",
364
+ message: `Output truncated${kept}: ${dropped.droppedLines} lines, ${dropped.droppedBytes} bytes dropped`,
365
+ };
366
+ }
367
+ /**
368
+ * Append a `pi.tool-result` entry. The content ends with the rendered diagnostics, so the stored message is exactly
369
+ * what the model sees; `data` keeps the structured list. A result's usage is added to `pi.usage` in the same commit.
370
+ */
371
+ export async function appendToolResult(tx, conversationId, call, result, timestamp) {
372
+ const diagnostics = [...(result.diagnostics ?? [])];
373
+ const content = [...(result.content ?? [])];
374
+ if (diagnostics.length > 0)
375
+ content.push({ type: "text", text: renderDiagnostics(diagnostics) });
376
+ const message = {
377
+ role: "toolResult",
378
+ toolCallId: call.id,
379
+ toolName: call.name,
380
+ content,
381
+ ...(result.details === undefined ? {} : { details: result.details }),
382
+ ...(result.usage === undefined ? {} : { usage: result.usage }),
383
+ isError: result.isError ?? false,
384
+ timestamp,
385
+ };
386
+ if (result.usage !== undefined)
387
+ await recordUsage(tx, conversationId, "tools", call.name, result.usage);
388
+ return tx.appendEntry(ToolResultEntry, conversationId, { model: [message], data: { diagnostics } });
389
+ }
390
+ function renderDiagnostics(diagnostics) {
391
+ return `<harness>\n${diagnostics.map((diagnostic) => `[${diagnostic.severity}] ${diagnostic.message}`).join("\n")}\n</harness>`;
392
+ }
393
+ /**
394
+ * Bound the text of result content. When the joined text exceeds the limits, the text items are replaced by one bounded
395
+ * item at the position of the first (head) or last (tail) text item; other content is kept.
396
+ */
397
+ function boundContent(content, limits) {
398
+ const texts = content.filter((item) => item.type === "text");
399
+ const bounded = boundOutput(texts.map((item) => item.text).join(""), limits);
400
+ if (bounded.droppedBytes === 0)
401
+ return { content, droppedBytes: 0, droppedLines: 0 };
402
+ const keep = limits.retain === "head" ? texts[0] : texts.at(-1);
403
+ const result = [];
404
+ for (const item of content) {
405
+ if (item.type !== "text")
406
+ result.push(item);
407
+ else if (item === keep)
408
+ result.push({ ...item, text: bounded.text });
409
+ }
410
+ return { content: result, droppedBytes: bounded.droppedBytes, droppedLines: bounded.droppedLines };
411
+ }
412
+ function errorText(error) {
413
+ return error instanceof Error ? error.message : String(error);
414
+ }
415
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/harness/tool.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Context, copyJson, type JsonValue } from \"@earendil-works/chord\";\nimport { awaitWithContext } from \"@earendil-works/chord/context\";\nimport { overlap } from \"@earendil-works/chord/delta\";\nimport type { ImageContent, TextContent, ToolCall, ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport { validateToolArguments } from \"@earendil-works/pi-ai/utils/validation\";\nimport { AssistantEntry, ToolResultEntry } from \"../entries.ts\";\nimport { defineTask } from \"../tasks.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, utf8ByteLength } from \"../truncate.ts\";\nimport type {\n\tConversationId,\n\tEntryId,\n\tJsonObject,\n\tTask,\n\tTaskId,\n\tTaskOptions,\n\tTaskRuntime,\n\tTx,\n\tTypedEntry,\n} from \"../types.ts\";\nimport { assignJson } from \"./json.ts\";\nimport { clearProgress, finishSlot, LiveDoc, type ToolSlot, toolSlot } from \"./live.ts\";\nimport { boundOutput, OutputBuffer, type OutputLimits, Progress } from \"./output.ts\";\nimport type {\n\tToolControl,\n\tToolDiagnostic,\n\tToolExecutionApi,\n\tToolExecutionResult,\n\tToolHooks,\n\tToolRegistration,\n} from \"./types.ts\";\nimport { recordUsage } from \"./usage.ts\";\n\nexport type ToolTaskInput = { assistant: EntryId; callId: string };\n\nexport type ToolTaskCheckpoint =\n\t| { phase: \"call\" }\n\t/** Durable intent: the final arguments and the replay policy recorded before execution. */\n\t| { phase: \"execute\"; arguments: JsonObject; replay: \"safe\" | \"unsafe\" };\n\nexport type ToolTaskResult = { entryId: EntryId; control?: ToolControl };\n\ntype Runtime = TaskRuntime<ToolTaskInput, ToolTaskCheckpoint, ToolTaskResult, ToolHooks>;\ntype Content = (TextContent | ImageContent)[];\n\n/**\n * Built-in tool task: resolves the called tool among its phase agent's tools, validates, runs `beforeTool`, records intent,\n * executes, runs `afterTool`, and appends the result, all in one `call` handler so nothing separates resolution from\n * settlement. `execute` is reached only by recovery and applies the replay rule.\n */\nexport const ToolTask = defineTask<ToolTaskInput, ToolTaskCheckpoint, ToolTaskResult, ToolHooks>({\n\tname: \"pi.tool\",\n\tversion: 1,\n\tinitial: () => ({ phase: \"call\" }),\n\tphases: {\n\t\tcall: async (task, runtime, context) => {\n\t\t\tconst call = await readCall(runtime, task.input, context);\n\t\t\tconst tool = (await runtime.agent(context)).tools.find((each) => each.name === call.name);\n\t\t\tif (tool === undefined) {\n\t\t\t\tconst error = harnessError(\"tool_unavailable\", `Tool ${call.name} is not available`);\n\t\t\t\treturn settle(runtime, call, COMPLETED, () => error, context);\n\t\t\t}\n\t\t\tconst prepared = prepare(tool, call.arguments as JsonObject);\n\t\t\tconst checked = \"error\" in prepared ? prepared : validate(tool, call, prepared.args);\n\t\t\tif (\"error\" in checked) return settle(runtime, call, COMPLETED, () => invalid(checked.error), context);\n\t\t\tlet args = checked.args;\n\t\t\tlet block: string | undefined;\n\t\t\tawait runtime.hooks.each(\"beforeTool\", async (hook) => {\n\t\t\t\tif (block !== undefined) return;\n\t\t\t\ttry {\n\t\t\t\t\tconst decision = await hook({ ...call, arguments: args }, runtime, context);\n\t\t\t\t\tif (decision?.block !== undefined) block = decision.block;\n\t\t\t\t\telse if (decision?.arguments !== undefined) args = decision.arguments;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (runtime.signal.aborted) throw error;\n\t\t\t\t\tblock = errorText(error);\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (block !== undefined) {\n\t\t\t\tconst blocked = harnessError(\"blocked\", `Tool call blocked: ${block}`);\n\t\t\t\treturn settle(runtime, call, COMPLETED, () => blocked, context);\n\t\t\t}\n\t\t\tconst validated = validate(tool, call, args);\n\t\t\tif (\"error\" in validated) return settle(runtime, call, COMPLETED, () => invalid(validated.error), context);\n\t\t\tconst final = validated.args;\n\t\t\tawait runtime.commit(async (tx) => {\n\t\t\t\tconst slot = toolSlot(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\t\t\tif (slot !== undefined) slot.status = \"running\";\n\t\t\t\tconst intent = { phase: \"execute\", arguments: final, replay: tool.replay ?? \"unsafe\" } as const;\n\t\t\t\treturn { status: \"running\", checkpoint: intent };\n\t\t\t}, context);\n\t\t\tawait run(runtime, call, tool, final, context);\n\t\t},\n\t\t/** Recovery after intent: rerun only when the stored and the current policy both say `safe`. */\n\t\texecute: async (task, runtime, context) => {\n\t\t\tconst { arguments: args, replay } = task.state.checkpoint;\n\t\t\tconst call = await readCall(runtime, task.input, context);\n\t\t\tconst tool = (await runtime.agent(context)).tools.find((each) => each.name === call.name);\n\t\t\tif (replay === \"safe\" && tool?.replay === \"safe\") {\n\t\t\t\t// The rerun reports from scratch; clear what the interrupted attempt published.\n\t\t\t\tawait runtime.commit(async (tx) => {\n\t\t\t\t\tconst slot = toolSlot(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\t\t\t\tif (slot !== undefined) clearProgress(slot);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}, context);\n\t\t\t\treturn run(runtime, call, tool, args, context);\n\t\t\t}\n\t\t\tconst message = `Tool ${call.name} was interrupted and may have partially run`;\n\t\t\t// `failed` records cancellation intent, so the call's owned conversations, left unsupervised, are aborted.\n\t\t\tconst ending = { status: \"failed\", message } as const;\n\t\t\tawait settle(runtime, call, ending, (slot) => fromSlot(slot, \"interrupted\", message), context);\n\t\t},\n\t},\n\tabort: async (task, runtime, context) => {\n\t\tconst call = await readCall(runtime, task.input, context);\n\t\tconst message = `Tool ${call.name} was aborted`;\n\t\tawait settle(runtime, call, { status: \"aborted\" }, (slot) => fromSlot(slot, \"aborted\", message), context);\n\t},\n});\n\n/** The tool call `callId` of the assistant entry. */\nasync function readCall(runtime: Runtime, input: ToolTaskInput, context: Context): Promise<ToolCall> {\n\tconst entry = await runtime.entry(AssistantEntry, input.assistant, context);\n\tconst message = entry?.model?.[0];\n\tconst call =\n\t\tmessage?.role === \"assistant\"\n\t\t\t? message.content.find(\n\t\t\t\t\t(content): content is ToolCall => content.type === \"toolCall\" && content.id === input.callId,\n\t\t\t\t)\n\t\t\t: undefined;\n\tif (call === undefined) throw new Error(`Entry ${input.assistant} has no tool call ${input.callId}`);\n\treturn call;\n}\n\n/** Arguments, or why they are invalid. */\ntype Checked = { readonly args: JsonObject } | { readonly error: string };\n\n/** The call's arguments as repaired by the tool; a throwing repair makes them invalid. */\nfunction prepare(tool: ToolRegistration, args: JsonObject): Checked {\n\tif (tool.prepareArguments === undefined) return { args };\n\ttry {\n\t\treturn { args: tool.prepareArguments(args) as JsonObject };\n\t} catch (error) {\n\t\treturn { error: errorText(error) };\n\t}\n}\n\n/** Arguments validated and coerced against the implementation's schema. */\nfunction validate(tool: ToolRegistration, call: ToolCall, args: JsonObject): Checked {\n\ttry {\n\t\treturn { args: validateToolArguments(tool, { ...call, arguments: args }) as JsonObject };\n\t} catch (error) {\n\t\treturn { error: errorText(error) };\n\t}\n}\n\nfunction invalid(message: string): ToolExecutionResult {\n\treturn harnessError(\"invalid_arguments\", message);\n}\n\n/** What a running tool reported through its api: output, the last details, and diagnostics. */\ntype Reported = {\n\treadonly output: OutputBuffer;\n\treadonly limits: OutputLimits;\n\treadonly diagnostics: ToolDiagnostic[];\n\tdetails: JsonValue | undefined;\n};\n\n/** Execute with the resolved implementation, then settle its result. */\nasync function run(\n\truntime: Runtime,\n\tcall: ToolCall,\n\ttool: ToolRegistration,\n\targs: JsonObject,\n\tcontext: Context,\n): Promise<void> {\n\tconst limits: OutputLimits = {\n\t\tmaxBytes: tool.outputLimits?.maxBytes ?? DEFAULT_MAX_BYTES,\n\t\tmaxLines: tool.outputLimits?.maxLines ?? DEFAULT_MAX_LINES,\n\t\tretain: tool.outputLimits?.retain ?? \"head\",\n\t};\n\tconst reported: Reported = { output: new OutputBuffer(limits), limits, diagnostics: [], details: undefined };\n\tconst progress = publishProgress(runtime, reported, context);\n\tlet ended = false;\n\tconst assertLive = (): void => {\n\t\tif (ended) throw new Error(`Tool call ${call.id} has settled`);\n\t};\n\tconst api: Omit<ToolExecutionApi, \"env\"> = {\n\t\ttaskId: runtime.taskId,\n\t\tconversationId: runtime.conversationId,\n\t\tcallId: call.id,\n\t\tregistry: runtime.registry,\n\t\tagent: runtime.agent,\n\t\toutput: (chunk) => {\n\t\t\tassertLive();\n\t\t\tif (reported.output.push(chunk)) progress.mark();\n\t\t},\n\t\tdiagnostic: (diagnostic) => {\n\t\t\tassertLive();\n\t\t\treported.diagnostics.push(copyJson(diagnostic, { omitUndefinedProperties: true }) as ToolDiagnostic);\n\t\t\tprogress.mark();\n\t\t},\n\t\tdetails: async (value, detailsContext) => {\n\t\t\tassertLive();\n\t\t\tdetailsContext.abortSignal?.throwIfAborted();\n\t\t\treported.details = copyJson(value, { omitUndefinedProperties: true });\n\t\t\tconst committed = progress.markAndWait();\n\t\t\t// Cancelling the wait leaves the update in place; the commit's own outcome stays observed.\n\t\t\tcommitted.catch(() => {});\n\t\t\treturn awaitWithContext(committed, detailsContext);\n\t\t},\n\t\tcommit: async (change, commitContext) => {\n\t\t\tlet result: Awaited<ReturnType<typeof change>> | undefined;\n\t\t\tawait runtime.commit(async (tx) => {\n\t\t\t\tresult = await change(tx);\n\t\t\t\treturn undefined;\n\t\t\t}, commitContext);\n\t\t\treturn result as Awaited<ReturnType<typeof change>>;\n\t\t},\n\t\tmemo: runtime.memo,\n\t\tcreateTask: async <I, S extends { phase: string }, R, H extends object>(\n\t\t\ttask: Task<I, S, R, H>,\n\t\t\tinput: I,\n\t\t\toptions: Omit<TaskOptions, \"conversationId\">,\n\t\t\ttaskContext: Context,\n\t\t): Promise<TaskId<R>> => {\n\t\t\tlet id: TaskId<R> | undefined;\n\t\t\tawait runtime.commit(async (tx) => {\n\t\t\t\tid = await tx.createTask(task, input, options);\n\t\t\t\treturn undefined;\n\t\t\t}, taskContext);\n\t\t\treturn id!;\n\t\t},\n\t\tgetTask: runtime.getTask,\n\t\twaitForTask: runtime.waitForTask,\n\t\tconversation: runtime.conversation,\n\t\tsnapshot: runtime.snapshot,\n\t\tsnapshotAsOf: runtime.snapshotAsOf,\n\t\twatchDoc: runtime.watchDoc,\n\t};\n\n\tlet result: ToolExecutionResult;\n\tlet ending = COMPLETED;\n\ttry {\n\t\t// Built for this call, so a rerun after recovery gets the conversation's environment at that time.\n\t\tconst env = await runtime.env(context);\n\t\tresult = await tool.execute(args, { ...api, env }, context);\n\t} catch (error) {\n\t\tif (runtime.signal.aborted) {\n\t\t\tended = true;\n\t\t\tfor (const waiter of await progress.stop()) waiter.reject(error);\n\t\t\tthrow error;\n\t\t}\n\t\tresult = { isError: true, diagnostics: [toolDiagnostic(\"tool_error\", errorText(error))] };\n\t\t// A throw, from `execute()` or from building the environment, ends the task `failed`, which cancels what the call owned; it no longer supervises it. The error text\n\t\t// is already in the result entry.\n\t\tending = { status: \"failed\", message: `Tool ${call.name} threw` };\n\t}\n\tended = true;\n\treported.output.end();\n\t// Details still waiting for a progress commit settle with the terminal commit, the final flush.\n\tconst pending = await progress.stop();\n\ttry {\n\t\tconst settled = await finalResult(runtime, call, result, reported, context);\n\t\tawait settle(runtime, call, ending, () => settled, context);\n\t} catch (error) {\n\t\tfor (const waiter of pending) waiter.reject(error);\n\t\tthrow error;\n\t}\n\tfor (const waiter of pending) waiter.resolve();\n}\n\n/**\n * Throttled commits of what the tool reported into its `pi.live.tools` slot, each writing only what changed since the\n * last one.\n */\nfunction publishProgress(runtime: Runtime, reported: Reported, context: Context): Progress {\n\tlet written = { text: \"\", details: undefined as JsonValue | undefined, diagnostics: 0 };\n\treturn new Progress(\n\t\tasync () => {\n\t\t\t// Capture everything synchronously: the tool keeps reporting while the commit is in flight.\n\t\t\tconst snapshot = reported.output.snapshot();\n\t\t\tconst current = { text: snapshot.text, details: reported.details, diagnostics: reported.diagnostics.length };\n\t\t\tconst added = reported.diagnostics.slice(written.diagnostics, current.diagnostics);\n\t\t\tconst detailsChanged = current.details !== written.details;\n\t\t\t// What the commit writes, as Chord diffs the string: an append, a trim plus an append of what follows the shared\n\t\t\t// part, or the whole window when its bounded overlap search finds nothing.\n\t\t\tlet bytes = 0;\n\t\t\tif (snapshot.text !== written.text) {\n\t\t\t\tconst shared = snapshot.text.startsWith(written.text)\n\t\t\t\t\t? written.text.length\n\t\t\t\t\t: overlap(written.text, snapshot.text, 65_536);\n\t\t\t\tbytes += utf8ByteLength(snapshot.text.slice(shared));\n\t\t\t}\n\t\t\tif (detailsChanged) bytes += utf8ByteLength(JSON.stringify(current.details ?? null));\n\t\t\tif (added.length > 0) bytes += utf8ByteLength(JSON.stringify(added));\n\t\t\tawait runtime.commit(async (tx) => {\n\t\t\t\tconst slot = toolSlot(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\t\t\tif (slot === undefined) return undefined;\n\t\t\t\t// REMINDER: assign `output` as one string field. Chord then diffs it into an append, or a trim plus an\n\t\t\t\t// append for a sliding tail; replacing the slot object would record the whole window on every commit.\n\t\t\t\tif ((slot.output ?? \"\") !== snapshot.text) slot.output = snapshot.text;\n\t\t\t\tif (snapshot.droppedBytes > 0) slot.droppedBytes = snapshot.droppedBytes;\n\t\t\t\tif (snapshot.droppedLines > 0) slot.droppedLines = snapshot.droppedLines;\n\t\t\t\t// Diff details leaf by leaf and append new diagnostics, so each commit writes only what changed.\n\t\t\t\tif (detailsChanged && current.details !== undefined) {\n\t\t\t\t\tassignJson(slot as unknown as Record<string, JsonValue>, \"details\", current.details);\n\t\t\t\t}\n\t\t\t\tif (added.length > 0) {\n\t\t\t\t\tif (slot.diagnostics === undefined) slot.diagnostics = [];\n\t\t\t\t\tfor (const diagnostic of added) slot.diagnostics.push(diagnostic);\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t}, context);\n\t\t\twritten = current;\n\t\t\treturn bytes;\n\t\t},\n\t\t(error) => {\n\t\t\t// Rejections after an abort mark or close are expected; the committed state stays consistent.\n\t\t\tif (!runtime.signal.aborted) runtime.report(error);\n\t\t},\n\t);\n}\n\n/**\n * The settled result: the tool's result with the retained output and last details as fallbacks, its diagnostics after\n * those reported through the api, `afterTool` applied, and explicit text bounded, with the Harness's truncation\n * diagnostic last.\n */\nasync function finalResult(\n\truntime: Runtime,\n\tcall: ToolCall,\n\tresult: ToolExecutionResult,\n\treported: Reported,\n\tcontext: Context,\n): Promise<ToolExecutionResult> {\n\tconst harness: ToolDiagnostic[] = [];\n\tconst retained = result.content === undefined ? reported.output.snapshot() : undefined;\n\tconst content: Content =\n\t\tretained === undefined ? result.content! : retained.text === \"\" ? [] : [{ type: \"text\", text: retained.text }];\n\tlet final: ToolExecutionResult = {\n\t\t...result,\n\t\tcontent,\n\t\tdetails: result.details === undefined ? reported.details : result.details,\n\t\tdiagnostics: [...reported.diagnostics, ...(result.diagnostics ?? [])],\n\t};\n\tawait runtime.hooks.each(\"afterTool\", async (hook) => {\n\t\tfinal = (await hook(call, final, runtime, context)) ?? final;\n\t});\n\t// The retained output's truncation applies only while afterTool kept that content.\n\tif (retained !== undefined && retained.droppedBytes > 0 && final.content === content) {\n\t\tharness.push(truncated(retained, reported.limits.retain));\n\t}\n\tconst bounded = boundContent(final.content ?? [], reported.limits);\n\tif (bounded.droppedBytes > 0) harness.push(truncated(bounded, reported.limits.retain));\n\treturn { ...final, content: bounded.content, diagnostics: [...(final.diagnostics ?? []), ...harness] };\n}\n\n/**\n * Commit the tool's terminal state: append its result entry, mark its slot done, and complete or end aborted with the\n * entry ID. `build` receives the slot so interruption and abort can report the durable partial output.\n */\nasync function settle(\n\truntime: Runtime,\n\tcall: ToolCall,\n\tending: Ending,\n\tbuild: (slot: Readonly<ToolSlot> | undefined) => ToolExecutionResult,\n\tcontext: Context,\n): Promise<void> {\n\tawait runtime.commit(async (tx) => {\n\t\tconst slot = toolSlot(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\tconst result = build(slot);\n\t\tconst entry = await appendToolResult(tx, runtime.conversationId, call, result, runtime.now());\n\t\tif (slot !== undefined) finishSlot(slot, entry.id);\n\t\tconst entryId = entry.id;\n\t\tif (ending.status === \"aborted\")\n\t\t\treturn { status: \"terminal\", outcome: { status: \"aborted\", result: { entryId } } };\n\t\tif (ending.status === \"failed\") {\n\t\t\tconst error = { message: ending.message };\n\t\t\treturn { status: \"terminal\", outcome: { status: \"failed\", error, result: { entryId } } };\n\t\t}\n\t\t// Tools build control objects freely; drop keys set to undefined so the task result is strict JSON.\n\t\tconst control =\n\t\t\tresult.control === undefined\n\t\t\t\t? {}\n\t\t\t\t: { control: copyJson(result.control as JsonValue, { omitUndefinedProperties: true }) as ToolControl };\n\t\treturn { status: \"terminal\", outcome: { status: \"completed\", result: { entryId, ...control } } };\n\t}, context);\n}\n\n/**\n * How a tool task ends; the result entry is appended either way. `failed` (execution threw or was interrupted)\n * records cancellation intent for the conversations the call owns; a result with `isError` still completes.\n */\ntype Ending = { readonly status: \"completed\" | \"aborted\" } | { readonly status: \"failed\"; readonly message: string };\n\nconst COMPLETED: Ending = { status: \"completed\" };\n\n/** An error result from the slot's durable partial output, details, and diagnostics. */\nfunction fromSlot(slot: Readonly<ToolSlot> | undefined, code: string, message: string): ToolExecutionResult {\n\tconst diagnostics = [...(slot?.diagnostics ?? [])];\n\tconst droppedBytes = slot?.droppedBytes ?? 0;\n\tif (droppedBytes > 0) diagnostics.push(truncated({ droppedBytes, droppedLines: slot?.droppedLines ?? 0 }));\n\tdiagnostics.push(toolDiagnostic(code, message));\n\treturn {\n\t\tcontent: slot?.output === undefined || slot.output === \"\" ? [] : [{ type: \"text\", text: slot.output }],\n\t\tisError: true,\n\t\t...(slot?.details === undefined ? {} : { details: slot.details }),\n\t\tdiagnostics,\n\t};\n}\n\n/** An error result the Harness writes itself: no content and one `error` diagnostic with `code`. */\nexport function harnessError(code: string, message: string): ToolExecutionResult {\n\treturn { content: [], isError: true, diagnostics: [toolDiagnostic(code, message)] };\n}\n\nfunction toolDiagnostic(code: string, message: string): ToolDiagnostic {\n\treturn { severity: \"error\", code, message };\n}\n\n/** The Harness's truncation diagnostic; `retain` is unknown when rebuilt from a slot after recovery. */\nfunction truncated(\n\tdropped: { readonly droppedLines: number; readonly droppedBytes: number },\n\tretain?: \"head\" | \"tail\",\n): ToolDiagnostic {\n\tconst kept = retain === undefined ? \"\" : ` to its ${retain === \"head\" ? \"beginning\" : \"end\"}`;\n\treturn {\n\t\tseverity: \"warn\",\n\t\tcode: \"truncated\",\n\t\tmessage: `Output truncated${kept}: ${dropped.droppedLines} lines, ${dropped.droppedBytes} bytes dropped`,\n\t};\n}\n\n/**\n * Append a `pi.tool-result` entry. The content ends with the rendered diagnostics, so the stored message is exactly\n * what the model sees; `data` keeps the structured list. A result's usage is added to `pi.usage` in the same commit.\n */\nexport async function appendToolResult(\n\ttx: Tx,\n\tconversationId: ConversationId,\n\tcall: ToolCall,\n\tresult: ToolExecutionResult,\n\ttimestamp: number,\n): Promise<TypedEntry<{ diagnostics: ToolDiagnostic[] }>> {\n\tconst diagnostics = [...(result.diagnostics ?? [])];\n\tconst content: Content = [...(result.content ?? [])];\n\tif (diagnostics.length > 0) content.push({ type: \"text\", text: renderDiagnostics(diagnostics) });\n\tconst message = {\n\t\trole: \"toolResult\",\n\t\ttoolCallId: call.id,\n\t\ttoolName: call.name,\n\t\tcontent,\n\t\t...(result.details === undefined ? {} : { details: result.details }),\n\t\t...(result.usage === undefined ? {} : { usage: result.usage }),\n\t\tisError: result.isError ?? false,\n\t\ttimestamp,\n\t} as ToolResultMessage;\n\tif (result.usage !== undefined) await recordUsage(tx, conversationId, \"tools\", call.name, result.usage);\n\treturn tx.appendEntry(ToolResultEntry, conversationId, { model: [message], data: { diagnostics } });\n}\n\nfunction renderDiagnostics(diagnostics: readonly ToolDiagnostic[]): string {\n\treturn `<harness>\\n${diagnostics.map((diagnostic) => `[${diagnostic.severity}] ${diagnostic.message}`).join(\"\\n\")}\\n</harness>`;\n}\n\n/**\n * Bound the text of result content. When the joined text exceeds the limits, the text items are replaced by one bounded\n * item at the position of the first (head) or last (tail) text item; other content is kept.\n */\nfunction boundContent(\n\tcontent: Content,\n\tlimits: OutputLimits,\n): { content: Content; droppedBytes: number; droppedLines: number } {\n\tconst texts = content.filter((item): item is TextContent => item.type === \"text\");\n\tconst bounded = boundOutput(texts.map((item) => item.text).join(\"\"), limits);\n\tif (bounded.droppedBytes === 0) return { content, droppedBytes: 0, droppedLines: 0 };\n\tconst keep = limits.retain === \"head\" ? texts[0] : texts.at(-1);\n\tconst result: Content = [];\n\tfor (const item of content) {\n\t\tif (item.type !== \"text\") result.push(item);\n\t\telse if (item === keep) result.push({ ...item, text: bounded.text });\n\t}\n\treturn { content: result, droppedBytes: bounded.droppedBytes, droppedLines: bounded.droppedLines };\n}\n\nfunction errorText(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n"]}