@xandout/libra-harness 0.1.8

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 (289) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +486 -0
  3. package/dist/agent.d.ts +174 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +575 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/ai-sdk-model.d.ts +26 -0
  8. package/dist/ai-sdk-model.d.ts.map +1 -0
  9. package/dist/ai-sdk-model.js +260 -0
  10. package/dist/ai-sdk-model.js.map +1 -0
  11. package/dist/context.d.ts +75 -0
  12. package/dist/context.d.ts.map +1 -0
  13. package/dist/context.js +2 -0
  14. package/dist/context.js.map +1 -0
  15. package/dist/errors.d.ts +33 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +59 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/extension.d.ts +63 -0
  20. package/dist/extension.d.ts.map +1 -0
  21. package/dist/extension.js +2 -0
  22. package/dist/extension.js.map +1 -0
  23. package/dist/extras/extension-loader.d.ts +205 -0
  24. package/dist/extras/extension-loader.d.ts.map +1 -0
  25. package/dist/extras/extension-loader.js +315 -0
  26. package/dist/extras/extension-loader.js.map +1 -0
  27. package/dist/extras/extensions/auto-steer/extension.d.ts +46 -0
  28. package/dist/extras/extensions/auto-steer/extension.d.ts.map +1 -0
  29. package/dist/extras/extensions/auto-steer/extension.js +52 -0
  30. package/dist/extras/extensions/auto-steer/extension.js.map +1 -0
  31. package/dist/extras/extensions/auto-steer/extension.json +7 -0
  32. package/dist/extras/extensions/auto-steer/index.d.ts +3 -0
  33. package/dist/extras/extensions/auto-steer/index.d.ts.map +1 -0
  34. package/dist/extras/extensions/auto-steer/index.js +2 -0
  35. package/dist/extras/extensions/auto-steer/index.js.map +1 -0
  36. package/dist/extras/extensions/disk-session/extension.d.ts +167 -0
  37. package/dist/extras/extensions/disk-session/extension.d.ts.map +1 -0
  38. package/dist/extras/extensions/disk-session/extension.js +492 -0
  39. package/dist/extras/extensions/disk-session/extension.js.map +1 -0
  40. package/dist/extras/extensions/disk-session/extension.json +6 -0
  41. package/dist/extras/extensions/disk-session/extension.test.d.ts +2 -0
  42. package/dist/extras/extensions/disk-session/extension.test.d.ts.map +1 -0
  43. package/dist/extras/extensions/disk-session/extension.test.js +672 -0
  44. package/dist/extras/extensions/disk-session/extension.test.js.map +1 -0
  45. package/dist/extras/extensions/disk-session/index.d.ts +3 -0
  46. package/dist/extras/extensions/disk-session/index.d.ts.map +1 -0
  47. package/dist/extras/extensions/disk-session/index.js +2 -0
  48. package/dist/extras/extensions/disk-session/index.js.map +1 -0
  49. package/dist/extras/extensions/emoji/extension.d.ts +20 -0
  50. package/dist/extras/extensions/emoji/extension.d.ts.map +1 -0
  51. package/dist/extras/extensions/emoji/extension.js +22 -0
  52. package/dist/extras/extensions/emoji/extension.js.map +1 -0
  53. package/dist/extras/extensions/emoji/extension.json +7 -0
  54. package/dist/extras/extensions/emoji/index.d.ts +3 -0
  55. package/dist/extras/extensions/emoji/index.d.ts.map +1 -0
  56. package/dist/extras/extensions/emoji/index.js +2 -0
  57. package/dist/extras/extensions/emoji/index.js.map +1 -0
  58. package/dist/extras/extensions/filesystem/extension.d.ts +53 -0
  59. package/dist/extras/extensions/filesystem/extension.d.ts.map +1 -0
  60. package/dist/extras/extensions/filesystem/extension.js +350 -0
  61. package/dist/extras/extensions/filesystem/extension.js.map +1 -0
  62. package/dist/extras/extensions/filesystem/extension.json +6 -0
  63. package/dist/extras/extensions/filesystem/extension.test.d.ts +2 -0
  64. package/dist/extras/extensions/filesystem/extension.test.d.ts.map +1 -0
  65. package/dist/extras/extensions/filesystem/extension.test.js +387 -0
  66. package/dist/extras/extensions/filesystem/extension.test.js.map +1 -0
  67. package/dist/extras/extensions/filesystem/index.d.ts +2 -0
  68. package/dist/extras/extensions/filesystem/index.d.ts.map +1 -0
  69. package/dist/extras/extensions/filesystem/index.js +2 -0
  70. package/dist/extras/extensions/filesystem/index.js.map +1 -0
  71. package/dist/extras/extensions/keyword-extractor/extension.d.ts +53 -0
  72. package/dist/extras/extensions/keyword-extractor/extension.d.ts.map +1 -0
  73. package/dist/extras/extensions/keyword-extractor/extension.js +61 -0
  74. package/dist/extras/extensions/keyword-extractor/extension.js.map +1 -0
  75. package/dist/extras/extensions/keyword-extractor/extension.json +6 -0
  76. package/dist/extras/extensions/keyword-extractor/extension.test.d.ts +2 -0
  77. package/dist/extras/extensions/keyword-extractor/extension.test.d.ts.map +1 -0
  78. package/dist/extras/extensions/keyword-extractor/extension.test.js +105 -0
  79. package/dist/extras/extensions/keyword-extractor/extension.test.js.map +1 -0
  80. package/dist/extras/extensions/keyword-extractor/index.d.ts +5 -0
  81. package/dist/extras/extensions/keyword-extractor/index.d.ts.map +1 -0
  82. package/dist/extras/extensions/keyword-extractor/index.js +3 -0
  83. package/dist/extras/extensions/keyword-extractor/index.js.map +1 -0
  84. package/dist/extras/extensions/keyword-extractor/nlp.d.ts +68 -0
  85. package/dist/extras/extensions/keyword-extractor/nlp.d.ts.map +1 -0
  86. package/dist/extras/extensions/keyword-extractor/nlp.js +156 -0
  87. package/dist/extras/extensions/keyword-extractor/nlp.js.map +1 -0
  88. package/dist/extras/extensions/keyword-extractor/nlp.test.d.ts +2 -0
  89. package/dist/extras/extensions/keyword-extractor/nlp.test.d.ts.map +1 -0
  90. package/dist/extras/extensions/keyword-extractor/nlp.test.js +176 -0
  91. package/dist/extras/extensions/keyword-extractor/nlp.test.js.map +1 -0
  92. package/dist/extras/extensions/logger/extension.d.ts +52 -0
  93. package/dist/extras/extensions/logger/extension.d.ts.map +1 -0
  94. package/dist/extras/extensions/logger/extension.js +68 -0
  95. package/dist/extras/extensions/logger/extension.js.map +1 -0
  96. package/dist/extras/extensions/logger/extension.json +7 -0
  97. package/dist/extras/extensions/logger/index.d.ts +3 -0
  98. package/dist/extras/extensions/logger/index.d.ts.map +1 -0
  99. package/dist/extras/extensions/logger/index.js +2 -0
  100. package/dist/extras/extensions/logger/index.js.map +1 -0
  101. package/dist/extras/extensions/mcp/extension.d.ts +63 -0
  102. package/dist/extras/extensions/mcp/extension.d.ts.map +1 -0
  103. package/dist/extras/extensions/mcp/extension.js +437 -0
  104. package/dist/extras/extensions/mcp/extension.js.map +1 -0
  105. package/dist/extras/extensions/mcp/extension.json +7 -0
  106. package/dist/extras/extensions/mcp/extension.test.d.ts +2 -0
  107. package/dist/extras/extensions/mcp/extension.test.d.ts.map +1 -0
  108. package/dist/extras/extensions/mcp/extension.test.js +286 -0
  109. package/dist/extras/extensions/mcp/extension.test.js.map +1 -0
  110. package/dist/extras/extensions/mcp/index.d.ts +3 -0
  111. package/dist/extras/extensions/mcp/index.d.ts.map +1 -0
  112. package/dist/extras/extensions/mcp/index.js +2 -0
  113. package/dist/extras/extensions/mcp/index.js.map +1 -0
  114. package/dist/extras/extensions/mem-session/extension.d.ts +24 -0
  115. package/dist/extras/extensions/mem-session/extension.d.ts.map +1 -0
  116. package/dist/extras/extensions/mem-session/extension.js +49 -0
  117. package/dist/extras/extensions/mem-session/extension.js.map +1 -0
  118. package/dist/extras/extensions/mem-session/extension.json +6 -0
  119. package/dist/extras/extensions/mem-session/index.d.ts +2 -0
  120. package/dist/extras/extensions/mem-session/index.d.ts.map +1 -0
  121. package/dist/extras/extensions/mem-session/index.js +2 -0
  122. package/dist/extras/extensions/mem-session/index.js.map +1 -0
  123. package/dist/extras/extensions/memory/extension.d.ts +54 -0
  124. package/dist/extras/extensions/memory/extension.d.ts.map +1 -0
  125. package/dist/extras/extensions/memory/extension.js +143 -0
  126. package/dist/extras/extensions/memory/extension.js.map +1 -0
  127. package/dist/extras/extensions/memory/extension.json +7 -0
  128. package/dist/extras/extensions/memory/index.d.ts +5 -0
  129. package/dist/extras/extensions/memory/index.d.ts.map +1 -0
  130. package/dist/extras/extensions/memory/index.js +3 -0
  131. package/dist/extras/extensions/memory/index.js.map +1 -0
  132. package/dist/extras/extensions/memory/llm-extractor.d.ts +42 -0
  133. package/dist/extras/extensions/memory/llm-extractor.d.ts.map +1 -0
  134. package/dist/extras/extensions/memory/llm-extractor.js +89 -0
  135. package/dist/extras/extensions/memory/llm-extractor.js.map +1 -0
  136. package/dist/extras/extensions/memory/types.d.ts +142 -0
  137. package/dist/extras/extensions/memory/types.d.ts.map +1 -0
  138. package/dist/extras/extensions/memory/types.js +2 -0
  139. package/dist/extras/extensions/memory/types.js.map +1 -0
  140. package/dist/extras/extensions/otel/extension.d.ts +57 -0
  141. package/dist/extras/extensions/otel/extension.d.ts.map +1 -0
  142. package/dist/extras/extensions/otel/extension.js +253 -0
  143. package/dist/extras/extensions/otel/extension.js.map +1 -0
  144. package/dist/extras/extensions/otel/extension.json +7 -0
  145. package/dist/extras/extensions/otel/extension.test.d.ts +2 -0
  146. package/dist/extras/extensions/otel/extension.test.d.ts.map +1 -0
  147. package/dist/extras/extensions/otel/extension.test.js +399 -0
  148. package/dist/extras/extensions/otel/extension.test.js.map +1 -0
  149. package/dist/extras/extensions/otel/index.d.ts +4 -0
  150. package/dist/extras/extensions/otel/index.d.ts.map +1 -0
  151. package/dist/extras/extensions/otel/index.js +3 -0
  152. package/dist/extras/extensions/otel/index.js.map +1 -0
  153. package/dist/extras/extensions/otel/jsonl-span-exporter.d.ts +60 -0
  154. package/dist/extras/extensions/otel/jsonl-span-exporter.d.ts.map +1 -0
  155. package/dist/extras/extensions/otel/jsonl-span-exporter.js +134 -0
  156. package/dist/extras/extensions/otel/jsonl-span-exporter.js.map +1 -0
  157. package/dist/extras/extensions/scripts/extension.d.ts +180 -0
  158. package/dist/extras/extensions/scripts/extension.d.ts.map +1 -0
  159. package/dist/extras/extensions/scripts/extension.js +618 -0
  160. package/dist/extras/extensions/scripts/extension.js.map +1 -0
  161. package/dist/extras/extensions/scripts/extension.json +7 -0
  162. package/dist/extras/extensions/scripts/extension.test.d.ts +2 -0
  163. package/dist/extras/extensions/scripts/extension.test.d.ts.map +1 -0
  164. package/dist/extras/extensions/scripts/extension.test.js +552 -0
  165. package/dist/extras/extensions/scripts/extension.test.js.map +1 -0
  166. package/dist/extras/extensions/scripts/index.d.ts +3 -0
  167. package/dist/extras/extensions/scripts/index.d.ts.map +1 -0
  168. package/dist/extras/extensions/scripts/index.js +2 -0
  169. package/dist/extras/extensions/scripts/index.js.map +1 -0
  170. package/dist/extras/extensions/skills/extension.d.ts +105 -0
  171. package/dist/extras/extensions/skills/extension.d.ts.map +1 -0
  172. package/dist/extras/extensions/skills/extension.js +419 -0
  173. package/dist/extras/extensions/skills/extension.js.map +1 -0
  174. package/dist/extras/extensions/skills/extension.json +7 -0
  175. package/dist/extras/extensions/skills/extension.test.d.ts +2 -0
  176. package/dist/extras/extensions/skills/extension.test.d.ts.map +1 -0
  177. package/dist/extras/extensions/skills/extension.test.js +184 -0
  178. package/dist/extras/extensions/skills/extension.test.js.map +1 -0
  179. package/dist/extras/extensions/skills/index.d.ts +4 -0
  180. package/dist/extras/extensions/skills/index.d.ts.map +1 -0
  181. package/dist/extras/extensions/skills/index.js +3 -0
  182. package/dist/extras/extensions/skills/index.js.map +1 -0
  183. package/dist/extras/extensions/streaming/extension.d.ts +15 -0
  184. package/dist/extras/extensions/streaming/extension.d.ts.map +1 -0
  185. package/dist/extras/extensions/streaming/extension.js +41 -0
  186. package/dist/extras/extensions/streaming/extension.js.map +1 -0
  187. package/dist/extras/extensions/streaming/extension.json +6 -0
  188. package/dist/extras/extensions/streaming/index.d.ts +2 -0
  189. package/dist/extras/extensions/streaming/index.d.ts.map +1 -0
  190. package/dist/extras/extensions/streaming/index.js +2 -0
  191. package/dist/extras/extensions/streaming/index.js.map +1 -0
  192. package/dist/extras/extensions/structured-output/extension.d.ts +11 -0
  193. package/dist/extras/extensions/structured-output/extension.d.ts.map +1 -0
  194. package/dist/extras/extensions/structured-output/extension.js +102 -0
  195. package/dist/extras/extensions/structured-output/extension.js.map +1 -0
  196. package/dist/extras/extensions/structured-output/extension.json +7 -0
  197. package/dist/extras/extensions/structured-output/index.d.ts +3 -0
  198. package/dist/extras/extensions/structured-output/index.d.ts.map +1 -0
  199. package/dist/extras/extensions/structured-output/index.js +2 -0
  200. package/dist/extras/extensions/structured-output/index.js.map +1 -0
  201. package/dist/extras/extensions/timestamp/extension.d.ts +11 -0
  202. package/dist/extras/extensions/timestamp/extension.d.ts.map +1 -0
  203. package/dist/extras/extensions/timestamp/extension.js +21 -0
  204. package/dist/extras/extensions/timestamp/extension.js.map +1 -0
  205. package/dist/extras/extensions/timestamp/extension.json +6 -0
  206. package/dist/extras/extensions/timestamp/index.d.ts +2 -0
  207. package/dist/extras/extensions/timestamp/index.d.ts.map +1 -0
  208. package/dist/extras/extensions/timestamp/index.js +2 -0
  209. package/dist/extras/extensions/timestamp/index.js.map +1 -0
  210. package/dist/extras/extensions/token-stats/extension.d.ts +50 -0
  211. package/dist/extras/extensions/token-stats/extension.d.ts.map +1 -0
  212. package/dist/extras/extensions/token-stats/extension.js +99 -0
  213. package/dist/extras/extensions/token-stats/extension.js.map +1 -0
  214. package/dist/extras/extensions/token-stats/extension.json +7 -0
  215. package/dist/extras/extensions/token-stats/extension.test.d.ts +2 -0
  216. package/dist/extras/extensions/token-stats/extension.test.d.ts.map +1 -0
  217. package/dist/extras/extensions/token-stats/extension.test.js +197 -0
  218. package/dist/extras/extensions/token-stats/extension.test.js.map +1 -0
  219. package/dist/extras/extensions/token-stats/index.d.ts +3 -0
  220. package/dist/extras/extensions/token-stats/index.d.ts.map +1 -0
  221. package/dist/extras/extensions/token-stats/index.js +2 -0
  222. package/dist/extras/extensions/token-stats/index.js.map +1 -0
  223. package/dist/extras/extensions/tool-buffer/extension.d.ts +89 -0
  224. package/dist/extras/extensions/tool-buffer/extension.d.ts.map +1 -0
  225. package/dist/extras/extensions/tool-buffer/extension.js +182 -0
  226. package/dist/extras/extensions/tool-buffer/extension.js.map +1 -0
  227. package/dist/extras/extensions/tool-buffer/extension.json +7 -0
  228. package/dist/extras/extensions/tool-buffer/extension.test.d.ts +2 -0
  229. package/dist/extras/extensions/tool-buffer/extension.test.d.ts.map +1 -0
  230. package/dist/extras/extensions/tool-buffer/extension.test.js +549 -0
  231. package/dist/extras/extensions/tool-buffer/extension.test.js.map +1 -0
  232. package/dist/extras/extensions/tool-buffer/index.d.ts +3 -0
  233. package/dist/extras/extensions/tool-buffer/index.d.ts.map +1 -0
  234. package/dist/extras/extensions/tool-buffer/index.js +2 -0
  235. package/dist/extras/extensions/tool-buffer/index.js.map +1 -0
  236. package/dist/extras/extensions/weather-tool/extension.d.ts +22 -0
  237. package/dist/extras/extensions/weather-tool/extension.d.ts.map +1 -0
  238. package/dist/extras/extensions/weather-tool/extension.js +33 -0
  239. package/dist/extras/extensions/weather-tool/extension.js.map +1 -0
  240. package/dist/extras/extensions/weather-tool/extension.json +7 -0
  241. package/dist/extras/extensions/weather-tool/index.d.ts +3 -0
  242. package/dist/extras/extensions/weather-tool/index.d.ts.map +1 -0
  243. package/dist/extras/extensions/weather-tool/index.js +2 -0
  244. package/dist/extras/extensions/weather-tool/index.js.map +1 -0
  245. package/dist/extras/models/ai-sdk-resolver.d.ts +15 -0
  246. package/dist/extras/models/ai-sdk-resolver.d.ts.map +1 -0
  247. package/dist/extras/models/ai-sdk-resolver.js +57 -0
  248. package/dist/extras/models/ai-sdk-resolver.js.map +1 -0
  249. package/dist/extras/models/index.d.ts +5 -0
  250. package/dist/extras/models/index.d.ts.map +1 -0
  251. package/dist/extras/models/index.js +3 -0
  252. package/dist/extras/models/index.js.map +1 -0
  253. package/dist/extras/models/routing-model.d.ts +18 -0
  254. package/dist/extras/models/routing-model.d.ts.map +1 -0
  255. package/dist/extras/models/routing-model.js +24 -0
  256. package/dist/extras/models/routing-model.js.map +1 -0
  257. package/dist/extras/openai-provider/index.d.ts +3 -0
  258. package/dist/extras/openai-provider/index.d.ts.map +1 -0
  259. package/dist/extras/openai-provider/index.js +2 -0
  260. package/dist/extras/openai-provider/index.js.map +1 -0
  261. package/dist/extras/openai-provider/server.d.ts +34 -0
  262. package/dist/extras/openai-provider/server.d.ts.map +1 -0
  263. package/dist/extras/openai-provider/server.js +335 -0
  264. package/dist/extras/openai-provider/server.js.map +1 -0
  265. package/dist/handle.d.ts +56 -0
  266. package/dist/handle.d.ts.map +1 -0
  267. package/dist/handle.js +2 -0
  268. package/dist/handle.js.map +1 -0
  269. package/dist/hooks.d.ts +98 -0
  270. package/dist/hooks.d.ts.map +1 -0
  271. package/dist/hooks.js +53 -0
  272. package/dist/hooks.js.map +1 -0
  273. package/dist/index.d.ts +16 -0
  274. package/dist/index.d.ts.map +1 -0
  275. package/dist/index.js +9 -0
  276. package/dist/index.js.map +1 -0
  277. package/dist/model.d.ts +75 -0
  278. package/dist/model.d.ts.map +1 -0
  279. package/dist/model.js +2 -0
  280. package/dist/model.js.map +1 -0
  281. package/dist/tool.d.ts +78 -0
  282. package/dist/tool.d.ts.map +1 -0
  283. package/dist/tool.js +78 -0
  284. package/dist/tool.js.map +1 -0
  285. package/dist/types.d.ts +83 -0
  286. package/dist/types.d.ts.map +1 -0
  287. package/dist/types.js +21 -0
  288. package/dist/types.js.map +1 -0
  289. package/package.json +178 -0
@@ -0,0 +1,99 @@
1
+ // Metadata key for accumulated usage across LLM calls in a turn.
2
+ const USAGE_KEY = '_tokenStats_usage';
3
+ /**
4
+ * Token-stats extension — appends a stats line to the agent's final
5
+ * response message.
6
+ *
7
+ * Accumulates token usage across all LLM calls in a turn (via
8
+ * `afterLLM`), then appends a summary to `turn.response.message` in
9
+ * `beforeResponse`. The stats include:
10
+ *
11
+ * - Total prompt + completion tokens
12
+ * - Cached and reasoning tokens (if reported)
13
+ * - Number of LLM calls / iterations
14
+ * - Context window usage percentage (if `contextWindow` is configured)
15
+ *
16
+ * ## Example output (Slack italics)
17
+ *
18
+ * ```
19
+ * _📊 105,445 prompt + 2,324 completion · 1,503 reasoning · 3 LLM calls · 82% of 128K context_
20
+ * ```
21
+ *
22
+ * ## Priority
23
+ *
24
+ * Uses priority -100 (runs late, after most extensions) so it sees
25
+ * the final response after other extensions have potentially modified
26
+ * it. The `afterLLM` accumulator runs at the same priority.
27
+ */
28
+ export default function createTokenStatsExtension(config) {
29
+ const contextWindow = config?.contextWindow;
30
+ const slackItalics = config?.slackItalics ?? true;
31
+ const label = config?.label ?? '📊';
32
+ return {
33
+ name: 'token-stats',
34
+ priority: -100,
35
+ install(agent) {
36
+ // ── afterLLM: accumulate usage ────────────────────────────────
37
+ agent.hook('afterLLM', 'token-stats', async (ctx) => {
38
+ const usage = ctx.modelResponse?.usage;
39
+ if (!usage)
40
+ return;
41
+ const meta = ctx.turn.metadata;
42
+ const acc = meta[USAGE_KEY] ?? {
43
+ promptTokens: 0,
44
+ completionTokens: 0,
45
+ cachedPromptTokens: 0,
46
+ cacheWriteTokens: 0,
47
+ reasoningTokens: 0,
48
+ calls: 0,
49
+ };
50
+ acc.promptTokens += usage.promptTokens ?? 0;
51
+ acc.completionTokens += usage.completionTokens ?? 0;
52
+ acc.cachedPromptTokens += usage.cachedPromptTokens ?? 0;
53
+ acc.cacheWriteTokens += usage.cacheWriteTokens ?? 0;
54
+ acc.reasoningTokens += usage.reasoningTokens ?? 0;
55
+ acc.calls += 1;
56
+ meta[USAGE_KEY] = acc;
57
+ });
58
+ // ── beforeResponse: append stats to the final message ─────────
59
+ agent.hook('beforeResponse', 'token-stats', async (ctx) => {
60
+ const response = ctx.turn.response;
61
+ if (!response)
62
+ return;
63
+ const acc = ctx.turn.metadata[USAGE_KEY];
64
+ if (!acc || acc.calls === 0)
65
+ return;
66
+ const parts = [
67
+ `${fmt(acc.promptTokens)} prompt + ${fmt(acc.completionTokens)} completion`,
68
+ ];
69
+ if (acc.cachedPromptTokens > 0 || acc.cacheWriteTokens > 0) {
70
+ const cacheParts = [];
71
+ if (acc.cachedPromptTokens > 0)
72
+ cacheParts.push(`${fmt(acc.cachedPromptTokens)} read`);
73
+ if (acc.cacheWriteTokens > 0)
74
+ cacheParts.push(`${fmt(acc.cacheWriteTokens)} written`);
75
+ parts.push(`cache: ${cacheParts.join(', ')}`);
76
+ }
77
+ if (acc.reasoningTokens > 0) {
78
+ parts.push(`${fmt(acc.reasoningTokens)} reasoning`);
79
+ }
80
+ parts.push(`${acc.calls} LLM call${acc.calls > 1 ? 's' : ''}`);
81
+ if (contextWindow) {
82
+ const pct = ((acc.promptTokens / contextWindow) * 100).toFixed(0);
83
+ parts.push(`${pct}% of ${fmt(contextWindow)} context`);
84
+ }
85
+ const line = parts.join(' · ');
86
+ const formatted = slackItalics ? `_${label} ${line}_` : `${label} ${line}`;
87
+ response.message = `${response.message}\n\n${formatted}`;
88
+ });
89
+ },
90
+ };
91
+ }
92
+ function fmt(n) {
93
+ if (n >= 1_000_000)
94
+ return `${(n / 1_000_000).toFixed(1)}M`;
95
+ if (n >= 1_000)
96
+ return `${(n / 1_000).toFixed(1)}K`;
97
+ return String(n);
98
+ }
99
+ //# sourceMappingURL=extension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.js","sourceRoot":"","sources":["../../../../src/extras/extensions/token-stats/extension.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "token-stats",
3
+ "description": "Appends token usage and context window stats to the agent's final response message. Accumulates usage across all LLM calls in a turn.",
4
+ "enabled": true,
5
+ "priority": -100,
6
+ "configKeys": ["contextWindow", "slackItalics", "label"]
7
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=extension.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.test.d.ts","sourceRoot":"","sources":["../../../../src/extras/extensions/token-stats/extension.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,197 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Agent } from '../../../agent.js';
3
+ import { createTokenStatsExtension } from './index.js';
4
+ // ── Mock model ─────────────────────────────────────────────────────
5
+ function mockModel(responses) {
6
+ let callCount = 0;
7
+ return {
8
+ async generate(req) {
9
+ const resp = responses[Math.min(callCount, responses.length - 1)];
10
+ callCount++;
11
+ if (typeof resp.message === 'function') {
12
+ return { ...resp, message: resp.message(req) };
13
+ }
14
+ return resp;
15
+ },
16
+ };
17
+ }
18
+ // ── Tests ──────────────────────────────────────────────────────────
19
+ describe('token-stats extension', () => {
20
+ it('appends stats to the final response', async () => {
21
+ const model = mockModel([{
22
+ message: { role: 'assistant', content: 'Hello!' },
23
+ finishReason: 'stop',
24
+ usage: { promptTokens: 1000, completionTokens: 50 },
25
+ }]);
26
+ const ext = createTokenStatsExtension({ contextWindow: 128000 });
27
+ const agent = new Agent({ model: model });
28
+ agent.use(ext);
29
+ const result = await agent.run({ message: 'hi' });
30
+ expect(result.message).toContain('Hello!');
31
+ expect(result.message).toContain('📊');
32
+ expect(result.message).toContain('1.0K prompt + 50 completion');
33
+ expect(result.message).toContain('1 LLM call');
34
+ expect(result.message).toContain('1% of 128.0K context');
35
+ });
36
+ it('uses Slack italics by default', async () => {
37
+ const model = mockModel([{
38
+ message: { role: 'assistant', content: 'Hi' },
39
+ finishReason: 'stop',
40
+ usage: { promptTokens: 100, completionTokens: 10 },
41
+ }]);
42
+ const ext = createTokenStatsExtension({ contextWindow: 128000 });
43
+ const agent = new Agent({ model: model });
44
+ agent.use(ext);
45
+ const result = await agent.run({ message: 'hi' });
46
+ // Should be wrapped in _..._ for Slack italics.
47
+ expect(result.message).toMatch(/_📊.*_/);
48
+ });
49
+ it('disables italics when configured', async () => {
50
+ const model = mockModel([{
51
+ message: { role: 'assistant', content: 'Hi' },
52
+ finishReason: 'stop',
53
+ usage: { promptTokens: 100, completionTokens: 10 },
54
+ }]);
55
+ const ext = createTokenStatsExtension({ contextWindow: 128000, slackItalics: false });
56
+ const agent = new Agent({ model: model });
57
+ agent.use(ext);
58
+ const result = await agent.run({ message: 'hi' });
59
+ expect(result.message).not.toMatch(/_📊.*_/);
60
+ expect(result.message).toContain('📊');
61
+ });
62
+ it('omits context percentage when contextWindow not set', async () => {
63
+ const model = mockModel([{
64
+ message: { role: 'assistant', content: 'Hi' },
65
+ finishReason: 'stop',
66
+ usage: { promptTokens: 100, completionTokens: 10 },
67
+ }]);
68
+ const ext = createTokenStatsExtension();
69
+ const agent = new Agent({ model: model });
70
+ agent.use(ext);
71
+ const result = await agent.run({ message: 'hi' });
72
+ expect(result.message).toContain('100 prompt + 10 completion');
73
+ expect(result.message).not.toContain('context');
74
+ });
75
+ it('accumulates usage across multiple LLM calls', async () => {
76
+ const tool = {
77
+ name: 'echo',
78
+ description: 'Echo',
79
+ parameters: { type: 'object', properties: {} },
80
+ async execute() {
81
+ return { toolCallId: '', content: 'echoed' };
82
+ },
83
+ };
84
+ const model = mockModel([
85
+ {
86
+ message: {
87
+ role: 'assistant',
88
+ content: '',
89
+ toolCalls: [{ id: 'tc1', name: 'echo', arguments: '{}' }],
90
+ },
91
+ finishReason: 'tool_calls',
92
+ usage: { promptTokens: 500, completionTokens: 20 },
93
+ },
94
+ {
95
+ message: { role: 'assistant', content: 'Done' },
96
+ finishReason: 'stop',
97
+ usage: { promptTokens: 800, completionTokens: 30 },
98
+ },
99
+ ]);
100
+ const ext = createTokenStatsExtension({ contextWindow: 128000 });
101
+ const agent = new Agent({ model: model, tools: [tool] });
102
+ agent.use(ext);
103
+ const result = await agent.run({ message: 'call echo' });
104
+ // Should show accumulated totals: 500+800=1300 prompt, 20+30=50 completion.
105
+ expect(result.message).toContain('1.3K prompt + 50 completion');
106
+ expect(result.message).toContain('2 LLM calls');
107
+ });
108
+ it('shows cache read, cache write, and reasoning tokens when present', async () => {
109
+ const model = mockModel([{
110
+ message: { role: 'assistant', content: 'Hi' },
111
+ finishReason: 'stop',
112
+ usage: {
113
+ promptTokens: 10000,
114
+ completionTokens: 100,
115
+ cachedPromptTokens: 5000,
116
+ cacheWriteTokens: 3000,
117
+ reasoningTokens: 200,
118
+ },
119
+ }]);
120
+ const ext = createTokenStatsExtension({ contextWindow: 128000 });
121
+ const agent = new Agent({ model: model });
122
+ agent.use(ext);
123
+ const result = await agent.run({ message: 'hi' });
124
+ expect(result.message).toContain('cache:');
125
+ expect(result.message).toContain('5.0K read');
126
+ expect(result.message).toContain('3.0K written');
127
+ expect(result.message).toContain('200 reasoning');
128
+ });
129
+ it('shows only cache read when no cache write', async () => {
130
+ const model = mockModel([{
131
+ message: { role: 'assistant', content: 'Hi' },
132
+ finishReason: 'stop',
133
+ usage: {
134
+ promptTokens: 10000,
135
+ completionTokens: 100,
136
+ cachedPromptTokens: 5000,
137
+ },
138
+ }]);
139
+ const ext = createTokenStatsExtension({ contextWindow: 128000 });
140
+ const agent = new Agent({ model: model });
141
+ agent.use(ext);
142
+ const result = await agent.run({ message: 'hi' });
143
+ expect(result.message).toContain('cache:');
144
+ expect(result.message).toContain('5.0K read');
145
+ expect(result.message).not.toContain('written');
146
+ });
147
+ it('omits cache/reasoning when not reported', async () => {
148
+ const model = mockModel([{
149
+ message: { role: 'assistant', content: 'Hi' },
150
+ finishReason: 'stop',
151
+ usage: { promptTokens: 100, completionTokens: 10 },
152
+ }]);
153
+ const ext = createTokenStatsExtension({ contextWindow: 128000 });
154
+ const agent = new Agent({ model: model });
155
+ agent.use(ext);
156
+ const result = await agent.run({ message: 'hi' });
157
+ expect(result.message).not.toContain('cached');
158
+ expect(result.message).not.toContain('reasoning');
159
+ });
160
+ it('does not append stats when no usage is reported', async () => {
161
+ const model = mockModel([{
162
+ message: { role: 'assistant', content: 'Hi' },
163
+ finishReason: 'stop',
164
+ }]);
165
+ const ext = createTokenStatsExtension({ contextWindow: 128000 });
166
+ const agent = new Agent({ model: model });
167
+ agent.use(ext);
168
+ const result = await agent.run({ message: 'hi' });
169
+ expect(result.message).toBe('Hi');
170
+ });
171
+ it('supports custom label', async () => {
172
+ const model = mockModel([{
173
+ message: { role: 'assistant', content: 'Hi' },
174
+ finishReason: 'stop',
175
+ usage: { promptTokens: 100, completionTokens: 10 },
176
+ }]);
177
+ const ext = createTokenStatsExtension({ contextWindow: 128000, label: 'Tokens:' });
178
+ const agent = new Agent({ model: model });
179
+ agent.use(ext);
180
+ const result = await agent.run({ message: 'hi' });
181
+ expect(result.message).toContain('Tokens:');
182
+ expect(result.message).not.toContain('📊');
183
+ });
184
+ it('computes context percentage correctly', async () => {
185
+ const model = mockModel([{
186
+ message: { role: 'assistant', content: 'Hi' },
187
+ finishReason: 'stop',
188
+ usage: { promptTokens: 64000, completionTokens: 100 },
189
+ }]);
190
+ const ext = createTokenStatsExtension({ contextWindow: 128000 });
191
+ const agent = new Agent({ model: model });
192
+ agent.use(ext);
193
+ const result = await agent.run({ message: 'hi' });
194
+ expect(result.message).toContain('50% of 128.0K context');
195
+ });
196
+ });
197
+ //# sourceMappingURL=extension.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.test.js","sourceRoot":"","sources":["../../../../src/extras/extensions/token-stats/extension.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { default as createTokenStatsExtension } from './extension.js';
2
+ export type { TokenStatsConfig } from './extension.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/extras/extensions/token-stats/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { default as createTokenStatsExtension } from './extension.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/extras/extensions/token-stats/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,89 @@
1
+ import type { Extension } from '../../../extension.js';
2
+ /**
3
+ * Config for the tool-buffer extension.
4
+ */
5
+ export interface ToolBufferConfig {
6
+ /**
7
+ * Directory to store buffered tool outputs. Created if it doesn't exist.
8
+ * Default: `./tool-buffers`.
9
+ */
10
+ bufferDir?: string;
11
+ /**
12
+ * Minimum content length (characters) to trigger buffering. Tool
13
+ * results shorter than this are passed through unchanged. Default:
14
+ * `2000`.
15
+ */
16
+ threshold?: number;
17
+ /**
18
+ * Tool names to buffer (whitelist). If set, only these tools are
19
+ * buffered. If omitted, all tools are eligible. Default: all tools.
20
+ */
21
+ tools?: string[];
22
+ /**
23
+ * Tool names to exclude from buffering (blacklist). Takes precedence
24
+ * over `tools`. Default: none.
25
+ */
26
+ excludeTools?: string[];
27
+ /**
28
+ * Whether to preserve the first N characters of the original content
29
+ * in the pointer message, giving the LLM a preview without needing
30
+ * to grep. Default: `200`.
31
+ */
32
+ previewLength?: number;
33
+ /**
34
+ * Prefix for buffer file names. A random ID is appended. Default:
35
+ * `buffer_`.
36
+ */
37
+ filePrefix?: string;
38
+ /**
39
+ * Name of the no-buffer control tool exposed to the LLM. Default:
40
+ * `no_buffer`.
41
+ */
42
+ noBufferToolName?: string;
43
+ }
44
+ /**
45
+ * Tool-buffer extension — redirects large tool outputs to files.
46
+ *
47
+ * Hooks `afterTool` and checks the result content size. If it exceeds
48
+ * the configured threshold, the content is written to a file in
49
+ * `bufferDir` and the tool result is replaced with a pointer message:
50
+ *
51
+ * ```
52
+ * [tool-buffer] Output saved to tool-buffers/buffer_a1b2c3.txt
53
+ * (234 lines, 12.5 KB). Use grep to search within this file.
54
+ *
55
+ * Preview (first 200 chars):
56
+ * {"jobs":[{"id":"JT-001","name":"Kitchen remodel"},…
57
+ * ```
58
+ *
59
+ * This keeps the LLM context small when tools return large datasets
60
+ * (e.g. list_jobs, list_messages, search results) while preserving
61
+ * full access to the data via a grep tool or filesystem reads.
62
+ *
63
+ * By default, all tools are buffered. Use `excludeTools` to exempt
64
+ * tools whose output the LLM always needs inline (e.g. read_file,
65
+ * send_message, status checks). The LLM can also call `no_buffer`
66
+ * at runtime to bypass buffering on demand.
67
+ *
68
+ * ## LLM-controlled bypass
69
+ *
70
+ * The extension registers a `no_buffer` tool that the LLM can call to
71
+ * disable buffering on demand — either for the next tool call only or
72
+ * for the rest of the turn. This gives the LLM real-time control without
73
+ * any tool knowing about buffering.
74
+ *
75
+ * - `no_buffer({ scope: "next" })` — skip buffering for the next tool
76
+ * call only. The flag is cleared after one tool executes.
77
+ * - `no_buffer({ scope: "turn" })` — skip buffering for all remaining
78
+ * tool calls in this turn.
79
+ *
80
+ * The LLM typically calls `no_buffer` before a tool whose full output
81
+ * it needs inline (e.g. reading a file it intends to summarize).
82
+ *
83
+ * ## Priority
84
+ *
85
+ * Uses priority -50 (runs after most extensions) so it sees the final
86
+ * tool result after other extensions have potentially modified it.
87
+ */
88
+ export default function createToolBufferExtension(config?: ToolBufferConfig): Extension;
89
+ //# sourceMappingURL=extension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../../../../src/extras/extensions/tool-buffer/extension.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIvD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAC/C,MAAM,CAAC,EAAE,gBAAgB,GACxB,SAAS,CAyIX"}
@@ -0,0 +1,182 @@
1
+ import { writeFileSync, mkdirSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { randomBytes } from 'node:crypto';
4
+ // ── Turn-level flag keys (stored in turn.metadata) ──────────────────
5
+ // These are internal to the extension and not part of the public API.
6
+ const FLAG_NEXT = '_toolBuffer_skipNext';
7
+ const FLAG_TURN = '_toolBuffer_skipTurn';
8
+ /**
9
+ * Tool-buffer extension — redirects large tool outputs to files.
10
+ *
11
+ * Hooks `afterTool` and checks the result content size. If it exceeds
12
+ * the configured threshold, the content is written to a file in
13
+ * `bufferDir` and the tool result is replaced with a pointer message:
14
+ *
15
+ * ```
16
+ * [tool-buffer] Output saved to tool-buffers/buffer_a1b2c3.txt
17
+ * (234 lines, 12.5 KB). Use grep to search within this file.
18
+ *
19
+ * Preview (first 200 chars):
20
+ * {"jobs":[{"id":"JT-001","name":"Kitchen remodel"},…
21
+ * ```
22
+ *
23
+ * This keeps the LLM context small when tools return large datasets
24
+ * (e.g. list_jobs, list_messages, search results) while preserving
25
+ * full access to the data via a grep tool or filesystem reads.
26
+ *
27
+ * By default, all tools are buffered. Use `excludeTools` to exempt
28
+ * tools whose output the LLM always needs inline (e.g. read_file,
29
+ * send_message, status checks). The LLM can also call `no_buffer`
30
+ * at runtime to bypass buffering on demand.
31
+ *
32
+ * ## LLM-controlled bypass
33
+ *
34
+ * The extension registers a `no_buffer` tool that the LLM can call to
35
+ * disable buffering on demand — either for the next tool call only or
36
+ * for the rest of the turn. This gives the LLM real-time control without
37
+ * any tool knowing about buffering.
38
+ *
39
+ * - `no_buffer({ scope: "next" })` — skip buffering for the next tool
40
+ * call only. The flag is cleared after one tool executes.
41
+ * - `no_buffer({ scope: "turn" })` — skip buffering for all remaining
42
+ * tool calls in this turn.
43
+ *
44
+ * The LLM typically calls `no_buffer` before a tool whose full output
45
+ * it needs inline (e.g. reading a file it intends to summarize).
46
+ *
47
+ * ## Priority
48
+ *
49
+ * Uses priority -50 (runs after most extensions) so it sees the final
50
+ * tool result after other extensions have potentially modified it.
51
+ */
52
+ export default function createToolBufferExtension(config) {
53
+ const bufferDir = config?.bufferDir ?? './tool-buffers';
54
+ const threshold = config?.threshold ?? 2000;
55
+ const whitelist = config?.tools ? new Set(config.tools) : undefined;
56
+ const blacklist = config?.excludeTools ? new Set(config.excludeTools) : undefined;
57
+ const previewLength = config?.previewLength ?? 200;
58
+ const filePrefix = config?.filePrefix ?? 'buffer_';
59
+ const noBufferToolName = config?.noBufferToolName ?? 'no_buffer';
60
+ // Ensure buffer directory exists at setup time.
61
+ if (!existsSync(bufferDir)) {
62
+ mkdirSync(bufferDir, { recursive: true });
63
+ }
64
+ function shouldBuffer(toolName) {
65
+ if (blacklist?.has(toolName))
66
+ return false;
67
+ if (whitelist && !whitelist.has(toolName))
68
+ return false;
69
+ return true;
70
+ }
71
+ function formatSize(bytes) {
72
+ if (bytes < 1024)
73
+ return `${bytes} B`;
74
+ if (bytes < 1024 * 1024)
75
+ return `${(bytes / 1024).toFixed(1)} KB`;
76
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
77
+ }
78
+ // ── no_buffer tool ────────────────────────────────────────────────
79
+ const noBufferTool = {
80
+ name: noBufferToolName,
81
+ description: 'Disable tool output buffering. Call this before a tool whose full output you need inline ' +
82
+ '(e.g. reading a file you intend to summarize or quote). ' +
83
+ 'scope "next" skips buffering for the next tool call only. ' +
84
+ 'scope "turn" skips buffering for all remaining tool calls this turn.',
85
+ parameters: {
86
+ type: 'object',
87
+ properties: {
88
+ scope: {
89
+ type: 'string',
90
+ enum: ['next', 'turn'],
91
+ description: '"next" = skip buffering for the next tool call only (default). "turn" = skip for all remaining tool calls this turn.',
92
+ default: 'next',
93
+ },
94
+ },
95
+ },
96
+ async execute(args, ctx) {
97
+ const scope = args.scope ?? 'next';
98
+ if (scope === 'turn') {
99
+ ctx.metadata[FLAG_TURN] = true;
100
+ }
101
+ else {
102
+ ctx.metadata[FLAG_NEXT] = true;
103
+ }
104
+ return {
105
+ toolCallId: '',
106
+ content: `Buffering disabled for ${scope === 'turn' ? 'the rest of this turn' : 'the next tool call'}.`,
107
+ };
108
+ },
109
+ };
110
+ return {
111
+ name: 'tool-buffer',
112
+ priority: -50,
113
+ install(agent) {
114
+ // Register the no_buffer tool so the LLM can call it.
115
+ agent.tool(noBufferTool);
116
+ agent.hook('afterTool', 'tool-buffer', async (ctx) => {
117
+ if (!ctx.toolCall || !ctx.toolResult)
118
+ return;
119
+ // The no_buffer tool itself is never buffered.
120
+ if (ctx.toolCall.name === noBufferToolName)
121
+ return;
122
+ const toolName = ctx.toolCall.name;
123
+ const result = ctx.toolResult;
124
+ const meta = ctx.turn.metadata;
125
+ // Check bypass flags set by the no_buffer tool.
126
+ const skipTurn = meta[FLAG_TURN] === true;
127
+ const skipNext = meta[FLAG_NEXT] === true;
128
+ if (skipTurn || skipNext) {
129
+ // Clear the one-shot flag (turn flag persists).
130
+ if (skipNext)
131
+ delete meta[FLAG_NEXT];
132
+ return;
133
+ }
134
+ // Don't buffer error results — the LLM needs to see those inline.
135
+ if (result.isError)
136
+ return;
137
+ // Don't buffer empty or short results.
138
+ const content = result.content;
139
+ if (!content || content.length < threshold)
140
+ return;
141
+ // Check whitelist/blacklist.
142
+ if (!shouldBuffer(toolName))
143
+ return;
144
+ // Generate a unique filename.
145
+ const id = randomBytes(4).toString('hex');
146
+ const fileName = `${filePrefix}${id}.txt`;
147
+ const filePath = join(bufferDir, fileName);
148
+ // Write the full content to the buffer file.
149
+ try {
150
+ writeFileSync(filePath, content, 'utf-8');
151
+ }
152
+ catch (err) {
153
+ // If we can't write, pass the result through unchanged.
154
+ // Don't break the tool call over a buffering failure.
155
+ return;
156
+ }
157
+ // Build the pointer message.
158
+ const lineCount = content.split('\n').length;
159
+ const sizeBytes = Buffer.byteLength(content, 'utf-8');
160
+ const preview = content.slice(0, previewLength);
161
+ const previewTruncated = content.length > previewLength ? '…' : '';
162
+ const pointer = [
163
+ `[tool-buffer] Output saved to ${filePath}`,
164
+ `(${lineCount} lines, ${formatSize(sizeBytes)}). Use grep to search within this file.`,
165
+ '',
166
+ `If you need the full output inline, call no_buffer({ scope: "next" }) and re-run this tool.`,
167
+ '',
168
+ `Preview (first ${previewLength} chars):`,
169
+ preview + previewTruncated,
170
+ ].join('\n');
171
+ // Replace the tool result with the pointer.
172
+ const bufferedResult = {
173
+ toolCallId: result.toolCallId,
174
+ content: pointer,
175
+ isError: false,
176
+ };
177
+ return { skip: true, value: bufferedResult };
178
+ });
179
+ },
180
+ };
181
+ }
182
+ //# sourceMappingURL=extension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.js","sourceRoot":"","sources":["../../../../src/extras/extensions/tool-buffer/extension.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "tool-buffer",
3
+ "description": "Redirects large tool outputs to files and returns a pointer to the LLM. Keeps context small when tools return large datasets. Pair with a grep tool for searching buffered content.",
4
+ "enabled": true,
5
+ "priority": -50,
6
+ "configKeys": ["bufferDir", "threshold", "tools", "excludeTools", "previewLength", "filePrefix"]
7
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=extension.test.d.ts.map