@odla-ai/harness 0.9.2 → 0.9.4

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.
@@ -1530,12 +1530,24 @@ async function materializeCommandWorkspace(input) {
1530
1530
  resume
1531
1531
  });
1532
1532
  if (command.payload.sourceSet) {
1533
- const selected = await input.control.source(command.sessionId);
1534
- if (selected.repository !== metadata.repository || selected.commitSha !== metadata.baseCommitSha || selected.treeDigest !== metadata.sourceTreeDigest) {
1533
+ if (prepared.trustedBaseDigest !== metadata.sourceTreeDigest) {
1535
1534
  await prepared.workspace.cleanup();
1536
1535
  throw new TypeError("Code local source does not match the selected GitHub primary source");
1537
1536
  }
1538
- await attachCodeRuntimeReferences(prepared.workspace, selected.references ?? []);
1537
+ const set = command.payload.sourceSet && typeof command.payload.sourceSet === "object" && !Array.isArray(command.payload.sourceSet) ? command.payload.sourceSet : null;
1538
+ const references = set?.references;
1539
+ if (!Array.isArray(references)) {
1540
+ await prepared.workspace.cleanup();
1541
+ throw new TypeError("Code selected source set is invalid");
1542
+ }
1543
+ if (references.length) {
1544
+ const selected = await input.control.source(command.sessionId);
1545
+ if (selected.repository !== metadata.repository || selected.commitSha !== metadata.baseCommitSha || selected.treeDigest !== metadata.sourceTreeDigest) {
1546
+ await prepared.workspace.cleanup();
1547
+ throw new TypeError("Code local source does not match the selected GitHub primary source");
1548
+ }
1549
+ await attachCodeRuntimeReferences(prepared.workspace, selected.references ?? []);
1550
+ }
1539
1551
  }
1540
1552
  return {
1541
1553
  workspace: prepared.workspace,
@@ -1747,7 +1759,8 @@ function codeSkill(opts) {
1747
1759
  true
1748
1760
  )
1749
1761
  ];
1750
- const tools = opts.surface === "v3" ? [...orientation, searchFiles, read2, applyPatch, runRecipe] : opts.surface === "v2" ? [listFiles, searchFiles, read2, applyPatch, runRecipe] : [read2, applyPatch, runRecipe];
1762
+ const effects = opts.readOnly ? [] : [applyPatch, runRecipe];
1763
+ const tools = opts.surface === "v3" ? [...orientation, searchFiles, read2, ...effects] : opts.surface === "v2" ? [listFiles, searchFiles, read2, ...effects] : [read2, ...effects];
1751
1764
  return { name: "code", tools };
1752
1765
  }
1753
1766
 
@@ -1760,6 +1773,7 @@ async function runCodeAgent(options) {
1760
1773
  lease: options.lease,
1761
1774
  workspaceDir: options.workspaceDir,
1762
1775
  surface,
1776
+ ...options.readOnly === void 0 ? {} : { readOnly: options.readOnly },
1763
1777
  ...options.recipeIds ? { recipeIds: options.recipeIds } : {},
1764
1778
  onToolCall: (call) => {
1765
1779
  toolCalls.push(call);
@@ -1772,7 +1786,7 @@ async function runCodeAgent(options) {
1772
1786
  {
1773
1787
  name: "odla-code",
1774
1788
  model: options.model,
1775
- system: options.system ?? SYSTEM_PROMPT_FOR[surface],
1789
+ system: options.system ?? `${SYSTEM_PROMPT_FOR[surface]}${options.readOnly ? "\n\nThis session is read-only. Inspect and report; repository mutation and recipe execution are intentionally unavailable." : ""}`,
1776
1790
  skills: [skill, ...options.extraSkills ?? []],
1777
1791
  maxSteps: options.maxSteps ?? 24,
1778
1792
  maxTokens: options.maxTokens ?? 16384
@@ -1802,6 +1816,7 @@ async function runCodeAgentAttempt(options) {
1802
1816
  // id only labels the request the control plane is about to rewrite.
1803
1817
  model: "brokered",
1804
1818
  surface,
1819
+ ...options.readOnly === void 0 ? {} : { readOnly: options.readOnly },
1805
1820
  ...options.recipeIds ? { recipeIds: options.recipeIds } : {},
1806
1821
  ...options.extraSkills ? { extraSkills: options.extraSkills } : {},
1807
1822
  ...options.maxSteps === void 0 ? {} : { maxSteps: options.maxSteps },
@@ -2819,7 +2834,16 @@ function createCodeRuntimeToolBroker(input, lease, role) {
2819
2834
  readerId: `code-session:${lease.task.taskId}`,
2820
2835
  readOnlyPrefixes: [".odla-references"]
2821
2836
  });
2822
- return role === "coding" ? broker : { execute: (context, request) => request.tool === "sandbox.read" ? broker.execute(context, request) : Promise.resolve({ requestId: request.requestId, ok: false, content: "review sessions are read-only" }) };
2837
+ const reviewReads = /* @__PURE__ */ new Set([
2838
+ "sandbox.read",
2839
+ "sandbox.list",
2840
+ "sandbox.search",
2841
+ "sandbox.overview",
2842
+ "sandbox.where_is",
2843
+ "sandbox.who_imports",
2844
+ "sandbox.who_touches"
2845
+ ]);
2846
+ return role === "coding" ? broker : { execute: (context, request) => reviewReads.has(request.tool) ? broker.execute(context, request) : Promise.resolve({ requestId: request.requestId, ok: false, content: "review sessions are read-only" }) };
2823
2847
  }
2824
2848
 
2825
2849
  // src/code-memory.ts
@@ -3397,6 +3421,7 @@ var TheseusRuntimeEngine = class {
3397
3421
  workspaceDir: active.workspace.workspaceDir,
3398
3422
  prompt: metadata.prompt,
3399
3423
  signal: active.abort.signal,
3424
+ readOnly: metadata.role === "review",
3400
3425
  recipeIds: this.options.recipes.map((recipe2) => recipe2.id),
3401
3426
  ...extraSkills.length ? { extraSkills } : {}
3402
3427
  });