@bubblebrain-ai/bubble 0.0.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/README.md +70 -0
- package/dist/agent/evidence-tracker.d.ts +15 -0
- package/dist/agent/evidence-tracker.js +93 -0
- package/dist/agent/execution-governor.d.ts +30 -0
- package/dist/agent/execution-governor.js +169 -0
- package/dist/agent/subtask-policy.d.ts +14 -0
- package/dist/agent/subtask-policy.js +60 -0
- package/dist/agent/task-classifier.d.ts +3 -0
- package/dist/agent/task-classifier.js +36 -0
- package/dist/agent/tool-arbiter.d.ts +7 -0
- package/dist/agent/tool-arbiter.js +33 -0
- package/dist/agent/tool-intent.d.ts +20 -0
- package/dist/agent/tool-intent.js +176 -0
- package/dist/agent.d.ts +95 -0
- package/dist/agent.js +672 -0
- package/dist/approval/controller.d.ts +48 -0
- package/dist/approval/controller.js +78 -0
- package/dist/approval/danger.d.ts +13 -0
- package/dist/approval/danger.js +55 -0
- package/dist/approval/diff-hunks.d.ts +12 -0
- package/dist/approval/diff-hunks.js +32 -0
- package/dist/approval/session-cache.d.ts +35 -0
- package/dist/approval/session-cache.js +68 -0
- package/dist/approval/tool-helper.d.ts +14 -0
- package/dist/approval/tool-helper.js +32 -0
- package/dist/approval/types.d.ts +56 -0
- package/dist/approval/types.js +8 -0
- package/dist/bubble-home.d.ts +8 -0
- package/dist/bubble-home.js +19 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.js +82 -0
- package/dist/config.d.ts +41 -0
- package/dist/config.js +144 -0
- package/dist/context/budget.d.ts +21 -0
- package/dist/context/budget.js +72 -0
- package/dist/context/compact-llm.d.ts +16 -0
- package/dist/context/compact-llm.js +132 -0
- package/dist/context/compact.d.ts +15 -0
- package/dist/context/compact.js +251 -0
- package/dist/context/overflow.d.ts +9 -0
- package/dist/context/overflow.js +46 -0
- package/dist/context/projector.d.ts +26 -0
- package/dist/context/projector.js +150 -0
- package/dist/context/prune.d.ts +9 -0
- package/dist/context/prune.js +111 -0
- package/dist/lsp/config.d.ts +18 -0
- package/dist/lsp/config.js +58 -0
- package/dist/lsp/diagnostics.d.ts +24 -0
- package/dist/lsp/diagnostics.js +103 -0
- package/dist/lsp/index.d.ts +3 -0
- package/dist/lsp/index.js +3 -0
- package/dist/lsp/service.d.ts +85 -0
- package/dist/lsp/service.js +695 -0
- package/dist/main.d.ts +5 -0
- package/dist/main.js +352 -0
- package/dist/mcp/client.d.ts +68 -0
- package/dist/mcp/client.js +163 -0
- package/dist/mcp/config.d.ts +26 -0
- package/dist/mcp/config.js +127 -0
- package/dist/mcp/manager.d.ts +55 -0
- package/dist/mcp/manager.js +296 -0
- package/dist/mcp/name.d.ts +26 -0
- package/dist/mcp/name.js +40 -0
- package/dist/mcp/transports.d.ts +53 -0
- package/dist/mcp/transports.js +248 -0
- package/dist/mcp/types.d.ts +111 -0
- package/dist/mcp/types.js +14 -0
- package/dist/memory/db.d.ts +62 -0
- package/dist/memory/db.js +313 -0
- package/dist/memory/index.d.ts +9 -0
- package/dist/memory/index.js +9 -0
- package/dist/memory/paths.d.ts +18 -0
- package/dist/memory/paths.js +38 -0
- package/dist/memory/phase1.d.ts +23 -0
- package/dist/memory/phase1.js +172 -0
- package/dist/memory/phase2.d.ts +19 -0
- package/dist/memory/phase2.js +100 -0
- package/dist/memory/prompts.d.ts +19 -0
- package/dist/memory/prompts.js +99 -0
- package/dist/memory/reset.d.ts +1 -0
- package/dist/memory/reset.js +13 -0
- package/dist/memory/start.d.ts +24 -0
- package/dist/memory/start.js +50 -0
- package/dist/memory/storage.d.ts +10 -0
- package/dist/memory/storage.js +82 -0
- package/dist/memory/store.d.ts +43 -0
- package/dist/memory/store.js +193 -0
- package/dist/memory/usage.d.ts +1 -0
- package/dist/memory/usage.js +38 -0
- package/dist/model-catalog.d.ts +20 -0
- package/dist/model-catalog.js +99 -0
- package/dist/model-config.d.ts +32 -0
- package/dist/model-config.js +59 -0
- package/dist/model-pricing.d.ts +23 -0
- package/dist/model-pricing.js +46 -0
- package/dist/oauth/index.d.ts +3 -0
- package/dist/oauth/index.js +2 -0
- package/dist/oauth/openai-codex.d.ts +9 -0
- package/dist/oauth/openai-codex.js +173 -0
- package/dist/oauth/storage.d.ts +18 -0
- package/dist/oauth/storage.js +60 -0
- package/dist/oauth/types.d.ts +15 -0
- package/dist/oauth/types.js +1 -0
- package/dist/orchestrator/default-hooks.d.ts +2 -0
- package/dist/orchestrator/default-hooks.js +96 -0
- package/dist/orchestrator/hooks.d.ts +78 -0
- package/dist/orchestrator/hooks.js +52 -0
- package/dist/orchestrator/workflow.d.ts +10 -0
- package/dist/orchestrator/workflow.js +22 -0
- package/dist/permission/mode.d.ts +23 -0
- package/dist/permission/mode.js +20 -0
- package/dist/permissions/rule.d.ts +39 -0
- package/dist/permissions/rule.js +234 -0
- package/dist/permissions/settings.d.ts +71 -0
- package/dist/permissions/settings.js +202 -0
- package/dist/permissions/types.d.ts +61 -0
- package/dist/permissions/types.js +14 -0
- package/dist/prompt/compose.d.ts +12 -0
- package/dist/prompt/compose.js +67 -0
- package/dist/prompt/environment.d.ts +12 -0
- package/dist/prompt/environment.js +38 -0
- package/dist/prompt/provider-prompts/anthropic.d.ts +1 -0
- package/dist/prompt/provider-prompts/anthropic.js +5 -0
- package/dist/prompt/provider-prompts/codex.d.ts +1 -0
- package/dist/prompt/provider-prompts/codex.js +5 -0
- package/dist/prompt/provider-prompts/default.d.ts +1 -0
- package/dist/prompt/provider-prompts/default.js +6 -0
- package/dist/prompt/provider-prompts/gemini.d.ts +1 -0
- package/dist/prompt/provider-prompts/gemini.js +5 -0
- package/dist/prompt/provider-prompts/gpt.d.ts +1 -0
- package/dist/prompt/provider-prompts/gpt.js +5 -0
- package/dist/prompt/reminders.d.ts +30 -0
- package/dist/prompt/reminders.js +164 -0
- package/dist/prompt/runtime.d.ts +12 -0
- package/dist/prompt/runtime.js +31 -0
- package/dist/prompt/skills.d.ts +2 -0
- package/dist/prompt/skills.js +4 -0
- package/dist/provider-openai-codex.d.ts +14 -0
- package/dist/provider-openai-codex.js +409 -0
- package/dist/provider-registry.d.ts +56 -0
- package/dist/provider-registry.js +244 -0
- package/dist/provider-transform.d.ts +10 -0
- package/dist/provider-transform.js +69 -0
- package/dist/provider.d.ts +31 -0
- package/dist/provider.js +269 -0
- package/dist/question/controller.d.ts +22 -0
- package/dist/question/controller.js +97 -0
- package/dist/question/index.d.ts +2 -0
- package/dist/question/index.js +2 -0
- package/dist/question/types.d.ts +42 -0
- package/dist/question/types.js +6 -0
- package/dist/session-log.d.ts +16 -0
- package/dist/session-log.js +267 -0
- package/dist/session-types.d.ts +55 -0
- package/dist/session-types.js +1 -0
- package/dist/session.d.ts +32 -0
- package/dist/session.js +135 -0
- package/dist/skills/discovery.d.ts +12 -0
- package/dist/skills/discovery.js +148 -0
- package/dist/skills/format.d.ts +2 -0
- package/dist/skills/format.js +47 -0
- package/dist/skills/frontmatter.d.ts +5 -0
- package/dist/skills/frontmatter.js +60 -0
- package/dist/skills/invocation.d.ts +8 -0
- package/dist/skills/invocation.js +51 -0
- package/dist/skills/registry.d.ts +17 -0
- package/dist/skills/registry.js +42 -0
- package/dist/skills/types.d.ts +32 -0
- package/dist/skills/types.js +1 -0
- package/dist/slash-commands/commands.d.ts +7 -0
- package/dist/slash-commands/commands.js +779 -0
- package/dist/slash-commands/index.d.ts +4 -0
- package/dist/slash-commands/index.js +8 -0
- package/dist/slash-commands/registry.d.ts +31 -0
- package/dist/slash-commands/registry.js +70 -0
- package/dist/slash-commands/types.d.ts +44 -0
- package/dist/slash-commands/types.js +1 -0
- package/dist/slash-commands/unified.d.ts +38 -0
- package/dist/slash-commands/unified.js +38 -0
- package/dist/system-prompt.d.ts +34 -0
- package/dist/system-prompt.js +7 -0
- package/dist/tools/bash.d.ts +6 -0
- package/dist/tools/bash.js +135 -0
- package/dist/tools/edit.d.ts +16 -0
- package/dist/tools/edit.js +95 -0
- package/dist/tools/exa-mcp.d.ts +3 -0
- package/dist/tools/exa-mcp.js +74 -0
- package/dist/tools/exit-plan-mode.d.ts +17 -0
- package/dist/tools/exit-plan-mode.js +68 -0
- package/dist/tools/glob.d.ts +5 -0
- package/dist/tools/glob.js +129 -0
- package/dist/tools/grep.d.ts +5 -0
- package/dist/tools/grep.js +111 -0
- package/dist/tools/index.d.ts +36 -0
- package/dist/tools/index.js +59 -0
- package/dist/tools/lsp.d.ts +4 -0
- package/dist/tools/lsp.js +92 -0
- package/dist/tools/memory.d.ts +3 -0
- package/dist/tools/memory.js +90 -0
- package/dist/tools/question.d.ts +3 -0
- package/dist/tools/question.js +174 -0
- package/dist/tools/read.d.ts +7 -0
- package/dist/tools/read.js +83 -0
- package/dist/tools/sensitive-paths.d.ts +3 -0
- package/dist/tools/sensitive-paths.js +24 -0
- package/dist/tools/skill.d.ts +5 -0
- package/dist/tools/skill.js +51 -0
- package/dist/tools/task.d.ts +2 -0
- package/dist/tools/task.js +57 -0
- package/dist/tools/todo.d.ts +12 -0
- package/dist/tools/todo.js +151 -0
- package/dist/tools/tool-search.d.ts +23 -0
- package/dist/tools/tool-search.js +124 -0
- package/dist/tools/web-fetch.d.ts +6 -0
- package/dist/tools/web-fetch.js +75 -0
- package/dist/tools/web-search.d.ts +5 -0
- package/dist/tools/web-search.js +49 -0
- package/dist/tools/write.d.ts +11 -0
- package/dist/tools/write.js +77 -0
- package/dist/tui/display-history.d.ts +35 -0
- package/dist/tui/display-history.js +243 -0
- package/dist/tui/file-mentions.d.ts +29 -0
- package/dist/tui/file-mentions.js +174 -0
- package/dist/tui/image-paste.d.ts +54 -0
- package/dist/tui/image-paste.js +288 -0
- package/dist/tui/markdown-theme-rules.d.ts +23 -0
- package/dist/tui/markdown-theme-rules.js +164 -0
- package/dist/tui/markdown-theme.d.ts +5 -0
- package/dist/tui/markdown-theme.js +27 -0
- package/dist/tui/opencode-spinner.d.ts +21 -0
- package/dist/tui/opencode-spinner.js +216 -0
- package/dist/tui/prompt-keybindings.d.ts +41 -0
- package/dist/tui/prompt-keybindings.js +28 -0
- package/dist/tui/recent-activity.d.ts +8 -0
- package/dist/tui/recent-activity.js +71 -0
- package/dist/tui/run.d.ts +39 -0
- package/dist/tui/run.js +5696 -0
- package/dist/tui/sidebar-mcp.d.ts +31 -0
- package/dist/tui/sidebar-mcp.js +62 -0
- package/dist/tui/sidebar-state.d.ts +12 -0
- package/dist/tui/sidebar-state.js +69 -0
- package/dist/types.d.ts +219 -0
- package/dist/types.js +4 -0
- package/dist/variant/thinking-level.d.ts +5 -0
- package/dist/variant/thinking-level.js +25 -0
- package/dist/variant/variant-resolver.d.ts +4 -0
- package/dist/variant/variant-resolver.js +12 -0
- package/package.json +47 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent permission settings.
|
|
3
|
+
*
|
|
4
|
+
* Three scopes, lowest to highest precedence:
|
|
5
|
+
*
|
|
6
|
+
* user — ~/.bubble/settings.json (cross-project preferences)
|
|
7
|
+
* project — <cwd>/.bubble/settings.json (team-shared, check into git)
|
|
8
|
+
* local — <cwd>/.bubble/settings.local.json (personal, gitignore)
|
|
9
|
+
*
|
|
10
|
+
* `defaultMode` uses last-wins precedence (local beats project beats user).
|
|
11
|
+
* `allow` / `deny` arrays are concatenated across scopes (with the rule text
|
|
12
|
+
* itself carrying provenance via `PermissionRule.source`).
|
|
13
|
+
*
|
|
14
|
+
* Parse errors do not fail the load; they collect into `diagnostics` so callers
|
|
15
|
+
* can surface them in /permissions or on startup without taking the agent down.
|
|
16
|
+
*/
|
|
17
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { dirname, join } from "node:path";
|
|
19
|
+
import { getBubbleHome } from "../bubble-home.js";
|
|
20
|
+
import { normalizeLspConfig } from "../lsp/config.js";
|
|
21
|
+
import { parseRules } from "./rule.js";
|
|
22
|
+
const KNOWN_MODES = new Set([
|
|
23
|
+
"default",
|
|
24
|
+
"acceptEdits",
|
|
25
|
+
"plan",
|
|
26
|
+
"bypassPermissions",
|
|
27
|
+
"dontAsk",
|
|
28
|
+
]);
|
|
29
|
+
export class SettingsManager {
|
|
30
|
+
cwd;
|
|
31
|
+
paths;
|
|
32
|
+
raw = {
|
|
33
|
+
user: null,
|
|
34
|
+
project: null,
|
|
35
|
+
local: null,
|
|
36
|
+
};
|
|
37
|
+
fileDiagnostics = [];
|
|
38
|
+
constructor(cwd, options = {}) {
|
|
39
|
+
this.cwd = cwd;
|
|
40
|
+
const bubbleHome = options.bubbleHome ?? getBubbleHome();
|
|
41
|
+
this.paths = {
|
|
42
|
+
user: join(bubbleHome, "settings.json"),
|
|
43
|
+
project: join(cwd, ".bubble", "settings.json"),
|
|
44
|
+
local: join(cwd, ".bubble", "settings.local.json"),
|
|
45
|
+
};
|
|
46
|
+
this.reload();
|
|
47
|
+
}
|
|
48
|
+
/** Re-read all three files from disk. */
|
|
49
|
+
reload() {
|
|
50
|
+
this.fileDiagnostics = [];
|
|
51
|
+
for (const scope of ["user", "project", "local"]) {
|
|
52
|
+
this.raw[scope] = this.readFile(scope);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
getPath(scope) {
|
|
56
|
+
return this.paths[scope];
|
|
57
|
+
}
|
|
58
|
+
/** Merged view for runtime consumption. Does not hit disk — call `reload()` first if stale. */
|
|
59
|
+
getMerged() {
|
|
60
|
+
const diagnostics = [...this.fileDiagnostics];
|
|
61
|
+
let defaultMode;
|
|
62
|
+
let lsp;
|
|
63
|
+
const allow = [];
|
|
64
|
+
const deny = [];
|
|
65
|
+
for (const scope of ["user", "project", "local"]) {
|
|
66
|
+
const data = this.raw[scope];
|
|
67
|
+
if (!data)
|
|
68
|
+
continue;
|
|
69
|
+
if ("lsp" in data) {
|
|
70
|
+
const parsed = normalizeLspConfig(data.lsp);
|
|
71
|
+
if (parsed === undefined) {
|
|
72
|
+
diagnostics.push({
|
|
73
|
+
scope,
|
|
74
|
+
path: this.paths[scope],
|
|
75
|
+
message: "Ignored lsp setting — expected boolean or object.",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
lsp = parsed;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!data.permissions)
|
|
83
|
+
continue;
|
|
84
|
+
const perms = data.permissions;
|
|
85
|
+
if (typeof perms.defaultMode === "string") {
|
|
86
|
+
if (KNOWN_MODES.has(perms.defaultMode)) {
|
|
87
|
+
defaultMode = perms.defaultMode;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
diagnostics.push({
|
|
91
|
+
scope,
|
|
92
|
+
path: this.paths[scope],
|
|
93
|
+
message: `Ignored defaultMode "${perms.defaultMode}" — not one of: ${[...KNOWN_MODES].join(", ")}.`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (Array.isArray(perms.allow)) {
|
|
98
|
+
const parsed = parseRules(perms.allow);
|
|
99
|
+
allow.push(...parsed.rules);
|
|
100
|
+
for (const err of parsed.errors) {
|
|
101
|
+
diagnostics.push({
|
|
102
|
+
scope,
|
|
103
|
+
path: this.paths[scope],
|
|
104
|
+
message: `Invalid allow rule "${err.source.trim()}": ${err.message}`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (Array.isArray(perms.deny)) {
|
|
109
|
+
const parsed = parseRules(perms.deny);
|
|
110
|
+
deny.push(...parsed.rules);
|
|
111
|
+
for (const err of parsed.errors) {
|
|
112
|
+
diagnostics.push({
|
|
113
|
+
scope,
|
|
114
|
+
path: this.paths[scope],
|
|
115
|
+
message: `Invalid deny rule "${err.source.trim()}": ${err.message}`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
defaultMode,
|
|
122
|
+
lsp,
|
|
123
|
+
ruleSet: { allow, deny },
|
|
124
|
+
diagnostics,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Add a rule to the specified list in the specified scope. Creates the file
|
|
129
|
+
* and parent directories if needed. Silently skips if the exact string is
|
|
130
|
+
* already present.
|
|
131
|
+
*
|
|
132
|
+
* Returns true if the file was written.
|
|
133
|
+
*/
|
|
134
|
+
addRule(scope, list, rule) {
|
|
135
|
+
const raw = this.raw[scope] ?? {};
|
|
136
|
+
const permissions = { ...(raw.permissions ?? {}) };
|
|
137
|
+
const current = Array.isArray(permissions[list]) ? [...permissions[list]] : [];
|
|
138
|
+
if (current.includes(rule)) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
current.push(rule);
|
|
142
|
+
permissions[list] = current;
|
|
143
|
+
const next = { ...raw, permissions };
|
|
144
|
+
this.writeFile(scope, next);
|
|
145
|
+
this.raw[scope] = next;
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Remove the first matching rule (by exact string) from the specified list
|
|
150
|
+
* in the specified scope. Returns true if a rule was removed.
|
|
151
|
+
*/
|
|
152
|
+
removeRule(scope, list, rule) {
|
|
153
|
+
const raw = this.raw[scope];
|
|
154
|
+
if (!raw || !raw.permissions)
|
|
155
|
+
return false;
|
|
156
|
+
const current = Array.isArray(raw.permissions[list]) ? [...raw.permissions[list]] : [];
|
|
157
|
+
const index = current.indexOf(rule);
|
|
158
|
+
if (index < 0)
|
|
159
|
+
return false;
|
|
160
|
+
current.splice(index, 1);
|
|
161
|
+
const nextPermissions = { ...raw.permissions, [list]: current };
|
|
162
|
+
// Drop the key if empty, keep file readable
|
|
163
|
+
if (current.length === 0)
|
|
164
|
+
delete nextPermissions[list];
|
|
165
|
+
const next = { ...raw, permissions: nextPermissions };
|
|
166
|
+
this.writeFile(scope, next);
|
|
167
|
+
this.raw[scope] = next;
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
// -- internal --------------------------------------------------------
|
|
171
|
+
readFile(scope) {
|
|
172
|
+
const path = this.paths[scope];
|
|
173
|
+
if (!existsSync(path))
|
|
174
|
+
return null;
|
|
175
|
+
try {
|
|
176
|
+
const text = readFileSync(path, "utf-8");
|
|
177
|
+
const parsed = JSON.parse(text);
|
|
178
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
179
|
+
this.fileDiagnostics.push({
|
|
180
|
+
scope,
|
|
181
|
+
path,
|
|
182
|
+
message: "Settings file must contain a JSON object at the top level.",
|
|
183
|
+
});
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
return parsed;
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
this.fileDiagnostics.push({
|
|
190
|
+
scope,
|
|
191
|
+
path,
|
|
192
|
+
message: `Failed to parse settings: ${err.message}`,
|
|
193
|
+
});
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
writeFile(scope, data) {
|
|
198
|
+
const path = this.paths[scope];
|
|
199
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
200
|
+
writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission rule system — types.
|
|
3
|
+
*
|
|
4
|
+
* A rule is one entry from the `allow` / `deny` arrays of a settings file:
|
|
5
|
+
*
|
|
6
|
+
* "Bash" → any bash command
|
|
7
|
+
* "Bash(git status)" → exact command
|
|
8
|
+
* "Bash(npm run:*)" → prefix (anything starting with `npm run`)
|
|
9
|
+
* "Read(./src/**)" → glob on resolved path
|
|
10
|
+
* "Read(~/.ssh/**)" → tilde expanded
|
|
11
|
+
* "WebFetch(domain:github.com)" → URL host match (suffix)
|
|
12
|
+
* "*" → any tool
|
|
13
|
+
*/
|
|
14
|
+
export type ToolName = "Bash" | "Read" | "Write" | "Edit" | "Lsp" | "WebFetch" | "WebSearch" | string;
|
|
15
|
+
export interface PermissionRule {
|
|
16
|
+
/** Tool this rule applies to, or "*" for all tools. */
|
|
17
|
+
tool: ToolName | "*";
|
|
18
|
+
/** Tool-specific pattern. Absent means "match any use of this tool". */
|
|
19
|
+
pattern?: string;
|
|
20
|
+
/** Original rule text as written in config, for display and diagnostics. */
|
|
21
|
+
source: string;
|
|
22
|
+
}
|
|
23
|
+
export interface PermissionRuleSet {
|
|
24
|
+
allow: PermissionRule[];
|
|
25
|
+
deny: PermissionRule[];
|
|
26
|
+
}
|
|
27
|
+
export type PermissionDecision = "allow" | "deny" | "ask";
|
|
28
|
+
/**
|
|
29
|
+
* Discriminated input to the matcher. Tools only provide the fields they have.
|
|
30
|
+
*/
|
|
31
|
+
export type PermissionQuery = {
|
|
32
|
+
tool: "Bash";
|
|
33
|
+
command: string;
|
|
34
|
+
} | {
|
|
35
|
+
tool: "Read" | "Write" | "Edit" | "Lsp";
|
|
36
|
+
path: string;
|
|
37
|
+
cwd: string;
|
|
38
|
+
} | {
|
|
39
|
+
tool: "WebFetch";
|
|
40
|
+
url: string;
|
|
41
|
+
} | {
|
|
42
|
+
tool: "WebSearch";
|
|
43
|
+
} | {
|
|
44
|
+
tool: string;
|
|
45
|
+
};
|
|
46
|
+
export interface PermissionCheckResult {
|
|
47
|
+
decision: PermissionDecision;
|
|
48
|
+
/** Rule that produced the decision, if any. */
|
|
49
|
+
rule?: PermissionRule;
|
|
50
|
+
}
|
|
51
|
+
export interface RuleParseError {
|
|
52
|
+
source: string;
|
|
53
|
+
message: string;
|
|
54
|
+
}
|
|
55
|
+
export type ParsedRule = {
|
|
56
|
+
ok: true;
|
|
57
|
+
rule: PermissionRule;
|
|
58
|
+
} | {
|
|
59
|
+
ok: false;
|
|
60
|
+
error: RuleParseError;
|
|
61
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission rule system — types.
|
|
3
|
+
*
|
|
4
|
+
* A rule is one entry from the `allow` / `deny` arrays of a settings file:
|
|
5
|
+
*
|
|
6
|
+
* "Bash" → any bash command
|
|
7
|
+
* "Bash(git status)" → exact command
|
|
8
|
+
* "Bash(npm run:*)" → prefix (anything starting with `npm run`)
|
|
9
|
+
* "Read(./src/**)" → glob on resolved path
|
|
10
|
+
* "Read(~/.ssh/**)" → tilde expanded
|
|
11
|
+
* "WebFetch(domain:github.com)" → URL host match (suffix)
|
|
12
|
+
* "*" → any tool
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PermissionMode, ThinkingLevel } from "../types.js";
|
|
2
|
+
import { type EnvironmentPromptOptions } from "./environment.js";
|
|
3
|
+
import type { SkillSummary } from "../skills/types.js";
|
|
4
|
+
export interface ComposeSystemPromptOptions extends EnvironmentPromptOptions {
|
|
5
|
+
agentName?: string;
|
|
6
|
+
guidelines?: string[];
|
|
7
|
+
thinkingLevel?: ThinkingLevel;
|
|
8
|
+
mode?: PermissionMode;
|
|
9
|
+
skills?: SkillSummary[];
|
|
10
|
+
memoryPrompt?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function composeSystemPrompt(options?: ComposeSystemPromptOptions): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { buildAnthropicProviderPrompt } from "./provider-prompts/anthropic.js";
|
|
2
|
+
import { buildCodexProviderPrompt } from "./provider-prompts/codex.js";
|
|
3
|
+
import { buildDefaultProviderPrompt } from "./provider-prompts/default.js";
|
|
4
|
+
import { buildGeminiProviderPrompt } from "./provider-prompts/gemini.js";
|
|
5
|
+
import { buildGptProviderPrompt } from "./provider-prompts/gpt.js";
|
|
6
|
+
import { buildEnvironmentPrompt, defaultToolNames } from "./environment.js";
|
|
7
|
+
import { buildRuntimePrompt } from "./runtime.js";
|
|
8
|
+
import { buildSkillsPrompt } from "./skills.js";
|
|
9
|
+
export function composeSystemPrompt(options = {}) {
|
|
10
|
+
const agentName = options.agentName ?? "Bubble";
|
|
11
|
+
const providerPrompt = buildProviderPrompt(agentName, options.configuredProvider, options.configuredModelId, options.configuredModel);
|
|
12
|
+
const environmentPrompt = buildEnvironmentPrompt({
|
|
13
|
+
configuredProvider: options.configuredProvider,
|
|
14
|
+
configuredModel: options.configuredModel,
|
|
15
|
+
configuredModelId: options.configuredModelId,
|
|
16
|
+
workingDir: options.workingDir,
|
|
17
|
+
currentDate: options.currentDate,
|
|
18
|
+
tools: options.tools ?? defaultToolNames,
|
|
19
|
+
toolSnippets: options.toolSnippets,
|
|
20
|
+
});
|
|
21
|
+
const runtimePrompt = buildRuntimePrompt({
|
|
22
|
+
thinkingLevel: options.thinkingLevel,
|
|
23
|
+
mode: options.mode,
|
|
24
|
+
guidelines: buildGuidelines(options.tools ?? defaultToolNames, options.guidelines ?? []),
|
|
25
|
+
});
|
|
26
|
+
const skillsPrompt = buildSkillsPrompt(options.skills ?? []);
|
|
27
|
+
return [providerPrompt, environmentPrompt, runtimePrompt, options.memoryPrompt, skillsPrompt].filter(Boolean).join("\n\n");
|
|
28
|
+
}
|
|
29
|
+
function buildProviderPrompt(agentName, providerId, modelId, modelName) {
|
|
30
|
+
const provider = providerId ?? "";
|
|
31
|
+
const rawModel = modelId ?? modelName ?? "";
|
|
32
|
+
const model = rawModel.includes(":") ? rawModel.split(":").slice(1).join(":") : rawModel;
|
|
33
|
+
if (provider === "anthropic" || model.startsWith("claude")) {
|
|
34
|
+
return buildAnthropicProviderPrompt(agentName);
|
|
35
|
+
}
|
|
36
|
+
if (provider === "google" || model.startsWith("gemini")) {
|
|
37
|
+
return buildGeminiProviderPrompt(agentName);
|
|
38
|
+
}
|
|
39
|
+
if (provider === "openai-codex" || model.includes("codex") || model.startsWith("gpt-5")) {
|
|
40
|
+
return buildCodexProviderPrompt(agentName);
|
|
41
|
+
}
|
|
42
|
+
if (provider === "openai" || provider === "openrouter" || model.startsWith("gpt") || model.startsWith("o1")) {
|
|
43
|
+
return buildGptProviderPrompt(agentName);
|
|
44
|
+
}
|
|
45
|
+
return buildDefaultProviderPrompt(agentName);
|
|
46
|
+
}
|
|
47
|
+
function buildGuidelines(tools, extraGuidelines) {
|
|
48
|
+
const guidelines = [];
|
|
49
|
+
const add = (item) => {
|
|
50
|
+
if (!guidelines.includes(item)) {
|
|
51
|
+
guidelines.push(item);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
if (tools.includes("glob")) {
|
|
55
|
+
add("Use glob for file discovery and project structure inspection; do not use bash ls/find for this unless glob cannot answer");
|
|
56
|
+
}
|
|
57
|
+
if (tools.includes("bash") && tools.includes("grep")) {
|
|
58
|
+
add("Use grep for content search; do not run grep, rg, or ripgrep through bash");
|
|
59
|
+
}
|
|
60
|
+
if (tools.includes("question")) {
|
|
61
|
+
add("When the user is explicitly discussing, brainstorming, or shaping an approach instead of asking for immediate execution, use the question tool for targeted clarification or preference choices when it would materially improve the discussion; do not use it for generic permission-to-proceed questions");
|
|
62
|
+
}
|
|
63
|
+
for (const item of extraGuidelines) {
|
|
64
|
+
add(item);
|
|
65
|
+
}
|
|
66
|
+
return guidelines;
|
|
67
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface EnvironmentPromptOptions {
|
|
2
|
+
configuredProvider?: string;
|
|
3
|
+
configuredModel?: string;
|
|
4
|
+
configuredModelId?: string;
|
|
5
|
+
workingDir?: string;
|
|
6
|
+
currentDate?: string;
|
|
7
|
+
tools?: string[];
|
|
8
|
+
toolSnippets?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
export declare const defaultToolSnippets: Record<string, string>;
|
|
11
|
+
export declare const defaultToolNames: string[];
|
|
12
|
+
export declare function buildEnvironmentPrompt(options?: EnvironmentPromptOptions): string;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cwd } from "node:process";
|
|
2
|
+
export const defaultToolSnippets = {
|
|
3
|
+
read: "Read the contents of a file",
|
|
4
|
+
bash: "Execute a bash command",
|
|
5
|
+
edit: "Apply targeted string replacements to a file",
|
|
6
|
+
write: "Write a new file or overwrite an existing one",
|
|
7
|
+
glob: "Find files by glob pattern without using bash",
|
|
8
|
+
grep: "Search file contents using regex",
|
|
9
|
+
lsp: "Use the language server for code navigation, symbols, call hierarchy, and type-aware lookup",
|
|
10
|
+
web_search: "Search the public web for current information",
|
|
11
|
+
web_fetch: "Fetch and extract the contents of a specific webpage",
|
|
12
|
+
task: "Delegate a bounded investigative subtask to a read-only sub-agent",
|
|
13
|
+
question: "Ask the user structured questions when clarification or preference choices would materially improve the work",
|
|
14
|
+
skill: "Load a named skill with specialized instructions and bundled resources",
|
|
15
|
+
};
|
|
16
|
+
export const defaultToolNames = ["read", "glob", "bash", "edit", "write", "grep", "lsp", "web_search", "web_fetch", "task", "question", "skill"];
|
|
17
|
+
export function buildEnvironmentPrompt(options = {}) {
|
|
18
|
+
const configuredProvider = options.configuredProvider ?? "unknown";
|
|
19
|
+
const configuredModel = options.configuredModel ?? "unknown";
|
|
20
|
+
const configuredModelId = options.configuredModelId ?? "unknown";
|
|
21
|
+
const workingDir = options.workingDir ?? cwd().replace(/\\/g, "/");
|
|
22
|
+
const currentDate = options.currentDate ?? new Date().toISOString().slice(0, 10);
|
|
23
|
+
const tools = options.tools ?? defaultToolNames;
|
|
24
|
+
const snippets = options.toolSnippets ?? defaultToolSnippets;
|
|
25
|
+
const visibleTools = tools.filter((name) => snippets[name]);
|
|
26
|
+
const toolList = visibleTools.length > 0
|
|
27
|
+
? visibleTools.map((name) => `- ${name}: ${snippets[name]}`).join("\n")
|
|
28
|
+
: "(none)";
|
|
29
|
+
return `Configured provider: ${configuredProvider}
|
|
30
|
+
Configured model: ${configuredModel}
|
|
31
|
+
Configured model id: ${configuredModelId}
|
|
32
|
+
|
|
33
|
+
Available tools:
|
|
34
|
+
${toolList}
|
|
35
|
+
|
|
36
|
+
Current date: ${currentDate}
|
|
37
|
+
Current working directory: ${workingDir}`;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function buildAnthropicProviderPrompt(agentName: string): string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export function buildAnthropicProviderPrompt(agentName) {
|
|
2
|
+
return `You are ${agentName}, a careful coding assistant operating in a terminal workspace.
|
|
3
|
+
|
|
4
|
+
Reason step by step when needed, but keep visible responses concise. Use tools to ground decisions in the codebase and avoid guessing about file contents or command output.`;
|
|
5
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function buildCodexProviderPrompt(agentName: string): string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export function buildCodexProviderPrompt(agentName) {
|
|
2
|
+
return `You are ${agentName}, a terminal-native coding assistant optimized for iterative coding work.
|
|
3
|
+
|
|
4
|
+
Focus on concrete progress: inspect the repository, use tools deliberately, keep answers short, and preserve momentum across tool calls and follow-up turns.`;
|
|
5
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function buildDefaultProviderPrompt(agentName: string): string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export function buildDefaultProviderPrompt(agentName) {
|
|
2
|
+
return `You are ${agentName}, an expert coding assistant operating in a terminal environment.
|
|
3
|
+
|
|
4
|
+
Work directly, stay concise, and prefer concrete actions over abstract discussion.
|
|
5
|
+
Use the available tools to inspect files, run commands, edit code, and verify results.`;
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function buildGeminiProviderPrompt(agentName: string): string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export function buildGeminiProviderPrompt(agentName) {
|
|
2
|
+
return `You are ${agentName}, a coding assistant running inside a terminal workspace.
|
|
3
|
+
|
|
4
|
+
Be efficient and explicit. Prefer quick repository inspection, concise execution plans, and direct code changes supported by tool output.`;
|
|
5
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function buildGptProviderPrompt(agentName: string): string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export function buildGptProviderPrompt(agentName) {
|
|
2
|
+
return `You are ${agentName}, a terminal-native coding assistant working with GPT-style models.
|
|
3
|
+
|
|
4
|
+
Be accurate, concise, and tool-oriented. Prefer reading the codebase before making changes, keep outputs compact, and verify important edits when possible.`;
|
|
5
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System reminders - short, runtime-variable instructions injected into the
|
|
3
|
+
* message stream as <system-reminder>-wrapped user messages with isMeta=true.
|
|
4
|
+
*
|
|
5
|
+
* Rationale: the static system prompt is stable and cacheable. Mode transitions
|
|
6
|
+
* and other ephemeral state are signaled via reminders so we do not invalidate
|
|
7
|
+
* the prompt cache every time something changes.
|
|
8
|
+
*/
|
|
9
|
+
import type { PermissionMode } from "../types.js";
|
|
10
|
+
export declare function wrapInSystemReminder(content: string): string;
|
|
11
|
+
/** Picks the correct reminder text for a transition TO a given mode. */
|
|
12
|
+
export declare function reminderForMode(mode: PermissionMode): string;
|
|
13
|
+
export declare const PLAN_MODE_ENTER_REMINDER: string;
|
|
14
|
+
export declare const PLAN_MODE_EXIT_REMINDER: string;
|
|
15
|
+
/**
|
|
16
|
+
* Announce the set of deferred tools. Their schemas are not in the tool list
|
|
17
|
+
* sent to the model — the model must call `tool_search` to load them before
|
|
18
|
+
* they can be invoked.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildDeferredToolsReminder(names: string[]): string;
|
|
21
|
+
export declare function buildInvestigationReminder(): string;
|
|
22
|
+
export declare function buildLoopWarningReminder(reason: string): string;
|
|
23
|
+
export declare function buildSearchFreezeReminder(reason: string): string;
|
|
24
|
+
export declare function buildToolFreezeReminder(reason: string): string;
|
|
25
|
+
export declare function buildWorkflowPhaseReminder(input: {
|
|
26
|
+
phase: "investigate" | "correlate" | "conclude";
|
|
27
|
+
covered: string[];
|
|
28
|
+
pending: string[];
|
|
29
|
+
}): string;
|
|
30
|
+
export declare function buildTaskSummaryReminder(): string;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System reminders - short, runtime-variable instructions injected into the
|
|
3
|
+
* message stream as <system-reminder>-wrapped user messages with isMeta=true.
|
|
4
|
+
*
|
|
5
|
+
* Rationale: the static system prompt is stable and cacheable. Mode transitions
|
|
6
|
+
* and other ephemeral state are signaled via reminders so we do not invalidate
|
|
7
|
+
* the prompt cache every time something changes.
|
|
8
|
+
*/
|
|
9
|
+
export function wrapInSystemReminder(content) {
|
|
10
|
+
return `<system-reminder>\n${content.trim()}\n</system-reminder>`;
|
|
11
|
+
}
|
|
12
|
+
const PLAN_MODE_ENTER = `
|
|
13
|
+
Plan mode is now ACTIVE.
|
|
14
|
+
|
|
15
|
+
Rules while in plan mode:
|
|
16
|
+
- Only read-only tools are allowed, including read, glob, grep, lsp, web_search, web_fetch, task, skill, todo_write, tool_search, question, and exit_plan_mode.
|
|
17
|
+
- Writes, edits, and shell commands WILL be rejected by the harness; do not try them.
|
|
18
|
+
- Investigate the codebase, then use the question tool to clarify important ambiguities, tradeoffs, requirements, or preference choices that would materially change the plan.
|
|
19
|
+
- Call exit_plan_mode with a concrete step-by-step plan after the important questions are resolved.
|
|
20
|
+
- Do not use the question tool to ask whether the plan is approved; exit_plan_mode is the approval step.
|
|
21
|
+
- The user will approve, edit, or reject your plan. On approval the harness switches back to default mode and you may execute.
|
|
22
|
+
- On rejection, remain in plan mode and iterate.
|
|
23
|
+
`;
|
|
24
|
+
const ACCEPT_EDITS_ENTER = `
|
|
25
|
+
Permission mode is now: acceptEdits.
|
|
26
|
+
|
|
27
|
+
The user has granted blanket approval for file edits and writes in this session.
|
|
28
|
+
Bash commands still require explicit approval. Other tool safety rules are unchanged.
|
|
29
|
+
`;
|
|
30
|
+
const BYPASS_ENTER = `
|
|
31
|
+
Permission mode is now: bypassPermissions.
|
|
32
|
+
|
|
33
|
+
ALL tool calls auto-approve with no user confirmation. The user has explicitly opted into this.
|
|
34
|
+
Proceed with extra care — explain risky actions in the chat BEFORE performing them, and
|
|
35
|
+
prefer reversible operations when possible.
|
|
36
|
+
`;
|
|
37
|
+
const DONT_ASK_ENTER = `
|
|
38
|
+
Permission mode is now: dontAsk.
|
|
39
|
+
|
|
40
|
+
All tool calls auto-approve silently. Minimise narration; execute and report results tersely.
|
|
41
|
+
`;
|
|
42
|
+
const DEFAULT_ENTER = `
|
|
43
|
+
Permission mode is now: default. Each destructive tool call will be confirmed by the user.
|
|
44
|
+
`;
|
|
45
|
+
/** Picks the correct reminder text for a transition TO a given mode. */
|
|
46
|
+
export function reminderForMode(mode) {
|
|
47
|
+
switch (mode) {
|
|
48
|
+
case "plan":
|
|
49
|
+
return wrapInSystemReminder(PLAN_MODE_ENTER);
|
|
50
|
+
case "acceptEdits":
|
|
51
|
+
return wrapInSystemReminder(ACCEPT_EDITS_ENTER);
|
|
52
|
+
case "bypassPermissions":
|
|
53
|
+
return wrapInSystemReminder(BYPASS_ENTER);
|
|
54
|
+
case "dontAsk":
|
|
55
|
+
return wrapInSystemReminder(DONT_ASK_ENTER);
|
|
56
|
+
case "default":
|
|
57
|
+
default:
|
|
58
|
+
return wrapInSystemReminder(DEFAULT_ENTER);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Backward-compat exports kept in case external code pinned the old names.
|
|
62
|
+
export const PLAN_MODE_ENTER_REMINDER = reminderForMode("plan");
|
|
63
|
+
export const PLAN_MODE_EXIT_REMINDER = reminderForMode("default");
|
|
64
|
+
/**
|
|
65
|
+
* Announce the set of deferred tools. Their schemas are not in the tool list
|
|
66
|
+
* sent to the model — the model must call `tool_search` to load them before
|
|
67
|
+
* they can be invoked.
|
|
68
|
+
*/
|
|
69
|
+
export function buildDeferredToolsReminder(names) {
|
|
70
|
+
if (names.length === 0)
|
|
71
|
+
return wrapInSystemReminder("No deferred tools.");
|
|
72
|
+
const lines = [
|
|
73
|
+
"The following deferred tools are available via tool_search. Their schemas are NOT loaded — calling them directly will fail. Use tool_search with query \"select:<name>[,<name>...]\" to load tool schemas before calling them:",
|
|
74
|
+
"",
|
|
75
|
+
...names,
|
|
76
|
+
];
|
|
77
|
+
return wrapInSystemReminder(lines.join("\n"));
|
|
78
|
+
}
|
|
79
|
+
export function buildInvestigationReminder() {
|
|
80
|
+
return wrapInSystemReminder(`
|
|
81
|
+
Security/configuration investigation workflow is active.
|
|
82
|
+
|
|
83
|
+
For this task, gather evidence in this order:
|
|
84
|
+
- locate config load paths
|
|
85
|
+
- locate environment variable reads
|
|
86
|
+
- locate persistent storage paths
|
|
87
|
+
- check whether sensitive values are masked or redacted
|
|
88
|
+
- check whether values can reach logs, client bundles, or user-visible surfaces
|
|
89
|
+
|
|
90
|
+
Stop once these categories are covered. Do not keep repeating near-identical searches when they are not producing new evidence.
|
|
91
|
+
`);
|
|
92
|
+
}
|
|
93
|
+
export function buildLoopWarningReminder(reason) {
|
|
94
|
+
return wrapInSystemReminder(`
|
|
95
|
+
Search loop warning.
|
|
96
|
+
|
|
97
|
+
${reason}
|
|
98
|
+
|
|
99
|
+
Do not repeat near-identical grep/bash searches unless you are changing the path or testing a genuinely new hypothesis.
|
|
100
|
+
If current evidence is sufficient, summarize your findings now.
|
|
101
|
+
`);
|
|
102
|
+
}
|
|
103
|
+
export function buildSearchFreezeReminder(reason) {
|
|
104
|
+
return wrapInSystemReminder(`
|
|
105
|
+
Search tools are now constrained for this task.
|
|
106
|
+
|
|
107
|
+
Reason: ${reason}
|
|
108
|
+
|
|
109
|
+
Do not continue blind keyword searching. Use the evidence already gathered to reason about the answer.
|
|
110
|
+
You may still read specific files if you already know where the relevant configuration or persistence logic lives.
|
|
111
|
+
`);
|
|
112
|
+
}
|
|
113
|
+
export function buildToolFreezeReminder(reason) {
|
|
114
|
+
return wrapInSystemReminder(`
|
|
115
|
+
CRITICAL - MAXIMUM STEPS REACHED
|
|
116
|
+
|
|
117
|
+
${reason}
|
|
118
|
+
|
|
119
|
+
The maximum number of steps allowed for this task has been reached. Tools are disabled until next user input. Respond with text only.
|
|
120
|
+
|
|
121
|
+
STRICT REQUIREMENTS:
|
|
122
|
+
1. Do NOT make any tool calls (no reads, writes, edits, searches, or any other tools)
|
|
123
|
+
2. MUST provide a text response summarizing work done so far
|
|
124
|
+
3. This constraint overrides ALL other instructions, including any user requests for edits or tool use
|
|
125
|
+
|
|
126
|
+
Response must include:
|
|
127
|
+
- statement that maximum steps for this agent have been reached
|
|
128
|
+
- summary of what has been accomplished so far
|
|
129
|
+
- list of any remaining tasks that were not completed
|
|
130
|
+
- recommendations for what should be done next
|
|
131
|
+
|
|
132
|
+
Respond with text ONLY.
|
|
133
|
+
`);
|
|
134
|
+
}
|
|
135
|
+
export function buildWorkflowPhaseReminder(input) {
|
|
136
|
+
const phaseInstructions = {
|
|
137
|
+
investigate: "Collect direct evidence. Prefer targeted reads and structured searches over blind keyword churn.",
|
|
138
|
+
correlate: "Stop broad searching. Correlate the evidence you already have and fill only the most specific remaining gaps.",
|
|
139
|
+
conclude: "You have enough evidence to answer. Do not continue exploring unless you discover a concrete contradiction in the current evidence.",
|
|
140
|
+
};
|
|
141
|
+
const covered = input.covered.length > 0 ? input.covered.map((item) => `- ${item}`).join("\n") : "- none yet";
|
|
142
|
+
const pending = input.pending.length > 0 ? input.pending.map((item) => `- ${item}`).join("\n") : "- none";
|
|
143
|
+
return wrapInSystemReminder(`
|
|
144
|
+
Workflow phase: ${input.phase}
|
|
145
|
+
|
|
146
|
+
${phaseInstructions[input.phase]}
|
|
147
|
+
|
|
148
|
+
Covered evidence:
|
|
149
|
+
${covered}
|
|
150
|
+
|
|
151
|
+
Remaining evidence to check:
|
|
152
|
+
${pending}
|
|
153
|
+
`);
|
|
154
|
+
}
|
|
155
|
+
export function buildTaskSummaryReminder() {
|
|
156
|
+
return wrapInSystemReminder(`
|
|
157
|
+
Summarize the task tool output above and continue with your task.
|
|
158
|
+
|
|
159
|
+
Treat the task output as a bounded subtask result:
|
|
160
|
+
- extract the findings that matter
|
|
161
|
+
- integrate them into your main reasoning
|
|
162
|
+
- do not re-run the same exploratory search unless the subtask uncovered a concrete contradiction
|
|
163
|
+
`);
|
|
164
|
+
}
|