@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,129 @@
1
+ import { type AgentConfig, type AgentSource, type ApprovalMode, type CanonicalEvent, type ContextEnvelope, type ContextEnvsPayload, type HostFrame, type InvokeRequest, type ModelInputPart, type ProviderSession, type RuntimeLimits } from "./index.js";
2
+ import type { StreamLimitPolicy } from "./stream-limits.js";
3
+ /**
4
+ * Public JSON invocation contract shared by CLI, SDK and channels.
5
+ * A plain JSON object satisfies this class structurally; `new` is not required.
6
+ * Only inputs and contexts are required. Defaults are resolved by the execution
7
+ * boundary, never by adding historical business context.
8
+ */
9
+ export declare class InvokeInput {
10
+ /** Exact current-turn text/image parts, in caller order. Image bytes occur here once. */
11
+ inputs: ModelInputPart[];
12
+ /** Caller-owned Context v3 snapshot; an empty items array is valid. */
13
+ contexts: ContextEnvelope;
14
+ /** Business session identity; generated if omitted. Does not imply provider resume. */
15
+ sessionId?: string;
16
+ /** 调用方拥有的展示名称;省略时由 Core 提供 UUID,不参与会话恢复。 */
17
+ sessionName?: string;
18
+ /** Output view of the same execution stream; defaults to native. */
19
+ protocol_mode?: "native" | "canonical";
20
+ /** Cancellation identity for this invocation; generated if omitted. */
21
+ invocationId?: string;
22
+ /** Business Run identity; generated if omitted. Independent of invocation/session IDs. */
23
+ runId?: string;
24
+ /** Caller-owned session generation, used to fence stale updates; no inferred default. */
25
+ sessionGeneration?: number;
26
+ /** Caller-owned provider binding epoch; no inferred default. */
27
+ providerEpoch?: number;
28
+ /** Agent ownership: preset for OpenClaw by default, provisioned otherwise. */
29
+ agentSource?: AgentSource;
30
+ /** Display-safe description; defaults to the textual summary of inputs. */
31
+ prompt?: string;
32
+ /** Exact textual provider turn when distinct from the multipart inputs summary. */
33
+ modelInput?: string;
34
+ /** Provider cwd; defaults to the resolved workspace (local cwd when omitted). */
35
+ workdir?: string;
36
+ /** Trusted instructions, separate from model business context; derived if omitted. */
37
+ controlInstructions?: string;
38
+ /** Execution approval policy, normally resolved from contexts; defaults to ask. */
39
+ approvalMode?: ApprovalMode;
40
+ /** Full provider configuration, including providerOptions; context selections must agree. */
41
+ agentConfig?: AgentConfig;
42
+ /** Complete timeout/enforcement settings; execution defaults apply when omitted. */
43
+ runtimeLimits?: RuntimeLimits;
44
+ /** JSON metadata for tracing/business identity; not model text or authorization evidence. */
45
+ runMetadata?: Record<string, unknown>;
46
+ /** Opaque provider session, including provider extension fields; never rebuilt from sessionId. */
47
+ providerSession?: ProviderSession;
48
+ /** Fresh/continue intent; defaults to continue with providerSession, fresh otherwise. */
49
+ continuityMode?: "fresh" | "continue";
50
+ /** Caller-provided exact recovery text; no automatic history reconstruction. */
51
+ freshFallbackModelInput?: string;
52
+ /** Caller-provided recovery text/image parts; recovery images must be supplied explicitly. */
53
+ freshFallbackModelInputParts?: ModelInputPart[];
54
+ /** Explicitly fail fresh fallback when historical image bytes cannot be supplied. */
55
+ freshFallbackImagesUnavailable?: boolean;
56
+ /** Checks the JSON boundary; resolved execution additionally uses assertInvokeRequest. */
57
+ static check(value: unknown): InvokeInput;
58
+ }
59
+ /** Local ingress execution settings, outside caller-owned JSON and Context. */
60
+ export interface InvokeEnvironment {
61
+ signal?: AbortSignal;
62
+ /** Trusted per-invocation values; forwarded privately across the Host process boundary. */
63
+ executionEnvOverrides?: ContextEnvsPayload;
64
+ /** Trusted transport and replay budgets; never sourced from caller Context. */
65
+ streamLimits?: StreamLimitPolicy;
66
+ }
67
+ export type InputFrames = (input: InvokeInput, environment?: InvokeEnvironment) => AsyncGenerator<HostFrame>;
68
+ /** Stable correlation IDs for cancellation and stream reconnection, without resolving execution. */
69
+ export declare function identifyInvokeInput(input: InvokeInput): {
70
+ invocationId: string;
71
+ runId: string;
72
+ sessionId: string;
73
+ /** Exact current-turn text/image parts, in caller order. Image bytes occur here once. */
74
+ inputs: ModelInputPart[];
75
+ /** Caller-owned Context v3 snapshot; an empty items array is valid. */
76
+ contexts: ContextEnvelope;
77
+ /** 调用方拥有的展示名称;省略时由 Core 提供 UUID,不参与会话恢复。 */
78
+ sessionName?: string;
79
+ /** Output view of the same execution stream; defaults to native. */
80
+ protocol_mode?: "native" | "canonical";
81
+ /** Caller-owned session generation, used to fence stale updates; no inferred default. */
82
+ sessionGeneration?: number;
83
+ /** Caller-owned provider binding epoch; no inferred default. */
84
+ providerEpoch?: number;
85
+ /** Agent ownership: preset for OpenClaw by default, provisioned otherwise. */
86
+ agentSource?: AgentSource;
87
+ /** Display-safe description; defaults to the textual summary of inputs. */
88
+ prompt?: string;
89
+ /** Exact textual provider turn when distinct from the multipart inputs summary. */
90
+ modelInput?: string;
91
+ /** Provider cwd; defaults to the resolved workspace (local cwd when omitted). */
92
+ workdir?: string;
93
+ /** Trusted instructions, separate from model business context; derived if omitted. */
94
+ controlInstructions?: string;
95
+ /** Execution approval policy, normally resolved from contexts; defaults to ask. */
96
+ approvalMode?: ApprovalMode;
97
+ /** Full provider configuration, including providerOptions; context selections must agree. */
98
+ agentConfig?: AgentConfig;
99
+ /** Complete timeout/enforcement settings; execution defaults apply when omitted. */
100
+ runtimeLimits?: RuntimeLimits;
101
+ /** JSON metadata for tracing/business identity; not model text or authorization evidence. */
102
+ runMetadata?: Record<string, unknown>;
103
+ /** Opaque provider session, including provider extension fields; never rebuilt from sessionId. */
104
+ providerSession?: ProviderSession;
105
+ /** Fresh/continue intent; defaults to continue with providerSession, fresh otherwise. */
106
+ continuityMode?: "fresh" | "continue";
107
+ /** Caller-provided exact recovery text; no automatic history reconstruction. */
108
+ freshFallbackModelInput?: string;
109
+ /** Caller-provided recovery text/image parts; recovery images must be supplied explicitly. */
110
+ freshFallbackModelInputParts?: ModelInputPart[];
111
+ /** Explicitly fail fresh fallback when historical image bytes cannot be supplied. */
112
+ freshFallbackImagesUnavailable?: boolean;
113
+ };
114
+ /** Select a view of one source stream; execution preparation belongs to the Host. */
115
+ export declare function createInputInvoke(source: InputFrames): InputInvoke;
116
+ export interface InputInvoke {
117
+ (input: InvokeInput & {
118
+ protocol_mode: "canonical";
119
+ }, environment?: InvokeEnvironment): AsyncGenerator<CanonicalEvent>;
120
+ (input: InvokeInput & {
121
+ protocol_mode?: "native";
122
+ }, environment?: InvokeEnvironment): AsyncGenerator<HostFrame>;
123
+ (input: InvokeInput, environment?: InvokeEnvironment): AsyncGenerator<CanonicalEvent | HostFrame>;
124
+ }
125
+ export declare function assertInvokeInput(value: unknown): asserts value is InvokeInput;
126
+ /** 校验可读名称,不解析业务身份;同一约束用于公开输入与内部 Adapter wire。 */
127
+ export declare function assertSessionName(value: unknown): asserts value is string;
128
+ /** Lossless field reorganization of a validated prepared execution, excluding its local signal. */
129
+ export declare function invocationInput(request: InvokeRequest, protocol_mode?: "native" | "canonical", adapterType?: string): InvokeInput;
@@ -0,0 +1,177 @@
1
+ import { assertAgentSource, assertApprovalMode, assertContextEnvelope, assertModelInputParts, assertRuntimeLimits } from "./index.js";
2
+ import { assertJsonValue } from "./context.js";
3
+ import { canonicalEvents } from "./run-protocol.js";
4
+ import { assertJsonByteLimit } from "./json-size.js";
5
+ import { INVOKE_REQUEST_MAX_BYTES } from "./invoke-size.js";
6
+ /**
7
+ * Public JSON invocation contract shared by CLI, SDK and channels.
8
+ * A plain JSON object satisfies this class structurally; `new` is not required.
9
+ * Only inputs and contexts are required. Defaults are resolved by the execution
10
+ * boundary, never by adding historical business context.
11
+ */
12
+ export class InvokeInput {
13
+ /** Checks the JSON boundary; resolved execution additionally uses assertInvokeRequest. */
14
+ static check(value) {
15
+ assertInvokeInput(value);
16
+ return value;
17
+ }
18
+ }
19
+ /** Stable correlation IDs for cancellation and stream reconnection, without resolving execution. */
20
+ export function identifyInvokeInput(input) {
21
+ return {
22
+ ...input,
23
+ invocationId: input.invocationId ?? globalThis.crypto.randomUUID(),
24
+ runId: input.runId ?? globalThis.crypto.randomUUID(),
25
+ sessionId: input.sessionId ?? globalThis.crypto.randomUUID()
26
+ };
27
+ }
28
+ /** Select a view of one source stream; execution preparation belongs to the Host. */
29
+ export function createInputInvoke(source) {
30
+ return async function* (input, environment) {
31
+ environment?.signal?.throwIfAborted();
32
+ const frames = source(input, environment);
33
+ if (input.protocol_mode === "canonical")
34
+ yield* canonicalEvents(frames);
35
+ else
36
+ yield* frames;
37
+ // Only normal exhaustion reports local abort; never replace a source error or iterator return.
38
+ environment?.signal?.throwIfAborted();
39
+ };
40
+ }
41
+ export function assertInvokeInput(value) {
42
+ assertJsonByteLimit(value instanceof InvokeInput ? { ...value } : value, INVOKE_REQUEST_MAX_BYTES, "INVOKE_REQUEST_TOO_LARGE");
43
+ assertJsonValue(value);
44
+ if (!value || typeof value !== "object" || Array.isArray(value))
45
+ throw new TypeError("InvokeInput must be a JSON object");
46
+ const input = value;
47
+ if (["protocolMode", "adapterType", "request", "options"].some((key) => Object.hasOwn(input, key)))
48
+ throw new TypeError("InvokeInput requires the unified JSON contract, not a prepared request wrapper");
49
+ if (Object.hasOwn(input, "signal"))
50
+ throw new TypeError("signal belongs to the local invoke environment");
51
+ if (Object.hasOwn(input, "executionEnvOverrides"))
52
+ throw new TypeError("executionEnvOverrides belongs to the local invoke environment");
53
+ assertModelInputParts(input.inputs);
54
+ assertContextEnvelope(input.contexts);
55
+ if (input.sessionName !== undefined)
56
+ assertSessionName(input.sessionName);
57
+ for (const key of [
58
+ "sessionId",
59
+ "invocationId",
60
+ "runId",
61
+ "prompt",
62
+ "modelInput",
63
+ "freshFallbackModelInput"
64
+ ])
65
+ if (input[key] !== undefined && (typeof input[key] !== "string" || !input[key]))
66
+ throw new TypeError(`InvokeInput.${key} must be a non-empty string`);
67
+ for (const key of ["workdir", "controlInstructions"])
68
+ if (input[key] !== undefined && typeof input[key] !== "string")
69
+ throw new TypeError(`InvokeInput.${key} must be a string`);
70
+ for (const key of ["sessionGeneration", "providerEpoch"])
71
+ if (input[key] !== undefined &&
72
+ (!Number.isSafeInteger(input[key]) || input[key] < 0))
73
+ throw new TypeError(`InvokeInput.${key} must be a non-negative safe integer`);
74
+ if (input.protocol_mode !== undefined &&
75
+ !["native", "canonical"].includes(input.protocol_mode))
76
+ throw new TypeError("protocol_mode must be native or canonical");
77
+ if (input.agentSource !== undefined)
78
+ assertAgentSource(input.agentSource);
79
+ if (input.approvalMode !== undefined)
80
+ assertApprovalMode(input.approvalMode);
81
+ if (input.continuityMode !== undefined &&
82
+ !["fresh", "continue"].includes(input.continuityMode))
83
+ throw new TypeError("continuityMode must be fresh or continue");
84
+ if (input.runtimeLimits !== undefined)
85
+ assertRuntimeLimits(input.runtimeLimits);
86
+ if (input.freshFallbackModelInputParts !== undefined)
87
+ assertModelInputParts(input.freshFallbackModelInputParts);
88
+ if (input.freshFallbackImagesUnavailable !== undefined &&
89
+ typeof input.freshFallbackImagesUnavailable !== "boolean")
90
+ throw new TypeError("freshFallbackImagesUnavailable must be a boolean");
91
+ for (const key of ["agentConfig", "runMetadata", "providerSession"])
92
+ if (input[key] !== undefined &&
93
+ (!input[key] || typeof input[key] !== "object" || Array.isArray(input[key])))
94
+ throw new TypeError(`InvokeInput.${key} must be a JSON object`);
95
+ if (input.agentConfig) {
96
+ const config = input.agentConfig;
97
+ for (const key of [
98
+ "workdir",
99
+ "model",
100
+ "sandbox",
101
+ "providerAgentId",
102
+ "effort",
103
+ "openclawConfigPath",
104
+ "openclawStateDir"
105
+ ])
106
+ if (config[key] !== undefined && typeof config[key] !== "string")
107
+ throw new TypeError(`agentConfig.${key} must be a string`);
108
+ if (config.timeoutSeconds !== undefined &&
109
+ (typeof config.timeoutSeconds !== "number" || config.timeoutSeconds <= 0))
110
+ throw new TypeError("agentConfig.timeoutSeconds must be positive");
111
+ if (config.providerOptions !== undefined &&
112
+ (!config.providerOptions ||
113
+ typeof config.providerOptions !== "object" ||
114
+ Array.isArray(config.providerOptions)))
115
+ throw new TypeError("agentConfig.providerOptions must be a JSON object");
116
+ }
117
+ if (input.providerSession) {
118
+ const session = input.providerSession;
119
+ if (typeof session.sessionId !== "string" || !session.sessionId.trim())
120
+ throw new TypeError("providerSession.sessionId must be a non-empty string");
121
+ if (session.resumable !== undefined && typeof session.resumable !== "boolean")
122
+ throw new TypeError("providerSession.resumable must be a boolean");
123
+ if (session.requireResume !== undefined && typeof session.requireResume !== "boolean")
124
+ throw new TypeError("providerSession.requireResume must be a boolean");
125
+ if (session.resumeOutcome !== undefined &&
126
+ !["new", "resumed", "fresh_fallback", "compacted"].includes(session.resumeOutcome))
127
+ throw new TypeError("providerSession.resumeOutcome is invalid");
128
+ if (session.resumeFallback !== undefined && typeof session.resumeFallback !== "string")
129
+ throw new TypeError("providerSession.resumeFallback must be a string");
130
+ if (session.contextInputVariant !== undefined &&
131
+ !["primary", "recovery"].includes(session.contextInputVariant))
132
+ throw new TypeError("providerSession.contextInputVariant is invalid");
133
+ }
134
+ }
135
+ /** 校验可读名称,不解析业务身份;同一约束用于公开输入与内部 Adapter wire。 */
136
+ export function assertSessionName(value) {
137
+ if (typeof value !== "string" ||
138
+ !value.trim() ||
139
+ value.length > 512 ||
140
+ [...value].length > 256 ||
141
+ [...value].some((character) => {
142
+ const code = character.charCodeAt(0);
143
+ return code < 32 || (code >= 127 && code <= 159);
144
+ }))
145
+ throw new TypeError("sessionName must contain 1-256 characters, not only whitespace, without control characters");
146
+ }
147
+ /** Lossless field reorganization of a validated prepared execution, excluding its local signal. */
148
+ export function invocationInput(request, protocol_mode = "native", adapterType) {
149
+ const { protocolVersion: _version, signal: _signal, executionEnvOverrides: _executionEnvOverrides, agentSessionId, context, modelInputParts, ...fields } = request;
150
+ void _version;
151
+ void _signal;
152
+ void _executionEnvOverrides;
153
+ const contexts = structuredClone(context);
154
+ if (adapterType) {
155
+ const adapter = contexts.items.find((item) => item.namespace === "octo.execution" && item.name === "adapter");
156
+ if (adapter && adapter.payload.type !== adapterType)
157
+ throw new TypeError("CONTEXT_PLAN_MISMATCH");
158
+ if (!adapter)
159
+ contexts.items.push({
160
+ namespace: "octo.execution",
161
+ name: "adapter",
162
+ payload: {
163
+ type: adapterType,
164
+ ...(adapterType === "openclaw"
165
+ ? { provider_agent_id: request.agentConfig.providerAgentId ?? "main" }
166
+ : {})
167
+ }
168
+ });
169
+ }
170
+ return InvokeInput.check({
171
+ ...fields,
172
+ sessionId: agentSessionId,
173
+ contexts,
174
+ protocol_mode,
175
+ inputs: modelInputParts ?? [{ type: "text", text: request.modelInput }]
176
+ });
177
+ }
@@ -0,0 +1,3 @@
1
+ import type { InvokeInput, InvokeEnvironment } from "./invoke-input.js";
2
+ export declare const INVOKE_REQUEST_MAX_BYTES: number;
3
+ export declare function assertInvokeRequestSize(input: InvokeInput, environment?: InvokeEnvironment): void;
@@ -0,0 +1,11 @@
1
+ import { assertJsonByteLimit } from "./json-size.js";
2
+ export const INVOKE_REQUEST_MAX_BYTES = 32 * 1024 * 1024;
3
+ export function assertInvokeRequestSize(input, environment = {}) {
4
+ assertJsonByteLimit({
5
+ input: { ...input },
6
+ ...(environment.executionEnvOverrides
7
+ ? { executionEnvOverrides: environment.executionEnvOverrides }
8
+ : {}),
9
+ ...(environment.streamLimits ? { streamLimits: environment.streamLimits } : {})
10
+ }, INVOKE_REQUEST_MAX_BYTES, "INVOKE_REQUEST_TOO_LARGE");
11
+ }
@@ -0,0 +1,2 @@
1
+ /** Count JSON bytes without allocating the complete serialized input. */
2
+ export declare function assertJsonByteLimit(value: unknown, limit: number, code: string): void;
@@ -0,0 +1,91 @@
1
+ /** Count JSON bytes without allocating the complete serialized input. */
2
+ export function assertJsonByteLimit(value, limit, code) {
3
+ let bytes = 0;
4
+ const parents = new Set();
5
+ const add = (count) => {
6
+ bytes += count;
7
+ if (bytes > limit)
8
+ throw Object.assign(new Error(code), { code });
9
+ };
10
+ const string = (text) => {
11
+ add(2);
12
+ for (let i = 0; i < text.length; i++) {
13
+ const char = text.charCodeAt(i);
14
+ if (char === 34 || char === 92 || [8, 9, 10, 12, 13].includes(char))
15
+ add(2);
16
+ else if (char < 32)
17
+ add(6);
18
+ else if (char < 128)
19
+ add(1);
20
+ else if (char < 2048)
21
+ add(2);
22
+ else if (char >= 0xd800 && char <= 0xdbff) {
23
+ const low = text.charCodeAt(i + 1);
24
+ if (low >= 0xdc00 && low <= 0xdfff) {
25
+ add(4);
26
+ i++;
27
+ }
28
+ else
29
+ add(6);
30
+ }
31
+ else if (char >= 0xdc00 && char <= 0xdfff)
32
+ add(6);
33
+ else
34
+ add(3);
35
+ }
36
+ };
37
+ const visit = (item, depth) => {
38
+ if (depth > 64)
39
+ throw new TypeError("JSON_DEPTH_EXCEEDED");
40
+ if (typeof item === "string") {
41
+ string(item);
42
+ return;
43
+ }
44
+ if (item === null || typeof item === "boolean" || typeof item === "number") {
45
+ add(JSON.stringify(item).length);
46
+ return;
47
+ }
48
+ if (typeof item !== "object")
49
+ throw new TypeError("JSON_VALUE_REQUIRED");
50
+ if (parents.has(item))
51
+ throw new TypeError("JSON_CIRCULAR_REFERENCE");
52
+ const array = Array.isArray(item);
53
+ if (!array &&
54
+ Object.getPrototypeOf(item) !== Object.prototype &&
55
+ Object.getPrototypeOf(item) !== null)
56
+ throw new TypeError("JSON_OBJECT_REQUIRED");
57
+ parents.add(item);
58
+ add(2);
59
+ let first = true;
60
+ if (array) {
61
+ for (let i = 0; i < item.length; i++) {
62
+ const descriptor = Object.getOwnPropertyDescriptor(item, String(i));
63
+ if (descriptor && !("value" in descriptor))
64
+ throw new TypeError("JSON_ACCESSOR_UNSUPPORTED");
65
+ if (!first)
66
+ add(1);
67
+ first = false;
68
+ visit(descriptor?.value ?? null, depth + 1);
69
+ }
70
+ }
71
+ else {
72
+ for (const key in item) {
73
+ const descriptor = Object.getOwnPropertyDescriptor(item, key);
74
+ if (!descriptor)
75
+ continue;
76
+ if (!("value" in descriptor))
77
+ throw new TypeError("JSON_ACCESSOR_UNSUPPORTED");
78
+ if (descriptor.value === undefined)
79
+ continue;
80
+ if (!first)
81
+ add(1);
82
+ first = false;
83
+ string(key);
84
+ add(1);
85
+ visit(descriptor.value, depth + 1);
86
+ }
87
+ }
88
+ parents.delete(item);
89
+ };
90
+ visit(value, 0);
91
+ }
@@ -0,0 +1,3 @@
1
+ import type { ContextEnvelope } from "./generated/protocol-types.js";
2
+ /** Sources are validated by the producer's authorization boundary, not by rendering. */
3
+ export declare function contextControlInstructions(context: ContextEnvelope): string;
@@ -0,0 +1,9 @@
1
+ import { assertContextEnvelope, contextItem } from "./context.js";
2
+ /** Sources are validated by the producer's authorization boundary, not by rendering. */
3
+ export function contextControlInstructions(context) {
4
+ assertContextEnvelope(context);
5
+ const entries = contextItem(context, "octo.control", "instructions")?.payload.entries;
6
+ return Array.isArray(entries)
7
+ ? entries.map((entry) => entry.text).join("\n")
8
+ : "";
9
+ }
@@ -0,0 +1,15 @@
1
+ import type { InvokeRequestWire, ModelInputPart } from "./generated/protocol-types.js";
2
+ export declare const IMAGE_INPUT_LIMITS: Readonly<{
3
+ maxCount: 4;
4
+ maxBytes: number;
5
+ maxTotalBytes: number;
6
+ }>;
7
+ export declare const IMAGE_INPUT_MIME_TYPES: readonly ["image/png", "image/jpeg", "image/webp"];
8
+ export declare function assertModelInputParts(value: unknown): asserts value is ModelInputPart[];
9
+ /** Never fall back to the display string when an invocation contains images. */
10
+ export declare function providerInputParts(request: InvokeRequestWire, freshFallback?: boolean): ModelInputPart[];
11
+ /** Current-turn images must survive recovery, even when historical images are prepended. */
12
+ export declare function assertImageRecoveryParts(request: Pick<InvokeRequestWire, "modelInputParts" | "freshFallbackModelInputParts" | "freshFallbackModelInput">): void;
13
+ export declare function hasImageInput(request: Pick<InvokeRequestWire, "modelInputParts" | "freshFallbackModelInputParts">): boolean;
14
+ /** Artifact sources must already have been fetched under the Run's authorization. */
15
+ export declare function modelPartsFromInput(input: Record<string, unknown>, prefix?: string): ModelInputPart[] | undefined;
@@ -0,0 +1,166 @@
1
+ import { createHash } from "node:crypto";
2
+ export const IMAGE_INPUT_LIMITS = Object.freeze({
3
+ maxCount: 4,
4
+ maxBytes: 2 * 1024 * 1024,
5
+ maxTotalBytes: 8 * 1024 * 1024
6
+ });
7
+ export const IMAGE_INPUT_MIME_TYPES = Object.freeze([
8
+ "image/png",
9
+ "image/jpeg",
10
+ "image/webp"
11
+ ]);
12
+ function record(value) {
13
+ if (!value || typeof value !== "object" || Array.isArray(value))
14
+ throw new TypeError("INPUT_PART_INVALID");
15
+ return value;
16
+ }
17
+ export function assertModelInputParts(value) {
18
+ if (!Array.isArray(value) || value.length === 0 || value.length > 128)
19
+ throw new TypeError("INPUT_PARTS_INVALID");
20
+ let count = 0;
21
+ let total = 0;
22
+ const ids = new Set();
23
+ for (const item of value) {
24
+ const part = record(item);
25
+ if (part.id !== undefined && (typeof part.id !== "string" || !part.id))
26
+ throw new TypeError("INPUT_PART_INVALID");
27
+ if (typeof part.id === "string") {
28
+ if (ids.has(part.id))
29
+ throw new TypeError("INPUT_PART_INVALID");
30
+ ids.add(part.id);
31
+ }
32
+ const allowed = part.type === "text"
33
+ ? ["id", "type", "text"]
34
+ : ["id", "type", "mimeType", "data", "byteSize", "sha256"];
35
+ if (Object.keys(part).some((key) => !allowed.includes(key)))
36
+ throw new TypeError("INPUT_PART_INVALID");
37
+ if (part.type === "text") {
38
+ if (typeof part.text !== "string")
39
+ throw new TypeError("INPUT_PART_INVALID");
40
+ continue;
41
+ }
42
+ if (part.type !== "image")
43
+ throw new TypeError("UNSUPPORTED_INPUT_MODALITY");
44
+ if (!IMAGE_INPUT_MIME_TYPES.includes(part.mimeType))
45
+ throw new TypeError("UNSUPPORTED_INPUT_MODALITY");
46
+ if (!Number.isSafeInteger(part.byteSize) ||
47
+ Number(part.byteSize) < 1 ||
48
+ Number(part.byteSize) > IMAGE_INPUT_LIMITS.maxBytes ||
49
+ typeof part.data !== "string" ||
50
+ part.data.length > Math.ceil(IMAGE_INPUT_LIMITS.maxBytes / 3) * 4)
51
+ throw new TypeError("IMAGE_LIMIT_EXCEEDED");
52
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(part.data))
53
+ throw new TypeError("ARTIFACT_INTEGRITY_MISMATCH");
54
+ const bytes = Buffer.from(part.data, "base64");
55
+ if (bytes.length !== part.byteSize ||
56
+ bytes.toString("base64") !== part.data ||
57
+ typeof part.sha256 !== "string" ||
58
+ !/^[a-f0-9]{64}$/.test(part.sha256) ||
59
+ createHash("sha256").update(bytes).digest("hex") !== part.sha256)
60
+ throw new TypeError("ARTIFACT_INTEGRITY_MISMATCH");
61
+ const validHeader = part.mimeType === "image/png"
62
+ ? bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))
63
+ : part.mimeType === "image/jpeg"
64
+ ? bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff
65
+ : bytes.subarray(0, 4).toString("ascii") === "RIFF" &&
66
+ bytes.subarray(8, 12).toString("ascii") === "WEBP";
67
+ if (!validHeader)
68
+ throw new TypeError("ARTIFACT_INTEGRITY_MISMATCH");
69
+ count += 1;
70
+ total += bytes.length;
71
+ if (count > IMAGE_INPUT_LIMITS.maxCount || total > IMAGE_INPUT_LIMITS.maxTotalBytes)
72
+ throw new TypeError("IMAGE_LIMIT_EXCEEDED");
73
+ }
74
+ }
75
+ /** Never fall back to the display string when an invocation contains images. */
76
+ export function providerInputParts(request, freshFallback = false) {
77
+ if (freshFallback && request.freshFallbackImagesUnavailable)
78
+ throw new TypeError("ARTIFACT_UNAVAILABLE: historical images cannot be restored");
79
+ assertImageRecoveryParts(request);
80
+ const parts = freshFallback
81
+ ? (request.freshFallbackModelInputParts ?? request.modelInputParts)
82
+ : request.modelInputParts;
83
+ if (parts) {
84
+ assertModelInputParts(parts);
85
+ return structuredClone(parts);
86
+ }
87
+ return [
88
+ {
89
+ type: "text",
90
+ text: freshFallback
91
+ ? (request.freshFallbackModelInput ?? request.modelInput)
92
+ : request.modelInput
93
+ }
94
+ ];
95
+ }
96
+ /** Current-turn images must survive recovery, even when historical images are prepended. */
97
+ export function assertImageRecoveryParts(request) {
98
+ const current = request.modelInputParts?.filter((part) => part.type === "image") ?? [];
99
+ if (!current.length)
100
+ return;
101
+ if (request.freshFallbackModelInput !== undefined &&
102
+ request.freshFallbackModelInputParts === undefined)
103
+ throw new TypeError("IMAGE_RECOVERY_INPUT_REQUIRED");
104
+ if (request.freshFallbackModelInputParts === undefined)
105
+ return;
106
+ const recovery = request.freshFallbackModelInputParts.filter((part) => part.type === "image");
107
+ const key = (part) => part.id ?? `${part.mimeType}:${part.byteSize}:${part.sha256}`;
108
+ for (const part of current) {
109
+ if (current.filter((item) => key(item) === key(part)).length !==
110
+ recovery.filter((item) => key(item) === key(part)).length)
111
+ throw new TypeError("IMAGE_RECOVERY_INPUT_REQUIRED");
112
+ }
113
+ let index = 0;
114
+ for (const part of recovery) {
115
+ const expected = current[index];
116
+ if (expected &&
117
+ expected.sha256 === part.sha256 &&
118
+ expected.byteSize === part.byteSize &&
119
+ expected.mimeType === part.mimeType &&
120
+ (expected.id === undefined || expected.id === part.id))
121
+ index += 1;
122
+ }
123
+ if (index !== current.length)
124
+ throw new TypeError("IMAGE_RECOVERY_INPUT_REQUIRED");
125
+ }
126
+ export function hasImageInput(request) {
127
+ return [...(request.modelInputParts ?? []), ...(request.freshFallbackModelInputParts ?? [])].some((part) => part.type === "image");
128
+ }
129
+ /** Artifact sources must already have been fetched under the Run's authorization. */
130
+ export function modelPartsFromInput(input, prefix = "") {
131
+ const parts = Array.isArray(input.parts) ? input.parts : [];
132
+ if (!parts.some((part) => part && typeof part === "object" && part.type === "image"))
133
+ return undefined;
134
+ const output = prefix ? [{ type: "text", text: prefix }] : [];
135
+ for (const value of parts) {
136
+ const part = record(value);
137
+ const id = typeof part.id === "string" ? { id: part.id } : {};
138
+ if (part.type === "text") {
139
+ if (typeof part.text !== "string")
140
+ throw new TypeError("INPUT_PART_INVALID");
141
+ output.push({ type: "text", text: part.text, ...id });
142
+ }
143
+ else if (part.type === "image") {
144
+ if (!part.source)
145
+ throw new TypeError("ARTIFACT_UNAVAILABLE");
146
+ const source = record(part.source);
147
+ if (Object.keys(source).some((key) => !["mimeType", "data", "byteSize", "sha256"].includes(key)))
148
+ throw new TypeError("INPUT_PART_INVALID");
149
+ const image = { ...source, type: "image", ...id };
150
+ assertModelInputParts([image]);
151
+ if (part.artifact !== undefined) {
152
+ const artifact = record(part.artifact);
153
+ if (artifact.mediaType !== source.mimeType ||
154
+ artifact.byteSize !== source.byteSize ||
155
+ artifact.sha256 !== source.sha256)
156
+ throw new TypeError("ARTIFACT_INTEGRITY_MISMATCH");
157
+ }
158
+ output.push(image);
159
+ }
160
+ else {
161
+ throw new TypeError("UNSUPPORTED_INPUT_MODALITY");
162
+ }
163
+ }
164
+ assertModelInputParts(output);
165
+ return output;
166
+ }
@@ -0,0 +1,2 @@
1
+ import type { NotificationMessage } from "./generated/protocol-types.js";
2
+ export declare function assertNotificationMessage(value: unknown): asserts value is NotificationMessage;