@ai-sdk/harness 1.0.72 → 1.0.73
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.
- package/CHANGELOG.md +7 -0
- package/README.md +23 -0
- package/dist/agent/index.d.ts +58 -14
- package/dist/agent/index.js +180 -22
- package/dist/agent/index.js.map +1 -1
- package/dist/bridge/index.js +14 -20
- package/dist/bridge/index.js.map +1 -1
- package/dist/index.d.ts +37 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/index.js +11 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/agent/harness-agent-session.ts +27 -7
- package/src/agent/harness-agent-settings.ts +8 -0
- package/src/agent/harness-agent.ts +74 -23
- package/src/agent/internal/harness-stream-text-result.ts +251 -59
- package/src/agent/internal/run-prompt.ts +10 -2
- package/src/bridge/index.ts +41 -37
- package/src/v1/harness-v1-bridge-protocol.ts +13 -0
- package/src/v1/harness-v1-response-format.ts +32 -0
- package/src/v1/harness-v1-session.ts +13 -0
- package/src/v1/index.ts +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @ai-sdk/harness
|
|
2
2
|
|
|
3
|
+
## 1.0.73
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 62a9c2a: feat(harness): add support for structured output to `HarnessAgent` via `output` property
|
|
8
|
+
- d25cae2: fix(harness): claim the bridge event stream on start/resume instead of on connect
|
|
9
|
+
|
|
3
10
|
## 1.0.72
|
|
4
11
|
|
|
5
12
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -81,6 +81,23 @@ try {
|
|
|
81
81
|
}
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
Set `output` on `HarnessAgent` to require the same typed, schema-backed output
|
|
85
|
+
on every turn. `generate()` exposes the validated value as `result.output`, and
|
|
86
|
+
`stream()` additionally exposes `partialOutputStream`; the JSON also remains on
|
|
87
|
+
the normal text and stream surfaces.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { Output } from 'ai';
|
|
91
|
+
|
|
92
|
+
const agent = new HarnessAgent({
|
|
93
|
+
harness: claudeCode,
|
|
94
|
+
sandbox,
|
|
95
|
+
output: Output.object({
|
|
96
|
+
schema: z.object({ answer: z.string() }),
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
84
101
|
Use `session.detach()` to park a bridge-backed session for later attach, `session.stop()` to save state and stop the sandbox, or `session.destroy()` to clean up without keeping resume state. Bridge-backed adapters such as Claude Code, Codex, OpenCode, and DeepAgents require a sandbox provider that exposes ports — `@ai-sdk/sandbox-vercel` is the supported choice today. `@ai-sdk/sandbox-just-bash` is suitable only for host-runtime or otherwise non-bridge flows, such as Pi.
|
|
85
102
|
|
|
86
103
|
`sandbox` is a required `HarnessV1SandboxProvider` — the agent calls `provider.createSession()` when a session starts. Use `sandboxConfig` for agent specific sandbox configuration that works independently from the sandbox provider that is used:
|
|
@@ -111,6 +128,12 @@ See the [harness adapters documentation](https://ai-sdk.dev/v7/docs/ai-sdk-harne
|
|
|
111
128
|
|
|
112
129
|
Implement the `HarnessV1` factory and a `HarnessV1Session` whose `doPromptTurn` emits events; the agent surface, streaming, tool execution, and multi-turn state are handled for you. Read `startOpts.sandboxSession` for the network sandbox session the agent created and will stop on cleanup. Call `sandboxSession.restricted()` for the tool-safe file-IO/exec/spawn surface.
|
|
113
130
|
|
|
131
|
+
Each prompt and continuation receives an optional `responseFormat`. JSON
|
|
132
|
+
formats carry a caller-provided JSON Schema plus optional name and description;
|
|
133
|
+
the adapter must enforce the schema and emit the resulting JSON through normal
|
|
134
|
+
text parts. If the runtime cannot honor the format, throw
|
|
135
|
+
`HarnessCapabilityUnsupportedError` before starting the turn.
|
|
136
|
+
|
|
114
137
|
Bootstrap recipe paths may be absolute or relative. Relative `bootstrapDir` and
|
|
115
138
|
file paths are resolved against `sandboxSession.defaultWorkingDirectory`.
|
|
116
139
|
The framework creates `bootstrapDir` before writing files, and bootstrap
|
package/dist/agent/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _ai_sdk_provider_utils from '@ai-sdk/provider-utils';
|
|
2
2
|
import { Experimental_SandboxSession, UserModelMessage, ToolSet, FlexibleSchema, Tool, Context, Arrayable, ToolApprovalResponse, ModelMessage } from '@ai-sdk/provider-utils';
|
|
3
|
-
import { StopCondition, ToolApprovalStatus, TelemetryOptions, ActiveTools, StreamTextResult, Agent, AgentCallParameters, GenerateTextResult, AgentStreamParameters, Telemetry } from 'ai';
|
|
3
|
+
import { OutputInterface, StopCondition, ToolApprovalStatus, TelemetryOptions, ActiveTools, StreamTextResult, Agent, AgentCallParameters, GenerateTextResult, AgentStreamParameters, Telemetry } from 'ai';
|
|
4
4
|
import { z } from 'zod/v4';
|
|
5
5
|
import { JSONValue, LanguageModelV4ToolCall, LanguageModelV4ToolApprovalRequest, LanguageModelV4ToolResult, LanguageModelV4FinishReason, LanguageModelV4Usage, JSONSchema7, AISDKError } from '@ai-sdk/provider';
|
|
6
6
|
|
|
@@ -435,6 +435,30 @@ type HarnessV1PromptControl = {
|
|
|
435
435
|
readonly done: PromiseLike<void>;
|
|
436
436
|
};
|
|
437
437
|
|
|
438
|
+
type HarnessV1JSONValue = null | boolean | number | string | HarnessV1JSONArray | HarnessV1JSONObject;
|
|
439
|
+
interface HarnessV1JSONArray extends Array<HarnessV1JSONValue> {
|
|
440
|
+
}
|
|
441
|
+
interface HarnessV1JSONObject {
|
|
442
|
+
[key: string]: HarnessV1JSONValue | undefined;
|
|
443
|
+
}
|
|
444
|
+
type HarnessV1JSONSchema = HarnessV1JSONObject;
|
|
445
|
+
/**
|
|
446
|
+
* Requested response format for one harness turn.
|
|
447
|
+
*
|
|
448
|
+
* This intentionally mirrors the AI SDK provider response-format shape
|
|
449
|
+
* without depending on `@ai-sdk/provider`. Harness implementations receive
|
|
450
|
+
* JSON Schema rather than the caller's original Zod or Standard Schema so the
|
|
451
|
+
* contract can cross process and package boundaries.
|
|
452
|
+
*/
|
|
453
|
+
type HarnessV1ResponseFormat = {
|
|
454
|
+
readonly type: 'text';
|
|
455
|
+
} | {
|
|
456
|
+
readonly type: 'json';
|
|
457
|
+
readonly schema?: HarnessV1JSONSchema;
|
|
458
|
+
readonly name?: string;
|
|
459
|
+
readonly description?: string;
|
|
460
|
+
};
|
|
461
|
+
|
|
438
462
|
type HarnessV1PendingToolApproval = {
|
|
439
463
|
readonly approvalId: string;
|
|
440
464
|
readonly toolCallId: string;
|
|
@@ -764,6 +788,11 @@ type HarnessV1PromptTurnOptions = {
|
|
|
764
788
|
* so prior turns are never replayed across the contract.
|
|
765
789
|
*/
|
|
766
790
|
readonly prompt: HarnessV1Prompt;
|
|
791
|
+
/**
|
|
792
|
+
* Response format requested for this turn. Adapters that cannot honor a
|
|
793
|
+
* JSON response format must throw `HarnessCapabilityUnsupportedError`.
|
|
794
|
+
*/
|
|
795
|
+
readonly responseFormat?: HarnessV1ResponseFormat;
|
|
767
796
|
/**
|
|
768
797
|
* Host-defined tools to make available to the underlying runtime for this
|
|
769
798
|
* turn. The harness emits `tool-call` events when the runtime calls one
|
|
@@ -799,6 +828,11 @@ type HarnessV1PromptTurnOptions = {
|
|
|
799
828
|
* that was previously suspended temporarily, e.g. by the workflow slice loop.
|
|
800
829
|
*/
|
|
801
830
|
type HarnessV1ContinueTurnOptions = {
|
|
831
|
+
/**
|
|
832
|
+
* Response format of the in-flight turn. Rerun-based adapters use this when
|
|
833
|
+
* reconstructing the turn; attach-based adapters may ignore it.
|
|
834
|
+
*/
|
|
835
|
+
readonly responseFormat?: HarnessV1ResponseFormat;
|
|
802
836
|
/**
|
|
803
837
|
* Host-defined tools to make available for the continued turn. Same shape
|
|
804
838
|
* as `doPromptTurn`'s `tools`. An adapter that purely attaches to a live turn
|
|
@@ -1235,7 +1269,7 @@ type HarnessAgentToolFilteringSettings<TOOLS extends ToolSet> = {
|
|
|
1235
1269
|
*/
|
|
1236
1270
|
readonly inactiveTools?: HarnessTools<TOOLS>;
|
|
1237
1271
|
};
|
|
1238
|
-
type HarnessAgentSettings<THarness extends HarnessAgentAdapter<any> = HarnessAgentAdapter, TUserTools extends ToolSet = {}, RUNTIME_CONTEXT extends Context = Context> = {
|
|
1272
|
+
type HarnessAgentSettings<THarness extends HarnessAgentAdapter<any> = HarnessAgentAdapter, TUserTools extends ToolSet = {}, RUNTIME_CONTEXT extends Context = Context, OUTPUT extends OutputInterface = never> = {
|
|
1239
1273
|
/**
|
|
1240
1274
|
* The harness adapter driving the underlying agent runtime. Its
|
|
1241
1275
|
* `builtinTools` are merged with the user-defined `tools` and exposed to
|
|
@@ -1269,6 +1303,11 @@ type HarnessAgentSettings<THarness extends HarnessAgentAdapter<any> = HarnessAge
|
|
|
1269
1303
|
* it to the first user message of a fresh session.
|
|
1270
1304
|
*/
|
|
1271
1305
|
readonly instructions?: string;
|
|
1306
|
+
/**
|
|
1307
|
+
* Optional specification for generating typed output. The same output
|
|
1308
|
+
* requirement is active for every turn run by this agent.
|
|
1309
|
+
*/
|
|
1310
|
+
readonly output?: OUTPUT;
|
|
1272
1311
|
/**
|
|
1273
1312
|
* Conditions that stop the current result after a completed harness tool
|
|
1274
1313
|
* step that can continue into another model step. The underlying turn remains
|
|
@@ -1366,8 +1405,8 @@ declare function collectHarnessAgentToolResultContinuations(input: {
|
|
|
1366
1405
|
messages: readonly ModelMessage[];
|
|
1367
1406
|
}): readonly HarnessAgentToolResultContinuation[];
|
|
1368
1407
|
|
|
1369
|
-
type HarnessAgentTurnResult<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context> = {
|
|
1370
|
-
result: StreamTextResult<TOOLS, RUNTIME_CONTEXT,
|
|
1408
|
+
type HarnessAgentTurnResult<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context, OUTPUT extends OutputInterface> = {
|
|
1409
|
+
result: StreamTextResult<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
|
|
1371
1410
|
done: Promise<void>;
|
|
1372
1411
|
};
|
|
1373
1412
|
type HarnessAgentTurnState = 'idle' | 'running' | 'awaiting-approval' | 'awaiting-tool-result' | 'suspended';
|
|
@@ -1425,7 +1464,7 @@ declare class HarnessAgentSession {
|
|
|
1425
1464
|
turnState?: HarnessAgentTurnState;
|
|
1426
1465
|
});
|
|
1427
1466
|
hasUnfinishedTurn(): boolean;
|
|
1428
|
-
promptTurn<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context>(options: {
|
|
1467
|
+
promptTurn<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context, OUTPUT extends OutputInterface>(options: {
|
|
1429
1468
|
prompt: HarnessAgentPrompt;
|
|
1430
1469
|
instructions: string | undefined;
|
|
1431
1470
|
tools: TOOLS;
|
|
@@ -1434,10 +1473,12 @@ declare class HarnessAgentSession {
|
|
|
1434
1473
|
builtinToolFiltering: HarnessV1BuiltinToolFiltering | undefined;
|
|
1435
1474
|
runtimeContext: RUNTIME_CONTEXT;
|
|
1436
1475
|
abortSignal: AbortSignal | undefined;
|
|
1476
|
+
responseFormat: HarnessV1ResponseFormat | undefined;
|
|
1477
|
+
output: OUTPUT | undefined;
|
|
1437
1478
|
telemetry: TelemetryOptions | undefined;
|
|
1438
1479
|
stopConditions: ReadonlyArray<StopCondition<TOOLS, RUNTIME_CONTEXT>>;
|
|
1439
|
-
}): HarnessAgentTurnResult<TOOLS, RUNTIME_CONTEXT>;
|
|
1440
|
-
continueTurn<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context>(options: {
|
|
1480
|
+
}): HarnessAgentTurnResult<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
|
|
1481
|
+
continueTurn<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context, OUTPUT extends OutputInterface>(options: {
|
|
1441
1482
|
instructions: string | undefined;
|
|
1442
1483
|
tools: TOOLS;
|
|
1443
1484
|
activeTools: ToolSet;
|
|
@@ -1445,11 +1486,13 @@ declare class HarnessAgentSession {
|
|
|
1445
1486
|
builtinToolFiltering: HarnessV1BuiltinToolFiltering | undefined;
|
|
1446
1487
|
runtimeContext: RUNTIME_CONTEXT;
|
|
1447
1488
|
abortSignal: AbortSignal | undefined;
|
|
1489
|
+
responseFormat: HarnessV1ResponseFormat | undefined;
|
|
1490
|
+
output: OUTPUT | undefined;
|
|
1448
1491
|
telemetry: TelemetryOptions | undefined;
|
|
1449
1492
|
stopConditions: ReadonlyArray<StopCondition<TOOLS, RUNTIME_CONTEXT>>;
|
|
1450
1493
|
toolApprovalContinuations?: readonly HarnessAgentToolApprovalContinuation[] | undefined;
|
|
1451
1494
|
toolResultContinuations?: readonly HarnessAgentToolResultContinuation[] | undefined;
|
|
1452
|
-
}): HarnessAgentTurnResult<TOOLS, RUNTIME_CONTEXT>;
|
|
1495
|
+
}): HarnessAgentTurnResult<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
|
|
1453
1496
|
/**
|
|
1454
1497
|
* Ask the underlying runtime to compact its context. The runtime performs
|
|
1455
1498
|
* the compaction itself; when it completes, a `compaction` part appears on
|
|
@@ -1556,7 +1599,7 @@ interface HarnessAgentCallExtensions {
|
|
|
1556
1599
|
* `Experimental_SandboxSession`) is handed to user-tool `execute()` calls
|
|
1557
1600
|
* via `experimental_sandbox`.
|
|
1558
1601
|
*/
|
|
1559
|
-
declare class HarnessAgent<THarness extends HarnessAgentAdapter<any> = HarnessAgentAdapter, TUserTools extends ToolSet = {}, RUNTIME_CONTEXT extends Context = Context> implements Agent<never, HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT,
|
|
1602
|
+
declare class HarnessAgent<THarness extends HarnessAgentAdapter<any> = HarnessAgentAdapter, TUserTools extends ToolSet = {}, RUNTIME_CONTEXT extends Context = Context, OUTPUT extends OutputInterface = never> implements Agent<never, HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT, OUTPUT> {
|
|
1560
1603
|
readonly version: "agent-v1";
|
|
1561
1604
|
readonly id: string | undefined;
|
|
1562
1605
|
/**
|
|
@@ -1572,7 +1615,7 @@ declare class HarnessAgent<THarness extends HarnessAgentAdapter<any> = HarnessAg
|
|
|
1572
1615
|
private readonly activeUserTools;
|
|
1573
1616
|
private readonly builtinToolFiltering;
|
|
1574
1617
|
private readonly permissionMode;
|
|
1575
|
-
constructor(settings: HarnessAgentSettings<THarness, TUserTools, RUNTIME_CONTEXT>);
|
|
1618
|
+
constructor(settings: HarnessAgentSettings<THarness, TUserTools, RUNTIME_CONTEXT, OUTPUT>);
|
|
1576
1619
|
/** Identifier of the harness backing this agent. */
|
|
1577
1620
|
get harnessId(): string;
|
|
1578
1621
|
/**
|
|
@@ -1605,8 +1648,8 @@ declare class HarnessAgent<THarness extends HarnessAgentAdapter<any> = HarnessAg
|
|
|
1605
1648
|
continueFrom?: HarnessAgentContinueTurnState;
|
|
1606
1649
|
abortSignal?: AbortSignal;
|
|
1607
1650
|
}): Promise<HarnessAgentSession>;
|
|
1608
|
-
generate(options: AgentCallParameters<never, HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT> & HarnessAgentCallExtensions): Promise<GenerateTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT,
|
|
1609
|
-
stream(options: AgentStreamParameters<never, HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT> & HarnessAgentCallExtensions): Promise<StreamTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT,
|
|
1651
|
+
generate(options: AgentCallParameters<never, HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT> & HarnessAgentCallExtensions): Promise<GenerateTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT, OUTPUT>>;
|
|
1652
|
+
stream(options: AgentStreamParameters<never, HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT> & HarnessAgentCallExtensions): Promise<StreamTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT, OUTPUT>>;
|
|
1610
1653
|
/**
|
|
1611
1654
|
* Continue the in-flight turn **without a new prompt**, draining it like
|
|
1612
1655
|
* {@link generate}. Used after `createSession({ continueFrom })` to finish
|
|
@@ -1617,7 +1660,7 @@ declare class HarnessAgent<THarness extends HarnessAgentAdapter<any> = HarnessAg
|
|
|
1617
1660
|
toolApprovalContinuations?: readonly HarnessAgentToolApprovalContinuation[];
|
|
1618
1661
|
toolResultContinuations?: readonly HarnessAgentToolResultContinuation[];
|
|
1619
1662
|
abortSignal?: AbortSignal;
|
|
1620
|
-
}): Promise<GenerateTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT,
|
|
1663
|
+
}): Promise<GenerateTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT, OUTPUT>>;
|
|
1621
1664
|
/**
|
|
1622
1665
|
* Continue the in-flight turn **without a new prompt**, streaming its events
|
|
1623
1666
|
* like {@link stream}. Used to keep consuming a turn that is still running
|
|
@@ -1631,12 +1674,13 @@ declare class HarnessAgent<THarness extends HarnessAgentAdapter<any> = HarnessAg
|
|
|
1631
1674
|
toolApprovalContinuations?: readonly HarnessAgentToolApprovalContinuation[];
|
|
1632
1675
|
toolResultContinuations?: readonly HarnessAgentToolResultContinuation[];
|
|
1633
1676
|
abortSignal?: AbortSignal;
|
|
1634
|
-
}): Promise<StreamTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT,
|
|
1677
|
+
}): Promise<StreamTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT, OUTPUT>>;
|
|
1635
1678
|
private _startTurn;
|
|
1636
1679
|
private _acquireSandbox;
|
|
1637
1680
|
private _resolveTurnInput;
|
|
1638
1681
|
private _toToolSpecs;
|
|
1639
1682
|
private _toGenerateResult;
|
|
1683
|
+
private _resolveResponseFormat;
|
|
1640
1684
|
}
|
|
1641
1685
|
|
|
1642
1686
|
type SandboxBootstrapSettings = Omit<HarnessAgentSandboxConfig, 'onSession'>;
|
package/dist/agent/index.js
CHANGED
|
@@ -250,6 +250,7 @@ var HarnessStreamTextResult = class {
|
|
|
250
250
|
this.toolsContext = options.toolsContext;
|
|
251
251
|
this.providerName = `harness:${options.harnessId}`;
|
|
252
252
|
this.modelId = options.sessionId;
|
|
253
|
+
this.outputSpecification = options.output;
|
|
253
254
|
let controllerRef;
|
|
254
255
|
const baseStream = new ReadableStream({
|
|
255
256
|
start(c) {
|
|
@@ -258,18 +259,13 @@ var HarnessStreamTextResult = class {
|
|
|
258
259
|
}
|
|
259
260
|
});
|
|
260
261
|
this.fullStreamController = controllerRef;
|
|
261
|
-
|
|
262
|
-
this.stream = forFull;
|
|
263
|
-
this.fullStream = this.stream;
|
|
264
|
-
this.textStream = forText.pipeThrough(
|
|
262
|
+
this.baseStream = options.output == null ? baseStream.pipeThrough(
|
|
265
263
|
new TransformStream({
|
|
266
264
|
transform(part, controller) {
|
|
267
|
-
|
|
268
|
-
controller.enqueue(part.text);
|
|
269
|
-
}
|
|
265
|
+
controller.enqueue({ part, partialOutput: void 0 });
|
|
270
266
|
}
|
|
271
267
|
})
|
|
272
|
-
);
|
|
268
|
+
) : baseStream.pipeThrough(createOutputTransformStream(options.output));
|
|
273
269
|
}
|
|
274
270
|
// ─── Writer-side methods used by the driver ────────────────────────
|
|
275
271
|
/**
|
|
@@ -525,6 +521,38 @@ var HarnessStreamTextResult = class {
|
|
|
525
521
|
}
|
|
526
522
|
}
|
|
527
523
|
// ─── Reader-side public surface (StreamTextResult contract) ────────
|
|
524
|
+
teeStream() {
|
|
525
|
+
const [stream, remainingStream] = this.baseStream.tee();
|
|
526
|
+
this.baseStream = remainingStream;
|
|
527
|
+
return stream;
|
|
528
|
+
}
|
|
529
|
+
get stream() {
|
|
530
|
+
return createAsyncIterableStream(
|
|
531
|
+
this.teeStream().pipeThrough(
|
|
532
|
+
new TransformStream({
|
|
533
|
+
transform({ part }, controller) {
|
|
534
|
+
controller.enqueue(part);
|
|
535
|
+
}
|
|
536
|
+
})
|
|
537
|
+
)
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
get fullStream() {
|
|
541
|
+
return this.stream;
|
|
542
|
+
}
|
|
543
|
+
get textStream() {
|
|
544
|
+
return createAsyncIterableStream(
|
|
545
|
+
this.stream.pipeThrough(
|
|
546
|
+
new TransformStream({
|
|
547
|
+
transform(part, controller) {
|
|
548
|
+
if (part.type === "text-delta") {
|
|
549
|
+
controller.enqueue(part.text);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
})
|
|
553
|
+
)
|
|
554
|
+
);
|
|
555
|
+
}
|
|
528
556
|
get content() {
|
|
529
557
|
return this._content.promise;
|
|
530
558
|
}
|
|
@@ -594,18 +622,48 @@ var HarnessStreamTextResult = class {
|
|
|
594
622
|
get providerMetadata() {
|
|
595
623
|
return this._providerMetadata.promise;
|
|
596
624
|
}
|
|
597
|
-
// Output-specification surfaces are not yet supported.
|
|
598
625
|
get experimental_partialOutputStream() {
|
|
599
|
-
|
|
626
|
+
return this.partialOutputStream;
|
|
600
627
|
}
|
|
601
628
|
get partialOutputStream() {
|
|
602
|
-
|
|
629
|
+
return createAsyncIterableStream(
|
|
630
|
+
this.teeStream().pipeThrough(
|
|
631
|
+
new TransformStream({
|
|
632
|
+
transform({ partialOutput }, controller) {
|
|
633
|
+
if (partialOutput != null) {
|
|
634
|
+
controller.enqueue(partialOutput);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
})
|
|
638
|
+
)
|
|
639
|
+
);
|
|
603
640
|
}
|
|
604
641
|
get elementStream() {
|
|
605
|
-
|
|
642
|
+
var _a4, _b4, _c;
|
|
643
|
+
const transform = (_a4 = this.outputSpecification) == null ? void 0 : _a4.createElementStreamTransform();
|
|
644
|
+
if (transform == null) {
|
|
645
|
+
throw notSupportedYet(
|
|
646
|
+
`element streams in ${(_c = (_b4 = this.outputSpecification) == null ? void 0 : _b4.name) != null ? _c : "text"} mode`
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
return createAsyncIterableStream(
|
|
650
|
+
this.teeStream().pipeThrough(transform)
|
|
651
|
+
);
|
|
606
652
|
}
|
|
607
653
|
get output() {
|
|
608
|
-
|
|
654
|
+
return this.finalStep.then((step) => {
|
|
655
|
+
if (this.outputSpecification == null) {
|
|
656
|
+
throw notSupportedYet("structured output");
|
|
657
|
+
}
|
|
658
|
+
return this.outputSpecification.parseCompleteOutput(
|
|
659
|
+
{ text: step.text },
|
|
660
|
+
{
|
|
661
|
+
response: step.response,
|
|
662
|
+
usage: step.usage,
|
|
663
|
+
finishReason: step.finishReason
|
|
664
|
+
}
|
|
665
|
+
);
|
|
666
|
+
});
|
|
609
667
|
}
|
|
610
668
|
async consumeStream() {
|
|
611
669
|
const reader = this.fullStream.getReader();
|
|
@@ -725,6 +783,73 @@ var HarnessStreamTextResult = class {
|
|
|
725
783
|
}
|
|
726
784
|
}
|
|
727
785
|
};
|
|
786
|
+
function createOutputTransformStream(output) {
|
|
787
|
+
let firstTextChunkId;
|
|
788
|
+
let text = "";
|
|
789
|
+
let textChunk = "";
|
|
790
|
+
let textProviderMetadata;
|
|
791
|
+
let lastPublishedValue = "";
|
|
792
|
+
const publishTextChunk = (options) => {
|
|
793
|
+
options.controller.enqueue({
|
|
794
|
+
part: {
|
|
795
|
+
type: "text-delta",
|
|
796
|
+
id: firstTextChunkId,
|
|
797
|
+
text: textChunk,
|
|
798
|
+
providerMetadata: textProviderMetadata
|
|
799
|
+
},
|
|
800
|
+
partialOutput: options.partialOutput
|
|
801
|
+
});
|
|
802
|
+
textChunk = "";
|
|
803
|
+
};
|
|
804
|
+
return new TransformStream({
|
|
805
|
+
async transform(chunk, controller) {
|
|
806
|
+
var _a4;
|
|
807
|
+
if (chunk.type === "finish-step" && textChunk.length > 0) {
|
|
808
|
+
publishTextChunk({ controller });
|
|
809
|
+
}
|
|
810
|
+
if (chunk.type !== "text-delta" && chunk.type !== "text-start" && chunk.type !== "text-end") {
|
|
811
|
+
controller.enqueue({ part: chunk, partialOutput: void 0 });
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
if (firstTextChunkId == null) {
|
|
815
|
+
firstTextChunkId = chunk.id;
|
|
816
|
+
} else if (chunk.id !== firstTextChunkId) {
|
|
817
|
+
controller.enqueue({ part: chunk, partialOutput: void 0 });
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (chunk.type === "text-start") {
|
|
821
|
+
controller.enqueue({ part: chunk, partialOutput: void 0 });
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
if (chunk.type === "text-end") {
|
|
825
|
+
if (textChunk.length > 0) {
|
|
826
|
+
publishTextChunk({ controller });
|
|
827
|
+
}
|
|
828
|
+
controller.enqueue({ part: chunk, partialOutput: void 0 });
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
text += chunk.text;
|
|
832
|
+
textChunk += chunk.text;
|
|
833
|
+
textProviderMetadata = (_a4 = chunk.providerMetadata) != null ? _a4 : textProviderMetadata;
|
|
834
|
+
if (chunk.text.length === 0 && chunk.providerMetadata != null) {
|
|
835
|
+
controller.enqueue({ part: chunk, partialOutput: void 0 });
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
const result = await output.parsePartialOutput({ text });
|
|
839
|
+
if (result === void 0) {
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
const currentValue = typeof result.partial === "string" ? result.partial : JSON.stringify(result.partial);
|
|
843
|
+
if (currentValue !== lastPublishedValue) {
|
|
844
|
+
publishTextChunk({
|
|
845
|
+
controller,
|
|
846
|
+
partialOutput: result.partial
|
|
847
|
+
});
|
|
848
|
+
lastPublishedValue = currentValue;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
}
|
|
728
853
|
function createEmptyPerformance() {
|
|
729
854
|
return {
|
|
730
855
|
effectiveOutputTokensPerSecond: 0,
|
|
@@ -1372,7 +1497,8 @@ function runPrompt(input) {
|
|
|
1372
1497
|
// toolsContext is not configurable for harnesses; pass undefined cast.
|
|
1373
1498
|
toolsContext: void 0,
|
|
1374
1499
|
harnessId: input.harness.harnessId,
|
|
1375
|
-
sessionId: input.session.sessionId
|
|
1500
|
+
sessionId: input.session.sessionId,
|
|
1501
|
+
output: input.output
|
|
1376
1502
|
});
|
|
1377
1503
|
const pendingToolApprovals = (_a4 = input.pendingToolApprovals) != null ? _a4 : [];
|
|
1378
1504
|
const pendingToolResults = (_b4 = input.pendingToolResults) != null ? _b4 : [];
|
|
@@ -1413,6 +1539,7 @@ function runPrompt(input) {
|
|
|
1413
1539
|
try {
|
|
1414
1540
|
bridge = await toHarnessStream({
|
|
1415
1541
|
invoke: input.mode === "continue" ? (emit) => input.session.doContinueTurn({
|
|
1542
|
+
responseFormat: input.responseFormat,
|
|
1416
1543
|
tools: input.toolSpecs,
|
|
1417
1544
|
instructions: input.instructions,
|
|
1418
1545
|
abortSignal: input.abortSignal,
|
|
@@ -1425,6 +1552,7 @@ function runPrompt(input) {
|
|
|
1425
1552
|
}
|
|
1426
1553
|
return input.session.doPromptTurn({
|
|
1427
1554
|
prompt: input.prompt,
|
|
1555
|
+
responseFormat: input.responseFormat,
|
|
1428
1556
|
tools: input.toolSpecs,
|
|
1429
1557
|
instructions: input.instructions,
|
|
1430
1558
|
abortSignal: input.abortSignal,
|
|
@@ -2273,6 +2401,8 @@ var HarnessAgentSession = class {
|
|
|
2273
2401
|
sessionWorkDir: this.sessionWorkDir,
|
|
2274
2402
|
runtimeContext: options.runtimeContext,
|
|
2275
2403
|
abortSignal: options.abortSignal,
|
|
2404
|
+
responseFormat: options.responseFormat,
|
|
2405
|
+
output: options.output,
|
|
2276
2406
|
telemetry: options.telemetry,
|
|
2277
2407
|
stopConditions: options.stopConditions,
|
|
2278
2408
|
toolApproval: this.toolApproval,
|
|
@@ -2326,6 +2456,8 @@ var HarnessAgentSession = class {
|
|
|
2326
2456
|
sessionWorkDir: this.sessionWorkDir,
|
|
2327
2457
|
runtimeContext: options.runtimeContext,
|
|
2328
2458
|
abortSignal: options.abortSignal,
|
|
2459
|
+
responseFormat: options.responseFormat,
|
|
2460
|
+
output: options.output,
|
|
2329
2461
|
telemetry: options.telemetry,
|
|
2330
2462
|
stopConditions: options.stopConditions,
|
|
2331
2463
|
toolApproval: this.toolApproval,
|
|
@@ -3342,11 +3474,13 @@ var HarnessAgent = class {
|
|
|
3342
3474
|
async generate(options) {
|
|
3343
3475
|
const turnInput = this._resolveTurnInput(options);
|
|
3344
3476
|
const runtimeContext = {};
|
|
3477
|
+
const responseFormat = await this._resolveResponseFormat();
|
|
3345
3478
|
const { result, done } = this._startTurn({
|
|
3346
3479
|
session: options.session,
|
|
3347
3480
|
turnInput,
|
|
3348
3481
|
runtimeContext,
|
|
3349
|
-
abortSignal: options.abortSignal
|
|
3482
|
+
abortSignal: options.abortSignal,
|
|
3483
|
+
responseFormat
|
|
3350
3484
|
});
|
|
3351
3485
|
await done;
|
|
3352
3486
|
return this._toGenerateResult(result);
|
|
@@ -3354,11 +3488,13 @@ var HarnessAgent = class {
|
|
|
3354
3488
|
async stream(options) {
|
|
3355
3489
|
const turnInput = this._resolveTurnInput(options);
|
|
3356
3490
|
const runtimeContext = {};
|
|
3491
|
+
const responseFormat = await this._resolveResponseFormat();
|
|
3357
3492
|
const { result } = this._startTurn({
|
|
3358
3493
|
session: options.session,
|
|
3359
3494
|
turnInput,
|
|
3360
3495
|
runtimeContext,
|
|
3361
|
-
abortSignal: options.abortSignal
|
|
3496
|
+
abortSignal: options.abortSignal,
|
|
3497
|
+
responseFormat
|
|
3362
3498
|
});
|
|
3363
3499
|
return result;
|
|
3364
3500
|
}
|
|
@@ -3370,6 +3506,7 @@ var HarnessAgent = class {
|
|
|
3370
3506
|
async continueGenerate(options) {
|
|
3371
3507
|
var _a4, _b4;
|
|
3372
3508
|
const runtimeContext = {};
|
|
3509
|
+
const responseFormat = await this._resolveResponseFormat();
|
|
3373
3510
|
const { result, done } = this._startTurn({
|
|
3374
3511
|
session: options.session,
|
|
3375
3512
|
turnInput: {
|
|
@@ -3378,7 +3515,8 @@ var HarnessAgent = class {
|
|
|
3378
3515
|
toolResultContinuations: (_b4 = options.toolResultContinuations) != null ? _b4 : []
|
|
3379
3516
|
},
|
|
3380
3517
|
runtimeContext,
|
|
3381
|
-
abortSignal: options.abortSignal
|
|
3518
|
+
abortSignal: options.abortSignal,
|
|
3519
|
+
responseFormat
|
|
3382
3520
|
});
|
|
3383
3521
|
await done;
|
|
3384
3522
|
return this._toGenerateResult(result);
|
|
@@ -3394,6 +3532,7 @@ var HarnessAgent = class {
|
|
|
3394
3532
|
async continueStream(options) {
|
|
3395
3533
|
var _a4, _b4;
|
|
3396
3534
|
const runtimeContext = {};
|
|
3535
|
+
const responseFormat = await this._resolveResponseFormat();
|
|
3397
3536
|
const { result } = this._startTurn({
|
|
3398
3537
|
session: options.session,
|
|
3399
3538
|
turnInput: {
|
|
@@ -3402,7 +3541,8 @@ var HarnessAgent = class {
|
|
|
3402
3541
|
toolResultContinuations: (_b4 = options.toolResultContinuations) != null ? _b4 : []
|
|
3403
3542
|
},
|
|
3404
3543
|
runtimeContext,
|
|
3405
|
-
abortSignal: options.abortSignal
|
|
3544
|
+
abortSignal: options.abortSignal,
|
|
3545
|
+
responseFormat
|
|
3406
3546
|
});
|
|
3407
3547
|
return result;
|
|
3408
3548
|
}
|
|
@@ -3417,6 +3557,8 @@ var HarnessAgent = class {
|
|
|
3417
3557
|
builtinToolFiltering: this.builtinToolFiltering,
|
|
3418
3558
|
runtimeContext: input.runtimeContext,
|
|
3419
3559
|
abortSignal: input.abortSignal,
|
|
3560
|
+
responseFormat: input.responseFormat,
|
|
3561
|
+
output: this.settings.output,
|
|
3420
3562
|
telemetry: this.settings.telemetry,
|
|
3421
3563
|
stopConditions: this.stopConditions,
|
|
3422
3564
|
toolApprovalContinuations: input.turnInput.toolApprovalContinuations,
|
|
@@ -3432,6 +3574,8 @@ var HarnessAgent = class {
|
|
|
3432
3574
|
builtinToolFiltering: this.builtinToolFiltering,
|
|
3433
3575
|
runtimeContext: input.runtimeContext,
|
|
3434
3576
|
abortSignal: input.abortSignal,
|
|
3577
|
+
responseFormat: input.responseFormat,
|
|
3578
|
+
output: this.settings.output,
|
|
3435
3579
|
telemetry: this.settings.telemetry,
|
|
3436
3580
|
stopConditions: this.stopConditions
|
|
3437
3581
|
});
|
|
@@ -3516,20 +3660,34 @@ var HarnessAgent = class {
|
|
|
3516
3660
|
return specs;
|
|
3517
3661
|
}
|
|
3518
3662
|
async _toGenerateResult(streamResult) {
|
|
3519
|
-
const [steps, usage, responseMessages] = await Promise.all([
|
|
3663
|
+
const [steps, usage, responseMessages, output] = await Promise.all([
|
|
3520
3664
|
streamResult.steps,
|
|
3521
3665
|
streamResult.usage,
|
|
3522
|
-
streamResult.responseMessages
|
|
3666
|
+
streamResult.responseMessages,
|
|
3667
|
+
this.settings.output == null ? Promise.resolve(void 0) : streamResult.output
|
|
3523
3668
|
]);
|
|
3524
|
-
return new HarnessGenerateTextResult({ steps, usage, responseMessages });
|
|
3669
|
+
return new HarnessGenerateTextResult({ steps, usage, responseMessages, output });
|
|
3670
|
+
}
|
|
3671
|
+
async _resolveResponseFormat() {
|
|
3672
|
+
var _a4;
|
|
3673
|
+
const responseFormat = await ((_a4 = this.settings.output) == null ? void 0 : _a4.responseFormat);
|
|
3674
|
+
if (responseFormat == null || responseFormat.type === "text") {
|
|
3675
|
+
return responseFormat == null ? void 0 : { type: "text" };
|
|
3676
|
+
}
|
|
3677
|
+
return {
|
|
3678
|
+
type: "json",
|
|
3679
|
+
...responseFormat.schema == null ? {} : { schema: responseFormat.schema },
|
|
3680
|
+
...responseFormat.name == null ? {} : { name: responseFormat.name },
|
|
3681
|
+
...responseFormat.description == null ? {} : { description: responseFormat.description }
|
|
3682
|
+
};
|
|
3525
3683
|
}
|
|
3526
3684
|
};
|
|
3527
3685
|
var HarnessGenerateTextResult = class {
|
|
3528
3686
|
constructor(options) {
|
|
3529
|
-
this.output = void 0;
|
|
3530
3687
|
this.steps = options.steps;
|
|
3531
3688
|
this.usage = options.usage;
|
|
3532
3689
|
this.responseMessages = options.responseMessages;
|
|
3690
|
+
this.output = options.output;
|
|
3533
3691
|
}
|
|
3534
3692
|
get finalStep() {
|
|
3535
3693
|
return this.steps.at(-1);
|