@garuda-agent/garuda 0.16.1 → 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 (54) hide show
  1. package/CHANGELOG.md +182 -0
  2. package/README.md +56 -1074
  3. package/SECURITY.md +9 -2
  4. package/dist/{acpCommand-OTAYW7BS.js → acpCommand-7EY3NHPG.js} +18 -16
  5. package/dist/{anthropic-NQDBCHJU.js → anthropic-NQIZLD64.js} +12 -6
  6. package/dist/{chunk-TILFPYWO.js → chunk-2DYMJMRQ.js} +1880 -982
  7. package/dist/{chunk-YQTGMCU6.js → chunk-3MRQJKUO.js} +29 -5
  8. package/dist/{chunk-WMOLXTBU.js → chunk-4LFJFDCF.js} +2 -1
  9. package/dist/{chunk-44QN5SWZ.js → chunk-55CFEEIN.js} +22 -2
  10. package/dist/{chunk-USCUXCAU.js → chunk-5B5HDTUR.js} +3 -3
  11. package/dist/{chunk-TUUEBHG2.js → chunk-6XHON3Q6.js} +2 -2
  12. package/dist/chunk-735BKABA.js +472 -0
  13. package/dist/{chunk-B6FA4I7Q.js → chunk-7TH3NHCQ.js} +2 -2
  14. package/dist/{chunk-OUCV3FCT.js → chunk-AOGFYUKG.js} +3 -3
  15. package/dist/{chunk-GXZACJUI.js → chunk-B4SJY7KT.js} +322 -39
  16. package/dist/{chunk-3EQU2GUX.js → chunk-BHZ6G65V.js} +5 -1
  17. package/dist/chunk-BSK5T2ZG.js +9 -0
  18. package/dist/chunk-J4346IDH.js +156 -0
  19. package/dist/{chunk-NP6RIC25.js → chunk-JRVWH27M.js} +7 -7
  20. package/dist/{chunk-HUIK3WXO.js → chunk-P6XWWIEK.js} +20 -3
  21. package/dist/{chunk-HE2K657U.js → chunk-TJIWD7R7.js} +47 -16
  22. package/dist/chunk-U65DQZDB.js +143 -0
  23. package/dist/chunk-UIRJOQQS.js +118 -0
  24. package/dist/{chunk-KKB5W47S.js → chunk-VTMD5IHD.js} +26 -2
  25. package/dist/{chunk-BQTUOO4G.js → chunk-Z66BWTMD.js} +81 -32
  26. package/dist/cli/index.js +99 -41
  27. package/dist/{create-VXIH2GUZ.js → create-W6BV75QX.js} +18 -12
  28. package/dist/{deadline-F5RJJ2QW.js → deadline-TP7ZGLT2.js} +4 -2
  29. package/dist/{evalCommand-RGMZRYDR.js → evalCommand-YVQAWUZE.js} +50 -29
  30. package/dist/{export-HN3CT7AV.js → export-SJ3DUP6W.js} +2 -2
  31. package/dist/{inkChat-WEDGQMCY.js → inkChat-FNUBK43U.js} +14 -13
  32. package/dist/install-TKWN5AYH.js +19 -0
  33. package/dist/{job-3NYDMP2M.js → job-7JOI4YIQ.js} +6 -2
  34. package/dist/jobCommand-FE6XN3YD.js +33 -0
  35. package/dist/{launchd-SSX362VG.js → launchd-3A3ONEW5.js} +9 -8
  36. package/dist/{lspCommand-JRXKRSL7.js → lspCommand-ZZ6R4PA2.js} +7 -6
  37. package/dist/{manager-773PUDFE.js → manager-73MPYFTP.js} +7 -6
  38. package/dist/{nightCommand-65MUEV32.js → nightCommand-OODJNWDT.js} +26 -20
  39. package/dist/{openaiCompatible-CJWVJP5M.js → openaiCompatible-6TLDDW5U.js} +29 -15
  40. package/dist/{policy-EJPLEEXP.js → policy-ZJ7RMOFN.js} +10 -4
  41. package/dist/{remove-6GDSTAIO.js → remove-42WQNVPX.js} +8 -7
  42. package/dist/{server-NGKVIQEV.js → server-VEYIJHEU.js} +2 -2
  43. package/dist/{setupCommand-VA7D5MZK.js → setupCommand-GBQIVYJC.js} +45 -13
  44. package/dist/text-LS35WSRL.js +17 -0
  45. package/native/bin/garuda-reap +0 -0
  46. package/native/garuda-reap.c +113 -0
  47. package/package.json +39 -13
  48. package/dist/chunk-EIXUSVFX.js +0 -116
  49. package/dist/chunk-R7I2NBPX.js +0 -71
  50. package/dist/chunk-ULAJPMY7.js +0 -107
  51. package/dist/chunk-VBZCNMUD.js +0 -9
  52. package/dist/install-EWDSPFFZ.js +0 -18
  53. package/dist/jobCommand-ACSGSBQ4.js +0 -32
  54. package/dist/text-J2GD647J.js +0 -11
package/dist/cli/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  runCommand,
16
16
  runTurnInTerminal,
17
17
  stopMessage
18
- } from "../chunk-BQTUOO4G.js";
18
+ } from "../chunk-Z66BWTMD.js";
19
19
  import {
20
20
  PlainRenderer
21
21
  } from "../chunk-FARS4R67.js";
@@ -32,24 +32,27 @@ import {
32
32
  } from "../chunk-4JSUUDFQ.js";
33
33
  import {
34
34
  DEFAULT_MAX_TOKENS,
35
- FileSessionStore,
36
35
  Runtime,
37
36
  ToolRegistry,
38
37
  auditDirFor,
39
- closeOpenToolCalls,
40
- createSession,
41
38
  defaultTools,
42
- parseRecords,
43
- popPoint,
44
- pushPoint,
45
39
  rebuildState,
46
- redoPoint,
47
40
  runAgent,
48
41
  signature
49
- } from "../chunk-TILFPYWO.js";
42
+ } from "../chunk-2DYMJMRQ.js";
50
43
  import "../chunk-G2AX7MGS.js";
51
44
  import "../chunk-A2QAIRQL.js";
52
- import "../chunk-ULAJPMY7.js";
45
+ import {
46
+ FileSessionStore,
47
+ closeOpenToolCalls,
48
+ createSession,
49
+ isCostIncomplete,
50
+ parseRecords,
51
+ pendingChildTokens,
52
+ popPoint,
53
+ pushPoint,
54
+ redoPoint
55
+ } from "../chunk-735BKABA.js";
53
56
  import {
54
57
  NO_MODEL,
55
58
  chooseModel,
@@ -57,9 +60,11 @@ import {
57
60
  loadModelsConfig,
58
61
  providerKeySource,
59
62
  resolveModel
60
- } from "../chunk-NP6RIC25.js";
61
- import "../chunk-EIXUSVFX.js";
62
- import "../chunk-R7I2NBPX.js";
63
+ } from "../chunk-JRVWH27M.js";
64
+ import {
65
+ loadCatalog
66
+ } from "../chunk-U65DQZDB.js";
67
+ import "../chunk-J4346IDH.js";
63
68
  import {
64
69
  loadSearchConfig
65
70
  } from "../chunk-B25VL4RU.js";
@@ -69,19 +74,20 @@ import {
69
74
  import "../chunk-EU7PA45O.js";
70
75
  import {
71
76
  VERSION
72
- } from "../chunk-VBZCNMUD.js";
77
+ } from "../chunk-BSK5T2ZG.js";
73
78
  import "../chunk-5DEWP3YX.js";
74
79
  import "../chunk-ZAE25JM2.js";
75
80
  import "../chunk-ATW7BNIG.js";
76
81
  import "../chunk-PZS3V6P3.js";
77
- import "../chunk-OUCV3FCT.js";
82
+ import "../chunk-AOGFYUKG.js";
78
83
  import {
79
84
  JOB_DENIAL
80
- } from "../chunk-GXZACJUI.js";
81
- import "../chunk-WMOLXTBU.js";
82
- import "../chunk-3EQU2GUX.js";
83
- import "../chunk-HUIK3WXO.js";
84
- import "../chunk-KKB5W47S.js";
85
+ } from "../chunk-B4SJY7KT.js";
86
+ import "../chunk-BHZ6G65V.js";
87
+ import "../chunk-P6XWWIEK.js";
88
+ import "../chunk-VTMD5IHD.js";
89
+ import "../chunk-UIRJOQQS.js";
90
+ import "../chunk-4LFJFDCF.js";
85
91
  import "../chunk-3NJNVZTK.js";
86
92
 
87
93
  // src/cli/index.ts
@@ -254,9 +260,41 @@ var NO_PERMISSIONS = {
254
260
  throw new Error("Replay runs no commands.");
255
261
  },
256
262
  deniedByPolicy: () => true,
263
+ writeScopeDenial: async () => "replay: no writes",
257
264
  commandPolicyDenial: () => "Replay runs no commands."
258
265
  };
259
266
 
267
+ // src/nodeFloor.ts
268
+ var NODE_SECURITY = {
269
+ date: "2026-10-21",
270
+ advisory: "https://nodejs.org/en/blog/vulnerability/october-2026-security-releases",
271
+ lines: { 22: "22.23.4", 24: "24.22.1", 26: "26.12.1" }
272
+ };
273
+ function nodeProblem(version = process.versions.node) {
274
+ const [major = 0] = parse(version);
275
+ const lines = Object.keys(NODE_SECURITY.lines).map(Number);
276
+ const floor = NODE_SECURITY.lines[major];
277
+ const use = `Use Node ${lines.map((l) => NODE_SECURITY.lines[l]).join(", ")} or later.`;
278
+ if (floor === void 0) {
279
+ if (major > Math.max(...lines)) return void 0;
280
+ return `Node ${version} gets no security fixes (Garuda supports ${lines.join(", ")}). ${use}`;
281
+ }
282
+ if (compare(parse(version), parse(floor)) < 0) {
283
+ return `Node ${version} does not have the security fixes of ${NODE_SECURITY.date} (${NODE_SECURITY.advisory}). Update to ${floor} or later.`;
284
+ }
285
+ return void 0;
286
+ }
287
+ function parse(version) {
288
+ return version.replace(/^v/, "").split(/[.-]/).slice(0, 3).map((n) => Number.parseInt(n, 10) || 0);
289
+ }
290
+ function compare(a, b) {
291
+ for (let i = 0; i < 3; i++) {
292
+ const d = (a[i] ?? 0) - (b[i] ?? 0);
293
+ if (d !== 0) return d;
294
+ }
295
+ return 0;
296
+ }
297
+
260
298
  // src/cli/banner.ts
261
299
  import { homedir } from "os";
262
300
  var WORDMARK = [
@@ -516,7 +554,8 @@ function assistantLine(response, model) {
516
554
  }),
517
555
  stop_reason: response.stopReason,
518
556
  stop_sequence: null,
519
- usage: apiUsage(response.usage)
557
+ usage: apiUsage(response.usage),
558
+ ...response.usageReported === false ? { usage_is_estimated: true } : {}
520
559
  },
521
560
  parent_tool_use_id: null
522
561
  };
@@ -564,7 +603,12 @@ function resultLine(outcome, info) {
564
603
  ...subtype === "success" ? { result: info.lastText } : {},
565
604
  stop_reason: result?.modelStopReason ?? null,
566
605
  total_cost_usd: info.totalCostUsd,
606
+ ...info.totalCostUnknown === true ? { total_cost_is_unknown: true } : {},
607
+ ...info.totalCostIncomplete === true ? { total_cost_is_incomplete: true } : {},
567
608
  usage: apiUsage(usage),
609
+ ...(result?.unreportedTokens ?? 0) > 0 ? { usage_is_incomplete: true, unreported_budget_tokens: result?.unreportedTokens } : {},
610
+ ...info.runCostUsd === void 0 ? { cost_is_unknown: true } : {},
611
+ ...info.runCostUsd !== void 0 && info.runCostIncomplete === true ? { cost_is_incomplete: true } : {},
568
612
  modelUsage: {
569
613
  [info.model]: {
570
614
  inputTokens: usage.inputTokens,
@@ -729,6 +773,9 @@ function askTerminal(prompt, history, io, completer) {
729
773
 
730
774
  // src/cli/index.ts
731
775
  async function main() {
776
+ const nodeWarning = nodeProblem();
777
+ if (nodeWarning !== void 0) process.stderr.write(`Warning: ${nodeWarning}
778
+ `);
732
779
  const program = new Command().enablePositionalOptions().name("garuda").description("A terminal coding agent. With no task, it starts a chat in the current folder.").version(VERSION).option("-p, --prompt <task>", "run one task and exit").option("-m, --model <id>", "model id (or set GARUDA_MODEL)").option(
733
780
  "--subagent-model <id>",
734
781
  "model of the explore subagent (or set GARUDA_SUBAGENT_MODEL); default: the main model"
@@ -772,7 +819,7 @@ async function main() {
772
819
  "--test-command <cmd>",
773
820
  "with --from-git: the test command; {files} takes the test files (default: detected)"
774
821
  ).action(async (options) => {
775
- const { runEvalCommand } = await import("../evalCommand-RGMZRYDR.js");
822
+ const { runEvalCommand } = await import("../evalCommand-YVQAWUZE.js");
776
823
  process.exitCode = await runEvalCommand({
777
824
  ...options,
778
825
  model: options.model ?? process.env.GARUDA_MODEL,
@@ -794,30 +841,30 @@ async function main() {
794
841
  program.command("night").description(
795
842
  "run this project's night queue (jobs from /schedule), up to 3 at a time, then write one digest"
796
843
  ).option("--at <HH:MM>", "wait until this local time first (keep the terminal open)").option("--parallel <n>", "jobs at the same time (1 to 10, default 3)").addOption(new Option("--from-launchd").hideHelp()).action(async (options) => {
797
- const { nightCommand } = await import("../nightCommand-65MUEV32.js");
844
+ const { nightCommand } = await import("../nightCommand-OODJNWDT.js");
798
845
  process.exitCode = await nightCommand(options, new PlainRenderer());
799
846
  });
800
847
  program.command("setup").description(
801
848
  "choose a provider and model and store its key (~/.garuda/credentials, only you can read it); editors run it to sign in"
802
849
  ).option("--show", "show the model and where each key comes from (keys masked)").option("--forget [key-name]", "remove a stored key, or all of them").action(async (options) => {
803
- const { setupCommand, terminalIO } = await import("../setupCommand-VA7D5MZK.js");
850
+ const { setupCommand, terminalIO } = await import("../setupCommand-GBQIVYJC.js");
804
851
  process.exitCode = await setupCommand(options, { io: await terminalIO() });
805
852
  });
806
853
  program.command("acp").description(
807
854
  "run as an agent for an editor over the Agent Client Protocol (VS Code with an extension, Zed, JetBrains, Neovim, Emacs); the editor starts it"
808
855
  ).argument("[action]", "setup: run garuda setup (the editor's sign-in, ACP Terminal Auth)").option("-m, --model <id>", "model id (or set GARUDA_MODEL)").action(async (action, options) => {
809
- const { acpCommand } = await import("../acpCommand-OTAYW7BS.js");
856
+ const { acpCommand } = await import("../acpCommand-7EY3NHPG.js");
810
857
  process.exitCode = await acpCommand({
811
858
  ...options,
812
859
  ...action === void 0 ? {} : { action }
813
860
  });
814
861
  });
815
862
  const lsp = program.command("lsp").description("show the language servers for diagnostics (TS/JS, Python, Java)").action(async () => {
816
- const { lspStatusCommand } = await import("../lspCommand-JRXKRSL7.js");
863
+ const { lspStatusCommand } = await import("../lspCommand-ZZ6R4PA2.js");
817
864
  process.exitCode = await lspStatusCommand();
818
865
  });
819
866
  lsp.command("install").description("install a pinned language server into ~/.garuda/lsp (needs the network)").argument("<language>", "typescript, python or java").action(async (language) => {
820
- const { lspInstallCommand } = await import("../lspCommand-JRXKRSL7.js");
867
+ const { lspInstallCommand } = await import("../lspCommand-ZZ6R4PA2.js");
821
868
  process.exitCode = await lspInstallCommand(language);
822
869
  });
823
870
  await program.parseAsync();
@@ -829,7 +876,7 @@ async function start(options, program) {
829
876
  let job;
830
877
  let jobGuard;
831
878
  if (options.job !== void 0) {
832
- const { prepareJob } = await import("../jobCommand-ACSGSBQ4.js");
879
+ const { prepareJob } = await import("../jobCommand-FE6XN3YD.js");
833
880
  const prepared = await prepareJob(
834
881
  mainRoot,
835
882
  options.job,
@@ -841,7 +888,7 @@ async function start(options, program) {
841
888
  );
842
889
  if (typeof prepared === "number") return prepared;
843
890
  job = prepared;
844
- const { failJobSync } = await import("../job-3NYDMP2M.js");
891
+ const { failJobSync } = await import("../job-7JOI4YIQ.js");
845
892
  const preparedJob = prepared;
846
893
  jobGuard = () => {
847
894
  if (preparedJob.job.status === "running") {
@@ -883,13 +930,14 @@ async function start(options, program) {
883
930
  }
884
931
  }
885
932
  const searchConfig = await loadSearchConfig();
933
+ const catalog = await loadCatalog();
886
934
  const credentialsWarning = await keepProviderKeys([
887
935
  resolved.def.apiKeyEnv,
888
936
  sub?.def.apiKeyEnv,
889
937
  "ANTHROPIC_API_KEY"
890
938
  ]);
891
939
  const anthropicKey = providerKeySource("ANTHROPIC_API_KEY");
892
- const { loadTeamPolicy } = await import("../policy-EJPLEEXP.js");
940
+ const { loadTeamPolicy } = await import("../policy-ZJ7RMOFN.js");
893
941
  let team;
894
942
  try {
895
943
  team = await loadTeamPolicy();
@@ -965,7 +1013,9 @@ async function start(options, program) {
965
1013
  ...r.maxTokens === void 0 ? {} : { maxTokens: r.maxTokens }
966
1014
  };
967
1015
  },
968
- configured: Object.keys(models.config.models)
1016
+ configured: Object.keys(models.config.models),
1017
+ // The account's model list from `garuda setup` (0.17); no network call here.
1018
+ ...catalog === void 0 ? {} : { catalog }
969
1019
  },
970
1020
  ...options.resume === void 0 ? {} : { resume: options.resume },
971
1021
  onEvent: (event) => events.event(event),
@@ -982,11 +1032,11 @@ async function start(options, program) {
982
1032
  "A job needs the OS sandbox (Seatbelt or bubblewrap): without it, every command would need an approval, and nobody is there to give it."
983
1033
  );
984
1034
  await runtime.close();
985
- const { saveJob } = await import("../job-3NYDMP2M.js");
1035
+ const { saveJob } = await import("../job-7JOI4YIQ.js");
986
1036
  job.job.status = "failed";
987
1037
  await saveJob(job.job);
988
1038
  if (options.fromLaunchd === true || job.job.launchd !== void 0) {
989
- const { removeAgent, defaultAgentEnv } = await import("../launchd-SSX362VG.js");
1039
+ const { removeAgent, defaultAgentEnv } = await import("../launchd-3A3ONEW5.js");
990
1040
  await removeAgent(runtime.executor, job.job, defaultAgentEnv());
991
1041
  }
992
1042
  return 1;
@@ -1036,11 +1086,19 @@ async function start(options, program) {
1036
1086
  });
1037
1087
  if (output !== void 0) events = output;
1038
1088
  const costBefore = runtime.session?.costUsd ?? 0;
1089
+ const unreportedBefore = runtime.session?.unreportedTokens ?? 0;
1090
+ const incompleteBefore = runtime.session?.costIncomplete ?? false;
1091
+ const pendingBefore = runtime.session === void 0 ? 0 : pendingChildTokens(runtime.session);
1039
1092
  const finish = (outcome2) => {
1040
- const cost = runtime.session?.costUsd;
1093
+ const session2 = runtime.session;
1094
+ const cost = session2?.costUsd;
1095
+ const runIncomplete = session2 !== void 0 && (session2.unreportedTokens > unreportedBefore || pendingChildTokens(session2) > pendingBefore || session2.costIncomplete && !incompleteBefore);
1041
1096
  output?.finish(outcome2, {
1042
1097
  totalCostUsd: cost ?? 0,
1098
+ ...cost === void 0 ? { totalCostUnknown: true } : {},
1099
+ ...session2 !== void 0 && isCostIncomplete(session2) ? { totalCostIncomplete: true } : {},
1043
1100
  runCostUsd: cost === void 0 ? void 0 : cost - costBefore,
1101
+ ...runIncomplete ? { runCostIncomplete: true } : {},
1044
1102
  contextWindow: runtime.limits.contextWindow,
1045
1103
  maxOutputTokens: resolved.maxTokens ?? DEFAULT_MAX_TOKENS,
1046
1104
  ...outcome2.kind === "done" ? { stopMessage: stopMessage(outcome2.result.stopReason, runtime.limits) } : {}
@@ -1057,7 +1115,7 @@ async function start(options, program) {
1057
1115
  if (resolved2.kind === "prompt") prompt = resolved2.prompt;
1058
1116
  }
1059
1117
  if (job !== void 0) {
1060
- const { testsBefore } = await import("../jobCommand-ACSGSBQ4.js");
1118
+ const { testsBefore } = await import("../jobCommand-FE6XN3YD.js");
1061
1119
  await testsBefore(job, runtime, renderer);
1062
1120
  }
1063
1121
  const outcome = await runTurnInTerminal(
@@ -1070,7 +1128,7 @@ async function start(options, program) {
1070
1128
  await runtime.close();
1071
1129
  finish(outcome);
1072
1130
  if (job !== void 0) {
1073
- const { finishJob } = await import("../jobCommand-ACSGSBQ4.js");
1131
+ const { finishJob } = await import("../jobCommand-FE6XN3YD.js");
1074
1132
  await finishJob(job, outcome, runtime, renderer, void 0, {
1075
1133
  fromLaunchd: options.fromLaunchd === true
1076
1134
  });
@@ -1083,7 +1141,7 @@ async function start(options, program) {
1083
1141
  const bannerInfo = {
1084
1142
  version: VERSION,
1085
1143
  model: modelId,
1086
- sandbox: runtime.executor.isolation === "none" ? "none: each command asks first" : `${runtime.executor.name} \xB7 no network`,
1144
+ sandbox: runtime.executor.isolation === "none" ? "none: each command asks first, unless a rule allows it" : `${runtime.executor.name} \xB7 no network`,
1087
1145
  root,
1088
1146
  extras: runtime.extras(),
1089
1147
  subagents: runtime.subagentsSummary(),
@@ -1156,7 +1214,7 @@ function wantsInk() {
1156
1214
  }
1157
1215
  async function loadInk(renderer) {
1158
1216
  try {
1159
- return await import("../inkChat-WEDGQMCY.js");
1217
+ return await import("../inkChat-FNUBK43U.js");
1160
1218
  } catch (error) {
1161
1219
  renderer.info(`Plain chat (Ink is not available: ${error.message.split("\n")[0]}).`);
1162
1220
  return void 0;
@@ -1187,9 +1245,9 @@ function recordJobDenial(job, tool, target) {
1187
1245
  job.denied.push({ tool, target: shown });
1188
1246
  }
1189
1247
  async function jobBatchClient(job, resolved, renderer) {
1190
- const { DeadlineClient } = await import("../deadline-F5RJJ2QW.js");
1191
- const { nextTime } = await import("../launchd-SSX362VG.js");
1192
- const { DEFAULT_FINISH_BY, DEFAULT_STEP_LIMIT_MINUTES, SWITCH_BEFORE_MS } = await import("../create-VXIH2GUZ.js");
1248
+ const { DeadlineClient } = await import("../deadline-TP7ZGLT2.js");
1249
+ const { nextTime } = await import("../launchd-3A3ONEW5.js");
1250
+ const { DEFAULT_FINISH_BY, DEFAULT_STEP_LIMIT_MINUTES, SWITCH_BEFORE_MS } = await import("../create-W6BV75QX.js");
1193
1251
  const finishBy = job.job.finishBy ?? DEFAULT_FINISH_BY;
1194
1252
  const stepLimit = job.job.stepLimitMinutes ?? DEFAULT_STEP_LIMIT_MINUTES;
1195
1253
  const switchAt = new Date(nextTime(finishBy).getTime() - SWITCH_BEFORE_MS);
@@ -3,7 +3,7 @@ import {
3
3
  jobBase,
4
4
  linkCandidates,
5
5
  worktreeDir
6
- } from "./chunk-44QN5SWZ.js";
6
+ } from "./chunk-55CFEEIN.js";
7
7
  import {
8
8
  agentLabel,
9
9
  defaultAgentEnv,
@@ -12,18 +12,17 @@ import {
12
12
  nextTime,
13
13
  nightAgentTime,
14
14
  nightSpec
15
- } from "./chunk-USCUXCAU.js";
15
+ } from "./chunk-5B5HDTUR.js";
16
16
  import {
17
17
  jobPrompt
18
- } from "./chunk-YQTGMCU6.js";
19
- import "./chunk-OUCV3FCT.js";
18
+ } from "./chunk-3MRQJKUO.js";
19
+ import "./chunk-AOGFYUKG.js";
20
20
  import {
21
21
  JobGitError
22
- } from "./chunk-TUUEBHG2.js";
23
- import "./chunk-GXZACJUI.js";
24
- import "./chunk-WMOLXTBU.js";
25
- import "./chunk-3EQU2GUX.js";
26
- import "./chunk-HUIK3WXO.js";
22
+ } from "./chunk-6XHON3Q6.js";
23
+ import "./chunk-B4SJY7KT.js";
24
+ import "./chunk-BHZ6G65V.js";
25
+ import "./chunk-P6XWWIEK.js";
27
26
  import {
28
27
  DEFAULT_JOB_MAX_STEPS,
29
28
  JOBS_DIR,
@@ -32,7 +31,11 @@ import {
32
31
  planPermissions,
33
32
  planTitle,
34
33
  saveJob
35
- } from "./chunk-KKB5W47S.js";
34
+ } from "./chunk-VTMD5IHD.js";
35
+ import {
36
+ proposeWriteScope
37
+ } from "./chunk-UIRJOQQS.js";
38
+ import "./chunk-4LFJFDCF.js";
36
39
  import "./chunk-3NJNVZTK.js";
37
40
 
38
41
  // src/jobs/create.ts
@@ -62,6 +65,7 @@ async function createJob(options) {
62
65
  throw error;
63
66
  }
64
67
  const { rules, problems } = planPermissions(options.plan.plan);
68
+ const proposed = proposeWriteScope(rules, options.root);
65
69
  const id = newJobId(options.now);
66
70
  const links = linkCandidates(options.root);
67
71
  const job = {
@@ -77,6 +81,7 @@ async function createJob(options) {
77
81
  planSession: options.plan.sessionId,
78
82
  model: options.modelId,
79
83
  allow: rules,
84
+ ...proposed.scope === void 0 ? {} : { writeScope: proposed.scope },
80
85
  onUnapproved: "deny-and-continue",
81
86
  ...options.at === void 0 ? {} : { at: options.at },
82
87
  maxSteps: options.maxSteps ?? DEFAULT_JOB_MAX_STEPS,
@@ -98,6 +103,7 @@ async function createJob(options) {
98
103
  ...problems.length === 0 ? [] : ["", "Not rules, left out:", ...problems.map((p) => ` ${p}`)],
99
104
  "",
100
105
  "Any other call that would ask is denied at run time; the job goes on and reports it.",
106
+ proposed.scope === void 0 ? `Write scope: the whole worktree (${proposed.reason}). Commands may write anywhere in it.` : `Write scope: commands (also the test command) and edits may change only ${proposed.scope.length === 0 ? "nothing in the worktree" : proposed.scope.join(", ")} (plus temp folders, caches and the links). The OS sandbox enforces it for commands; no command runs outside the sandbox. A test that writes outside the scope fails.`,
101
107
  job.network === void 0 ? "Network: none for commands." : `Network: ${job.network.join(", ")}, through Garuda's proxy; other hosts are denied and reported.`,
102
108
  "",
103
109
  options.test === void 0 ? 'Proof of work: no test command found (set "test" in the job file); a principal-engineer review of the diff (one model call).' : `Proof of work: \`${options.test}\` runs in the sandbox before and after the job; a principal-engineer review of the diff (one model call).`,
@@ -144,7 +150,7 @@ async function createJob(options) {
144
150
  await saveJob(job);
145
151
  const lines = [
146
152
  `Created job ${id}: ${jobPath(options.root, id)}`,
147
- 'You can edit its approval list ("allow") in that file before the run.'
153
+ 'You can edit its approval list ("allow") and its write scope ("writeScope") in that file before the run.'
148
154
  ];
149
155
  const platform = options.launchd?.platform ?? process.platform;
150
156
  if (options.launchd !== void 0 && options.at !== void 0 && platform === "darwin") {
@@ -221,4 +227,4 @@ export {
221
227
  createJob,
222
228
  switchTime
223
229
  };
224
- //# sourceMappingURL=create-VXIH2GUZ.js.map
230
+ //# sourceMappingURL=create-W6BV75QX.js.map
@@ -41,7 +41,8 @@ var DeadlineClient = class {
41
41
  try {
42
42
  const events = [];
43
43
  for await (const event of this.primary.stream(request, {
44
- signal: AbortSignal.any(signals)
44
+ signal: AbortSignal.any(signals),
45
+ ...options?.meter === void 0 ? {} : { meter: options.meter }
45
46
  })) {
46
47
  events.push(event);
47
48
  }
@@ -49,6 +50,7 @@ var DeadlineClient = class {
49
50
  yield* events;
50
51
  } catch (error) {
51
52
  if (options?.signal?.aborted === true || !timer.signal.aborted) throw error;
53
+ options?.meter?.retry();
52
54
  if (stepOnly) {
53
55
  this.calls.slow++;
54
56
  this.fallbackClient ??= await this.fallback();
@@ -83,4 +85,4 @@ export {
83
85
  DeadlineReached,
84
86
  StepTooSlow
85
87
  };
86
- //# sourceMappingURL=deadline-F5RJJ2QW.js.map
88
+ //# sourceMappingURL=deadline-TP7ZGLT2.js.map
@@ -5,28 +5,30 @@ import {
5
5
  import {
6
6
  CODE_INDEX_MODES,
7
7
  DEFAULT_CODE_INDEX_MODE,
8
- FileSessionStore,
9
8
  Runtime,
10
- detectProfiles,
11
- newSessionId
12
- } from "./chunk-TILFPYWO.js";
9
+ detectProfiles
10
+ } from "./chunk-2DYMJMRQ.js";
13
11
  import "./chunk-G2AX7MGS.js";
14
12
  import "./chunk-A2QAIRQL.js";
15
- import "./chunk-ULAJPMY7.js";
13
+ import {
14
+ FileSessionStore,
15
+ isCostIncomplete,
16
+ newSessionId
17
+ } from "./chunk-735BKABA.js";
16
18
  import {
17
19
  NO_MODEL,
18
20
  chooseModel,
19
21
  keepProviderKeys,
20
22
  loadModelsConfig,
21
23
  resolveModel
22
- } from "./chunk-NP6RIC25.js";
23
- import "./chunk-EIXUSVFX.js";
24
+ } from "./chunk-JRVWH27M.js";
25
+ import "./chunk-U65DQZDB.js";
24
26
  import {
25
27
  totalTokens
26
- } from "./chunk-R7I2NBPX.js";
28
+ } from "./chunk-J4346IDH.js";
27
29
  import "./chunk-B25VL4RU.js";
28
30
  import "./chunk-23FXBNBB.js";
29
- import "./chunk-VBZCNMUD.js";
31
+ import "./chunk-BSK5T2ZG.js";
30
32
  import "./chunk-5DEWP3YX.js";
31
33
  import "./chunk-ZAE25JM2.js";
32
34
  import "./chunk-ATW7BNIG.js";
@@ -34,17 +36,17 @@ import "./chunk-PZS3V6P3.js";
34
36
  import {
35
37
  LINK_CANDIDATES,
36
38
  cleanArgs
37
- } from "./chunk-44QN5SWZ.js";
39
+ } from "./chunk-55CFEEIN.js";
38
40
  import {
39
41
  MANIFEST,
40
42
  TEST_PATH,
41
43
  detectTestCommand,
42
44
  tail
43
- } from "./chunk-OUCV3FCT.js";
45
+ } from "./chunk-AOGFYUKG.js";
44
46
  import {
45
47
  git,
46
48
  shellWord
47
- } from "./chunk-TUUEBHG2.js";
49
+ } from "./chunk-6XHON3Q6.js";
48
50
  import {
49
51
  DEFAULT_ENV_ALLOWLIST,
50
52
  EXECUTOR_NAMES,
@@ -52,10 +54,11 @@ import {
52
54
  createExecutor,
53
55
  expandAllowlist,
54
56
  parseSettings
55
- } from "./chunk-GXZACJUI.js";
56
- import "./chunk-WMOLXTBU.js";
57
- import "./chunk-3EQU2GUX.js";
58
- import "./chunk-HUIK3WXO.js";
57
+ } from "./chunk-B4SJY7KT.js";
58
+ import "./chunk-BHZ6G65V.js";
59
+ import "./chunk-P6XWWIEK.js";
60
+ import "./chunk-UIRJOQQS.js";
61
+ import "./chunk-4LFJFDCF.js";
59
62
  import "./chunk-3NJNVZTK.js";
60
63
 
61
64
  // src/cli/evalCommand.ts
@@ -604,7 +607,9 @@ async function runEvalTask(task4, options) {
604
607
  const session = runtime.session;
605
608
  if (session !== void 0) {
606
609
  result.tokens = totalTokens(session.usage);
610
+ if (session.unreportedTokens > 0) result.unreportedTokens = session.unreportedTokens;
607
611
  result.costUsd = session.costUsd;
612
+ if (isCostIncomplete(session)) result.costIncomplete = true;
608
613
  result.cacheReadTokens = session.usage.cacheReadTokens;
609
614
  if (options.outDir !== void 0) {
610
615
  mkdirSync2(options.outDir, { recursive: true });
@@ -684,26 +689,41 @@ async function changedFiles(root, before) {
684
689
  function isErrorRun(result) {
685
690
  return result.stopReason === "error";
686
691
  }
692
+ function summarize(results) {
693
+ const attempted = results.length;
694
+ const errors = results.filter(isErrorRun).length;
695
+ const scored = attempted - errors;
696
+ const passed = results.filter((r) => r.passed).length;
697
+ return {
698
+ attempted,
699
+ errors,
700
+ scored,
701
+ passed,
702
+ ...scored === 0 ? {} : { scoredRate: passed / scored },
703
+ ...attempted === 0 ? {} : { attemptedRate: passed / attempted }
704
+ };
705
+ }
706
+ var percent = (rate) => rate === void 0 ? "\u2013" : `${Math.round(rate * 100)}%`;
687
707
  function formatReport(results) {
688
708
  const rows = results.map(
689
709
  (r) => [
690
710
  r.passed ? "PASS" : isErrorRun(r) ? "ERR " : "FAIL",
691
711
  r.id.padEnd(18),
692
712
  `${String(r.steps).padStart(3)} steps`,
693
- `${(r.tokens / 1e3).toFixed(1).padStart(7)}k tok`,
694
- r.costUsd === void 0 ? " cost ?" : `$${r.costUsd.toFixed(4).padStart(7)}`,
713
+ (r.unreportedTokens ?? 0) > 0 ? `tok ? (${r.unreportedTokens} reserved)` : `${(r.tokens / 1e3).toFixed(1).padStart(7)}k tok`,
714
+ r.costUsd === void 0 ? " cost ?" : `${r.costIncomplete === true ? "\u2265" : ""}$${r.costUsd.toFixed(4).padStart(7)}`,
695
715
  `${(r.durationMs / 1e3).toFixed(0).padStart(4)} s`,
696
716
  r.stopReason
697
717
  ].join(" ")
698
718
  );
699
- const passed = results.filter((r) => r.passed).length;
700
- const errors = results.filter(isErrorRun).length;
701
- const cost = results.every((r) => r.costUsd !== void 0) ? `$${results.reduce((s, r) => s + (r.costUsd ?? 0), 0).toFixed(4)}` : "unknown";
719
+ const summary = summarize(results);
720
+ const known = `$${results.reduce((s, r) => s + (r.costUsd ?? 0), 0).toFixed(4)}`;
721
+ const cost = !results.every((r) => r.costUsd !== void 0) ? "unknown" : results.some((r) => r.costIncomplete === true) ? `\u2265 ${known} (incomplete)` : known;
702
722
  const steps = results.reduce((s, r) => s + r.steps, 0);
703
- const counted = `${passed}/${results.length - errors} passed${errors > 0 ? ` \xB7 ${errors} error run(s) not counted` : ""}`;
723
+ const counted = `${summary.passed}/${summary.scored} scored runs passed (${percent(summary.scoredRate)}) \xB7 ${summary.passed}/${summary.attempted} of all attempted runs (${percent(summary.attemptedRate)})` + (summary.errors > 0 ? ` \xB7 ${summary.errors} error run(s)` : "");
704
724
  const tokens = results.reduce((s, r) => s + r.tokens, 0);
705
725
  const cached = results.reduce((s, r) => s + (r.cacheReadTokens ?? 0), 0);
706
- const share = tokens === 0 ? "" : ` \xB7 ${Math.round(cached / tokens * 100)}% of tokens from the cache`;
726
+ const share = results.some((r) => (r.unreportedTokens ?? 0) > 0) ? " \xB7 cache share unknown" : tokens === 0 ? "" : ` \xB7 ${Math.round(cached / tokens * 100)}% of tokens from the cache`;
707
727
  const lines4 = [...rows, "", `${counted} \xB7 ${steps} steps \xB7 cost ${cost}${share}`];
708
728
  const ids = [...new Set(results.map((r) => r.id))];
709
729
  if (ids.length < results.length) lines4.push("", "Mean per task:", ...meanRows(results, ids));
@@ -714,7 +734,8 @@ function meanRows(results, ids) {
714
734
  const all = results.filter((r) => r.id === id);
715
735
  const runs = all.filter((r) => !isErrorRun(r));
716
736
  const errors = all.length - runs.length;
717
- const note = errors > 0 ? ` (${errors} error run(s) left out)` : "";
737
+ const passesAll = all.filter((r) => r.passed).length;
738
+ const note = errors > 0 ? ` (${passesAll}/${all.length} of all attempts; ${errors} error run(s))` : "";
718
739
  if (runs.length === 0) return `${"0/0".padEnd(4)} ${id.padEnd(18)}${note}`;
719
740
  const mean = (f) => runs.reduce((s, r) => s + f(r), 0) / runs.length;
720
741
  const passes = runs.filter((r) => r.passed).length;
@@ -722,8 +743,8 @@ function meanRows(results, ids) {
722
743
  `${passes}/${runs.length}`.padEnd(4),
723
744
  id.padEnd(18),
724
745
  `${mean((r) => r.steps).toFixed(1).padStart(5)} steps`,
725
- `${(mean((r) => r.tokens) / 1e3).toFixed(1).padStart(7)}k tok`,
726
- runs.every((r) => r.costUsd !== void 0) ? `$${mean((r) => r.costUsd ?? 0).toFixed(4).padStart(7)}` : " cost ?"
746
+ runs.some((r) => (r.unreportedTokens ?? 0) > 0) ? " tok ?" : `${(mean((r) => r.tokens) / 1e3).toFixed(1).padStart(7)}k tok`,
747
+ runs.every((r) => r.costUsd !== void 0) ? `${runs.some((r) => r.costIncomplete === true) ? "\u2265" : ""}$${mean((r) => r.costUsd ?? 0).toFixed(4).padStart(7)}` : " cost ?"
727
748
  ].join(" ") + note;
728
749
  });
729
750
  }
@@ -2542,7 +2563,7 @@ async function runEvalCommand(options) {
2542
2563
  return 1;
2543
2564
  }
2544
2565
  }
2545
- const { assertModelAllowedByPolicy, loadTeamPolicy } = await import("./policy-EJPLEEXP.js");
2566
+ const { assertModelAllowedByPolicy, loadTeamPolicy } = await import("./policy-ZJ7RMOFN.js");
2546
2567
  let team;
2547
2568
  try {
2548
2569
  team = await loadTeamPolicy();
@@ -2677,7 +2698,7 @@ ${where}, in scratch folders.
2677
2698
  Wall time: ${Math.round(wallMs / 1e3)} s${parallel > 1 ? ` (${parallel} tasks at a time)` : ""}.`;
2678
2699
  writeFileSync(
2679
2700
  join5(outDir, "report.json"),
2680
- `${JSON.stringify({ model: modelId, suite, repeat, codeIndex: index, subagents: subagentsMode, subagentModel: sub?.spec, todo: todoMode, lsp: lspMode, keepThinking: thinkingMode, format: formatMode, network: network ?? "off", batch: batchMode, parallel, wallMs, executor: executor.name, results }, null, 2)}
2701
+ `${JSON.stringify({ model: modelId, suite, repeat, codeIndex: index, subagents: subagentsMode, subagentModel: sub?.spec, todo: todoMode, lsp: lspMode, keepThinking: thinkingMode, format: formatMode, network: network ?? "off", batch: batchMode, parallel, wallMs, executor: executor.name, summary: summarize(results), results }, null, 2)}
2681
2702
  `
2682
2703
  );
2683
2704
  process.stdout.write(`
@@ -2809,4 +2830,4 @@ export {
2809
2830
  BATCH_TASK_TIMEOUT_MS,
2810
2831
  runEvalCommand
2811
2832
  };
2812
- //# sourceMappingURL=evalCommand-RGMZRYDR.js.map
2833
+ //# sourceMappingURL=evalCommand-YVQAWUZE.js.map
@@ -11,7 +11,7 @@ import {
11
11
  PathOutsideRootError,
12
12
  displayPath,
13
13
  resolveInRoot
14
- } from "./chunk-WMOLXTBU.js";
14
+ } from "./chunk-4LFJFDCF.js";
15
15
 
16
16
  // src/cli/export.ts
17
17
  import { mkdir, writeFile } from "fs/promises";
@@ -155,4 +155,4 @@ export {
155
155
  sessionMarkdown,
156
156
  writeExport
157
157
  };
158
- //# sourceMappingURL=export-HN3CT7AV.js.map
158
+ //# sourceMappingURL=export-SJ3DUP6W.js.map