@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.
- package/LICENSE +201 -0
- package/README.md +55 -0
- package/dist/backgroundTasks.d.ts +113 -0
- package/dist/backgroundTasks.d.ts.map +1 -0
- package/dist/backgroundTasks.js +137 -0
- package/dist/backgroundTasks.js.map +1 -0
- package/dist/checkpoints.d.ts +99 -0
- package/dist/checkpoints.d.ts.map +1 -0
- package/dist/checkpoints.js +227 -0
- package/dist/checkpoints.js.map +1 -0
- package/dist/hooks.d.ts +51 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +152 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/insights.d.ts +398 -0
- package/dist/insights.d.ts.map +1 -0
- package/dist/insights.js +1933 -0
- package/dist/insights.js.map +1 -0
- package/dist/mcp.d.ts +60 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +281 -0
- package/dist/mcp.js.map +1 -0
- package/dist/mcpConnectors.d.ts +108 -0
- package/dist/mcpConnectors.d.ts.map +1 -0
- package/dist/mcpConnectors.js +217 -0
- package/dist/mcpConnectors.js.map +1 -0
- package/dist/mcpToolCache.d.ts +43 -0
- package/dist/mcpToolCache.d.ts.map +1 -0
- package/dist/mcpToolCache.js +150 -0
- package/dist/mcpToolCache.js.map +1 -0
- package/dist/mcpTrust.d.ts +22 -0
- package/dist/mcpTrust.d.ts.map +1 -0
- package/dist/mcpTrust.js +104 -0
- package/dist/mcpTrust.js.map +1 -0
- package/dist/memory.d.ts +38 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +151 -0
- package/dist/memory.js.map +1 -0
- package/dist/memoryIndex.d.ts +38 -0
- package/dist/memoryIndex.d.ts.map +1 -0
- package/dist/memoryIndex.js +142 -0
- package/dist/memoryIndex.js.map +1 -0
- package/dist/mentions.d.ts +33 -0
- package/dist/mentions.d.ts.map +1 -0
- package/dist/mentions.js +126 -0
- package/dist/mentions.js.map +1 -0
- package/dist/ollamaModels.d.ts +36 -0
- package/dist/ollamaModels.d.ts.map +1 -0
- package/dist/ollamaModels.js +83 -0
- package/dist/ollamaModels.js.map +1 -0
- package/dist/permissions.d.ts +72 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +271 -0
- package/dist/permissions.js.map +1 -0
- package/dist/tools/extraTools.d.ts +80 -0
- package/dist/tools/extraTools.d.ts.map +1 -0
- package/dist/tools/extraTools.js +471 -0
- package/dist/tools/extraTools.js.map +1 -0
- package/dist/tools/readMemoryTool.d.ts +3 -0
- package/dist/tools/readMemoryTool.d.ts.map +1 -0
- package/dist/tools/readMemoryTool.js +115 -0
- package/dist/tools/readMemoryTool.js.map +1 -0
- package/dist/tools/taskTool.d.ts +119 -0
- package/dist/tools/taskTool.d.ts.map +1 -0
- package/dist/tools/taskTool.js +466 -0
- package/dist/tools/taskTool.js.map +1 -0
- package/dist/tools/testRunTool.d.ts +59 -0
- package/dist/tools/testRunTool.d.ts.map +1 -0
- package/dist/tools/testRunTool.js +308 -0
- package/dist/tools/testRunTool.js.map +1 -0
- package/dist/turnLog.d.ts +89 -0
- package/dist/turnLog.d.ts.map +1 -0
- package/dist/turnLog.js +469 -0
- package/dist/turnLog.js.map +1 -0
- 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"}
|
package/dist/mentions.js
ADDED
|
@@ -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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"}
|