@ai-sdk/harness 0.0.0 → 1.0.0-beta.14
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 +110 -0
- package/LICENSE +13 -0
- package/README.md +142 -0
- package/agent/index.ts +47 -0
- package/bridge/index.ts +10 -0
- package/dist/agent/index.d.ts +1521 -0
- package/dist/agent/index.js +2958 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/bridge/index.d.ts +111 -0
- package/dist/bridge/index.js +415 -0
- package/dist/bridge/index.js.map +1 -0
- package/dist/index.d.ts +1536 -0
- package/dist/index.js +15834 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/index.d.ts +225 -0
- package/dist/utils/index.js +12148 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +99 -1
- package/src/agent/harness-agent-session.ts +509 -0
- package/src/agent/harness-agent-settings.ts +131 -0
- package/src/agent/harness-agent-tool-approval-continuation.ts +94 -0
- package/src/agent/harness-agent-types.ts +50 -0
- package/src/agent/harness-agent.ts +819 -0
- package/src/agent/internal/bootstrap-recipe.ts +124 -0
- package/src/agent/internal/bridge-port-registry.ts +52 -0
- package/src/agent/internal/harness-stream-text-result.ts +720 -0
- package/src/agent/internal/lifecycle-state-validation.ts +95 -0
- package/src/agent/internal/permission-mode.ts +50 -0
- package/src/agent/internal/resolve-observability.ts +128 -0
- package/src/agent/internal/run-prompt.ts +813 -0
- package/src/agent/internal/strip-work-dir.ts +68 -0
- package/src/agent/internal/to-harness-stream.ts +75 -0
- package/src/agent/internal/translate-stream-part.ts +221 -0
- package/src/agent/internal/turn-telemetry.ts +359 -0
- package/src/agent/observability/file-reporter.ts +206 -0
- package/src/agent/observability/index.ts +15 -0
- package/src/agent/observability/trace-tree-reporter.ts +122 -0
- package/src/agent/observability/types.ts +86 -0
- package/src/agent/prewarm.ts +47 -0
- package/src/bridge/index.ts +702 -0
- package/src/errors/harness-capability-unsupported-error.ts +41 -0
- package/src/errors/harness-error.ts +22 -0
- package/src/index.ts +3 -0
- package/src/utils/bridge-ready.ts +277 -0
- package/src/utils/classify-disk-log.ts +43 -0
- package/src/utils/index.ts +15 -0
- package/src/utils/sandbox-channel.ts +453 -0
- package/src/v1/harness-v1-bootstrap.ts +46 -0
- package/src/v1/harness-v1-bridge-protocol.ts +310 -0
- package/src/v1/harness-v1-builtin-tool.ts +138 -0
- package/src/v1/harness-v1-call-warning.ts +22 -0
- package/src/v1/harness-v1-diagnostic.ts +66 -0
- package/src/v1/harness-v1-lifecycle-state.ts +65 -0
- package/src/v1/harness-v1-metadata.ts +13 -0
- package/src/v1/harness-v1-network-sandbox-session.ts +123 -0
- package/src/v1/harness-v1-observability.ts +20 -0
- package/src/v1/harness-v1-permission-mode.ts +11 -0
- package/src/v1/harness-v1-prompt-control.ts +41 -0
- package/src/v1/harness-v1-prompt.ts +11 -0
- package/src/v1/harness-v1-sandbox-provider.ts +76 -0
- package/src/v1/harness-v1-session.ts +272 -0
- package/src/v1/harness-v1-skill.ts +36 -0
- package/src/v1/harness-v1-stream-part.ts +363 -0
- package/src/v1/harness-v1-tool-spec.ts +31 -0
- package/src/v1/harness-v1.ts +83 -0
- package/src/v1/index.ts +93 -0
- package/utils/index.ts +1 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HarnessDebugConfig,
|
|
3
|
+
HarnessDiagnostic,
|
|
4
|
+
} from './observability/types';
|
|
5
|
+
import type { HarnessV1SandboxProvider } from '../v1';
|
|
6
|
+
import type {
|
|
7
|
+
HarnessAgentAdapter,
|
|
8
|
+
HarnessAgentPermissionMode,
|
|
9
|
+
HarnessAgentSkill,
|
|
10
|
+
} from './harness-agent-types';
|
|
11
|
+
import type {
|
|
12
|
+
Experimental_SandboxSession as SandboxSession,
|
|
13
|
+
ToolSet,
|
|
14
|
+
} from '@ai-sdk/provider-utils';
|
|
15
|
+
import type { TelemetryOptions, ToolApprovalStatus } from 'ai';
|
|
16
|
+
|
|
17
|
+
export type HarnessAgentToolApprovalConfiguration = Readonly<
|
|
18
|
+
Record<string, ToolApprovalStatus>
|
|
19
|
+
>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Construction-time settings for a `HarnessAgent`.
|
|
23
|
+
*
|
|
24
|
+
* Per-call settings (prompt, abortSignal, callbacks) belong on the
|
|
25
|
+
* `AgentCallParameters` / `AgentStreamParameters` passed to `generate` /
|
|
26
|
+
* `stream` and are not duplicated here.
|
|
27
|
+
*/
|
|
28
|
+
export type HarnessAgentSettings<
|
|
29
|
+
THarness extends HarnessAgentAdapter<any> = HarnessAgentAdapter,
|
|
30
|
+
TUserTools extends ToolSet = {},
|
|
31
|
+
> = {
|
|
32
|
+
/**
|
|
33
|
+
* The harness adapter driving the underlying agent runtime. Its
|
|
34
|
+
* `builtinTools` are merged with the user-defined `tools` and exposed to
|
|
35
|
+
* AI SDK consumers in the typed `tool-call` stream.
|
|
36
|
+
*/
|
|
37
|
+
readonly harness: THarness;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Stable identifier for this agent instance. Exposed via `agent.id`.
|
|
41
|
+
* If omitted, `agent.id` is `undefined`.
|
|
42
|
+
*/
|
|
43
|
+
readonly id?: string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Tools available to the underlying runtime in addition to the harness's
|
|
47
|
+
* own builtins. The agent forwards each tool to the harness as a
|
|
48
|
+
* `HarnessAgentToolSpec`; when the runtime calls one, the agent executes
|
|
49
|
+
* `tool.execute()` on the host and submits the result back to the harness.
|
|
50
|
+
*
|
|
51
|
+
* User tools take precedence over harness builtins on key collision —
|
|
52
|
+
* declare a tool with the same name as a builtin to override.
|
|
53
|
+
*/
|
|
54
|
+
readonly tools?: TUserTools;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Skills made available to the underlying runtime for the lifetime of
|
|
58
|
+
* the session. Each adapter decides how to surface skills (file in the
|
|
59
|
+
* working tree, prompt prefix, …).
|
|
60
|
+
*/
|
|
61
|
+
readonly skills?: ReadonlyArray<HarnessAgentSkill>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Instructions for the underlying agent runtime. Adapters prepend this to
|
|
65
|
+
* the first user message of a fresh session, once — it is not re-applied on
|
|
66
|
+
* later turns or when resuming a previously ended session.
|
|
67
|
+
*/
|
|
68
|
+
readonly instructions?: string;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Built-in tool permission mode. Defaults to `'allow-all'`, preserving the
|
|
72
|
+
* existing bypass-permissions behavior unless users opt in.
|
|
73
|
+
*/
|
|
74
|
+
readonly permissionMode?: HarnessAgentPermissionMode;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Per custom-tool approval statuses. This mirrors AI SDK `toolApproval`
|
|
78
|
+
* object configuration for host-executed tools, without callback support.
|
|
79
|
+
*
|
|
80
|
+
* `not-applicable` and `approved` run the tool, `user-approval` pauses the
|
|
81
|
+
* turn for a user decision, and `denied` immediately submits an
|
|
82
|
+
* `execution-denied` result.
|
|
83
|
+
*/
|
|
84
|
+
readonly toolApproval?: HarnessAgentToolApprovalConfiguration;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Sandbox provider whose `create()` produces the network sandbox session the
|
|
88
|
+
* harness runs against. Its `restricted()` view is also propagated to user
|
|
89
|
+
* tool `execute()` calls (as the `experimental_sandbox` field), typed as
|
|
90
|
+
* `Experimental_SandboxSession` so tools cannot reach the infra surface.
|
|
91
|
+
*/
|
|
92
|
+
readonly sandbox: HarnessV1SandboxProvider;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Called after each sandbox session is acquired and the session work
|
|
96
|
+
* directory exists, before the harness adapter starts. Runs for fresh and
|
|
97
|
+
* resumed sessions.
|
|
98
|
+
*
|
|
99
|
+
* Use this to write per-session config, install lightweight tools, activate
|
|
100
|
+
* licenses, or prepare files in `sessionWorkDir`. Keep it idempotent if the
|
|
101
|
+
* agent may resume sessions.
|
|
102
|
+
*/
|
|
103
|
+
readonly onSandboxSession?: (opts: {
|
|
104
|
+
readonly session: SandboxSession;
|
|
105
|
+
readonly sessionWorkDir: string;
|
|
106
|
+
readonly abortSignal?: AbortSignal;
|
|
107
|
+
}) => Promise<void>;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Telemetry configuration. The harness drives AI SDK's pluggable
|
|
111
|
+
* `Telemetry` integration contract from the turn lifecycle, so a harness turn
|
|
112
|
+
* appears in a consumer's traces with the same span shape as `streamText`.
|
|
113
|
+
* Register an integration here (e.g. `@ai-sdk/otel`) or globally via
|
|
114
|
+
* `registerTelemetry`. The harness itself stays OpenTelemetry-agnostic.
|
|
115
|
+
*/
|
|
116
|
+
readonly telemetry?: TelemetryOptions;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Diagnostics configuration. Enables bridge log forwarding (sandbox
|
|
120
|
+
* console + structured `debug-event`s) and the `HARNESS_DEBUG` stderr default.
|
|
121
|
+
* Set `{ enabled: true }` to turn it on in code; env vars fill unset fields.
|
|
122
|
+
*/
|
|
123
|
+
readonly debug?: HarnessDebugConfig;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Programmatic sink for forwarded bridge diagnostics. Receives every
|
|
127
|
+
* captured console line and structured event, normalized. Independent of the
|
|
128
|
+
* stderr default — wire this to capture diagnostics in code.
|
|
129
|
+
*/
|
|
130
|
+
readonly onLog?: (event: HarnessDiagnostic) => void;
|
|
131
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { HarnessError } from '../errors/harness-error';
|
|
2
|
+
import type {
|
|
3
|
+
ModelMessage,
|
|
4
|
+
ToolApprovalRequest,
|
|
5
|
+
ToolApprovalResponse,
|
|
6
|
+
} from '@ai-sdk/provider-utils';
|
|
7
|
+
|
|
8
|
+
export type HarnessAgentToolApprovalContinuation = {
|
|
9
|
+
readonly approvalResponse: ToolApprovalResponse;
|
|
10
|
+
readonly toolCall: {
|
|
11
|
+
readonly type: 'tool-call';
|
|
12
|
+
readonly toolCallId: string;
|
|
13
|
+
readonly toolName: string;
|
|
14
|
+
readonly input: unknown;
|
|
15
|
+
readonly providerExecuted?: boolean;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Extract approval decisions that should continue a suspended harness turn.
|
|
21
|
+
*
|
|
22
|
+
* AI SDK clients send approval decisions as a trailing `role: "tool"` message
|
|
23
|
+
* containing `tool-approval-response` parts. The response only carries the
|
|
24
|
+
* approval id, so the harness has to recover the matching approval request
|
|
25
|
+
* locally to find the original tool call before it can resume the paused turn.
|
|
26
|
+
* Responses that already have a tool result are ignored, because those
|
|
27
|
+
* approvals were already consumed by a prior continuation.
|
|
28
|
+
*/
|
|
29
|
+
export function collectHarnessAgentToolApprovalContinuations(input: {
|
|
30
|
+
messages: readonly ModelMessage[];
|
|
31
|
+
}): readonly HarnessAgentToolApprovalContinuation[] {
|
|
32
|
+
const lastMessage = input.messages.at(-1);
|
|
33
|
+
if (lastMessage?.role !== 'tool') return [];
|
|
34
|
+
|
|
35
|
+
const toolCallsByToolCallId = new Map<
|
|
36
|
+
string,
|
|
37
|
+
HarnessAgentToolApprovalContinuation['toolCall']
|
|
38
|
+
>();
|
|
39
|
+
const approvalRequestsByApprovalId = new Map<string, ToolApprovalRequest>();
|
|
40
|
+
for (const message of input.messages) {
|
|
41
|
+
if (message.role !== 'assistant' || typeof message.content === 'string') {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
for (const part of message.content) {
|
|
45
|
+
if (part.type === 'tool-call') {
|
|
46
|
+
toolCallsByToolCallId.set(part.toolCallId, {
|
|
47
|
+
type: 'tool-call',
|
|
48
|
+
toolCallId: part.toolCallId,
|
|
49
|
+
toolName: part.toolName,
|
|
50
|
+
input: part.input,
|
|
51
|
+
...(part.providerExecuted !== undefined
|
|
52
|
+
? { providerExecuted: part.providerExecuted }
|
|
53
|
+
: {}),
|
|
54
|
+
});
|
|
55
|
+
} else if (part.type === 'tool-approval-request') {
|
|
56
|
+
approvalRequestsByApprovalId.set(part.approvalId, part);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const toolResultIds = new Set<string>();
|
|
62
|
+
for (const part of lastMessage.content) {
|
|
63
|
+
if (part.type === 'tool-result') {
|
|
64
|
+
toolResultIds.add(part.toolCallId);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const continuations: HarnessAgentToolApprovalContinuation[] = [];
|
|
69
|
+
for (const part of lastMessage.content) {
|
|
70
|
+
if (part.type !== 'tool-approval-response') continue;
|
|
71
|
+
|
|
72
|
+
const approvalRequest = approvalRequestsByApprovalId.get(part.approvalId);
|
|
73
|
+
if (approvalRequest == null) {
|
|
74
|
+
throw new HarnessError({
|
|
75
|
+
message: `Tool approval response '${part.approvalId}' does not match a prior tool approval request.`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (toolResultIds.has(approvalRequest.toolCallId)) continue;
|
|
79
|
+
|
|
80
|
+
const toolCall = toolCallsByToolCallId.get(approvalRequest.toolCallId);
|
|
81
|
+
if (toolCall == null) {
|
|
82
|
+
throw new HarnessError({
|
|
83
|
+
message: `Tool approval request '${approvalRequest.approvalId}' references unknown tool call '${approvalRequest.toolCallId}'.`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
continuations.push({
|
|
88
|
+
approvalResponse: part,
|
|
89
|
+
toolCall,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return continuations;
|
|
94
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ToolSet } from '@ai-sdk/provider-utils';
|
|
2
|
+
import type {
|
|
3
|
+
HARNESS_V1_BUILTIN_TOOLS,
|
|
4
|
+
HarnessV1,
|
|
5
|
+
HarnessV1BuiltinTool,
|
|
6
|
+
HarnessV1BuiltinToolName,
|
|
7
|
+
HarnessV1BuiltinToolUseKind,
|
|
8
|
+
HarnessV1ContinueTurnOptions,
|
|
9
|
+
HarnessV1ContinueTurnState,
|
|
10
|
+
HarnessV1LifecycleState,
|
|
11
|
+
HarnessV1PendingToolApproval,
|
|
12
|
+
HarnessV1PermissionMode,
|
|
13
|
+
HarnessV1Prompt,
|
|
14
|
+
HarnessV1PromptControl,
|
|
15
|
+
HarnessV1PromptTurnOptions,
|
|
16
|
+
HarnessV1ResumeSessionState,
|
|
17
|
+
HarnessV1Session,
|
|
18
|
+
HarnessV1Skill,
|
|
19
|
+
HarnessV1StartOptions,
|
|
20
|
+
HarnessV1StreamPart,
|
|
21
|
+
HarnessV1ToolSpec,
|
|
22
|
+
} from '../v1';
|
|
23
|
+
|
|
24
|
+
export type HarnessAgentAdapter<TBuiltinTools extends ToolSet = ToolSet> =
|
|
25
|
+
HarnessV1<TBuiltinTools>;
|
|
26
|
+
|
|
27
|
+
export type HarnessAgentBuiltinTool<
|
|
28
|
+
INPUT = unknown,
|
|
29
|
+
OUTPUT = unknown,
|
|
30
|
+
> = HarnessV1BuiltinTool<INPUT, OUTPUT>;
|
|
31
|
+
export type HarnessAgentBuiltinToolName = HarnessV1BuiltinToolName;
|
|
32
|
+
export type HarnessAgentBuiltinToolUseKind = HarnessV1BuiltinToolUseKind;
|
|
33
|
+
export type HarnessAgentBuiltinTools = typeof HARNESS_V1_BUILTIN_TOOLS;
|
|
34
|
+
|
|
35
|
+
export type HarnessAgentStartOptions = HarnessV1StartOptions;
|
|
36
|
+
export type HarnessAgentAdapterSession = HarnessV1Session;
|
|
37
|
+
export type HarnessAgentPrompt = HarnessV1Prompt;
|
|
38
|
+
export type HarnessAgentPromptControl = HarnessV1PromptControl;
|
|
39
|
+
export type HarnessAgentPromptTurnOptions = HarnessV1PromptTurnOptions;
|
|
40
|
+
export type HarnessAgentContinueTurnOptions = HarnessV1ContinueTurnOptions;
|
|
41
|
+
export type HarnessAgentStreamPart = HarnessV1StreamPart;
|
|
42
|
+
export type HarnessAgentToolSpec = HarnessV1ToolSpec;
|
|
43
|
+
|
|
44
|
+
export type HarnessAgentLifecycleState = HarnessV1LifecycleState;
|
|
45
|
+
export type HarnessAgentResumeSessionState = HarnessV1ResumeSessionState;
|
|
46
|
+
export type HarnessAgentContinueTurnState = HarnessV1ContinueTurnState;
|
|
47
|
+
export type HarnessAgentPendingToolApproval = HarnessV1PendingToolApproval;
|
|
48
|
+
|
|
49
|
+
export type HarnessAgentSkill = HarnessV1Skill;
|
|
50
|
+
export type HarnessAgentPermissionMode = HarnessV1PermissionMode;
|