@ai-sdk/harness 0.0.0 → 1.0.0-canary.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +13 -0
  3. package/README.md +142 -0
  4. package/agent/index.ts +17 -0
  5. package/bridge/index.ts +10 -0
  6. package/dist/agent/index.d.ts +1480 -0
  7. package/dist/agent/index.js +2554 -0
  8. package/dist/agent/index.js.map +1 -0
  9. package/dist/bridge/index.d.ts +111 -0
  10. package/dist/bridge/index.js +414 -0
  11. package/dist/bridge/index.js.map +1 -0
  12. package/dist/index.d.ts +1510 -0
  13. package/dist/index.js +15834 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/observability/index.d.ts +97 -0
  16. package/dist/observability/index.js +225 -0
  17. package/dist/observability/index.js.map +1 -0
  18. package/dist/utils/index.d.ts +196 -0
  19. package/dist/utils/index.js +327 -0
  20. package/dist/utils/index.js.map +1 -0
  21. package/package.json +104 -1
  22. package/src/agent/harness-agent-session.ts +352 -0
  23. package/src/agent/harness-agent-settings.ts +131 -0
  24. package/src/agent/harness-agent-tool-approval-continuation.ts +94 -0
  25. package/src/agent/harness-agent.ts +750 -0
  26. package/src/agent/harness-diagnostics.ts +88 -0
  27. package/src/agent/internal/bootstrap-recipe.ts +124 -0
  28. package/src/agent/internal/bridge-port-registry.ts +52 -0
  29. package/src/agent/internal/harness-stream-text-result.ts +720 -0
  30. package/src/agent/internal/permission-mode.ts +50 -0
  31. package/src/agent/internal/resolve-observability.ts +128 -0
  32. package/src/agent/internal/resume-state-validation.ts +51 -0
  33. package/src/agent/internal/run-prompt.ts +811 -0
  34. package/src/agent/internal/strip-work-dir.ts +68 -0
  35. package/src/agent/internal/to-harness-stream.ts +75 -0
  36. package/src/agent/internal/translate-stream-part.ts +221 -0
  37. package/src/agent/internal/turn-telemetry.ts +359 -0
  38. package/src/agent/prewarm.ts +46 -0
  39. package/src/bridge/index.ts +700 -0
  40. package/src/errors/harness-capability-unsupported-error.ts +41 -0
  41. package/src/errors/harness-error.ts +22 -0
  42. package/src/index.ts +3 -0
  43. package/src/observability/file-reporter.ts +209 -0
  44. package/src/observability/index.ts +13 -0
  45. package/src/observability/trace-tree-reporter.ts +122 -0
  46. package/src/utils/classify-disk-log.ts +43 -0
  47. package/src/utils/index.ts +7 -0
  48. package/src/utils/sandbox-channel.ts +453 -0
  49. package/src/v1/harness-v1-bootstrap.ts +46 -0
  50. package/src/v1/harness-v1-bridge-protocol.ts +310 -0
  51. package/src/v1/harness-v1-builtin-tool.ts +138 -0
  52. package/src/v1/harness-v1-call-warning.ts +22 -0
  53. package/src/v1/harness-v1-diagnostic.ts +66 -0
  54. package/src/v1/harness-v1-metadata.ts +13 -0
  55. package/src/v1/harness-v1-network-sandbox-session.ts +123 -0
  56. package/src/v1/harness-v1-observability.ts +20 -0
  57. package/src/v1/harness-v1-permission-mode.ts +11 -0
  58. package/src/v1/harness-v1-prompt-control.ts +41 -0
  59. package/src/v1/harness-v1-prompt.ts +11 -0
  60. package/src/v1/harness-v1-resume-state.ts +46 -0
  61. package/src/v1/harness-v1-sandbox-provider.ts +76 -0
  62. package/src/v1/harness-v1-session.ts +268 -0
  63. package/src/v1/harness-v1-skill.ts +22 -0
  64. package/src/v1/harness-v1-stream-part.ts +363 -0
  65. package/src/v1/harness-v1-tool-spec.ts +31 -0
  66. package/src/v1/harness-v1.ts +83 -0
  67. package/src/v1/index.ts +93 -0
  68. package/utils/index.ts +1 -0
@@ -0,0 +1,2554 @@
1
+ // src/errors/harness-capability-unsupported-error.ts
2
+ import { AISDKError as AISDKError2 } from "@ai-sdk/provider";
3
+
4
+ // src/errors/harness-error.ts
5
+ import { AISDKError } from "@ai-sdk/provider";
6
+ var name = "AI_HarnessError";
7
+ var marker = `vercel.ai.error.${name}`;
8
+ var symbol = Symbol.for(marker);
9
+ var _a, _b;
10
+ var HarnessError = class extends (_b = AISDKError, _a = symbol, _b) {
11
+ constructor({ message, cause }) {
12
+ super({ name, message, cause });
13
+ this[_a] = true;
14
+ }
15
+ static isInstance(error) {
16
+ return AISDKError.hasMarker(error, marker);
17
+ }
18
+ };
19
+
20
+ // src/errors/harness-capability-unsupported-error.ts
21
+ var name2 = "AI_HarnessCapabilityUnsupportedError";
22
+ var marker2 = `vercel.ai.error.${name2}`;
23
+ var symbol2 = Symbol.for(marker2);
24
+ var _a2, _b2;
25
+ var HarnessCapabilityUnsupportedError = class extends (_b2 = HarnessError, _a2 = symbol2, _b2) {
26
+ constructor({
27
+ message,
28
+ harnessId,
29
+ cause
30
+ }) {
31
+ super({ message, cause });
32
+ this[_a2] = true;
33
+ Object.defineProperty(this, "name", { value: name2 });
34
+ this.harnessId = harnessId;
35
+ }
36
+ static isInstance(error) {
37
+ return AISDKError2.hasMarker(error, marker2);
38
+ }
39
+ };
40
+
41
+ // src/agent/harness-agent.ts
42
+ import {
43
+ asSchema,
44
+ generateId as generateId5
45
+ } from "@ai-sdk/provider-utils";
46
+
47
+ // src/agent/internal/bridge-port-registry.ts
48
+ var registries = /* @__PURE__ */ new WeakMap();
49
+ function acquireBridgePort(options) {
50
+ let entry = registries.get(options.poolKey);
51
+ if (entry == null) {
52
+ entry = { pool: options.pool, leases: /* @__PURE__ */ new Map() };
53
+ registries.set(options.poolKey, entry);
54
+ }
55
+ const existing = entry.leases.get(options.sessionId);
56
+ if (existing != null) return existing;
57
+ const leased = new Set(entry.leases.values());
58
+ for (const port of entry.pool) {
59
+ if (!leased.has(port)) {
60
+ entry.leases.set(options.sessionId, port);
61
+ return port;
62
+ }
63
+ }
64
+ throw new Error(
65
+ `No available bridge port \u2014 pool of ${entry.pool.length} ports is fully leased.`
66
+ );
67
+ }
68
+ function releaseBridgePort(options) {
69
+ const entry = registries.get(options.poolKey);
70
+ if (entry == null) return;
71
+ entry.leases.delete(options.sessionId);
72
+ }
73
+
74
+ // src/agent/internal/resume-state-validation.ts
75
+ import { safeValidateTypes } from "@ai-sdk/provider-utils";
76
+ async function validateResumeStateData(input) {
77
+ const { harness, state } = input;
78
+ if (state.specificationVersion !== "harness-v1") {
79
+ throw new HarnessError({
80
+ message: `Resume state has unexpected specificationVersion '${state.specificationVersion}'; expected 'harness-v1'.`
81
+ });
82
+ }
83
+ if (state.harnessId !== harness.harnessId) {
84
+ throw new HarnessError({
85
+ message: `Resume state was produced by harness '${state.harnessId}' but this agent uses '${harness.harnessId}'.`
86
+ });
87
+ }
88
+ if (harness.resumeStateSchema == null) {
89
+ return state;
90
+ }
91
+ const result = await safeValidateTypes({
92
+ value: state.data,
93
+ schema: harness.resumeStateSchema
94
+ });
95
+ if (!result.success) {
96
+ throw new HarnessError({
97
+ message: `Resume state failed schema validation for harness '${harness.harnessId}': ${result.error.message}`,
98
+ cause: result.error
99
+ });
100
+ }
101
+ return {
102
+ harnessId: state.harnessId,
103
+ specificationVersion: state.specificationVersion,
104
+ data: result.value,
105
+ ...state.pendingToolApprovals !== void 0 ? { pendingToolApprovals: state.pendingToolApprovals } : {}
106
+ };
107
+ }
108
+
109
+ // src/agent/internal/to-harness-stream.ts
110
+ async function toHarnessStream(options) {
111
+ let controller;
112
+ let closed = false;
113
+ const stream = new ReadableStream({
114
+ start(c) {
115
+ controller = c;
116
+ }
117
+ });
118
+ const safeEnqueue = (part) => {
119
+ if (closed) return;
120
+ controller.enqueue(part);
121
+ };
122
+ const safeClose = () => {
123
+ if (closed) return;
124
+ closed = true;
125
+ controller.close();
126
+ };
127
+ const control = await options.invoke(safeEnqueue);
128
+ Promise.resolve(control.done).then(
129
+ () => safeClose(),
130
+ (err) => {
131
+ safeEnqueue({ type: "error", error: err });
132
+ safeClose();
133
+ }
134
+ ).catch(() => {
135
+ });
136
+ return { stream, control };
137
+ }
138
+
139
+ // src/agent/internal/run-prompt.ts
140
+ import {
141
+ executeTool,
142
+ generateId as generateId4,
143
+ isExecutableTool,
144
+ safeParseJSON
145
+ } from "@ai-sdk/provider-utils";
146
+ import { parseToolCall } from "ai/internal";
147
+
148
+ // src/agent/internal/harness-stream-text-result.ts
149
+ import {
150
+ DelayedPromise,
151
+ generateId
152
+ } from "@ai-sdk/provider-utils";
153
+ import {
154
+ addLanguageModelUsage,
155
+ asLanguageModelUsage,
156
+ createAsyncIterableStream,
157
+ createNullLanguageModelUsage,
158
+ DefaultStepResult,
159
+ toResponseMessages
160
+ } from "ai/internal";
161
+ import {
162
+ createUIMessageStreamResponse,
163
+ toUIMessageStream as toUIMessageStreamHelper
164
+ } from "ai";
165
+ var HarnessStreamTextResult = class {
166
+ constructor(options) {
167
+ // Delayed promises backing every PromiseLike accessor. Each is typed
168
+ // against the corresponding `StreamTextResult` property so the public
169
+ // surface stays in lockstep with AI SDK's interface as it evolves.
170
+ this._content = new DelayedPromise();
171
+ this._text = new DelayedPromise();
172
+ this._reasoning = new DelayedPromise();
173
+ this._reasoningText = new DelayedPromise();
174
+ this._files = new DelayedPromise();
175
+ this._sources = new DelayedPromise();
176
+ this._toolCalls = new DelayedPromise();
177
+ this._staticToolCalls = new DelayedPromise();
178
+ this._dynamicToolCalls = new DelayedPromise();
179
+ this._toolResults = new DelayedPromise();
180
+ this._staticToolResults = new DelayedPromise();
181
+ this._dynamicToolResults = new DelayedPromise();
182
+ this._finishReason = new DelayedPromise();
183
+ this._rawFinishReason = new DelayedPromise();
184
+ this._usage = new DelayedPromise();
185
+ this._warnings = new DelayedPromise();
186
+ this._steps = new DelayedPromise();
187
+ this._finalStep = new DelayedPromise();
188
+ this._request = new DelayedPromise();
189
+ this._response = new DelayedPromise();
190
+ this._responseMessages = new DelayedPromise();
191
+ this._providerMetadata = new DelayedPromise();
192
+ this.stepsBuffer = [];
193
+ this.currentStepContent = [];
194
+ this.currentStepWarnings = [];
195
+ this.stepNumber = 0;
196
+ // Accumulators that span the whole turn.
197
+ this.accumulatedUsage = createNullLanguageModelUsage();
198
+ this.finalProviderMetadata = void 0;
199
+ this.finalFinishReason = "other";
200
+ this.finalRawFinishReason = void 0;
201
+ this.aggregateWarnings = [];
202
+ this.settled = false;
203
+ this.tools = options.tools;
204
+ this.runtimeContext = options.runtimeContext;
205
+ this.toolsContext = options.toolsContext;
206
+ this.providerName = `harness:${options.harnessId}`;
207
+ this.modelId = options.sessionId;
208
+ let controllerRef;
209
+ const baseStream = new ReadableStream({
210
+ start(c) {
211
+ controllerRef = c;
212
+ }
213
+ });
214
+ this.fullStreamController = controllerRef;
215
+ const [forFull, forText] = baseStream.tee();
216
+ this.stream = forFull;
217
+ this.fullStream = this.stream;
218
+ this.textStream = forText.pipeThrough(
219
+ new TransformStream({
220
+ transform(part, controller) {
221
+ if (part.type === "text-delta") {
222
+ controller.enqueue(part.text);
223
+ }
224
+ }
225
+ })
226
+ );
227
+ }
228
+ // ─── Writer-side methods used by the driver ────────────────────────
229
+ /**
230
+ * Push a translated `TextStreamPart` into `fullStream` and accumulate it
231
+ * into the current step's content array where applicable.
232
+ */
233
+ enqueue(part) {
234
+ this.fullStreamController.enqueue(part);
235
+ this.appendToCurrentStepContent(part);
236
+ }
237
+ /**
238
+ * Mark the end of a step. Builds a `StepResult` from the accumulated
239
+ * content and records it in the steps array. Accepts the V4-shaped
240
+ * finish reason / usage the harness emits and normalizes to AI SDK's
241
+ * flat shape internally.
242
+ */
243
+ finishStep(input) {
244
+ const normalizedUsage = asLanguageModelUsage(input.usage);
245
+ const finishReason = input.finishReason.unified;
246
+ const rawFinishReason = input.finishReason.raw;
247
+ const step = new DefaultStepResult({
248
+ callId: generateId(),
249
+ stepNumber: this.stepNumber,
250
+ provider: this.providerName,
251
+ modelId: this.modelId,
252
+ runtimeContext: this.runtimeContext,
253
+ toolsContext: this.toolsContext,
254
+ content: this.currentStepContent,
255
+ finishReason,
256
+ rawFinishReason,
257
+ usage: normalizedUsage,
258
+ performance: createEmptyPerformance(),
259
+ warnings: input.warnings.length > 0 ? input.warnings : void 0,
260
+ request: {},
261
+ response: {
262
+ id: generateId(),
263
+ timestamp: /* @__PURE__ */ new Date(),
264
+ modelId: this.modelId,
265
+ messages: []
266
+ },
267
+ providerMetadata: input.providerMetadata
268
+ });
269
+ this.stepsBuffer.push(step);
270
+ this.fullStreamController.enqueue({
271
+ type: "finish-step",
272
+ finishReason,
273
+ rawFinishReason,
274
+ usage: normalizedUsage,
275
+ providerMetadata: input.providerMetadata,
276
+ response: step.response,
277
+ performance: createEmptyPerformance()
278
+ });
279
+ this.accumulatedUsage = addLanguageModelUsage(
280
+ this.accumulatedUsage,
281
+ normalizedUsage
282
+ );
283
+ this.finalFinishReason = finishReason;
284
+ this.finalRawFinishReason = rawFinishReason;
285
+ this.finalProviderMetadata = input.providerMetadata;
286
+ if (input.warnings.length > 0)
287
+ this.aggregateWarnings.push(...input.warnings);
288
+ this.stepNumber += 1;
289
+ this.currentStepContent = [];
290
+ this.currentStepWarnings = [];
291
+ }
292
+ /**
293
+ * Resolve every delayed promise and close `fullStream`. Idempotent.
294
+ */
295
+ async finish() {
296
+ if (this.settled) return;
297
+ this.settled = true;
298
+ if (this.currentStepContent.length > 0) {
299
+ const trailingStep = new DefaultStepResult({
300
+ callId: generateId(),
301
+ stepNumber: this.stepNumber,
302
+ provider: this.providerName,
303
+ modelId: this.modelId,
304
+ runtimeContext: this.runtimeContext,
305
+ toolsContext: this.toolsContext,
306
+ content: this.currentStepContent,
307
+ finishReason: this.finalFinishReason,
308
+ rawFinishReason: this.finalRawFinishReason,
309
+ usage: createNullLanguageModelUsage(),
310
+ performance: createEmptyPerformance(),
311
+ warnings: this.currentStepWarnings.length > 0 ? this.currentStepWarnings : void 0,
312
+ request: {},
313
+ response: {
314
+ id: generateId(),
315
+ timestamp: /* @__PURE__ */ new Date(),
316
+ modelId: this.modelId,
317
+ messages: []
318
+ },
319
+ providerMetadata: this.finalProviderMetadata
320
+ });
321
+ this.stepsBuffer.push(trailingStep);
322
+ this.currentStepContent = [];
323
+ this.currentStepWarnings = [];
324
+ }
325
+ const finalStep = this.stepsBuffer.length > 0 ? this.stepsBuffer[this.stepsBuffer.length - 1] : new DefaultStepResult({
326
+ callId: generateId(),
327
+ stepNumber: 0,
328
+ provider: this.providerName,
329
+ modelId: this.modelId,
330
+ runtimeContext: this.runtimeContext,
331
+ toolsContext: this.toolsContext,
332
+ content: [],
333
+ finishReason: this.finalFinishReason,
334
+ rawFinishReason: this.finalRawFinishReason,
335
+ usage: createNullLanguageModelUsage(),
336
+ performance: createEmptyPerformance(),
337
+ warnings: void 0,
338
+ request: {},
339
+ response: {
340
+ id: generateId(),
341
+ timestamp: /* @__PURE__ */ new Date(),
342
+ modelId: this.modelId,
343
+ messages: []
344
+ },
345
+ providerMetadata: void 0
346
+ });
347
+ const aggregatedContent = this.stepsBuffer.flatMap((s) => s.content);
348
+ this._content.resolve(
349
+ aggregatedContent
350
+ );
351
+ this._text.resolve(finalStep.text);
352
+ this._reasoning.resolve(
353
+ []
354
+ );
355
+ this._reasoningText.resolve(void 0);
356
+ this._files.resolve(this.stepsBuffer.flatMap((s) => s.files));
357
+ this._sources.resolve(this.stepsBuffer.flatMap((s) => s.sources));
358
+ this._toolCalls.resolve(
359
+ this.stepsBuffer.flatMap((s) => s.toolCalls)
360
+ );
361
+ this._staticToolCalls.resolve(
362
+ this.stepsBuffer.flatMap((s) => s.staticToolCalls)
363
+ );
364
+ this._dynamicToolCalls.resolve(
365
+ this.stepsBuffer.flatMap((s) => s.dynamicToolCalls)
366
+ );
367
+ this._toolResults.resolve(
368
+ this.stepsBuffer.flatMap((s) => s.toolResults)
369
+ );
370
+ this._staticToolResults.resolve(
371
+ this.stepsBuffer.flatMap((s) => s.staticToolResults)
372
+ );
373
+ this._dynamicToolResults.resolve(
374
+ this.stepsBuffer.flatMap((s) => s.dynamicToolResults)
375
+ );
376
+ this._finishReason.resolve(this.finalFinishReason);
377
+ this._rawFinishReason.resolve(this.finalRawFinishReason);
378
+ this._usage.resolve(this.accumulatedUsage);
379
+ this._warnings.resolve(
380
+ this.aggregateWarnings.length > 0 ? this.aggregateWarnings : void 0
381
+ );
382
+ this._steps.resolve(this.stepsBuffer);
383
+ this._finalStep.resolve(finalStep);
384
+ this._request.resolve(finalStep.request);
385
+ this._response.resolve(finalStep.response);
386
+ this._providerMetadata.resolve(this.finalProviderMetadata);
387
+ const responseMessages = await toResponseMessages({
388
+ content: aggregatedContent,
389
+ tools: this.tools
390
+ });
391
+ this._responseMessages.resolve(responseMessages);
392
+ this.fullStreamController.enqueue({
393
+ type: "finish",
394
+ finishReason: this.finalFinishReason,
395
+ rawFinishReason: this.finalRawFinishReason,
396
+ totalUsage: this.accumulatedUsage,
397
+ providerMetadata: this.finalProviderMetadata
398
+ });
399
+ this.fullStreamController.close();
400
+ }
401
+ /**
402
+ * Surface a fatal error as a stream `error` part + reject every delayed
403
+ * promise so awaiting consumers stop hanging. Idempotent.
404
+ */
405
+ fail(error) {
406
+ if (this.settled) return;
407
+ this.settled = true;
408
+ this.fullStreamController.enqueue({
409
+ type: "error",
410
+ error
411
+ });
412
+ this.fullStreamController.close();
413
+ for (const dp of [
414
+ this._content,
415
+ this._text,
416
+ this._reasoning,
417
+ this._reasoningText,
418
+ this._files,
419
+ this._sources,
420
+ this._toolCalls,
421
+ this._staticToolCalls,
422
+ this._dynamicToolCalls,
423
+ this._toolResults,
424
+ this._staticToolResults,
425
+ this._dynamicToolResults,
426
+ this._finishReason,
427
+ this._rawFinishReason,
428
+ this._usage,
429
+ this._warnings,
430
+ this._steps,
431
+ this._finalStep,
432
+ this._request,
433
+ this._response,
434
+ this._responseMessages,
435
+ this._providerMetadata
436
+ ]) {
437
+ try {
438
+ dp.reject(error);
439
+ } catch (e) {
440
+ }
441
+ }
442
+ }
443
+ // ─── Reader-side public surface (StreamTextResult contract) ────────
444
+ get content() {
445
+ return this._content.promise;
446
+ }
447
+ get text() {
448
+ return this._text.promise;
449
+ }
450
+ get reasoning() {
451
+ return this._reasoning.promise;
452
+ }
453
+ get reasoningText() {
454
+ return this._reasoningText.promise;
455
+ }
456
+ get files() {
457
+ return this._files.promise;
458
+ }
459
+ get sources() {
460
+ return this._sources.promise;
461
+ }
462
+ get toolCalls() {
463
+ return this._toolCalls.promise;
464
+ }
465
+ get staticToolCalls() {
466
+ return this._staticToolCalls.promise;
467
+ }
468
+ get dynamicToolCalls() {
469
+ return this._dynamicToolCalls.promise;
470
+ }
471
+ get toolResults() {
472
+ return this._toolResults.promise;
473
+ }
474
+ get staticToolResults() {
475
+ return this._staticToolResults.promise;
476
+ }
477
+ get dynamicToolResults() {
478
+ return this._dynamicToolResults.promise;
479
+ }
480
+ get finishReason() {
481
+ return this._finishReason.promise;
482
+ }
483
+ get rawFinishReason() {
484
+ return this._rawFinishReason.promise;
485
+ }
486
+ get usage() {
487
+ return this._usage.promise;
488
+ }
489
+ get totalUsage() {
490
+ return this._usage.promise;
491
+ }
492
+ get warnings() {
493
+ return this._warnings.promise;
494
+ }
495
+ get steps() {
496
+ return this._steps.promise;
497
+ }
498
+ get finalStep() {
499
+ return this._finalStep.promise;
500
+ }
501
+ get request() {
502
+ return this._request.promise;
503
+ }
504
+ get response() {
505
+ return this._response.promise;
506
+ }
507
+ get responseMessages() {
508
+ return this._responseMessages.promise;
509
+ }
510
+ get providerMetadata() {
511
+ return this._providerMetadata.promise;
512
+ }
513
+ // Output-specification surfaces are not yet supported.
514
+ get experimental_partialOutputStream() {
515
+ throw notSupportedYet("partial output stream");
516
+ }
517
+ get partialOutputStream() {
518
+ throw notSupportedYet("partial output stream");
519
+ }
520
+ get elementStream() {
521
+ throw notSupportedYet("element stream");
522
+ }
523
+ get output() {
524
+ throw notSupportedYet("structured output");
525
+ }
526
+ async consumeStream() {
527
+ const reader = this.fullStream.getReader();
528
+ try {
529
+ while (true) {
530
+ const { done } = await reader.read();
531
+ if (done) return;
532
+ }
533
+ } finally {
534
+ reader.releaseLock();
535
+ }
536
+ }
537
+ toUIMessageStream({
538
+ originalMessages,
539
+ generateMessageId,
540
+ onFinish,
541
+ messageMetadata,
542
+ sendReasoning,
543
+ sendSources,
544
+ sendStart,
545
+ sendFinish,
546
+ onError
547
+ } = {}) {
548
+ return createAsyncIterableStream(
549
+ toUIMessageStreamHelper({
550
+ stream: this.stream,
551
+ tools: this.tools,
552
+ originalMessages,
553
+ generateMessageId,
554
+ onFinish,
555
+ messageMetadata,
556
+ sendReasoning,
557
+ sendSources,
558
+ sendStart,
559
+ sendFinish,
560
+ onError
561
+ })
562
+ );
563
+ }
564
+ pipeUIMessageStreamToResponse() {
565
+ throw notSupportedYet("pipeUIMessageStreamToResponse");
566
+ }
567
+ pipeTextStreamToResponse() {
568
+ throw notSupportedYet("pipeTextStreamToResponse");
569
+ }
570
+ toUIMessageStreamResponse({
571
+ originalMessages,
572
+ generateMessageId,
573
+ onFinish,
574
+ messageMetadata,
575
+ sendReasoning,
576
+ sendSources,
577
+ sendStart,
578
+ sendFinish,
579
+ onError,
580
+ ...init
581
+ } = {}) {
582
+ return createUIMessageStreamResponse({
583
+ stream: this.toUIMessageStream({
584
+ originalMessages,
585
+ generateMessageId,
586
+ onFinish,
587
+ messageMetadata,
588
+ sendReasoning,
589
+ sendSources,
590
+ sendStart,
591
+ sendFinish,
592
+ onError
593
+ }),
594
+ ...init
595
+ });
596
+ }
597
+ toTextStreamResponse() {
598
+ throw notSupportedYet("toTextStreamResponse");
599
+ }
600
+ // ─── Helpers ────────────────────────────────────────────────────────
601
+ appendToCurrentStepContent(part) {
602
+ switch (part.type) {
603
+ case "text-delta": {
604
+ const last = this.currentStepContent[this.currentStepContent.length - 1];
605
+ if (last && last.type === "text") {
606
+ last.text += part.text;
607
+ } else {
608
+ this.currentStepContent.push({
609
+ type: "text",
610
+ text: part.text
611
+ });
612
+ }
613
+ return;
614
+ }
615
+ case "tool-call":
616
+ this.currentStepContent.push({
617
+ ...part
618
+ });
619
+ return;
620
+ case "tool-approval-request":
621
+ this.currentStepContent.push({
622
+ ...part
623
+ });
624
+ return;
625
+ case "tool-approval-response":
626
+ this.currentStepContent.push({
627
+ ...part
628
+ });
629
+ return;
630
+ case "tool-result":
631
+ this.currentStepContent.push({
632
+ ...part
633
+ });
634
+ return;
635
+ default:
636
+ return;
637
+ }
638
+ }
639
+ };
640
+ function createEmptyPerformance() {
641
+ return {
642
+ effectiveOutputTokensPerSecond: 0,
643
+ outputTokensPerSecond: void 0,
644
+ inputTokensPerSecond: void 0,
645
+ effectiveTotalTokensPerSecond: 0,
646
+ stepTimeMs: 0,
647
+ responseTimeMs: 0,
648
+ toolExecutionMs: {},
649
+ timeToFirstOutputMs: void 0
650
+ };
651
+ }
652
+ function notSupportedYet(feature) {
653
+ return new Error(
654
+ `HarnessAgent: ${feature} is not implemented yet. Track the foundation review for follow-up.`
655
+ );
656
+ }
657
+
658
+ // src/agent/internal/translate-stream-part.ts
659
+ import { generateId as generateId2 } from "@ai-sdk/provider-utils";
660
+ function translateStreamPart(event) {
661
+ switch (event.type) {
662
+ case "stream-start":
663
+ return [];
664
+ case "text-start":
665
+ return [
666
+ {
667
+ type: "text-start",
668
+ id: event.id,
669
+ providerMetadata: event.harnessMetadata
670
+ }
671
+ ];
672
+ case "text-delta":
673
+ return [
674
+ {
675
+ type: "text-delta",
676
+ id: event.id,
677
+ text: event.delta,
678
+ providerMetadata: event.harnessMetadata
679
+ }
680
+ ];
681
+ case "text-end":
682
+ return [
683
+ {
684
+ type: "text-end",
685
+ id: event.id,
686
+ providerMetadata: event.harnessMetadata
687
+ }
688
+ ];
689
+ case "reasoning-start":
690
+ return [
691
+ {
692
+ type: "reasoning-start",
693
+ id: event.id,
694
+ providerMetadata: event.harnessMetadata
695
+ }
696
+ ];
697
+ case "reasoning-delta":
698
+ return [
699
+ {
700
+ type: "reasoning-delta",
701
+ id: event.id,
702
+ text: event.delta,
703
+ providerMetadata: event.harnessMetadata
704
+ }
705
+ ];
706
+ case "reasoning-end":
707
+ return [
708
+ {
709
+ type: "reasoning-end",
710
+ id: event.id,
711
+ providerMetadata: event.harnessMetadata
712
+ }
713
+ ];
714
+ case "tool-call":
715
+ return [];
716
+ case "tool-approval-request":
717
+ return [];
718
+ case "tool-result":
719
+ return [
720
+ {
721
+ type: "tool-result",
722
+ toolCallId: event.toolCallId,
723
+ toolName: event.toolName,
724
+ input: void 0,
725
+ output: event.result,
726
+ ...event.preliminary !== void 0 ? { preliminary: event.preliminary } : {},
727
+ ...event.providerMetadata !== void 0 ? { providerMetadata: event.providerMetadata } : {}
728
+ }
729
+ ];
730
+ case "file-change": {
731
+ const toolCallId = `harness-file-change-${generateId2()}`;
732
+ const payload = { event: event.event, path: event.path };
733
+ return [
734
+ {
735
+ type: "tool-call",
736
+ toolCallId,
737
+ toolName: "fileChange",
738
+ input: payload,
739
+ dynamic: true,
740
+ providerExecuted: true,
741
+ ...event.harnessMetadata !== void 0 ? { providerMetadata: event.harnessMetadata } : {}
742
+ },
743
+ {
744
+ type: "tool-result",
745
+ toolCallId,
746
+ toolName: "fileChange",
747
+ input: payload,
748
+ output: payload,
749
+ dynamic: true,
750
+ providerExecuted: true,
751
+ ...event.harnessMetadata !== void 0 ? { providerMetadata: event.harnessMetadata } : {}
752
+ }
753
+ ];
754
+ }
755
+ case "compaction": {
756
+ const toolCallId = `harness-compaction-${generateId2()}`;
757
+ const output = {
758
+ trigger: event.trigger,
759
+ summary: event.summary,
760
+ ...event.tokensBefore !== void 0 ? { tokensBefore: event.tokensBefore } : {},
761
+ ...event.tokensAfter !== void 0 ? { tokensAfter: event.tokensAfter } : {}
762
+ };
763
+ return [
764
+ {
765
+ type: "tool-call",
766
+ toolCallId,
767
+ toolName: "compaction",
768
+ input: {},
769
+ dynamic: true,
770
+ providerExecuted: true,
771
+ ...event.harnessMetadata !== void 0 ? { providerMetadata: event.harnessMetadata } : {}
772
+ },
773
+ {
774
+ type: "tool-result",
775
+ toolCallId,
776
+ toolName: "compaction",
777
+ input: {},
778
+ output,
779
+ dynamic: true,
780
+ providerExecuted: true,
781
+ ...event.harnessMetadata !== void 0 ? { providerMetadata: event.harnessMetadata } : {}
782
+ }
783
+ ];
784
+ }
785
+ case "error":
786
+ return [{ type: "error", error: event.error }];
787
+ case "raw":
788
+ return [
789
+ { type: "raw", rawValue: event.rawValue }
790
+ ];
791
+ case "finish-step":
792
+ case "finish":
793
+ return [];
794
+ }
795
+ }
796
+
797
+ // src/agent/internal/strip-work-dir.ts
798
+ function stripWorkDir(part, sessionWorkDir) {
799
+ if (sessionWorkDir.length === 0) return part;
800
+ switch (part.type) {
801
+ case "tool-call":
802
+ return { ...part, input: stripString(part.input, sessionWorkDir) };
803
+ case "tool-result":
804
+ return {
805
+ ...part,
806
+ result: stripDeep(part.result, sessionWorkDir)
807
+ };
808
+ case "file-change":
809
+ return { ...part, path: stripString(part.path, sessionWorkDir) };
810
+ default:
811
+ return part;
812
+ }
813
+ }
814
+ function stripString(value, workDir) {
815
+ return value.split(`${workDir}/`).join("").split(workDir).join(".");
816
+ }
817
+ function stripDeep(value, workDir) {
818
+ if (typeof value === "string") return stripString(value, workDir);
819
+ if (Array.isArray(value)) return value.map((item) => stripDeep(item, workDir));
820
+ if (value !== null && typeof value === "object") {
821
+ const out = {};
822
+ for (const [key, val] of Object.entries(value)) {
823
+ out[key] = stripDeep(val, workDir);
824
+ }
825
+ return out;
826
+ }
827
+ return value;
828
+ }
829
+
830
+ // src/agent/internal/turn-telemetry.ts
831
+ import { generateId as generateId3 } from "@ai-sdk/provider-utils";
832
+ import { createTelemetryDispatcher } from "ai/internal";
833
+ var NOOP = {
834
+ start() {
835
+ },
836
+ ensureStepOpen() {
837
+ },
838
+ stepFinish() {
839
+ },
840
+ toolStart() {
841
+ },
842
+ toolEnd() {
843
+ },
844
+ end() {
845
+ },
846
+ error() {
847
+ }
848
+ };
849
+ function createTurnTelemetry(opts) {
850
+ var _a3;
851
+ if (opts.telemetry == null) return NOOP;
852
+ const dispatcher = createTelemetryDispatcher({ telemetry: opts.telemetry });
853
+ const callId = generateId3();
854
+ const provider = opts.harnessId;
855
+ let modelId = (_a3 = opts.modelId) != null ? _a3 : "";
856
+ const runtimeContext = opts.runtimeContext;
857
+ const inputMessages = [
858
+ { role: "user", content: opts.promptText }
859
+ ];
860
+ let started = false;
861
+ let stepOpen = false;
862
+ let stepNumber = 0;
863
+ let ended = false;
864
+ const openTools = /* @__PURE__ */ new Map();
865
+ const cast = (event) => event;
866
+ const fireStart = () => {
867
+ var _a4;
868
+ if (started) return;
869
+ started = true;
870
+ (_a4 = dispatcher.onStart) == null ? void 0 : _a4.call(
871
+ dispatcher,
872
+ cast({
873
+ callId,
874
+ operationId: "ai.harness",
875
+ provider,
876
+ modelId,
877
+ tools: void 0,
878
+ toolChoice: void 0,
879
+ activeTools: void 0,
880
+ maxRetries: 0,
881
+ timeout: void 0,
882
+ headers: void 0,
883
+ providerOptions: void 0,
884
+ output: void 0,
885
+ toolsContext: void 0,
886
+ runtimeContext,
887
+ instructions: opts.instructions,
888
+ messages: inputMessages
889
+ })
890
+ );
891
+ };
892
+ const start = (overrideModelId) => {
893
+ if (started) return;
894
+ if (overrideModelId) modelId = overrideModelId;
895
+ fireStart();
896
+ };
897
+ const ensureStepOpen = () => {
898
+ var _a4, _b3;
899
+ if (!started) fireStart();
900
+ if (stepOpen || ended) return;
901
+ stepOpen = true;
902
+ (_a4 = dispatcher.onStepStart) == null ? void 0 : _a4.call(
903
+ dispatcher,
904
+ cast({
905
+ callId,
906
+ provider,
907
+ modelId,
908
+ stepNumber,
909
+ tools: void 0,
910
+ toolChoice: void 0,
911
+ activeTools: void 0,
912
+ steps: new Array(stepNumber),
913
+ providerOptions: void 0,
914
+ output: void 0,
915
+ runtimeContext,
916
+ messages: inputMessages
917
+ })
918
+ );
919
+ (_b3 = dispatcher.onLanguageModelCallStart) == null ? void 0 : _b3.call(
920
+ dispatcher,
921
+ cast({
922
+ callId,
923
+ provider,
924
+ modelId,
925
+ messages: inputMessages,
926
+ tools: void 0
927
+ })
928
+ );
929
+ };
930
+ const inferenceEnd = (info) => {
931
+ var _a4;
932
+ (_a4 = dispatcher.onLanguageModelCallEnd) == null ? void 0 : _a4.call(
933
+ dispatcher,
934
+ cast({
935
+ callId,
936
+ finishReason: info.finishReason,
937
+ responseId: callId,
938
+ usage: info.usage,
939
+ content: info.content
940
+ })
941
+ );
942
+ };
943
+ const closeOpenTools = () => {
944
+ var _a4;
945
+ for (const call of openTools.values()) {
946
+ (_a4 = dispatcher.onToolExecutionEnd) == null ? void 0 : _a4.call(
947
+ dispatcher,
948
+ cast({
949
+ callId,
950
+ toolExecutionMs: 0,
951
+ messages: [],
952
+ toolCall: {
953
+ type: "tool-call",
954
+ toolCallId: call.toolCallId,
955
+ toolName: call.toolName,
956
+ input: call.input,
957
+ dynamic: true
958
+ },
959
+ toolContext: void 0,
960
+ toolOutput: { type: "error", error: new Error("tool span unclosed") }
961
+ })
962
+ );
963
+ }
964
+ openTools.clear();
965
+ };
966
+ return {
967
+ start,
968
+ ensureStepOpen,
969
+ stepFinish(info) {
970
+ var _a4, _b3;
971
+ if (!stepOpen) return;
972
+ const content = (_a4 = info.content) != null ? _a4 : [];
973
+ closeOpenTools();
974
+ inferenceEnd({
975
+ finishReason: info.finishReason,
976
+ usage: info.usage,
977
+ content
978
+ });
979
+ (_b3 = dispatcher.onStepEnd) == null ? void 0 : _b3.call(
980
+ dispatcher,
981
+ cast({
982
+ callId,
983
+ finishReason: info.finishReason,
984
+ usage: info.usage,
985
+ providerMetadata: info.providerMetadata,
986
+ content,
987
+ response: {
988
+ id: callId,
989
+ modelId,
990
+ timestamp: /* @__PURE__ */ new Date(0),
991
+ messages: []
992
+ }
993
+ })
994
+ );
995
+ stepOpen = false;
996
+ stepNumber += 1;
997
+ },
998
+ toolStart(call) {
999
+ var _a4;
1000
+ ensureStepOpen();
1001
+ openTools.set(call.toolCallId, call);
1002
+ (_a4 = dispatcher.onToolExecutionStart) == null ? void 0 : _a4.call(
1003
+ dispatcher,
1004
+ cast({
1005
+ callId,
1006
+ messages: [],
1007
+ toolCall: {
1008
+ type: "tool-call",
1009
+ toolCallId: call.toolCallId,
1010
+ toolName: call.toolName,
1011
+ input: call.input,
1012
+ dynamic: true
1013
+ },
1014
+ toolContext: void 0
1015
+ })
1016
+ );
1017
+ },
1018
+ toolEnd(toolCallId, output) {
1019
+ var _a4;
1020
+ const call = openTools.get(toolCallId);
1021
+ if (call == null) return;
1022
+ openTools.delete(toolCallId);
1023
+ (_a4 = dispatcher.onToolExecutionEnd) == null ? void 0 : _a4.call(
1024
+ dispatcher,
1025
+ cast({
1026
+ callId,
1027
+ toolExecutionMs: 0,
1028
+ messages: [],
1029
+ toolCall: {
1030
+ type: "tool-call",
1031
+ toolCallId: call.toolCallId,
1032
+ toolName: call.toolName,
1033
+ input: call.input,
1034
+ dynamic: true
1035
+ },
1036
+ toolContext: void 0,
1037
+ toolOutput: output.ok ? { type: "tool-result", output: output.output } : { type: "error", error: output.error }
1038
+ })
1039
+ );
1040
+ },
1041
+ end(info) {
1042
+ var _a4, _b3;
1043
+ if (ended) return;
1044
+ if (!started) fireStart();
1045
+ if (stepOpen) {
1046
+ closeOpenTools();
1047
+ inferenceEnd({
1048
+ finishReason: info.finishReason,
1049
+ usage: info.usage,
1050
+ content: []
1051
+ });
1052
+ (_a4 = dispatcher.onStepEnd) == null ? void 0 : _a4.call(
1053
+ dispatcher,
1054
+ cast({
1055
+ callId,
1056
+ finishReason: info.finishReason,
1057
+ usage: info.usage,
1058
+ providerMetadata: void 0,
1059
+ content: [],
1060
+ response: {
1061
+ id: callId,
1062
+ modelId,
1063
+ timestamp: /* @__PURE__ */ new Date(0),
1064
+ messages: []
1065
+ }
1066
+ })
1067
+ );
1068
+ stepOpen = false;
1069
+ }
1070
+ ended = true;
1071
+ (_b3 = dispatcher.onEnd) == null ? void 0 : _b3.call(
1072
+ dispatcher,
1073
+ cast({
1074
+ callId,
1075
+ operationId: "ai.harness",
1076
+ finishReason: info.finishReason,
1077
+ usage: info.usage,
1078
+ totalUsage: info.usage,
1079
+ content: [],
1080
+ steps: new Array(stepNumber),
1081
+ response: {
1082
+ id: callId,
1083
+ modelId,
1084
+ timestamp: /* @__PURE__ */ new Date(0),
1085
+ messages: []
1086
+ },
1087
+ runtimeContext
1088
+ })
1089
+ );
1090
+ },
1091
+ error(err) {
1092
+ var _a4;
1093
+ if (ended) return;
1094
+ if (!started) fireStart();
1095
+ closeOpenTools();
1096
+ ended = true;
1097
+ (_a4 = dispatcher.onError) == null ? void 0 : _a4.call(dispatcher, err);
1098
+ }
1099
+ };
1100
+ }
1101
+
1102
+ // src/agent/internal/permission-mode.ts
1103
+ var DEFAULT_PERMISSION_MODE = "allow-all";
1104
+ function resolvePermissionMode(input) {
1105
+ var _a3;
1106
+ return (_a3 = input.permissionMode) != null ? _a3 : DEFAULT_PERMISSION_MODE;
1107
+ }
1108
+ function permissionModeNeedsBuiltinSupport(input) {
1109
+ return input.permissionMode !== "allow-all";
1110
+ }
1111
+ function resolveCustomToolApproval(input) {
1112
+ var _a3;
1113
+ const status = normalizeToolApprovalStatus({
1114
+ status: (_a3 = input.toolApproval) == null ? void 0 : _a3[input.toolName]
1115
+ });
1116
+ switch (status.type) {
1117
+ case "not-applicable":
1118
+ case "approved":
1119
+ return { type: "allow", reason: status.reason };
1120
+ case "denied":
1121
+ return { type: "deny", reason: status.reason };
1122
+ case "user-approval":
1123
+ return { type: "request" };
1124
+ }
1125
+ }
1126
+ function normalizeToolApprovalStatus(input) {
1127
+ if (input.status === void 0) return { type: "not-applicable" };
1128
+ if (typeof input.status === "string") return { type: input.status };
1129
+ return input.status;
1130
+ }
1131
+
1132
+ // src/agent/internal/run-prompt.ts
1133
+ function runPrompt(input) {
1134
+ var _a3, _b3, _c;
1135
+ const result = new HarnessStreamTextResult({
1136
+ tools: input.tools,
1137
+ runtimeContext: input.runtimeContext,
1138
+ // toolsContext is not configurable for harnesses; pass undefined cast.
1139
+ toolsContext: void 0,
1140
+ harnessId: input.harness.harnessId,
1141
+ sessionId: input.session.sessionId
1142
+ });
1143
+ const pendingToolApprovals = (_a3 = input.pendingToolApprovals) != null ? _a3 : [];
1144
+ const onPendingToolApproval = (_b3 = input.onPendingToolApproval) != null ? _b3 : (() => {
1145
+ });
1146
+ const onToolApprovalSettled = (_c = input.onToolApprovalSettled) != null ? _c : (() => {
1147
+ });
1148
+ const telemetry = createTurnTelemetry({
1149
+ telemetry: input.telemetry,
1150
+ harnessId: input.harness.harnessId,
1151
+ modelId: input.session.modelId,
1152
+ instructions: input.instructions,
1153
+ promptText: input.prompt != null ? promptToText(input.prompt) : "",
1154
+ runtimeContext: input.runtimeContext
1155
+ });
1156
+ const done = (async () => {
1157
+ var _a4, _b4, _c2, _d, _e, _f;
1158
+ let bridge;
1159
+ try {
1160
+ bridge = await toHarnessStream({
1161
+ invoke: input.mode === "continue" ? (emit) => input.session.doContinueTurn({
1162
+ tools: input.toolSpecs,
1163
+ abortSignal: input.abortSignal,
1164
+ emit
1165
+ }) : (emit) => {
1166
+ if (input.prompt == null) {
1167
+ throw new Error(
1168
+ 'runPrompt: `prompt` is required for mode "prompt".'
1169
+ );
1170
+ }
1171
+ return input.session.doPromptTurn({
1172
+ prompt: input.prompt,
1173
+ tools: input.toolSpecs,
1174
+ instructions: input.instructions,
1175
+ abortSignal: input.abortSignal,
1176
+ emit
1177
+ });
1178
+ }
1179
+ });
1180
+ } catch (err) {
1181
+ telemetry.error(err);
1182
+ result.fail(err);
1183
+ return;
1184
+ }
1185
+ const { stream, control } = bridge;
1186
+ const reader = stream.getReader();
1187
+ const toolCallsByToolCallId = /* @__PURE__ */ new Map();
1188
+ const rawToolCallsByToolCallId = /* @__PURE__ */ new Map();
1189
+ const pendingApprovalsByApprovalId = new Map(
1190
+ pendingToolApprovals.map((approval) => [approval.approvalId, approval])
1191
+ );
1192
+ const pendingApprovalsByToolCallId = new Map(
1193
+ pendingToolApprovals.map((approval) => [approval.toolCallId, approval])
1194
+ );
1195
+ const continuationsByApprovalId = new Map(
1196
+ ((_a4 = input.toolApprovalContinuations) != null ? _a4 : []).map((continuation) => [
1197
+ continuation.approvalResponse.approvalId,
1198
+ continuation
1199
+ ])
1200
+ );
1201
+ const settledApprovalToolCallIds = /* @__PURE__ */ new Set();
1202
+ let stepText = "";
1203
+ let stepReasoning = "";
1204
+ let stepToolCalls = [];
1205
+ const buildStepContent = () => {
1206
+ const parts = [];
1207
+ if (stepText) parts.push({ type: "text", text: stepText });
1208
+ if (stepReasoning) parts.push({ type: "reasoning", text: stepReasoning });
1209
+ parts.push(...stepToolCalls);
1210
+ return parts;
1211
+ };
1212
+ const resetStepContent = () => {
1213
+ stepText = "";
1214
+ stepReasoning = "";
1215
+ stepToolCalls = [];
1216
+ };
1217
+ const zeroUsage = {
1218
+ inputTokens: {
1219
+ total: void 0,
1220
+ noCache: void 0,
1221
+ cacheRead: void 0,
1222
+ cacheWrite: void 0
1223
+ },
1224
+ outputTokens: {
1225
+ total: void 0,
1226
+ text: void 0,
1227
+ reasoning: void 0
1228
+ }
1229
+ };
1230
+ const toolCallsFinishReason = {
1231
+ unified: "tool-calls",
1232
+ raw: void 0
1233
+ };
1234
+ const finishForToolApprovalPause = async () => {
1235
+ telemetry.stepFinish({
1236
+ finishReason: toolCallsFinishReason,
1237
+ usage: zeroUsage,
1238
+ content: buildStepContent()
1239
+ });
1240
+ resetStepContent();
1241
+ result.finishStep({
1242
+ finishReason: toolCallsFinishReason,
1243
+ usage: zeroUsage,
1244
+ providerMetadata: void 0,
1245
+ warnings: []
1246
+ });
1247
+ telemetry.end({
1248
+ finishReason: toolCallsFinishReason,
1249
+ usage: zeroUsage
1250
+ });
1251
+ await result.finish();
1252
+ };
1253
+ const enqueueApprovalRequest = (approval) => {
1254
+ result.enqueue({
1255
+ type: "tool-approval-request",
1256
+ approvalId: approval.approvalId,
1257
+ toolCall: approval.toolCall,
1258
+ ...approval.isAutomatic !== void 0 ? { isAutomatic: approval.isAutomatic } : {}
1259
+ });
1260
+ };
1261
+ const enqueueAutomaticApprovalResponse = (input2) => {
1262
+ result.enqueue({
1263
+ type: "tool-approval-response",
1264
+ approvalId: input2.approvalId,
1265
+ toolCall: input2.toolCall,
1266
+ approved: input2.approved,
1267
+ ...input2.reason !== void 0 ? { reason: input2.reason } : {},
1268
+ ...input2.providerExecuted !== void 0 ? { providerExecuted: input2.providerExecuted } : {}
1269
+ });
1270
+ };
1271
+ const enqueueApprovalResponse = (approval, continuation) => {
1272
+ result.enqueue({
1273
+ type: "tool-approval-response",
1274
+ approvalId: approval.approvalId,
1275
+ toolCall: continuation.toolCall,
1276
+ approved: continuation.approvalResponse.approved,
1277
+ ...continuation.approvalResponse.reason !== void 0 ? { reason: continuation.approvalResponse.reason } : {},
1278
+ ...approval.providerExecuted !== void 0 ? { providerExecuted: approval.providerExecuted } : {}
1279
+ });
1280
+ };
1281
+ const processPendingApprovalContinuation = async (approval, continuation) => {
1282
+ var _a5;
1283
+ enqueueApprovalResponse(approval, continuation);
1284
+ onToolApprovalSettled(approval.approvalId);
1285
+ pendingApprovalsByApprovalId.delete(approval.approvalId);
1286
+ pendingApprovalsByToolCallId.delete(approval.toolCallId);
1287
+ settledApprovalToolCallIds.add(approval.toolCallId);
1288
+ if (approval.kind === "builtin") {
1289
+ if (control.submitToolApproval == null) {
1290
+ throw new Error(
1291
+ `Harness '${input.harness.harnessId}' emitted a built-in tool approval request but does not support approval responses.`
1292
+ );
1293
+ }
1294
+ await control.submitToolApproval({
1295
+ approvalId: approval.approvalId,
1296
+ approved: continuation.approvalResponse.approved,
1297
+ reason: continuation.approvalResponse.reason
1298
+ });
1299
+ return;
1300
+ }
1301
+ if (!continuation.approvalResponse.approved) {
1302
+ await control.submitToolResult({
1303
+ toolCallId: approval.toolCallId,
1304
+ output: {
1305
+ type: "execution-denied",
1306
+ reason: continuation.approvalResponse.reason
1307
+ }
1308
+ });
1309
+ return;
1310
+ }
1311
+ const rawToolCall = (_a5 = rawToolCallsByToolCallId.get(approval.toolCallId)) != null ? _a5 : {
1312
+ type: "tool-call",
1313
+ toolCallId: approval.toolCallId,
1314
+ toolName: approval.toolName,
1315
+ input: approval.input
1316
+ };
1317
+ const outcome = await maybeExecuteHostTool({
1318
+ event: rawToolCall,
1319
+ tools: input.tools,
1320
+ sandboxSession: input.sandboxSession,
1321
+ abortSignal: input.abortSignal,
1322
+ control,
1323
+ onPreliminaryResult: (preliminaryOutput) => {
1324
+ const stripped = stripWorkDir(
1325
+ {
1326
+ type: "tool-result",
1327
+ toolCallId: rawToolCall.toolCallId,
1328
+ toolName: rawToolCall.toolName,
1329
+ result: preliminaryOutput
1330
+ },
1331
+ input.sessionWorkDir
1332
+ );
1333
+ result.enqueue({
1334
+ type: "tool-result",
1335
+ toolCallId: rawToolCall.toolCallId,
1336
+ toolName: rawToolCall.toolName,
1337
+ input: void 0,
1338
+ output: stripped.result,
1339
+ preliminary: true
1340
+ });
1341
+ }
1342
+ });
1343
+ telemetry.toolEnd(rawToolCall.toolCallId, outcome);
1344
+ };
1345
+ try {
1346
+ for (const approval of pendingToolApprovals) {
1347
+ const continuation = continuationsByApprovalId.get(approval.approvalId);
1348
+ if (continuation != null) {
1349
+ await processPendingApprovalContinuation(approval, continuation);
1350
+ }
1351
+ }
1352
+ while (true) {
1353
+ const { value, done: done2 } = await reader.read();
1354
+ if (done2) break;
1355
+ if (value == null) continue;
1356
+ if (value.type === "stream-start") {
1357
+ telemetry.start((_b4 = value.modelId) != null ? _b4 : input.session.modelId);
1358
+ }
1359
+ if (value.type !== "stream-start" && value.type !== "finish-step" && value.type !== "finish" && value.type !== "error") {
1360
+ telemetry.ensureStepOpen();
1361
+ }
1362
+ const displayValue = stripWorkDir(value, input.sessionWorkDir);
1363
+ const settledApprovalToolCallReplay = displayValue.type === "tool-call" && !displayValue.providerExecuted && settledApprovalToolCallIds.has(displayValue.toolCallId);
1364
+ if (settledApprovalToolCallReplay) {
1365
+ continue;
1366
+ }
1367
+ for (const part of translateStreamPart(displayValue)) {
1368
+ result.enqueue(part);
1369
+ }
1370
+ if (displayValue.type === "tool-call") {
1371
+ const parsed = await validateToolCall({
1372
+ event: displayValue,
1373
+ tools: input.tools
1374
+ });
1375
+ const parsedToolCall = asToolCallTextStreamPart({ part: parsed });
1376
+ rawToolCallsByToolCallId.set(displayValue.toolCallId, displayValue);
1377
+ toolCallsByToolCallId.set(displayValue.toolCallId, parsedToolCall);
1378
+ result.enqueue(parsed);
1379
+ }
1380
+ if (value.type === "text-delta") {
1381
+ stepText += value.delta;
1382
+ } else if (value.type === "reasoning-delta") {
1383
+ stepReasoning += value.delta;
1384
+ }
1385
+ if (value.type === "tool-call") {
1386
+ stepToolCalls.push({
1387
+ type: "tool-call",
1388
+ toolCallId: value.toolCallId,
1389
+ toolName: value.toolName,
1390
+ input: value.input
1391
+ });
1392
+ telemetry.toolStart({
1393
+ toolCallId: value.toolCallId,
1394
+ toolName: value.toolName,
1395
+ input: value.input
1396
+ });
1397
+ }
1398
+ if (value.type === "tool-result") {
1399
+ telemetry.toolEnd(
1400
+ value.toolCallId,
1401
+ value.isError ? { ok: false, error: value.result } : { ok: true, output: value.result }
1402
+ );
1403
+ }
1404
+ if (value.type === "tool-approval-request") {
1405
+ const toolCall = toolCallsByToolCallId.get(value.toolCallId);
1406
+ if (toolCall == null) {
1407
+ throw new Error(
1408
+ `Harness '${input.harness.harnessId}' emitted approval request '${value.approvalId}' for unknown tool call '${value.toolCallId}'.`
1409
+ );
1410
+ }
1411
+ const rawToolCall = rawToolCallsByToolCallId.get(value.toolCallId);
1412
+ const pendingApproval = (_e = pendingApprovalsByApprovalId.get(value.approvalId)) != null ? _e : {
1413
+ approvalId: value.approvalId,
1414
+ toolCallId: value.toolCallId,
1415
+ toolName: toolCall.toolName,
1416
+ input: (_c2 = rawToolCall == null ? void 0 : rawToolCall.input) != null ? _c2 : JSON.stringify(toolCall.input),
1417
+ kind: "builtin",
1418
+ providerExecuted: (_d = rawToolCall == null ? void 0 : rawToolCall.providerExecuted) != null ? _d : true,
1419
+ ...(rawToolCall == null ? void 0 : rawToolCall.nativeName) !== void 0 ? { nativeName: rawToolCall.nativeName } : {}
1420
+ };
1421
+ pendingApprovalsByApprovalId.set(
1422
+ pendingApproval.approvalId,
1423
+ pendingApproval
1424
+ );
1425
+ pendingApprovalsByToolCallId.set(
1426
+ pendingApproval.toolCallId,
1427
+ pendingApproval
1428
+ );
1429
+ const continuation = continuationsByApprovalId.get(
1430
+ pendingApproval.approvalId
1431
+ );
1432
+ if (continuation != null) {
1433
+ await processPendingApprovalContinuation(
1434
+ pendingApproval,
1435
+ continuation
1436
+ );
1437
+ continue;
1438
+ }
1439
+ onPendingToolApproval(pendingApproval);
1440
+ enqueueApprovalRequest({
1441
+ approvalId: pendingApproval.approvalId,
1442
+ toolCall
1443
+ });
1444
+ await finishForToolApprovalPause();
1445
+ return;
1446
+ }
1447
+ if (value.type === "finish-step") {
1448
+ telemetry.stepFinish({
1449
+ finishReason: value.finishReason,
1450
+ usage: value.usage,
1451
+ providerMetadata: value.harnessMetadata,
1452
+ content: buildStepContent()
1453
+ });
1454
+ resetStepContent();
1455
+ result.finishStep({
1456
+ finishReason: value.finishReason,
1457
+ usage: value.usage,
1458
+ providerMetadata: value.harnessMetadata,
1459
+ warnings: []
1460
+ });
1461
+ }
1462
+ if (value.type === "finish") {
1463
+ telemetry.end({
1464
+ finishReason: value.finishReason,
1465
+ usage: value.totalUsage
1466
+ });
1467
+ }
1468
+ if (value.type === "tool-call" && !value.providerExecuted) {
1469
+ const toolCall = value;
1470
+ const parsedToolCall = toolCallsByToolCallId.get(toolCall.toolCallId);
1471
+ if (parsedToolCall == null) {
1472
+ throw new Error(
1473
+ `Harness '${input.harness.harnessId}' could not find parsed tool call '${toolCall.toolCallId}' for custom tool approval.`
1474
+ );
1475
+ }
1476
+ const customToolApprovalDecision = resolveCustomToolApproval({
1477
+ toolName: toolCall.toolName,
1478
+ toolApproval: input.toolApproval
1479
+ });
1480
+ if (customToolApprovalDecision.type === "deny") {
1481
+ const approvalId = generateId4();
1482
+ enqueueApprovalRequest({
1483
+ approvalId,
1484
+ toolCall: parsedToolCall,
1485
+ isAutomatic: true
1486
+ });
1487
+ enqueueAutomaticApprovalResponse({
1488
+ approvalId,
1489
+ toolCall: parsedToolCall,
1490
+ approved: false,
1491
+ reason: customToolApprovalDecision.reason,
1492
+ providerExecuted: false
1493
+ });
1494
+ const output = {
1495
+ type: "execution-denied",
1496
+ reason: customToolApprovalDecision.reason
1497
+ };
1498
+ await control.submitToolResult({
1499
+ toolCallId: toolCall.toolCallId,
1500
+ output
1501
+ });
1502
+ telemetry.toolEnd(toolCall.toolCallId, { ok: true, output });
1503
+ continue;
1504
+ }
1505
+ const pendingApproval = (_f = pendingApprovalsByToolCallId.get(toolCall.toolCallId)) != null ? _f : customToolApprovalDecision.type === "request" ? {
1506
+ approvalId: generateId4(),
1507
+ toolCallId: toolCall.toolCallId,
1508
+ toolName: toolCall.toolName,
1509
+ input: toolCall.input,
1510
+ kind: "custom",
1511
+ providerExecuted: false,
1512
+ ...toolCall.nativeName !== void 0 ? { nativeName: toolCall.nativeName } : {}
1513
+ } : void 0;
1514
+ if (pendingApproval != null) {
1515
+ pendingApprovalsByApprovalId.set(
1516
+ pendingApproval.approvalId,
1517
+ pendingApproval
1518
+ );
1519
+ pendingApprovalsByToolCallId.set(
1520
+ pendingApproval.toolCallId,
1521
+ pendingApproval
1522
+ );
1523
+ const continuation = continuationsByApprovalId.get(
1524
+ pendingApproval.approvalId
1525
+ );
1526
+ if (continuation != null) {
1527
+ await processPendingApprovalContinuation(
1528
+ pendingApproval,
1529
+ continuation
1530
+ );
1531
+ continue;
1532
+ }
1533
+ const pendingParsedToolCall = toolCallsByToolCallId.get(
1534
+ pendingApproval.toolCallId
1535
+ );
1536
+ if (pendingParsedToolCall == null) {
1537
+ throw new Error(
1538
+ `Harness '${input.harness.harnessId}' could not find parsed tool call '${pendingApproval.toolCallId}' for approval request '${pendingApproval.approvalId}'.`
1539
+ );
1540
+ }
1541
+ onPendingToolApproval(pendingApproval);
1542
+ enqueueApprovalRequest({
1543
+ approvalId: pendingApproval.approvalId,
1544
+ toolCall: pendingParsedToolCall
1545
+ });
1546
+ await finishForToolApprovalPause();
1547
+ return;
1548
+ }
1549
+ const outcome = await maybeExecuteHostTool({
1550
+ event: toolCall,
1551
+ tools: input.tools,
1552
+ sandboxSession: input.sandboxSession,
1553
+ abortSignal: input.abortSignal,
1554
+ control,
1555
+ onPreliminaryResult: (preliminaryOutput) => {
1556
+ const stripped = stripWorkDir(
1557
+ {
1558
+ type: "tool-result",
1559
+ toolCallId: toolCall.toolCallId,
1560
+ toolName: toolCall.toolName,
1561
+ result: preliminaryOutput
1562
+ },
1563
+ input.sessionWorkDir
1564
+ );
1565
+ result.enqueue({
1566
+ type: "tool-result",
1567
+ toolCallId: toolCall.toolCallId,
1568
+ toolName: toolCall.toolName,
1569
+ input: void 0,
1570
+ output: stripped.result,
1571
+ preliminary: true
1572
+ });
1573
+ }
1574
+ });
1575
+ telemetry.toolEnd(toolCall.toolCallId, outcome);
1576
+ }
1577
+ if (value.type === "error") {
1578
+ telemetry.error(value.error);
1579
+ result.fail(value.error);
1580
+ return;
1581
+ }
1582
+ }
1583
+ await result.finish();
1584
+ } catch (err) {
1585
+ telemetry.error(err);
1586
+ result.fail(err);
1587
+ } finally {
1588
+ reader.releaseLock();
1589
+ }
1590
+ })();
1591
+ done.catch(() => {
1592
+ });
1593
+ return { result, done };
1594
+ }
1595
+ function asToolCallTextStreamPart(input) {
1596
+ if (input.part.type !== "tool-call") {
1597
+ throw new Error(
1598
+ `Expected parsed tool-call stream part, got '${input.part.type}'.`
1599
+ );
1600
+ }
1601
+ return input.part;
1602
+ }
1603
+ async function maybeExecuteHostTool(input) {
1604
+ const tool = input.tools[input.event.toolName];
1605
+ if (!isExecutableTool(tool)) return { ok: true, output: void 0 };
1606
+ const parsed = await safeParseJSON({ text: input.event.input });
1607
+ const args = parsed.success ? parsed.value : input.event.input;
1608
+ try {
1609
+ let output;
1610
+ const stream = executeTool({
1611
+ tool,
1612
+ input: args,
1613
+ options: {
1614
+ toolCallId: input.event.toolCallId,
1615
+ messages: [],
1616
+ abortSignal: input.abortSignal,
1617
+ context: void 0,
1618
+ experimental_sandbox: input.sandboxSession
1619
+ }
1620
+ });
1621
+ for await (const part of stream) {
1622
+ if (part.type === "preliminary") {
1623
+ input.onPreliminaryResult(part.output);
1624
+ } else {
1625
+ output = part.output;
1626
+ }
1627
+ }
1628
+ await input.control.submitToolResult({
1629
+ toolCallId: input.event.toolCallId,
1630
+ output
1631
+ });
1632
+ return { ok: true, output };
1633
+ } catch (err) {
1634
+ await input.control.submitToolResult({
1635
+ toolCallId: input.event.toolCallId,
1636
+ output: { error: String(err) },
1637
+ isError: true
1638
+ });
1639
+ return { ok: false, error: err };
1640
+ }
1641
+ }
1642
+ async function validateToolCall(args) {
1643
+ const { event, tools } = args;
1644
+ const toolCall = {
1645
+ type: "tool-call",
1646
+ toolCallId: event.toolCallId,
1647
+ toolName: event.toolName,
1648
+ input: event.input,
1649
+ ...event.providerExecuted !== void 0 ? { providerExecuted: event.providerExecuted } : {},
1650
+ ...event.providerMetadata !== void 0 ? { providerMetadata: event.providerMetadata } : {}
1651
+ };
1652
+ const parsed = await parseToolCall({
1653
+ toolCall,
1654
+ tools,
1655
+ repairToolCall: void 0,
1656
+ refineToolInput: void 0,
1657
+ instructions: void 0,
1658
+ messages: []
1659
+ });
1660
+ return parsed;
1661
+ }
1662
+ function promptToText(prompt) {
1663
+ if (typeof prompt === "string") return prompt;
1664
+ const content = prompt.content;
1665
+ if (typeof content === "string") return content;
1666
+ if (Array.isArray(content)) {
1667
+ return content.filter(
1668
+ (part) => typeof part === "object" && part != null && part.type === "text"
1669
+ ).map((part) => part.text).join("");
1670
+ }
1671
+ return "";
1672
+ }
1673
+
1674
+ // src/agent/harness-agent-session.ts
1675
+ var HarnessAgentSession = class {
1676
+ constructor(options) {
1677
+ this.pendingToolApprovals = /* @__PURE__ */ new Map();
1678
+ this.stopped = false;
1679
+ var _a3;
1680
+ this.sessionId = options.sessionId;
1681
+ this.harness = options.harness;
1682
+ this.underlyingSession = options.underlyingSession;
1683
+ this.sandboxSession = options.sandboxSession;
1684
+ this.sandboxProvider = options.sandboxProvider;
1685
+ this.leasedBridgePort = options.leasedBridgePort;
1686
+ this.sessionWorkDir = options.sessionWorkDir;
1687
+ this.toolApproval = options.toolApproval;
1688
+ for (const approval of (_a3 = options.pendingToolApprovals) != null ? _a3 : []) {
1689
+ this.pendingToolApprovals.set(approval.approvalId, approval);
1690
+ }
1691
+ this.isResume = options.underlyingSession.isResume;
1692
+ }
1693
+ /**
1694
+ * Active network sandbox session.
1695
+ *
1696
+ * @internal — accessed by session turn and lifecycle drivers.
1697
+ */
1698
+ getSandboxSession() {
1699
+ if (this.stopped || this.sandboxSession == null) {
1700
+ throw new Error(
1701
+ `Harness session ${this.sessionId} has ended and cannot be reused.`
1702
+ );
1703
+ }
1704
+ return this.sandboxSession;
1705
+ }
1706
+ /**
1707
+ * Working directory the agent runs in for this session. Used to strip the
1708
+ * prefix from absolute paths in stream events before they reach consumers.
1709
+ *
1710
+ * @internal — accessed by session turn drivers.
1711
+ */
1712
+ getSessionWorkDir() {
1713
+ return this.sessionWorkDir;
1714
+ }
1715
+ promptTurn(options) {
1716
+ const session = this.requireReusableSession();
1717
+ if (this.pendingToolApprovals.size > 0) {
1718
+ throw new Error(
1719
+ `Harness session ${this.sessionId} has pending tool approvals and must be continued with approval responses before accepting a new prompt.`
1720
+ );
1721
+ }
1722
+ const sandboxSession = this.getSandboxSession();
1723
+ return runPrompt({
1724
+ harness: this.harness,
1725
+ session,
1726
+ prompt: options.prompt,
1727
+ instructions: options.instructions,
1728
+ tools: options.tools,
1729
+ toolSpecs: options.toolSpecs,
1730
+ sandboxSession: sandboxSession.restricted(),
1731
+ sessionWorkDir: this.sessionWorkDir,
1732
+ runtimeContext: options.runtimeContext,
1733
+ abortSignal: options.abortSignal,
1734
+ telemetry: options.telemetry,
1735
+ toolApproval: this.toolApproval,
1736
+ pendingToolApprovals: this.getPendingToolApprovals(),
1737
+ onPendingToolApproval: (approval) => {
1738
+ this.pendingToolApprovals.set(approval.approvalId, approval);
1739
+ },
1740
+ onToolApprovalSettled: (approvalId) => {
1741
+ this.pendingToolApprovals.delete(approvalId);
1742
+ }
1743
+ });
1744
+ }
1745
+ continueTurn(options) {
1746
+ const session = this.requireReusableSession();
1747
+ const sandboxSession = this.getSandboxSession();
1748
+ return runPrompt({
1749
+ harness: this.harness,
1750
+ session,
1751
+ mode: "continue",
1752
+ instructions: options.instructions,
1753
+ tools: options.tools,
1754
+ toolSpecs: options.toolSpecs,
1755
+ sandboxSession: sandboxSession.restricted(),
1756
+ sessionWorkDir: this.sessionWorkDir,
1757
+ runtimeContext: options.runtimeContext,
1758
+ abortSignal: options.abortSignal,
1759
+ telemetry: options.telemetry,
1760
+ toolApproval: this.toolApproval,
1761
+ pendingToolApprovals: this.getPendingToolApprovals(),
1762
+ toolApprovalContinuations: options.toolApprovalContinuations,
1763
+ onPendingToolApproval: (approval) => {
1764
+ this.pendingToolApprovals.set(approval.approvalId, approval);
1765
+ },
1766
+ onToolApprovalSettled: (approvalId) => {
1767
+ this.pendingToolApprovals.delete(approvalId);
1768
+ }
1769
+ });
1770
+ }
1771
+ /**
1772
+ * Ask the underlying runtime to compact its context. The runtime performs
1773
+ * the compaction itself; when it completes, a `compaction` part appears on
1774
+ * the active (or next) turn's stream. Safe to call between turns for
1775
+ * runtimes whose compaction is session-scoped (e.g. Pi).
1776
+ *
1777
+ * Throws `HarnessCapabilityUnsupportedError` for harnesses that cannot
1778
+ * trigger compaction manually (e.g. Codex, which still auto-compacts under
1779
+ * the hood). Throws if the session has ended.
1780
+ */
1781
+ async compact(customInstructions) {
1782
+ await this.requireReusableSession().doCompact(customInstructions);
1783
+ }
1784
+ /**
1785
+ * Park the session, returning a payload the caller can persist and later
1786
+ * pass to `agent.createSession({ sessionId, resumeFrom })` to reconnect.
1787
+ * The runtime and sandbox keep running; this local session handle becomes
1788
+ * unusable.
1789
+ */
1790
+ async detach() {
1791
+ if (this.stopped || this.underlyingSession == null) {
1792
+ throw new Error(
1793
+ `Harness session ${this.sessionId} is not active and cannot be detached.`
1794
+ );
1795
+ }
1796
+ const session = this.underlyingSession;
1797
+ try {
1798
+ const raw = await session.doDetach();
1799
+ const validated = await validateResumeStateData({
1800
+ harness: this.harness,
1801
+ state: raw
1802
+ });
1803
+ return this.withPendingToolApprovals(validated);
1804
+ } finally {
1805
+ this.endLocalHandle({ releasePortLease: false });
1806
+ }
1807
+ }
1808
+ /**
1809
+ * Persist enough state to resume later, then stop the runtime and sandbox.
1810
+ * Returns the resume state for a future
1811
+ * `agent.createSession({ sessionId, resumeFrom })` call.
1812
+ */
1813
+ async stop() {
1814
+ if (this.stopped || this.underlyingSession == null) {
1815
+ throw new Error(
1816
+ `Harness session ${this.sessionId} is not active and cannot be stopped.`
1817
+ );
1818
+ }
1819
+ const session = this.underlyingSession;
1820
+ const sandboxSession = this.getSandboxSession();
1821
+ try {
1822
+ const raw = await session.doStop();
1823
+ const validated = await validateResumeStateData({
1824
+ harness: this.harness,
1825
+ state: raw
1826
+ });
1827
+ return this.withPendingToolApprovals(validated);
1828
+ } finally {
1829
+ this.endLocalHandle({ releasePortLease: true });
1830
+ await Promise.resolve(sandboxSession.stop()).catch(() => {
1831
+ });
1832
+ }
1833
+ }
1834
+ /**
1835
+ * Stop the runtime and discard resumability. The sandbox is destroyed when
1836
+ * the provider supports destruction; otherwise it is stopped.
1837
+ */
1838
+ async destroy() {
1839
+ var _a3, _b3;
1840
+ if (this.stopped) return;
1841
+ const session = this.underlyingSession;
1842
+ const sandboxSession = this.getSandboxSession();
1843
+ this.endLocalHandle({ releasePortLease: true });
1844
+ if (session != null) {
1845
+ await Promise.resolve(session.doDestroy()).catch(() => {
1846
+ });
1847
+ }
1848
+ await Promise.resolve(
1849
+ (_b3 = (_a3 = sandboxSession.destroy) == null ? void 0 : _a3.call(sandboxSession)) != null ? _b3 : sandboxSession.stop()
1850
+ ).catch(() => {
1851
+ });
1852
+ }
1853
+ /**
1854
+ * Gracefully freeze the active turn at the slice boundary and return the
1855
+ * resume payload, **leaving the sandbox/runtime running** so the next process
1856
+ * can resume. Resolves once the in-flight `stream()`/`continueTurn()`
1857
+ * has cleanly wound down at a precise cursor (see
1858
+ * {@link HarnessV1Session.doSuspendTurn}).
1859
+ *
1860
+ * After this call the session is marked stopped. This in-process handle no
1861
+ * longer drives turns; a future slice creates a fresh session from the
1862
+ * returned state. The sandbox is **not** stopped and no port lease is
1863
+ * released, because bridge-backed adapters may still have a live bridge on
1864
+ * that port.
1865
+ */
1866
+ async suspendTurn() {
1867
+ if (this.stopped || this.underlyingSession == null) {
1868
+ throw new Error(
1869
+ `Harness session ${this.sessionId} is not active and cannot be suspended.`
1870
+ );
1871
+ }
1872
+ const session = this.underlyingSession;
1873
+ const raw = await session.doSuspendTurn();
1874
+ const validated = await validateResumeStateData({
1875
+ harness: this.harness,
1876
+ state: raw
1877
+ });
1878
+ this.stopped = true;
1879
+ this.underlyingSession = void 0;
1880
+ this.sandboxSession = void 0;
1881
+ return this.withPendingToolApprovals(validated);
1882
+ }
1883
+ getPendingToolApprovals() {
1884
+ return Array.from(this.pendingToolApprovals.values());
1885
+ }
1886
+ withPendingToolApprovals(state) {
1887
+ const pendingToolApprovals = this.getPendingToolApprovals();
1888
+ if (pendingToolApprovals.length === 0) {
1889
+ return {
1890
+ harnessId: state.harnessId,
1891
+ specificationVersion: state.specificationVersion,
1892
+ data: state.data
1893
+ };
1894
+ }
1895
+ return {
1896
+ ...state,
1897
+ pendingToolApprovals
1898
+ };
1899
+ }
1900
+ endLocalHandle(options) {
1901
+ this.stopped = true;
1902
+ this.underlyingSession = void 0;
1903
+ this.sandboxSession = void 0;
1904
+ if (options.releasePortLease) {
1905
+ this.releasePortLease();
1906
+ }
1907
+ }
1908
+ releasePortLease() {
1909
+ if (this.leasedBridgePort == null) return;
1910
+ releaseBridgePort({
1911
+ poolKey: this.sandboxProvider,
1912
+ sessionId: this.sessionId
1913
+ });
1914
+ this.leasedBridgePort = void 0;
1915
+ }
1916
+ requireReusableSession() {
1917
+ if (this.stopped || this.underlyingSession == null) {
1918
+ throw new Error(
1919
+ `Harness session ${this.sessionId} has ended and cannot be reused.`
1920
+ );
1921
+ }
1922
+ return this.underlyingSession;
1923
+ }
1924
+ };
1925
+
1926
+ // src/agent/harness-agent-tool-approval-continuation.ts
1927
+ function collectHarnessAgentToolApprovalContinuations(input) {
1928
+ const lastMessage = input.messages.at(-1);
1929
+ if ((lastMessage == null ? void 0 : lastMessage.role) !== "tool") return [];
1930
+ const toolCallsByToolCallId = /* @__PURE__ */ new Map();
1931
+ const approvalRequestsByApprovalId = /* @__PURE__ */ new Map();
1932
+ for (const message of input.messages) {
1933
+ if (message.role !== "assistant" || typeof message.content === "string") {
1934
+ continue;
1935
+ }
1936
+ for (const part of message.content) {
1937
+ if (part.type === "tool-call") {
1938
+ toolCallsByToolCallId.set(part.toolCallId, {
1939
+ type: "tool-call",
1940
+ toolCallId: part.toolCallId,
1941
+ toolName: part.toolName,
1942
+ input: part.input,
1943
+ ...part.providerExecuted !== void 0 ? { providerExecuted: part.providerExecuted } : {}
1944
+ });
1945
+ } else if (part.type === "tool-approval-request") {
1946
+ approvalRequestsByApprovalId.set(part.approvalId, part);
1947
+ }
1948
+ }
1949
+ }
1950
+ const toolResultIds = /* @__PURE__ */ new Set();
1951
+ for (const part of lastMessage.content) {
1952
+ if (part.type === "tool-result") {
1953
+ toolResultIds.add(part.toolCallId);
1954
+ }
1955
+ }
1956
+ const continuations = [];
1957
+ for (const part of lastMessage.content) {
1958
+ if (part.type !== "tool-approval-response") continue;
1959
+ const approvalRequest = approvalRequestsByApprovalId.get(part.approvalId);
1960
+ if (approvalRequest == null) {
1961
+ throw new HarnessError({
1962
+ message: `Tool approval response '${part.approvalId}' does not match a prior tool approval request.`
1963
+ });
1964
+ }
1965
+ if (toolResultIds.has(approvalRequest.toolCallId)) continue;
1966
+ const toolCall = toolCallsByToolCallId.get(approvalRequest.toolCallId);
1967
+ if (toolCall == null) {
1968
+ throw new HarnessError({
1969
+ message: `Tool approval request '${approvalRequest.approvalId}' references unknown tool call '${approvalRequest.toolCallId}'.`
1970
+ });
1971
+ }
1972
+ continuations.push({
1973
+ approvalResponse: part,
1974
+ toolCall
1975
+ });
1976
+ }
1977
+ return continuations;
1978
+ }
1979
+
1980
+ // src/agent/internal/bootstrap-recipe.ts
1981
+ var BOOTSTRAP_SCHEMA_VERSION = 1;
1982
+ async function hashBootstrap(recipe) {
1983
+ const encoder = new TextEncoder();
1984
+ const chunks = [];
1985
+ const pushString = (value) => {
1986
+ chunks.push(encoder.encode(value));
1987
+ chunks.push(encoder.encode("\0"));
1988
+ };
1989
+ pushString(recipe.harnessId);
1990
+ pushString(recipe.bootstrapDir);
1991
+ const sortedFiles = [...recipe.files].sort(
1992
+ (a, b) => a.path.localeCompare(b.path)
1993
+ );
1994
+ for (const file of sortedFiles) {
1995
+ pushString(file.path);
1996
+ pushString(file.content);
1997
+ }
1998
+ pushString(JSON.stringify(recipe.commands));
1999
+ pushString(String(BOOTSTRAP_SCHEMA_VERSION));
2000
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
2001
+ const buffer = new Uint8Array(totalLength);
2002
+ let offset = 0;
2003
+ for (const chunk of chunks) {
2004
+ buffer.set(chunk, offset);
2005
+ offset += chunk.length;
2006
+ }
2007
+ const digest = await crypto.subtle.digest("SHA-256", buffer);
2008
+ const bytes = new Uint8Array(digest);
2009
+ let hex = "";
2010
+ for (let i = 0; i < 8; i++) {
2011
+ hex += bytes[i].toString(16).padStart(2, "0");
2012
+ }
2013
+ return hex;
2014
+ }
2015
+ function bootstrapMarkerPath(recipe, identity) {
2016
+ return `${recipe.bootstrapDir}/.bootstrap-${identity}.ok`;
2017
+ }
2018
+ async function applyBootstrapRecipe(session, recipe, identity, options) {
2019
+ const markerPath = bootstrapMarkerPath(recipe, identity);
2020
+ const existingMarker = await session.readTextFile({
2021
+ path: markerPath,
2022
+ abortSignal: options == null ? void 0 : options.abortSignal
2023
+ });
2024
+ if (existingMarker !== null) {
2025
+ return;
2026
+ }
2027
+ for (const file of recipe.files) {
2028
+ await session.writeTextFile({
2029
+ path: file.path,
2030
+ content: file.content,
2031
+ abortSignal: options == null ? void 0 : options.abortSignal
2032
+ });
2033
+ }
2034
+ for (const cmd of recipe.commands) {
2035
+ const result = await session.run({
2036
+ command: cmd.command,
2037
+ workingDirectory: cmd.workingDirectory,
2038
+ abortSignal: options == null ? void 0 : options.abortSignal
2039
+ });
2040
+ if (result.exitCode !== 0) {
2041
+ throw new Error(
2042
+ `Bootstrap command failed for harness '${recipe.harnessId}' (exit ${result.exitCode}): ${cmd.command}
2043
+ ${result.stderr || result.stdout}`
2044
+ );
2045
+ }
2046
+ }
2047
+ await session.writeTextFile({
2048
+ path: markerPath,
2049
+ content: "",
2050
+ abortSignal: options == null ? void 0 : options.abortSignal
2051
+ });
2052
+ }
2053
+
2054
+ // src/agent/internal/resolve-observability.ts
2055
+ import { asArray } from "@ai-sdk/provider-utils";
2056
+ var ENV_TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
2057
+ function parseList(value) {
2058
+ if (!value) return void 0;
2059
+ const items = value.split(",").map((item) => item.trim()).filter(Boolean);
2060
+ return items.length > 0 ? items : void 0;
2061
+ }
2062
+ function resolveDebugConfig(debug, env = process.env) {
2063
+ var _a3, _b3, _c, _d;
2064
+ const enabled = (_b3 = debug == null ? void 0 : debug.enabled) != null ? _b3 : ENV_TRUTHY.has(((_a3 = env.HARNESS_DEBUG) != null ? _a3 : "").toLowerCase());
2065
+ const level = (_d = (_c = debug == null ? void 0 : debug.level) != null ? _c : env.HARNESS_DEBUG_LEVEL) != null ? _d : "debug";
2066
+ const subsystems = (debug == null ? void 0 : debug.subsystems) ? [...debug.subsystems] : parseList(env.HARNESS_DEBUG_SUBSYSTEMS);
2067
+ return { enabled, level, subsystems };
2068
+ }
2069
+ function toHarnessDiagnostic(d) {
2070
+ return {
2071
+ level: d.level,
2072
+ message: d.message,
2073
+ subsystem: d.subsystem,
2074
+ kind: d.kind,
2075
+ source: d.source,
2076
+ stream: d.stream,
2077
+ attrs: d.attrs,
2078
+ error: d.error,
2079
+ sessionId: d.sessionId,
2080
+ timestamp: d.timestamp
2081
+ };
2082
+ }
2083
+ function formatForStderr(d) {
2084
+ var _a3;
2085
+ const parts = [`[harness:${d.level}]`, d.subsystem, d.message].filter(
2086
+ Boolean
2087
+ );
2088
+ let line = parts.join(" ");
2089
+ if (d.error) {
2090
+ line += ` (${(_a3 = d.error.name) != null ? _a3 : "Error"}: ${d.error.message})`;
2091
+ }
2092
+ return `${line}
2093
+ `;
2094
+ }
2095
+ function buildObservability(options) {
2096
+ var _a3;
2097
+ const resolved = resolveDebugConfig(options.settings.debug);
2098
+ if (!resolved.enabled) return void 0;
2099
+ const onLog = options.settings.onLog;
2100
+ const integrations = ((_a3 = options.settings.telemetry) == null ? void 0 : _a3.integrations) ? asArray(options.settings.telemetry.integrations) : [];
2101
+ const diagnosticConsumers = integrations.filter(
2102
+ (integration) => typeof integration.ingestDiagnostic === "function"
2103
+ );
2104
+ const report = (emitted) => {
2105
+ var _a4;
2106
+ const diagnostic = toHarnessDiagnostic(emitted);
2107
+ try {
2108
+ process.stderr.write(formatForStderr(diagnostic));
2109
+ } catch (e) {
2110
+ }
2111
+ onLog == null ? void 0 : onLog(diagnostic);
2112
+ for (const consumer of diagnosticConsumers) {
2113
+ (_a4 = consumer.ingestDiagnostic) == null ? void 0 : _a4.call(consumer, diagnostic);
2114
+ }
2115
+ };
2116
+ return {
2117
+ debug: {
2118
+ enabled: true,
2119
+ level: resolved.level,
2120
+ ...resolved.subsystems ? { subsystems: resolved.subsystems } : {}
2121
+ },
2122
+ report
2123
+ };
2124
+ }
2125
+
2126
+ // src/agent/harness-agent.ts
2127
+ var HarnessAgent = class {
2128
+ constructor(settings) {
2129
+ this.version = "agent-v1";
2130
+ var _a3;
2131
+ this.settings = settings;
2132
+ this.id = settings.id;
2133
+ this.userTools = (_a3 = settings.tools) != null ? _a3 : {};
2134
+ this.permissionMode = resolvePermissionMode({
2135
+ permissionMode: settings.permissionMode
2136
+ });
2137
+ if (Object.keys(settings.harness.builtinTools).length > 0 && permissionModeNeedsBuiltinSupport({
2138
+ permissionMode: this.permissionMode
2139
+ }) && settings.harness.supportsBuiltinToolApprovals !== true) {
2140
+ throw new HarnessCapabilityUnsupportedError({
2141
+ message: `Harness '${settings.harness.harnessId}' does not support built-in tool approval requests; use permissionMode: 'allow-all'.`,
2142
+ harnessId: settings.harness.harnessId
2143
+ });
2144
+ }
2145
+ this.tools = {
2146
+ ...settings.harness.builtinTools,
2147
+ ...this.userTools
2148
+ };
2149
+ }
2150
+ /** Identifier of the harness backing this agent. */
2151
+ get harnessId() {
2152
+ return this.settings.harness.harnessId;
2153
+ }
2154
+ /**
2155
+ * Start a fresh session, or resume from state previously returned by
2156
+ * `session.detach()` or `session.stop()`. The returned
2157
+ * `HarnessAgentSession` must be passed to subsequent `generate` / `stream`
2158
+ * calls; end it with `session.detach()`, `session.stop()`, or
2159
+ * `session.destroy()`.
2160
+ */
2161
+ async createSession(options) {
2162
+ var _a3;
2163
+ const sessionId = (_a3 = options == null ? void 0 : options.sessionId) != null ? _a3 : generateId5();
2164
+ const resumeFrom = options == null ? void 0 : options.resumeFrom;
2165
+ const abortSignal = options == null ? void 0 : options.abortSignal;
2166
+ const harness = this.settings.harness;
2167
+ const sandboxProvider = this.settings.sandbox;
2168
+ let validatedResumeFrom;
2169
+ if (resumeFrom != null) {
2170
+ validatedResumeFrom = await validateResumeStateData({
2171
+ harness,
2172
+ state: resumeFrom
2173
+ });
2174
+ }
2175
+ let recipe;
2176
+ let identity;
2177
+ if (harness.getBootstrap != null) {
2178
+ recipe = await harness.getBootstrap({ abortSignal });
2179
+ identity = await hashBootstrap(recipe);
2180
+ }
2181
+ const acquiredSandboxSession = await this._acquireSandbox({
2182
+ sandboxProvider,
2183
+ sessionId,
2184
+ isResume: validatedResumeFrom != null,
2185
+ recipe,
2186
+ identity,
2187
+ abortSignal
2188
+ });
2189
+ const leased = applyPortLease({
2190
+ provider: sandboxProvider,
2191
+ sandboxSession: acquiredSandboxSession,
2192
+ sessionId
2193
+ });
2194
+ const sandboxSession = leased.sandboxSession;
2195
+ const leasedBridgePort = leased.port;
2196
+ const sessionWorkDir = `${sandboxSession.defaultWorkingDirectory}/${harness.harnessId}-${sessionId}`;
2197
+ try {
2198
+ if (validatedResumeFrom == null && recipe != null && identity != null) {
2199
+ await applyBootstrapRecipe(
2200
+ sandboxSession.restricted(),
2201
+ recipe,
2202
+ identity,
2203
+ { abortSignal }
2204
+ );
2205
+ }
2206
+ await sandboxSession.run({
2207
+ command: `mkdir -p ${sessionWorkDir}`,
2208
+ abortSignal
2209
+ });
2210
+ if (this.settings.onSandboxSession != null) {
2211
+ await this.settings.onSandboxSession({
2212
+ session: sandboxSession.restricted(),
2213
+ sessionWorkDir,
2214
+ abortSignal
2215
+ });
2216
+ }
2217
+ } catch (err) {
2218
+ await cleanupAfterStartFailure({
2219
+ sandboxProvider,
2220
+ sandboxSession,
2221
+ sessionId,
2222
+ leasedBridgePort
2223
+ });
2224
+ throw err;
2225
+ }
2226
+ try {
2227
+ const baseStartOptions = {
2228
+ sessionId,
2229
+ skills: this.settings.skills,
2230
+ resumeFrom: validatedResumeFrom,
2231
+ permissionMode: this.permissionMode,
2232
+ abortSignal,
2233
+ observability: buildObservability({ settings: this.settings })
2234
+ };
2235
+ const underlyingSession = await harness.doStart({
2236
+ ...baseStartOptions,
2237
+ sandboxSession,
2238
+ sessionWorkDir
2239
+ });
2240
+ return new HarnessAgentSession({
2241
+ sessionId,
2242
+ harness,
2243
+ underlyingSession,
2244
+ sandboxSession,
2245
+ sandboxProvider,
2246
+ leasedBridgePort,
2247
+ sessionWorkDir,
2248
+ toolApproval: this.settings.toolApproval,
2249
+ pendingToolApprovals: validatedResumeFrom == null ? void 0 : validatedResumeFrom.pendingToolApprovals
2250
+ });
2251
+ } catch (error) {
2252
+ await cleanupAfterStartFailure({
2253
+ sandboxProvider,
2254
+ sandboxSession,
2255
+ sessionId,
2256
+ leasedBridgePort
2257
+ });
2258
+ throw error;
2259
+ }
2260
+ }
2261
+ async generate(options) {
2262
+ const turnInput = this._resolveTurnInput(options);
2263
+ const runtimeContext = {};
2264
+ const { result, done } = this._startTurn({
2265
+ session: options.session,
2266
+ turnInput,
2267
+ runtimeContext,
2268
+ abortSignal: options.abortSignal
2269
+ });
2270
+ await done;
2271
+ return this._toGenerateResult(result);
2272
+ }
2273
+ async stream(options) {
2274
+ const turnInput = this._resolveTurnInput(options);
2275
+ const runtimeContext = {};
2276
+ const { result } = this._startTurn({
2277
+ session: options.session,
2278
+ turnInput,
2279
+ runtimeContext,
2280
+ abortSignal: options.abortSignal
2281
+ });
2282
+ return result;
2283
+ }
2284
+ /**
2285
+ * Continue the in-flight turn **without a new prompt**, streaming its events
2286
+ * like {@link stream}. Used to keep consuming a turn that is still running
2287
+ * (or finished) in the runtime after a process boundary — the workflow slice
2288
+ * loop calls this on every slice after the first. Routes through the adapter's
2289
+ * `doContinueTurn`; what it can guarantee (lossless attach vs. lossy rerun)
2290
+ * follows from how the adapter resumed the session.
2291
+ */
2292
+ async continueTurn(options) {
2293
+ const runtimeContext = {};
2294
+ const { result } = options.session.continueTurn({
2295
+ instructions: this.settings.instructions,
2296
+ tools: this.tools,
2297
+ toolSpecs: this._toToolSpecs(),
2298
+ runtimeContext,
2299
+ abortSignal: options.abortSignal,
2300
+ telemetry: this.settings.telemetry,
2301
+ toolApprovalContinuations: options.toolApprovalContinuations
2302
+ });
2303
+ return result;
2304
+ }
2305
+ // ─── Internals ──────────────────────────────────────────────────────
2306
+ _startTurn(input) {
2307
+ if (input.turnInput.mode === "continue") {
2308
+ return input.session.continueTurn({
2309
+ instructions: this.settings.instructions,
2310
+ tools: this.tools,
2311
+ toolSpecs: this._toToolSpecs(),
2312
+ runtimeContext: input.runtimeContext,
2313
+ abortSignal: input.abortSignal,
2314
+ telemetry: this.settings.telemetry,
2315
+ toolApprovalContinuations: input.turnInput.toolApprovalContinuations
2316
+ });
2317
+ }
2318
+ return input.session.promptTurn({
2319
+ prompt: input.turnInput.prompt,
2320
+ instructions: this.settings.instructions,
2321
+ tools: this.tools,
2322
+ toolSpecs: this._toToolSpecs(),
2323
+ runtimeContext: input.runtimeContext,
2324
+ abortSignal: input.abortSignal,
2325
+ telemetry: this.settings.telemetry
2326
+ });
2327
+ }
2328
+ async _acquireSandbox(input) {
2329
+ const { sandboxProvider } = input;
2330
+ if (input.isResume) {
2331
+ if (sandboxProvider.resumeSession == null) {
2332
+ throw new HarnessCapabilityUnsupportedError({
2333
+ message: `Sandbox provider '${sandboxProvider.providerId}' does not support resume.`,
2334
+ harnessId: this.settings.harness.harnessId
2335
+ });
2336
+ }
2337
+ return sandboxProvider.resumeSession({
2338
+ sessionId: input.sessionId,
2339
+ abortSignal: input.abortSignal
2340
+ });
2341
+ }
2342
+ return sandboxProvider.createSession({
2343
+ sessionId: input.sessionId,
2344
+ abortSignal: input.abortSignal,
2345
+ identity: input.identity,
2346
+ onFirstCreate: input.recipe != null && input.identity != null ? (session, opts) => applyBootstrapRecipe(
2347
+ session,
2348
+ input.recipe,
2349
+ input.identity,
2350
+ opts
2351
+ ) : void 0
2352
+ });
2353
+ }
2354
+ /*
2355
+ * Reduce AI SDK input to the single user message the harness should run
2356
+ * for this turn. The harness session owns prior-turn state (system
2357
+ * prompt, assistant turns, tool results) — we never replay it. A bare
2358
+ * string is forwarded as-is; a message array is collapsed to its last
2359
+ * `role: 'user'` entry. Inputs whose only messages are non-user (system,
2360
+ * assistant, tool) have no fresh user input and are rejected.
2361
+ */
2362
+ _resolveTurnInput(options) {
2363
+ if (typeof options.prompt === "string") {
2364
+ return { mode: "prompt", prompt: options.prompt };
2365
+ }
2366
+ const messages = Array.isArray(options.prompt) ? options.prompt : options.messages;
2367
+ if (Array.isArray(messages)) {
2368
+ const toolApprovalContinuations = collectHarnessAgentToolApprovalContinuations({ messages });
2369
+ if (toolApprovalContinuations.length > 0) {
2370
+ return {
2371
+ mode: "continue",
2372
+ toolApprovalContinuations
2373
+ };
2374
+ }
2375
+ for (let i = messages.length - 1; i >= 0; i--) {
2376
+ const message = messages[i];
2377
+ if ((message == null ? void 0 : message.role) === "user")
2378
+ return { mode: "prompt", prompt: message };
2379
+ }
2380
+ throw new Error(
2381
+ 'HarnessAgent: messages must contain at least one `role: "user"` entry.'
2382
+ );
2383
+ }
2384
+ throw new Error("HarnessAgent: either `prompt` or `messages` is required.");
2385
+ }
2386
+ /*
2387
+ * Wire-format projection of user-defined tools only. Harness builtins are
2388
+ * executed by the runtime and the bridge already knows about them — we
2389
+ * never re-declare them over the wire.
2390
+ */
2391
+ _toToolSpecs() {
2392
+ const specs = [];
2393
+ for (const [name3, tool] of Object.entries(
2394
+ this.userTools
2395
+ )) {
2396
+ const t = tool;
2397
+ let inputSchema;
2398
+ if (t.inputSchema != null) {
2399
+ try {
2400
+ inputSchema = asSchema(
2401
+ t.inputSchema
2402
+ ).jsonSchema;
2403
+ } catch (e) {
2404
+ }
2405
+ }
2406
+ specs.push({ name: name3, description: t.description, inputSchema });
2407
+ }
2408
+ return specs;
2409
+ }
2410
+ async _toGenerateResult(streamResult) {
2411
+ const [steps, usage, responseMessages] = await Promise.all([
2412
+ streamResult.steps,
2413
+ streamResult.usage,
2414
+ streamResult.responseMessages
2415
+ ]);
2416
+ return new HarnessGenerateTextResult({ steps, usage, responseMessages });
2417
+ }
2418
+ };
2419
+ var HarnessGenerateTextResult = class {
2420
+ constructor(options) {
2421
+ this.output = void 0;
2422
+ this.steps = options.steps;
2423
+ this.usage = options.usage;
2424
+ this.responseMessages = options.responseMessages;
2425
+ }
2426
+ get finalStep() {
2427
+ return this.steps.at(-1);
2428
+ }
2429
+ get content() {
2430
+ return this.steps.flatMap((step) => step.content);
2431
+ }
2432
+ get text() {
2433
+ return this.finalStep.text;
2434
+ }
2435
+ get files() {
2436
+ return this.steps.flatMap((step) => step.files);
2437
+ }
2438
+ get sources() {
2439
+ return this.steps.flatMap((step) => step.sources);
2440
+ }
2441
+ get toolCalls() {
2442
+ return this.steps.flatMap((step) => step.toolCalls);
2443
+ }
2444
+ get staticToolCalls() {
2445
+ return this.steps.flatMap((step) => step.staticToolCalls);
2446
+ }
2447
+ get dynamicToolCalls() {
2448
+ return this.steps.flatMap((step) => step.dynamicToolCalls);
2449
+ }
2450
+ get toolResults() {
2451
+ return this.steps.flatMap((step) => step.toolResults);
2452
+ }
2453
+ get staticToolResults() {
2454
+ return this.steps.flatMap((step) => step.staticToolResults);
2455
+ }
2456
+ get dynamicToolResults() {
2457
+ return this.steps.flatMap((step) => step.dynamicToolResults);
2458
+ }
2459
+ get finishReason() {
2460
+ return this.finalStep.finishReason;
2461
+ }
2462
+ get rawFinishReason() {
2463
+ return this.finalStep.rawFinishReason;
2464
+ }
2465
+ get warnings() {
2466
+ return this.steps.flatMap((step) => {
2467
+ var _a3;
2468
+ return (_a3 = step.warnings) != null ? _a3 : [];
2469
+ });
2470
+ }
2471
+ get reasoning() {
2472
+ return this.finalStep.content.filter(
2473
+ (part) => part.type === "reasoning" || part.type === "reasoning-file"
2474
+ );
2475
+ }
2476
+ get reasoningText() {
2477
+ return this.finalStep.reasoningText;
2478
+ }
2479
+ get totalUsage() {
2480
+ return this.usage;
2481
+ }
2482
+ get request() {
2483
+ return this.finalStep.request;
2484
+ }
2485
+ get response() {
2486
+ return this.finalStep.response;
2487
+ }
2488
+ get providerMetadata() {
2489
+ return this.finalStep.providerMetadata;
2490
+ }
2491
+ };
2492
+ function applyPortLease(input) {
2493
+ const pool = input.provider.bridgePorts;
2494
+ if (pool == null || pool.length === 0) {
2495
+ return { sandboxSession: input.sandboxSession, port: void 0 };
2496
+ }
2497
+ const port = acquireBridgePort({
2498
+ poolKey: input.provider,
2499
+ pool,
2500
+ sessionId: input.sessionId
2501
+ });
2502
+ return {
2503
+ sandboxSession: narrowNetworkSessionPorts(input.sandboxSession, port),
2504
+ port
2505
+ };
2506
+ }
2507
+ function narrowNetworkSessionPorts(sandboxSession, leasedPort) {
2508
+ return Object.create(sandboxSession, {
2509
+ ports: {
2510
+ value: [leasedPort],
2511
+ enumerable: true
2512
+ }
2513
+ });
2514
+ }
2515
+ async function cleanupAfterStartFailure(input) {
2516
+ await Promise.resolve(input.sandboxSession.stop()).catch(() => {
2517
+ });
2518
+ if (input.leasedBridgePort != null) {
2519
+ releaseBridgePort({
2520
+ poolKey: input.sandboxProvider,
2521
+ sessionId: input.sessionId
2522
+ });
2523
+ }
2524
+ }
2525
+
2526
+ // src/agent/prewarm.ts
2527
+ async function prewarmHarness(options) {
2528
+ var _a3, _b3;
2529
+ const recipe = await ((_b3 = (_a3 = options.harness).getBootstrap) == null ? void 0 : _b3.call(_a3, {
2530
+ abortSignal: options.abortSignal
2531
+ }));
2532
+ if (recipe == null) return;
2533
+ const identity = await hashBootstrap(recipe);
2534
+ const sandboxSession = await options.sandboxProvider.createSession({
2535
+ abortSignal: options.abortSignal,
2536
+ identity,
2537
+ onFirstCreate: (session, opts) => applyBootstrapRecipe(session, recipe, identity, opts)
2538
+ });
2539
+ try {
2540
+ await applyBootstrapRecipe(sandboxSession.restricted(), recipe, identity, {
2541
+ abortSignal: options.abortSignal
2542
+ });
2543
+ } finally {
2544
+ await Promise.resolve(sandboxSession.stop()).catch(() => {
2545
+ });
2546
+ }
2547
+ }
2548
+ export {
2549
+ HarnessAgent,
2550
+ HarnessAgentSession,
2551
+ collectHarnessAgentToolApprovalContinuations,
2552
+ prewarmHarness
2553
+ };
2554
+ //# sourceMappingURL=index.js.map