@ai-sdk/harness 1.0.70 → 1.0.72
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 +21 -0
- package/dist/agent/index.d.ts +90 -9
- package/dist/agent/index.js +69 -44
- package/dist/agent/index.js.map +1 -1
- package/dist/index.d.ts +88 -10
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts +59 -7
- package/dist/utils/index.js +36 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +3 -3
- package/src/agent/internal/run-prompt.ts +78 -40
- package/src/agent/prepare-sandbox-for-harness.ts +8 -18
- package/src/errors/harness-capability-unsupported-error.ts +2 -2
- package/src/utils/index.ts +5 -0
- package/src/utils/sandbox-credential-brokering.ts +41 -0
- package/src/v1/harness-v1-network-sandbox-session.ts +87 -5
- package/src/v1/harness-v1-session.ts +4 -2
- package/src/v1/index.ts +2 -0
|
@@ -248,6 +248,25 @@ export function runPrompt<
|
|
|
248
248
|
| Extract<HarnessV1StreamPart, { type: 'finish' }>
|
|
249
249
|
| undefined;
|
|
250
250
|
const completedSteps: Array<StepResult<TOOLS, RUNTIME_CONTEXT>> = [];
|
|
251
|
+
const outstandingHostToolExecutions: Promise<void>[] = [];
|
|
252
|
+
const startHostToolExecution = (execution: Promise<void>): void => {
|
|
253
|
+
outstandingHostToolExecutions.push(execution);
|
|
254
|
+
// The execution is joined at the next step boundary. Attach a rejection
|
|
255
|
+
// handler immediately so failures cannot become unhandled in the
|
|
256
|
+
// meantime; awaiting the original promise still propagates the failure.
|
|
257
|
+
void execution.catch(() => {});
|
|
258
|
+
};
|
|
259
|
+
const waitForOutstandingHostToolExecutions = async (): Promise<void> => {
|
|
260
|
+
if (outstandingHostToolExecutions.length === 0) return;
|
|
261
|
+
const executions = outstandingHostToolExecutions.splice(0);
|
|
262
|
+
const results = await Promise.allSettled(executions);
|
|
263
|
+
const failedExecution = results.find(
|
|
264
|
+
result => result.status === 'rejected',
|
|
265
|
+
);
|
|
266
|
+
if (failedExecution != null) {
|
|
267
|
+
throw failedExecution.reason;
|
|
268
|
+
}
|
|
269
|
+
};
|
|
251
270
|
const releasePendingStopBoundary = (): void => {
|
|
252
271
|
pendingStopBoundary?.releaseCheckpoint?.();
|
|
253
272
|
pendingStopBoundary = undefined;
|
|
@@ -311,6 +330,7 @@ export function runPrompt<
|
|
|
311
330
|
const finishForHostInputPause = async (options: {
|
|
312
331
|
completeCurrentStep: boolean;
|
|
313
332
|
}): Promise<void> => {
|
|
333
|
+
await waitForOutstandingHostToolExecutions();
|
|
314
334
|
if (options.completeCurrentStep) {
|
|
315
335
|
await completeStep({
|
|
316
336
|
finishReason: toolCallsFinishReason,
|
|
@@ -665,6 +685,7 @@ export function runPrompt<
|
|
|
665
685
|
* or a second `error` part from `fail`).
|
|
666
686
|
*/
|
|
667
687
|
if (value.type === 'error' && displayValue.type === 'error') {
|
|
688
|
+
await waitForOutstandingHostToolExecutions();
|
|
668
689
|
// Telemetry and stderr diagnostics keep the raw error (absolute
|
|
669
690
|
// paths help debugging); the consumer-facing settle uses the
|
|
670
691
|
// workDir-stripped one, like every other forwarded part.
|
|
@@ -784,6 +805,7 @@ export function runPrompt<
|
|
|
784
805
|
|
|
785
806
|
// Drive step boundaries.
|
|
786
807
|
if (value.type === 'finish-step') {
|
|
808
|
+
await waitForOutstandingHostToolExecutions();
|
|
787
809
|
await completeStep({
|
|
788
810
|
finishReason: value.finishReason,
|
|
789
811
|
usage: value.usage,
|
|
@@ -799,6 +821,7 @@ export function runPrompt<
|
|
|
799
821
|
}
|
|
800
822
|
|
|
801
823
|
if (value.type === 'finish') {
|
|
824
|
+
await waitForOutstandingHostToolExecutions();
|
|
802
825
|
finalFinish = value;
|
|
803
826
|
await telemetry.end({
|
|
804
827
|
finishReason: value.finishReason,
|
|
@@ -910,52 +933,62 @@ export function runPrompt<
|
|
|
910
933
|
await finishForHostInputPause({ completeCurrentStep: true });
|
|
911
934
|
return;
|
|
912
935
|
}
|
|
913
|
-
|
|
914
|
-
event: toolCall,
|
|
915
|
-
tools: activeTools,
|
|
916
|
-
wrappedExecuteTool: telemetry.executeTool,
|
|
917
|
-
sandboxSession: input.sandboxSession,
|
|
918
|
-
abortSignal: input.abortSignal,
|
|
919
|
-
control,
|
|
920
|
-
onPreliminaryResult: preliminaryOutput => {
|
|
921
|
-
/*
|
|
922
|
-
* Project a `yield`ed value as a preliminary AI SDK
|
|
923
|
-
* `tool-result` part. Unlike the final result — which is
|
|
924
|
-
* submitted to the runtime, echoed back as a `tool-result`
|
|
925
|
-
* event, and stripped on its way through the loop above —
|
|
926
|
-
* preliminary values never reach the runtime, so strip the
|
|
927
|
-
* working directory here to match the final result's projection.
|
|
928
|
-
*/
|
|
929
|
-
const stripped = stripWorkDir(
|
|
930
|
-
{
|
|
931
|
-
type: 'tool-result',
|
|
932
|
-
toolCallId: toolCall.toolCallId,
|
|
933
|
-
toolName: toolCall.toolName,
|
|
934
|
-
result: preliminaryOutput as Extract<
|
|
935
|
-
HarnessV1StreamPart,
|
|
936
|
-
{ type: 'tool-result' }
|
|
937
|
-
>['result'],
|
|
938
|
-
},
|
|
939
|
-
input.sessionWorkDir,
|
|
940
|
-
) as Extract<HarnessV1StreamPart, { type: 'tool-result' }>;
|
|
941
|
-
result.enqueue({
|
|
942
|
-
type: 'tool-result',
|
|
943
|
-
toolCallId: toolCall.toolCallId,
|
|
944
|
-
toolName: toolCall.toolName,
|
|
945
|
-
input: undefined,
|
|
946
|
-
output: stripped.result,
|
|
947
|
-
preliminary: true,
|
|
948
|
-
} as TextStreamPart<TOOLS>);
|
|
949
|
-
},
|
|
950
|
-
});
|
|
951
|
-
if (!execution.executed) {
|
|
936
|
+
if (!isExecutableTool(activeTools[toolCall.toolName])) {
|
|
952
937
|
recordPendingToolResult({ toolCall });
|
|
953
938
|
await finishForHostInputPause({ completeCurrentStep: true });
|
|
954
939
|
return;
|
|
955
940
|
}
|
|
956
|
-
|
|
941
|
+
startHostToolExecution(
|
|
942
|
+
(async () => {
|
|
943
|
+
const execution = await maybeExecuteHostTool({
|
|
944
|
+
event: toolCall,
|
|
945
|
+
tools: activeTools,
|
|
946
|
+
wrappedExecuteTool: telemetry.executeTool,
|
|
947
|
+
sandboxSession: input.sandboxSession,
|
|
948
|
+
abortSignal: input.abortSignal,
|
|
949
|
+
control,
|
|
950
|
+
onPreliminaryResult: preliminaryOutput => {
|
|
951
|
+
/*
|
|
952
|
+
* Project a `yield`ed value as a preliminary AI SDK
|
|
953
|
+
* `tool-result` part. Unlike the final result — which is
|
|
954
|
+
* submitted to the runtime, echoed back as a `tool-result`
|
|
955
|
+
* event, and stripped on its way through the loop above —
|
|
956
|
+
* preliminary values never reach the runtime, so strip the
|
|
957
|
+
* working directory here to match the final result's projection.
|
|
958
|
+
*/
|
|
959
|
+
const stripped = stripWorkDir(
|
|
960
|
+
{
|
|
961
|
+
type: 'tool-result',
|
|
962
|
+
toolCallId: toolCall.toolCallId,
|
|
963
|
+
toolName: toolCall.toolName,
|
|
964
|
+
result: preliminaryOutput as Extract<
|
|
965
|
+
HarnessV1StreamPart,
|
|
966
|
+
{ type: 'tool-result' }
|
|
967
|
+
>['result'],
|
|
968
|
+
},
|
|
969
|
+
input.sessionWorkDir,
|
|
970
|
+
) as Extract<HarnessV1StreamPart, { type: 'tool-result' }>;
|
|
971
|
+
result.enqueue({
|
|
972
|
+
type: 'tool-result',
|
|
973
|
+
toolCallId: toolCall.toolCallId,
|
|
974
|
+
toolName: toolCall.toolName,
|
|
975
|
+
input: undefined,
|
|
976
|
+
output: stripped.result,
|
|
977
|
+
preliminary: true,
|
|
978
|
+
} as TextStreamPart<TOOLS>);
|
|
979
|
+
},
|
|
980
|
+
});
|
|
981
|
+
if (!execution.executed) {
|
|
982
|
+
throw new Error(
|
|
983
|
+
`Harness '${input.harness.harnessId}' could not execute host tool '${toolCall.toolName}'.`,
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
await telemetry.toolEnd(toolCall.toolCallId, execution.outcome);
|
|
987
|
+
})(),
|
|
988
|
+
);
|
|
957
989
|
}
|
|
958
990
|
}
|
|
991
|
+
await waitForOutstandingHostToolExecutions();
|
|
959
992
|
const isTurnSuspending = input.isTurnSuspending?.() === true;
|
|
960
993
|
if (isTurnSuspending) {
|
|
961
994
|
if (finalFinish == null) {
|
|
@@ -982,6 +1015,11 @@ export function runPrompt<
|
|
|
982
1015
|
: undefined,
|
|
983
1016
|
);
|
|
984
1017
|
} catch (err) {
|
|
1018
|
+
try {
|
|
1019
|
+
await waitForOutstandingHostToolExecutions();
|
|
1020
|
+
} catch {
|
|
1021
|
+
// Preserve the error that stopped the reader loop.
|
|
1022
|
+
}
|
|
985
1023
|
await telemetry.error(err);
|
|
986
1024
|
logBridgeError({
|
|
987
1025
|
harnessId: input.harness.harnessId,
|
|
@@ -36,6 +36,9 @@ export type PrepareSandboxForHarnessResult = {
|
|
|
36
36
|
* When a later `HarnessAgent` session uses a sandbox created from the persisted
|
|
37
37
|
* artifact, the adapter recomputes the same recipe identity and the existing
|
|
38
38
|
* bootstrap marker makes the bootstrap logic a no-op.
|
|
39
|
+
*
|
|
40
|
+
* Repeated harness IDs are prepared once. When multiple adapters use the same
|
|
41
|
+
* ID, the last adapter in `harnesses` is used.
|
|
39
42
|
*/
|
|
40
43
|
export async function prepareSandboxForHarness(options: {
|
|
41
44
|
readonly session: SandboxSession;
|
|
@@ -52,10 +55,11 @@ export async function prepareSandboxForHarness(options: {
|
|
|
52
55
|
);
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
const harnesses = [
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
const harnesses = [
|
|
59
|
+
...new Map(
|
|
60
|
+
options.harnesses.map(harness => [harness.harnessId, harness]),
|
|
61
|
+
).values(),
|
|
62
|
+
].sort((a, b) => a.harnessId.localeCompare(b.harnessId));
|
|
59
63
|
|
|
60
64
|
const workDir =
|
|
61
65
|
sandboxConfig.workDir == null
|
|
@@ -112,20 +116,6 @@ export async function prepareSandboxForHarness(options: {
|
|
|
112
116
|
};
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
function assertUniqueHarnessIds(
|
|
116
|
-
harnesses: ReadonlyArray<HarnessAgentAdapter>,
|
|
117
|
-
): void {
|
|
118
|
-
const seen = new Set<string>();
|
|
119
|
-
for (const harness of harnesses) {
|
|
120
|
-
if (seen.has(harness.harnessId)) {
|
|
121
|
-
throw new Error(
|
|
122
|
-
`prepareSandboxForHarness: duplicate harness id "${harness.harnessId}".`,
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
seen.add(harness.harnessId);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
119
|
async function resolvePreparedSandboxIdentity({
|
|
130
120
|
recipeIdentities,
|
|
131
121
|
bootstrapHash,
|
|
@@ -8,8 +8,8 @@ const symbol = Symbol.for(marker);
|
|
|
8
8
|
/**
|
|
9
9
|
* Thrown when a caller asks the harness to do something the adapter (or the
|
|
10
10
|
* supplied sandbox) does not support, e.g. requesting manual compaction from
|
|
11
|
-
* an adapter that only auto-compacts, or invoking `
|
|
12
|
-
* that does not expose one.
|
|
11
|
+
* an adapter that only auto-compacts, or invoking `getPortEndpoint` on a
|
|
12
|
+
* sandbox that does not expose one.
|
|
13
13
|
*
|
|
14
14
|
* The caller supplies the full human-readable message. Optional `harnessId`
|
|
15
15
|
* is recorded as structured context for tooling.
|
package/src/utils/index.ts
CHANGED
|
@@ -6,6 +6,11 @@ export {
|
|
|
6
6
|
} from './sandbox-channel';
|
|
7
7
|
export { classifyDiskLog, type DiskLogRecoveryMode } from './classify-disk-log';
|
|
8
8
|
export { getAiGatewayAuthFromEnv } from './ai-gateway-auth';
|
|
9
|
+
export {
|
|
10
|
+
createCredentialRequestTransformation,
|
|
11
|
+
maskSandboxCredentials,
|
|
12
|
+
warnCredentialBrokeringUnavailable,
|
|
13
|
+
} from './sandbox-credential-brokering';
|
|
9
14
|
export { resolveSandboxHomeDir } from './sandbox-home-dir';
|
|
10
15
|
export { shellQuote } from './shell-quote';
|
|
11
16
|
export {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { HarnessV1RequestTransformation } from '../v1';
|
|
2
|
+
|
|
3
|
+
export function warnCredentialBrokeringUnavailable(): void {
|
|
4
|
+
console.warn(
|
|
5
|
+
'The sandbox implementation does not support configuring request transformations, so credential brokering does not work. Falling back to less secure credential forwarding.',
|
|
6
|
+
);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function maskSandboxCredentials({
|
|
10
|
+
environment,
|
|
11
|
+
credentialEnvironmentVariables,
|
|
12
|
+
}: {
|
|
13
|
+
environment: Readonly<Record<string, string>>;
|
|
14
|
+
credentialEnvironmentVariables: ReadonlyArray<string>;
|
|
15
|
+
}): Record<string, string> {
|
|
16
|
+
const maskedEnvironment = { ...environment };
|
|
17
|
+
for (const name of credentialEnvironmentVariables) {
|
|
18
|
+
if (maskedEnvironment[name] != null) {
|
|
19
|
+
maskedEnvironment[name] = name;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return maskedEnvironment;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createCredentialRequestTransformation({
|
|
26
|
+
baseUrl,
|
|
27
|
+
headers,
|
|
28
|
+
}: {
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
headers: Readonly<Record<string, string>>;
|
|
31
|
+
}): HarnessV1RequestTransformation {
|
|
32
|
+
const url = new URL(baseUrl);
|
|
33
|
+
const pathname = url.pathname.replace(/\/+$/, '');
|
|
34
|
+
return {
|
|
35
|
+
match: {
|
|
36
|
+
host: url.hostname,
|
|
37
|
+
...(pathname.length === 0 ? {} : { path: { startsWith: pathname } }),
|
|
38
|
+
},
|
|
39
|
+
transform: { headers },
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { Experimental_SandboxSession as SandboxSession } from '@ai-sdk/provider-utils';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Connection details for a sandbox-exposed port. Headers are scoped to the
|
|
5
|
+
* returned URL and must be included when opening the connection.
|
|
6
|
+
*/
|
|
7
|
+
export type HarnessV1PortEndpoint = {
|
|
8
|
+
readonly url: string;
|
|
9
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
10
|
+
};
|
|
11
|
+
|
|
3
12
|
/**
|
|
4
13
|
* Network sandbox session returned by `HarnessV1SandboxProvider.createSession()`. The
|
|
5
14
|
* harness keeps this for the lifetime of a session. It is itself a
|
|
@@ -8,8 +17,8 @@ import type { Experimental_SandboxSession as SandboxSession } from '@ai-sdk/prov
|
|
|
8
17
|
*
|
|
9
18
|
* Code that should only touch the filesystem and spawn processes receives the
|
|
10
19
|
* reduced view from {@link HarnessV1NetworkSandboxSession.restricted}, never the
|
|
11
|
-
* network sandbox session itself — so it cannot stop the sandbox
|
|
12
|
-
* network
|
|
20
|
+
* network sandbox session itself — so it cannot stop the sandbox, change
|
|
21
|
+
* network access, or transform requests.
|
|
13
22
|
*/
|
|
14
23
|
export interface HarnessV1NetworkSandboxSession extends SandboxSession {
|
|
15
24
|
/**
|
|
@@ -36,13 +45,23 @@ export interface HarnessV1NetworkSandboxSession extends SandboxSession {
|
|
|
36
45
|
*/
|
|
37
46
|
readonly defaultWorkingDirectory: string;
|
|
38
47
|
|
|
39
|
-
/** Ports the sandbox exposes; resolvable
|
|
48
|
+
/** Ports the sandbox exposes; resolvable via `getPortEndpoint`. */
|
|
40
49
|
readonly ports: ReadonlyArray<number>;
|
|
41
50
|
|
|
42
51
|
/**
|
|
43
|
-
* Resolve
|
|
52
|
+
* Resolve the connection details for a sandbox-exposed port. Bridge-backed
|
|
44
53
|
* adapters call this to open their WebSocket to the in-sandbox bridge.
|
|
45
54
|
*/
|
|
55
|
+
readonly getPortEndpoint: (options: {
|
|
56
|
+
port: number;
|
|
57
|
+
protocol?: 'http' | 'https' | 'ws';
|
|
58
|
+
}) => PromiseLike<HarnessV1PortEndpoint>;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Resolve a publicly-reachable URL for a sandbox-exposed port.
|
|
62
|
+
*
|
|
63
|
+
* @deprecated Use `getPortEndpoint` instead.
|
|
64
|
+
*/
|
|
46
65
|
readonly getPortUrl: (options: {
|
|
47
66
|
port: number;
|
|
48
67
|
protocol?: 'http' | 'https' | 'ws';
|
|
@@ -68,6 +87,29 @@ export interface HarnessV1NetworkSandboxSession extends SandboxSession {
|
|
|
68
87
|
policy: HarnessV1NetworkPolicy,
|
|
69
88
|
) => PromiseLike<void>;
|
|
70
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Replace the sandbox's outbound request-transformation rules. Optional —
|
|
92
|
+
* implementations expose this only when credentials can be injected outside
|
|
93
|
+
* the sandbox security boundary. Calling this method assumes authority over
|
|
94
|
+
* the complete transformation set; harness adapters should normally use
|
|
95
|
+
* `addRequestTransformations` instead. Adapters may preserve legacy
|
|
96
|
+
* credential-forwarding behavior when additive request transformations are
|
|
97
|
+
* unavailable.
|
|
98
|
+
*/
|
|
99
|
+
readonly setRequestTransformations?: (
|
|
100
|
+
transformations: ReadonlyArray<HarnessV1RequestTransformation>,
|
|
101
|
+
) => PromiseLike<void>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Add outbound request-transformation rules without replacing rules already
|
|
105
|
+
* managed by the sandbox session. Optional for the same reason as
|
|
106
|
+
* `setRequestTransformations`. Harness adapters should use this additive
|
|
107
|
+
* capability unless they explicitly own the complete transformation set.
|
|
108
|
+
*/
|
|
109
|
+
readonly addRequestTransformations?: (
|
|
110
|
+
transformations: ReadonlyArray<HarnessV1RequestTransformation>,
|
|
111
|
+
) => PromiseLike<void>;
|
|
112
|
+
|
|
71
113
|
/**
|
|
72
114
|
* Replace the set of ports exposed by the sandbox. Full-replacement
|
|
73
115
|
* semantics: ports omitted from the array are deregistered. Optional —
|
|
@@ -86,7 +128,8 @@ export interface HarnessV1NetworkSandboxSession extends SandboxSession {
|
|
|
86
128
|
*
|
|
87
129
|
* The returned object points at exactly the same underlying sandbox
|
|
88
130
|
* resource as the network sandbox session it was produced from; it is only a
|
|
89
|
-
* narrower surface over the same resource, not a separate sandbox.
|
|
131
|
+
* narrower surface over the same resource, not a separate sandbox. In
|
|
132
|
+
* particular, it cannot mutate network access or request transformations.
|
|
90
133
|
*/
|
|
91
134
|
readonly restricted: () => SandboxSession;
|
|
92
135
|
}
|
|
@@ -121,3 +164,42 @@ export type HarnessV1NetworkPolicy =
|
|
|
121
164
|
allowedCIDRs: ReadonlyArray<string>;
|
|
122
165
|
deniedCIDRs?: ReadonlyArray<string>;
|
|
123
166
|
};
|
|
167
|
+
|
|
168
|
+
type HarnessV1RequestTransformationPathMatcher =
|
|
169
|
+
| { exact: string }
|
|
170
|
+
| { startsWith: string }
|
|
171
|
+
| { regex: string };
|
|
172
|
+
|
|
173
|
+
type HarnessV1RequestTransformationKeyValuePartMatcher =
|
|
174
|
+
| { exact: string }
|
|
175
|
+
| { startsWith: string }
|
|
176
|
+
| { regex: string };
|
|
177
|
+
|
|
178
|
+
type HarnessV1RequestTransformationKeyValueMatcher = {
|
|
179
|
+
readonly key?: HarnessV1RequestTransformationKeyValuePartMatcher;
|
|
180
|
+
readonly value?: HarnessV1RequestTransformationKeyValuePartMatcher;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Outbound HTTPS request transformation applied outside the sandbox security
|
|
185
|
+
* boundary. The host is part of the match so each rule is self-contained and
|
|
186
|
+
* several rules, including several for the same host, can be installed at
|
|
187
|
+
* once.
|
|
188
|
+
*
|
|
189
|
+
* Credential values belong in `transform.headers`, while the sandbox process
|
|
190
|
+
* receives only a non-secret placeholder. Implementations must overwrite
|
|
191
|
+
* matching request headers after the request leaves the sandbox rather than
|
|
192
|
+
* making transformed values available inside it.
|
|
193
|
+
*/
|
|
194
|
+
export type HarnessV1RequestTransformation = {
|
|
195
|
+
readonly match: {
|
|
196
|
+
readonly host: string;
|
|
197
|
+
readonly path?: HarnessV1RequestTransformationPathMatcher;
|
|
198
|
+
readonly method?: ReadonlyArray<string>;
|
|
199
|
+
readonly queryString?: ReadonlyArray<HarnessV1RequestTransformationKeyValueMatcher>;
|
|
200
|
+
readonly headers?: ReadonlyArray<HarnessV1RequestTransformationKeyValueMatcher>;
|
|
201
|
+
};
|
|
202
|
+
readonly transform: {
|
|
203
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
204
|
+
};
|
|
205
|
+
};
|
|
@@ -78,8 +78,10 @@ export type HarnessV1StartOptions = {
|
|
|
78
78
|
* Network sandbox session the adapter operates against. It is owned and
|
|
79
79
|
* lifecycled by `HarnessAgent`. Adapters call `restricted()` for the
|
|
80
80
|
* tool-safe filesystem/exec/spawn surface, and use the infra methods
|
|
81
|
-
* (`
|
|
82
|
-
*
|
|
81
|
+
* (`getPortEndpoint`, `ports`, `setNetworkPolicy`,
|
|
82
|
+
* `setRequestTransformations`, `addRequestTransformations`) for bridge
|
|
83
|
+
* wiring. Adapters must not call `stop()` themselves; the agent does that
|
|
84
|
+
* during cleanup.
|
|
83
85
|
*/
|
|
84
86
|
readonly sandboxSession: HarnessV1NetworkSandboxSession;
|
|
85
87
|
|
package/src/v1/index.ts
CHANGED
|
@@ -36,6 +36,8 @@ export type {
|
|
|
36
36
|
export type {
|
|
37
37
|
HarnessV1NetworkPolicy,
|
|
38
38
|
HarnessV1NetworkSandboxSession,
|
|
39
|
+
HarnessV1PortEndpoint,
|
|
40
|
+
HarnessV1RequestTransformation,
|
|
39
41
|
} from './harness-v1-network-sandbox-session';
|
|
40
42
|
export type { HarnessV1Skill } from './harness-v1-skill';
|
|
41
43
|
export type { HarnessV1StreamPart } from './harness-v1-stream-part';
|