@burtson-labs/agent-core 1.6.13
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 +88 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/activation.d.ts +60 -0
- package/dist/mcp/activation.d.ts.map +1 -0
- package/dist/mcp/activation.js +139 -0
- package/dist/mcp/activation.js.map +1 -0
- package/dist/mcp/clientPool.d.ts +202 -0
- package/dist/mcp/clientPool.d.ts.map +1 -0
- package/dist/mcp/clientPool.js +469 -0
- package/dist/mcp/clientPool.js.map +1 -0
- package/dist/mcp/index.d.ts +18 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +28 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +43 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +130 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/toolAdapter.d.ts +57 -0
- package/dist/mcp/toolAdapter.d.ts.map +1 -0
- package/dist/mcp/toolAdapter.js +223 -0
- package/dist/mcp/toolAdapter.js.map +1 -0
- package/dist/mcp/types.d.ts +122 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +15 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/providers/deterministic-provider.d.ts +21 -0
- package/dist/providers/deterministic-provider.d.ts.map +1 -0
- package/dist/providers/deterministic-provider.js +80 -0
- package/dist/providers/deterministic-provider.js.map +1 -0
- package/dist/providers/provider-client.d.ts +12 -0
- package/dist/providers/provider-client.d.ts.map +1 -0
- package/dist/providers/provider-client.js +11 -0
- package/dist/providers/provider-client.js.map +1 -0
- package/dist/runtime/AgentRuntime.d.ts +67 -0
- package/dist/runtime/AgentRuntime.d.ts.map +1 -0
- package/dist/runtime/AgentRuntime.js +382 -0
- package/dist/runtime/AgentRuntime.js.map +1 -0
- package/dist/security/secretPatterns.d.ts +76 -0
- package/dist/security/secretPatterns.d.ts.map +1 -0
- package/dist/security/secretPatterns.js +290 -0
- package/dist/security/secretPatterns.js.map +1 -0
- package/dist/tools/ask-user-tool.d.ts +19 -0
- package/dist/tools/ask-user-tool.d.ts.map +1 -0
- package/dist/tools/ask-user-tool.js +148 -0
- package/dist/tools/ask-user-tool.js.map +1 -0
- package/dist/tools/compactMessages.d.ts +52 -0
- package/dist/tools/compactMessages.d.ts.map +1 -0
- package/dist/tools/compactMessages.js +158 -0
- package/dist/tools/compactMessages.js.map +1 -0
- package/dist/tools/core-tools.d.ts +29 -0
- package/dist/tools/core-tools.d.ts.map +1 -0
- package/dist/tools/core-tools.js +2214 -0
- package/dist/tools/core-tools.js.map +1 -0
- package/dist/tools/git-tools.d.ts +32 -0
- package/dist/tools/git-tools.d.ts.map +1 -0
- package/dist/tools/git-tools.js +330 -0
- package/dist/tools/git-tools.js.map +1 -0
- package/dist/tools/index.d.ts +15 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +31 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/language-adapters.d.ts +48 -0
- package/dist/tools/language-adapters.d.ts.map +1 -0
- package/dist/tools/language-adapters.js +299 -0
- package/dist/tools/language-adapters.js.map +1 -0
- package/dist/tools/loop/compactionTrigger.d.ts +47 -0
- package/dist/tools/loop/compactionTrigger.d.ts.map +1 -0
- package/dist/tools/loop/compactionTrigger.js +32 -0
- package/dist/tools/loop/compactionTrigger.js.map +1 -0
- package/dist/tools/loop/finalAnswerNudges.d.ts +68 -0
- package/dist/tools/loop/finalAnswerNudges.d.ts.map +1 -0
- package/dist/tools/loop/finalAnswerNudges.js +87 -0
- package/dist/tools/loop/finalAnswerNudges.js.map +1 -0
- package/dist/tools/loop/goalAnchor.d.ts +72 -0
- package/dist/tools/loop/goalAnchor.d.ts.map +1 -0
- package/dist/tools/loop/goalAnchor.js +76 -0
- package/dist/tools/loop/goalAnchor.js.map +1 -0
- package/dist/tools/loop/llmStream.d.ts +70 -0
- package/dist/tools/loop/llmStream.d.ts.map +1 -0
- package/dist/tools/loop/llmStream.js +181 -0
- package/dist/tools/loop/llmStream.js.map +1 -0
- package/dist/tools/loop/parallelExecute.d.ts +57 -0
- package/dist/tools/loop/parallelExecute.d.ts.map +1 -0
- package/dist/tools/loop/parallelExecute.js +54 -0
- package/dist/tools/loop/parallelExecute.js.map +1 -0
- package/dist/tools/loop/singleToolExecute.d.ts +71 -0
- package/dist/tools/loop/singleToolExecute.d.ts.map +1 -0
- package/dist/tools/loop/singleToolExecute.js +139 -0
- package/dist/tools/loop/singleToolExecute.js.map +1 -0
- package/dist/tools/loop/toolCallNormalize.d.ts +57 -0
- package/dist/tools/loop/toolCallNormalize.d.ts.map +1 -0
- package/dist/tools/loop/toolCallNormalize.js +99 -0
- package/dist/tools/loop/toolCallNormalize.js.map +1 -0
- package/dist/tools/loop/turnSetup.d.ts +43 -0
- package/dist/tools/loop/turnSetup.d.ts.map +1 -0
- package/dist/tools/loop/turnSetup.js +48 -0
- package/dist/tools/loop/turnSetup.js.map +1 -0
- package/dist/tools/ocr.d.ts +52 -0
- package/dist/tools/ocr.d.ts.map +1 -0
- package/dist/tools/ocr.js +238 -0
- package/dist/tools/ocr.js.map +1 -0
- package/dist/tools/post-edit-checks.d.ts +46 -0
- package/dist/tools/post-edit-checks.d.ts.map +1 -0
- package/dist/tools/post-edit-checks.js +236 -0
- package/dist/tools/post-edit-checks.js.map +1 -0
- package/dist/tools/skill-loader.d.ts +94 -0
- package/dist/tools/skill-loader.d.ts.map +1 -0
- package/dist/tools/skill-loader.js +422 -0
- package/dist/tools/skill-loader.js.map +1 -0
- package/dist/tools/skill-registry.d.ts +44 -0
- package/dist/tools/skill-registry.d.ts.map +1 -0
- package/dist/tools/skill-registry.js +118 -0
- package/dist/tools/skill-registry.js.map +1 -0
- package/dist/tools/skill-types.d.ts +38 -0
- package/dist/tools/skill-types.d.ts.map +1 -0
- package/dist/tools/skill-types.js +10 -0
- package/dist/tools/skill-types.js.map +1 -0
- package/dist/tools/skills/code-review-skill.d.ts +9 -0
- package/dist/tools/skills/code-review-skill.d.ts.map +1 -0
- package/dist/tools/skills/code-review-skill.js +66 -0
- package/dist/tools/skills/code-review-skill.js.map +1 -0
- package/dist/tools/skills/core-skill.d.ts +13 -0
- package/dist/tools/skills/core-skill.d.ts.map +1 -0
- package/dist/tools/skills/core-skill.js +23 -0
- package/dist/tools/skills/core-skill.js.map +1 -0
- package/dist/tools/skills/git-skill.d.ts +10 -0
- package/dist/tools/skills/git-skill.d.ts.map +1 -0
- package/dist/tools/skills/git-skill.js +30 -0
- package/dist/tools/skills/git-skill.js.map +1 -0
- package/dist/tools/skills/index.d.ts +17 -0
- package/dist/tools/skills/index.d.ts.map +1 -0
- package/dist/tools/skills/index.js +49 -0
- package/dist/tools/skills/index.js.map +1 -0
- package/dist/tools/skills/interaction-skill.d.ts +14 -0
- package/dist/tools/skills/interaction-skill.d.ts.map +1 -0
- package/dist/tools/skills/interaction-skill.js +24 -0
- package/dist/tools/skills/interaction-skill.js.map +1 -0
- package/dist/tools/skills/mail-search-skill.d.ts +25 -0
- package/dist/tools/skills/mail-search-skill.d.ts.map +1 -0
- package/dist/tools/skills/mail-search-skill.js +343 -0
- package/dist/tools/skills/mail-search-skill.js.map +1 -0
- package/dist/tools/skills/plan-skill.d.ts +10 -0
- package/dist/tools/skills/plan-skill.d.ts.map +1 -0
- package/dist/tools/skills/plan-skill.js +126 -0
- package/dist/tools/skills/plan-skill.js.map +1 -0
- package/dist/tools/skills/semantic-search-skill.d.ts +22 -0
- package/dist/tools/skills/semantic-search-skill.d.ts.map +1 -0
- package/dist/tools/skills/semantic-search-skill.js +244 -0
- package/dist/tools/skills/semantic-search-skill.js.map +1 -0
- package/dist/tools/skills/test-gen-skill.d.ts +9 -0
- package/dist/tools/skills/test-gen-skill.d.ts.map +1 -0
- package/dist/tools/skills/test-gen-skill.js +123 -0
- package/dist/tools/skills/test-gen-skill.js.map +1 -0
- package/dist/tools/tool-registry.d.ts +60 -0
- package/dist/tools/tool-registry.d.ts.map +1 -0
- package/dist/tools/tool-registry.js +200 -0
- package/dist/tools/tool-registry.js.map +1 -0
- package/dist/tools/tool-types.d.ts +281 -0
- package/dist/tools/tool-types.d.ts.map +1 -0
- package/dist/tools/tool-types.js +10 -0
- package/dist/tools/tool-types.js.map +1 -0
- package/dist/tools/tool-use-loop.d.ts +231 -0
- package/dist/tools/tool-use-loop.d.ts.map +1 -0
- package/dist/tools/tool-use-loop.js +2057 -0
- package/dist/tools/tool-use-loop.js.map +1 -0
- package/dist/tools/tool-use-parser.d.ts +78 -0
- package/dist/tools/tool-use-parser.d.ts.map +1 -0
- package/dist/tools/tool-use-parser.js +427 -0
- package/dist/tools/tool-use-parser.js.map +1 -0
- package/dist/tools/toolAvailabilityDetector.d.ts +48 -0
- package/dist/tools/toolAvailabilityDetector.d.ts.map +1 -0
- package/dist/tools/toolAvailabilityDetector.js +156 -0
- package/dist/tools/toolAvailabilityDetector.js.map +1 -0
- package/dist/tools/unified-patch.d.ts +87 -0
- package/dist/tools/unified-patch.d.ts.map +1 -0
- package/dist/tools/unified-patch.js +217 -0
- package/dist/tools/unified-patch.js.map +1 -0
- package/dist/types/agent.d.ts +69 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +54 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/tasks.d.ts +22 -0
- package/dist/types/tasks.d.ts.map +1 -0
- package/dist/types/tasks.js +3 -0
- package/dist/types/tasks.js.map +1 -0
- package/dist/utils/event-emitter.d.ts +13 -0
- package/dist/utils/event-emitter.d.ts.map +1 -0
- package/dist/utils/event-emitter.js +54 -0
- package/dist/utils/event-emitter.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Secret-leak protection. Tool output (file reads, command
|
|
4
|
+
* stdout, search results) routinely surfaces API keys, OAuth tokens,
|
|
5
|
+
* and private keys when the user asks the agent to inspect dotfiles,
|
|
6
|
+
* environment variables, or anything else that touches credentials.
|
|
7
|
+
* Without redaction, those tokens land in:
|
|
8
|
+
*
|
|
9
|
+
* 1. The model's context window → the model may echo them in its
|
|
10
|
+
* next response, embed them in a tool call, or hallucinate
|
|
11
|
+
* surrounding prose that quotes them verbatim
|
|
12
|
+
* 2. The user's terminal → secrets sit in the scrollback,
|
|
13
|
+
* visible to over-shoulder readers and any screen-sharing flow
|
|
14
|
+
* 3. The session log on disk → ~/.bandit/sessions/*.jsonl
|
|
15
|
+
* persists raw secrets indefinitely, becoming a target if
|
|
16
|
+
* that file is ever shared (bug reports, etc.)
|
|
17
|
+
*
|
|
18
|
+
* The redactor is the single source of truth for "what looks like a
|
|
19
|
+
* secret." Patterns target high-confidence formats (known prefixes,
|
|
20
|
+
* stable lengths) — we deliberately do NOT redact generic
|
|
21
|
+
* high-entropy strings because false positives are corrosive (the
|
|
22
|
+
* agent loses the ability to reason about real data that happens to
|
|
23
|
+
* look secret-shaped).
|
|
24
|
+
*
|
|
25
|
+
* Each pattern has a `kind` label so the redaction replaces the match
|
|
26
|
+
* with a typed token like `<REDACTED:github-pat>` — preserves enough
|
|
27
|
+
* structure for the model (and the user) to understand what kind of
|
|
28
|
+
* thing was hidden without leaking the value.
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.BUILTIN_SECRET_PATTERNS = void 0;
|
|
32
|
+
exports.redactSecrets = redactSecrets;
|
|
33
|
+
exports.redactSecretsString = redactSecretsString;
|
|
34
|
+
/**
|
|
35
|
+
* Built-in secret patterns. Each entry is a high-confidence format with
|
|
36
|
+
* either a recognized prefix or a strict length/charset constraint.
|
|
37
|
+
*
|
|
38
|
+
* Order matters: longer / more-specific patterns must match BEFORE
|
|
39
|
+
* shorter / more-generic ones, otherwise a fine-grained PAT would be
|
|
40
|
+
* partially matched by the classic-PAT pattern. We rely on the regex
|
|
41
|
+
* engine matching in source order via the array iteration below.
|
|
42
|
+
*/
|
|
43
|
+
exports.BUILTIN_SECRET_PATTERNS = [
|
|
44
|
+
// ─── GitHub ───────────────────────────────────────────────────────
|
|
45
|
+
// Fine-grained PATs are LONG (82 char body) — listed first so the
|
|
46
|
+
// classic-PAT pattern doesn't truncate them.
|
|
47
|
+
{
|
|
48
|
+
kind: 'github-pat-fine-grained',
|
|
49
|
+
label: 'GitHub fine-grained PAT',
|
|
50
|
+
re: /\bgithub_pat_[A-Za-z0-9_]{82}\b/g
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
kind: 'github-pat',
|
|
54
|
+
label: 'GitHub classic PAT',
|
|
55
|
+
re: /\bghp_[A-Za-z0-9]{36,251}\b/g
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
kind: 'github-oauth',
|
|
59
|
+
label: 'GitHub OAuth token',
|
|
60
|
+
re: /\bgho_[A-Za-z0-9]{36,251}\b/g
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
kind: 'github-server-token',
|
|
64
|
+
label: 'GitHub server-to-server token',
|
|
65
|
+
re: /\bghs_[A-Za-z0-9]{36,251}\b/g
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
kind: 'github-user-token',
|
|
69
|
+
label: 'GitHub user-to-server token',
|
|
70
|
+
re: /\bghu_[A-Za-z0-9]{36,251}\b/g
|
|
71
|
+
},
|
|
72
|
+
// ─── Slack ────────────────────────────────────────────────────────
|
|
73
|
+
{
|
|
74
|
+
kind: 'slack-bot-token',
|
|
75
|
+
label: 'Slack bot token',
|
|
76
|
+
re: /\bxoxb-[A-Za-z0-9-]+\b/g
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
kind: 'slack-user-token',
|
|
80
|
+
label: 'Slack user token',
|
|
81
|
+
re: /\bxoxp-[A-Za-z0-9-]+\b/g
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
kind: 'slack-app-token',
|
|
85
|
+
label: 'Slack app token',
|
|
86
|
+
re: /\bxoxa-[A-Za-z0-9-]+\b/g
|
|
87
|
+
},
|
|
88
|
+
// ─── GitLab ───────────────────────────────────────────────────────
|
|
89
|
+
{
|
|
90
|
+
kind: 'gitlab-pat',
|
|
91
|
+
label: 'GitLab personal access token',
|
|
92
|
+
re: /\bglpat-[A-Za-z0-9_-]{20,}\b/g
|
|
93
|
+
},
|
|
94
|
+
// ─── AWS ──────────────────────────────────────────────────────────
|
|
95
|
+
{
|
|
96
|
+
kind: 'aws-access-key',
|
|
97
|
+
label: 'AWS access key',
|
|
98
|
+
re: /\b(?:AKIA|ASIA|AROA|AGPA|AIPA)[0-9A-Z]{16}\b/g
|
|
99
|
+
},
|
|
100
|
+
// ─── Anthropic / OpenAI / Bandit ──────────────────────────────────
|
|
101
|
+
{
|
|
102
|
+
kind: 'anthropic-key',
|
|
103
|
+
label: 'Anthropic API key',
|
|
104
|
+
re: /\bsk-ant-[A-Za-z0-9_-]{20,}\b/g
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
kind: 'openai-key',
|
|
108
|
+
label: 'OpenAI API key',
|
|
109
|
+
re: /\bsk-(?!ant-)[A-Za-z0-9_-]{20,}\b/g
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
kind: 'bandit-api-key',
|
|
113
|
+
label: 'Bandit / Burtson Labs API key',
|
|
114
|
+
re: /\bbai_[A-Za-z0-9]{20,}\b/g
|
|
115
|
+
},
|
|
116
|
+
// ─── Google ───────────────────────────────────────────────────────
|
|
117
|
+
// Google API keys start with "AIza" + 35 chars (39 total).
|
|
118
|
+
{
|
|
119
|
+
kind: 'google-api-key',
|
|
120
|
+
label: 'Google API key',
|
|
121
|
+
re: /\bAIza[A-Za-z0-9_-]{35}\b/g
|
|
122
|
+
},
|
|
123
|
+
// OAuth refresh tokens are "1//" + base64-like body.
|
|
124
|
+
{
|
|
125
|
+
kind: 'google-oauth-refresh',
|
|
126
|
+
label: 'Google OAuth refresh token',
|
|
127
|
+
re: /\b1\/\/[A-Za-z0-9_-]{43,}\b/g
|
|
128
|
+
},
|
|
129
|
+
// ─── Stripe ───────────────────────────────────────────────────────
|
|
130
|
+
{
|
|
131
|
+
kind: 'stripe-secret-key',
|
|
132
|
+
label: 'Stripe secret key',
|
|
133
|
+
re: /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{24,}\b/g
|
|
134
|
+
},
|
|
135
|
+
// ─── JWTs (generic, three base64-url segments) ────────────────────
|
|
136
|
+
// Any token shaped `eyJ<header>.<payload>.<signature>` — covers JWTs
|
|
137
|
+
// from any issuer. Catches our own AuthApi tokens, Auth0, Google ID
|
|
138
|
+
// tokens, etc. Long enough that the entropy filter doesn't hurt.
|
|
139
|
+
{
|
|
140
|
+
kind: 'jwt',
|
|
141
|
+
label: 'JWT token',
|
|
142
|
+
re: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g
|
|
143
|
+
},
|
|
144
|
+
// ─── Private keys (PEM blocks) ────────────────────────────────────
|
|
145
|
+
// PEM-armored RSA / ECDSA / OpenSSH / generic private keys. The
|
|
146
|
+
// multi-line matcher captures the entire BEGIN...END block.
|
|
147
|
+
{
|
|
148
|
+
kind: 'private-key',
|
|
149
|
+
label: 'PEM private key',
|
|
150
|
+
re: /-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP |ENCRYPTED )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |DSA |PGP |ENCRYPTED )?PRIVATE KEY-----/g
|
|
151
|
+
},
|
|
152
|
+
// ─── HTTP Authorization headers ───────────────────────────────────
|
|
153
|
+
// Catches "Authorization: Bearer <token>" / Token / Basic shapes that
|
|
154
|
+
// leak through curl outputs, request logs, fetch() debugging, etc.
|
|
155
|
+
// Preserves the header name + scheme so the model can still reason
|
|
156
|
+
// about WHAT auth was attempted, just not the token value.
|
|
157
|
+
//
|
|
158
|
+
// Caps the token character class at non-quote / non-whitespace /
|
|
159
|
+
// non-angle-bracket so this pattern can't re-match an already-redacted
|
|
160
|
+
// placeholder like "<REDACTED:jwt>".
|
|
161
|
+
{
|
|
162
|
+
kind: 'authorization-bearer',
|
|
163
|
+
label: 'HTTP Authorization header token',
|
|
164
|
+
// Backreferences \1 and \3 balance quotes around the key name and
|
|
165
|
+
// the value so both `Authorization: Bearer foo` and the JSON form
|
|
166
|
+
// `"Authorization": "Bearer foo"` match without grabbing stray quotes.
|
|
167
|
+
re: /(["']?)Authorization\1(\s*[:=]\s*)(["']?)(Bearer|Token|Basic)\s+([^\s"'<>]{16,})\3/gi
|
|
168
|
+
},
|
|
169
|
+
// ─── JSON / JS-style camelCase secret fields ──────────────────────
|
|
170
|
+
// The env-secret pattern below only catches SHOUTY_CASE names. JSON
|
|
171
|
+
// config files use camelCase ("apiKey", "accessToken", "clientSecret"),
|
|
172
|
+
// so the env pattern misses them entirely. ~/.bandit/config.json
|
|
173
|
+
// ({"bandit": {"apiKey": "..."}}) was leaking because of exactly this
|
|
174
|
+
// gap (2026-05-26).
|
|
175
|
+
//
|
|
176
|
+
// Matches: any quoted-or-bare camelCase identifier that ends in
|
|
177
|
+
// Key|Token|Secret|Password|Credentials, OR standalone "password" /
|
|
178
|
+
// "passwd" / "secret". Value must be a quoted string of 8+ chars.
|
|
179
|
+
// The value char class excludes <, > so it can't re-match a prior
|
|
180
|
+
// pattern's placeholder.
|
|
181
|
+
//
|
|
182
|
+
// Preserves: opening key quote, key name, closing key quote,
|
|
183
|
+
// separator (`: ` or `=`), opening value quote, closing value quote.
|
|
184
|
+
// Replaces: only the value's content.
|
|
185
|
+
{
|
|
186
|
+
kind: 'json-camelcase-secret',
|
|
187
|
+
label: 'camelCase config secret value',
|
|
188
|
+
re: /(["']?)([a-z][a-zA-Z0-9]*(?:Key|Token|Secret|Password|Credentials?)|password|passwd|secret)\1(\s*[:=]\s*)(["'])([^"'\s<>]{8,})\4/g
|
|
189
|
+
},
|
|
190
|
+
// ─── Generic env-style secret lines ───────────────────────────────
|
|
191
|
+
// Last-resort pattern: capture `*_TOKEN=<value>`, `*_KEY=<value>`,
|
|
192
|
+
// `*_SECRET=<value>`, `*_PASSWORD=<value>` style env lines where
|
|
193
|
+
// the value is non-empty and has at least 8 characters. Replaces
|
|
194
|
+
// only the value, not the variable name (preserves readability).
|
|
195
|
+
//
|
|
196
|
+
// The variable name is either a prefix + underscore + keyword
|
|
197
|
+
// (`STRIPE_SECRET_KEY`, `GITHUB_TOKEN`) OR just the keyword on its
|
|
198
|
+
// own (`PASSWORD=`, `TOKEN=`, `API_KEY=`). The optional non-capturing
|
|
199
|
+
// group `(?:[A-Z][A-Z0-9_]*_)?` handles both cases — the trailing
|
|
200
|
+
// underscore forces a real prefix boundary so `DEBUG=...` (which has
|
|
201
|
+
// no keyword suffix) never matches.
|
|
202
|
+
//
|
|
203
|
+
// Quoted values supported via the back-referenced quote group.
|
|
204
|
+
{
|
|
205
|
+
kind: 'env-secret',
|
|
206
|
+
label: 'env-style secret value',
|
|
207
|
+
re: /\b((?:[A-Z][A-Z0-9_]*_)?(?:TOKEN|KEY|SECRET|PASSWORD|PASSWD|PWD|CREDENTIALS?|API|AUTH))\s*[:=]\s*(['"]?)([^\s'"]{8,})\2/g
|
|
208
|
+
}
|
|
209
|
+
];
|
|
210
|
+
/**
|
|
211
|
+
* Apply all built-in patterns to a string. Replaces each match with
|
|
212
|
+
* `<REDACTED:{kind}>`. The replacement is intentionally short so the
|
|
213
|
+
* model can still reason about the surrounding text without burning
|
|
214
|
+
* context on long placeholder tokens.
|
|
215
|
+
*
|
|
216
|
+
* The `env-secret` pattern is special: it preserves the variable name
|
|
217
|
+
* and only redacts the value. So `GITHUB_TOKEN=ghp_abc...` becomes
|
|
218
|
+
* `GITHUB_TOKEN=<REDACTED:env-secret>` — the model can still see WHAT
|
|
219
|
+
* variable was set without seeing its value. For all other patterns
|
|
220
|
+
* the whole match is replaced.
|
|
221
|
+
*/
|
|
222
|
+
function redactSecrets(text, patterns = exports.BUILTIN_SECRET_PATTERNS) {
|
|
223
|
+
if (!text || text.length === 0) {
|
|
224
|
+
return { text, redactionCount: 0, kinds: [] };
|
|
225
|
+
}
|
|
226
|
+
let working = text;
|
|
227
|
+
let redactionCount = 0;
|
|
228
|
+
const kinds = [];
|
|
229
|
+
const recordKind = (kind) => {
|
|
230
|
+
if (!kinds.includes(kind))
|
|
231
|
+
kinds.push(kind);
|
|
232
|
+
};
|
|
233
|
+
for (const pattern of patterns) {
|
|
234
|
+
// Reset lastIndex so the global regex starts at 0 every call.
|
|
235
|
+
pattern.re.lastIndex = 0;
|
|
236
|
+
let mutated = false;
|
|
237
|
+
if (pattern.kind === 'env-secret') {
|
|
238
|
+
// Preserve `VAR=` prefix, redact only the value.
|
|
239
|
+
working = working.replace(pattern.re, (_match, varName) => {
|
|
240
|
+
redactionCount++;
|
|
241
|
+
recordKind(pattern.kind);
|
|
242
|
+
mutated = true;
|
|
243
|
+
return `${varName}=<REDACTED:${pattern.kind}>`;
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
else if (pattern.kind === 'json-camelcase-secret') {
|
|
247
|
+
// Preserve quoted key + separator + value quotes; redact only the
|
|
248
|
+
// inner value. Groups: (1) keyQuote, (2) keyName, (3) separator,
|
|
249
|
+
// (4) valueQuote, (5) value.
|
|
250
|
+
working = working.replace(pattern.re, (_match, kq, keyName, sep, vq) => {
|
|
251
|
+
redactionCount++;
|
|
252
|
+
recordKind(pattern.kind);
|
|
253
|
+
mutated = true;
|
|
254
|
+
return `${kq}${keyName}${kq}${sep}${vq}<REDACTED:${pattern.kind}>${vq}`;
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
else if (pattern.kind === 'authorization-bearer') {
|
|
258
|
+
// Preserve quoted header name + separator + value quotes; redact
|
|
259
|
+
// only the token. Groups: (1) keyQuote, (2) separator, (3) valueQuote,
|
|
260
|
+
// (4) scheme, (5) token.
|
|
261
|
+
working = working.replace(pattern.re, (_match, kq, sep, vq, scheme) => {
|
|
262
|
+
redactionCount++;
|
|
263
|
+
recordKind(pattern.kind);
|
|
264
|
+
mutated = true;
|
|
265
|
+
return `${kq}Authorization${kq}${sep}${vq}${scheme} <REDACTED:${pattern.kind}>${vq}`;
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
working = working.replace(pattern.re, () => {
|
|
270
|
+
redactionCount++;
|
|
271
|
+
recordKind(pattern.kind);
|
|
272
|
+
mutated = true;
|
|
273
|
+
return `<REDACTED:${pattern.kind}>`;
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
// Defensive: regex with `g` flag carries state across calls. Reset
|
|
277
|
+
// again to guarantee no leftover lastIndex affects later callers.
|
|
278
|
+
if (mutated)
|
|
279
|
+
pattern.re.lastIndex = 0;
|
|
280
|
+
}
|
|
281
|
+
return { text: working, redactionCount, kinds };
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Convenience helper used by callers that don't need the metadata —
|
|
285
|
+
* just want the masked string. Equivalent to redactSecrets(text).text.
|
|
286
|
+
*/
|
|
287
|
+
function redactSecretsString(text) {
|
|
288
|
+
return redactSecrets(text).text;
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=secretPatterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secretPatterns.js","sourceRoot":"","sources":["../../src/security/secretPatterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ask_user` — pose one or more clarifying questions to the user and wait
|
|
3
|
+
* for their answer before continuing.
|
|
4
|
+
*
|
|
5
|
+
* Host-agnostic: the tool only formats the question payload and the answer
|
|
6
|
+
* summary. The actual interactive prompt is rendered by the host via
|
|
7
|
+
* `ToolExecutionContext.requestUserInput` (the CLI's ink form, the
|
|
8
|
+
* extension's webview card). When a host doesn't provide that callback the
|
|
9
|
+
* tool degrades to telling the model to ask in plain text, so it's always
|
|
10
|
+
* safe to register — though hosts typically only register the owning skill
|
|
11
|
+
* when they actually have an interactive surface (see interaction-skill).
|
|
12
|
+
*/
|
|
13
|
+
import type { AgentTool, UserInputQuestion } from './tool-types';
|
|
14
|
+
/** Parse the model-supplied `questions` JSON into validated question specs.
|
|
15
|
+
* Tolerant: accepts a single object (not wrapped in an array), string
|
|
16
|
+
* options, and `question`/`text`/`prompt` aliases. Assigns stable ids. */
|
|
17
|
+
export declare function parseAskUserQuestions(rawJson: string): UserInputQuestion[];
|
|
18
|
+
export declare const askUserTool: AgentTool;
|
|
19
|
+
//# sourceMappingURL=ask-user-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask-user-tool.d.ts","sourceRoot":"","sources":["../../src/tools/ask-user-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAyBjE;;2EAE2E;AAC3E,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,EAAE,CA4B1E;AAED,eAAO,MAAM,WAAW,EAAE,SAmFzB,CAAC"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `ask_user` — pose one or more clarifying questions to the user and wait
|
|
4
|
+
* for their answer before continuing.
|
|
5
|
+
*
|
|
6
|
+
* Host-agnostic: the tool only formats the question payload and the answer
|
|
7
|
+
* summary. The actual interactive prompt is rendered by the host via
|
|
8
|
+
* `ToolExecutionContext.requestUserInput` (the CLI's ink form, the
|
|
9
|
+
* extension's webview card). When a host doesn't provide that callback the
|
|
10
|
+
* tool degrades to telling the model to ask in plain text, so it's always
|
|
11
|
+
* safe to register — though hosts typically only register the owning skill
|
|
12
|
+
* when they actually have an interactive surface (see interaction-skill).
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.askUserTool = void 0;
|
|
16
|
+
exports.parseAskUserQuestions = parseAskUserQuestions;
|
|
17
|
+
const QUESTIONS_EXAMPLE = '[{"question":"Which database should we use?","header":"Database","options":' +
|
|
18
|
+
'[{"label":"Postgres","description":"Relational, strong consistency"},' +
|
|
19
|
+
'{"label":"MongoDB","description":"Flexible document store"}],"allowFreeform":true}]';
|
|
20
|
+
function coerceOption(raw) {
|
|
21
|
+
if (typeof raw === 'string')
|
|
22
|
+
return raw.trim() ? { label: raw.trim() } : null;
|
|
23
|
+
if (raw && typeof raw === 'object') {
|
|
24
|
+
const o = raw;
|
|
25
|
+
const label = typeof o.label === 'string' ? o.label
|
|
26
|
+
: typeof o.value === 'string' ? o.value
|
|
27
|
+
: typeof o.text === 'string' ? o.text : '';
|
|
28
|
+
if (!label.trim())
|
|
29
|
+
return null;
|
|
30
|
+
return {
|
|
31
|
+
label: label.trim(),
|
|
32
|
+
description: typeof o.description === 'string' ? o.description : undefined
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
/** Parse the model-supplied `questions` JSON into validated question specs.
|
|
38
|
+
* Tolerant: accepts a single object (not wrapped in an array), string
|
|
39
|
+
* options, and `question`/`text`/`prompt` aliases. Assigns stable ids. */
|
|
40
|
+
function parseAskUserQuestions(rawJson) {
|
|
41
|
+
let parsed;
|
|
42
|
+
try {
|
|
43
|
+
parsed = JSON.parse(rawJson);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
const list = Array.isArray(parsed) ? parsed : [parsed];
|
|
49
|
+
const questions = [];
|
|
50
|
+
list.forEach((raw, i) => {
|
|
51
|
+
if (!raw || typeof raw !== 'object')
|
|
52
|
+
return;
|
|
53
|
+
const r = raw;
|
|
54
|
+
const text = typeof r.question === 'string' ? r.question
|
|
55
|
+
: typeof r.text === 'string' ? r.text
|
|
56
|
+
: typeof r.prompt === 'string' ? r.prompt : '';
|
|
57
|
+
if (!text.trim())
|
|
58
|
+
return;
|
|
59
|
+
const options = (Array.isArray(r.options) ? r.options : [])
|
|
60
|
+
.map(coerceOption)
|
|
61
|
+
.filter((o) => o !== null);
|
|
62
|
+
questions.push({
|
|
63
|
+
id: typeof r.id === 'string' && r.id.trim() ? r.id.trim() : `q${i + 1}`,
|
|
64
|
+
question: text.trim(),
|
|
65
|
+
header: typeof r.header === 'string' && r.header.trim() ? r.header.trim() : undefined,
|
|
66
|
+
options: options.length > 0 ? options : undefined,
|
|
67
|
+
allowFreeform: r.allowFreeform === false ? false : true
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
return questions;
|
|
71
|
+
}
|
|
72
|
+
exports.askUserTool = {
|
|
73
|
+
name: 'ask_user',
|
|
74
|
+
description: 'Ask the user one or more clarifying questions and WAIT for their answer before continuing. ' +
|
|
75
|
+
'ALWAYS use this tool when you need a decision or direction from the user — do NOT ask in your prose ' +
|
|
76
|
+
'response and end the turn. A prose question is passive (the user must start a new turn); this renders ' +
|
|
77
|
+
'an interactive prompt they answer in one click. ' +
|
|
78
|
+
'Use only when you are genuinely blocked on a decision that is the user\'s to make and cannot be ' +
|
|
79
|
+
'resolved from the request, the code, or sensible defaults — not for routine choices you can make ' +
|
|
80
|
+
'yourself. Provide 1–4 questions, each with 2–4 suggested options (the user can also type their own ' +
|
|
81
|
+
'answer). When one option is the clear best choice, make it the FIRST option and append ' +
|
|
82
|
+
'" (Recommended)" to its label — it is pre-selected, so the user can accept it with one click. ' +
|
|
83
|
+
'The tool result contains the user\'s answers; act on them directly without re-asking.',
|
|
84
|
+
parameters: [
|
|
85
|
+
{
|
|
86
|
+
name: 'questions',
|
|
87
|
+
description: 'A JSON array of question objects. Each object: { "question": "the question text", ' +
|
|
88
|
+
'"header": "short tab label", "options": [{ "label": "...", "description": "optional context" }], ' +
|
|
89
|
+
'"allowFreeform": true }. Example: ' + QUESTIONS_EXAMPLE,
|
|
90
|
+
required: true,
|
|
91
|
+
schema: {
|
|
92
|
+
type: 'array',
|
|
93
|
+
items: {
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
question: { type: 'string', description: 'The question to ask the user.' },
|
|
97
|
+
header: { type: 'string', description: 'A short (≤12 char) label for the question tab.' },
|
|
98
|
+
options: {
|
|
99
|
+
type: 'array',
|
|
100
|
+
description: 'Suggested answers (2–4). If one is the clear best choice, list it first and append " (Recommended)" to its label.',
|
|
101
|
+
items: {
|
|
102
|
+
type: 'object',
|
|
103
|
+
properties: {
|
|
104
|
+
label: { type: 'string', description: 'The option text.' },
|
|
105
|
+
description: { type: 'string', description: 'Optional one-line explanation of the option.' }
|
|
106
|
+
},
|
|
107
|
+
required: ['label']
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
allowFreeform: { type: 'boolean', description: 'Allow a typed custom answer (default true).' }
|
|
111
|
+
},
|
|
112
|
+
required: ['question']
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
async execute(params, ctx) {
|
|
118
|
+
if (!ctx.requestUserInput) {
|
|
119
|
+
return {
|
|
120
|
+
output: 'Interactive questions are not available in this environment. Ask your question(s) in plain ' +
|
|
121
|
+
'text in your normal response and wait for the user to reply.',
|
|
122
|
+
isError: false
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const questions = parseAskUserQuestions(params.questions ?? '');
|
|
126
|
+
if (questions.length === 0) {
|
|
127
|
+
return {
|
|
128
|
+
output: 'ask_user failed: the `questions` parameter must be a JSON array of {question, options} ' +
|
|
129
|
+
'objects. Example: ' + QUESTIONS_EXAMPLE,
|
|
130
|
+
isError: true
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const res = await ctx.requestUserInput({ questions });
|
|
134
|
+
if (res.cancelled) {
|
|
135
|
+
return {
|
|
136
|
+
output: 'The user dismissed the question(s) without answering. Do not immediately re-ask — proceed ' +
|
|
137
|
+
'with your best judgment, or briefly explain what you need and let the user respond when ready.',
|
|
138
|
+
isError: false
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const lines = questions.map((q) => {
|
|
142
|
+
const a = res.answers[q.id];
|
|
143
|
+
return `Q: ${q.question}\nA: ${a !== undefined && a !== '' ? a : '(no answer)'}`;
|
|
144
|
+
});
|
|
145
|
+
return { output: 'The user answered:\n\n' + lines.join('\n\n'), isError: false };
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=ask-user-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask-user-tool.js","sourceRoot":"","sources":["../../src/tools/ask-user-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message-history compaction for the tool-use loop.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists:
|
|
5
|
+
* The loop appends every tool result as a `user` message carrying
|
|
6
|
+
* the raw output (read_file content, search_code hits, command
|
|
7
|
+
* stdout). After ~6 iterations on a medium-sized codebase the
|
|
8
|
+
* cumulative tool output is ~12-18k tokens — enough to push past
|
|
9
|
+
* the Ollama num_ctx ceiling on small/medium models, which causes
|
|
10
|
+
* silent front-truncation of the system prompt (the exact failure
|
|
11
|
+
* mode that made `bandit-core:12b-it-qat` appear to "refuse" C#
|
|
12
|
+
* edits until num_ctx was properly configured).
|
|
13
|
+
*
|
|
14
|
+
* Strategy (deterministic, no extra LLM calls):
|
|
15
|
+
* 1. Count tokens with a cheap char/4 heuristic.
|
|
16
|
+
* 2. If the total fits a configurable budget, return as-is.
|
|
17
|
+
* 3. Otherwise, walk the message list from oldest to newest and
|
|
18
|
+
* replace the *body* of earlier tool-result messages with a
|
|
19
|
+
* one-line placeholder ("[read_file api/foo.cs — 412 lines, see
|
|
20
|
+
* earlier]"). We always keep the system prompt, the initial user
|
|
21
|
+
* prompt, the last N tool results in full, and any assistant
|
|
22
|
+
* message that contains a tool call (the model needs to see its
|
|
23
|
+
* own prior reasoning).
|
|
24
|
+
*
|
|
25
|
+
* The compacted messages retain the same `role` / `content` shape so
|
|
26
|
+
* the loop doesn't need to change — it just passes them through chat.
|
|
27
|
+
*/
|
|
28
|
+
import type { ToolLoopMessage } from './tool-types';
|
|
29
|
+
export interface CompactionOptions {
|
|
30
|
+
/** Target token budget. Defaults to 12000 (safe for 16k num_ctx). */
|
|
31
|
+
tokenBudget?: number;
|
|
32
|
+
/** Always keep the last N tool-result messages in full. Default 2. */
|
|
33
|
+
keepRecentToolResults?: number;
|
|
34
|
+
/** Character-to-token ratio for heuristic counting. Default 4. */
|
|
35
|
+
charsPerToken?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface CompactionReport {
|
|
38
|
+
compacted: ToolLoopMessage[];
|
|
39
|
+
/** Pre-compaction estimated token count. */
|
|
40
|
+
beforeTokens: number;
|
|
41
|
+
/** Post-compaction estimated token count. */
|
|
42
|
+
afterTokens: number;
|
|
43
|
+
/** How many messages were collapsed to placeholders. */
|
|
44
|
+
messagesCompacted: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Main entry point. Returns compacted messages plus a report of what
|
|
48
|
+
* changed. Callers can log the report so agents/CLIs can surface
|
|
49
|
+
* "compacted history — N msgs, saved X tokens" as a status line.
|
|
50
|
+
*/
|
|
51
|
+
export declare function compactToolMessages(messages: ToolLoopMessage[], options?: CompactionOptions): CompactionReport;
|
|
52
|
+
//# sourceMappingURL=compactMessages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compactMessages.d.ts","sourceRoot":"","sources":["../../src/tools/compactMessages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAyCD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EAAE,EAC3B,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CA0FlB"}
|