tubeless 0.3.3 → 0.3.4
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/README.md +7 -7
- package/dist/agent/anthropic-protocol.d.ts +48 -0
- package/dist/agent/anthropic-protocol.js +180 -0
- package/dist/agent/anthropic-protocol.js.map +1 -0
- package/dist/agent/anthropic.d.ts +38 -0
- package/dist/agent/anthropic.js +208 -0
- package/dist/agent/anthropic.js.map +1 -0
- package/dist/agent/compile-agent.js +1 -0
- package/dist/agent/compile-agent.js.map +1 -1
- package/dist/agent/default-tools.js +38 -13
- package/dist/agent/default-tools.js.map +1 -1
- package/dist/agent/model-types.d.ts +2 -2
- package/dist/agent/model-types.js.map +1 -1
- package/dist/agent/openai-http.d.ts +1 -2
- package/dist/agent/openai-http.js +32 -44
- package/dist/agent/openai-http.js.map +1 -1
- package/dist/agent/openai-protocol.d.ts +2 -3
- package/dist/agent/openai-protocol.js +6 -75
- package/dist/agent/openai-protocol.js.map +1 -1
- package/dist/agent/openai-tool-outputs.js +3 -52
- package/dist/agent/openai-tool-outputs.js.map +1 -1
- package/dist/agent/openai.d.ts +10 -2
- package/dist/agent/openai.js +37 -13
- package/dist/agent/openai.js.map +1 -1
- package/dist/agent/provider-http.d.ts +11 -0
- package/dist/agent/provider-http.js +73 -0
- package/dist/agent/provider-http.js.map +1 -0
- package/dist/agent/provider-schema.d.ts +18 -0
- package/dist/agent/provider-schema.js +83 -0
- package/dist/agent/provider-schema.js.map +1 -0
- package/dist/agent/provider-tool-outputs.d.ts +8 -0
- package/dist/agent/provider-tool-outputs.js +57 -0
- package/dist/agent/provider-tool-outputs.js.map +1 -0
- package/dist/agent/tool-progress.d.ts +13 -0
- package/dist/agent/tool-progress.js +18 -0
- package/dist/agent/tool-progress.js.map +1 -0
- package/dist/agent/tools.d.ts +4 -0
- package/dist/agent/tools.js +16 -4
- package/dist/agent/tools.js.map +1 -1
- package/dist/agent/turn.js +35 -8
- package/dist/agent/turn.js.map +1 -1
- package/dist/cli/cli-parser.js +11 -2
- package/dist/cli/cli-parser.js.map +1 -1
- package/dist/cloudflare/cloudflare-access.d.ts +14 -0
- package/dist/cloudflare/cloudflare-access.js +178 -0
- package/dist/cloudflare/cloudflare-access.js.map +1 -0
- package/dist/cloudflare/cloudflare-cache.d.ts +23 -0
- package/dist/cloudflare/cloudflare-cache.js +112 -0
- package/dist/cloudflare/cloudflare-cache.js.map +1 -0
- package/dist/cloudflare/cloudflare-launches.d.ts +45 -0
- package/dist/cloudflare/cloudflare-launches.js +179 -0
- package/dist/cloudflare/cloudflare-launches.js.map +1 -0
- package/dist/cloudflare/cloudflare-run-store.d.ts +9 -0
- package/dist/cloudflare/cloudflare-run-store.js +33 -0
- package/dist/cloudflare/cloudflare-run-store.js.map +1 -0
- package/dist/cloudflare/cloudflare-run.d.ts +60 -0
- package/dist/cloudflare/cloudflare-run.js +225 -0
- package/dist/cloudflare/cloudflare-run.js.map +1 -0
- package/dist/cloudflare/cloudflare-storage.d.ts +17 -0
- package/dist/cloudflare/cloudflare-storage.js +2 -0
- package/dist/cloudflare/cloudflare-storage.js.map +1 -0
- package/dist/cloudflare/cloudflare-studio-client.d.ts +1 -0
- package/dist/cloudflare/cloudflare-studio-client.js +1 -0
- package/dist/cloudflare/cloudflare-studio.d.ts +32 -0
- package/dist/cloudflare/cloudflare-studio.js +165 -0
- package/dist/cloudflare/cloudflare-studio.js.map +1 -0
- package/dist/cloudflare/cloudflare.d.ts +25 -0
- package/dist/cloudflare/cloudflare.js +119 -0
- package/dist/cloudflare/cloudflare.js.map +1 -0
- package/dist/core/child-progress.d.ts +1 -1
- package/dist/core/child-progress.js +6 -2
- package/dist/core/child-progress.js.map +1 -1
- package/dist/core/iteration.js +3 -2
- package/dist/core/iteration.js.map +1 -1
- package/dist/core/pipeline-step-builders.d.ts +6 -0
- package/dist/core/pipeline-step-builders.js +19 -0
- package/dist/core/pipeline-step-builders.js.map +1 -1
- package/dist/core/pipeline-steps.d.ts +12 -0
- package/dist/core/pipeline-steps.js +5 -1
- package/dist/core/pipeline-steps.js.map +1 -1
- package/dist/hosting/hosting-commands.d.ts +49 -0
- package/dist/hosting/hosting-commands.js +138 -0
- package/dist/hosting/hosting-commands.js.map +1 -0
- package/dist/hosting/hosting-studio.d.ts +13 -0
- package/dist/hosting/hosting-studio.js +24 -0
- package/dist/hosting/hosting-studio.js.map +1 -0
- package/dist/run-store/run-store-sql.d.ts +59 -0
- package/dist/run-store/run-store-sql.js +150 -0
- package/dist/run-store/run-store-sql.js.map +1 -0
- package/dist/run-store/run-store-sqlite.js +18 -144
- package/dist/run-store/run-store-sqlite.js.map +1 -1
- package/dist/studio/run-store-ui-api.d.ts +1 -2
- package/dist/studio/run-store-ui-api.js +72 -92
- package/dist/studio/run-store-ui-api.js.map +1 -1
- package/dist/studio/run-store-ui-children.d.ts +12 -0
- package/dist/studio/run-store-ui-children.js +38 -0
- package/dist/studio/run-store-ui-children.js.map +1 -0
- package/dist/studio/run-store-ui-client-browser.js +4 -4
- package/dist/studio/run-store-ui-client-transport.d.ts +3 -2
- package/dist/studio/run-store-ui-client-transport.js +13 -1
- package/dist/studio/run-store-ui-client-transport.js.map +1 -1
- package/dist/studio/run-store-ui-graph.d.ts +12 -1
- package/dist/studio/run-store-ui-graph.js +62 -22
- package/dist/studio/run-store-ui-graph.js.map +1 -1
- package/dist/studio/run-store-ui-handler.d.ts +23 -0
- package/dist/studio/run-store-ui-handler.js +56 -0
- package/dist/studio/run-store-ui-handler.js.map +1 -0
- package/dist/studio/run-store-ui-hosting.d.ts +3 -3
- package/dist/studio/run-store-ui-hosting.js +20 -15
- package/dist/studio/run-store-ui-hosting.js.map +1 -1
- package/dist/studio/run-store-ui-http.d.ts +5 -6
- package/dist/studio/run-store-ui-http.js +45 -38
- package/dist/studio/run-store-ui-http.js.map +1 -1
- package/dist/studio/run-store-ui-node.d.ts +10 -0
- package/dist/studio/run-store-ui-node.js +44 -0
- package/dist/studio/run-store-ui-node.js.map +1 -0
- package/dist/studio/run-store-ui-page-node.d.ts +2 -0
- package/dist/studio/run-store-ui-page-node.js +8 -0
- package/dist/studio/run-store-ui-page-node.js.map +1 -0
- package/dist/studio/run-store-ui-page.d.ts +6 -8
- package/dist/studio/run-store-ui-page.js +32 -23
- package/dist/studio/run-store-ui-page.js.map +1 -1
- package/dist/studio/run-store-ui-runs.js +52 -5
- package/dist/studio/run-store-ui-runs.js.map +1 -1
- package/dist/studio/run-store-ui-schema.d.ts +20 -0
- package/dist/studio/run-store-ui-schema.js +13 -0
- package/dist/studio/run-store-ui-schema.js.map +1 -1
- package/dist/studio/run-store-ui-state.d.ts +7 -1
- package/dist/studio/run-store-ui-state.js +60 -10
- package/dist/studio/run-store-ui-state.js.map +1 -1
- package/dist/studio/run-store-ui.d.ts +4 -2
- package/dist/studio/run-store-ui.js +58 -53
- package/dist/studio/run-store-ui.js.map +1 -1
- package/dist/utilities/abort.d.ts +2 -0
- package/dist/utilities/abort.js +21 -0
- package/dist/utilities/abort.js.map +1 -1
- package/dist/workbench/cloud-client.d.ts +28 -0
- package/dist/workbench/cloud-client.js +264 -0
- package/dist/workbench/cloud-client.js.map +1 -0
- package/dist/workbench/cloud-config.d.ts +3 -0
- package/dist/workbench/cloud-config.js +30 -0
- package/dist/workbench/cloud-config.js.map +1 -0
- package/dist/workbench/cloud-credentials.d.ts +22 -0
- package/dist/workbench/cloud-credentials.js +121 -0
- package/dist/workbench/cloud-credentials.js.map +1 -0
- package/dist/workbench/cloud-protocol.d.ts +112 -0
- package/dist/workbench/cloud-protocol.js +183 -0
- package/dist/workbench/cloud-protocol.js.map +1 -0
- package/dist/workbench/pipeline-module.d.ts +1 -3
- package/dist/workbench/pipeline-module.js.map +1 -1
- package/dist/workbench/workbench-agent-commands.d.ts +8 -0
- package/dist/workbench/workbench-agent-commands.js +27 -0
- package/dist/workbench/workbench-agent-commands.js.map +1 -0
- package/dist/workbench/workbench-agent-environment.d.ts +7 -0
- package/dist/workbench/workbench-agent-environment.js +26 -0
- package/dist/workbench/workbench-agent-environment.js.map +1 -0
- package/dist/workbench/workbench-agent-history.d.ts +0 -1
- package/dist/workbench/workbench-agent-history.js +1 -3
- package/dist/workbench/workbench-agent-history.js.map +1 -1
- package/dist/workbench/workbench-agent-input.d.ts +14 -0
- package/dist/workbench/workbench-agent-input.js +72 -0
- package/dist/workbench/workbench-agent-input.js.map +1 -0
- package/dist/workbench/workbench-agent-prompt.d.ts +19 -0
- package/dist/workbench/workbench-agent-prompt.js +92 -0
- package/dist/workbench/workbench-agent-prompt.js.map +1 -0
- package/dist/workbench/workbench-agent-session.d.ts +6 -0
- package/dist/workbench/workbench-agent-session.js +96 -0
- package/dist/workbench/workbench-agent-session.js.map +1 -0
- package/dist/workbench/workbench-agent.d.ts +2 -0
- package/dist/workbench/workbench-agent.js +110 -0
- package/dist/workbench/workbench-agent.js.map +1 -0
- package/dist/workbench/workbench-auth.d.ts +3 -0
- package/dist/workbench/workbench-auth.js +165 -0
- package/dist/workbench/workbench-auth.js.map +1 -0
- package/dist/workbench/workbench-bin.js +1 -0
- package/dist/workbench/workbench-bin.js.map +1 -1
- package/dist/workbench/workbench-cloud-shared.d.ts +18 -0
- package/dist/workbench/workbench-cloud-shared.js +28 -0
- package/dist/workbench/workbench-cloud-shared.js.map +1 -0
- package/dist/workbench/workbench-cloud.d.ts +3 -0
- package/dist/workbench/workbench-cloud.js +306 -0
- package/dist/workbench/workbench-cloud.js.map +1 -0
- package/dist/workbench/workbench-command-execution.d.ts +2 -2
- package/dist/workbench/workbench-command-execution.js +4 -44
- package/dist/workbench/workbench-command-execution.js.map +1 -1
- package/dist/workbench/workbench-history.js +2 -2
- package/dist/workbench/workbench-history.js.map +1 -1
- package/dist/workbench/workbench-init-cloudflare.d.ts +11 -0
- package/dist/workbench/workbench-init-cloudflare.js +145 -0
- package/dist/workbench/workbench-init-cloudflare.js.map +1 -0
- package/dist/workbench/workbench-init.d.ts +30 -0
- package/dist/workbench/workbench-init.js +114 -0
- package/dist/workbench/workbench-init.js.map +1 -0
- package/dist/workbench/workbench-project-loader.d.ts +2 -0
- package/dist/workbench/workbench-project-loader.js +4 -19
- package/dist/workbench/workbench-project-loader.js.map +1 -1
- package/dist/workbench/workbench-shared.d.ts +10 -3
- package/dist/workbench/workbench-shared.js +5 -18
- package/dist/workbench/workbench-shared.js.map +1 -1
- package/dist/workbench/workbench-ui.js +2 -10
- package/dist/workbench/workbench-ui.js.map +1 -1
- package/dist/workbench/workbench.js +20 -0
- package/dist/workbench/workbench.js.map +1 -1
- package/docs/README.md +1 -0
- package/docs/agent-guide.md +86 -1
- package/docs/agents.md +205 -2
- package/docs/api-reference.md +56 -23
- package/docs/api-report.json +35 -7
- package/docs/child-pipeline-composition.md +3 -0
- package/docs/cli.md +262 -1
- package/docs/concepts.md +31 -0
- package/docs/declarative-pipelines.md +92 -0
- package/docs/deploy-cloudflare.md +269 -0
- package/docs/llms.txt +8 -1
- package/docs/recipes.md +64 -55
- package/docs/studio.md +4 -1
- package/examples/agent-model-anthropic.ts +11 -0
- package/examples/agent-repl-model.ts +16 -0
- package/examples/cloudflare/cloudflare/worker.ts +7 -0
- package/examples/cloudflare/cloudflare.config.ts +38 -0
- package/examples/cloudflare/tubeless.project.ts +2 -0
- package/examples/cloudflare/vite.config.ts +4 -0
- package/examples/declarative/user-input.yaml +30 -0
- package/examples/project/tubeless.project.ts +2 -0
- package/examples/user-input.ts +24 -0
- package/examples/yaml-user-input.ts +98 -0
- package/package.json +35 -10
- package/skills/tubeless/SKILL.md +36 -3
package/README.md
CHANGED
|
@@ -30,8 +30,7 @@ For scheduling or crash recovery, pair it with a [queue or workflow engine](./do
|
|
|
30
30
|
npm install tubeless
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
The
|
|
34
|
-
Bun 1.3.14 or later. Linux and macOS are supported; Windows is untested.
|
|
33
|
+
The ESM library requires Node.js 22.6+. The CLI requires Bun 1.3.14+. Linux and macOS are supported; Windows is untested.
|
|
35
34
|
See [installation and runtime support](./docs/getting-started.md#install).
|
|
36
35
|
|
|
37
36
|
Tubeless is pre-1.0, so the public API may change between releases.
|
|
@@ -89,12 +88,15 @@ In your app, add `tubeless.project.ts` and omit `--project`. Pass application
|
|
|
89
88
|
flags after `--`. Record runs with `--store` or `--trace`, then inspect them with
|
|
90
89
|
`tubeless history` or the [local studio](./docs/studio.md).
|
|
91
90
|
|
|
91
|
+
Use `tubeless auth login` and `tubeless cloud list` to select a Cloud pipeline by name or slug; see [Cloud CLI usage](./docs/cli.md#tubeless-cloud).
|
|
92
|
+
To host Studio in your own Cloudflare account, see [Deploy to Cloudflare](./docs/deploy-cloudflare.md).
|
|
93
|
+
|
|
92
94
|
## Find your route
|
|
93
95
|
|
|
94
96
|
- Start with a [typed import](./examples/typed-import.ts) or add
|
|
95
97
|
[dry runs and write gates](./examples/publish-with-gates.ts) to a publishing job.
|
|
96
98
|
- Build workflows with [child pipelines](./examples/child-pipeline.ts) and [fan-out](./examples/fan-out-progress.ts).
|
|
97
|
-
- Run
|
|
99
|
+
- Run `tubeless agent --model gpt-5.4-mini` for a [conversation loop with live pipeline reporting](./docs/cli.md#prompt-a-workspace-agent), or build [agents](./docs/agents.md) with custom tools, subagents, and durable checkpoints.
|
|
98
100
|
- Optional: the [peloton example](./examples/peloton.ts) is a cycling-themed kitchen sink for the live TUI.
|
|
99
101
|
|
|
100
102
|
The [recipe index](./docs/recipes.md) covers validation, retries, tracing, testing,
|
|
@@ -110,10 +112,8 @@ and more. For help authoring pipelines with a coding agent, install the
|
|
|
110
112
|
|
|
111
113
|
## Contributing
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
large change or changing the public API. See [CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
116
|
-
for local setup and checks, and [SECURITY.md](./SECURITY.md) to report a vulnerability privately.
|
|
115
|
+
Open an [issue](https://github.com/chetmancini/tubeless/issues) before a large change or public API change.
|
|
116
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for checks and [SECURITY.md](./SECURITY.md) to report vulnerabilities.
|
|
117
117
|
|
|
118
118
|
## License
|
|
119
119
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export declare const COMPACTION_BETA = "compact-2026-09-04";
|
|
2
|
+
/** Build a Messages API tool whose arguments carry the value in one wrapped field. */
|
|
3
|
+
export declare function anthropicTool(name: string, description: string, field: "input" | "result", schema: Readonly<Record<string, unknown>>, strict: boolean): {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
input_schema: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
[x: string]: Readonly<Record<string, unknown>>;
|
|
10
|
+
};
|
|
11
|
+
required: string[];
|
|
12
|
+
additionalProperties: boolean;
|
|
13
|
+
};
|
|
14
|
+
strict?: undefined;
|
|
15
|
+
} | {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
input_schema: {
|
|
19
|
+
type: string;
|
|
20
|
+
properties: {
|
|
21
|
+
[x: string]: Readonly<Record<string, unknown>>;
|
|
22
|
+
};
|
|
23
|
+
required: string[];
|
|
24
|
+
additionalProperties: boolean;
|
|
25
|
+
};
|
|
26
|
+
strict: boolean;
|
|
27
|
+
};
|
|
28
|
+
/** Validate a Messages API response and return its stop reason and content blocks. */
|
|
29
|
+
export declare function anthropicMessage(body: unknown): {
|
|
30
|
+
stopReason: string;
|
|
31
|
+
content: Record<string, unknown>[];
|
|
32
|
+
};
|
|
33
|
+
/** Map tool_use blocks to a harness decision; null when the model replied without tools. */
|
|
34
|
+
export declare function anthropicDecision(content: readonly Record<string, unknown>[]): {
|
|
35
|
+
kind: string;
|
|
36
|
+
result: unknown;
|
|
37
|
+
calls?: undefined;
|
|
38
|
+
} | {
|
|
39
|
+
result?: undefined;
|
|
40
|
+
kind: string;
|
|
41
|
+
calls: {
|
|
42
|
+
id: string;
|
|
43
|
+
tool: string;
|
|
44
|
+
input: unknown;
|
|
45
|
+
}[];
|
|
46
|
+
} | null;
|
|
47
|
+
/** Whether a message list carries a compaction block, which requires the beta header. */
|
|
48
|
+
export declare function carriesCompaction(messages: readonly unknown[]): boolean;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { record, strictParameter } from "./provider-schema.js";
|
|
2
|
+
export const COMPACTION_BETA = "compact-2026-09-04";
|
|
3
|
+
const FORMATS = new Set([
|
|
4
|
+
"date-time",
|
|
5
|
+
"time",
|
|
6
|
+
"date",
|
|
7
|
+
"duration",
|
|
8
|
+
"email",
|
|
9
|
+
"hostname",
|
|
10
|
+
"uri",
|
|
11
|
+
"ipv4",
|
|
12
|
+
"ipv6",
|
|
13
|
+
"uuid",
|
|
14
|
+
]);
|
|
15
|
+
// Annotations with no model-facing meaning; references are rejected, so definitions are unused.
|
|
16
|
+
const DROPPED = new Set(["$schema", "$id", "$comment", "$defs", "definitions"]);
|
|
17
|
+
const KEPT = new Set([
|
|
18
|
+
"type",
|
|
19
|
+
"title",
|
|
20
|
+
"description",
|
|
21
|
+
"default",
|
|
22
|
+
"required",
|
|
23
|
+
"additionalProperties",
|
|
24
|
+
// Kept so strictParameter rejects them instead of hiding them in a description.
|
|
25
|
+
"$ref",
|
|
26
|
+
"$dynamicRef",
|
|
27
|
+
"$recursiveRef",
|
|
28
|
+
]);
|
|
29
|
+
const scalar = (value) => value === null || typeof value !== "object";
|
|
30
|
+
function jsonType(value) {
|
|
31
|
+
if (value === null)
|
|
32
|
+
return "null";
|
|
33
|
+
if (typeof value === "boolean")
|
|
34
|
+
return "boolean";
|
|
35
|
+
if (typeof value === "string")
|
|
36
|
+
return "string";
|
|
37
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
38
|
+
return Number.isInteger(value) ? "integer" : "number";
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
/** Anthropic's grammar requires a type unless a combinator is present. */
|
|
42
|
+
function withStrictType(schema) {
|
|
43
|
+
if (schema.type !== undefined ||
|
|
44
|
+
schema.anyOf !== undefined ||
|
|
45
|
+
schema.allOf !== undefined ||
|
|
46
|
+
// Leave references in place so the shared checker rejects them before I/O.
|
|
47
|
+
["$ref", "$dynamicRef", "$recursiveRef"].some((keyword) => Object.hasOwn(schema, keyword)))
|
|
48
|
+
return schema;
|
|
49
|
+
if (Object.hasOwn(schema, "const")) {
|
|
50
|
+
const type = jsonType(schema.const);
|
|
51
|
+
if (type !== undefined)
|
|
52
|
+
return { type, ...schema };
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
55
|
+
const types = schema.enum.map(jsonType);
|
|
56
|
+
if (types.every((type) => type !== undefined)) {
|
|
57
|
+
const unique = [...new Set(types)];
|
|
58
|
+
if (unique.length === 1 || unique.every((type) => type === "integer" || type === "number"))
|
|
59
|
+
return { type: unique.length === 1 ? unique[0] : "number", ...schema };
|
|
60
|
+
const groups = new Map();
|
|
61
|
+
for (const [index, type] of types.entries()) {
|
|
62
|
+
const values = groups.get(type) ?? [];
|
|
63
|
+
values.push(schema.enum[index]);
|
|
64
|
+
groups.set(type, values);
|
|
65
|
+
}
|
|
66
|
+
const { enum: _enum, ...rest } = schema;
|
|
67
|
+
return { ...rest, anyOf: [...groups].map(([type, values]) => ({ type, enum: values })) };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
throw new Error("Anthropic strict schema requires a type when anyOf and allOf are absent");
|
|
71
|
+
}
|
|
72
|
+
// Strict tool use compiles only a subset of JSON Schema. The harness still validates the
|
|
73
|
+
// original schema, so every other keyword moves into the description instead.
|
|
74
|
+
function strictSubset(schema) {
|
|
75
|
+
if (!record(schema))
|
|
76
|
+
return schema;
|
|
77
|
+
const kept = {};
|
|
78
|
+
const moved = {};
|
|
79
|
+
for (const [keyword, value] of Object.entries(schema)) {
|
|
80
|
+
if (DROPPED.has(keyword))
|
|
81
|
+
continue;
|
|
82
|
+
if (keyword === "properties" && record(value))
|
|
83
|
+
kept.properties = Object.fromEntries(Object.entries(value).map(([name, child]) => [name, strictSubset(child)]));
|
|
84
|
+
else if (keyword === "items" && record(value))
|
|
85
|
+
kept.items = strictSubset(value);
|
|
86
|
+
else if ((keyword === "anyOf" || keyword === "allOf") && Array.isArray(value))
|
|
87
|
+
kept[keyword] = value.map(strictSubset);
|
|
88
|
+
else if (keyword === "oneOf" && Array.isArray(value) && !Array.isArray(schema.anyOf))
|
|
89
|
+
kept.anyOf = value.map(strictSubset);
|
|
90
|
+
else if (KEPT.has(keyword) ||
|
|
91
|
+
(keyword === "minItems" && (value === 0 || value === 1)) ||
|
|
92
|
+
(keyword === "format" && FORMATS.has(value)) ||
|
|
93
|
+
(keyword === "const" && scalar(value)) ||
|
|
94
|
+
(keyword === "enum" && Array.isArray(value) && value.every(scalar)))
|
|
95
|
+
kept[keyword] = value;
|
|
96
|
+
else
|
|
97
|
+
moved[keyword] = value;
|
|
98
|
+
}
|
|
99
|
+
if (Object.keys(moved).length > 0) {
|
|
100
|
+
const note = `Constraints: ${JSON.stringify(moved)}`;
|
|
101
|
+
kept.description =
|
|
102
|
+
typeof kept.description === "string" && kept.description
|
|
103
|
+
? `${kept.description}\n${note}`
|
|
104
|
+
: note;
|
|
105
|
+
}
|
|
106
|
+
return withStrictType(kept);
|
|
107
|
+
}
|
|
108
|
+
/** Build a Messages API tool whose arguments carry the value in one wrapped field. */
|
|
109
|
+
export function anthropicTool(name, description, field, schema, strict) {
|
|
110
|
+
// The Messages API accepts at most 64 characters. The harness allows 128.
|
|
111
|
+
if (name.length > 64)
|
|
112
|
+
throw new Error(`Anthropic tool name exceeds 64 characters: ${name}`);
|
|
113
|
+
if (!strict) {
|
|
114
|
+
// Wrapping still relocates the reference root, so references stay rejected.
|
|
115
|
+
return {
|
|
116
|
+
name,
|
|
117
|
+
description,
|
|
118
|
+
input_schema: strictParameter(field, schema, `tool ${name}`, {
|
|
119
|
+
provider: "Anthropic",
|
|
120
|
+
closedObjects: false,
|
|
121
|
+
requireEveryProperty: false,
|
|
122
|
+
}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// Check the schema the provider will see: stripped constraints are enforced only by
|
|
126
|
+
// the harness, so their subschemas need not meet strict-mode object rules.
|
|
127
|
+
// SAFETY: strictSubset returns a record when given a record.
|
|
128
|
+
const visible = strictSubset(schema);
|
|
129
|
+
return {
|
|
130
|
+
name,
|
|
131
|
+
description,
|
|
132
|
+
input_schema: strictParameter(field, visible, `tool ${name}`, {
|
|
133
|
+
provider: "Anthropic",
|
|
134
|
+
closedObjects: true,
|
|
135
|
+
requireEveryProperty: false,
|
|
136
|
+
}),
|
|
137
|
+
strict: true,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/** Validate a Messages API response and return its stop reason and content blocks. */
|
|
141
|
+
export function anthropicMessage(body) {
|
|
142
|
+
if (!record(body) ||
|
|
143
|
+
body.type !== "message" ||
|
|
144
|
+
typeof body.stop_reason !== "string" ||
|
|
145
|
+
!Array.isArray(body.content) ||
|
|
146
|
+
!body.content.every(record))
|
|
147
|
+
throw new Error("Anthropic returned an invalid message");
|
|
148
|
+
return { stopReason: body.stop_reason, content: body.content };
|
|
149
|
+
}
|
|
150
|
+
/** Map tool_use blocks to a harness decision; null when the model replied without tools. */
|
|
151
|
+
export function anthropicDecision(content) {
|
|
152
|
+
const calls = [];
|
|
153
|
+
for (const block of content) {
|
|
154
|
+
if (block.type !== "tool_use")
|
|
155
|
+
continue;
|
|
156
|
+
if (typeof block.id !== "string" || typeof block.name !== "string" || !record(block.input))
|
|
157
|
+
throw new Error("Invalid Anthropic tool call");
|
|
158
|
+
const key = block.name === "_finish" ? "result" : "input";
|
|
159
|
+
if (Object.keys(block.input).length !== 1 || !Object.hasOwn(block.input, key))
|
|
160
|
+
throw new Error(`Anthropic tool arguments require exactly one ${key} field`);
|
|
161
|
+
calls.push({ id: block.id, tool: block.name, input: block.input[key] });
|
|
162
|
+
}
|
|
163
|
+
if (calls.length === 0)
|
|
164
|
+
return null;
|
|
165
|
+
if (calls.some((call) => call.tool === "_finish")) {
|
|
166
|
+
if (calls.length !== 1)
|
|
167
|
+
throw new Error("Anthropic must finish without other calls");
|
|
168
|
+
return { kind: "finish", result: calls[0].input };
|
|
169
|
+
}
|
|
170
|
+
return { kind: "continue", calls };
|
|
171
|
+
}
|
|
172
|
+
/** Whether a message list carries a compaction block, which requires the beta header. */
|
|
173
|
+
export function carriesCompaction(messages) {
|
|
174
|
+
const first = messages[0];
|
|
175
|
+
return (record(first) &&
|
|
176
|
+
Array.isArray(first.content) &&
|
|
177
|
+
record(first.content[0]) &&
|
|
178
|
+
first.content[0].type === "compaction");
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=anthropic-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic-protocol.js","sourceRoot":"","sources":["../../src/agent/anthropic-protocol.ts"],"names":[],"mappings":"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","sourcesContent":["import { record, strictParameter } from \"./provider-schema.js\";\n\nexport const COMPACTION_BETA = \"compact-2026-09-04\";\n\nconst FORMATS = new Set([\n \"date-time\",\n \"time\",\n \"date\",\n \"duration\",\n \"email\",\n \"hostname\",\n \"uri\",\n \"ipv4\",\n \"ipv6\",\n \"uuid\",\n]);\n// Annotations with no model-facing meaning; references are rejected, so definitions are unused.\nconst DROPPED = new Set([\"$schema\", \"$id\", \"$comment\", \"$defs\", \"definitions\"]);\nconst KEPT = new Set([\n \"type\",\n \"title\",\n \"description\",\n \"default\",\n \"required\",\n \"additionalProperties\",\n // Kept so strictParameter rejects them instead of hiding them in a description.\n \"$ref\",\n \"$dynamicRef\",\n \"$recursiveRef\",\n]);\nconst scalar = (value: unknown) => value === null || typeof value !== \"object\";\n\nfunction jsonType(\n value: unknown\n): \"null\" | \"boolean\" | \"integer\" | \"number\" | \"string\" | undefined {\n if (value === null) return \"null\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"string\") return \"string\";\n if (typeof value === \"number\" && Number.isFinite(value))\n return Number.isInteger(value) ? \"integer\" : \"number\";\n return undefined;\n}\n\n/** Anthropic's grammar requires a type unless a combinator is present. */\nfunction withStrictType(schema: Record<string, unknown>): Record<string, unknown> {\n if (\n schema.type !== undefined ||\n schema.anyOf !== undefined ||\n schema.allOf !== undefined ||\n // Leave references in place so the shared checker rejects them before I/O.\n [\"$ref\", \"$dynamicRef\", \"$recursiveRef\"].some((keyword) => Object.hasOwn(schema, keyword))\n )\n return schema;\n if (Object.hasOwn(schema, \"const\")) {\n const type = jsonType(schema.const);\n if (type !== undefined) return { type, ...schema };\n }\n if (Array.isArray(schema.enum) && schema.enum.length > 0) {\n const types = schema.enum.map(jsonType);\n if (types.every((type) => type !== undefined)) {\n const unique = [...new Set(types)];\n if (unique.length === 1 || unique.every((type) => type === \"integer\" || type === \"number\"))\n return { type: unique.length === 1 ? unique[0] : \"number\", ...schema };\n const groups = new Map<string, unknown[]>();\n for (const [index, type] of types.entries()) {\n const values = groups.get(type!) ?? [];\n values.push(schema.enum[index]);\n groups.set(type!, values);\n }\n const { enum: _enum, ...rest } = schema;\n return { ...rest, anyOf: [...groups].map(([type, values]) => ({ type, enum: values })) };\n }\n }\n throw new Error(\"Anthropic strict schema requires a type when anyOf and allOf are absent\");\n}\n\n// Strict tool use compiles only a subset of JSON Schema. The harness still validates the\n// original schema, so every other keyword moves into the description instead.\nfunction strictSubset(schema: unknown): unknown {\n if (!record(schema)) return schema;\n const kept: Record<string, unknown> = {};\n const moved: Record<string, unknown> = {};\n for (const [keyword, value] of Object.entries(schema)) {\n if (DROPPED.has(keyword)) continue;\n if (keyword === \"properties\" && record(value))\n kept.properties = Object.fromEntries(\n Object.entries(value).map(([name, child]) => [name, strictSubset(child)])\n );\n else if (keyword === \"items\" && record(value)) kept.items = strictSubset(value);\n else if ((keyword === \"anyOf\" || keyword === \"allOf\") && Array.isArray(value))\n kept[keyword] = value.map(strictSubset);\n else if (keyword === \"oneOf\" && Array.isArray(value) && !Array.isArray(schema.anyOf))\n kept.anyOf = value.map(strictSubset);\n else if (\n KEPT.has(keyword) ||\n (keyword === \"minItems\" && (value === 0 || value === 1)) ||\n (keyword === \"format\" && FORMATS.has(value as string)) ||\n (keyword === \"const\" && scalar(value)) ||\n (keyword === \"enum\" && Array.isArray(value) && value.every(scalar))\n )\n kept[keyword] = value;\n else moved[keyword] = value;\n }\n if (Object.keys(moved).length > 0) {\n const note = `Constraints: ${JSON.stringify(moved)}`;\n kept.description =\n typeof kept.description === \"string\" && kept.description\n ? `${kept.description}\\n${note}`\n : note;\n }\n return withStrictType(kept);\n}\n\n/** Build a Messages API tool whose arguments carry the value in one wrapped field. */\nexport function anthropicTool(\n name: string,\n description: string,\n field: \"input\" | \"result\",\n schema: Readonly<Record<string, unknown>>,\n strict: boolean\n) {\n // The Messages API accepts at most 64 characters. The harness allows 128.\n if (name.length > 64) throw new Error(`Anthropic tool name exceeds 64 characters: ${name}`);\n if (!strict) {\n // Wrapping still relocates the reference root, so references stay rejected.\n return {\n name,\n description,\n input_schema: strictParameter(field, schema, `tool ${name}`, {\n provider: \"Anthropic\",\n closedObjects: false,\n requireEveryProperty: false,\n }),\n };\n }\n // Check the schema the provider will see: stripped constraints are enforced only by\n // the harness, so their subschemas need not meet strict-mode object rules.\n // SAFETY: strictSubset returns a record when given a record.\n const visible = strictSubset(schema) as Record<string, unknown>;\n return {\n name,\n description,\n input_schema: strictParameter(field, visible, `tool ${name}`, {\n provider: \"Anthropic\",\n closedObjects: true,\n requireEveryProperty: false,\n }),\n strict: true,\n };\n}\n\n/** Validate a Messages API response and return its stop reason and content blocks. */\nexport function anthropicMessage(body: unknown) {\n if (\n !record(body) ||\n body.type !== \"message\" ||\n typeof body.stop_reason !== \"string\" ||\n !Array.isArray(body.content) ||\n !body.content.every(record)\n )\n throw new Error(\"Anthropic returned an invalid message\");\n return { stopReason: body.stop_reason, content: body.content };\n}\n\n/** Map tool_use blocks to a harness decision; null when the model replied without tools. */\nexport function anthropicDecision(content: readonly Record<string, unknown>[]) {\n const calls: { id: string; tool: string; input: unknown }[] = [];\n for (const block of content) {\n if (block.type !== \"tool_use\") continue;\n if (typeof block.id !== \"string\" || typeof block.name !== \"string\" || !record(block.input))\n throw new Error(\"Invalid Anthropic tool call\");\n const key = block.name === \"_finish\" ? \"result\" : \"input\";\n if (Object.keys(block.input).length !== 1 || !Object.hasOwn(block.input, key))\n throw new Error(`Anthropic tool arguments require exactly one ${key} field`);\n calls.push({ id: block.id, tool: block.name, input: block.input[key] });\n }\n if (calls.length === 0) return null;\n if (calls.some((call) => call.tool === \"_finish\")) {\n if (calls.length !== 1) throw new Error(\"Anthropic must finish without other calls\");\n return { kind: \"finish\", result: calls[0]!.input };\n }\n return { kind: \"continue\", calls };\n}\n\n/** Whether a message list carries a compaction block, which requires the beta header. */\nexport function carriesCompaction(messages: readonly unknown[]): boolean {\n const first = messages[0];\n return (\n record(first) &&\n Array.isArray(first.content) &&\n record(first.content[0]) &&\n first.content[0].type === \"compaction\"\n );\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AgentModel } from "./model-types.js";
|
|
2
|
+
/** Messages API transport settings; credentials are resolved only when a decision executes. */
|
|
3
|
+
export interface AnthropicModelOptions {
|
|
4
|
+
/** Defaults to ANTHROPIC_MODEL or claude-opus-5-5. Must support tool use. */
|
|
5
|
+
readonly model?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Sent as x-api-key. Without an explicit apiKey or authToken, ANTHROPIC_API_KEY is
|
|
8
|
+
* used at execution time, then ANTHROPIC_AUTH_TOKEN.
|
|
9
|
+
*/
|
|
10
|
+
readonly apiKey?: string;
|
|
11
|
+
/** Sent as Authorization: Bearer, for compatible servers and gateways that expect it. */
|
|
12
|
+
readonly authToken?: string;
|
|
13
|
+
/**
|
|
14
|
+
* API root without the version path. Defaults to ANTHROPIC_BASE_URL or
|
|
15
|
+
* https://api.anthropic.com; set it for any server that implements the Messages API.
|
|
16
|
+
*/
|
|
17
|
+
readonly baseUrl?: string;
|
|
18
|
+
/** Sent as output_config.effort. Omitted by default; null also omits it. */
|
|
19
|
+
readonly reasoningEffort?: "low" | "medium" | "high" | "xhigh" | "max" | null;
|
|
20
|
+
/** Output cap for each request, including thinking; defaults to 32000 tokens. */
|
|
21
|
+
readonly maxTokens?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Send tools with strict: true (default). Unsupported constraints move into descriptions;
|
|
24
|
+
* the harness still validates every argument. Disable for servers without strict tool use.
|
|
25
|
+
*/
|
|
26
|
+
readonly strict?: boolean;
|
|
27
|
+
/** Send top-level cache_control (default). Disable for servers that reject it. */
|
|
28
|
+
readonly promptCaching?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Compact above this history size (default 65536 bytes), or when the complete decision
|
|
31
|
+
* exceeds the request limit. null never requests on-demand compaction.
|
|
32
|
+
*/
|
|
33
|
+
readonly compactAfterBytes?: number | null;
|
|
34
|
+
/** Combined deadline for compaction and decision requests; defaults to 600000 ms. */
|
|
35
|
+
readonly timeoutMs?: number;
|
|
36
|
+
}
|
|
37
|
+
/** Create a dependency-free Anthropic Messages model with native history and on-demand compaction. */
|
|
38
|
+
export declare function anthropicModel(options?: AnthropicModelOptions): AgentModel;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { anthropicDecision, anthropicMessage, anthropicTool, carriesCompaction, COMPACTION_BETA, } from "./anthropic-protocol.js";
|
|
2
|
+
import { MAX_REQUEST_BYTES, providerBaseUrl, providerRequest } from "./provider-http.js";
|
|
3
|
+
import { record } from "./provider-schema.js";
|
|
4
|
+
import { boundedToolOutputs } from "./provider-tool-outputs.js";
|
|
5
|
+
import { throwIfAborted } from "../utilities/abort.js";
|
|
6
|
+
const DEFAULT_BASE_URL = "https://api.anthropic.com";
|
|
7
|
+
const EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
8
|
+
const TOOL_RULE = "Respond only by calling tools. Call _finish alone with the final answer. Never mix _finish with tool calls.";
|
|
9
|
+
const SUMMARY_INSTRUCTIONS = "Summarize this agent conversation so the agent can continue the task. Preserve the task, decisions, file paths, commands run, verified results, failures, and remaining work. Do not call tools; respond with the summary text only.";
|
|
10
|
+
// `{"role":"user","content":[]}` plus a separator, rounded up.
|
|
11
|
+
const RESULT_MESSAGE_BYTES = 64;
|
|
12
|
+
/** Create a dependency-free Anthropic Messages model with native history and on-demand compaction. */
|
|
13
|
+
export function anthropicModel(options = {}) {
|
|
14
|
+
const { model, apiKey, authToken, baseUrl, reasoningEffort = null, maxTokens = 32_000, strict = true, promptCaching = true, compactAfterBytes = 65_536, timeoutMs = 600_000, } = options;
|
|
15
|
+
if ((compactAfterBytes !== null &&
|
|
16
|
+
(!Number.isSafeInteger(compactAfterBytes) ||
|
|
17
|
+
compactAfterBytes < 1 ||
|
|
18
|
+
compactAfterBytes >= MAX_REQUEST_BYTES)) ||
|
|
19
|
+
!Number.isSafeInteger(maxTokens) ||
|
|
20
|
+
maxTokens < 1 ||
|
|
21
|
+
!Number.isSafeInteger(timeoutMs) ||
|
|
22
|
+
timeoutMs < 1 ||
|
|
23
|
+
timeoutMs > 2_147_483_647 ||
|
|
24
|
+
typeof strict !== "boolean" ||
|
|
25
|
+
typeof promptCaching !== "boolean" ||
|
|
26
|
+
(apiKey !== undefined && authToken !== undefined) ||
|
|
27
|
+
(authToken !== undefined && (typeof authToken !== "string" || !authToken.trim())) ||
|
|
28
|
+
(model !== undefined && (typeof model !== "string" || !model.trim())) ||
|
|
29
|
+
(apiKey !== undefined && (typeof apiKey !== "string" || !apiKey.trim())) ||
|
|
30
|
+
(baseUrl !== undefined && typeof baseUrl !== "string") ||
|
|
31
|
+
(reasoningEffort !== null && !EFFORTS.includes(reasoningEffort)))
|
|
32
|
+
throw new Error("Invalid Anthropic model configuration");
|
|
33
|
+
if (baseUrl !== undefined)
|
|
34
|
+
providerBaseUrl("Anthropic", baseUrl);
|
|
35
|
+
return async (request, context) => {
|
|
36
|
+
const selectedModel = (model ?? process.env.ANTHROPIC_MODEL)?.trim() || "claude-opus-5-5";
|
|
37
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
38
|
+
const signal = context.signal ? AbortSignal.any([context.signal, timeout]) : timeout;
|
|
39
|
+
const base = providerBaseUrl("Anthropic", baseUrl ?? (process.env.ANTHROPIC_BASE_URL?.trim() || DEFAULT_BASE_URL));
|
|
40
|
+
throwIfAborted(signal, "Anthropic decision");
|
|
41
|
+
// Validate descriptors before either compaction or a decision can perform I/O.
|
|
42
|
+
const tools = [
|
|
43
|
+
...context.capabilities.map((tool) => anthropicTool(tool.name, tool.description, "input", tool.inputJsonSchema, strict)),
|
|
44
|
+
anthropicTool("_finish", "Finish with the final answer.", "result", context.resultJsonSchema, strict),
|
|
45
|
+
];
|
|
46
|
+
if (request.conversation !== null &&
|
|
47
|
+
(!Array.isArray(request.conversation) || !request.conversation.every(record)))
|
|
48
|
+
throw new Error("Invalid Anthropic conversation state");
|
|
49
|
+
// A restored session starts a new prompt with no tool outcomes. Close _finish
|
|
50
|
+
// before appending that prompt; the Messages API rejects a second user message.
|
|
51
|
+
const newPrompt = context.turn === 1 && request.outcomes.length === 0;
|
|
52
|
+
let messages = request.conversation === null
|
|
53
|
+
? []
|
|
54
|
+
: newPrompt
|
|
55
|
+
? closeFinishedTools(request.conversation)
|
|
56
|
+
: [...request.conversation];
|
|
57
|
+
const withPrompt = (history) => newPrompt ? appendUserText(history, request.task) : [...history];
|
|
58
|
+
const send = (endpointLabel, payload, beta) => providerRequest("Anthropic", endpointLabel, `${base}/v1/messages`, {
|
|
59
|
+
...credentials(apiKey, authToken),
|
|
60
|
+
"anthropic-version": "2023-06-01",
|
|
61
|
+
...(beta || carriesCompaction(payload.messages)
|
|
62
|
+
? { "anthropic-beta": COMPACTION_BETA }
|
|
63
|
+
: {}),
|
|
64
|
+
}, payload, signal);
|
|
65
|
+
const system = `${request.instructions}\n\n${TOOL_RULE}`;
|
|
66
|
+
// Summaries read the same system prompt and tools as the conversation.
|
|
67
|
+
const compactRequest = {
|
|
68
|
+
model: selectedModel,
|
|
69
|
+
max_tokens: maxTokens,
|
|
70
|
+
system,
|
|
71
|
+
tools,
|
|
72
|
+
compaction: { type: "summarize", instructions: SUMMARY_INSTRUCTIONS },
|
|
73
|
+
messages,
|
|
74
|
+
};
|
|
75
|
+
const decisionRequest = {
|
|
76
|
+
model: selectedModel,
|
|
77
|
+
max_tokens: maxTokens,
|
|
78
|
+
...(reasoningEffort === null ? {} : { output_config: { effort: reasoningEffort } }),
|
|
79
|
+
...(promptCaching ? { cache_control: { type: "ephemeral" } } : {}),
|
|
80
|
+
system,
|
|
81
|
+
tools,
|
|
82
|
+
// Current models reject forced tool choice; the system rule and retry below steer instead.
|
|
83
|
+
tool_choice: { type: "auto" },
|
|
84
|
+
messages,
|
|
85
|
+
};
|
|
86
|
+
const results = (availableBytes) => request.outcomes.length === 0
|
|
87
|
+
? []
|
|
88
|
+
: [
|
|
89
|
+
{
|
|
90
|
+
role: "user",
|
|
91
|
+
content: boundedToolOutputs("Anthropic", request.outcomes, availableBytes - RESULT_MESSAGE_BYTES, (outcome, output) => ({
|
|
92
|
+
type: "tool_result",
|
|
93
|
+
tool_use_id: outcome.id,
|
|
94
|
+
content: output,
|
|
95
|
+
...(outcome.ok ? {} : { is_error: true }),
|
|
96
|
+
})),
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
const history = [...messages, ...results(MAX_REQUEST_BYTES)];
|
|
100
|
+
const completed = withPrompt(history);
|
|
101
|
+
const shouldCompact = compactAfterBytes !== null &&
|
|
102
|
+
(request.outcomes.length > 0 || (newPrompt && request.conversation !== null)) &&
|
|
103
|
+
(Buffer.byteLength(JSON.stringify(completed)) > compactAfterBytes ||
|
|
104
|
+
Buffer.byteLength(JSON.stringify({ ...decisionRequest, messages: completed })) >
|
|
105
|
+
MAX_REQUEST_BYTES);
|
|
106
|
+
// Compaction covers completed history only. The new prompt stays verbatim.
|
|
107
|
+
const nextRequest = shouldCompact ? compactRequest : decisionRequest;
|
|
108
|
+
const payload = shouldCompact ? history : completed;
|
|
109
|
+
const fits = Buffer.byteLength(JSON.stringify({ ...nextRequest, messages: payload })) <= MAX_REQUEST_BYTES;
|
|
110
|
+
messages = fits
|
|
111
|
+
? payload
|
|
112
|
+
: [
|
|
113
|
+
...messages,
|
|
114
|
+
...results(MAX_REQUEST_BYTES - Buffer.byteLength(JSON.stringify(nextRequest))),
|
|
115
|
+
];
|
|
116
|
+
if (shouldCompact) {
|
|
117
|
+
const summary = anthropicMessage(await send("compaction", { ...compactRequest, messages }, true));
|
|
118
|
+
const [block] = summary.content;
|
|
119
|
+
if (summary.stopReason === "compaction" &&
|
|
120
|
+
summary.content.length === 1 &&
|
|
121
|
+
block?.type === "compaction" &&
|
|
122
|
+
typeof block.content === "string" &&
|
|
123
|
+
typeof block.signature === "string") {
|
|
124
|
+
// The returned assistant message replaces every message it covered, unchanged.
|
|
125
|
+
// The summarized history ended with tool results, so a user turn must follow it.
|
|
126
|
+
messages = [
|
|
127
|
+
{ role: "assistant", content: [block] },
|
|
128
|
+
{ role: "user", content: "Continue the task from this summary." },
|
|
129
|
+
];
|
|
130
|
+
context.log.log("Compacted agent conversation");
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
context.log.log(`Anthropic compaction returned no summary (${summary.stopReason})`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (newPrompt && (shouldCompact || !fits))
|
|
137
|
+
messages = withPrompt(messages);
|
|
138
|
+
for (let attempt = 0;; attempt++) {
|
|
139
|
+
const response = anthropicMessage(await send("messages", { ...decisionRequest, messages }, false));
|
|
140
|
+
if (response.stopReason === "max_tokens")
|
|
141
|
+
throw new Error("Anthropic response reached max_tokens; raise maxTokens");
|
|
142
|
+
if (response.stopReason === "refusal")
|
|
143
|
+
throw new Error("Anthropic refused the request");
|
|
144
|
+
if (response.stopReason !== "tool_use" && response.stopReason !== "end_turn")
|
|
145
|
+
throw new Error(`Anthropic stopped unexpectedly (${response.stopReason})`);
|
|
146
|
+
if (response.content.length === 0)
|
|
147
|
+
throw new Error("Anthropic returned empty content");
|
|
148
|
+
// Keep every block, including thinking and signatures, exactly as returned.
|
|
149
|
+
messages = [...messages, { role: "assistant", content: response.content }];
|
|
150
|
+
const decision = anthropicDecision(response.content);
|
|
151
|
+
if (decision)
|
|
152
|
+
return { decision, conversation: messages };
|
|
153
|
+
if (attempt > 0)
|
|
154
|
+
throw new Error("Anthropic returned no tool calls");
|
|
155
|
+
messages = [...messages, { role: "user", content: TOOL_RULE }];
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/** Pair each pending `_finish` call with a tool_result so the next user prompt is valid. */
|
|
160
|
+
function closeFinishedTools(messages) {
|
|
161
|
+
const last = messages.at(-1);
|
|
162
|
+
if (!record(last) || last.role !== "assistant" || !Array.isArray(last.content))
|
|
163
|
+
return [...messages];
|
|
164
|
+
const uses = last.content.filter((block) => record(block) && block.type === "tool_use");
|
|
165
|
+
if (uses.length === 0)
|
|
166
|
+
return [...messages];
|
|
167
|
+
if (uses.some((block) => block.name !== "_finish"))
|
|
168
|
+
throw new Error("Anthropic conversation has unfinished tool calls");
|
|
169
|
+
return [
|
|
170
|
+
...messages,
|
|
171
|
+
{
|
|
172
|
+
role: "user",
|
|
173
|
+
content: uses.map((block) => ({
|
|
174
|
+
type: "tool_result",
|
|
175
|
+
tool_use_id: block.id,
|
|
176
|
+
content: JSON.stringify(record(block.input) && Object.hasOwn(block.input, "result")
|
|
177
|
+
? block.input.result
|
|
178
|
+
: block.input),
|
|
179
|
+
})),
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
}
|
|
183
|
+
/** Add a user prompt, extending a trailing user message so roles keep alternating. */
|
|
184
|
+
function appendUserText(messages, text) {
|
|
185
|
+
const last = messages.at(-1);
|
|
186
|
+
if (record(last) && last.role === "user") {
|
|
187
|
+
const content = Array.isArray(last.content)
|
|
188
|
+
? [...last.content, { type: "text", text }]
|
|
189
|
+
: [
|
|
190
|
+
{ type: "text", text: last.content },
|
|
191
|
+
{ type: "text", text },
|
|
192
|
+
];
|
|
193
|
+
return [...messages.slice(0, -1), { role: "user", content }];
|
|
194
|
+
}
|
|
195
|
+
return [...messages, { role: "user", content: text }];
|
|
196
|
+
}
|
|
197
|
+
function credentials(apiKey, authToken) {
|
|
198
|
+
if (authToken !== undefined)
|
|
199
|
+
return { Authorization: `Bearer ${authToken.trim()}` };
|
|
200
|
+
const key = (apiKey ?? process.env.ANTHROPIC_API_KEY)?.trim();
|
|
201
|
+
if (key)
|
|
202
|
+
return { "x-api-key": key };
|
|
203
|
+
const token = process.env.ANTHROPIC_AUTH_TOKEN?.trim();
|
|
204
|
+
if (token)
|
|
205
|
+
return { Authorization: `Bearer ${token}` };
|
|
206
|
+
throw new Error("ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN is required for live model execution");
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/agent/anthropic.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentModel } from \"./model-types.js\";\nimport {\n anthropicDecision,\n anthropicMessage,\n anthropicTool,\n carriesCompaction,\n COMPACTION_BETA,\n} from \"./anthropic-protocol.js\";\nimport { MAX_REQUEST_BYTES, providerBaseUrl, providerRequest } from \"./provider-http.js\";\nimport { record } from \"./provider-schema.js\";\nimport { boundedToolOutputs } from \"./provider-tool-outputs.js\";\nimport { throwIfAborted } from \"../utilities/abort.js\";\n\n/** Messages API transport settings; credentials are resolved only when a decision executes. */\nexport interface AnthropicModelOptions {\n /** Defaults to ANTHROPIC_MODEL or claude-opus-5-5. Must support tool use. */\n readonly model?: string;\n /**\n * Sent as x-api-key. Without an explicit apiKey or authToken, ANTHROPIC_API_KEY is\n * used at execution time, then ANTHROPIC_AUTH_TOKEN.\n */\n readonly apiKey?: string;\n /** Sent as Authorization: Bearer, for compatible servers and gateways that expect it. */\n readonly authToken?: string;\n /**\n * API root without the version path. Defaults to ANTHROPIC_BASE_URL or\n * https://api.anthropic.com; set it for any server that implements the Messages API.\n */\n readonly baseUrl?: string;\n /** Sent as output_config.effort. Omitted by default; null also omits it. */\n readonly reasoningEffort?: \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\" | null;\n /** Output cap for each request, including thinking; defaults to 32000 tokens. */\n readonly maxTokens?: number;\n /**\n * Send tools with strict: true (default). Unsupported constraints move into descriptions;\n * the harness still validates every argument. Disable for servers without strict tool use.\n */\n readonly strict?: boolean;\n /** Send top-level cache_control (default). Disable for servers that reject it. */\n readonly promptCaching?: boolean;\n /**\n * Compact above this history size (default 65536 bytes), or when the complete decision\n * exceeds the request limit. null never requests on-demand compaction.\n */\n readonly compactAfterBytes?: number | null;\n /** Combined deadline for compaction and decision requests; defaults to 600000 ms. */\n readonly timeoutMs?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.anthropic.com\";\nconst EFFORTS = [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\nconst TOOL_RULE =\n \"Respond only by calling tools. Call _finish alone with the final answer. Never mix _finish with tool calls.\";\nconst SUMMARY_INSTRUCTIONS =\n \"Summarize this agent conversation so the agent can continue the task. Preserve the task, decisions, file paths, commands run, verified results, failures, and remaining work. Do not call tools; respond with the summary text only.\";\n// `{\"role\":\"user\",\"content\":[]}` plus a separator, rounded up.\nconst RESULT_MESSAGE_BYTES = 64;\n\n/** Create a dependency-free Anthropic Messages model with native history and on-demand compaction. */\nexport function anthropicModel(options: AnthropicModelOptions = {}): AgentModel {\n const {\n model,\n apiKey,\n authToken,\n baseUrl,\n reasoningEffort = null,\n maxTokens = 32_000,\n strict = true,\n promptCaching = true,\n compactAfterBytes = 65_536,\n timeoutMs = 600_000,\n } = options;\n if (\n (compactAfterBytes !== null &&\n (!Number.isSafeInteger(compactAfterBytes) ||\n compactAfterBytes < 1 ||\n compactAfterBytes >= MAX_REQUEST_BYTES)) ||\n !Number.isSafeInteger(maxTokens) ||\n maxTokens < 1 ||\n !Number.isSafeInteger(timeoutMs) ||\n timeoutMs < 1 ||\n timeoutMs > 2_147_483_647 ||\n typeof strict !== \"boolean\" ||\n typeof promptCaching !== \"boolean\" ||\n (apiKey !== undefined && authToken !== undefined) ||\n (authToken !== undefined && (typeof authToken !== \"string\" || !authToken.trim())) ||\n (model !== undefined && (typeof model !== \"string\" || !model.trim())) ||\n (apiKey !== undefined && (typeof apiKey !== \"string\" || !apiKey.trim())) ||\n (baseUrl !== undefined && typeof baseUrl !== \"string\") ||\n (reasoningEffort !== null && !EFFORTS.includes(reasoningEffort))\n )\n throw new Error(\"Invalid Anthropic model configuration\");\n if (baseUrl !== undefined) providerBaseUrl(\"Anthropic\", baseUrl);\n return async (request, context) => {\n const selectedModel = (model ?? process.env.ANTHROPIC_MODEL)?.trim() || \"claude-opus-5-5\";\n const timeout = AbortSignal.timeout(timeoutMs);\n const signal = context.signal ? AbortSignal.any([context.signal, timeout]) : timeout;\n const base = providerBaseUrl(\n \"Anthropic\",\n baseUrl ?? (process.env.ANTHROPIC_BASE_URL?.trim() || DEFAULT_BASE_URL)\n );\n throwIfAborted(signal, \"Anthropic decision\");\n // Validate descriptors before either compaction or a decision can perform I/O.\n const tools = [\n ...context.capabilities.map((tool) =>\n anthropicTool(tool.name, tool.description, \"input\", tool.inputJsonSchema, strict)\n ),\n anthropicTool(\n \"_finish\",\n \"Finish with the final answer.\",\n \"result\",\n context.resultJsonSchema,\n strict\n ),\n ];\n if (\n request.conversation !== null &&\n (!Array.isArray(request.conversation) || !request.conversation.every(record))\n )\n throw new Error(\"Invalid Anthropic conversation state\");\n // A restored session starts a new prompt with no tool outcomes. Close _finish\n // before appending that prompt; the Messages API rejects a second user message.\n const newPrompt = context.turn === 1 && request.outcomes.length === 0;\n let messages: unknown[] =\n request.conversation === null\n ? []\n : newPrompt\n ? closeFinishedTools(request.conversation)\n : [...request.conversation];\n const withPrompt = (history: readonly unknown[]) =>\n newPrompt ? appendUserText(history, request.task) : [...history];\n const send = (endpointLabel: string, payload: { messages: unknown[] }, beta: boolean) =>\n providerRequest(\n \"Anthropic\",\n endpointLabel,\n `${base}/v1/messages`,\n {\n ...credentials(apiKey, authToken),\n \"anthropic-version\": \"2023-06-01\",\n ...(beta || carriesCompaction(payload.messages)\n ? { \"anthropic-beta\": COMPACTION_BETA }\n : {}),\n },\n payload,\n signal\n );\n const system = `${request.instructions}\\n\\n${TOOL_RULE}`;\n // Summaries read the same system prompt and tools as the conversation.\n const compactRequest = {\n model: selectedModel,\n max_tokens: maxTokens,\n system,\n tools,\n compaction: { type: \"summarize\", instructions: SUMMARY_INSTRUCTIONS },\n messages,\n };\n const decisionRequest = {\n model: selectedModel,\n max_tokens: maxTokens,\n ...(reasoningEffort === null ? {} : { output_config: { effort: reasoningEffort } }),\n ...(promptCaching ? { cache_control: { type: \"ephemeral\" } } : {}),\n system,\n tools,\n // Current models reject forced tool choice; the system rule and retry below steer instead.\n tool_choice: { type: \"auto\" },\n messages,\n };\n const results = (availableBytes: number) =>\n request.outcomes.length === 0\n ? []\n : [\n {\n role: \"user\",\n content: boundedToolOutputs(\n \"Anthropic\",\n request.outcomes,\n availableBytes - RESULT_MESSAGE_BYTES,\n (outcome, output) => ({\n type: \"tool_result\",\n tool_use_id: outcome.id,\n content: output,\n ...(outcome.ok ? {} : { is_error: true }),\n })\n ),\n },\n ];\n const history = [...messages, ...results(MAX_REQUEST_BYTES)];\n const completed = withPrompt(history);\n const shouldCompact =\n compactAfterBytes !== null &&\n (request.outcomes.length > 0 || (newPrompt && request.conversation !== null)) &&\n (Buffer.byteLength(JSON.stringify(completed)) > compactAfterBytes ||\n Buffer.byteLength(JSON.stringify({ ...decisionRequest, messages: completed })) >\n MAX_REQUEST_BYTES);\n // Compaction covers completed history only. The new prompt stays verbatim.\n const nextRequest = shouldCompact ? compactRequest : decisionRequest;\n const payload = shouldCompact ? history : completed;\n const fits =\n Buffer.byteLength(JSON.stringify({ ...nextRequest, messages: payload })) <= MAX_REQUEST_BYTES;\n messages = fits\n ? payload\n : [\n ...messages,\n ...results(MAX_REQUEST_BYTES - Buffer.byteLength(JSON.stringify(nextRequest))),\n ];\n if (shouldCompact) {\n const summary = anthropicMessage(\n await send(\"compaction\", { ...compactRequest, messages }, true)\n );\n const [block] = summary.content;\n if (\n summary.stopReason === \"compaction\" &&\n summary.content.length === 1 &&\n block?.type === \"compaction\" &&\n typeof block.content === \"string\" &&\n typeof block.signature === \"string\"\n ) {\n // The returned assistant message replaces every message it covered, unchanged.\n // The summarized history ended with tool results, so a user turn must follow it.\n messages = [\n { role: \"assistant\", content: [block] },\n { role: \"user\", content: \"Continue the task from this summary.\" },\n ];\n context.log.log(\"Compacted agent conversation\");\n } else {\n context.log.log(`Anthropic compaction returned no summary (${summary.stopReason})`);\n }\n }\n if (newPrompt && (shouldCompact || !fits)) messages = withPrompt(messages);\n for (let attempt = 0; ; attempt++) {\n const response = anthropicMessage(\n await send(\"messages\", { ...decisionRequest, messages }, false)\n );\n if (response.stopReason === \"max_tokens\")\n throw new Error(\"Anthropic response reached max_tokens; raise maxTokens\");\n if (response.stopReason === \"refusal\") throw new Error(\"Anthropic refused the request\");\n if (response.stopReason !== \"tool_use\" && response.stopReason !== \"end_turn\")\n throw new Error(`Anthropic stopped unexpectedly (${response.stopReason})`);\n if (response.content.length === 0) throw new Error(\"Anthropic returned empty content\");\n // Keep every block, including thinking and signatures, exactly as returned.\n messages = [...messages, { role: \"assistant\", content: response.content }];\n const decision = anthropicDecision(response.content);\n if (decision) return { decision, conversation: messages };\n if (attempt > 0) throw new Error(\"Anthropic returned no tool calls\");\n messages = [...messages, { role: \"user\", content: TOOL_RULE }];\n }\n };\n}\n\n/** Pair each pending `_finish` call with a tool_result so the next user prompt is valid. */\nfunction closeFinishedTools(messages: readonly unknown[]): unknown[] {\n const last = messages.at(-1);\n if (!record(last) || last.role !== \"assistant\" || !Array.isArray(last.content))\n return [...messages];\n const uses = last.content.filter((block) => record(block) && block.type === \"tool_use\");\n if (uses.length === 0) return [...messages];\n if (uses.some((block) => block.name !== \"_finish\"))\n throw new Error(\"Anthropic conversation has unfinished tool calls\");\n return [\n ...messages,\n {\n role: \"user\",\n content: uses.map((block) => ({\n type: \"tool_result\",\n tool_use_id: block.id,\n content: JSON.stringify(\n record(block.input) && Object.hasOwn(block.input, \"result\")\n ? block.input.result\n : block.input\n ),\n })),\n },\n ];\n}\n\n/** Add a user prompt, extending a trailing user message so roles keep alternating. */\nfunction appendUserText(messages: readonly unknown[], text: string): unknown[] {\n const last = messages.at(-1);\n if (record(last) && last.role === \"user\") {\n const content = Array.isArray(last.content)\n ? [...last.content, { type: \"text\", text }]\n : [\n { type: \"text\", text: last.content },\n { type: \"text\", text },\n ];\n return [...messages.slice(0, -1), { role: \"user\", content }];\n }\n return [...messages, { role: \"user\", content: text }];\n}\n\nfunction credentials(\n apiKey: string | undefined,\n authToken: string | undefined\n): Record<string, string> {\n if (authToken !== undefined) return { Authorization: `Bearer ${authToken.trim()}` };\n const key = (apiKey ?? process.env.ANTHROPIC_API_KEY)?.trim();\n if (key) return { \"x-api-key\": key };\n const token = process.env.ANTHROPIC_AUTH_TOKEN?.trim();\n if (token) return { Authorization: `Bearer ${token}` };\n throw new Error(\"ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN is required for live model execution\");\n}\n"]}
|
|
@@ -32,6 +32,7 @@ export function compileAgent(definition, resolveCwd = false) {
|
|
|
32
32
|
const turn = createAgentTurn(config, registry, limits, { capabilities, resultJsonSchema });
|
|
33
33
|
const { iteratePipeline } = createSteps(config.inputSchema);
|
|
34
34
|
const agent = iteratePipeline("agent", {
|
|
35
|
+
name: "Agent",
|
|
35
36
|
pipeline: turn,
|
|
36
37
|
maxIterations: limits.maxTurns,
|
|
37
38
|
dryRun: config.dryRun ? undefined : "skip",
|