@odla-ai/harness 0.8.1 → 0.9.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/README.md +11 -0
- package/dist/{chunk-IWVGSWY6.js → chunk-4EPJJMFG.js} +330 -106
- package/dist/chunk-4EPJJMFG.js.map +1 -0
- package/dist/{chunk-NWNBSX56.js → chunk-CIKYMC67.js} +4 -4
- package/dist/{chunk-CR6RE3A2.js → chunk-HDIR4MM5.js} +3 -3
- package/dist/{chunk-RXNHCGWE.js → chunk-I43KTCJ2.js} +1 -1
- package/dist/{chunk-RXNHCGWE.js.map → chunk-I43KTCJ2.js.map} +1 -1
- package/dist/{chunk-Q7CKOT7T.js → chunk-VGNIDRKM.js} +2 -2
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/code-runtime-cli.cjs +330 -102
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +12 -5
- package/dist/code-runtime-cli.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/node.cjs +327 -101
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +71 -17
- package/dist/node.d.ts +71 -17
- package/dist/node.js +9 -5
- package/dist/node.js.map +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/{types-BNJikP5h.d.cts → types-BazqxWK8.d.cts} +14 -0
- package/dist/{types-BNJikP5h.d.ts → types-BazqxWK8.d.ts} +14 -0
- package/package.json +2 -2
- package/dist/chunk-IWVGSWY6.js.map +0 -1
- /package/dist/{chunk-NWNBSX56.js.map → chunk-CIKYMC67.js.map} +0 -0
- /package/dist/{chunk-CR6RE3A2.js.map → chunk-HDIR4MM5.js.map} +0 -0
- /package/dist/{chunk-Q7CKOT7T.js.map → chunk-VGNIDRKM.js.map} +0 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
var import_node_os3 = require("os");
|
|
6
6
|
var import_promises10 = require("fs/promises");
|
|
7
7
|
|
|
8
|
-
// src/code-runtime-client.ts
|
|
8
|
+
// src/code-runtime-client-validation.ts
|
|
9
9
|
var import_code = require("@odla-ai/camel/code");
|
|
10
10
|
var CodeRuntimeControlError = class extends Error {
|
|
11
11
|
constructor(message2, status, code = "control_error") {
|
|
@@ -17,101 +17,6 @@ var CodeRuntimeControlError = class extends Error {
|
|
|
17
17
|
code;
|
|
18
18
|
name = "CodeRuntimeControlError";
|
|
19
19
|
};
|
|
20
|
-
function createCodeRuntimeControlClient(options) {
|
|
21
|
-
const endpoint = validatedEndpoint(options.endpoint);
|
|
22
|
-
if (!/^odla_code_host_[0-9a-f]{64}$/.test(options.token)) throw new TypeError("invalid Code host credential");
|
|
23
|
-
const requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
|
|
24
|
-
if (!Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 1e3 || requestTimeoutMs > 12e4) {
|
|
25
|
-
throw new TypeError("requestTimeoutMs must be an integer from 1000 to 120000");
|
|
26
|
-
}
|
|
27
|
-
const modelRequestTimeoutMs = options.modelRequestTimeoutMs ?? 15 * 6e4;
|
|
28
|
-
if (!Number.isSafeInteger(modelRequestTimeoutMs) || modelRequestTimeoutMs < 3e4 || modelRequestTimeoutMs > 30 * 6e4) {
|
|
29
|
-
throw new TypeError("modelRequestTimeoutMs must be an integer from 30000 to 1800000");
|
|
30
|
-
}
|
|
31
|
-
const request = options.fetch ?? fetch;
|
|
32
|
-
const call = async (path, body, timeoutMs = requestTimeoutMs) => {
|
|
33
|
-
const timeout = AbortSignal.timeout(timeoutMs);
|
|
34
|
-
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
35
|
-
let response2;
|
|
36
|
-
try {
|
|
37
|
-
response2 = await request(`${endpoint}${path}`, {
|
|
38
|
-
method: "POST",
|
|
39
|
-
headers: { authorization: `Bearer ${options.token}`, "content-type": "application/json" },
|
|
40
|
-
body: JSON.stringify(body),
|
|
41
|
-
redirect: "error",
|
|
42
|
-
signal
|
|
43
|
-
});
|
|
44
|
-
} catch (cause) {
|
|
45
|
-
if (options.signal?.aborted) throw cause;
|
|
46
|
-
throw new CodeRuntimeControlError("Code runtime control plane is unavailable", 503, "transport_unavailable");
|
|
47
|
-
}
|
|
48
|
-
const value = await response2.json().catch(() => null);
|
|
49
|
-
if (!response2.ok) {
|
|
50
|
-
const problem = record(record(value)?.error);
|
|
51
|
-
throw new CodeRuntimeControlError(
|
|
52
|
-
typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
|
|
53
|
-
response2.status,
|
|
54
|
-
typeof problem?.code === "string" ? problem.code : void 0
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
return value;
|
|
58
|
-
};
|
|
59
|
-
return {
|
|
60
|
-
heartbeat: async (version, capabilities) => {
|
|
61
|
-
validateHeartbeat(version, capabilities);
|
|
62
|
-
return parseSnapshot(await call("/registry/code/runtime/heartbeat", { runtimeVersion: version, capabilities }));
|
|
63
|
-
},
|
|
64
|
-
acknowledge: async (commandId, result) => {
|
|
65
|
-
if (!/^ccmd_[0-9a-f]{32}$/.test(commandId)) throw new TypeError("invalid Code runtime command id");
|
|
66
|
-
await call(`/registry/code/runtime/commands/${commandId}/ack`, result);
|
|
67
|
-
},
|
|
68
|
-
source: async (sessionId) => parseSource(
|
|
69
|
-
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
70
|
-
),
|
|
71
|
-
infer: async (sessionId, inference) => {
|
|
72
|
-
const value = record(await call(
|
|
73
|
-
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
74
|
-
inference,
|
|
75
|
-
modelRequestTimeoutMs
|
|
76
|
-
));
|
|
77
|
-
if (!value || value.requestId !== inference.requestId || !record(value.response) || !record(value.receipt)) {
|
|
78
|
-
throw new CodeRuntimeControlError("invalid Code inference response", 502, "invalid_response");
|
|
79
|
-
}
|
|
80
|
-
return value;
|
|
81
|
-
},
|
|
82
|
-
review: async (sessionId, review) => parseReview(
|
|
83
|
-
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/review`, review, modelRequestTimeoutMs)
|
|
84
|
-
),
|
|
85
|
-
submitCandidate: async (sessionId, checkpointId, verification) => {
|
|
86
|
-
if (!/^cpoint_[0-9a-f]{32}$/.test(checkpointId)) throw new TypeError("invalid Code checkpoint id");
|
|
87
|
-
return parseCandidate(await call(
|
|
88
|
-
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/candidates`,
|
|
89
|
-
{ checkpointId, verification }
|
|
90
|
-
));
|
|
91
|
-
},
|
|
92
|
-
appendSessionEvent: async (sessionId, eventId, event) => {
|
|
93
|
-
const serialized = JSON.stringify(event);
|
|
94
|
-
if (!/^[A-Za-z0-9._:-]{1,120}$/.test(eventId) || !event || typeof event !== "object" || new TextEncoder().encode(serialized).byteLength > 24e3) {
|
|
95
|
-
throw new TypeError("invalid Code session event");
|
|
96
|
-
}
|
|
97
|
-
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/chat/events`, { eventId, event });
|
|
98
|
-
},
|
|
99
|
-
recallMemories: async (sessionId, subjects, limit) => {
|
|
100
|
-
const response2 = await call(
|
|
101
|
-
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/recall`,
|
|
102
|
-
{ subjects: [...subjects], limit }
|
|
103
|
-
);
|
|
104
|
-
return Array.isArray(response2.memories) ? response2.memories : [];
|
|
105
|
-
},
|
|
106
|
-
rememberMemory: async (sessionId, memory) => {
|
|
107
|
-
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/remember`, memory);
|
|
108
|
-
},
|
|
109
|
-
reportSessionFailure: async (sessionId, message2) => {
|
|
110
|
-
if (!message2.trim() || message2.length > 2e3) throw new TypeError("invalid Code session failure");
|
|
111
|
-
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/failure`, { message: message2 });
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
20
|
function validatedEndpoint(value) {
|
|
116
21
|
const endpoint = value.replace(/\/+$/, "");
|
|
117
22
|
let url;
|
|
@@ -130,6 +35,10 @@ function validSessionId(value) {
|
|
|
130
35
|
if (!/^csess_[0-9a-f]{32}$/.test(value)) throw new TypeError("invalid Code session id");
|
|
131
36
|
return value;
|
|
132
37
|
}
|
|
38
|
+
function validCommandId(value) {
|
|
39
|
+
if (!/^ccmd_[0-9a-f]{32}$/.test(value)) throw new TypeError("invalid Code runtime command id");
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
133
42
|
function validateHeartbeat(version, capabilities) {
|
|
134
43
|
if (!version.trim() || version.length > 80) throw new TypeError("runtimeVersion is required and at most 80 characters");
|
|
135
44
|
if (capabilities.protocolVersion !== CODE_RUNTIME_PROTOCOL_VERSION) throw new TypeError("unsupported Code runtime protocol version");
|
|
@@ -211,9 +120,211 @@ function parseCandidate(value) {
|
|
|
211
120
|
}
|
|
212
121
|
return { candidateId: candidate.candidateId, status: candidate.status };
|
|
213
122
|
}
|
|
123
|
+
function parseCollaborationSkills(value) {
|
|
124
|
+
const items = record(value)?.skills;
|
|
125
|
+
if (!Array.isArray(items) || items.length > 16) throw invalid("collaboration skills");
|
|
126
|
+
const skillNames = /* @__PURE__ */ new Set();
|
|
127
|
+
const toolNames = /* @__PURE__ */ new Set();
|
|
128
|
+
return items.map((item) => {
|
|
129
|
+
const skill = record(item);
|
|
130
|
+
if (!skill || !validManifestName(skill.name) || skillNames.has(skill.name) || skill.instructions !== void 0 && (typeof skill.instructions !== "string" || utf8Bytes(skill.instructions) > 32e3) || !Array.isArray(skill.tools) || !skill.tools.length || skill.tools.length > 128) {
|
|
131
|
+
throw invalid("collaboration skill");
|
|
132
|
+
}
|
|
133
|
+
skillNames.add(skill.name);
|
|
134
|
+
const tools = skill.tools.map((candidate) => {
|
|
135
|
+
const tool = record(candidate);
|
|
136
|
+
const inputSchema = record(tool?.inputSchema);
|
|
137
|
+
if (!tool || !validManifestName(tool.name) || toolNames.has(tool.name) || typeof tool.description !== "string" || utf8Bytes(tool.description) > 8e3 || !inputSchema || jsonBytes(inputSchema) > 64e3 || tool.concurrency !== void 0 && tool.concurrency !== "parallel") {
|
|
138
|
+
throw invalid("collaboration tool");
|
|
139
|
+
}
|
|
140
|
+
const outputTaint = parseTaintLabels(tool.outputTaint);
|
|
141
|
+
const acceptsTaint = parseTaintLabels(tool.acceptsTaint);
|
|
142
|
+
toolNames.add(tool.name);
|
|
143
|
+
return {
|
|
144
|
+
name: tool.name,
|
|
145
|
+
description: tool.description,
|
|
146
|
+
inputSchema,
|
|
147
|
+
...tool.concurrency === "parallel" ? { concurrency: "parallel" } : {},
|
|
148
|
+
...outputTaint ? { outputTaint } : {},
|
|
149
|
+
...acceptsTaint ? { acceptsTaint } : {}
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
return {
|
|
153
|
+
name: skill.name,
|
|
154
|
+
...typeof skill.instructions === "string" ? { instructions: skill.instructions } : {},
|
|
155
|
+
tools
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function validateCollaborationToolRequest(value) {
|
|
160
|
+
validCommandId(value.commandId);
|
|
161
|
+
if (typeof value.toolCallId !== "string" || value.toolCallId.length > 256 || !/^[^\s\u0000-\u001f\u007f]+$/.test(value.toolCallId) || !validManifestName(value.skill) || !validManifestName(value.tool) || !record(value.input) || jsonBytes(value.input) > 128e3) {
|
|
162
|
+
throw new TypeError("invalid Code collaboration tool request");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function parseCollaborationToolOutput(value) {
|
|
166
|
+
const output = record(record(value)?.output);
|
|
167
|
+
if (!output || output.isError !== void 0 && typeof output.isError !== "boolean") {
|
|
168
|
+
throw invalid("collaboration tool");
|
|
169
|
+
}
|
|
170
|
+
if (typeof output.content === "string") {
|
|
171
|
+
if (utf8Bytes(output.content) > 1e6) throw invalid("collaboration tool");
|
|
172
|
+
return { content: output.content, ...output.isError === true ? { isError: true } : {} };
|
|
173
|
+
}
|
|
174
|
+
if (!Array.isArray(output.content) || output.content.length > 64 || jsonBytes(output.content) > 1e6 || !output.content.every((block) => {
|
|
175
|
+
const item = record(block);
|
|
176
|
+
return item && ["text", "image", "audio", "document", "tool_use", "tool_result", "thinking"].includes(String(item.type));
|
|
177
|
+
})) throw invalid("collaboration tool");
|
|
178
|
+
return {
|
|
179
|
+
content: output.content,
|
|
180
|
+
...output.isError === true ? { isError: true } : {}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function parseTaintLabels(value) {
|
|
184
|
+
if (value === void 0) return void 0;
|
|
185
|
+
if (!Array.isArray(value) || value.length > 16) throw invalid("collaboration tool taint");
|
|
186
|
+
const labels = value.map((item) => {
|
|
187
|
+
if (item === "web_untrusted" || item === "operator_pasted_untrusted" || item === "llm_inherited") return item;
|
|
188
|
+
if (typeof item === "string" && /^tool_untrusted:[^\s\u0000-\u001f\u007f]{1,100}$/.test(item)) {
|
|
189
|
+
return item;
|
|
190
|
+
}
|
|
191
|
+
throw invalid("collaboration tool taint");
|
|
192
|
+
});
|
|
193
|
+
return [...new Set(labels)];
|
|
194
|
+
}
|
|
195
|
+
function validManifestName(value) {
|
|
196
|
+
return typeof value === "string" && /^[A-Za-z][A-Za-z0-9._:-]{0,127}$/.test(value);
|
|
197
|
+
}
|
|
198
|
+
function utf8Bytes(value) {
|
|
199
|
+
return new TextEncoder().encode(value).byteLength;
|
|
200
|
+
}
|
|
201
|
+
function jsonBytes(value) {
|
|
202
|
+
try {
|
|
203
|
+
return utf8Bytes(JSON.stringify(value));
|
|
204
|
+
} catch {
|
|
205
|
+
return Number.POSITIVE_INFINITY;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
214
208
|
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
215
209
|
var invalid = (part) => new CodeRuntimeControlError(`invalid Code runtime ${part} response`, 502, "invalid_response");
|
|
216
210
|
|
|
211
|
+
// src/code-runtime-client.ts
|
|
212
|
+
function createCodeRuntimeControlClient(options) {
|
|
213
|
+
const endpoint = validatedEndpoint(options.endpoint);
|
|
214
|
+
if (!/^odla_code_host_[0-9a-f]{64}$/.test(options.token)) throw new TypeError("invalid Code host credential");
|
|
215
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
|
|
216
|
+
if (!Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 1e3 || requestTimeoutMs > 12e4) {
|
|
217
|
+
throw new TypeError("requestTimeoutMs must be an integer from 1000 to 120000");
|
|
218
|
+
}
|
|
219
|
+
const modelRequestTimeoutMs = options.modelRequestTimeoutMs ?? 15 * 6e4;
|
|
220
|
+
if (!Number.isSafeInteger(modelRequestTimeoutMs) || modelRequestTimeoutMs < 3e4 || modelRequestTimeoutMs > 30 * 6e4) {
|
|
221
|
+
throw new TypeError("modelRequestTimeoutMs must be an integer from 30000 to 1800000");
|
|
222
|
+
}
|
|
223
|
+
const request = options.fetch ?? fetch;
|
|
224
|
+
const call = async (path, body, timeoutMs = requestTimeoutMs, operationSignal) => {
|
|
225
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
226
|
+
const signals = [options.signal, operationSignal, timeout].filter((item) => Boolean(item));
|
|
227
|
+
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
228
|
+
let response2;
|
|
229
|
+
try {
|
|
230
|
+
response2 = await request(`${endpoint}${path}`, {
|
|
231
|
+
method: "POST",
|
|
232
|
+
headers: { authorization: `Bearer ${options.token}`, "content-type": "application/json" },
|
|
233
|
+
body: JSON.stringify(body),
|
|
234
|
+
redirect: "error",
|
|
235
|
+
signal
|
|
236
|
+
});
|
|
237
|
+
} catch (cause) {
|
|
238
|
+
if (options.signal?.aborted || operationSignal?.aborted) throw cause;
|
|
239
|
+
throw new CodeRuntimeControlError("Code runtime control plane is unavailable", 503, "transport_unavailable");
|
|
240
|
+
}
|
|
241
|
+
const value = await response2.json().catch(() => null);
|
|
242
|
+
if (!response2.ok) {
|
|
243
|
+
const problem = record(record(value)?.error);
|
|
244
|
+
throw new CodeRuntimeControlError(
|
|
245
|
+
typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
|
|
246
|
+
response2.status,
|
|
247
|
+
typeof problem?.code === "string" ? problem.code : void 0
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
return value;
|
|
251
|
+
};
|
|
252
|
+
return {
|
|
253
|
+
heartbeat: async (version, capabilities) => {
|
|
254
|
+
validateHeartbeat(version, capabilities);
|
|
255
|
+
return parseSnapshot(await call("/registry/code/runtime/heartbeat", { runtimeVersion: version, capabilities }));
|
|
256
|
+
},
|
|
257
|
+
acknowledge: async (commandId, result) => {
|
|
258
|
+
await call(`/registry/code/runtime/commands/${validCommandId(commandId)}/ack`, result);
|
|
259
|
+
},
|
|
260
|
+
source: async (sessionId) => parseSource(
|
|
261
|
+
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
262
|
+
),
|
|
263
|
+
infer: async (sessionId, inference) => {
|
|
264
|
+
const value = record(await call(
|
|
265
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
266
|
+
inference,
|
|
267
|
+
modelRequestTimeoutMs
|
|
268
|
+
));
|
|
269
|
+
if (!value || value.requestId !== inference.requestId || !record(value.response) || !record(value.receipt)) {
|
|
270
|
+
throw new CodeRuntimeControlError("invalid Code inference response", 502, "invalid_response");
|
|
271
|
+
}
|
|
272
|
+
return value;
|
|
273
|
+
},
|
|
274
|
+
review: async (sessionId, review) => parseReview(
|
|
275
|
+
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/review`, review, modelRequestTimeoutMs)
|
|
276
|
+
),
|
|
277
|
+
submitCandidate: async (sessionId, checkpointId, verification) => {
|
|
278
|
+
if (!/^cpoint_[0-9a-f]{32}$/.test(checkpointId)) throw new TypeError("invalid Code checkpoint id");
|
|
279
|
+
return parseCandidate(await call(
|
|
280
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/candidates`,
|
|
281
|
+
{ checkpointId, verification }
|
|
282
|
+
));
|
|
283
|
+
},
|
|
284
|
+
appendSessionEvent: async (sessionId, eventId, event) => {
|
|
285
|
+
const serialized = JSON.stringify(event);
|
|
286
|
+
if (!/^[A-Za-z0-9._:-]{1,120}$/.test(eventId) || !event || typeof event !== "object" || new TextEncoder().encode(serialized).byteLength > 24e3) {
|
|
287
|
+
throw new TypeError("invalid Code session event");
|
|
288
|
+
}
|
|
289
|
+
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/chat/events`, { eventId, event });
|
|
290
|
+
},
|
|
291
|
+
recallMemories: async (sessionId, subjects, limit) => {
|
|
292
|
+
const response2 = await call(
|
|
293
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/recall`,
|
|
294
|
+
{ subjects: [...subjects], limit }
|
|
295
|
+
);
|
|
296
|
+
return Array.isArray(response2.memories) ? response2.memories : [];
|
|
297
|
+
},
|
|
298
|
+
rememberMemory: async (sessionId, memory) => {
|
|
299
|
+
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/remember`, memory);
|
|
300
|
+
},
|
|
301
|
+
collaborationSkills: async (sessionId, commandId) => {
|
|
302
|
+
try {
|
|
303
|
+
return parseCollaborationSkills(await call(
|
|
304
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/collaboration/skills`,
|
|
305
|
+
{ commandId: validCommandId(commandId) }
|
|
306
|
+
));
|
|
307
|
+
} catch (cause) {
|
|
308
|
+
if (cause instanceof CodeRuntimeControlError && cause.status === 404 && cause.code === "not_found") return [];
|
|
309
|
+
throw cause;
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
executeCollaborationTool: async (sessionId, collaboration, signal) => {
|
|
313
|
+
validateCollaborationToolRequest(collaboration);
|
|
314
|
+
return parseCollaborationToolOutput(await call(
|
|
315
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/collaboration/tools`,
|
|
316
|
+
collaboration,
|
|
317
|
+
requestTimeoutMs,
|
|
318
|
+
signal
|
|
319
|
+
));
|
|
320
|
+
},
|
|
321
|
+
reportSessionFailure: async (sessionId, message2) => {
|
|
322
|
+
if (!message2.trim() || message2.length > 2e3) throw new TypeError("invalid Code session failure");
|
|
323
|
+
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/failure`, { message: message2 });
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
217
328
|
// src/code-runtime.ts
|
|
218
329
|
var CODE_RUNTIME_PROTOCOL_VERSION = 1;
|
|
219
330
|
async function runCodeRuntimeHeartbeatLoop(options) {
|
|
@@ -240,12 +351,14 @@ async function runCodeRuntimeHeartbeatLoop(options) {
|
|
|
240
351
|
} while (!options.signal?.aborted);
|
|
241
352
|
}
|
|
242
353
|
var CodeRuntimeReconciler = class {
|
|
243
|
-
constructor(control, engine) {
|
|
354
|
+
constructor(control, engine, onDiagnostic) {
|
|
244
355
|
this.control = control;
|
|
245
356
|
this.engine = engine;
|
|
357
|
+
this.onDiagnostic = onDiagnostic;
|
|
246
358
|
}
|
|
247
359
|
control;
|
|
248
360
|
engine;
|
|
361
|
+
onDiagnostic;
|
|
249
362
|
results = /* @__PURE__ */ new Map();
|
|
250
363
|
async reconcile(snapshot) {
|
|
251
364
|
for (const command of snapshot.commands) {
|
|
@@ -263,7 +376,13 @@ var CodeRuntimeReconciler = class {
|
|
|
263
376
|
}
|
|
264
377
|
await this.control.acknowledge(command.commandId, completed.result);
|
|
265
378
|
if (!completed.notified) {
|
|
266
|
-
|
|
379
|
+
try {
|
|
380
|
+
await this.engine.acknowledged?.(command, completed.result);
|
|
381
|
+
} catch (error) {
|
|
382
|
+
this.onDiagnostic?.(
|
|
383
|
+
`command ${command.commandId} acknowledged handling failed \xB7 ${error instanceof Error ? error.message : String(error)}`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
267
386
|
completed.notified = true;
|
|
268
387
|
}
|
|
269
388
|
}
|
|
@@ -1659,6 +1778,36 @@ Finish with a concise, non-empty answer to the owner. Do not call tools or promi
|
|
|
1659
1778
|
}
|
|
1660
1779
|
|
|
1661
1780
|
// src/code-runtime-session-skills.ts
|
|
1781
|
+
function createCodeRuntimeSessionSkillLoader(control) {
|
|
1782
|
+
const load = control.collaborationSkills?.bind(control);
|
|
1783
|
+
const execute2 = control.executeCollaborationTool?.bind(control);
|
|
1784
|
+
if (!load || !execute2) return async () => [];
|
|
1785
|
+
return async (command) => {
|
|
1786
|
+
const manifests = await load(command.sessionId, command.commandId);
|
|
1787
|
+
return manifests.map((manifest) => ({
|
|
1788
|
+
name: manifest.name,
|
|
1789
|
+
...manifest.instructions === void 0 ? {} : { instructions: manifest.instructions },
|
|
1790
|
+
tools: manifest.tools.map((tool) => ({
|
|
1791
|
+
name: tool.name,
|
|
1792
|
+
description: tool.description,
|
|
1793
|
+
inputSchema: tool.inputSchema,
|
|
1794
|
+
...tool.concurrency === void 0 ? {} : { concurrency: tool.concurrency },
|
|
1795
|
+
...tool.outputTaint === void 0 ? {} : { outputTaint: tool.outputTaint },
|
|
1796
|
+
...tool.acceptsTaint === void 0 ? {} : { acceptsTaint: tool.acceptsTaint },
|
|
1797
|
+
handler: async (input, context) => {
|
|
1798
|
+
if (!context.toolCallId) throw new TypeError("collaboration tool call identity is required");
|
|
1799
|
+
return execute2(command.sessionId, {
|
|
1800
|
+
commandId: command.commandId,
|
|
1801
|
+
toolCallId: context.toolCallId,
|
|
1802
|
+
skill: manifest.name,
|
|
1803
|
+
tool: tool.name,
|
|
1804
|
+
input
|
|
1805
|
+
}, context.signal);
|
|
1806
|
+
}
|
|
1807
|
+
}))
|
|
1808
|
+
}));
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1662
1811
|
async function sessionSkillsFor(options, command) {
|
|
1663
1812
|
try {
|
|
1664
1813
|
return await options.sessionSkills?.(command) ?? [];
|
|
@@ -2865,6 +3014,76 @@ function codeToolResultPresentation(request, response2) {
|
|
|
2865
3014
|
};
|
|
2866
3015
|
}
|
|
2867
3016
|
|
|
3017
|
+
// src/code-runtime-acknowledgement-gate.ts
|
|
3018
|
+
function codeRuntimeAcknowledgementGate(signal) {
|
|
3019
|
+
let settle;
|
|
3020
|
+
let settled = false;
|
|
3021
|
+
const ready = new Promise((resolve6) => {
|
|
3022
|
+
settle = resolve6;
|
|
3023
|
+
});
|
|
3024
|
+
const release = (run) => {
|
|
3025
|
+
if (settled) return;
|
|
3026
|
+
settled = true;
|
|
3027
|
+
signal.removeEventListener("abort", onAbort);
|
|
3028
|
+
settle(run);
|
|
3029
|
+
};
|
|
3030
|
+
const onAbort = () => release(false);
|
|
3031
|
+
if (signal.aborted) release(false);
|
|
3032
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
3033
|
+
return { ready, release };
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
// src/code-runtime-session-activity.ts
|
|
3037
|
+
function observeCodeRuntimeSessionSkills(command, skills, emit) {
|
|
3038
|
+
return skills.map((skill) => ({
|
|
3039
|
+
...skill,
|
|
3040
|
+
tools: skill.tools.map((tool) => {
|
|
3041
|
+
if (!tool.handler) return tool;
|
|
3042
|
+
const handler = tool.handler;
|
|
3043
|
+
return {
|
|
3044
|
+
...tool,
|
|
3045
|
+
handler: async (input, context) => {
|
|
3046
|
+
const startedAt = Date.now();
|
|
3047
|
+
const operationId = digestRuntimeValue(
|
|
3048
|
+
`${command.commandId}:${skill.name}:${tool.name}:${context.toolCallId ?? "missing"}`
|
|
3049
|
+
);
|
|
3050
|
+
await emit({
|
|
3051
|
+
type: "collaboration",
|
|
3052
|
+
phase: "started",
|
|
3053
|
+
skill: skill.name,
|
|
3054
|
+
tool: tool.name,
|
|
3055
|
+
operationId
|
|
3056
|
+
}).catch(() => void 0);
|
|
3057
|
+
try {
|
|
3058
|
+
const output = await handler(input, context);
|
|
3059
|
+
await emit({
|
|
3060
|
+
type: "collaboration",
|
|
3061
|
+
phase: "completed",
|
|
3062
|
+
skill: skill.name,
|
|
3063
|
+
tool: tool.name,
|
|
3064
|
+
operationId,
|
|
3065
|
+
ok: output.isError !== true,
|
|
3066
|
+
durationMs: Date.now() - startedAt
|
|
3067
|
+
}).catch(() => void 0);
|
|
3068
|
+
return output;
|
|
3069
|
+
} catch (cause) {
|
|
3070
|
+
await emit({
|
|
3071
|
+
type: "collaboration",
|
|
3072
|
+
phase: "completed",
|
|
3073
|
+
skill: skill.name,
|
|
3074
|
+
tool: tool.name,
|
|
3075
|
+
operationId,
|
|
3076
|
+
ok: false,
|
|
3077
|
+
durationMs: Date.now() - startedAt
|
|
3078
|
+
}).catch(() => void 0);
|
|
3079
|
+
throw cause;
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
};
|
|
3083
|
+
})
|
|
3084
|
+
}));
|
|
3085
|
+
}
|
|
3086
|
+
|
|
2868
3087
|
// src/code-runtime-engine.ts
|
|
2869
3088
|
var TheseusRuntimeEngine = class {
|
|
2870
3089
|
constructor(options) {
|
|
@@ -2895,6 +3114,8 @@ var TheseusRuntimeEngine = class {
|
|
|
2895
3114
|
const active = this.#active.get(command.sessionId);
|
|
2896
3115
|
if (!active || result.status !== "running") return;
|
|
2897
3116
|
active.acknowledged = true;
|
|
3117
|
+
active.startGate?.release(true);
|
|
3118
|
+
active.startGate = void 0;
|
|
2898
3119
|
if (active.failure) await this.options.control.reportSessionFailure(command.sessionId, active.failure).catch(() => void 0);
|
|
2899
3120
|
}
|
|
2900
3121
|
async close() {
|
|
@@ -2914,13 +3135,14 @@ var TheseusRuntimeEngine = class {
|
|
|
2914
3135
|
control: this.options.control,
|
|
2915
3136
|
...this.options.localSource ? { localSource: this.options.localSource } : {}
|
|
2916
3137
|
});
|
|
2917
|
-
const abort = new AbortController();
|
|
3138
|
+
const abort = new AbortController(), startGate = codeRuntimeAcknowledgementGate(abort.signal);
|
|
2918
3139
|
const conversationRefs = [];
|
|
2919
3140
|
const active = {
|
|
2920
3141
|
workspace,
|
|
2921
3142
|
abort,
|
|
2922
3143
|
conversationRefs,
|
|
2923
3144
|
acknowledged: false,
|
|
3145
|
+
startGate,
|
|
2924
3146
|
role: metadata.role,
|
|
2925
3147
|
title: metadata.title,
|
|
2926
3148
|
maxTokensPerInteraction: metadata.maxTokensPerInteraction,
|
|
@@ -2944,7 +3166,7 @@ var TheseusRuntimeEngine = class {
|
|
|
2944
3166
|
body: `Source snapshot: local checkout ${requestedLocal.snapshotDigest} \xB7 ${requestedLocal.modified ? "modified" : "clean"} \xB7 Git ${requestedLocal.headCommitSha}`
|
|
2945
3167
|
}, conversationRefs);
|
|
2946
3168
|
}
|
|
2947
|
-
active.done = this.#runAttempt(command, metadata, active).catch(async (cause) => {
|
|
3169
|
+
active.done = startGate.ready.then((run) => run ? this.#runAttempt(command, metadata, active) : null).catch(async (cause) => {
|
|
2948
3170
|
const detail = runtimeErrorMessage(cause);
|
|
2949
3171
|
await this.#event(command, { type: "message", actor: "system", body: detail }, conversationRefs).catch(() => void 0);
|
|
2950
3172
|
await this.#diagnostic(command, active, detail);
|
|
@@ -3059,7 +3281,7 @@ var TheseusRuntimeEngine = class {
|
|
|
3059
3281
|
event: (event) => this.#event(command, event, active.conversationRefs)
|
|
3060
3282
|
});
|
|
3061
3283
|
await this.#event(command, { type: "status", status: "running" }, active.conversationRefs);
|
|
3062
|
-
const extraSkills = await sessionSkillsFor(this.options, command);
|
|
3284
|
+
const extraSkills = observeCodeRuntimeSessionSkills(command, await sessionSkillsFor(this.options, command), (event) => this.#event(command, event, active.conversationRefs));
|
|
3063
3285
|
const result = await this.#attempt({
|
|
3064
3286
|
inference,
|
|
3065
3287
|
broker,
|
|
@@ -3231,10 +3453,16 @@ async function main() {
|
|
|
3231
3453
|
engine,
|
|
3232
3454
|
recipes: policy.recipes,
|
|
3233
3455
|
recipeAuthorization: policy.recipeAuthorization,
|
|
3456
|
+
sessionSkills: createCodeRuntimeSessionSkillLoader(control),
|
|
3234
3457
|
onDiagnostic: (message2) => process.stderr.write(`[odla-code-runtime] agent failed \xB7 ${message2}
|
|
3235
3458
|
`)
|
|
3236
3459
|
});
|
|
3237
|
-
const reconciler = new CodeRuntimeReconciler(
|
|
3460
|
+
const reconciler = new CodeRuntimeReconciler(
|
|
3461
|
+
control,
|
|
3462
|
+
commandEngine,
|
|
3463
|
+
(message2) => process.stderr.write(`[odla-code-runtime] ${message2}
|
|
3464
|
+
`)
|
|
3465
|
+
);
|
|
3238
3466
|
try {
|
|
3239
3467
|
await runCodeRuntimeHeartbeatLoop({
|
|
3240
3468
|
control,
|