@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.
Files changed (67) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/LICENSE +13 -0
  3. package/README.md +142 -0
  4. package/agent/index.ts +47 -0
  5. package/bridge/index.ts +10 -0
  6. package/dist/agent/index.d.ts +1521 -0
  7. package/dist/agent/index.js +2958 -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 +415 -0
  11. package/dist/bridge/index.js.map +1 -0
  12. package/dist/index.d.ts +1536 -0
  13. package/dist/index.js +15834 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/utils/index.d.ts +225 -0
  16. package/dist/utils/index.js +12148 -0
  17. package/dist/utils/index.js.map +1 -0
  18. package/package.json +99 -1
  19. package/src/agent/harness-agent-session.ts +509 -0
  20. package/src/agent/harness-agent-settings.ts +131 -0
  21. package/src/agent/harness-agent-tool-approval-continuation.ts +94 -0
  22. package/src/agent/harness-agent-types.ts +50 -0
  23. package/src/agent/harness-agent.ts +819 -0
  24. package/src/agent/internal/bootstrap-recipe.ts +124 -0
  25. package/src/agent/internal/bridge-port-registry.ts +52 -0
  26. package/src/agent/internal/harness-stream-text-result.ts +720 -0
  27. package/src/agent/internal/lifecycle-state-validation.ts +95 -0
  28. package/src/agent/internal/permission-mode.ts +50 -0
  29. package/src/agent/internal/resolve-observability.ts +128 -0
  30. package/src/agent/internal/run-prompt.ts +813 -0
  31. package/src/agent/internal/strip-work-dir.ts +68 -0
  32. package/src/agent/internal/to-harness-stream.ts +75 -0
  33. package/src/agent/internal/translate-stream-part.ts +221 -0
  34. package/src/agent/internal/turn-telemetry.ts +359 -0
  35. package/src/agent/observability/file-reporter.ts +206 -0
  36. package/src/agent/observability/index.ts +15 -0
  37. package/src/agent/observability/trace-tree-reporter.ts +122 -0
  38. package/src/agent/observability/types.ts +86 -0
  39. package/src/agent/prewarm.ts +47 -0
  40. package/src/bridge/index.ts +702 -0
  41. package/src/errors/harness-capability-unsupported-error.ts +41 -0
  42. package/src/errors/harness-error.ts +22 -0
  43. package/src/index.ts +3 -0
  44. package/src/utils/bridge-ready.ts +277 -0
  45. package/src/utils/classify-disk-log.ts +43 -0
  46. package/src/utils/index.ts +15 -0
  47. package/src/utils/sandbox-channel.ts +453 -0
  48. package/src/v1/harness-v1-bootstrap.ts +46 -0
  49. package/src/v1/harness-v1-bridge-protocol.ts +310 -0
  50. package/src/v1/harness-v1-builtin-tool.ts +138 -0
  51. package/src/v1/harness-v1-call-warning.ts +22 -0
  52. package/src/v1/harness-v1-diagnostic.ts +66 -0
  53. package/src/v1/harness-v1-lifecycle-state.ts +65 -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-sandbox-provider.ts +76 -0
  61. package/src/v1/harness-v1-session.ts +272 -0
  62. package/src/v1/harness-v1-skill.ts +36 -0
  63. package/src/v1/harness-v1-stream-part.ts +363 -0
  64. package/src/v1/harness-v1-tool-spec.ts +31 -0
  65. package/src/v1/harness-v1.ts +83 -0
  66. package/src/v1/index.ts +93 -0
  67. 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;