qlogicagent 0.2.1 → 0.4.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 (226) hide show
  1. package/README.md +45 -45
  2. package/package.json +56 -42
  3. package/dist/agent/agent.d.ts +0 -43
  4. package/dist/agent/agent.js +0 -113
  5. package/dist/agent/tool-loop.d.ts +0 -64
  6. package/dist/agent/tool-loop.js +0 -575
  7. package/dist/agent/types.d.ts +0 -175
  8. package/dist/agent/types.js +0 -14
  9. package/dist/cli/main.d.ts +0 -11
  10. package/dist/cli/main.js +0 -23
  11. package/dist/cli/stdio-server.d.ts +0 -45
  12. package/dist/cli/stdio-server.js +0 -463
  13. package/dist/config/config.d.ts +0 -17
  14. package/dist/config/config.js +0 -21
  15. package/dist/contracts/hooks.d.ts +0 -120
  16. package/dist/contracts/hooks.js +0 -7
  17. package/dist/contracts/index.d.ts +0 -10
  18. package/dist/contracts/index.js +0 -10
  19. package/dist/contracts/planner.d.ts +0 -35
  20. package/dist/contracts/planner.js +0 -2
  21. package/dist/contracts/skill-candidate.d.ts +0 -63
  22. package/dist/contracts/skill-candidate.js +0 -195
  23. package/dist/contracts/todo.d.ts +0 -14
  24. package/dist/contracts/todo.js +0 -9
  25. package/dist/index.d.ts +0 -13
  26. package/dist/index.js +0 -15
  27. package/dist/llm/builtin-providers.d.ts +0 -10
  28. package/dist/llm/builtin-providers.js +0 -531
  29. package/dist/llm/index.d.ts +0 -15
  30. package/dist/llm/index.js +0 -14
  31. package/dist/llm/llm-client.d.ts +0 -43
  32. package/dist/llm/llm-client.js +0 -67
  33. package/dist/llm/model-catalog.d.ts +0 -53
  34. package/dist/llm/model-catalog.js +0 -191
  35. package/dist/llm/provider-def.d.ts +0 -59
  36. package/dist/llm/provider-def.js +0 -12
  37. package/dist/llm/provider-registry.d.ts +0 -54
  38. package/dist/llm/provider-registry.js +0 -147
  39. package/dist/llm/transport.d.ts +0 -62
  40. package/dist/llm/transport.js +0 -27
  41. package/dist/llm/transports/anthropic-messages.d.ts +0 -31
  42. package/dist/llm/transports/anthropic-messages.js +0 -293
  43. package/dist/llm/transports/openai-chat.d.ts +0 -36
  44. package/dist/llm/transports/openai-chat.js +0 -165
  45. package/dist/orchestration/agent-registry.d.ts +0 -41
  46. package/dist/orchestration/agent-registry.js +0 -116
  47. package/dist/orchestration/approval-aware-tool-plan.d.ts +0 -32
  48. package/dist/orchestration/approval-aware-tool-plan.js +0 -87
  49. package/dist/orchestration/context-compression.d.ts +0 -220
  50. package/dist/orchestration/context-compression.js +0 -583
  51. package/dist/orchestration/conversation-repair.d.ts +0 -61
  52. package/dist/orchestration/conversation-repair.js +0 -429
  53. package/dist/orchestration/curator-scheduler.d.ts +0 -119
  54. package/dist/orchestration/curator-scheduler.js +0 -135
  55. package/dist/orchestration/embedded-failover-policy.d.ts +0 -110
  56. package/dist/orchestration/embedded-failover-policy.js +0 -168
  57. package/dist/orchestration/error-classification.d.ts +0 -12
  58. package/dist/orchestration/error-classification.js +0 -77
  59. package/dist/orchestration/failover-classification.d.ts +0 -8
  60. package/dist/orchestration/failover-classification.js +0 -381
  61. package/dist/orchestration/failover-error.d.ts +0 -33
  62. package/dist/orchestration/failover-error.js +0 -198
  63. package/dist/orchestration/fork-subagent.d.ts +0 -100
  64. package/dist/orchestration/fork-subagent.js +0 -98
  65. package/dist/orchestration/index.d.ts +0 -120
  66. package/dist/orchestration/index.js +0 -267
  67. package/dist/orchestration/memory-flush-policy.d.ts +0 -57
  68. package/dist/orchestration/memory-flush-policy.js +0 -85
  69. package/dist/orchestration/memory-provider.d.ts +0 -14
  70. package/dist/orchestration/memory-provider.js +0 -2
  71. package/dist/orchestration/parallel-tool-calls.d.ts +0 -41
  72. package/dist/orchestration/parallel-tool-calls.js +0 -59
  73. package/dist/orchestration/prompt-cache-strategy.d.ts +0 -126
  74. package/dist/orchestration/prompt-cache-strategy.js +0 -228
  75. package/dist/orchestration/reactive-compact.d.ts +0 -73
  76. package/dist/orchestration/reactive-compact.js +0 -78
  77. package/dist/orchestration/retry-loop.d.ts +0 -22
  78. package/dist/orchestration/retry-loop.js +0 -24
  79. package/dist/orchestration/skill-candidate.d.ts +0 -52
  80. package/dist/orchestration/skill-candidate.js +0 -141
  81. package/dist/orchestration/skill-consolidation.d.ts +0 -123
  82. package/dist/orchestration/skill-consolidation.js +0 -220
  83. package/dist/orchestration/skill-improvement.d.ts +0 -59
  84. package/dist/orchestration/skill-improvement.js +0 -66
  85. package/dist/orchestration/skill-similarity.d.ts +0 -98
  86. package/dist/orchestration/skill-similarity.js +0 -131
  87. package/dist/orchestration/streaming-tool-executor.d.ts +0 -73
  88. package/dist/orchestration/streaming-tool-executor.js +0 -96
  89. package/dist/orchestration/team-orchestration.d.ts +0 -195
  90. package/dist/orchestration/team-orchestration.js +0 -369
  91. package/dist/orchestration/team-tool-loop-wiring.d.ts +0 -92
  92. package/dist/orchestration/team-tool-loop-wiring.js +0 -147
  93. package/dist/orchestration/tool-choice-policy.d.ts +0 -54
  94. package/dist/orchestration/tool-choice-policy.js +0 -164
  95. package/dist/orchestration/tool-loop-state.d.ts +0 -50
  96. package/dist/orchestration/tool-loop-state.js +0 -133
  97. package/dist/orchestration/tool-schema.d.ts +0 -39
  98. package/dist/orchestration/tool-schema.js +0 -297
  99. package/dist/orchestration/transcript-repair.d.ts +0 -42
  100. package/dist/orchestration/transcript-repair.js +0 -426
  101. package/dist/orchestration/turn-loop-guard.d.ts +0 -86
  102. package/dist/orchestration/turn-loop-guard.js +0 -92
  103. package/dist/orchestration/web-browser-policy.d.ts +0 -17
  104. package/dist/orchestration/web-browser-policy.js +0 -39
  105. package/dist/runtime/context-compression.d.ts +0 -61
  106. package/dist/runtime/context-compression.js +0 -274
  107. package/dist/runtime/hook-registry.d.ts +0 -12
  108. package/dist/runtime/hook-registry.js +0 -53
  109. package/dist/runtime/memory-hooks.d.ts +0 -23
  110. package/dist/runtime/memory-hooks.js +0 -65
  111. package/dist/runtime/tool-eligibility.d.ts +0 -59
  112. package/dist/runtime/tool-eligibility.js +0 -111
  113. package/dist/skills/index.d.ts +0 -108
  114. package/dist/skills/index.js +0 -82
  115. package/dist/skills/memory-extractor.d.ts +0 -64
  116. package/dist/skills/memory-extractor.js +0 -173
  117. package/dist/skills/memory-query-tool.d.ts +0 -43
  118. package/dist/skills/memory-query-tool.js +0 -127
  119. package/dist/skills/memory-store.d.ts +0 -66
  120. package/dist/skills/memory-store.js +0 -228
  121. package/dist/skills/memory-tool.d.ts +0 -67
  122. package/dist/skills/memory-tool.js +0 -192
  123. package/dist/skills/portable-tool.d.ts +0 -71
  124. package/dist/skills/portable-tool.js +0 -14
  125. package/dist/skills/qmemory-adapter.d.ts +0 -52
  126. package/dist/skills/qmemory-adapter.js +0 -165
  127. package/dist/skills/skill-frontmatter.d.ts +0 -19
  128. package/dist/skills/skill-frontmatter.js +0 -344
  129. package/dist/skills/skill-guard.d.ts +0 -23
  130. package/dist/skills/skill-guard.js +0 -229
  131. package/dist/skills/skill-loader.d.ts +0 -16
  132. package/dist/skills/skill-loader.js +0 -303
  133. package/dist/skills/skill-source.d.ts +0 -119
  134. package/dist/skills/skill-source.js +0 -126
  135. package/dist/skills/skill-types.d.ts +0 -199
  136. package/dist/skills/skill-types.js +0 -6
  137. package/dist/skills/think-tool.d.ts +0 -16
  138. package/dist/skills/think-tool.js +0 -59
  139. package/dist/skills/todo-tool.d.ts +0 -63
  140. package/dist/skills/todo-tool.js +0 -114
  141. package/dist/skills/tools/agent-tool.d.ts +0 -91
  142. package/dist/skills/tools/agent-tool.js +0 -142
  143. package/dist/skills/tools/apply-patch-tool.d.ts +0 -29
  144. package/dist/skills/tools/apply-patch-tool.js +0 -184
  145. package/dist/skills/tools/ask-user-tool.d.ts +0 -80
  146. package/dist/skills/tools/ask-user-tool.js +0 -121
  147. package/dist/skills/tools/brief-tool.d.ts +0 -74
  148. package/dist/skills/tools/brief-tool.js +0 -95
  149. package/dist/skills/tools/browser-tool.d.ts +0 -114
  150. package/dist/skills/tools/browser-tool.js +0 -155
  151. package/dist/skills/tools/checkpoint-tool.d.ts +0 -66
  152. package/dist/skills/tools/checkpoint-tool.js +0 -102
  153. package/dist/skills/tools/config-tool.d.ts +0 -63
  154. package/dist/skills/tools/config-tool.js +0 -143
  155. package/dist/skills/tools/cron-tool.d.ts +0 -116
  156. package/dist/skills/tools/cron-tool.js +0 -175
  157. package/dist/skills/tools/edit-tool.d.ts +0 -43
  158. package/dist/skills/tools/edit-tool.js +0 -70
  159. package/dist/skills/tools/exec-tool.d.ts +0 -102
  160. package/dist/skills/tools/exec-tool.js +0 -133
  161. package/dist/skills/tools/image-generate-tool.d.ts +0 -62
  162. package/dist/skills/tools/image-generate-tool.js +0 -67
  163. package/dist/skills/tools/instructions-tool.d.ts +0 -103
  164. package/dist/skills/tools/instructions-tool.js +0 -187
  165. package/dist/skills/tools/lsp-tool.d.ts +0 -153
  166. package/dist/skills/tools/lsp-tool.js +0 -227
  167. package/dist/skills/tools/mcp-client-types.d.ts +0 -269
  168. package/dist/skills/tools/mcp-client-types.js +0 -53
  169. package/dist/skills/tools/mcp-tool.d.ts +0 -249
  170. package/dist/skills/tools/mcp-tool.js +0 -503
  171. package/dist/skills/tools/memory-tool.d.ts +0 -74
  172. package/dist/skills/tools/memory-tool.js +0 -88
  173. package/dist/skills/tools/monitor-tool.d.ts +0 -113
  174. package/dist/skills/tools/monitor-tool.js +0 -131
  175. package/dist/skills/tools/music-generate-tool.d.ts +0 -55
  176. package/dist/skills/tools/music-generate-tool.js +0 -62
  177. package/dist/skills/tools/notify-tool.d.ts +0 -53
  178. package/dist/skills/tools/notify-tool.js +0 -62
  179. package/dist/skills/tools/patch-tool.d.ts +0 -45
  180. package/dist/skills/tools/patch-tool.js +0 -505
  181. package/dist/skills/tools/pdf-tool.d.ts +0 -66
  182. package/dist/skills/tools/pdf-tool.js +0 -88
  183. package/dist/skills/tools/plan-mode-tool.d.ts +0 -59
  184. package/dist/skills/tools/plan-mode-tool.js +0 -122
  185. package/dist/skills/tools/read-tool.d.ts +0 -51
  186. package/dist/skills/tools/read-tool.js +0 -84
  187. package/dist/skills/tools/repl-tool.d.ts +0 -70
  188. package/dist/skills/tools/repl-tool.js +0 -69
  189. package/dist/skills/tools/search-tool.d.ts +0 -112
  190. package/dist/skills/tools/search-tool.js +0 -225
  191. package/dist/skills/tools/send-message-tool.d.ts +0 -51
  192. package/dist/skills/tools/send-message-tool.js +0 -76
  193. package/dist/skills/tools/skill-list-tool.d.ts +0 -33
  194. package/dist/skills/tools/skill-list-tool.js +0 -54
  195. package/dist/skills/tools/skill-manage-tool.d.ts +0 -73
  196. package/dist/skills/tools/skill-manage-tool.js +0 -153
  197. package/dist/skills/tools/skill-view-tool.d.ts +0 -37
  198. package/dist/skills/tools/skill-view-tool.js +0 -72
  199. package/dist/skills/tools/sleep-tool.d.ts +0 -49
  200. package/dist/skills/tools/sleep-tool.js +0 -81
  201. package/dist/skills/tools/structured-output-tool.d.ts +0 -116
  202. package/dist/skills/tools/structured-output-tool.js +0 -176
  203. package/dist/skills/tools/task-tool.d.ts +0 -104
  204. package/dist/skills/tools/task-tool.js +0 -161
  205. package/dist/skills/tools/team-tool.d.ts +0 -89
  206. package/dist/skills/tools/team-tool.js +0 -105
  207. package/dist/skills/tools/tool-search-tool.d.ts +0 -51
  208. package/dist/skills/tools/tool-search-tool.js +0 -110
  209. package/dist/skills/tools/tts-tool.d.ts +0 -38
  210. package/dist/skills/tools/tts-tool.js +0 -45
  211. package/dist/skills/tools/video-edit-tool.d.ts +0 -69
  212. package/dist/skills/tools/video-edit-tool.js +0 -74
  213. package/dist/skills/tools/video-generate-tool.d.ts +0 -62
  214. package/dist/skills/tools/video-generate-tool.js +0 -66
  215. package/dist/skills/tools/video-merge-tool.d.ts +0 -105
  216. package/dist/skills/tools/video-merge-tool.js +0 -92
  217. package/dist/skills/tools/video-upscale-tool.d.ts +0 -45
  218. package/dist/skills/tools/video-upscale-tool.js +0 -52
  219. package/dist/skills/tools/web-fetch-tool.d.ts +0 -78
  220. package/dist/skills/tools/web-fetch-tool.js +0 -92
  221. package/dist/skills/tools/web-search-tool.d.ts +0 -57
  222. package/dist/skills/tools/web-search-tool.js +0 -86
  223. package/dist/skills/tools/worktree-tool.d.ts +0 -69
  224. package/dist/skills/tools/worktree-tool.js +0 -147
  225. package/dist/skills/tools/write-tool.d.ts +0 -45
  226. package/dist/skills/tools/write-tool.js +0 -81
@@ -1,583 +0,0 @@
1
- // ============================================================
2
- // Context compression strategies — reduce message history
3
- // to fit within a token budget.
4
- //
5
- // Phase 1: ToolResultTrim + SlidingWindow (sync, no LLM)
6
- // Phase 2: Structured LLM summarization + head/tail protection
7
- // Phase 3: Prompt cache awareness
8
- // Phase 4: Adaptive threshold + metrics + engine plugin
9
- // ============================================================
10
- export function isAsyncCompressionStrategy(s) {
11
- return typeof s.compressAsync === "function";
12
- }
13
- // ── Sliding Window ──────────────────────────────────────────
14
- // Keep the system prompt(s) + last N messages that fit the budget.
15
- export class SlidingWindowStrategy {
16
- estimateTokens;
17
- constructor(estimateTokens) {
18
- this.estimateTokens = estimateTokens;
19
- }
20
- compress(messages, budget) {
21
- const systemMessages = [];
22
- const nonSystem = [];
23
- for (const msg of messages) {
24
- if (msg.role === "system") {
25
- systemMessages.push(msg);
26
- }
27
- else {
28
- nonSystem.push(msg);
29
- }
30
- }
31
- let remaining = budget;
32
- for (const sys of systemMessages) {
33
- remaining -= this.estimateTokens(sys);
34
- }
35
- if (remaining <= 0) {
36
- return { messages: systemMessages, droppedCount: nonSystem.length, strategy: "sliding-window" };
37
- }
38
- const kept = [];
39
- for (let i = nonSystem.length - 1; i >= 0; i--) {
40
- const cost = this.estimateTokens(nonSystem[i]);
41
- if (remaining - cost < 0)
42
- break;
43
- remaining -= cost;
44
- kept.unshift(nonSystem[i]);
45
- }
46
- return {
47
- messages: [...systemMessages, ...kept],
48
- droppedCount: nonSystem.length - kept.length,
49
- strategy: "sliding-window",
50
- };
51
- }
52
- }
53
- // ── Summarize Old ───────────────────────────────────────────
54
- // Keep last `recentCount` messages as-is, replace older ones
55
- // with a single system-role summary placeholder.
56
- export class SummarizeOldStrategy {
57
- recentCount;
58
- summarize;
59
- constructor(recentCount, summarize) {
60
- this.recentCount = recentCount;
61
- this.summarize = summarize;
62
- }
63
- compress(messages, _budget) {
64
- const systemMessages = messages.filter((m) => m.role === "system");
65
- const nonSystem = messages.filter((m) => m.role !== "system");
66
- if (nonSystem.length <= this.recentCount) {
67
- return { messages, droppedCount: 0, strategy: "summarize-old" };
68
- }
69
- const oldMessages = nonSystem.slice(0, nonSystem.length - this.recentCount);
70
- const recentMessages = nonSystem.slice(nonSystem.length - this.recentCount);
71
- const summary = this.summarize(oldMessages);
72
- return {
73
- messages: [
74
- ...systemMessages,
75
- { role: "system", content: `[Conversation summary]\n${summary}` },
76
- ...recentMessages,
77
- ],
78
- droppedCount: oldMessages.length,
79
- strategy: "summarize-old",
80
- };
81
- }
82
- }
83
- // ── Tool Result Trim ────────────────────────────────────────
84
- // Truncate oversized tool results to a max character length.
85
- export class ToolResultTrimStrategy {
86
- maxToolResultChars;
87
- constructor(maxToolResultChars = 8000) {
88
- this.maxToolResultChars = maxToolResultChars;
89
- }
90
- compress(messages, _budget) {
91
- let trimmedCount = 0;
92
- const result = messages.map((msg) => {
93
- if (msg.role !== "tool" || typeof msg.content !== "string")
94
- return msg;
95
- if (msg.content.length <= this.maxToolResultChars)
96
- return msg;
97
- trimmedCount++;
98
- return {
99
- ...msg,
100
- content: msg.content.slice(0, this.maxToolResultChars) + "\n[...truncated]",
101
- };
102
- });
103
- return {
104
- messages: result,
105
- droppedCount: trimmedCount,
106
- strategy: "tool-result-trim",
107
- };
108
- }
109
- }
110
- // ── Composite ───────────────────────────────────────────────
111
- // Apply multiple strategies in sequence.
112
- export function composeStrategies(...strategies) {
113
- return {
114
- compress(messages, budget) {
115
- let current = messages;
116
- let totalDropped = 0;
117
- const names = [];
118
- for (const strategy of strategies) {
119
- const result = strategy.compress(current, budget);
120
- current = result.messages;
121
- totalDropped += result.droppedCount;
122
- if (result.droppedCount > 0) {
123
- names.push(result.strategy);
124
- }
125
- }
126
- return {
127
- messages: current,
128
- droppedCount: totalDropped,
129
- strategy: names.length > 0 ? names.join("+") : "none",
130
- };
131
- },
132
- };
133
- }
134
- /**
135
- * Compose strategies with async support — if any strategy is async,
136
- * the pipeline becomes async.
137
- */
138
- export function composeAsyncStrategies(...strategies) {
139
- return {
140
- compress(messages, budget) {
141
- // Sync fallback: skip async strategies
142
- let current = messages;
143
- let totalDropped = 0;
144
- const names = [];
145
- for (const strategy of strategies) {
146
- const result = strategy.compress(current, budget);
147
- current = result.messages;
148
- totalDropped += result.droppedCount;
149
- if (result.droppedCount > 0)
150
- names.push(result.strategy);
151
- }
152
- return {
153
- messages: current,
154
- droppedCount: totalDropped,
155
- strategy: names.length > 0 ? names.join("+") : "none",
156
- };
157
- },
158
- async compressAsync(messages, budget) {
159
- let current = messages;
160
- let totalDropped = 0;
161
- const names = [];
162
- let totalLatency = 0;
163
- let anyLlm = false;
164
- let anyCacheInvalidated = false;
165
- for (const strategy of strategies) {
166
- const result = isAsyncCompressionStrategy(strategy)
167
- ? await strategy.compressAsync(current, budget)
168
- : strategy.compress(current, budget);
169
- current = result.messages;
170
- totalDropped += result.droppedCount;
171
- if (result.droppedCount > 0)
172
- names.push(result.strategy);
173
- if (result.metrics) {
174
- totalLatency += result.metrics.latencyMs;
175
- anyLlm = anyLlm || result.metrics.usedLlm;
176
- anyCacheInvalidated = anyCacheInvalidated || !!result.metrics.cacheInvalidated;
177
- }
178
- }
179
- return {
180
- messages: current,
181
- droppedCount: totalDropped,
182
- strategy: names.length > 0 ? names.join("+") : "none",
183
- metrics: totalLatency > 0 || anyLlm
184
- ? { tokensBefore: 0, tokensAfter: 0, compressionRatio: 0, latencyMs: totalLatency, usedLlm: anyLlm, cacheInvalidated: anyCacheInvalidated }
185
- : undefined,
186
- };
187
- },
188
- };
189
- }
190
- // ════════════════════════════════════════════════════════════
191
- // Phase 2: LLM Summarization Strategies
192
- // ════════════════════════════════════════════════════════════
193
- // ── 2.1 Structured Summary Prompt (Claude Code style) ──────
194
- /**
195
- * Build the structured 9-section summary instruction for the LLM.
196
- * Based on Claude Code's Full Compact mode, adapted for Hub.
197
- */
198
- export function buildStructuredSummaryPrompt(messagesToSummarize, opts) {
199
- const userMsgs = messagesToSummarize.filter((m) => m.role === "user");
200
- const toolCalls = messagesToSummarize.filter((m) => m.tool_calls != null);
201
- const toolResults = messagesToSummarize.filter((m) => m.role === "tool");
202
- const sections = [
203
- "You are a conversation summarizer. Produce a structured summary of the conversation history below.",
204
- "",
205
- "## Instructions",
206
- "Analyze the conversation and produce a summary with these sections:",
207
- "",
208
- "### 1. Primary Objective",
209
- "What is the user's main goal or task? State it in one sentence.",
210
- "",
211
- "### 2. Key Decisions Made",
212
- "List the important decisions, choices, or conclusions reached during the conversation.",
213
- "",
214
- "### 3. Current Progress",
215
- `Describe the current state. ${toolCalls.length > 0 ? `${toolCalls.length} tool calls and ${toolResults.length} tool results were exchanged.` : "No tools were used."}`,
216
- "",
217
- "### 4. Pending Tasks",
218
- "List any tasks that are in-progress or planned but not yet completed.",
219
- "",
220
- "### 5. Important Context",
221
- "Note any critical facts, constraints, or preferences the user mentioned that must be preserved.",
222
- "",
223
- "### 6. Error & Recovery History",
224
- "Summarize any errors encountered and how they were resolved.",
225
- "",
226
- "### 7. User Preferences Expressed",
227
- `The user sent ${userMsgs.length} messages. Note any stated preferences about style, approach, or constraints.`,
228
- "",
229
- "### 8. Technical State",
230
- "Note file paths, variable names, API endpoints, or configuration values that were discussed.",
231
- "",
232
- "### 9. Conversation Flow",
233
- "Briefly describe the overall flow: what happened first, what changed, where we are now.",
234
- ];
235
- if (opts?.taskContext) {
236
- sections.push("", `## Additional Context`, opts.taskContext);
237
- }
238
- sections.push("", "## Conversation to Summarize", "", ...messagesToSummarize.map((m) => {
239
- const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content ?? "");
240
- const truncated = content.length > 2000 ? content.slice(0, 2000) + "..." : content;
241
- return `[${m.role}]: ${truncated}`;
242
- }), "", "## Output Format", "Respond with a concise summary covering all 9 sections above. Use markdown headers.", "Keep the total summary under 800 words. Focus on actionable information.");
243
- return sections.join("\n");
244
- }
245
- /**
246
- * Phase 2.2: Head/Tail protected summarization.
247
- *
248
- * Protects: system messages + first exchange + last N messages.
249
- * Compresses: middle section via LLM summarization.
250
- */
251
- export class HeadTailProtectedStrategy {
252
- config;
253
- constructor(config) {
254
- this.config = {
255
- protectedHeadExchanges: config.protectedHeadExchanges,
256
- protectedTailMessages: config.protectedTailMessages,
257
- summarize: config.summarize,
258
- estimateTokens: config.estimateTokens ?? defaultEstimateTokens,
259
- taskContext: config.taskContext,
260
- };
261
- }
262
- compress(messages, _budget) {
263
- // Sync fallback: just pass through (cannot call LLM synchronously)
264
- return { messages, droppedCount: 0, strategy: "head-tail-protected" };
265
- }
266
- async compressAsync(messages, budget) {
267
- const start = Date.now();
268
- const { system, nonSystem } = splitSystemMessages(messages);
269
- // Check if compression is needed
270
- const totalTokens = messages.reduce((sum, m) => sum + this.config.estimateTokens(m), 0);
271
- if (totalTokens <= budget) {
272
- return { messages, droppedCount: 0, strategy: "head-tail-protected" };
273
- }
274
- // Identify protected head: first N user+assistant exchanges
275
- let headEnd = 0;
276
- let exchangeCount = 0;
277
- for (let i = 0; i < nonSystem.length; i++) {
278
- if (nonSystem[i].role === "user")
279
- exchangeCount++;
280
- if (exchangeCount > this.config.protectedHeadExchanges)
281
- break;
282
- headEnd = i + 1;
283
- }
284
- // Protected tail
285
- const tailStart = Math.max(headEnd, nonSystem.length - this.config.protectedTailMessages);
286
- // If there's nothing to compress in the middle, pass through
287
- if (tailStart <= headEnd) {
288
- return { messages, droppedCount: 0, strategy: "head-tail-protected" };
289
- }
290
- const headMessages = nonSystem.slice(0, headEnd);
291
- const middleMessages = nonSystem.slice(headEnd, tailStart);
292
- const tailMessages = nonSystem.slice(tailStart);
293
- // Summarize middle section via LLM
294
- const instruction = buildStructuredSummaryPrompt(middleMessages, {
295
- taskContext: this.config.taskContext,
296
- });
297
- const summary = await this.config.summarize(middleMessages, instruction);
298
- const summaryMessage = {
299
- role: "system",
300
- content: `[Conversation summary — ${middleMessages.length} messages compressed]\n\n${summary}`,
301
- };
302
- const resultMessages = [
303
- ...system,
304
- ...headMessages,
305
- summaryMessage,
306
- ...tailMessages,
307
- ];
308
- const latencyMs = Date.now() - start;
309
- const tokensAfter = resultMessages.reduce((sum, m) => sum + this.config.estimateTokens(m), 0);
310
- return {
311
- messages: resultMessages,
312
- droppedCount: middleMessages.length,
313
- strategy: "head-tail-protected",
314
- metrics: {
315
- tokensBefore: totalTokens,
316
- tokensAfter,
317
- compressionRatio: totalTokens > 0 ? tokensAfter / totalTokens : 1,
318
- latencyMs,
319
- usedLlm: true,
320
- cacheInvalidated: true,
321
- },
322
- };
323
- }
324
- }
325
- /**
326
- * Phase 2.3: Incremental (partial) compaction.
327
- *
328
- * Only summarizes the oldest messages beyond the preserve window.
329
- * Avoids repeatedly re-summarizing already-compressed content.
330
- * If a previous summary marker exists, only new old messages are compressed.
331
- */
332
- export class IncrementalCompactStrategy {
333
- config;
334
- constructor(config) {
335
- this.config = {
336
- preserveRecentCount: config.preserveRecentCount,
337
- summarize: config.summarize,
338
- estimateTokens: config.estimateTokens ?? defaultEstimateTokens,
339
- };
340
- }
341
- compress(messages, _budget) {
342
- return { messages, droppedCount: 0, strategy: "incremental-compact" };
343
- }
344
- async compressAsync(messages, budget) {
345
- const start = Date.now();
346
- const { system, nonSystem } = splitSystemMessages(messages);
347
- // Find existing summary marker
348
- const existingSummaryIdx = system.findIndex((m) => typeof m.content === "string" && m.content.startsWith("[Conversation summary"));
349
- const existingSummary = existingSummaryIdx >= 0 ? system[existingSummaryIdx] : undefined;
350
- const systemWithoutOldSummary = existingSummaryIdx >= 0
351
- ? [...system.slice(0, existingSummaryIdx), ...system.slice(existingSummaryIdx + 1)]
352
- : system;
353
- // How many messages to preserve
354
- const preserveStart = Math.max(0, nonSystem.length - this.config.preserveRecentCount);
355
- if (preserveStart <= 0) {
356
- return { messages, droppedCount: 0, strategy: "incremental-compact" };
357
- }
358
- const totalTokens = messages.reduce((sum, m) => sum + this.config.estimateTokens(m), 0);
359
- if (totalTokens <= budget) {
360
- return { messages, droppedCount: 0, strategy: "incremental-compact" };
361
- }
362
- const oldMessages = nonSystem.slice(0, preserveStart);
363
- const recentMessages = nonSystem.slice(preserveStart);
364
- // Build summary instruction including existing summary context
365
- const contextPrefix = existingSummary && typeof existingSummary.content === "string"
366
- ? `Previous summary:\n${existingSummary.content}\n\nNew messages to integrate:`
367
- : undefined;
368
- const instruction = buildStructuredSummaryPrompt(oldMessages, { taskContext: contextPrefix });
369
- const summary = await this.config.summarize(oldMessages, instruction);
370
- const summaryMessage = {
371
- role: "system",
372
- content: `[Conversation summary — ${oldMessages.length} messages compressed]\n\n${summary}`,
373
- };
374
- const resultMessages = [...systemWithoutOldSummary, summaryMessage, ...recentMessages];
375
- const latencyMs = Date.now() - start;
376
- const tokensAfter = resultMessages.reduce((sum, m) => sum + this.config.estimateTokens(m), 0);
377
- return {
378
- messages: resultMessages,
379
- droppedCount: oldMessages.length,
380
- strategy: "incremental-compact",
381
- metrics: {
382
- tokensBefore: totalTokens,
383
- tokensAfter,
384
- compressionRatio: totalTokens > 0 ? tokensAfter / totalTokens : 1,
385
- latencyMs,
386
- usedLlm: true,
387
- cacheInvalidated: true,
388
- },
389
- };
390
- }
391
- }
392
- /**
393
- * Phase 3: Cache-aware wrapper.
394
- *
395
- * Wraps any strategy and tracks whether compression invalidated the
396
- * provider prompt cache. System prompt prefix stability is preserved
397
- * by never modifying system[0] (the original system prompt).
398
- */
399
- export class CacheAwareCompressionStrategy {
400
- config;
401
- constructor(config) {
402
- this.config = config;
403
- }
404
- compress(messages, budget) {
405
- const beforeHash = computeMessagePrefixHash(messages);
406
- const result = this.config.inner.compress(messages, budget);
407
- const afterHash = computeMessagePrefixHash(result.messages);
408
- const cacheInvalidated = beforeHash !== afterHash && result.droppedCount > 0;
409
- if (cacheInvalidated) {
410
- this.config.onCacheInvalidated?.({ droppedCount: result.droppedCount, strategy: result.strategy });
411
- }
412
- return {
413
- ...result,
414
- metrics: {
415
- ...(result.metrics ?? {
416
- tokensBefore: 0, tokensAfter: 0, compressionRatio: 0, latencyMs: 0, usedLlm: false,
417
- }),
418
- cacheInvalidated,
419
- },
420
- };
421
- }
422
- async compressAsync(messages, budget) {
423
- const beforeHash = computeMessagePrefixHash(messages);
424
- const result = isAsyncCompressionStrategy(this.config.inner)
425
- ? await this.config.inner.compressAsync(messages, budget)
426
- : this.config.inner.compress(messages, budget);
427
- const afterHash = computeMessagePrefixHash(result.messages);
428
- const cacheInvalidated = beforeHash !== afterHash && result.droppedCount > 0;
429
- if (cacheInvalidated) {
430
- this.config.onCacheInvalidated?.({ droppedCount: result.droppedCount, strategy: result.strategy });
431
- }
432
- return {
433
- ...result,
434
- metrics: {
435
- ...(result.metrics ?? {
436
- tokensBefore: 0, tokensAfter: 0, compressionRatio: 0, latencyMs: 0, usedLlm: false,
437
- }),
438
- cacheInvalidated,
439
- },
440
- };
441
- }
442
- }
443
- export const DEFAULT_ADAPTIVE_BUDGET_CONFIG = {
444
- modelContextWindow: 128_000,
445
- targetUsageRatio: 0.75,
446
- minBudget: 16_000,
447
- maxBudget: 120_000,
448
- };
449
- /**
450
- * Compute the adaptive token budget for a given model + message history.
451
- *
452
- * Adjusts based on model context window, and uses the target ratio
453
- * so compression triggers before hitting the hard limit.
454
- */
455
- export function computeAdaptiveBudget(config = {}) {
456
- const c = { ...DEFAULT_ADAPTIVE_BUDGET_CONFIG, ...config };
457
- const target = Math.floor(c.modelContextWindow * c.targetUsageRatio);
458
- return Math.max(c.minBudget, Math.min(target, c.maxBudget));
459
- }
460
- export function selectCompressionTier(currentTokens, budget) {
461
- const ratio = currentTokens / budget;
462
- if (ratio <= 0.8)
463
- return "none";
464
- if (ratio <= 1.0)
465
- return "trim-only";
466
- if (ratio <= 1.5)
467
- return "sliding-window";
468
- return "llm-summarize";
469
- }
470
- export class CompressionMetricsCollector {
471
- events = [];
472
- maxEvents;
473
- constructor(maxEvents = 100) {
474
- this.maxEvents = maxEvents;
475
- }
476
- record(event) {
477
- this.events.push(event);
478
- if (this.events.length > this.maxEvents) {
479
- this.events.shift();
480
- }
481
- }
482
- snapshot() {
483
- const total = this.events.length;
484
- if (total === 0) {
485
- return {
486
- totalCompressions: 0,
487
- totalLlmCalls: 0,
488
- totalCacheInvalidations: 0,
489
- averageCompressionRatio: 1,
490
- averageLatencyMs: 0,
491
- totalTokensSaved: 0,
492
- recentEvents: [],
493
- };
494
- }
495
- let sumRatio = 0;
496
- let sumLatency = 0;
497
- let tokensSaved = 0;
498
- let llmCalls = 0;
499
- let cacheInvalidations = 0;
500
- for (const e of this.events) {
501
- sumRatio += e.tokensBefore > 0 ? e.tokensAfter / e.tokensBefore : 1;
502
- sumLatency += e.latencyMs;
503
- tokensSaved += Math.max(0, e.tokensBefore - e.tokensAfter);
504
- if (e.usedLlm)
505
- llmCalls++;
506
- if (e.cacheInvalidated)
507
- cacheInvalidations++;
508
- }
509
- return {
510
- totalCompressions: total,
511
- totalLlmCalls: llmCalls,
512
- totalCacheInvalidations: cacheInvalidations,
513
- averageCompressionRatio: sumRatio / total,
514
- averageLatencyMs: sumLatency / total,
515
- totalTokensSaved: tokensSaved,
516
- recentEvents: this.events.slice(-10),
517
- };
518
- }
519
- reset() {
520
- this.events.length = 0;
521
- }
522
- }
523
- /**
524
- * Registry for context engines. Enforces single-active constraint.
525
- */
526
- export class ContextEngineRegistry {
527
- engines = new Map();
528
- activeId;
529
- register(engine) {
530
- this.engines.set(engine.id, engine);
531
- }
532
- activate(id) {
533
- if (!this.engines.has(id))
534
- return false;
535
- this.activeId = id;
536
- return true;
537
- }
538
- getActive() {
539
- return this.activeId ? this.engines.get(this.activeId) : undefined;
540
- }
541
- listEngines() {
542
- return Array.from(this.engines.values()).map((e) => ({
543
- id: e.id,
544
- label: e.label,
545
- active: e.id === this.activeId,
546
- }));
547
- }
548
- }
549
- // ── Helpers ─────────────────────────────────────────────────
550
- function splitSystemMessages(messages) {
551
- const system = [];
552
- const nonSystem = [];
553
- for (const msg of messages) {
554
- if (msg.role === "system")
555
- system.push(msg);
556
- else
557
- nonSystem.push(msg);
558
- }
559
- return { system, nonSystem };
560
- }
561
- function defaultEstimateTokens(msg) {
562
- const text = typeof msg.content === "string"
563
- ? msg.content
564
- : msg.content != null
565
- ? JSON.stringify(msg.content)
566
- : "";
567
- return Math.ceil(text.length / 4);
568
- }
569
- /**
570
- * Simple hash of the first few messages' role+content prefix.
571
- * Used to detect whether compression changed the prompt prefix
572
- * (which would invalidate provider prompt caching).
573
- */
574
- function computeMessagePrefixHash(messages) {
575
- const prefixCount = Math.min(messages.length, 5);
576
- const parts = [];
577
- for (let i = 0; i < prefixCount; i++) {
578
- const m = messages[i];
579
- const content = typeof m.content === "string" ? m.content.slice(0, 200) : "";
580
- parts.push(`${m.role}:${content}`);
581
- }
582
- return parts.join("|");
583
- }
@@ -1,61 +0,0 @@
1
- export interface OpenAiToolCall {
2
- id: string;
3
- type: "function";
4
- function: {
5
- name: string;
6
- arguments: string;
7
- };
8
- }
9
- export interface OpenAiChatToolCallRef {
10
- id?: string;
11
- function?: {
12
- name?: string;
13
- arguments?: string;
14
- };
15
- name?: string;
16
- [key: string]: unknown;
17
- }
18
- export interface OpenAiChatMessageLike {
19
- role: string;
20
- content?: unknown;
21
- tool_calls?: OpenAiChatToolCallRef[];
22
- tool_call_id?: string;
23
- [key: string]: unknown;
24
- }
25
- export interface OpenAiResponsesItemLike {
26
- type: string;
27
- id?: string;
28
- call_id?: string;
29
- role?: string;
30
- content?: unknown;
31
- status?: string;
32
- [key: string]: unknown;
33
- }
34
- export interface ConversationRepairOptions {
35
- stopReason?: string;
36
- placeholderToolResult?: string;
37
- placeholderFunctionCallOutput?: string;
38
- forcedStopReasons?: readonly string[];
39
- }
40
- type ContentPart = {
41
- type: string;
42
- text?: string;
43
- [key: string]: unknown;
44
- };
45
- export type { ContentPart };
46
- export declare function sanitizeOpenAiChatMessages<T extends OpenAiChatMessageLike>(messages: readonly T[]): T[];
47
- export declare function ensureAssistantReasoningContent<T extends Record<string, unknown>>(messages: readonly T[], defaultValue?: string): T[];
48
- export declare function stripForcedStopAssistantToolMetadata<T extends OpenAiChatMessageLike>(messages: readonly T[], options?: ConversationRepairOptions): T[];
49
- export declare function injectDanglingToolCallPlaceholders<T extends OpenAiChatMessageLike>(messages: readonly T[], options?: ConversationRepairOptions): T[];
50
- export declare function repairOpenAiChatConversation<T extends OpenAiChatMessageLike>(messages: readonly T[], options?: ConversationRepairOptions): T[];
51
- export declare function downgradeOpenAiFunctionCallReasoningPairs<T extends {
52
- role?: unknown;
53
- content?: unknown;
54
- toolCallId?: unknown;
55
- toolUseId?: unknown;
56
- }>(messages: readonly T[]): T[];
57
- export declare function downgradeOpenAiReasoningBlocks<T extends {
58
- role?: unknown;
59
- content?: unknown;
60
- }>(messages: readonly T[]): T[];
61
- export declare function repairOpenAiResponsesItems<T extends OpenAiResponsesItemLike>(items: readonly T[], options?: ConversationRepairOptions): T[];