@bubblebrain-ai/bubble 0.0.46 → 0.0.48
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/dist/agent/child-runner.js +11 -0
- package/dist/agent/internal-reminder-sanitizer.d.ts +7 -0
- package/dist/agent/internal-reminder-sanitizer.js +13 -0
- package/dist/agent/subagent/router.d.ts +65 -0
- package/dist/agent/subagent/router.js +198 -0
- package/dist/agent/subagent/runtime.d.ts +203 -0
- package/dist/agent/subagent/runtime.js +789 -0
- package/dist/agent/subagent-control.d.ts +5 -0
- package/dist/agent/subagent-store.d.ts +18 -2
- package/dist/agent/subagent-store.js +32 -1
- package/dist/agent/tool-intent.d.ts +10 -0
- package/dist/agent/tool-intent.js +18 -0
- package/dist/agent/tool-visibility.d.ts +10 -0
- package/dist/agent/tool-visibility.js +6 -0
- package/dist/agent/workflow/runs.d.ts +72 -0
- package/dist/agent/workflow/runs.js +157 -0
- package/dist/agent.d.ts +78 -96
- package/dist/agent.js +266 -1072
- package/dist/context/compact-llm.js +14 -5
- package/dist/context/compact.d.ts +9 -0
- package/dist/context/compact.js +36 -10
- package/dist/context/compaction-files.d.ts +44 -0
- package/dist/context/compaction-files.js +148 -0
- package/dist/context/llm-compactor.js +24 -5
- package/dist/feishu/agent-host/run-driver.js +3 -0
- package/dist/feishu/serve.js +13 -9
- package/dist/goal/tools.js +12 -3
- package/dist/main.js +24 -19
- package/dist/model-catalog.js +36 -2
- package/dist/model-config.d.ts +2 -0
- package/dist/model-data.generated.d.ts +16 -0
- package/dist/model-data.generated.js +275 -0
- package/dist/model-pricing.d.ts +11 -7
- package/dist/model-pricing.js +0 -0
- package/dist/oauth/grok.d.ts +1 -1
- package/dist/oauth/grok.js +5 -2
- package/dist/oauth/storage.d.ts +9 -2
- package/dist/oauth/storage.js +16 -9
- package/dist/orchestrator/default-hooks.d.ts +20 -0
- package/dist/orchestrator/default-hooks.js +47 -295
- package/dist/orchestrator/hooks.d.ts +6 -39
- package/dist/permission/mode.js +1 -1
- package/dist/prompt/reminders.d.ts +9 -35
- package/dist/prompt/reminders.js +30 -136
- package/dist/prompt/task-reminders.d.ts +0 -31
- package/dist/prompt/task-reminders.js +0 -87
- package/dist/provider-ai-sdk.d.ts +2 -0
- package/dist/provider-ai-sdk.js +1 -0
- package/dist/provider-anthropic.d.ts +2 -0
- package/dist/provider-anthropic.js +3 -1
- package/dist/provider-ark-responses.d.ts +2 -0
- package/dist/provider-ark-responses.js +1 -0
- package/dist/provider-grok.d.ts +26 -0
- package/dist/provider-grok.js +39 -0
- package/dist/provider-registry.d.ts +14 -1
- package/dist/provider-registry.js +131 -3
- package/dist/provider-transform.js +37 -1
- package/dist/provider.d.ts +7 -0
- package/dist/provider.js +13 -1
- package/dist/sdk/index.d.ts +152 -0
- package/dist/sdk/index.js +400 -0
- package/dist/session.js +9 -1
- package/dist/slash-commands/commands.js +33 -19
- package/dist/tools/exit-plan-mode.js +8 -0
- package/dist/tui/detect-theme.d.ts +1 -0
- package/dist/tui/detect-theme.js +11 -5
- package/dist/tui-ink/app.d.ts +2 -6
- package/dist/tui-ink/app.js +11 -18
- package/dist/tui-ink/display-history.d.ts +0 -2
- package/dist/tui-ink/display-history.js +0 -52
- package/dist/tui-ink/footer.js +2 -2
- package/dist/tui-ink/input-box.js +18 -6
- package/dist/tui-ink/message-list.js +62 -23
- package/dist/tui-ink/run.js +10 -5
- package/dist/tui-ink/sync-output.d.ts +17 -0
- package/dist/tui-ink/sync-output.js +35 -0
- package/dist/tui-ink/theme.d.ts +0 -1
- package/dist/tui-ink/theme.js +0 -2
- package/dist/tui-ink/trace-groups.js +1 -1
- package/dist/types.d.ts +21 -1
- package/package.json +16 -2
- package/dist/agent/discovery-barrier.d.ts +0 -21
- package/dist/agent/discovery-barrier.js +0 -173
- package/dist/agent/evidence-tracker.d.ts +0 -15
- package/dist/agent/evidence-tracker.js +0 -93
- package/dist/agent/execution-governor.d.ts +0 -36
- package/dist/agent/execution-governor.js +0 -218
- package/dist/agent/task-classifier.d.ts +0 -3
- package/dist/agent/task-classifier.js +0 -114
- package/dist/agent/task-size.d.ts +0 -9
- package/dist/agent/task-size.js +0 -33
- package/dist/agent/tool-arbiter.d.ts +0 -7
- package/dist/agent/tool-arbiter.js +0 -33
- package/dist/orchestrator/workflow.d.ts +0 -10
- package/dist/orchestrator/workflow.js +0 -22
package/dist/prompt/reminders.js
CHANGED
|
@@ -9,12 +9,21 @@
|
|
|
9
9
|
export function wrapInSystemReminder(content) {
|
|
10
10
|
return content.trim();
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Headlines of the mode reminders below. Identity is content-based because a
|
|
14
|
+
* reminder is just text once it lands in the message stream; keeping the
|
|
15
|
+
* headlines in one place stops the matcher from drifting when the bodies are
|
|
16
|
+
* reworded.
|
|
17
|
+
*/
|
|
18
|
+
const MODE_REMINDER_HEADLINES = [
|
|
19
|
+
"Plan mode is now ACTIVE",
|
|
20
|
+
"Permission mode is now: bypassPermissions",
|
|
21
|
+
"Permission mode is now: default Build mode",
|
|
22
|
+
];
|
|
12
23
|
export function isPermissionModeReminder(content) {
|
|
13
24
|
if (typeof content !== "string")
|
|
14
25
|
return false;
|
|
15
|
-
return content.includes(
|
|
16
|
-
|| content.includes("Permission mode is now: bypassPermissions")
|
|
17
|
-
|| content.includes("Permission mode is now: default Build mode");
|
|
26
|
+
return MODE_REMINDER_HEADLINES.some((headline) => content.includes(headline));
|
|
18
27
|
}
|
|
19
28
|
const PLAN_MODE_ENTER = `
|
|
20
29
|
Plan mode is now ACTIVE.
|
|
@@ -32,6 +41,7 @@ Rules while in plan mode:
|
|
|
32
41
|
const BYPASS_ENTER = `
|
|
33
42
|
Permission mode is now: bypassPermissions.
|
|
34
43
|
|
|
44
|
+
This replaces every earlier mode reminder in this conversation. Plan mode is NOT active; exit_plan_mode does not exist in your tool list and must not be called or searched for.
|
|
35
45
|
ALL tool calls auto-approve with no user confirmation. The user has explicitly opted into this.
|
|
36
46
|
Proceed with extra care — explain risky actions in the chat BEFORE performing them, and
|
|
37
47
|
prefer reversible operations when possible.
|
|
@@ -40,6 +50,7 @@ Do not perform destructive operations, credential exposure, or unrelated reversi
|
|
|
40
50
|
const DEFAULT_ENTER = `
|
|
41
51
|
Permission mode is now: default Build mode.
|
|
42
52
|
|
|
53
|
+
This replaces every earlier mode reminder in this conversation. Plan mode is NOT active; exit_plan_mode does not exist in your tool list and must not be called or searched for.
|
|
43
54
|
File edits and writes auto-approve. Bash commands and other destructive tools still require explicit approval unless allowed by rules.
|
|
44
55
|
Execute the requested change end to end; do not stop at analysis unless blocked or the user explicitly asks for discussion only.
|
|
45
56
|
`;
|
|
@@ -72,75 +83,6 @@ export function buildDeferredToolsReminder(names) {
|
|
|
72
83
|
];
|
|
73
84
|
return wrapInSystemReminder(lines.join("\n"));
|
|
74
85
|
}
|
|
75
|
-
export function buildInvestigationReminder() {
|
|
76
|
-
return wrapInSystemReminder(`
|
|
77
|
-
Security/configuration investigation workflow is active.
|
|
78
|
-
|
|
79
|
-
For this task, gather evidence in this order:
|
|
80
|
-
- locate config load paths
|
|
81
|
-
- locate environment variable reads
|
|
82
|
-
- locate persistent storage paths
|
|
83
|
-
- check whether sensitive values are masked or redacted
|
|
84
|
-
- check whether values can reach logs, client bundles, or user-visible surfaces
|
|
85
|
-
|
|
86
|
-
Stop once these categories are covered. Do not keep repeating near-identical searches when they are not producing new evidence.
|
|
87
|
-
`);
|
|
88
|
-
}
|
|
89
|
-
export function buildLoopWarningReminder(reason) {
|
|
90
|
-
return wrapInSystemReminder(`
|
|
91
|
-
Further broad exploration is low value unless there is a concrete remaining evidence gap.
|
|
92
|
-
|
|
93
|
-
${reason}
|
|
94
|
-
|
|
95
|
-
Do not repeat near-identical reads or searches unless the path or hypothesis is materially different.
|
|
96
|
-
If current evidence is sufficient, answer with the findings.
|
|
97
|
-
`);
|
|
98
|
-
}
|
|
99
|
-
export function buildSearchFreezeReminder(reason) {
|
|
100
|
-
return wrapInSystemReminder(`
|
|
101
|
-
Search tools are now constrained for this task.
|
|
102
|
-
|
|
103
|
-
Reason: ${reason}
|
|
104
|
-
|
|
105
|
-
Do not continue blind keyword searching. Use the evidence already gathered to reason about the answer.
|
|
106
|
-
You may still read specific files if you already know where the relevant configuration or persistence logic lives.
|
|
107
|
-
`);
|
|
108
|
-
}
|
|
109
|
-
export function buildExplorationFreezeReminder(reason) {
|
|
110
|
-
return wrapInSystemReminder(`
|
|
111
|
-
Implementation phase has advanced from exploration to modification.
|
|
112
|
-
|
|
113
|
-
Reason: ${reason}
|
|
114
|
-
|
|
115
|
-
You have enough context to act. Do not continue reading, searching, or delegating exploration.
|
|
116
|
-
Choose one of:
|
|
117
|
-
1. Use edit/write to make the requested change.
|
|
118
|
-
2. If no safe change can be made from the gathered context, explain the concrete blocker.
|
|
119
|
-
3. If files were already changed, verify the change and run the affected module's existing tests, or finish with the result.
|
|
120
|
-
`);
|
|
121
|
-
}
|
|
122
|
-
export function buildCompletionSelfCheckReminder(options = {}) {
|
|
123
|
-
const sections = [];
|
|
124
|
-
const modifiedTests = options.modifiedExistingTests ?? [];
|
|
125
|
-
if (modifiedTests.length > 0) {
|
|
126
|
-
// Disclosure, not accusation: legitimate test updates (requirement
|
|
127
|
-
// changes, fixing a broken test, refactors) are normal engineering — the
|
|
128
|
-
// rule is that they must be declared, never silent. Confrontational
|
|
129
|
-
// wording risks a model rolling back a user-requested test update.
|
|
130
|
-
const listed = modifiedTests
|
|
131
|
-
.slice(0, 8)
|
|
132
|
-
.map((t) => `- ${t.path}${t.deletedLines > 0 ? ` (${t.deletedLines} line${t.deletedLines === 1 ? "" : "s"} removed)` : ""}`)
|
|
133
|
-
.join("\n");
|
|
134
|
-
sections.push(`This run modified pre-existing test files:
|
|
135
|
-
${listed}${modifiedTests.length > 8 ? `\n- …and ${modifiedTests.length - 8} more` : ""}
|
|
136
|
-
|
|
137
|
-
If a change you made caused an existing test to fail and you edited the test so the suite passes, restore the original behavior instead — weakening a test to make it pass is never acceptable. If the test changes are intentional (the request requires new expected behavior, or the test itself was wrong), keep them and state each modified test and the reason in your final summary.`);
|
|
138
|
-
}
|
|
139
|
-
sections.push(`You appear to be finishing. Before giving your final answer, re-read the user's original request in this conversation and check whether any explicit requirement in it remains unfulfilled — including requirements about how the work should be delivered or reported, not only whether the implementation works.
|
|
140
|
-
|
|
141
|
-
If something is missing, complete it now. If everything is done, give your final answer directly — do not repeat this check or re-verify work you have already verified.`);
|
|
142
|
-
return wrapInSystemReminder(`\n${sections.join("\n\n")}\n`);
|
|
143
|
-
}
|
|
144
86
|
export function buildToolFreezeReminder(reason) {
|
|
145
87
|
return wrapInSystemReminder(`
|
|
146
88
|
CRITICAL - MAXIMUM STEPS REACHED
|
|
@@ -163,79 +105,31 @@ Response must include:
|
|
|
163
105
|
Respond with text ONLY.
|
|
164
106
|
`);
|
|
165
107
|
}
|
|
166
|
-
export function buildWorkflowPhaseReminder(input) {
|
|
167
|
-
const phaseInstructions = {
|
|
168
|
-
investigate: "Collect direct evidence. Prefer targeted reads and structured searches over blind keyword churn.",
|
|
169
|
-
correlate: "Stop broad searching. Correlate the evidence you already have and fill only the most specific remaining gaps.",
|
|
170
|
-
conclude: "You have enough evidence to answer. Do not continue exploring unless you discover a concrete contradiction in the current evidence.",
|
|
171
|
-
};
|
|
172
|
-
const covered = input.covered.length > 0 ? input.covered.map((item) => `- ${item}`).join("\n") : "- none yet";
|
|
173
|
-
const pending = input.pending.length > 0 ? input.pending.map((item) => `- ${item}`).join("\n") : "- none";
|
|
174
|
-
return wrapInSystemReminder(`
|
|
175
|
-
Workflow phase: ${input.phase}
|
|
176
|
-
|
|
177
|
-
${phaseInstructions[input.phase]}
|
|
178
|
-
|
|
179
|
-
Covered evidence:
|
|
180
|
-
${covered}
|
|
181
|
-
|
|
182
|
-
Remaining evidence to check:
|
|
183
|
-
${pending}
|
|
184
|
-
`);
|
|
185
|
-
}
|
|
186
108
|
// Removed: buildVerificationReminder / buildVerificationFailureReminder.
|
|
187
109
|
// The verification reminder ladder pressured the model to run a "verification"
|
|
188
110
|
// after every file change. For models with hex-tokenization blind spots (e.g.
|
|
189
111
|
// DeepSeek v4-pro), this triggered death loops where the model wrote ad-hoc
|
|
190
112
|
// validation scripts that found the bug but could never fix it. CC trusts the
|
|
191
113
|
// model to decide when verification is meaningful; we follow that.
|
|
114
|
+
// Removed: buildFinalizeOpportunityReminder. Was paired with the verification
|
|
115
|
+
// nag ladder. Without the ladder, "you can finalize now" advice is redundant —
|
|
116
|
+
// the model finalises whenever its own judgement says the task is done.
|
|
192
117
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*/
|
|
199
|
-
export function buildEditRetryEscalationReminder(reason) {
|
|
200
|
-
return wrapInSystemReminder(`
|
|
201
|
-
A file mutation just failed in a way that usually means your local view of the file is stale or the edit anchor is wrong.
|
|
202
|
-
|
|
203
|
-
${reason}
|
|
204
|
-
|
|
205
|
-
Stop retrying from memory. Pick one of:
|
|
206
|
-
- Re-read the target file and compare the actual bytes to your intended oldText / newText. Trailing whitespace, unicode lookalikes, or off-by-one boundaries are common causes.
|
|
207
|
-
- If you intended to add a single character (e.g. fixing a 5-digit hex color to 6 digits), confirm that your newText string actually contains the added character before sending again.
|
|
208
|
-
- Use the write tool with the full new content instead of edit — useful when the change spans many lines or the diff anchor is ambiguous.
|
|
209
|
-
- If you cannot determine the cause, ask the user for clarification.
|
|
210
|
-
`);
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Fired the FIRST time the model re-reads a file it already read in this turn.
|
|
214
|
-
* Soft — does not freeze the tool. The model may still re-read when context was
|
|
215
|
-
* pruned, the requested range changed, or a later mutation needs verification.
|
|
216
|
-
*/
|
|
217
|
-
export function buildRedundantReadReminder(path) {
|
|
218
|
-
return wrapInSystemReminder(`
|
|
219
|
-
You already read ${path} earlier in this turn. If that content is still available and nothing changed, rely on it rather than re-reading.
|
|
220
|
-
It is okay to re-read when you need to recover pruned context, inspect a different range, or verify a later edit/write/bash change.
|
|
221
|
-
`);
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Injected once at task start when the user's input looks like a small,
|
|
225
|
-
* focused task (e.g. "write an HTML page about X"). Counterweight to the
|
|
226
|
-
* default protocol which biases toward thorough exploration.
|
|
118
|
+
* Modified-existing-tests disclosure (docs/harness-thinning.md): a fact from
|
|
119
|
+
* git ground truth — bash writes and subagent worktree merges do not appear
|
|
120
|
+
* in the model's own tool memory. Disclosure, not accusation: legitimate
|
|
121
|
+
* test updates are normal engineering; the rule is that they must be
|
|
122
|
+
* declared, never silent.
|
|
227
123
|
*/
|
|
228
|
-
export function
|
|
124
|
+
export function buildModifiedTestsDisclosure(modifiedExistingTests) {
|
|
125
|
+
const listed = modifiedExistingTests
|
|
126
|
+
.slice(0, 8)
|
|
127
|
+
.map((t) => `- ${t.path}${t.deletedLines > 0 ? ` (${t.deletedLines} line${t.deletedLines === 1 ? "" : "s"} removed)` : ""}`)
|
|
128
|
+
.join("\n");
|
|
229
129
|
return wrapInSystemReminder(`
|
|
230
|
-
|
|
130
|
+
Fact from git: this run modified pre-existing test files:
|
|
131
|
+
${listed}${modifiedExistingTests.length > 8 ? `\n- …and ${modifiedExistingTests.length - 8} more` : ""}
|
|
231
132
|
|
|
232
|
-
|
|
233
|
-
- If the target file path is given or obvious, use write/edit directly.
|
|
234
|
-
- Do not glob, read, or grep adjacent files unless the request explicitly references them.
|
|
235
|
-
- Do not pre-plan with todo_write for tasks that can be done in one or two tool calls.
|
|
236
|
-
- Skip the "investigate the codebase" step that applies to larger changes.
|
|
133
|
+
If a change you made caused an existing test to fail and you edited the test so the suite passes, restore the original behavior instead — weakening a test to make it pass is never acceptable. If the test changes are intentional (the request requires new expected behavior, or the test itself was wrong), keep them and state each modified test and the reason in your final summary. Then finish normally.
|
|
237
134
|
`);
|
|
238
135
|
}
|
|
239
|
-
// Removed: buildFinalizeOpportunityReminder. Was paired with the verification
|
|
240
|
-
// nag ladder. Without the ladder, "you can finalize now" advice is redundant —
|
|
241
|
-
// the model finalises whenever its own judgement says the task is done.
|
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
import type { TaskType } from "../agent/task-classifier.js";
|
|
2
1
|
import { type RoutableModelEntry } from "../agent/routing-catalog.js";
|
|
3
|
-
export interface TaskReminderOptions {
|
|
4
|
-
/** Whether this agent has the delegation tools (parent agents only). */
|
|
5
|
-
canDelegate?: boolean;
|
|
6
|
-
}
|
|
7
|
-
export declare function reminderForTaskType(taskType: TaskType, options?: TaskReminderOptions): string | undefined;
|
|
8
|
-
/**
|
|
9
|
-
* Deterministic detector for an explicit user request for a coordinated
|
|
10
|
-
* multi-agent run. Three rounds of prompt wording lost to the model's
|
|
11
|
-
* "agent team = parallel spawns" prior in live tests (opus-4.8, 2026-07-06);
|
|
12
|
-
* per the task-reminder principle above, a reminder injected at the decision
|
|
13
|
-
* turn is the lever that actually works — the harness remembers so the model
|
|
14
|
-
* does not have to.
|
|
15
|
-
*/
|
|
16
2
|
/**
|
|
17
3
|
* User-named model resolution (model-routing design v3.6): when the user's
|
|
18
4
|
* message names a model that exists in the routable catalog, hand the main
|
|
@@ -23,21 +9,4 @@ export declare function reminderForTaskType(taskType: TaskType, options?: TaskRe
|
|
|
23
9
|
* this, 2026-07-12 live case). Silent on no match: zero standing noise.
|
|
24
10
|
*/
|
|
25
11
|
export declare function userNamedModelReminder(input: string | import("../types.js").ContentPart[], routableModels: RoutableModelEntry[] | undefined, canSpawn: boolean): string | undefined;
|
|
26
|
-
/**
|
|
27
|
-
* Large-change checkpoint (large-task-delegation design §3): injected once
|
|
28
|
-
* per session at the first mutation of a broad turn. Carries the routing
|
|
29
|
-
* decision criteria (independent vs entangled) and the real worktree
|
|
30
|
-
* mechanics (children fork from the last commit; disjoint file sets) —
|
|
31
|
-
* never a command. The "smallest coherent edit" clause is lifted verbatim
|
|
32
|
-
* from the implementation-workflow reminder so the two compose.
|
|
33
|
-
*/
|
|
34
|
-
export interface LargeTaskReminderInput {
|
|
35
|
-
exploredFiles: number;
|
|
36
|
-
pendingTodos: number;
|
|
37
|
-
/** Mutations already applied this turn (the current batch lands regardless). */
|
|
38
|
-
appliedEdits: number;
|
|
39
|
-
/** orchestrationRequestReminder fired this turn: only offer run_workflow. */
|
|
40
|
-
orchestrationRequested: boolean;
|
|
41
|
-
}
|
|
42
|
-
export declare function largeImplementationTaskReminder(input: LargeTaskReminderInput): string;
|
|
43
12
|
export declare function orchestrationRequestReminder(input: string | import("../types.js").ContentPart[], canRunWorkflow: boolean): string | undefined;
|
|
@@ -1,76 +1,4 @@
|
|
|
1
1
|
import { normalizeModelToken } from "../agent/routing-catalog.js";
|
|
2
|
-
import { wrapInSystemReminder } from "./reminders.js";
|
|
3
|
-
/**
|
|
4
|
-
* Delegation nudge for exploration-shaped tasks: injected at the decision
|
|
5
|
-
* point (start of turn), where it carries far more weight for weakly
|
|
6
|
-
* delegating models than the session-start system prompt. Task-type gating
|
|
7
|
-
* keeps it away from ordinary implementation/debugging turns, so it cannot
|
|
8
|
-
* amplify over-delegation.
|
|
9
|
-
*/
|
|
10
|
-
const DELEGATION_NUDGE = "- If answering needs scanning many files and only the conclusion matters, delegate to a background subagent (spawn_agent); when it is the same read-only question over several independent items, fan out with a run_workflow script.";
|
|
11
|
-
export function reminderForTaskType(taskType, options = {}) {
|
|
12
|
-
switch (taskType) {
|
|
13
|
-
case "debugging":
|
|
14
|
-
return wrapInSystemReminder(`
|
|
15
|
-
Debugging workflow:
|
|
16
|
-
- Reproduce or identify the failing boundary before editing.
|
|
17
|
-
- Trace input, transformation, and output paths.
|
|
18
|
-
- Prefer fixing the mechanism over raising thresholds or adding superficial fallbacks.
|
|
19
|
-
- Verify the specific failure path after the change.
|
|
20
|
-
`);
|
|
21
|
-
case "implementation":
|
|
22
|
-
return wrapInSystemReminder(`
|
|
23
|
-
Implementation workflow:
|
|
24
|
-
- Do not stop at a proposal when the user asked for a change.
|
|
25
|
-
- Inspect the relevant files first, then make the smallest coherent edit.
|
|
26
|
-
- Keep unrelated files and behavior out of scope.
|
|
27
|
-
- Verify the change, then run the existing tests of the affected module — not only the cases you added — or explain why they cannot be run.
|
|
28
|
-
`);
|
|
29
|
-
case "code_review":
|
|
30
|
-
return wrapInSystemReminder(`
|
|
31
|
-
Code review workflow:
|
|
32
|
-
- Lead with concrete findings, ordered by severity.
|
|
33
|
-
- Reference file paths and line numbers when possible.
|
|
34
|
-
- Prioritize bugs, regressions, missing tests, security, and user-visible risk.
|
|
35
|
-
- Keep summaries secondary to findings.
|
|
36
|
-
`);
|
|
37
|
-
case "code_explanation":
|
|
38
|
-
return wrapInSystemReminder(`
|
|
39
|
-
Code explanation workflow:
|
|
40
|
-
- Answer the direct question first.
|
|
41
|
-
- Ground claims in concrete files, functions, and call paths.
|
|
42
|
-
- Distinguish current source evidence from inference.
|
|
43
|
-
- Avoid proposing changes unless the user asks for them.
|
|
44
|
-
`);
|
|
45
|
-
case "repo_orientation":
|
|
46
|
-
return wrapInSystemReminder(`
|
|
47
|
-
Repository orientation workflow:
|
|
48
|
-
- Start with the repo purpose and main execution paths.
|
|
49
|
-
- Inspect README/package metadata plus core runtime files before summarizing.
|
|
50
|
-
- Keep the first pass read-only unless the user asks for changes or runtime verification.
|
|
51
|
-
${options.canDelegate ? `${DELEGATION_NUDGE}\n` : ""}`);
|
|
52
|
-
case "product_discussion":
|
|
53
|
-
return wrapInSystemReminder(`
|
|
54
|
-
Product discussion workflow:
|
|
55
|
-
- Clarify the product goal, user workflow, and tradeoffs before suggesting implementation.
|
|
56
|
-
- Give direct product judgment when the user asks for direction.
|
|
57
|
-
- Avoid drifting into code changes unless the user explicitly asks to execute.
|
|
58
|
-
`);
|
|
59
|
-
case "security_investigation":
|
|
60
|
-
case "code_search":
|
|
61
|
-
case "general":
|
|
62
|
-
default:
|
|
63
|
-
return undefined;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Deterministic detector for an explicit user request for a coordinated
|
|
68
|
-
* multi-agent run. Three rounds of prompt wording lost to the model's
|
|
69
|
-
* "agent team = parallel spawns" prior in live tests (opus-4.8, 2026-07-06);
|
|
70
|
-
* per the task-reminder principle above, a reminder injected at the decision
|
|
71
|
-
* turn is the lever that actually works — the harness remembers so the model
|
|
72
|
-
* does not have to.
|
|
73
|
-
*/
|
|
74
2
|
/**
|
|
75
3
|
* User-named model resolution (model-routing design v3.6): when the user's
|
|
76
4
|
* message names a model that exists in the routable catalog, hand the main
|
|
@@ -119,21 +47,6 @@ export function userNamedModelReminder(input, routableModels, canSpawn) {
|
|
|
119
47
|
"Use these exact ids in spawn_agent / run_workflow `model` params; do not retype model ids from memory.",
|
|
120
48
|
].join("\n");
|
|
121
49
|
}
|
|
122
|
-
export function largeImplementationTaskReminder(input) {
|
|
123
|
-
const plan = input.pendingTodos > 0 ? ` (plan: ${input.pendingTodos} open todo items)` : "";
|
|
124
|
-
const applied = input.appliedEdits > 0
|
|
125
|
-
? ` ${input.appliedEdits} edit${input.appliedEdits === 1 ? "" : "s"} from your current batch will land regardless.`
|
|
126
|
-
: "";
|
|
127
|
-
const mechanism = input.orchestrationRequested
|
|
128
|
-
? "one run_workflow script (the user asked for a coordinated workflow — keep everything in it)"
|
|
129
|
-
: "write_worktree subagents (spawn_agent with the implementer profile) or one run_workflow script";
|
|
130
|
-
return wrapInSystemReminder(`
|
|
131
|
-
Large-change checkpoint: you have read ${input.exploredFiles} files this turn${plan} and are starting to edit.${applied}
|
|
132
|
-
- If the REMAINING edits form INDEPENDENT groups (per-module, per-file, same shape repeated), split them across ${mechanism}. Assign each child a DISJOINT file set; keep shared files (types, registries, exports) for yourself. Children fork from the last COMMIT — commit your applied edits first, or fold them into the child briefings. Each child still makes the smallest coherent edit for its group.
|
|
133
|
-
- If the edits are ENTANGLED (shared state, one file feeding the next, order matters), delegation only adds merge risk — proceed yourself.
|
|
134
|
-
- Work already small enough to finish directly: just finish it.
|
|
135
|
-
`);
|
|
136
|
-
}
|
|
137
50
|
const ORCHESTRATION_REQUEST = /\b(?:workflows?|orchestrat\w*|agent[ -]?teams?|fan[ -]?out)\b|工作流|编排|(?:智能体|代理|agent)\s*(?:团队|小队)/i;
|
|
138
51
|
export function orchestrationRequestReminder(input, canRunWorkflow) {
|
|
139
52
|
if (!canRunWorkflow)
|
|
@@ -17,6 +17,8 @@ export interface AiSdkProviderOptions {
|
|
|
17
17
|
apiKey: string;
|
|
18
18
|
baseURL?: string;
|
|
19
19
|
thinkingLevel?: ThinkingLevel;
|
|
20
|
+
/** User-configured extra headers, merged last (client-identity gates). */
|
|
21
|
+
headers?: Record<string, string>;
|
|
20
22
|
/** Transport override for tests; defaults to the shared provider fetch. */
|
|
21
23
|
fetch?: ProviderFetch;
|
|
22
24
|
}
|
package/dist/provider-ai-sdk.js
CHANGED
|
@@ -21,6 +21,7 @@ const AI_SDK_PROVIDER_FACTORIES = {
|
|
|
21
21
|
const provider = createGoogleGenerativeAI({
|
|
22
22
|
apiKey: options.apiKey,
|
|
23
23
|
...(options.baseURL ? { baseURL: normalizeBaseURL(options.baseURL) } : {}),
|
|
24
|
+
...(options.headers ? { headers: options.headers } : {}),
|
|
24
25
|
fetch: (options.fetch ?? createProviderFetch({
|
|
25
26
|
providerName: "Google Gemini",
|
|
26
27
|
verboseEnvVar: "BUBBLE_AI_SDK_FETCH_VERBOSE",
|
|
@@ -7,6 +7,8 @@ export interface AnthropicProviderOptions {
|
|
|
7
7
|
apiKey: string;
|
|
8
8
|
baseURL: string;
|
|
9
9
|
thinkingLevel?: ThinkingLevel;
|
|
10
|
+
/** User-configured extra headers, merged last (client-identity gates). */
|
|
11
|
+
headers?: Record<string, string>;
|
|
10
12
|
}
|
|
11
13
|
interface AnthropicRequest {
|
|
12
14
|
model: string;
|
|
@@ -627,7 +627,9 @@ function buildAnthropicHeaders(options, stream) {
|
|
|
627
627
|
}
|
|
628
628
|
if (stream)
|
|
629
629
|
headers.accept = "text/event-stream";
|
|
630
|
-
|
|
630
|
+
// User-configured headers win last: anthropic-protocol coding plans gate on
|
|
631
|
+
// client identity (e.g. a specific User-Agent), configured per provider.
|
|
632
|
+
return { ...headers, ...(options.headers ?? {}) };
|
|
631
633
|
}
|
|
632
634
|
async function readAnthropicErrorDetail(response) {
|
|
633
635
|
try {
|
|
@@ -4,6 +4,8 @@ export interface ArkResponsesProviderOptions {
|
|
|
4
4
|
apiKey: string;
|
|
5
5
|
baseURL: string;
|
|
6
6
|
thinkingLevel?: ThinkingLevel;
|
|
7
|
+
/** User-configured extra headers, merged last (client-identity gates). */
|
|
8
|
+
headers?: Record<string, string>;
|
|
7
9
|
}
|
|
8
10
|
export declare function createArkResponsesProvider(options: ArkResponsesProviderOptions): Provider;
|
|
9
11
|
export declare function buildArkResponsesBody(messages: ProviderMessage[], options: {
|
|
@@ -124,6 +124,7 @@ async function sendArkResponsesRequest(options, body, requestOptions) {
|
|
|
124
124
|
"Authorization": `Bearer ${options.apiKey}`,
|
|
125
125
|
"Content-Type": "application/json",
|
|
126
126
|
"Accept": body.stream ? "text/event-stream" : "application/json",
|
|
127
|
+
...(options.headers ?? {}),
|
|
127
128
|
},
|
|
128
129
|
body: JSON.stringify(body),
|
|
129
130
|
signal: requestOptions.signal,
|
package/dist/provider-grok.d.ts
CHANGED
|
@@ -9,9 +9,21 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { OAuthCredentials } from "./oauth/types.js";
|
|
11
11
|
import { type ChatGptFetch } from "./network/chatgpt-transport.js";
|
|
12
|
+
import type { ThinkingLevel } from "./types.js";
|
|
12
13
|
export declare const GROK_SUBSCRIPTION_BASE_URL = "https://cli-chat-proxy.grok.com/v1";
|
|
13
14
|
export declare const GROK_CLIENT_VERSION = "0.2.93";
|
|
14
15
|
export declare function isGrokSubscriptionBaseUrl(baseURL: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Infer reasoning levels for a Grok model id the /models endpoint returned
|
|
18
|
+
* without metadata. Flagship reasoning models (grok-N.M) expose
|
|
19
|
+
* low/medium/high with thinking always on — matching the curated grok-4.5
|
|
20
|
+
* entry — while composer/fast/mini/flash variants have no effort control and
|
|
21
|
+
* map to the plain "off" ladder.
|
|
22
|
+
*/
|
|
23
|
+
export declare function inferGrokReasoningLevels(modelId: string): {
|
|
24
|
+
levels: ThinkingLevel[];
|
|
25
|
+
defaultLevel?: ThinkingLevel;
|
|
26
|
+
};
|
|
15
27
|
export declare function buildGrokSubscriptionHeaders(): Record<string, string>;
|
|
16
28
|
export interface GrokAuthAdapter {
|
|
17
29
|
getCredentials: () => OAuthCredentials | undefined | Promise<OAuthCredentials | undefined>;
|
|
@@ -24,3 +36,17 @@ export interface GrokAuthAdapter {
|
|
|
24
36
|
* proactively refreshes) the stored OAuth credentials per request instead.
|
|
25
37
|
*/
|
|
26
38
|
export declare function createGrokSubscriptionFetch(auth: GrokAuthAdapter, baseFetch?: ChatGptFetch): ChatGptFetch;
|
|
39
|
+
/**
|
|
40
|
+
* Fetch the model list for a Grok subscription account. The CLI chat proxy
|
|
41
|
+
* gates on the grok-cli identity headers plus a fresh OAuth bearer, so this
|
|
42
|
+
* rides the same refreshing fetch used for chat requests instead of a raw
|
|
43
|
+
* global fetch. Throws on a non-2xx so callers fall back to the curated
|
|
44
|
+
* catalog when the proxy exposes no /models endpoint.
|
|
45
|
+
*/
|
|
46
|
+
export declare function fetchGrokSubscriptionModels(baseURL: string, auth: GrokAuthAdapter, options?: {
|
|
47
|
+
timeoutMs?: number;
|
|
48
|
+
fetch?: ChatGptFetch;
|
|
49
|
+
}): Promise<Array<{
|
|
50
|
+
id: string;
|
|
51
|
+
name?: string;
|
|
52
|
+
}>>;
|
package/dist/provider-grok.js
CHANGED
|
@@ -17,6 +17,20 @@ export function isGrokSubscriptionBaseUrl(baseURL) {
|
|
|
17
17
|
const normalized = baseURL.trim().replace(/\/+$/, "");
|
|
18
18
|
return normalized === GROK_SUBSCRIPTION_BASE_URL || normalized.startsWith(`${GROK_SUBSCRIPTION_BASE_URL}/`);
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Infer reasoning levels for a Grok model id the /models endpoint returned
|
|
22
|
+
* without metadata. Flagship reasoning models (grok-N.M) expose
|
|
23
|
+
* low/medium/high with thinking always on — matching the curated grok-4.5
|
|
24
|
+
* entry — while composer/fast/mini/flash variants have no effort control and
|
|
25
|
+
* map to the plain "off" ladder.
|
|
26
|
+
*/
|
|
27
|
+
export function inferGrokReasoningLevels(modelId) {
|
|
28
|
+
const id = modelId.toLowerCase();
|
|
29
|
+
if (/(composer|fast|mini|flash)/.test(id)) {
|
|
30
|
+
return { levels: ["off"] };
|
|
31
|
+
}
|
|
32
|
+
return { levels: ["low", "medium", "high"], defaultLevel: "high" };
|
|
33
|
+
}
|
|
20
34
|
export function buildGrokSubscriptionHeaders() {
|
|
21
35
|
return {
|
|
22
36
|
"User-Agent": "grok-cli",
|
|
@@ -57,3 +71,28 @@ export function createGrokSubscriptionFetch(auth, baseFetch = getChatGptFetch())
|
|
|
57
71
|
return baseFetch(input, { ...(init ?? {}), headers });
|
|
58
72
|
};
|
|
59
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Fetch the model list for a Grok subscription account. The CLI chat proxy
|
|
76
|
+
* gates on the grok-cli identity headers plus a fresh OAuth bearer, so this
|
|
77
|
+
* rides the same refreshing fetch used for chat requests instead of a raw
|
|
78
|
+
* global fetch. Throws on a non-2xx so callers fall back to the curated
|
|
79
|
+
* catalog when the proxy exposes no /models endpoint.
|
|
80
|
+
*/
|
|
81
|
+
export async function fetchGrokSubscriptionModels(baseURL, auth, options = {}) {
|
|
82
|
+
const fetchImpl = createGrokSubscriptionFetch(auth, options.fetch);
|
|
83
|
+
const base = baseURL.trim().replace(/\/+$/, "");
|
|
84
|
+
const response = await fetchImpl(`${base}/models`, {
|
|
85
|
+
headers: buildGrokSubscriptionHeaders(),
|
|
86
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 5_000),
|
|
87
|
+
});
|
|
88
|
+
if (!response.ok)
|
|
89
|
+
throw new Error(`HTTP ${response.status}`);
|
|
90
|
+
const payload = (await response.json());
|
|
91
|
+
const entries = payload.data ?? payload.models ?? [];
|
|
92
|
+
return entries
|
|
93
|
+
.filter((entry) => typeof entry?.id === "string" && entry.id.trim().length > 0)
|
|
94
|
+
.map((entry) => ({
|
|
95
|
+
id: entry.id,
|
|
96
|
+
name: typeof entry.name === "string" ? entry.name : undefined,
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
@@ -9,7 +9,7 @@ import { type ProviderProtocol } from "./model-catalog.js";
|
|
|
9
9
|
import { ModelConfig } from "./model-config.js";
|
|
10
10
|
import { AuthStorage } from "./oauth/index.js";
|
|
11
11
|
import { type OpenAICodexAuthAdapter } from "./provider-openai-codex.js";
|
|
12
|
-
import type
|
|
12
|
+
import { type GrokAuthAdapter } from "./provider-grok.js";
|
|
13
13
|
import type { ThinkingLevel } from "./types.js";
|
|
14
14
|
export interface ProviderProfile {
|
|
15
15
|
id: string;
|
|
@@ -19,7 +19,20 @@ export interface ProviderProfile {
|
|
|
19
19
|
enabled: boolean;
|
|
20
20
|
authType?: "api" | "oauth";
|
|
21
21
|
protocol?: ProviderProtocol;
|
|
22
|
+
/**
|
|
23
|
+
* Extra request headers sent with every call to this provider. The knob for
|
|
24
|
+
* client-identity gates: coding-plan endpoints commonly allowlist specific
|
|
25
|
+
* User-Agent strings (Grok wants "grok-cli", plans that "support Claude
|
|
26
|
+
* Code" check for its UA), so users can satisfy them from config without
|
|
27
|
+
* code changes. Merged over protocol defaults; same-name keys win.
|
|
28
|
+
*/
|
|
29
|
+
headers?: Record<string, string>;
|
|
22
30
|
}
|
|
31
|
+
/** True for providers speaking the OpenAI chat-completions protocol (the default). */
|
|
32
|
+
export declare function isOpenAICompatibleProtocol(protocol: ProviderProtocol | undefined): boolean;
|
|
33
|
+
export declare function isLikelyChatModelId(id: string): boolean;
|
|
34
|
+
/** Keep only string-valued headers; returns undefined for empty/invalid maps. */
|
|
35
|
+
export declare function sanitizeProviderHeaders(value: unknown): Record<string, string> | undefined;
|
|
23
36
|
export interface ModelInfo {
|
|
24
37
|
id: string;
|
|
25
38
|
name: string;
|