@astralyn/pi 1.4.8 → 1.4.10

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 (243) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/bundle/chunks/{chunk-I2COK45I.js → chunk-6GTUUUCJ.js} +128 -128
  3. package/dist/bundle/chunks/{chunk-PU2IWWXV.js → chunk-DLVLAQEG.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-MG5XDTXV.js → chunk-LPF7HQ3S.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-FODCTZXW.js → chunk-MF3T3ETJ.js} +4 -4
  6. package/dist/bundle/chunks/{chunk-RO2VH6UZ.js → chunk-R267VV5T.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-RLNMABLM.js → chunk-RVKT7OUB.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-5VIOGR76.js → chunk-UCH27AVQ.js} +1 -1
  9. package/dist/bundle/chunks/{cli-MEL7SGJD.js → cli-MBFCFGI2.js} +1 -1
  10. package/dist/bundle/chunks/{execute-4CWRNHPO.js → execute-FQKRO4XJ.js} +1 -1
  11. package/dist/bundle/chunks/{runtime-R3X67XMR.js → runtime-3RPOJS5T.js} +1 -1
  12. package/dist/bundle/chunks/{virtual-modules-BBLD7YLS.js → virtual-modules-6IJ6V2CG.js} +1 -1
  13. package/dist/bundle/cli.js +1 -1
  14. package/dist/bundle/index.js +1 -1
  15. package/dist/bundle/rpc-entry.js +1 -1
  16. package/dist/core/agent-session.d.ts +34 -0
  17. package/dist/core/agent-session.d.ts.map +1 -1
  18. package/dist/core/agent-session.js +99 -2
  19. package/dist/core/agent-session.js.map +1 -1
  20. package/dist/core/isolated-session.d.ts +7 -2
  21. package/dist/core/isolated-session.d.ts.map +1 -1
  22. package/dist/core/isolated-session.js +7 -3
  23. package/dist/core/isolated-session.js.map +1 -1
  24. package/dist/core/session-manager.d.ts +17 -1
  25. package/dist/core/session-manager.d.ts.map +1 -1
  26. package/dist/core/session-manager.js +29 -0
  27. package/dist/core/session-manager.js.map +1 -1
  28. package/dist/core/tasks/delivery.d.ts +42 -7
  29. package/dist/core/tasks/delivery.d.ts.map +1 -1
  30. package/dist/core/tasks/delivery.js +129 -15
  31. package/dist/core/tasks/delivery.js.map +1 -1
  32. package/dist/core/tasks/execution.d.ts +27 -14
  33. package/dist/core/tasks/execution.d.ts.map +1 -1
  34. package/dist/core/tasks/execution.js +90 -86
  35. package/dist/core/tasks/execution.js.map +1 -1
  36. package/dist/core/tasks/limits.d.ts +13 -1
  37. package/dist/core/tasks/limits.d.ts.map +1 -1
  38. package/dist/core/tasks/limits.js +14 -1
  39. package/dist/core/tasks/limits.js.map +1 -1
  40. package/dist/core/tasks/output.d.ts +9 -1
  41. package/dist/core/tasks/output.d.ts.map +1 -1
  42. package/dist/core/tasks/output.js +23 -10
  43. package/dist/core/tasks/output.js.map +1 -1
  44. package/dist/core/tasks/presentation.d.ts +1 -1
  45. package/dist/core/tasks/presentation.d.ts.map +1 -1
  46. package/dist/core/tasks/presentation.js +6 -4
  47. package/dist/core/tasks/presentation.js.map +1 -1
  48. package/dist/core/tasks/record.d.ts +41 -29
  49. package/dist/core/tasks/record.d.ts.map +1 -1
  50. package/dist/core/tasks/record.js +82 -37
  51. package/dist/core/tasks/record.js.map +1 -1
  52. package/dist/core/tasks/reminder.js +2 -2
  53. package/dist/core/tasks/reminder.js.map +1 -1
  54. package/dist/core/tasks/runtime.d.ts +17 -14
  55. package/dist/core/tasks/runtime.d.ts.map +1 -1
  56. package/dist/core/tasks/runtime.js +68 -116
  57. package/dist/core/tasks/runtime.js.map +1 -1
  58. package/dist/core/tasks/scheduler.d.ts +3 -1
  59. package/dist/core/tasks/scheduler.d.ts.map +1 -1
  60. package/dist/core/tasks/scheduler.js +16 -12
  61. package/dist/core/tasks/scheduler.js.map +1 -1
  62. package/dist/core/tasks/session.d.ts +24 -3
  63. package/dist/core/tasks/session.d.ts.map +1 -1
  64. package/dist/core/tasks/session.js +73 -9
  65. package/dist/core/tasks/session.js.map +1 -1
  66. package/dist/core/tasks/store.d.ts +17 -4
  67. package/dist/core/tasks/store.d.ts.map +1 -1
  68. package/dist/core/tasks/store.js +45 -48
  69. package/dist/core/tasks/store.js.map +1 -1
  70. package/dist/core/tasks/types.d.ts +72 -29
  71. package/dist/core/tasks/types.d.ts.map +1 -1
  72. package/dist/core/tasks/types.js +7 -0
  73. package/dist/core/tasks/types.js.map +1 -1
  74. package/dist/core/tasks/wait.d.ts +19 -0
  75. package/dist/core/tasks/wait.d.ts.map +1 -0
  76. package/dist/core/tasks/wait.js +45 -0
  77. package/dist/core/tasks/wait.js.map +1 -0
  78. package/dist/core/tools/shell-execution.d.ts.map +1 -1
  79. package/dist/core/tools/shell-execution.js +2 -1
  80. package/dist/core/tools/shell-execution.js.map +1 -1
  81. package/dist/core/tools/shell-tool.d.ts.map +1 -1
  82. package/dist/core/tools/shell-tool.js +20 -13
  83. package/dist/core/tools/shell-tool.js.map +1 -1
  84. package/dist/extensions/agent/activity-lines.d.ts +10 -0
  85. package/dist/extensions/agent/activity-lines.d.ts.map +1 -0
  86. package/dist/extensions/agent/activity-lines.js +76 -0
  87. package/dist/extensions/agent/activity-lines.js.map +1 -0
  88. package/dist/extensions/agent/activity.d.ts +25 -4
  89. package/dist/extensions/agent/activity.d.ts.map +1 -1
  90. package/dist/extensions/agent/activity.js +76 -21
  91. package/dist/extensions/agent/activity.js.map +1 -1
  92. package/dist/extensions/agent/artifacts.d.ts +15 -0
  93. package/dist/extensions/agent/artifacts.d.ts.map +1 -0
  94. package/dist/extensions/agent/artifacts.js +44 -0
  95. package/dist/extensions/agent/artifacts.js.map +1 -0
  96. package/dist/extensions/agent/progress.d.ts +30 -4
  97. package/dist/extensions/agent/progress.d.ts.map +1 -1
  98. package/dist/extensions/agent/progress.js +84 -11
  99. package/dist/extensions/agent/progress.js.map +1 -1
  100. package/dist/extensions/agent/render.d.ts.map +1 -1
  101. package/dist/extensions/agent/render.js +8 -24
  102. package/dist/extensions/agent/render.js.map +1 -1
  103. package/dist/extensions/agent/report.d.ts +11 -3
  104. package/dist/extensions/agent/report.d.ts.map +1 -1
  105. package/dist/extensions/agent/report.js +32 -7
  106. package/dist/extensions/agent/report.js.map +1 -1
  107. package/dist/extensions/agent/tool.d.ts.map +1 -1
  108. package/dist/extensions/agent/tool.js +20 -5
  109. package/dist/extensions/agent/tool.js.map +1 -1
  110. package/dist/extensions/agent/types.d.ts +2 -0
  111. package/dist/extensions/agent/types.d.ts.map +1 -1
  112. package/dist/extensions/agent/types.js.map +1 -1
  113. package/dist/extensions/agent/worker.d.ts +7 -0
  114. package/dist/extensions/agent/worker.d.ts.map +1 -1
  115. package/dist/extensions/agent/worker.js +9 -3
  116. package/dist/extensions/agent/worker.js.map +1 -1
  117. package/dist/extensions/index.d.ts.map +1 -1
  118. package/dist/extensions/index.js +2 -0
  119. package/dist/extensions/index.js.map +1 -1
  120. package/dist/extensions/search/index.d.ts.map +1 -1
  121. package/dist/extensions/search/index.js +6 -7
  122. package/dist/extensions/search/index.js.map +1 -1
  123. package/dist/extensions/shell-paths/index.d.ts +8 -0
  124. package/dist/extensions/shell-paths/index.d.ts.map +1 -0
  125. package/dist/extensions/shell-paths/index.js +74 -0
  126. package/dist/extensions/shell-paths/index.js.map +1 -0
  127. package/dist/extensions/tasks/tool/actions.d.ts.map +1 -1
  128. package/dist/extensions/tasks/tool/actions.js +15 -11
  129. package/dist/extensions/tasks/tool/actions.js.map +1 -1
  130. package/dist/extensions/tasks/tool/schema.d.ts +1 -1
  131. package/dist/extensions/tasks/tool/schema.d.ts.map +1 -1
  132. package/dist/extensions/tasks/tool/schema.js +5 -5
  133. package/dist/extensions/tasks/tool/schema.js.map +1 -1
  134. package/dist/extensions/tasks/tool/types.d.ts +2 -0
  135. package/dist/extensions/tasks/tool/types.d.ts.map +1 -1
  136. package/dist/extensions/tasks/tool/types.js.map +1 -1
  137. package/dist/extensions/tasks/tool.d.ts.map +1 -1
  138. package/dist/extensions/tasks/tool.js +3 -2
  139. package/dist/extensions/tasks/tool.js.map +1 -1
  140. package/dist/extensions/tasks/ui/completion-render.d.ts.map +1 -1
  141. package/dist/extensions/tasks/ui/completion-render.js +2 -2
  142. package/dist/extensions/tasks/ui/completion-render.js.map +1 -1
  143. package/dist/extensions/tasks/ui/footer.d.ts +37 -0
  144. package/dist/extensions/tasks/ui/footer.d.ts.map +1 -0
  145. package/dist/extensions/tasks/ui/footer.js +129 -0
  146. package/dist/extensions/tasks/ui/footer.js.map +1 -0
  147. package/dist/extensions/tasks/ui/frame.d.ts +36 -0
  148. package/dist/extensions/tasks/ui/frame.d.ts.map +1 -0
  149. package/dist/extensions/tasks/ui/frame.js +33 -0
  150. package/dist/extensions/tasks/ui/frame.js.map +1 -0
  151. package/dist/extensions/tasks/ui/help.d.ts +6 -0
  152. package/dist/extensions/tasks/ui/help.d.ts.map +1 -0
  153. package/dist/extensions/tasks/ui/help.js +53 -0
  154. package/dist/extensions/tasks/ui/help.js.map +1 -0
  155. package/dist/extensions/tasks/ui/inspector.d.ts +4 -3
  156. package/dist/extensions/tasks/ui/inspector.d.ts.map +1 -1
  157. package/dist/extensions/tasks/ui/inspector.js +25 -6
  158. package/dist/extensions/tasks/ui/inspector.js.map +1 -1
  159. package/dist/extensions/tasks/ui/layout.d.ts +15 -18
  160. package/dist/extensions/tasks/ui/layout.d.ts.map +1 -1
  161. package/dist/extensions/tasks/ui/layout.js +162 -255
  162. package/dist/extensions/tasks/ui/layout.js.map +1 -1
  163. package/dist/extensions/tasks/ui/list.d.ts +3 -1
  164. package/dist/extensions/tasks/ui/list.d.ts.map +1 -1
  165. package/dist/extensions/tasks/ui/list.js +19 -14
  166. package/dist/extensions/tasks/ui/list.js.map +1 -1
  167. package/dist/extensions/tasks/ui/manager.d.ts +17 -5
  168. package/dist/extensions/tasks/ui/manager.d.ts.map +1 -1
  169. package/dist/extensions/tasks/ui/manager.js +147 -90
  170. package/dist/extensions/tasks/ui/manager.js.map +1 -1
  171. package/dist/extensions/tasks/ui/model.d.ts +32 -5
  172. package/dist/extensions/tasks/ui/model.d.ts.map +1 -1
  173. package/dist/extensions/tasks/ui/model.js +7 -7
  174. package/dist/extensions/tasks/ui/model.js.map +1 -1
  175. package/dist/extensions/tasks/ui/panes.d.ts +40 -0
  176. package/dist/extensions/tasks/ui/panes.d.ts.map +1 -0
  177. package/dist/extensions/tasks/ui/panes.js +144 -0
  178. package/dist/extensions/tasks/ui/panes.js.map +1 -0
  179. package/dist/extensions/tasks/ui/render.d.ts.map +1 -1
  180. package/dist/extensions/tasks/ui/render.js +11 -7
  181. package/dist/extensions/tasks/ui/render.js.map +1 -1
  182. package/dist/extensions/tasks/ui/viewport.d.ts +12 -4
  183. package/dist/extensions/tasks/ui/viewport.d.ts.map +1 -1
  184. package/dist/extensions/tasks/ui/viewport.js +23 -7
  185. package/dist/extensions/tasks/ui/viewport.js.map +1 -1
  186. package/dist/extensions/tasks/ui/views.d.ts +12 -6
  187. package/dist/extensions/tasks/ui/views.d.ts.map +1 -1
  188. package/dist/extensions/tasks/ui/views.js +39 -32
  189. package/dist/extensions/tasks/ui/views.js.map +1 -1
  190. package/dist/modes/interactive/chat-viewport.d.ts +5 -2
  191. package/dist/modes/interactive/chat-viewport.d.ts.map +1 -1
  192. package/dist/modes/interactive/chat-viewport.js +72 -12
  193. package/dist/modes/interactive/chat-viewport.js.map +1 -1
  194. package/dist/modes/interactive/components/session-selector.d.ts +11 -1
  195. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  196. package/dist/modes/interactive/components/session-selector.js +43 -17
  197. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  198. package/dist/modes/interactive/components/sticky-user-message.d.ts +23 -0
  199. package/dist/modes/interactive/components/sticky-user-message.d.ts.map +1 -0
  200. package/dist/modes/interactive/components/sticky-user-message.js +58 -0
  201. package/dist/modes/interactive/components/sticky-user-message.js.map +1 -0
  202. package/dist/modes/interactive/components/user-message.d.ts +9 -0
  203. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  204. package/dist/modes/interactive/components/user-message.js +51 -10
  205. package/dist/modes/interactive/components/user-message.js.map +1 -1
  206. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  207. package/dist/modes/interactive/interactive-mode.js +4 -1
  208. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  209. package/dist/modes/interactive/tool-view/chat.d.ts +3 -2
  210. package/dist/modes/interactive/tool-view/chat.d.ts.map +1 -1
  211. package/dist/modes/interactive/tool-view/chat.js +3 -3
  212. package/dist/modes/interactive/tool-view/chat.js.map +1 -1
  213. package/dist/modes/interactive/transcript-layout.d.ts +23 -0
  214. package/dist/modes/interactive/transcript-layout.d.ts.map +1 -0
  215. package/dist/modes/interactive/transcript-layout.js +29 -0
  216. package/dist/modes/interactive/transcript-layout.js.map +1 -0
  217. package/dist/modes/print-mode.d.ts.map +1 -1
  218. package/dist/modes/print-mode.js +37 -2
  219. package/dist/modes/print-mode.js.map +1 -1
  220. package/dist/modes/rpc/rpc-client.d.ts +11 -0
  221. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  222. package/dist/modes/rpc/rpc-client.js +48 -0
  223. package/dist/modes/rpc/rpc-client.js.map +1 -1
  224. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  225. package/dist/modes/rpc/rpc-mode.js +1 -0
  226. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  227. package/dist/modes/rpc/rpc-types.d.ts +2 -0
  228. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  229. package/dist/modes/rpc/rpc-types.js.map +1 -1
  230. package/docs/bundled/README.md +1 -0
  231. package/docs/bundled/extensions/agent.md +39 -7
  232. package/docs/bundled/extensions/shell-paths.md +17 -0
  233. package/docs/bundled/tasks.md +25 -21
  234. package/docs/docs.json +4 -0
  235. package/docs/extensions.md +14 -10
  236. package/docs/json.md +3 -0
  237. package/docs/rpc.md +14 -0
  238. package/docs/sdk.md +11 -1
  239. package/docs/session-format.md +10 -4
  240. package/docs/sessions.md +1 -1
  241. package/docs/usage.md +3 -1
  242. package/npm-shrinkwrap.json +2 -2
  243. package/package.json +1 -1
@@ -14,33 +14,34 @@ function mergeFacts(base = [], terminal) {
14
14
  * One live task: its caller, controller, parent link and executor callbacks.
15
15
  *
16
16
  * A caller may be handed off to the background whenever no executor preflight is in
17
- * progress: before `run` starts (queued) or after the executor calls `accept()`.
17
+ * progress: before `run` starts (queued) or after the executor calls `accept()`. A release
18
+ * requested during preflight waits for `accept()`; if the run settles first, the caller is
19
+ * answered inline and the task never becomes background.
18
20
  */
19
21
  export class Execution {
20
22
  constructor(host, record, execution) {
21
23
  this.controller = new AbortController();
22
24
  this.modeListeners = new Set();
23
25
  this.releaseParentSignal = () => { };
24
- this.started = false;
25
- this.accepted = false;
26
- this.handedOff = false;
26
+ this.phase = "queued";
27
27
  this.host = host;
28
28
  this.record = record;
29
29
  this.run = execution.run;
30
+ this.kind = execution.kind;
30
31
  this.concurrency = execution.concurrency;
31
- this.detachRequested = execution.background ?? false;
32
- this.onUpdate = execution.background ? undefined : execution.onUpdate;
32
+ this.placement = execution.background ? "detaching" : "caller";
33
+ this.reason = execution.background ? "submitted" : "detached";
34
+ this.onUpdate = execution.onUpdate;
33
35
  this.caller = new Promise((resolve, reject) => {
34
36
  this.resolveCaller = resolve;
35
37
  this.rejectCaller = reject;
36
38
  });
37
- this.done = new Promise((resolve) => {
38
- this.resolveDone = resolve;
39
- });
39
+ this.done = record.settlement;
40
40
  const parent = execution.signal;
41
41
  if (parent) {
42
+ // A caller that is leaving survives the parent's abort unless the executor is mid-preflight.
42
43
  const abort = () => {
43
- if (!this.detachRequested || !this.handoffAllowed)
44
+ if (this.placement === "caller" || this.phase === "preflight")
44
45
  this.cancel();
45
46
  };
46
47
  parent.addEventListener("abort", abort, { once: true });
@@ -68,16 +69,16 @@ export class Execution {
68
69
  get aborted() {
69
70
  return this.controller.signal.aborted;
70
71
  }
71
- /** Executor preflight never races a handoff; queued work has not begun any. */
72
- get handoffAllowed() {
73
- return !this.started || this.accepted;
74
- }
75
72
  /** Waiting for a concurrency slot: registered, but `run` has not started. */
76
73
  get queued() {
77
- return !this.started && !this.record.settled;
74
+ return this.phase === "queued";
75
+ }
76
+ /** Holds a background slot: handed off, or asked to be and waiting for the executor to accept. */
77
+ get holdsBackgroundSlot() {
78
+ return this.phase !== "settled" && this.placement !== "caller";
78
79
  }
79
80
  get detachable() {
80
- return !this.record.settled && !this.detachRequested && !this.aborted;
81
+ return this.phase !== "settled" && this.placement === "caller" && !this.aborted;
81
82
  }
82
83
  /** Stop forwarding to the launching tool call: parent cancellation and progress no longer reach it. */
83
84
  releaseParent() {
@@ -86,13 +87,13 @@ export class Execution {
86
87
  this.onUpdate = undefined;
87
88
  }
88
89
  start() {
89
- if (this.started || this.record.settled)
90
+ if (this.phase !== "queued")
90
91
  return;
91
92
  if (this.host.closed) {
92
93
  this.cancel();
93
94
  return;
94
95
  }
95
- this.started = true;
96
+ this.phase = "preflight";
96
97
  this.record.update((task) => {
97
98
  task.status = "running";
98
99
  });
@@ -105,56 +106,38 @@ export class Execution {
105
106
  this.finish(undefined, error);
106
107
  }
107
108
  }
108
- handoff() {
109
- if (!this.handoffAllowed ||
110
- !this.detachRequested ||
111
- this.record.settled ||
112
- this.handedOff ||
113
- this.host.closed ||
114
- !this.record.visible ||
115
- this.aborted)
116
- return;
117
- this.handedOff = true;
118
- this.record.update((task) => {
119
- task.mode = "background";
120
- });
121
- this.releaseParent();
122
- this.resolveCaller({ kind: "background", task: this.record.snapshot() });
123
- this.host.emit();
124
- }
125
- requestDetach() {
126
- this.detachRequested = true;
127
- this.record.update((task) => {
128
- task.mode = "background";
129
- });
130
- if (this.accepted)
131
- this.releaseParent();
109
+ /** Queued background work returns at once; started work is released at its own `accept()`. */
110
+ handOffIfQueued() {
111
+ if (this.phase === "queued" && this.handoff())
112
+ this.announceBackground();
132
113
  }
133
- completeDetach() {
134
- this.handoff();
135
- for (const listener of this.modeListeners) {
136
- try {
137
- listener(this.task.mode);
138
- }
139
- catch {
140
- /* Executors own their diagnostics. */
141
- }
142
- }
114
+ /**
115
+ * Ask to move the caller to the background. The placement changes now; the returned
116
+ * notification is for the caller of a batch to run after every placement has changed, so no
117
+ * listener ever observes a half-moved set.
118
+ */
119
+ detach() {
120
+ this.placement = "detaching";
121
+ const released = this.handoff();
122
+ return () => {
123
+ if (released)
124
+ this.announceBackground();
125
+ };
143
126
  }
144
127
  cancel() {
145
- if (this.record.settled || this.aborted)
128
+ if (this.phase === "settled" || this.aborted)
146
129
  return false;
147
130
  this.record.update((task) => {
148
131
  task.status = "stopping";
149
132
  });
150
133
  this.controller.abort();
151
- if (!this.started)
134
+ if (this.phase === "queued")
152
135
  this.finish(undefined, new Error("Task cancelled before it started."));
153
136
  this.host.emit();
154
137
  return true;
155
138
  }
156
139
  onModeChange(listener) {
157
- if (this.record.settled || this.host.closed)
140
+ if (this.phase === "settled" || this.host.closed)
158
141
  return () => { };
159
142
  this.modeListeners.add(listener);
160
143
  return () => {
@@ -162,10 +145,10 @@ export class Execution {
162
145
  };
163
146
  }
164
147
  accept() {
165
- if (this.record.settled || this.accepted || this.host.closed || this.aborted)
148
+ if (this.phase !== "preflight" || this.host.closed || this.aborted)
166
149
  return;
167
- this.accepted = true;
168
- if (this.detachRequested)
150
+ this.phase = "accepted";
151
+ if (this.placement === "detaching")
169
152
  this.releaseParent();
170
153
  // An already available final result wins over a handoff, so decide only once the
171
154
  // executor's own synchronous code has yielded.
@@ -173,9 +156,10 @@ export class Execution {
173
156
  this.host.emit();
174
157
  }
175
158
  /**
176
- * Hand off unless `run` has already settled. A reaction on an already settled promise is
177
- * queued at once, ahead of the pending-check below whatever the promise's chain depth, so
178
- * the outcome never depends on how `start()` chains its own handlers.
159
+ * Hand off unless `run` has already settled. This observes the `run` promise's own settlement,
160
+ * not the phase `finish()` sets: a reaction on an already settled promise is queued at once,
161
+ * ahead of the pending-check below whatever the promise's chain depth, so the outcome never
162
+ * depends on how many hops `start()` chains before `finish()`.
179
163
  */
180
164
  handoffIfRunning() {
181
165
  let settled = false;
@@ -184,12 +168,42 @@ export class Execution {
184
168
  };
185
169
  this.running?.then(markSettled, markSettled);
186
170
  void Promise.resolve().then(() => {
187
- if (!settled)
188
- this.handoff();
171
+ if (!settled && this.handoff())
172
+ this.announceBackground();
189
173
  });
190
174
  }
175
+ /**
176
+ * Release the caller with a background outcome if it asked to leave and the executor's
177
+ * preflight is over. The task is background exactly from here on.
178
+ */
179
+ handoff() {
180
+ if (this.placement !== "detaching" ||
181
+ (this.phase !== "queued" && this.phase !== "accepted") ||
182
+ this.host.closed ||
183
+ !this.record.visible ||
184
+ this.aborted)
185
+ return false;
186
+ this.placement = "background";
187
+ this.record.update((task) => {
188
+ task.mode = "background";
189
+ });
190
+ this.releaseParent();
191
+ this.resolveCaller({ kind: "background", task: this.record.snapshot(), reason: this.reason });
192
+ return true;
193
+ }
194
+ announceBackground() {
195
+ for (const listener of [...this.modeListeners]) {
196
+ try {
197
+ listener("background");
198
+ }
199
+ catch {
200
+ /* Executors own their diagnostics. */
201
+ }
202
+ }
203
+ this.host.emit();
204
+ }
191
205
  publish(update) {
192
- if (this.record.settled || this.host.closed)
206
+ if (this.phase === "settled" || this.host.closed)
193
207
  return;
194
208
  this.record.update((task) => {
195
209
  task.result = resultText(update.result);
@@ -198,7 +212,7 @@ export class Execution {
198
212
  });
199
213
  if (update.usage !== undefined)
200
214
  this.usage = structuredClone(update.usage);
201
- if (!this.detachRequested && !this.handedOff) {
215
+ if (this.placement === "caller") {
202
216
  try {
203
217
  this.onUpdate?.(update.result);
204
218
  }
@@ -209,27 +223,22 @@ export class Execution {
209
223
  this.host.emit();
210
224
  }
211
225
  attachOutput(path, cleanup) {
212
- if (this.record.settled) {
226
+ if (this.phase === "settled") {
213
227
  if (cleanup)
214
228
  throw new Error("Task execution has settled");
215
229
  return;
216
230
  }
217
- if (this.record.cleanup)
218
- throw new Error("Managed output is already registered");
219
231
  // Never truncate a real filesystem path into a different path.
220
232
  if (Buffer.byteLength(path) > TASK_PATH_BYTES)
221
233
  throw new Error("Task output path is too large");
222
- this.record.cleanup = cleanup;
223
- this.record.update((task) => {
224
- task.outputPath = path;
225
- });
234
+ this.record.attachOutput(path, cleanup);
226
235
  this.host.emit();
227
236
  }
228
237
  finish(completion, error) {
229
238
  const record = this.record;
230
- if (record.settled)
239
+ if (this.phase === "settled")
231
240
  return;
232
- record.settled = true;
241
+ this.phase = "settled";
233
242
  this.modeListeners.clear();
234
243
  this.releaseParent();
235
244
  const status = completion
@@ -240,10 +249,10 @@ export class Execution {
240
249
  record.update((task) => {
241
250
  task.endedAt = Date.now();
242
251
  task.status = status;
252
+ // Progress display is the executor's live view; a terminal snapshot shows only what the completion gives.
253
+ task.display = completion?.display === undefined ? undefined : boundTaskText(completion.display);
243
254
  if (completion) {
244
255
  task.result = resultText(completion.result);
245
- if (completion.display !== undefined)
246
- task.display = boundTaskText(completion.display);
247
256
  if (completion.error !== undefined)
248
257
  task.error = boundText(completion.error, TASK_ERROR_BYTES);
249
258
  if (completion.facts)
@@ -254,6 +263,8 @@ export class Execution {
254
263
  task.result = prependText(task.error, task.result);
255
264
  }
256
265
  });
266
+ // The caller sees the executor's own diagnostic; a settlement warning is already in its content.
267
+ const diagnostic = record.task.error;
257
268
  let warning;
258
269
  try {
259
270
  this.host.settle(record.snapshot(), completion?.usage ?? completion?.result.usage ?? this.usage);
@@ -267,12 +278,7 @@ export class Execution {
267
278
  });
268
279
  }
269
280
  this.usage = undefined;
270
- // Delivery becomes eligible only after settlement is persisted.
271
- if (this.handedOff)
272
- record.queueDelivery();
273
- else
274
- record.acknowledge();
275
- if (!this.handedOff) {
281
+ if (this.placement !== "background") {
276
282
  if (!completion)
277
283
  this.rejectCaller(error);
278
284
  else {
@@ -280,12 +286,10 @@ export class Execution {
280
286
  const { usage: _usage, ...result } = completion.result;
281
287
  if (warning)
282
288
  result.content = [{ type: "text", text: warning }, ...result.content];
283
- this.resolveCaller({ kind: "result", result, status, error: record.task.error });
289
+ this.resolveCaller({ kind: "result", result, status, error: diagnostic });
284
290
  }
285
291
  }
286
- for (const waiter of [...record.waiters])
287
- waiter();
288
- this.resolveDone();
292
+ record.settle();
289
293
  this.host.released(this);
290
294
  }
291
295
  }
@@ -1 +1 @@
1
- {"version":3,"file":"execution.js","sourceRoot":"","sources":["../../../src/core/tasks/execution.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentToolResult } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai/compat\";\nimport {\n\tboundFacts,\n\tboundTaskText,\n\tboundText,\n\terrorText,\n\tresultText,\n\tTASK_ERROR_BYTES,\n\tTASK_PATH_BYTES,\n} from \"./output.ts\";\nimport type { TaskRecord } from \"./record.ts\";\nimport type {\n\tTaskCompletion,\n\tTaskControl,\n\tTaskExecution,\n\tTaskFact,\n\tTaskMode,\n\tTaskSnapshot,\n\tTaskTerminalStatus,\n\tTaskText,\n\tTaskToolOutcome,\n\tTaskUpdate,\n} from \"./types.ts\";\n\n/** The runtime services one execution needs; it never reaches into the runtime itself. */\nexport interface ExecutionHost {\n\treadonly closed: boolean;\n\temit(): void;\n\t/** Synchronous persistence before terminal observers or notifications. */\n\tsettle(task: TaskSnapshot, usage: Usage | undefined): void;\n\t/** The execution settled and holds no slot, parent listener or caller any more. */\n\treleased(execution: Execution<unknown>): void;\n}\n\nfunction prependText(text: string, result: TaskText | undefined): TaskText {\n\treturn {\n\t\ttext: boundText(result?.text ? `${text}\\n${result.text}` : text),\n\t\ttruncated: result?.truncated ?? false,\n\t};\n}\n\n/** Terminal rows replace launch rows with the same label and append new ones. */\nfunction mergeFacts(base: readonly TaskFact[] = [], terminal: readonly TaskFact[]): TaskFact[] {\n\tconst labels = new Set(terminal.map(([label]) => label));\n\treturn boundFacts([...base.filter(([label]) => !labels.has(label)), ...terminal]);\n}\n\n/**\n * One live task: its caller, controller, parent link and executor callbacks.\n *\n * A caller may be handed off to the background whenever no executor preflight is in\n * progress: before `run` starts (queued) or after the executor calls `accept()`.\n */\nexport class Execution<T> {\n\treadonly record: TaskRecord;\n\treadonly concurrency?: { key: string; limit: number };\n\treadonly caller: Promise<TaskToolOutcome<T>>;\n\treadonly done: Promise<void>;\n\treadonly control: TaskControl<T>;\n\tprivate readonly host: ExecutionHost;\n\tprivate readonly run: TaskExecution<T>[\"run\"];\n\tprivate readonly controller = new AbortController();\n\tprivate readonly modeListeners = new Set<(mode: TaskMode) => void>();\n\tprivate resolveCaller!: (outcome: TaskToolOutcome<T>) => void;\n\tprivate rejectCaller!: (error: unknown) => void;\n\tprivate resolveDone!: () => void;\n\tprivate onUpdate?: (result: AgentToolResult<T>) => void;\n\tprivate releaseParentSignal: () => void = () => {};\n\tprivate usage?: Usage;\n\tprivate started = false;\n\tprivate accepted = false;\n\tprivate handedOff = false;\n\tprivate detachRequested: boolean;\n\t/** The executor's `run` promise once started. */\n\tprivate running?: Promise<TaskCompletion<T>>;\n\n\tconstructor(host: ExecutionHost, record: TaskRecord, execution: TaskExecution<T>) {\n\t\tthis.host = host;\n\t\tthis.record = record;\n\t\tthis.run = execution.run;\n\t\tthis.concurrency = execution.concurrency;\n\t\tthis.detachRequested = execution.background ?? false;\n\t\tthis.onUpdate = execution.background ? undefined : execution.onUpdate;\n\t\tthis.caller = new Promise<TaskToolOutcome<T>>((resolve, reject) => {\n\t\t\tthis.resolveCaller = resolve;\n\t\t\tthis.rejectCaller = reject;\n\t\t});\n\t\tthis.done = new Promise<void>((resolve) => {\n\t\t\tthis.resolveDone = resolve;\n\t\t});\n\t\tconst parent = execution.signal;\n\t\tif (parent) {\n\t\t\tconst abort = () => {\n\t\t\t\tif (!this.detachRequested || !this.handoffAllowed) this.cancel();\n\t\t\t};\n\t\t\tparent.addEventListener(\"abort\", abort, { once: true });\n\t\t\tthis.releaseParentSignal = () => parent.removeEventListener(\"abort\", abort);\n\t\t}\n\t\tconst task = record.task;\n\t\tthis.control = {\n\t\t\tid: task.id,\n\t\t\tsignal: this.controller.signal,\n\t\t\tget mode() {\n\t\t\t\treturn task.mode;\n\t\t\t},\n\t\t\tonModeChange: (listener) => this.onModeChange(listener),\n\t\t\taccept: () => this.accept(),\n\t\t\tpublish: (update) => this.publish(update),\n\t\t\tattachOutput: (path, cleanup) => this.attachOutput(path, cleanup),\n\t\t\tcancel: () => {\n\t\t\t\tthis.cancel();\n\t\t\t},\n\t\t};\n\t}\n\n\tget task(): Readonly<TaskSnapshot> {\n\t\treturn this.record.task;\n\t}\n\n\tget aborted(): boolean {\n\t\treturn this.controller.signal.aborted;\n\t}\n\n\t/** Executor preflight never races a handoff; queued work has not begun any. */\n\tprivate get handoffAllowed(): boolean {\n\t\treturn !this.started || this.accepted;\n\t}\n\n\t/** Waiting for a concurrency slot: registered, but `run` has not started. */\n\tget queued(): boolean {\n\t\treturn !this.started && !this.record.settled;\n\t}\n\n\tget detachable(): boolean {\n\t\treturn !this.record.settled && !this.detachRequested && !this.aborted;\n\t}\n\n\t/** Stop forwarding to the launching tool call: parent cancellation and progress no longer reach it. */\n\treleaseParent(): void {\n\t\tthis.releaseParentSignal();\n\t\tthis.releaseParentSignal = () => {};\n\t\tthis.onUpdate = undefined;\n\t}\n\n\tstart(): void {\n\t\tif (this.started || this.record.settled) return;\n\t\tif (this.host.closed) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\t\tthis.started = true;\n\t\tthis.record.update((task) => {\n\t\t\ttask.status = \"running\";\n\t\t});\n\t\t// The rejection handler is installed immediately, including for synchronous preflight throws.\n\t\ttry {\n\t\t\tthis.running = Promise.resolve(this.run(this.control));\n\t\t\tvoid this.running.then(\n\t\t\t\t(completion) => this.finish(completion),\n\t\t\t\t(error: unknown) => this.finish(undefined, error),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthis.finish(undefined, error);\n\t\t}\n\t}\n\n\thandoff(): void {\n\t\tif (\n\t\t\t!this.handoffAllowed ||\n\t\t\t!this.detachRequested ||\n\t\t\tthis.record.settled ||\n\t\t\tthis.handedOff ||\n\t\t\tthis.host.closed ||\n\t\t\t!this.record.visible ||\n\t\t\tthis.aborted\n\t\t)\n\t\t\treturn;\n\t\tthis.handedOff = true;\n\t\tthis.record.update((task) => {\n\t\t\ttask.mode = \"background\";\n\t\t});\n\t\tthis.releaseParent();\n\t\tthis.resolveCaller({ kind: \"background\", task: this.record.snapshot() });\n\t\tthis.host.emit();\n\t}\n\n\trequestDetach(): void {\n\t\tthis.detachRequested = true;\n\t\tthis.record.update((task) => {\n\t\t\ttask.mode = \"background\";\n\t\t});\n\t\tif (this.accepted) this.releaseParent();\n\t}\n\n\tcompleteDetach(): void {\n\t\tthis.handoff();\n\t\tfor (const listener of this.modeListeners) {\n\t\t\ttry {\n\t\t\t\tlistener(this.task.mode);\n\t\t\t} catch {\n\t\t\t\t/* Executors own their diagnostics. */\n\t\t\t}\n\t\t}\n\t}\n\n\tcancel(): boolean {\n\t\tif (this.record.settled || this.aborted) return false;\n\t\tthis.record.update((task) => {\n\t\t\ttask.status = \"stopping\";\n\t\t});\n\t\tthis.controller.abort();\n\t\tif (!this.started) this.finish(undefined, new Error(\"Task cancelled before it started.\"));\n\t\tthis.host.emit();\n\t\treturn true;\n\t}\n\n\tprivate onModeChange(listener: (mode: TaskMode) => void): () => void {\n\t\tif (this.record.settled || this.host.closed) return () => {};\n\t\tthis.modeListeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.modeListeners.delete(listener);\n\t\t};\n\t}\n\n\tprivate accept(): void {\n\t\tif (this.record.settled || this.accepted || this.host.closed || this.aborted) return;\n\t\tthis.accepted = true;\n\t\tif (this.detachRequested) this.releaseParent();\n\t\t// An already available final result wins over a handoff, so decide only once the\n\t\t// executor's own synchronous code has yielded.\n\t\tqueueMicrotask(() => this.handoffIfRunning());\n\t\tthis.host.emit();\n\t}\n\n\t/**\n\t * Hand off unless `run` has already settled. A reaction on an already settled promise is\n\t * queued at once, ahead of the pending-check below whatever the promise's chain depth, so\n\t * the outcome never depends on how `start()` chains its own handlers.\n\t */\n\tprivate handoffIfRunning(): void {\n\t\tlet settled = false;\n\t\tconst markSettled = () => {\n\t\t\tsettled = true;\n\t\t};\n\t\tthis.running?.then(markSettled, markSettled);\n\t\tvoid Promise.resolve().then(() => {\n\t\t\tif (!settled) this.handoff();\n\t\t});\n\t}\n\n\tprivate publish(update: TaskUpdate<T>): void {\n\t\tif (this.record.settled || this.host.closed) return;\n\t\tthis.record.update((task) => {\n\t\t\ttask.result = resultText(update.result);\n\t\t\tif (update.display !== undefined) task.display = boundTaskText(update.display);\n\t\t});\n\t\tif (update.usage !== undefined) this.usage = structuredClone(update.usage);\n\t\tif (!this.detachRequested && !this.handedOff) {\n\t\t\ttry {\n\t\t\t\tthis.onUpdate?.(update.result);\n\t\t\t} catch {\n\t\t\t\t/* UI observers cannot stop execution. */\n\t\t\t}\n\t\t}\n\t\tthis.host.emit();\n\t}\n\n\tprivate attachOutput(path: string, cleanup?: () => void | Promise<void>): void {\n\t\tif (this.record.settled) {\n\t\t\tif (cleanup) throw new Error(\"Task execution has settled\");\n\t\t\treturn;\n\t\t}\n\t\tif (this.record.cleanup) throw new Error(\"Managed output is already registered\");\n\t\t// Never truncate a real filesystem path into a different path.\n\t\tif (Buffer.byteLength(path) > TASK_PATH_BYTES) throw new Error(\"Task output path is too large\");\n\t\tthis.record.cleanup = cleanup;\n\t\tthis.record.update((task) => {\n\t\t\ttask.outputPath = path;\n\t\t});\n\t\tthis.host.emit();\n\t}\n\n\tprivate finish(completion: TaskCompletion<T> | undefined, error?: unknown): void {\n\t\tconst record = this.record;\n\t\tif (record.settled) return;\n\t\trecord.settled = true;\n\t\tthis.modeListeners.clear();\n\t\tthis.releaseParent();\n\t\tconst status: TaskTerminalStatus = completion\n\t\t\t? (completion.status ?? (this.aborted ? \"cancelled\" : \"completed\"))\n\t\t\t: this.aborted\n\t\t\t\t? \"cancelled\"\n\t\t\t\t: \"failed\";\n\t\trecord.update((task) => {\n\t\t\ttask.endedAt = Date.now();\n\t\t\ttask.status = status;\n\t\t\tif (completion) {\n\t\t\t\ttask.result = resultText(completion.result);\n\t\t\t\tif (completion.display !== undefined) task.display = boundTaskText(completion.display);\n\t\t\t\tif (completion.error !== undefined) task.error = boundText(completion.error, TASK_ERROR_BYTES);\n\t\t\t\tif (completion.facts) task.facts = mergeFacts(task.facts, completion.facts);\n\t\t\t} else {\n\t\t\t\ttask.error = errorText(error);\n\t\t\t\ttask.result = prependText(task.error, task.result);\n\t\t\t}\n\t\t});\n\t\tlet warning: string | undefined;\n\t\ttry {\n\t\t\tthis.host.settle(record.snapshot(), completion?.usage ?? completion?.result.usage ?? this.usage);\n\t\t} catch (accountingError) {\n\t\t\tconst notice = `Usage settlement failed: ${errorText(accountingError)}`;\n\t\t\twarning = notice;\n\t\t\trecord.update((task) => {\n\t\t\t\ttask.error = boundText([task.error, notice].filter(Boolean).join(\"\\n\"), 2 * TASK_ERROR_BYTES);\n\t\t\t\ttask.result = prependText(notice, task.result);\n\t\t\t});\n\t\t}\n\t\tthis.usage = undefined;\n\t\t// Delivery becomes eligible only after settlement is persisted.\n\t\tif (this.handedOff) record.queueDelivery();\n\t\telse record.acknowledge();\n\t\tif (!this.handedOff) {\n\t\t\tif (!completion) this.rejectCaller(error);\n\t\t\telse {\n\t\t\t\t// The task ledger owns usage; a tool result never carries it a second time.\n\t\t\t\tconst { usage: _usage, ...result } = completion.result;\n\t\t\t\tif (warning) result.content = [{ type: \"text\", text: warning }, ...result.content];\n\t\t\t\tthis.resolveCaller({ kind: \"result\", result, status, error: record.task.error });\n\t\t\t}\n\t\t}\n\t\tfor (const waiter of [...record.waiters]) waiter();\n\t\tthis.resolveDone();\n\t\tthis.host.released(this as Execution<unknown>);\n\t}\n}\n"]}
1
+ {"version":3,"file":"execution.js","sourceRoot":"","sources":["../../../src/core/tasks/execution.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,UAAU,EACV,aAAa,EACb,SAAS,EACT,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,eAAe,GACf,MAAM,aAAa,CAAC;AAyBrB,SAAS,WAAW,CAAC,IAAY,EAAE,MAA4B;IAC9D,OAAO;QACN,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAChE,SAAS,EAAE,MAAM,EAAE,SAAS,IAAI,KAAK;KACrC,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,SAAS,UAAU,CAAC,OAA4B,EAAE,EAAE,QAA6B;IAChF,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IACzD,OAAO,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC;AACnF,CAAC;AAYD;;;;;;;GAOG;AACH,MAAM,OAAO,SAAS;IAsBrB,YAAY,IAAmB,EAAE,MAAkB,EAAE,SAA2B;QAb/D,eAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,kBAAa,GAAG,IAAI,GAAG,EAA4B,CAAC;QAI7D,wBAAmB,GAAe,GAAG,EAAE,GAAE,CAAC,CAAC;QAE3C,UAAK,GAAU,QAAQ,CAAC;QAO/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC;QACzC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC/D,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC;QAC9D,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,OAAO,CAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACjE,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;YAC7B,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC;QAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;QAChC,IAAI,MAAM,EAAE,CAAC;YACZ,6FAA6F;YAC7F,MAAM,KAAK,GAAG,GAAG,EAAE;gBAClB,IAAI,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW;oBAAE,IAAI,CAAC,MAAM,EAAE,CAAC;YAC9E,CAAC,CAAC;YACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACxD,IAAI,CAAC,mBAAmB,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC7E,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG;YACd,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM;YAC9B,IAAI,IAAI;gBACP,OAAO,IAAI,CAAC,IAAI,CAAC;YAClB,CAAC;YACD,YAAY,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;YACvD,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE;YAC3B,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;YACzC,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC;YACjE,MAAM,EAAE,GAAG,EAAE;gBACZ,IAAI,CAAC,MAAM,EAAE,CAAC;YACf,CAAC;SACD,CAAC;IACH,CAAC;IAED,IAAI,IAAI;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IACzB,CAAC;IAED,IAAI,OAAO;QACV,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC;IACvC,CAAC;IAED,6EAA6E;IAC7E,IAAI,MAAM;QACT,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC;IAChC,CAAC;IAED,kGAAkG;IAClG,IAAI,mBAAmB;QACtB,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC;IAChE,CAAC;IAED,IAAI,UAAU;QACb,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;IACjF,CAAC;IAED,uGAAuG;IACvG,aAAa;QACZ,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,mBAAmB,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;QACpC,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;IAC3B,CAAC;IAED,KAAK;QACJ,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO;QACpC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,OAAO;QACR,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC;QACzB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;YAC3B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACzB,CAAC,CAAC,CAAC;QACH,8FAA8F;QAC9F,IAAI,CAAC;YACJ,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YACvD,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CACrB,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EACvC,CAAC,KAAc,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CACjD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;IACF,CAAC;IAED,8FAA8F;IAC9F,eAAe;QACd,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE;YAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC1E,CAAC;IAED;;;;OAIG;IACH,MAAM;QACL,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAChC,OAAO,GAAG,EAAE;YACX,IAAI,QAAQ;gBAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACzC,CAAC,CAAC;IACH,CAAC;IAED,MAAM;QACL,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;YAC3B,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC;QAC1B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ;YAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC,CAAC;QACpG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACb,CAAC;IAEO,YAAY,CAAC,QAAkC;QACtD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;QAClE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACjC,OAAO,GAAG,EAAE;YACX,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC,CAAC;IACH,CAAC;IAEO,MAAM;QACb,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAC3E,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC;QACxB,IAAI,IAAI,CAAC,SAAS,KAAK,WAAW;YAAE,IAAI,CAAC,aAAa,EAAE,CAAC;QACzD,iFAAiF;QACjF,+CAA+C;QAC/C,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,CAAC;IAED;;;;;OAKG;IACK,gBAAgB;QACvB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,WAAW,GAAG,GAAG,EAAE;YACxB,OAAO,GAAG,IAAI,CAAC;QAChB,CAAC,CAAC;QACF,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QAC7C,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;YAChC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE;gBAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC3D,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACK,OAAO;QACd,IACC,IAAI,CAAC,SAAS,KAAK,WAAW;YAC9B,CAAC,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,CAAC;YACtD,IAAI,CAAC,IAAI,CAAC,MAAM;YAChB,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;YACpB,IAAI,CAAC,OAAO;YAEZ,OAAO,KAAK,CAAC;QACd,IAAI,CAAC,SAAS,GAAG,YAAY,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;YAC3B,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QAC1B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9F,OAAO,IAAI,CAAC;IACb,CAAC;IAEO,kBAAkB;QACzB,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;YAChD,IAAI,CAAC;gBACJ,QAAQ,CAAC,YAAY,CAAC,CAAC;YACxB,CAAC;YAAC,MAAM,CAAC;gBACR,sCAAsC;YACvC,CAAC;QACF,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,CAAC;IAEO,OAAO,CAAC,MAAqB;QACpC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;YAC3B,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS;gBAAE,IAAI,CAAC,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3E,IAAI,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YACjC,IAAI,CAAC;gBACJ,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACR,yCAAyC;YAC1C,CAAC;QACF,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,CAAC;IAEO,YAAY,CAAC,IAAY,EAAE,OAAoC;QACtE,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;YAC3D,OAAO;QACR,CAAC;QACD,+DAA+D;QAC/D,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,eAAe;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAChG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,CAAC;IAEO,MAAM,CAAC,UAAyC,EAAE,KAAe;QACxE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO;QACrC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAC3B,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,MAAM,MAAM,GAAuB,UAAU;YAC5C,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;YACnE,CAAC,CAAC,IAAI,CAAC,OAAO;gBACb,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,QAAQ,CAAC;QACb,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;YACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;YACrB,0GAA0G;YAC1G,IAAI,CAAC,OAAO,GAAG,UAAU,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACjG,IAAI,UAAU,EAAE,CAAC;gBAChB,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;gBAC5C,IAAI,UAAU,CAAC,KAAK,KAAK,SAAS;oBAAE,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;gBAC/F,IAAI,UAAU,CAAC,KAAK;oBAAE,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;YAC7E,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;gBAC9B,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YACpD,CAAC;QACF,CAAC,CAAC,CAAC;QACH,iGAAiG;QACjG,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;QACrC,IAAI,OAA2B,CAAC;QAChC,IAAI,CAAC;YACJ,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;QAClG,CAAC;QAAC,OAAO,eAAe,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,4BAA4B,SAAS,CAAC,eAAe,CAAC,EAAE,CAAC;YACxE,OAAO,GAAG,MAAM,CAAC;YACjB,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;gBACtB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,gBAAgB,CAAC,CAAC;gBAC9F,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAChD,CAAC,CAAC,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,IAAI,IAAI,CAAC,SAAS,KAAK,YAAY,EAAE,CAAC;YACrC,IAAI,CAAC,UAAU;gBAAE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;iBACrC,CAAC;gBACL,4EAA4E;gBAC5E,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC;gBACvD,IAAI,OAAO;oBAAE,MAAM,CAAC,OAAO,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;gBACnF,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;YAC3E,CAAC;QACF,CAAC;QACD,MAAM,CAAC,MAAM,EAAE,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAA0B,CAAC,CAAC;IAChD,CAAC;CACD","sourcesContent":["import type { AgentToolResult } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai/compat\";\nimport {\n\tboundFacts,\n\tboundTaskText,\n\tboundText,\n\terrorText,\n\tresultText,\n\tTASK_ERROR_BYTES,\n\tTASK_PATH_BYTES,\n} from \"./output.ts\";\nimport type { TaskRecord } from \"./record.ts\";\nimport type {\n\tTaskCompletion,\n\tTaskControl,\n\tTaskExecution,\n\tTaskFact,\n\tTaskMode,\n\tTaskSnapshot,\n\tTaskTerminalStatus,\n\tTaskText,\n\tTaskToolOutcome,\n\tTaskUpdate,\n} from \"./types.ts\";\n\n/** The runtime services one execution needs; it never reaches into the runtime itself. */\nexport interface ExecutionHost {\n\treadonly closed: boolean;\n\temit(): void;\n\t/** Synchronous persistence before terminal observers or notifications. */\n\tsettle(task: TaskSnapshot, usage: Usage | undefined): void;\n\t/** The execution settled and holds no slot, parent listener or caller any more. */\n\treleased(execution: Execution<unknown>): void;\n}\n\nfunction prependText(text: string, result: TaskText | undefined): TaskText {\n\treturn {\n\t\ttext: boundText(result?.text ? `${text}\\n${result.text}` : text),\n\t\ttruncated: result?.truncated ?? false,\n\t};\n}\n\n/** Terminal rows replace launch rows with the same label and append new ones. */\nfunction mergeFacts(base: readonly TaskFact[] = [], terminal: readonly TaskFact[]): TaskFact[] {\n\tconst labels = new Set(terminal.map(([label]) => label));\n\treturn boundFacts([...base.filter(([label]) => !labels.has(label)), ...terminal]);\n}\n\n/** queued: waiting for a lane slot; preflight: `run` started, `accept()` not yet called; settled: terminal. */\ntype Phase = \"queued\" | \"preflight\" | \"accepted\" | \"settled\";\n\n/**\n * Where the launching tool call stands: still waiting for the result (`caller`), asked to be\n * released but held until the executor accepts (`detaching`), or released with a background\n * outcome (`background`). Only `background` is reported as the task's mode.\n */\ntype Placement = \"caller\" | \"detaching\" | \"background\";\n\n/**\n * One live task: its caller, controller, parent link and executor callbacks.\n *\n * A caller may be handed off to the background whenever no executor preflight is in\n * progress: before `run` starts (queued) or after the executor calls `accept()`. A release\n * requested during preflight waits for `accept()`; if the run settles first, the caller is\n * answered inline and the task never becomes background.\n */\nexport class Execution<T> {\n\treadonly record: TaskRecord;\n\treadonly kind: string;\n\treadonly concurrency?: { key: string; limit: number };\n\treadonly caller: Promise<TaskToolOutcome<T>>;\n\treadonly done: Promise<void>;\n\treadonly control: TaskControl<T>;\n\tprivate readonly host: ExecutionHost;\n\tprivate readonly run: TaskExecution<T>[\"run\"];\n\tprivate readonly controller = new AbortController();\n\tprivate readonly modeListeners = new Set<(mode: TaskMode) => void>();\n\tprivate resolveCaller!: (outcome: TaskToolOutcome<T>) => void;\n\tprivate rejectCaller!: (error: unknown) => void;\n\tprivate onUpdate?: (result: AgentToolResult<T>) => void;\n\tprivate releaseParentSignal: () => void = () => {};\n\tprivate usage?: Usage;\n\tprivate phase: Phase = \"queued\";\n\tprivate placement: Placement;\n\tprivate readonly reason: \"submitted\" | \"detached\";\n\t/** The executor's `run` promise once started. */\n\tprivate running?: Promise<TaskCompletion<T>>;\n\n\tconstructor(host: ExecutionHost, record: TaskRecord, execution: TaskExecution<T>) {\n\t\tthis.host = host;\n\t\tthis.record = record;\n\t\tthis.run = execution.run;\n\t\tthis.kind = execution.kind;\n\t\tthis.concurrency = execution.concurrency;\n\t\tthis.placement = execution.background ? \"detaching\" : \"caller\";\n\t\tthis.reason = execution.background ? \"submitted\" : \"detached\";\n\t\tthis.onUpdate = execution.onUpdate;\n\t\tthis.caller = new Promise<TaskToolOutcome<T>>((resolve, reject) => {\n\t\t\tthis.resolveCaller = resolve;\n\t\t\tthis.rejectCaller = reject;\n\t\t});\n\t\tthis.done = record.settlement;\n\t\tconst parent = execution.signal;\n\t\tif (parent) {\n\t\t\t// A caller that is leaving survives the parent's abort unless the executor is mid-preflight.\n\t\t\tconst abort = () => {\n\t\t\t\tif (this.placement === \"caller\" || this.phase === \"preflight\") this.cancel();\n\t\t\t};\n\t\t\tparent.addEventListener(\"abort\", abort, { once: true });\n\t\t\tthis.releaseParentSignal = () => parent.removeEventListener(\"abort\", abort);\n\t\t}\n\t\tconst task = record.task;\n\t\tthis.control = {\n\t\t\tid: task.id,\n\t\t\tsignal: this.controller.signal,\n\t\t\tget mode() {\n\t\t\t\treturn task.mode;\n\t\t\t},\n\t\t\tonModeChange: (listener) => this.onModeChange(listener),\n\t\t\taccept: () => this.accept(),\n\t\t\tpublish: (update) => this.publish(update),\n\t\t\tattachOutput: (path, cleanup) => this.attachOutput(path, cleanup),\n\t\t\tcancel: () => {\n\t\t\t\tthis.cancel();\n\t\t\t},\n\t\t};\n\t}\n\n\tget task(): Readonly<TaskSnapshot> {\n\t\treturn this.record.task;\n\t}\n\n\tget aborted(): boolean {\n\t\treturn this.controller.signal.aborted;\n\t}\n\n\t/** Waiting for a concurrency slot: registered, but `run` has not started. */\n\tget queued(): boolean {\n\t\treturn this.phase === \"queued\";\n\t}\n\n\t/** Holds a background slot: handed off, or asked to be and waiting for the executor to accept. */\n\tget holdsBackgroundSlot(): boolean {\n\t\treturn this.phase !== \"settled\" && this.placement !== \"caller\";\n\t}\n\n\tget detachable(): boolean {\n\t\treturn this.phase !== \"settled\" && this.placement === \"caller\" && !this.aborted;\n\t}\n\n\t/** Stop forwarding to the launching tool call: parent cancellation and progress no longer reach it. */\n\treleaseParent(): void {\n\t\tthis.releaseParentSignal();\n\t\tthis.releaseParentSignal = () => {};\n\t\tthis.onUpdate = undefined;\n\t}\n\n\tstart(): void {\n\t\tif (this.phase !== \"queued\") return;\n\t\tif (this.host.closed) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\t\tthis.phase = \"preflight\";\n\t\tthis.record.update((task) => {\n\t\t\ttask.status = \"running\";\n\t\t});\n\t\t// The rejection handler is installed immediately, including for synchronous preflight throws.\n\t\ttry {\n\t\t\tthis.running = Promise.resolve(this.run(this.control));\n\t\t\tvoid this.running.then(\n\t\t\t\t(completion) => this.finish(completion),\n\t\t\t\t(error: unknown) => this.finish(undefined, error),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthis.finish(undefined, error);\n\t\t}\n\t}\n\n\t/** Queued background work returns at once; started work is released at its own `accept()`. */\n\thandOffIfQueued(): void {\n\t\tif (this.phase === \"queued\" && this.handoff()) this.announceBackground();\n\t}\n\n\t/**\n\t * Ask to move the caller to the background. The placement changes now; the returned\n\t * notification is for the caller of a batch to run after every placement has changed, so no\n\t * listener ever observes a half-moved set.\n\t */\n\tdetach(): () => void {\n\t\tthis.placement = \"detaching\";\n\t\tconst released = this.handoff();\n\t\treturn () => {\n\t\t\tif (released) this.announceBackground();\n\t\t};\n\t}\n\n\tcancel(): boolean {\n\t\tif (this.phase === \"settled\" || this.aborted) return false;\n\t\tthis.record.update((task) => {\n\t\t\ttask.status = \"stopping\";\n\t\t});\n\t\tthis.controller.abort();\n\t\tif (this.phase === \"queued\") this.finish(undefined, new Error(\"Task cancelled before it started.\"));\n\t\tthis.host.emit();\n\t\treturn true;\n\t}\n\n\tprivate onModeChange(listener: (mode: TaskMode) => void): () => void {\n\t\tif (this.phase === \"settled\" || this.host.closed) return () => {};\n\t\tthis.modeListeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.modeListeners.delete(listener);\n\t\t};\n\t}\n\n\tprivate accept(): void {\n\t\tif (this.phase !== \"preflight\" || this.host.closed || this.aborted) return;\n\t\tthis.phase = \"accepted\";\n\t\tif (this.placement === \"detaching\") this.releaseParent();\n\t\t// An already available final result wins over a handoff, so decide only once the\n\t\t// executor's own synchronous code has yielded.\n\t\tqueueMicrotask(() => this.handoffIfRunning());\n\t\tthis.host.emit();\n\t}\n\n\t/**\n\t * Hand off unless `run` has already settled. This observes the `run` promise's own settlement,\n\t * not the phase `finish()` sets: a reaction on an already settled promise is queued at once,\n\t * ahead of the pending-check below whatever the promise's chain depth, so the outcome never\n\t * depends on how many hops `start()` chains before `finish()`.\n\t */\n\tprivate handoffIfRunning(): void {\n\t\tlet settled = false;\n\t\tconst markSettled = () => {\n\t\t\tsettled = true;\n\t\t};\n\t\tthis.running?.then(markSettled, markSettled);\n\t\tvoid Promise.resolve().then(() => {\n\t\t\tif (!settled && this.handoff()) this.announceBackground();\n\t\t});\n\t}\n\n\t/**\n\t * Release the caller with a background outcome if it asked to leave and the executor's\n\t * preflight is over. The task is background exactly from here on.\n\t */\n\tprivate handoff(): boolean {\n\t\tif (\n\t\t\tthis.placement !== \"detaching\" ||\n\t\t\t(this.phase !== \"queued\" && this.phase !== \"accepted\") ||\n\t\t\tthis.host.closed ||\n\t\t\t!this.record.visible ||\n\t\t\tthis.aborted\n\t\t)\n\t\t\treturn false;\n\t\tthis.placement = \"background\";\n\t\tthis.record.update((task) => {\n\t\t\ttask.mode = \"background\";\n\t\t});\n\t\tthis.releaseParent();\n\t\tthis.resolveCaller({ kind: \"background\", task: this.record.snapshot(), reason: this.reason });\n\t\treturn true;\n\t}\n\n\tprivate announceBackground(): void {\n\t\tfor (const listener of [...this.modeListeners]) {\n\t\t\ttry {\n\t\t\t\tlistener(\"background\");\n\t\t\t} catch {\n\t\t\t\t/* Executors own their diagnostics. */\n\t\t\t}\n\t\t}\n\t\tthis.host.emit();\n\t}\n\n\tprivate publish(update: TaskUpdate<T>): void {\n\t\tif (this.phase === \"settled\" || this.host.closed) return;\n\t\tthis.record.update((task) => {\n\t\t\ttask.result = resultText(update.result);\n\t\t\tif (update.display !== undefined) task.display = boundTaskText(update.display);\n\t\t});\n\t\tif (update.usage !== undefined) this.usage = structuredClone(update.usage);\n\t\tif (this.placement === \"caller\") {\n\t\t\ttry {\n\t\t\t\tthis.onUpdate?.(update.result);\n\t\t\t} catch {\n\t\t\t\t/* UI observers cannot stop execution. */\n\t\t\t}\n\t\t}\n\t\tthis.host.emit();\n\t}\n\n\tprivate attachOutput(path: string, cleanup?: () => void | Promise<void>): void {\n\t\tif (this.phase === \"settled\") {\n\t\t\tif (cleanup) throw new Error(\"Task execution has settled\");\n\t\t\treturn;\n\t\t}\n\t\t// Never truncate a real filesystem path into a different path.\n\t\tif (Buffer.byteLength(path) > TASK_PATH_BYTES) throw new Error(\"Task output path is too large\");\n\t\tthis.record.attachOutput(path, cleanup);\n\t\tthis.host.emit();\n\t}\n\n\tprivate finish(completion: TaskCompletion<T> | undefined, error?: unknown): void {\n\t\tconst record = this.record;\n\t\tif (this.phase === \"settled\") return;\n\t\tthis.phase = \"settled\";\n\t\tthis.modeListeners.clear();\n\t\tthis.releaseParent();\n\t\tconst status: TaskTerminalStatus = completion\n\t\t\t? (completion.status ?? (this.aborted ? \"cancelled\" : \"completed\"))\n\t\t\t: this.aborted\n\t\t\t\t? \"cancelled\"\n\t\t\t\t: \"failed\";\n\t\trecord.update((task) => {\n\t\t\ttask.endedAt = Date.now();\n\t\t\ttask.status = status;\n\t\t\t// Progress display is the executor's live view; a terminal snapshot shows only what the completion gives.\n\t\t\ttask.display = completion?.display === undefined ? undefined : boundTaskText(completion.display);\n\t\t\tif (completion) {\n\t\t\t\ttask.result = resultText(completion.result);\n\t\t\t\tif (completion.error !== undefined) task.error = boundText(completion.error, TASK_ERROR_BYTES);\n\t\t\t\tif (completion.facts) task.facts = mergeFacts(task.facts, completion.facts);\n\t\t\t} else {\n\t\t\t\ttask.error = errorText(error);\n\t\t\t\ttask.result = prependText(task.error, task.result);\n\t\t\t}\n\t\t});\n\t\t// The caller sees the executor's own diagnostic; a settlement warning is already in its content.\n\t\tconst diagnostic = record.task.error;\n\t\tlet warning: string | undefined;\n\t\ttry {\n\t\t\tthis.host.settle(record.snapshot(), completion?.usage ?? completion?.result.usage ?? this.usage);\n\t\t} catch (accountingError) {\n\t\t\tconst notice = `Usage settlement failed: ${errorText(accountingError)}`;\n\t\t\twarning = notice;\n\t\t\trecord.update((task) => {\n\t\t\t\ttask.error = boundText([task.error, notice].filter(Boolean).join(\"\\n\"), 2 * TASK_ERROR_BYTES);\n\t\t\t\ttask.result = prependText(notice, task.result);\n\t\t\t});\n\t\t}\n\t\tthis.usage = undefined;\n\t\tif (this.placement !== \"background\") {\n\t\t\tif (!completion) this.rejectCaller(error);\n\t\t\telse {\n\t\t\t\t// The task ledger owns usage; a tool result never carries it a second time.\n\t\t\t\tconst { usage: _usage, ...result } = completion.result;\n\t\t\t\tif (warning) result.content = [{ type: \"text\", text: warning }, ...result.content];\n\t\t\t\tthis.resolveCaller({ kind: \"result\", result, status, error: diagnostic });\n\t\t\t}\n\t\t}\n\t\trecord.settle();\n\t\tthis.host.released(this as Execution<unknown>);\n\t}\n}\n"]}
@@ -1,8 +1,15 @@
1
+ import type { TaskSnapshot } from "./types.ts";
1
2
  /**
2
3
  * Shared observation-window limits, the one definition used by the runtime and the tasks tool.
3
4
  * Execution timeouts are configured separately by each executor.
5
+ *
6
+ * A wait returns as soon as its task settles, and steering or another completion interrupts it, so
7
+ * the default only bounds how long a wait on unfinished work blocks before the model has control
8
+ * again. It is the longest window that stays inside the common five-minute prompt-cache lifetime,
9
+ * with a minute's margin for the request around it: a longer one makes the next request miss the
10
+ * cache or need a warming refresh.
4
11
  */
5
- export declare const TASKS_WAIT_DEFAULT_MS = 20000;
12
+ export declare const TASKS_WAIT_DEFAULT_MS: number;
6
13
  export declare const TASKS_WAIT_MIN_MS = 1000;
7
14
  export declare const TASKS_WAIT_MAX_MS: number;
8
15
  /** Floor for a model-requested read slice; the default and ceiling are TASK_READ_BYTES and TASK_RESULT_BYTES. */
@@ -13,4 +20,9 @@ export declare function boundWaitMs(waitMs: number | undefined): number;
13
20
  export declare function clampWaitMs(waitMs: number | undefined): number;
14
21
  /** The read size a tool call runs. */
15
22
  export declare function clampReadBytes(bytes: number | undefined): number;
23
+ /**
24
+ * The end a read starts from when the caller names none, the one definition every read,
25
+ * wait and projection shares: a report's beginning is its summary, a log's end is its outcome.
26
+ */
27
+ export declare function defaultReadMode(task: Pick<TaskSnapshot, "output">): "head" | "tail";
16
28
  //# sourceMappingURL=limits.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../../src/core/tasks/limits.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAC5C,eAAO,MAAM,iBAAiB,OAAQ,CAAC;AACvC,eAAO,MAAM,iBAAiB,QAAc,CAAC;AAC7C,iHAAiH;AACjH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE9D;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE9D;AAED,sCAAsC;AACtC,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAEhE"}
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../../src/core/tasks/limits.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,QAAa,CAAC;AAChD,eAAO,MAAM,iBAAiB,OAAQ,CAAC;AACvC,eAAO,MAAM,iBAAiB,QAAc,CAAC;AAC7C,iHAAiH;AACjH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE9D;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE9D;AAED,sCAAsC;AACtC,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAEhE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,GAAG,MAAM,GAAG,MAAM,CAEnF"}
@@ -3,8 +3,14 @@ import { boundReadBytes } from "./output.js";
3
3
  /**
4
4
  * Shared observation-window limits, the one definition used by the runtime and the tasks tool.
5
5
  * Execution timeouts are configured separately by each executor.
6
+ *
7
+ * A wait returns as soon as its task settles, and steering or another completion interrupts it, so
8
+ * the default only bounds how long a wait on unfinished work blocks before the model has control
9
+ * again. It is the longest window that stays inside the common five-minute prompt-cache lifetime,
10
+ * with a minute's margin for the request around it: a longer one makes the next request miss the
11
+ * cache or need a warming refresh.
6
12
  */
7
- export const TASKS_WAIT_DEFAULT_MS = 20_000;
13
+ export const TASKS_WAIT_DEFAULT_MS = 4 * 60_000;
8
14
  export const TASKS_WAIT_MIN_MS = 1_000;
9
15
  export const TASKS_WAIT_MAX_MS = 10 * 60_000;
10
16
  /** Floor for a model-requested read slice; the default and ceiling are TASK_READ_BYTES and TASK_RESULT_BYTES. */
@@ -21,4 +27,11 @@ export function clampWaitMs(waitMs) {
21
27
  export function clampReadBytes(bytes) {
22
28
  return Math.max(TASKS_READ_MIN_BYTES, boundReadBytes(bytes));
23
29
  }
30
+ /**
31
+ * The end a read starts from when the caller names none, the one definition every read,
32
+ * wait and projection shares: a report's beginning is its summary, a log's end is its outcome.
33
+ */
34
+ export function defaultReadMode(task) {
35
+ return task.output === "report" ? "head" : "tail";
36
+ }
24
37
  //# sourceMappingURL=limits.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../../src/core/tasks/limits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAC5C,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AACvC,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,GAAG,MAAM,CAAC;AAC7C,iHAAiH;AACjH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAExC,4FAA4F;AAC5F,MAAM,UAAU,WAAW,CAAC,MAA0B;IACrD,OAAO,WAAW,CAAC,MAAM,EAAE,qBAAqB,EAAE,iBAAiB,CAAC,CAAC;AACtE,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,WAAW,CAAC,MAA0B;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,cAAc,CAAC,KAAyB;IACvD,OAAO,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import { finiteLimit } from \"../../utils/number.ts\";\nimport { boundReadBytes } from \"./output.ts\";\n\n/**\n * Shared observation-window limits, the one definition used by the runtime and the tasks tool.\n * Execution timeouts are configured separately by each executor.\n */\nexport const TASKS_WAIT_DEFAULT_MS = 20_000;\nexport const TASKS_WAIT_MIN_MS = 1_000;\nexport const TASKS_WAIT_MAX_MS = 10 * 60_000;\n/** Floor for a model-requested read slice; the default and ceiling are TASK_READ_BYTES and TASK_RESULT_BYTES. */\nexport const TASKS_READ_MIN_BYTES = 256;\n\n/** The wait window the runtime honours: any finite value up to the ceiling, including 0. */\nexport function boundWaitMs(waitMs: number | undefined): number {\n\treturn finiteLimit(waitMs, TASKS_WAIT_DEFAULT_MS, TASKS_WAIT_MAX_MS);\n}\n\n/** The wait window a tool call runs, which the pending-call renderer also shows. */\nexport function clampWaitMs(waitMs: number | undefined): number {\n\treturn Math.max(TASKS_WAIT_MIN_MS, boundWaitMs(waitMs));\n}\n\n/** The read size a tool call runs. */\nexport function clampReadBytes(bytes: number | undefined): number {\n\treturn Math.max(TASKS_READ_MIN_BYTES, boundReadBytes(bytes));\n}\n"]}
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../../src/core/tasks/limits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,GAAG,MAAM,CAAC;AAChD,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AACvC,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,GAAG,MAAM,CAAC;AAC7C,iHAAiH;AACjH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAExC,4FAA4F;AAC5F,MAAM,UAAU,WAAW,CAAC,MAA0B;IACrD,OAAO,WAAW,CAAC,MAAM,EAAE,qBAAqB,EAAE,iBAAiB,CAAC,CAAC;AACtE,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,WAAW,CAAC,MAA0B;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,cAAc,CAAC,KAAyB;IACvD,OAAO,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAkC;IACjE,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;AACnD,CAAC","sourcesContent":["import { finiteLimit } from \"../../utils/number.ts\";\nimport { boundReadBytes } from \"./output.ts\";\nimport type { TaskSnapshot } from \"./types.ts\";\n\n/**\n * Shared observation-window limits, the one definition used by the runtime and the tasks tool.\n * Execution timeouts are configured separately by each executor.\n *\n * A wait returns as soon as its task settles, and steering or another completion interrupts it, so\n * the default only bounds how long a wait on unfinished work blocks before the model has control\n * again. It is the longest window that stays inside the common five-minute prompt-cache lifetime,\n * with a minute's margin for the request around it: a longer one makes the next request miss the\n * cache or need a warming refresh.\n */\nexport const TASKS_WAIT_DEFAULT_MS = 4 * 60_000;\nexport const TASKS_WAIT_MIN_MS = 1_000;\nexport const TASKS_WAIT_MAX_MS = 10 * 60_000;\n/** Floor for a model-requested read slice; the default and ceiling are TASK_READ_BYTES and TASK_RESULT_BYTES. */\nexport const TASKS_READ_MIN_BYTES = 256;\n\n/** The wait window the runtime honours: any finite value up to the ceiling, including 0. */\nexport function boundWaitMs(waitMs: number | undefined): number {\n\treturn finiteLimit(waitMs, TASKS_WAIT_DEFAULT_MS, TASKS_WAIT_MAX_MS);\n}\n\n/** The wait window a tool call runs, which the pending-call renderer also shows. */\nexport function clampWaitMs(waitMs: number | undefined): number {\n\treturn Math.max(TASKS_WAIT_MIN_MS, boundWaitMs(waitMs));\n}\n\n/** The read size a tool call runs. */\nexport function clampReadBytes(bytes: number | undefined): number {\n\treturn Math.max(TASKS_READ_MIN_BYTES, boundReadBytes(bytes));\n}\n\n/**\n * The end a read starts from when the caller names none, the one definition every read,\n * wait and projection shares: a report's beginning is its summary, a log's end is its outcome.\n */\nexport function defaultReadMode(task: Pick<TaskSnapshot, \"output\">): \"head\" | \"tail\" {\n\treturn task.output === \"report\" ? \"head\" : \"tail\";\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
2
- import type { TaskFact, TaskInput, TaskReadOptions, TaskText } from "./types.ts";
2
+ import type { TaskFact, TaskInput, TaskRead, TaskReadOptions, TaskText } from "./types.ts";
3
3
  export declare const TASK_RESULT_BYTES: number;
4
4
  export declare const TASK_DETAILS_BYTES: number;
5
5
  export declare const TASK_READ_BYTES: number;
@@ -30,7 +30,15 @@ export interface OutputSlice {
30
30
  totalBytes: number;
31
31
  truncated: boolean;
32
32
  fromByte: number;
33
+ /** Raw source-byte end, independent of UTF-8 decoding or replacement characters. */
34
+ endByte: number;
33
35
  }
36
+ /** Raw source-byte range; decoding invalid UTF-8 must never move the continuation offset. */
37
+ export declare function taskReadRange(slice: Pick<TaskRead, "text" | "fromByte" | "endByte">): {
38
+ from: number;
39
+ to: number;
40
+ };
41
+ export declare function taskReadBytes(slice: Pick<TaskRead, "text" | "fromByte" | "endByte">): number;
34
42
  /** Positioned, bounded reads only. The executor owns the path and its cleanup. */
35
43
  export declare function readOutputSlice(path: string, options?: TaskReadOptions): Promise<OutputSlice>;
36
44
  export declare function sliceText(text: string, options?: TaskReadOptions): OutputSlice;
@@ -1 +1 @@
1
- {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../../src/core/tasks/output.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEjF,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAa,CAAC;AAC7C,eAAO,MAAM,eAAe,QAAW,CAAC;AACxC,eAAO,MAAM,gBAAgB,QAAY,CAAC;AAC1C,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,eAAO,MAAM,UAAU,KAAK,CAAC;AAC7B,eAAO,MAAM,eAAe,OAAO,CAAC;AACpC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,yDAAyD;AACzD,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAA8C,GAAG,MAAM,CAMxG;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAEhE;AAmBD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAoB,GAAG,MAAM,CAMzE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAoB,GAAG,QAAQ,CAG/E;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAQtD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAIjE;AAED,uFAAuF;AACvF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAI3F;AAED,sGAAsG;AACtG,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,QAAQ,CAgBrE;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAuBD,kFAAkF;AAClF,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,WAAW,CAAC,CAavG;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,WAAW,CAKlF"}
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../../src/core/tasks/output.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3F,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAa,CAAC;AAC7C,eAAO,MAAM,eAAe,QAAW,CAAC;AACxC,eAAO,MAAM,gBAAgB,QAAY,CAAC;AAC1C,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,eAAO,MAAM,UAAU,KAAK,CAAC;AAC7B,eAAO,MAAM,eAAe,OAAO,CAAC;AACpC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,yDAAyD;AACzD,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAA8C,GAAG,MAAM,CAMxG;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAEhE;AAmBD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAoB,GAAG,MAAM,CAMzE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAoB,GAAG,QAAQ,CAG/E;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAQtD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAIjE;AAED,uFAAuF;AACvF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAI3F;AAED,sGAAsG;AACtG,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,QAAQ,CAgBrE;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,6FAA6F;AAC7F,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAGlH;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC,GAAG,MAAM,CAG5F;AA2BD,kFAAkF;AAClF,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,WAAW,CAAC,CAkBvG;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,WAAW,CAUlF"}
@@ -94,23 +94,36 @@ export function resultText(result) {
94
94
  }
95
95
  return { text: boundText(parts.join("\n")), truncated };
96
96
  }
97
+ /** Raw source-byte range; decoding invalid UTF-8 must never move the continuation offset. */
98
+ export function taskReadRange(slice) {
99
+ const from = slice.fromByte ?? 0;
100
+ return { from, to: slice.endByte ?? from + Buffer.byteLength(slice.text) };
101
+ }
102
+ export function taskReadBytes(slice) {
103
+ const { from, to } = taskReadRange(slice);
104
+ return to - from;
105
+ }
97
106
  /**
98
107
  * The byte window a read covers in an output of `size` bytes. `floor` is the first byte the
99
- * caller has not seen yet; anything before it never counts as truncated.
108
+ * caller has not seen yet. A head read continues forward from it; a tail read keeps the newest
109
+ * unread window. Anything before the floor never counts as truncated.
100
110
  */
101
111
  function readWindow(size, options) {
102
112
  const bytes = boundReadBytes(options.bytes);
103
113
  const since = finiteLimit(options.sinceBytes, 0, Number.MAX_SAFE_INTEGER);
104
114
  const floor = since > size ? 0 : since;
105
- const start = options.sinceBytes !== undefined
106
- ? Math.max(floor, size - bytes)
107
- : options.mode === "head"
108
- ? 0
109
- : Math.max(0, size - bytes);
115
+ const start = options.mode === "head" ? (options.sinceBytes !== undefined ? floor : 0) : Math.max(floor, size - bytes);
110
116
  return { start, length: Math.min(bytes, size - start), floor };
111
117
  }
112
- function outputSlice(size, floor, slice, fromByte) {
113
- return { text: slice.toString("utf8"), totalBytes: size, truncated: slice.length < size - floor, fromByte };
118
+ function outputSlice(size, slice, fromByte, earlierTruncated) {
119
+ const endByte = fromByte + slice.byteLength;
120
+ return {
121
+ text: slice.toString("utf8"),
122
+ totalBytes: size,
123
+ truncated: earlierTruncated || endByte < size,
124
+ fromByte,
125
+ endByte,
126
+ };
114
127
  }
115
128
  /** Positioned, bounded reads only. The executor owns the path and its cleanup. */
116
129
  export async function readOutputSlice(path, options = {}) {
@@ -122,7 +135,7 @@ export async function readOutputSlice(path, options = {}) {
122
135
  const buffer = Buffer.alloc(Math.min(length + 1, size - start));
123
136
  const { bytesRead } = await file.read(buffer, 0, buffer.length, start);
124
137
  const slice = alignedSlice(buffer.subarray(0, bytesRead), 0, Math.min(length, bytesRead));
125
- return outputSlice(size, floor, slice, start + slice.byteOffset - buffer.byteOffset);
138
+ return outputSlice(size, slice, start + slice.byteOffset - buffer.byteOffset, options.mode !== "head" && start > floor);
126
139
  }
127
140
  finally {
128
141
  await file.close();
@@ -132,6 +145,6 @@ export function sliceText(text, options = {}) {
132
145
  const buffer = Buffer.from(text);
133
146
  const { start, length, floor } = readWindow(buffer.length, options);
134
147
  const slice = alignedSlice(buffer, start, start + length);
135
- return outputSlice(buffer.length, floor, slice, slice.byteOffset - buffer.byteOffset);
148
+ return outputSlice(buffer.length, slice, slice.byteOffset - buffer.byteOffset, options.mode !== "head" && start > floor);
136
149
  }
137
150
  //# sourceMappingURL=output.js.map