bazilion 0.16.0 → 0.17.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.
Files changed (123) hide show
  1. package/dist/cli.js +399 -83
  2. package/dist/cli.js.map +1 -1
  3. package/dist/daemon.js +1840 -132
  4. package/dist/daemon.js.map +1 -1
  5. package/dist/migrations/0001_init.sql +62 -0
  6. package/dist/web/client/assets/ChatPane-BI3QVX2A.js +76 -0
  7. package/dist/web/client/assets/{ConfirmDialog-MicTTwdy.js → ConfirmDialog-Qam6Ut08.js} +1 -1
  8. package/dist/web/client/assets/{FieldRow-DJ2Gx4Du.js → FieldRow-swJ5qn2h.js} +1 -1
  9. package/dist/web/client/assets/ProductionTeamPolicyEditor-LGw1UuF-.js +7 -0
  10. package/dist/web/client/assets/ProfileCommunicationEditor-raoau_YB.js +1 -0
  11. package/dist/web/client/assets/{ResultSourceConversation-CtzJ8QA-.js → ResultSourceConversation-BEwkhFog.js} +1 -1
  12. package/dist/web/client/assets/TeamTabs-BDdTLGIf.js +1 -0
  13. package/dist/web/client/assets/TemplatesTabs-BX5hhEJ7.js +1 -0
  14. package/dist/web/client/assets/{UnsavedChangesGuard-BCsLw-mU.js → UnsavedChangesGuard-CVJtmR1D.js} +1 -1
  15. package/dist/web/client/assets/{_id-BEDmIXVf.js → _id-BQPOE3gj.js} +1 -1
  16. package/dist/web/client/assets/{_id-BFM_tn_s.js → _id-BkYrUP0R.js} +1 -1
  17. package/dist/web/client/assets/{_id-B5Ov7cdO.js → _id-CriixlZD.js} +1 -1
  18. package/dist/web/client/assets/_id-DHqq2W34.js +1 -0
  19. package/dist/web/client/assets/{_id-DxopIW-y.js → _id-DmEVZIlx.js} +1 -1
  20. package/dist/web/client/assets/activity-Bzr8R18n.js +1 -0
  21. package/dist/web/client/assets/{agents-CMPOFcs-.js → agents-13oafeOA.js} +1 -1
  22. package/dist/web/client/assets/{agents-O4soKZND.js → agents-DF4NALmJ.js} +1 -1
  23. package/dist/web/client/assets/{approvals-CwDu_lrA.js → approvals-DsG0Ijls.js} +1 -1
  24. package/dist/web/client/assets/{attention-BY8MMuM_.js → attention-DG7K25mz.js} +1 -1
  25. package/dist/web/client/assets/chevron-right-DFqFHtLy.js +1 -0
  26. package/dist/web/client/assets/config-DAaDbFhS.js +3 -0
  27. package/dist/web/client/assets/createServerFn-Dzfjt7mA.js +3 -0
  28. package/dist/web/client/assets/git-review-presentation-CzHQulDk.js +1 -0
  29. package/dist/web/client/assets/{inbox-CWXql0K3.js → inbox-C9e7QjsX.js} +1 -1
  30. package/dist/web/client/assets/index-Dsstm2hf.js +50 -0
  31. package/dist/web/client/assets/{learning-DAm4dyyc.js → learning-23arTd21.js} +1 -1
  32. package/dist/web/client/assets/login-BIXnBsuj.js +1 -0
  33. package/dist/web/client/assets/{mcp-loLB0wuh.js → mcp-Cdx9qAl8.js} +1 -1
  34. package/dist/web/client/assets/{members-BJ0ikkTl.js → members-fotOz4rR.js} +1 -1
  35. package/dist/web/client/assets/{memory-Drn9Gv53.js → memory-C7UznVot.js} +1 -1
  36. package/dist/web/client/assets/policy-CactLW1X.js +1 -0
  37. package/dist/web/client/assets/{results-9exU6f7m.js → results-DqEuYnQH.js} +1 -1
  38. package/dist/web/client/assets/review-CnBSYclb.js +1 -0
  39. package/dist/web/client/assets/routes-COxMuwVz.js +1 -0
  40. package/dist/web/client/assets/{services-BLso1BhA.js → services-xEANwZAu.js} +1 -1
  41. package/dist/web/client/assets/{skills-BodqX3OI.js → skills-CamV6wYM.js} +1 -1
  42. package/dist/web/client/assets/{styles-d1yjBpud.css → styles-C6br2xxl.css} +1 -1
  43. package/dist/web/client/assets/{teams-ByQedXZi.js → teams-B12dMSjW.js} +1 -1
  44. package/dist/web/client/assets/{teams-SYsF1bOR.js → teams-CbH43zSG.js} +1 -1
  45. package/dist/web/client/assets/{telegram-DC9PLFbB.js → telegram-muIql4u-.js} +1 -1
  46. package/dist/web/client/assets/{tokens-DGy8Ijpo.js → tokens-DGFmApdF.js} +1 -1
  47. package/dist/web/client/assets/{triggers-BXpVmHDG.js → triggers-D8n3sCBW.js} +1 -1
  48. package/dist/web/client/assets/welcome-CMHWRZhs.js +1 -0
  49. package/dist/web/client/assets/x-BGCHwZOH.js +1 -0
  50. package/dist/web/server/assets/{ChatPane-CS5oI7ef.js → ChatPane-DybH39WK.js} +301 -14
  51. package/dist/web/server/assets/{ResultSourceConversation-BpVWBRYq.js → ResultSourceConversation-BmXTcEOA.js} +1 -1
  52. package/dist/web/server/assets/{TeamTabs-TCKjCvMA.js → TeamTabs-DQXh-3ij.js} +4 -0
  53. package/dist/web/server/assets/{_id-5pgKoid8.js → _id-BN1BPEDI.js} +1 -1
  54. package/dist/web/server/assets/{_id-DFHB9JFQ.js → _id-BYKpT2Tu.js} +1 -1
  55. package/dist/web/server/assets/{_id-B_-CDHOi2.js → _id-BqWTB8Wl.js} +1 -1
  56. package/dist/web/server/assets/{_id-BG2nkGxa.js → _id-CPfILjMa.js} +1 -1
  57. package/dist/web/server/assets/{_id-BnqodC1c2.js → _id-C_2er0_G.js} +3 -3
  58. package/dist/web/server/assets/{_id-DtIYuJJX.js → _id-DDyA9CGX.js} +1 -1
  59. package/dist/web/server/assets/{_id-DrB9uR1b.js → _id-DSPFJMzf.js} +1 -1
  60. package/dist/web/server/assets/{_id-DwZXhLlL.js → _id-DtJn-yzu.js} +2 -2
  61. package/dist/web/server/assets/{_id-CeyHEsCZ.js → _id-Dw3gKOGv.js} +1 -1
  62. package/dist/web/server/assets/{_id-DOnInzLb.js → _id-XkxlX036.js} +1 -1
  63. package/dist/web/server/assets/{_tanstack-start-manifest_v-D7gKv6m6.js → _tanstack-start-manifest_v-vOXRfKJD.js} +91 -78
  64. package/dist/web/server/assets/{activity-C88qrNqv.js → activity-BX1jZBPQ.js} +1 -1
  65. package/dist/web/server/assets/{activity-5fWKPDxj.js → activity-bozPMU2v.js} +2 -2
  66. package/dist/web/server/assets/{activity-0VUkStOu.js → activity-pYQPT3QT.js} +1 -1
  67. package/dist/web/server/assets/{agents-CSXLaM2W.js → agents-BKBfW700.js} +1 -1
  68. package/dist/web/server/assets/{agents-C86K6HvS.js → agents-CVCfxnE2.js} +1 -1
  69. package/dist/web/server/assets/{approvals-C3MysI6l.js → approvals-C3ya6B6U.js} +1 -1
  70. package/dist/web/server/assets/{attention-DLeLCKah.js → attention-l28hGCHS.js} +1 -1
  71. package/dist/web/server/assets/{auth-CDZp9aEk.js → auth-AvDFNeOv.js} +1 -1
  72. package/dist/web/server/assets/{config-BVTHvljR.js → config-BWKn4XII.js} +1 -1
  73. package/dist/web/server/assets/{daemon-client-xWyYKAcx.js → daemon-client-Cgx-YW4l.js} +30 -0
  74. package/dist/web/server/assets/git-review-BB3M9nYg.js +26 -0
  75. package/dist/web/server/assets/git-review-f_uta4Mx.js +179 -0
  76. package/dist/web/server/assets/git-review-presentation-TJiHJhen.js +81 -0
  77. package/dist/web/server/assets/{inbox-PQTrjtOw.js → inbox-BBZiOOQs.js} +1 -1
  78. package/dist/web/server/assets/{learning-K0cmDsZR.js → learning-CheVay1i.js} +1 -1
  79. package/dist/web/server/assets/{mcp-Rtraochg.js → mcp-DCBDmNtQ.js} +1 -1
  80. package/dist/web/server/assets/{members-grAKFWgv.js → members-C2He8mrU.js} +1 -1
  81. package/dist/web/server/assets/{members-CD3GiTfF.js → members-C5FQruRZ.js} +1 -1
  82. package/dist/web/server/assets/{members-cc5SeEaW.js → members-C_2xB9pr.js} +2 -2
  83. package/dist/web/server/assets/{memory-CVqnyUaM.js → memory-4tI-UbOf.js} +1 -1
  84. package/dist/web/server/assets/{memory-C5i_GxlY.js → memory-BTcmf0sD.js} +2 -2
  85. package/dist/web/server/assets/{memory-egFpBD1g.js → memory-rmiFNj7T.js} +1 -1
  86. package/dist/web/server/assets/{policy-Dgjb0_gK.js → policy-BGILKGHz.js} +2 -2
  87. package/dist/web/server/assets/{policy-bLKwvnja.js → policy-C3D2Q1EA.js} +1 -1
  88. package/dist/web/server/assets/{policy-ooSBgiDS.js → policy-DS7LGe--.js} +1 -1
  89. package/dist/web/server/assets/{results-DXdYtHaq.js → results-C6H42tjq.js} +1 -1
  90. package/dist/web/server/assets/{results-CfNsFncJ.js → results-Cgx7SrFt.js} +3 -3
  91. package/dist/web/server/assets/{results-N-kP4iFT.js → results-_6K_zXQS.js} +1 -1
  92. package/dist/web/server/assets/review-DHhiL-JQ.js +393 -0
  93. package/dist/web/server/assets/review-DSTMCwP4.js +14 -0
  94. package/dist/web/server/assets/{router-B6g_s7W3.js → router-ElARvZzI.js} +34 -27
  95. package/dist/web/server/assets/{routes-pcRy_Sf8.js → routes-B6x3pEW8.js} +1 -1
  96. package/dist/web/server/assets/{routes-BqSaZvlu.js → routes-BgMvLvS-.js} +1 -1
  97. package/dist/web/server/assets/{routes-Dsh8F_7i.js → routes-DsUzzxk4.js} +2 -2
  98. package/dist/web/server/assets/{services-DlZA8fRL.js → services-yHZroxsi.js} +1 -1
  99. package/dist/web/server/assets/{skills-i2duvgW-.js → skills--bIKID1Q.js} +1 -1
  100. package/dist/web/server/assets/{teams-B1AI3NfN.js → teams--a956vWL.js} +1 -1
  101. package/dist/web/server/assets/{teams-6RYIj1Xm.js → teams-BbRfuVXY.js} +1 -1
  102. package/dist/web/server/assets/{telegram-C4q0YTVn.js → telegram-4kMxRtVZ.js} +1 -1
  103. package/dist/web/server/assets/{tokens-RFquA2Bx.js → tokens-BYwTN9JZ.js} +1 -1
  104. package/dist/web/server/assets/{triggers-DVD-iE-N.js → triggers-BM6mzLuH.js} +1 -1
  105. package/dist/web/server/server.js +56 -28
  106. package/dist/worker.js +292 -75
  107. package/dist/worker.js.map +1 -1
  108. package/package.json +3 -3
  109. package/dist/web/client/assets/ChatPane-CQZzxOir.js +0 -76
  110. package/dist/web/client/assets/ProductionTeamPolicyEditor-DqUnN_ce.js +0 -7
  111. package/dist/web/client/assets/ProfileCommunicationEditor-DArXub0b.js +0 -1
  112. package/dist/web/client/assets/TeamTabs-DbIk5vvT.js +0 -1
  113. package/dist/web/client/assets/TemplatesTabs-CatJIjGi.js +0 -1
  114. package/dist/web/client/assets/_id-CrnEVcw8.js +0 -1
  115. package/dist/web/client/assets/activity-CrG8Bg60.js +0 -1
  116. package/dist/web/client/assets/chevron-right-Cf6vKeLq.js +0 -1
  117. package/dist/web/client/assets/config-Cn4iBJFz.js +0 -3
  118. package/dist/web/client/assets/index-Bq2zrG2_.js +0 -52
  119. package/dist/web/client/assets/login-BsABI8Ux.js +0 -1
  120. package/dist/web/client/assets/policy-pQadZyk0.js +0 -1
  121. package/dist/web/client/assets/routes-jw1PuqiR.js +0 -1
  122. package/dist/web/client/assets/welcome-BKsnsCxJ.js +0 -1
  123. package/dist/web/client/assets/x-BK4xNCzS.js +0 -1
package/dist/daemon.js CHANGED
@@ -24915,14 +24915,16 @@ function safeReaddir(path) {
24915
24915
  import { chmodSync, existsSync as existsSync17, mkdirSync as mkdirSync10, writeFileSync as writeFileSync14 } from "fs";
24916
24916
 
24917
24917
  // ../daemon/src/core/repos/coding-commands.ts
24918
+ var CODING_COMMAND_TTL_MS = 7 * 864e5;
24919
+ var CODING_COMMAND_MAX_PER_TEAM = 200;
24918
24920
  function pruneCodingCommands(db, teamId) {
24919
24921
  db.raw.run(
24920
24922
  "DELETE FROM coding_commands WHERE team_id = ? AND state != 'running' AND created_at < ?",
24921
- [teamId, Date.now() - 7 * 864e5]
24923
+ [teamId, Date.now() - CODING_COMMAND_TTL_MS]
24922
24924
  );
24923
24925
  db.raw.run(
24924
- "DELETE FROM coding_commands WHERE id IN (SELECT id FROM coding_commands WHERE team_id = ? AND state != 'running' ORDER BY created_at DESC, id DESC LIMIT -1 OFFSET 20)",
24925
- [teamId]
24926
+ "DELETE FROM coding_commands WHERE id IN (SELECT id FROM coding_commands WHERE team_id = ? AND state != 'running' ORDER BY created_at DESC, id DESC LIMIT -1 OFFSET ?)",
24927
+ [teamId, CODING_COMMAND_MAX_PER_TEAM]
24926
24928
  );
24927
24929
  }
24928
24930
  function saveCodingCommand(db, receipt) {
@@ -28690,6 +28692,41 @@ function qmdBackend(root, options = {}) {
28690
28692
  };
28691
28693
  }
28692
28694
 
28695
+ // ../daemon/src/lib/coding-environment/receipt-reference.ts
28696
+ function codingLogReference(content) {
28697
+ const parsed = objectFrom(textContent(content));
28698
+ if (!parsed) return null;
28699
+ return referenceFrom(parsed) ?? referenceFrom(objectFrom(parsed.receipt));
28700
+ }
28701
+ function referenceFrom(value) {
28702
+ if (!value) return null;
28703
+ const commandId = value.id;
28704
+ const teamId = value.teamId;
28705
+ if (typeof commandId !== "string" || commandId.length === 0) return null;
28706
+ if (typeof teamId !== "string" || teamId.length === 0) return null;
28707
+ return { commandId, teamId };
28708
+ }
28709
+ function textContent(content) {
28710
+ if (typeof content === "string") return content;
28711
+ if (!Array.isArray(content)) return "";
28712
+ let out = "";
28713
+ for (const block of content) {
28714
+ if (block && block.type === "text" && typeof block.text === "string") out += block.text;
28715
+ }
28716
+ return out;
28717
+ }
28718
+ function objectFrom(value) {
28719
+ if (typeof value === "string") {
28720
+ try {
28721
+ return objectFrom(JSON.parse(value));
28722
+ } catch {
28723
+ return null;
28724
+ }
28725
+ }
28726
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
28727
+ return value;
28728
+ }
28729
+
28693
28730
  // ../daemon/src/runtime/pi/events.ts
28694
28731
  function extractAssistantText(m2) {
28695
28732
  let out = "";
@@ -28759,14 +28796,22 @@ function piMessagesToProviderView(messages, questionResultVisible) {
28759
28796
  }
28760
28797
  case "toolResult": {
28761
28798
  const tr = m2;
28762
- if (["repository_context", "coding_environment", "coding_command", "coding_receipt"].includes(
28763
- tr.toolName ?? ""
28764
- )) {
28799
+ if ([
28800
+ "repository_context",
28801
+ "coding_environment",
28802
+ "coding_command",
28803
+ "coding_receipt",
28804
+ "coding_log",
28805
+ // BAZ-042: a snapshot result names withheld paths, so history must not render it.
28806
+ "source_snapshot"
28807
+ ].includes(tr.toolName ?? "")) {
28808
+ const codingLog = codingLogReference(tr.content);
28765
28809
  out.push({
28766
28810
  role: "tool",
28767
28811
  toolCallId: tr.toolCallId,
28768
28812
  toolName: tr.toolName,
28769
- content: "Coding context or command evidence retained privately. Captured transport output follows its communication approval; ask the Agent for the relevant result."
28813
+ content: "Coding context or command evidence retained privately. Captured transport output follows its communication approval; ask the Agent for the relevant result.",
28814
+ ...codingLog ? { codingLog } : {}
28770
28815
  });
28771
28816
  break;
28772
28817
  }
@@ -28803,14 +28848,10 @@ function extractResultReference(value) {
28803
28848
  return typeof id === "string" && /^[a-f0-9-]{36}$/.test(id) ? { resultId: id } : void 0;
28804
28849
  }
28805
28850
 
28806
- // ../daemon/src/runtime/pi/coding.ts
28807
- import {
28808
- createLocalBashOperations
28809
- } from "@earendil-works/pi-coding-agent";
28810
- import { Type } from "typebox";
28811
-
28812
28851
  // ../daemon/src/lib/coding-environment/diagnostics.ts
28813
28852
  var CODING_OUTPUT_BYTES = 64 * 1024;
28853
+ var CODING_RETAINED_BYTES = 2 * 1024 * 1024;
28854
+ var CODING_LIVE_BYTES = 8 * 1024;
28814
28855
  function diagnosticTail(value, limit) {
28815
28856
  const bytes = Buffer.from(value);
28816
28857
  if (bytes.length <= limit) return { text: value, truncated: false };
@@ -28822,28 +28863,47 @@ var CodingDiagnostics = class {
28822
28863
  decoder = new TextDecoder();
28823
28864
  secrets;
28824
28865
  lookbehind;
28866
+ retainBytes;
28825
28867
  pending = "";
28826
28868
  text = "";
28827
28869
  truncated = false;
28870
+ observedBytes = 0;
28871
+ redactions = 0;
28828
28872
  finished = false;
28829
- constructor(secrets) {
28873
+ constructor(secrets, retainBytes = CODING_OUTPUT_BYTES) {
28830
28874
  this.secrets = [...new Set(secrets.filter(Boolean))].sort((a, b2) => b2.length - a.length);
28831
28875
  this.lookbehind = Math.max(1, ...this.secrets.map((secret) => secret.length)) - 1;
28876
+ this.retainBytes = retainBytes;
28832
28877
  }
28833
28878
  append(bytes) {
28834
28879
  if (this.finished) throw new Error("Coding diagnostics already finalized");
28880
+ this.observedBytes += bytes.length;
28835
28881
  for (let offset = 0; offset < bytes.length; offset += 8192) {
28836
28882
  this.pending += this.decoder.decode(bytes.subarray(offset, offset + 8192), { stream: true });
28837
28883
  this.flush(false);
28838
28884
  }
28839
28885
  }
28886
+ /**
28887
+ * Redacted cumulative tail for a live update. Unlike `finish()` this does not seal
28888
+ * the diagnostics: more output may follow. It deliberately excludes the trailing
28889
+ * lookbehind bytes so a credential split across chunks cannot leak before the next
28890
+ * chunk resolves it.
28891
+ */
28892
+ preview(limit = CODING_LIVE_BYTES) {
28893
+ return diagnosticTail(this.text, limit);
28894
+ }
28840
28895
  finish() {
28841
28896
  if (!this.finished) {
28842
28897
  this.pending += this.decoder.decode();
28843
28898
  this.flush(true);
28844
28899
  this.finished = true;
28845
28900
  }
28846
- return { diagnostic: this.text, truncated: this.truncated };
28901
+ return {
28902
+ diagnostic: this.text,
28903
+ truncated: this.truncated,
28904
+ observedBytes: this.observedBytes,
28905
+ redacted: this.redactions > 0
28906
+ };
28847
28907
  }
28848
28908
  flush(final) {
28849
28909
  let end = final ? this.pending.length : Math.max(0, this.pending.length - this.lookbehind);
@@ -28855,6 +28915,7 @@ var CodingDiagnostics = class {
28855
28915
  const secret = this.secrets.find((value) => this.pending.startsWith(value, offset));
28856
28916
  if (secret) {
28857
28917
  safe += "[redacted]";
28918
+ this.redactions++;
28858
28919
  offset += secret.length;
28859
28920
  } else {
28860
28921
  const code = this.pending.charCodeAt(offset);
@@ -28864,7 +28925,7 @@ var CodingDiagnostics = class {
28864
28925
  }
28865
28926
  }
28866
28927
  this.pending = this.pending.slice(offset);
28867
- const tail = diagnosticTail(this.text + safe, CODING_OUTPUT_BYTES);
28928
+ const tail = diagnosticTail(this.text + safe, this.retainBytes);
28868
28929
  this.text = tail.text;
28869
28930
  this.truncated ||= tail.truncated;
28870
28931
  }
@@ -28888,6 +28949,12 @@ function codingSecrets(env) {
28888
28949
  return [...values];
28889
28950
  }
28890
28951
 
28952
+ // ../daemon/src/runtime/pi/coding.ts
28953
+ import {
28954
+ createLocalBashOperations
28955
+ } from "@earendil-works/pi-coding-agent";
28956
+ import { Type } from "typebox";
28957
+
28891
28958
  // ../daemon/src/runtime/shell/approval.ts
28892
28959
  import { defineTool } from "@earendil-works/pi-coding-agent";
28893
28960
 
@@ -29265,6 +29332,7 @@ function createApprovalGatedBashTool(base, host2) {
29265
29332
 
29266
29333
  // ../daemon/src/runtime/pi/coding.ts
29267
29334
  function codingTools(input) {
29335
+ const readSecrets = () => typeof input.secrets === "function" ? input.secrets() : input.secrets ?? [];
29268
29336
  const hostOperations = input.docker ? null : createLocalBashOperations();
29269
29337
  return [
29270
29338
  {
@@ -29305,7 +29373,7 @@ function codingTools(input) {
29305
29373
  { additionalProperties: false }
29306
29374
  ),
29307
29375
  executionMode: "sequential",
29308
- async execute(toolCallId, params, signal) {
29376
+ async execute(toolCallId, params, signal, onUpdate) {
29309
29377
  const command = validateCodingCommand(params);
29310
29378
  const context = await input.context(command.cwd);
29311
29379
  if (context.instructions.state !== "complete")
@@ -29315,7 +29383,27 @@ function codingTools(input) {
29315
29383
  toolCallId,
29316
29384
  input: command
29317
29385
  });
29318
- const output = new CodingDiagnostics(input.secrets ?? []);
29386
+ const output = new CodingDiagnostics(readSecrets());
29387
+ let lastProgressAt = 0;
29388
+ const emitProgress = (force) => {
29389
+ if (!onUpdate) return;
29390
+ const now = Date.now();
29391
+ if (!force && now - lastProgressAt < 400) return;
29392
+ lastProgressAt = now;
29393
+ const preview = output.preview(CODING_LIVE_BYTES);
29394
+ onUpdate({
29395
+ content: [{ type: "text", text: preview.text }],
29396
+ details: {
29397
+ codingProgress: {
29398
+ id: toolCallId,
29399
+ commandId: started.id,
29400
+ output: preview.text,
29401
+ truncated: preview.truncated,
29402
+ elapsedMs: Math.max(0, now - started.startedAt)
29403
+ }
29404
+ }
29405
+ });
29406
+ };
29319
29407
  let outcome;
29320
29408
  try {
29321
29409
  if (input.approval)
@@ -29336,8 +29424,16 @@ function codingTools(input) {
29336
29424
  const result = await operations.exec(
29337
29425
  command.command,
29338
29426
  input.docker ? input.root : cwd.path,
29339
- { onData: (bytes) => output.append(bytes), signal, timeout: command.timeoutSeconds }
29427
+ {
29428
+ onData: (bytes) => {
29429
+ output.append(bytes);
29430
+ emitProgress(false);
29431
+ },
29432
+ signal,
29433
+ timeout: command.timeoutSeconds
29434
+ }
29340
29435
  );
29436
+ emitProgress(true);
29341
29437
  outcome = {
29342
29438
  state: result.exitCode === 0 ? "succeeded" : result.exitCode === null ? "interrupted" : "failed",
29343
29439
  exitCode: result.exitCode,
@@ -29382,6 +29478,74 @@ function codingTools(input) {
29382
29478
  details: { codingEvidence: value }
29383
29479
  };
29384
29480
  }
29481
+ },
29482
+ {
29483
+ name: "coding_log",
29484
+ label: "Read retained command output",
29485
+ description: "Read a bounded page of retained diagnostic output for a coding receipt, or search it. Producing Agents may read their own logs; a teammate must pass the id of the policy-authorized message that carried coding-receipt:<id>. Output is bounded and may be truncated, expired, deleted or unavailable \u2014 report that state honestly and do not infer completeness from an exit code.",
29486
+ parameters: Type.Object(
29487
+ {
29488
+ id: Type.String({ maxLength: 64 }),
29489
+ messageId: Type.Optional(Type.String({ maxLength: 64 })),
29490
+ search: Type.Optional(Type.String({ minLength: 1, maxLength: 256 })),
29491
+ offset: Type.Optional(Type.Integer({ minimum: 0 })),
29492
+ limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 65536 }))
29493
+ },
29494
+ { additionalProperties: false }
29495
+ ),
29496
+ executionMode: "sequential",
29497
+ async execute(_id, params) {
29498
+ const values = params;
29499
+ if (values.search !== void 0) {
29500
+ const result = await input.host.invoke({
29501
+ action: "log-search",
29502
+ id: values.id,
29503
+ query: values.search,
29504
+ ...values.messageId !== void 0 ? { messageId: values.messageId } : {}
29505
+ });
29506
+ return {
29507
+ content: [{ type: "text", text: JSON.stringify(result) }],
29508
+ details: { codingLogSearch: result }
29509
+ };
29510
+ }
29511
+ const value = await input.host.invoke({
29512
+ action: "log",
29513
+ id: values.id,
29514
+ ...values.messageId !== void 0 ? { messageId: values.messageId } : {},
29515
+ ...values.offset !== void 0 ? { offset: values.offset } : {},
29516
+ ...values.limit !== void 0 ? { limit: values.limit } : {}
29517
+ });
29518
+ return {
29519
+ content: [{ type: "text", text: JSON.stringify(value) }],
29520
+ details: { codingLog: value }
29521
+ };
29522
+ }
29523
+ },
29524
+ {
29525
+ name: "source_snapshot",
29526
+ label: "Capture the current source state",
29527
+ description: "Capture a bounded snapshot of the repository source you are about to work from, and quote its reference in your summary. Call this once before you start editing: the reference is how a later change or verification is tied to the version it actually applies to. It is read-only and never changes the repository. The result may be incomplete \u2014 when it is, say so, because an incomplete snapshot cannot support a claim that code is unchanged. Untracked file content is never included unless you name each path.",
29528
+ parameters: Type.Object(
29529
+ {
29530
+ includeUntracked: Type.Optional(
29531
+ Type.Array(Type.String({ maxLength: 4096 }), { maxItems: 1e3 })
29532
+ )
29533
+ },
29534
+ { additionalProperties: false }
29535
+ ),
29536
+ executionMode: "sequential",
29537
+ async execute(toolCallId, params) {
29538
+ const values = params;
29539
+ const value = await input.host.invoke({
29540
+ action: "snapshot",
29541
+ toolCallId,
29542
+ ...values.includeUntracked ? { includeUntracked: values.includeUntracked } : {}
29543
+ });
29544
+ return {
29545
+ content: [{ type: "text", text: JSON.stringify(value) }],
29546
+ details: { sourceSnapshot: value }
29547
+ };
29548
+ }
29385
29549
  }
29386
29550
  ];
29387
29551
  }
@@ -29719,6 +29883,20 @@ function fallbackApi(providerName) {
29719
29883
  return "openai-completions";
29720
29884
  }
29721
29885
  }
29886
+ var UnknownModelError = class extends Error {
29887
+ providerName;
29888
+ modelId;
29889
+ // Fields are declared explicitly, not as constructor parameter properties:
29890
+ // worker subprocesses run Node's strip-only TypeScript, which rejects them.
29891
+ constructor(providerName, modelId) {
29892
+ super(
29893
+ `Unknown model "${modelId}" for provider "${providerName}": it is not in that provider's catalog, and no endpoint is configured for it. Choose a catalogued model, or configure an endpoint for a custom one.`
29894
+ );
29895
+ this.name = "UnknownModelError";
29896
+ this.providerName = providerName;
29897
+ this.modelId = modelId;
29898
+ }
29899
+ };
29722
29900
  function fallbackPiModel(providerName, modelId, baseUrl) {
29723
29901
  return {
29724
29902
  id: modelId,
@@ -29779,6 +29957,7 @@ async function createBazilionPiRuntime(options) {
29779
29957
  function resolvePiModel(runtime, providerName, modelId, baseUrl) {
29780
29958
  const known = runtime.getModel(piProviderName(providerName), modelId);
29781
29959
  if (known) return baseUrl ? { ...known, baseUrl } : known;
29960
+ if (!baseUrl) throw new UnknownModelError(providerName, modelId);
29782
29961
  return fallbackPiModel(providerName, modelId, baseUrl);
29783
29962
  }
29784
29963
 
@@ -33218,6 +33397,7 @@ async function createBazilionSession(opts) {
33218
33397
  if (repository) bazilionTools.push(repository.tool);
33219
33398
  if (repository && opts.codingHost && shellTools && shellTools.config.sandboxMode === "docker" && !opts.preparedDocker)
33220
33399
  throw new Error("Coding commands require the admitted Docker runtime");
33400
+ const redactionSecrets = uniqueSecrets(codingSecrets(env));
33221
33401
  if (repository && opts.codingHost && shellTools)
33222
33402
  bazilionTools.push(
33223
33403
  ...codingTools({
@@ -33228,7 +33408,7 @@ async function createBazilionSession(opts) {
33228
33408
  lifecycle: opts.dockerLifecycle,
33229
33409
  approval: shellTools.config.approvalMode === "dangerous",
33230
33410
  approvalHost: bashApprovalHost,
33231
- secrets: Object.entries(env).filter(([key3]) => /KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|AUTH/i.test(key3)).map(([, value]) => value ?? "")
33411
+ secrets: () => redactionSecrets
33232
33412
  })
33233
33413
  );
33234
33414
  const customTools = shellTools?.customBash ? [...bazilionTools, shellTools.customBash] : bazilionTools;
@@ -33250,7 +33430,9 @@ async function createBazilionSession(opts) {
33250
33430
  session.agent.getApiKey = async (requestedProvider) => {
33251
33431
  if (requestedProvider !== piProviderName(providerName)) return void 0;
33252
33432
  try {
33253
- return await refreshApiKey(providerName);
33433
+ const token = await refreshApiKey(providerName);
33434
+ if (token && !redactionSecrets.includes(token)) redactionSecrets.push(token);
33435
+ return token;
33254
33436
  } catch {
33255
33437
  return void 0;
33256
33438
  }
@@ -33263,6 +33445,9 @@ async function createBazilionSession(opts) {
33263
33445
  }
33264
33446
  };
33265
33447
  }
33448
+ function uniqueSecrets(values) {
33449
+ return [...new Set(values.filter(Boolean))];
33450
+ }
33266
33451
  function splitModelString(s) {
33267
33452
  const idx = s.indexOf(":");
33268
33453
  if (idx === -1) {
@@ -34875,6 +35060,223 @@ function createBrowserHost(config2) {
34875
35060
  // ../daemon/src/lib/coding-environment/agent-host.ts
34876
35061
  import { createHash as createHash10, randomUUID as randomUUID30 } from "crypto";
34877
35062
 
35063
+ // ../daemon/src/core/repos/coding-command-logs.ts
35064
+ var CODING_LOG_BYTES = 2 * 1024 * 1024;
35065
+ var CODING_LOG_HOME_BYTES = 256 * 1024 * 1024;
35066
+ var CODING_LOG_TTL_MS = 7 * 864e5;
35067
+ var CODING_LOG_PAGE_BYTES = 64 * 1024;
35068
+ var CODING_LOG_SEARCH_HITS = 50;
35069
+ var CODING_LOG_SEARCH_SCAN_BYTES = 512 * 1024;
35070
+ function headBytes(value, limit) {
35071
+ const bytes = Buffer.from(value);
35072
+ if (bytes.length <= limit) return value;
35073
+ let end = limit;
35074
+ while (end < bytes.length && ((bytes[end] ?? 0) & 192) === 128) end++;
35075
+ return bytes.subarray(0, end).toString("utf8");
35076
+ }
35077
+ function mayDisclose(view2, audience) {
35078
+ return audience === "producer" || view2.releasedAt !== null;
35079
+ }
35080
+ var COLUMNS = `command_id, team_id, agent_id, turn_id, tool_call_id, state, text,
35081
+ byte_length, observed_bytes, redacted, truncated, created_at, expires_at, released_at, retired_at`;
35082
+ function expiryDue(row, now) {
35083
+ return row.state === "retained" && row.expires_at <= now;
35084
+ }
35085
+ function liveText(row, now) {
35086
+ return row.state === "retained" && !expiryDue(row, now) ? row.text : null;
35087
+ }
35088
+ function availabilityOf(row, now) {
35089
+ if (row.state === "deleted") return "deleted";
35090
+ if (row.state === "expired" || expiryDue(row, now)) return "expired";
35091
+ return row.truncated === 1 ? "truncated" : "available";
35092
+ }
35093
+ function toView(row, now) {
35094
+ return {
35095
+ commandId: row.command_id,
35096
+ teamId: row.team_id,
35097
+ agentId: row.agent_id,
35098
+ turnId: row.turn_id,
35099
+ toolCallId: row.tool_call_id,
35100
+ availability: availabilityOf(row, now),
35101
+ byteLength: row.byte_length,
35102
+ observedBytes: row.observed_bytes,
35103
+ redacted: row.redacted === 1,
35104
+ createdAt: row.created_at,
35105
+ expiresAt: row.expires_at,
35106
+ releasedAt: row.released_at,
35107
+ retiredAt: row.retired_at
35108
+ };
35109
+ }
35110
+ function rowFor(db, commandId) {
35111
+ return db.raw.query(`SELECT ${COLUMNS} FROM coding_command_logs WHERE command_id = ?`).get(commandId) ?? null;
35112
+ }
35113
+ function unavailableLog(commandId) {
35114
+ return {
35115
+ commandId,
35116
+ teamId: "",
35117
+ agentId: "",
35118
+ turnId: "",
35119
+ toolCallId: "",
35120
+ availability: "unavailable",
35121
+ byteLength: 0,
35122
+ observedBytes: 0,
35123
+ redacted: false,
35124
+ createdAt: 0,
35125
+ expiresAt: 0,
35126
+ releasedAt: null,
35127
+ retiredAt: null
35128
+ };
35129
+ }
35130
+ function getCodingCommandLog(db, commandId, now = Date.now()) {
35131
+ const row = rowFor(db, commandId);
35132
+ return row ? toView(row, now) : unavailableLog(commandId);
35133
+ }
35134
+ function saveCodingCommandLog(db, input) {
35135
+ const now = input.now ?? Date.now();
35136
+ const retained = diagnosticTail(input.diagnostic, CODING_LOG_BYTES);
35137
+ const byteLength = Buffer.byteLength(retained.text);
35138
+ const observed = Math.max(input.observedBytes, byteLength);
35139
+ const truncated = retained.truncated || input.truncated || !input.redacted && input.observedBytes > byteLength;
35140
+ db.raw.run(
35141
+ `INSERT INTO coding_command_logs (${COLUMNS})
35142
+ VALUES (?, ?, ?, ?, ?, 'retained', ?, ?, ?, ?, ?, ?, ?, NULL, NULL)
35143
+ ON CONFLICT(command_id) DO UPDATE SET
35144
+ state = 'retained',
35145
+ text = excluded.text,
35146
+ byte_length = excluded.byte_length,
35147
+ observed_bytes = excluded.observed_bytes,
35148
+ redacted = excluded.redacted,
35149
+ truncated = excluded.truncated,
35150
+ -- created_at/expires_at stay pinned to the first write: a later update must not
35151
+ -- extend the retention window. released_at is deliberately cleared, because
35152
+ -- changed output cannot inherit an earlier approval to disclose it.
35153
+ released_at = NULL,
35154
+ retired_at = NULL`,
35155
+ [
35156
+ input.commandId,
35157
+ input.teamId,
35158
+ input.agentId,
35159
+ input.turnId,
35160
+ input.toolCallId,
35161
+ retained.text,
35162
+ byteLength,
35163
+ observed,
35164
+ input.redacted ? 1 : 0,
35165
+ truncated ? 1 : 0,
35166
+ now,
35167
+ now + CODING_LOG_TTL_MS
35168
+ ]
35169
+ );
35170
+ pruneCodingCommandLogs(db, now);
35171
+ return getCodingCommandLog(db, input.commandId, now);
35172
+ }
35173
+ function codingCommandIdFromResult(result) {
35174
+ try {
35175
+ const parsed = JSON.parse(result);
35176
+ return typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : null;
35177
+ } catch {
35178
+ return null;
35179
+ }
35180
+ }
35181
+ function releaseCodingCommandLog(db, commandId, now = Date.now()) {
35182
+ db.raw.run(
35183
+ `UPDATE coding_command_logs SET released_at = COALESCE(released_at, ?)
35184
+ WHERE command_id = ? AND state = 'retained'`,
35185
+ [now, commandId]
35186
+ );
35187
+ }
35188
+ function readCodingCommandLog(db, commandId, options, now = Date.now()) {
35189
+ const view2 = getCodingCommandLog(db, commandId, now);
35190
+ if (!mayDisclose(view2, options.audience)) return null;
35191
+ const row = rowFor(db, commandId);
35192
+ const limit = Math.min(Math.max(options.limit ?? CODING_LOG_PAGE_BYTES, 1), CODING_LOG_BYTES);
35193
+ const text3 = row ? liveText(row, now) : null;
35194
+ if (!row || text3 === null) {
35195
+ return {
35196
+ commandId,
35197
+ availability: view2.availability,
35198
+ offset: 0,
35199
+ text: "",
35200
+ hasMore: false,
35201
+ byteLength: view2.byteLength
35202
+ };
35203
+ }
35204
+ const bytes = Buffer.from(text3);
35205
+ let offset = Math.min(Math.max(options.offset ?? 0, 0), bytes.length);
35206
+ while (offset < bytes.length && ((bytes[offset] ?? 0) & 192) === 128) offset++;
35207
+ let end = Math.min(offset + limit, bytes.length);
35208
+ while (end < bytes.length && ((bytes[end] ?? 0) & 192) === 128) end++;
35209
+ return {
35210
+ commandId,
35211
+ availability: view2.availability,
35212
+ offset,
35213
+ text: bytes.subarray(offset, end).toString("utf8"),
35214
+ hasMore: end < bytes.length,
35215
+ byteLength: view2.byteLength
35216
+ };
35217
+ }
35218
+ function searchCodingCommandLog(db, commandId, needle, options, now = Date.now()) {
35219
+ const view2 = getCodingCommandLog(db, commandId, now);
35220
+ if (!mayDisclose(view2, options.audience)) return null;
35221
+ const row = rowFor(db, commandId);
35222
+ const empty = {
35223
+ commandId,
35224
+ availability: view2.availability,
35225
+ matches: [],
35226
+ bounded: false
35227
+ };
35228
+ const text3 = row ? liveText(row, now) : null;
35229
+ if (text3 === null || !needle) return empty;
35230
+ const scanned = Buffer.byteLength(text3) > CODING_LOG_SEARCH_SCAN_BYTES;
35231
+ const haystack = scanned ? headBytes(text3, CODING_LOG_SEARCH_SCAN_BYTES) : text3;
35232
+ const matches = [];
35233
+ let from = 0;
35234
+ let hitLimit = false;
35235
+ for (; ; ) {
35236
+ if (matches.length === CODING_LOG_SEARCH_HITS) {
35237
+ hitLimit = true;
35238
+ break;
35239
+ }
35240
+ const at = haystack.indexOf(needle, from);
35241
+ if (at < 0) break;
35242
+ const prefix = haystack.slice(0, at);
35243
+ matches.push({
35244
+ offset: Buffer.byteLength(prefix),
35245
+ line: prefix.split("\n").length,
35246
+ excerpt: haystack.slice(Math.max(0, at - 40), at + needle.length + 40)
35247
+ });
35248
+ from = at + needle.length;
35249
+ }
35250
+ return {
35251
+ commandId,
35252
+ availability: view2.availability,
35253
+ matches,
35254
+ bounded: scanned || hitLimit
35255
+ };
35256
+ }
35257
+ function pruneCodingCommandLogs(db, now = Date.now(), budgetBytes = CODING_LOG_HOME_BYTES) {
35258
+ db.raw.run(
35259
+ `UPDATE coding_command_logs SET state = 'expired', text = NULL, byte_length = 0, retired_at = ?
35260
+ WHERE state = 'retained' AND expires_at <= ?`,
35261
+ [now, now]
35262
+ );
35263
+ for (; ; ) {
35264
+ const total = db.raw.query(
35265
+ "SELECT SUM(byte_length) AS bytes FROM coding_command_logs"
35266
+ ).get()?.bytes ?? 0;
35267
+ if (total <= budgetBytes) return;
35268
+ const oldest = db.raw.query(
35269
+ `SELECT command_id FROM coding_command_logs
35270
+ WHERE byte_length > 0 AND command_id NOT IN (
35271
+ SELECT id FROM coding_commands WHERE state = 'running'
35272
+ )
35273
+ ORDER BY created_at ASC, command_id ASC LIMIT 1`
35274
+ ).get();
35275
+ if (!oldest) return;
35276
+ db.raw.run("DELETE FROM coding_command_logs WHERE command_id = ?", [oldest.command_id]);
35277
+ }
35278
+ }
35279
+
34878
35280
  // ../daemon/src/lib/communication.ts
34879
35281
  import { randomUUID as randomUUID29 } from "crypto";
34880
35282
 
@@ -35273,11 +35675,15 @@ function authorizeHttpChatFrame(db, agentId, requestAttemptId, frameIndex, frame
35273
35675
  if (frame.kind === "event" && frame.event.type === "file") {
35274
35676
  releaseResultFile(db, agentId, frame.event.result);
35275
35677
  }
35678
+ if (frame.kind === "event" && frame.event.type === "tool_result" && frame.event.name === "coding_command") {
35679
+ const id = codingCommandIdFromResult(frame.event.result);
35680
+ if (id) releaseCodingCommandLog(db, id);
35681
+ }
35276
35682
  }
35277
35683
  function isUserFacingFrame(frame) {
35278
35684
  if (frame.kind === "fatal") return true;
35279
35685
  if (frame.kind !== "event") return false;
35280
- return frame.event.type === "assistant_message" || frame.event.type === "assistant_delta" || frame.event.type === "file" || frame.event.type === "tool_result" && (frame.event.name === "repository_context" || Boolean(frame.event.images?.length)) || frame.event.type === "error";
35686
+ return frame.event.type === "assistant_message" || frame.event.type === "assistant_delta" || frame.event.type === "coding_progress" || frame.event.type === "file" || frame.event.type === "tool_result" && (frame.event.name === "repository_context" || frame.event.name === "coding_command" || Boolean(frame.event.images?.length)) || frame.event.type === "error";
35281
35687
  }
35282
35688
  function sendAgentMessage(db, input) {
35283
35689
  const attemptKind = input.attemptKind ?? "agent_tool";
@@ -35608,48 +36014,112 @@ function claimDeliverableInbox(db, agentId, onAllowed, conversationId) {
35608
36014
  return outcome.allowed;
35609
36015
  }
35610
36016
 
35611
- // ../daemon/src/lib/repository-context/git.ts
36017
+ // ../daemon/src/core/repos/source-snapshots.ts
36018
+ var SOURCE_SNAPSHOT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
36019
+ var COLUMNS2 = "snapshot_id, team_id, captured_by, agent_id, turn_id, tool_call_id, complete, head, base_oid, entry_count, captured_content_bytes, manifest_json, created_at, expires_at";
36020
+ function toRecord(row) {
36021
+ return {
36022
+ snapshotId: row.snapshot_id,
36023
+ teamId: row.team_id,
36024
+ capturedBy: row.captured_by === "agent" ? "agent" : "operator",
36025
+ agentId: row.agent_id,
36026
+ turnId: row.turn_id,
36027
+ toolCallId: row.tool_call_id,
36028
+ complete: row.complete === 1,
36029
+ head: row.head,
36030
+ baseOid: row.base_oid,
36031
+ entryCount: row.entry_count,
36032
+ capturedContentBytes: row.captured_content_bytes,
36033
+ manifestJson: row.manifest_json,
36034
+ createdAt: row.created_at,
36035
+ expiresAt: row.expires_at
36036
+ };
36037
+ }
36038
+ function saveSourceSnapshot(db, input) {
36039
+ const now = input.now ?? Date.now();
36040
+ db.raw.run(
36041
+ `INSERT INTO source_snapshots (${COLUMNS2})
36042
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
36043
+ ON CONFLICT(snapshot_id, team_id) DO NOTHING`,
36044
+ [
36045
+ input.snapshotId,
36046
+ input.teamId,
36047
+ input.capturedBy,
36048
+ input.agentId,
36049
+ input.turnId,
36050
+ input.toolCallId,
36051
+ input.complete ? 1 : 0,
36052
+ input.head,
36053
+ input.baseOid,
36054
+ input.entryCount,
36055
+ input.capturedContentBytes,
36056
+ input.manifestJson,
36057
+ now,
36058
+ now + SOURCE_SNAPSHOT_TTL_MS
36059
+ ]
36060
+ );
36061
+ pruneSourceSnapshots(db, now);
36062
+ }
36063
+ function getSourceSnapshot(db, teamId, snapshotId, now = Date.now()) {
36064
+ const row = db.raw.query(
36065
+ `SELECT ${COLUMNS2} FROM source_snapshots
36066
+ WHERE team_id = ? AND snapshot_id = ? AND expires_at > ?`
36067
+ ).get(teamId, snapshotId, now);
36068
+ return row ? toRecord(row) : null;
36069
+ }
36070
+ function pruneSourceSnapshots(db, now = Date.now()) {
36071
+ const result = db.raw.run("DELETE FROM source_snapshots WHERE expires_at <= ?", [now]);
36072
+ return Number(result.changes);
36073
+ }
36074
+ function listSourceSnapshots(db, teamId, limit = 50, now = Date.now()) {
36075
+ return db.raw.query(
36076
+ `SELECT ${COLUMNS2} FROM source_snapshots
36077
+ WHERE team_id = ? AND expires_at > ?
36078
+ ORDER BY created_at DESC, snapshot_id LIMIT ?`
36079
+ ).all(teamId, now, limit).map(toRecord);
36080
+ }
36081
+
36082
+ // ../daemon/src/lib/git/capture.ts
35612
36083
  import { execFile } from "child_process";
35613
36084
  import { mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync4, rmSync as rmSync14, writeFileSync as writeFileSync12 } from "fs";
35614
36085
  import { tmpdir as tmpdir4 } from "os";
35615
36086
  import { join as join25 } from "path";
35616
- var emptyGit = () => ({
35617
- state: "not_repository",
35618
- root: null,
35619
- branch: null,
35620
- head: null,
35621
- headState: null,
35622
- staged: 0,
35623
- unstaged: 0,
35624
- untracked: 0,
35625
- conflicted: 0,
35626
- issues: []
35627
- });
35628
- async function inspectGit(ancestry) {
35629
- const report = emptyGit();
35630
- let scratch;
36087
+ var INSPECTION_BUDGET_MS = 5e3;
36088
+ var METADATA_BUDGET_MS = 2500;
36089
+ var METADATA_BYTES = 64 * 1024 * 1024;
36090
+ var METADATA_ENTRIES = 16384;
36091
+ var METADATA_DEPTH = 32;
36092
+ var METADATA_READ = 64 * 1024;
36093
+ var METADATA_READ_LARGE = 16 * 1024 * 1024;
36094
+ var RUN_MAX_BUFFER = 1024 * 1024;
36095
+ function findRepositoryRoot(ancestry) {
36096
+ return [...ancestry].reverse().find((dir) => dir.entry(".git") !== null) ?? null;
36097
+ }
36098
+ async function captureRepositoryGit(root, budgetMs = INSPECTION_BUDGET_MS) {
36099
+ const start = Date.now();
36100
+ let bytes = 0;
36101
+ let count3 = 0;
36102
+ const marker = root.entry(".git");
36103
+ if (!marker?.isDirectory() || marker.isSymbolicLink())
36104
+ throw new ContextReadError("unsupported_git_metadata");
36105
+ const git = root.directory(".git");
36106
+ if (git.entry("commondir") || git.entry("gitdir")) {
36107
+ throw new ContextReadError("unsupported_git_metadata");
36108
+ }
36109
+ const info = git.entry("info");
36110
+ if (info) {
36111
+ const dir = git.directory("info");
36112
+ if (dir.entry("sparse-checkout")) throw new ContextReadError("unsupported_git_metadata");
36113
+ }
36114
+ const scratch = mkdtempSync4(join25(tmpdir4(), "bazilion-context-git-"));
36115
+ let cleaned = false;
36116
+ const cleanup = () => {
36117
+ if (cleaned) return;
36118
+ cleaned = true;
36119
+ rmSync14(scratch, { recursive: true, force: true });
36120
+ };
35631
36121
  try {
35632
- const root = [...ancestry].reverse().find((dir) => dir.entry(".git") !== null);
35633
- if (!root) return report;
35634
- report.root = root.label;
35635
- report.state = "unavailable";
35636
- const marker = root.entry(".git");
35637
- if (!marker?.isDirectory() || marker.isSymbolicLink())
35638
- throw new ContextReadError("unsupported_git_metadata");
35639
- const git = root.directory(".git");
35640
- if (git.entry("commondir") || git.entry("gitdir")) {
35641
- throw new ContextReadError("unsupported_git_metadata");
35642
- }
35643
- const info = git.entry("info");
35644
- if (info) {
35645
- const dir = git.directory("info");
35646
- if (dir.entry("sparse-checkout")) throw new ContextReadError("unsupported_git_metadata");
35647
- }
35648
- const start = Date.now();
35649
- let bytes = 0;
35650
- let count3 = 0;
35651
- scratch = mkdtempSync4(join25(tmpdir4(), "bazilion-context-git-"));
35652
- const runGit = (args) => new Promise((resolve8, reject2) => {
36122
+ const rawGit = (args) => new Promise((resolve8, reject2) => {
35653
36123
  execFile(
35654
36124
  "git",
35655
36125
  args,
@@ -35666,21 +36136,40 @@ async function inspectGit(ancestry) {
35666
36136
  GIT_OPTIONAL_LOCKS: "0",
35667
36137
  LC_ALL: "C"
35668
36138
  },
35669
- timeout: Math.max(1, 5e3 - (Date.now() - start)),
35670
- maxBuffer: 1024 * 1024,
36139
+ timeout: Math.max(1, budgetMs - (Date.now() - start)),
36140
+ maxBuffer: RUN_MAX_BUFFER,
35671
36141
  encoding: "utf8"
35672
36142
  },
35673
36143
  (error2, stdout) => error2 ? reject2(error2) : resolve8(stdout)
35674
36144
  );
35675
36145
  });
35676
- const capturedConfig = git.read("config", 64 * 1024);
36146
+ const inspectionArgs = (args) => [
36147
+ "--no-optional-locks",
36148
+ `--git-dir=${scratch}`,
36149
+ `--work-tree=/proc/${process.pid}/fd/${root.fd}`,
36150
+ "-c",
36151
+ "core.fsmonitor=false",
36152
+ "-c",
36153
+ "core.untrackedCache=false",
36154
+ "-c",
36155
+ "core.hooksPath=/dev/null",
36156
+ "-c",
36157
+ "core.excludesFile=/dev/null",
36158
+ "-c",
36159
+ "diff.renames=false",
36160
+ "-c",
36161
+ "submodule.recurse=false",
36162
+ ...args
36163
+ ];
36164
+ const runGit = (args) => rawGit(inspectionArgs(args));
36165
+ const capturedConfig = git.read("config", METADATA_READ);
35677
36166
  const configLines = ["[core]", "repositoryformatversion = 0", "bare = false", "filemode = true"];
35678
36167
  if (capturedConfig) {
35679
36168
  bytes += capturedConfig.length;
35680
36169
  count3++;
35681
36170
  const configPath = join25(scratch, "captured-config");
35682
36171
  writeFileSync12(configPath, capturedConfig, { mode: 384 });
35683
- const config2 = await runGit([
36172
+ const config2 = await rawGit([
35684
36173
  "config",
35685
36174
  "--no-includes",
35686
36175
  "--file",
@@ -35728,7 +36217,7 @@ objectformat = ${value}`
35728
36217
  }
35729
36218
  }
35730
36219
  if (info) {
35731
- const exclude = git.directory("info").read("exclude", 64 * 1024);
36220
+ const exclude = git.directory("info").read("exclude", METADATA_READ);
35732
36221
  if (exclude) {
35733
36222
  bytes += exclude.length;
35734
36223
  mkdirSync9(join25(scratch, "info"), { mode: 448 });
@@ -35736,9 +36225,9 @@ objectformat = ${value}`
35736
36225
  }
35737
36226
  }
35738
36227
  const copy = (source, destination, depth) => {
35739
- if (depth > 32) throw new ContextReadError("git_metadata_limit");
36228
+ if (depth > METADATA_DEPTH) throw new ContextReadError("git_metadata_limit");
35740
36229
  for (const name of source.list()) {
35741
- if (++count3 > 16384 || Date.now() - start > 2500)
36230
+ if (++count3 > METADATA_ENTRIES || Date.now() - start > METADATA_BUDGET_MS)
35742
36231
  throw new ContextReadError("git_metadata_limit");
35743
36232
  const entry = source.entry(name);
35744
36233
  if (!entry || entry.isSymbolicLink()) throw new ContextReadError("unsupported_git_metadata");
@@ -35748,7 +36237,7 @@ objectformat = ${value}`
35748
36237
  mkdirSync9(join25(destination, name), { mode: 448 });
35749
36238
  copy(source.directory(name), join25(destination, name), depth + 1);
35750
36239
  } else {
35751
- const content = source.read(name, 64 * 1024 * 1024 - bytes);
36240
+ const content = source.read(name, METADATA_BYTES - bytes);
35752
36241
  if (!content) throw new ContextReadError("source_changed");
35753
36242
  bytes += content.length;
35754
36243
  writeFileSync12(join25(destination, name), content, { mode: 384 });
@@ -35756,38 +36245,873 @@ objectformat = ${value}`
35756
36245
  }
35757
36246
  };
35758
36247
  for (const name of ["HEAD", "index", "packed-refs", "shallow"]) {
35759
- const content = git.read(name, Math.min(16 * 1024 * 1024, 64 * 1024 * 1024 - bytes));
36248
+ const content = git.read(name, Math.min(METADATA_READ_LARGE, METADATA_BYTES - bytes));
35760
36249
  if (content) {
35761
36250
  count3++;
35762
36251
  bytes += content.length;
35763
36252
  writeFileSync12(join25(scratch, name), content, { mode: 384 });
35764
36253
  }
35765
36254
  }
35766
- for (const name of ["refs", "objects"]) {
35767
- mkdirSync9(join25(scratch, name), { mode: 448 });
35768
- if (git.entry(name)) copy(git.directory(name), join25(scratch, name), 0);
36255
+ for (const name of ["refs", "objects"]) {
36256
+ mkdirSync9(join25(scratch, name), { mode: 448 });
36257
+ if (git.entry(name)) copy(git.directory(name), join25(scratch, name), 0);
36258
+ }
36259
+ if (git.list().some((name) => name.startsWith("sharedindex.")))
36260
+ throw new ContextReadError("unsupported_git_metadata");
36261
+ writeFileSync12(join25(scratch, "config"), `${configLines.join("\n")}
36262
+ `, { mode: 384 });
36263
+ const validate2 = () => git.validate();
36264
+ validate2();
36265
+ return { root, runGit, validate: validate2, cleanup };
36266
+ } catch (error2) {
36267
+ cleanup();
36268
+ throw error2;
36269
+ }
36270
+ }
36271
+
36272
+ // ../../packages/api-types/src/entities.ts
36273
+ var REASONING_LEVELS2 = [
36274
+ "off",
36275
+ "minimal",
36276
+ "low",
36277
+ "medium",
36278
+ "high",
36279
+ "xhigh"
36280
+ ];
36281
+
36282
+ // ../../packages/api-types/src/git-review.ts
36283
+ var REVIEW_LIMITS = {
36284
+ files: 1e3,
36285
+ fileBytes: 1024 * 1024,
36286
+ totalBytes: 16 * 1024 * 1024,
36287
+ patchBytes: 256 * 1024
36288
+ };
36289
+
36290
+ // ../../packages/api-types/src/index.ts
36291
+ var PROFILE_FILES = [
36292
+ "profile.json",
36293
+ "SOUL.md",
36294
+ "IDENTITY.md",
36295
+ "BOOTSTRAP.md",
36296
+ "AGENTS.md",
36297
+ "TOOLS.md"
36298
+ ];
36299
+
36300
+ // ../daemon/src/lib/git-review/refs.ts
36301
+ var REF_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,254}$/;
36302
+ function isCommitOid(value) {
36303
+ return /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(value);
36304
+ }
36305
+ var ReviewRefError = class extends Error {
36306
+ value;
36307
+ constructor(value) {
36308
+ super("Comparison base must be a plain branch, tag or commit id.");
36309
+ this.name = "ReviewRefError";
36310
+ this.value = value;
36311
+ }
36312
+ };
36313
+ function validateRefName(value) {
36314
+ if (typeof value !== "string" || value.length === 0 || value.length > 255) {
36315
+ throw new ReviewRefError(typeof value === "string" ? value : "");
36316
+ }
36317
+ if (!REF_PATTERN.test(value)) throw new ReviewRefError(value);
36318
+ if (value === "." || value === "..") throw new ReviewRefError(value);
36319
+ if (value.includes("..") || value.includes("@{") || value.includes("//")) {
36320
+ throw new ReviewRefError(value);
36321
+ }
36322
+ if (value.endsWith("/") || value.endsWith(".") || value.endsWith(".lock")) {
36323
+ throw new ReviewRefError(value);
36324
+ }
36325
+ return value;
36326
+ }
36327
+
36328
+ // ../daemon/src/lib/git-review/identity.ts
36329
+ var ReviewBaseError = class extends Error {
36330
+ code;
36331
+ constructor(code, message) {
36332
+ super(message);
36333
+ this.name = "ReviewBaseError";
36334
+ this.code = code;
36335
+ }
36336
+ };
36337
+ var BRANCH_MAX = 4096;
36338
+ async function readRepositoryIdentity(captured) {
36339
+ const branch = await readOrNull(captured, ["symbolic-ref", "--quiet", "--short", "HEAD"]);
36340
+ const head = await readOrNull(captured, ["rev-parse", "--verify", "--quiet", "HEAD^{commit}"]);
36341
+ if (branch !== null && branch.length > BRANCH_MAX) {
36342
+ throw new ReviewBaseError("git_output_limit", "Repository identity exceeds its bound.");
36343
+ }
36344
+ const resolvedHead = head !== null && isCommitOid(head) ? head : null;
36345
+ return {
36346
+ branch,
36347
+ head: resolvedHead,
36348
+ headState: resolvedHead ? branch ? "branch" : "detached" : "unborn"
36349
+ };
36350
+ }
36351
+ async function resolveComparisonBase(captured, requestedRef = "HEAD") {
36352
+ let ref;
36353
+ try {
36354
+ ref = validateRefName(requestedRef);
36355
+ } catch (error2) {
36356
+ if (error2 instanceof ReviewRefError) {
36357
+ throw new ReviewBaseError("invalid_base", error2.message);
36358
+ }
36359
+ throw error2;
36360
+ }
36361
+ const resolved = await readOrNull(captured, [
36362
+ "rev-parse",
36363
+ "--verify",
36364
+ "--quiet",
36365
+ "--end-of-options",
36366
+ `${ref}^{commit}`
36367
+ ]);
36368
+ if (resolved === null || !isCommitOid(resolved)) {
36369
+ throw new ReviewBaseError(
36370
+ "unknown_base",
36371
+ `Comparison base "${ref}" does not name a commit in this repository.`
36372
+ );
36373
+ }
36374
+ return { requestedRef: ref, resolvedOid: resolved };
36375
+ }
36376
+ async function readOrNull(captured, args) {
36377
+ try {
36378
+ const output = await captured.runGit(args);
36379
+ const trimmed = output.trim();
36380
+ return trimmed.length === 0 ? null : trimmed;
36381
+ } catch (error2) {
36382
+ const code = error2.code;
36383
+ if (code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" || code === "ETIMEDOUT" || error2.killed) {
36384
+ throw new ReviewBaseError("git_output_limit", "Repository identity exceeds its bound.");
36385
+ }
36386
+ return null;
36387
+ }
36388
+ }
36389
+
36390
+ // ../daemon/src/lib/git-review/scope.ts
36391
+ var BAZILION_STATE_PREFIXES = ["memory/"];
36392
+ var CREDENTIAL_SEGMENTS = /* @__PURE__ */ new Set([".ssh", ".gnupg", ".aws", ".docker"]);
36393
+ var CREDENTIAL_BASENAME_PATTERNS = [
36394
+ /^\.env(\..*)?$/,
36395
+ /^\.netrc$/,
36396
+ /^_netrc$/,
36397
+ /^\.npmrc$/,
36398
+ /^\.pgpass$/,
36399
+ /^\.git-credentials$/,
36400
+ /^credentials(\..*)?$/,
36401
+ /^secrets(\..*)?$/,
36402
+ /^id_(rsa|dsa|ecdsa|ed25519)(\.pub)?$/
36403
+ ];
36404
+ var CREDENTIAL_EXTENSIONS = [
36405
+ ".pem",
36406
+ ".key",
36407
+ ".p12",
36408
+ ".pfx",
36409
+ ".jks",
36410
+ ".keystore",
36411
+ ".kdbx",
36412
+ ".ppk",
36413
+ ".asc"
36414
+ ];
36415
+ function reviewScope(path) {
36416
+ const segments = path.split("/");
36417
+ const basename6 = segments[segments.length - 1] ?? "";
36418
+ for (const prefix of BAZILION_STATE_PREFIXES) {
36419
+ if (path === prefix.slice(0, -1) || path.startsWith(prefix)) {
36420
+ return { included: false, reason: "bazilion_state" };
36421
+ }
36422
+ }
36423
+ if (segments.some((segment) => CREDENTIAL_SEGMENTS.has(segment.toLowerCase()))) {
36424
+ return { included: false, reason: "credential_shaped" };
36425
+ }
36426
+ const lower = basename6.toLowerCase();
36427
+ if (CREDENTIAL_BASENAME_PATTERNS.some((pattern) => pattern.test(lower))) {
36428
+ return { included: false, reason: "credential_shaped" };
36429
+ }
36430
+ if (CREDENTIAL_EXTENSIONS.some((extension) => lower.endsWith(extension))) {
36431
+ return { included: false, reason: "credential_shaped" };
36432
+ }
36433
+ return { included: true };
36434
+ }
36435
+
36436
+ // ../daemon/src/lib/git-review/changes.ts
36437
+ async function listChanges(captured, base, identity2, limits = REVIEW_LIMITS) {
36438
+ const issues = [];
36439
+ const raw2 = await readDiff(captured, base, [
36440
+ "--raw",
36441
+ "-M",
36442
+ "--no-ext-diff",
36443
+ "--no-textconv",
36444
+ "-z"
36445
+ ]);
36446
+ const numstat = await readDiff(captured, base, [
36447
+ "--numstat",
36448
+ "-M",
36449
+ "--no-ext-diff",
36450
+ "--no-textconv",
36451
+ "-z"
36452
+ ]);
36453
+ const counts = parseNumstat(numstat);
36454
+ const entries = [];
36455
+ let excludedTracked = 0;
36456
+ for (const header of parseRaw(raw2)) {
36457
+ const scoped = reviewScope(header.path);
36458
+ if (!scoped.included) excludedTracked++;
36459
+ const count3 = counts.get(header.path) ?? null;
36460
+ entries.push({
36461
+ path: header.path,
36462
+ previousPath: header.previousPath,
36463
+ status: header.status,
36464
+ binary: count3?.binary ?? false,
36465
+ addedLines: count3?.added ?? null,
36466
+ deletedLines: count3?.deleted ?? null,
36467
+ oldMode: header.oldMode,
36468
+ newMode: header.newMode,
36469
+ patch: null,
36470
+ patchTruncated: false,
36471
+ // A credential-shaped or Bazilion-owned tracked file may be listed, but never captured.
36472
+ contentOmitted: scoped.included ? count3?.binary ? "binary" : null : "excluded",
36473
+ excludedReason: scoped.included ? null : scoped.reason
36474
+ });
36475
+ }
36476
+ const untrackedNames = parseNameList(
36477
+ await captured.runGit(["ls-files", "--others", "--exclude-standard", "-z"])
36478
+ );
36479
+ let excludedUntracked = 0;
36480
+ for (const path of untrackedNames) {
36481
+ const scoped = reviewScope(path);
36482
+ if (!scoped.included) {
36483
+ excludedUntracked++;
36484
+ continue;
36485
+ }
36486
+ entries.push({
36487
+ path,
36488
+ previousPath: null,
36489
+ status: "untracked",
36490
+ binary: false,
36491
+ addedLines: null,
36492
+ deletedLines: null,
36493
+ oldMode: null,
36494
+ newMode: null,
36495
+ patch: null,
36496
+ patchTruncated: false,
36497
+ contentOmitted: "untracked_not_selected",
36498
+ excludedReason: null
36499
+ });
36500
+ }
36501
+ entries.sort((a, b2) => a.path < b2.path ? -1 : a.path > b2.path ? 1 : 0);
36502
+ const truncated = entries.length > limits.files;
36503
+ const kept = truncated ? entries.slice(0, limits.files) : entries;
36504
+ if (truncated) {
36505
+ issues.push({
36506
+ code: "file_limit",
36507
+ message: `The change list exceeded ${limits.files} files; it is incomplete.`
36508
+ });
36509
+ }
36510
+ return {
36511
+ base,
36512
+ identity: identity2,
36513
+ changes: kept,
36514
+ truncated,
36515
+ // Counts of what was *not* captured, so an omission is never invisible. Untracked
36516
+ // credential-shaped names are counted here but never listed by name.
36517
+ withheld: {
36518
+ untracked: excludedUntracked,
36519
+ excluded: excludedTracked,
36520
+ binary: kept.filter((change) => change.binary).length,
36521
+ tooLarge: 0
36522
+ },
36523
+ issues
36524
+ };
36525
+ }
36526
+ async function readChangePatch(captured, base, change, patchBytes = REVIEW_LIMITS.patchBytes) {
36527
+ if (change.excludedReason) return { patch: null, truncated: false, omitted: "excluded" };
36528
+ if (change.binary) return { patch: null, truncated: false, omitted: "binary" };
36529
+ if (change.status === "untracked") {
36530
+ return { patch: null, truncated: false, omitted: "untracked_not_selected" };
36531
+ }
36532
+ const target = change.status === "deleted" ? change.previousPath ?? change.path : change.path;
36533
+ let output;
36534
+ try {
36535
+ output = await captured.runGit([
36536
+ "diff",
36537
+ "--no-ext-diff",
36538
+ "--no-textconv",
36539
+ "--no-color",
36540
+ "--unified=3",
36541
+ "-M",
36542
+ base.resolvedOid,
36543
+ "--",
36544
+ target
36545
+ ]);
36546
+ } catch (error2) {
36547
+ if (error2 instanceof ReviewBaseError) throw error2;
36548
+ return { patch: null, truncated: false, omitted: "unavailable" };
36549
+ }
36550
+ if (output.length <= patchBytes) return { patch: output, truncated: false, omitted: null };
36551
+ return {
36552
+ patch: `${output.slice(0, patchBytes)}
36553
+ [truncated: diff exceeds ${patchBytes} bytes]`,
36554
+ truncated: true,
36555
+ omitted: null
36556
+ };
36557
+ }
36558
+ async function attachPatches(captured, report, limits = REVIEW_LIMITS, onlyPath) {
36559
+ let total = 0;
36560
+ let tooLarge = 0;
36561
+ const changes = [];
36562
+ for (const change of report.changes) {
36563
+ if (onlyPath !== void 0 && change.path !== onlyPath) {
36564
+ changes.push(change);
36565
+ continue;
36566
+ }
36567
+ if (change.contentOmitted && change.contentOmitted !== "binary") {
36568
+ changes.push(change);
36569
+ continue;
36570
+ }
36571
+ if (total >= limits.totalBytes) {
36572
+ changes.push({ ...change, contentOmitted: "total_limit" });
36573
+ continue;
36574
+ }
36575
+ const read = await readChangePatch(captured, report.base, change, limits.patchBytes);
36576
+ const bytes = read.patch ? Buffer.byteLength(read.patch) : 0;
36577
+ if (bytes > limits.fileBytes && !read.truncated) {
36578
+ tooLarge++;
36579
+ changes.push({ ...change, patch: null, contentOmitted: "too_large" });
36580
+ continue;
36581
+ }
36582
+ total += bytes;
36583
+ changes.push({
36584
+ ...change,
36585
+ patch: read.patch,
36586
+ patchTruncated: read.truncated,
36587
+ contentOmitted: read.omitted
36588
+ });
36589
+ }
36590
+ return { ...report, changes, withheld: { ...report.withheld, tooLarge } };
36591
+ }
36592
+ async function readDiff(captured, base, args) {
36593
+ return captured.runGit(["diff", base.resolvedOid, ...args]);
36594
+ }
36595
+ function parseNameList(output) {
36596
+ return output.split("\0").filter((name) => name.length > 0);
36597
+ }
36598
+ var RAW_HEADER = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) ([A-Z])(\d*)$/;
36599
+ function parseRaw(output) {
36600
+ const records2 = output.split("\0");
36601
+ const headers = [];
36602
+ for (let i = 0; i < records2.length; i++) {
36603
+ const record3 = records2[i] ?? "";
36604
+ if (record3.length === 0) continue;
36605
+ const match = RAW_HEADER.exec(record3);
36606
+ if (!match) continue;
36607
+ const status2 = statusFrom(match[5] ?? "");
36608
+ const paired = status2 === "renamed" || status2 === "copied";
36609
+ const first = records2[++i] ?? "";
36610
+ if (paired) {
36611
+ const second = records2[++i] ?? "";
36612
+ if (first.length === 0 || second.length === 0) continue;
36613
+ headers.push({
36614
+ path: second,
36615
+ previousPath: first,
36616
+ status: status2,
36617
+ oldMode: match[1] ?? null,
36618
+ newMode: match[2] ?? null
36619
+ });
36620
+ continue;
36621
+ }
36622
+ if (first.length === 0) continue;
36623
+ headers.push({
36624
+ path: first,
36625
+ previousPath: null,
36626
+ status: status2,
36627
+ oldMode: match[1] ?? null,
36628
+ newMode: match[2] ?? null
36629
+ });
36630
+ }
36631
+ return headers;
36632
+ }
36633
+ function statusFrom(letter) {
36634
+ switch (letter) {
36635
+ case "A":
36636
+ return "added";
36637
+ case "M":
36638
+ return "modified";
36639
+ case "D":
36640
+ return "deleted";
36641
+ case "R":
36642
+ return "renamed";
36643
+ case "C":
36644
+ return "copied";
36645
+ case "T":
36646
+ return "type_changed";
36647
+ case "U":
36648
+ return "unmerged";
36649
+ default:
36650
+ return "unknown";
36651
+ }
36652
+ }
36653
+ function parseNumstat(output) {
36654
+ const records2 = output.split("\0");
36655
+ const counts = /* @__PURE__ */ new Map();
36656
+ for (let i = 0; i < records2.length; i++) {
36657
+ const record3 = records2[i] ?? "";
36658
+ if (record3.length === 0) continue;
36659
+ const firstTab = record3.indexOf(" ");
36660
+ if (firstTab < 0) continue;
36661
+ const secondTab = record3.indexOf(" ", firstTab + 1);
36662
+ if (secondTab < 0) continue;
36663
+ const addedField = record3.slice(0, firstTab);
36664
+ const deletedField = record3.slice(firstTab + 1, secondTab);
36665
+ let path = record3.slice(secondTab + 1);
36666
+ if (path.length === 0) {
36667
+ i++;
36668
+ path = records2[i + 1] ?? "";
36669
+ i++;
36670
+ if (path.length === 0) continue;
36671
+ }
36672
+ const binary = addedField === "-" || deletedField === "-";
36673
+ counts.set(path, {
36674
+ added: binary ? null : Number.parseInt(addedField, 10),
36675
+ deleted: binary ? null : Number.parseInt(deletedField, 10),
36676
+ binary
36677
+ });
36678
+ }
36679
+ return counts;
36680
+ }
36681
+
36682
+ // ../daemon/src/lib/git-review/snapshot.ts
36683
+ function snapshotReference(snapshot) {
36684
+ return { id: snapshot.id, complete: snapshot.complete, capturedAt: snapshot.capturedAt };
36685
+ }
36686
+ function compareSnapshots(before, after) {
36687
+ if (!before.complete || !after.complete) return "unknown";
36688
+ return before.id === after.id ? "identical" : "changed";
36689
+ }
36690
+ async function captureSourceSnapshot(captured, base, identity2, input = {}) {
36691
+ const limits = input.limits ?? REVIEW_LIMITS;
36692
+ const issues = [];
36693
+ const withheld = { excluded: 0, tooLarge: 0, notRegular: 0, unstable: 0 };
36694
+ let incomplete = false;
36695
+ const fail3 = (code, message) => {
36696
+ incomplete = true;
36697
+ issues.push({ code, message });
36698
+ };
36699
+ const index = await readIndexDigest(captured);
36700
+ if (index === null) fail3("unstable", "The index could not be read at snapshot time.");
36701
+ if (index && index.entries > limits.files)
36702
+ fail3(
36703
+ "file_limit",
36704
+ `The index holds more than ${limits.files} entries; the snapshot is incomplete.`
36705
+ );
36706
+ const changes = await listChanges(captured, base, identity2, limits);
36707
+ if (changes.truncated)
36708
+ fail3("file_limit", "The change list was truncated; the snapshot is incomplete.");
36709
+ const entries = [];
36710
+ let totalBytes = 0;
36711
+ for (const change of changes.changes) {
36712
+ if (change.status === "untracked") continue;
36713
+ if (change.path === ".git") continue;
36714
+ if (change.status === "deleted") {
36715
+ entries.push({
36716
+ path: change.path,
36717
+ layer: "worktree",
36718
+ kind: "deleted",
36719
+ digest: null,
36720
+ bytes: null
36721
+ });
36722
+ continue;
36723
+ }
36724
+ const read = readWorktreeFile(captured.root, change.path, limits.fileBytes);
36725
+ switch (read.kind) {
36726
+ case "file":
36727
+ totalBytes += read.bytes;
36728
+ if (totalBytes > limits.totalBytes) {
36729
+ fail3(
36730
+ "file_limit",
36731
+ `Captured content exceeded ${limits.totalBytes} bytes; the snapshot is incomplete.`
36732
+ );
36733
+ entries.push({
36734
+ path: change.path,
36735
+ layer: "worktree",
36736
+ kind: "too_large",
36737
+ digest: null,
36738
+ bytes: read.bytes
36739
+ });
36740
+ break;
36741
+ }
36742
+ entries.push({
36743
+ path: change.path,
36744
+ layer: "worktree",
36745
+ kind: "file",
36746
+ digest: read.digest,
36747
+ bytes: read.bytes
36748
+ });
36749
+ break;
36750
+ case "deleted":
36751
+ entries.push({
36752
+ path: change.path,
36753
+ layer: "worktree",
36754
+ kind: "deleted",
36755
+ digest: null,
36756
+ bytes: null
36757
+ });
36758
+ break;
36759
+ case "too_large":
36760
+ withheld.tooLarge++;
36761
+ fail3(
36762
+ "file_limit",
36763
+ `A changed file exceeds ${limits.fileBytes} bytes; the snapshot is incomplete.`
36764
+ );
36765
+ entries.push({
36766
+ path: change.path,
36767
+ layer: "worktree",
36768
+ kind: "too_large",
36769
+ digest: null,
36770
+ bytes: null
36771
+ });
36772
+ break;
36773
+ case "not_regular":
36774
+ withheld.notRegular++;
36775
+ fail3(
36776
+ "unstable",
36777
+ "A changed path is not a regular file (symlink or special); its content cannot be fingerprinted."
36778
+ );
36779
+ entries.push({
36780
+ path: change.path,
36781
+ layer: "worktree",
36782
+ kind: "not_regular",
36783
+ digest: null,
36784
+ bytes: null
36785
+ });
36786
+ break;
36787
+ case "unstable":
36788
+ withheld.unstable++;
36789
+ fail3(
36790
+ "unstable",
36791
+ "A changed file changed while it was being read; the snapshot is incomplete."
36792
+ );
36793
+ entries.push({
36794
+ path: change.path,
36795
+ layer: "worktree",
36796
+ kind: "unstable",
36797
+ digest: null,
36798
+ bytes: null
36799
+ });
36800
+ break;
36801
+ }
36802
+ }
36803
+ const untrackedIncluded = [];
36804
+ const exclusions = [];
36805
+ for (const path of [...new Set(input.includeUntracked ?? [])].sort()) {
36806
+ const scoped = reviewScope(path);
36807
+ if (!scoped.included) {
36808
+ exclusions.push({ path, reason: scoped.reason });
36809
+ withheld.excluded++;
36810
+ continue;
36811
+ }
36812
+ if (totalBytes >= limits.totalBytes) {
36813
+ fail3(
36814
+ "file_limit",
36815
+ `Captured content exceeded ${limits.totalBytes} bytes; the snapshot is incomplete.`
36816
+ );
36817
+ continue;
36818
+ }
36819
+ const read = readWorktreeFile(captured.root, path, limits.fileBytes);
36820
+ if (read.kind === "file") {
36821
+ totalBytes += read.bytes;
36822
+ untrackedIncluded.push(path);
36823
+ entries.push({
36824
+ path,
36825
+ layer: "untracked",
36826
+ kind: "file",
36827
+ digest: read.digest,
36828
+ bytes: read.bytes
36829
+ });
36830
+ continue;
36831
+ }
36832
+ if (read.kind === "too_large") withheld.tooLarge++;
36833
+ else if (read.kind === "not_regular") withheld.notRegular++;
36834
+ else withheld.unstable++;
36835
+ fail3(
36836
+ read.kind === "too_large" ? "file_limit" : "unstable",
36837
+ `Included untracked path "${path}" could not be fingerprinted (${read.kind}); the snapshot is incomplete.`
36838
+ );
36839
+ entries.push({
36840
+ path,
36841
+ layer: "untracked",
36842
+ kind: read.kind === "too_large" ? "too_large" : read.kind === "not_regular" ? "not_regular" : "unstable",
36843
+ digest: null,
36844
+ bytes: null
36845
+ });
36846
+ }
36847
+ entries.sort(
36848
+ (a, b2) => a.layer === b2.layer ? a.path < b2.path ? -1 : a.path > b2.path ? 1 : 0 : a.layer < b2.layer ? -1 : 1
36849
+ );
36850
+ exclusions.sort((a, b2) => a.path < b2.path ? -1 : a.path > b2.path ? 1 : 0);
36851
+ const canonical = {
36852
+ identity: identity2,
36853
+ base: base.resolvedOid,
36854
+ head: index?.head ?? null,
36855
+ indexDigest: index?.digest ?? null,
36856
+ indexEntries: index?.entries ?? 0,
36857
+ entries: entries.map((entry) => [
36858
+ entry.layer,
36859
+ entry.path,
36860
+ entry.kind,
36861
+ entry.digest,
36862
+ entry.bytes
36863
+ ]),
36864
+ untrackedIncluded,
36865
+ exclusions: exclusions.map((exclusion) => [exclusion.path, exclusion.reason]),
36866
+ withheld,
36867
+ limits,
36868
+ complete: !incomplete
36869
+ };
36870
+ return {
36871
+ id: hash3(JSON.stringify(canonical)),
36872
+ capturedAt: Date.now(),
36873
+ complete: !incomplete,
36874
+ identity: identity2,
36875
+ base,
36876
+ head: canonical.head,
36877
+ indexDigest: canonical.indexDigest,
36878
+ indexEntries: canonical.indexEntries,
36879
+ entries,
36880
+ untrackedIncluded,
36881
+ exclusions,
36882
+ withheld,
36883
+ limits,
36884
+ issues
36885
+ };
36886
+ }
36887
+ async function readIndexDigest(captured) {
36888
+ try {
36889
+ const output = await captured.runGit(["ls-files", "--stage", "-z"]);
36890
+ const lines = [];
36891
+ let entries = 0;
36892
+ for (const record3 of output.split("\0")) {
36893
+ if (record3.length === 0) continue;
36894
+ const tab = record3.indexOf(" ");
36895
+ if (tab < 0) continue;
36896
+ const meta2 = record3.slice(0, tab);
36897
+ const path = record3.slice(tab + 1);
36898
+ if (path.length === 0) continue;
36899
+ entries++;
36900
+ lines.push(`${meta2} ${path}`);
36901
+ }
36902
+ lines.sort();
36903
+ const head = (await captured.runGit(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"])).trim();
36904
+ return {
36905
+ digest: hash3(lines.join("\n")),
36906
+ entries,
36907
+ head: /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(head) ? head : null
36908
+ };
36909
+ } catch {
36910
+ return null;
36911
+ }
36912
+ }
36913
+ function readWorktreeFile(root, path, maxBytes) {
36914
+ let parts;
36915
+ try {
36916
+ parts = relativeParts(path);
36917
+ } catch {
36918
+ return { kind: "not_regular" };
36919
+ }
36920
+ if (parts.length === 0) return { kind: "not_regular" };
36921
+ let current = root;
36922
+ try {
36923
+ for (let i = 0; i < parts.length; i++) {
36924
+ const part = parts[i];
36925
+ const entry = current.entry(part);
36926
+ if (!entry) return { kind: "deleted" };
36927
+ if (entry.isSymbolicLink()) return { kind: "not_regular" };
36928
+ const last = i === parts.length - 1;
36929
+ if (last) {
36930
+ if (!entry.isFile()) return { kind: "not_regular" };
36931
+ if (entry.size > maxBytes) return { kind: "too_large" };
36932
+ const bytes = current.read(part, maxBytes);
36933
+ if (!bytes) return { kind: "unstable" };
36934
+ return { kind: "file", digest: hash3(bytes), bytes: bytes.length };
36935
+ }
36936
+ if (!entry.isDirectory()) return { kind: "not_regular" };
36937
+ current = current.directory(part);
36938
+ }
36939
+ } catch {
36940
+ return { kind: "unstable" };
36941
+ }
36942
+ return { kind: "not_regular" };
36943
+ }
36944
+
36945
+ // ../daemon/src/lib/git-review/service.ts
36946
+ var ReviewUnavailableError = class extends Error {
36947
+ code;
36948
+ constructor(code, message) {
36949
+ super(message);
36950
+ this.name = "ReviewUnavailableError";
36951
+ this.code = code;
36952
+ }
36953
+ };
36954
+ function requireTeam(db, paths, teamId) {
36955
+ const team = get3(db, teamId, paths);
36956
+ if (!team) throw new ReviewUnavailableError("team_not_found", "Team not found.");
36957
+ return { id: team.id, path: team.path };
36958
+ }
36959
+ async function withRepository(teamDir, fn) {
36960
+ let directory;
36961
+ try {
36962
+ directory = new ContextDirectory(teamDir);
36963
+ } catch (error2) {
36964
+ throw new ReviewUnavailableError(
36965
+ "unsupported_layout",
36966
+ error2 instanceof ContextReadError ? `This Team's workspace cannot be inspected safely (${error2.code}).` : "This Team workspace cannot be inspected."
36967
+ );
36968
+ }
36969
+ try {
36970
+ const root = findRepositoryRoot([directory]);
36971
+ if (!root) {
36972
+ throw new ReviewUnavailableError("not_repository", "This Team is not a Git repository.");
35769
36973
  }
35770
- if (git.list().some((name) => name.startsWith("sharedindex.")))
35771
- throw new ContextReadError("unsupported_git_metadata");
35772
- writeFileSync12(join25(scratch, "config"), `${configLines.join("\n")}
35773
- `, { mode: 384 });
35774
- git.validate();
35775
- const output = await runGit([
35776
- "--no-optional-locks",
35777
- `--git-dir=${scratch}`,
35778
- `--work-tree=/proc/${process.pid}/fd/${root.fd}`,
35779
- "-c",
35780
- "core.fsmonitor=false",
35781
- "-c",
35782
- "core.untrackedCache=false",
35783
- "-c",
35784
- "core.hooksPath=/dev/null",
35785
- "-c",
35786
- "core.excludesFile=/dev/null",
35787
- "-c",
35788
- "diff.renames=false",
35789
- "-c",
35790
- "submodule.recurse=false",
36974
+ let captured;
36975
+ try {
36976
+ captured = await captureRepositoryGit(root);
36977
+ } catch (error2) {
36978
+ throw new ReviewUnavailableError(
36979
+ "unsupported_layout",
36980
+ error2 instanceof ContextReadError ? `This repository layout is not supported (${error2.code}).` : "This repository cannot be inspected."
36981
+ );
36982
+ }
36983
+ try {
36984
+ return await fn(captured);
36985
+ } finally {
36986
+ captured.cleanup();
36987
+ }
36988
+ } finally {
36989
+ directory.close();
36990
+ }
36991
+ }
36992
+ async function readTeamReview(db, paths, teamId, input = {}) {
36993
+ const team = requireTeam(db, paths, teamId);
36994
+ const changes = await withRepository(team.path, async (captured) => {
36995
+ const base = await resolveComparisonBase(captured, input.base ?? "HEAD");
36996
+ const identity2 = await readRepositoryIdentity(captured);
36997
+ const listed = await listChanges(captured, base, identity2, input.limits);
36998
+ return input.patches ? attachPatches(captured, listed, input.limits, input.patchPath) : listed;
36999
+ });
37000
+ return { teamId: team.id, changes };
37001
+ }
37002
+ async function captureTeamSnapshot(db, paths, teamId, input) {
37003
+ const team = requireTeam(db, paths, teamId);
37004
+ const snapshot = await withRepository(team.path, async (captured) => {
37005
+ const base = await resolveComparisonBase(captured, input.base ?? "HEAD");
37006
+ const identity2 = await readRepositoryIdentity(captured);
37007
+ return captureSourceSnapshot(captured, base, identity2, {
37008
+ ...input.includeUntracked ? { includeUntracked: input.includeUntracked } : {},
37009
+ ...input.limits ? { limits: input.limits } : {}
37010
+ });
37011
+ });
37012
+ saveSourceSnapshot(db, {
37013
+ snapshotId: snapshot.id,
37014
+ teamId: team.id,
37015
+ capturedBy: input.capturedBy,
37016
+ agentId: input.agentId ?? null,
37017
+ turnId: input.turnId ?? null,
37018
+ toolCallId: input.toolCallId ?? null,
37019
+ complete: snapshot.complete,
37020
+ head: snapshot.head,
37021
+ baseOid: snapshot.base.resolvedOid,
37022
+ entryCount: snapshot.entries.length,
37023
+ capturedContentBytes: snapshot.entries.reduce((total, entry) => total + (entry.bytes ?? 0), 0),
37024
+ manifestJson: JSON.stringify(snapshot)
37025
+ });
37026
+ return { snapshot, reference: snapshotReference(snapshot) };
37027
+ }
37028
+ async function readSnapshotApplicability(db, paths, teamId, snapshotId) {
37029
+ const team = requireTeam(db, paths, teamId);
37030
+ const record3 = getSourceSnapshot(db, team.id, snapshotId);
37031
+ if (!record3) return { comparison: "unknown", reason: "no_snapshot", currentSnapshot: null };
37032
+ let stored;
37033
+ try {
37034
+ stored = JSON.parse(record3.manifestJson);
37035
+ } catch {
37036
+ return { comparison: "unknown", reason: "no_snapshot", currentSnapshot: null };
37037
+ }
37038
+ if (!stored.complete) {
37039
+ return { comparison: "unknown", reason: "incomplete_snapshot", currentSnapshot: null };
37040
+ }
37041
+ let current;
37042
+ try {
37043
+ current = await withRepository(team.path, async (captured) => {
37044
+ const base = await resolveComparisonBase(captured, "HEAD");
37045
+ const identity2 = await readRepositoryIdentity(captured);
37046
+ return captureSourceSnapshot(captured, base, identity2, {
37047
+ includeUntracked: stored.untrackedIncluded
37048
+ });
37049
+ });
37050
+ } catch {
37051
+ return { comparison: "unknown", reason: "capture_unavailable", currentSnapshot: null };
37052
+ }
37053
+ const comparison = compareSnapshots(stored, current);
37054
+ return {
37055
+ comparison,
37056
+ reason: comparison === "identical" ? "source_unchanged" : "source_changed",
37057
+ currentSnapshot: snapshotReference(current)
37058
+ };
37059
+ }
37060
+ function listTeamSnapshots(db, paths, teamId, limit = 50) {
37061
+ const team = requireTeam(db, paths, teamId);
37062
+ return listSourceSnapshots(db, team.id, limit).map(toSummary);
37063
+ }
37064
+ function readTeamSnapshot(db, paths, teamId, snapshotId) {
37065
+ const team = requireTeam(db, paths, teamId);
37066
+ const record3 = getSourceSnapshot(db, team.id, snapshotId);
37067
+ if (!record3) return null;
37068
+ try {
37069
+ return JSON.parse(record3.manifestJson);
37070
+ } catch {
37071
+ return null;
37072
+ }
37073
+ }
37074
+ function toSummary(record3) {
37075
+ return {
37076
+ snapshotId: record3.snapshotId,
37077
+ teamId: record3.teamId,
37078
+ capturedBy: record3.capturedBy,
37079
+ agentId: record3.agentId,
37080
+ turnId: record3.turnId,
37081
+ toolCallId: record3.toolCallId,
37082
+ complete: record3.complete,
37083
+ head: record3.head,
37084
+ baseOid: record3.baseOid,
37085
+ entryCount: record3.entryCount,
37086
+ capturedContentBytes: record3.capturedContentBytes,
37087
+ createdAt: record3.createdAt,
37088
+ expiresAt: record3.expiresAt
37089
+ };
37090
+ }
37091
+
37092
+ // ../daemon/src/lib/repository-context/git.ts
37093
+ var emptyGit = () => ({
37094
+ state: "not_repository",
37095
+ root: null,
37096
+ branch: null,
37097
+ head: null,
37098
+ headState: null,
37099
+ staged: 0,
37100
+ unstaged: 0,
37101
+ untracked: 0,
37102
+ conflicted: 0,
37103
+ issues: []
37104
+ });
37105
+ async function inspectGit(ancestry) {
37106
+ const report = emptyGit();
37107
+ let capture2 = null;
37108
+ try {
37109
+ const root = findRepositoryRoot(ancestry);
37110
+ if (!root) return report;
37111
+ report.root = root.label;
37112
+ report.state = "unavailable";
37113
+ capture2 = await captureRepositoryGit(root);
37114
+ const output = await capture2.runGit([
35791
37115
  "status",
35792
37116
  "--porcelain=v2",
35793
37117
  "--branch",
@@ -35795,7 +37119,7 @@ objectformat = ${value}`
35795
37119
  "--ignore-submodules=all",
35796
37120
  "-z"
35797
37121
  ]);
35798
- git.validate();
37122
+ capture2.validate();
35799
37123
  const records2 = output.split("\0");
35800
37124
  for (let i = 0; i < records2.length; i++) {
35801
37125
  const line = records2[i] ?? "";
@@ -35824,7 +37148,7 @@ objectformat = ${value}`
35824
37148
  message: `Git inspection is unavailable (${code.replaceAll("_", " ")}); repository instructions are evaluated separately.`
35825
37149
  });
35826
37150
  } finally {
35827
- if (scratch) rmSync14(scratch, { recursive: true, force: true });
37151
+ capture2?.cleanup();
35828
37152
  }
35829
37153
  return report;
35830
37154
  }
@@ -36030,6 +37354,19 @@ function createCodingHost(input) {
36030
37354
  const active = /* @__PURE__ */ new Set();
36031
37355
  const calls = /* @__PURE__ */ new Set();
36032
37356
  let starting = false;
37357
+ let sourceBefore = null;
37358
+ function authorizeEvidence(id, messageId) {
37359
+ const receipt = getCodingCommand(input.db, id);
37360
+ if (agents_exports.get(input.db, input.agentId)?.teamId !== input.teamId || !receipt || receipt.teamId !== input.teamId || agents_exports.get(input.db, receipt.agentId)?.teamId !== input.teamId)
37361
+ throw new Error("Coding receipt unavailable");
37362
+ if (receipt.agentId === input.agentId) return { receipt, producer: true };
37363
+ const message = deliverableInbox(input.db, input.agentId, false).find(
37364
+ (candidate) => candidate.id === messageId
37365
+ );
37366
+ if (!message || message.fromAgentId !== receipt.agentId || !message.payload.includes(`coding-receipt:${receipt.id}`) || receipt.state === "running")
37367
+ throw new Error("Coding receipt requires an authorized producer message");
37368
+ return { receipt, producer: false };
37369
+ }
36033
37370
  async function snapshot(target) {
36034
37371
  input.assertActive();
36035
37372
  codingRelativePath(target);
@@ -36087,6 +37424,33 @@ function createCodingHost(input) {
36087
37424
  input.assertActive();
36088
37425
  if (!raw2 || typeof raw2 !== "object") throw new Error("Invalid coding request");
36089
37426
  if (raw2.action === "environment") return snapshot(raw2.target);
37427
+ if (raw2.action === "snapshot") {
37428
+ if (typeof raw2.toolCallId !== "string" || !raw2.toolCallId || raw2.toolCallId.length > 256 || calls.has(raw2.toolCallId))
37429
+ throw new Error("Invalid or repeated coding tool call");
37430
+ if (calls.size >= 64) throw new Error("Turn operation limit reached");
37431
+ calls.add(raw2.toolCallId);
37432
+ const requested = Array.isArray(raw2.includeUntracked) ? raw2.includeUntracked.filter((entry) => typeof entry === "string") : [];
37433
+ if (requested.length > 1e3 || requested.some((entry) => entry.length > 4096))
37434
+ throw new Error("Invalid untracked selection");
37435
+ const captured = await captureTeamSnapshot(input.db, input.paths, input.teamId, {
37436
+ capturedBy: "agent",
37437
+ agentId: input.agentId,
37438
+ turnId: input.turnId,
37439
+ toolCallId: raw2.toolCallId,
37440
+ ...requested.length > 0 ? { includeUntracked: requested } : {}
37441
+ });
37442
+ sourceBefore ??= captured.reference;
37443
+ const result = {
37444
+ reference: captured.reference,
37445
+ head: captured.snapshot.head,
37446
+ baseOid: captured.snapshot.base.resolvedOid,
37447
+ entries: captured.snapshot.entries.length,
37448
+ includedUntracked: captured.snapshot.untrackedIncluded,
37449
+ exclusions: captured.snapshot.exclusions,
37450
+ issues: captured.snapshot.issues
37451
+ };
37452
+ return result;
37453
+ }
36090
37454
  if (raw2.action === "start") {
36091
37455
  if (starting || active.size || calls.size >= 64)
36092
37456
  throw new Error("Coding operation already active or turn operation limit reached");
@@ -36112,7 +37476,9 @@ function createCodingHost(input) {
36112
37476
  exitCode: null,
36113
37477
  diagnostic: "",
36114
37478
  truncated: false,
36115
- reason: null
37479
+ reason: null,
37480
+ sourceBefore,
37481
+ sourceAfter: null
36116
37482
  };
36117
37483
  saveCodingCommand(input.db, receipt);
36118
37484
  active.add(receipt.id);
@@ -36131,35 +37497,53 @@ function createCodingHost(input) {
36131
37497
  value.state
36132
37498
  ) || typeof value.diagnostic !== "string" || Buffer.byteLength(value.diagnostic) > 256 * 1024 || value.exitCode !== null && !Number.isSafeInteger(value.exitCode) || value.reason !== null && (typeof value.reason !== "string" || value.reason.length > 4096) || typeof value.truncated !== "boolean" || value.state === "succeeded" && value.exitCode !== 0 || value.state === "failed" && (value.exitCode === null || value.exitCode === 0))
36133
37499
  throw new Error("Invalid coding terminal evidence");
36134
- const diagnostic = new CodingDiagnostics(input.secrets);
37500
+ const diagnostic = new CodingDiagnostics(input.secrets, CODING_RETAINED_BYTES);
36135
37501
  diagnostic.append(Buffer.from(value.diagnostic));
36136
- const safe = diagnostic.finish();
37502
+ const retained = diagnostic.finish();
37503
+ const tail = diagnosticTail(retained.diagnostic, CODING_OUTPUT_BYTES);
36137
37504
  const reason = new CodingDiagnostics(input.secrets);
36138
37505
  reason.append(Buffer.from(value.reason ?? ""));
36139
37506
  Object.assign(receipt, {
36140
37507
  state: value.state,
36141
37508
  exitCode: value.exitCode,
36142
- diagnostic: safe.diagnostic,
36143
- truncated: safe.truncated || value.truncated,
37509
+ diagnostic: tail.text,
37510
+ truncated: tail.truncated || value.truncated,
36144
37511
  reason: reason.finish().diagnostic || null,
36145
37512
  finishedAt: Date.now()
36146
37513
  });
37514
+ try {
37515
+ const captured = await captureTeamSnapshot(input.db, input.paths, input.teamId, {
37516
+ capturedBy: "agent",
37517
+ agentId: input.agentId,
37518
+ turnId: input.turnId,
37519
+ toolCallId: receipt.toolCallId
37520
+ });
37521
+ receipt.sourceAfter = captured.reference;
37522
+ } catch {
37523
+ receipt.sourceAfter = null;
37524
+ }
36147
37525
  saveCodingCommand(input.db, receipt);
37526
+ try {
37527
+ saveCodingCommandLog(input.db, {
37528
+ commandId: receipt.id,
37529
+ teamId: input.teamId,
37530
+ agentId: input.agentId,
37531
+ turnId: input.turnId,
37532
+ toolCallId: receipt.toolCallId,
37533
+ diagnostic: retained.diagnostic,
37534
+ observedBytes: retained.observedBytes,
37535
+ redacted: retained.redacted,
37536
+ truncated: retained.truncated || value.truncated
37537
+ });
37538
+ } catch {
37539
+ }
36148
37540
  active.delete(raw2.id);
36149
37541
  return receipt;
36150
37542
  }
36151
37543
  if (raw2.action === "read") {
36152
37544
  pruneCodingCommands(input.db, input.teamId);
36153
- const receipt = getCodingCommand(input.db, raw2.id);
36154
- if (agents_exports.get(input.db, input.agentId)?.teamId !== input.teamId || !receipt || receipt.teamId !== input.teamId || agents_exports.get(input.db, receipt.agentId)?.teamId !== input.teamId)
36155
- throw new Error("Coding receipt unavailable");
36156
- if (receipt.agentId !== input.agentId) {
36157
- const message = deliverableInbox(input.db, input.agentId, false).find(
36158
- (message2) => message2.id === raw2.messageId
36159
- );
36160
- if (!message || message.fromAgentId !== receipt.agentId || !message.payload.includes(`coding-receipt:${receipt.id}`) || receipt.state === "running")
36161
- throw new Error("Coding receipt requires an authorized producer message");
36162
- }
37545
+ pruneCodingCommandLogs(input.db);
37546
+ const { receipt } = authorizeEvidence(raw2.id, raw2.messageId);
36163
37547
  let current;
36164
37548
  try {
36165
37549
  current = await snapshot(receipt.input.cwd);
@@ -36170,6 +37554,28 @@ function createCodingHost(input) {
36170
37554
  const applicability = !current.inputFingerprint || !receipt.environment.inputFingerprint ? "unknown" : receipt.state === "interrupted" || Date.now() - receipt.startedAt > 15 * 6e4 || Date.now() < receipt.startedAt || current.rootIdentity !== receipt.environment.rootIdentity || current.posture !== receipt.environment.posture || current.imageId !== receipt.environment.imageId || current.inputFingerprint !== receipt.environment.inputFingerprint ? "stale" : "fresh";
36171
37555
  return { receipt, applicability };
36172
37556
  }
37557
+ if (raw2.action === "log") {
37558
+ pruneCodingCommandLogs(input.db);
37559
+ const { producer } = authorizeEvidence(raw2.id, raw2.messageId);
37560
+ if (!producer) releaseCodingCommandLog(input.db, raw2.id);
37561
+ const page = readCodingCommandLog(input.db, raw2.id, {
37562
+ audience: producer ? "producer" : "disclosure",
37563
+ ...raw2.offset !== void 0 ? { offset: raw2.offset } : {},
37564
+ ...raw2.limit !== void 0 ? { limit: raw2.limit } : {}
37565
+ });
37566
+ if (!page) throw new Error("Coding log unavailable");
37567
+ return page;
37568
+ }
37569
+ if (raw2.action === "log-search") {
37570
+ pruneCodingCommandLogs(input.db);
37571
+ const { producer } = authorizeEvidence(raw2.id, raw2.messageId);
37572
+ if (!producer) releaseCodingCommandLog(input.db, raw2.id);
37573
+ const result = searchCodingCommandLog(input.db, raw2.id, raw2.query, {
37574
+ audience: producer ? "producer" : "disclosure"
37575
+ });
37576
+ if (!result) throw new Error("Coding log unavailable");
37577
+ return result;
37578
+ }
36173
37579
  throw new Error("Unknown coding operation");
36174
37580
  }
36175
37581
  };
@@ -39684,6 +41090,8 @@ async function mirrorAgentTurnFrame(agentIdOrAgent, frame, attemptBase = `telegr
39684
41090
  if (!agent) return;
39685
41091
  const topicId = agents_exports.getTelegramTopicId(deps.db, agent.id);
39686
41092
  if (topicId === null) return;
41093
+ const codingReleaseId = frame.kind === "event" && frame.event.type === "tool_result" && frame.event.name === "coding_command" ? codingCommandIdFromResult(frame.event.result) : null;
41094
+ let codingReleased = false;
39687
41095
  const imagePayload = frameToolResultImages(frame);
39688
41096
  if (imagePayload) {
39689
41097
  await mirrorImages(deps, topicId, agent.id, imagePayload, `${attemptBase}:images`);
@@ -39715,6 +41123,10 @@ async function mirrorAgentTurnFrame(agentIdOrAgent, frame, attemptBase = `telegr
39715
41123
  ...isReply ? { parse_mode: "HTML" } : {}
39716
41124
  })
39717
41125
  );
41126
+ if (codingReleaseId && !codingReleased) {
41127
+ releaseCodingCommandLog(deps.db, codingReleaseId);
41128
+ codingReleased = true;
41129
+ }
39718
41130
  } catch (e) {
39719
41131
  if (isThreadGoneError(e)) {
39720
41132
  console.warn(
@@ -39890,6 +41302,8 @@ function renderFrame(frame, mode) {
39890
41302
  return null;
39891
41303
  case "command_approval":
39892
41304
  return null;
41305
+ case "coding_progress":
41306
+ return null;
39893
41307
  case "user_message":
39894
41308
  case "assistant_delta":
39895
41309
  return null;
@@ -40048,6 +41462,7 @@ async function* runAgentTurn(turn) {
40048
41462
  const selectedDocker = turn.protectedExecution?.docker ?? turn.configuredDocker?.docker;
40049
41463
  const codingHost = createCodingHost({
40050
41464
  db,
41465
+ paths,
40051
41466
  agentId: agent.agent.id,
40052
41467
  teamId: agent.team.id,
40053
41468
  turnId,
@@ -41218,28 +42633,6 @@ async function authMiddleware(c, next) {
41218
42633
  import { randomUUID as randomUUID34 } from "crypto";
41219
42634
  import { existsSync as existsSync18, readFileSync as readFileSync12 } from "fs";
41220
42635
  import { join as join28 } from "path";
41221
-
41222
- // ../../packages/api-types/src/entities.ts
41223
- var REASONING_LEVELS2 = [
41224
- "off",
41225
- "minimal",
41226
- "low",
41227
- "medium",
41228
- "high",
41229
- "xhigh"
41230
- ];
41231
-
41232
- // ../../packages/api-types/src/index.ts
41233
- var PROFILE_FILES = [
41234
- "profile.json",
41235
- "SOUL.md",
41236
- "IDENTITY.md",
41237
- "BOOTSTRAP.md",
41238
- "AGENTS.md",
41239
- "TOOLS.md"
41240
- ];
41241
-
41242
- // ../daemon/src/routes/agents.ts
41243
42636
  import { Hono } from "hono";
41244
42637
 
41245
42638
  // ../daemon/src/lib/agent-id.ts
@@ -45126,7 +46519,7 @@ function isChatFrame(value) {
45126
46519
  return isNonEmptyString3(event2.name) && isNonEmptyString3(event2.mimeType) && isBase64(event2.data);
45127
46520
  }
45128
46521
  if (event2.type === "tool_result") {
45129
- return isNonEmptyString3(event2.id) && isNonEmptyString3(event2.name) && typeof event2.result === "string" && Array.isArray(event2.images) && event2.images.length > 0 && event2.images.every(isToolResultImage);
46522
+ return isNonEmptyString3(event2.id) && isNonEmptyString3(event2.name) && typeof event2.result === "string" && (event2.images === void 0 || Array.isArray(event2.images) && event2.images.every(isToolResultImage));
45130
46523
  }
45131
46524
  return false;
45132
46525
  }
@@ -45565,6 +46958,10 @@ async function deliver(plan) {
45565
46958
  capturedResultFile(getCtx().db, plan.payload.agentId, frame.event);
45566
46959
  releaseResultFile(getCtx().db, plan.payload.agentId, frame.event.result);
45567
46960
  }
46961
+ if (frame.kind === "event" && frame.event.type === "tool_result" && frame.event.name === "coding_command") {
46962
+ const id = codingCommandIdFromResult(frame.event.result);
46963
+ if (id) releaseCodingCommandLog(getCtx().db, id);
46964
+ }
45568
46965
  return;
45569
46966
  }
45570
46967
  if (plan.kind === "inbox_message") {
@@ -48807,6 +50204,128 @@ async function codingEnvironmentStatus(db, team, env = process.env) {
48807
50204
  };
48808
50205
  }
48809
50206
 
50207
+ // ../daemon/src/lib/git-review/feedback.ts
50208
+ var FEEDBACK_LIMITS = {
50209
+ excerptChars: 4e3,
50210
+ noteChars: 2e3
50211
+ };
50212
+ var feedbackReferencePrefix = "review-feedback:";
50213
+ function feedbackReference(document) {
50214
+ return `${feedbackReferencePrefix}${document.snapshotId ?? "none"}:${document.path}`;
50215
+ }
50216
+ function composeFeedbackMessage(document, note) {
50217
+ const lines = [
50218
+ `${feedbackReferencePrefix}${document.snapshotId ?? "none"}`,
50219
+ `Repository source snapshot: ${document.snapshotId ?? "(not captured)"}`,
50220
+ `File: ${document.previousPath ? `${document.previousPath} -> ${document.path}` : document.path}`,
50221
+ `Change: ${document.status}`
50222
+ ];
50223
+ if (document.lineRange)
50224
+ lines.push(`Selected lines: ${document.lineRange.start}-${document.lineRange.end} (context)`);
50225
+ lines.push(
50226
+ `Source identity: ${document.applicability === "current" ? "unchanged since this snapshot" : document.applicability === "stale" ? "CHANGED since this snapshot \u2014 the selected lines may have moved" : `unknown (${document.applicabilityReason})`}`
50227
+ );
50228
+ if (document.excerpt !== null) {
50229
+ lines.push(
50230
+ "",
50231
+ `Excerpt (${document.excerptSource === "current_read" ? "read now, not stored in the snapshot" : document.excerptSource}):`,
50232
+ "```",
50233
+ document.excerpt,
50234
+ "```"
50235
+ );
50236
+ } else {
50237
+ lines.push("", `Excerpt: unavailable (${document.excerptSource})`);
50238
+ }
50239
+ const trimmed = note?.trim();
50240
+ if (trimmed) lines.push("", `Operator note: ${trimmed.slice(0, FEEDBACK_LIMITS.noteChars)}`);
50241
+ return lines.join("\n");
50242
+ }
50243
+ async function buildReviewFeedback(db, paths, teamId, request2) {
50244
+ const team = requireTeam(db, paths, teamId);
50245
+ const record3 = request2.snapshotId ? getSourceSnapshot(db, team.id, request2.snapshotId) : null;
50246
+ const stored = parseStoredSnapshot(record3);
50247
+ return withRepository(team.path, async (captured) => {
50248
+ const base = await resolveComparisonBase(captured, request2.base ?? "HEAD");
50249
+ const identity2 = await readRepositoryIdentity(captured);
50250
+ const listed = await attachPatches(
50251
+ captured,
50252
+ await listChanges(captured, base, identity2),
50253
+ void 0,
50254
+ request2.path
50255
+ );
50256
+ const change = listed.changes.find((entry) => entry.path === request2.path);
50257
+ if (!change)
50258
+ throw new FeedbackTargetError("unknown_path", "That path is not changed since this baseline.");
50259
+ const current = await captureSourceSnapshot(captured, base, identity2);
50260
+ return {
50261
+ teamId: team.id,
50262
+ path: change.path,
50263
+ previousPath: change.previousPath,
50264
+ status: change.status,
50265
+ snapshotId: request2.snapshotId ?? null,
50266
+ lineRange: lineRange(request2, captured),
50267
+ excerpt: excerptOf(change),
50268
+ excerptSource: excerptSource(change),
50269
+ ...applicabilityOf(stored, current, change.path)
50270
+ };
50271
+ });
50272
+ }
50273
+ var FeedbackTargetError = class extends Error {
50274
+ code;
50275
+ constructor(code, message) {
50276
+ super(message);
50277
+ this.name = "FeedbackTargetError";
50278
+ this.code = code;
50279
+ }
50280
+ };
50281
+ function applicabilityOf(stored, current, path) {
50282
+ if (!stored) return { applicability: "unknown", applicabilityReason: "no_snapshot" };
50283
+ if (!stored.complete)
50284
+ return { applicability: "unknown", applicabilityReason: "incomplete_snapshot" };
50285
+ if (!current.complete)
50286
+ return { applicability: "unknown", applicabilityReason: "capture_incomplete" };
50287
+ const before = stored.entries.find((entry) => entry.path === path) ?? null;
50288
+ const after = current.entries.find((entry) => entry.path === path) ?? null;
50289
+ if (before === null && after === null) {
50290
+ return { applicability: "current", applicabilityReason: "path_unchanged" };
50291
+ }
50292
+ if (before?.digest && before.digest === after?.digest && before.kind === after?.kind) {
50293
+ return { applicability: "current", applicabilityReason: "path_unchanged" };
50294
+ }
50295
+ return { applicability: "stale", applicabilityReason: "path_changed" };
50296
+ }
50297
+ function parseStoredSnapshot(record3) {
50298
+ if (!record3) return null;
50299
+ try {
50300
+ return JSON.parse(record3.manifestJson);
50301
+ } catch {
50302
+ return null;
50303
+ }
50304
+ }
50305
+ function excerptSource(change) {
50306
+ if (change.excludedReason) return "excluded";
50307
+ if (change.binary) return "binary";
50308
+ if (change.patch === null) return "omitted";
50309
+ return "current_read";
50310
+ }
50311
+ function excerptOf(change) {
50312
+ if (change.excludedReason || change.binary || change.patch === null) return null;
50313
+ return change.patch.slice(0, FEEDBACK_LIMITS.excerptChars);
50314
+ }
50315
+ function lineRange(request2, _captured) {
50316
+ const { startLine, endLine } = request2;
50317
+ if (startLine === void 0 && endLine === void 0) return null;
50318
+ const start = startLine ?? endLine;
50319
+ const end = endLine ?? startLine;
50320
+ if (start === void 0 || end === void 0 || !Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 1 || end < start || end > 1e6) {
50321
+ throw new FeedbackTargetError(
50322
+ "invalid_range",
50323
+ "Selected lines must be an ordered positive range."
50324
+ );
50325
+ }
50326
+ return { start, end };
50327
+ }
50328
+
48810
50329
  // ../daemon/src/routes/teams.ts
48811
50330
  var USER_MD_MAX_BYTES2 = 12e3;
48812
50331
  var teamsRouter = new Hono18();
@@ -48846,6 +50365,195 @@ teamsRouter.get("/:id/repository-context", async (c) => {
48846
50365
  })
48847
50366
  );
48848
50367
  });
50368
+ function codingLogCommand(c) {
50369
+ const { db, paths } = getCtx();
50370
+ const team = teams_exports.get(db, c.req.param("id") ?? "", paths);
50371
+ if (!team) return { error: "Team not found", status: 404 };
50372
+ const commandId = c.req.param("commandId") ?? "";
50373
+ const receipt = getCodingCommand(db, commandId);
50374
+ if (!receipt || receipt.teamId !== team.id)
50375
+ return { error: "Coding command not found", status: 404 };
50376
+ const view2 = getCodingCommandLog(db, commandId);
50377
+ if (view2.releasedAt === null) return { error: "Coding log has not been shared", status: 403 };
50378
+ return { db, commandId, view: view2 };
50379
+ }
50380
+ teamsRouter.get("/:id/coding-commands/:commandId/log", (c) => {
50381
+ const resolved = codingLogCommand(c);
50382
+ if ("error" in resolved) return c.json({ error: resolved.error }, resolved.status);
50383
+ const offset = Number(c.req.query("offset") ?? "0");
50384
+ const limit = Number(c.req.query("limit") ?? "65536");
50385
+ if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(limit))
50386
+ return c.json({ error: "Invalid page request" }, 400);
50387
+ const page = readCodingCommandLog(resolved.db, resolved.commandId, {
50388
+ offset,
50389
+ limit,
50390
+ audience: "disclosure"
50391
+ });
50392
+ c.header("Cache-Control", "no-store");
50393
+ return c.json({ view: resolved.view, page });
50394
+ });
50395
+ teamsRouter.get("/:id/coding-commands/:commandId/log/search", (c) => {
50396
+ const resolved = codingLogCommand(c);
50397
+ if ("error" in resolved) return c.json({ error: resolved.error }, resolved.status);
50398
+ const query = c.req.query("q") ?? "";
50399
+ if (!query || query.length > 256) return c.json({ error: "Invalid search query" }, 400);
50400
+ const result = searchCodingCommandLog(resolved.db, resolved.commandId, query, {
50401
+ audience: "disclosure"
50402
+ });
50403
+ c.header("Cache-Control", "no-store");
50404
+ return c.json({ view: resolved.view, result });
50405
+ });
50406
+ function reviewFailure(c, error2) {
50407
+ if (error2 instanceof ReviewBaseError)
50408
+ return c.json({ error: error2.message, code: error2.code }, 400);
50409
+ if (error2 instanceof FeedbackTargetError)
50410
+ return c.json({ error: error2.message, code: error2.code }, 400);
50411
+ if (error2 instanceof ReviewUnavailableError) {
50412
+ return c.json(
50413
+ { error: error2.message, code: error2.code },
50414
+ error2.code === "team_not_found" ? 404 : 409
50415
+ );
50416
+ }
50417
+ throw error2;
50418
+ }
50419
+ teamsRouter.get("/:id/review", async (c) => {
50420
+ const { db, paths } = getCtx();
50421
+ c.header("Cache-Control", "no-store");
50422
+ const base = c.req.query("base");
50423
+ const patches = c.req.query("patches") === "1";
50424
+ const patchPath = c.req.query("path");
50425
+ if (patchPath !== void 0 && (patchPath.length === 0 || patchPath.length > 4096)) {
50426
+ return c.json({ error: "Invalid path", code: "invalid_path" }, 400);
50427
+ }
50428
+ try {
50429
+ const review = await readTeamReview(db, paths, c.req.param("id"), {
50430
+ ...base === void 0 ? {} : { base },
50431
+ patches,
50432
+ ...patchPath === void 0 ? {} : { patchPath }
50433
+ });
50434
+ return c.json(review);
50435
+ } catch (error2) {
50436
+ return reviewFailure(c, error2);
50437
+ }
50438
+ });
50439
+ teamsRouter.get("/:id/review/snapshots", (c) => {
50440
+ const { db, paths } = getCtx();
50441
+ c.header("Cache-Control", "no-store");
50442
+ try {
50443
+ return c.json({ snapshots: listTeamSnapshots(db, paths, c.req.param("id")) });
50444
+ } catch (error2) {
50445
+ return reviewFailure(c, error2);
50446
+ }
50447
+ });
50448
+ teamsRouter.get("/:id/review/snapshots/:snapshotId", (c) => {
50449
+ const { db, paths } = getCtx();
50450
+ c.header("Cache-Control", "no-store");
50451
+ try {
50452
+ const snapshot = readTeamSnapshot(db, paths, c.req.param("id"), c.req.param("snapshotId"));
50453
+ if (!snapshot) return c.json({ error: "Snapshot not found" }, 404);
50454
+ return c.json({ snapshot });
50455
+ } catch (error2) {
50456
+ return reviewFailure(c, error2);
50457
+ }
50458
+ });
50459
+ teamsRouter.post("/:id/review/feedback", async (c) => {
50460
+ const { db, paths } = getCtx();
50461
+ c.header("Cache-Control", "no-store");
50462
+ const body = await c.req.json().catch(() => null);
50463
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
50464
+ return c.json({ error: "Invalid feedback request" }, 400);
50465
+ }
50466
+ const input = body;
50467
+ if (Object.keys(input).some(
50468
+ (key3) => !["path", "base", "snapshotId", "startLine", "endLine", "note"].includes(key3)
50469
+ )) {
50470
+ return c.json({ error: "Invalid feedback request" }, 400);
50471
+ }
50472
+ if (typeof input.path !== "string" || input.path.length === 0 || input.path.length > 4096) {
50473
+ return c.json({ error: "A changed path is required" }, 400);
50474
+ }
50475
+ if (input.base !== void 0 && (typeof input.base !== "string" || input.base.length > 4096)) {
50476
+ return c.json({ error: "Invalid comparison base" }, 400);
50477
+ }
50478
+ if (input.snapshotId !== void 0 && (typeof input.snapshotId !== "string" || input.snapshotId.length > 128)) {
50479
+ return c.json({ error: "Invalid snapshot reference" }, 400);
50480
+ }
50481
+ if (input.note !== void 0 && (typeof input.note !== "string" || input.note.length > FEEDBACK_LIMITS.noteChars)) {
50482
+ return c.json({ error: "Invalid feedback note" }, 400);
50483
+ }
50484
+ for (const key3 of ["startLine", "endLine"]) {
50485
+ const value = input[key3];
50486
+ if (value !== void 0 && (typeof value !== "number" || !Number.isSafeInteger(value))) {
50487
+ return c.json({ error: "Selected lines must be whole numbers" }, 400);
50488
+ }
50489
+ }
50490
+ try {
50491
+ const feedback = await buildReviewFeedback(db, paths, c.req.param("id"), {
50492
+ path: input.path,
50493
+ ...input.base === void 0 ? {} : { base: input.base },
50494
+ ...input.snapshotId === void 0 ? {} : { snapshotId: input.snapshotId },
50495
+ ...input.startLine === void 0 ? {} : { startLine: input.startLine },
50496
+ ...input.endLine === void 0 ? {} : { endLine: input.endLine },
50497
+ ...input.note === void 0 ? {} : { note: input.note }
50498
+ });
50499
+ return c.json({
50500
+ feedback,
50501
+ reference: feedbackReference(feedback),
50502
+ message: composeFeedbackMessage(feedback, input.note)
50503
+ });
50504
+ } catch (error2) {
50505
+ return reviewFailure(c, error2);
50506
+ }
50507
+ });
50508
+ teamsRouter.get("/:id/review/snapshots/:snapshotId/applicability", async (c) => {
50509
+ const { db, paths } = getCtx();
50510
+ c.header("Cache-Control", "no-store");
50511
+ try {
50512
+ return c.json(
50513
+ await readSnapshotApplicability(db, paths, c.req.param("id"), c.req.param("snapshotId"))
50514
+ );
50515
+ } catch (error2) {
50516
+ return reviewFailure(c, error2);
50517
+ }
50518
+ });
50519
+ teamsRouter.post("/:id/review/snapshots", async (c) => {
50520
+ const { db, paths } = getCtx();
50521
+ c.header("Cache-Control", "no-store");
50522
+ const body = await c.req.json().catch(() => null);
50523
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
50524
+ return c.json({ error: "Invalid snapshot request" }, 400);
50525
+ }
50526
+ const input = body;
50527
+ if (Object.keys(input).some((key3) => !["base", "includeUntracked"].includes(key3))) {
50528
+ return c.json({ error: "Invalid snapshot request" }, 400);
50529
+ }
50530
+ if (input.base !== void 0 && (typeof input.base !== "string" || input.base.length > 4096)) {
50531
+ return c.json({ error: "Invalid comparison base" }, 400);
50532
+ }
50533
+ let includeUntracked;
50534
+ if (input.includeUntracked !== void 0) {
50535
+ if (!Array.isArray(input.includeUntracked) || input.includeUntracked.length > REVIEW_LIMITS.files) {
50536
+ return c.json({ error: "Invalid untracked selection" }, 400);
50537
+ }
50538
+ includeUntracked = [];
50539
+ for (const entry of input.includeUntracked) {
50540
+ if (typeof entry !== "string" || entry.length === 0 || entry.length > 4096) {
50541
+ return c.json({ error: "Invalid untracked selection" }, 400);
50542
+ }
50543
+ includeUntracked.push(entry);
50544
+ }
50545
+ }
50546
+ try {
50547
+ const captured = await captureTeamSnapshot(db, paths, c.req.param("id"), {
50548
+ capturedBy: "operator",
50549
+ ...input.base === void 0 ? {} : { base: input.base },
50550
+ ...includeUntracked ? { includeUntracked } : {}
50551
+ });
50552
+ return c.json(captured);
50553
+ } catch (error2) {
50554
+ return reviewFailure(c, error2);
50555
+ }
50556
+ });
48849
50557
  teamsRouter.get("/:id/coding-environment", async (c) => {
48850
50558
  const { db, paths } = getCtx();
48851
50559
  const team = teams_exports.get(db, c.req.param("id"), paths);