@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
package/dist/memory.js ADDED
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ /**
3
+ * Memory — auto-load workspace-local context files (BANDIT.md / CLAUDE.md)
4
+ * and inline them into the system prompt so the agent follows project rules
5
+ * without being re-told on every turn.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.loadCombinedMemory = loadCombinedMemory;
42
+ exports.loadMemory = loadMemory;
43
+ exports.appendMemory = appendMemory;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const memoryIndex_1 = require("./memoryIndex");
47
+ const CANDIDATES = [
48
+ 'BANDIT.md',
49
+ 'CLAUDE.md',
50
+ 'AGENTS.md', // OpenAI Codex / GitHub Copilot convention — load alongside BANDIT.md, don't pick one
51
+ '.bandit/BANDIT.md',
52
+ '.bandit/memory.md'
53
+ ];
54
+ const MAX_BYTES = 32 * 1024;
55
+ /**
56
+ * loadMemory + MEMORY.md index, fused into a single bundle the host can
57
+ * inject as the system-prompt memory block. Always-loaded files come
58
+ * first; the index follows under its own source marker so the model
59
+ * knows which entries are full content vs. on-demand topic pointers.
60
+ *
61
+ * Returns sources from BOTH passes when present — callers use this for
62
+ * the `/memory` slash command and boot-status output.
63
+ */
64
+ async function loadCombinedMemory(cwd, warn) {
65
+ const base = await loadMemory(cwd);
66
+ const index = await (0, memoryIndex_1.loadMemoryIndex)(cwd, warn);
67
+ const indexBlock = (0, memoryIndex_1.renderMemoryIndexBlock)(index);
68
+ if (!indexBlock)
69
+ return base;
70
+ const headed = `<!-- source: ${memoryIndex_1.MEMORY_INDEX_FILE} -->\n${indexBlock}`;
71
+ return {
72
+ content: base.content ? `${base.content}\n\n${headed}` : headed,
73
+ sources: index.source ? [...base.sources, memoryIndex_1.MEMORY_INDEX_FILE] : base.sources
74
+ };
75
+ }
76
+ async function loadMemory(cwd) {
77
+ const sections = [];
78
+ const sources = [];
79
+ for (const rel of CANDIDATES) {
80
+ const abs = path.resolve(cwd, rel);
81
+ try {
82
+ const raw = await fs.promises.readFile(abs);
83
+ if (raw.byteLength === 0)
84
+ continue;
85
+ const truncated = raw.byteLength > MAX_BYTES;
86
+ const text = raw.subarray(0, MAX_BYTES).toString('utf-8');
87
+ sections.push(`<!-- source: ${rel} -->\n${text}${truncated ? '\n… (truncated)' : ''}`);
88
+ sources.push(rel);
89
+ }
90
+ catch {
91
+ // Not present — that's fine.
92
+ }
93
+ }
94
+ return {
95
+ content: sections.join('\n\n'),
96
+ sources
97
+ };
98
+ }
99
+ /**
100
+ * Append a single fact to project memory so it survives across sessions.
101
+ *
102
+ * Persists to `BANDIT.md` at the workspace root (creates the file with a
103
+ * minimal frontmatter-free header when it doesn't exist). Bullets are
104
+ * appended under a `## Notes` heading so multiple `/remember` calls
105
+ * accumulate without scattering. CLAUDE.md-only repos still get found
106
+ * by loadMemory, but new facts always land in BANDIT.md to keep one
107
+ * canonical write target.
108
+ *
109
+ * Returns the absolute path written so the caller can echo it back to
110
+ * the user — visibility matters because the user typed "remember X"
111
+ * and needs to see WHERE the fact landed.
112
+ */
113
+ async function appendMemory(cwd, fact) {
114
+ const trimmed = fact.trim();
115
+ if (!trimmed)
116
+ throw new Error('appendMemory: fact must be a non-empty string');
117
+ const abs = path.resolve(cwd, 'BANDIT.md');
118
+ let existing = '';
119
+ try {
120
+ existing = await fs.promises.readFile(abs, 'utf-8');
121
+ }
122
+ catch {
123
+ // File doesn't exist — start with a minimal scaffold so the file is
124
+ // self-explanatory if the user opens it later.
125
+ existing =
126
+ '# Project Memory\n\n' +
127
+ 'Auto-loaded by Bandit on every prompt. Add facts the agent should\n' +
128
+ 'remember across sessions — preferences, repo locations, conventions,\n' +
129
+ 'incident notes. Use `/remember <fact>` to append a bullet under the\n' +
130
+ 'Notes heading.\n';
131
+ }
132
+ // First bullet under "## Notes"? Append a new section. Otherwise
133
+ // tack the bullet onto the existing section.
134
+ const NOTES_HEADING = '## Notes';
135
+ const hasSection = existing.includes(`\n${NOTES_HEADING}`) || existing.startsWith(NOTES_HEADING);
136
+ const bullet = `- ${trimmed}`;
137
+ let next;
138
+ if (hasSection) {
139
+ // Insert at the end of the file — bullets stay chronological so the
140
+ // most recent context lands at the bottom where it's easiest to see
141
+ // when scrolling.
142
+ next = existing.endsWith('\n') ? `${existing}${bullet}\n` : `${existing}\n${bullet}\n`;
143
+ }
144
+ else {
145
+ const sep = existing.endsWith('\n\n') ? '' : existing.endsWith('\n') ? '\n' : '\n\n';
146
+ next = `${existing}${sep}${NOTES_HEADING}\n\n${bullet}\n`;
147
+ }
148
+ await fs.promises.writeFile(abs, next, 'utf-8');
149
+ return abs;
150
+ }
151
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export declare const MAX_INDEX_BYTES: number;
2
+ export declare const MAX_MEMORY_FILE_BYTES: number;
3
+ export declare const MEMORY_DIR = "memory";
4
+ export declare const MEMORY_INDEX_FILE = "MEMORY.md";
5
+ export interface MemoryIndexEntry {
6
+ /** Slug derived from filename (e.g. "auth-conventions"). Used as the `name` arg to read_memory. */
7
+ name: string;
8
+ /** Human label from the markdown link. */
9
+ title: string;
10
+ /** One-line summary after the em-dash. */
11
+ hook: string;
12
+ /** Path relative to the workspace root, e.g. "memory/auth-conventions.md". */
13
+ relPath: string;
14
+ /** Resolved absolute path on disk. */
15
+ absPath: string;
16
+ }
17
+ export interface MemoryIndex {
18
+ /** The MEMORY.md content (trimmed to MAX_INDEX_BYTES). Empty when the file is missing. */
19
+ indexContent: string;
20
+ /** Parsed entries; dangling links are dropped (with a warning). */
21
+ entries: MemoryIndexEntry[];
22
+ /** Absolute path of MEMORY.md if found, else null. */
23
+ source: string | null;
24
+ }
25
+ /**
26
+ * Optional warn sink. Defaults to a single-line stderr write so the host
27
+ * sees dangling-link warnings without surfacing them to the model.
28
+ */
29
+ export type MemoryWarnFn = (msg: string) => void;
30
+ /**
31
+ * Render a MEMORY.md index as a system-prompt block. Includes a one-line
32
+ * nudge telling the model to call `read_memory(name=...)` when an entry's
33
+ * hook matches the task. Returns an empty string when the index has no
34
+ * entries so the caller can `if (block) ...` without a null check.
35
+ */
36
+ export declare function renderMemoryIndexBlock(index: MemoryIndex): string;
37
+ export declare function loadMemoryIndex(cwd: string, warn?: MemoryWarnFn): Promise<MemoryIndex>;
38
+ //# sourceMappingURL=memoryIndex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoryIndex.d.ts","sourceRoot":"","sources":["../src/memoryIndex.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,eAAe,QAAW,CAAC;AACxC,eAAO,MAAM,qBAAqB,QAAY,CAAC;AAC/C,eAAO,MAAM,UAAU,WAAW,CAAC;AACnC,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,mGAAmG;IACnG,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,0FAA0F;IAC1F,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,sDAAsD;IACtD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAQD;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;AAMjD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAWjE;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,YAA0B,GAAG,OAAO,CAAC,WAAW,CAAC,CAuDzG"}
@@ -0,0 +1,142 @@
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.MEMORY_INDEX_FILE = exports.MEMORY_DIR = exports.MAX_MEMORY_FILE_BYTES = exports.MAX_INDEX_BYTES = void 0;
37
+ exports.renderMemoryIndexBlock = renderMemoryIndexBlock;
38
+ exports.loadMemoryIndex = loadMemoryIndex;
39
+ /**
40
+ * Lazy-load topic memory.
41
+ *
42
+ * MEMORY.md at the workspace root is an INDEX — one bullet per topic, each
43
+ * pointing at a markdown file in a sibling `memory/` directory. The index
44
+ * itself goes into the system prompt every turn (small — capped at 4 KB).
45
+ * The linked files are NOT preloaded; the agent reads them via the
46
+ * `read_memory` tool when it decides a topic is relevant.
47
+ *
48
+ * Index entry format:
49
+ *
50
+ * - [Topic title](memory/file.md) — one-line hook for relevance matching
51
+ *
52
+ * The hook is what the agent sees and reasons about. Pick hooks that name
53
+ * the situation ("when editing auth code", "when the user mentions X").
54
+ */
55
+ const fs = __importStar(require("fs"));
56
+ const path = __importStar(require("path"));
57
+ exports.MAX_INDEX_BYTES = 4 * 1024;
58
+ exports.MAX_MEMORY_FILE_BYTES = 32 * 1024;
59
+ exports.MEMORY_DIR = 'memory';
60
+ exports.MEMORY_INDEX_FILE = 'MEMORY.md';
61
+ const ENTRY_RE = /^[-*]\s*\[([^\]]+)\]\(memory\/([^)]+)\)\s*(?:[—–-]+)\s*(.+)$/;
62
+ function slugFromFilename(file) {
63
+ return file.replace(/\.md$/i, '');
64
+ }
65
+ const defaultWarn = (msg) => {
66
+ process.stderr.write(`[memory-index] ${msg}\n`);
67
+ };
68
+ /**
69
+ * Render a MEMORY.md index as a system-prompt block. Includes a one-line
70
+ * nudge telling the model to call `read_memory(name=...)` when an entry's
71
+ * hook matches the task. Returns an empty string when the index has no
72
+ * entries so the caller can `if (block) ...` without a null check.
73
+ */
74
+ function renderMemoryIndexBlock(index) {
75
+ if (index.entries.length === 0)
76
+ return '';
77
+ const lines = index.entries.map((e) => `- [${e.title}](${e.relPath}) — ${e.hook}`);
78
+ return [
79
+ '<!-- source: MEMORY.md (index — call read_memory(name="<slug>") to load a topic) -->',
80
+ 'These are topic memories — full content is NOT preloaded. Read the hook; if it matches the current task, call `read_memory(name="<slug>")` BEFORE making changes. The slug is the part after `memory/` and before `.md` in the link.',
81
+ '',
82
+ ...lines
83
+ ].join('\n');
84
+ }
85
+ async function loadMemoryIndex(cwd, warn = defaultWarn) {
86
+ const abs = path.resolve(cwd, exports.MEMORY_INDEX_FILE);
87
+ let raw;
88
+ try {
89
+ raw = await fs.promises.readFile(abs);
90
+ }
91
+ catch {
92
+ return { indexContent: '', entries: [], source: null };
93
+ }
94
+ if (raw.byteLength === 0) {
95
+ return { indexContent: '', entries: [], source: abs };
96
+ }
97
+ const truncated = raw.byteLength > exports.MAX_INDEX_BYTES;
98
+ const text = raw.subarray(0, exports.MAX_INDEX_BYTES).toString('utf-8');
99
+ const indexContent = truncated ? `${text}\n… (truncated — MEMORY.md exceeds ${exports.MAX_INDEX_BYTES} bytes; split topics into smaller files)` : text;
100
+ const entries = [];
101
+ const seen = new Set();
102
+ for (const line of text.split(/\r?\n/)) {
103
+ const match = ENTRY_RE.exec(line.trim());
104
+ if (!match)
105
+ continue;
106
+ const [, title, fileRaw, hookRaw] = match;
107
+ const file = fileRaw.trim();
108
+ if (!file || file.includes('..') || path.isAbsolute(file)) {
109
+ warn(`skipped entry "${title}" — invalid memory/ path "${file}"`);
110
+ continue;
111
+ }
112
+ const relPath = path.posix.join(exports.MEMORY_DIR, file);
113
+ const absPath = path.resolve(cwd, exports.MEMORY_DIR, file);
114
+ let exists = false;
115
+ try {
116
+ const stat = await fs.promises.stat(absPath);
117
+ exists = stat.isFile();
118
+ }
119
+ catch {
120
+ exists = false;
121
+ }
122
+ if (!exists) {
123
+ warn(`dangling link: ${relPath} (referenced by MEMORY.md but no such file)`);
124
+ continue;
125
+ }
126
+ const name = slugFromFilename(file);
127
+ if (seen.has(name)) {
128
+ warn(`duplicate slug "${name}" — keeping first occurrence`);
129
+ continue;
130
+ }
131
+ seen.add(name);
132
+ entries.push({
133
+ name,
134
+ title: title.trim(),
135
+ hook: hookRaw.trim(),
136
+ relPath,
137
+ absPath
138
+ });
139
+ }
140
+ return { indexContent, entries, source: abs };
141
+ }
142
+ //# sourceMappingURL=memoryIndex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoryIndex.js","sourceRoot":"","sources":["../src/memoryIndex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @-mentions — detect `@path/to/file` patterns in user input and inline the
3
+ * file contents into the prompt so the model sees them in the first turn.
4
+ *
5
+ * user> explain @src/auth.ts
6
+ * → prompt becomes: "explain @src/auth.ts\n\n[File: src/auth.ts]\n<content>"
7
+ *
8
+ * Images (png/jpg/etc.) are NOT inlined as text — doing so dumps raw binary
9
+ * bytes decoded as UTF-8 into the prompt, and the model interprets the
10
+ * resulting gibberish as corrupt data. Instead, image mentions are
11
+ * collected into `images[]` as base64 data URLs so the caller can
12
+ * forward them on the provider's `images` field (Ollama vision models
13
+ * accept this; Bandit cloud forwards through to the gateway).
14
+ */
15
+ export interface ExpandedPrompt {
16
+ prompt: string;
17
+ mentions: {
18
+ path: string;
19
+ bytes: number;
20
+ ok: boolean;
21
+ kind?: 'text' | 'image';
22
+ }[];
23
+ /**
24
+ * Base64-encoded payloads for any image mentions in the prompt, in the
25
+ * order they appeared. Empty when no images were attached. Caller is
26
+ * responsible for forwarding these to the provider — the prompt text
27
+ * only contains a `[Image: path]` placeholder so the model has a label
28
+ * to anchor references to.
29
+ */
30
+ images: string[];
31
+ }
32
+ export declare function expandMentions(input: string, cwd: string): Promise<ExpandedPrompt>;
33
+ //# sourceMappingURL=mentions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mentions.d.ts","sourceRoot":"","sources":["../src/mentions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAwBH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,EAAE,CAAC;IAClF;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAwDxF"}
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * @-mentions — detect `@path/to/file` patterns in user input and inline the
4
+ * file contents into the prompt so the model sees them in the first turn.
5
+ *
6
+ * user> explain @src/auth.ts
7
+ * → prompt becomes: "explain @src/auth.ts\n\n[File: src/auth.ts]\n<content>"
8
+ *
9
+ * Images (png/jpg/etc.) are NOT inlined as text — doing so dumps raw binary
10
+ * bytes decoded as UTF-8 into the prompt, and the model interprets the
11
+ * resulting gibberish as corrupt data. Instead, image mentions are
12
+ * collected into `images[]` as base64 data URLs so the caller can
13
+ * forward them on the provider's `images` field (Ollama vision models
14
+ * accept this; Bandit cloud forwards through to the gateway).
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
29
+ }) : function(o, v) {
30
+ o["default"] = v;
31
+ });
32
+ var __importStar = (this && this.__importStar) || (function () {
33
+ var ownKeys = function(o) {
34
+ ownKeys = Object.getOwnPropertyNames || function (o) {
35
+ var ar = [];
36
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
+ return ar;
38
+ };
39
+ return ownKeys(o);
40
+ };
41
+ return function (mod) {
42
+ if (mod && mod.__esModule) return mod;
43
+ var result = {};
44
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
+ __setModuleDefault(result, mod);
46
+ return result;
47
+ };
48
+ })();
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.expandMentions = expandMentions;
51
+ const fs = __importStar(require("fs"));
52
+ const path = __importStar(require("path"));
53
+ const agent_core_1 = require("@burtson-labs/agent-core");
54
+ const MENTION_REGEX = /(?:^|\s)@([^\s@]+)/g;
55
+ /**
56
+ * redact secrets from @-mentioned file content with the
57
+ * same opt-out as the rest of the redactor (`BANDIT_NO_SECRET_REDACTION=1`).
58
+ * Pulled into a helper so the env check happens once per mention pass.
59
+ */
60
+ function applyMentionSecretRedaction(text) {
61
+ if (/^(1|true)$/i.test(process.env.BANDIT_NO_SECRET_REDACTION ?? '')) {
62
+ return text;
63
+ }
64
+ if (!text || text.length === 0)
65
+ return text;
66
+ return (0, agent_core_1.redactSecretsString)(text);
67
+ }
68
+ const MAX_FILE_BYTES = 64 * 1024; // 64 KB per mention
69
+ const MAX_MENTIONS = 8;
70
+ const IMAGE_EXT = /\.(?:png|jpe?g|gif|webp|heic|bmp)$/i;
71
+ async function expandMentions(input, cwd) {
72
+ const matches = [...input.matchAll(MENTION_REGEX)].slice(0, MAX_MENTIONS);
73
+ if (matches.length === 0)
74
+ return { prompt: input, mentions: [], images: [] };
75
+ const seen = new Set();
76
+ const sections = [];
77
+ const mentions = [];
78
+ const images = [];
79
+ for (const m of matches) {
80
+ const rel = m[1];
81
+ if (seen.has(rel))
82
+ continue;
83
+ seen.add(rel);
84
+ const abs = path.isAbsolute(rel) ? rel : path.resolve(cwd, rel);
85
+ // Guard: don't read outside the workspace.
86
+ if (!abs.startsWith(path.resolve(cwd))) {
87
+ mentions.push({ path: rel, bytes: 0, ok: false });
88
+ continue;
89
+ }
90
+ try {
91
+ const stat = await fs.promises.stat(abs);
92
+ if (!stat.isFile()) {
93
+ mentions.push({ path: rel, bytes: 0, ok: false });
94
+ continue;
95
+ }
96
+ const raw = await fs.promises.readFile(abs);
97
+ // Image branch: encode + register as attachment, emit a short
98
+ // placeholder so the prompt doesn't contain binary gibberish.
99
+ if (IMAGE_EXT.test(rel)) {
100
+ images.push(raw.toString('base64'));
101
+ sections.push(`[Image attached: ${rel} — ${raw.byteLength} bytes]`);
102
+ mentions.push({ path: rel, bytes: raw.byteLength, ok: true, kind: 'image' });
103
+ continue;
104
+ }
105
+ const truncated = raw.byteLength > MAX_FILE_BYTES;
106
+ const rawContent = raw.subarray(0, MAX_FILE_BYTES).toString('utf-8');
107
+ // redact secrets from @-mentioned file content before
108
+ // it lands in the user message. @-mentions inline file content
109
+ // into the prompt body verbatim; without this pass, dropping
110
+ // `@.env` or `@~/.aws/credentials` into the composer would ship
111
+ // every key in there to the model context and the session log.
112
+ // Same redactor that runs on tool-result output, applied here
113
+ // for parity. Opt-out via BANDIT_NO_SECRET_REDACTION env.
114
+ const content = applyMentionSecretRedaction(rawContent);
115
+ const suffix = truncated ? `\n… (truncated, full file is ${raw.byteLength} bytes)` : '';
116
+ sections.push(`[File: ${rel}]\n\`\`\`\n${content}${suffix}\n\`\`\``);
117
+ mentions.push({ path: rel, bytes: raw.byteLength, ok: true, kind: 'text' });
118
+ }
119
+ catch {
120
+ mentions.push({ path: rel, bytes: 0, ok: false });
121
+ }
122
+ }
123
+ const body = sections.length > 0 ? `${input}\n\n${sections.join('\n\n')}` : input;
124
+ return { prompt: body, mentions, images };
125
+ }
126
+ //# sourceMappingURL=mentions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mentions.js","sourceRoot":"","sources":["../src/mentions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Shared Ollama model-discovery helpers used by both the CLI and the
3
+ * VS Code extension. Both surfaces need to:
4
+ * - list what's actually pulled on the local runtime (`/api/tags`),
5
+ * - map an unpulled model to the closest installed match so users
6
+ * don't have to know about `-it-qat` vs `-it-q4_K_M` suffixes.
7
+ *
8
+ * Keeping the logic here means CLI auto-switch and extension UI pickers
9
+ * give identical suggestions for the same installed set.
10
+ */
11
+ export interface OllamaModelInfo {
12
+ /** Full tag as returned by `/api/tags` (e.g. "gemma3:12b-it-qat"). */
13
+ name: string;
14
+ /** Approximate on-disk size in bytes, when reported. */
15
+ size?: number;
16
+ /** Last modified ISO string, when reported. */
17
+ modifiedAt?: string;
18
+ }
19
+ /**
20
+ * Fetch the list of models installed on an Ollama runtime. Embedding-only
21
+ * models are NOT filtered out here — callers that want just chat-capable
22
+ * models should pass `{ chatOnly: true }` or filter via {@link isChatCapable}.
23
+ */
24
+ export declare function listInstalledOllamaModels(baseUrl: string, options?: {
25
+ timeoutMs?: number;
26
+ chatOnly?: boolean;
27
+ }): Promise<OllamaModelInfo[]>;
28
+ /** Embedding-only models should be excluded from chat/agent picklists. */
29
+ export declare function isChatCapable(name: string): boolean;
30
+ /**
31
+ * Rank installed Ollama models by how well they match a requested name.
32
+ * Prefers: same family + same param-size > same family > any chat-capable
33
+ * model. Excludes embedding-only models from the returned list.
34
+ */
35
+ export declare function suggestOllamaMatch(requested: string, installed: string[]): string[];
36
+ //# sourceMappingURL=ollamaModels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollamaModels.d.ts","sourceRoot":"","sources":["../src/ollamaModels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GACvD,OAAO,CAAC,eAAe,EAAE,CAAC,CAmB5B;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CA0BnF"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ /**
3
+ * Shared Ollama model-discovery helpers used by both the CLI and the
4
+ * VS Code extension. Both surfaces need to:
5
+ * - list what's actually pulled on the local runtime (`/api/tags`),
6
+ * - map an unpulled model to the closest installed match so users
7
+ * don't have to know about `-it-qat` vs `-it-q4_K_M` suffixes.
8
+ *
9
+ * Keeping the logic here means CLI auto-switch and extension UI pickers
10
+ * give identical suggestions for the same installed set.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.listInstalledOllamaModels = listInstalledOllamaModels;
14
+ exports.isChatCapable = isChatCapable;
15
+ exports.suggestOllamaMatch = suggestOllamaMatch;
16
+ /**
17
+ * Fetch the list of models installed on an Ollama runtime. Embedding-only
18
+ * models are NOT filtered out here — callers that want just chat-capable
19
+ * models should pass `{ chatOnly: true }` or filter via {@link isChatCapable}.
20
+ */
21
+ async function listInstalledOllamaModels(baseUrl, options = {}) {
22
+ const url = `${baseUrl.replace(/\/+$/, '')}/api/tags`;
23
+ const timeoutMs = options.timeoutMs ?? 3000;
24
+ try {
25
+ const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
26
+ if (!res.ok)
27
+ return [];
28
+ const data = (await res.json());
29
+ const all = (data.models ?? [])
30
+ .filter((m) => typeof m?.name === 'string')
31
+ .map((m) => ({ name: m.name, size: m.size, modifiedAt: m.modified_at }));
32
+ if (options.chatOnly) {
33
+ return all.filter((m) => isChatCapable(m.name));
34
+ }
35
+ return all;
36
+ }
37
+ catch {
38
+ return [];
39
+ }
40
+ }
41
+ /** Embedding-only models should be excluded from chat/agent picklists. */
42
+ function isChatCapable(name) {
43
+ return !/embed|embedding|nomic/i.test(name);
44
+ }
45
+ /**
46
+ * Rank installed Ollama models by how well they match a requested name.
47
+ * Prefers: same family + same param-size > same family > any chat-capable
48
+ * model. Excludes embedding-only models from the returned list.
49
+ */
50
+ function suggestOllamaMatch(requested, installed) {
51
+ const [reqStem = '', reqTag = ''] = requested.split(':');
52
+ // "gemma4" → "gemma"; "qwen2.5-coder" → "qwen". Used to cross-match
53
+ // across major versions of the same family (gemma4:e4b → gemma3:12b-it-qat).
54
+ const familyBase = (stem) => stem.replace(/[\d.]+.*$/, '') || stem;
55
+ const reqFamily = familyBase(reqStem);
56
+ const score = (name) => {
57
+ if (!isChatCapable(name))
58
+ return -1;
59
+ const [stem, tag = ''] = name.split(':');
60
+ let s = 0;
61
+ if (stem === reqStem)
62
+ s += 20;
63
+ else if (stem.startsWith(reqStem) || reqStem.startsWith(stem))
64
+ s += 10;
65
+ else if (reqFamily.length >= 3 && familyBase(stem) === reqFamily)
66
+ s += 5;
67
+ if (s === 0)
68
+ return 0;
69
+ if (reqTag && tag.startsWith(reqTag))
70
+ s += 6;
71
+ else if (reqTag && tag.includes(reqTag))
72
+ s += 3;
73
+ if (/qat|q4|q5|q8/i.test(tag))
74
+ s += 1;
75
+ return s;
76
+ };
77
+ return installed
78
+ .map((name) => ({ name, s: score(name) }))
79
+ .filter((x) => x.s > 0)
80
+ .sort((a, b) => b.s - a.s)
81
+ .map((x) => x.name);
82
+ }
83
+ //# sourceMappingURL=ollamaModels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollamaModels.js","sourceRoot":"","sources":["../src/ollamaModels.ts"],"names":[],"mappings":"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"}