@autonome-research/thread-phase 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/dist/agent/index.d.ts +28 -0
  4. package/dist/agent/index.d.ts.map +1 -0
  5. package/dist/agent/index.js +28 -0
  6. package/dist/agent/index.js.map +1 -0
  7. package/dist/agent/openai-adapter.d.ts +15 -0
  8. package/dist/agent/openai-adapter.d.ts.map +1 -0
  9. package/dist/agent/openai-adapter.js +57 -0
  10. package/dist/agent/openai-adapter.js.map +1 -0
  11. package/dist/agent/parse-json.d.ts +12 -0
  12. package/dist/agent/parse-json.d.ts.map +1 -0
  13. package/dist/agent/parse-json.js +31 -0
  14. package/dist/agent/parse-json.js.map +1 -0
  15. package/dist/agent/retry.d.ts +15 -0
  16. package/dist/agent/retry.d.ts.map +1 -0
  17. package/dist/agent/retry.js +35 -0
  18. package/dist/agent/retry.js.map +1 -0
  19. package/dist/agent/runner.d.ts +25 -0
  20. package/dist/agent/runner.d.ts.map +1 -0
  21. package/dist/agent/runner.js +270 -0
  22. package/dist/agent/runner.js.map +1 -0
  23. package/dist/agent/stream-consumer.d.ts +57 -0
  24. package/dist/agent/stream-consumer.d.ts.map +1 -0
  25. package/dist/agent/stream-consumer.js +126 -0
  26. package/dist/agent/stream-consumer.js.map +1 -0
  27. package/dist/agent/types.d.ts +135 -0
  28. package/dist/agent/types.d.ts.map +1 -0
  29. package/dist/agent/types.js +9 -0
  30. package/dist/agent/types.js.map +1 -0
  31. package/dist/agent-runner.d.ts +10 -0
  32. package/dist/agent-runner.d.ts.map +1 -0
  33. package/dist/agent-runner.js +10 -0
  34. package/dist/agent-runner.js.map +1 -0
  35. package/dist/agents/capability.d.ts +36 -0
  36. package/dist/agents/capability.d.ts.map +1 -0
  37. package/dist/agents/capability.js +51 -0
  38. package/dist/agents/capability.js.map +1 -0
  39. package/dist/agents/event-bus.d.ts +20 -0
  40. package/dist/agents/event-bus.d.ts.map +1 -0
  41. package/dist/agents/event-bus.js +40 -0
  42. package/dist/agents/event-bus.js.map +1 -0
  43. package/dist/agents/index.d.ts +23 -0
  44. package/dist/agents/index.d.ts.map +1 -0
  45. package/dist/agents/index.js +33 -0
  46. package/dist/agents/index.js.map +1 -0
  47. package/dist/agents/inference-adapter.d.ts +52 -0
  48. package/dist/agents/inference-adapter.d.ts.map +1 -0
  49. package/dist/agents/inference-adapter.js +209 -0
  50. package/dist/agents/inference-adapter.js.map +1 -0
  51. package/dist/agents/job-store-bridge.d.ts +44 -0
  52. package/dist/agents/job-store-bridge.d.ts.map +1 -0
  53. package/dist/agents/job-store-bridge.js +58 -0
  54. package/dist/agents/job-store-bridge.js.map +1 -0
  55. package/dist/agents/memory.d.ts +40 -0
  56. package/dist/agents/memory.d.ts.map +1 -0
  57. package/dist/agents/memory.js +14 -0
  58. package/dist/agents/memory.js.map +1 -0
  59. package/dist/agents/protocol.d.ts +302 -0
  60. package/dist/agents/protocol.d.ts.map +1 -0
  61. package/dist/agents/protocol.js +36 -0
  62. package/dist/agents/protocol.js.map +1 -0
  63. package/dist/agents/run-helpers.d.ts +70 -0
  64. package/dist/agents/run-helpers.d.ts.map +1 -0
  65. package/dist/agents/run-helpers.js +131 -0
  66. package/dist/agents/run-helpers.js.map +1 -0
  67. package/dist/agents/serialize-error.d.ts +18 -0
  68. package/dist/agents/serialize-error.d.ts.map +1 -0
  69. package/dist/agents/serialize-error.js +27 -0
  70. package/dist/agents/serialize-error.js.map +1 -0
  71. package/dist/agents/structured-output.d.ts +90 -0
  72. package/dist/agents/structured-output.d.ts.map +1 -0
  73. package/dist/agents/structured-output.js +101 -0
  74. package/dist/agents/structured-output.js.map +1 -0
  75. package/dist/agents/test-utils/conformance.d.ts +59 -0
  76. package/dist/agents/test-utils/conformance.d.ts.map +1 -0
  77. package/dist/agents/test-utils/conformance.js +207 -0
  78. package/dist/agents/test-utils/conformance.js.map +1 -0
  79. package/dist/agents/test-utils/index.d.ts +12 -0
  80. package/dist/agents/test-utils/index.d.ts.map +1 -0
  81. package/dist/agents/test-utils/index.js +12 -0
  82. package/dist/agents/test-utils/index.js.map +1 -0
  83. package/dist/agents/test-utils/mock-agent.d.ts +66 -0
  84. package/dist/agents/test-utils/mock-agent.d.ts.map +1 -0
  85. package/dist/agents/test-utils/mock-agent.js +244 -0
  86. package/dist/agents/test-utils/mock-agent.js.map +1 -0
  87. package/dist/agents/thread.d.ts +57 -0
  88. package/dist/agents/thread.d.ts.map +1 -0
  89. package/dist/agents/thread.js +128 -0
  90. package/dist/agents/thread.js.map +1 -0
  91. package/dist/agents/turn-accumulator.d.ts +94 -0
  92. package/dist/agents/turn-accumulator.d.ts.map +1 -0
  93. package/dist/agents/turn-accumulator.js +150 -0
  94. package/dist/agents/turn-accumulator.js.map +1 -0
  95. package/dist/agents/with-memory.d.ts +55 -0
  96. package/dist/agents/with-memory.d.ts.map +1 -0
  97. package/dist/agents/with-memory.js +155 -0
  98. package/dist/agents/with-memory.js.map +1 -0
  99. package/dist/agents/with-thread.d.ts +45 -0
  100. package/dist/agents/with-thread.d.ts.map +1 -0
  101. package/dist/agents/with-thread.js +70 -0
  102. package/dist/agents/with-thread.js.map +1 -0
  103. package/dist/cache.d.ts +47 -0
  104. package/dist/cache.d.ts.map +1 -0
  105. package/dist/cache.js +81 -0
  106. package/dist/cache.js.map +1 -0
  107. package/dist/context/compressor.d.ts +36 -0
  108. package/dist/context/compressor.d.ts.map +1 -0
  109. package/dist/context/compressor.js +158 -0
  110. package/dist/context/compressor.js.map +1 -0
  111. package/dist/context/index.d.ts +4 -0
  112. package/dist/context/index.d.ts.map +1 -0
  113. package/dist/context/index.js +4 -0
  114. package/dist/context/index.js.map +1 -0
  115. package/dist/context/result-capper.d.ts +32 -0
  116. package/dist/context/result-capper.d.ts.map +1 -0
  117. package/dist/context/result-capper.js +50 -0
  118. package/dist/context/result-capper.js.map +1 -0
  119. package/dist/context/token-budget.d.ts +81 -0
  120. package/dist/context/token-budget.d.ts.map +1 -0
  121. package/dist/context/token-budget.js +99 -0
  122. package/dist/context/token-budget.js.map +1 -0
  123. package/dist/helpers/caller.d.ts +18 -0
  124. package/dist/helpers/caller.d.ts.map +1 -0
  125. package/dist/helpers/caller.js +40 -0
  126. package/dist/helpers/caller.js.map +1 -0
  127. package/dist/helpers/hook.d.ts +73 -0
  128. package/dist/helpers/hook.d.ts.map +1 -0
  129. package/dist/helpers/hook.js +244 -0
  130. package/dist/helpers/hook.js.map +1 -0
  131. package/dist/helpers/index.d.ts +12 -0
  132. package/dist/helpers/index.d.ts.map +1 -0
  133. package/dist/helpers/index.js +11 -0
  134. package/dist/helpers/index.js.map +1 -0
  135. package/dist/helpers/one-shot.d.ts +27 -0
  136. package/dist/helpers/one-shot.d.ts.map +1 -0
  137. package/dist/helpers/one-shot.js +43 -0
  138. package/dist/helpers/one-shot.js.map +1 -0
  139. package/dist/helpers/schedule.d.ts +59 -0
  140. package/dist/helpers/schedule.d.ts.map +1 -0
  141. package/dist/helpers/schedule.js +118 -0
  142. package/dist/helpers/schedule.js.map +1 -0
  143. package/dist/helpers/types.d.ts +34 -0
  144. package/dist/helpers/types.d.ts.map +1 -0
  145. package/dist/helpers/types.js +11 -0
  146. package/dist/helpers/types.js.map +1 -0
  147. package/dist/index.d.ts +26 -0
  148. package/dist/index.d.ts.map +1 -0
  149. package/dist/index.js +37 -0
  150. package/dist/index.js.map +1 -0
  151. package/dist/inference.d.ts +27 -0
  152. package/dist/inference.d.ts.map +1 -0
  153. package/dist/inference.js +34 -0
  154. package/dist/inference.js.map +1 -0
  155. package/dist/messages.d.ts +64 -0
  156. package/dist/messages.d.ts.map +1 -0
  157. package/dist/messages.js +17 -0
  158. package/dist/messages.js.map +1 -0
  159. package/dist/orchestrator.d.ts +56 -0
  160. package/dist/orchestrator.d.ts.map +1 -0
  161. package/dist/orchestrator.js +62 -0
  162. package/dist/orchestrator.js.map +1 -0
  163. package/dist/patterns/bounded-fanout-of.d.ts +61 -0
  164. package/dist/patterns/bounded-fanout-of.d.ts.map +1 -0
  165. package/dist/patterns/bounded-fanout-of.js +142 -0
  166. package/dist/patterns/bounded-fanout-of.js.map +1 -0
  167. package/dist/patterns/bounded-fanout.d.ts +111 -0
  168. package/dist/patterns/bounded-fanout.d.ts.map +1 -0
  169. package/dist/patterns/bounded-fanout.js +151 -0
  170. package/dist/patterns/bounded-fanout.js.map +1 -0
  171. package/dist/patterns/index.d.ts +14 -0
  172. package/dist/patterns/index.d.ts.map +1 -0
  173. package/dist/patterns/index.js +13 -0
  174. package/dist/patterns/index.js.map +1 -0
  175. package/dist/patterns/intent-gate.d.ts +27 -0
  176. package/dist/patterns/intent-gate.d.ts.map +1 -0
  177. package/dist/patterns/intent-gate.js +32 -0
  178. package/dist/patterns/intent-gate.js.map +1 -0
  179. package/dist/patterns/match.d.ts +30 -0
  180. package/dist/patterns/match.d.ts.map +1 -0
  181. package/dist/patterns/match.js +58 -0
  182. package/dist/patterns/match.js.map +1 -0
  183. package/dist/patterns/parallel-fanout.d.ts +28 -0
  184. package/dist/patterns/parallel-fanout.d.ts.map +1 -0
  185. package/dist/patterns/parallel-fanout.js +24 -0
  186. package/dist/patterns/parallel-fanout.js.map +1 -0
  187. package/dist/patterns/parallel-phases.d.ts +27 -0
  188. package/dist/patterns/parallel-phases.d.ts.map +1 -0
  189. package/dist/patterns/parallel-phases.js +77 -0
  190. package/dist/patterns/parallel-phases.js.map +1 -0
  191. package/dist/patterns/preflight-confidence.d.ts +20 -0
  192. package/dist/patterns/preflight-confidence.d.ts.map +1 -0
  193. package/dist/patterns/preflight-confidence.js +38 -0
  194. package/dist/patterns/preflight-confidence.js.map +1 -0
  195. package/dist/patterns/spot-check.d.ts +19 -0
  196. package/dist/patterns/spot-check.d.ts.map +1 -0
  197. package/dist/patterns/spot-check.js +33 -0
  198. package/dist/patterns/spot-check.js.map +1 -0
  199. package/dist/patterns/sub-pipeline.d.ts +84 -0
  200. package/dist/patterns/sub-pipeline.d.ts.map +1 -0
  201. package/dist/patterns/sub-pipeline.js +90 -0
  202. package/dist/patterns/sub-pipeline.js.map +1 -0
  203. package/dist/patterns/synthesize-with-followup.d.ts +35 -0
  204. package/dist/patterns/synthesize-with-followup.d.ts.map +1 -0
  205. package/dist/patterns/synthesize-with-followup.js +45 -0
  206. package/dist/patterns/synthesize-with-followup.js.map +1 -0
  207. package/dist/patterns/while-condition.d.ts +31 -0
  208. package/dist/patterns/while-condition.d.ts.map +1 -0
  209. package/dist/patterns/while-condition.js +59 -0
  210. package/dist/patterns/while-condition.js.map +1 -0
  211. package/dist/patterns/with-retry.d.ts +37 -0
  212. package/dist/patterns/with-retry.d.ts.map +1 -0
  213. package/dist/patterns/with-retry.js +73 -0
  214. package/dist/patterns/with-retry.js.map +1 -0
  215. package/dist/phase.d.ts +78 -0
  216. package/dist/phase.d.ts.map +1 -0
  217. package/dist/phase.js +36 -0
  218. package/dist/phase.js.map +1 -0
  219. package/dist/session/index.d.ts +5 -0
  220. package/dist/session/index.d.ts.map +1 -0
  221. package/dist/session/index.js +4 -0
  222. package/dist/session/index.js.map +1 -0
  223. package/dist/session/job-runner.d.ts +67 -0
  224. package/dist/session/job-runner.d.ts.map +1 -0
  225. package/dist/session/job-runner.js +131 -0
  226. package/dist/session/job-runner.js.map +1 -0
  227. package/dist/session/job-store.d.ts +98 -0
  228. package/dist/session/job-store.d.ts.map +1 -0
  229. package/dist/session/job-store.js +37 -0
  230. package/dist/session/job-store.js.map +1 -0
  231. package/dist/session/sqlite-job-store.d.ts +40 -0
  232. package/dist/session/sqlite-job-store.d.ts.map +1 -0
  233. package/dist/session/sqlite-job-store.js +200 -0
  234. package/dist/session/sqlite-job-store.js.map +1 -0
  235. package/dist/session/sse.d.ts +60 -0
  236. package/dist/session/sse.d.ts.map +1 -0
  237. package/dist/session/sse.js +97 -0
  238. package/dist/session/sse.js.map +1 -0
  239. package/dist/tools/index.d.ts +2 -0
  240. package/dist/tools/index.d.ts.map +1 -0
  241. package/dist/tools/index.js +2 -0
  242. package/dist/tools/index.js.map +1 -0
  243. package/dist/tools/registry.d.ts +44 -0
  244. package/dist/tools/registry.d.ts.map +1 -0
  245. package/dist/tools/registry.js +74 -0
  246. package/dist/tools/registry.js.map +1 -0
  247. package/dist/triggers/index.d.ts +15 -0
  248. package/dist/triggers/index.d.ts.map +1 -0
  249. package/dist/triggers/index.js +14 -0
  250. package/dist/triggers/index.js.map +1 -0
  251. package/dist/triggers/run-trigger.d.ts +86 -0
  252. package/dist/triggers/run-trigger.d.ts.map +1 -0
  253. package/dist/triggers/run-trigger.js +146 -0
  254. package/dist/triggers/run-trigger.js.map +1 -0
  255. package/dist/triggers/timer-trigger.d.ts +46 -0
  256. package/dist/triggers/timer-trigger.d.ts.map +1 -0
  257. package/dist/triggers/timer-trigger.js +74 -0
  258. package/dist/triggers/timer-trigger.js.map +1 -0
  259. package/dist/triggers/types.d.ts +61 -0
  260. package/dist/triggers/types.d.ts.map +1 -0
  261. package/dist/triggers/types.js +23 -0
  262. package/dist/triggers/types.js.map +1 -0
  263. package/package.json +64 -0
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Message compressor — Layer 2 of token defense.
3
+ *
4
+ * When the total message history exceeds the compression threshold, older
5
+ * tool-result messages get their content replaced with one-line summaries.
6
+ * Tool call/result pairing is preserved (orphaned calls or results would
7
+ * cause API errors at the next request).
8
+ *
9
+ * Operates on the framework's internal Message shape (see ../messages.ts).
10
+ * Tool results are their own role:'tool' messages with toolCallId pointing
11
+ * back to an assistant message's toolCalls[i].id.
12
+ */
13
+ import type { Message } from '../messages.js';
14
+ export interface CompressorStrategy {
15
+ compress(messages: Message[], options: CompressionOptions): Message[];
16
+ }
17
+ export interface CompressionOptions {
18
+ /** Number of leading messages to keep verbatim (system + initial user). */
19
+ protectFirst: number;
20
+ /** Number of trailing messages to keep verbatim (recent context). */
21
+ protectLast: number;
22
+ /** Activity-log entries used to enrich compressed summaries. Optional. */
23
+ activityLog: Array<{
24
+ agent: string;
25
+ action: string;
26
+ detail?: string;
27
+ }>;
28
+ }
29
+ export declare class DeterministicCompressor implements CompressorStrategy {
30
+ compress(messages: Message[], options: CompressionOptions): Message[];
31
+ }
32
+ export declare class AggressiveCompressor implements CompressorStrategy {
33
+ compress(messages: Message[], options: CompressionOptions): Message[];
34
+ }
35
+ export declare function sanitizeToolPairs(messages: Message[]): Message[];
36
+ //# sourceMappingURL=compressor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compressor.d.ts","sourceRoot":"","sources":["../../src/context/compressor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAuC,MAAM,gBAAgB,CAAC;AAMnF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,EAAE,CAAC;CACvE;AAED,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,WAAW,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxE;AAMD,qBAAa,uBAAwB,YAAW,kBAAkB;IAChE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,EAAE;CA2BtE;AAOD,qBAAa,oBAAqB,YAAW,kBAAkB;IAC7D,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,EAAE;CAqCtE;AAiBD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAmEhE"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Message compressor — Layer 2 of token defense.
3
+ *
4
+ * When the total message history exceeds the compression threshold, older
5
+ * tool-result messages get their content replaced with one-line summaries.
6
+ * Tool call/result pairing is preserved (orphaned calls or results would
7
+ * cause API errors at the next request).
8
+ *
9
+ * Operates on the framework's internal Message shape (see ../messages.ts).
10
+ * Tool results are their own role:'tool' messages with toolCallId pointing
11
+ * back to an assistant message's toolCalls[i].id.
12
+ */
13
+ // ---------------------------------------------------------------------------
14
+ // Default: deterministic compression of old tool-result messages.
15
+ // ---------------------------------------------------------------------------
16
+ export class DeterministicCompressor {
17
+ compress(messages, options) {
18
+ const { protectFirst, protectLast } = options;
19
+ if (messages.length <= protectFirst + protectLast) {
20
+ return messages;
21
+ }
22
+ const head = messages.slice(0, protectFirst);
23
+ const tail = messages.slice(-protectLast);
24
+ const middle = messages.slice(protectFirst, messages.length - protectLast);
25
+ const compressed = [...head];
26
+ for (const msg of middle) {
27
+ if (msg.role === 'tool') {
28
+ const compressedMsg = {
29
+ role: 'tool',
30
+ toolCallId: msg.toolCallId,
31
+ content: `[Previous tool result: ${msg.content.length} chars — compressed to save context]`,
32
+ };
33
+ compressed.push(compressedMsg);
34
+ }
35
+ else {
36
+ compressed.push(msg);
37
+ }
38
+ }
39
+ compressed.push(...tail);
40
+ return compressed;
41
+ }
42
+ }
43
+ // ---------------------------------------------------------------------------
44
+ // Aggressive: also compress tool-call arguments in old assistant messages.
45
+ // Used when deterministic compression isn't enough (HARD_STOP path).
46
+ // ---------------------------------------------------------------------------
47
+ export class AggressiveCompressor {
48
+ compress(messages, options) {
49
+ const { protectFirst, protectLast } = options;
50
+ if (messages.length <= protectFirst + protectLast) {
51
+ return messages;
52
+ }
53
+ const head = messages.slice(0, protectFirst);
54
+ const tail = messages.slice(-protectLast);
55
+ const middle = messages.slice(protectFirst, messages.length - protectLast);
56
+ const compressed = [...head];
57
+ for (const msg of middle) {
58
+ if (msg.role === 'tool') {
59
+ compressed.push({
60
+ role: 'tool',
61
+ toolCallId: msg.toolCallId,
62
+ content: '[Compressed — old tool result removed]',
63
+ });
64
+ }
65
+ else if (msg.role === 'assistant' && msg.toolCalls.length > 0) {
66
+ const stubbed = {
67
+ role: 'assistant',
68
+ content: msg.content,
69
+ toolCalls: msg.toolCalls.map((tc) => ({
70
+ id: tc.id,
71
+ name: tc.name,
72
+ input: { _compressed: true, summary: `Called ${tc.name}` },
73
+ })),
74
+ };
75
+ compressed.push(stubbed);
76
+ }
77
+ else {
78
+ compressed.push(msg);
79
+ }
80
+ }
81
+ compressed.push(...tail);
82
+ return compressed;
83
+ }
84
+ }
85
+ // ---------------------------------------------------------------------------
86
+ // Tool-pair sanitization.
87
+ //
88
+ // Ensures every assistant ToolCall has a matching tool-result message and
89
+ // vice versa. Orphaned results get dropped; orphaned calls get stub results
90
+ // inserted. Run AFTER compression and before the next API call.
91
+ //
92
+ // Partial-orphan handling: when an assistant emits multiple tool calls and
93
+ // only some have matching results, stubs for the orphaned calls are
94
+ // appended *after* the run of real tool-result messages (i.e. immediately
95
+ // before whatever non-tool message comes next, or end-of-array). This
96
+ // preserves the OpenAI invariant "every tool_call.id must have a tool
97
+ // message" without re-ordering existing real results.
98
+ // ---------------------------------------------------------------------------
99
+ export function sanitizeToolPairs(messages) {
100
+ const callIds = new Set();
101
+ const resultIds = new Set();
102
+ for (const msg of messages) {
103
+ if (msg.role === 'assistant') {
104
+ for (const tc of msg.toolCalls)
105
+ callIds.add(tc.id);
106
+ }
107
+ else if (msg.role === 'tool') {
108
+ resultIds.add(msg.toolCallId);
109
+ }
110
+ }
111
+ const orphanedCalls = new Set([...callIds].filter((id) => !resultIds.has(id)));
112
+ const orphanedResults = new Set([...resultIds].filter((id) => !callIds.has(id)));
113
+ if (orphanedCalls.size === 0 && orphanedResults.size === 0) {
114
+ return messages;
115
+ }
116
+ const result = [];
117
+ for (let i = 0; i < messages.length; i++) {
118
+ const msg = messages[i];
119
+ // Drop orphaned tool-result messages outright.
120
+ if (msg.role === 'tool' && orphanedResults.has(msg.toolCallId)) {
121
+ continue;
122
+ }
123
+ result.push(msg);
124
+ // Insert stub results for orphaned calls right after the assistant +
125
+ // any contiguous run of (kept) real tool-result messages that follow
126
+ // it. This handles the partial-orphan case where some calls have real
127
+ // results and some don't — stubs go AFTER the real ones rather than
128
+ // being skipped entirely (which would leave the orphans unpaired).
129
+ if (msg.role === 'assistant') {
130
+ const orphansHere = msg.toolCalls.map((tc) => tc.id).filter((id) => orphanedCalls.has(id));
131
+ if (orphansHere.length === 0)
132
+ continue;
133
+ // Walk forward over any tool messages immediately following the
134
+ // assistant — append the kept ones to result first, then stub the
135
+ // orphans, so the final order is: assistant → real results → stubs.
136
+ let j = i + 1;
137
+ while (j < messages.length && messages[j].role === 'tool') {
138
+ const next = messages[j];
139
+ if (!orphanedResults.has(next.toolCallId)) {
140
+ result.push(next);
141
+ }
142
+ j++;
143
+ }
144
+ for (const id of orphansHere) {
145
+ result.push({
146
+ role: 'tool',
147
+ toolCallId: id,
148
+ content: '[Result removed during context compression]',
149
+ });
150
+ }
151
+ // Skip past the tool messages we just consumed; the outer loop's
152
+ // `i++` lands on `j`.
153
+ i = j - 1;
154
+ }
155
+ }
156
+ return result;
157
+ }
158
+ //# sourceMappingURL=compressor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compressor.js","sourceRoot":"","sources":["../../src/context/compressor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAqBH,8EAA8E;AAC9E,kEAAkE;AAClE,8EAA8E;AAE9E,MAAM,OAAO,uBAAuB;IAClC,QAAQ,CAAC,QAAmB,EAAE,OAA2B;QACvD,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;QAC9C,IAAI,QAAQ,CAAC,MAAM,IAAI,YAAY,GAAG,WAAW,EAAE,CAAC;YAClD,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC;QAE3E,MAAM,UAAU,GAAc,CAAC,GAAG,IAAI,CAAC,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;YACzB,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACxB,MAAM,aAAa,GAAsB;oBACvC,IAAI,EAAE,MAAM;oBACZ,UAAU,EAAE,GAAG,CAAC,UAAU;oBAC1B,OAAO,EAAE,0BAA0B,GAAG,CAAC,OAAO,CAAC,MAAM,sCAAsC;iBAC5F,CAAC;gBACF,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACjC,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAEzB,OAAO,UAAU,CAAC;IACpB,CAAC;CACF;AAED,8EAA8E;AAC9E,2EAA2E;AAC3E,qEAAqE;AACrE,8EAA8E;AAE9E,MAAM,OAAO,oBAAoB;IAC/B,QAAQ,CAAC,QAAmB,EAAE,OAA2B;QACvD,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;QAC9C,IAAI,QAAQ,CAAC,MAAM,IAAI,YAAY,GAAG,WAAW,EAAE,CAAC;YAClD,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC;QAE3E,MAAM,UAAU,GAAc,CAAC,GAAG,IAAI,CAAC,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;YACzB,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACxB,UAAU,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,MAAM;oBACZ,UAAU,EAAE,GAAG,CAAC,UAAU;oBAC1B,OAAO,EAAE,wCAAwC;iBAClD,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChE,MAAM,OAAO,GAAqB;oBAChC,IAAI,EAAE,WAAW;oBACjB,OAAO,EAAE,GAAG,CAAC,OAAO;oBACpB,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;wBACpC,EAAE,EAAE,EAAE,CAAC,EAAE;wBACT,IAAI,EAAE,EAAE,CAAC,IAAI;wBACb,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE;qBAC3D,CAAC,CAAC;iBACJ,CAAC;gBACF,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAEzB,OAAO,UAAU,CAAC;IACpB,CAAC;CACF;AAED,8EAA8E;AAC9E,0BAA0B;AAC1B,EAAE;AACF,0EAA0E;AAC1E,4EAA4E;AAC5E,gEAAgE;AAChE,EAAE;AACF,2EAA2E;AAC3E,oEAAoE;AACpE,0EAA0E;AAC1E,sEAAsE;AACtE,sEAAsE;AACtE,sDAAsD;AACtD,8EAA8E;AAE9E,MAAM,UAAU,iBAAiB,CAAC,QAAmB;IACnD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IAEpC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAC7B,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,SAAS;gBAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACrD,CAAC;aAAM,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC/B,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/E,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAEjF,IAAI,aAAa,CAAC,IAAI,KAAK,CAAC,IAAI,eAAe,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC3D,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAc,EAAE,CAAC;IAE7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;QAEzB,+CAA+C;QAC/C,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/D,SAAS;QACX,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEjB,qEAAqE;QACrE,qEAAqE;QACrE,sEAAsE;QACtE,oEAAoE;QACpE,mEAAmE;QACnE,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAC7B,MAAM,WAAW,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3F,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAEvC,gEAAgE;YAChE,kEAAkE;YAClE,oEAAoE;YACpE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,OAAO,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAE,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC3D,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAwC,CAAC;gBAChE,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC1C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpB,CAAC;gBACD,CAAC,EAAE,CAAC;YACN,CAAC;YAED,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,MAAM;oBACZ,UAAU,EAAE,EAAE;oBACd,OAAO,EAAE,6CAA6C;iBACvD,CAAC,CAAC;YACL,CAAC;YAED,iEAAiE;YACjE,sBAAsB;YACtB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACZ,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { TokenBudgetTracker, BudgetStatus, RoughTokenEstimator, type TokenEstimator, type TokenBudgetConfig, type BudgetCheck, } from './token-budget.js';
2
+ export { TruncateAndCacheResultCapper, NoOpResultCapper, type ResultCapper, } from './result-capper.js';
3
+ export { DeterministicCompressor, AggressiveCompressor, sanitizeToolPairs, type CompressorStrategy, type CompressionOptions, } from './compressor.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,4BAA4B,EAC5B,gBAAgB,EAChB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { TokenBudgetTracker, BudgetStatus, RoughTokenEstimator, } from './token-budget.js';
2
+ export { TruncateAndCacheResultCapper, NoOpResultCapper, } from './result-capper.js';
3
+ export { DeterministicCompressor, AggressiveCompressor, sanitizeToolPairs, } from './compressor.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,GAIpB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,4BAA4B,EAC5B,gBAAgB,GAEjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,GAGlB,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Tool-result capping — Layer 1 of token defense.
3
+ *
4
+ * Caps individual tool results before they enter the message history.
5
+ * Oversize results get a preview returned and (optionally) the full content
6
+ * stored in the pipeline cache for later retrieval.
7
+ */
8
+ import type { PipelineCache } from '../cache.js';
9
+ export interface ResultCapper {
10
+ /**
11
+ * Cap a tool result. If under limit, returns as-is.
12
+ * If over, returns a preview and (if cache is provided) stows the full
13
+ * content under `full_result:{toolCallId}` for later retrieval.
14
+ */
15
+ cap(content: string, toolName: string, toolCallId: string, cache?: PipelineCache | null): string;
16
+ }
17
+ /**
18
+ * Default capper: truncate at last newline within the preview budget,
19
+ * cache full content if a cache is provided.
20
+ */
21
+ export declare class TruncateAndCacheResultCapper implements ResultCapper {
22
+ private maxChars;
23
+ private previewChars;
24
+ constructor(maxChars?: number, previewChars?: number);
25
+ cap(content: string, _toolName: string, toolCallId: string, cache?: PipelineCache | null): string;
26
+ private generatePreview;
27
+ }
28
+ /** No-op capper for tests or when capping is disabled. */
29
+ export declare class NoOpResultCapper implements ResultCapper {
30
+ cap(content: string): string;
31
+ }
32
+ //# sourceMappingURL=result-capper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-capper.d.ts","sourceRoot":"","sources":["../../src/context/result-capper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,CAAC;CAClG;AAED;;;GAGG;AACH,qBAAa,4BAA6B,YAAW,YAAY;IAC/D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAS;gBAEjB,QAAQ,GAAE,MAAe,EAAE,YAAY,GAAE,MAAc;IAKnE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM;IAmBjG,OAAO,CAAC,eAAe;CAaxB;AAED,0DAA0D;AAC1D,qBAAa,gBAAiB,YAAW,YAAY;IACnD,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;CAG7B"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Tool-result capping — Layer 1 of token defense.
3
+ *
4
+ * Caps individual tool results before they enter the message history.
5
+ * Oversize results get a preview returned and (optionally) the full content
6
+ * stored in the pipeline cache for later retrieval.
7
+ */
8
+ /**
9
+ * Default capper: truncate at last newline within the preview budget,
10
+ * cache full content if a cache is provided.
11
+ */
12
+ export class TruncateAndCacheResultCapper {
13
+ maxChars;
14
+ previewChars;
15
+ constructor(maxChars = 12_000, previewChars = 1_500) {
16
+ this.maxChars = maxChars;
17
+ this.previewChars = previewChars;
18
+ }
19
+ cap(content, _toolName, toolCallId, cache) {
20
+ if (content.length <= this.maxChars) {
21
+ return content;
22
+ }
23
+ const preview = this.generatePreview(content);
24
+ const fullLength = content.length;
25
+ if (cache) {
26
+ cache.set(`full_result:${toolCallId}`, content);
27
+ }
28
+ return (`[Result capped: ${fullLength} chars total, showing first ${preview.length} chars. ` +
29
+ `Full result available in pipeline cache (key: full_result:${toolCallId}).]\n\n` +
30
+ preview);
31
+ }
32
+ generatePreview(content) {
33
+ if (content.length <= this.previewChars) {
34
+ return content;
35
+ }
36
+ const truncated = content.slice(0, this.previewChars);
37
+ const lastNewline = truncated.lastIndexOf('\n');
38
+ if (lastNewline > this.previewChars / 2) {
39
+ return truncated.slice(0, lastNewline + 1) + '\n...[truncated]';
40
+ }
41
+ return truncated + '\n...[truncated]';
42
+ }
43
+ }
44
+ /** No-op capper for tests or when capping is disabled. */
45
+ export class NoOpResultCapper {
46
+ cap(content) {
47
+ return content;
48
+ }
49
+ }
50
+ //# sourceMappingURL=result-capper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-capper.js","sourceRoot":"","sources":["../../src/context/result-capper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH;;;GAGG;AACH,MAAM,OAAO,4BAA4B;IAC/B,QAAQ,CAAS;IACjB,YAAY,CAAS;IAE7B,YAAY,WAAmB,MAAM,EAAE,eAAuB,KAAK;QACjE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAED,GAAG,CAAC,OAAe,EAAE,SAAiB,EAAE,UAAkB,EAAE,KAA4B;QACtF,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;QAElC,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,GAAG,CAAC,eAAe,UAAU,EAAE,EAAE,OAAO,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,CACL,mBAAmB,UAAU,+BAA+B,OAAO,CAAC,MAAM,UAAU;YACpF,6DAA6D,UAAU,SAAS;YAChF,OAAO,CACR,CAAC;IACJ,CAAC;IAEO,eAAe,CAAC,OAAe;QACrC,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACxC,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QACtD,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,WAAW,GAAG,IAAI,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;YACxC,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,GAAG,kBAAkB,CAAC;QAClE,CAAC;QAED,OAAO,SAAS,GAAG,kBAAkB,CAAC;IACxC,CAAC;CACF;AAED,0DAA0D;AAC1D,MAAM,OAAO,gBAAgB;IAC3B,GAAG,CAAC,OAAe;QACjB,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Token budget tracking and enforcement.
3
+ *
4
+ * Estimates token usage for the next API request (system + messages + tools)
5
+ * and reports a status code that the agent runner uses to trigger compression
6
+ * or force a final response.
7
+ *
8
+ * Estimator strategy is swappable. Default is a chars-per-token heuristic;
9
+ * downstream can plug in a tiktoken-based estimator for exact counts.
10
+ */
11
+ import type { Message, ToolDefinition } from '../messages.js';
12
+ export interface TokenEstimator {
13
+ estimate(text: string): number;
14
+ estimateRequest(systemPrompt: string, messages: Message[], tools: ToolDefinition[]): number;
15
+ }
16
+ /**
17
+ * Default: ~4 chars per token. Accurate enough for budget enforcement.
18
+ * Replace with a tiktoken-based estimator for exact counts.
19
+ */
20
+ export declare class RoughTokenEstimator implements TokenEstimator {
21
+ private readonly charsPerToken;
22
+ constructor(charsPerToken?: number);
23
+ estimate(text: string): number;
24
+ estimateRequest(systemPrompt: string, messages: Message[], tools: ToolDefinition[]): number;
25
+ }
26
+ export interface TokenBudgetConfig {
27
+ contextWindow: number;
28
+ /** Trigger compression at this fraction of available budget (default 0.50). */
29
+ compressionThreshold: number;
30
+ /** Force output at this fraction of available budget (default 0.80). */
31
+ hardStopThreshold: number;
32
+ /** Tokens reserved for the model's response. */
33
+ responseReserve: number;
34
+ /** Per-result cap (chars) before the result-capper truncates. */
35
+ perResultCapChars: number;
36
+ /** Preview size (chars) when a result is capped. */
37
+ previewSizeChars: number;
38
+ /**
39
+ * Leading messages preserved verbatim during compression — typically the
40
+ * system prompt and the seed user message. Default 1.
41
+ */
42
+ protectFirst: number;
43
+ /**
44
+ * Trailing messages preserved verbatim during compression — recent context
45
+ * the model needs to make progress. Default 6 (deterministic) / 4 (aggressive).
46
+ * Set to a per-pipeline value when the workflow needs more or less recent history.
47
+ */
48
+ protectLast: number;
49
+ /** Trailing messages preserved during the aggressive (HARD_STOP) compressor. Default 4. */
50
+ protectLastAggressive: number;
51
+ }
52
+ export declare enum BudgetStatus {
53
+ OK = "ok",
54
+ COMPRESS = "compress",
55
+ HARD_STOP = "hard_stop"
56
+ }
57
+ export interface BudgetCheck {
58
+ status: BudgetStatus;
59
+ estimatedTokens: number;
60
+ /** 0.0 to 1.0+. Fraction of (contextWindow - responseReserve). */
61
+ budgetUsed: number;
62
+ compressionThreshold: number;
63
+ hardStopThreshold: number;
64
+ }
65
+ export declare class TokenBudgetTracker {
66
+ private config;
67
+ private estimator;
68
+ constructor(config?: Partial<TokenBudgetConfig>, estimator?: TokenEstimator);
69
+ check(systemPrompt: string, messages: Message[], tools: ToolDefinition[]): BudgetCheck;
70
+ getResultCap(): {
71
+ maxChars: number;
72
+ previewChars: number;
73
+ };
74
+ getProtectCounts(): {
75
+ protectFirst: number;
76
+ protectLast: number;
77
+ protectLastAggressive: number;
78
+ };
79
+ getEstimator(): TokenEstimator;
80
+ }
81
+ //# sourceMappingURL=token-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-budget.d.ts","sourceRoot":"","sources":["../../src/context/token-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAM9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM,CAAC;CAC7F;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,cAAc;IACxD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,aAAa,GAAE,MAAU;IAIrC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAK9B,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM;CAO5F;AAMD,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oDAAoD;IACpD,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAkBD,oBAAY,YAAY;IACtB,EAAE,OAAO;IACT,QAAQ,aAAa;IACrB,SAAS,cAAc;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,YAAY,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,SAAS,CAAiB;gBAEtB,MAAM,GAAE,OAAO,CAAC,iBAAiB,CAAM,EAAE,SAAS,CAAC,EAAE,cAAc;IAK/E,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,WAAW;IAqBtF,YAAY,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAO1D,gBAAgB,IAAI;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,qBAAqB,EAAE,MAAM,CAAC;KAC/B;IAQD,YAAY,IAAI,cAAc;CAG/B"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Token budget tracking and enforcement.
3
+ *
4
+ * Estimates token usage for the next API request (system + messages + tools)
5
+ * and reports a status code that the agent runner uses to trigger compression
6
+ * or force a final response.
7
+ *
8
+ * Estimator strategy is swappable. Default is a chars-per-token heuristic;
9
+ * downstream can plug in a tiktoken-based estimator for exact counts.
10
+ */
11
+ /**
12
+ * Default: ~4 chars per token. Accurate enough for budget enforcement.
13
+ * Replace with a tiktoken-based estimator for exact counts.
14
+ */
15
+ export class RoughTokenEstimator {
16
+ charsPerToken;
17
+ constructor(charsPerToken = 4) {
18
+ this.charsPerToken = charsPerToken;
19
+ }
20
+ estimate(text) {
21
+ if (!text)
22
+ return 0;
23
+ return Math.ceil(text.length / this.charsPerToken);
24
+ }
25
+ estimateRequest(systemPrompt, messages, tools) {
26
+ let totalChars = 0;
27
+ if (systemPrompt)
28
+ totalChars += systemPrompt.length;
29
+ if (messages.length > 0)
30
+ totalChars += JSON.stringify(messages).length;
31
+ if (tools.length > 0)
32
+ totalChars += JSON.stringify(tools).length;
33
+ return Math.ceil(totalChars / this.charsPerToken);
34
+ }
35
+ }
36
+ const DEFAULT_CONFIG = {
37
+ contextWindow: 131_072,
38
+ compressionThreshold: 0.5,
39
+ hardStopThreshold: 0.8,
40
+ responseReserve: 4_096,
41
+ perResultCapChars: 12_000,
42
+ previewSizeChars: 1_500,
43
+ protectFirst: 1,
44
+ protectLast: 6,
45
+ protectLastAggressive: 4,
46
+ };
47
+ // ---------------------------------------------------------------------------
48
+ // Budget tracker
49
+ // ---------------------------------------------------------------------------
50
+ export var BudgetStatus;
51
+ (function (BudgetStatus) {
52
+ BudgetStatus["OK"] = "ok";
53
+ BudgetStatus["COMPRESS"] = "compress";
54
+ BudgetStatus["HARD_STOP"] = "hard_stop";
55
+ })(BudgetStatus || (BudgetStatus = {}));
56
+ export class TokenBudgetTracker {
57
+ config;
58
+ estimator;
59
+ constructor(config = {}, estimator) {
60
+ this.config = { ...DEFAULT_CONFIG, ...config };
61
+ this.estimator = estimator ?? new RoughTokenEstimator();
62
+ }
63
+ check(systemPrompt, messages, tools) {
64
+ const estimatedTokens = this.estimator.estimateRequest(systemPrompt, messages, tools);
65
+ const available = this.config.contextWindow - this.config.responseReserve;
66
+ const budgetUsed = estimatedTokens / available;
67
+ let status = BudgetStatus.OK;
68
+ if (budgetUsed >= this.config.hardStopThreshold) {
69
+ status = BudgetStatus.HARD_STOP;
70
+ }
71
+ else if (budgetUsed >= this.config.compressionThreshold) {
72
+ status = BudgetStatus.COMPRESS;
73
+ }
74
+ return {
75
+ status,
76
+ estimatedTokens,
77
+ budgetUsed,
78
+ compressionThreshold: this.config.compressionThreshold,
79
+ hardStopThreshold: this.config.hardStopThreshold,
80
+ };
81
+ }
82
+ getResultCap() {
83
+ return {
84
+ maxChars: this.config.perResultCapChars,
85
+ previewChars: this.config.previewSizeChars,
86
+ };
87
+ }
88
+ getProtectCounts() {
89
+ return {
90
+ protectFirst: this.config.protectFirst,
91
+ protectLast: this.config.protectLast,
92
+ protectLastAggressive: this.config.protectLastAggressive,
93
+ };
94
+ }
95
+ getEstimator() {
96
+ return this.estimator;
97
+ }
98
+ }
99
+ //# sourceMappingURL=token-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-budget.js","sourceRoot":"","sources":["../../src/context/token-budget.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Best-effort name derivation from the calling file's path.
3
+ *
4
+ * Walks the captured stack and returns the basename of the first frame
5
+ * outside this `helpers/` directory. Used by `oneShot` / `schedule` /
6
+ * `hook` to auto-name registrations when the caller didn't pass one.
7
+ *
8
+ * Pure heuristic — for production registrations the caller should pass
9
+ * an explicit `name`. The stack-walk is fast and synchronous; the
10
+ * helper file paths the matcher looks for are stable.
11
+ */
12
+ /**
13
+ * Returns a slug-y name derived from the calling file (the first stack
14
+ * frame that isn't inside `helpers/`). Falls back to `fallback` if no
15
+ * usable frame is found.
16
+ */
17
+ export declare function deriveNameFromCaller(fallback: string): string;
18
+ //# sourceMappingURL=caller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caller.d.ts","sourceRoot":"","sources":["../../src/helpers/caller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAYH;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAa7D"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Best-effort name derivation from the calling file's path.
3
+ *
4
+ * Walks the captured stack and returns the basename of the first frame
5
+ * outside this `helpers/` directory. Used by `oneShot` / `schedule` /
6
+ * `hook` to auto-name registrations when the caller didn't pass one.
7
+ *
8
+ * Pure heuristic — for production registrations the caller should pass
9
+ * an explicit `name`. The stack-walk is fast and synchronous; the
10
+ * helper file paths the matcher looks for are stable.
11
+ */
12
+ import { basename } from 'node:path';
13
+ const HELPER_FILE_MATCHERS = [
14
+ 'helpers/one-shot',
15
+ 'helpers/schedule',
16
+ 'helpers/hook',
17
+ 'helpers/caller',
18
+ 'helpers/index',
19
+ ];
20
+ /**
21
+ * Returns a slug-y name derived from the calling file (the first stack
22
+ * frame that isn't inside `helpers/`). Falls back to `fallback` if no
23
+ * usable frame is found.
24
+ */
25
+ export function deriveNameFromCaller(fallback) {
26
+ const stack = new Error().stack ?? '';
27
+ const lines = stack.split('\n').slice(1);
28
+ for (const line of lines) {
29
+ const match = line.match(/\(?(\/[^):\s]+\.(?:ts|js|mts|mjs|cjs))(?::\d+)?(?::\d+)?\)?/);
30
+ if (!match)
31
+ continue;
32
+ const path = match[1];
33
+ if (HELPER_FILE_MATCHERS.some((m) => path.includes(m)))
34
+ continue;
35
+ const base = basename(path).replace(/\.(ts|js|mts|mjs|cjs)$/, '');
36
+ return base;
37
+ }
38
+ return fallback;
39
+ }
40
+ //# sourceMappingURL=caller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caller.js","sourceRoot":"","sources":["../../src/helpers/caller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,oBAAoB,GAAG;IAC3B,kBAAkB;IAClB,kBAAkB;IAClB,cAAc;IACd,gBAAgB;IAChB,eAAe;CAChB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACxF,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACvB,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAAE,SAAS;QACjE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;QAClE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * `hook` — register a pipeline driven by an inbound HTTP webhook.
3
+ *
4
+ * export default hook({ path: '/webhook/digest' }, async (payload, ctx) => {
5
+ * return { ok: true, processed: payload };
6
+ * });
7
+ *
8
+ * The library maintains one `node:http` server per project — every `hook`
9
+ * registration adds a new route to the same server. The server starts
10
+ * lazily on the first `Trigger.start()`. Routes resolve by exact path
11
+ * match; only POST with a JSON body is supported in v3.0.0.
12
+ *
13
+ * The handler is called with the parsed JSON body and ctx. Its return
14
+ * value is JSON-serialized and sent back as the HTTP response body
15
+ * (status 200). Throws produce status 500 with `{ error: message }`.
16
+ *
17
+ * Server port: `process.env.THREAD_PHASE_HTTP_PORT` if set, else 7777.
18
+ */
19
+ import { type Server } from 'node:http';
20
+ import type { Trigger, TriggerEvent } from '../triggers/types.js';
21
+ import type { ExtensionRegisterFn, HelperHandler } from './types.js';
22
+ export interface HookSpec {
23
+ /** URL path the webhook listens on. Exact match. */
24
+ path: string;
25
+ /** HTTP method. Only 'POST' is supported in v3.0.0. */
26
+ method?: 'POST';
27
+ }
28
+ export interface HookOptions {
29
+ /** Pipeline + trigger base name. Defaults to the calling file's basename. */
30
+ name?: string;
31
+ /** Free-form description for `thread-phase list`. */
32
+ description?: string;
33
+ }
34
+ /** Test-only: tear down the shared server so each test starts clean. */
35
+ export declare function _resetHttpServerForTests(): void;
36
+ export declare class HttpTrigger implements Trigger<unknown> {
37
+ readonly name: string;
38
+ readonly path: string;
39
+ readonly method: 'POST';
40
+ /** @internal — exposed for tests asserting the shared-server invariant. */
41
+ readonly _server: Server;
42
+ private seq;
43
+ private stopped;
44
+ private queued;
45
+ private waiter;
46
+ private readonly shared;
47
+ /** Map event.id → pending response so the handler's return value can be sent back. */
48
+ private readonly pendingById;
49
+ /** Map event.id → user handler's return value (so dispatch can resolve sync). */
50
+ private readonly resultsById;
51
+ constructor(opts: {
52
+ name: string;
53
+ path: string;
54
+ method: 'POST';
55
+ });
56
+ /**
57
+ * Called by the shared HTTP handler. Enqueues a new event and returns
58
+ * a promise that resolves with the user handler's return value.
59
+ */
60
+ dispatch(body: unknown): Promise<unknown>;
61
+ /**
62
+ * Resolve the pending HTTP response for a given event id. Called by the
63
+ * helper-generated Phase once the user handler has returned.
64
+ */
65
+ resolveResponse(eventId: number, value: unknown): void;
66
+ /** Reject the pending HTTP response for a given event id. */
67
+ rejectResponse(eventId: number, err: Error): void;
68
+ start(): AsyncGenerator<TriggerEvent<unknown>, void>;
69
+ stop(): Promise<void>;
70
+ private ensureServerStarted;
71
+ }
72
+ export declare function hook<TBody = unknown, TResult = unknown>(spec: HookSpec, handler: HelperHandler<TBody, TResult>, options?: HookOptions): ExtensionRegisterFn;
73
+ //# sourceMappingURL=hook.d.ts.map