@cuylabs/agent-core 0.8.0 → 0.10.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 (127) hide show
  1. package/README.md +33 -17
  2. package/dist/chunk-2O4MCSQS.js +780 -0
  3. package/dist/chunk-2TTOLHBT.js +198 -0
  4. package/dist/chunk-5FMSGQVX.js +281 -0
  5. package/dist/chunk-5NVVNXPQ.js +288 -0
  6. package/dist/{chunk-CAA7FHIH.js → chunk-6HZBHFOL.js} +3 -103
  7. package/dist/chunk-CJI7PVS2.js +58 -0
  8. package/dist/{chunk-N6HWIEEA.js → chunk-CMYN2RCB.js} +278 -61
  9. package/dist/chunk-FII65CN7.js +117 -0
  10. package/dist/{chunk-IVUJDISU.js → chunk-GFTW23FV.js} +5 -14
  11. package/dist/chunk-I6PKJ7XQ.js +292 -0
  12. package/dist/{chunk-BDBZ3SLK.js → chunk-ICZ66572.js} +48 -4
  13. package/dist/chunk-KYLPMBHD.js +316 -0
  14. package/dist/chunk-MXAP4UG6.js +2956 -0
  15. package/dist/{chunk-RZITT45F.js → chunk-N3VX7FEE.js} +39 -6
  16. package/dist/{chunk-YSLSEQ6B.js → chunk-NDZWXCBZ.js} +218 -95
  17. package/dist/{chunk-P6YF7USR.js → chunk-Q742PSH3.js} +23 -38
  18. package/dist/chunk-QAL3OMI3.js +943 -0
  19. package/dist/{chunk-RFEKJKTO.js → chunk-RN6WZEUF.js} +330 -280
  20. package/dist/{chunk-ZXAKHMWH.js → chunk-ROTGCYDW.js} +22 -84
  21. package/dist/chunk-SPBFQXOT.js +0 -0
  22. package/dist/{chunk-LRHOS4ZN.js → chunk-SPILYYDF.js} +3 -2
  23. package/dist/chunk-SSFBF3US.js +602 -0
  24. package/dist/chunk-SZ2XBPTW.js +8 -0
  25. package/dist/chunk-T4UIX5D7.js +115 -0
  26. package/dist/chunk-TIHPYVAJ.js +102 -0
  27. package/dist/{chunk-YUUJK53A.js → chunk-TOTDGK3P.js} +1 -1
  28. package/dist/chunk-V4RFNEET.js +563 -0
  29. package/dist/chunk-VOUEJSW6.js +0 -0
  30. package/dist/{chunk-4BDA7DQY.js → chunk-WBPOZ7CL.js} +673 -273
  31. package/dist/chunk-X4VN4GIJ.js +185 -0
  32. package/dist/dispatch/index.d.ts +93 -0
  33. package/dist/dispatch/index.js +37 -0
  34. package/dist/events/index.d.ts +93 -0
  35. package/dist/events/index.js +6 -0
  36. package/dist/{runtime → execution}/index.d.ts +120 -34
  37. package/dist/{runtime → execution}/index.js +18 -13
  38. package/dist/index-BCqEGzBj.d.ts +251 -0
  39. package/dist/index.d.ts +490 -122
  40. package/dist/index.js +2104 -615
  41. package/dist/{errors → inference/errors}/index.d.ts +2 -2
  42. package/dist/{errors → inference/errors}/index.js +1 -1
  43. package/dist/inference/index.d.ts +16 -23
  44. package/dist/inference/index.js +45 -16
  45. package/dist/instance-BqV2D5pc.d.ts +5723 -0
  46. package/dist/logger/index.d.ts +50 -0
  47. package/dist/logger/index.js +11 -0
  48. package/dist/mcp/index.d.ts +5 -9
  49. package/dist/mcp/index.js +2 -3
  50. package/dist/middleware/index.d.ts +10 -149
  51. package/dist/middleware/index.js +11 -3
  52. package/dist/model-messages-B4nK9D1-.d.ts +13 -0
  53. package/dist/models/index.d.ts +23 -18
  54. package/dist/models/index.js +48 -11
  55. package/dist/models/reasoning/index.d.ts +4 -0
  56. package/dist/{reasoning → models/reasoning}/index.js +3 -3
  57. package/dist/plugin/index.d.ts +458 -0
  58. package/dist/plugin/index.js +32 -0
  59. package/dist/profiles/index.d.ts +55 -0
  60. package/dist/profiles/index.js +30 -0
  61. package/dist/prompt/index.d.ts +8 -12
  62. package/dist/prompt/index.js +3 -2
  63. package/dist/safety/index.d.ts +109 -14
  64. package/dist/safety/index.js +59 -3
  65. package/dist/sandbox/index.d.ts +81 -0
  66. package/dist/sandbox/index.js +1 -0
  67. package/dist/skill/index.d.ts +10 -8
  68. package/dist/skill/index.js +3 -3
  69. package/dist/storage/index.d.ts +12 -4
  70. package/dist/storage/index.js +1 -1
  71. package/dist/subagents/index.d.ts +177 -0
  72. package/dist/subagents/index.js +78 -0
  73. package/dist/team/index.d.ts +544 -0
  74. package/dist/team/index.js +41 -0
  75. package/dist/tool/host/index.d.ts +41 -0
  76. package/dist/tool/host/index.js +10 -0
  77. package/dist/tool/index.d.ts +125 -21
  78. package/dist/tool/index.js +20 -13
  79. package/dist/{types-VQgymC1N.d.ts → types-Bj_J8u_W.d.ts} +44 -64
  80. package/dist/{types-CHiPh8U2.d.ts → types-C_LCeYNg.d.ts} +7 -7
  81. package/dist/types-RSCv7nQ4.d.ts +59 -0
  82. package/package.json +58 -53
  83. package/dist/builder-UpOWQMW3.d.ts +0 -34
  84. package/dist/chunk-7MUFEN4K.js +0 -559
  85. package/dist/chunk-7VKQ4WPB.js +0 -73
  86. package/dist/chunk-BFM2YHNM.js +0 -222
  87. package/dist/chunk-DWYX7ASF.js +0 -26
  88. package/dist/chunk-KUVSERLJ.js +0 -50
  89. package/dist/chunk-N7P4PN3O.js +0 -84
  90. package/dist/chunk-SDSBEQXG.js +0 -157
  91. package/dist/chunk-SQU2AJHO.js +0 -305
  92. package/dist/chunk-VBWWUHWI.js +0 -724
  93. package/dist/chunk-VEKUXUVF.js +0 -41
  94. package/dist/chunk-VNQBHPCT.js +0 -398
  95. package/dist/chunk-WWYYNWEW.js +0 -259
  96. package/dist/context/index.d.ts +0 -259
  97. package/dist/context/index.js +0 -26
  98. package/dist/events-CE72w8W4.d.ts +0 -149
  99. package/dist/host/index.d.ts +0 -45
  100. package/dist/host/index.js +0 -8
  101. package/dist/index-CWSchSql.d.ts +0 -1058
  102. package/dist/messages-BYWGn8TY.d.ts +0 -110
  103. package/dist/presets/index.d.ts +0 -53
  104. package/dist/presets/index.js +0 -28
  105. package/dist/reasoning/index.d.ts +0 -116
  106. package/dist/registry-DwYqsQkX.d.ts +0 -164
  107. package/dist/runner-e2YRcUoX.d.ts +0 -786
  108. package/dist/scope/index.d.ts +0 -10
  109. package/dist/scope/index.js +0 -14
  110. package/dist/session-manager-B_CWGTsl.d.ts +0 -274
  111. package/dist/signal/index.d.ts +0 -28
  112. package/dist/signal/index.js +0 -6
  113. package/dist/sub-agent/index.d.ts +0 -23
  114. package/dist/sub-agent/index.js +0 -15
  115. package/dist/tool-BHbyUAy3.d.ts +0 -150
  116. package/dist/tool-DLXAR9Ce.d.ts +0 -145
  117. package/dist/tracker-DClqYqTj.d.ts +0 -96
  118. package/dist/tracking/index.d.ts +0 -111
  119. package/dist/tracking/index.js +0 -20
  120. package/dist/types-BfNpU8NS.d.ts +0 -270
  121. package/dist/types-BnpEOYV-.d.ts +0 -50
  122. package/dist/types-CQL-SvTn.d.ts +0 -29
  123. package/dist/types-CWm-7rvB.d.ts +0 -55
  124. package/dist/types-KKDrdU9Y.d.ts +0 -325
  125. package/dist/types-QA4WhEfz.d.ts +0 -138
  126. package/dist/types-QKHHQLLq.d.ts +0 -336
  127. package/dist/types-YuWV4ag7.d.ts +0 -72
@@ -0,0 +1,292 @@
1
+ // src/models/types.ts
2
+ var SourcePriority = /* @__PURE__ */ ((SourcePriority2) => {
3
+ SourcePriority2[SourcePriority2["UserConfig"] = 0] = "UserConfig";
4
+ SourcePriority2[SourcePriority2["LocalCache"] = 1] = "LocalCache";
5
+ SourcePriority2[SourcePriority2["BundledData"] = 2] = "BundledData";
6
+ SourcePriority2[SourcePriority2["PatternMatch"] = 3] = "PatternMatch";
7
+ SourcePriority2[SourcePriority2["RemoteAPI"] = 4] = "RemoteAPI";
8
+ return SourcePriority2;
9
+ })(SourcePriority || {});
10
+ var DEFAULT_RESOLVER_OPTIONS = {
11
+ enableRemoteFetch: false,
12
+ remoteApiUrl: "https://models.dev",
13
+ cachePath: ".agent-core/cache",
14
+ cacheTtlMs: 60 * 60 * 1e3,
15
+ // 1 hour
16
+ networkTimeoutMs: 10 * 1e3,
17
+ // 10 seconds
18
+ modelOverrides: {}
19
+ };
20
+
21
+ // src/models/profiles.ts
22
+ var REASONING_PATTERNS = [
23
+ // OpenAI o-series
24
+ {
25
+ pattern: /^o[134]-?(mini|pro|preview)?$/i,
26
+ provider: "openai",
27
+ capabilities: { reasoning: true, toolCalling: true },
28
+ compatibility: { supportsReasoningEffort: true, thinkingFormat: "openai" },
29
+ confidence: 0.95
30
+ },
31
+ // OpenAI GPT-5.x
32
+ {
33
+ pattern: /gpt-?5(\.\d)?/i,
34
+ provider: "openai",
35
+ capabilities: { reasoning: true, toolCalling: true },
36
+ compatibility: { supportsReasoningEffort: true, thinkingFormat: "openai" },
37
+ confidence: 0.9
38
+ },
39
+ // DeepSeek R1 variants
40
+ {
41
+ pattern: /deepseek[_-]?r1|r1[_-]distill/i,
42
+ capabilities: { reasoning: true, toolCalling: false },
43
+ confidence: 0.95
44
+ },
45
+ // Anthropic Claude with thinking
46
+ {
47
+ pattern: /claude.*thinking|thinking.*claude/i,
48
+ provider: "anthropic",
49
+ capabilities: { reasoning: true, toolCalling: true },
50
+ compatibility: { thinkingFormat: "anthropic" },
51
+ confidence: 0.9
52
+ },
53
+ // Claude 4.x series (reasoning capable)
54
+ {
55
+ pattern: /claude[_-]?(opus|sonnet)[_-]?4/i,
56
+ provider: "anthropic",
57
+ capabilities: { reasoning: true, toolCalling: true },
58
+ compatibility: { thinkingFormat: "anthropic" },
59
+ confidence: 0.85
60
+ },
61
+ // Gemini thinking models
62
+ {
63
+ pattern: /gemini.*thinking|gemini[_-]?2\.5[_-]?pro/i,
64
+ provider: "google",
65
+ capabilities: { reasoning: true, toolCalling: true },
66
+ compatibility: { thinkingFormat: "google" },
67
+ confidence: 0.85
68
+ },
69
+ // Gemini 3.x (future-proofing)
70
+ {
71
+ pattern: /gemini[_-]?3/i,
72
+ provider: "google",
73
+ capabilities: { reasoning: true, toolCalling: true },
74
+ compatibility: { thinkingFormat: "google" },
75
+ confidence: 0.8
76
+ },
77
+ // Grok reasoning models
78
+ {
79
+ pattern: /grok[_-]?\d[_-]?(mini|reasoning)/i,
80
+ provider: "xai",
81
+ capabilities: { reasoning: true, toolCalling: true },
82
+ confidence: 0.85
83
+ },
84
+ // Qwen thinking models
85
+ {
86
+ pattern: /qwen.*thinking|qwen3/i,
87
+ capabilities: { reasoning: true, toolCalling: true },
88
+ confidence: 0.8
89
+ },
90
+ // Generic reasoning/thinking in name
91
+ {
92
+ pattern: /reasoning|thinking/i,
93
+ capabilities: { reasoning: true },
94
+ confidence: 0.7
95
+ }
96
+ ];
97
+ var PROVIDER_PATTERNS = [
98
+ { pattern: /^(gpt|o[134]|chatgpt|davinci)/i, provider: "openai" },
99
+ { pattern: /^claude/i, provider: "anthropic" },
100
+ { pattern: /^gemini|^palm/i, provider: "google" },
101
+ { pattern: /^grok/i, provider: "xai" },
102
+ { pattern: /^deepseek/i, provider: "deepseek" },
103
+ { pattern: /^mistral|^mixtral|codestral/i, provider: "mistral" },
104
+ { pattern: /^llama/i, provider: "meta" },
105
+ { pattern: /^qwen/i, provider: "alibaba" },
106
+ { pattern: /^command/i, provider: "cohere" }
107
+ ];
108
+ var CONTEXT_WINDOW_PROFILES = [
109
+ // Anthropic Claude 4.x — 200k
110
+ { pattern: /claude[_-]?(opus|sonnet)[_-]?4/i, tokens: 2e5 },
111
+ // Anthropic Claude 3.5 — 200k
112
+ { pattern: /claude[_-]?3[._-]?5/i, tokens: 2e5 },
113
+ // Anthropic Claude 3 Opus/Sonnet/Haiku — 200k
114
+ { pattern: /claude[_-]?3/i, tokens: 2e5 },
115
+ // Anthropic Claude 2 — 100k
116
+ { pattern: /claude[_-]?2/i, tokens: 1e5 },
117
+ // OpenAI o-series (o1, o3, o4) — 200k
118
+ { pattern: /^o[134]-?(mini|pro|preview)?$/i, tokens: 2e5 },
119
+ // OpenAI GPT-5.x — 1M
120
+ { pattern: /gpt-?5/i, tokens: 1e6 },
121
+ // OpenAI GPT-4o — 128k
122
+ { pattern: /gpt-?4o/i, tokens: 128e3 },
123
+ // OpenAI GPT-4 turbo — 128k
124
+ { pattern: /gpt-?4[_-]?turbo/i, tokens: 128e3 },
125
+ // OpenAI GPT-4 — 8k (original)
126
+ { pattern: /gpt-?4(?!o|[_-]?turbo)/i, tokens: 8192 },
127
+ // OpenAI GPT-3.5 turbo — 16k
128
+ { pattern: /gpt-?3[._-]?5/i, tokens: 16384 },
129
+ // Google Gemini 2.5 Pro — 1M
130
+ { pattern: /gemini[_-]?2[._-]?5[_-]?pro/i, tokens: 1e6 },
131
+ // Google Gemini 2.x Flash — 1M
132
+ { pattern: /gemini[_-]?2.*flash/i, tokens: 1e6 },
133
+ // Google Gemini 2.x Pro — 1M
134
+ { pattern: /gemini[_-]?2[._-]?\d?[_-]?pro/i, tokens: 1e6 },
135
+ // Google Gemini 1.5 Pro/Flash — 1M
136
+ { pattern: /gemini[_-]?1[._-]?5/i, tokens: 1e6 },
137
+ // xAI Grok 3+ — 128k
138
+ { pattern: /grok[_-]?\d/i, tokens: 128e3 },
139
+ // Mistral Large — 128k
140
+ { pattern: /mistral[_-]?large/i, tokens: 128e3 },
141
+ // Mistral Medium — 32k
142
+ { pattern: /mistral[_-]?medium/i, tokens: 32768 },
143
+ // Codestral — 256k
144
+ { pattern: /codestral/i, tokens: 256e3 },
145
+ // Mistral generic — 32k
146
+ { pattern: /mistral|mixtral/i, tokens: 32768 },
147
+ // DeepSeek R1/V3 — 128k
148
+ { pattern: /deepseek/i, tokens: 128e3 },
149
+ // Qwen 3 — 128k
150
+ { pattern: /qwen/i, tokens: 128e3 },
151
+ // Meta Llama 3 — 128k
152
+ { pattern: /llama[_-]?3/i, tokens: 128e3 },
153
+ // Cohere Command R+ — 128k
154
+ { pattern: /command[_-]?r/i, tokens: 128e3 }
155
+ ];
156
+ function inferContextWindow(modelId) {
157
+ const normalized = modelId.toLowerCase();
158
+ for (const { pattern, tokens } of CONTEXT_WINDOW_PROFILES) {
159
+ if (pattern.test(normalized)) {
160
+ return tokens;
161
+ }
162
+ }
163
+ return void 0;
164
+ }
165
+ function inferProvider(modelId) {
166
+ const normalized = modelId.toLowerCase();
167
+ for (const { pattern, provider } of PROVIDER_PATTERNS) {
168
+ if (pattern.test(normalized)) {
169
+ return provider;
170
+ }
171
+ }
172
+ if (modelId.includes("/")) {
173
+ return modelId.split("/")[0];
174
+ }
175
+ return void 0;
176
+ }
177
+ function matchPatterns(modelId, providerHint) {
178
+ const normalized = modelId.toLowerCase();
179
+ for (const rule of REASONING_PATTERNS) {
180
+ if (rule.provider && providerHint && rule.provider !== providerHint) {
181
+ continue;
182
+ }
183
+ const matches = typeof rule.pattern === "string" ? normalized.includes(rule.pattern.toLowerCase()) : rule.pattern.test(normalized);
184
+ if (matches) {
185
+ return { rule, confidence: rule.confidence };
186
+ }
187
+ }
188
+ return void 0;
189
+ }
190
+ function createDefaultCapabilities() {
191
+ return {
192
+ reasoning: false,
193
+ toolCalling: true,
194
+ temperature: true,
195
+ attachments: false,
196
+ streaming: true,
197
+ inputModalities: ["text"],
198
+ outputModalities: ["text"]
199
+ };
200
+ }
201
+ var PatternCapabilitySource = class {
202
+ priority = 3 /* PatternMatch */;
203
+ name = "Pattern Matching";
204
+ async lookup(modelId, providerHint) {
205
+ const provider = providerHint || inferProvider(modelId);
206
+ const match = matchPatterns(modelId, provider);
207
+ const baseCapabilities = createDefaultCapabilities();
208
+ if (match) {
209
+ const entry = {
210
+ id: modelId,
211
+ name: modelId,
212
+ provider: match.rule.provider || provider || "unknown",
213
+ capabilities: {
214
+ ...baseCapabilities,
215
+ ...match.rule.capabilities,
216
+ contextWindow: inferContextWindow(modelId)
217
+ },
218
+ compatibility: match.rule.compatibility
219
+ };
220
+ return {
221
+ entry,
222
+ source: this.priority,
223
+ confident: match.confidence > 0.8
224
+ };
225
+ }
226
+ return {
227
+ entry: {
228
+ id: modelId,
229
+ name: modelId,
230
+ provider: provider || "unknown",
231
+ capabilities: {
232
+ ...baseCapabilities,
233
+ contextWindow: inferContextWindow(modelId)
234
+ }
235
+ },
236
+ source: this.priority,
237
+ confident: false
238
+ };
239
+ }
240
+ async isAvailable() {
241
+ return true;
242
+ }
243
+ };
244
+ function likelySupportsReasoning(modelId) {
245
+ const match = matchPatterns(modelId);
246
+ return match !== void 0 && match.rule.capabilities.reasoning === true;
247
+ }
248
+ function getProviderCompatibility(modelId, provider) {
249
+ const match = matchPatterns(modelId, provider);
250
+ return match?.rule.compatibility;
251
+ }
252
+
253
+ // src/models/identifiers.ts
254
+ function getModelId(model) {
255
+ if (typeof model === "string") return model;
256
+ if (typeof model === "object" && model !== null && "modelId" in model) {
257
+ return String(model.modelId);
258
+ }
259
+ return String(model);
260
+ }
261
+ function getProviderId(model) {
262
+ if (typeof model === "string") {
263
+ if (model.includes("/")) {
264
+ return model.split("/")[0];
265
+ }
266
+ return void 0;
267
+ }
268
+ if (typeof model === "object" && model !== null && "provider" in model) {
269
+ const provider = String(model.provider);
270
+ return provider.split(".")[0];
271
+ }
272
+ return void 0;
273
+ }
274
+ var extractModelId = getModelId;
275
+ function extractProvider(model) {
276
+ const provider = getProviderId(model);
277
+ return provider ?? inferProvider(getModelId(model));
278
+ }
279
+
280
+ export {
281
+ SourcePriority,
282
+ DEFAULT_RESOLVER_OPTIONS,
283
+ inferContextWindow,
284
+ inferProvider,
285
+ PatternCapabilitySource,
286
+ likelySupportsReasoning,
287
+ getProviderCompatibility,
288
+ getModelId,
289
+ getProviderId,
290
+ extractModelId,
291
+ extractProvider
292
+ };
@@ -9,7 +9,7 @@ function generateEntryId(existingIds) {
9
9
  }
10
10
  return crypto.randomUUID();
11
11
  }
12
- function parseJSONL(content) {
12
+ function parseJSONL(content, logger) {
13
13
  const entries = [];
14
14
  const lines = content.split("\n");
15
15
  for (const line of lines) {
@@ -18,7 +18,7 @@ function parseJSONL(content) {
18
18
  try {
19
19
  entries.push(JSON.parse(trimmed));
20
20
  } catch {
21
- console.warn("[Storage] Skipping malformed JSONL line");
21
+ logger?.warn("Skipping malformed JSONL line");
22
22
  }
23
23
  }
24
24
  return entries;
@@ -160,7 +160,6 @@ function getLeafId(entries) {
160
160
  children.push(e.id);
161
161
  childMap.set(e.parentId, children);
162
162
  }
163
- const _allIds = new Set(entries.slice(1).map((e) => e.id));
164
163
  const hasChildren = /* @__PURE__ */ new Set();
165
164
  for (const children of childMap.values()) {
166
165
  for (const child of children) {
@@ -204,7 +203,8 @@ function buildMessagesFromEntries(entries, leafId) {
204
203
  let skipUntilId;
205
204
  for (const entry of path) {
206
205
  if (entry.type === "compaction") {
207
- skipUntilId = entry.firstKeptEntryId;
206
+ messages.length = 0;
207
+ skipUntilId = entry.firstKeptEntryId !== entry.id ? entry.firstKeptEntryId : void 0;
208
208
  messages.push({
209
209
  id: entry.id,
210
210
  role: "system",
@@ -291,6 +291,16 @@ async function ensureStorageDirectory(directory) {
291
291
  }
292
292
  function getSessionFilePath(directory, extension, sessionId) {
293
293
  const safeId = sessionId.replace(/[^a-zA-Z0-9-_]/g, "_");
294
+ const needsHash = safeId !== sessionId;
295
+ if (needsHash) {
296
+ let hash = 2166136261;
297
+ for (let i = 0; i < sessionId.length; i++) {
298
+ hash ^= sessionId.charCodeAt(i);
299
+ hash = hash * 16777619 >>> 0;
300
+ }
301
+ const suffix = hash.toString(16).padStart(8, "0");
302
+ return join(directory, `${safeId}_${suffix}${extension}`);
303
+ }
294
304
  return join(directory, `${safeId}${extension}`);
295
305
  }
296
306
  async function loadEntriesFromDisk(filePath, sessionId) {
@@ -615,6 +625,40 @@ var SessionManager = class {
615
625
  this.currentLeafId = entry.id;
616
626
  return entry.id;
617
627
  }
628
+ async replaceWithCompaction(options) {
629
+ if (!this.currentSessionId) {
630
+ throw new Error("No session loaded");
631
+ }
632
+ const compactionId = generateEntryId(this.idsCache);
633
+ this.idsCache.add(compactionId);
634
+ let parentId = compactionId;
635
+ const messageEntries = [];
636
+ for (const message of options.messages) {
637
+ const entry = createMessageEntry(message, parentId, this.idsCache);
638
+ messageEntries.push(entry);
639
+ this.idsCache.add(entry.id);
640
+ parentId = entry.id;
641
+ }
642
+ const compactionEntry = {
643
+ type: "compaction",
644
+ id: compactionId,
645
+ parentId: this.currentLeafId,
646
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
647
+ summary: options.summary,
648
+ firstKeptEntryId: messageEntries[0]?.id ?? compactionId,
649
+ tokensBefore: options.tokensBefore,
650
+ tokensAfter: options.tokensAfter,
651
+ ...options.readFiles ? { readFiles: options.readFiles } : {},
652
+ ...options.modifiedFiles ? { modifiedFiles: options.modifiedFiles } : {}
653
+ };
654
+ await this.storage.appendBatch(this.currentSessionId, [
655
+ compactionEntry,
656
+ ...messageEntries
657
+ ]);
658
+ this.entriesCache.push(compactionEntry, ...messageEntries);
659
+ this.currentLeafId = messageEntries[messageEntries.length - 1]?.id ?? compactionEntry.id;
660
+ return compactionEntry.id;
661
+ }
618
662
  async branch(fromEntryId, summary) {
619
663
  if (!this.currentSessionId) {
620
664
  throw new Error("No session loaded");
@@ -0,0 +1,316 @@
1
+ import {
2
+ Tool
3
+ } from "./chunk-Q742PSH3.js";
4
+
5
+ // src/tool/registry.ts
6
+ function escapeRegExp(s) {
7
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8
+ }
9
+ var ToolRegistry = class {
10
+ tools = /* @__PURE__ */ new Map();
11
+ groups = /* @__PURE__ */ new Map();
12
+ // --------------------------------------------------------------------------
13
+ // Tool registration
14
+ // --------------------------------------------------------------------------
15
+ /**
16
+ * Register a tool. Throws if a tool with the same ID is already registered.
17
+ * Use `set()` for upsert semantics.
18
+ */
19
+ register(tool) {
20
+ if (this.tools.has(tool.id)) {
21
+ throw new Error(`Tool '${tool.id}' is already registered`);
22
+ }
23
+ this.tools.set(tool.id, tool);
24
+ }
25
+ /** Register multiple tools (throws on duplicates). */
26
+ registerAll(tools) {
27
+ for (const tool of tools) {
28
+ this.register(tool);
29
+ }
30
+ }
31
+ /** Register or replace a tool (upsert). */
32
+ set(tool) {
33
+ this.tools.set(tool.id, tool);
34
+ }
35
+ /** Unregister a tool by ID. Returns `true` if it existed. */
36
+ unregister(id) {
37
+ return this.tools.delete(id);
38
+ }
39
+ /** Get a tool by ID. */
40
+ get(id) {
41
+ return this.tools.get(id);
42
+ }
43
+ /** Check if a tool is registered. */
44
+ has(id) {
45
+ return this.tools.has(id);
46
+ }
47
+ /** Get all tool IDs. */
48
+ ids() {
49
+ return Array.from(this.tools.keys());
50
+ }
51
+ /** Get all tools. */
52
+ all() {
53
+ return Array.from(this.tools.values());
54
+ }
55
+ /** Clear all tools and groups. */
56
+ clear() {
57
+ this.tools.clear();
58
+ this.groups.clear();
59
+ }
60
+ /** Number of registered tools. */
61
+ get size() {
62
+ return this.tools.size;
63
+ }
64
+ // --------------------------------------------------------------------------
65
+ // Group management
66
+ // --------------------------------------------------------------------------
67
+ /**
68
+ * Register a named group of tool IDs.
69
+ * The group name can be used in `resolve()` specs.
70
+ * Tool IDs don't need to be registered yet — resolution is lazy.
71
+ */
72
+ registerGroup(name, toolIds) {
73
+ this.groups.set(name, toolIds);
74
+ }
75
+ /** Get tools in a group (only returns registered tools). */
76
+ getGroup(name) {
77
+ const ids = this.groups.get(name);
78
+ if (!ids) return void 0;
79
+ return ids.map((id) => this.tools.get(id)).filter((t) => t !== void 0);
80
+ }
81
+ /** Check if a group is registered. */
82
+ hasGroup(name) {
83
+ return this.groups.has(name);
84
+ }
85
+ /** List all group names. */
86
+ listGroups() {
87
+ return Array.from(this.groups.keys());
88
+ }
89
+ // --------------------------------------------------------------------------
90
+ // Deferred loading
91
+ // --------------------------------------------------------------------------
92
+ /**
93
+ * Partition registered tools into eager and deferred sets.
94
+ *
95
+ * Eager tools have their full schemas sent to the LLM on turn 1.
96
+ * Deferred tools are held back — the model discovers them via a
97
+ * search mechanism when needed, saving context window space.
98
+ *
99
+ * @returns `{ eager, deferred }` arrays of tool infos.
100
+ */
101
+ partition() {
102
+ const eager = [];
103
+ const deferred = [];
104
+ for (const tool of this.tools.values()) {
105
+ if (tool.deferred) {
106
+ deferred.push(tool);
107
+ } else {
108
+ eager.push(tool);
109
+ }
110
+ }
111
+ return { eager, deferred };
112
+ }
113
+ /**
114
+ * Search deferred tools by keyword with multi-signal weighted scoring.
115
+ *
116
+ * Scoring per term:
117
+ * - Exact tool ID match: +10
118
+ * - Partial tool ID match (substring): +5
119
+ * - Keyword word-boundary match: +4
120
+ * - Keyword substring match: +2
121
+ *
122
+ * @param query - Space-separated search terms
123
+ * @param maxResults - Maximum results to return (default: 5)
124
+ * @returns Matching deferred tools sorted by relevance (highest score first)
125
+ */
126
+ searchDeferred(query, maxResults = 5) {
127
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
128
+ if (terms.length === 0) return [];
129
+ const { deferred } = this.partition();
130
+ const scored = deferred.map((tool) => {
131
+ let score = 0;
132
+ const id = tool.id.toLowerCase();
133
+ const keywords = (tool.searchKeywords ?? []).join(" ").toLowerCase();
134
+ for (const term of terms) {
135
+ if (id === term) {
136
+ score += 10;
137
+ continue;
138
+ }
139
+ if (id.includes(term)) {
140
+ score += 5;
141
+ continue;
142
+ }
143
+ const wordBoundary = new RegExp(`\\b${escapeRegExp(term)}\\b`);
144
+ if (wordBoundary.test(keywords)) {
145
+ score += 4;
146
+ continue;
147
+ }
148
+ if (keywords.includes(term)) {
149
+ score += 2;
150
+ }
151
+ }
152
+ return { tool, score };
153
+ }).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score);
154
+ return scored.slice(0, maxResults).map(({ tool }) => tool);
155
+ }
156
+ // --------------------------------------------------------------------------
157
+ // Resolution
158
+ // --------------------------------------------------------------------------
159
+ /**
160
+ * Resolve a `ToolSpec` to an array of tools.
161
+ *
162
+ * Supports group names, individual tool IDs, comma-separated strings,
163
+ * exclusions with `-` prefix, booleans, and pass-through arrays.
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * registry.resolve("read-only"); // group name
168
+ * registry.resolve("read,grep,glob"); // comma-separated IDs
169
+ * registry.resolve("all,-bash"); // all except bash
170
+ * registry.resolve(["read", "grep"]); // array of IDs
171
+ * registry.resolve(true); // all registered tools
172
+ * registry.resolve(false); // empty array
173
+ * ```
174
+ */
175
+ resolve(spec) {
176
+ if (spec === true) return this.all();
177
+ if (spec === false) return [];
178
+ if (Array.isArray(spec) && spec.length > 0 && typeof spec[0] !== "string") {
179
+ return spec;
180
+ }
181
+ const tokens = Array.isArray(spec) ? spec : spec.split(",").map((s) => s.trim()).filter(Boolean);
182
+ const includes = [];
183
+ const excludes = [];
184
+ for (const token of tokens) {
185
+ if (token.startsWith("-")) {
186
+ excludes.push(token.slice(1));
187
+ } else {
188
+ includes.push(token);
189
+ }
190
+ }
191
+ const result = /* @__PURE__ */ new Map();
192
+ if (includes.length === 0 && excludes.length > 0) {
193
+ for (const [id, tool] of this.tools) {
194
+ result.set(id, tool);
195
+ }
196
+ } else {
197
+ for (const token of includes) {
198
+ if (token === "all") {
199
+ for (const [id, tool] of this.tools) {
200
+ result.set(id, tool);
201
+ }
202
+ } else if (this.groups.has(token)) {
203
+ const ids = this.groups.get(token);
204
+ for (const id of ids) {
205
+ const tool = this.tools.get(id);
206
+ if (tool) result.set(id, tool);
207
+ }
208
+ } else if (this.tools.has(token)) {
209
+ result.set(token, this.tools.get(token));
210
+ }
211
+ }
212
+ }
213
+ for (const id of excludes) {
214
+ if (this.groups.has(id)) {
215
+ const ids = this.groups.get(id);
216
+ for (const gid of ids) {
217
+ result.delete(gid);
218
+ }
219
+ } else {
220
+ result.delete(id);
221
+ }
222
+ }
223
+ return Array.from(result.values());
224
+ }
225
+ };
226
+ var defaultRegistry = new ToolRegistry();
227
+
228
+ // src/tool/tool-search.ts
229
+ import { z } from "zod";
230
+ function createToolSearchTool(options) {
231
+ const { registry, maxResults = 5, onMatch } = options;
232
+ return Tool.define("tool_search", {
233
+ description: "Search for additional tools by keyword. Some tools are not loaded by default to save context. Call this when you need a capability that isn't available in your current tool set. Returns matching tool names and descriptions.",
234
+ parameters: z.object({
235
+ query: z.string().describe(
236
+ "Space-separated keywords describing the tool you need (e.g. 'jupyter notebook cells' or 'docker container'). Use 'select:tool_name' to load a specific tool by name, or 'select:foo,bar' for multiple."
237
+ )
238
+ }),
239
+ capabilities: {
240
+ parallelSafe: true,
241
+ readOnly: true,
242
+ riskLevel: "safe"
243
+ },
244
+ execute: async ({ query }, ctx) => {
245
+ if (query.startsWith("select:")) {
246
+ const ids = query.slice(7).split(",").map((s) => s.trim()).filter(Boolean);
247
+ const found = [];
248
+ const missing = [];
249
+ for (const id of ids) {
250
+ const tool = registry.get(id);
251
+ if (tool) {
252
+ found.push(tool);
253
+ } else {
254
+ missing.push(id);
255
+ }
256
+ }
257
+ if (found.length > 0) onMatch(found);
258
+ const summaries2 = [];
259
+ for (const t of found) {
260
+ try {
261
+ const init = await t.init({ cwd: ctx.cwd });
262
+ summaries2.push(`- **${t.id}**: ${init.description}`);
263
+ } catch {
264
+ summaries2.push(`- **${t.id}**: (description unavailable)`);
265
+ }
266
+ }
267
+ if (missing.length > 0) {
268
+ summaries2.push(`
269
+ Not found: ${missing.join(", ")}`);
270
+ }
271
+ return {
272
+ title: `Loaded ${found.length} tool(s)`,
273
+ output: found.length > 0 ? summaries2.join("\n") + "\n\nThese tools are now available for use." : `No tools found: ${missing.join(", ")}`,
274
+ metadata: {
275
+ matchCount: found.length,
276
+ toolIds: found.map((t) => t.id)
277
+ }
278
+ };
279
+ }
280
+ const matches = registry.searchDeferred(query, maxResults);
281
+ if (matches.length === 0) {
282
+ return {
283
+ title: `No tools found for "${query}"`,
284
+ output: `No matching tools found for query: "${query}". Try different keywords.`,
285
+ metadata: { matchCount: 0, toolIds: [] }
286
+ };
287
+ }
288
+ const summaries = [];
289
+ for (const tool of matches) {
290
+ try {
291
+ const init = await tool.init({ cwd: ctx.cwd });
292
+ summaries.push(`- **${tool.id}**: ${init.description}`);
293
+ } catch {
294
+ summaries.push(`- **${tool.id}**: (description unavailable)`);
295
+ }
296
+ }
297
+ onMatch(matches);
298
+ return {
299
+ title: `Found ${matches.length} tool(s)`,
300
+ output: `Found ${matches.length} tool(s) matching "${query}":
301
+
302
+ ` + summaries.join("\n") + "\n\nThese tools are now available for use.",
303
+ metadata: {
304
+ matchCount: matches.length,
305
+ toolIds: matches.map((t) => t.id)
306
+ }
307
+ };
308
+ }
309
+ });
310
+ }
311
+
312
+ export {
313
+ ToolRegistry,
314
+ defaultRegistry,
315
+ createToolSearchTool
316
+ };