@zihanw/pi-forge 0.4.0-beta.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -1
- package/PUBLIC_API.md +3 -26
- package/README.md +90 -601
- package/README.zh-CN.md +86 -585
- package/SUBAGENT_ADAPTER_CONTRACT.md +3 -197
- package/dist/agent-profile.d.ts +24 -1
- package/dist/agent-profile.d.ts.map +1 -1
- package/dist/agent-profile.js +146 -36
- package/dist/agent-profile.js.map +1 -1
- package/dist/catalog.d.ts +27 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +59 -0
- package/dist/catalog.js.map +1 -0
- package/dist/forge-config.d.ts +106 -0
- package/dist/forge-config.d.ts.map +1 -1
- package/dist/forge-config.js +305 -18
- package/dist/forge-config.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -8
- package/dist/index.js.map +1 -1
- package/dist/lifecycle.d.ts +2 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +7 -4
- package/dist/lifecycle.js.map +1 -1
- package/dist/loader.d.ts +17 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +84 -25
- package/dist/loader.js.map +1 -1
- package/dist/preset-command.d.ts +1 -1
- package/dist/preset-command.d.ts.map +1 -1
- package/dist/preset-command.js +38 -10
- package/dist/preset-command.js.map +1 -1
- package/dist/profile-command.d.ts.map +1 -1
- package/dist/profile-command.js +46 -21
- package/dist/profile-command.js.map +1 -1
- package/dist/profile-service.d.ts +6 -3
- package/dist/profile-service.d.ts.map +1 -1
- package/dist/profile-service.js +55 -13
- package/dist/profile-service.js.map +1 -1
- package/dist/resource-identity.d.ts +33 -0
- package/dist/resource-identity.d.ts.map +1 -0
- package/dist/resource-identity.js +56 -0
- package/dist/resource-identity.js.map +1 -0
- package/dist/runtime/profile-runtime.d.ts.map +1 -1
- package/dist/runtime/profile-runtime.js +7 -2
- package/dist/runtime/profile-runtime.js.map +1 -1
- package/dist/runtime/prompt-stack-runtime.d.ts +1 -1
- package/dist/runtime/prompt-stack-runtime.d.ts.map +1 -1
- package/dist/runtime/prompt-stack-runtime.js +22 -12
- package/dist/runtime/prompt-stack-runtime.js.map +1 -1
- package/dist/runtime/subagent-runtime.d.ts +23 -8
- package/dist/runtime/subagent-runtime.d.ts.map +1 -1
- package/dist/runtime/subagent-runtime.js +296 -67
- package/dist/runtime/subagent-runtime.js.map +1 -1
- package/dist/storage.d.ts +11 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +51 -2
- package/dist/storage.js.map +1 -1
- package/dist/subagent/canonical.d.ts +19 -7
- package/dist/subagent/canonical.d.ts.map +1 -1
- package/dist/subagent/canonical.js +19 -47
- package/dist/subagent/canonical.js.map +1 -1
- package/dist/subagent/contract.d.ts +1 -2
- package/dist/subagent/contract.d.ts.map +1 -1
- package/dist/subagent/contract.js +1 -2
- package/dist/subagent/contract.js.map +1 -1
- package/dist/subagent/index.d.ts +4 -3
- package/dist/subagent/index.d.ts.map +1 -1
- package/dist/subagent/index.js +4 -3
- package/dist/subagent/index.js.map +1 -1
- package/dist/subagent/plan.d.ts +5 -1
- package/dist/subagent/plan.d.ts.map +1 -1
- package/dist/subagent/plan.js +29 -32
- package/dist/subagent/plan.js.map +1 -1
- package/dist/subagent/request.d.ts.map +1 -1
- package/dist/subagent/request.js +36 -4
- package/dist/subagent/request.js.map +1 -1
- package/dist/subagent/types.d.ts +66 -178
- package/dist/subagent/types.d.ts.map +1 -1
- package/dist/subagent/types.js +1 -1
- package/dist/subagent/types.js.map +1 -1
- package/dist/subagent/validation.d.ts +14 -14
- package/dist/subagent/validation.d.ts.map +1 -1
- package/dist/subagent/validation.js +52 -238
- package/dist/subagent/validation.js.map +1 -1
- package/dist/subagent-command.d.ts +2 -1
- package/dist/subagent-command.d.ts.map +1 -1
- package/dist/subagent-command.js +115 -19
- package/dist/subagent-command.js.map +1 -1
- package/dist/subagent-host.d.ts.map +1 -1
- package/dist/subagent-host.js +43 -16
- package/dist/subagent-host.js.map +1 -1
- package/dist/subagent-profile-tool.d.ts +25 -2
- package/dist/subagent-profile-tool.d.ts.map +1 -1
- package/dist/subagent-profile-tool.js +41 -9
- package/dist/subagent-profile-tool.js.map +1 -1
- package/dist/subagent-tool.d.ts +31 -4
- package/dist/subagent-tool.d.ts.map +1 -1
- package/dist/subagent-tool.js +311 -137
- package/dist/subagent-tool.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/web-editor/client-script.generated.d.ts +1 -1
- package/dist/web-editor/client-script.generated.d.ts.map +1 -1
- package/dist/web-editor/client-script.generated.js +1 -1
- package/dist/web-editor/client-script.generated.js.map +1 -1
- package/dist/web-editor/client-styles.d.ts +2 -0
- package/dist/web-editor/client-styles.d.ts.map +1 -0
- package/dist/web-editor/client-styles.generated.d.ts +2 -0
- package/dist/web-editor/client-styles.generated.d.ts.map +1 -0
- package/dist/web-editor/client-styles.generated.js +3 -0
- package/dist/web-editor/client-styles.generated.js.map +1 -0
- package/dist/web-editor/client-styles.js +2 -0
- package/dist/web-editor/client-styles.js.map +1 -0
- package/dist/web-editor/page.d.ts +2 -0
- package/dist/web-editor/page.d.ts.map +1 -1
- package/dist/web-editor/page.js +11 -73
- package/dist/web-editor/page.js.map +1 -1
- package/dist/web-editor/server.d.ts.map +1 -1
- package/dist/web-editor/server.js +176 -1
- package/dist/web-editor/server.js.map +1 -1
- package/dist/web-editor/styles.d.ts.map +1 -1
- package/dist/web-editor/styles.js +60 -3
- package/dist/web-editor/styles.js.map +1 -1
- package/dist/web-editor/types.d.ts +87 -0
- package/dist/web-editor/types.d.ts.map +1 -1
- package/dist/web-host.d.ts +13 -2
- package/dist/web-host.d.ts.map +1 -1
- package/dist/web-host.js +371 -27
- package/dist/web-host.js.map +1 -1
- package/docs/README.md +41 -0
- package/docs/concepts/agent-profiles.md +60 -0
- package/docs/concepts/prompt-stacks.md +95 -0
- package/docs/design/README.md +17 -0
- package/docs/design/roadmap-0.4-archive.md +216 -0
- package/docs/design/subagents/design-review.md +220 -0
- package/docs/design/subagents/interface-design.md +274 -0
- package/docs/design/subagents/sdk-spike-findings.md +117 -0
- package/docs/development/complexity-review.md +86 -0
- package/docs/development/release.md +31 -0
- package/docs/development/roadmap.md +36 -0
- package/docs/development/scoped-global-profiles-stacks.md +325 -0
- package/docs/development/setup.md +75 -0
- package/docs/getting-started.md +95 -0
- package/docs/guides/custom-macros-and-slots.md +68 -0
- package/docs/guides/debugging.md +39 -0
- package/docs/guides/delegation.md +107 -0
- package/docs/guides/sillytavern-import.md +47 -0
- package/docs/guides/use-cases.md +65 -0
- package/docs/guides/web-editor.md +75 -0
- package/docs/reference/commands.md +60 -0
- package/docs/reference/configuration.md +66 -0
- package/docs/reference/features.md +280 -0
- package/docs/reference/macros-and-slots.md +82 -0
- package/docs/reference/public-api.md +28 -0
- package/docs/reference/stack-schema.md +167 -0
- package/docs/reference/subagent-adapter.md +204 -0
- package/docs/zh-CN/README.md +37 -0
- package/docs/zh-CN/concepts/agent-profiles.md +46 -0
- package/docs/zh-CN/concepts/prompt-stacks.md +42 -0
- package/docs/zh-CN/getting-started.md +81 -0
- package/docs/zh-CN/guides/delegation.md +66 -0
- package/docs/zh-CN/guides/web-editor.md +45 -0
- package/docs/zh-CN/reference/commands.md +60 -0
- package/package.json +29 -15
- package/dist/subagent/backend-registry.d.ts +0 -75
- package/dist/subagent/backend-registry.d.ts.map +0 -1
- package/dist/subagent/backend-registry.js +0 -463
- package/dist/subagent/backend-registry.js.map +0 -1
- package/dist/subagent/diagnostics.d.ts +0 -3
- package/dist/subagent/diagnostics.d.ts.map +0 -1
- package/dist/subagent/diagnostics.js +0 -5
- package/dist/subagent/diagnostics.js.map +0 -1
- package/dist/subagent/pi-model-runtime.d.ts +0 -8
- package/dist/subagent/pi-model-runtime.d.ts.map +0 -1
- package/dist/subagent/pi-model-runtime.js +0 -22
- package/dist/subagent/pi-model-runtime.js.map +0 -1
- package/dist/subagent/pi-sdk-backend.d.ts +0 -23
- package/dist/subagent/pi-sdk-backend.d.ts.map +0 -1
- package/dist/subagent/pi-sdk-backend.js +0 -383
- package/dist/subagent/pi-sdk-backend.js.map +0 -1
- package/dist/subagent/pi-subprocess-backend.d.ts +0 -72
- package/dist/subagent/pi-subprocess-backend.d.ts.map +0 -1
- package/dist/subagent/pi-subprocess-backend.js +0 -756
- package/dist/subagent/pi-subprocess-backend.js.map +0 -1
- package/dist/subagent/subprocess-bridge.d.ts +0 -21
- package/dist/subagent/subprocess-bridge.d.ts.map +0 -1
- package/dist/subagent/subprocess-bridge.js +0 -87
- package/dist/subagent/subprocess-bridge.js.map +0 -1
- package/dist/subagent/subprocess-report.d.ts +0 -4
- package/dist/subagent/subprocess-report.d.ts.map +0 -1
- package/dist/subagent/subprocess-report.js +0 -55
- package/dist/subagent/subprocess-report.js.map +0 -1
- package/dist/subagent-contract.d.ts +0 -8
- package/dist/subagent-contract.d.ts.map +0 -1
- package/dist/subagent-contract.js +0 -8
- package/dist/subagent-contract.js.map +0 -1
- package/dist/web-editor/client/api.d.ts +0 -9
- package/dist/web-editor/client/api.d.ts.map +0 -1
- package/dist/web-editor/client/api.js +0 -26
- package/dist/web-editor/client/api.js.map +0 -1
- package/dist/web-editor/client/dom.d.ts +0 -13
- package/dist/web-editor/client/dom.d.ts.map +0 -1
- package/dist/web-editor/client/dom.js +0 -30
- package/dist/web-editor/client/dom.js.map +0 -1
- package/dist/web-editor/client/inspector.d.ts +0 -22
- package/dist/web-editor/client/inspector.d.ts.map +0 -1
- package/dist/web-editor/client/inspector.js +0 -226
- package/dist/web-editor/client/inspector.js.map +0 -1
- package/dist/web-editor/client/main.d.ts +0 -2
- package/dist/web-editor/client/main.d.ts.map +0 -1
- package/dist/web-editor/client/main.js +0 -1468
- package/dist/web-editor/client/main.js.map +0 -1
- package/dist/web-editor/client/policy-editor.d.ts +0 -16
- package/dist/web-editor/client/policy-editor.d.ts.map +0 -1
- package/dist/web-editor/client/policy-editor.js +0 -330
- package/dist/web-editor/client/policy-editor.js.map +0 -1
- package/dist/web-editor/client/regex-editor.d.ts +0 -19
- package/dist/web-editor/client/regex-editor.d.ts.map +0 -1
- package/dist/web-editor/client/regex-editor.js +0 -281
- package/dist/web-editor/client/regex-editor.js.map +0 -1
- package/dist/web-editor/client/types.d.ts +0 -60
- package/dist/web-editor/client/types.d.ts.map +0 -1
- package/dist/web-editor/client/types.js +0 -2
- package/dist/web-editor/client/types.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zihanw/pi-forge",
|
|
3
|
-
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Pi extension for prompt stacks, one-shot agent profiles, policy, import, and debugging.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"import": "./dist/*.js",
|
|
34
34
|
"default": "./dist/*.js"
|
|
35
35
|
},
|
|
36
|
+
"./src/web-editor/client/*": null,
|
|
36
37
|
"./examples/*": "./examples/*"
|
|
37
38
|
},
|
|
38
39
|
"keywords": [
|
|
@@ -51,9 +52,11 @@
|
|
|
51
52
|
]
|
|
52
53
|
},
|
|
53
54
|
"peerDependencies": {
|
|
54
|
-
"@earendil-works/pi-agent-core": "
|
|
55
|
-
"@earendil-works/pi-ai": "
|
|
56
|
-
"@earendil-works/pi-coding-agent": "
|
|
55
|
+
"@earendil-works/pi-agent-core": "*",
|
|
56
|
+
"@earendil-works/pi-ai": "*",
|
|
57
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
58
|
+
"@earendil-works/pi-tui": "*",
|
|
59
|
+
"typebox": "*"
|
|
57
60
|
},
|
|
58
61
|
"peerDependenciesMeta": {
|
|
59
62
|
"@earendil-works/pi-agent-core": {
|
|
@@ -64,36 +67,47 @@
|
|
|
64
67
|
},
|
|
65
68
|
"@earendil-works/pi-coding-agent": {
|
|
66
69
|
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"@earendil-works/pi-tui": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"typebox": {
|
|
75
|
+
"optional": true
|
|
67
76
|
}
|
|
68
77
|
},
|
|
69
78
|
"dependencies": {
|
|
70
|
-
"@
|
|
71
|
-
"typebox": "^1.1.38"
|
|
79
|
+
"@zihanw/pi-subagent-runtime": "0.1.0-beta.2"
|
|
72
80
|
},
|
|
73
81
|
"devDependencies": {
|
|
74
|
-
"@earendil-works/pi-agent-core": "0.
|
|
75
|
-
"@earendil-works/pi-ai": "0.
|
|
76
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
82
|
+
"@earendil-works/pi-agent-core": "0.83.0",
|
|
83
|
+
"@earendil-works/pi-ai": "0.83.0",
|
|
84
|
+
"@earendil-works/pi-coding-agent": "0.83.0",
|
|
85
|
+
"@earendil-works/pi-tui": "0.83.0",
|
|
77
86
|
"@types/node": "22.20.1",
|
|
78
|
-
"
|
|
87
|
+
"@vitejs/plugin-vue": "6.0.8",
|
|
79
88
|
"playwright-core": "1.61.1",
|
|
80
|
-
"
|
|
89
|
+
"typebox": "1.3.7",
|
|
90
|
+
"typescript": "6.0.3",
|
|
91
|
+
"vite": "8.1.5",
|
|
92
|
+
"vue": "3.5.40",
|
|
93
|
+
"vue-tsc": "3.3.8"
|
|
81
94
|
},
|
|
82
95
|
"scripts": {
|
|
83
96
|
"build": "npm run build:client && tsc -p tsconfig.build.json",
|
|
84
97
|
"build:client": "node scripts/build-web-client.mjs",
|
|
85
98
|
"check:client": "node scripts/build-web-client.mjs --check",
|
|
99
|
+
"check:docs": "node scripts/check-docs.mjs",
|
|
86
100
|
"check:dist": "node scripts/check-dist.mjs",
|
|
87
101
|
"check:package": "node scripts/check-package.mjs",
|
|
88
102
|
"prepack": "npm run build",
|
|
89
|
-
"typecheck": "tsc --noEmit",
|
|
103
|
+
"typecheck": "tsc --noEmit && vue-tsc --noEmit -p tsconfig.web.json",
|
|
90
104
|
"test": "node --test tests/*.test.ts",
|
|
91
|
-
"test:browser": "node --test tests-browser/*.test.ts",
|
|
92
|
-
"
|
|
93
|
-
"verify": "npm test && npm run test:browser && npm run typecheck && npm run check:client && npm run check:dist && npm run check:package"
|
|
105
|
+
"test:browser": "node --test --test-concurrency=1 tests-browser/*.test.ts",
|
|
106
|
+
"verify": "npm test && npm run test:browser && npm run typecheck && npm run check:client && npm run check:docs && npm run check:dist && npm run check:package"
|
|
94
107
|
},
|
|
95
108
|
"files": [
|
|
96
109
|
"dist/",
|
|
110
|
+
"docs/",
|
|
97
111
|
"examples/",
|
|
98
112
|
"README.md",
|
|
99
113
|
"README.zh-CN.md",
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { type AgentExecutionPlan, type AgentProfileSnapshot, type AgentRequest, type AgentResponse, type BackendPreflightResult, type SubagentBackendDescriptor, type SubagentHostPlanPreparer, type SubagentPreparationBaseInput, type SubagentPreparationResult, type SubagentPreparationRuntime, type SubagentTraceReference } from "./contract.ts";
|
|
2
|
-
type WithoutTrace<T> = T extends AgentResponse ? Omit<T, "trace"> : never;
|
|
3
|
-
export interface SubagentBackendTraceResult {
|
|
4
|
-
id: string;
|
|
5
|
-
expiresAt?: string;
|
|
6
|
-
}
|
|
7
|
-
export type SubagentBackendExecutionResult = WithoutTrace<AgentResponse> & {
|
|
8
|
-
trace?: SubagentBackendTraceResult;
|
|
9
|
-
};
|
|
10
|
-
export interface SubagentBackendPreflightInput {
|
|
11
|
-
request: AgentRequest;
|
|
12
|
-
snapshot: AgentProfileSnapshot;
|
|
13
|
-
signal?: AbortSignal;
|
|
14
|
-
}
|
|
15
|
-
export interface SubagentBackendPreparationContext {
|
|
16
|
-
signal?: AbortSignal;
|
|
17
|
-
prepare(runtime: SubagentPreparationRuntime): Promise<SubagentPreparationResult>;
|
|
18
|
-
}
|
|
19
|
-
export interface SubagentBackendExecutionContext {
|
|
20
|
-
signal: AbortSignal;
|
|
21
|
-
onUpdate?: (update: SubagentBackendExecutionUpdate) => void;
|
|
22
|
-
}
|
|
23
|
-
export interface SubagentBackendExecutionUpdate {
|
|
24
|
-
phase: "starting" | "message" | "tool-result" | "finishing";
|
|
25
|
-
message: string;
|
|
26
|
-
details?: unknown;
|
|
27
|
-
}
|
|
28
|
-
export interface SubagentBackendCancelInput {
|
|
29
|
-
runId: string;
|
|
30
|
-
reason: string;
|
|
31
|
-
}
|
|
32
|
-
export interface SubagentBackendTraceInput {
|
|
33
|
-
traceId: string;
|
|
34
|
-
signal?: AbortSignal;
|
|
35
|
-
}
|
|
36
|
-
export interface SubagentBackend {
|
|
37
|
-
readonly descriptor: SubagentBackendDescriptor;
|
|
38
|
-
preflight(input: SubagentBackendPreflightInput): Promise<BackendPreflightResult> | BackendPreflightResult;
|
|
39
|
-
prepare?(input: SubagentPreparationBaseInput, context: SubagentBackendPreparationContext): Promise<SubagentPreparationResult> | SubagentPreparationResult;
|
|
40
|
-
execute(plan: AgentExecutionPlan, context: SubagentBackendExecutionContext): Promise<SubagentBackendExecutionResult> | SubagentBackendExecutionResult;
|
|
41
|
-
discard?(preflightId: string): Promise<boolean> | boolean;
|
|
42
|
-
cancel?(input: SubagentBackendCancelInput): Promise<void> | void;
|
|
43
|
-
inspectTrace?(input: SubagentBackendTraceInput): Promise<unknown> | unknown;
|
|
44
|
-
}
|
|
45
|
-
export interface SubagentBackendRegistryOptions {
|
|
46
|
-
idFactory?: (kind: "preflight" | "trace") => string;
|
|
47
|
-
now?: () => number;
|
|
48
|
-
}
|
|
49
|
-
export interface SubagentExecutionOptions {
|
|
50
|
-
authorizationScope: string;
|
|
51
|
-
signal?: AbortSignal;
|
|
52
|
-
onUpdate?: (update: SubagentBackendExecutionUpdate) => void;
|
|
53
|
-
}
|
|
54
|
-
export declare class SubagentBackendRegistryError extends Error {
|
|
55
|
-
readonly code: string;
|
|
56
|
-
constructor(code: string, message: string);
|
|
57
|
-
}
|
|
58
|
-
export declare class SubagentBackendRegistry {
|
|
59
|
-
#private;
|
|
60
|
-
constructor(options?: SubagentBackendRegistryOptions);
|
|
61
|
-
get size(): number;
|
|
62
|
-
register(backend: SubagentBackend): () => boolean;
|
|
63
|
-
unregister(backendId: string): boolean;
|
|
64
|
-
descriptors(): SubagentBackendDescriptor[];
|
|
65
|
-
forgetPreflight(preflightId: string): boolean;
|
|
66
|
-
discard(preflightId: string): Promise<boolean>;
|
|
67
|
-
preflight(backendId: string, request: AgentRequest, snapshot: AgentProfileSnapshot, signal?: AbortSignal): Promise<BackendPreflightResult>;
|
|
68
|
-
prepare(backendId: string, input: SubagentPreparationBaseInput, hostPreparer: SubagentHostPlanPreparer, signal?: AbortSignal): Promise<SubagentPreparationResult>;
|
|
69
|
-
execute(plan: AgentExecutionPlan, options: SubagentExecutionOptions): Promise<AgentResponse>;
|
|
70
|
-
cancel(runId: string, reason?: string): Promise<boolean>;
|
|
71
|
-
inspectTrace(reference: SubagentTraceReference, authorizationScope: string, signal?: AbortSignal): Promise<unknown>;
|
|
72
|
-
forgetTrace(handle: string): boolean;
|
|
73
|
-
}
|
|
74
|
-
export {};
|
|
75
|
-
//# sourceMappingURL=backend-registry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"backend-registry.d.ts","sourceRoot":"","sources":["../../src/subagent/backend-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,463 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { SUBAGENT_CONTRACT_VERSION, canonicalSubagentJson, hasSubagentErrors, validateAgentExecutionPlan, validateAgentProfileSnapshot, validateAgentRequest, validateAgentResponse, validateBackendPreflight, validatePreparationRuntime, validateSubagentTraceReference, } from "./contract.js";
|
|
3
|
-
export class SubagentBackendRegistryError extends Error {
|
|
4
|
-
code;
|
|
5
|
-
constructor(code, message) {
|
|
6
|
-
super(message);
|
|
7
|
-
this.name = "SubagentBackendRegistryError";
|
|
8
|
-
this.code = code;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
export class SubagentBackendRegistry {
|
|
12
|
-
#backends = new Map();
|
|
13
|
-
#preflights = new Map();
|
|
14
|
-
#traces = new Map();
|
|
15
|
-
#activeRuns = new Map();
|
|
16
|
-
#idFactory;
|
|
17
|
-
#now;
|
|
18
|
-
constructor(options = {}) {
|
|
19
|
-
this.#idFactory = options.idFactory ?? ((kind) => `${kind}:${randomUUID()}`);
|
|
20
|
-
this.#now = options.now ?? Date.now;
|
|
21
|
-
}
|
|
22
|
-
get size() {
|
|
23
|
-
return this.#backends.size;
|
|
24
|
-
}
|
|
25
|
-
register(backend) {
|
|
26
|
-
validateRegistration(backend);
|
|
27
|
-
const id = backend.descriptor.id;
|
|
28
|
-
if (this.#backends.has(id))
|
|
29
|
-
throw new SubagentBackendRegistryError("backend.duplicate", `A subagent backend is already registered as ${id}.`);
|
|
30
|
-
this.#backends.set(id, backend);
|
|
31
|
-
return () => this.unregister(id);
|
|
32
|
-
}
|
|
33
|
-
unregister(backendId) {
|
|
34
|
-
if ([...this.#activeRuns.values()].some((run) => run.backend.descriptor.id === backendId)) {
|
|
35
|
-
throw new SubagentBackendRegistryError("backend.active", `Cannot unregister backend ${backendId} while one of its runs is active or draining.`);
|
|
36
|
-
}
|
|
37
|
-
const removed = this.#backends.delete(backendId);
|
|
38
|
-
if (!removed)
|
|
39
|
-
return false;
|
|
40
|
-
for (const [id, record] of this.#preflights)
|
|
41
|
-
if (record.backendId === backendId)
|
|
42
|
-
this.#preflights.delete(id);
|
|
43
|
-
for (const [handle, record] of this.#traces)
|
|
44
|
-
if (record.reference.backendId === backendId)
|
|
45
|
-
this.#traces.delete(handle);
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
descriptors() {
|
|
49
|
-
return [...this.#backends.values()]
|
|
50
|
-
.map((backend) => structuredClone(backend.descriptor))
|
|
51
|
-
.sort((left, right) => left.id.localeCompare(right.id));
|
|
52
|
-
}
|
|
53
|
-
forgetPreflight(preflightId) {
|
|
54
|
-
if ([...this.#activeRuns.values()].some((run) => run.plan.preflightId === preflightId)) {
|
|
55
|
-
throw new SubagentBackendRegistryError("preflight.active", `Cannot forget preflight ${preflightId} while its run is active or draining.`);
|
|
56
|
-
}
|
|
57
|
-
return this.#preflights.delete(preflightId);
|
|
58
|
-
}
|
|
59
|
-
async discard(preflightId) {
|
|
60
|
-
if ([...this.#activeRuns.values()].some((run) => run.plan.preflightId === preflightId)) {
|
|
61
|
-
throw new SubagentBackendRegistryError("preflight.active", `Cannot discard preflight ${preflightId} while its run is active or draining.`);
|
|
62
|
-
}
|
|
63
|
-
const record = this.#preflights.get(preflightId);
|
|
64
|
-
if (!record)
|
|
65
|
-
return false;
|
|
66
|
-
const backend = this.#requireBackend(record.backendId);
|
|
67
|
-
await backend.discard?.(preflightId);
|
|
68
|
-
this.#preflights.delete(preflightId);
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
async preflight(backendId, request, snapshot, signal) {
|
|
72
|
-
const backend = this.#requireBackend(backendId);
|
|
73
|
-
const inputDiagnostics = [...validateAgentRequest(request), ...validateAgentProfileSnapshot(snapshot)];
|
|
74
|
-
if (hasSubagentErrors(inputDiagnostics))
|
|
75
|
-
return this.#rejectedPreflight(backend, inputDiagnostics);
|
|
76
|
-
if (signal?.aborted)
|
|
77
|
-
return this.#rejectedPreflight(backend, [diagnostic("preflight.cancelled", "Preflight was cancelled before backend discovery.")]);
|
|
78
|
-
let result;
|
|
79
|
-
try {
|
|
80
|
-
result = await backend.preflight({ request: structuredClone(request), snapshot: structuredClone(snapshot), signal });
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
return this.#rejectedPreflight(backend, [diagnostic("preflight.backend-error", errorMessage(error))]);
|
|
84
|
-
}
|
|
85
|
-
const diagnostics = validateBackendPreflight(result, request, snapshot);
|
|
86
|
-
if (result.backend?.id !== backendId || !sameDescriptor(result.backend, backend.descriptor)) {
|
|
87
|
-
diagnostics.push(diagnostic("preflight.backend-identity", "Backend preflight descriptor does not match its registered identity.", "backend"));
|
|
88
|
-
}
|
|
89
|
-
if (this.#preflights.has(result.preflightId))
|
|
90
|
-
diagnostics.push(diagnostic("preflight.duplicate-id", "Backend reused an existing preflightId.", "preflightId"));
|
|
91
|
-
if (hasSubagentErrors(diagnostics))
|
|
92
|
-
return this.#rejectedPreflight(backend, diagnostics, validOpaqueId(result.preflightId) ? result.preflightId : undefined);
|
|
93
|
-
if (result.status === "accepted") {
|
|
94
|
-
this.#preflights.set(result.preflightId, {
|
|
95
|
-
backendId,
|
|
96
|
-
requestId: request.requestId,
|
|
97
|
-
profileFingerprint: snapshot.profileFingerprint,
|
|
98
|
-
requestCanonical: canonicalSubagentJson(request),
|
|
99
|
-
snapshotCanonical: canonicalSubagentJson(snapshot),
|
|
100
|
-
preflightCanonical: canonicalSubagentJson(result),
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
return structuredClone(result);
|
|
104
|
-
}
|
|
105
|
-
async prepare(backendId, input, hostPreparer, signal) {
|
|
106
|
-
const backend = this.#requireBackend(backendId);
|
|
107
|
-
this.#assertPreflightBinding(input.preflight, input.request, input.snapshot);
|
|
108
|
-
const record = this.#preflights.get(input.preflight.preflightId);
|
|
109
|
-
if (record.preparation)
|
|
110
|
-
throw new SubagentBackendRegistryError("preparation.already-bound", "Accepted preflight already has a host preparation bound to it.");
|
|
111
|
-
if (signal?.aborted)
|
|
112
|
-
throw new SubagentBackendRegistryError("preparation.cancelled", "Preparation was cancelled before it started.");
|
|
113
|
-
const fidelity = backend.descriptor.capabilities.promptRuntimeFidelity;
|
|
114
|
-
if (fidelity === "partial")
|
|
115
|
-
throw new SubagentBackendRegistryError("preparation.partial", "A partial prompt runtime cannot prepare an execution plan.");
|
|
116
|
-
let hostResult;
|
|
117
|
-
const prepare = async (runtime) => {
|
|
118
|
-
if (hostResult)
|
|
119
|
-
throw new SubagentBackendRegistryError("preparation.duplicate", "Backend attempted to prepare the same request more than once.");
|
|
120
|
-
const runtimeDiagnostics = [];
|
|
121
|
-
validatePreparationRuntime(runtime, "runtime", runtimeDiagnostics, fidelity);
|
|
122
|
-
if (runtime.model.provider !== input.preflight.model.provider || runtime.model.id !== input.preflight.model.id) {
|
|
123
|
-
runtimeDiagnostics.push(diagnostic("preparation.runtime-model", "Prompt runtime model does not match the accepted preflight model.", "runtime.model"));
|
|
124
|
-
}
|
|
125
|
-
if (hasSubagentErrors(runtimeDiagnostics))
|
|
126
|
-
throw new SubagentBackendRegistryError("preparation.invalid-runtime", summarizeDiagnostics(runtimeDiagnostics));
|
|
127
|
-
const exactInput = { ...structuredClone(input), runtime: structuredClone(runtime) };
|
|
128
|
-
hostResult = {
|
|
129
|
-
runtime: structuredClone(runtime),
|
|
130
|
-
preparation: structuredClone(await hostPreparer(exactInput)),
|
|
131
|
-
};
|
|
132
|
-
return structuredClone(hostResult);
|
|
133
|
-
};
|
|
134
|
-
if (fidelity === "exact-preflight") {
|
|
135
|
-
if (!input.preflight.promptRuntime)
|
|
136
|
-
throw new SubagentBackendRegistryError("preparation.runtime-missing", "Exact-preflight backend did not provide a prompt runtime receipt.");
|
|
137
|
-
const result = await prepare(input.preflight.promptRuntime);
|
|
138
|
-
this.#bindPreparation(record, result);
|
|
139
|
-
return result;
|
|
140
|
-
}
|
|
141
|
-
if (!backend.prepare)
|
|
142
|
-
throw new SubagentBackendRegistryError("preparation.unsupported", `Backend ${backendId} advertises backend-assisted preparation but has no prepare method.`);
|
|
143
|
-
const backendResult = structuredClone(await backend.prepare(structuredClone(input), { signal, prepare }));
|
|
144
|
-
if (!hostResult)
|
|
145
|
-
throw new SubagentBackendRegistryError("preparation.host-bypass", `Backend ${backendId} returned without invoking host preparation.`);
|
|
146
|
-
if (canonicalSubagentJson(backendResult) !== canonicalSubagentJson(hostResult)) {
|
|
147
|
-
throw new SubagentBackendRegistryError("preparation.host-mismatch", `Backend ${backendId} altered the host preparation result.`);
|
|
148
|
-
}
|
|
149
|
-
this.#bindPreparation(record, backendResult);
|
|
150
|
-
return backendResult;
|
|
151
|
-
}
|
|
152
|
-
async execute(plan, options) {
|
|
153
|
-
const backend = this.#requireBackend(plan.backendId);
|
|
154
|
-
const planDiagnostics = validateAgentExecutionPlan(plan);
|
|
155
|
-
if (hasSubagentErrors(planDiagnostics))
|
|
156
|
-
throw new SubagentBackendRegistryError("execution.invalid-plan", summarizeDiagnostics(planDiagnostics));
|
|
157
|
-
const preflightRecord = this.#preflights.get(plan.preflightId);
|
|
158
|
-
if (!preflightRecord
|
|
159
|
-
|| preflightRecord.backendId !== plan.backendId
|
|
160
|
-
|| preflightRecord.requestId !== plan.requestId
|
|
161
|
-
|| preflightRecord.profileFingerprint !== plan.profile.profileFingerprint
|
|
162
|
-
|| preflightRecord.snapshotCanonical !== canonicalSubagentJson(plan.profile)
|
|
163
|
-
|| preflightRecord.preflightCanonical !== canonicalSubagentJson(plan.preflight)) {
|
|
164
|
-
throw new SubagentBackendRegistryError("execution.unbound-preflight", "Execution plan is not bound to an accepted registry preflight.");
|
|
165
|
-
}
|
|
166
|
-
const preparation = preflightRecord.preparation;
|
|
167
|
-
if (!preparation
|
|
168
|
-
|| preparation.promptRuntimeFingerprint !== plan.promptRuntimeFingerprint
|
|
169
|
-
|| preparation.systemPrompt !== plan.systemPrompt
|
|
170
|
-
|| preparation.messagesCanonical !== canonicalSubagentJson(plan.messages)
|
|
171
|
-
|| preparation.effectiveToolIdsCanonical !== canonicalSubagentJson(plan.effectiveToolIds)
|
|
172
|
-
|| preparation.contextBudgetCanonical !== canonicalSubagentJson(plan.contextBudget ?? null)) {
|
|
173
|
-
throw new SubagentBackendRegistryError("execution.unbound-preparation", "Execution plan does not match the exact host preparation bound to its preflight.");
|
|
174
|
-
}
|
|
175
|
-
if (!validNamespace(options.authorizationScope))
|
|
176
|
-
throw new SubagentBackendRegistryError("execution.authorization", "authorizationScope must be a normalized namespace.");
|
|
177
|
-
if (this.#activeRuns.has(plan.runId))
|
|
178
|
-
throw new SubagentBackendRegistryError("execution.duplicate-run", `Run ${plan.runId} is already active.`);
|
|
179
|
-
const startedAt = this.#now();
|
|
180
|
-
const controller = new AbortController();
|
|
181
|
-
let resolveTerminal;
|
|
182
|
-
const terminal = new Promise((resolve) => { resolveTerminal = resolve; });
|
|
183
|
-
const active = { backend, plan, controller, settled: false, terminal };
|
|
184
|
-
this.#activeRuns.set(plan.runId, active);
|
|
185
|
-
let timeout;
|
|
186
|
-
let externalAbort;
|
|
187
|
-
const settle = (response) => {
|
|
188
|
-
if (active.settled)
|
|
189
|
-
return false;
|
|
190
|
-
active.settled = true;
|
|
191
|
-
if (timeout)
|
|
192
|
-
clearTimeout(timeout);
|
|
193
|
-
if (externalAbort && options.signal)
|
|
194
|
-
options.signal.removeEventListener("abort", externalAbort);
|
|
195
|
-
resolveTerminal(response);
|
|
196
|
-
return true;
|
|
197
|
-
};
|
|
198
|
-
const cancelRun = (kind, reason) => {
|
|
199
|
-
if (active.settled || active.cancellation)
|
|
200
|
-
return;
|
|
201
|
-
active.cancellation = { kind, reason };
|
|
202
|
-
controller.abort(reason);
|
|
203
|
-
active.cancelPromise = Promise.resolve(backend.cancel?.({ runId: plan.runId, reason })).catch(() => undefined);
|
|
204
|
-
};
|
|
205
|
-
active.requestCancellation = cancelRun;
|
|
206
|
-
const settleCancellation = () => {
|
|
207
|
-
const cancellation = active.cancellation;
|
|
208
|
-
if (!cancellation)
|
|
209
|
-
return false;
|
|
210
|
-
const durationMs = elapsed(this.#now(), startedAt);
|
|
211
|
-
return settle(cancellation.kind === "timed-out"
|
|
212
|
-
? { ...responseCommon(plan, durationMs), status: "timed-out", reason: cancellation.reason, enforcedTimeoutMs: plan.limits.timeoutMs.value }
|
|
213
|
-
: { ...responseCommon(plan, durationMs), status: "cancelled", reason: cancellation.reason });
|
|
214
|
-
};
|
|
215
|
-
if (options.signal) {
|
|
216
|
-
externalAbort = () => cancelRun("cancelled", abortReason(options.signal, "user"));
|
|
217
|
-
if (options.signal.aborted)
|
|
218
|
-
externalAbort();
|
|
219
|
-
else
|
|
220
|
-
options.signal.addEventListener("abort", externalAbort, { once: true });
|
|
221
|
-
}
|
|
222
|
-
const timeoutLimit = plan.limits.timeoutMs;
|
|
223
|
-
if (!active.settled && timeoutLimit?.enforcement === "host-abort") {
|
|
224
|
-
timeout = setTimeout(() => cancelRun("timed-out", "host timeout"), timeoutLimit.value);
|
|
225
|
-
}
|
|
226
|
-
const backendExecution = Promise.resolve()
|
|
227
|
-
.then(async () => {
|
|
228
|
-
if (active.cancellation) {
|
|
229
|
-
await active.cancelPromise;
|
|
230
|
-
await backend.discard?.(plan.preflightId);
|
|
231
|
-
settleCancellation();
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
const result = await backend.execute(structuredClone(plan), {
|
|
235
|
-
signal: controller.signal,
|
|
236
|
-
onUpdate: options.onUpdate
|
|
237
|
-
? (update) => {
|
|
238
|
-
try {
|
|
239
|
-
options.onUpdate?.(structuredClone(update));
|
|
240
|
-
}
|
|
241
|
-
catch {
|
|
242
|
-
// UI/reporting callbacks cannot fail backend execution.
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
: undefined,
|
|
246
|
-
});
|
|
247
|
-
if (active.cancellation) {
|
|
248
|
-
await active.cancelPromise;
|
|
249
|
-
settleCancellation();
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
settle(this.#normalizeBackendResponse(result, plan, options.authorizationScope, elapsed(this.#now(), startedAt)));
|
|
253
|
-
})
|
|
254
|
-
.catch(async (error) => {
|
|
255
|
-
if (active.cancellation) {
|
|
256
|
-
await active.cancelPromise;
|
|
257
|
-
settleCancellation();
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
settle({
|
|
261
|
-
...responseCommon(plan, elapsed(this.#now(), startedAt)),
|
|
262
|
-
status: "failed",
|
|
263
|
-
error: { code: "backend-execution", message: errorMessage(error), retryable: false },
|
|
264
|
-
});
|
|
265
|
-
})
|
|
266
|
-
.finally(() => {
|
|
267
|
-
if (timeout)
|
|
268
|
-
clearTimeout(timeout);
|
|
269
|
-
if (externalAbort && options.signal)
|
|
270
|
-
options.signal.removeEventListener("abort", externalAbort);
|
|
271
|
-
if (this.#activeRuns.get(plan.runId) === active)
|
|
272
|
-
this.#activeRuns.delete(plan.runId);
|
|
273
|
-
this.#preflights.delete(plan.preflightId);
|
|
274
|
-
});
|
|
275
|
-
void backendExecution;
|
|
276
|
-
return terminal;
|
|
277
|
-
}
|
|
278
|
-
async cancel(runId, reason = "user") {
|
|
279
|
-
const active = this.#activeRuns.get(runId);
|
|
280
|
-
if (!active || active.settled || active.cancellation)
|
|
281
|
-
return false;
|
|
282
|
-
active.requestCancellation?.("cancelled", reason);
|
|
283
|
-
await active.terminal;
|
|
284
|
-
return true;
|
|
285
|
-
}
|
|
286
|
-
async inspectTrace(reference, authorizationScope, signal) {
|
|
287
|
-
const diagnostics = validateSubagentTraceReference(reference);
|
|
288
|
-
if (hasSubagentErrors(diagnostics))
|
|
289
|
-
throw new SubagentBackendRegistryError("trace.invalid", summarizeDiagnostics(diagnostics));
|
|
290
|
-
const record = this.#traces.get(reference.handle);
|
|
291
|
-
if (!record || !sameTraceReference(record.reference, reference))
|
|
292
|
-
throw new SubagentBackendRegistryError("trace.unknown", "Trace handle is unknown or does not match its registered route.");
|
|
293
|
-
if (reference.authorizationScope !== authorizationScope)
|
|
294
|
-
throw new SubagentBackendRegistryError("trace.forbidden", "Trace authorization scope does not match the caller.");
|
|
295
|
-
if (reference.expiresAt && Date.parse(reference.expiresAt) <= this.#now()) {
|
|
296
|
-
this.#traces.delete(reference.handle);
|
|
297
|
-
throw new SubagentBackendRegistryError("trace.expired", "Trace handle has expired.");
|
|
298
|
-
}
|
|
299
|
-
const backend = this.#requireBackend(reference.backendId);
|
|
300
|
-
if (!backend.descriptor.capabilities.traceInspection || !backend.inspectTrace) {
|
|
301
|
-
throw new SubagentBackendRegistryError("trace.unsupported", `Backend ${reference.backendId} does not support trace inspection.`);
|
|
302
|
-
}
|
|
303
|
-
return backend.inspectTrace({ traceId: record.backendTraceId, signal });
|
|
304
|
-
}
|
|
305
|
-
forgetTrace(handle) {
|
|
306
|
-
return this.#traces.delete(handle);
|
|
307
|
-
}
|
|
308
|
-
#requireBackend(backendId) {
|
|
309
|
-
const backend = this.#backends.get(backendId);
|
|
310
|
-
if (!backend)
|
|
311
|
-
throw new SubagentBackendRegistryError("backend.unknown", `Unknown subagent backend: ${backendId}`);
|
|
312
|
-
return backend;
|
|
313
|
-
}
|
|
314
|
-
#assertPreflightBinding(preflight, request, snapshot) {
|
|
315
|
-
const record = this.#preflights.get(preflight.preflightId);
|
|
316
|
-
if (!record
|
|
317
|
-
|| record.backendId !== preflight.backend.id
|
|
318
|
-
|| record.requestId !== request.requestId
|
|
319
|
-
|| record.profileFingerprint !== snapshot.profileFingerprint
|
|
320
|
-
|| record.requestCanonical !== canonicalSubagentJson(request)
|
|
321
|
-
|| record.snapshotCanonical !== canonicalSubagentJson(snapshot)
|
|
322
|
-
|| record.preflightCanonical !== canonicalSubagentJson(preflight)) {
|
|
323
|
-
throw new SubagentBackendRegistryError("preparation.unbound-preflight", "Preparation input is not bound to an accepted registry preflight.");
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
#bindPreparation(record, result) {
|
|
327
|
-
record.preparation = {
|
|
328
|
-
promptRuntimeFingerprint: result.runtime.promptRuntimeFingerprint,
|
|
329
|
-
systemPrompt: result.preparation.systemPrompt,
|
|
330
|
-
messagesCanonical: canonicalSubagentJson(result.preparation.messages),
|
|
331
|
-
effectiveToolIdsCanonical: canonicalSubagentJson(result.preparation.toolNegotiation.effectiveToolIds),
|
|
332
|
-
contextBudgetCanonical: canonicalSubagentJson(result.preparation.contextBudget ?? null),
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
#rejectedPreflight(backend, diagnostics, preflightId) {
|
|
336
|
-
return {
|
|
337
|
-
status: "rejected",
|
|
338
|
-
preflightId: preflightId ?? this.#idFactory("preflight"),
|
|
339
|
-
backend: structuredClone(backend.descriptor),
|
|
340
|
-
diagnostics: ensureErrorDiagnostics(diagnostics),
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
#normalizeBackendResponse(result, plan, authorizationScope, durationMs) {
|
|
344
|
-
let trace;
|
|
345
|
-
if (result.trace) {
|
|
346
|
-
if (!validOpaqueId(result.trace.id) || (result.trace.expiresAt !== undefined && !validIsoDate(result.trace.expiresAt))) {
|
|
347
|
-
return invalidBackendResponse(plan, durationMs, "Backend returned malformed trace routing metadata.");
|
|
348
|
-
}
|
|
349
|
-
trace = {
|
|
350
|
-
handle: this.#uniqueId("trace", this.#traces),
|
|
351
|
-
backendId: plan.backendId,
|
|
352
|
-
authorizationScope,
|
|
353
|
-
expiresAt: result.trace.expiresAt,
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
const { trace: _backendTrace, ...responseFields } = result;
|
|
357
|
-
const response = { ...responseFields, durationMs, trace };
|
|
358
|
-
const diagnostics = validateAgentResponse(response, { plan });
|
|
359
|
-
if (hasSubagentErrors(diagnostics))
|
|
360
|
-
return invalidBackendResponse(plan, durationMs, summarizeDiagnostics(diagnostics));
|
|
361
|
-
if (trace && result.trace)
|
|
362
|
-
this.#traces.set(trace.handle, { reference: trace, backendTraceId: result.trace.id });
|
|
363
|
-
return structuredClone(response);
|
|
364
|
-
}
|
|
365
|
-
#uniqueId(kind, values) {
|
|
366
|
-
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
367
|
-
const id = this.#idFactory(kind);
|
|
368
|
-
if (validOpaqueId(id) && !values.has(id))
|
|
369
|
-
return id;
|
|
370
|
-
}
|
|
371
|
-
throw new SubagentBackendRegistryError(`${kind}.id`, `Could not allocate a unique valid ${kind} identifier.`);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
function validateRegistration(backend) {
|
|
375
|
-
if (!backend || typeof backend !== "object")
|
|
376
|
-
throw new SubagentBackendRegistryError("backend.type", "Subagent backend must be an object.");
|
|
377
|
-
if (typeof backend.preflight !== "function" || typeof backend.execute !== "function") {
|
|
378
|
-
throw new SubagentBackendRegistryError("backend.methods", "Subagent backend must implement preflight and execute.");
|
|
379
|
-
}
|
|
380
|
-
const diagnostics = validateBackendPreflight({
|
|
381
|
-
status: "rejected",
|
|
382
|
-
preflightId: "descriptor-check",
|
|
383
|
-
backend: backend.descriptor,
|
|
384
|
-
diagnostics: [diagnostic("backend.descriptor-check", "Descriptor validation sentinel.")],
|
|
385
|
-
});
|
|
386
|
-
if (hasSubagentErrors(diagnostics))
|
|
387
|
-
throw new SubagentBackendRegistryError("backend.descriptor", summarizeDiagnostics(diagnostics));
|
|
388
|
-
if (backend.descriptor.capabilities.promptRuntimeFidelity === "backend-assisted" && typeof backend.prepare !== "function") {
|
|
389
|
-
throw new SubagentBackendRegistryError("backend.prepare", "A backend-assisted backend must implement prepare.");
|
|
390
|
-
}
|
|
391
|
-
if (backend.descriptor.capabilities.traceInspection && typeof backend.inspectTrace !== "function") {
|
|
392
|
-
throw new SubagentBackendRegistryError("backend.trace", "A trace-inspection backend must implement inspectTrace.");
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
function responseCommon(plan, durationMs) {
|
|
396
|
-
return {
|
|
397
|
-
schemaVersion: SUBAGENT_CONTRACT_VERSION,
|
|
398
|
-
requestId: plan.requestId,
|
|
399
|
-
runId: plan.runId,
|
|
400
|
-
backendId: plan.backendId,
|
|
401
|
-
profileFingerprint: plan.profile.profileFingerprint,
|
|
402
|
-
executionFingerprint: plan.executionFingerprint,
|
|
403
|
-
model: structuredClone(plan.model),
|
|
404
|
-
effectiveToolIds: [...plan.effectiveToolIds],
|
|
405
|
-
enforcement: { access: structuredClone(plan.access), limits: structuredClone(plan.limits) },
|
|
406
|
-
durationMs,
|
|
407
|
-
artifacts: [],
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
function invalidBackendResponse(plan, durationMs, detail) {
|
|
411
|
-
return {
|
|
412
|
-
...responseCommon(plan, durationMs),
|
|
413
|
-
status: "failed",
|
|
414
|
-
error: { code: "backend-invalid-response", message: detail, retryable: false },
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
function diagnostic(code, message, path) {
|
|
418
|
-
return { level: "error", code, message, path };
|
|
419
|
-
}
|
|
420
|
-
function ensureErrorDiagnostics(diagnostics) {
|
|
421
|
-
const cloned = structuredClone(diagnostics);
|
|
422
|
-
if (cloned.some((item) => item.level === "error"))
|
|
423
|
-
return [...cloned];
|
|
424
|
-
return [diagnostic("registry.rejected", "Registry rejected the backend operation."), ...cloned];
|
|
425
|
-
}
|
|
426
|
-
function summarizeDiagnostics(diagnostics) {
|
|
427
|
-
return diagnostics.filter((item) => item.level === "error").slice(0, 4).map((item) => `${item.code}: ${item.message}`).join("; ") || "Subagent validation failed.";
|
|
428
|
-
}
|
|
429
|
-
function sameDescriptor(left, right) {
|
|
430
|
-
try {
|
|
431
|
-
return canonicalSubagentJson(left) === canonicalSubagentJson(right);
|
|
432
|
-
}
|
|
433
|
-
catch {
|
|
434
|
-
return false;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
function sameTraceReference(left, right) {
|
|
438
|
-
return left.handle === right.handle
|
|
439
|
-
&& left.backendId === right.backendId
|
|
440
|
-
&& left.authorizationScope === right.authorizationScope
|
|
441
|
-
&& left.expiresAt === right.expiresAt;
|
|
442
|
-
}
|
|
443
|
-
function validOpaqueId(value) {
|
|
444
|
-
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value);
|
|
445
|
-
}
|
|
446
|
-
function validNamespace(value) {
|
|
447
|
-
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value);
|
|
448
|
-
}
|
|
449
|
-
function validIsoDate(value) {
|
|
450
|
-
return !Number.isNaN(Date.parse(value)) && new Date(value).toISOString() === value;
|
|
451
|
-
}
|
|
452
|
-
function elapsed(now, start) {
|
|
453
|
-
return Math.max(0, now - start);
|
|
454
|
-
}
|
|
455
|
-
function abortReason(signal, fallback) {
|
|
456
|
-
if (!signal)
|
|
457
|
-
return fallback;
|
|
458
|
-
return typeof signal.reason === "string" && signal.reason.trim() ? signal.reason : fallback;
|
|
459
|
-
}
|
|
460
|
-
function errorMessage(error) {
|
|
461
|
-
return error instanceof Error ? error.message : String(error);
|
|
462
|
-
}
|
|
463
|
-
//# sourceMappingURL=backend-registry.js.map
|