@burtson-labs/host-kit 0.3.1

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 (79) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +55 -0
  3. package/dist/backgroundTasks.d.ts +113 -0
  4. package/dist/backgroundTasks.d.ts.map +1 -0
  5. package/dist/backgroundTasks.js +137 -0
  6. package/dist/backgroundTasks.js.map +1 -0
  7. package/dist/checkpoints.d.ts +99 -0
  8. package/dist/checkpoints.d.ts.map +1 -0
  9. package/dist/checkpoints.js +227 -0
  10. package/dist/checkpoints.js.map +1 -0
  11. package/dist/hooks.d.ts +51 -0
  12. package/dist/hooks.d.ts.map +1 -0
  13. package/dist/hooks.js +152 -0
  14. package/dist/hooks.js.map +1 -0
  15. package/dist/index.d.ts +19 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +95 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/insights.d.ts +398 -0
  20. package/dist/insights.d.ts.map +1 -0
  21. package/dist/insights.js +1933 -0
  22. package/dist/insights.js.map +1 -0
  23. package/dist/mcp.d.ts +60 -0
  24. package/dist/mcp.d.ts.map +1 -0
  25. package/dist/mcp.js +281 -0
  26. package/dist/mcp.js.map +1 -0
  27. package/dist/mcpConnectors.d.ts +108 -0
  28. package/dist/mcpConnectors.d.ts.map +1 -0
  29. package/dist/mcpConnectors.js +217 -0
  30. package/dist/mcpConnectors.js.map +1 -0
  31. package/dist/mcpToolCache.d.ts +43 -0
  32. package/dist/mcpToolCache.d.ts.map +1 -0
  33. package/dist/mcpToolCache.js +150 -0
  34. package/dist/mcpToolCache.js.map +1 -0
  35. package/dist/mcpTrust.d.ts +22 -0
  36. package/dist/mcpTrust.d.ts.map +1 -0
  37. package/dist/mcpTrust.js +104 -0
  38. package/dist/mcpTrust.js.map +1 -0
  39. package/dist/memory.d.ts +38 -0
  40. package/dist/memory.d.ts.map +1 -0
  41. package/dist/memory.js +151 -0
  42. package/dist/memory.js.map +1 -0
  43. package/dist/memoryIndex.d.ts +38 -0
  44. package/dist/memoryIndex.d.ts.map +1 -0
  45. package/dist/memoryIndex.js +142 -0
  46. package/dist/memoryIndex.js.map +1 -0
  47. package/dist/mentions.d.ts +33 -0
  48. package/dist/mentions.d.ts.map +1 -0
  49. package/dist/mentions.js +126 -0
  50. package/dist/mentions.js.map +1 -0
  51. package/dist/ollamaModels.d.ts +36 -0
  52. package/dist/ollamaModels.d.ts.map +1 -0
  53. package/dist/ollamaModels.js +83 -0
  54. package/dist/ollamaModels.js.map +1 -0
  55. package/dist/permissions.d.ts +72 -0
  56. package/dist/permissions.d.ts.map +1 -0
  57. package/dist/permissions.js +271 -0
  58. package/dist/permissions.js.map +1 -0
  59. package/dist/tools/extraTools.d.ts +80 -0
  60. package/dist/tools/extraTools.d.ts.map +1 -0
  61. package/dist/tools/extraTools.js +471 -0
  62. package/dist/tools/extraTools.js.map +1 -0
  63. package/dist/tools/readMemoryTool.d.ts +3 -0
  64. package/dist/tools/readMemoryTool.d.ts.map +1 -0
  65. package/dist/tools/readMemoryTool.js +115 -0
  66. package/dist/tools/readMemoryTool.js.map +1 -0
  67. package/dist/tools/taskTool.d.ts +119 -0
  68. package/dist/tools/taskTool.d.ts.map +1 -0
  69. package/dist/tools/taskTool.js +466 -0
  70. package/dist/tools/taskTool.js.map +1 -0
  71. package/dist/tools/testRunTool.d.ts +59 -0
  72. package/dist/tools/testRunTool.d.ts.map +1 -0
  73. package/dist/tools/testRunTool.js +308 -0
  74. package/dist/tools/testRunTool.js.map +1 -0
  75. package/dist/turnLog.d.ts +89 -0
  76. package/dist/turnLog.d.ts.map +1 -0
  77. package/dist/turnLog.js +469 -0
  78. package/dist/turnLog.js.map +1 -0
  79. package/package.json +35 -0
@@ -0,0 +1,471 @@
1
+ "use strict";
2
+ /**
3
+ * Extra tools layered onto the core ToolRegistry for the CLI host:
4
+ * - todo_write: in-agent todo tracking, persisted in-memory for the session
5
+ * - web_fetch: GET a URL and return a trimmed text body
6
+ * - web_search: query a search API (Tavily), return ranked snippets
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.TodoStore = void 0;
10
+ exports.buildTodoWriteTool = buildTodoWriteTool;
11
+ exports.buildRememberTool = buildRememberTool;
12
+ exports.isPrivateHost = isPrivateHost;
13
+ exports.buildWebFetchTool = buildWebFetchTool;
14
+ exports.buildWebSearchTool = buildWebSearchTool;
15
+ const node_dns_1 = require("node:dns");
16
+ const node_net_1 = require("node:net");
17
+ const memory_1 = require("../memory");
18
+ /**
19
+ * Models don't agree on status vocabulary — some emit `"done"`, others
20
+ * `"complete"`, `"completed"`, `"in-progress"`, `"inprogress"`, etc.
21
+ * Normalize to the three statuses the store actually stores so the UI
22
+ * (which only knows `done` / `in_progress` / `pending`) reliably ticks
23
+ * items off. Without this, a model saying `"complete"` leaves items
24
+ * visually stuck on `○` forever despite the plan advancing.
25
+ */
26
+ function normalizeStatus(raw) {
27
+ const s = typeof raw === 'string' ? raw.trim().toLowerCase().replace(/[-\s]/g, '_') : '';
28
+ if (s === 'done' || s === 'complete' || s === 'completed' || s === 'finished')
29
+ return 'done';
30
+ if (s === 'in_progress' || s === 'inprogress' || s === 'active' || s === 'working' || s === 'running')
31
+ return 'in_progress';
32
+ return 'pending';
33
+ }
34
+ class TodoStore {
35
+ constructor() {
36
+ this.items = [];
37
+ this.nextId = 1;
38
+ }
39
+ snapshot() {
40
+ return [...this.items];
41
+ }
42
+ render() {
43
+ if (this.items.length === 0)
44
+ return '(no todos)';
45
+ return this.items
46
+ .map(t => {
47
+ const mark = t.status === 'done' ? '[x]' : t.status === 'in_progress' ? '[~]' : '[ ]';
48
+ return `${mark} ${t.id}. ${t.content}`;
49
+ })
50
+ .join('\n');
51
+ }
52
+ /**
53
+ * Accepts a JSON array of { content, status? } to replace the list, or a
54
+ * single string 'content' for append. Simple to be forgiving of small models.
55
+ */
56
+ upsert(raw) {
57
+ const trimmed = raw.trim();
58
+ if (!trimmed)
59
+ return 'No todo payload provided.';
60
+ try {
61
+ const parsed = JSON.parse(trimmed);
62
+ if (Array.isArray(parsed)) {
63
+ // Small models (Qwen 2.5 Coder on S3Api
64
+ // turn) sometimes pass a bare string array — ["Read X", "Edit Y"]
65
+ // — instead of the documented [{content, status}] shape. Treat
66
+ // each string as a pending todo with its content equal to the
67
+ // string. Previous code did String(undefined ?? '') here and
68
+ // rendered N empty bullets in the Plan block.
69
+ //
70
+ // Mark 2026-05-26: when the tool was declared with `type: string`,
71
+ // the agent-core parser stringified each nested {content,status}
72
+ // object on the way in. The array branch then saw a list of
73
+ // JSON-SHAPED STRINGS like '{"content":"Install TypeScript",
74
+ // "status":"done"}' and the string branch below set content
75
+ // to the whole JSON dump — Plan rendered raw JSON instead of
76
+ // a clean checklist. We now detect JSON-object-shaped strings
77
+ // and re-parse them so the legacy path (and any provider that
78
+ // still stringifies nested values) recovers the right shape.
79
+ this.items = parsed.map((entry, i) => {
80
+ if (typeof entry === 'string') {
81
+ const candidate = entry.trim();
82
+ if (candidate.startsWith('{') && candidate.endsWith('}')) {
83
+ try {
84
+ const obj = JSON.parse(candidate);
85
+ if (obj && typeof obj === 'object' && typeof obj.content === 'string') {
86
+ return {
87
+ id: i + 1,
88
+ status: normalizeStatus(obj.status),
89
+ content: obj.content,
90
+ };
91
+ }
92
+ }
93
+ catch {
94
+ /* fall through to bare-string treatment */
95
+ }
96
+ }
97
+ return { id: i + 1, status: 'pending', content: entry };
98
+ }
99
+ return {
100
+ id: i + 1,
101
+ status: normalizeStatus(entry?.status),
102
+ content: String(entry?.content ?? '')
103
+ };
104
+ });
105
+ this.nextId = this.items.length + 1;
106
+ return `Todo list updated (${this.items.length} items).`;
107
+ }
108
+ if (parsed && typeof parsed === 'object' && typeof parsed.content === 'string') {
109
+ this.items.push({ id: this.nextId++, status: normalizeStatus(parsed.status), content: parsed.content });
110
+ return `Added todo #${this.items.length}.`;
111
+ }
112
+ }
113
+ catch {
114
+ // Fall through: treat as plain text append.
115
+ }
116
+ this.items.push({ id: this.nextId++, status: 'pending', content: trimmed });
117
+ return `Added todo #${this.items.length}.`;
118
+ }
119
+ /**
120
+ * A short progress summary the tool returns so the model reads a
121
+ * clear "update succeeded, here's what's left" signal instead of
122
+ * interpreting the echoed list as a reset. The trailing nudge is
123
+ * deliberately blunt — bandit-core-1 and similar small models have
124
+ * been observed to claim completion without invoking the write
125
+ * tool, and this helper is the most direct place to remind the
126
+ * model of the expectation between tool calls.
127
+ */
128
+ summary() {
129
+ const done = this.items.filter(t => t.status === 'done').length;
130
+ const inProgress = this.items.filter(t => t.status === 'in_progress').length;
131
+ const pending = this.items.filter(t => t.status === 'pending');
132
+ if (this.items.length === 0)
133
+ return '';
134
+ const nextPending = pending[0];
135
+ const pieces = [
136
+ `${done} of ${this.items.length} complete`,
137
+ inProgress > 0 ? `${inProgress} in progress` : null,
138
+ nextPending ? `next: "${nextPending.content}"` : null
139
+ ].filter(Boolean);
140
+ const status = pieces.join(' · ');
141
+ if (done === this.items.length) {
142
+ return `\n\n${status}. Before finalizing, verify the work is actually complete — a todo marked "done" is not proof the underlying change was made.`;
143
+ }
144
+ return `\n\n${status}. Continue the task — DO NOT claim completion in your response until the underlying work (write_file / apply_edit / replace_range / run_command) has actually executed.`;
145
+ }
146
+ }
147
+ exports.TodoStore = TodoStore;
148
+ function buildTodoWriteTool(store) {
149
+ return {
150
+ name: 'todo_write',
151
+ description: 'Track progress on the current task. Pass an array of {content, status} objects to replace the list, or a plain string to append one item. Call this whenever you change plans or finish a step.',
152
+ parameters: [
153
+ {
154
+ name: 'items',
155
+ description: 'Array of {content, status} objects, where status is one of "pending" | "in_progress" | "done". Send a real array — do NOT pre-stringify the objects.',
156
+ required: true,
157
+ schema: {
158
+ type: 'array',
159
+ items: {
160
+ type: 'object',
161
+ properties: {
162
+ content: { type: 'string', description: 'The todo item text shown to the user.' },
163
+ status: {
164
+ type: 'string',
165
+ description: 'One of "pending" | "in_progress" | "done".',
166
+ enum: ['pending', 'in_progress', 'done'],
167
+ },
168
+ },
169
+ required: ['content'],
170
+ },
171
+ },
172
+ }
173
+ ],
174
+ async execute(params, _ctx) {
175
+ // params.items may arrive as a real array (native-tools providers
176
+ // honouring the schema), a JSON string (text-tool-loop tool_call
177
+ // parser), or undefined. The interface types params as
178
+ // Record<string,string> but the agent runtime delivers richer
179
+ // shapes when the param schema declares them — cast and normalize.
180
+ // Normalize to a JSON string so the existing store.upsert()
181
+ // text-based parsing keeps working unchanged; if a richer-shaped
182
+ // array arrives, JSON.stringify round-trips it cleanly.
183
+ const raw = params.items;
184
+ const serialized = typeof raw === 'string'
185
+ ? raw
186
+ : raw === undefined || raw === null
187
+ ? ''
188
+ : JSON.stringify(raw);
189
+ const header = store.upsert(serialized);
190
+ const summary = store.summary();
191
+ return { output: `${header}\n\n${store.render()}${summary}`, isError: false };
192
+ }
193
+ };
194
+ }
195
+ /**
196
+ * `remember` tool — persists a single fact to the workspace's BANDIT.md
197
+ * so it survives across sessions. Use case: the user says "remember
198
+ * that all my repos live in ~/Documents/GitHub" and wants the next
199
+ * Bandit session to know that without being re-told.
200
+ *
201
+ * Why a dedicated tool instead of asking the model to apply_edit
202
+ * BANDIT.md directly: small models (gemma4:e4b, qwen 4B) hallucinated
203
+ * the existing file contents when invited to edit it, and even on
204
+ * larger models the apply_edit dance for "append a bullet" was
205
+ * comically inefficient (4-5 turns minimum). This is one tool call
206
+ * with a single string parameter — same shape as todo_write.
207
+ *
208
+ * on the CLI: user said "you should add to your
209
+ * memory where my repos are", model used `todo_write` thinking it was
210
+ * the persistence mechanism, nothing actually landed on disk and the
211
+ * next session knew nothing.
212
+ */
213
+ function buildRememberTool() {
214
+ return {
215
+ name: 'remember',
216
+ description: 'Persist a single fact to project memory (BANDIT.md at the workspace root) so it survives across sessions. Use this when the user says "remember X", "always do Y", "note that I", or otherwise asks for cross-session persistence — NOT for transient task tracking (use `todo_write` for that). One bullet per call. Examples: "All repos live in ~/Documents/GitHub", "Prefer pnpm over npm", "Local Ollama at http://k3s.lan:11434".',
217
+ parameters: [
218
+ { name: 'fact', description: 'The single fact to remember. Will be appended as a bullet under the "## Notes" heading in BANDIT.md (file is created with a minimal scaffold if missing).', required: true }
219
+ ],
220
+ async execute(params, ctx) {
221
+ const fact = (params.fact ?? '').trim();
222
+ if (!fact)
223
+ return { output: 'Error: fact parameter is required and must be a non-empty string.', isError: true };
224
+ try {
225
+ const abs = await (0, memory_1.appendMemory)(ctx.workspaceRoot, fact);
226
+ return { output: `Saved to project memory: "${fact}"\n\nPersisted to ${abs}. The next Bandit session in this workspace will load this automatically.` };
227
+ }
228
+ catch (err) {
229
+ const msg = err instanceof Error ? err.message : String(err);
230
+ return { output: `Could not write to BANDIT.md: ${msg}`, isError: true };
231
+ }
232
+ }
233
+ };
234
+ }
235
+ /**
236
+ * SSRF guard for web_fetch — resolves the URL's hostname and refuses to
237
+ * proceed if it lands on a private/loopback/link-local address. This is
238
+ * the difference between "agent can read public docs" and "attacker prompt
239
+ * tricks agent into hitting http://169.254.169.254/latest/meta-data/ or
240
+ * http://localhost:6443 on the user's box."
241
+ *
242
+ * Set BANDIT_ALLOW_PRIVATE_WEB_FETCH=1 to opt out — appropriate when the
243
+ * user is intentionally pointing the agent at internal docs.
244
+ */
245
+ const PRIVATE_HOSTNAMES = new Set(['localhost', 'ip6-localhost', 'ip6-loopback']);
246
+ function isPrivateIPv4(ip) {
247
+ const parts = ip.split('.').map((p) => Number(p));
248
+ if (parts.length !== 4 || parts.some((p) => !Number.isInteger(p) || p < 0 || p > 255))
249
+ return false;
250
+ const [a, b] = parts;
251
+ if (a === 0)
252
+ return true; // 0.0.0.0/8 — "this network"
253
+ if (a === 10)
254
+ return true; // 10.0.0.0/8 — RFC1918
255
+ if (a === 127)
256
+ return true; // 127.0.0.0/8 — loopback
257
+ if (a === 169 && b === 254)
258
+ return true; // 169.254.0.0/16 — link-local (cloud metadata)
259
+ if (a === 172 && b >= 16 && b <= 31)
260
+ return true; // 172.16.0.0/12 — RFC1918
261
+ if (a === 192 && b === 168)
262
+ return true; // 192.168.0.0/16 — RFC1918
263
+ if (a === 100 && b >= 64 && b <= 127)
264
+ return true; // 100.64.0.0/10 — CGNAT (often internal)
265
+ return false;
266
+ }
267
+ function isPrivateIPv6(ip) {
268
+ const lower = ip.toLowerCase();
269
+ if (lower === '::1' || lower === '::')
270
+ return true;
271
+ // IPv4-mapped IPv6 (::ffff:a.b.c.d) — re-check the embedded IPv4.
272
+ const mapped = lower.match(/^::ffff:([0-9.]+)$/);
273
+ if (mapped && (0, node_net_1.isIP)(mapped[1]) === 4)
274
+ return isPrivateIPv4(mapped[1]);
275
+ if (/^f[cd][0-9a-f]{2}:/.test(lower))
276
+ return true; // fc00::/7 — Unique Local
277
+ if (/^fe[89ab][0-9a-f]:/.test(lower))
278
+ return true; // fe80::/10 — link-local
279
+ return false;
280
+ }
281
+ async function isPrivateHost(hostname) {
282
+ // Node's URL.hostname returns IPv6 literals WITH brackets (e.g. "[::1]").
283
+ // Strip them so isIP() classifies the address and DNS doesn't try to
284
+ // resolve a bracketed string.
285
+ const stripped = hostname.startsWith('[') && hostname.endsWith(']')
286
+ ? hostname.slice(1, -1)
287
+ : hostname;
288
+ const lower = stripped.toLowerCase();
289
+ if (PRIVATE_HOSTNAMES.has(lower))
290
+ return true;
291
+ const family = (0, node_net_1.isIP)(stripped);
292
+ if (family === 4)
293
+ return isPrivateIPv4(stripped);
294
+ if (family === 6)
295
+ return isPrivateIPv6(stripped);
296
+ try {
297
+ const records = await node_dns_1.promises.lookup(stripped, { all: true, verbatim: true });
298
+ for (const rec of records) {
299
+ if (rec.family === 4 && isPrivateIPv4(rec.address))
300
+ return true;
301
+ if (rec.family === 6 && isPrivateIPv6(rec.address))
302
+ return true;
303
+ }
304
+ return false;
305
+ }
306
+ catch {
307
+ // DNS resolution failure isn't itself a security signal — let fetch
308
+ // report the network error so the model sees a normal failure path.
309
+ return false;
310
+ }
311
+ }
312
+ function buildWebFetchTool() {
313
+ return {
314
+ name: 'web_fetch',
315
+ description: 'HTTP GET a URL and return a trimmed plaintext body (up to ~16 KB). Use for docs, RFCs, release notes. No JS execution, no auth.',
316
+ parameters: [
317
+ { name: 'url', description: 'Absolute http(s) URL', required: true }
318
+ ],
319
+ async execute(params, _ctx) {
320
+ const raw = params.url?.trim();
321
+ if (!raw)
322
+ return { output: 'Missing url parameter.', isError: true };
323
+ let url;
324
+ try {
325
+ url = new URL(raw);
326
+ }
327
+ catch {
328
+ return { output: `Invalid URL: ${raw}`, isError: true };
329
+ }
330
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
331
+ return { output: `Unsupported protocol: ${url.protocol}`, isError: true };
332
+ }
333
+ if (process.env.BANDIT_ALLOW_PRIVATE_WEB_FETCH !== '1') {
334
+ if (await isPrivateHost(url.hostname)) {
335
+ return {
336
+ output: `Blocked: ${url.hostname} resolves to a private/loopback/link-local address. Set BANDIT_ALLOW_PRIVATE_WEB_FETCH=1 to allow fetches against internal networks.`,
337
+ isError: true
338
+ };
339
+ }
340
+ }
341
+ try {
342
+ const res = await fetch(url.toString(), {
343
+ redirect: 'follow',
344
+ headers: { 'User-Agent': 'bandit-cli/0.1', Accept: 'text/html,text/plain,application/json;q=0.9,*/*;q=0.5' },
345
+ signal: AbortSignal.timeout(15000)
346
+ });
347
+ const ct = res.headers.get('content-type') ?? '';
348
+ const text = await res.text();
349
+ const body = ct.includes('html') ? stripHtml(text) : text;
350
+ const trimmed = body.length > 16 * 1024 ? body.slice(0, 16 * 1024) + '\n… (truncated)' : body;
351
+ return {
352
+ output: `HTTP ${res.status} ${res.statusText} • ${url.host}\nContent-Type: ${ct || '(unknown)'}\n\n${trimmed}`,
353
+ isError: !res.ok
354
+ };
355
+ }
356
+ catch (err) {
357
+ return { output: `Fetch failed: ${err instanceof Error ? err.message : String(err)}`, isError: true };
358
+ }
359
+ }
360
+ };
361
+ }
362
+ function stripHtml(html) {
363
+ return html
364
+ .replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, '')
365
+ .replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, '')
366
+ .replace(/<[^>]+>/g, ' ')
367
+ .replace(/&nbsp;/g, ' ')
368
+ .replace(/&amp;/g, '&')
369
+ .replace(/&lt;/g, '<')
370
+ .replace(/&gt;/g, '>')
371
+ .replace(/&quot;/g, '"')
372
+ .replace(/\s+/g, ' ')
373
+ .trim();
374
+ }
375
+ /**
376
+ * Build a `web_search` tool backed by Tavily (purpose-built for LLM
377
+ * agents — returns ranked snippets, not raw HTML). When TAVILY_API_KEY
378
+ * is unset the tool returns a clear configuration error so the model
379
+ * can fall back to other tools (web_fetch with a known URL, ask the
380
+ * user, etc.) instead of hallucinating results.
381
+ *
382
+ * Why Tavily over scraping or Google CSE:
383
+ * - Generous free tier (1k req/mo at time of writing)
384
+ * - Single API call returns ranked snippets ready for the model
385
+ * - No CAPTCHA / rate-limit roulette like search-result scraping
386
+ * - Optional `answer` field gives the model an LLM-summarized answer
387
+ * directly when one fits the query
388
+ */
389
+ function buildWebSearchTool(options = {}) {
390
+ const endpoint = options.endpoint ?? 'https://api.tavily.com/search';
391
+ return {
392
+ name: 'web_search',
393
+ description: 'Search the web and return ranked snippets. Use for: looking up library APIs, finding documentation, checking what a CLI flag does, researching error messages. Each result has a title, URL, and a short content snippet — call `web_fetch` on the URL when you need the full page. Requires a Tavily API key (configure via the host or set TAVILY_API_KEY).',
394
+ parameters: [
395
+ { name: 'query', description: 'The search query — natural language works ("how to configure typescript paths"), keywords work too ("typescript paths config").', required: true },
396
+ { name: 'num_results', description: 'Number of results to return. Default 5, max 10.', required: false }
397
+ ],
398
+ async execute(params, _ctx) {
399
+ const query = params.query?.trim();
400
+ if (!query) {
401
+ return { output: 'Missing query parameter.', isError: true };
402
+ }
403
+ const apiKey = options.apiKey?.trim() || process.env.TAVILY_API_KEY?.trim();
404
+ if (!apiKey) {
405
+ return {
406
+ output: 'web_search is not configured. Set the TAVILY_API_KEY environment variable (free tier: https://tavily.com), or use `web_fetch` with a known URL instead.',
407
+ isError: true
408
+ };
409
+ }
410
+ const requestedCount = Number.parseInt(params.num_results ?? '', 10);
411
+ const maxResults = Number.isFinite(requestedCount) && requestedCount > 0
412
+ ? Math.min(requestedCount, 10)
413
+ : 5;
414
+ try {
415
+ const res = await fetch(endpoint, {
416
+ method: 'POST',
417
+ headers: { 'Content-Type': 'application/json' },
418
+ body: JSON.stringify({
419
+ api_key: apiKey,
420
+ query,
421
+ search_depth: 'basic',
422
+ include_answer: true,
423
+ max_results: maxResults
424
+ }),
425
+ signal: AbortSignal.timeout(15000)
426
+ });
427
+ if (!res.ok) {
428
+ const detail = await res.text().catch(() => '');
429
+ return {
430
+ output: `Search failed: HTTP ${res.status} ${res.statusText}${detail ? ` — ${detail.slice(0, 240)}` : ''}`,
431
+ isError: true
432
+ };
433
+ }
434
+ const data = (await res.json());
435
+ const results = Array.isArray(data.results) ? data.results : [];
436
+ if (results.length === 0) {
437
+ return {
438
+ output: `No results for "${query}".${data.answer ? `\n\nDirect answer: ${data.answer}` : ''}`,
439
+ isError: false
440
+ };
441
+ }
442
+ const blocks = [];
443
+ if (data.answer) {
444
+ blocks.push(`Direct answer: ${data.answer}`);
445
+ blocks.push('');
446
+ }
447
+ blocks.push(`Results for "${query}":`);
448
+ results.slice(0, maxResults).forEach((r, idx) => {
449
+ const title = r.title?.trim() || '(untitled)';
450
+ const url = r.url?.trim() || '';
451
+ const snippet = (r.content ?? '').trim();
452
+ const trimmedSnippet = snippet.length > 600 ? snippet.slice(0, 600) + '…' : snippet;
453
+ blocks.push('');
454
+ blocks.push(`${idx + 1}. ${title}`);
455
+ if (url)
456
+ blocks.push(` ${url}`);
457
+ if (trimmedSnippet)
458
+ blocks.push(` ${trimmedSnippet}`);
459
+ });
460
+ return { output: blocks.join('\n'), isError: false };
461
+ }
462
+ catch (err) {
463
+ return {
464
+ output: `Search failed: ${err instanceof Error ? err.message : String(err)}`,
465
+ isError: true
466
+ };
467
+ }
468
+ }
469
+ };
470
+ }
471
+ //# sourceMappingURL=extraTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extraTools.js","sourceRoot":"","sources":["../../src/tools/extraTools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { AgentTool } from '@burtson-labs/agent-core';
2
+ export declare function buildReadMemoryTool(): AgentTool;
3
+ //# sourceMappingURL=readMemoryTool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readMemoryTool.d.ts","sourceRoot":"","sources":["../../src/tools/readMemoryTool.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAoC,MAAM,0BAA0B,CAAC;AAO5F,wBAAgB,mBAAmB,IAAI,SAAS,CAmE/C"}
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.buildReadMemoryTool = buildReadMemoryTool;
37
+ /**
38
+ * `read_memory` — load a single topic file from `memory/<name>.md` on
39
+ * demand. Paired with `loadMemoryIndex` (the MEMORY.md index injected
40
+ * into the system prompt every turn). The agent decides whether a topic
41
+ * is relevant by reading the index's hook, then calls this tool to
42
+ * pull the full file into context.
43
+ *
44
+ * Rejects path traversal (`..`, absolute paths). On miss, the error
45
+ * lists the available slugs so the model can self-correct.
46
+ */
47
+ const fs = __importStar(require("fs"));
48
+ const path = __importStar(require("path"));
49
+ const memoryIndex_1 = require("../memoryIndex");
50
+ function buildReadMemoryTool() {
51
+ return {
52
+ name: 'read_memory',
53
+ description: 'Read a single topic memory file by slug. Use this when MEMORY.md (shown in the system prompt) lists a topic whose hook matches the current task. The slug is the part after "memory/" in [Title](memory/<slug>.md) — without the .md suffix. Returns the full file content (capped at 32 KB).',
54
+ parameters: [
55
+ {
56
+ name: 'name',
57
+ description: 'Memory file slug (no `memory/` prefix, no `.md` suffix). Example: "auth-conventions" loads memory/auth-conventions.md.',
58
+ required: true
59
+ }
60
+ ],
61
+ async execute(params, ctx) {
62
+ const requested = (params.name ?? '').trim();
63
+ if (!requested) {
64
+ return { output: 'Error: name parameter is required.', isError: true };
65
+ }
66
+ // Reject path traversal and absolute paths up front — slug must be a
67
+ // plain filename, not a path. The MEMORY.md format only ever yields
68
+ // simple filenames (the parser enforces this on the index side),
69
+ // and any model passing a `..` here is either confused or hostile.
70
+ if (requested.includes('/') ||
71
+ requested.includes('\\') ||
72
+ requested.includes('..') ||
73
+ path.isAbsolute(requested)) {
74
+ return {
75
+ output: `Error: name must be a plain slug like "auth-conventions", not a path. Got: "${requested}"`,
76
+ isError: true
77
+ };
78
+ }
79
+ const slug = requested.replace(/\.md$/i, '');
80
+ const index = await (0, memoryIndex_1.loadMemoryIndex)(ctx.workspaceRoot);
81
+ const entry = index.entries.find((e) => e.name === slug);
82
+ if (!entry) {
83
+ const available = index.entries.map((e) => e.name);
84
+ if (available.length === 0) {
85
+ return {
86
+ output: `No memory index found. MEMORY.md is missing or has no entries at ${path.resolve(ctx.workspaceRoot, 'MEMORY.md')}.`,
87
+ isError: true
88
+ };
89
+ }
90
+ return {
91
+ output: `Memory slug "${slug}" not found. Available: ${available.join(', ')}`,
92
+ isError: true
93
+ };
94
+ }
95
+ try {
96
+ const raw = await fs.promises.readFile(entry.absPath);
97
+ const truncated = raw.byteLength > memoryIndex_1.MAX_MEMORY_FILE_BYTES;
98
+ const text = raw.subarray(0, memoryIndex_1.MAX_MEMORY_FILE_BYTES).toString('utf-8');
99
+ const body = truncated ? `${text}\n… (truncated — file exceeds ${memoryIndex_1.MAX_MEMORY_FILE_BYTES} bytes)` : text;
100
+ return {
101
+ output: `<!-- source: ${entry.relPath} -->\n${body}`,
102
+ isError: false
103
+ };
104
+ }
105
+ catch (err) {
106
+ const msg = err instanceof Error ? err.message : String(err);
107
+ return {
108
+ output: `Could not read ${memoryIndex_1.MEMORY_DIR}/${slug}.md: ${msg}`,
109
+ isError: true
110
+ };
111
+ }
112
+ }
113
+ };
114
+ }
115
+ //# sourceMappingURL=readMemoryTool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readMemoryTool.js","sourceRoot":"","sources":["../../src/tools/readMemoryTool.ts"],"names":[],"mappings":"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"}