@odla-ai/harness 0.8.0 → 0.9.0
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-ISR434K7.js → chunk-NG7AYYH3.js} +272 -92
- package/dist/chunk-NG7AYYH3.js.map +1 -0
- package/dist/code-runtime-cli.cjs +278 -99
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +3 -1
- package/dist/code-runtime-cli.js.map +1 -1
- package/dist/node.cjs +281 -99
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +70 -6
- package/dist/node.d.ts +70 -6
- package/dist/node.js +5 -1
- package/dist/node.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-ISR434K7.js.map +0 -1
package/dist/code-runtime-cli.js
CHANGED
|
@@ -5,8 +5,9 @@ import {
|
|
|
5
5
|
TheseusRuntimeEngine,
|
|
6
6
|
assertCodeBuildRecipe,
|
|
7
7
|
createCodeRuntimeControlClient,
|
|
8
|
+
createCodeRuntimeSessionSkillLoader,
|
|
8
9
|
runCodeRuntimeHeartbeatLoop
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-NG7AYYH3.js";
|
|
10
11
|
import {
|
|
11
12
|
assertPinnedImage,
|
|
12
13
|
selectContainerEngine
|
|
@@ -91,6 +92,7 @@ async function main() {
|
|
|
91
92
|
engine,
|
|
92
93
|
recipes: policy.recipes,
|
|
93
94
|
recipeAuthorization: policy.recipeAuthorization,
|
|
95
|
+
sessionSkills: createCodeRuntimeSessionSkillLoader(control),
|
|
94
96
|
onDiagnostic: (message) => process.stderr.write(`[odla-code-runtime] agent failed \xB7 ${message}
|
|
95
97
|
`)
|
|
96
98
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/code-runtime-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { cpus, totalmem } from \"node:os\";\nimport { readFile } from \"node:fs/promises\";\nimport {\n CODE_RUNTIME_PROTOCOL_VERSION,\n CodeRuntimeReconciler,\n createCodeRuntimeControlClient,\n runCodeRuntimeHeartbeatLoop,\n type CodeRuntimeCapabilities,\n} from \"./code-runtime\";\nimport { TheseusRuntimeEngine } from \"./code-runtime-engine\";\nimport { assertPinnedImage, selectContainerEngine, type ContainerEngine } from \"./container\";\nimport { assertCodeBuildRecipe } from \"./recipe-container\";\nimport type { CodeBuildRecipe } from \"./code-tool-types\";\n\nconst VERSION = \"0.1.0\";\n\nfunction usage(): string {\n return `Usage:\n ODLA_CODE_HOST_TOKEN=odla_code_host_... odla-code-runtime \\\\\n --endpoint https://odla.ai --image registry/odla-pi@sha256:... \\\\\n --build-policy ./odla-code-build.json [--engine auto|container|podman|docker] [--once]\n\nThe host token is read only from ODLA_CODE_HOST_TOKEN. The runtime makes\noutbound HTTPS heartbeats and never opens a listener.`;\n}\n\nfunction parse(argv: string[]): {\n endpoint: string; engine: ContainerEngine | \"auto\"; image: string;\n buildPolicy: string; heartbeatMs: number; once: boolean;\n} {\n const values = new Map<string, string>();\n const flags = new Set<string>();\n for (let index = 0; index < argv.length; index++) {\n const arg = argv[index]!;\n if (arg === \"--once\") { flags.add(arg); continue; }\n if (!arg.startsWith(\"--\") || !argv[index + 1]) throw new TypeError(usage());\n values.set(arg, argv[++index]!);\n }\n const endpoint = values.get(\"--endpoint\");\n const image = values.get(\"--image\");\n const buildPolicy = values.get(\"--build-policy\");\n const engine = values.get(\"--engine\") ?? \"auto\";\n const heartbeatMs = Number(values.get(\"--heartbeat-ms\") ?? 15_000);\n if (!endpoint || !image || !buildPolicy || ![\"auto\", \"container\", \"podman\", \"docker\"].includes(engine)) {\n throw new TypeError(usage());\n }\n assertPinnedImage(image);\n if (!Number.isSafeInteger(heartbeatMs) || heartbeatMs < 1_000 || heartbeatMs > 300_000) {\n throw new TypeError(\"--heartbeat-ms must be an integer from 1000 to 300000\");\n }\n return { endpoint, image, buildPolicy, engine: engine as ContainerEngine | \"auto\",\n heartbeatMs, once: flags.has(\"--once\") };\n}\n\nasync function readPolicy(path: string): Promise<{\n recipes: CodeBuildRecipe[]; recipeAuthorization: \"registered_recipe\" | \"exact_approval\";\n}> {\n const value = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n if (!value || Object.keys(value).some((key) => ![\"recipes\", \"recipeAuthorization\"].includes(key))\n || !Array.isArray(value.recipes) || !value.recipes.length\n || (value.recipeAuthorization !== undefined && value.recipeAuthorization !== \"registered_recipe\"\n && value.recipeAuthorization !== \"exact_approval\")) throw new TypeError(\"invalid Code build policy file\");\n const recipes = value.recipes as CodeBuildRecipe[];\n for (const recipe of recipes) assertCodeBuildRecipe(recipe);\n const recipeAuthorization = value.recipeAuthorization === \"exact_approval\"\n ? \"exact_approval\" : \"registered_recipe\";\n return { recipes, recipeAuthorization };\n}\n\nasync function main(): Promise<void> {\n const token = process.env.ODLA_CODE_HOST_TOKEN;\n if (!token) throw new TypeError(\"ODLA_CODE_HOST_TOKEN is required\");\n const options = parse(process.argv.slice(2));\n if (process.platform !== \"darwin\" && process.platform !== \"linux\") throw new TypeError(\"Code runtime requires macOS or Linux\");\n const engine = await selectContainerEngine(options.engine);\n const policy = await readPolicy(options.buildPolicy);\n const capabilities: CodeRuntimeCapabilities = {\n protocolVersion: CODE_RUNTIME_PROTOCOL_VERSION,\n platform: process.platform === \"darwin\" ? \"macos\" : \"linux\",\n arch: process.arch,\n engines: [engine],\n cpuCount: cpus().length,\n memoryBytes: totalmem(),\n };\n const controller = new AbortController();\n for (const signal of [\"SIGINT\", \"SIGTERM\"] as const) process.once(signal, () => controller.abort(signal));\n // The heartbeat stops on the process signal, but the client must remain able\n // to report the active attempt's terminal events during engine.close().\n const control = createCodeRuntimeControlClient({ endpoint: options.endpoint, token });\n const commandEngine = new TheseusRuntimeEngine({\n control, engine, recipes: policy.recipes,\n recipeAuthorization: policy.recipeAuthorization,\n onDiagnostic: (message) => process.stderr.write(`[odla-code-runtime] agent failed · ${message}\\n`),\n });\n const reconciler = new CodeRuntimeReconciler(control, commandEngine);\n try {\n await runCodeRuntimeHeartbeatLoop({\n control, runtimeVersion: VERSION, capabilities, heartbeatMs: options.heartbeatMs,\n once: options.once, signal: controller.signal,\n onSnapshot: async (snapshot) => {\n process.stderr.write(\n `[odla-code-runtime] host ${snapshot.host.hostId} online · ${snapshot.bindings.length} active binding(s) · ${snapshot.commands.length} command(s)\\n`,\n );\n if (options.once && snapshot.commands.length) {\n throw new TypeError(\"--once is diagnostic-only and refuses pending Code commands\");\n }\n await reconciler.reconcile(snapshot);\n },\n onRetry: (error, delayMs) => {\n process.stderr.write(\n `[odla-code-runtime] control plane unavailable; retrying in ${delayMs}ms · ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n },\n });\n } finally { await commandEngine.close(); }\n}\n\nmain().catch((error) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n});\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/code-runtime-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { cpus, totalmem } from \"node:os\";\nimport { readFile } from \"node:fs/promises\";\nimport {\n CODE_RUNTIME_PROTOCOL_VERSION,\n CodeRuntimeReconciler,\n createCodeRuntimeControlClient,\n runCodeRuntimeHeartbeatLoop,\n type CodeRuntimeCapabilities,\n} from \"./code-runtime\";\nimport { TheseusRuntimeEngine } from \"./code-runtime-engine\";\nimport { createCodeRuntimeSessionSkillLoader } from \"./code-runtime-session-skills\";\nimport { assertPinnedImage, selectContainerEngine, type ContainerEngine } from \"./container\";\nimport { assertCodeBuildRecipe } from \"./recipe-container\";\nimport type { CodeBuildRecipe } from \"./code-tool-types\";\n\nconst VERSION = \"0.1.0\";\n\nfunction usage(): string {\n return `Usage:\n ODLA_CODE_HOST_TOKEN=odla_code_host_... odla-code-runtime \\\\\n --endpoint https://odla.ai --image registry/odla-pi@sha256:... \\\\\n --build-policy ./odla-code-build.json [--engine auto|container|podman|docker] [--once]\n\nThe host token is read only from ODLA_CODE_HOST_TOKEN. The runtime makes\noutbound HTTPS heartbeats and never opens a listener.`;\n}\n\nfunction parse(argv: string[]): {\n endpoint: string; engine: ContainerEngine | \"auto\"; image: string;\n buildPolicy: string; heartbeatMs: number; once: boolean;\n} {\n const values = new Map<string, string>();\n const flags = new Set<string>();\n for (let index = 0; index < argv.length; index++) {\n const arg = argv[index]!;\n if (arg === \"--once\") { flags.add(arg); continue; }\n if (!arg.startsWith(\"--\") || !argv[index + 1]) throw new TypeError(usage());\n values.set(arg, argv[++index]!);\n }\n const endpoint = values.get(\"--endpoint\");\n const image = values.get(\"--image\");\n const buildPolicy = values.get(\"--build-policy\");\n const engine = values.get(\"--engine\") ?? \"auto\";\n const heartbeatMs = Number(values.get(\"--heartbeat-ms\") ?? 15_000);\n if (!endpoint || !image || !buildPolicy || ![\"auto\", \"container\", \"podman\", \"docker\"].includes(engine)) {\n throw new TypeError(usage());\n }\n assertPinnedImage(image);\n if (!Number.isSafeInteger(heartbeatMs) || heartbeatMs < 1_000 || heartbeatMs > 300_000) {\n throw new TypeError(\"--heartbeat-ms must be an integer from 1000 to 300000\");\n }\n return { endpoint, image, buildPolicy, engine: engine as ContainerEngine | \"auto\",\n heartbeatMs, once: flags.has(\"--once\") };\n}\n\nasync function readPolicy(path: string): Promise<{\n recipes: CodeBuildRecipe[]; recipeAuthorization: \"registered_recipe\" | \"exact_approval\";\n}> {\n const value = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n if (!value || Object.keys(value).some((key) => ![\"recipes\", \"recipeAuthorization\"].includes(key))\n || !Array.isArray(value.recipes) || !value.recipes.length\n || (value.recipeAuthorization !== undefined && value.recipeAuthorization !== \"registered_recipe\"\n && value.recipeAuthorization !== \"exact_approval\")) throw new TypeError(\"invalid Code build policy file\");\n const recipes = value.recipes as CodeBuildRecipe[];\n for (const recipe of recipes) assertCodeBuildRecipe(recipe);\n const recipeAuthorization = value.recipeAuthorization === \"exact_approval\"\n ? \"exact_approval\" : \"registered_recipe\";\n return { recipes, recipeAuthorization };\n}\n\nasync function main(): Promise<void> {\n const token = process.env.ODLA_CODE_HOST_TOKEN;\n if (!token) throw new TypeError(\"ODLA_CODE_HOST_TOKEN is required\");\n const options = parse(process.argv.slice(2));\n if (process.platform !== \"darwin\" && process.platform !== \"linux\") throw new TypeError(\"Code runtime requires macOS or Linux\");\n const engine = await selectContainerEngine(options.engine);\n const policy = await readPolicy(options.buildPolicy);\n const capabilities: CodeRuntimeCapabilities = {\n protocolVersion: CODE_RUNTIME_PROTOCOL_VERSION,\n platform: process.platform === \"darwin\" ? \"macos\" : \"linux\",\n arch: process.arch,\n engines: [engine],\n cpuCount: cpus().length,\n memoryBytes: totalmem(),\n };\n const controller = new AbortController();\n for (const signal of [\"SIGINT\", \"SIGTERM\"] as const) process.once(signal, () => controller.abort(signal));\n // The heartbeat stops on the process signal, but the client must remain able\n // to report the active attempt's terminal events during engine.close().\n const control = createCodeRuntimeControlClient({ endpoint: options.endpoint, token });\n const commandEngine = new TheseusRuntimeEngine({\n control, engine, recipes: policy.recipes,\n recipeAuthorization: policy.recipeAuthorization,\n sessionSkills: createCodeRuntimeSessionSkillLoader(control),\n onDiagnostic: (message) => process.stderr.write(`[odla-code-runtime] agent failed · ${message}\\n`),\n });\n const reconciler = new CodeRuntimeReconciler(control, commandEngine);\n try {\n await runCodeRuntimeHeartbeatLoop({\n control, runtimeVersion: VERSION, capabilities, heartbeatMs: options.heartbeatMs,\n once: options.once, signal: controller.signal,\n onSnapshot: async (snapshot) => {\n process.stderr.write(\n `[odla-code-runtime] host ${snapshot.host.hostId} online · ${snapshot.bindings.length} active binding(s) · ${snapshot.commands.length} command(s)\\n`,\n );\n if (options.once && snapshot.commands.length) {\n throw new TypeError(\"--once is diagnostic-only and refuses pending Code commands\");\n }\n await reconciler.reconcile(snapshot);\n },\n onRetry: (error, delayMs) => {\n process.stderr.write(\n `[odla-code-runtime] control plane unavailable; retrying in ${delayMs}ms · ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n },\n });\n } finally { await commandEngine.close(); }\n}\n\nmain().catch((error) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n});\n"],"mappings":"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","names":[]}
|
package/dist/node.cjs
CHANGED
|
@@ -43,6 +43,7 @@ __export(node_exports, {
|
|
|
43
43
|
codeSkill: () => codeSkill,
|
|
44
44
|
createCodeRuntimeControlClient: () => createCodeRuntimeControlClient,
|
|
45
45
|
createCodeRuntimeInference: () => createCodeRuntimeInference,
|
|
46
|
+
createCodeRuntimeSessionSkillLoader: () => createCodeRuntimeSessionSkillLoader,
|
|
46
47
|
createCodeToolBroker: () => createCodeToolBroker,
|
|
47
48
|
createCodeWorkspaceCheckpoint: () => createCodeWorkspaceCheckpoint,
|
|
48
49
|
createContainerRecipeExecutor: () => createContainerRecipeExecutor,
|
|
@@ -79,6 +80,7 @@ __export(node_exports, {
|
|
|
79
80
|
safeWorkspaceLabel: () => safeWorkspaceLabel,
|
|
80
81
|
selectContainerEngine: () => selectContainerEngine,
|
|
81
82
|
selectWinner: () => selectWinner,
|
|
83
|
+
sessionSkillsFor: () => sessionSkillsFor,
|
|
82
84
|
stageWorkspace: () => stageWorkspace,
|
|
83
85
|
stageWorkspacePair: () => stageWorkspacePair,
|
|
84
86
|
straySubGoalFiles: () => straySubGoalFiles,
|
|
@@ -892,7 +894,7 @@ async function runHarnessRunner(options) {
|
|
|
892
894
|
} while (!options.signal?.aborted);
|
|
893
895
|
}
|
|
894
896
|
|
|
895
|
-
// src/code-runtime-client.ts
|
|
897
|
+
// src/code-runtime-client-validation.ts
|
|
896
898
|
var import_code = require("@odla-ai/camel/code");
|
|
897
899
|
var CodeRuntimeControlError = class extends Error {
|
|
898
900
|
constructor(message2, status, code = "control_error") {
|
|
@@ -904,101 +906,6 @@ var CodeRuntimeControlError = class extends Error {
|
|
|
904
906
|
code;
|
|
905
907
|
name = "CodeRuntimeControlError";
|
|
906
908
|
};
|
|
907
|
-
function createCodeRuntimeControlClient(options) {
|
|
908
|
-
const endpoint = validatedEndpoint(options.endpoint);
|
|
909
|
-
if (!/^odla_code_host_[0-9a-f]{64}$/.test(options.token)) throw new TypeError("invalid Code host credential");
|
|
910
|
-
const requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
|
|
911
|
-
if (!Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 1e3 || requestTimeoutMs > 12e4) {
|
|
912
|
-
throw new TypeError("requestTimeoutMs must be an integer from 1000 to 120000");
|
|
913
|
-
}
|
|
914
|
-
const modelRequestTimeoutMs = options.modelRequestTimeoutMs ?? 15 * 6e4;
|
|
915
|
-
if (!Number.isSafeInteger(modelRequestTimeoutMs) || modelRequestTimeoutMs < 3e4 || modelRequestTimeoutMs > 30 * 6e4) {
|
|
916
|
-
throw new TypeError("modelRequestTimeoutMs must be an integer from 30000 to 1800000");
|
|
917
|
-
}
|
|
918
|
-
const request = options.fetch ?? fetch;
|
|
919
|
-
const call = async (path, body, timeoutMs = requestTimeoutMs) => {
|
|
920
|
-
const timeout = AbortSignal.timeout(timeoutMs);
|
|
921
|
-
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
922
|
-
let response2;
|
|
923
|
-
try {
|
|
924
|
-
response2 = await request(`${endpoint}${path}`, {
|
|
925
|
-
method: "POST",
|
|
926
|
-
headers: { authorization: `Bearer ${options.token}`, "content-type": "application/json" },
|
|
927
|
-
body: JSON.stringify(body),
|
|
928
|
-
redirect: "error",
|
|
929
|
-
signal
|
|
930
|
-
});
|
|
931
|
-
} catch (cause) {
|
|
932
|
-
if (options.signal?.aborted) throw cause;
|
|
933
|
-
throw new CodeRuntimeControlError("Code runtime control plane is unavailable", 503, "transport_unavailable");
|
|
934
|
-
}
|
|
935
|
-
const value = await response2.json().catch(() => null);
|
|
936
|
-
if (!response2.ok) {
|
|
937
|
-
const problem = record2(record2(value)?.error);
|
|
938
|
-
throw new CodeRuntimeControlError(
|
|
939
|
-
typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
|
|
940
|
-
response2.status,
|
|
941
|
-
typeof problem?.code === "string" ? problem.code : void 0
|
|
942
|
-
);
|
|
943
|
-
}
|
|
944
|
-
return value;
|
|
945
|
-
};
|
|
946
|
-
return {
|
|
947
|
-
heartbeat: async (version, capabilities) => {
|
|
948
|
-
validateHeartbeat(version, capabilities);
|
|
949
|
-
return parseSnapshot(await call("/registry/code/runtime/heartbeat", { runtimeVersion: version, capabilities }));
|
|
950
|
-
},
|
|
951
|
-
acknowledge: async (commandId, result) => {
|
|
952
|
-
if (!/^ccmd_[0-9a-f]{32}$/.test(commandId)) throw new TypeError("invalid Code runtime command id");
|
|
953
|
-
await call(`/registry/code/runtime/commands/${commandId}/ack`, result);
|
|
954
|
-
},
|
|
955
|
-
source: async (sessionId) => parseSource(
|
|
956
|
-
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
957
|
-
),
|
|
958
|
-
infer: async (sessionId, inference) => {
|
|
959
|
-
const value = record2(await call(
|
|
960
|
-
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
961
|
-
inference,
|
|
962
|
-
modelRequestTimeoutMs
|
|
963
|
-
));
|
|
964
|
-
if (!value || value.requestId !== inference.requestId || !record2(value.response) || !record2(value.receipt)) {
|
|
965
|
-
throw new CodeRuntimeControlError("invalid Code inference response", 502, "invalid_response");
|
|
966
|
-
}
|
|
967
|
-
return value;
|
|
968
|
-
},
|
|
969
|
-
review: async (sessionId, review) => parseReview(
|
|
970
|
-
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/review`, review, modelRequestTimeoutMs)
|
|
971
|
-
),
|
|
972
|
-
submitCandidate: async (sessionId, checkpointId, verification) => {
|
|
973
|
-
if (!/^cpoint_[0-9a-f]{32}$/.test(checkpointId)) throw new TypeError("invalid Code checkpoint id");
|
|
974
|
-
return parseCandidate(await call(
|
|
975
|
-
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/candidates`,
|
|
976
|
-
{ checkpointId, verification }
|
|
977
|
-
));
|
|
978
|
-
},
|
|
979
|
-
appendSessionEvent: async (sessionId, eventId, event) => {
|
|
980
|
-
const serialized = JSON.stringify(event);
|
|
981
|
-
if (!/^[A-Za-z0-9._:-]{1,120}$/.test(eventId) || !event || typeof event !== "object" || new TextEncoder().encode(serialized).byteLength > 24e3) {
|
|
982
|
-
throw new TypeError("invalid Code session event");
|
|
983
|
-
}
|
|
984
|
-
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/chat/events`, { eventId, event });
|
|
985
|
-
},
|
|
986
|
-
recallMemories: async (sessionId, subjects, limit) => {
|
|
987
|
-
const response2 = await call(
|
|
988
|
-
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/recall`,
|
|
989
|
-
{ subjects: [...subjects], limit }
|
|
990
|
-
);
|
|
991
|
-
return Array.isArray(response2.memories) ? response2.memories : [];
|
|
992
|
-
},
|
|
993
|
-
rememberMemory: async (sessionId, memory) => {
|
|
994
|
-
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/remember`, memory);
|
|
995
|
-
},
|
|
996
|
-
reportSessionFailure: async (sessionId, message2) => {
|
|
997
|
-
if (!message2.trim() || message2.length > 2e3) throw new TypeError("invalid Code session failure");
|
|
998
|
-
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/failure`, { message: message2 });
|
|
999
|
-
}
|
|
1000
|
-
};
|
|
1001
|
-
}
|
|
1002
909
|
function validatedEndpoint(value) {
|
|
1003
910
|
const endpoint = value.replace(/\/+$/, "");
|
|
1004
911
|
let url;
|
|
@@ -1017,6 +924,10 @@ function validSessionId(value) {
|
|
|
1017
924
|
if (!/^csess_[0-9a-f]{32}$/.test(value)) throw new TypeError("invalid Code session id");
|
|
1018
925
|
return value;
|
|
1019
926
|
}
|
|
927
|
+
function validCommandId(value) {
|
|
928
|
+
if (!/^ccmd_[0-9a-f]{32}$/.test(value)) throw new TypeError("invalid Code runtime command id");
|
|
929
|
+
return value;
|
|
930
|
+
}
|
|
1020
931
|
function validateHeartbeat(version, capabilities) {
|
|
1021
932
|
if (!version.trim() || version.length > 80) throw new TypeError("runtimeVersion is required and at most 80 characters");
|
|
1022
933
|
if (capabilities.protocolVersion !== CODE_RUNTIME_PROTOCOL_VERSION) throw new TypeError("unsupported Code runtime protocol version");
|
|
@@ -1098,9 +1009,211 @@ function parseCandidate(value) {
|
|
|
1098
1009
|
}
|
|
1099
1010
|
return { candidateId: candidate.candidateId, status: candidate.status };
|
|
1100
1011
|
}
|
|
1012
|
+
function parseCollaborationSkills(value) {
|
|
1013
|
+
const items = record2(value)?.skills;
|
|
1014
|
+
if (!Array.isArray(items) || items.length > 16) throw invalid("collaboration skills");
|
|
1015
|
+
const skillNames = /* @__PURE__ */ new Set();
|
|
1016
|
+
const toolNames = /* @__PURE__ */ new Set();
|
|
1017
|
+
return items.map((item) => {
|
|
1018
|
+
const skill = record2(item);
|
|
1019
|
+
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) {
|
|
1020
|
+
throw invalid("collaboration skill");
|
|
1021
|
+
}
|
|
1022
|
+
skillNames.add(skill.name);
|
|
1023
|
+
const tools = skill.tools.map((candidate) => {
|
|
1024
|
+
const tool = record2(candidate);
|
|
1025
|
+
const inputSchema = record2(tool?.inputSchema);
|
|
1026
|
+
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") {
|
|
1027
|
+
throw invalid("collaboration tool");
|
|
1028
|
+
}
|
|
1029
|
+
const outputTaint = parseTaintLabels(tool.outputTaint);
|
|
1030
|
+
const acceptsTaint = parseTaintLabels(tool.acceptsTaint);
|
|
1031
|
+
toolNames.add(tool.name);
|
|
1032
|
+
return {
|
|
1033
|
+
name: tool.name,
|
|
1034
|
+
description: tool.description,
|
|
1035
|
+
inputSchema,
|
|
1036
|
+
...tool.concurrency === "parallel" ? { concurrency: "parallel" } : {},
|
|
1037
|
+
...outputTaint ? { outputTaint } : {},
|
|
1038
|
+
...acceptsTaint ? { acceptsTaint } : {}
|
|
1039
|
+
};
|
|
1040
|
+
});
|
|
1041
|
+
return {
|
|
1042
|
+
name: skill.name,
|
|
1043
|
+
...typeof skill.instructions === "string" ? { instructions: skill.instructions } : {},
|
|
1044
|
+
tools
|
|
1045
|
+
};
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
function validateCollaborationToolRequest(value) {
|
|
1049
|
+
validCommandId(value.commandId);
|
|
1050
|
+
if (typeof value.toolCallId !== "string" || value.toolCallId.length > 256 || !/^[^\s\u0000-\u001f\u007f]+$/.test(value.toolCallId) || !validManifestName(value.skill) || !validManifestName(value.tool) || !record2(value.input) || jsonBytes(value.input) > 128e3) {
|
|
1051
|
+
throw new TypeError("invalid Code collaboration tool request");
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
function parseCollaborationToolOutput(value) {
|
|
1055
|
+
const output = record2(record2(value)?.output);
|
|
1056
|
+
if (!output || output.isError !== void 0 && typeof output.isError !== "boolean") {
|
|
1057
|
+
throw invalid("collaboration tool");
|
|
1058
|
+
}
|
|
1059
|
+
if (typeof output.content === "string") {
|
|
1060
|
+
if (utf8Bytes(output.content) > 1e6) throw invalid("collaboration tool");
|
|
1061
|
+
return { content: output.content, ...output.isError === true ? { isError: true } : {} };
|
|
1062
|
+
}
|
|
1063
|
+
if (!Array.isArray(output.content) || output.content.length > 64 || jsonBytes(output.content) > 1e6 || !output.content.every((block) => {
|
|
1064
|
+
const item = record2(block);
|
|
1065
|
+
return item && ["text", "image", "audio", "document", "tool_use", "tool_result", "thinking"].includes(String(item.type));
|
|
1066
|
+
})) throw invalid("collaboration tool");
|
|
1067
|
+
return {
|
|
1068
|
+
content: output.content,
|
|
1069
|
+
...output.isError === true ? { isError: true } : {}
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
function parseTaintLabels(value) {
|
|
1073
|
+
if (value === void 0) return void 0;
|
|
1074
|
+
if (!Array.isArray(value) || value.length > 16) throw invalid("collaboration tool taint");
|
|
1075
|
+
const labels = value.map((item) => {
|
|
1076
|
+
if (item === "web_untrusted" || item === "operator_pasted_untrusted" || item === "llm_inherited") return item;
|
|
1077
|
+
if (typeof item === "string" && /^tool_untrusted:[^\s\u0000-\u001f\u007f]{1,100}$/.test(item)) {
|
|
1078
|
+
return item;
|
|
1079
|
+
}
|
|
1080
|
+
throw invalid("collaboration tool taint");
|
|
1081
|
+
});
|
|
1082
|
+
return [...new Set(labels)];
|
|
1083
|
+
}
|
|
1084
|
+
function validManifestName(value) {
|
|
1085
|
+
return typeof value === "string" && /^[A-Za-z][A-Za-z0-9._:-]{0,127}$/.test(value);
|
|
1086
|
+
}
|
|
1087
|
+
function utf8Bytes(value) {
|
|
1088
|
+
return new TextEncoder().encode(value).byteLength;
|
|
1089
|
+
}
|
|
1090
|
+
function jsonBytes(value) {
|
|
1091
|
+
try {
|
|
1092
|
+
return utf8Bytes(JSON.stringify(value));
|
|
1093
|
+
} catch {
|
|
1094
|
+
return Number.POSITIVE_INFINITY;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1101
1097
|
var record2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1102
1098
|
var invalid = (part) => new CodeRuntimeControlError(`invalid Code runtime ${part} response`, 502, "invalid_response");
|
|
1103
1099
|
|
|
1100
|
+
// src/code-runtime-client.ts
|
|
1101
|
+
function createCodeRuntimeControlClient(options) {
|
|
1102
|
+
const endpoint = validatedEndpoint(options.endpoint);
|
|
1103
|
+
if (!/^odla_code_host_[0-9a-f]{64}$/.test(options.token)) throw new TypeError("invalid Code host credential");
|
|
1104
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
|
|
1105
|
+
if (!Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 1e3 || requestTimeoutMs > 12e4) {
|
|
1106
|
+
throw new TypeError("requestTimeoutMs must be an integer from 1000 to 120000");
|
|
1107
|
+
}
|
|
1108
|
+
const modelRequestTimeoutMs = options.modelRequestTimeoutMs ?? 15 * 6e4;
|
|
1109
|
+
if (!Number.isSafeInteger(modelRequestTimeoutMs) || modelRequestTimeoutMs < 3e4 || modelRequestTimeoutMs > 30 * 6e4) {
|
|
1110
|
+
throw new TypeError("modelRequestTimeoutMs must be an integer from 30000 to 1800000");
|
|
1111
|
+
}
|
|
1112
|
+
const request = options.fetch ?? fetch;
|
|
1113
|
+
const call = async (path, body, timeoutMs = requestTimeoutMs, operationSignal) => {
|
|
1114
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
1115
|
+
const signals = [options.signal, operationSignal, timeout].filter((item) => Boolean(item));
|
|
1116
|
+
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
1117
|
+
let response2;
|
|
1118
|
+
try {
|
|
1119
|
+
response2 = await request(`${endpoint}${path}`, {
|
|
1120
|
+
method: "POST",
|
|
1121
|
+
headers: { authorization: `Bearer ${options.token}`, "content-type": "application/json" },
|
|
1122
|
+
body: JSON.stringify(body),
|
|
1123
|
+
redirect: "error",
|
|
1124
|
+
signal
|
|
1125
|
+
});
|
|
1126
|
+
} catch (cause) {
|
|
1127
|
+
if (options.signal?.aborted || operationSignal?.aborted) throw cause;
|
|
1128
|
+
throw new CodeRuntimeControlError("Code runtime control plane is unavailable", 503, "transport_unavailable");
|
|
1129
|
+
}
|
|
1130
|
+
const value = await response2.json().catch(() => null);
|
|
1131
|
+
if (!response2.ok) {
|
|
1132
|
+
const problem = record2(record2(value)?.error);
|
|
1133
|
+
throw new CodeRuntimeControlError(
|
|
1134
|
+
typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
|
|
1135
|
+
response2.status,
|
|
1136
|
+
typeof problem?.code === "string" ? problem.code : void 0
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
return value;
|
|
1140
|
+
};
|
|
1141
|
+
return {
|
|
1142
|
+
heartbeat: async (version, capabilities) => {
|
|
1143
|
+
validateHeartbeat(version, capabilities);
|
|
1144
|
+
return parseSnapshot(await call("/registry/code/runtime/heartbeat", { runtimeVersion: version, capabilities }));
|
|
1145
|
+
},
|
|
1146
|
+
acknowledge: async (commandId, result) => {
|
|
1147
|
+
await call(`/registry/code/runtime/commands/${validCommandId(commandId)}/ack`, result);
|
|
1148
|
+
},
|
|
1149
|
+
source: async (sessionId) => parseSource(
|
|
1150
|
+
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
1151
|
+
),
|
|
1152
|
+
infer: async (sessionId, inference) => {
|
|
1153
|
+
const value = record2(await call(
|
|
1154
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
1155
|
+
inference,
|
|
1156
|
+
modelRequestTimeoutMs
|
|
1157
|
+
));
|
|
1158
|
+
if (!value || value.requestId !== inference.requestId || !record2(value.response) || !record2(value.receipt)) {
|
|
1159
|
+
throw new CodeRuntimeControlError("invalid Code inference response", 502, "invalid_response");
|
|
1160
|
+
}
|
|
1161
|
+
return value;
|
|
1162
|
+
},
|
|
1163
|
+
review: async (sessionId, review) => parseReview(
|
|
1164
|
+
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/review`, review, modelRequestTimeoutMs)
|
|
1165
|
+
),
|
|
1166
|
+
submitCandidate: async (sessionId, checkpointId, verification) => {
|
|
1167
|
+
if (!/^cpoint_[0-9a-f]{32}$/.test(checkpointId)) throw new TypeError("invalid Code checkpoint id");
|
|
1168
|
+
return parseCandidate(await call(
|
|
1169
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/candidates`,
|
|
1170
|
+
{ checkpointId, verification }
|
|
1171
|
+
));
|
|
1172
|
+
},
|
|
1173
|
+
appendSessionEvent: async (sessionId, eventId, event) => {
|
|
1174
|
+
const serialized = JSON.stringify(event);
|
|
1175
|
+
if (!/^[A-Za-z0-9._:-]{1,120}$/.test(eventId) || !event || typeof event !== "object" || new TextEncoder().encode(serialized).byteLength > 24e3) {
|
|
1176
|
+
throw new TypeError("invalid Code session event");
|
|
1177
|
+
}
|
|
1178
|
+
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/chat/events`, { eventId, event });
|
|
1179
|
+
},
|
|
1180
|
+
recallMemories: async (sessionId, subjects, limit) => {
|
|
1181
|
+
const response2 = await call(
|
|
1182
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/recall`,
|
|
1183
|
+
{ subjects: [...subjects], limit }
|
|
1184
|
+
);
|
|
1185
|
+
return Array.isArray(response2.memories) ? response2.memories : [];
|
|
1186
|
+
},
|
|
1187
|
+
rememberMemory: async (sessionId, memory) => {
|
|
1188
|
+
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/remember`, memory);
|
|
1189
|
+
},
|
|
1190
|
+
collaborationSkills: async (sessionId, commandId) => {
|
|
1191
|
+
try {
|
|
1192
|
+
return parseCollaborationSkills(await call(
|
|
1193
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/collaboration/skills`,
|
|
1194
|
+
{ commandId: validCommandId(commandId) }
|
|
1195
|
+
));
|
|
1196
|
+
} catch (cause) {
|
|
1197
|
+
if (cause instanceof CodeRuntimeControlError && cause.status === 404 && cause.code === "not_found") return [];
|
|
1198
|
+
throw cause;
|
|
1199
|
+
}
|
|
1200
|
+
},
|
|
1201
|
+
executeCollaborationTool: async (sessionId, collaboration, signal) => {
|
|
1202
|
+
validateCollaborationToolRequest(collaboration);
|
|
1203
|
+
return parseCollaborationToolOutput(await call(
|
|
1204
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/collaboration/tools`,
|
|
1205
|
+
collaboration,
|
|
1206
|
+
requestTimeoutMs,
|
|
1207
|
+
signal
|
|
1208
|
+
));
|
|
1209
|
+
},
|
|
1210
|
+
reportSessionFailure: async (sessionId, message2) => {
|
|
1211
|
+
if (!message2.trim() || message2.length > 2e3) throw new TypeError("invalid Code session failure");
|
|
1212
|
+
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/failure`, { message: message2 });
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1104
1217
|
// src/code-runtime.ts
|
|
1105
1218
|
var CODE_RUNTIME_PROTOCOL_VERSION = 1;
|
|
1106
1219
|
async function runCodeRuntimeHeartbeatLoop(options) {
|
|
@@ -2208,6 +2321,7 @@ async function runCodeAgentAttempt(options) {
|
|
|
2208
2321
|
model: "brokered",
|
|
2209
2322
|
surface,
|
|
2210
2323
|
...options.recipeIds ? { recipeIds: options.recipeIds } : {},
|
|
2324
|
+
...options.extraSkills ? { extraSkills: options.extraSkills } : {},
|
|
2211
2325
|
...options.maxSteps === void 0 ? {} : { maxSteps: options.maxSteps },
|
|
2212
2326
|
...options.budget ? { budget: options.budget } : {},
|
|
2213
2327
|
...options.signal ? { signal: options.signal } : {},
|
|
@@ -2239,6 +2353,48 @@ Finish with a concise, non-empty answer to the owner. Do not call tools or promi
|
|
|
2239
2353
|
}
|
|
2240
2354
|
}
|
|
2241
2355
|
|
|
2356
|
+
// src/code-runtime-session-skills.ts
|
|
2357
|
+
function createCodeRuntimeSessionSkillLoader(control) {
|
|
2358
|
+
const load = control.collaborationSkills?.bind(control);
|
|
2359
|
+
const execute2 = control.executeCollaborationTool?.bind(control);
|
|
2360
|
+
if (!load || !execute2) return async () => [];
|
|
2361
|
+
return async (command) => {
|
|
2362
|
+
const manifests = await load(command.sessionId, command.commandId);
|
|
2363
|
+
return manifests.map((manifest) => ({
|
|
2364
|
+
name: manifest.name,
|
|
2365
|
+
...manifest.instructions === void 0 ? {} : { instructions: manifest.instructions },
|
|
2366
|
+
tools: manifest.tools.map((tool) => ({
|
|
2367
|
+
name: tool.name,
|
|
2368
|
+
description: tool.description,
|
|
2369
|
+
inputSchema: tool.inputSchema,
|
|
2370
|
+
...tool.concurrency === void 0 ? {} : { concurrency: tool.concurrency },
|
|
2371
|
+
...tool.outputTaint === void 0 ? {} : { outputTaint: tool.outputTaint },
|
|
2372
|
+
...tool.acceptsTaint === void 0 ? {} : { acceptsTaint: tool.acceptsTaint },
|
|
2373
|
+
handler: async (input, context) => {
|
|
2374
|
+
if (!context.toolCallId) throw new TypeError("collaboration tool call identity is required");
|
|
2375
|
+
return execute2(command.sessionId, {
|
|
2376
|
+
commandId: command.commandId,
|
|
2377
|
+
toolCallId: context.toolCallId,
|
|
2378
|
+
skill: manifest.name,
|
|
2379
|
+
tool: tool.name,
|
|
2380
|
+
input
|
|
2381
|
+
}, context.signal);
|
|
2382
|
+
}
|
|
2383
|
+
}))
|
|
2384
|
+
}));
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
async function sessionSkillsFor(options, command) {
|
|
2388
|
+
try {
|
|
2389
|
+
return await options.sessionSkills?.(command) ?? [];
|
|
2390
|
+
} catch (cause) {
|
|
2391
|
+
options.onDiagnostic?.(
|
|
2392
|
+
`session skills unavailable, continuing with code tools only: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
2393
|
+
);
|
|
2394
|
+
return [];
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2242
2398
|
// src/code-runtime-inference.ts
|
|
2243
2399
|
async function handleCodeRuntimeInference(input) {
|
|
2244
2400
|
const { command, request, state } = input;
|
|
@@ -3457,6 +3613,25 @@ function codeToolResultPresentation(request, response2) {
|
|
|
3457
3613
|
};
|
|
3458
3614
|
}
|
|
3459
3615
|
|
|
3616
|
+
// src/code-runtime-acknowledgement-gate.ts
|
|
3617
|
+
function codeRuntimeAcknowledgementGate(signal) {
|
|
3618
|
+
let settle;
|
|
3619
|
+
let settled = false;
|
|
3620
|
+
const ready = new Promise((resolve7) => {
|
|
3621
|
+
settle = resolve7;
|
|
3622
|
+
});
|
|
3623
|
+
const release = (run) => {
|
|
3624
|
+
if (settled) return;
|
|
3625
|
+
settled = true;
|
|
3626
|
+
signal.removeEventListener("abort", onAbort);
|
|
3627
|
+
settle(run);
|
|
3628
|
+
};
|
|
3629
|
+
const onAbort = () => release(false);
|
|
3630
|
+
if (signal.aborted) release(false);
|
|
3631
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
3632
|
+
return { ready, release };
|
|
3633
|
+
}
|
|
3634
|
+
|
|
3460
3635
|
// src/code-runtime-engine.ts
|
|
3461
3636
|
var TheseusRuntimeEngine = class {
|
|
3462
3637
|
constructor(options) {
|
|
@@ -3487,6 +3662,8 @@ var TheseusRuntimeEngine = class {
|
|
|
3487
3662
|
const active = this.#active.get(command.sessionId);
|
|
3488
3663
|
if (!active || result.status !== "running") return;
|
|
3489
3664
|
active.acknowledged = true;
|
|
3665
|
+
active.startGate?.release(true);
|
|
3666
|
+
active.startGate = void 0;
|
|
3490
3667
|
if (active.failure) await this.options.control.reportSessionFailure(command.sessionId, active.failure).catch(() => void 0);
|
|
3491
3668
|
}
|
|
3492
3669
|
async close() {
|
|
@@ -3506,13 +3683,14 @@ var TheseusRuntimeEngine = class {
|
|
|
3506
3683
|
control: this.options.control,
|
|
3507
3684
|
...this.options.localSource ? { localSource: this.options.localSource } : {}
|
|
3508
3685
|
});
|
|
3509
|
-
const abort = new AbortController();
|
|
3686
|
+
const abort = new AbortController(), startGate = codeRuntimeAcknowledgementGate(abort.signal);
|
|
3510
3687
|
const conversationRefs = [];
|
|
3511
3688
|
const active = {
|
|
3512
3689
|
workspace,
|
|
3513
3690
|
abort,
|
|
3514
3691
|
conversationRefs,
|
|
3515
3692
|
acknowledged: false,
|
|
3693
|
+
startGate,
|
|
3516
3694
|
role: metadata.role,
|
|
3517
3695
|
title: metadata.title,
|
|
3518
3696
|
maxTokensPerInteraction: metadata.maxTokensPerInteraction,
|
|
@@ -3536,7 +3714,7 @@ var TheseusRuntimeEngine = class {
|
|
|
3536
3714
|
body: `Source snapshot: local checkout ${requestedLocal.snapshotDigest} \xB7 ${requestedLocal.modified ? "modified" : "clean"} \xB7 Git ${requestedLocal.headCommitSha}`
|
|
3537
3715
|
}, conversationRefs);
|
|
3538
3716
|
}
|
|
3539
|
-
active.done = this.#runAttempt(command, metadata, active).catch(async (cause) => {
|
|
3717
|
+
active.done = startGate.ready.then((run) => run ? this.#runAttempt(command, metadata, active) : null).catch(async (cause) => {
|
|
3540
3718
|
const detail = runtimeErrorMessage(cause);
|
|
3541
3719
|
await this.#event(command, { type: "message", actor: "system", body: detail }, conversationRefs).catch(() => void 0);
|
|
3542
3720
|
await this.#diagnostic(command, active, detail);
|
|
@@ -3651,6 +3829,7 @@ var TheseusRuntimeEngine = class {
|
|
|
3651
3829
|
event: (event) => this.#event(command, event, active.conversationRefs)
|
|
3652
3830
|
});
|
|
3653
3831
|
await this.#event(command, { type: "status", status: "running" }, active.conversationRefs);
|
|
3832
|
+
const extraSkills = await sessionSkillsFor(this.options, command);
|
|
3654
3833
|
const result = await this.#attempt({
|
|
3655
3834
|
inference,
|
|
3656
3835
|
broker,
|
|
@@ -3658,7 +3837,8 @@ var TheseusRuntimeEngine = class {
|
|
|
3658
3837
|
workspaceDir: active.workspace.workspaceDir,
|
|
3659
3838
|
prompt: metadata.prompt,
|
|
3660
3839
|
signal: active.abort.signal,
|
|
3661
|
-
recipeIds: this.options.recipes.map((recipe2) => recipe2.id)
|
|
3840
|
+
recipeIds: this.options.recipes.map((recipe2) => recipe2.id),
|
|
3841
|
+
...extraSkills.length ? { extraSkills } : {}
|
|
3662
3842
|
});
|
|
3663
3843
|
const closing = result.finalText.trim();
|
|
3664
3844
|
const completed = result.status === "completed" && Boolean(closing);
|
|
@@ -4071,6 +4251,7 @@ async function installedDependencies(repoRoot) {
|
|
|
4071
4251
|
codeSkill,
|
|
4072
4252
|
createCodeRuntimeControlClient,
|
|
4073
4253
|
createCodeRuntimeInference,
|
|
4254
|
+
createCodeRuntimeSessionSkillLoader,
|
|
4074
4255
|
createCodeToolBroker,
|
|
4075
4256
|
createCodeWorkspaceCheckpoint,
|
|
4076
4257
|
createContainerRecipeExecutor,
|
|
@@ -4107,6 +4288,7 @@ async function installedDependencies(repoRoot) {
|
|
|
4107
4288
|
safeWorkspaceLabel,
|
|
4108
4289
|
selectContainerEngine,
|
|
4109
4290
|
selectWinner,
|
|
4291
|
+
sessionSkillsFor,
|
|
4110
4292
|
stageWorkspace,
|
|
4111
4293
|
stageWorkspacePair,
|
|
4112
4294
|
straySubGoalFiles,
|