@hachej/boring-agent 0.1.90 → 0.1.92

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 (37) hide show
  1. package/dist/{chunk-4PKGVPJA.js → chunk-2GBHYJVC.js} +27 -5
  2. package/dist/{chunk-BEFJNMKQ.js → chunk-CJ2BW2W2.js} +23 -8
  3. package/dist/{chunk-F7ZS27HE.js → chunk-IWBC6SF7.js} +93 -19
  4. package/dist/chunk-NITBKFG6.js +1246 -0
  5. package/dist/{chunk-JEDRQAR5.js → chunk-OFQ7J6SI.js} +8 -2
  6. package/dist/{chunk-5CYVBVP3.js → chunk-V3ZD3T3G.js} +4785 -2153
  7. package/dist/core/index.d.ts +5 -5
  8. package/dist/core/index.js +2 -2
  9. package/dist/{createHarness-UCCGMVQJ.js → createHarness-PMYRKDE4.js} +2 -2
  10. package/dist/errors-C38f6pol.d.ts +200 -0
  11. package/dist/front/index.d.ts +20 -4
  12. package/dist/front/index.js +172 -44
  13. package/dist/{harness-Rbkdpvse.d.ts → harness-Cc48o6uS.d.ts} +2 -2
  14. package/dist/{workspaceAgentDispatcher-50Uzuxhs.d.ts → lease-BqeCLGPL.d.ts} +266 -57
  15. package/dist/{protocol-CDA4bcP9.d.ts → middleware-CzW8n7B7.d.ts} +20 -2
  16. package/dist/piChatEvent-BsMp3MSo.d.ts +368 -0
  17. package/dist/{piChatSessionService-DDKPOj4x.d.ts → piChatSessionService-BrPqKrpY.d.ts} +5 -1
  18. package/dist/{sandbox-OEG_l9GE.d.ts → sandbox-DAD8cWQR.d.ts} +14 -1
  19. package/dist/server/agent-host/testing/gatewayConformance.d.ts +41 -0
  20. package/dist/server/agent-host/testing/gatewayConformance.js +465 -0
  21. package/dist/server/index.d.ts +451 -528
  22. package/dist/server/index.js +43 -9
  23. package/dist/server/worker/index.d.ts +2 -2
  24. package/dist/server/worker/index.js +7 -7
  25. package/dist/shared/index.d.ts +115 -41
  26. package/dist/shared/index.js +65 -282
  27. package/dist/types-uQozUhGt.d.ts +759 -0
  28. package/docs/ACCESSIBILITY.md +4 -5
  29. package/docs/API.md +3 -0
  30. package/docs/AUTHORING.md +68 -0
  31. package/docs/ERROR_CODES.md +27 -6
  32. package/docs/README.md +1 -0
  33. package/docs/runtime.md +3 -3
  34. package/package.json +22 -13
  35. package/dist/chunk-CFQQXJKK.js +0 -385
  36. package/dist/chunk-DGRKMMXO.js +0 -8
  37. package/dist/piChatEvent-CHgln6yx.d.ts +0 -367
@@ -37,9 +37,8 @@ This runs in CI automatically through the existing `pnpm e2e` job.
37
37
 
38
38
  ## Known Gaps
39
39
 
40
- 1. Storybook a11y pipeline is not present yet.
41
- There is no `.storybook` config, no `@storybook/addon-a11y`, and no
42
- story-level axe scan.
40
+ 1. A registered agent-component UI-review spec is not present yet, so there is
41
+ no fixture-level axe scan for agent primitives.
43
42
  2. Current automated axe gate targets the shipped chat surface only.
44
43
  Workspace pane-level a11y checks (file tree, editor panes, dock tabs) are
45
44
  outside this package and need dedicated coverage in `@hachej/boring-workspace`.
@@ -47,7 +46,7 @@ This runs in CI automatically through the existing `pnpm e2e` job.
47
46
 
48
47
  ## Next Increment
49
48
 
50
- 1. Stand up Storybook for `@hachej/boring-agent` primitives.
51
- 2. Enable addon-a11y and add story-level CI checks.
49
+ 1. Register deterministic agent-primitive checkpoints in `tools/ui-review`.
50
+ 2. Add fixture-level axe hard gates to that registered spec.
52
51
  3. Extend browser axe checks to `workspace-playground` once pane a11y owners
53
52
  land stable selectors/landmarks.
package/docs/API.md CHANGED
@@ -58,6 +58,9 @@ The Node runtime. Key exports:
58
58
  `createVercelProvisioningAdapter`.
59
59
  - HTTP/CSP: `applyCspHeaders`; `fileRoutes` for mounting file routes alone.
60
60
  - Harness: `createResourceSettingsManager` and the Pi harness option types.
61
+ - Declarative authoring: `compileAgentDirectory()` and
62
+ `materializeAgentDirectory()`; see [AUTHORING](./AUTHORING.md). The materialized
63
+ value contains no tools, plugin selectors, paths, or runtime handles.
61
64
  - `createLogger`.
62
65
 
63
66
  ### Trusted workspace agent dispatch
@@ -0,0 +1,68 @@
1
+ # Declarative agent authoring
2
+
3
+ An authored agent directory describes identity, safe display metadata, and
4
+ instructions. It does not select executable behavior.
5
+
6
+ ```text
7
+ my-agent/
8
+ ├── agent.json
9
+ └── instructions.md
10
+ ```
11
+
12
+ ```json
13
+ {
14
+ "schemaVersion": 1,
15
+ "definitionId": "claims-assistant",
16
+ "version": "2026.07.20",
17
+ "label": "Claims assistant",
18
+ "description": "Helps process insurance claims.",
19
+ "instructionsRef": "instructions.md"
20
+ }
21
+ ```
22
+
23
+ `label` and `description` are optional. Labels are limited to 128 characters;
24
+ descriptions are limited to 1,024 characters. Both must be trimmed, valid
25
+ Unicode without control or bidi-spoofing characters.
26
+
27
+ The loader:
28
+
29
+ - reads only `agent.json` and `instructions.md`;
30
+ - requires regular, non-symlink files contained in the directory;
31
+ - limits the manifest to 64 KiB and instructions to 256 KiB before decoding;
32
+ - requires valid UTF-8 and non-whitespace instructions;
33
+ - returns a frozen `AuthoredAgentSourceV1` from
34
+ `materializeAgentDirectory()` containing only identity, metadata, and
35
+ instructions.
36
+
37
+ ## Executable behavior belongs to trusted host policy
38
+
39
+ Tools, capabilities, skills, MCP servers, packages, models, credentials,
40
+ plugins, runtime modes, and Workspace roots are configured by trusted host and
41
+ plugin code. Authored JSON cannot enable them.
42
+
43
+ Legacy manifests may still contain these keys while being cleaned up:
44
+
45
+ - `capabilityRequirements`
46
+ - `toolRefs`
47
+ - `skillRefs`
48
+ - `mcpServerRefs`
49
+
50
+ Absent or empty arrays are accepted and stripped. A non-empty array fails with
51
+ `AUTHORED_AGENT_REFERENCE_UNSUPPORTED`; move the requested behavior to trusted
52
+ host/plugin configuration.
53
+
54
+ The removed server contracts are `AuthoredAgentToolCatalog`,
55
+ `MaterializeAgentDirectoryInput.toolCatalog`, and the materialized `tools` and
56
+ `declaredToolRefs` fields. This correction removes no HTTP or product endpoint.
57
+
58
+ ## Validation
59
+
60
+ ```bash
61
+ boring-ui agent validate ./my-agent
62
+ boring-ui agent validate ./my-agent --json
63
+ ```
64
+
65
+ Success reports only identity, optional metadata, and instruction byte length.
66
+ It never reports or resolves a tool catalog. Errors are field-specific and
67
+ redacted; unexpected failures return `INTERNAL_ERROR` without local paths or
68
+ secret-like source values.
@@ -39,6 +39,9 @@ All API failures must use the response envelope:
39
39
  | `AGENT_RUNTIME_NOT_READY` | Selected workspace runtime dependencies (`runtime-dependencies` or `runtime:<name>`, e.g. `runtime:python`/`runtime:node`) are still preparing | 503 | retry | warn | stable (public API) |
40
40
  | `AGENT_BINDING_DISPOSED` | A caller retained an agent binding after its host retired it | 410 | resolve a fresh binding | warn | stable (trusted API) |
41
41
  | `AGENT_CONTROL_RECEIPT_INVALID` | The existing agent runtime returned a malformed interrupt/stop receipt through the trusted dispatcher | 500 | report-bug | error | stable (trusted API) |
42
+ | `AGENT_FLEET_PLUGIN_UNKNOWN` | Agent fleet configuration references a plugin that the app did not load | 500 | fix app configuration | error | stable (trusted API) |
43
+ | `AGENT_FLEET_CONFIG_BINDING_UNKNOWN` | Agent fleet configuration passes a plugin config key outside its declared Agent config contract | 500 | fix app configuration | error | stable (trusted API) |
44
+ | `AGENT_FLEET_MODEL_POLICY_UNCOMPILED` | Agent fleet configuration declares a model policy that the app fleet compiler did not resolve | 500 | configure app fleet compiler | error | stable (trusted API) |
42
45
  | `RUNTIME_PROVISIONING_FAILED` | Agent runtime dependency provisioning failed before Level 3 runtime dependencies became ready | 503 | retry/report | error | stable (public API) |
43
46
  | `RUNTIME_PROVISIONING_LOCKED` | Agent runtime provisioning is locked by another reconciler | 423 | retry | warn | stable (public API) |
44
47
  | `BWRAP_UNAVAILABLE` | `bwrap` binary not found | 500 | report-bug | error | stable (public API) |
@@ -47,6 +50,28 @@ All API failures must use the response envelope:
47
50
  | `SANDBOX_NOT_READY` | Remote sandbox cold start / provisioning | 503 | retry | warn | stable (public API) |
48
51
  | `SANDBOX_EXPIRED` | Remote sandbox TTL elapsed | 410 | retry | warn | stable (public API) |
49
52
  | `VERCEL_API_ERROR` | Generic upstream Vercel SDK/API failure | 502 | retry | error | stable (public API) |
53
+ | `REMOTE_WORKER_CONFIG_INVALID` | Static remote-worker fleet configuration is invalid or incomplete | 500 | report-bug | error | stable (trusted API) |
54
+ | `REMOTE_WORKER_PROTOCOL_MISMATCH` | Worker protocol or provider-contract version differs from the configured V1 cohort | 502 | operator-fix | error | stable (trusted API) |
55
+ | `REMOTE_WORKER_UNAUTHENTICATED` | Worker rejected the per-box capability or authenticated receipt | 401 | operator-fix | warn | stable (trusted API) |
56
+ | `REMOTE_WORKER_UNAVAILABLE` | The statically owning worker cannot serve the request; no fallback is attempted | 503 | retry | warn | stable (public API) |
57
+ | `REMOTE_WORKER_UNQUALIFIED` | Worker qualification or artifact facts do not match static placement config | 503 | operator-fix | error | stable (trusted API) |
58
+ | `REMOTE_WORKER_REQUEST_INVALID` | A strict remote-worker request schema rejected the request | 400 | report-bug | warn | stable (trusted API) |
59
+ | `REMOTE_WORKER_RESPONSE_INVALID` | A strict remote-worker response schema rejected the worker response | 502 | operator-fix | error | stable (trusted API) |
60
+ | `REMOTE_WORKER_CAPABILITY_EXPIRED` | A short-lived worker capability expired or exceeded its maximum lifetime | 401 | retry | warn | stable (trusted API) |
61
+ | `REMOTE_WORKER_AUTHORIZED_WORKSPACE_REQUIRED` | Remote provider creation lacked an authenticated workspace identity | 403 | report-bug | warn | stable (trusted API) |
62
+ | `REMOTE_WORKER_BINDING_RECEIPT_INVALID` | Create returned an unauthenticated or mismatched sandbox/workspace binding receipt | 502 | operator-fix | error | stable (trusted API) |
63
+ | `REMOTE_WORKER_SANDBOX_WORKSPACE_MISMATCH` | Authorized workspace capability does not match the immutable sandbox lease binding | 404 | report-security | warn | stable (trusted API) |
64
+ | `REMOTE_WORKER_SANDBOX_NOT_FOUND` | Addressed remote sandbox lease does not exist | 404 | reacquire | warn | stable (trusted API) |
65
+ | `REMOTE_WORKER_SANDBOX_EXPIRED` | Addressed remote sandbox lease has expired | 410 | reacquire | warn | stable (trusted API) |
66
+ | `REMOTE_WORKER_SANDBOX_DISPOSED` | Addressed remote sandbox lease was already disposed | 410 | reacquire | warn | stable (trusted API) |
67
+ | `REMOTE_WORKER_CREATE_CONCURRENCY_EXHAUSTED` | Worker create concurrency is exhausted | 429 | retry | warn | stable (trusted API) |
68
+ | `REMOTE_WORKER_EXEC_CONCURRENCY_EXHAUSTED` | Worker execution concurrency is exhausted | 429 | retry | warn | stable (trusted API) |
69
+ | `REMOTE_WORKER_IDEMPOTENCY_CONFLICT` | Reused lease/invocation id has a different request digest | 409 | report-bug | warn | stable (trusted API) |
70
+ | `REMOTE_WORKER_EXEC_IN_PROGRESS` | Duplicate invocation is still executing | 409 | retry | warn | stable (trusted API) |
71
+ | `REMOTE_WORKER_SECRET_INVOCATION_NOT_REPLAYABLE` | Completed secret-bearing invocation cannot replay cached output | 409 | start-new-request | warn | stable (trusted API) |
72
+ | `REMOTE_WORKER_OUTCOME_UNKNOWN` | Worker loss left an effectful invocation outcome unknown; no automatic replay is safe | 502 | inspect-before-retry | error | stable (public API) |
73
+ | `REMOTE_WORKER_INCOMPLETE_CLEANUP` | Provider could not prove remote lease teardown after bounded retries | 502 | operator-fix | error | stable (trusted API) |
74
+ | `REMOTE_WORKER_DOCKER_COMMAND_FAILED` | Worker runtime command failed without exposing infrastructure stderr | 502 | operator-fix | error | stable (trusted API) |
50
75
  | `REMOTE_WORKER_TIMEOUT` | Remote worker request exceeded its client-side timeout before a response arrived | 504 | retry | warn | stable (public API) |
51
76
  | `REMOTE_WORKER_STREAM_CLOSED` | Remote worker filesystem event stream closed unexpectedly | 502 | retry | warn | stable (public API) |
52
77
  | `CIRCUIT_OPEN` | Circuit breaker open; request fast-failed | 503 | retry | warn | stable (public API) |
@@ -64,12 +89,8 @@ All API failures must use the response envelope:
64
89
  | `TOOL_EXECUTION_ERROR` | Tool threw or returned execution failure | 500 | report-bug | error | stable (public API) |
65
90
  | `AUTHORED_AGENT_ID_INVALID` | Authored agent materialization received an agent type id outside the product-safe grammar | 400 | user-fix | warn | stable (trusted API) |
66
91
  | `AUTHORED_AGENT_TYPE_MISMATCH` | Trusted host expected one authored agent type but the directory declares another | 409 | user-fix | warn | stable (trusted API) |
67
- | `AUTHORED_AGENT_CATALOG_REQUIRED` | Authored tool refs are present before a trusted server catalog resolver is available | 400 | user-fix | warn | stable (trusted API) |
68
- | `AUTHORED_AGENT_CATALOG_INVALID` | Trusted authored-tool catalog failed while resolving a declared tool reference | 500 | report-bug | error | stable (trusted API) |
69
- | `AUTHORED_AGENT_REFERENCE_UNKNOWN` | Authored agent materialization could not resolve a declared trusted reference | 400 | user-fix | warn | stable (trusted API) |
70
- | `AUTHORED_AGENT_REFERENCE_UNSUPPORTED` | Authored agent materialization received non-empty reference families unsupported by v1 | 400 | user-fix | warn | stable (trusted API) |
71
- | `AUTHORED_AGENT_TOOL_INVALID` | Trusted authored-tool catalog resolution produced a tool that fails authored-tool validation | 500 | report-bug | error | stable (trusted API) |
72
- | `AUTHORED_AGENT_TOOL_COLLISION` | Trusted authored-tool resolution produced duplicate or colliding tool names | 409 | user-fix | warn | stable (trusted API) |
92
+ | `AUTHORED_AGENT_REFERENCE_UNSUPPORTED` | Authored source contains a non-empty legacy capability/tool/skill/MCP selector; move behavior to trusted host plugins | 400 | user-fix | warn | stable migration error |
93
+ | `AUTHORED_AGENT_TOOL_COLLISION` | Normal trusted tool composition produced duplicate or colliding tool names | 409 | user-fix | warn | stable (trusted API) |
73
94
  | `MCP_AGENT_ARTIFACT_INVALID` | Managed MCP delivery artifact is path-shaped, non-Markdown, binary, malformed UTF-8, or otherwise invalid | 400 | user-fix | warn | stable (public API) |
74
95
  | `MCP_AGENT_ARTIFACT_TOO_LARGE` | Managed MCP final text, inline Markdown artifact, or serialized result exceeds the delivery v0 byte cap | 413 | user-fix | warn | stable (public API) |
75
96
  | `MCP_AGENT_ARTIFACT_UNAVAILABLE` | Managed MCP artifact is missing, unreadable through the authorized workspace, or changed during read | 409 | retry | warn | stable (public API) |
package/docs/README.md CHANGED
@@ -67,6 +67,7 @@ the agent ↔ workspace boundary is in
67
67
  **Surfaces & integration**
68
68
 
69
69
  - [API](./API.md) — the four entry points and what each exports.
70
+ - [AUTHORING](./AUTHORING.md) — declarative agent directories, bounds, validation, and legacy catalog migration.
70
71
  - [STYLING](./STYLING.md) — CSS-variable theming contract and public selectors.
71
72
  - [UI-SHADCN](./UI-SHADCN.md) — `ChatPanel` styling model and tool-renderer overrides.
72
73
  - [tools](./tools.md) — built-in tools, package-added tools, and how to add custom tools.
package/docs/runtime.md CHANGED
@@ -204,11 +204,11 @@ modes, the adapter maps that host path into the public runtime namespace
204
204
 
205
205
  ### Production storage roots in `vercel-sandbox` mode
206
206
 
207
- In a Fly-hosted app using `BORING_AGENT_MODE=vercel-sandbox`, there are two
207
+ In a hosted app using `BORING_AGENT_MODE=vercel-sandbox`, there are two
208
208
  filesystems with different jobs:
209
209
 
210
210
  ```txt
211
- Fly app container / mounted volume:
211
+ App container / mounted volume:
212
212
  /data/workspaces/<workspaceId> host/control-plane workspace anchor
213
213
  /data/pi-sessions/<workspaceId> durable chat transcript storage
214
214
 
@@ -225,7 +225,7 @@ Those belong to the sandbox runtime root, `/workspace`.
225
225
 
226
226
  Production chat history also must not use the container root filesystem. Set
227
227
  `BORING_AGENT_SESSION_ROOT` to a mounted-volume path such as `/data/pi-sessions`
228
- so Pi wrapper/native transcripts survive Fly deploys and restarts. Core-hosted
228
+ so Pi wrapper/native transcripts survive host deploys and restarts. Core-hosted
229
229
  apps that run `vercel-sandbox` with `BORING_AGENT_WORKSPACE_ROOT=/data/workspaces`
230
230
  default this to the sibling `/data/pi-sessions` path when the env var is absent.
231
231
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-agent",
3
- "version": "0.1.90",
3
+ "version": "0.1.92",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Pane-embeddable coding agent. Ships direct/local/vercel-sandbox execution modes behind one interface.",
@@ -32,6 +32,11 @@
32
32
  "import": "./dist/server/pi-session-readability.js",
33
33
  "default": "./dist/server/pi-session-readability.js"
34
34
  },
35
+ "./server/agent-host/testing/gatewayConformance": {
36
+ "types": "./dist/server/agent-host/testing/gatewayConformance.d.ts",
37
+ "import": "./dist/server/agent-host/testing/gatewayConformance.js",
38
+ "default": "./dist/server/agent-host/testing/gatewayConformance.js"
39
+ },
35
40
  "./server/worker": {
36
41
  "types": "./dist/server/worker/index.d.ts",
37
42
  "import": "./dist/server/worker/index.js",
@@ -71,27 +76,27 @@
71
76
  "@streamdown/math": "^1.0.2",
72
77
  "@streamdown/mermaid": "^1.0.2",
73
78
  "@tanstack/react-query": "^5.101.1",
74
- "ai": "^7.0.9",
79
+ "ai": "^7.0.31",
75
80
  "class-variance-authority": "^0.7.1",
76
81
  "clsx": "^2.1.1",
77
82
  "cmdk": "^1.1.1",
78
83
  "diff": "^9.0.0",
79
84
  "fastify": "^5.9.0",
80
- "lucide-react": "^1.21.0",
85
+ "lucide-react": "^1.25.0",
81
86
  "motion": "^12.42.0",
82
- "nanoid": "^5.1.16",
87
+ "nanoid": "^6.0.0",
83
88
  "shiki": "^4.3.0",
84
89
  "streamdown": "^2.5.0",
85
90
  "tailwind-merge": "^3.5.0",
86
91
  "use-stick-to-bottom": "^1.1.6",
87
92
  "yaml": "^2.9.0",
88
93
  "zod": "^3.25.76",
89
- "@hachej/boring-bash": "0.1.90",
90
- "@hachej/boring-ui-kit": "0.1.90",
91
- "@hachej/boring-sandbox": "0.1.90"
94
+ "@hachej/boring-bash": "0.1.92",
95
+ "@hachej/boring-sandbox": "0.1.92",
96
+ "@hachej/boring-ui-kit": "0.1.92"
92
97
  },
93
98
  "devDependencies": {
94
- "@vercel/sandbox": "2.3.0",
99
+ "@vercel/sandbox": "2.7.1",
95
100
  "@antithesishq/bombadil": "0.6.1",
96
101
  "@opentelemetry/api": "^1.9.1",
97
102
  "@tailwindcss/postcss": "^4.3.1",
@@ -100,20 +105,21 @@
100
105
  "@types/react-dom": "^19.1.3",
101
106
  "@vitejs/plugin-react": "^4.7.0",
102
107
  "ajv": "^8.20.0",
103
- "esbuild": "^0.25.12",
104
- "postcss": "^8.5.15",
108
+ "esbuild": "^0.28.1",
109
+ "postcss": "^8.5.19",
105
110
  "react": "^19.2.7",
106
111
  "react-dom": "^19.2.7",
107
112
  "tsup": "^8.4.0",
108
- "tsx": "^4.22.4",
113
+ "tsx": "^4.23.1",
109
114
  "typescript": "^6.0.3",
110
- "vite": "^8.1.2",
115
+ "vite": "^8.1.5",
111
116
  "vitest": "^4.1.9"
112
117
  },
113
118
  "peerDependencies": {
114
119
  "react": "^18.0.0 || ^19.0.0",
115
120
  "react-dom": "^18.0.0 || ^19.0.0",
116
- "tailwindcss": ">=3.4.0 || >=4.0.0"
121
+ "tailwindcss": ">=3.4.0 || >=4.0.0",
122
+ "vitest": "^4.1.9"
117
123
  },
118
124
  "peerDependenciesMeta": {
119
125
  "react": {
@@ -124,6 +130,9 @@
124
130
  },
125
131
  "tailwindcss": {
126
132
  "optional": true
133
+ },
134
+ "vitest": {
135
+ "optional": true
127
136
  }
128
137
  },
129
138
  "scripts": {
@@ -1,385 +0,0 @@
1
- import {
2
- AgentDefinitionErrorCode,
3
- AgentDeploymentErrorCode,
4
- ErrorCode
5
- } from "./chunk-4PKGVPJA.js";
6
-
7
- // src/shared/agent-definition.ts
8
- import { z as z2 } from "zod";
9
-
10
- // src/shared/digest.ts
11
- function toHex(bytes) {
12
- return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
13
- }
14
- async function digestBytes(bytes) {
15
- const hash = await globalThis.crypto.subtle.digest("SHA-256", new Uint8Array(bytes));
16
- return `sha256:${toHex(new Uint8Array(hash))}`;
17
- }
18
- async function sha256(value) {
19
- return digestBytes(new TextEncoder().encode(value));
20
- }
21
- async function sha256Bytes(bytes) {
22
- return digestBytes(bytes);
23
- }
24
- function canonicalStringify(value) {
25
- if (value === null || typeof value !== "object") {
26
- const encoded = JSON.stringify(value);
27
- if (encoded === void 0) throw new TypeError("Cannot canonicalize undefined");
28
- return encoded;
29
- }
30
- if (Array.isArray(value)) return `[${value.map(canonicalStringify).join(",")}]`;
31
- const record = value;
32
- return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalStringify(record[key])}`).join(",")}}`;
33
- }
34
-
35
- // src/shared/schema-issue.ts
36
- import { z } from "zod";
37
- function formatPath(path) {
38
- if (path.length === 0) return "<root>";
39
- return path.reduce(
40
- (result, part) => typeof part === "number" ? `${result}[${part}]` : result.length === 0 ? String(part) : `${result}.${String(part)}`,
41
- ""
42
- );
43
- }
44
- function mapZodIssues(issues, invalidCode, unsupportedCode) {
45
- return issues.flatMap((issue) => {
46
- if (issue.code === z.ZodIssueCode.unrecognized_keys) {
47
- const parent = formatPath(issue.path);
48
- return [...issue.keys].sort().map((key) => ({
49
- code: unsupportedCode,
50
- field: parent === "<root>" ? key : `${parent}.${key}`,
51
- message: `${key} is not supported by schema version 1`
52
- }));
53
- }
54
- const field = formatPath(issue.path);
55
- return [{
56
- code: invalidCode,
57
- field,
58
- message: field === "<root>" ? issue.message : `${field} ${issue.message}`
59
- }];
60
- });
61
- }
62
- var SchemaValidationError = class extends Error {
63
- code = ErrorCode.enum.CONFIG_INVALID;
64
- field;
65
- validationCode;
66
- constructor(issue) {
67
- super(issue.message);
68
- this.field = issue.field;
69
- this.validationCode = issue.code;
70
- }
71
- };
72
-
73
- // src/shared/agent-definition.ts
74
- var SHA256_DIGEST_RE = /^sha256:[a-f0-9]{64}$/;
75
- function hasWellFormedUnicode(value) {
76
- for (let index = 0; index < value.length; index += 1) {
77
- const code = value.charCodeAt(index);
78
- if (code >= 55296 && code <= 56319) {
79
- if (index + 1 >= value.length) return false;
80
- const next = value.charCodeAt(index + 1);
81
- if (next < 56320 || next > 57343) return false;
82
- index += 1;
83
- } else if (code >= 56320 && code <= 57343) {
84
- return false;
85
- }
86
- }
87
- return true;
88
- }
89
- function isOpaqueRef(value) {
90
- return hasWellFormedUnicode(value) && value.trim() === value && !/[\0-\x1f\x7f]/.test(value);
91
- }
92
- function isSafeAssetPath(value) {
93
- const segments = value.split("/");
94
- return hasWellFormedUnicode(value) && value.normalize("NFC") === value && !/[\0-\x1f\x7f]/.test(value) && !value.includes("\\") && !value.startsWith("/") && !/^[A-Za-z]:[\\/]/.test(value) && !value.startsWith("./") && segments.every((segment) => segment !== "" && segment !== "." && segment !== "..");
95
- }
96
- var OpaqueRefSchema = z2.string().min(1, "must be a non-empty reference").max(256, "must be at most 256 characters").refine(isOpaqueRef, "must be a non-empty reference");
97
- var ReferenceArraySchema = z2.array(OpaqueRefSchema).superRefine((refs, ctx) => {
98
- const seen = /* @__PURE__ */ new Set();
99
- refs.forEach((ref, index) => {
100
- if (seen.has(ref)) {
101
- ctx.addIssue({
102
- code: z2.ZodIssueCode.custom,
103
- path: [index],
104
- message: "must not contain duplicate references"
105
- });
106
- }
107
- seen.add(ref);
108
- });
109
- });
110
- var SafeAssetPathSchema = z2.string().min(1).refine(isSafeAssetPath, "must be a safe relative asset path");
111
- var Sha256DigestSchema = z2.string().regex(SHA256_DIGEST_RE, "must be a lowercase sha256 digest").transform((value) => value);
112
- var AgentDefinitionSchema = z2.object({
113
- schemaVersion: z2.literal(1),
114
- definitionId: OpaqueRefSchema,
115
- version: OpaqueRefSchema,
116
- label: z2.string().refine(hasWellFormedUnicode, "must contain well-formed Unicode").optional(),
117
- instructionsRef: SafeAssetPathSchema,
118
- capabilityRequirements: ReferenceArraySchema.optional(),
119
- toolRefs: ReferenceArraySchema.optional(),
120
- skillRefs: ReferenceArraySchema.optional(),
121
- mcpServerRefs: ReferenceArraySchema.optional()
122
- }).strict();
123
- var AgentDefinitionReferenceSchema = z2.object({
124
- definitionId: OpaqueRefSchema,
125
- version: OpaqueRefSchema,
126
- digest: Sha256DigestSchema
127
- }).strict();
128
- var AgentDeploymentSchema = z2.object({
129
- deploymentId: OpaqueRefSchema,
130
- version: OpaqueRefSchema,
131
- agentId: OpaqueRefSchema,
132
- definition: AgentDefinitionReferenceSchema
133
- }).strict();
134
- var AgentDefinitionDigestAssetSchema = z2.object({
135
- path: SafeAssetPathSchema,
136
- digest: Sha256DigestSchema,
137
- content: z2.string().refine(hasWellFormedUnicode, "must contain well-formed Unicode")
138
- }).strict();
139
- function validateAgentDefinition(raw) {
140
- const result = AgentDefinitionSchema.safeParse(raw);
141
- if (!result.success) {
142
- return {
143
- valid: false,
144
- issues: mapZodIssues(
145
- result.error.issues,
146
- AgentDefinitionErrorCode.enum.AGENT_DEFINITION_INVALID,
147
- AgentDefinitionErrorCode.enum.AGENT_DEFINITION_UNSUPPORTED_FIELD
148
- )
149
- };
150
- }
151
- return { valid: true, value: result.data };
152
- }
153
- function validateAgentDeployment(raw) {
154
- const result = AgentDeploymentSchema.safeParse(raw);
155
- if (!result.success) {
156
- return {
157
- valid: false,
158
- issues: mapZodIssues(
159
- result.error.issues,
160
- AgentDeploymentErrorCode.enum.AGENT_DEPLOYMENT_INVALID,
161
- AgentDeploymentErrorCode.enum.AGENT_DEPLOYMENT_UNSUPPORTED_FIELD
162
- )
163
- };
164
- }
165
- return { valid: true, value: result.data };
166
- }
167
- async function createAgentAssetDigest(content) {
168
- if (!hasWellFormedUnicode(content)) {
169
- throw new AgentDefinitionValidationError({
170
- code: AgentDefinitionErrorCode.enum.AGENT_DEFINITION_INVALID,
171
- field: "content",
172
- message: "content must contain well-formed Unicode"
173
- });
174
- }
175
- return sha256(content);
176
- }
177
- var AgentDefinitionValidationError = class extends SchemaValidationError {
178
- constructor(issue) {
179
- super(issue);
180
- this.name = "AgentDefinitionValidationError";
181
- }
182
- };
183
- var AgentDeploymentValidationError = class extends SchemaValidationError {
184
- constructor(issue) {
185
- super(issue);
186
- this.name = "AgentDeploymentValidationError";
187
- }
188
- };
189
- async function validatedDefinitionAssets(definition, assets) {
190
- if (!Array.isArray(assets)) {
191
- throw new AgentDefinitionValidationError({
192
- code: AgentDefinitionErrorCode.enum.AGENT_DEFINITION_INVALID,
193
- field: "assets",
194
- message: "assets must be an array"
195
- });
196
- }
197
- const canonicalAssets = [];
198
- for (const [index, input] of assets.entries()) {
199
- const result = AgentDefinitionDigestAssetSchema.safeParse(input);
200
- if (!result.success) {
201
- const issue = mapZodIssues(
202
- result.error.issues,
203
- AgentDefinitionErrorCode.enum.AGENT_DEFINITION_INVALID,
204
- AgentDefinitionErrorCode.enum.AGENT_DEFINITION_UNSUPPORTED_FIELD
205
- )[0];
206
- throw new AgentDefinitionValidationError({
207
- ...issue,
208
- field: `assets[${index}].${issue.field}`
209
- });
210
- }
211
- if (await createAgentAssetDigest(result.data.content) !== result.data.digest) {
212
- throw new AgentDefinitionValidationError({
213
- code: AgentDefinitionErrorCode.enum.AGENT_DEFINITION_INVALID,
214
- field: "assets.digest",
215
- message: `asset digest does not match UTF-8 content: ${result.data.path}`
216
- });
217
- }
218
- canonicalAssets.push(result.data);
219
- }
220
- canonicalAssets.sort(
221
- (left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0
222
- );
223
- for (let index = 1; index < canonicalAssets.length; index += 1) {
224
- if (canonicalAssets[index - 1].path === canonicalAssets[index].path) {
225
- throw new AgentDefinitionValidationError({
226
- code: AgentDefinitionErrorCode.enum.AGENT_DEFINITION_INVALID,
227
- field: "assets.path",
228
- message: `duplicate asset path: ${canonicalAssets[index].path}`
229
- });
230
- }
231
- }
232
- if (!canonicalAssets.some((asset) => asset.path === definition.instructionsRef)) {
233
- throw new AgentDefinitionValidationError({
234
- code: AgentDefinitionErrorCode.enum.AGENT_DEFINITION_INVALID,
235
- field: "instructionsRef",
236
- message: "instructionsRef must name an included verified asset"
237
- });
238
- }
239
- return canonicalAssets;
240
- }
241
- async function createAgentDefinitionDigest(input) {
242
- const validated = validateAgentDefinition(input.definition);
243
- if (!validated.valid) throw new AgentDefinitionValidationError(validated.issues[0]);
244
- const assets = await validatedDefinitionAssets(validated.value, input.assets);
245
- return sha256(canonicalStringify({ definition: validated.value, assets }));
246
- }
247
- async function createAgentDeploymentDigest(deployment) {
248
- const validated = validateAgentDeployment(deployment);
249
- if (!validated.valid) throw new AgentDeploymentValidationError(validated.issues[0]);
250
- return sha256(canonicalStringify(validated.value));
251
- }
252
-
253
- // src/shared/validateTool.ts
254
- function validateTool(tool) {
255
- if (typeof tool !== "object" || tool === null) return null;
256
- const t = tool;
257
- if (typeof t.name !== "string" || t.name.length === 0) return null;
258
- if (typeof t.description !== "string") return null;
259
- if (typeof t.parameters !== "object" || t.parameters === null) return null;
260
- if (typeof t.execute !== "function") return null;
261
- return t;
262
- }
263
-
264
- // src/shared/share-entry.ts
265
- import { z as z3 } from "zod";
266
- var nonEmptyString = z3.string().min(1);
267
- function isOpaqueLocatorId(value) {
268
- return value.trim() === value && !/[\0-\x1f\x7f]/.test(value) && !value.includes("/") && !value.includes("\\") && !value.includes(":") && value !== "." && value !== "..";
269
- }
270
- var OpaqueShareLocatorIdSchema = z3.string().min(1, "must be a non-empty opaque id").max(256, "must be at most 256 characters").refine(isOpaqueLocatorId, "must be an opaque platform-owned id (no path separators, no scheme, no traversal)");
271
- function mintShareEntryId() {
272
- return globalThis.crypto.randomUUID();
273
- }
274
- var ShareEntryProvenanceSchema = z3.object({
275
- producerPrincipalRef: nonEmptyString,
276
- createdAt: nonEmptyString
277
- }).strict();
278
- var ShareEntryV1Schema = z3.object({
279
- schemaVersion: z3.literal(1),
280
- id: OpaqueShareLocatorIdSchema,
281
- workspaceId: OpaqueShareLocatorIdSchema,
282
- path: nonEmptyString,
283
- provenance: ShareEntryProvenanceSchema
284
- }).strict();
285
- var ShareEntryErrorCode = z3.enum(["AR1_SHARE_NOT_FOUND", "AR1_SHARE_TOMBSTONED"]);
286
- var ShareEntryValidationCode = z3.enum(["SHARE_ENTRY_INPUT_INVALID"]);
287
- var ShareEntryValidationError = class extends Error {
288
- code = ErrorCode.enum.CONFIG_INVALID;
289
- field;
290
- constructor(issue) {
291
- super(issue.message);
292
- this.name = "ShareEntryValidationError";
293
- this.field = issue.field;
294
- }
295
- };
296
- function assertValidCreateInput(raw) {
297
- const result = CreateShareEntryInputSchema.safeParse(raw);
298
- if (result.success) return;
299
- const issue = result.error.issues[0];
300
- const field = formatPath(issue.path);
301
- throw new ShareEntryValidationError({
302
- code: ShareEntryValidationCode.enum.SHARE_ENTRY_INPUT_INVALID,
303
- field,
304
- message: field === "<root>" ? issue.message : `${field} ${issue.message}`
305
- });
306
- }
307
- var CreateShareEntryInputSchema = z3.object({
308
- workspaceId: OpaqueShareLocatorIdSchema,
309
- path: nonEmptyString,
310
- provenance: z3.object({
311
- producerPrincipalRef: nonEmptyString,
312
- createdAt: nonEmptyString.optional()
313
- }).strict()
314
- }).strict();
315
- var InMemoryShareEntryStore = class {
316
- entries = /* @__PURE__ */ new Map();
317
- async create(input) {
318
- assertValidCreateInput(input);
319
- const entry = {
320
- schemaVersion: 1,
321
- id: mintShareEntryId(),
322
- workspaceId: input.workspaceId,
323
- path: input.path,
324
- provenance: {
325
- producerPrincipalRef: input.provenance.producerPrincipalRef,
326
- createdAt: input.provenance.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
327
- }
328
- };
329
- this.entries.set(entry.id, entry);
330
- return entry;
331
- }
332
- async get(id) {
333
- return this.entries.get(id) ?? null;
334
- }
335
- async delete(id) {
336
- this.entries.delete(id);
337
- }
338
- async list(workspaceId) {
339
- return Array.from(this.entries.values()).filter((entry) => entry.workspaceId === workspaceId);
340
- }
341
- };
342
- async function resolveShareEntry(store, id, workspace) {
343
- const entry = await store.get(id);
344
- if (!entry) {
345
- return { status: "not_found", code: ShareEntryErrorCode.enum.AR1_SHARE_NOT_FOUND };
346
- }
347
- try {
348
- await workspace.stat(entry.path);
349
- } catch {
350
- return {
351
- status: "tombstoned",
352
- code: ShareEntryErrorCode.enum.AR1_SHARE_TOMBSTONED,
353
- tombstone: {
354
- id: entry.id,
355
- workspaceId: entry.workspaceId,
356
- provenance: entry.provenance
357
- }
358
- };
359
- }
360
- return { status: "ok", entry };
361
- }
362
-
363
- export {
364
- sha256Bytes,
365
- canonicalStringify,
366
- formatPath,
367
- SchemaValidationError,
368
- OpaqueRefSchema,
369
- Sha256DigestSchema,
370
- validateAgentDefinition,
371
- validateAgentDeployment,
372
- createAgentAssetDigest,
373
- AgentDefinitionValidationError,
374
- AgentDeploymentValidationError,
375
- createAgentDefinitionDigest,
376
- createAgentDeploymentDigest,
377
- validateTool,
378
- OpaqueShareLocatorIdSchema,
379
- ShareEntryProvenanceSchema,
380
- ShareEntryV1Schema,
381
- ShareEntryErrorCode,
382
- ShareEntryValidationError,
383
- InMemoryShareEntryStore,
384
- resolveShareEntry
385
- };