agents 0.28.0 → 0.29.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 (121) hide show
  1. package/dist/{agent-routing-QDcchcQP.d.ts → agent-routing-CDbpVjYq.d.ts} +18 -8
  2. package/dist/agent-routing.d.ts +1 -1
  3. package/dist/agent-tool-types.d.ts +1 -1
  4. package/dist/{agent-tools-DHL2XI7u.d.ts → agent-tools-C8DH8Yii.d.ts} +2 -2
  5. package/dist/agent-tools.d.ts +1 -1
  6. package/dist/{ai-ahruGw7e.d.ts → ai-iXw0uOGj.d.ts} +3 -3
  7. package/dist/{ai-sdk-Bdsf6mSA.js → ai-sdk-DRPh2rRa.js} +218 -132
  8. package/dist/ai-sdk-DRPh2rRa.js.map +1 -0
  9. package/dist/browser/ai.d.ts +1 -1
  10. package/dist/browser/ai.js +2 -2
  11. package/dist/browser/index.d.ts +8 -8
  12. package/dist/browser/index.js +3 -3
  13. package/dist/browser/tools/ai-sdk.d.ts +2 -2
  14. package/dist/browser/tools/ai-sdk.js +1 -1
  15. package/dist/browser/tools/pi.d.ts +1 -1
  16. package/dist/browser/tools/pi.js +3 -3
  17. package/dist/browser/tools/pi.js.map +1 -1
  18. package/dist/browser/tools/tanstack-ai.d.ts +2 -2
  19. package/dist/browser/tools/tanstack-ai.js +1 -1
  20. package/dist/{browser-CEdNlsaD.js → browser-DccPw_wW.js} +130 -79
  21. package/dist/browser-DccPw_wW.js.map +1 -0
  22. package/dist/{browser-D30VlpcA.d.ts → browser-DlXhSAdf.d.ts} +41 -37
  23. package/dist/{browser-tool-4iomavs_.js → browser-tool-B1mhifin.js} +3 -3
  24. package/dist/{browser-tool-4iomavs_.js.map → browser-tool-B1mhifin.js.map} +1 -1
  25. package/dist/{browser-tool-vtrPOV8e.d.ts → browser-tool-Cxjk5sgG.d.ts} +2 -2
  26. package/dist/chat/index.d.ts +143 -71
  27. package/dist/chat/index.js +269 -110
  28. package/dist/chat/index.js.map +1 -1
  29. package/dist/chat-sdk/index.d.ts +1 -1
  30. package/dist/chat-sdk/index.js +1 -1
  31. package/dist/cli.js +33 -33
  32. package/dist/{client-BGIQKJhX.d.ts → client-C27CtRZr.d.ts} +2 -2
  33. package/dist/client.d.ts +1 -1
  34. package/dist/{connector-DEXzVoaY.d.ts → connector-20DJ6p4k.d.ts} +2 -2
  35. package/dist/{connector-CtZylXdk.js → connector-DAXBOIIi.js} +2 -2
  36. package/dist/{connector-CtZylXdk.js.map → connector-DAXBOIIi.js.map} +1 -1
  37. package/dist/experimental/channels/web/index.js +1 -1
  38. package/dist/{handler-stateless-VvrWSAVA.js → handler-stateless-Bly_nSH3.js} +12 -3
  39. package/dist/handler-stateless-Bly_nSH3.js.map +1 -0
  40. package/dist/harness/container/index.js +1 -1
  41. package/dist/harness/container/index.js.map +1 -1
  42. package/dist/harness/opencode/index.d.ts +182 -6
  43. package/dist/harness/opencode/index.js +354 -23
  44. package/dist/harness/opencode/index.js.map +1 -1
  45. package/dist/harness/pi/index.d.ts +13 -5
  46. package/dist/harness/pi/index.js +8 -214
  47. package/dist/harness/pi/index.js.map +1 -1
  48. package/dist/{images-Ce9SCu3X.js → images-xaDv2Fqe.js} +662 -152
  49. package/dist/images-xaDv2Fqe.js.map +1 -0
  50. package/dist/index-DCxk3OSJ.d.ts +378 -0
  51. package/dist/{index-kbSUIer3.d.ts → index-iv9FTXX9.d.ts} +80 -112
  52. package/dist/index.d.ts +1 -1
  53. package/dist/index.js +1 -1
  54. package/dist/{input-schema-D2QAtHZH.js → input-schema-B0Etuo-o.js} +2 -2
  55. package/dist/{input-schema-D2QAtHZH.js.map → input-schema-B0Etuo-o.js.map} +1 -1
  56. package/dist/lifecycle/index.js +1 -1
  57. package/dist/{lifecycle-DGorpAmA.js → lifecycle-ClT4DmF2.js} +11 -3
  58. package/dist/lifecycle-ClT4DmF2.js.map +1 -0
  59. package/dist/mcp/client/index.d.ts +1 -1
  60. package/dist/mcp/index.d.ts +1 -1
  61. package/dist/mcp/index.js +17 -3
  62. package/dist/mcp/index.js.map +1 -1
  63. package/dist/mcp/server/index.js +1 -1
  64. package/dist/models/ai-sdk/index.d.ts +31 -326
  65. package/dist/models/ai-sdk/index.js +1 -1
  66. package/dist/models/opencode/index.d.ts +25 -13
  67. package/dist/models/opencode/index.js +16 -11
  68. package/dist/models/opencode/index.js.map +1 -1
  69. package/dist/models/pi-ai/index.d.ts +17 -13
  70. package/dist/models/pi-ai/index.js +89 -36
  71. package/dist/models/pi-ai/index.js.map +1 -1
  72. package/dist/react.d.ts +2 -2
  73. package/dist/routing/index.d.ts +1 -1
  74. package/dist/serializable.d.ts +1 -1
  75. package/dist/{session-store-DoPY12xM.d.ts → session-store-_IK6dV-w.d.ts} +14 -2
  76. package/dist/{session-store-Do4xkDcf.js → session-store-yvwq1f9x.js} +55 -2
  77. package/dist/session-store-yvwq1f9x.js.map +1 -0
  78. package/dist/{settings-C45hDliX.js → settings-Dk2il2Hf.js} +10 -4
  79. package/dist/settings-Dk2il2Hf.js.map +1 -0
  80. package/dist/skills/index.d.ts +22 -28
  81. package/dist/skills/index.js +183 -143
  82. package/dist/skills/index.js.map +1 -1
  83. package/dist/{src-CQxJFv4l.js → src-6lr7MtM_.js} +3 -3
  84. package/dist/src-6lr7MtM_.js.map +1 -0
  85. package/dist/sub-routing.d.ts +1 -1
  86. package/dist/{tool-cQRkwSZZ.js → tool-DkT_DJBm.js} +2 -2
  87. package/dist/tool-DkT_DJBm.js.map +1 -0
  88. package/dist/transport-CPh7YGiw.d.ts +623 -0
  89. package/dist/voice/index.d.ts +1 -1
  90. package/dist/web/fetch/tools/pi.js +2 -2
  91. package/dist/web/fetch/tools/pi.js.map +1 -1
  92. package/dist/web/search/index.js +1 -1
  93. package/dist/web/search/tools/ai-sdk.js +2 -2
  94. package/dist/web/search/tools/pi.js +3 -3
  95. package/dist/web/search/tools/pi.js.map +1 -1
  96. package/dist/web/search/tools/tanstack-ai.js +2 -2
  97. package/dist/websockets/index.js +1 -1
  98. package/dist/{websockets-C3zy8YR0.js → websockets-CqxnntmE.js} +2 -2
  99. package/dist/{websockets-C3zy8YR0.js.map → websockets-CqxnntmE.js.map} +1 -1
  100. package/dist/workflow-types.d.ts +1 -1
  101. package/dist/workflows.d.ts +1 -1
  102. package/dist/workflows.js +1 -1
  103. package/docs/browse-the-web.md +1 -1
  104. package/docs/chat-agents.md +19 -18
  105. package/docs/harnesses/pi.md +47 -18
  106. package/docs/mcp-client.md +1 -1
  107. package/docs/mcp-servers.md +1 -1
  108. package/docs/models-pi-ai.md +70 -17
  109. package/docs/models.md +219 -152
  110. package/package.json +14 -14
  111. package/dist/ai-sdk-Bdsf6mSA.js.map +0 -1
  112. package/dist/browser-CEdNlsaD.js.map +0 -1
  113. package/dist/handler-stateless-VvrWSAVA.js.map +0 -1
  114. package/dist/images-Ce9SCu3X.js.map +0 -1
  115. package/dist/lifecycle-DGorpAmA.js.map +0 -1
  116. package/dist/session-store-Do4xkDcf.js.map +0 -1
  117. package/dist/settings-C45hDliX.js.map +0 -1
  118. package/dist/settings-NgGwF7Qt.d.ts +0 -331
  119. package/dist/src-CQxJFv4l.js.map +0 -1
  120. package/dist/tool-cQRkwSZZ.js.map +0 -1
  121. package/dist/transport-k2mFrtL8.d.ts +0 -200
@@ -30,7 +30,10 @@ type OpenCodeInput =
30
30
  type OpenCodeMessage = Awaited<
31
31
  ReturnType<OpenCode["sessions"]["context"]>
32
32
  >[number];
33
- /** One of OpenCode's events: durable transcript events and live deltas. */
33
+ /**
34
+ * One of OpenCode's own events: durable transcript events and live deltas.
35
+ * `session.events()` translates these into `OpenCodeSessionEvent`s.
36
+ */
34
37
  type OpenCodeEvent =
35
38
  ReturnType<OpenCode["events"]["subscribe"]> extends AsyncIterable<infer E>
36
39
  ? E
@@ -101,6 +104,173 @@ type OpenCodeSessionInfo = {
101
104
  readonly title?: string /** Running, input waiting, or a turn a restart cut off, about to resume. */;
102
105
  readonly busy: boolean;
103
106
  };
107
+ /** A permission OpenCode asked for, waiting on a reply. */
108
+ type OpenCodePermission = {
109
+ readonly id: string;
110
+ readonly session: OpenCodeSessionId /** What OpenCode wants to do, such as `edit` or `webfetch`. */;
111
+ readonly action: string /** What the action applies to, such as paths or URLs. */;
112
+ readonly resources: readonly string[];
113
+ readonly message?: string;
114
+ };
115
+ /** How an assistant message's step ended, as OpenCode reports it. */
116
+ type OpenCodeFinish =
117
+ | "stop"
118
+ | "length"
119
+ | "tool-calls"
120
+ | "content-filter"
121
+ | "error"
122
+ | "unknown";
123
+ /**
124
+ * One event on a session's stream, translated from OpenCode's events into
125
+ * the same kinds of event `PiHarness` and `ContainerHarness` report.
126
+ * `messageId` is OpenCode's assistant message id. Text and reasoning are
127
+ * addressed by `ordinal`, their position among the message's parts of the
128
+ * same kind.
129
+ */
130
+ type OpenCodeSessionEvent =
131
+ /**
132
+ * An input is in OpenCode's inbox. Every input goes through it, so this
133
+ * comes before `operation_start` even when the session is idle.
134
+ */
135
+ | {
136
+ readonly type: "operation_queued";
137
+ readonly operationId: string;
138
+ } /** A queued input was withdrawn before it ran. */
139
+ | {
140
+ readonly type: "operation_withdrawn";
141
+ readonly operationId: string;
142
+ } /** An input joined the transcript; the next assistant message answers it. */
143
+ | {
144
+ readonly type: "operation_start";
145
+ readonly operationId: string;
146
+ } /** An input has its answer: the same result `wait()` returns. */
147
+ | {
148
+ readonly type: "operation_end";
149
+ readonly result: OpenCodeOperationResult;
150
+ } /** OpenCode started a run. One run answers every input it drains. */
151
+ | {
152
+ readonly type: "run_start";
153
+ }
154
+ | {
155
+ readonly type: "run_end";
156
+ readonly outcome: "succeeded" | "failed" | "interrupted";
157
+ readonly error?: string;
158
+ }
159
+ | {
160
+ readonly type: "message_start";
161
+ readonly messageId: string;
162
+ readonly agent: string;
163
+ readonly model: {
164
+ readonly providerID: string;
165
+ readonly id: string;
166
+ };
167
+ }
168
+ | {
169
+ readonly type: "text_delta";
170
+ readonly messageId: string;
171
+ readonly ordinal: number;
172
+ readonly delta: string;
173
+ } /** A text part is complete; `text` is the whole part. */
174
+ | {
175
+ readonly type: "text_end";
176
+ readonly messageId: string;
177
+ readonly ordinal: number;
178
+ readonly text: string;
179
+ }
180
+ | {
181
+ readonly type: "reasoning_delta";
182
+ readonly messageId: string;
183
+ readonly ordinal: number;
184
+ readonly delta: string;
185
+ }
186
+ | {
187
+ readonly type: "reasoning_end";
188
+ readonly messageId: string;
189
+ readonly ordinal: number;
190
+ readonly text: string;
191
+ } /** The model called a tool and OpenCode has its whole input. */
192
+ | {
193
+ readonly type: "tool_start";
194
+ readonly messageId: string;
195
+ readonly toolCallId: string;
196
+ readonly name: string;
197
+ readonly input: Readonly<Record<string, unknown>>;
198
+ }
199
+ | {
200
+ readonly type: "tool_end";
201
+ readonly messageId: string;
202
+ readonly toolCallId: string;
203
+ readonly name: string;
204
+ readonly error: boolean /** The tool's text output, or its error message. */;
205
+ readonly output: string;
206
+ }
207
+ | {
208
+ readonly type: "message_end";
209
+ readonly messageId: string;
210
+ readonly finish: OpenCodeFinish;
211
+ readonly error?: string;
212
+ } /** A model call failed and OpenCode will try again at `at`. */
213
+ | {
214
+ readonly type: "retry_scheduled";
215
+ readonly messageId: string;
216
+ readonly attempt: number;
217
+ readonly at: number;
218
+ readonly error: string;
219
+ }
220
+ | {
221
+ readonly type: "compaction_start";
222
+ readonly reason: "auto" | "manual";
223
+ }
224
+ /**
225
+ * OpenCode compacted the session: `messages()` is now shorter, and
226
+ * `history()` has a compaction message. Read the transcript again.
227
+ */
228
+ | {
229
+ readonly type: "transcript_reset";
230
+ readonly reason: "compaction";
231
+ }
232
+ | {
233
+ readonly type: "permission_asked";
234
+ readonly permission: OpenCodePermission;
235
+ }
236
+ | {
237
+ readonly type: "permission_replied";
238
+ readonly permissionId: string;
239
+ readonly reply: "once" | "always" | "reject";
240
+ }
241
+ /**
242
+ * Something went wrong outside a model step: `compaction_failed`, or
243
+ * `events_failed` when OpenCode's event stream ended with an error. No
244
+ * more events follow `events_failed`; open a new stream.
245
+ */
246
+ | {
247
+ readonly type: "fault";
248
+ readonly code: "compaction_failed" | "events_failed";
249
+ readonly message: string;
250
+ };
251
+ /** A session's state when its event stream opens. */
252
+ type OpenCodeSessionSnapshot = {
253
+ readonly session: OpenCodeSessionId /** The whole transcript, compacted messages included: `history()`. */;
254
+ readonly messages: readonly OpenCodeMessage[];
255
+ readonly pending: readonly OpenCodePendingOperation[];
256
+ readonly busy: boolean /** Permissions OpenCode is waiting on. */;
257
+ readonly permissions: readonly OpenCodePermission[];
258
+ };
259
+ /**
260
+ * A session's events: the snapshot taken when the stream opened, then every
261
+ * event after it, in batches, including any that arrived before `start()`.
262
+ * An event may repeat what the snapshot already shows; messages are keyed
263
+ * by id. Live: events reach listeners in the isolate that runs the harness,
264
+ * and the stream holds OpenCode open until `stop()`. Call `stop()` when
265
+ * done, started or not.
266
+ */
267
+ type OpenCodeEventStream = {
268
+ readonly snapshot: OpenCodeSessionSnapshot /** Start delivering events. Call once. */;
269
+ start(
270
+ listener: (events: readonly OpenCodeSessionEvent[]) => void
271
+ ): void /** Stop delivering events and release OpenCode. */;
272
+ stop(): Promise<void>;
273
+ };
104
274
  //#endregion
105
275
  //#region src/harness/opencode/harness.d.ts
106
276
  /** The root session's id. OpenCode session ids start with `ses`. */
@@ -304,12 +474,13 @@ declare class OpenCodeSession {
304
474
  */
305
475
  history(): Promise<OpenCodeMessage[]>;
306
476
  /**
307
- * OpenCode's events for this session as they happen, durable and live
308
- * (text deltas, tool input), until `signal` aborts. Read the transcript
309
- * with `messages()` first for a snapshot; use `log()` to resume from a
310
- * durable position instead.
477
+ * The session's state now, then everything that happens to it after, as
478
+ * `OpenCodeSessionEvent`s: the same kinds of event `PiHarness` reports,
479
+ * live text deltas included. The stream holds OpenCode open until
480
+ * `stop()`. For OpenCode's own events, durable and resumable by sequence
481
+ * number, use `log()`.
311
482
  */
312
- events(signal?: AbortSignal): AsyncIterable<OpenCodeEvent>;
483
+ events(): Promise<OpenCodeEventStream>;
313
484
  /**
314
485
  * The session's durable events after sequence number `after` (from the
315
486
  * start by default), then, with `follow`, new ones as they are committed,
@@ -344,6 +515,8 @@ declare class OpenCodeSessions {
344
515
  export {
345
516
  type OpenCode,
346
517
  type OpenCodeEvent,
518
+ type OpenCodeEventStream,
519
+ type OpenCodeFinish,
347
520
  OpenCodeHarness,
348
521
  type OpenCodeHarnessContext,
349
522
  type OpenCodeHarnessFactory,
@@ -354,13 +527,16 @@ export {
354
527
  type OpenCodeModel,
355
528
  type OpenCodeOperationResult,
356
529
  type OpenCodePendingOperation,
530
+ type OpenCodePermission,
357
531
  type OpenCodePromptResponse,
358
532
  type OpenCodeReceipt,
359
533
  OpenCodeSession,
360
534
  type OpenCodeSessionDefaults,
535
+ type OpenCodeSessionEvent,
361
536
  type OpenCodeSessionId,
362
537
  type OpenCodeSessionInfo,
363
538
  type OpenCodeSessionOptions,
539
+ type OpenCodeSessionSnapshot,
364
540
  OpenCodeSessions,
365
541
  type OpenCodeSubmitOptions,
366
542
  type OpenCodeWhenBusy,
@@ -1,7 +1,7 @@
1
1
  import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "../../classPrivateFieldGet2-DZBYAB34.js";
2
2
  import { t as _classPrivateMethodInitSpec } from "../../classPrivateMethodInitSpec-qMjJ6sHQ.js";
3
3
  import { t as LifecycleCapability } from "../../capability-DBAKc6UZ.js";
4
- import "../../lifecycle-DGorpAmA.js";
4
+ import "../../lifecycle-ClT4DmF2.js";
5
5
  //#region src/harness/table-prefix.ts
6
6
  /**
7
7
  * A view of `storage` whose SQL is rewritten into `prefix`'s namespace.
@@ -284,6 +284,268 @@ function closing(sql, start, close) {
284
284
  return sql.length;
285
285
  }
286
286
  //#endregion
287
+ //#region src/harness/opencode/operation-id.ts
288
+ const OPERATION_ID = /^[A-Za-z0-9._~-]+$/;
289
+ /** OpenCode's id for the user message an operation submits. */
290
+ function messageIdOf(operationId) {
291
+ if (!OPERATION_ID.test(operationId)) throw new TypeError(`Invalid operation id ${JSON.stringify(operationId)}: use letters, digits, ".", "_", "~" and "-"`);
292
+ return `msg_${operationId}`;
293
+ }
294
+ /** The operation id of a message or inbox item, the inverse of `messageIdOf`. */
295
+ function operationIdOf(messageId) {
296
+ return messageId.startsWith("msg_") ? messageId.slice(4) : messageId;
297
+ }
298
+ //#endregion
299
+ //#region src/harness/opencode/events.ts
300
+ /** The text parts of a tool's result, joined. */
301
+ function toolText(content) {
302
+ return (content ?? []).map((part) => "text" in part ? part.text : "").join("\n");
303
+ }
304
+ /** The session an OpenCode event belongs to, if it belongs to one. */
305
+ function sessionIdOf(event) {
306
+ const data = event.data;
307
+ return typeof data === "object" && data !== null && "sessionID" in data && typeof data.sessionID === "string" ? data.sessionID : void 0;
308
+ }
309
+ function answerOf(message) {
310
+ if (message.type !== "assistant") return void 0;
311
+ const text = /* @__PURE__ */ new Map();
312
+ for (const part of message.content) if (part.type === "text") text.set(text.size, part.text);
313
+ return {
314
+ id: message.id,
315
+ text,
316
+ error: message.error?.message
317
+ };
318
+ }
319
+ var _session = /* @__PURE__ */ new WeakMap();
320
+ var _toolNames = /* @__PURE__ */ new WeakMap();
321
+ var _answering = /* @__PURE__ */ new WeakMap();
322
+ var _ended = /* @__PURE__ */ new WeakMap();
323
+ var _answered = /* @__PURE__ */ new WeakMap();
324
+ var _answer = /* @__PURE__ */ new WeakMap();
325
+ var _SessionEventProjector_brand = /* @__PURE__ */ new WeakSet();
326
+ /**
327
+ * Translates one session's OpenCode events into `OpenCodeSessionEvent`s.
328
+ *
329
+ * OpenCode's events leave out some of what the translated ones carry, so
330
+ * the projector keeps it: tool names (`session.tool.called` has only the
331
+ * call id), and which inputs the current answer is for, so it can report
332
+ * `operation_end`. It starts from the stream's snapshot, so a stream opened
333
+ * mid-run knows the run's inputs and tools.
334
+ *
335
+ * `operation_end` follows the rule `wait()` uses on the transcript: an
336
+ * input's answer ends at the next input that joins after an assistant
337
+ * message, or at the end of the run.
338
+ *
339
+ * Events that arrive while the snapshot is read are projected after it, so
340
+ * some repeat what the snapshot shows. Projection is idempotent for those:
341
+ * an input the projector already knows is not started or ended again.
342
+ */
343
+ var SessionEventProjector = class {
344
+ constructor(snapshot) {
345
+ _classPrivateMethodInitSpec(this, _SessionEventProjector_brand);
346
+ _classPrivateFieldInitSpec(this, _session, void 0);
347
+ _classPrivateFieldInitSpec(this, _toolNames, /* @__PURE__ */ new Map());
348
+ _classPrivateFieldInitSpec(this, _answering, []);
349
+ _classPrivateFieldInitSpec(this, _ended, /* @__PURE__ */ new Set());
350
+ _classPrivateFieldInitSpec(this, _answered, false);
351
+ _classPrivateFieldInitSpec(this, _answer, void 0);
352
+ _classPrivateFieldSet2(_session, this, snapshot.session);
353
+ for (const message of snapshot.messages) {
354
+ if (message.type !== "assistant") continue;
355
+ for (const part of message.content) if (part.type === "tool") _classPrivateFieldGet2(_toolNames, this).set(part.id, part.name);
356
+ }
357
+ _classPrivateFieldSet2(_answering, this, snapshot.pending.filter((operation) => operation.status === "running").map((operation) => operation.operationId));
358
+ if (_classPrivateFieldGet2(_answering, this).length > 0) {
359
+ const last = snapshot.messages.at(-1);
360
+ _classPrivateFieldSet2(_answer, this, last === void 0 ? void 0 : answerOf(last));
361
+ _classPrivateFieldSet2(_answered, this, _classPrivateFieldGet2(_answer, this) !== void 0);
362
+ }
363
+ }
364
+ /** The translated events for one OpenCode event; often none. */
365
+ project(event) {
366
+ switch (event.type) {
367
+ case "session.inbox.enqueued": return event.data.item.type === "user" ? [{
368
+ type: "operation_queued",
369
+ operationId: operationIdOf(event.data.inboxID)
370
+ }] : [];
371
+ case "session.inbox.cancelled": return [{
372
+ type: "operation_withdrawn",
373
+ operationId: operationIdOf(event.data.inboxID)
374
+ }];
375
+ case "session.inbox.delivered": {
376
+ const operationId = operationIdOf(event.data.inboxID);
377
+ if (_classPrivateFieldGet2(_answering, this).includes(operationId) || _classPrivateFieldGet2(_ended, this).has(operationId)) return [];
378
+ const ended = _classPrivateFieldGet2(_answered, this) ? _assertClassBrand(_SessionEventProjector_brand, this, _settle).call(this, "succeeded") : [];
379
+ _classPrivateFieldGet2(_answering, this).push(operationId);
380
+ return [...ended, {
381
+ type: "operation_start",
382
+ operationId
383
+ }];
384
+ }
385
+ case "session.execution.started":
386
+ if (_classPrivateFieldGet2(_answering, this).length === 0) {
387
+ _classPrivateFieldSet2(_answer, this, void 0);
388
+ _classPrivateFieldSet2(_answered, this, false);
389
+ }
390
+ return [{ type: "run_start" }];
391
+ case "session.execution.succeeded": return [..._assertClassBrand(_SessionEventProjector_brand, this, _settle).call(this, "succeeded"), runEnd("succeeded")];
392
+ case "session.execution.failed": return [..._assertClassBrand(_SessionEventProjector_brand, this, _settle).call(this, "failed", event.data.error.message), runEnd("failed", event.data.error.message)];
393
+ case "session.execution.interrupted": return [..._assertClassBrand(_SessionEventProjector_brand, this, _settle).call(this, "interrupted"), runEnd("interrupted")];
394
+ case "session.step.started":
395
+ _classPrivateFieldSet2(_answered, this, true);
396
+ _classPrivateFieldSet2(_answer, this, {
397
+ id: event.data.assistantMessageID,
398
+ text: /* @__PURE__ */ new Map(),
399
+ error: void 0
400
+ });
401
+ return [{
402
+ type: "message_start",
403
+ messageId: event.data.assistantMessageID,
404
+ agent: event.data.agent,
405
+ model: {
406
+ providerID: event.data.model.providerID,
407
+ id: event.data.model.id
408
+ }
409
+ }];
410
+ case "session.step.ended": return [{
411
+ type: "message_end",
412
+ messageId: event.data.assistantMessageID,
413
+ finish: event.data.finish
414
+ }];
415
+ case "session.step.failed":
416
+ if (_classPrivateFieldGet2(_answer, this)?.id === event.data.assistantMessageID) _classPrivateFieldGet2(_answer, this).error = event.data.error.message;
417
+ return [{
418
+ type: "message_end",
419
+ messageId: event.data.assistantMessageID,
420
+ finish: "error",
421
+ error: event.data.error.message
422
+ }];
423
+ case "session.text.delta":
424
+ case "session.reasoning.delta": return [{
425
+ type: event.type === "session.text.delta" ? "text_delta" : "reasoning_delta",
426
+ messageId: event.data.assistantMessageID,
427
+ ordinal: event.data.ordinal,
428
+ delta: event.data.delta
429
+ }];
430
+ case "session.text.ended":
431
+ if (_classPrivateFieldGet2(_answer, this)?.id === event.data.assistantMessageID) _classPrivateFieldGet2(_answer, this).text.set(event.data.ordinal, event.data.text);
432
+ return [{
433
+ type: "text_end",
434
+ messageId: event.data.assistantMessageID,
435
+ ordinal: event.data.ordinal,
436
+ text: event.data.text
437
+ }];
438
+ case "session.reasoning.ended": return [{
439
+ type: "reasoning_end",
440
+ messageId: event.data.assistantMessageID,
441
+ ordinal: event.data.ordinal,
442
+ text: event.data.text
443
+ }];
444
+ case "session.tool.input.started":
445
+ _classPrivateFieldGet2(_toolNames, this).set(event.data.id, event.data.name);
446
+ return [];
447
+ case "session.tool.called": return [{
448
+ type: "tool_start",
449
+ messageId: event.data.assistantMessageID,
450
+ toolCallId: event.data.id,
451
+ name: _classPrivateFieldGet2(_toolNames, this).get(event.data.id) ?? "",
452
+ input: event.data.input
453
+ }];
454
+ case "session.tool.success": return [{
455
+ type: "tool_end",
456
+ messageId: event.data.assistantMessageID,
457
+ toolCallId: event.data.id,
458
+ name: _classPrivateFieldGet2(_toolNames, this).get(event.data.id) ?? "",
459
+ error: false,
460
+ output: toolText(event.data.content)
461
+ }];
462
+ case "session.tool.failed": return [{
463
+ type: "tool_end",
464
+ messageId: event.data.assistantMessageID,
465
+ toolCallId: event.data.id,
466
+ name: _classPrivateFieldGet2(_toolNames, this).get(event.data.id) ?? "",
467
+ error: true,
468
+ output: event.data.error.message
469
+ }];
470
+ case "session.retry.scheduled": return [{
471
+ type: "retry_scheduled",
472
+ messageId: event.data.assistantMessageID,
473
+ attempt: event.data.attempt,
474
+ at: event.data.at,
475
+ error: event.data.error.message
476
+ }];
477
+ case "session.compaction.started": return [{
478
+ type: "compaction_start",
479
+ reason: event.data.reason
480
+ }];
481
+ case "session.compaction.ended": return [{
482
+ type: "transcript_reset",
483
+ reason: "compaction"
484
+ }];
485
+ case "session.compaction.failed": return [{
486
+ type: "fault",
487
+ code: "compaction_failed",
488
+ message: event.data.error.message
489
+ }];
490
+ case "permission.asked": return [{
491
+ type: "permission_asked",
492
+ permission: {
493
+ id: event.data.id,
494
+ session: event.data.sessionID,
495
+ action: event.data.action,
496
+ resources: event.data.resources,
497
+ ...event.data.message === void 0 ? {} : { message: event.data.message }
498
+ }
499
+ }];
500
+ case "permission.replied": return [{
501
+ type: "permission_replied",
502
+ permissionId: event.data.requestID,
503
+ reply: event.data.reply
504
+ }];
505
+ default: return [];
506
+ }
507
+ }
508
+ };
509
+ /** End every input the latest answer was for. */
510
+ function _settle(outcome, runError) {
511
+ const answer = _classPrivateFieldGet2(_answer, this);
512
+ const text = [...(answer?.text ?? /* @__PURE__ */ new Map()).entries()].sort(([a], [b]) => a - b).map(([, part]) => part).join("");
513
+ const error = answer?.error ?? runError;
514
+ const ended = _classPrivateFieldGet2(_answering, this).map((operationId) => {
515
+ _classPrivateFieldGet2(_ended, this).add(operationId);
516
+ return {
517
+ type: "operation_end",
518
+ result: resultOf(_classPrivateFieldGet2(_session, this), operationId, outcome, text, error)
519
+ };
520
+ });
521
+ _classPrivateFieldSet2(_answering, this, []);
522
+ _classPrivateFieldSet2(_answered, this, false);
523
+ _classPrivateFieldSet2(_answer, this, void 0);
524
+ return ended;
525
+ }
526
+ function runEnd(outcome, error) {
527
+ return {
528
+ type: "run_end",
529
+ outcome,
530
+ ...error === void 0 ? {} : { error }
531
+ };
532
+ }
533
+ /** The result `wait()` would return for the same ending. */
534
+ function resultOf(session, operationId, outcome, text, error) {
535
+ if (outcome === "succeeded" && error === void 0) return {
536
+ operationId,
537
+ session,
538
+ status: "done",
539
+ text
540
+ };
541
+ return {
542
+ operationId,
543
+ session,
544
+ status: "unanswered",
545
+ reason: outcome === "interrupted" ? "interrupted" : error ?? "failed"
546
+ };
547
+ }
548
+ //#endregion
287
549
  //#region src/harness/opencode/harness.ts
288
550
  /** The root session's id. OpenCode session ids start with `ses`. */
289
551
  const ROOT_SESSION = "ses_root";
@@ -319,16 +581,6 @@ function wakeJobId(session) {
319
581
  function sessionOfJob(payload) {
320
582
  return typeof payload === "object" && payload !== null && "session" in payload && typeof payload.session === "string" ? payload.session : void 0;
321
583
  }
322
- const OPERATION_ID = /^[A-Za-z0-9._~-]+$/;
323
- /** OpenCode's id for the user message an operation submits. */
324
- function messageIdOf(operationId) {
325
- if (!OPERATION_ID.test(operationId)) throw new TypeError(`Invalid operation id ${JSON.stringify(operationId)}: use letters, digits, ".", "_", "~" and "-"`);
326
- return `msg_${operationId}`;
327
- }
328
- /** The operation id of a message or inbox item, the inverse of `messageIdOf`. */
329
- function operationIdOf(messageId) {
330
- return messageId.startsWith("msg_") ? messageId.slice(4) : messageId;
331
- }
332
584
  /** Whether an OpenCode client error is the tagged error `tag`. */
333
585
  function isTagged(error, tag) {
334
586
  return typeof error === "object" && error !== null && "_tag" in error && error._tag === tag;
@@ -343,11 +595,6 @@ function textOf(message) {
343
595
  if (message?.type !== "assistant") return "";
344
596
  return message.content.map((part) => part.type === "text" ? part.text : "").join("");
345
597
  }
346
- function sessionIdOf(event) {
347
- if (!("data" in event)) return void 0;
348
- const data = event.data;
349
- return typeof data === "object" && data !== null && "sessionID" in data && typeof data.sessionID === "string" ? data.sessionID : void 0;
350
- }
351
598
  var _options = /* @__PURE__ */ new WeakMap();
352
599
  var _waits = /* @__PURE__ */ new WeakMap();
353
600
  var _admitting = /* @__PURE__ */ new WeakMap();
@@ -910,6 +1157,7 @@ function delay(ms, signal) {
910
1157
  });
911
1158
  }
912
1159
  var _harness = /* @__PURE__ */ new WeakMap();
1160
+ var _OpenCodeSession_brand = /* @__PURE__ */ new WeakSet();
913
1161
  /** One OpenCode session, addressed through the harness. */
914
1162
  var OpenCodeSession = class {
915
1163
  /**
@@ -917,6 +1165,7 @@ var OpenCodeSession = class {
917
1165
  * @param id - The session's id.
918
1166
  */
919
1167
  constructor(harness, id) {
1168
+ _classPrivateMethodInitSpec(this, _OpenCodeSession_brand);
920
1169
  _classPrivateFieldInitSpec(this, _harness, void 0);
921
1170
  _classPrivateFieldSet2(_harness, this, harness);
922
1171
  this.id = id;
@@ -1001,18 +1250,79 @@ var OpenCodeSession = class {
1001
1250
  });
1002
1251
  }
1003
1252
  /**
1004
- * OpenCode's events for this session as they happen, durable and live
1005
- * (text deltas, tool input), until `signal` aborts. Read the transcript
1006
- * with `messages()` first for a snapshot; use `log()` to resume from a
1007
- * durable position instead.
1253
+ * The session's state now, then everything that happens to it after, as
1254
+ * `OpenCodeSessionEvent`s: the same kinds of event `PiHarness` reports,
1255
+ * live text deltas included. The stream holds OpenCode open until
1256
+ * `stop()`. For OpenCode's own events, durable and resumable by sequence
1257
+ * number, use `log()`.
1008
1258
  */
1009
- async *events(signal) {
1259
+ async events() {
1010
1260
  const release = _classPrivateFieldGet2(_harness, this).lease();
1261
+ const controller = new AbortController();
1262
+ const early = [];
1263
+ const pending = [];
1264
+ let projector;
1265
+ let listener;
1266
+ let stopped = false;
1267
+ const deliver = (events) => {
1268
+ if (events.length === 0 || stopped) return;
1269
+ if (listener === void 0) pending.push(...events);
1270
+ else listener(events);
1271
+ };
1272
+ let attached;
1273
+ const subscribed = new Promise((resolve) => {
1274
+ attached = resolve;
1275
+ });
1276
+ let pump = Promise.resolve();
1011
1277
  try {
1012
1278
  const opencode = await _classPrivateFieldGet2(_harness, this).opencode();
1013
- for await (const event of opencode.events.subscribe(signal ? { signal } : {})) if (sessionIdOf(event) === this.id) yield event;
1014
- } finally {
1279
+ pump = (async () => {
1280
+ try {
1281
+ for await (const event of opencode.events.subscribe({ signal: controller.signal })) {
1282
+ if (event.type === "server.connected") attached();
1283
+ if (sessionIdOf(event) !== this.id) continue;
1284
+ if (projector === void 0) early.push(event);
1285
+ else deliver(projector.project(event));
1286
+ }
1287
+ } catch (error) {
1288
+ if (!controller.signal.aborted) deliver([{
1289
+ type: "fault",
1290
+ code: "events_failed",
1291
+ message: describe(error)
1292
+ }]);
1293
+ } finally {
1294
+ attached();
1295
+ }
1296
+ })();
1297
+ await subscribed;
1298
+ const snapshot = await _assertClassBrand(_OpenCodeSession_brand, this, _snapshot).call(this, opencode);
1299
+ projector = new SessionEventProjector(snapshot);
1300
+ for (const event of early.splice(0)) deliver(projector.project(event));
1301
+ let stopping;
1302
+ return {
1303
+ snapshot,
1304
+ start: (next) => {
1305
+ if (listener !== void 0) throw new Error("OpenCodeEventStream.start() was already called");
1306
+ if (stopping !== void 0) return;
1307
+ listener = next;
1308
+ deliver(pending.splice(0));
1309
+ },
1310
+ stop: () => {
1311
+ stopping ??= (async () => {
1312
+ stopped = true;
1313
+ pending.length = 0;
1314
+ controller.abort();
1315
+ await pump;
1316
+ await release();
1317
+ })();
1318
+ return stopping;
1319
+ }
1320
+ };
1321
+ } catch (error) {
1322
+ controller.abort();
1323
+ await pump;
1015
1324
  await release();
1325
+ throw error;
1016
1326
  }
1017
1327
  }
1018
1328
  /**
@@ -1040,6 +1350,27 @@ var OpenCodeSession = class {
1040
1350
  return _classPrivateFieldGet2(_harness, this).use(async (opencode) => sessionBusy(opencode, this.id, await opencode.sessions.active()));
1041
1351
  }
1042
1352
  };
1353
+ async function _snapshot(opencode) {
1354
+ const [messages, busy, pending, permissions] = await Promise.all([
1355
+ this.history(),
1356
+ this.busy(),
1357
+ _classPrivateFieldGet2(_harness, this).pending({ session: this.id }),
1358
+ opencode.permission.list({ sessionID: this.id })
1359
+ ]);
1360
+ return {
1361
+ session: this.id,
1362
+ messages,
1363
+ busy,
1364
+ pending,
1365
+ permissions: permissions.map((request) => ({
1366
+ id: request.id,
1367
+ session: request.sessionID,
1368
+ action: request.action,
1369
+ resources: request.resources,
1370
+ ...request.message === void 0 ? {} : { message: request.message }
1371
+ }))
1372
+ };
1373
+ }
1043
1374
  var _harness2 = /* @__PURE__ */ new WeakMap();
1044
1375
  /** Every OpenCode session in this object. */
1045
1376
  var OpenCodeSessions = class {