@ai-sdk/harness 0.0.0-6b196531-20260710185421

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 (77) hide show
  1. package/CHANGELOG.md +414 -0
  2. package/LICENSE +13 -0
  3. package/README.md +176 -0
  4. package/agent/index.ts +56 -0
  5. package/bridge/index.ts +10 -0
  6. package/dist/agent/index.d.ts +1631 -0
  7. package/dist/agent/index.js +3491 -0
  8. package/dist/agent/index.js.map +1 -0
  9. package/dist/bridge/index.d.ts +129 -0
  10. package/dist/bridge/index.js +482 -0
  11. package/dist/bridge/index.js.map +1 -0
  12. package/dist/index.d.ts +1587 -0
  13. package/dist/index.js +517 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/utils/index.d.ts +329 -0
  16. package/dist/utils/index.js +1241 -0
  17. package/dist/utils/index.js.map +1 -0
  18. package/package.json +100 -0
  19. package/src/agent/harness-agent-session.ts +518 -0
  20. package/src/agent/harness-agent-settings.ts +187 -0
  21. package/src/agent/harness-agent-tool-approval-continuation.ts +94 -0
  22. package/src/agent/harness-agent-tool-types.ts +15 -0
  23. package/src/agent/harness-agent-types.ts +50 -0
  24. package/src/agent/harness-agent.ts +865 -0
  25. package/src/agent/internal/bootstrap-recipe.ts +124 -0
  26. package/src/agent/internal/bridge-port-registry.ts +52 -0
  27. package/src/agent/internal/harness-stream-text-result.ts +731 -0
  28. package/src/agent/internal/lifecycle-state-validation.ts +95 -0
  29. package/src/agent/internal/permission-mode.ts +50 -0
  30. package/src/agent/internal/resolve-observability.ts +128 -0
  31. package/src/agent/internal/run-prompt.ts +901 -0
  32. package/src/agent/internal/sandbox-bootstrap.ts +266 -0
  33. package/src/agent/internal/strip-work-dir.ts +68 -0
  34. package/src/agent/internal/to-harness-stream.ts +75 -0
  35. package/src/agent/internal/tool-filtering.ts +114 -0
  36. package/src/agent/internal/translate-stream-part.ts +221 -0
  37. package/src/agent/internal/turn-telemetry.ts +361 -0
  38. package/src/agent/observability/file-reporter.ts +206 -0
  39. package/src/agent/observability/index.ts +15 -0
  40. package/src/agent/observability/trace-tree-reporter.ts +122 -0
  41. package/src/agent/observability/types.ts +86 -0
  42. package/src/agent/prepare-harness-sandbox-template.ts +68 -0
  43. package/src/agent/prepare-sandbox-for-harness.ts +165 -0
  44. package/src/bridge/index.ts +797 -0
  45. package/src/errors/harness-capability-unsupported-error.ts +41 -0
  46. package/src/errors/harness-error.ts +22 -0
  47. package/src/index.ts +3 -0
  48. package/src/utils/ai-gateway-auth.ts +15 -0
  49. package/src/utils/bridge-diagnostics.ts +213 -0
  50. package/src/utils/bridge-ready.ts +277 -0
  51. package/src/utils/classify-disk-log.ts +43 -0
  52. package/src/utils/index.ts +31 -0
  53. package/src/utils/sandbox-channel.ts +525 -0
  54. package/src/utils/sandbox-home-dir.ts +22 -0
  55. package/src/utils/shell-quote.ts +3 -0
  56. package/src/utils/write-skills.ts +141 -0
  57. package/src/v1/harness-v1-bootstrap.ts +46 -0
  58. package/src/v1/harness-v1-bridge-protocol.ts +342 -0
  59. package/src/v1/harness-v1-builtin-tool.ts +138 -0
  60. package/src/v1/harness-v1-call-warning.ts +22 -0
  61. package/src/v1/harness-v1-diagnostic.ts +66 -0
  62. package/src/v1/harness-v1-lifecycle-state.ts +65 -0
  63. package/src/v1/harness-v1-metadata.ts +13 -0
  64. package/src/v1/harness-v1-network-sandbox-session.ts +123 -0
  65. package/src/v1/harness-v1-observability.ts +20 -0
  66. package/src/v1/harness-v1-permission-mode.ts +11 -0
  67. package/src/v1/harness-v1-prompt-control.ts +41 -0
  68. package/src/v1/harness-v1-prompt.ts +11 -0
  69. package/src/v1/harness-v1-sandbox-provider.ts +76 -0
  70. package/src/v1/harness-v1-session.ts +280 -0
  71. package/src/v1/harness-v1-skill.ts +36 -0
  72. package/src/v1/harness-v1-stream-part.ts +363 -0
  73. package/src/v1/harness-v1-tool-filtering.ts +25 -0
  74. package/src/v1/harness-v1-tool-spec.ts +31 -0
  75. package/src/v1/harness-v1.ts +94 -0
  76. package/src/v1/index.ts +99 -0
  77. package/utils/index.ts +1 -0
@@ -0,0 +1,1241 @@
1
+ // src/utils/sandbox-channel.ts
2
+ import {
3
+ safeParseJSON,
4
+ safeValidateTypes
5
+ } from "@ai-sdk/provider-utils";
6
+ var sleep = (ms) => new Promise((resolve) => {
7
+ var _a;
8
+ const t = setTimeout(resolve, ms);
9
+ (_a = t.unref) == null ? void 0 : _a.call(t);
10
+ });
11
+ var SandboxChannel = class {
12
+ constructor(options) {
13
+ this.listeners = /* @__PURE__ */ new Map();
14
+ this.buffered = /* @__PURE__ */ new Map();
15
+ this.onCloseHandlers = /* @__PURE__ */ new Set();
16
+ this.connected = false;
17
+ /** Host has begun teardown; suppresses reconnect so a bridge-side close finalises. */
18
+ this.closing = false;
19
+ /**
20
+ * Host has gracefully suspended (slice boundary). Inbound frames are ignored
21
+ * from this point so the cursor stops advancing exactly at the last delivered
22
+ * event — the bridge keeps the turn running and the not-yet-delivered tail is
23
+ * replayed to the next process on `resume`.
24
+ */
25
+ this.suspended = false;
26
+ /** Channel is fully torn down; `send` throws and `onClose` has fired. */
27
+ this.terminal = false;
28
+ this._lastSeenEventId = 0;
29
+ this.pendingSends = [];
30
+ this.dispatchChain = Promise.resolve();
31
+ var _a, _b, _c, _d, _e, _f, _g;
32
+ this.connectThunk = options.connect;
33
+ this.outboundSchema = options.outboundSchema;
34
+ this.onDebug = options.onDebug;
35
+ this.onDiagnostic = options.onDiagnostic;
36
+ this.onBridgeError = options.onBridgeError;
37
+ this.maxElapsedMs = (_b = (_a = options.reconnect) == null ? void 0 : _a.maxElapsedMs) != null ? _b : 3e4;
38
+ this.initialDelayMs = (_d = (_c = options.reconnect) == null ? void 0 : _c.initialDelayMs) != null ? _d : 50;
39
+ this.maxDelayMs = (_f = (_e = options.reconnect) == null ? void 0 : _e.maxDelayMs) != null ? _f : 2e3;
40
+ this._lastSeenEventId = (_g = options.initialLastSeenEventId) != null ? _g : 0;
41
+ }
42
+ /**
43
+ * Highest bridge event `seq` this channel has observed. Persist it (e.g. via
44
+ * the adapter's resume handle) so a future process can seed
45
+ * {@link SandboxChannelOptions.initialLastSeenEventId} and attach.
46
+ */
47
+ get lastSeenEventId() {
48
+ return this._lastSeenEventId;
49
+ }
50
+ /**
51
+ * Establish the initial connection. A single attempt — startup failures
52
+ * reject so the caller can fail `doStart` cleanly. Reconnect retries apply
53
+ * only to drops after a successful open.
54
+ *
55
+ * Pass `{ resume: true }` to attach to a bridge that is already mid-session:
56
+ * after the socket opens, the channel sends `{ type: 'resume', lastSeenEventId }`
57
+ * so the bridge replays everything past the seeded cursor. This is the
58
+ * cross-process attach handshake — identical to what a transient reconnect
59
+ * does, but triggered by the initial open from a new process.
60
+ */
61
+ async open(opts) {
62
+ if (this.terminal) {
63
+ throw new Error("SandboxChannel: cannot open a closed channel.");
64
+ }
65
+ const ws = await this.connectThunk();
66
+ this.wire(ws);
67
+ this.ws = ws;
68
+ this.connected = true;
69
+ if (opts == null ? void 0 : opts.resume) {
70
+ this.rawSend(
71
+ JSON.stringify({
72
+ type: "resume",
73
+ lastSeenEventId: this._lastSeenEventId
74
+ })
75
+ );
76
+ }
77
+ }
78
+ on(type, listener) {
79
+ let set = this.listeners.get(type);
80
+ if (!set) {
81
+ set = /* @__PURE__ */ new Set();
82
+ this.listeners.set(type, set);
83
+ }
84
+ set.add(listener);
85
+ const buffered = this.buffered.get(type);
86
+ if (buffered) {
87
+ this.buffered.delete(type);
88
+ for (const event of buffered) {
89
+ listener(event);
90
+ }
91
+ }
92
+ return () => {
93
+ set.delete(listener);
94
+ };
95
+ }
96
+ onClose(handler) {
97
+ this.onCloseHandlers.add(handler);
98
+ }
99
+ send(message) {
100
+ if (this.terminal) {
101
+ throw new Error(
102
+ `SandboxChannel: cannot send ${message.type} \u2014 channel is closed.`
103
+ );
104
+ }
105
+ this.rawSend(JSON.stringify(message));
106
+ }
107
+ /**
108
+ * Mark that the host is tearing the session down. The next socket close is
109
+ * then treated as terminal rather than triggering a reconnect. Call before
110
+ * sending a `shutdown` / `detach` message whose ack the bridge follows with a
111
+ * socket close.
112
+ */
113
+ beginClose() {
114
+ this.closing = true;
115
+ }
116
+ close() {
117
+ var _a;
118
+ if (this.terminal) return;
119
+ this.closing = true;
120
+ try {
121
+ (_a = this.ws) == null ? void 0 : _a.close();
122
+ } catch (e) {
123
+ }
124
+ this.enqueue(() => this.finalizeClose(1e3, "closed"));
125
+ }
126
+ interrupt(options) {
127
+ var _a;
128
+ const timeoutMs = (_a = options == null ? void 0 : options.timeoutMs) != null ? _a : 5e3;
129
+ return new Promise((resolve, reject) => {
130
+ var _a2;
131
+ let settled = false;
132
+ let unsub = () => {
133
+ };
134
+ const timer = setTimeout(() => {
135
+ complete(
136
+ new Error(
137
+ `SandboxChannel: interrupt was not acknowledged within ${timeoutMs}ms.`
138
+ )
139
+ );
140
+ }, timeoutMs);
141
+ (_a2 = timer.unref) == null ? void 0 : _a2.call(timer);
142
+ const complete = (error) => {
143
+ if (settled) return;
144
+ settled = true;
145
+ clearTimeout(timer);
146
+ unsub();
147
+ if (error) {
148
+ reject(error);
149
+ } else {
150
+ resolve();
151
+ }
152
+ };
153
+ unsub = this.on("bridge-interrupted", (event) => {
154
+ const response = event;
155
+ if (response.ok) {
156
+ complete();
157
+ return;
158
+ }
159
+ complete(
160
+ new Error(
161
+ `SandboxChannel: interrupt failed: ${formatControlError(
162
+ response.error
163
+ )}`
164
+ )
165
+ );
166
+ });
167
+ try {
168
+ this.send({ type: "interrupt" });
169
+ } catch (err) {
170
+ complete(err);
171
+ }
172
+ });
173
+ }
174
+ /**
175
+ * Gracefully suspend at a slice boundary: stop processing inbound frames
176
+ * (so the cursor freezes at the last delivered event), drain any frames
177
+ * already queued for dispatch, then close the socket and finalise with the
178
+ * close reason `'suspended'`. Resolves with the final `lastSeenEventId`.
179
+ *
180
+ * The bridge keeps the in-flight turn running (a host socket close never
181
+ * aborts it) and accumulates events past the cursor for the next process to
182
+ * `resume`. Unlike {@link close}, the consumer's active turn is wound down
183
+ * cleanly — adapters distinguish a suspend from an unexpected drop via the
184
+ * `'suspended'` close reason and resolve `done` successfully.
185
+ */
186
+ suspend() {
187
+ return new Promise((resolve) => {
188
+ if (this.terminal) {
189
+ resolve(this._lastSeenEventId);
190
+ return;
191
+ }
192
+ this.suspended = true;
193
+ this.closing = true;
194
+ this.onClose(() => resolve(this._lastSeenEventId));
195
+ this.enqueue(() => {
196
+ var _a;
197
+ try {
198
+ (_a = this.ws) == null ? void 0 : _a.close();
199
+ } catch (e) {
200
+ }
201
+ this.finalizeClose(1e3, "suspended");
202
+ });
203
+ });
204
+ }
205
+ isClosed() {
206
+ return this.terminal;
207
+ }
208
+ // ─── internals ──────────────────────────────────────────────────────
209
+ wire(ws) {
210
+ let dropped = false;
211
+ const onDrop = (code, reason) => {
212
+ if (dropped) return;
213
+ dropped = true;
214
+ if (ws !== this.ws) return;
215
+ this.connected = false;
216
+ if (this.closing) {
217
+ this.enqueue(() => this.finalizeClose(code, reason));
218
+ } else {
219
+ this.enqueue(() => this.reconnectLoop());
220
+ }
221
+ };
222
+ ws.on("message", (raw) => {
223
+ if (this.suspended) return;
224
+ const text = typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8");
225
+ this.enqueue(() => this.handleIncoming(text));
226
+ });
227
+ ws.on(
228
+ "close",
229
+ (code, reason) => {
230
+ var _a, _b;
231
+ return onDrop(code, (_b = (_a = reason == null ? void 0 : reason.toString) == null ? void 0 : _a.call(reason, "utf8")) != null ? _b : "");
232
+ }
233
+ );
234
+ ws.on("error", () => onDrop(1006, "socket error"));
235
+ }
236
+ async reconnectLoop() {
237
+ var _a, _b, _c;
238
+ if (this.terminal || this.closing) return;
239
+ const start = Date.now();
240
+ let attempt = 0;
241
+ let delay = this.initialDelayMs;
242
+ while (!this.terminal && !this.closing) {
243
+ attempt++;
244
+ (_a = this.onDebug) == null ? void 0 : _a.call(this, {
245
+ event: "reconnect-attempt",
246
+ attempt,
247
+ lastSeenEventId: this._lastSeenEventId
248
+ });
249
+ try {
250
+ const ws = await this.connectThunk();
251
+ if (this.terminal || this.closing) {
252
+ try {
253
+ ws.close();
254
+ } catch (e) {
255
+ }
256
+ return;
257
+ }
258
+ this.wire(ws);
259
+ this.ws = ws;
260
+ this.connected = true;
261
+ this.rawSend(
262
+ JSON.stringify({
263
+ type: "resume",
264
+ lastSeenEventId: this._lastSeenEventId
265
+ })
266
+ );
267
+ this.flushPending();
268
+ (_b = this.onDebug) == null ? void 0 : _b.call(this, {
269
+ event: "reconnected",
270
+ attempt,
271
+ lastSeenEventId: this._lastSeenEventId
272
+ });
273
+ return;
274
+ } catch (cause) {
275
+ if (Date.now() - start >= this.maxElapsedMs) {
276
+ this.finalizeClose(1006, "reconnect failed");
277
+ (_c = this.onDebug) == null ? void 0 : _c.call(this, {
278
+ event: "reconnect-failed",
279
+ attempts: attempt,
280
+ lastSeenEventId: this._lastSeenEventId,
281
+ cause
282
+ });
283
+ return;
284
+ }
285
+ await sleep(delay);
286
+ delay = Math.min(delay * 1.5, this.maxDelayMs);
287
+ }
288
+ }
289
+ }
290
+ rawSend(text) {
291
+ if (this.connected && this.ws) {
292
+ this.ws.send(text);
293
+ } else {
294
+ this.pendingSends.push(text);
295
+ }
296
+ }
297
+ flushPending() {
298
+ if (!this.connected || !this.ws) return;
299
+ const queued = this.pendingSends.splice(0);
300
+ for (const text of queued) this.ws.send(text);
301
+ }
302
+ enqueue(work) {
303
+ this.dispatchChain = this.dispatchChain.then(work);
304
+ }
305
+ async handleIncoming(text) {
306
+ const raw = await safeParseJSON({ text });
307
+ let seq;
308
+ if (raw.success && raw.value != null && typeof raw.value === "object" && typeof raw.value.seq === "number") {
309
+ seq = raw.value.seq;
310
+ }
311
+ const validated = await safeValidateTypes({
312
+ value: raw.success ? raw.value : void 0,
313
+ schema: this.outboundSchema
314
+ });
315
+ if (validated.success) {
316
+ this.dispatch(validated.value);
317
+ } else {
318
+ this.dispatch({
319
+ type: "error",
320
+ error: validated.error
321
+ });
322
+ }
323
+ if (seq !== void 0 && seq > this._lastSeenEventId) {
324
+ this._lastSeenEventId = seq;
325
+ }
326
+ }
327
+ dispatch(message) {
328
+ var _a, _b;
329
+ if (message.type === "sandbox-log" || message.type === "debug-event") {
330
+ (_a = this.onDiagnostic) == null ? void 0 : _a.call(
331
+ this,
332
+ message
333
+ );
334
+ return;
335
+ }
336
+ if (message.type === "error") {
337
+ (_b = this.onBridgeError) == null ? void 0 : _b.call(this, message);
338
+ }
339
+ const type = message.type;
340
+ const set = this.listeners.get(type);
341
+ if (!set || set.size === 0) {
342
+ let bucket = this.buffered.get(type);
343
+ if (!bucket) {
344
+ bucket = [];
345
+ this.buffered.set(type, bucket);
346
+ }
347
+ bucket.push(message);
348
+ return;
349
+ }
350
+ for (const listener of set) {
351
+ listener(message);
352
+ }
353
+ }
354
+ finalizeClose(code, reason) {
355
+ if (this.terminal) return;
356
+ this.terminal = true;
357
+ this.connected = false;
358
+ for (const h of this.onCloseHandlers) h(code, reason);
359
+ }
360
+ };
361
+ function formatControlError(error) {
362
+ if (error instanceof Error) return error.message;
363
+ if (error && typeof error === "object") {
364
+ const message = error.message;
365
+ if (typeof message === "string" && message.length > 0) return message;
366
+ }
367
+ if (typeof error === "string" && error.length > 0) return error;
368
+ return "unknown error";
369
+ }
370
+
371
+ // src/utils/classify-disk-log.ts
372
+ import { safeParseJSON as safeParseJSON2 } from "@ai-sdk/provider-utils";
373
+ async function classifyDiskLog(eventLog) {
374
+ if (!eventLog) return "rerun";
375
+ const lines = eventLog.split("\n").map((line) => line.trim()).filter(Boolean);
376
+ const lastLine = lines.at(-1);
377
+ if (lastLine == null) return "rerun";
378
+ const parsed = await safeParseJSON2({ text: lastLine });
379
+ if (parsed.success && parsed.value != null && typeof parsed.value === "object" && parsed.value.type === "finish") {
380
+ return "replay";
381
+ }
382
+ return "rerun";
383
+ }
384
+
385
+ // src/utils/ai-gateway-auth.ts
386
+ var DEFAULT_AI_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh";
387
+ function getAiGatewayAuthFromEnv({
388
+ env
389
+ }) {
390
+ var _a;
391
+ return {
392
+ apiKey: env.AI_GATEWAY_API_KEY || env.VERCEL_OIDC_TOKEN || void 0,
393
+ baseUrl: (_a = env.AI_GATEWAY_BASE_URL) != null ? _a : DEFAULT_AI_GATEWAY_BASE_URL
394
+ };
395
+ }
396
+
397
+ // src/utils/sandbox-home-dir.ts
398
+ import path from "path";
399
+ async function resolveSandboxHomeDir({
400
+ sandbox,
401
+ abortSignal
402
+ }) {
403
+ const result = await sandbox.run({
404
+ command: 'printf "%s" "$HOME"',
405
+ abortSignal
406
+ });
407
+ const homeDir = result.stdout.trim();
408
+ if (result.exitCode !== 0 || !homeDir || !path.posix.isAbsolute(homeDir)) {
409
+ throw new Error(
410
+ `Unable to resolve sandbox HOME directory: ${result.stderr || result.stdout}`
411
+ );
412
+ }
413
+ return homeDir;
414
+ }
415
+
416
+ // src/utils/shell-quote.ts
417
+ function shellQuote(value) {
418
+ return `'${value.replace(/'/g, `'\\''`)}'`;
419
+ }
420
+
421
+ // src/utils/write-skills.ts
422
+ import path2 from "path";
423
+ async function writeSkills({
424
+ sandbox,
425
+ rootDir,
426
+ skills,
427
+ abortSignal,
428
+ skillNamePattern = /^[A-Za-z0-9._-]+$/,
429
+ invalidSkillNameMessage = ({ name }) => `Invalid skill name: ${name}`,
430
+ filePathMode = "relative",
431
+ invalidSkillFilePathMessage = ({ skillName, filePath }) => `Invalid skill file path for ${skillName}: ${filePath}`,
432
+ trailingNewline = false
433
+ }) {
434
+ var _a, _b;
435
+ for (const skill of skills) {
436
+ validateSkillName({
437
+ name: skill.name,
438
+ pattern: skillNamePattern,
439
+ message: invalidSkillNameMessage
440
+ });
441
+ for (const file of (_a = skill.files) != null ? _a : []) {
442
+ normalizeSkillFilePath({
443
+ skillName: skill.name,
444
+ filePath: file.path,
445
+ mode: filePathMode,
446
+ message: invalidSkillFilePathMessage
447
+ });
448
+ }
449
+ }
450
+ await sandbox.run({
451
+ command: `mkdir -p ${shellQuote(rootDir)}`,
452
+ abortSignal
453
+ });
454
+ for (const skill of skills) {
455
+ const name = validateSkillName({
456
+ name: skill.name,
457
+ pattern: skillNamePattern,
458
+ message: invalidSkillNameMessage
459
+ });
460
+ const skillDir = path2.posix.join(rootDir, name);
461
+ await sandbox.writeTextFile({
462
+ path: path2.posix.join(skillDir, "SKILL.md"),
463
+ content: renderSkillFile({ skill, trailingNewline }),
464
+ abortSignal
465
+ });
466
+ for (const file of (_b = skill.files) != null ? _b : []) {
467
+ const filePath = normalizeSkillFilePath({
468
+ skillName: skill.name,
469
+ filePath: file.path,
470
+ mode: filePathMode,
471
+ message: invalidSkillFilePathMessage
472
+ });
473
+ await sandbox.writeTextFile({
474
+ path: path2.posix.join(skillDir, filePath),
475
+ content: file.content,
476
+ abortSignal
477
+ });
478
+ }
479
+ }
480
+ }
481
+ function validateSkillName({
482
+ name,
483
+ pattern,
484
+ message
485
+ }) {
486
+ var _a;
487
+ if (!pattern.test(name) || name === "." || name === "..") {
488
+ throw new Error((_a = message == null ? void 0 : message({ name })) != null ? _a : `Invalid skill name: ${name}`);
489
+ }
490
+ return name;
491
+ }
492
+ function normalizeSkillFilePath({
493
+ skillName,
494
+ filePath,
495
+ mode,
496
+ message
497
+ }) {
498
+ var _a;
499
+ const normalized = mode === "strip-leading-slashes" ? filePath.replace(/^\/+/, "") : path2.posix.normalize(filePath);
500
+ const invalid = normalized === "" || mode === "relative" && normalized === "." || normalized.startsWith("../") || normalized.includes("/../") || normalized.endsWith("/..") || mode === "relative" && path2.posix.isAbsolute(normalized);
501
+ if (invalid) {
502
+ throw new Error(
503
+ (_a = message == null ? void 0 : message({ skillName: skillName != null ? skillName : "", filePath })) != null ? _a : `Invalid skill file path for ${skillName}: ${filePath}`
504
+ );
505
+ }
506
+ return normalized;
507
+ }
508
+ function renderSkillFile({
509
+ skill,
510
+ trailingNewline
511
+ }) {
512
+ const content = `---
513
+ name: ${skill.name}
514
+ description: ${skill.description}
515
+ ---
516
+
517
+ ${skill.content}`;
518
+ return trailingNewline ? `${content}
519
+ ` : content;
520
+ }
521
+
522
+ // src/utils/bridge-ready.ts
523
+ import {
524
+ safeParseJSON as safeParseJSON3
525
+ } from "@ai-sdk/provider-utils";
526
+ import { z as z4 } from "zod/v4";
527
+
528
+ // src/v1/harness-v1-bridge-protocol.ts
529
+ import { z as z3 } from "zod/v4";
530
+
531
+ // src/v1/harness-v1-diagnostic.ts
532
+ import { z } from "zod/v4";
533
+ var harnessV1DebugLevelSchema = z.enum([
534
+ "error",
535
+ "warn",
536
+ "info",
537
+ "debug",
538
+ "trace"
539
+ ]);
540
+ var harnessV1DebugConfigSchema = z.object({
541
+ enabled: z.boolean().optional(),
542
+ level: harnessV1DebugLevelSchema.optional(),
543
+ subsystems: z.array(z.string()).optional()
544
+ });
545
+
546
+ // src/v1/harness-v1-stream-part.ts
547
+ import { z as z2 } from "zod/v4";
548
+ var harnessV1JsonValueSchema = z2.lazy(
549
+ () => z2.union([
550
+ z2.string(),
551
+ z2.number(),
552
+ z2.boolean(),
553
+ z2.null(),
554
+ z2.array(harnessV1JsonValueSchema),
555
+ z2.record(z2.string(), harnessV1JsonValueSchema)
556
+ ])
557
+ );
558
+ var harnessV1ToolResultValueSchema = harnessV1JsonValueSchema;
559
+ var harnessV1JsonObjectSchema = z2.record(
560
+ z2.string(),
561
+ harnessV1JsonValueSchema
562
+ );
563
+ var harnessV1MetadataSchema = z2.record(
564
+ z2.string(),
565
+ z2.record(z2.string(), harnessV1JsonValueSchema)
566
+ );
567
+ var harnessV1ProviderMetadataSchema = z2.record(
568
+ z2.string(),
569
+ z2.record(z2.string(), harnessV1JsonValueSchema)
570
+ );
571
+ var harnessV1CallWarningSchema = z2.union([
572
+ z2.object({
573
+ type: z2.literal("unsupported-setting"),
574
+ setting: z2.string(),
575
+ details: z2.string().optional()
576
+ }),
577
+ z2.object({
578
+ type: z2.literal("unsupported-tool"),
579
+ tool: z2.string(),
580
+ details: z2.string().optional()
581
+ }),
582
+ z2.object({ type: z2.literal("other"), message: z2.string() })
583
+ ]);
584
+ var harnessV1UsageSchema = z2.object({
585
+ inputTokens: z2.object({
586
+ total: z2.number().optional(),
587
+ noCache: z2.number().optional(),
588
+ cacheRead: z2.number().optional(),
589
+ cacheWrite: z2.number().optional()
590
+ }),
591
+ outputTokens: z2.object({
592
+ total: z2.number().optional(),
593
+ text: z2.number().optional(),
594
+ reasoning: z2.number().optional()
595
+ }),
596
+ raw: harnessV1JsonObjectSchema.optional()
597
+ });
598
+ var harnessV1FinishReasonSchema = z2.object({
599
+ unified: z2.enum([
600
+ "stop",
601
+ "length",
602
+ "content-filter",
603
+ "tool-calls",
604
+ "error",
605
+ "other"
606
+ ]),
607
+ raw: z2.string().optional()
608
+ });
609
+ var harnessV1StreamStartPartSchema = z2.object({
610
+ type: z2.literal("stream-start"),
611
+ warnings: z2.array(harnessV1CallWarningSchema).readonly().optional(),
612
+ modelId: z2.string().optional()
613
+ });
614
+ var harnessV1TextStartPartSchema = z2.object({
615
+ type: z2.literal("text-start"),
616
+ id: z2.string(),
617
+ harnessMetadata: harnessV1MetadataSchema.optional()
618
+ });
619
+ var harnessV1TextDeltaPartSchema = z2.object({
620
+ type: z2.literal("text-delta"),
621
+ id: z2.string(),
622
+ delta: z2.string(),
623
+ harnessMetadata: harnessV1MetadataSchema.optional()
624
+ });
625
+ var harnessV1TextEndPartSchema = z2.object({
626
+ type: z2.literal("text-end"),
627
+ id: z2.string(),
628
+ harnessMetadata: harnessV1MetadataSchema.optional()
629
+ });
630
+ var harnessV1ReasoningStartPartSchema = z2.object({
631
+ type: z2.literal("reasoning-start"),
632
+ id: z2.string(),
633
+ harnessMetadata: harnessV1MetadataSchema.optional()
634
+ });
635
+ var harnessV1ReasoningDeltaPartSchema = z2.object({
636
+ type: z2.literal("reasoning-delta"),
637
+ id: z2.string(),
638
+ delta: z2.string(),
639
+ harnessMetadata: harnessV1MetadataSchema.optional()
640
+ });
641
+ var harnessV1ReasoningEndPartSchema = z2.object({
642
+ type: z2.literal("reasoning-end"),
643
+ id: z2.string(),
644
+ harnessMetadata: harnessV1MetadataSchema.optional()
645
+ });
646
+ var harnessV1ToolCallPartSchema = z2.object({
647
+ type: z2.literal("tool-call"),
648
+ toolCallId: z2.string(),
649
+ toolName: z2.string(),
650
+ input: z2.string(),
651
+ providerExecuted: z2.boolean().optional(),
652
+ dynamic: z2.boolean().optional(),
653
+ providerMetadata: harnessV1ProviderMetadataSchema.optional(),
654
+ nativeName: z2.string().optional()
655
+ });
656
+ var harnessV1ToolApprovalRequestPartSchema = z2.object({
657
+ type: z2.literal("tool-approval-request"),
658
+ approvalId: z2.string(),
659
+ toolCallId: z2.string(),
660
+ providerMetadata: harnessV1ProviderMetadataSchema.optional()
661
+ });
662
+ var harnessV1ToolResultPartSchema = z2.object({
663
+ type: z2.literal("tool-result"),
664
+ toolCallId: z2.string(),
665
+ toolName: z2.string(),
666
+ result: harnessV1ToolResultValueSchema,
667
+ isError: z2.boolean().optional(),
668
+ preliminary: z2.boolean().optional(),
669
+ dynamic: z2.boolean().optional(),
670
+ providerMetadata: harnessV1ProviderMetadataSchema.optional()
671
+ });
672
+ var harnessV1FinishStepPartSchema = z2.object({
673
+ type: z2.literal("finish-step"),
674
+ finishReason: harnessV1FinishReasonSchema,
675
+ usage: harnessV1UsageSchema,
676
+ harnessMetadata: harnessV1MetadataSchema.optional()
677
+ });
678
+ var harnessV1FinishPartSchema = z2.object({
679
+ type: z2.literal("finish"),
680
+ finishReason: harnessV1FinishReasonSchema,
681
+ totalUsage: harnessV1UsageSchema,
682
+ harnessMetadata: harnessV1MetadataSchema.optional()
683
+ });
684
+ var harnessV1FileChangePartSchema = z2.object({
685
+ type: z2.literal("file-change"),
686
+ event: z2.enum(["create", "modify", "delete"]),
687
+ path: z2.string(),
688
+ harnessMetadata: harnessV1MetadataSchema.optional()
689
+ });
690
+ var harnessV1CompactionPartSchema = z2.object({
691
+ type: z2.literal("compaction"),
692
+ trigger: z2.enum(["manual", "auto"]),
693
+ summary: z2.string(),
694
+ tokensBefore: z2.number().optional(),
695
+ tokensAfter: z2.number().optional(),
696
+ harnessMetadata: harnessV1MetadataSchema.optional()
697
+ });
698
+ var harnessV1ErrorPartSchema = z2.object({
699
+ type: z2.literal("error"),
700
+ error: z2.unknown()
701
+ });
702
+ var harnessV1RawPartSchema = z2.object({
703
+ type: z2.literal("raw"),
704
+ rawValue: z2.unknown()
705
+ });
706
+ var harnessV1StreamPartSchema = z2.discriminatedUnion("type", [
707
+ harnessV1StreamStartPartSchema,
708
+ harnessV1TextStartPartSchema,
709
+ harnessV1TextDeltaPartSchema,
710
+ harnessV1TextEndPartSchema,
711
+ harnessV1ReasoningStartPartSchema,
712
+ harnessV1ReasoningDeltaPartSchema,
713
+ harnessV1ReasoningEndPartSchema,
714
+ harnessV1ToolCallPartSchema,
715
+ harnessV1ToolApprovalRequestPartSchema,
716
+ harnessV1ToolResultPartSchema,
717
+ harnessV1FinishStepPartSchema,
718
+ harnessV1FinishPartSchema,
719
+ harnessV1FileChangePartSchema,
720
+ harnessV1CompactionPartSchema,
721
+ harnessV1ErrorPartSchema,
722
+ harnessV1RawPartSchema
723
+ ]);
724
+
725
+ // src/v1/harness-v1-bridge-protocol.ts
726
+ var harnessV1BridgeToolWireSchema = z3.object({
727
+ name: z3.string(),
728
+ description: z3.string().optional(),
729
+ inputSchema: z3.unknown().optional()
730
+ });
731
+ var harnessV1BridgePermissionModeSchema = z3.enum([
732
+ "allow-reads",
733
+ "allow-edits",
734
+ "allow-all"
735
+ ]);
736
+ var harnessV1BridgeBuiltinToolFilteringSchema = z3.discriminatedUnion(
737
+ "mode",
738
+ [
739
+ z3.object({
740
+ mode: z3.literal("allow"),
741
+ toolNames: z3.array(z3.string())
742
+ }),
743
+ z3.object({
744
+ mode: z3.literal("deny"),
745
+ toolNames: z3.array(z3.string())
746
+ })
747
+ ]
748
+ );
749
+ var harnessV1BridgeStartBaseSchema = z3.object({
750
+ type: z3.literal("start"),
751
+ prompt: z3.string(),
752
+ tools: z3.array(harnessV1BridgeToolWireSchema).optional(),
753
+ model: z3.string().optional(),
754
+ debug: harnessV1DebugConfigSchema.optional(),
755
+ permissionMode: harnessV1BridgePermissionModeSchema.optional(),
756
+ builtinToolFiltering: harnessV1BridgeBuiltinToolFilteringSchema.optional()
757
+ });
758
+ var harnessV1BridgeHelloSchema = z3.object({
759
+ type: z3.literal("bridge-hello"),
760
+ state: z3.string().optional(),
761
+ lastSeq: z3.number().optional()
762
+ });
763
+ var harnessV1BridgeDetachSchema = z3.object({
764
+ type: z3.literal("bridge-detach"),
765
+ data: z3.unknown()
766
+ });
767
+ var harnessV1BridgeThreadSchema = z3.object({
768
+ type: z3.literal("bridge-thread"),
769
+ threadId: z3.string()
770
+ });
771
+ var harnessV1BridgeInterruptedSchema = z3.object({
772
+ type: z3.literal("bridge-interrupted"),
773
+ ok: z3.boolean(),
774
+ error: z3.unknown().optional()
775
+ });
776
+ var harnessV1BridgeSandboxLogSchema = z3.object({
777
+ type: z3.literal("sandbox-log"),
778
+ source: z3.string(),
779
+ stream: z3.enum(["stdout", "stderr"]),
780
+ line: z3.string()
781
+ });
782
+ var harnessV1BridgeDebugEventSchema = z3.object({
783
+ type: z3.literal("debug-event"),
784
+ level: harnessV1DebugLevelSchema,
785
+ subsystem: z3.string(),
786
+ message: z3.string(),
787
+ attrs: z3.record(z3.string(), z3.unknown()).optional(),
788
+ error: z3.object({
789
+ name: z3.string().optional(),
790
+ message: z3.string(),
791
+ stack: z3.string().optional()
792
+ }).optional()
793
+ });
794
+ var harnessV1BridgeOutboundMessageSchema = z3.discriminatedUnion(
795
+ "type",
796
+ [
797
+ harnessV1StreamStartPartSchema,
798
+ harnessV1TextStartPartSchema,
799
+ harnessV1TextDeltaPartSchema,
800
+ harnessV1TextEndPartSchema,
801
+ harnessV1ReasoningStartPartSchema,
802
+ harnessV1ReasoningDeltaPartSchema,
803
+ harnessV1ReasoningEndPartSchema,
804
+ harnessV1ToolCallPartSchema,
805
+ harnessV1ToolApprovalRequestPartSchema,
806
+ harnessV1ToolResultPartSchema,
807
+ harnessV1FinishStepPartSchema,
808
+ harnessV1FinishPartSchema,
809
+ harnessV1FileChangePartSchema,
810
+ harnessV1CompactionPartSchema,
811
+ harnessV1ErrorPartSchema,
812
+ harnessV1RawPartSchema,
813
+ harnessV1BridgeHelloSchema,
814
+ harnessV1BridgeDetachSchema,
815
+ harnessV1BridgeThreadSchema,
816
+ harnessV1BridgeInterruptedSchema,
817
+ harnessV1BridgeSandboxLogSchema,
818
+ harnessV1BridgeDebugEventSchema
819
+ ]
820
+ );
821
+ var harnessV1BridgeToolResultInboundSchema = z3.object({
822
+ type: z3.literal("tool-result"),
823
+ toolCallId: z3.string(),
824
+ output: z3.unknown(),
825
+ isError: z3.boolean().optional()
826
+ });
827
+ var harnessV1BridgeToolApprovalResponseInboundSchema = z3.object({
828
+ type: z3.literal("tool-approval-response"),
829
+ approvalId: z3.string(),
830
+ approved: z3.boolean(),
831
+ reason: z3.string().optional()
832
+ });
833
+ var harnessV1BridgeUserMessageInboundSchema = z3.object({
834
+ type: z3.literal("user-message"),
835
+ text: z3.string()
836
+ });
837
+ var harnessV1BridgeAbortInboundSchema = z3.object({
838
+ type: z3.literal("abort")
839
+ });
840
+ var harnessV1BridgeInterruptInboundSchema = z3.object({
841
+ type: z3.literal("interrupt")
842
+ });
843
+ var harnessV1BridgeShutdownInboundSchema = z3.object({
844
+ type: z3.literal("shutdown")
845
+ });
846
+ var harnessV1BridgeResumeInboundSchema = z3.object({
847
+ type: z3.literal("resume"),
848
+ lastSeenEventId: z3.number()
849
+ });
850
+ var harnessV1BridgeDetachInboundSchema = z3.object({
851
+ type: z3.literal("detach")
852
+ });
853
+ var harnessV1BridgeReadySchema = z3.object({
854
+ type: z3.literal("bridge-ready"),
855
+ port: z3.number()
856
+ });
857
+
858
+ // src/utils/bridge-ready.ts
859
+ var bridgeMetaSchema = z4.object({
860
+ type: z4.string().optional(),
861
+ port: z4.number().optional(),
862
+ state: z4.string().optional(),
863
+ pid: z4.number().optional()
864
+ });
865
+ async function markBridgeStarting({
866
+ sandbox,
867
+ bridgeStateDir,
868
+ bridgeType,
869
+ abortSignal
870
+ }) {
871
+ try {
872
+ await sandbox.writeTextFile({
873
+ path: bridgeMetaPath(bridgeStateDir),
874
+ content: JSON.stringify({ type: bridgeType, state: "starting" }),
875
+ abortSignal
876
+ });
877
+ } catch (e) {
878
+ }
879
+ }
880
+ async function waitForBridgeReady({
881
+ proc,
882
+ sandbox,
883
+ bridgeStateDir,
884
+ bridgeType,
885
+ timeoutMs,
886
+ abortSignal,
887
+ pollIntervalMs = 100,
888
+ createTimeoutError,
889
+ createExitError
890
+ }) {
891
+ var _a;
892
+ const reader = proc.stdout.pipeThrough(new TextDecoderStream()).getReader();
893
+ const decoder = lineDecoder();
894
+ const stdoutTail = [];
895
+ const deadline = Date.now() + timeoutMs;
896
+ let pendingStdoutRead;
897
+ let pendingMetaRead;
898
+ let nextMetaReadAt = 0;
899
+ let cancelReader = false;
900
+ try {
901
+ while (true) {
902
+ if (abortSignal == null ? void 0 : abortSignal.aborted) {
903
+ cancelReader = true;
904
+ await proc.kill();
905
+ throw (_a = abortSignal.reason) != null ? _a : new DOMException("Aborted", "AbortError");
906
+ }
907
+ const remaining = deadline - Date.now();
908
+ if (remaining <= 0) {
909
+ cancelReader = true;
910
+ await proc.kill();
911
+ throw await makeBridgeReadyError({
912
+ createError: createTimeoutError,
913
+ fallbackMessage: "bridge did not become ready in time.",
914
+ proc,
915
+ stdoutTail
916
+ });
917
+ }
918
+ pendingStdoutRead != null ? pendingStdoutRead : pendingStdoutRead = reader.read();
919
+ const metaReadDelayMs = Math.max(0, nextMetaReadAt - Date.now());
920
+ if (pendingMetaRead === void 0 && metaReadDelayMs === 0) {
921
+ pendingMetaRead = readBridgeMetaReady({
922
+ sandbox,
923
+ bridgeStateDir,
924
+ bridgeType,
925
+ abortSignal
926
+ });
927
+ }
928
+ const result = await Promise.race([
929
+ pendingStdoutRead.then((read) => ({ source: "stdout", read })),
930
+ ...pendingMetaRead === void 0 ? [] : [
931
+ pendingMetaRead.then((port) => ({
932
+ source: "metadata",
933
+ port
934
+ }))
935
+ ],
936
+ sleep2(
937
+ Math.min(
938
+ remaining,
939
+ pendingMetaRead === void 0 ? metaReadDelayMs : pollIntervalMs
940
+ )
941
+ ).then(() => void 0)
942
+ ]);
943
+ if (result === void 0) continue;
944
+ if (result.source === "metadata") {
945
+ pendingMetaRead = void 0;
946
+ if (result.port !== void 0) {
947
+ cancelReader = true;
948
+ return {
949
+ port: result.port,
950
+ source: "metadata",
951
+ stdoutTail: [...stdoutTail]
952
+ };
953
+ }
954
+ nextMetaReadAt = Date.now() + pollIntervalMs;
955
+ continue;
956
+ }
957
+ pendingStdoutRead = void 0;
958
+ const { value, done } = result.read;
959
+ if (done) {
960
+ for (const line of decoder.flush()) {
961
+ pushTail({ lines: stdoutTail, line });
962
+ }
963
+ throw await makeBridgeReadyError({
964
+ createError: createExitError,
965
+ fallbackMessage: "bridge exited before becoming ready.",
966
+ proc,
967
+ stdoutTail
968
+ });
969
+ }
970
+ if (value === void 0) continue;
971
+ for (const line of decoder.push(value)) {
972
+ pushTail({ lines: stdoutTail, line });
973
+ const parsed = await safeParseJSON3({
974
+ text: line,
975
+ schema: harnessV1BridgeReadySchema
976
+ });
977
+ if (parsed.success) {
978
+ return {
979
+ port: parsed.value.port,
980
+ source: "stdout",
981
+ stdoutTail: [...stdoutTail]
982
+ };
983
+ }
984
+ }
985
+ }
986
+ } finally {
987
+ if (cancelReader) {
988
+ await Promise.race([reader.cancel().catch(() => {
989
+ }), sleep2(100)]);
990
+ }
991
+ try {
992
+ reader.releaseLock();
993
+ } catch (e) {
994
+ }
995
+ }
996
+ }
997
+ async function readBridgeMetaReady({
998
+ sandbox,
999
+ bridgeStateDir,
1000
+ bridgeType,
1001
+ abortSignal
1002
+ }) {
1003
+ const raw = await Promise.resolve(
1004
+ sandbox.readTextFile({
1005
+ path: bridgeMetaPath(bridgeStateDir),
1006
+ abortSignal
1007
+ })
1008
+ ).catch(() => null);
1009
+ if (raw == null) return void 0;
1010
+ const parsed = await safeParseJSON3({ text: raw, schema: bridgeMetaSchema });
1011
+ if (!parsed.success) return void 0;
1012
+ if (parsed.value.type !== bridgeType) return void 0;
1013
+ if (parsed.value.state !== "waiting") return void 0;
1014
+ if (parsed.value.port === void 0 || parsed.value.port <= 0) {
1015
+ return void 0;
1016
+ }
1017
+ return parsed.value.port;
1018
+ }
1019
+ async function makeBridgeReadyError({
1020
+ createError,
1021
+ fallbackMessage,
1022
+ proc,
1023
+ stdoutTail
1024
+ }) {
1025
+ if (createError) {
1026
+ return createError({ proc, stdoutTail: [...stdoutTail] });
1027
+ }
1028
+ return new Error(fallbackMessage);
1029
+ }
1030
+ function bridgeMetaPath(bridgeStateDir) {
1031
+ return `${bridgeStateDir}/bridge-meta.json`;
1032
+ }
1033
+ function pushTail({ lines, line }) {
1034
+ lines.push(line);
1035
+ if (lines.length > 20) lines.shift();
1036
+ }
1037
+ function sleep2(ms) {
1038
+ return new Promise((resolve) => setTimeout(resolve, ms));
1039
+ }
1040
+ function lineDecoder() {
1041
+ let buffer = "";
1042
+ return {
1043
+ push(chunk) {
1044
+ buffer += chunk;
1045
+ const lines = [];
1046
+ let nl;
1047
+ while ((nl = buffer.indexOf("\n")) !== -1) {
1048
+ const raw = buffer.slice(0, nl);
1049
+ buffer = buffer.slice(nl + 1);
1050
+ const line = raw.replace(/\r$/, "").trim();
1051
+ if (line.length > 0) lines.push(line);
1052
+ }
1053
+ return lines;
1054
+ },
1055
+ flush() {
1056
+ const line = buffer.replace(/\r$/, "").trim();
1057
+ buffer = "";
1058
+ return line.length > 0 ? [line] : [];
1059
+ }
1060
+ };
1061
+ }
1062
+
1063
+ // src/utils/bridge-diagnostics.ts
1064
+ var DEFAULT_TAIL_LIMIT = 20;
1065
+ function isSerializedError(error) {
1066
+ return typeof error === "object" && error != null && typeof error.message === "string";
1067
+ }
1068
+ function stringifyUnknown(value) {
1069
+ if (typeof value === "string") return value;
1070
+ try {
1071
+ return JSON.stringify(value);
1072
+ } catch (e) {
1073
+ return String(value);
1074
+ }
1075
+ }
1076
+ function formatBridgeError(error) {
1077
+ var _a, _b;
1078
+ if (error instanceof Error) {
1079
+ return (_a = error.stack) != null ? _a : `${error.name}: ${error.message}`;
1080
+ }
1081
+ if (isSerializedError(error)) {
1082
+ return (_b = error.stack) != null ? _b : `${error.name ? `${error.name}: ` : ""}${error.message}`;
1083
+ }
1084
+ return stringifyUnknown(error);
1085
+ }
1086
+ function writeToStderr(line) {
1087
+ try {
1088
+ process.stderr.write(line);
1089
+ } catch (e) {
1090
+ }
1091
+ }
1092
+ function logBridgeError({
1093
+ harnessId,
1094
+ sessionId,
1095
+ context,
1096
+ error,
1097
+ write = writeToStderr
1098
+ }) {
1099
+ const prefix = `[harness:${harnessId}:error${sessionId ? ` session=${sessionId}` : ""}]`;
1100
+ const message = context ? `${context}: ${formatBridgeError(error)}` : formatBridgeError(error);
1101
+ for (const line of message.split("\n")) {
1102
+ if (line.trim().length > 0) {
1103
+ write(`${prefix} ${line}
1104
+ `);
1105
+ }
1106
+ }
1107
+ }
1108
+ function createBridgeErrorHandler({
1109
+ harnessId,
1110
+ sessionId
1111
+ }) {
1112
+ return (event) => {
1113
+ logBridgeError({
1114
+ harnessId,
1115
+ sessionId,
1116
+ context: "bridge emitted an error frame",
1117
+ error: event.error
1118
+ });
1119
+ };
1120
+ }
1121
+ async function createBridgeStartupError({
1122
+ message,
1123
+ proc,
1124
+ stdoutTail,
1125
+ stderrTail,
1126
+ stderrDone
1127
+ }) {
1128
+ if (stderrDone) {
1129
+ await Promise.race([stderrDone, sleep3(250)]).catch(() => {
1130
+ });
1131
+ }
1132
+ let exitStatus = "";
1133
+ try {
1134
+ const result = await Promise.race([
1135
+ proc.wait(),
1136
+ sleep3(250).then(() => void 0)
1137
+ ]);
1138
+ if ((result == null ? void 0 : result.exitCode) !== void 0) {
1139
+ exitStatus = ` Exit code: ${result.exitCode}.`;
1140
+ }
1141
+ } catch (e) {
1142
+ }
1143
+ const details = [];
1144
+ if (stdoutTail.length > 0) {
1145
+ details.push(`stdout:
1146
+ ${stdoutTail.join("\n")}`);
1147
+ }
1148
+ if (stderrTail.length > 0) {
1149
+ details.push(`stderr:
1150
+ ${stderrTail.join("\n")}`);
1151
+ }
1152
+ return new Error(
1153
+ `${message}${exitStatus}${details.length > 0 ? `
1154
+
1155
+ ${details.join("\n\n")}` : ""}`
1156
+ );
1157
+ }
1158
+ async function forwardBridgeProcessStream({
1159
+ stream,
1160
+ streamName,
1161
+ source = "bridge",
1162
+ collectTail,
1163
+ tailLimit = DEFAULT_TAIL_LIMIT
1164
+ }) {
1165
+ try {
1166
+ const reader = stream.pipeThrough(new TextDecoderStream()).getReader();
1167
+ const decoder = lineDecoder2();
1168
+ while (true) {
1169
+ const { value, done } = await reader.read();
1170
+ const lines = done ? decoder.flush() : value ? decoder.push(value) : [];
1171
+ for (const line of lines) {
1172
+ recordTail({ lines: collectTail, line, limit: tailLimit });
1173
+ writeToStderr(`[harness:${source}:${streamName}] ${line}
1174
+ `);
1175
+ }
1176
+ if (done) return;
1177
+ }
1178
+ } catch (e) {
1179
+ }
1180
+ }
1181
+ async function drainBridgeProcessStream(stream) {
1182
+ try {
1183
+ const reader = stream.pipeThrough(new TextDecoderStream()).getReader();
1184
+ while (true) {
1185
+ const { done } = await reader.read();
1186
+ if (done) return;
1187
+ }
1188
+ } catch (e) {
1189
+ }
1190
+ }
1191
+ function recordTail({
1192
+ lines,
1193
+ line,
1194
+ limit
1195
+ }) {
1196
+ if (!lines) return;
1197
+ lines.push(line);
1198
+ while (lines.length > limit) lines.shift();
1199
+ }
1200
+ function lineDecoder2() {
1201
+ let buffer = "";
1202
+ return {
1203
+ push(chunk) {
1204
+ buffer += chunk;
1205
+ const lines = [];
1206
+ let nl;
1207
+ while ((nl = buffer.indexOf("\n")) !== -1) {
1208
+ const raw = buffer.slice(0, nl);
1209
+ buffer = buffer.slice(nl + 1);
1210
+ const line = raw.replace(/\r$/, "").trim();
1211
+ if (line.length > 0) lines.push(line);
1212
+ }
1213
+ return lines;
1214
+ },
1215
+ flush() {
1216
+ const line = buffer.replace(/\r$/, "").trim();
1217
+ buffer = "";
1218
+ return line.length > 0 ? [line] : [];
1219
+ }
1220
+ };
1221
+ }
1222
+ function sleep3(ms) {
1223
+ return new Promise((resolve) => setTimeout(resolve, ms));
1224
+ }
1225
+ export {
1226
+ SandboxChannel,
1227
+ classifyDiskLog,
1228
+ createBridgeErrorHandler,
1229
+ createBridgeStartupError,
1230
+ drainBridgeProcessStream,
1231
+ formatBridgeError,
1232
+ forwardBridgeProcessStream,
1233
+ getAiGatewayAuthFromEnv,
1234
+ logBridgeError,
1235
+ markBridgeStarting,
1236
+ resolveSandboxHomeDir,
1237
+ shellQuote,
1238
+ waitForBridgeReady,
1239
+ writeSkills
1240
+ };
1241
+ //# sourceMappingURL=index.js.map