@iowarp/clio-coder 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +3 -3
  4. package/dist/{acp-P2AQILE2.js → acp-S5R4RR5B.js} +7 -6
  5. package/dist/{agents-72W3BI7I.js → agents-P6DMMVZY.js} +24 -21
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5TWEIYDN.js → auth-2XCZLPKS.js} +12 -8
  8. package/dist/{chunk-GGXXDWE4.js → chunk-22NAGB7X.js} +2 -2
  9. package/dist/{chunk-2DJ2KNFG.js → chunk-2LZI5CAG.js} +133 -13
  10. package/dist/{chunk-OAO4GE4M.js → chunk-2TZWSW76.js} +2 -2
  11. package/dist/{chunk-OOJYHWRB.js → chunk-34475P3I.js} +2 -2
  12. package/dist/{chunk-4XUGQOHA.js → chunk-35MKKU5R.js} +4 -4
  13. package/dist/{chunk-LM5TQCJZ.js → chunk-3HZ5RWN2.js} +5 -5
  14. package/dist/{chunk-AGYYIBLL.js → chunk-3JLKSKD7.js} +2 -2
  15. package/dist/{chunk-KZWTDYJF.js → chunk-4JUF2NNX.js} +7 -7
  16. package/dist/{chunk-ZDOOVTXZ.js → chunk-4OC57DA6.js} +27 -4
  17. package/dist/chunk-5M54SPOL.js +926 -0
  18. package/dist/{chunk-STBPMHSX.js → chunk-7RXG6QRZ.js} +51 -11
  19. package/dist/{chunk-X6IAEBZR.js → chunk-A2GZF7DC.js} +5 -5
  20. package/dist/{chunk-A3CYT5EX.js → chunk-AD2SYQYC.js} +55 -2
  21. package/dist/chunk-AOCYTWAV.js +449 -0
  22. package/dist/chunk-BEY543CS.js +258 -0
  23. package/dist/{chunk-6N5PTWMY.js → chunk-BP4OYD6A.js} +32 -13
  24. package/dist/chunk-BPGS2WCQ.js +612 -0
  25. package/dist/{chunk-V6RTAOC2.js → chunk-BRXQQJFP.js} +8 -8
  26. package/dist/chunk-CFGTUFWB.js +67 -0
  27. package/dist/{chunk-CBCAPZAA.js → chunk-E25LMLRW.js} +2 -2
  28. package/dist/{chunk-DUYJ5IO6.js → chunk-EDRHSCIE.js} +4 -4
  29. package/dist/{chunk-XBXAASKX.js → chunk-EFADSJET.js} +2 -2
  30. package/dist/{chunk-M6SHUN7Q.js → chunk-FO5ZOVUY.js} +2 -2
  31. package/dist/chunk-FYYLNIL5.js +313 -0
  32. package/dist/{chunk-FNTMWMX5.js → chunk-HV5X7OR2.js} +14 -12
  33. package/dist/{chunk-TZTZS7QK.js → chunk-HXG4IURW.js} +5 -3
  34. package/dist/{chunk-5UFT4SUX.js → chunk-K6WL7QZT.js} +3 -3
  35. package/dist/chunk-K7VKOLQQ.js +15 -0
  36. package/dist/{chunk-BMEMKKIT.js → chunk-KOHPCX4K.js} +2 -2
  37. package/dist/{chunk-OC7FIQPC.js → chunk-KRPY7NTG.js} +10 -7
  38. package/dist/chunk-LL4KHSZI.js +22 -0
  39. package/dist/{chunk-DSELYM6W.js → chunk-MEQ45TQ4.js} +15 -9
  40. package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
  41. package/dist/{chunk-6SGHMWE3.js → chunk-N4CZJQRK.js} +5 -5
  42. package/dist/{chunk-TZK7PACC.js → chunk-NILBFAPG.js} +14 -8
  43. package/dist/chunk-OZNBF4L3.js +23 -0
  44. package/dist/{verify-375KUB3Y.js → chunk-PCZJO5TI.js} +127 -42
  45. package/dist/chunk-QQK64KLB.js +1360 -0
  46. package/dist/{chunk-SRDMMSEP.js → chunk-QQL5RT5M.js} +979 -1619
  47. package/dist/{chunk-LZSJBIVT.js → chunk-QWU7ZBO7.js} +70 -720
  48. package/dist/{chunk-2TLUCQVG.js → chunk-RD5U66HV.js} +3 -3
  49. package/dist/{chunk-OKGUZO2U.js → chunk-SPULKLCF.js} +4 -3
  50. package/dist/{chunk-OQ33BKR3.js → chunk-TTNYS3EA.js} +3 -60
  51. package/dist/chunk-TW3WDMVS.js +677 -0
  52. package/dist/chunk-TZSKNMZG.js +434 -0
  53. package/dist/{chunk-7MNJORFF.js → chunk-UL3WSD3F.js} +6 -1
  54. package/dist/{chunk-PIWWS5BL.js → chunk-UZHIZC5S.js} +7 -7
  55. package/dist/{chunk-UFIIWP2H.js → chunk-VAWWTKDP.js} +8 -8
  56. package/dist/{chunk-COU2UHX6.js → chunk-VEZEGCGW.js} +170 -2
  57. package/dist/{chunk-LW6DSM3M.js → chunk-VMNQ6OZA.js} +98 -202
  58. package/dist/chunk-VSNATDE6.js +122 -0
  59. package/dist/chunk-W6GROXXM.js +69 -0
  60. package/dist/chunk-WPQLXFOZ.js +375 -0
  61. package/dist/{chunk-ORBHGJC5.js → chunk-WR67VIZY.js} +3 -3
  62. package/dist/{chunk-PAJK6MAQ.js → chunk-X6COSD2O.js} +5 -5
  63. package/dist/chunk-ZGVHUX3M.js +66 -0
  64. package/dist/{chunk-LWLEKMDQ.js → chunk-ZYKPLLNQ.js} +510 -547
  65. package/dist/cli/index.js +27 -23
  66. package/dist/{clio-JOU4FXVA.js → clio-J5JIOIDS.js} +7 -6
  67. package/dist/{code-nav-7AX6FYE6.js → code-nav-AXCXSBHX.js} +5 -3
  68. package/dist/{config-XCDVKR23.js → config-OEBMIN2U.js} +37 -27
  69. package/dist/{configure-4GAP54ZW.js → configure-PUQOSIXQ.js} +16 -13
  70. package/dist/{context-5VKGUVJJ.js → context-EKDCKUUZ.js} +82 -7
  71. package/dist/{context-4UOGGLQ5.js → context-MGSE4Z2T.js} +33 -23
  72. package/dist/{context-77FM5DV5.js → context-URSXPBCK.js} +17 -9
  73. package/dist/{context-clear-XXJRLCJJ.js → context-clear-KDAJRNUK.js} +33 -23
  74. package/dist/context-working-set-SBKMPPI2.js +1552 -0
  75. package/dist/{dispatch-runner-QPRDDBDX.js → dispatch-runner-MSWN72NK.js} +43 -29
  76. package/dist/{doctor-HR46URBJ.js → doctor-7BSE27PJ.js} +10 -10
  77. package/dist/{eval-XSSNATB4.js → eval-IZGDOO4H.js} +9 -8
  78. package/dist/{evidence-6HG2PY2B.js → evidence-SR7WXB5B.js} +51 -23
  79. package/dist/{evolve-K7YU3NCY.js → evolve-K7VE2CBX.js} +30 -20
  80. package/dist/{fleet-VY3HHKN6.js → fleet-7XMJNQNF.js} +48 -38
  81. package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-AQNAH644.js} +3 -3
  82. package/dist/{init-JYGXI3FK.js → init-JGNPAYXT.js} +41 -31
  83. package/dist/{memory-WFZMGYHX.js → memory-4ALKDJ4Q.js} +32 -22
  84. package/dist/{models-I5QWSEOM.js → models-ZMMLFJNN.js} +22 -19
  85. package/dist/{monitor-GE4ID3IA.js → monitor-2F3T5KHP.js} +55 -43
  86. package/dist/{orchestrator-EM5MC3HM.js → orchestrator-ORHT43JB.js} +572 -388
  87. package/dist/{reset-L2FQEE3E.js → reset-NXGTYNUO.js} +4 -3
  88. package/dist/{run-ZU3QMZPZ.js → run-RF4WJGMT.js} +51 -41
  89. package/dist/{share-S5BZQC5I.js → share-UT3W6E4M.js} +5 -4
  90. package/dist/{skills-X5VXCRNQ.js → skills-PSACKC5Q.js} +2 -2
  91. package/dist/{skills-eval-WKIHWTHR.js → skills-eval-WJSI55RZ.js} +34 -24
  92. package/dist/{targets-SNCPI2NR.js → targets-PIIRAOYS.js} +23 -20
  93. package/dist/{terminal-lease-BNAHVHBS.js → terminal-lease-ULWXWNVY.js} +4 -3
  94. package/dist/{upgrade-JQHHPQ4K.js → upgrade-346TZ6AV.js} +18 -17
  95. package/dist/{usage-OR4O5SMZ.js → usage-6KKXR32N.js} +34 -24
  96. package/dist/verifiers-4UUM6TEE.js +1214 -0
  97. package/dist/verify-X5HDROLA.js +25 -0
  98. package/dist/{wiki-generate-UEXP2ARI.js → wiki-generate-7STOCIFZ.js} +42 -31
  99. package/dist/worker/entry.js +33 -24
  100. package/docs/README.md +7 -6
  101. package/docs/acp.md +1 -1
  102. package/docs/alcf-provider.md +1 -1
  103. package/docs/architecture.md +2 -2
  104. package/docs/artifact-versions.md +1 -1
  105. package/docs/built-in-agents.md +1 -1
  106. package/docs/capacity-and-scheduling.md +1 -1
  107. package/docs/commands-and-modes.md +53 -21
  108. package/docs/config-knobs-audit.md +1 -2
  109. package/docs/configuration-and-targets.md +15 -1
  110. package/docs/context-engine.md +64 -12
  111. package/docs/context-working-set.md +194 -0
  112. package/docs/development-pipeline.md +1 -1
  113. package/docs/documentation-coverage.md +5 -5
  114. package/docs/documentation-guide.md +6 -5
  115. package/docs/environment-variables.md +2 -1
  116. package/docs/eval-runner.md +1 -1
  117. package/docs/evals-internal.md +14 -1
  118. package/docs/evidence-and-memory.md +74 -2
  119. package/docs/evolution.md +1 -1
  120. package/docs/exit-codes-and-output.md +1 -1
  121. package/docs/extensions-and-sharing.md +2 -2
  122. package/docs/fleet-dispatch.md +22 -7
  123. package/docs/glossary.md +21 -1
  124. package/docs/installation-and-lifecycle.md +2 -2
  125. package/docs/middleware-and-components.md +1 -1
  126. package/docs/model-catalog.md +7 -9
  127. package/docs/observability.md +4 -4
  128. package/docs/proactive-memory.md +1 -1
  129. package/docs/prompt-envelope-and-tools.md +4 -4
  130. package/docs/provider-adapter-cookbook.md +1 -1
  131. package/docs/release-cut-checklist.md +31 -31
  132. package/docs/safety-model.md +23 -4
  133. package/docs/scientific-validation.md +21 -3
  134. package/docs/session-lifecycle.md +3 -3
  135. package/docs/skills-marketplace.md +1 -1
  136. package/docs/tool-usage.md +79 -12
  137. package/docs/trace-store.md +1 -1
  138. package/docs/troubleshooting.md +1 -1
  139. package/docs/tui-design.md +1 -1
  140. package/docs/worker-dispatch-mechanics.md +11 -1
  141. package/package.json +8 -11
  142. package/skills/meta/clio-test/SKILL.md +20 -17
  143. package/skills/meta/clio-test/evals.md +3 -3
  144. package/skills/meta/clio-test/references/harness.md +35 -6
  145. package/skills/meta/clio-test/references/test-map.md +20 -10
  146. package/skills/registry.yaml +2 -2
  147. package/skills/skill-marketplace.json +1 -1
  148. package/src/cli/context-working-set.ts +513 -0
  149. package/src/cli/context.ts +8 -0
  150. package/src/cli/evidence.ts +20 -2
  151. package/src/cli/index.ts +4 -0
  152. package/src/cli/verifiers.ts +325 -0
  153. package/src/core/bash-exec.ts +39 -14
  154. package/src/core/bus-events.ts +19 -4
  155. package/src/core/config.ts +54 -0
  156. package/src/core/defaults.ts +50 -3
  157. package/src/core/verification-scripts.ts +6 -0
  158. package/src/domains/agents/builtins/verifier.md +3 -0
  159. package/src/domains/config/classify.ts +1 -0
  160. package/src/domains/context/working-set/contract.ts +161 -0
  161. package/src/domains/context/working-set/defaults.ts +28 -0
  162. package/src/domains/context/working-set/engine.ts +203 -0
  163. package/src/domains/context/working-set/fold.ts +62 -0
  164. package/src/domains/context/working-set/horizon.ts +38 -0
  165. package/src/domains/context/working-set/marker.ts +103 -0
  166. package/src/domains/context/working-set/path-index.ts +436 -0
  167. package/src/domains/context/working-set/payload.ts +152 -0
  168. package/src/domains/context/working-set/policies/age-horizon.ts +55 -0
  169. package/src/domains/context/working-set/policies/index.ts +21 -0
  170. package/src/domains/context/working-set/policies/structural.ts +160 -0
  171. package/src/domains/context/working-set/project.ts +132 -0
  172. package/src/domains/context/working-set/protect.ts +109 -0
  173. package/src/domains/context/working-set/recall.ts +177 -0
  174. package/src/domains/context/working-set/replay/controls.ts +112 -0
  175. package/src/domains/context/working-set/replay/load-clio.ts +199 -0
  176. package/src/domains/context/working-set/replay/metrics.ts +185 -0
  177. package/src/domains/context/working-set/replay/reference-graph.ts +79 -0
  178. package/src/domains/context/working-set/replay/report.ts +139 -0
  179. package/src/domains/context/working-set/replay/runner.ts +325 -0
  180. package/src/domains/context/working-set/replay/synthetic.ts +422 -0
  181. package/src/domains/context/working-set/replay/trace.ts +21 -0
  182. package/src/domains/context/working-set/visible.ts +54 -0
  183. package/src/domains/evidence/build.ts +112 -45
  184. package/src/domains/evidence/eval.ts +24 -7
  185. package/src/domains/evidence/index.ts +53 -0
  186. package/src/domains/evidence/ordering.ts +12 -0
  187. package/src/domains/evidence/run-trust.ts +221 -0
  188. package/src/domains/evidence/store.ts +46 -6
  189. package/src/domains/evidence/trust-status.ts +854 -0
  190. package/src/domains/evidence/types.ts +26 -0
  191. package/src/domains/middleware/memory-intervention.ts +3 -0
  192. package/src/domains/middleware/stalled-turn.ts +165 -4
  193. package/src/domains/safety/autonomy.ts +1 -1
  194. package/src/domains/safety/default-path-policy.ts +8 -0
  195. package/src/domains/safety/finish-contract.ts +4 -3
  196. package/src/domains/safety/policy-engine.ts +48 -6
  197. package/src/domains/session/compaction/compact.ts +23 -1
  198. package/src/domains/session/compaction/cut-point.ts +2 -0
  199. package/src/domains/session/compaction/tokens.ts +16 -1
  200. package/src/domains/session/context-ledger.ts +2 -0
  201. package/src/domains/session/entries.ts +107 -1
  202. package/src/domains/session/manager.ts +9 -2
  203. package/src/domains/session/migrations/index.ts +22 -3
  204. package/src/engine/acp/server.ts +3 -0
  205. package/src/engine/agent.ts +18 -1
  206. package/src/engine/session.ts +9 -3
  207. package/src/entry/orchestrator.ts +16 -4
  208. package/src/interactive/chat-loop-messages.ts +18 -6
  209. package/src/interactive/chat-panel.ts +17 -1
  210. package/src/interactive/chat-renderer.ts +30 -21
  211. package/src/interactive/context-meter.ts +10 -0
  212. package/src/interactive/context-overlay.ts +81 -6
  213. package/src/interactive/context-recall-command.ts +110 -0
  214. package/src/interactive/interactive-slash-runtime.ts +37 -1
  215. package/src/interactive/model-session-replay.ts +21 -0
  216. package/src/interactive/overlay-general-openers.ts +6 -0
  217. package/src/interactive/overlay-session-lifecycle.ts +8 -4
  218. package/src/interactive/renderers/tool-execution.ts +18 -2
  219. package/src/interactive/session-transcript.ts +2 -2
  220. package/src/interactive/slash-commands.ts +29 -2
  221. package/src/interactive/turn-context.ts +238 -88
  222. package/src/interactive/turn-middleware.ts +6 -6
  223. package/src/tools/agent-tools.ts +11 -4
  224. package/src/tools/bash.ts +144 -82
  225. package/src/tools/builtin-tool-catalog.ts +11 -5
  226. package/src/tools/context/index.ts +105 -3
  227. package/src/tools/context/surface.ts +3 -2
  228. package/src/tools/core-bootstrap.ts +21 -0
  229. package/src/tools/dispatch-runner.ts +9 -7
  230. package/src/tools/monitor.ts +28 -20
  231. package/src/tools/registry.ts +59 -7
  232. package/src/tools/result-disposition.ts +550 -0
  233. package/src/tools/result-shaping.ts +262 -19
  234. package/src/tools/safe-exec.ts +2 -0
  235. package/src/tools/verify/authoring.ts +1119 -0
  236. package/src/tools/verify/catalog.ts +346 -0
  237. package/src/tools/verify/index.ts +13 -3
  238. package/src/tools/verify/scripts.ts +135 -37
  239. package/src/tools/verify/surface.ts +9 -5
  240. package/src/tools/worker-evidence.ts +35 -12
  241. package/dist/chunk-J7CWMCQD.js +0 -255
  242. package/dist/chunk-T6YILFSB.js +0 -80
  243. package/dist/chunk-VAKQQHWR.js +0 -434
  244. package/dist/chunk-VPAYEGVX.js +0 -184
@@ -0,0 +1,926 @@
1
+ import { createRequire as __clioCreateRequire } from "node:module"; const require = __clioCreateRequire(import.meta.url);
2
+ import {
3
+ formatSize,
4
+ truncateHead,
5
+ truncateTail
6
+ } from "./chunk-OZNBF4L3.js";
7
+ import {
8
+ createRedactionTally,
9
+ redactSecretsText
10
+ } from "./chunk-4BJ5BYCE.js";
11
+ import {
12
+ byteLength,
13
+ truncateUtf8
14
+ } from "./chunk-6XLNIQDB.js";
15
+ import {
16
+ GUARDRAIL_DEFAULTS,
17
+ GUARDRAIL_ENV_VARS,
18
+ resolveGuardrail
19
+ } from "./chunk-4ZG3XFUR.js";
20
+ import {
21
+ clioStateDir
22
+ } from "./chunk-BNAZZHFG.js";
23
+ import {
24
+ init_esm_shims
25
+ } from "./chunk-3R73A4XB.js";
26
+
27
+ // src/tools/truncate.ts
28
+ init_esm_shims();
29
+ var DEFAULT_MAX_LINES = 2e3;
30
+ var DEFAULT_MAX_BYTES = 16 * 1024;
31
+ function splitLinesForCounting(content) {
32
+ if (content.length === 0) return [];
33
+ const lines = content.split("\n");
34
+ if (content.endsWith("\n")) lines.pop();
35
+ return lines;
36
+ }
37
+ function withClioDefaults(options) {
38
+ return {
39
+ maxLines: options.maxLines ?? DEFAULT_MAX_LINES,
40
+ maxBytes: options.maxBytes ?? DEFAULT_MAX_BYTES
41
+ };
42
+ }
43
+ function truncateHead2(content, options = {}) {
44
+ return truncateHead(content, withClioDefaults(options));
45
+ }
46
+ function truncateTail2(content, options = {}) {
47
+ return truncateTail(content, withClioDefaults(options));
48
+ }
49
+
50
+ // src/tools/observation.ts
51
+ init_esm_shims();
52
+
53
+ // src/tools/result-shaping.ts
54
+ init_esm_shims();
55
+ import { createHash as createHash2 } from "node:crypto";
56
+ import { mkdirSync, writeFileSync } from "node:fs";
57
+ import { isAbsolute, join, relative, resolve } from "node:path";
58
+
59
+ // src/tools/result-disposition.ts
60
+ init_esm_shims();
61
+ import { createHash } from "node:crypto";
62
+ var ESSENTIAL_DETAIL_KEYS = /* @__PURE__ */ new Set([
63
+ "aborted",
64
+ "continuation",
65
+ "continuationHint",
66
+ "decision",
67
+ "error",
68
+ "errors",
69
+ "evidence",
70
+ "exitCode",
71
+ "exitStatus",
72
+ "outputBytes",
73
+ "outputCapped",
74
+ "next",
75
+ "outcome",
76
+ "safety",
77
+ "safetyDecision",
78
+ "status",
79
+ "signal",
80
+ "terminate",
81
+ "timedOut",
82
+ "retainedBytes",
83
+ "stderrBytes",
84
+ "stdoutBytes"
85
+ ]);
86
+ function positiveByteCap(value, fallback) {
87
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
88
+ }
89
+ function normalizeToolResultDisposition(spec, hardMaxBytes, requested = spec.metadata?.resultDisposition) {
90
+ const declared = requested;
91
+ if (declared === void 0) return null;
92
+ const hardCap = positiveByteCap(hardMaxBytes, 1);
93
+ const presentationCap = Math.min(positiveByteCap(declared.presentation.maxBytes, hardCap), hardCap);
94
+ const contextCap = Math.min(positiveByteCap(declared.context.maxBytes, hardCap), hardCap);
95
+ return {
96
+ presentation: {
97
+ foldDefault: declared.presentation.foldDefault,
98
+ showDiffWhenFolded: declared.presentation.showDiffWhenFolded,
99
+ failureExcerpt: declared.presentation.failureExcerpt,
100
+ maxBytes: presentationCap,
101
+ ...declared.presentation.overflow === void 0 ? {} : { overflow: declared.presentation.overflow }
102
+ },
103
+ context: { ...declared.context, maxBytes: contextCap },
104
+ ...declared.fallback === void 0 ? {} : { fallback: declared.fallback }
105
+ };
106
+ }
107
+ function isRecord(value) {
108
+ return value !== null && typeof value === "object" && !Array.isArray(value);
109
+ }
110
+ function stableValue(value, seen) {
111
+ if (value === null || typeof value !== "object") return value;
112
+ if (seen.has(value)) return "[circular]";
113
+ seen.add(value);
114
+ if (Array.isArray(value)) {
115
+ const items = value.map((item) => stableValue(item, seen));
116
+ seen.delete(value);
117
+ return items;
118
+ }
119
+ const record = value;
120
+ const out = {};
121
+ for (const key of Object.keys(record).sort()) out[key] = stableValue(record[key], seen);
122
+ seen.delete(value);
123
+ return out;
124
+ }
125
+ function stableJson(value) {
126
+ try {
127
+ return JSON.stringify(stableValue(value, /* @__PURE__ */ new Set())) ?? "null";
128
+ } catch {
129
+ return JSON.stringify(String(value));
130
+ }
131
+ }
132
+ function essentialFacts(details) {
133
+ if (details === void 0) return null;
134
+ const facts = {};
135
+ for (const key of Object.keys(details).sort()) {
136
+ if (ESSENTIAL_DETAIL_KEYS.has(key)) facts[key] = details[key];
137
+ }
138
+ const observation = isRecord(details.observation) ? details.observation : null;
139
+ if (observation !== null && observation.next !== void 0) facts.next = observation.next;
140
+ return Object.keys(facts).length > 0 ? facts : null;
141
+ }
142
+ function utf8Prefix(text, maxBytes) {
143
+ if (maxBytes <= 0) return "";
144
+ const buffer = Buffer.from(text, "utf8");
145
+ if (buffer.byteLength <= maxBytes) return text;
146
+ let end = Math.min(maxBytes, buffer.byteLength);
147
+ while (end > 0 && end < buffer.byteLength && (buffer[end] ?? 0) >>> 6 === 2) end -= 1;
148
+ return buffer.subarray(0, end).toString("utf8");
149
+ }
150
+ function utf8Suffix(text, maxBytes) {
151
+ if (maxBytes <= 0) return "";
152
+ const buffer = Buffer.from(text, "utf8");
153
+ if (buffer.byteLength <= maxBytes) return text;
154
+ let start = Math.max(0, buffer.byteLength - maxBytes);
155
+ while (start < buffer.byteLength && (buffer[start] ?? 0) >>> 6 === 2) start += 1;
156
+ return buffer.subarray(start).toString("utf8");
157
+ }
158
+ function capUtf8(text, maxBytes) {
159
+ return byteLength(text) <= maxBytes ? text : utf8Prefix(text, maxBytes);
160
+ }
161
+ function boundedToolResultExcerpt(text, maxBytes, bias = "head-tail") {
162
+ if (maxBytes <= 0) return "";
163
+ if (byteLength(text) <= maxBytes) return text;
164
+ if (bias === "tail") return utf8Suffix(text, maxBytes);
165
+ const marker = "\n[\u2026 omitted \u2026]\n";
166
+ const markerBytes = byteLength(marker);
167
+ if (markerBytes >= maxBytes) return utf8Prefix(text, maxBytes);
168
+ const bodyBudget = maxBytes - markerBytes;
169
+ const headBudget = Math.floor(bodyBudget / 2);
170
+ const tailBudget = bodyBudget - headBudget;
171
+ return `${utf8Prefix(text, headBudget)}${marker}${utf8Suffix(text, tailBudget)}`;
172
+ }
173
+ function sourceLineCount(text) {
174
+ if (text.length === 0) return 0;
175
+ return text.replace(/\r\n/gu, "\n").replace(/\r/gu, "\n").split("\n").length;
176
+ }
177
+ function summaryProvenance(text, algorithm = "sha256-head-tail-v1", redactions = 0) {
178
+ return {
179
+ producer: "code",
180
+ algorithm,
181
+ sourceSha256: createHash("sha256").update(text).digest("hex"),
182
+ sourceLines: sourceLineCount(text),
183
+ ...redactions > 0 ? { redactions } : {}
184
+ };
185
+ }
186
+ var ERROR_LIKE_LINE = /(?:\b(?:abort(?:ed)?|assert(?:ion)?|error|exception|fail(?:ed|ure)?|fatal|killed|panic|signal|timed?\s*out|timeout)\b|\bnot ok\b|\bERR!\b)/iu;
187
+ var SUMMARY_LINE_MAX_BYTES = 768;
188
+ function summaryLine(line) {
189
+ return boundedToolResultExcerpt(line, SUMMARY_LINE_MAX_BYTES);
190
+ }
191
+ function normalizeNewlines(text) {
192
+ return text.replace(/\r\n/gu, "\n").replace(/\r/gu, "\n");
193
+ }
194
+ function contentLines(text) {
195
+ return normalizeNewlines(text).split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
196
+ }
197
+ function deterministicHeadTailSummary(text, redact = true) {
198
+ const tally = createRedactionTally();
199
+ const safeText = redact ? redactSecretsText(text, tally) : text;
200
+ return { text: contentLines(safeText).join("\n"), redactions: tally.count, complete: tally.count === 0 };
201
+ }
202
+ function renderSummarySection(label, lines) {
203
+ return lines.length === 0 ? "" : `[${label}]
204
+ ${lines.map(summaryLine).join("\n")}`;
205
+ }
206
+ function deterministicDiagnosticSummary(text, maxBytes, redact = true) {
207
+ if (maxBytes <= 0 || text.length === 0) return { text: "", redactions: 0, complete: text.length === 0 };
208
+ const tally = createRedactionTally();
209
+ const safeText = redact ? redactSecretsText(text, tally) : text;
210
+ const lines = normalizeNewlines(safeText).split("\n");
211
+ const nonempty = lines.map((line, index) => ({ line: line.trim(), index })).filter((entry) => entry.line.length > 0);
212
+ if (nonempty.length === 0) return { text: "", redactions: tally.count, complete: tally.count === 0 };
213
+ if (nonempty.length === 1) {
214
+ const only = nonempty[0]?.line ?? "";
215
+ return {
216
+ text: boundedToolResultExcerpt(only, maxBytes),
217
+ redactions: tally.count,
218
+ complete: tally.count === 0 && byteLength(only) <= maxBytes
219
+ };
220
+ }
221
+ const head = nonempty.slice(0, 4);
222
+ const tail = nonempty.slice(Math.max(head.length, nonempty.length - 6));
223
+ const edgeIndexes = new Set([...head, ...tail].map((entry) => entry.index));
224
+ const diagnostics = nonempty.filter((entry) => !edgeIndexes.has(entry.index) && ERROR_LIKE_LINE.test(entry.line));
225
+ const sections = [
226
+ renderSummarySection(
227
+ "diagnostic head",
228
+ head.map((entry) => entry.line)
229
+ ),
230
+ renderSummarySection(
231
+ "error-like lines",
232
+ diagnostics.map((entry) => entry.line)
233
+ ),
234
+ renderSummarySection(
235
+ "diagnostic tail",
236
+ tail.map((entry) => entry.line)
237
+ )
238
+ ].filter((section) => section.length > 0);
239
+ const selected = [...head, ...diagnostics, ...tail];
240
+ const keepsEveryLine = selected.length === nonempty.length && selected.every((entry) => byteLength(entry.line) <= SUMMARY_LINE_MAX_BYTES);
241
+ const rendered = keepsEveryLine ? selected.map((entry) => summaryLine(entry.line)).join("\n") : sections.join("\n");
242
+ if (byteLength(rendered) <= maxBytes) {
243
+ return { text: rendered, redactions: tally.count, complete: keepsEveryLine && tally.count === 0 };
244
+ }
245
+ const weights = diagnostics.length > 0 ? [0.25, 0.45, 0.3] : [0.4, 0, 0.6];
246
+ const boundedSections = [];
247
+ for (const [index, section] of sections.entries()) {
248
+ const weight = diagnostics.length > 0 ? weights[index] ?? 0 : index === 0 ? 0.4 : 0.6;
249
+ const budget = Math.floor(maxBytes * weight);
250
+ if (budget > 0) boundedSections.push(boundedToolResultExcerpt(section ?? "", budget));
251
+ }
252
+ return { text: capUtf8(boundedSections.join("\n"), maxBytes), redactions: tally.count, complete: false };
253
+ }
254
+ function retrievalLine(offloadPath, followUpHint2) {
255
+ return offloadPath === null ? `retrieve=${JSON.stringify(followUpHint2)}` : `retrieve=${JSON.stringify(`read ${offloadPath} with offset and limit`)}`;
256
+ }
257
+ function contextHeader(input, appliedMode, truncated, provenance) {
258
+ const lines = [
259
+ `[tool-result ${appliedMode}]`,
260
+ `kind=${input.kind} capturedBytes=${input.capturedBytes} displayedBytes=${input.displayedBytes} truncated=${String(truncated)}`,
261
+ retrievalLine(input.offloadPath, input.followUpHint),
262
+ `followUp=${JSON.stringify(input.followUpHint)}`
263
+ ];
264
+ if (provenance !== void 0) {
265
+ lines.push(`summary=code/${provenance.algorithm} sha256=${provenance.sourceSha256} lines=${provenance.sourceLines}`);
266
+ }
267
+ if (input.disposition.fallback !== void 0) {
268
+ lines.push(`fallback=${input.disposition.fallback.reason} ${JSON.stringify(input.disposition.fallback.message)}`);
269
+ }
270
+ const facts = essentialFacts(input.details);
271
+ if (facts !== null) lines.push(`facts=${stableJson(facts)}`);
272
+ return lines.join("\n");
273
+ }
274
+ function joinHeaderAndBody(header, body, maxBytes, bias = "head-tail") {
275
+ if (body.length === 0) return capUtf8(header, maxBytes);
276
+ const separator = "\n";
277
+ const headerBytes = byteLength(header);
278
+ const separatorBytes = byteLength(separator);
279
+ if (headerBytes + separatorBytes >= maxBytes) return capUtf8(header, maxBytes);
280
+ const bodyBudget = maxBytes - headerBytes - separatorBytes;
281
+ return `${header}${separator}${boundedToolResultExcerpt(body, bodyBudget, bias)}`;
282
+ }
283
+ function fullTrailer(input) {
284
+ const facts = essentialFacts(input.details);
285
+ const lines = [];
286
+ if (facts !== null) lines.push(`facts=${stableJson(facts)}`);
287
+ if (input.followUpHint.length > 0) lines.push(`followUp=${JSON.stringify(input.followUpHint)}`);
288
+ return lines.length === 0 ? "" : `[tool-result metadata]
289
+ ${lines.join("\n")}`;
290
+ }
291
+ var NUL = "\0";
292
+ function stripNulForContext(text) {
293
+ const parts = text.split(NUL);
294
+ return parts.length === 1 ? { text, stripped: 0 } : { text: parts.join(""), stripped: parts.length - 1 };
295
+ }
296
+ function projectToolResultContext(input) {
297
+ const requestedMode = input.disposition.context.mode;
298
+ const maxBytes = input.disposition.context.maxBytes;
299
+ const sanitized = stripNulForContext(input.text);
300
+ const body = sanitized.text;
301
+ const capturedTextIsComplete = input.capturedBytes <= byteLength(input.text) && sanitized.stripped === 0;
302
+ if (requestedMode === "full") {
303
+ const trailer = fullTrailer(input);
304
+ const candidate = trailer.length === 0 ? body : `${body}
305
+
306
+ ${trailer}`;
307
+ if (capturedTextIsComplete && byteLength(candidate) <= maxBytes) {
308
+ return { text: candidate, appliedMode: "full", truncated: false };
309
+ }
310
+ const header2 = contextHeader(input, "bounded", true);
311
+ return {
312
+ text: joinHeaderAndBody(header2, body, maxBytes, input.disposition.context.downgradeExcerpt),
313
+ appliedMode: "bounded",
314
+ truncated: true,
315
+ downgrade: { from: "full", to: "bounded", reason: "hard-budget" }
316
+ };
317
+ }
318
+ if (requestedMode === "metadata-only") {
319
+ return {
320
+ text: capUtf8(contextHeader(input, "metadata-only", input.capturedBytes > 0), maxBytes),
321
+ appliedMode: "metadata-only",
322
+ truncated: input.capturedBytes > 0
323
+ };
324
+ }
325
+ if (requestedMode === "summary") {
326
+ const diagnostic = input.disposition.context.strategy === "diagnostic";
327
+ const algorithm = diagnostic ? "sha256-diagnostic-v1" : "sha256-head-tail-v1";
328
+ const redact = input.disposition.context.redact !== false;
329
+ const budgetHeader = contextHeader(input, "summary", true, summaryProvenance(input.text, algorithm));
330
+ const bodyBudget = Math.max(0, maxBytes - byteLength(budgetHeader) - 1);
331
+ const selected = diagnostic ? deterministicDiagnosticSummary(body, bodyBudget, redact) : deterministicHeadTailSummary(body, redact);
332
+ const provenance = summaryProvenance(input.text, algorithm, selected.redactions);
333
+ const completeHeader = contextHeader(input, "summary", false, provenance);
334
+ const fits = byteLength(completeHeader) + 1 + byteLength(selected.text) <= maxBytes;
335
+ const truncated = input.capturedBytes > 0 && !(capturedTextIsComplete && selected.complete && fits);
336
+ const header2 = contextHeader(input, "summary", truncated, provenance);
337
+ return {
338
+ text: joinHeaderAndBody(header2, selected.text, maxBytes),
339
+ appliedMode: "summary",
340
+ truncated,
341
+ summaryProvenance: provenance
342
+ };
343
+ }
344
+ const header = contextHeader(input, "bounded", input.capturedBytes > maxBytes);
345
+ const text = joinHeaderAndBody(header, body, maxBytes, input.disposition.context.excerpt);
346
+ return {
347
+ text,
348
+ appliedMode: "bounded",
349
+ truncated: byteLength(header) + 1 + byteLength(body) > maxBytes || !capturedTextIsComplete
350
+ };
351
+ }
352
+ function toolResultContextText(result) {
353
+ if (result.modelContext !== void 0) return result.modelContext;
354
+ return result.kind === "ok" ? result.output : result.message;
355
+ }
356
+ function isDispositionedToolResultError(result) {
357
+ if (!isRecord(result)) return false;
358
+ const details = isRecord(result.details) ? result.details : null;
359
+ const metadata = details !== null && isRecord(details.resultDisposition) ? details.resultDisposition : null;
360
+ const kind = result.kind ?? details?.kind;
361
+ return kind === "error" && metadata?.version === 1 && metadata.applications === 1;
362
+ }
363
+ function toolResultPresentationText(result) {
364
+ if (!isRecord(result)) return null;
365
+ const details = isRecord(result.details) ? result.details : null;
366
+ const metadata = details !== null && isRecord(details.resultDisposition) ? details.resultDisposition : null;
367
+ const presentation = metadata !== null && isRecord(metadata.presentation) ? metadata.presentation : null;
368
+ return presentation !== null && typeof presentation.content === "string" ? presentation.content : null;
369
+ }
370
+ function toolResultPresentationPolicy(result) {
371
+ if (!isRecord(result)) return null;
372
+ const details = isRecord(result.details) ? result.details : null;
373
+ const metadata = details !== null && isRecord(details.resultDisposition) ? details.resultDisposition : null;
374
+ const presentation = metadata !== null && isRecord(metadata.presentation) ? metadata.presentation : null;
375
+ if (presentation === null || presentation.foldDefault !== "folded" && presentation.foldDefault !== "expanded" || typeof presentation.showDiffWhenFolded !== "boolean" || typeof presentation.failureExcerpt !== "boolean") {
376
+ return null;
377
+ }
378
+ return {
379
+ foldDefault: presentation.foldDefault,
380
+ showDiffWhenFolded: presentation.showDiffWhenFolded,
381
+ failureExcerpt: presentation.failureExcerpt
382
+ };
383
+ }
384
+
385
+ // src/tools/result-shaping.ts
386
+ var DEFAULT_TOOL_RESULT_MAX_BYTES = DEFAULT_MAX_BYTES + 2 * 1024;
387
+ var RESULT_TRUNCATION_MARKER = "\n[tool result truncated]";
388
+ var RESULT_OFFLOAD_MAX_BYTES = 10 * 1024 * 1024;
389
+ var TAIL_NOTICE_RESERVE_BYTES = 512;
390
+ function mergeDetails(details, resultSize) {
391
+ return { ...details ?? {}, resultSize };
392
+ }
393
+ function maxBytesFor(spec) {
394
+ const configured = spec.metadata?.resultSizePolicy?.maxBytes;
395
+ return typeof configured === "number" && Number.isFinite(configured) && configured > 0 ? Math.floor(configured) : DEFAULT_TOOL_RESULT_MAX_BYTES;
396
+ }
397
+ function followUpHint(spec) {
398
+ return spec.metadata?.resultSizePolicy?.followUpHint ?? "Use a narrower query, offset/limit arguments, or a more specific tool call to inspect the omitted content.";
399
+ }
400
+ function offloadMaxBytesFor(spec) {
401
+ const configured = spec.metadata?.resultSizePolicy?.offloadMaxBytes;
402
+ return typeof configured === "number" && Number.isFinite(configured) && configured > 0 ? Math.floor(configured) : RESULT_OFFLOAD_MAX_BYTES;
403
+ }
404
+ function detailsRecord(details, key) {
405
+ const candidate = details?.[key];
406
+ return candidate !== null && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : null;
407
+ }
408
+ function existingOffloadPath(details) {
409
+ const fromResultSize = detailsRecord(details, "resultSize")?.offloadPath;
410
+ if (typeof fromResultSize === "string" && fromResultSize.length > 0) return fromResultSize;
411
+ const fromObservation = detailsRecord(details, "observation")?.offloadPath;
412
+ return typeof fromObservation === "string" && fromObservation.length > 0 ? fromObservation : null;
413
+ }
414
+ function existingDisposition(details) {
415
+ const disposition = detailsRecord(details, "resultDisposition");
416
+ return disposition?.version === 1 && disposition.applications === 1 ? disposition : null;
417
+ }
418
+ function safePathSegment(value) {
419
+ const safe = value.trim().replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 128);
420
+ return safe.length > 0 ? safe : "unnamed";
421
+ }
422
+ function offloadBody(text, bytes, maxBytes) {
423
+ if (bytes <= maxBytes) return text;
424
+ const notice = `
425
+ [clio-coder scratch output truncated at ${maxBytes} bytes; original size ${bytes} bytes]`;
426
+ const prefixBudget = maxBytes - byteLength(notice);
427
+ return `${truncateUtf8(text, Math.max(0, prefixBudget), "")}${notice}`;
428
+ }
429
+ function writeToolOffload(text, context, maxBytes = RESULT_OFFLOAD_MAX_BYTES, digest = createHash2("sha256").update(text).digest("hex")) {
430
+ try {
431
+ const bytes = byteLength(text);
432
+ const sessionId = safePathSegment(context?.sessionId ?? "no-session");
433
+ const dir = join(clioStateDir(), "scratch", sessionId);
434
+ const path = join(dir, `${digest}.txt`);
435
+ mkdirSync(dir, { recursive: true });
436
+ writeFileSync(path, offloadBody(text, bytes, maxBytes), "utf8");
437
+ return path;
438
+ } catch {
439
+ return null;
440
+ }
441
+ }
442
+ var OFFLOAD_POINTER_NOTE = "overflow copy, read-only; not the workspace";
443
+ function offloadPointer(offloadPath) {
444
+ return `full: ${offloadPath} (${OFFLOAD_POINTER_NOTE})`;
445
+ }
446
+ function isSessionOffloadPath(filePath, sessionId) {
447
+ const dir = join(clioStateDir(), "scratch", safePathSegment(sessionId ?? "no-session"));
448
+ const rel = relative(dir, resolve(filePath));
449
+ return rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel);
450
+ }
451
+ function bracketedHint(spec, offloadPath) {
452
+ const hint = followUpHint(spec);
453
+ if (offloadPath === null) return hint;
454
+ return `${hint} ${offloadPointer(offloadPath)}; read it with offset and limit to inspect the rest.`;
455
+ }
456
+ function shapeJsonOverflow(spec, result, text, bytes, maxBytes, context, offloadMaxBytes) {
457
+ const observation = detailsRecord(result.details, "observation");
458
+ const offloadPath = writeToolOffload(text, context, offloadMaxBytes);
459
+ const next = typeof observation?.next === "string" && observation.next.length > 0 ? observation.next : followUpHint(spec);
460
+ const stub = JSON.stringify({
461
+ error: `result exceeded ${maxBytes} bytes`,
462
+ ...offloadPath !== null ? { offloadPath, offloadPathNote: OFFLOAD_POINTER_NOTE } : {},
463
+ next
464
+ });
465
+ const resultSize = {
466
+ bytes,
467
+ shownBytes: byteLength(stub),
468
+ maxBytes,
469
+ truncated: true,
470
+ policy: spec.metadata?.resultSizePolicy?.kind ?? "truncate",
471
+ followUpHint: followUpHint(spec),
472
+ ...offloadPath !== null ? { offloadPath } : {}
473
+ };
474
+ if (result.kind === "ok") {
475
+ return { ...result, output: stub, details: mergeDetails(result.details, resultSize) };
476
+ }
477
+ return { ...result, message: stub, details: mergeDetails(result.details, resultSize) };
478
+ }
479
+ function shapeTailOverflow(spec, result, text, bytes, maxBytes, context, offloadMaxBytes) {
480
+ const truncation = truncateTail2(text, {
481
+ maxLines: DEFAULT_MAX_LINES,
482
+ maxBytes: Math.max(1, maxBytes - TAIL_NOTICE_RESERVE_BYTES)
483
+ });
484
+ const offloadPath = writeToolOffload(text, context, offloadMaxBytes);
485
+ const startLine = truncation.totalLines - truncation.outputLines + 1;
486
+ const scope = truncation.lastLinePartial ? `last ${formatSize(truncation.outputBytes)} of line ${truncation.totalLines} (line is large)` : `lines ${startLine}-${truncation.totalLines} of ${truncation.totalLines}`;
487
+ const location = offloadPath === null ? "" : ` Full output saved to ${offloadPath}; ${OFFLOAD_POINTER_NOTE}; read it with offset/limit.`;
488
+ const note = `[Output tail-truncated: showing ${scope} (${formatSize(maxBytes)} display limit).${location}]`;
489
+ const resultSize = {
490
+ bytes,
491
+ shownBytes: truncation.outputBytes,
492
+ maxBytes,
493
+ truncated: true,
494
+ policy: "tail",
495
+ followUpHint: followUpHint(spec),
496
+ ...offloadPath === null ? {} : { offloadPath }
497
+ };
498
+ const displayed = `${truncation.content}
499
+
500
+ ${note}`;
501
+ if (result.kind === "ok") return { ...result, output: displayed, details: mergeDetails(result.details, resultSize) };
502
+ return { ...result, message: displayed, details: mergeDetails(result.details, resultSize) };
503
+ }
504
+ function shapeLegacyToolResult(spec, result, context, overflow = "head") {
505
+ if (existingOffloadPath(result.details) !== null) return result;
506
+ const maxBytes = maxBytesFor(spec);
507
+ const offloadMaxBytes = offloadMaxBytesFor(spec);
508
+ const text = result.kind === "ok" ? result.output : result.message;
509
+ const bytes = byteLength(text);
510
+ if (bytes <= maxBytes) return result;
511
+ const observation = detailsRecord(result.details, "observation");
512
+ if (observation?.format === "json") {
513
+ return shapeJsonOverflow(spec, result, text, bytes, maxBytes, context, offloadMaxBytes);
514
+ }
515
+ if (overflow === "tail") {
516
+ return shapeTailOverflow(spec, result, text, bytes, maxBytes, context, offloadMaxBytes);
517
+ }
518
+ const truncated = truncateUtf8(text, maxBytes, RESULT_TRUNCATION_MARKER);
519
+ const offloadPath = writeToolOffload(text, context, offloadMaxBytes);
520
+ const resultSize = {
521
+ bytes,
522
+ shownBytes: byteLength(truncated),
523
+ maxBytes,
524
+ truncated: true,
525
+ policy: spec.metadata?.resultSizePolicy?.kind ?? "truncate",
526
+ followUpHint: followUpHint(spec),
527
+ ...offloadPath !== null ? { offloadPath } : {}
528
+ };
529
+ if (result.kind === "ok") {
530
+ return {
531
+ ...result,
532
+ output: `${truncated}
533
+
534
+ [${bracketedHint(spec, offloadPath)}]`,
535
+ details: mergeDetails(result.details, resultSize)
536
+ };
537
+ }
538
+ return {
539
+ ...result,
540
+ message: `${truncated}
541
+
542
+ [${bracketedHint(spec, offloadPath)}]`,
543
+ details: mergeDetails(result.details, resultSize)
544
+ };
545
+ }
546
+ function specWithDisplayCap(spec, maxBytes) {
547
+ const metadata = spec.metadata;
548
+ if (metadata === void 0) return spec;
549
+ return {
550
+ ...spec,
551
+ metadata: {
552
+ ...metadata,
553
+ resultSizePolicy: { ...metadata.resultSizePolicy, maxBytes }
554
+ }
555
+ };
556
+ }
557
+ function resultText(result) {
558
+ return result.kind === "ok" ? result.output : result.message;
559
+ }
560
+ function withFallbackNotice(result, fallback) {
561
+ const note = `
562
+
563
+ [result disposition fell back to metadata-only: ${fallback.reason} (${fallback.message}); the model received facts and retrieval only]`;
564
+ return result.kind === "ok" ? { ...result, output: `${result.output}${note}` } : { ...result, message: `${result.message}${note}` };
565
+ }
566
+ function numberField(record, key) {
567
+ const value = record?.[key];
568
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
569
+ }
570
+ function capturedBytesFor(result, text) {
571
+ const declared = numberField(detailsRecord(result.details, "resultSize"), "bytes");
572
+ return Math.max(byteLength(text), declared ?? 0);
573
+ }
574
+ function resultWasTruncated(result) {
575
+ return detailsRecord(result.details, "resultSize")?.truncated === true;
576
+ }
577
+ function withOffloadMetadata(result, offloadPath, capturedBytes, displayedBytes, maxBytes, hint) {
578
+ const current = detailsRecord(result.details, "resultSize") ?? {};
579
+ const resultSize = {
580
+ bytes: numberField(current, "bytes") ?? capturedBytes,
581
+ shownBytes: numberField(current, "shownBytes") ?? displayedBytes,
582
+ maxBytes: numberField(current, "maxBytes") ?? maxBytes,
583
+ truncated: current.truncated === true,
584
+ policy: typeof current.policy === "string" ? current.policy : "disposition",
585
+ followUpHint: typeof current.followUpHint === "string" ? current.followUpHint : hint,
586
+ ...current,
587
+ offloadPath
588
+ };
589
+ return { ...result, details: mergeDetails(result.details, resultSize) };
590
+ }
591
+ function shapeToolResult(spec, result, context, requestedDisposition) {
592
+ if (existingDisposition(result.details) !== null) return result;
593
+ const hardMaxBytes = maxBytesFor(spec);
594
+ const disposition = normalizeToolResultDisposition(spec, hardMaxBytes, requestedDisposition);
595
+ if (disposition === null) return shapeLegacyToolResult(spec, result, context);
596
+ const capturedText = resultText(result);
597
+ const capturedBytes = capturedBytesFor(result, capturedText);
598
+ let displayed = shapeLegacyToolResult(
599
+ specWithDisplayCap(spec, disposition.presentation.maxBytes),
600
+ result,
601
+ context,
602
+ disposition.presentation.overflow
603
+ );
604
+ if (disposition.fallback !== void 0) displayed = withFallbackNotice(displayed, disposition.fallback);
605
+ let displayedText = resultText(displayed);
606
+ let displayedBytes = byteLength(displayedText);
607
+ let offloadPath = existingOffloadPath(displayed.details);
608
+ const projectionInput = {
609
+ text: capturedText,
610
+ kind: result.kind,
611
+ details: displayed.details,
612
+ disposition,
613
+ capturedBytes,
614
+ displayedBytes,
615
+ offloadPath,
616
+ followUpHint: followUpHint(spec)
617
+ };
618
+ let projection = projectToolResultContext(projectionInput);
619
+ if (offloadPath === null && projection.truncated) {
620
+ offloadPath = writeToolOffload(
621
+ capturedText,
622
+ context,
623
+ offloadMaxBytesFor(spec),
624
+ projection.summaryProvenance?.sourceSha256
625
+ );
626
+ if (offloadPath !== null) {
627
+ displayed = withOffloadMetadata(
628
+ displayed,
629
+ offloadPath,
630
+ capturedBytes,
631
+ displayedBytes,
632
+ disposition.presentation.maxBytes,
633
+ followUpHint(spec)
634
+ );
635
+ displayedText = resultText(displayed);
636
+ displayedBytes = byteLength(displayedText);
637
+ projection = projectToolResultContext({
638
+ ...projectionInput,
639
+ details: displayed.details,
640
+ displayedBytes,
641
+ offloadPath
642
+ });
643
+ }
644
+ }
645
+ const metadata = {
646
+ version: 1,
647
+ applications: 1,
648
+ presentation: { ...disposition.presentation, content: displayedText },
649
+ context: {
650
+ requestedMode: disposition.context.mode,
651
+ appliedMode: projection.appliedMode,
652
+ maxBytes: disposition.context.maxBytes,
653
+ ...disposition.context.mode === "bounded" && disposition.context.excerpt !== void 0 ? { excerpt: disposition.context.excerpt } : {},
654
+ ...disposition.context.mode === "full" && disposition.context.downgradeExcerpt !== void 0 ? { excerpt: disposition.context.downgradeExcerpt } : {},
655
+ ...disposition.context.mode === "summary" && disposition.context.strategy !== void 0 ? { summaryStrategy: disposition.context.strategy } : {}
656
+ },
657
+ capturedBytes,
658
+ displayedBytes,
659
+ contextBytes: byteLength(projection.text),
660
+ presentationTruncated: resultWasTruncated(displayed),
661
+ contextTruncated: projection.truncated,
662
+ ...projection.downgrade === void 0 ? {} : { downgrade: projection.downgrade },
663
+ ...offloadPath === null ? {} : { offloadPath },
664
+ retrieval: offloadPath === null ? followUpHint(spec) : `read ${offloadPath} with offset and limit to retrieve omitted content`,
665
+ ...projection.summaryProvenance === void 0 ? {} : { summaryProvenance: projection.summaryProvenance },
666
+ ...disposition.fallback === void 0 ? {} : { fallback: disposition.fallback }
667
+ };
668
+ return {
669
+ ...displayed,
670
+ details: { ...displayed.details ?? {}, resultDisposition: metadata },
671
+ modelContext: projection.text
672
+ };
673
+ }
674
+
675
+ // src/tools/observation.ts
676
+ var OBSERVATION_POLICY_SLACK_BYTES = 2 * 1024;
677
+ var OBSERVE_SELF_CAPS = {
678
+ grepContent: 16 * 1024,
679
+ grepFilesCount: 8 * 1024,
680
+ find: 8 * 1024,
681
+ ls: 8 * 1024,
682
+ codeNav: 16 * 1024,
683
+ contextDocs: 16 * 1024,
684
+ contextSkills: 50 * 1024,
685
+ contextWorkspace: 50 * 1024
686
+ };
687
+ var DEFAULT_OBSERVATION_TURN_BUDGET_BYTES = GUARDRAIL_DEFAULTS.observationTurnBudgetBytes;
688
+ var OBSERVATION_TURN_BUDGET_ENV = GUARDRAIL_ENV_VARS.observationTurnBudgetBytes;
689
+ var MIN_BUDGET_SLICE_BYTES = 1024;
690
+ var BUDGET_TRACK_LIMIT = 256;
691
+ var turnBudgets = /* @__PURE__ */ new Map();
692
+ var MAX_CONTINUATION_CHARS = 200;
693
+ function boundContinuation(next) {
694
+ if (next === void 0 || next.length <= MAX_CONTINUATION_CHARS) return next;
695
+ return `${Array.from(next).slice(0, MAX_CONTINUATION_CHARS).join("")}\u2026`;
696
+ }
697
+ function observationTurnBudgetLimit(env = process.env) {
698
+ return Math.max(MIN_BUDGET_SLICE_BYTES, resolveGuardrail("observationTurnBudgetBytes", env));
699
+ }
700
+ function budgetKey(options) {
701
+ const turnId = options?.turnId?.trim();
702
+ if (!turnId) return null;
703
+ const sessionId = options?.sessionId?.trim() || "no-session";
704
+ return `${sessionId}:${turnId}`;
705
+ }
706
+ function pruneBudgetMap() {
707
+ while (turnBudgets.size > BUDGET_TRACK_LIMIT) {
708
+ let oldestKey = null;
709
+ let oldestSeen = Number.POSITIVE_INFINITY;
710
+ for (const [key, state] of turnBudgets) {
711
+ if (state.lastSeenAt >= oldestSeen) continue;
712
+ oldestKey = key;
713
+ oldestSeen = state.lastSeenAt;
714
+ }
715
+ if (oldestKey === null) break;
716
+ turnBudgets.delete(oldestKey);
717
+ }
718
+ }
719
+ function reserveObservation(selfCapBytes, options) {
720
+ const key = budgetKey(options);
721
+ if (key === null) {
722
+ return {
723
+ key,
724
+ limitBytes: observationTurnBudgetLimit(),
725
+ usedBeforeBytes: 0,
726
+ selfCapBytes,
727
+ callCapBytes: selfCapBytes,
728
+ limited: false,
729
+ exhausted: false,
730
+ committed: false,
731
+ settled: false
732
+ };
733
+ }
734
+ const limitBytes = observationTurnBudgetLimit();
735
+ const state = turnBudgets.get(key) ?? { usedBytes: 0, lastSeenAt: Date.now() };
736
+ state.lastSeenAt = Date.now();
737
+ turnBudgets.set(key, state);
738
+ pruneBudgetMap();
739
+ const remaining = Math.max(0, limitBytes - state.usedBytes);
740
+ if (remaining < MIN_BUDGET_SLICE_BYTES) {
741
+ return {
742
+ key,
743
+ limitBytes,
744
+ usedBeforeBytes: state.usedBytes,
745
+ selfCapBytes,
746
+ callCapBytes: 0,
747
+ limited: true,
748
+ exhausted: true,
749
+ committed: false,
750
+ settled: false
751
+ };
752
+ }
753
+ const callCapBytes = Math.min(selfCapBytes, remaining);
754
+ return {
755
+ key,
756
+ limitBytes,
757
+ usedBeforeBytes: state.usedBytes,
758
+ selfCapBytes,
759
+ callCapBytes,
760
+ limited: callCapBytes < selfCapBytes,
761
+ exhausted: false,
762
+ committed: false,
763
+ settled: false
764
+ };
765
+ }
766
+ function commitObservationReservation(reservation) {
767
+ if (reservation.key === null || reservation.committed || reservation.settled) return;
768
+ if (reservation.callCapBytes <= 0) return;
769
+ reservation.committed = true;
770
+ const state = turnBudgets.get(reservation.key) ?? {
771
+ usedBytes: reservation.usedBeforeBytes,
772
+ lastSeenAt: Date.now()
773
+ };
774
+ state.usedBytes += reservation.callCapBytes;
775
+ state.lastSeenAt = Date.now();
776
+ turnBudgets.set(reservation.key, state);
777
+ }
778
+ function releaseObservation(reservation) {
779
+ if (reservation.key === null || reservation.settled) return;
780
+ reservation.settled = true;
781
+ if (!reservation.committed) return;
782
+ const state = turnBudgets.get(reservation.key);
783
+ if (state === void 0) return;
784
+ state.usedBytes = Math.max(0, state.usedBytes - reservation.callCapBytes);
785
+ state.lastSeenAt = Date.now();
786
+ turnBudgets.set(reservation.key, state);
787
+ }
788
+ function recordSpentBytes(reservation, bytes) {
789
+ if (reservation.key === null || reservation.settled) return;
790
+ reservation.settled = true;
791
+ const committedBytes = reservation.committed ? reservation.callCapBytes : 0;
792
+ const state = turnBudgets.get(reservation.key) ?? {
793
+ usedBytes: reservation.usedBeforeBytes + committedBytes,
794
+ lastSeenAt: Date.now()
795
+ };
796
+ state.usedBytes = Math.max(0, state.usedBytes + bytes - committedBytes);
797
+ state.lastSeenAt = Date.now();
798
+ turnBudgets.set(reservation.key, state);
799
+ }
800
+ function budgetDetails(reservation) {
801
+ if (reservation.key === null) return null;
802
+ return {
803
+ limitBytes: reservation.limitBytes,
804
+ usedBeforeBytes: reservation.usedBeforeBytes,
805
+ exhausted: reservation.exhausted
806
+ };
807
+ }
808
+ function observationBudgetExhausted(input) {
809
+ const { tool, unit, reservation, subject, hint } = input;
810
+ const output = `[observation budget exhausted for this turn before ${tool} ${subject}: ${formatSize(
811
+ reservation.usedBeforeBytes
812
+ )} already returned of ${formatSize(reservation.limitBytes)}. ${hint}]`;
813
+ const shownBytes = byteLength(output);
814
+ recordSpentBytes(reservation, shownBytes);
815
+ const budget = budgetDetails(reservation);
816
+ const observation = {
817
+ tool,
818
+ unit,
819
+ shownCount: 0,
820
+ totalCount: null,
821
+ shownBytes,
822
+ // The notice message is the whole rendering here (the search never ran), so
823
+ // the full size is the message itself; keep totalBytes >= shownBytes.
824
+ totalBytes: shownBytes,
825
+ truncated: true,
826
+ format: "text",
827
+ ...budget !== null ? { budget } : {}
828
+ };
829
+ return { kind: "ok", output, details: { observation } };
830
+ }
831
+ function noticeLine(input, bodyBytes, totalBytes, offloadPath, next) {
832
+ const totalSegment = input.totalCount === null ? `${input.shownCount}+` : String(input.totalCount);
833
+ const parts = [
834
+ `${input.tool}: ${input.shownCount}/${totalSegment} ${input.unit} shown (${formatSize(bodyBytes)} of ${formatSize(totalBytes)})`
835
+ ];
836
+ if (offloadPath !== null) parts.push(offloadPointer(offloadPath));
837
+ if (next !== void 0 && next.length > 0) parts.push(`next: ${next}`);
838
+ return `[${parts.join(" | ")}]`;
839
+ }
840
+ function limitedBudgetNote(reservation) {
841
+ return `[Per-turn observation budget: ${formatSize(reservation.usedBeforeBytes)} already returned of ${formatSize(
842
+ reservation.limitBytes
843
+ )} before this call. Use narrower arguments or continue in a follow-up turn for more content.]`;
844
+ }
845
+ function jsonStubBudgetNote(reservation) {
846
+ return `per-turn observation budget nearly exhausted: ${formatSize(reservation.usedBeforeBytes)} of ${formatSize(reservation.limitBytes)} already returned this turn. Do not retry this call; answer from what you have gathered, or continue in a follow-up turn.`;
847
+ }
848
+ function finalizeObservation(input) {
849
+ const format = input.format ?? "text";
850
+ let output = input.output;
851
+ let truncated = input.truncated;
852
+ let shownCount = input.shownCount;
853
+ let offloadPath = null;
854
+ const bodyBytes = byteLength(output);
855
+ const totalBytes = input.totalBytes ?? byteLength(input.fullOutput ?? input.output);
856
+ const next = boundContinuation(input.next);
857
+ if (format === "json" && bodyBytes > input.reservation.callCapBytes) {
858
+ truncated = true;
859
+ shownCount = 0;
860
+ offloadPath = writeToolOffload(input.fullOutput ?? output, input.options);
861
+ const poolBound = input.reservation.limited;
862
+ output = JSON.stringify({
863
+ error: `result exceeded ${formatSize(input.reservation.callCapBytes)}`,
864
+ ...poolBound ? { budget: jsonStubBudgetNote(input.reservation) } : {},
865
+ ...offloadPath !== null ? { offloadPath } : {},
866
+ ...!poolBound && next !== void 0 && next.length > 0 ? { next } : {}
867
+ });
868
+ } else if (truncated && input.fullOutput !== void 0) {
869
+ offloadPath = writeToolOffload(input.fullOutput, input.options);
870
+ }
871
+ if (format === "text") {
872
+ if (truncated && input.omitNotice !== true)
873
+ output += `
874
+
875
+ ${noticeLine(input, bodyBytes, totalBytes, offloadPath, next)}`;
876
+ if (input.reservation.limited && !input.reservation.exhausted) {
877
+ output += `
878
+
879
+ ${limitedBudgetNote(input.reservation)}`;
880
+ }
881
+ }
882
+ const shownBytes = byteLength(output);
883
+ recordSpentBytes(input.reservation, shownBytes);
884
+ const budget = budgetDetails(input.reservation);
885
+ const observation = {
886
+ tool: input.tool,
887
+ unit: input.unit,
888
+ shownCount,
889
+ totalCount: input.totalCount,
890
+ shownBytes,
891
+ // shownBytes counts the appended notice/limited-budget lines; totalBytes
892
+ // measured only the pre-notice rendering (and equals it when a count-limited
893
+ // tool like ls offloads nothing). Floor the full-rendering size at the bytes
894
+ // actually returned so the envelope invariant totalBytes >= shownBytes holds.
895
+ totalBytes: Math.max(totalBytes, shownBytes),
896
+ truncated,
897
+ format,
898
+ ...next !== void 0 && next.length > 0 ? { next } : {},
899
+ ...offloadPath !== null ? { offloadPath } : {},
900
+ ...budget !== null ? { budget } : {}
901
+ };
902
+ return { kind: "ok", output, details: { ...input.details ?? {}, observation } };
903
+ }
904
+
905
+ export {
906
+ deterministicDiagnosticSummary,
907
+ toolResultContextText,
908
+ isDispositionedToolResultError,
909
+ toolResultPresentationText,
910
+ toolResultPresentationPolicy,
911
+ DEFAULT_MAX_LINES,
912
+ splitLinesForCounting,
913
+ truncateHead2 as truncateHead,
914
+ truncateTail2 as truncateTail,
915
+ DEFAULT_TOOL_RESULT_MAX_BYTES,
916
+ isSessionOffloadPath,
917
+ shapeToolResult,
918
+ OBSERVATION_POLICY_SLACK_BYTES,
919
+ OBSERVE_SELF_CAPS,
920
+ reserveObservation,
921
+ commitObservationReservation,
922
+ releaseObservation,
923
+ observationBudgetExhausted,
924
+ finalizeObservation
925
+ };
926
+ //# sourceMappingURL=chunk-5M54SPOL.js.map