@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
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission policy for tool execution.
|
|
3
|
+
*
|
|
4
|
+
* Hosts (CLI, VS Code extension) consult the policy BEFORE a tool runs.
|
|
5
|
+
* The policy returns one of:
|
|
6
|
+
* - 'allow' : proceed without prompting
|
|
7
|
+
* - 'ask' : prompt the user (host decides how — modal, [y/N], etc.)
|
|
8
|
+
* - 'deny' : abort, model sees a blocked-tool result
|
|
9
|
+
*
|
|
10
|
+
* Patterns can target a tool by name ("write_file") or a tool+arg filter
|
|
11
|
+
* ("write_file:src/**", "run_command:npm test*"). Glob matching uses the
|
|
12
|
+
* same minimal engine as host-kit/mentions — *, **, ?, and {a,b}.
|
|
13
|
+
*
|
|
14
|
+
* Config shape in .bandit/settings.json:
|
|
15
|
+
*
|
|
16
|
+
* {
|
|
17
|
+
* "permissions": {
|
|
18
|
+
* "allow": ["read_file", "list_files", "search_code", "write_file:docs/**"],
|
|
19
|
+
* "deny": ["run_command:rm *"],
|
|
20
|
+
* "ask": ["write_file", "run_command"]
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* Evaluation order: deny > allow > ask > default.
|
|
25
|
+
* Default for dangerous tools (write_file, apply_edit, replace_range, run_command) is 'ask'.
|
|
26
|
+
* Default for read-only tools is 'allow'.
|
|
27
|
+
*/
|
|
28
|
+
export type PermissionDecision = 'allow' | 'ask' | 'deny';
|
|
29
|
+
export interface PermissionPolicy {
|
|
30
|
+
allow: string[];
|
|
31
|
+
deny: string[];
|
|
32
|
+
ask: string[];
|
|
33
|
+
}
|
|
34
|
+
export declare const emptyPolicy: () => PermissionPolicy;
|
|
35
|
+
/**
|
|
36
|
+
* Merge two policies. Used to combine workspace settings with session
|
|
37
|
+
* overrides (e.g. the user's "always allow for this session" choices).
|
|
38
|
+
*/
|
|
39
|
+
export declare function mergePolicies(a: PermissionPolicy, b: PermissionPolicy): PermissionPolicy;
|
|
40
|
+
/**
|
|
41
|
+
* Evaluate the policy for a tool invocation. Returns the decision.
|
|
42
|
+
*
|
|
43
|
+
* @param toolName Tool being invoked (e.g. "write_file")
|
|
44
|
+
* @param primary The first meaningful param value (path, cmd, url…)
|
|
45
|
+
* used for fine-grained pattern matching. Empty
|
|
46
|
+
* string if unknown.
|
|
47
|
+
* @param policy Merged workspace + session policy.
|
|
48
|
+
* @param primaryFull Optional fuller representation of the invocation
|
|
49
|
+
* used for pattern matching alongside `primary`.
|
|
50
|
+
* For `run_command` the host should pass the full
|
|
51
|
+
* command line ("git push origin main") so glob
|
|
52
|
+
* patterns like `run_command:git *` and
|
|
53
|
+
* `run_command:rm *` work intuitively. When
|
|
54
|
+
* omitted (default) only `primary` is consulted —
|
|
55
|
+
* preserves the original semantics for callers
|
|
56
|
+
* that don't need the wider match.
|
|
57
|
+
*/
|
|
58
|
+
export declare function evaluatePermission(toolName: string, primary: string, policy: PermissionPolicy, primaryFull?: string): PermissionDecision;
|
|
59
|
+
/**
|
|
60
|
+
* In-memory store for session-level "always allow" choices. Hosts instantiate
|
|
61
|
+
* one per conversation so approvals reset when the user starts fresh.
|
|
62
|
+
*/
|
|
63
|
+
export declare class SessionPermissionStore {
|
|
64
|
+
private readonly allow;
|
|
65
|
+
/** Remember an "always allow for this session" choice. */
|
|
66
|
+
grant(toolName: string, primary?: string): void;
|
|
67
|
+
/** Returns a policy fragment reflecting session grants only. */
|
|
68
|
+
toPolicy(): PermissionPolicy;
|
|
69
|
+
clear(): void;
|
|
70
|
+
size(): number;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=permissions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,EAAE,CAAC;CACf;AA0FD,eAAO,MAAM,WAAW,QAAO,gBAAsD,CAAC;AAEtF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,CAMxF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,gBAAgB,EACxB,WAAW,CAAC,EAAE,MAAM,GACnB,kBAAkB,CAepB;AA2ED;;;GAGG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAE3C,0DAA0D;IAC1D,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAI/C,gEAAgE;IAChE,QAAQ,IAAI,gBAAgB;IAI5B,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,MAAM;CAGf"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Permission policy for tool execution.
|
|
4
|
+
*
|
|
5
|
+
* Hosts (CLI, VS Code extension) consult the policy BEFORE a tool runs.
|
|
6
|
+
* The policy returns one of:
|
|
7
|
+
* - 'allow' : proceed without prompting
|
|
8
|
+
* - 'ask' : prompt the user (host decides how — modal, [y/N], etc.)
|
|
9
|
+
* - 'deny' : abort, model sees a blocked-tool result
|
|
10
|
+
*
|
|
11
|
+
* Patterns can target a tool by name ("write_file") or a tool+arg filter
|
|
12
|
+
* ("write_file:src/**", "run_command:npm test*"). Glob matching uses the
|
|
13
|
+
* same minimal engine as host-kit/mentions — *, **, ?, and {a,b}.
|
|
14
|
+
*
|
|
15
|
+
* Config shape in .bandit/settings.json:
|
|
16
|
+
*
|
|
17
|
+
* {
|
|
18
|
+
* "permissions": {
|
|
19
|
+
* "allow": ["read_file", "list_files", "search_code", "write_file:docs/**"],
|
|
20
|
+
* "deny": ["run_command:rm *"],
|
|
21
|
+
* "ask": ["write_file", "run_command"]
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* Evaluation order: deny > allow > ask > default.
|
|
26
|
+
* Default for dangerous tools (write_file, apply_edit, replace_range, run_command) is 'ask'.
|
|
27
|
+
* Default for read-only tools is 'allow'.
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.SessionPermissionStore = exports.emptyPolicy = void 0;
|
|
31
|
+
exports.mergePolicies = mergePolicies;
|
|
32
|
+
exports.evaluatePermission = evaluatePermission;
|
|
33
|
+
/** Tools that mutate state or execute code and therefore require ask-by-default. */
|
|
34
|
+
const DANGEROUS_TOOLS = new Set(['write_file', 'apply_edit', 'replace_range', 'apply_patch', 'run_command']);
|
|
35
|
+
/**
|
|
36
|
+
* Mutating-tool name patterns used to gate MCP-bridged tools that
|
|
37
|
+
* aren't in the static DANGEROUS_TOOLS set. Catches tools whose names
|
|
38
|
+
* imply they change state outside Bandit's process — Gmail filters,
|
|
39
|
+
* Calendar events, Drive files, etc. — so the user sees a permission
|
|
40
|
+
* card before the agent silently archives 200 emails or creates a
|
|
41
|
+
* filter that auto-trashes mail.
|
|
42
|
+
*
|
|
43
|
+
* Captured 2026-05-25: bandit-logic ran createFilter +
|
|
44
|
+
* modifyMessageLabels + trashMessage without any permission prompt
|
|
45
|
+
* because they're all MCP-bridged tools and the evaluator defaulted
|
|
46
|
+
* to 'allow' for anything not in DANGEROUS_TOOLS. The user had no
|
|
47
|
+
* idea the inbox was being changed until after the fact.
|
|
48
|
+
*
|
|
49
|
+
* Read-only patterns (list*, get*, search*, read*) are deliberately
|
|
50
|
+
* NOT in this list — auto-allowing those keeps the agent able to
|
|
51
|
+
* browse without prompt-spam. The principle: ask before write,
|
|
52
|
+
* allow read by default.
|
|
53
|
+
*/
|
|
54
|
+
const MUTATING_PATTERNS = [
|
|
55
|
+
/^create[A-Z_]/, // createFilter, createLabel, createEvent, createFolder, create_event
|
|
56
|
+
/^update[A-Z_]/, // updateDraft, updateEvent, updateSpreadsheet
|
|
57
|
+
/^modify[A-Z_]/, // modifyMessageLabels, modify_message_labels
|
|
58
|
+
/^delete[A-Z_]/, // deleteEvent, deleteFile, deleteFilter
|
|
59
|
+
/^remove[A-Z_]/, // removeLabel, removeMember
|
|
60
|
+
/^trash[A-Z_]/, // trashMessage
|
|
61
|
+
/^archive[A-Z_]/, // archiveMessage
|
|
62
|
+
/^move[A-Z_]/, // moveFile, moveMessage
|
|
63
|
+
/^send[A-Z_]/, // sendEmail, sendDraft, sendMessage
|
|
64
|
+
/^post[A-Z_]/, // postMessage, postComment
|
|
65
|
+
/^add[A-Z_]/, // addComment, addMember (catches additive mutations too)
|
|
66
|
+
/^insert[A-Z_]/, // insertText, insertEvent
|
|
67
|
+
/^replace[A-Z_]/, // replaceTableRowData, findAndReplace
|
|
68
|
+
/^rename[A-Z_]/, // renameSheet, renameTab, renameFile
|
|
69
|
+
/^duplicate[A-Z_]/, // duplicateSheet
|
|
70
|
+
/^batch[A-Z_]/, // batchWrite, batchUpdate
|
|
71
|
+
/^write[A-Z_]/, // writeSpreadsheet
|
|
72
|
+
/^append[A-Z_]/, // appendTableRows, appendToGoogleDoc, appendSpreadsheetRows
|
|
73
|
+
/^clear[A-Z_]/, // clearSpreadsheetRange
|
|
74
|
+
/^copy[A-Z_]/, // copyFile, copySheetTo (creates a new resource)
|
|
75
|
+
/^upload[A-Z_]/, // uploadFile
|
|
76
|
+
/^revoke[A-Z_]/, // revokeAccess
|
|
77
|
+
/^grant[A-Z_]/, // grantPermission
|
|
78
|
+
/^triage[A-Z_]/, // triageInbox (writes label/state)
|
|
79
|
+
/^apply[A-Z_]/, // applyTextStyle, applyParagraphStyle (modifies docs)
|
|
80
|
+
/^set[A-Z_]/, // setCellBorders, setRowHeights, setColumnWidths
|
|
81
|
+
/^protect[A-Z_]/, // protectRange
|
|
82
|
+
/^group[A-Z_]/, // groupRows
|
|
83
|
+
/^ungroup[A-Z_]/, // ungroupAllRows
|
|
84
|
+
/^freeze[A-Z_]/, // freezeRowsAndColumns
|
|
85
|
+
/^auto[Rr]esize/, // autoResizeColumns / autoResizeRows
|
|
86
|
+
/^reply[A-Z_]/, // replyToComment, replyToSheetsComment
|
|
87
|
+
/^cancel[A-Z_]/, // cancelEvent
|
|
88
|
+
/^quick[Aa]dd/, // quickAddEvent
|
|
89
|
+
];
|
|
90
|
+
/**
|
|
91
|
+
* Strip the MCP namespace prefix (e.g. "burtson-labs.createFilter" →
|
|
92
|
+
* "createFilter") before pattern-testing. Tools registered via
|
|
93
|
+
* mcpToolToAgentTool get a "<server>.<tool>" naming convention; the
|
|
94
|
+
* mutating-verb test operates on the tool name itself.
|
|
95
|
+
*/
|
|
96
|
+
function stripMcpNamespace(toolName) {
|
|
97
|
+
const dotIdx = toolName.indexOf('.');
|
|
98
|
+
if (dotIdx > 0)
|
|
99
|
+
return toolName.slice(dotIdx + 1);
|
|
100
|
+
// mcp__server__tool naming convention (alternate)
|
|
101
|
+
const underIdx = toolName.indexOf('__');
|
|
102
|
+
if (underIdx > 0 && toolName.startsWith('mcp__')) {
|
|
103
|
+
const after = toolName.slice(underIdx + 2);
|
|
104
|
+
const next = after.indexOf('__');
|
|
105
|
+
return next > 0 ? after.slice(next + 2) : after;
|
|
106
|
+
}
|
|
107
|
+
return toolName;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Does this tool's name look like a mutating operation?
|
|
111
|
+
* Used as the default-ask gate for MCP-bridged tools that aren't
|
|
112
|
+
* declared in the in-tree DANGEROUS_TOOLS set.
|
|
113
|
+
*/
|
|
114
|
+
function looksMutating(toolName) {
|
|
115
|
+
const bareName = stripMcpNamespace(toolName);
|
|
116
|
+
return MUTATING_PATTERNS.some((re) => re.test(bareName));
|
|
117
|
+
}
|
|
118
|
+
const emptyPolicy = () => ({ allow: [], deny: [], ask: [] });
|
|
119
|
+
exports.emptyPolicy = emptyPolicy;
|
|
120
|
+
/**
|
|
121
|
+
* Merge two policies. Used to combine workspace settings with session
|
|
122
|
+
* overrides (e.g. the user's "always allow for this session" choices).
|
|
123
|
+
*/
|
|
124
|
+
function mergePolicies(a, b) {
|
|
125
|
+
return {
|
|
126
|
+
allow: [...new Set([...a.allow, ...b.allow])],
|
|
127
|
+
deny: [...new Set([...a.deny, ...b.deny])],
|
|
128
|
+
ask: [...new Set([...a.ask, ...b.ask])]
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Evaluate the policy for a tool invocation. Returns the decision.
|
|
133
|
+
*
|
|
134
|
+
* @param toolName Tool being invoked (e.g. "write_file")
|
|
135
|
+
* @param primary The first meaningful param value (path, cmd, url…)
|
|
136
|
+
* used for fine-grained pattern matching. Empty
|
|
137
|
+
* string if unknown.
|
|
138
|
+
* @param policy Merged workspace + session policy.
|
|
139
|
+
* @param primaryFull Optional fuller representation of the invocation
|
|
140
|
+
* used for pattern matching alongside `primary`.
|
|
141
|
+
* For `run_command` the host should pass the full
|
|
142
|
+
* command line ("git push origin main") so glob
|
|
143
|
+
* patterns like `run_command:git *` and
|
|
144
|
+
* `run_command:rm *` work intuitively. When
|
|
145
|
+
* omitted (default) only `primary` is consulted —
|
|
146
|
+
* preserves the original semantics for callers
|
|
147
|
+
* that don't need the wider match.
|
|
148
|
+
*/
|
|
149
|
+
function evaluatePermission(toolName, primary, policy, primaryFull) {
|
|
150
|
+
// Deny has highest precedence — explicit deny wins over explicit allow.
|
|
151
|
+
if (matchesAny(toolName, primary, policy.deny, primaryFull))
|
|
152
|
+
return 'deny';
|
|
153
|
+
if (matchesAny(toolName, primary, policy.allow, primaryFull))
|
|
154
|
+
return 'allow';
|
|
155
|
+
if (matchesAny(toolName, primary, policy.ask, primaryFull))
|
|
156
|
+
return 'ask';
|
|
157
|
+
// Default: dangerous tools require explicit permission, read-only are OK.
|
|
158
|
+
// MCP-bridged tools whose names imply they mutate external state
|
|
159
|
+
// (createFilter, trashMessage, modifyMessageLabels, sendEmail, etc.)
|
|
160
|
+
// also default to 'ask' — without this, every MCP tool gets auto-
|
|
161
|
+
// allowed and the user has no chance to stop the agent before it
|
|
162
|
+
// archives 200 emails or creates a forwarding rule.
|
|
163
|
+
if (DANGEROUS_TOOLS.has(toolName))
|
|
164
|
+
return 'ask';
|
|
165
|
+
if (looksMutating(toolName))
|
|
166
|
+
return 'ask';
|
|
167
|
+
return 'allow';
|
|
168
|
+
}
|
|
169
|
+
function matchesAny(toolName, primary, patterns, primaryFull) {
|
|
170
|
+
for (const raw of patterns) {
|
|
171
|
+
const pattern = raw.trim();
|
|
172
|
+
if (!pattern)
|
|
173
|
+
continue;
|
|
174
|
+
const colon = pattern.indexOf(':');
|
|
175
|
+
if (colon === -1) {
|
|
176
|
+
// Tool-only pattern: matches every invocation of that tool.
|
|
177
|
+
if (pattern === toolName)
|
|
178
|
+
return true;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const toolPart = pattern.slice(0, colon);
|
|
182
|
+
const argPart = pattern.slice(colon + 1);
|
|
183
|
+
if (toolPart !== toolName)
|
|
184
|
+
continue;
|
|
185
|
+
// Try the wider form first when provided — this is what makes
|
|
186
|
+
// patterns like `run_command:git *` and `run_command:rm *` work
|
|
187
|
+
// (matching against "git push origin main", not just "git"). Fall
|
|
188
|
+
// back to the narrow primary for grants stored at binary-only
|
|
189
|
+
// scope and for non-run_command tools where primaryFull is
|
|
190
|
+
// identical to primary.
|
|
191
|
+
if (primaryFull && globMatch(argPart, primaryFull))
|
|
192
|
+
return true;
|
|
193
|
+
if (globMatch(argPart, primary))
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Permission-pattern glob matcher.
|
|
200
|
+
*
|
|
201
|
+
* Unlike path-glob matchers, this one treats `*` as greedy (matching slashes
|
|
202
|
+
* too) because permission patterns target heterogeneous inputs (commands,
|
|
203
|
+
* URLs, paths) and users don't expect `/` to be segmenting. Keep `**` as an
|
|
204
|
+
* alias for `*` for user convenience so both "write_file:src/**" and
|
|
205
|
+
* "run_command:rm *" behave intuitively.
|
|
206
|
+
*/
|
|
207
|
+
function globMatch(pattern, input) {
|
|
208
|
+
const regex = globToRegex(pattern);
|
|
209
|
+
return regex.test(input);
|
|
210
|
+
}
|
|
211
|
+
function globToRegex(glob) {
|
|
212
|
+
let out = '^';
|
|
213
|
+
for (let i = 0; i < glob.length; i++) {
|
|
214
|
+
const ch = glob[i];
|
|
215
|
+
if (ch === '*') {
|
|
216
|
+
if (glob[i + 1] === '*') {
|
|
217
|
+
i++;
|
|
218
|
+
}
|
|
219
|
+
out += '.*';
|
|
220
|
+
}
|
|
221
|
+
else if (ch === '?') {
|
|
222
|
+
out += '.';
|
|
223
|
+
}
|
|
224
|
+
else if (ch === '{') {
|
|
225
|
+
const end = glob.indexOf('}', i);
|
|
226
|
+
if (end === -1) {
|
|
227
|
+
out += '\\{';
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const opts = glob.slice(i + 1, end).split(',').map(escapeRegex).join('|');
|
|
231
|
+
out += `(?:${opts})`;
|
|
232
|
+
i = end;
|
|
233
|
+
}
|
|
234
|
+
else if (/[.+^$()|\\]/.test(ch)) {
|
|
235
|
+
out += '\\' + ch;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
out += ch;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
out += '$';
|
|
242
|
+
return new RegExp(out);
|
|
243
|
+
}
|
|
244
|
+
function escapeRegex(s) {
|
|
245
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* In-memory store for session-level "always allow" choices. Hosts instantiate
|
|
249
|
+
* one per conversation so approvals reset when the user starts fresh.
|
|
250
|
+
*/
|
|
251
|
+
class SessionPermissionStore {
|
|
252
|
+
constructor() {
|
|
253
|
+
this.allow = new Set();
|
|
254
|
+
}
|
|
255
|
+
/** Remember an "always allow for this session" choice. */
|
|
256
|
+
grant(toolName, primary) {
|
|
257
|
+
this.allow.add(primary ? `${toolName}:${primary}` : toolName);
|
|
258
|
+
}
|
|
259
|
+
/** Returns a policy fragment reflecting session grants only. */
|
|
260
|
+
toPolicy() {
|
|
261
|
+
return { allow: [...this.allow], deny: [], ask: [] };
|
|
262
|
+
}
|
|
263
|
+
clear() {
|
|
264
|
+
this.allow.clear();
|
|
265
|
+
}
|
|
266
|
+
size() {
|
|
267
|
+
return this.allow.size;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
exports.SessionPermissionStore = SessionPermissionStore;
|
|
271
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extra tools layered onto the core ToolRegistry for the CLI host:
|
|
3
|
+
* - todo_write: in-agent todo tracking, persisted in-memory for the session
|
|
4
|
+
* - web_fetch: GET a URL and return a trimmed text body
|
|
5
|
+
* - web_search: query a search API (Tavily), return ranked snippets
|
|
6
|
+
*/
|
|
7
|
+
import type { AgentTool } from '@burtson-labs/agent-core';
|
|
8
|
+
interface TodoItem {
|
|
9
|
+
id: number;
|
|
10
|
+
status: 'pending' | 'in_progress' | 'done';
|
|
11
|
+
content: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class TodoStore {
|
|
14
|
+
private items;
|
|
15
|
+
private nextId;
|
|
16
|
+
snapshot(): TodoItem[];
|
|
17
|
+
render(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Accepts a JSON array of { content, status? } to replace the list, or a
|
|
20
|
+
* single string 'content' for append. Simple to be forgiving of small models.
|
|
21
|
+
*/
|
|
22
|
+
upsert(raw: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* A short progress summary the tool returns so the model reads a
|
|
25
|
+
* clear "update succeeded, here's what's left" signal instead of
|
|
26
|
+
* interpreting the echoed list as a reset. The trailing nudge is
|
|
27
|
+
* deliberately blunt — bandit-core-1 and similar small models have
|
|
28
|
+
* been observed to claim completion without invoking the write
|
|
29
|
+
* tool, and this helper is the most direct place to remind the
|
|
30
|
+
* model of the expectation between tool calls.
|
|
31
|
+
*/
|
|
32
|
+
summary(): string;
|
|
33
|
+
}
|
|
34
|
+
export declare function buildTodoWriteTool(store: TodoStore): AgentTool;
|
|
35
|
+
/**
|
|
36
|
+
* `remember` tool — persists a single fact to the workspace's BANDIT.md
|
|
37
|
+
* so it survives across sessions. Use case: the user says "remember
|
|
38
|
+
* that all my repos live in ~/Documents/GitHub" and wants the next
|
|
39
|
+
* Bandit session to know that without being re-told.
|
|
40
|
+
*
|
|
41
|
+
* Why a dedicated tool instead of asking the model to apply_edit
|
|
42
|
+
* BANDIT.md directly: small models (gemma4:e4b, qwen 4B) hallucinated
|
|
43
|
+
* the existing file contents when invited to edit it, and even on
|
|
44
|
+
* larger models the apply_edit dance for "append a bullet" was
|
|
45
|
+
* comically inefficient (4-5 turns minimum). This is one tool call
|
|
46
|
+
* with a single string parameter — same shape as todo_write.
|
|
47
|
+
*
|
|
48
|
+
* on the CLI: user said "you should add to your
|
|
49
|
+
* memory where my repos are", model used `todo_write` thinking it was
|
|
50
|
+
* the persistence mechanism, nothing actually landed on disk and the
|
|
51
|
+
* next session knew nothing.
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildRememberTool(): AgentTool;
|
|
54
|
+
export declare function isPrivateHost(hostname: string): Promise<boolean>;
|
|
55
|
+
export declare function buildWebFetchTool(): AgentTool;
|
|
56
|
+
export interface WebSearchToolOptions {
|
|
57
|
+
/** Tavily API key. Falls back to env var TAVILY_API_KEY when omitted.
|
|
58
|
+
* When neither is set, the tool returns a configuration error
|
|
59
|
+
* (model can fall back to web_fetch with a known URL). */
|
|
60
|
+
apiKey?: string;
|
|
61
|
+
/** Override the search endpoint. Default: https://api.tavily.com/search */
|
|
62
|
+
endpoint?: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Build a `web_search` tool backed by Tavily (purpose-built for LLM
|
|
66
|
+
* agents — returns ranked snippets, not raw HTML). When TAVILY_API_KEY
|
|
67
|
+
* is unset the tool returns a clear configuration error so the model
|
|
68
|
+
* can fall back to other tools (web_fetch with a known URL, ask the
|
|
69
|
+
* user, etc.) instead of hallucinating results.
|
|
70
|
+
*
|
|
71
|
+
* Why Tavily over scraping or Google CSE:
|
|
72
|
+
* - Generous free tier (1k req/mo at time of writing)
|
|
73
|
+
* - Single API call returns ranked snippets ready for the model
|
|
74
|
+
* - No CAPTCHA / rate-limit roulette like search-result scraping
|
|
75
|
+
* - Optional `answer` field gives the model an LLM-summarized answer
|
|
76
|
+
* directly when one fits the query
|
|
77
|
+
*/
|
|
78
|
+
export declare function buildWebSearchTool(options?: WebSearchToolOptions): AgentTool;
|
|
79
|
+
export {};
|
|
80
|
+
//# sourceMappingURL=extraTools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extraTools.d.ts","sourceRoot":"","sources":["../../src/tools/extraTools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAoC,MAAM,0BAA0B,CAAC;AAG5F,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC;CACjB;AAiBD,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,MAAM,CAAK;IAEnB,QAAQ,IAAI,QAAQ,EAAE;IAItB,MAAM,IAAI,MAAM;IAUhB;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IA6D3B;;;;;;;;OAQG;IACH,OAAO,IAAI,MAAM;CAiBlB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CA8C9D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,IAAI,SAAS,CAmB7C;AAuCD,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBtE;AAED,wBAAgB,iBAAiB,IAAI,SAAS,CA4C7C;AAgBD,MAAM,WAAW,oBAAoB;IACnC;;8DAE0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAeD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,oBAAyB,GAAG,SAAS,CAsFhF"}
|