@odla-ai/harness 0.6.0 → 0.7.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.
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CODE_RUNTIME_PROTOCOL_VERSION,
4
- CodePiRuntimeEngine,
5
4
  CodeRuntimeReconciler,
5
+ TheseusRuntimeEngine,
6
6
  assertCodeBuildRecipe,
7
7
  createCodeRuntimeControlClient,
8
8
  runCodeRuntimeHeartbeatLoop
9
- } from "./chunk-ZYNGL5SC.js";
9
+ } from "./chunk-GYWQM76X.js";
10
10
  import {
11
11
  assertPinnedImage,
12
12
  selectContainerEngine
@@ -85,8 +85,8 @@ async function main() {
85
85
  };
86
86
  const controller = new AbortController();
87
87
  for (const signal of ["SIGINT", "SIGTERM"]) process.once(signal, () => controller.abort(signal));
88
- const control = createCodeRuntimeControlClient({ endpoint: options.endpoint, token, signal: controller.signal });
89
- const commandEngine = new CodePiRuntimeEngine({
88
+ const control = createCodeRuntimeControlClient({ endpoint: options.endpoint, token });
89
+ const commandEngine = new TheseusRuntimeEngine({
90
90
  control,
91
91
  engine,
92
92
  recipes: policy.recipes,
@@ -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 { CodePiRuntimeEngine } 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 const control = createCodeRuntimeControlClient({ endpoint: options.endpoint, token, signal: controller.signal });\n const commandEngine = new CodePiRuntimeEngine({\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":"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","names":[]}
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":"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","names":[]}
package/dist/node.cjs CHANGED
@@ -21,7 +21,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var node_exports = {};
22
22
  __export(node_exports, {
23
23
  CODE_RUNTIME_PROTOCOL_VERSION: () => CODE_RUNTIME_PROTOCOL_VERSION,
24
- CodePiRuntimeEngine: () => CodePiRuntimeEngine,
25
24
  CodeRuntimeCheckpointManager: () => CodeRuntimeCheckpointManager,
26
25
  CodeRuntimeControlError: () => CodeRuntimeControlError,
27
26
  CodeRuntimeReconciler: () => CodeRuntimeReconciler,
@@ -29,6 +28,7 @@ __export(node_exports, {
29
28
  MAX_MEMORY_BODY: () => MAX_MEMORY_BODY,
30
29
  MEASURED_PREMIUM: () => MEASURED_PREMIUM,
31
30
  SYSTEM_PROMPT_FOR: () => SYSTEM_PROMPT_FOR,
31
+ TheseusRuntimeEngine: () => TheseusRuntimeEngine,
32
32
  V1_SYSTEM_PROMPT: () => V1_SYSTEM_PROMPT,
33
33
  V2_SYSTEM_PROMPT: () => V2_SYSTEM_PROMPT,
34
34
  V3_SYSTEM_PROMPT: () => V3_SYSTEM_PROMPT,
@@ -1731,7 +1731,7 @@ var CodeRuntimeCheckpointManager = class {
1731
1731
  await this.options.event(command, { type: "message", actor: "system", body: prepared.note }, active.conversationRefs).catch(() => void 0);
1732
1732
  await active.workspace.cleanup();
1733
1733
  await this.options.event(command, { type: "status", status: "checkpointed" }, active.conversationRefs).catch(() => void 0);
1734
- return { status: "checkpointed", checkpoint: prepared.checkpoint, message: "Pi stopped at a portable checkpoint" };
1734
+ return { status: "checkpointed", checkpoint: prepared.checkpoint, message: "Theseus stopped at a portable checkpoint" };
1735
1735
  }
1736
1736
  async acknowledged(command, result) {
1737
1737
  if (command.kind !== "checkpoint_stop" || result.status !== "checkpointed") return false;
@@ -1973,7 +1973,7 @@ var import_ai2 = require("@odla-ai/ai");
1973
1973
  var import_ai = require("@odla-ai/ai");
1974
1974
 
1975
1975
  // src/code-agent-skill.ts
1976
- var V1_SYSTEM_PROMPT = `You are Pi, the coding agent inside an odla Code harness.
1976
+ var V1_SYSTEM_PROMPT = `You are Theseus, the coding agent inside an odla Code harness.
1977
1977
  Use only the odla_read, odla_apply_git_diff, and odla_run_recipe tools.
1978
1978
  For mutations, call odla_apply_git_diff with raw git diff text. It must start
1979
1979
  with "diff --git a/<path> b/<path>", include matching "---" and "+++" file
@@ -1983,7 +1983,10 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
1983
1983
  var V2_SYSTEM_PROMPT = `You are the coding agent inside an odla Code harness.
1984
1984
  Start by orienting: odla_list shows the files in the workspace and odla_search
1985
1985
  finds a literal string across them. Prefer those over guessing a path.
1986
- Then odla_read a bounded range, and odla_apply_git_diff to mutate.
1986
+ Then odla_read a bounded range, and odla_apply_git_diff to mutate. When you
1987
+ need several independent searches or file ranges, issue those read-only calls
1988
+ together in one turn; their results stay ordered and the harness overlaps them.
1989
+ Never issue odla_apply_git_diff or odla_run_recipe alongside another tool call.
1987
1990
  For mutations, call odla_apply_git_diff with raw git diff text. It must start
1988
1991
  with "diff --git a/<path> b/<path>", include matching "---" and "+++" file
1989
1992
  headers and numbered "@@" hunks, and never use "*** Begin Patch" wrappers.
@@ -2014,18 +2017,26 @@ var SYSTEM_PROMPT_FOR = {
2014
2017
  };
2015
2018
  function codeSkill(opts) {
2016
2019
  let seq = 0;
2020
+ let nextCompletion = 1;
2021
+ const completed = /* @__PURE__ */ new Map();
2017
2022
  const call = async (tool, input, signal) => {
2023
+ const sequence = ++seq;
2018
2024
  const startedAt = Date.now();
2019
2025
  const response2 = await opts.broker.execute(
2020
2026
  { lease: opts.lease, workspaceDir: opts.workspaceDir, signal },
2021
- { requestId: `bench-${tool}-${++seq}`, tool, input }
2027
+ { requestId: `bench-${tool}-${sequence}`, tool, input }
2022
2028
  );
2023
- opts.onToolCall?.({
2029
+ completed.set(sequence, {
2024
2030
  tool,
2025
2031
  ok: response2.ok,
2026
2032
  durationMs: Date.now() - startedAt,
2027
2033
  ...response2.ok ? {} : { error: String(response2.content).slice(0, 300) }
2028
2034
  });
2035
+ while (completed.has(nextCompletion)) {
2036
+ const completion = completed.get(nextCompletion);
2037
+ completed.delete(nextCompletion++);
2038
+ opts.onToolCall?.(completion);
2039
+ }
2029
2040
  return { content: response2.content, isError: !response2.ok };
2030
2041
  };
2031
2042
  const read2 = {
@@ -2041,6 +2052,7 @@ function codeSkill(opts) {
2041
2052
  },
2042
2053
  additionalProperties: false
2043
2054
  },
2055
+ concurrency: "parallel",
2044
2056
  handler: (input, ctx) => call("sandbox.read", input, ctx.signal)
2045
2057
  };
2046
2058
  const applyPatch = {
@@ -2056,11 +2068,17 @@ function codeSkill(opts) {
2056
2068
  };
2057
2069
  const runRecipe = {
2058
2070
  name: "odla_run_recipe",
2059
- description: "Run one app-registered build or test recipe through CaMeL policy.",
2071
+ description: `Run one app-registered build or test recipe through CaMeL policy.${opts.recipeIds?.length ? ` Available recipes: ${opts.recipeIds.join(", ")}.` : ""}`,
2060
2072
  inputSchema: {
2061
2073
  type: "object",
2062
2074
  required: ["recipeId"],
2063
- properties: { recipeId: { type: "string", minLength: 1, maxLength: 120, pattern: "^[a-zA-Z0-9._:-]+$" } },
2075
+ properties: { recipeId: {
2076
+ type: "string",
2077
+ minLength: 1,
2078
+ maxLength: 120,
2079
+ pattern: "^[a-zA-Z0-9._:-]+$",
2080
+ ...opts.recipeIds?.length ? { enum: [...opts.recipeIds] } : {}
2081
+ } },
2064
2082
  additionalProperties: false
2065
2083
  },
2066
2084
  handler: (input, ctx) => call("sandbox.run_recipe", input, ctx.signal)
@@ -2076,6 +2094,7 @@ function codeSkill(opts) {
2076
2094
  },
2077
2095
  additionalProperties: false
2078
2096
  },
2097
+ concurrency: "parallel",
2079
2098
  handler: (input, ctx) => call("sandbox.list", input, ctx.signal)
2080
2099
  };
2081
2100
  const searchFiles = {
@@ -2092,11 +2111,13 @@ function codeSkill(opts) {
2092
2111
  },
2093
2112
  additionalProperties: false
2094
2113
  },
2114
+ concurrency: "parallel",
2095
2115
  handler: (input, ctx) => call("sandbox.search", input, ctx.signal)
2096
2116
  };
2097
2117
  const graphTool = (name, tool, description, required) => ({
2098
2118
  name,
2099
2119
  description,
2120
+ concurrency: "parallel",
2100
2121
  inputSchema: {
2101
2122
  type: "object",
2102
2123
  ...required ? { required: ["query"] } : {},
@@ -2144,6 +2165,7 @@ async function runCodeAgent(options) {
2144
2165
  lease: options.lease,
2145
2166
  workspaceDir: options.workspaceDir,
2146
2167
  surface,
2168
+ ...options.recipeIds ? { recipeIds: options.recipeIds } : {},
2147
2169
  onToolCall: (call) => {
2148
2170
  toolCalls.push(call);
2149
2171
  options.onToolCall?.(call);
@@ -2174,7 +2196,7 @@ async function runCodeAgent(options) {
2174
2196
  // src/code-runtime-attempt.ts
2175
2197
  async function runCodeAgentAttempt(options) {
2176
2198
  try {
2177
- const surface = options.surface ?? "v2";
2199
+ const surface = options.surface ?? "v3";
2178
2200
  const { run } = await runCodeAgent({
2179
2201
  inference: options.inference,
2180
2202
  broker: options.broker,
@@ -2185,6 +2207,7 @@ async function runCodeAgentAttempt(options) {
2185
2207
  // id only labels the request the control plane is about to rewrite.
2186
2208
  model: "brokered",
2187
2209
  surface,
2210
+ ...options.recipeIds ? { recipeIds: options.recipeIds } : {},
2188
2211
  ...options.maxSteps === void 0 ? {} : { maxSteps: options.maxSteps },
2189
2212
  ...options.budget ? { budget: options.budget } : {},
2190
2213
  ...options.signal ? { signal: options.signal } : {},
@@ -2524,11 +2547,30 @@ function response(request, ok, content, details) {
2524
2547
  var import_promises11 = require("fs/promises");
2525
2548
 
2526
2549
  // src/code-tool-discovery.ts
2550
+ var import_node_child_process6 = require("child_process");
2527
2551
  var import_promises9 = require("fs/promises");
2528
2552
  var import_node_path9 = require("path");
2529
2553
  var DEFAULT_MAX_FILES = 2e4;
2530
2554
  var DEFAULT_MAX_RESULTS = 100;
2531
2555
  var DEFAULT_MAX_FILE_BYTES = 512 * 1024;
2556
+ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = registeredFiles) {
2557
+ const cache2 = /* @__PURE__ */ new Map();
2558
+ return {
2559
+ files(root) {
2560
+ const existing = cache2.get(root);
2561
+ if (existing) return existing;
2562
+ const pending = enumerate(root, limit).then((paths) => Object.freeze(paths));
2563
+ cache2.set(root, pending);
2564
+ void pending.catch(() => {
2565
+ if (cache2.get(root) === pending) cache2.delete(root);
2566
+ });
2567
+ return pending;
2568
+ },
2569
+ invalidate(root) {
2570
+ cache2.delete(root);
2571
+ }
2572
+ };
2573
+ }
2532
2574
  async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
2533
2575
  const paths = [];
2534
2576
  const walk = async (directory) => {
@@ -2559,28 +2601,122 @@ function listWorkspace(paths, options = {}) {
2559
2601
  return scoped.slice(0, max);
2560
2602
  }
2561
2603
  async function searchWorkspace(root, paths, options) {
2562
- const query = options.caseSensitive === false ? options.query.toLowerCase() : options.query;
2563
- if (!query) throw new TypeError("search query must be a non-empty string");
2604
+ options.signal?.throwIfAborted();
2605
+ if (!options.query) throw new TypeError("search query must be a non-empty string");
2564
2606
  const maxResults = options.maxResults ?? DEFAULT_MAX_RESULTS;
2565
2607
  const maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
2566
2608
  const scoped = listWorkspace(paths, { ...options.prefix ? { prefix: options.prefix } : {}, maxEntries: paths.length });
2609
+ if (scoped.length === 0) return [];
2610
+ try {
2611
+ return await nativeSearch(root, scoped, { ...options, maxResults, maxFileBytes });
2612
+ } catch (error) {
2613
+ options.signal?.throwIfAborted();
2614
+ return fallbackSearch(root, scoped, { ...options, maxResults, maxFileBytes });
2615
+ }
2616
+ }
2617
+ var MAX_NATIVE_ARG_BYTES = 96 * 1024;
2618
+ async function nativeSearch(root, paths, options) {
2619
+ const batches = [];
2620
+ let batch = [];
2621
+ let bytes = 0;
2622
+ for (const path of paths) {
2623
+ const size = Buffer.byteLength(path) + 1;
2624
+ if (batch.length > 0 && bytes + size > MAX_NATIVE_ARG_BYTES) {
2625
+ batches.push(batch);
2626
+ batch = [];
2627
+ bytes = 0;
2628
+ }
2629
+ batch.push(path);
2630
+ bytes += size;
2631
+ }
2632
+ if (batch.length > 0) batches.push(batch);
2633
+ const matches = [];
2634
+ for (const files of batches) {
2635
+ const remaining = options.maxResults - matches.length;
2636
+ if (remaining <= 0) break;
2637
+ matches.push(...await nativeSearchBatch(root, files, options, remaining));
2638
+ }
2639
+ return matches;
2640
+ }
2641
+ function nativeSearchBatch(root, paths, options, remaining) {
2642
+ return new Promise((resolveMatches, reject) => {
2643
+ const args = [
2644
+ "--fixed-strings",
2645
+ "--json",
2646
+ "--no-messages",
2647
+ "--sort=path",
2648
+ `--max-filesize=${options.maxFileBytes}`,
2649
+ "--max-columns=4096",
2650
+ "--max-columns-preview",
2651
+ options.caseSensitive === false ? "--ignore-case" : "--case-sensitive",
2652
+ "--",
2653
+ options.query,
2654
+ ...paths
2655
+ ];
2656
+ const child = (0, import_node_child_process6.spawn)("rg", args, {
2657
+ cwd: root,
2658
+ stdio: ["ignore", "pipe", "ignore"],
2659
+ ...options.signal ? { signal: options.signal } : {}
2660
+ });
2661
+ const matches = [];
2662
+ let carry = "";
2663
+ let stopped = false;
2664
+ const consume = (line) => {
2665
+ if (matches.length >= remaining) return;
2666
+ let event;
2667
+ try {
2668
+ event = JSON.parse(line);
2669
+ } catch {
2670
+ return;
2671
+ }
2672
+ const path = event.data?.path?.text;
2673
+ const lineNumber = event.data?.line_number;
2674
+ const source = event.data?.lines?.text;
2675
+ if (event.type !== "match" || path === void 0 || lineNumber === void 0 || source === void 0) return;
2676
+ matches.push({ path, line: lineNumber, text: source.trim().slice(0, 240) });
2677
+ if (matches.length >= remaining) {
2678
+ stopped = true;
2679
+ child.kill();
2680
+ }
2681
+ };
2682
+ child.stdout.setEncoding("utf8");
2683
+ child.stdout.on("data", (chunk) => {
2684
+ carry += chunk;
2685
+ let newline = carry.indexOf("\n");
2686
+ while (newline >= 0) {
2687
+ consume(carry.slice(0, newline));
2688
+ carry = carry.slice(newline + 1);
2689
+ newline = carry.indexOf("\n");
2690
+ }
2691
+ });
2692
+ child.once("error", reject);
2693
+ child.once("close", (code) => {
2694
+ if (carry) consume(carry);
2695
+ if (stopped || code === 0 || code === 1) resolveMatches(matches);
2696
+ else reject(new Error(`native search exited with status ${code ?? "unknown"}`));
2697
+ });
2698
+ });
2699
+ }
2700
+ async function fallbackSearch(root, scoped, options) {
2701
+ const query = options.caseSensitive === false ? options.query.toLowerCase() : options.query;
2567
2702
  const matches = [];
2568
2703
  for (const path of scoped) {
2569
- if (matches.length >= maxResults) break;
2704
+ options.signal?.throwIfAborted();
2705
+ if (matches.length >= options.maxResults) break;
2570
2706
  let source;
2571
2707
  try {
2572
2708
  source = await (0, import_promises9.readFile)((0, import_node_path9.resolve)(root, path));
2573
2709
  } catch {
2574
2710
  continue;
2575
2711
  }
2576
- if (source.byteLength > maxFileBytes || source.includes(0)) continue;
2712
+ if (source.byteLength > options.maxFileBytes || source.includes(0)) continue;
2577
2713
  const lines = source.toString("utf8").split("\n");
2578
2714
  for (let index = 0; index < lines.length; index += 1) {
2579
2715
  const raw = lines[index];
2580
2716
  const haystack = options.caseSensitive === false ? raw.toLowerCase() : raw;
2581
2717
  if (!haystack.includes(query)) continue;
2582
2718
  matches.push({ path, line: index + 1, text: raw.trim().slice(0, 240) });
2583
- if (matches.length >= maxResults) break;
2719
+ if (matches.length >= options.maxResults) break;
2584
2720
  }
2585
2721
  }
2586
2722
  return matches;
@@ -2605,6 +2741,9 @@ function workspaceGraphs(workspaceDir, paths) {
2605
2741
  cache.set(workspaceDir, built);
2606
2742
  return built;
2607
2743
  }
2744
+ function forgetWorkspaceGraphs(workspaceDir) {
2745
+ cache.delete(workspaceDir);
2746
+ }
2608
2747
  var shortId = (id) => id.slice(id.indexOf(":") + 1);
2609
2748
  function renderOverview(graphs, prefix) {
2610
2749
  const rows = (0, import_graph.rollup)(graphs.graph, import_code4.FILE, prefix === void 0 ? {} : { prefix });
@@ -2651,7 +2790,7 @@ var GRAPH_TOOLS = /* @__PURE__ */ new Set([
2651
2790
  "sandbox.who_imports",
2652
2791
  "sandbox.who_touches"
2653
2792
  ]);
2654
- async function read(context, request, options, policy) {
2793
+ async function read(context, request, options, policy, registry) {
2655
2794
  exactKeys(request.input, ["path", "startLine", "endLine"]);
2656
2795
  const path = stringField(request.input, "path");
2657
2796
  const startLine = optionalInteger(request.input.startLine) ?? 1;
@@ -2659,7 +2798,7 @@ async function read(context, request, options, policy) {
2659
2798
  if (endLine < startLine || endLine - startLine + 1 > (options.maxReadLines ?? 2e3)) {
2660
2799
  throw new TypeError("requested line range exceeds its bound");
2661
2800
  }
2662
- const paths = await registeredFiles(context.workspaceDir, 2e4);
2801
+ const paths = await registry.files(context.workspaceDir);
2663
2802
  if (!paths.includes(path)) {
2664
2803
  throw new TypeError(`no such file in the staged workspace: "${path}". Use sandbox.overview, sandbox.where_is or sandbox.search to find the correct path.`);
2665
2804
  }
@@ -2679,13 +2818,13 @@ async function read(context, request, options, policy) {
2679
2818
  }
2680
2819
  return response(request, true, content, { path, startLine, endLine: Math.min(endLine, lines.length) });
2681
2820
  }
2682
- async function list(context, request, options, policy) {
2821
+ async function list(context, request, options, policy, registry) {
2683
2822
  exactKeys(request.input, ["prefix", "maxEntries"]);
2684
2823
  const raw = request.input.prefix;
2685
2824
  const prefix = typeof raw === "string" && raw.length > 0 ? raw : void 0;
2686
2825
  const maxEntries = optionalInteger(request.input.maxEntries) ?? 1e3;
2687
2826
  if (maxEntries > 5e3) throw new TypeError("maxEntries exceeds its bound");
2688
- const paths = await registeredFiles(context.workspaceDir, 2e4);
2827
+ const paths = await registry.files(context.workspaceDir);
2689
2828
  const allowed = await policy.list(policyContext(context, request, options, { paths, ...prefix ? { prefix } : {} }));
2690
2829
  if (!allowed) return response(request, false, "tool denied by CaMeL policy");
2691
2830
  const entries = listWorkspace(paths, { ...prefix ? { prefix } : {}, maxEntries });
@@ -2703,7 +2842,7 @@ async function list(context, request, options, policy) {
2703
2842
  { count: entries.length, truncated }
2704
2843
  );
2705
2844
  }
2706
- async function search(context, request, options, policy) {
2845
+ async function search(context, request, options, policy, registry) {
2707
2846
  exactKeys(request.input, ["query", "prefix", "maxResults", "caseSensitive"]);
2708
2847
  const query = stringField(request.input, "query");
2709
2848
  if (query.length > 512) throw new TypeError("search query exceeds its bound");
@@ -2712,21 +2851,22 @@ async function search(context, request, options, policy) {
2712
2851
  const maxResults = optionalInteger(request.input.maxResults) ?? 100;
2713
2852
  if (maxResults > 500) throw new TypeError("maxResults exceeds its bound");
2714
2853
  const caseSensitive = request.input.caseSensitive === void 0 ? true : request.input.caseSensitive === true;
2715
- const paths = await registeredFiles(context.workspaceDir, 2e4);
2854
+ const paths = await registry.files(context.workspaceDir);
2716
2855
  const allowed = await policy.search(policyContext(context, request, options, { paths, query, ...prefix ? { prefix } : {} }));
2717
2856
  if (!allowed) return response(request, false, "tool denied by CaMeL policy");
2718
2857
  const matches = await searchWorkspace(context.workspaceDir, paths, {
2719
2858
  query,
2720
2859
  maxResults,
2721
2860
  caseSensitive,
2722
- ...prefix ? { prefix } : {}
2861
+ ...prefix ? { prefix } : {},
2862
+ ...context.signal ? { signal: context.signal } : {}
2723
2863
  });
2724
2864
  if (!matches.length) return response(request, true, `No match for "${query}".`, { count: 0 });
2725
2865
  return response(request, true, matches.map((match) => `${match.path}:${match.line}: ${match.text}`).join("\n"), {
2726
2866
  count: matches.length
2727
2867
  });
2728
2868
  }
2729
- async function graphQuery(context, request, options, policy) {
2869
+ async function graphQuery(context, request, options, policy, registry) {
2730
2870
  exactKeys(request.input, ["query"]);
2731
2871
  const raw = request.input.query;
2732
2872
  const query = typeof raw === "string" ? raw : "";
@@ -2736,7 +2876,7 @@ async function graphQuery(context, request, options, policy) {
2736
2876
  selector: query
2737
2877
  }));
2738
2878
  if (!allowed) return response(request, false, "tool denied by CaMeL policy");
2739
- const paths = await registeredFiles(context.workspaceDir, 2e4);
2879
+ const paths = await registry.files(context.workspaceDir);
2740
2880
  const graphs = await workspaceGraphs(context.workspaceDir, paths);
2741
2881
  if (request.tool === "sandbox.overview") {
2742
2882
  return response(request, true, renderOverview(graphs, query || void 0));
@@ -2752,23 +2892,38 @@ function createCodeToolBroker(options) {
2752
2892
  validateOptions(options);
2753
2893
  const recipes = new Map(options.recipes.map((recipe2) => [recipe2.id, recipe2]));
2754
2894
  const policy = createCodePolicyGate(options);
2755
- let tail = Promise.resolve();
2895
+ const registry = createWorkspaceFileRegistry();
2896
+ let barrier = Promise.resolve();
2897
+ const activeReads = /* @__PURE__ */ new Set();
2756
2898
  return {
2757
2899
  execute(context, request) {
2758
- const result = tail.then(() => route(context, request, options, recipes, policy));
2759
- tail = result.then(() => void 0, () => void 0);
2900
+ if (isReadTool(request.tool)) {
2901
+ const result2 = barrier.then(() => route(context, request, options, recipes, policy, registry));
2902
+ const settled = result2.then(() => void 0, () => void 0);
2903
+ activeReads.add(settled);
2904
+ void settled.then(() => {
2905
+ activeReads.delete(settled);
2906
+ });
2907
+ return result2;
2908
+ }
2909
+ const earlierReads = [...activeReads];
2910
+ const result = barrier.then(() => Promise.all(earlierReads)).then(() => route(context, request, options, recipes, policy, registry));
2911
+ barrier = result.then(() => void 0, () => void 0);
2760
2912
  return result;
2761
2913
  }
2762
2914
  };
2763
2915
  }
2764
- async function route(context, request, options, recipes, policy) {
2916
+ function isReadTool(tool) {
2917
+ return tool === "sandbox.read" || tool === "sandbox.list" || tool === "sandbox.search" || GRAPH_TOOLS.has(tool);
2918
+ }
2919
+ async function route(context, request, options, recipes, policy, registry) {
2765
2920
  try {
2766
2921
  if (context.signal?.aborted) throw new TypeError("tool request was cancelled");
2767
- if (request.tool === "sandbox.read") return await read(context, request, options, policy);
2768
- if (request.tool === "sandbox.list") return await list(context, request, options, policy);
2769
- if (request.tool === "sandbox.search") return await search(context, request, options, policy);
2770
- if (GRAPH_TOOLS.has(request.tool)) return await graphQuery(context, request, options, policy);
2771
- if (request.tool === "sandbox.apply_patch") return await patch(context, request, options, policy);
2922
+ if (request.tool === "sandbox.read") return await read(context, request, options, policy, registry);
2923
+ if (request.tool === "sandbox.list") return await list(context, request, options, policy, registry);
2924
+ if (request.tool === "sandbox.search") return await search(context, request, options, policy, registry);
2925
+ if (GRAPH_TOOLS.has(request.tool)) return await graphQuery(context, request, options, policy, registry);
2926
+ if (request.tool === "sandbox.apply_patch") return await patch(context, request, options, policy, registry);
2772
2927
  return await recipe(context, request, options, recipes, policy);
2773
2928
  } catch (reason) {
2774
2929
  return response(request, false, toolFailureMessage(reason));
@@ -2783,7 +2938,7 @@ function toolFailureMessage(reason) {
2783
2938
  if (code === "EACCES" || code === "EPERM") return "that path is not readable through this tool";
2784
2939
  return "tool failed closed";
2785
2940
  }
2786
- async function patch(context, request, options, policy) {
2941
+ async function patch(context, request, options, policy, registry) {
2787
2942
  exactKeys(request.input, ["patch"]);
2788
2943
  const value = stringField(request.input, "patch");
2789
2944
  const paths = validateCodePatch(value, options.maxPatchBytes ?? 256 * 1024);
@@ -2793,6 +2948,8 @@ async function patch(context, request, options, policy) {
2793
2948
  const allowed = await policy.patch(policyContext(context, request, options, { patch: value }));
2794
2949
  if (!allowed) return response(request, false, "tool denied by CaMeL policy");
2795
2950
  await applyCodePatch(context.workspaceDir, value, paths);
2951
+ registry.invalidate(context.workspaceDir);
2952
+ forgetWorkspaceGraphs(context.workspaceDir);
2796
2953
  return response(request, true, `Applied patch to ${paths.length} file(s).`, { paths });
2797
2954
  }
2798
2955
  async function recipe(context, request, options, recipes, policy) {
@@ -3184,7 +3341,7 @@ var digestRuntimeValue = (value) => `sha256:${(0, import_node_crypto4.createHash
3184
3341
  var runtimeErrorMessage = (value) => value instanceof Error ? value.message : String(value);
3185
3342
 
3186
3343
  // src/code-runtime-engine.ts
3187
- var CodePiRuntimeEngine = class {
3344
+ var TheseusRuntimeEngine = class {
3188
3345
  constructor(options) {
3189
3346
  this.options = options;
3190
3347
  this.#attempt = options.runAgentAttempt ?? runCodeAgentAttempt;
@@ -3270,7 +3427,7 @@ var CodePiRuntimeEngine = class {
3270
3427
  await this.#failure(command, active, detail);
3271
3428
  return null;
3272
3429
  });
3273
- return { status: "running", message: resume ? "Pi resumed from a portable checkpoint" : "Pi started" };
3430
+ return { status: "running", message: resume ? "Theseus resumed from a portable checkpoint" : "Theseus started" };
3274
3431
  }
3275
3432
  /**
3276
3433
  * Pursue a goal: attempt, judge with the clean verifier, re-prompt from what
@@ -3358,7 +3515,7 @@ var CodePiRuntimeEngine = class {
3358
3515
  await this.#failure(command, active, detail);
3359
3516
  return null;
3360
3517
  });
3361
- return { status: "running", message: "Pi accepted the owner prompt" };
3518
+ return { status: "running", message: "Theseus accepted the owner prompt" };
3362
3519
  }
3363
3520
  async #runAttempt(command, metadata, active) {
3364
3521
  const lease = fakeCodeLease(command, metadata);
@@ -3383,7 +3540,8 @@ var CodePiRuntimeEngine = class {
3383
3540
  lease,
3384
3541
  workspaceDir: active.workspace.workspaceDir,
3385
3542
  prompt: metadata.prompt,
3386
- signal: active.abort.signal
3543
+ signal: active.abort.signal,
3544
+ recipeIds: this.options.recipes.map((recipe2) => recipe2.id)
3387
3545
  });
3388
3546
  const closing = result.finalText.trim();
3389
3547
  const completed = result.status === "completed" && Boolean(closing);
@@ -3447,7 +3605,7 @@ var CodePiRuntimeEngine = class {
3447
3605
  }
3448
3606
  }
3449
3607
  async #diagnostic(command, active, value) {
3450
- const detail = value.trim().slice(0, 2e3) || "Pi runtime failed";
3608
+ const detail = value.trim().slice(0, 2e3) || "Theseus runtime failed";
3451
3609
  this.options.onDiagnostic?.(detail);
3452
3610
  await this.#event(
3453
3611
  command,
@@ -3763,7 +3921,6 @@ async function installedDependencies(repoRoot) {
3763
3921
  // Annotate the CommonJS export names for ESM import in node:
3764
3922
  0 && (module.exports = {
3765
3923
  CODE_RUNTIME_PROTOCOL_VERSION,
3766
- CodePiRuntimeEngine,
3767
3924
  CodeRuntimeCheckpointManager,
3768
3925
  CodeRuntimeControlError,
3769
3926
  CodeRuntimeReconciler,
@@ -3771,6 +3928,7 @@ async function installedDependencies(repoRoot) {
3771
3928
  MAX_MEMORY_BODY,
3772
3929
  MEASURED_PREMIUM,
3773
3930
  SYSTEM_PROMPT_FOR,
3931
+ TheseusRuntimeEngine,
3774
3932
  V1_SYSTEM_PROMPT,
3775
3933
  V2_SYSTEM_PROMPT,
3776
3934
  V3_SYSTEM_PROMPT,