@ai-sdk/harness 1.0.92 → 1.0.94
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 +29 -0
- package/README.md +5 -1
- package/dist/agent/index.d.ts +166 -118
- package/dist/agent/index.js +399 -102
- package/dist/agent/index.js.map +1 -1
- package/dist/bridge/index.d.ts +20 -1
- package/dist/bridge/index.js +29 -5
- package/dist/bridge/index.js.map +1 -1
- package/dist/index.d.ts +142 -102
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts +8 -2
- package/dist/utils/index.js +339 -33
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -5
- package/src/agent/harness-agent-session.ts +115 -7
- package/src/agent/harness-agent-settings.ts +87 -7
- package/src/agent/harness-agent.ts +328 -112
- package/src/agent/internal/lifecycle-state-validation.ts +3 -0
- package/src/agent/internal/run-prompt.ts +49 -11
- package/src/agent/internal/strip-work-dir.ts +102 -0
- package/src/agent/internal/translate-stream-part.ts +5 -0
- package/src/bridge/index.ts +73 -6
- package/src/utils/index.ts +1 -0
- package/src/utils/write-skills.ts +419 -32
- package/src/v1/harness-v1-bridge-protocol.ts +6 -0
- package/src/v1/harness-v1-lifecycle-state.ts +45 -0
- package/src/v1/harness-v1-session.ts +3 -48
- package/src/v1/harness-v1-stream-part.ts +31 -1
- package/src/v1/index.ts +1 -0
|
@@ -15,6 +15,8 @@ import type {
|
|
|
15
15
|
HarnessV1NetworkSandboxSession,
|
|
16
16
|
HarnessV1PromptControl,
|
|
17
17
|
HarnessV1ResponseFormat,
|
|
18
|
+
HarnessV1Skill,
|
|
19
|
+
HarnessV1TurnSettings,
|
|
18
20
|
} from '../v1';
|
|
19
21
|
import { HarnessCapabilityUnsupportedError } from '../errors/harness-capability-unsupported-error';
|
|
20
22
|
import type {
|
|
@@ -40,6 +42,7 @@ type HarnessAgentTurnResult<
|
|
|
40
42
|
> = {
|
|
41
43
|
result: StreamTextResult<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
|
|
42
44
|
done: Promise<void>;
|
|
45
|
+
ready: Promise<void>;
|
|
43
46
|
};
|
|
44
47
|
|
|
45
48
|
type HarnessAgentSessionState = 'active' | 'detached' | 'stopped' | 'destroyed';
|
|
@@ -58,6 +61,13 @@ type ActivePromptControl = {
|
|
|
58
61
|
settled: boolean;
|
|
59
62
|
};
|
|
60
63
|
|
|
64
|
+
type ActiveTurnSettings = {
|
|
65
|
+
readonly persisted: HarnessV1TurnSettings;
|
|
66
|
+
readonly tools: ToolSet;
|
|
67
|
+
readonly activeTools: ToolSet;
|
|
68
|
+
readonly builtinToolFiltering: HarnessV1BuiltinToolFiltering | undefined;
|
|
69
|
+
};
|
|
70
|
+
|
|
61
71
|
/**
|
|
62
72
|
* Live harness session held by the caller.
|
|
63
73
|
*
|
|
@@ -105,6 +115,8 @@ export class HarnessAgentSession {
|
|
|
105
115
|
private suspendedTurnState:
|
|
106
116
|
| Promise<HarnessAgentContinueTurnState>
|
|
107
117
|
| undefined;
|
|
118
|
+
private activeTurnSettings: ActiveTurnSettings | undefined;
|
|
119
|
+
private persistedTurnSettings: HarnessV1TurnSettings | undefined;
|
|
108
120
|
|
|
109
121
|
/**
|
|
110
122
|
* Whether this session was created from `resumeFrom` or `continueFrom`.
|
|
@@ -122,6 +134,7 @@ export class HarnessAgentSession {
|
|
|
122
134
|
toolApproval: HarnessAgentToolApprovalConfiguration | undefined;
|
|
123
135
|
pendingToolApprovals?: readonly HarnessAgentPendingToolApproval[];
|
|
124
136
|
pendingToolResults?: readonly HarnessAgentPendingToolResult[];
|
|
137
|
+
turnSettings?: HarnessV1TurnSettings;
|
|
125
138
|
turnState?: HarnessAgentTurnState;
|
|
126
139
|
}) {
|
|
127
140
|
this.sessionId = options.sessionId;
|
|
@@ -137,6 +150,7 @@ export class HarnessAgentSession {
|
|
|
137
150
|
for (const pendingResult of options.pendingToolResults ?? []) {
|
|
138
151
|
this.pendingToolResults.set(pendingResult.toolCallId, pendingResult);
|
|
139
152
|
}
|
|
153
|
+
this.persistedTurnSettings = options.turnSettings;
|
|
140
154
|
this.turnState =
|
|
141
155
|
options.turnState ??
|
|
142
156
|
(this.pendingToolApprovals.size > 0
|
|
@@ -181,6 +195,8 @@ export class HarnessAgentSession {
|
|
|
181
195
|
OUTPUT extends Output,
|
|
182
196
|
>(options: {
|
|
183
197
|
prompt: HarnessAgentPrompt;
|
|
198
|
+
model: string | undefined;
|
|
199
|
+
skills: ReadonlyArray<HarnessV1Skill>;
|
|
184
200
|
instructions: string | undefined;
|
|
185
201
|
tools: TOOLS;
|
|
186
202
|
activeTools: ToolSet;
|
|
@@ -195,6 +211,20 @@ export class HarnessAgentSession {
|
|
|
195
211
|
}): HarnessAgentTurnResult<TOOLS, RUNTIME_CONTEXT, OUTPUT> {
|
|
196
212
|
const session = this.requireReusableSession();
|
|
197
213
|
this.requirePromptableTurn();
|
|
214
|
+
this.persistedTurnSettings = {
|
|
215
|
+
...(options.model == null ? {} : { model: options.model }),
|
|
216
|
+
skills: options.skills,
|
|
217
|
+
...(options.instructions == null
|
|
218
|
+
? {}
|
|
219
|
+
: { instructions: options.instructions }),
|
|
220
|
+
tools: options.toolSpecs,
|
|
221
|
+
};
|
|
222
|
+
this.activeTurnSettings = {
|
|
223
|
+
persisted: this.persistedTurnSettings,
|
|
224
|
+
tools: options.tools,
|
|
225
|
+
activeTools: options.activeTools,
|
|
226
|
+
builtinToolFiltering: options.builtinToolFiltering,
|
|
227
|
+
};
|
|
198
228
|
const sandboxSession = this.getSandboxSession();
|
|
199
229
|
const turnId = this.startTrackedTurn();
|
|
200
230
|
try {
|
|
@@ -202,6 +232,8 @@ export class HarnessAgentSession {
|
|
|
202
232
|
harness: this.harness,
|
|
203
233
|
session,
|
|
204
234
|
prompt: options.prompt,
|
|
235
|
+
model: options.model,
|
|
236
|
+
skills: options.skills,
|
|
205
237
|
instructions: options.instructions,
|
|
206
238
|
tools: options.tools,
|
|
207
239
|
activeTools: options.activeTools,
|
|
@@ -246,7 +278,10 @@ export class HarnessAgentSession {
|
|
|
246
278
|
onStopConditionMet: () =>
|
|
247
279
|
this.captureStopConditionBoundary({ session, turnId }),
|
|
248
280
|
});
|
|
249
|
-
return
|
|
281
|
+
return {
|
|
282
|
+
...turn,
|
|
283
|
+
ready: this.waitForPromptControl({ turnId }),
|
|
284
|
+
};
|
|
250
285
|
} catch (error) {
|
|
251
286
|
this.finishTrackedTurn({ turnId });
|
|
252
287
|
throw error;
|
|
@@ -258,6 +293,8 @@ export class HarnessAgentSession {
|
|
|
258
293
|
RUNTIME_CONTEXT extends Context,
|
|
259
294
|
OUTPUT extends Output,
|
|
260
295
|
>(options: {
|
|
296
|
+
model: string | undefined;
|
|
297
|
+
skills: ReadonlyArray<HarnessV1Skill>;
|
|
261
298
|
instructions: string | undefined;
|
|
262
299
|
tools: TOOLS;
|
|
263
300
|
activeTools: ToolSet;
|
|
@@ -278,6 +315,15 @@ export class HarnessAgentSession {
|
|
|
278
315
|
}): HarnessAgentTurnResult<TOOLS, RUNTIME_CONTEXT, OUTPUT> {
|
|
279
316
|
const session = this.requireReusableSession();
|
|
280
317
|
this.requireContinuableTurn();
|
|
318
|
+
const turnSettings = this.resolveActiveTurnSettings({
|
|
319
|
+
model: options.model,
|
|
320
|
+
skills: options.skills,
|
|
321
|
+
instructions: options.instructions,
|
|
322
|
+
tools: options.tools,
|
|
323
|
+
activeTools: options.activeTools,
|
|
324
|
+
toolSpecs: options.toolSpecs,
|
|
325
|
+
builtinToolFiltering: options.builtinToolFiltering,
|
|
326
|
+
});
|
|
281
327
|
const sandboxSession = this.getSandboxSession();
|
|
282
328
|
const turnId = this.startTrackedTurn();
|
|
283
329
|
try {
|
|
@@ -285,11 +331,13 @@ export class HarnessAgentSession {
|
|
|
285
331
|
harness: this.harness,
|
|
286
332
|
session,
|
|
287
333
|
mode: 'continue',
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
334
|
+
model: turnSettings.persisted.model,
|
|
335
|
+
skills: turnSettings.persisted.skills,
|
|
336
|
+
instructions: turnSettings.persisted.instructions,
|
|
337
|
+
tools: turnSettings.tools as TOOLS,
|
|
338
|
+
activeTools: turnSettings.activeTools,
|
|
339
|
+
toolSpecs: [...turnSettings.persisted.tools],
|
|
340
|
+
builtinToolFiltering: turnSettings.builtinToolFiltering,
|
|
293
341
|
sandboxSession: getRestrictedSandboxSession(sandboxSession),
|
|
294
342
|
sessionWorkDir: this.sessionWorkDir,
|
|
295
343
|
runtimeContext: options.runtimeContext,
|
|
@@ -331,7 +379,10 @@ export class HarnessAgentSession {
|
|
|
331
379
|
onStopConditionMet: () =>
|
|
332
380
|
this.captureStopConditionBoundary({ session, turnId }),
|
|
333
381
|
});
|
|
334
|
-
return
|
|
382
|
+
return {
|
|
383
|
+
...turn,
|
|
384
|
+
ready: this.waitForPromptControl({ turnId }),
|
|
385
|
+
};
|
|
335
386
|
} catch (error) {
|
|
336
387
|
this.finishTrackedTurn({ turnId });
|
|
337
388
|
throw error;
|
|
@@ -521,6 +572,7 @@ export class HarnessAgentSession {
|
|
|
521
572
|
private addPendingToolState(
|
|
522
573
|
state: HarnessAgentContinueTurnState,
|
|
523
574
|
): HarnessAgentContinueTurnState {
|
|
575
|
+
const turnSettings = this.persistedTurnSettings;
|
|
524
576
|
const pendingToolApprovals = this.getPendingToolApprovals();
|
|
525
577
|
const pendingToolResults = this.getPendingToolResults();
|
|
526
578
|
if (pendingToolApprovals.length === 0 && pendingToolResults.length === 0) {
|
|
@@ -529,10 +581,12 @@ export class HarnessAgentSession {
|
|
|
529
581
|
harnessId: state.harnessId,
|
|
530
582
|
specificationVersion: state.specificationVersion,
|
|
531
583
|
data: state.data,
|
|
584
|
+
...(turnSettings == null ? {} : { turnSettings }),
|
|
532
585
|
};
|
|
533
586
|
}
|
|
534
587
|
return {
|
|
535
588
|
...state,
|
|
589
|
+
...(turnSettings == null ? {} : { turnSettings }),
|
|
536
590
|
...(pendingToolApprovals.length > 0 ? { pendingToolApprovals } : {}),
|
|
537
591
|
...(pendingToolResults.length > 0 ? { pendingToolResults } : {}),
|
|
538
592
|
};
|
|
@@ -663,6 +717,14 @@ export class HarnessAgentSession {
|
|
|
663
717
|
this.settleActivePromptControl(options.control);
|
|
664
718
|
}
|
|
665
719
|
|
|
720
|
+
private async waitForPromptControl(options: {
|
|
721
|
+
turnId: number;
|
|
722
|
+
}): Promise<void> {
|
|
723
|
+
const activePromptControl = this.activePromptControl;
|
|
724
|
+
if (activePromptControl?.turnId !== options.turnId) return;
|
|
725
|
+
await activePromptControl.promise;
|
|
726
|
+
}
|
|
727
|
+
|
|
666
728
|
private settleActivePromptControl(
|
|
667
729
|
control: HarnessV1PromptControl | undefined,
|
|
668
730
|
): void {
|
|
@@ -691,9 +753,55 @@ export class HarnessAgentSession {
|
|
|
691
753
|
this.pendingToolApprovals.clear();
|
|
692
754
|
this.pendingToolResults.clear();
|
|
693
755
|
this.suspendedTurnState = undefined;
|
|
756
|
+
this.activeTurnSettings = undefined;
|
|
757
|
+
this.persistedTurnSettings = undefined;
|
|
694
758
|
this.turnState = 'idle';
|
|
695
759
|
}
|
|
696
760
|
|
|
761
|
+
private resolveActiveTurnSettings(options: {
|
|
762
|
+
model: string | undefined;
|
|
763
|
+
skills: ReadonlyArray<HarnessV1Skill>;
|
|
764
|
+
instructions: string | undefined;
|
|
765
|
+
tools: ToolSet;
|
|
766
|
+
activeTools: ToolSet;
|
|
767
|
+
toolSpecs: HarnessAgentToolSpec[];
|
|
768
|
+
builtinToolFiltering: HarnessV1BuiltinToolFiltering | undefined;
|
|
769
|
+
}): ActiveTurnSettings {
|
|
770
|
+
if (this.activeTurnSettings != null) {
|
|
771
|
+
return this.activeTurnSettings;
|
|
772
|
+
}
|
|
773
|
+
const persisted =
|
|
774
|
+
this.persistedTurnSettings ??
|
|
775
|
+
({
|
|
776
|
+
...(options.model == null ? {} : { model: options.model }),
|
|
777
|
+
skills: options.skills,
|
|
778
|
+
...(options.instructions == null
|
|
779
|
+
? {}
|
|
780
|
+
: { instructions: options.instructions }),
|
|
781
|
+
tools: options.toolSpecs,
|
|
782
|
+
} satisfies HarnessV1TurnSettings);
|
|
783
|
+
this.persistedTurnSettings = persisted;
|
|
784
|
+
|
|
785
|
+
const activeTools: ToolSet = {};
|
|
786
|
+
for (const toolSpec of persisted.tools) {
|
|
787
|
+
const tool = options.activeTools[toolSpec.name];
|
|
788
|
+
if (tool == null) {
|
|
789
|
+
throw new Error(
|
|
790
|
+
`HarnessAgent cannot continue turn because tool '${toolSpec.name}' is no longer available.`,
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
activeTools[toolSpec.name] = tool;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
this.activeTurnSettings = {
|
|
797
|
+
persisted,
|
|
798
|
+
tools: options.tools,
|
|
799
|
+
activeTools,
|
|
800
|
+
builtinToolFiltering: options.builtinToolFiltering,
|
|
801
|
+
};
|
|
802
|
+
return this.activeTurnSettings;
|
|
803
|
+
}
|
|
804
|
+
|
|
697
805
|
private endLocalHandle(options: {
|
|
698
806
|
sessionState: Exclude<HarnessAgentSessionState, 'active'>;
|
|
699
807
|
}): void {
|
|
@@ -12,11 +12,15 @@ import type {
|
|
|
12
12
|
Arrayable,
|
|
13
13
|
Context,
|
|
14
14
|
Experimental_SandboxSession as SandboxSession,
|
|
15
|
+
FlexibleSchema,
|
|
16
|
+
MaybePromiseLike,
|
|
15
17
|
ToolSet,
|
|
16
18
|
} from '@ai-sdk/provider-utils';
|
|
17
19
|
import type {
|
|
18
20
|
ActiveTools,
|
|
21
|
+
AgentCallParameters,
|
|
19
22
|
OutputInterface as Output,
|
|
23
|
+
Prompt,
|
|
20
24
|
StopCondition,
|
|
21
25
|
TelemetryOptions,
|
|
22
26
|
ToolApprovalStatus,
|
|
@@ -74,9 +78,10 @@ type HarnessTools<TOOLS extends ToolSet> = ActiveTools<NoInfer<TOOLS>>;
|
|
|
74
78
|
/**
|
|
75
79
|
* Construction-time settings for a `HarnessAgent`.
|
|
76
80
|
*
|
|
77
|
-
*
|
|
81
|
+
* Prompt, abortSignal, callbacks, and custom call options belong on the
|
|
78
82
|
* `AgentCallParameters` / `AgentStreamParameters` passed to `generate` /
|
|
79
|
-
* `stream`
|
|
83
|
+
* `stream`. `prepareCall` can derive turn-scoped model, skills, instructions,
|
|
84
|
+
* and tools from those custom call options.
|
|
80
85
|
*/
|
|
81
86
|
type HarnessAgentToolFilteringSettings<TOOLS extends ToolSet> =
|
|
82
87
|
| {
|
|
@@ -101,6 +106,7 @@ export type HarnessAgentSettings<
|
|
|
101
106
|
TUserTools extends ToolSet = {},
|
|
102
107
|
RUNTIME_CONTEXT extends Context = Context,
|
|
103
108
|
OUTPUT extends Output = never,
|
|
109
|
+
CALL_OPTIONS = never,
|
|
104
110
|
> = {
|
|
105
111
|
/**
|
|
106
112
|
* The harness adapter driving the underlying agent runtime. Its
|
|
@@ -115,6 +121,13 @@ export type HarnessAgentSettings<
|
|
|
115
121
|
*/
|
|
116
122
|
readonly id?: string;
|
|
117
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Model identifier used by the harness adapter. Supported values are
|
|
126
|
+
* defined by the selected harness. `prepareCall` can replace it between
|
|
127
|
+
* completed turns.
|
|
128
|
+
*/
|
|
129
|
+
readonly model?: string;
|
|
130
|
+
|
|
118
131
|
/**
|
|
119
132
|
* Tools available to the underlying runtime in addition to the harness's
|
|
120
133
|
* own builtins. The agent forwards each tool to the harness as a
|
|
@@ -127,19 +140,86 @@ export type HarnessAgentSettings<
|
|
|
127
140
|
readonly tools?: TUserTools;
|
|
128
141
|
|
|
129
142
|
/**
|
|
130
|
-
* Skills made available to the underlying runtime
|
|
131
|
-
*
|
|
132
|
-
* working tree, prompt prefix, …).
|
|
143
|
+
* Skills made available to the underlying runtime. Each adapter decides how
|
|
144
|
+
* to surface skills. `prepareCall` can replace them between completed turns.
|
|
133
145
|
*/
|
|
134
146
|
readonly skills?: ReadonlyArray<HarnessAgentSkill>;
|
|
135
147
|
|
|
136
148
|
/**
|
|
137
|
-
* Instructions for the underlying agent runtime. Adapters append
|
|
149
|
+
* Instructions for the underlying agent runtime. Adapters append these to a
|
|
138
150
|
* native system or developer prompt when supported. Otherwise, they prepend
|
|
139
|
-
*
|
|
151
|
+
* them to the user message. `prepareCall` can replace them between completed
|
|
152
|
+
* turns.
|
|
140
153
|
*/
|
|
141
154
|
readonly instructions?: string;
|
|
142
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Schema for validating the custom options passed to each agent call.
|
|
158
|
+
*/
|
|
159
|
+
readonly callOptionsSchema?: FlexibleSchema<CALL_OPTIONS>;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Prepares the prompt and the settings that may vary between completed
|
|
163
|
+
* turns. The prepared values are frozen for the lifetime of the turn,
|
|
164
|
+
* including any suspended-turn continuations.
|
|
165
|
+
*
|
|
166
|
+
* Preserve the remaining arguments with the rest-spread pattern when a
|
|
167
|
+
* field should be removable by returning `undefined`:
|
|
168
|
+
*
|
|
169
|
+
* ```ts
|
|
170
|
+
* prepareCall: ({ options, ...rest }) => ({
|
|
171
|
+
* ...rest,
|
|
172
|
+
* instructions: options.instructions,
|
|
173
|
+
* })
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
readonly prepareCall?: (
|
|
177
|
+
options: Omit<
|
|
178
|
+
AgentCallParameters<
|
|
179
|
+
CALL_OPTIONS,
|
|
180
|
+
HarnessAllTools<THarness, TUserTools>,
|
|
181
|
+
RUNTIME_CONTEXT
|
|
182
|
+
>,
|
|
183
|
+
| 'abortSignal'
|
|
184
|
+
| 'timeout'
|
|
185
|
+
| 'onStart'
|
|
186
|
+
| 'experimental_onStart'
|
|
187
|
+
| 'onStepStart'
|
|
188
|
+
| 'experimental_onStepStart'
|
|
189
|
+
| 'onToolExecutionStart'
|
|
190
|
+
| 'experimental_onToolCallStart'
|
|
191
|
+
| 'onToolExecutionEnd'
|
|
192
|
+
| 'experimental_onToolCallFinish'
|
|
193
|
+
| 'onStepEnd'
|
|
194
|
+
| 'onStepFinish'
|
|
195
|
+
| 'onEnd'
|
|
196
|
+
| 'onFinish'
|
|
197
|
+
| 'experimental_sandbox'
|
|
198
|
+
> &
|
|
199
|
+
Pick<
|
|
200
|
+
HarnessAgentSettings<
|
|
201
|
+
THarness,
|
|
202
|
+
TUserTools,
|
|
203
|
+
RUNTIME_CONTEXT,
|
|
204
|
+
NoInfer<OUTPUT>,
|
|
205
|
+
CALL_OPTIONS
|
|
206
|
+
>,
|
|
207
|
+
'model' | 'skills' | 'instructions' | 'tools'
|
|
208
|
+
>,
|
|
209
|
+
) => MaybePromiseLike<
|
|
210
|
+
Pick<
|
|
211
|
+
HarnessAgentSettings<
|
|
212
|
+
THarness,
|
|
213
|
+
TUserTools,
|
|
214
|
+
RUNTIME_CONTEXT,
|
|
215
|
+
NoInfer<OUTPUT>,
|
|
216
|
+
CALL_OPTIONS
|
|
217
|
+
>,
|
|
218
|
+
'model' | 'skills' | 'instructions' | 'tools'
|
|
219
|
+
> &
|
|
220
|
+
Omit<Prompt, 'system' | 'instructions' | 'allowSystemInMessages'>
|
|
221
|
+
>;
|
|
222
|
+
|
|
143
223
|
/**
|
|
144
224
|
* Optional specification for generating typed output. The same output
|
|
145
225
|
* requirement is active for every turn run by this agent.
|