@mininglamp-oss/octo-runtime-client 0.0.0-stage → 1.1.11

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 (87) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +38 -2
  3. package/build-info.json +5 -0
  4. package/dist/async-question-reply.d.ts +30 -0
  5. package/dist/async-question-reply.js +199 -0
  6. package/dist/browser.d.ts +1 -0
  7. package/dist/browser.js +1 -0
  8. package/dist/client.d.ts +10 -0
  9. package/dist/client.js +465 -0
  10. package/dist/index.d.ts +6 -0
  11. package/dist/index.js +6 -0
  12. package/dist/internal.d.ts +4 -0
  13. package/dist/internal.js +5 -0
  14. package/dist/invocation-lifecycle.d.ts +59 -0
  15. package/dist/invocation-lifecycle.js +287 -0
  16. package/dist/notification.d.ts +1 -0
  17. package/dist/notification.js +1 -0
  18. package/dist/protocol.d.ts +1 -0
  19. package/dist/protocol.js +1 -0
  20. package/dist/runtime-path.d.ts +21 -0
  21. package/dist/runtime-path.js +138 -0
  22. package/dist/startup-catalog.d.ts +8 -0
  23. package/dist/startup-catalog.js +65 -0
  24. package/dist/startup-diagnostics.d.ts +82 -0
  25. package/dist/startup-diagnostics.js +371 -0
  26. package/dist/submission-error.d.ts +10 -0
  27. package/dist/submission-error.js +23 -0
  28. package/dist/types.d.ts +72 -0
  29. package/dist/types.js +10 -0
  30. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/actmode.d.ts +4 -0
  31. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/actmode.js +36 -0
  32. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/adapter-metadata.d.ts +7 -0
  33. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/adapter-metadata.js +1 -0
  34. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/browser.d.ts +7 -0
  35. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/browser.js +6 -0
  36. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-catalog.d.ts +11 -0
  37. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-catalog.js +33 -0
  38. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-configuration.d.ts +5 -0
  39. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-configuration.js +58 -0
  40. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-hash.d.ts +2 -0
  41. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-hash.js +5 -0
  42. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context.d.ts +29 -0
  43. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context.js +566 -0
  44. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/execution-plan.d.ts +3 -0
  45. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/execution-plan.js +80 -0
  46. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/experience.d.ts +4 -0
  47. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/experience.js +166 -0
  48. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/failure.d.ts +82 -0
  49. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/failure.js +562 -0
  50. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/generated/protocol-types.d.ts +3022 -0
  51. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/generated/protocol-types.js +6 -0
  52. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/index.d.ts +124 -0
  53. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/index.js +709 -0
  54. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-input.d.ts +129 -0
  55. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-input.js +177 -0
  56. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-size.d.ts +3 -0
  57. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-size.js +11 -0
  58. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/json-size.d.ts +2 -0
  59. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/json-size.js +91 -0
  60. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/model-context.d.ts +3 -0
  61. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/model-context.js +9 -0
  62. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/multimodal.d.ts +15 -0
  63. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/multimodal.js +166 -0
  64. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/notification.d.ts +2 -0
  65. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/notification.js +137 -0
  66. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/questions.d.ts +9 -0
  67. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/questions.js +84 -0
  68. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/run-protocol.d.ts +20 -0
  69. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/run-protocol.js +294 -0
  70. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/runtime-selection.d.ts +3 -0
  71. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/runtime-selection.js +68 -0
  72. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/service-processes.d.ts +5 -0
  73. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/service-processes.js +65 -0
  74. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/stream-limits.d.ts +27 -0
  75. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/stream-limits.js +56 -0
  76. package/node_modules/@mininglamp-oss/octo-runtime-protocol/package.json +15 -0
  77. package/package.json +55 -4
  78. package/schema/context-configuration-fixtures.json +1219 -0
  79. package/schema/context-fixtures.json +5988 -0
  80. package/schema/kernel-client.behavior.json +56 -0
  81. package/schema/kernel-ipc-fixtures.json +53 -0
  82. package/schema/kernel-ipc.schema.json +538 -0
  83. package/schema/native-invoke-fixtures.json +116 -0
  84. package/schema/protocol.schema.json +4525 -0
  85. package/schema/run-protocol-fixtures.json +761 -0
  86. package/schema/runtime-selection-fixtures.json +663 -0
  87. package/schema/session-name-fixtures.json +191 -0
@@ -0,0 +1,58 @@
1
+ import { assertContextEnvelope, canonicalContext, mergeContext, mergeContextEnvs, assertContextEnvs, assertContextMcp, CONTEXT_SCHEMA } from "./context.js";
2
+ import { findContextTypeDefinition } from "./context-catalog.js";
3
+ const empty = () => ({ schema: CONTEXT_SCHEMA, items: [] });
4
+ const keyOf = (item) => `${item.namespace}/${item.name}`;
5
+ /** 校验可持久化配置;身份、资源权限和强制来源仍由 Worker 验证。 */
6
+ export function assertContextConfiguration(value) {
7
+ if (!value ||
8
+ typeof value !== "object" ||
9
+ Array.isArray(value) ||
10
+ Object.keys(value).some((key) => !["defaults", "mandatory"].includes(key)))
11
+ throw new TypeError("CONTEXT_CONFIGURATION_INVALID");
12
+ resolveContextConfiguration(value, empty());
13
+ }
14
+ function validateLayers(configuration) {
15
+ for (const [layer, envelope] of [configuration.defaults, configuration.mandatory].entries()) {
16
+ assertContextEnvelope(envelope);
17
+ for (const item of envelope.items) {
18
+ if (!findContextTypeDefinition(item.namespace, item.name)?.persistent)
19
+ throw new TypeError("CONTEXT_CONFIGURATION_TYPE_FORBIDDEN");
20
+ if (item.namespace === "octo.control" && layer === 0)
21
+ throw new TypeError("CONTEXT_CONFIGURATION_TYPE_FORBIDDEN");
22
+ }
23
+ }
24
+ }
25
+ /** Agent mandatory values are authoritative; request differences are overwritten. */
26
+ export function resolveContextConfiguration(configuration, request) {
27
+ validateLayers(configuration);
28
+ assertContextEnvelope(request);
29
+ const effective = mergeContext(configuration.defaults, request);
30
+ const items = new Map(effective.items.map((item) => [keyOf(item), item]));
31
+ for (const required of configuration.mandatory.items) {
32
+ const key = keyOf(required);
33
+ const selected = items.get(key);
34
+ let payload = structuredClone(required.payload);
35
+ if (key === "octo.execution/model" || key === "octo.execution/limits") {
36
+ payload = { ...structuredClone(selected?.payload ?? {}), ...payload };
37
+ }
38
+ else if (key === "octo.execution/mcp") {
39
+ assertContextMcp(required.payload);
40
+ payload = {
41
+ mcpServers: {
42
+ ...selected?.payload.mcpServers,
43
+ ...structuredClone(required.payload.mcpServers)
44
+ }
45
+ };
46
+ }
47
+ else if (key === "octo.execution/envs") {
48
+ assertContextEnvs(required.payload);
49
+ payload = {
50
+ ...mergeContextEnvs(selected?.payload ?? { env_variables: [] }, required.payload)
51
+ };
52
+ }
53
+ items.set(key, { ...structuredClone(required), payload });
54
+ }
55
+ const result = { schema: CONTEXT_SCHEMA, items: [...items.values()] };
56
+ assertContextEnvelope(result);
57
+ return JSON.parse(canonicalContext(result));
58
+ }
@@ -0,0 +1,2 @@
1
+ import type { ContextEnvelope } from "./generated/protocol-types.js";
2
+ export declare function contextHash(context: ContextEnvelope): string;
@@ -0,0 +1,5 @@
1
+ import { createHash } from "node:crypto";
2
+ import { canonicalContext } from "./context.js";
3
+ export function contextHash(context) {
4
+ return `sha256:${createHash("sha256").update(canonicalContext(context)).digest("hex")}`;
5
+ }
@@ -0,0 +1,29 @@
1
+ import type { ApprovalMode, ContextEnvsPayload, ContextEnvelope, ContextItem, McpServers, ResolvedExecutionPlan } from "./generated/protocol-types.js";
2
+ export declare const CONTEXT_SCHEMA: "octo.context-envelope/v3";
3
+ export declare const RUNTIME_RUN_CONTRACT: Readonly<{
4
+ readonly schema_version: 7;
5
+ readonly routing_schema: "octo.runtime-selection/v2";
6
+ readonly context_schema: "octo.context-envelope/v3";
7
+ readonly approval_schema: "octo.approval-request/v1";
8
+ }>;
9
+ export declare const CONTEXT_MAX_BYTES = 65536;
10
+ export declare const CONTEXT_MAX_ITEMS = 32;
11
+ export declare const DEFAULT_RUN_TIMEOUT_MS = 86400000;
12
+ export declare const MAX_RUN_TIMEOUT_MS = 604800000;
13
+ export declare const APPROVAL_MODES: readonly ApprovalMode[];
14
+ export declare function assertAgentSource(value: unknown): asserts value is "preset" | "provisioned";
15
+ export declare function assertApprovalMode(value: unknown): asserts value is ApprovalMode;
16
+ export declare function assertContextEnvelope(value: unknown): asserts value is ContextEnvelope;
17
+ export declare function contextItem(context: ContextEnvelope, namespace: string, name: string): ContextItem | undefined;
18
+ export declare function canonicalContext(context: ContextEnvelope): string;
19
+ export declare function mergeContext(defaults: ContextEnvelope, overrides: ContextEnvelope): ContextEnvelope;
20
+ export declare function assertResolvedExecutionPlan(value: unknown): asserts value is ResolvedExecutionPlan;
21
+ export declare function assertContextMcp(value: unknown): asserts value is {
22
+ mcpServers: McpServers;
23
+ };
24
+ export declare function assertMcpServers(value: unknown, templates?: boolean): asserts value is McpServers;
25
+ export declare function assertJsonValue(value: unknown): void;
26
+ export declare function mergeReferences(base: Record<string, unknown>[], next: Record<string, unknown>[]): Record<string, unknown>[];
27
+ /** Environment names are values in a list, never dynamic protocol object keys. */
28
+ export declare function assertContextEnvs(value: unknown): asserts value is ContextEnvsPayload;
29
+ export declare function mergeContextEnvs(base: unknown, overrides: unknown): ContextEnvsPayload;
@@ -0,0 +1,566 @@
1
+ import { RUNTIME_SELECTION_SCHEMA } from "./runtime-selection.js";
2
+ export const CONTEXT_SCHEMA = "octo.context-envelope/v3";
3
+ export const RUNTIME_RUN_CONTRACT = Object.freeze({
4
+ schema_version: 7,
5
+ routing_schema: RUNTIME_SELECTION_SCHEMA,
6
+ context_schema: CONTEXT_SCHEMA,
7
+ approval_schema: "octo.approval-request/v1"
8
+ });
9
+ export const CONTEXT_MAX_BYTES = 65_536;
10
+ export const CONTEXT_MAX_ITEMS = 32;
11
+ // 整轮预算与首帧/空闲检测独立;七天仍在 Node 单次定时器的安全范围内。
12
+ export const DEFAULT_RUN_TIMEOUT_MS = 86_400_000;
13
+ export const MAX_RUN_TIMEOUT_MS = 604_800_000;
14
+ export const APPROVAL_MODES = ["ask", "assisted", "full_access"];
15
+ export function assertAgentSource(value) {
16
+ if (value !== "preset" && value !== "provisioned")
17
+ throw new TypeError("AGENT_SOURCE_INVALID");
18
+ }
19
+ export function assertApprovalMode(value) {
20
+ if (!APPROVAL_MODES.includes(value))
21
+ throw new TypeError("APPROVAL_MODE_UNSUPPORTED");
22
+ }
23
+ export function assertContextEnvelope(value) {
24
+ object(value, "CONTEXT_REQUIRED");
25
+ fields(value, ["schema", "items"]);
26
+ if (value.schema !== CONTEXT_SCHEMA || !Array.isArray(value.items))
27
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
28
+ if (value.items.length > CONTEXT_MAX_ITEMS)
29
+ throw new TypeError("CONTEXT_BUDGET_EXCEEDED");
30
+ assertJsonValue(value);
31
+ const keys = new Set();
32
+ for (const item of value.items) {
33
+ object(item, "CONTEXT_SCHEMA_INVALID");
34
+ fields(item, ["namespace", "name", "guidance", "data_format", "payload"]);
35
+ for (const property of ["namespace", "name"]) {
36
+ const text = item[property];
37
+ if (typeof text !== "string" || text.length > 96 || !/^[a-z0-9][a-z0-9._-]*$/.test(text))
38
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
39
+ }
40
+ const key = JSON.stringify([item.namespace, item.name]);
41
+ if (keys.has(key))
42
+ throw new TypeError("CONTEXT_KEY_DUPLICATED");
43
+ keys.add(key);
44
+ if (item.guidance !== undefined) {
45
+ if (typeof item.guidance !== "string")
46
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
47
+ if (["octo.execution", "octo.runtime", "octo.security", "octo.control"].includes(String(item.namespace)))
48
+ throw new TypeError("CONTEXT_GUIDANCE_FORBIDDEN");
49
+ }
50
+ object(item.payload, "CONTEXT_SCHEMA_INVALID");
51
+ if (item.data_format !== undefined) {
52
+ if (item.data_format !== "text" || !String(item.namespace).startsWith("extra."))
53
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
54
+ fields(item.payload, ["text"]);
55
+ if (typeof item.payload.text !== "string")
56
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
57
+ }
58
+ publicPayload(item.namespace, item.name, item.payload);
59
+ }
60
+ if (new TextEncoder().encode(JSON.stringify(value)).length > CONTEXT_MAX_BYTES)
61
+ throw new TypeError("CONTEXT_BUDGET_EXCEEDED");
62
+ }
63
+ export function contextItem(context, namespace, name) {
64
+ return context.items.find((item) => item.namespace === namespace && item.name === name);
65
+ }
66
+ export function canonicalContext(context) {
67
+ assertContextEnvelope(context);
68
+ return JSON.stringify(sorted({
69
+ schema: context.schema,
70
+ items: context.items
71
+ }));
72
+ }
73
+ export function mergeContext(defaults, overrides) {
74
+ assertContextEnvelope(defaults);
75
+ assertContextEnvelope(overrides);
76
+ const items = new Map();
77
+ for (const item of [...defaults.items, ...overrides.items]) {
78
+ const key = JSON.stringify([item.namespace, item.name]);
79
+ const previous = items.get(key);
80
+ const next = structuredClone(item);
81
+ if (previous &&
82
+ item.namespace === "octo.execution" &&
83
+ ["model", "limits"].includes(item.name)) {
84
+ next.payload = { ...previous.payload, ...next.payload };
85
+ }
86
+ if (previous && item.namespace === "octo.execution" && item.name === "envs")
87
+ next.payload = { ...mergeContextEnvs(previous.payload, next.payload) };
88
+ if (previous && item.namespace === "octo.execution" && item.name === "mcp")
89
+ next.payload = {
90
+ mcpServers: {
91
+ ...previous.payload.mcpServers,
92
+ ...next.payload.mcpServers
93
+ }
94
+ };
95
+ if (previous && item.namespace === "octo.runtime" && item.name === "selection") {
96
+ for (const field of ["offline_behavior", "failover"])
97
+ if (next.payload[field] === undefined && previous.payload[field] !== undefined)
98
+ next.payload[field] = previous.payload[field];
99
+ }
100
+ if (previous && item.namespace === "octo.content" && item.name === "references")
101
+ next.payload.entries = mergeReferences(previous.payload.entries, next.payload.entries);
102
+ items.set(key, next);
103
+ }
104
+ const result = { schema: CONTEXT_SCHEMA, items: [...items.values()] };
105
+ assertContextEnvelope(result);
106
+ return JSON.parse(canonicalContext(result));
107
+ }
108
+ export function assertResolvedExecutionPlan(value) {
109
+ object(value, "RESOLVED_EXECUTION_PLAN_REQUIRED");
110
+ assertJsonValue(value);
111
+ fields(value, [
112
+ "agent_source",
113
+ "adapter_type",
114
+ "provider_agent_id",
115
+ "workdir",
116
+ "workspace",
117
+ "approval_mode",
118
+ "model",
119
+ "effort",
120
+ "actmode",
121
+ "runtime_limits",
122
+ "envs",
123
+ "mcp"
124
+ ]);
125
+ if (value.envs !== undefined)
126
+ assertContextEnvs(value.envs);
127
+ if (value.mcp !== undefined)
128
+ assertContextMcp(value.mcp);
129
+ assertAgentSource(value.agent_source);
130
+ if (value.actmode !== undefined && value.actmode !== "default" && value.actmode !== "plan")
131
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
132
+ text(value.adapter_type);
133
+ assertProviderAgent(value.adapter_type, value.provider_agent_id);
134
+ if (typeof value.workdir !== "string")
135
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
136
+ if (value.workspace !== undefined) {
137
+ object(value.workspace, "RESOLVED_EXECUTION_PLAN_INVALID");
138
+ executionField("octo.execution", "workspace", value.workspace);
139
+ }
140
+ if (!value.workdir &&
141
+ value.agent_source !== "preset" &&
142
+ (!isObject(value.workspace) || value.workspace.mode !== "random"))
143
+ throw new TypeError("RESOLVED_EXECUTION_PLAN_INVALID");
144
+ if (isObject(value.workspace) &&
145
+ value.workspace.mode === "none" &&
146
+ (value.agent_source !== "preset" || value.workdir !== ""))
147
+ throw new TypeError("RESOLVED_EXECUTION_PLAN_INVALID");
148
+ if (value.agent_source === "preset" && typeof value.provider_agent_id !== "string")
149
+ throw new TypeError("PRESET_AGENT_TARGET_REQUIRED");
150
+ assertApprovalMode(value.approval_mode);
151
+ for (const property of ["model", "effort"])
152
+ if (value[property] !== undefined)
153
+ text(value[property]);
154
+ if (value.runtime_limits !== undefined) {
155
+ object(value.runtime_limits, "RESOLVED_EXECUTION_PLAN_INVALID");
156
+ limits(value.runtime_limits);
157
+ }
158
+ }
159
+ export function assertContextMcp(value) {
160
+ object(value, "CONTEXT_SCHEMA_INVALID");
161
+ fields(value, ["mcpServers"]);
162
+ try {
163
+ assertMcpServers(value.mcpServers, true);
164
+ }
165
+ catch {
166
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
167
+ }
168
+ }
169
+ export function assertMcpServers(value, templates = false) {
170
+ if (!isObject(value))
171
+ throw new TypeError("mcpServers must be an object");
172
+ const entries = Object.entries(value);
173
+ if (entries.length > 32)
174
+ throw new TypeError("mcpServers must not contain more than 32 servers");
175
+ for (const [name, server] of entries) {
176
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(name)) {
177
+ throw new TypeError("mcpServers contains an invalid server name");
178
+ }
179
+ if (!isObject(server))
180
+ throw new TypeError(`mcpServers.${name} must be an object`);
181
+ const allowed = server.type === "stdio"
182
+ ? new Set([
183
+ "type",
184
+ "command",
185
+ "args",
186
+ "env",
187
+ "cwd",
188
+ "required",
189
+ "startupTimeoutMs",
190
+ "toolTimeoutMs"
191
+ ])
192
+ : server.type === "streamable_http"
193
+ ? new Set(["type", "url", "headers", "required", "startupTimeoutMs", "toolTimeoutMs"])
194
+ : undefined;
195
+ if (!allowed)
196
+ throw new TypeError(`mcpServers.${name}.type is unsupported`);
197
+ for (const key of Object.keys(server)) {
198
+ if (!allowed.has(key))
199
+ throw new TypeError(`mcpServers.${name}.${key} is unsupported`);
200
+ }
201
+ if (server.type === "stdio") {
202
+ requireNonEmptyString(server, "command", `mcpServers.${name}`);
203
+ assertStringArray(server.args, `mcpServers.${name}.args`, 128);
204
+ assertStringRecord(server.env, `mcpServers.${name}.env`, 128);
205
+ for (const [key, value] of Object.entries(server.env ?? {})) {
206
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key) || /\0/.test(String(value)))
207
+ throw new TypeError(`MCP_ENV_INVALID: ${name}.${key}`);
208
+ }
209
+ if (server.cwd !== undefined && (typeof server.cwd !== "string" || server.cwd.length === 0)) {
210
+ throw new TypeError(`mcpServers.${name}.cwd must be a non-empty string when present`);
211
+ }
212
+ }
213
+ else {
214
+ requireNonEmptyString(server, "url", `mcpServers.${name}`);
215
+ const templated = templates && /\$\$?\{[A-Za-z_][A-Za-z0-9_]*\}/.test(server.url);
216
+ let parsed;
217
+ try {
218
+ parsed = new URL(templated ? "https://template.invalid" : server.url);
219
+ }
220
+ catch {
221
+ throw new TypeError(`mcpServers.${name}.url must be an absolute HTTP URL`);
222
+ }
223
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
224
+ throw new TypeError(`mcpServers.${name}.url must be an absolute HTTP URL`);
225
+ }
226
+ if (Array.from(server.url).length > 4096) {
227
+ throw new TypeError(`mcpServers.${name}.url must not exceed 4096 characters`);
228
+ }
229
+ assertStringRecord(server.headers, `mcpServers.${name}.headers`, 64);
230
+ const seen = new Set();
231
+ for (const [key, value] of Object.entries(server.headers ?? {})) {
232
+ if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(key) ||
233
+ /[\r\n\0]/.test(String(value)) ||
234
+ seen.has(key.toLowerCase()))
235
+ throw new TypeError(`MCP_HEADER_INVALID: ${name}.${key}`);
236
+ seen.add(key.toLowerCase());
237
+ }
238
+ }
239
+ if (server.required !== undefined && typeof server.required !== "boolean") {
240
+ throw new TypeError(`mcpServers.${name}.required must be boolean when present`);
241
+ }
242
+ assertMcpTimeout(server.startupTimeoutMs, `mcpServers.${name}.startupTimeoutMs`, 300_000);
243
+ assertMcpTimeout(server.toolTimeoutMs, `mcpServers.${name}.toolTimeoutMs`, 3_600_000);
244
+ }
245
+ }
246
+ function requireNonEmptyString(value, key, path) {
247
+ if (typeof value[key] !== "string" || value[key].length === 0) {
248
+ throw new TypeError(`${path}.${key} must be a non-empty string`);
249
+ }
250
+ }
251
+ function assertStringArray(value, path, limit) {
252
+ if (value === undefined)
253
+ return;
254
+ if (!Array.isArray(value) ||
255
+ value.length > limit ||
256
+ value.some((item) => typeof item !== "string")) {
257
+ throw new TypeError(`${path} must be an array of strings`);
258
+ }
259
+ }
260
+ function assertStringRecord(value, path, limit) {
261
+ if (value === undefined)
262
+ return;
263
+ if (!isObject(value) ||
264
+ Object.keys(value).length > limit ||
265
+ Object.values(value).some((item) => typeof item !== "string")) {
266
+ throw new TypeError(`${path} must be a string map`);
267
+ }
268
+ }
269
+ function assertMcpTimeout(value, path, maximum) {
270
+ if (value === undefined)
271
+ return;
272
+ if (!Number.isSafeInteger(value) || Number(value) < 100 || Number(value) > maximum) {
273
+ throw new TypeError(`${path} is out of range`);
274
+ }
275
+ }
276
+ export function assertJsonValue(value) {
277
+ json(value, 0);
278
+ }
279
+ export function mergeReferences(base, next) {
280
+ const entries = new Map([...base, ...next].map((entry) => [String(entry.resource_ref), structuredClone(entry)]));
281
+ return [...entries].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)).map(([, entry]) => entry);
282
+ }
283
+ function publicPayload(namespace, name, value) {
284
+ if (namespace === "octo.execution" && name === "actmode") {
285
+ fields(value, ["mode"]);
286
+ if (value.mode !== "default" && value.mode !== "plan")
287
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
288
+ return;
289
+ }
290
+ if (namespace === "octo.execution" && name === "mcp") {
291
+ assertContextMcp(value);
292
+ return;
293
+ }
294
+ if (namespace === "octo.execution" && name === "envs") {
295
+ assertContextEnvs(value);
296
+ return;
297
+ }
298
+ if (namespace === "octo.resources")
299
+ throw new TypeError("CONTEXT_TYPE_REMOVED");
300
+ if (namespace === "octo.execution" && name === "model") {
301
+ fields(value, ["selection", "reasoning"]);
302
+ if (!Object.keys(value).length)
303
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
304
+ for (const [key, setting] of Object.entries(value)) {
305
+ object(setting, "CONTEXT_SCHEMA_INVALID");
306
+ executionField("octo.execution", key === "selection" ? "model" : "reasoning", setting);
307
+ }
308
+ return;
309
+ }
310
+ if ((namespace === "octo.execution" && ["adapter", "workspace", "limits"].includes(name)) ||
311
+ (namespace === "octo.runtime" && name === "selection") ||
312
+ (namespace === "octo.security" && name === "approval_policy")) {
313
+ executionField(namespace, name, value);
314
+ return;
315
+ }
316
+ if ((namespace === "octo.content" && ["inline", "references"].includes(name)) ||
317
+ (namespace === "octo.control" && name === "instructions")) {
318
+ fields(value, ["entries"]);
319
+ if (!Array.isArray(value.entries) || (name !== "instructions" && value.entries.length === 0))
320
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
321
+ const refs = new Set();
322
+ for (const entry of value.entries) {
323
+ object(entry, "CONTEXT_SCHEMA_INVALID");
324
+ if (name === "instructions") {
325
+ fields(entry, ["text"]);
326
+ text(entry.text);
327
+ }
328
+ else if (name === "references") {
329
+ resource(entry);
330
+ const ref = String(entry.resource_ref);
331
+ if (refs.has(ref))
332
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
333
+ refs.add(ref);
334
+ }
335
+ else if (name === "inline") {
336
+ fields(entry, ["text", "resource_ref", "source", "summary"]);
337
+ text(entry.text);
338
+ if (entry.resource_ref !== undefined)
339
+ text(entry.resource_ref);
340
+ if (entry.source !== undefined &&
341
+ typeof entry.source !== "string" &&
342
+ !isObject(entry.source))
343
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
344
+ if (entry.summary !== undefined && typeof entry.summary !== "string")
345
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
346
+ }
347
+ else
348
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
349
+ }
350
+ return;
351
+ }
352
+ // Unknown optional types remain opaque until a consumer explicitly supports them.
353
+ }
354
+ function executionField(namespace, name, value) {
355
+ if (namespace === "octo.execution") {
356
+ if (name === "adapter") {
357
+ fields(value, ["type", "provider_agent_id"]);
358
+ text(value.type);
359
+ assertProviderAgent(value.type, value.provider_agent_id);
360
+ return;
361
+ }
362
+ if (name === "model" || name === "reasoning") {
363
+ const property = name === "model" ? "id" : "effort";
364
+ fields(value, [property, "use_provider_default"]);
365
+ if (value.use_provider_default === true && value[property] === undefined)
366
+ return;
367
+ if (value.use_provider_default !== undefined)
368
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
369
+ text(value[property]);
370
+ return;
371
+ }
372
+ if (name === "workspace") {
373
+ fields(value, ["mode", "path"]);
374
+ if (value.mode === "none" && value.path === undefined)
375
+ return;
376
+ if (value.mode === "fixed") {
377
+ text(value.path);
378
+ return;
379
+ }
380
+ if (value.mode === "random" && value.path === undefined)
381
+ return;
382
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
383
+ }
384
+ if (name === "limits") {
385
+ limits(value);
386
+ return;
387
+ }
388
+ }
389
+ if (namespace === "octo.runtime" && name === "selection") {
390
+ fields(value, [
391
+ "mode",
392
+ "runtime_id",
393
+ "runtime_ids",
394
+ "strategy",
395
+ "offline_behavior",
396
+ "failover"
397
+ ]);
398
+ if (value.offline_behavior !== undefined &&
399
+ value.offline_behavior !== "wait" &&
400
+ value.offline_behavior !== "fail")
401
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
402
+ if (value.failover !== undefined &&
403
+ value.failover !== "none" &&
404
+ value.failover !== "candidates")
405
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
406
+ if (value.mode === "explicit" && value.failover !== undefined && value.failover !== "none")
407
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
408
+ if (value.mode === "explicit" &&
409
+ value.runtime_ids === undefined &&
410
+ value.strategy === undefined) {
411
+ text(value.runtime_id);
412
+ return;
413
+ }
414
+ if (value.mode === "candidates" &&
415
+ value.runtime_id === undefined &&
416
+ value.strategy === "preferred_failover") {
417
+ stringList(value.runtime_ids, 64, 1);
418
+ return;
419
+ }
420
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
421
+ }
422
+ if (namespace === "octo.security" && name === "approval_policy") {
423
+ fields(value, ["mode"]);
424
+ assertApprovalMode(value.mode);
425
+ return;
426
+ }
427
+ throw new TypeError("CONTEXT_TYPE_UNSUPPORTED");
428
+ }
429
+ function limits(value) {
430
+ const bounds = {
431
+ first_frame_timeout_ms: 1_800_000,
432
+ model_idle_timeout_ms: 3_600_000,
433
+ model_idle_warning_ms: 3_600_000,
434
+ tool_timeout_ms: 86_400_000,
435
+ tool_wall_timeout_ms: 86_400_000,
436
+ run_timeout_ms: MAX_RUN_TIMEOUT_MS,
437
+ cancel_grace_ms: 60_000
438
+ };
439
+ fields(value, [...Object.keys(bounds), "model_idle_policy", "enforcement"]);
440
+ for (const [key, setting] of Object.entries(value)) {
441
+ if (key === "model_idle_policy" || key === "enforcement") {
442
+ if (setting !== "enforce" && setting !== "observe")
443
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
444
+ }
445
+ else
446
+ integer(setting, 100, bounds[key]);
447
+ }
448
+ if (value.enforcement === "observe" &&
449
+ ["model_idle_policy", "model_idle_warning_ms", "tool_wall_timeout_ms"].some((key) => key in value))
450
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
451
+ }
452
+ function resource(value) {
453
+ fields(value, ["resource_ref", "resolver", "locator", "params", "authorization_ref", "revision"]);
454
+ text(value.resource_ref);
455
+ for (const property of ["authorization_ref", "revision"])
456
+ if (value[property] !== undefined)
457
+ text(value[property]);
458
+ if (value.params !== undefined)
459
+ object(value.params, "CONTEXT_SCHEMA_INVALID");
460
+ const locator = value.locator;
461
+ object(locator, "CONTEXT_SCHEMA_INVALID");
462
+ if (value.resolver !== "mcp")
463
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
464
+ fields(locator, ["server_ref", "tool"]);
465
+ text(locator.server_ref);
466
+ text(locator.tool);
467
+ }
468
+ function integer(value, minimum, maximum = Number.MAX_SAFE_INTEGER) {
469
+ if (!Number.isSafeInteger(value) || Number(value) < minimum || Number(value) > maximum)
470
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
471
+ }
472
+ function stringList(value, maximum, minimum = 0) {
473
+ if (!Array.isArray(value) ||
474
+ value.length < minimum ||
475
+ value.length > maximum ||
476
+ value.some((item) => typeof item !== "string" || !item) ||
477
+ new Set(value).size !== value.length)
478
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
479
+ }
480
+ function assertProviderAgent(type, id) {
481
+ if (type === "openclaw") {
482
+ if (typeof id !== "string" || !/^[a-z0-9_][a-z0-9_-]{0,63}$/.test(id))
483
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
484
+ }
485
+ else if (id !== undefined)
486
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
487
+ }
488
+ function fields(value, allowed) {
489
+ if (Object.keys(value).some((key) => !allowed.includes(key)))
490
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
491
+ }
492
+ function text(value) {
493
+ if (typeof value !== "string" || value.length === 0)
494
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
495
+ }
496
+ function isObject(value) {
497
+ return value !== null && typeof value === "object" && !Array.isArray(value);
498
+ }
499
+ function object(value, code) {
500
+ if (!isObject(value))
501
+ throw new TypeError(code);
502
+ }
503
+ function json(value, depth) {
504
+ if (depth > 32)
505
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
506
+ if (typeof value === "string") {
507
+ if (/[\uD800-\uDFFF]/u.test(value))
508
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
509
+ return;
510
+ }
511
+ if (value === null || typeof value === "boolean")
512
+ return;
513
+ if (typeof value === "number" && Number.isFinite(value))
514
+ return;
515
+ if (Array.isArray(value)) {
516
+ for (const item of value)
517
+ json(item, depth + 1);
518
+ return;
519
+ }
520
+ if (isObject(value)) {
521
+ for (const [key, item] of Object.entries(value)) {
522
+ json(key, depth + 1);
523
+ json(item, depth + 1);
524
+ }
525
+ return;
526
+ }
527
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
528
+ }
529
+ function sorted(value) {
530
+ if (Array.isArray(value))
531
+ return value.map(sorted);
532
+ if (isObject(value))
533
+ return Object.fromEntries(Object.keys(value)
534
+ .sort()
535
+ .map((key) => [key, sorted(value[key])]));
536
+ return value;
537
+ }
538
+ /** Environment names are values in a list, never dynamic protocol object keys. */
539
+ export function assertContextEnvs(value) {
540
+ object(value, "CONTEXT_SCHEMA_INVALID");
541
+ fields(value, ["env_variables"]);
542
+ if (!Array.isArray(value.env_variables) || value.env_variables.length > 128)
543
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
544
+ const keys = new Set();
545
+ for (const entry of value.env_variables) {
546
+ object(entry, "CONTEXT_SCHEMA_INVALID");
547
+ fields(entry, ["key", "value"]);
548
+ if (typeof entry.key !== "string" ||
549
+ !/^[A-Za-z_][A-Za-z0-9_]*$/.test(entry.key) ||
550
+ typeof entry.value !== "string" ||
551
+ entry.value.includes("\0") ||
552
+ keys.has(entry.key))
553
+ throw new TypeError("CONTEXT_SCHEMA_INVALID");
554
+ keys.add(entry.key);
555
+ }
556
+ }
557
+ export function mergeContextEnvs(base, overrides) {
558
+ assertContextEnvs(base);
559
+ assertContextEnvs(overrides);
560
+ const entries = new Map([...base.env_variables, ...overrides.env_variables].map((entry) => [entry.key, entry.value]));
561
+ return {
562
+ env_variables: [...entries]
563
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
564
+ .map(([key, value]) => ({ key, value }))
565
+ };
566
+ }
@@ -0,0 +1,3 @@
1
+ import type { ContextEnvelope, ResolvedExecutionPlan } from "./generated/protocol-types.js";
2
+ /** 验证有效 Context 与冻结执行计划逐项一致;不匹配时禁止进入资源准备。 */
3
+ export declare function assertFrozenContextPlan(context: ContextEnvelope, plan: ResolvedExecutionPlan): void;