@bubblebrain-ai/bubble 0.0.12 → 0.0.14
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/execution-governor.js +1 -1
- package/dist/agent/input-controller.d.ts +11 -0
- package/dist/agent/input-controller.js +30 -0
- package/dist/agent/tool-intent.js +1 -0
- package/dist/agent.d.ts +8 -4
- package/dist/agent.js +623 -312
- package/dist/approval/controller.d.ts +1 -0
- package/dist/approval/controller.js +20 -3
- package/dist/approval/tool-helper.js +2 -0
- package/dist/approval/types.d.ts +14 -1
- package/dist/context/compact.js +9 -3
- package/dist/context/projector.js +27 -12
- package/dist/debug-trace.d.ts +27 -0
- package/dist/debug-trace.js +385 -0
- package/dist/feishu/agent-host/approval-card.js +9 -0
- package/dist/feishu/serve.js +7 -1
- package/dist/main.js +86 -9
- package/dist/model-catalog.js +1 -0
- package/dist/orchestrator/default-hooks.js +19 -8
- package/dist/orchestrator/hooks.d.ts +1 -0
- package/dist/prompt/environment.js +2 -0
- package/dist/prompt/reminders.d.ts +5 -6
- package/dist/prompt/reminders.js +8 -9
- package/dist/prompt/runtime.js +2 -2
- package/dist/provider-openai-codex.d.ts +7 -0
- package/dist/provider-openai-codex.js +265 -124
- package/dist/provider-registry.d.ts +2 -0
- package/dist/provider-registry.js +58 -9
- package/dist/provider.d.ts +3 -0
- package/dist/provider.js +5 -1
- package/dist/session-log.js +13 -1
- package/dist/slash-commands/commands.js +39 -0
- package/dist/slash-commands/types.d.ts +12 -0
- package/dist/stats/usage.d.ts +52 -0
- package/dist/stats/usage.js +414 -0
- package/dist/tools/apply-patch.d.ts +9 -0
- package/dist/tools/apply-patch.js +330 -0
- package/dist/tools/bash.js +205 -44
- package/dist/tools/edit-apply.d.ts +5 -2
- package/dist/tools/edit-apply.js +221 -31
- package/dist/tools/edit.js +12 -3
- package/dist/tools/file-mutation-queue.d.ts +1 -0
- package/dist/tools/file-mutation-queue.js +12 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +7 -1
- package/dist/tools/patch-apply.d.ts +41 -0
- package/dist/tools/patch-apply.js +312 -0
- package/dist/tools/server-manager.d.ts +36 -0
- package/dist/tools/server-manager.js +234 -0
- package/dist/tools/server.d.ts +6 -0
- package/dist/tools/server.js +245 -0
- package/dist/tools/write.d.ts +3 -6
- package/dist/tools/write.js +26 -46
- package/dist/tui/clipboard.d.ts +1 -0
- package/dist/tui/clipboard.js +53 -0
- package/dist/tui/detect-theme.d.ts +2 -0
- package/dist/tui/detect-theme.js +87 -0
- package/dist/tui/display-history.d.ts +63 -0
- package/dist/tui/display-history.js +306 -0
- package/dist/tui/edit-diff.d.ts +11 -0
- package/dist/tui/edit-diff.js +57 -0
- package/dist/tui/escape-confirmation.d.ts +15 -0
- package/dist/tui/escape-confirmation.js +30 -0
- package/dist/tui/file-mentions.d.ts +29 -0
- package/dist/tui/file-mentions.js +174 -0
- package/dist/tui/global-key-router.d.ts +3 -0
- package/dist/tui/global-key-router.js +87 -0
- package/dist/tui/image-paste.d.ts +95 -0
- package/dist/tui/image-paste.js +505 -0
- package/dist/tui/input-history.d.ts +16 -0
- package/dist/tui/input-history.js +79 -0
- package/dist/tui/markdown-inline.d.ts +22 -0
- package/dist/tui/markdown-inline.js +68 -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/model-picker-data.d.ts +10 -0
- package/dist/tui/model-picker-data.js +32 -0
- package/dist/tui/opencode-spinner.d.ts +22 -0
- package/dist/tui/opencode-spinner.js +216 -0
- package/dist/tui/prompt-keybindings.d.ts +42 -0
- package/dist/tui/prompt-keybindings.js +35 -0
- package/dist/tui/recent-activity.d.ts +8 -0
- package/dist/tui/recent-activity.js +71 -0
- package/dist/tui/render-signature.d.ts +1 -0
- package/dist/tui/render-signature.js +7 -0
- package/dist/tui/run.d.ts +45 -0
- package/dist/tui/run.js +9359 -0
- package/dist/tui/session-display.d.ts +6 -0
- package/dist/tui/session-display.js +12 -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/tui/streaming-tool-args.d.ts +15 -0
- package/dist/tui/streaming-tool-args.js +30 -0
- package/dist/tui/tool-renderers/fallback.d.ts +2 -0
- package/dist/tui/tool-renderers/fallback.js +75 -0
- package/dist/tui/tool-renderers/registry.d.ts +3 -0
- package/dist/tui/tool-renderers/registry.js +11 -0
- package/dist/tui/tool-renderers/subagent.d.ts +2 -0
- package/dist/tui/tool-renderers/subagent.js +135 -0
- package/dist/tui/tool-renderers/types.d.ts +36 -0
- package/dist/tui/tool-renderers/types.js +1 -0
- package/dist/tui/tool-renderers/write-preview.d.ts +12 -0
- package/dist/tui/tool-renderers/write-preview.js +32 -0
- package/dist/tui/tool-renderers/write.d.ts +6 -0
- package/dist/tui/tool-renderers/write.js +88 -0
- package/dist/tui/trace-groups.d.ts +27 -0
- package/dist/tui/trace-groups.js +419 -0
- package/dist/tui/wordmark.d.ts +15 -0
- package/dist/tui/wordmark.js +179 -0
- package/dist/tui-ink/app.js +45 -9
- package/dist/tui-ink/approval/approval-dialog.js +7 -1
- package/dist/tui-ink/display-history.d.ts +1 -0
- package/dist/tui-ink/display-history.js +5 -4
- package/dist/tui-ink/message-list.js +23 -9
- package/dist/tui-ink/theme.d.ts +3 -9
- package/dist/tui-ink/theme.js +39 -45
- package/dist/tui-ink/trace-groups.js +1 -1
- package/dist/tui-ink/welcome.js +22 -78
- package/dist/tui-opentui/app.d.ts +54 -0
- package/dist/tui-opentui/app.js +1365 -0
- package/dist/tui-opentui/approval/approval-dialog.d.ts +15 -0
- package/dist/tui-opentui/approval/approval-dialog.js +145 -0
- package/dist/tui-opentui/approval/diff-view.d.ts +9 -0
- package/dist/tui-opentui/approval/diff-view.js +43 -0
- package/dist/tui-opentui/approval/select.d.ts +37 -0
- package/dist/tui-opentui/approval/select.js +91 -0
- package/dist/tui-opentui/detect-theme.d.ts +2 -0
- package/dist/tui-opentui/detect-theme.js +87 -0
- package/dist/tui-opentui/display-history.d.ts +56 -0
- package/dist/tui-opentui/display-history.js +130 -0
- package/dist/tui-opentui/edit-diff.d.ts +11 -0
- package/dist/tui-opentui/edit-diff.js +57 -0
- package/dist/tui-opentui/feedback-dialog.d.ts +21 -0
- package/dist/tui-opentui/feedback-dialog.js +164 -0
- package/dist/tui-opentui/feishu-setup-picker.d.ts +7 -0
- package/dist/tui-opentui/feishu-setup-picker.js +272 -0
- package/dist/tui-opentui/file-mentions.d.ts +29 -0
- package/dist/tui-opentui/file-mentions.js +174 -0
- package/dist/tui-opentui/footer.d.ts +26 -0
- package/dist/tui-opentui/footer.js +40 -0
- package/dist/tui-opentui/image-paste.d.ts +54 -0
- package/dist/tui-opentui/image-paste.js +288 -0
- package/dist/tui-opentui/input-box.d.ts +34 -0
- package/dist/tui-opentui/input-box.js +471 -0
- package/dist/tui-opentui/input-history.d.ts +16 -0
- package/dist/tui-opentui/input-history.js +79 -0
- package/dist/tui-opentui/markdown.d.ts +66 -0
- package/dist/tui-opentui/markdown.js +127 -0
- package/dist/tui-opentui/message-list.d.ts +31 -0
- package/dist/tui-opentui/message-list.js +128 -0
- package/dist/tui-opentui/model-picker.d.ts +63 -0
- package/dist/tui-opentui/model-picker.js +450 -0
- package/dist/tui-opentui/plan-confirm.d.ts +9 -0
- package/dist/tui-opentui/plan-confirm.js +124 -0
- package/dist/tui-opentui/question-dialog.d.ts +10 -0
- package/dist/tui-opentui/question-dialog.js +110 -0
- package/dist/tui-opentui/recent-activity.d.ts +8 -0
- package/dist/tui-opentui/recent-activity.js +71 -0
- package/dist/tui-opentui/run-session-picker.d.ts +10 -0
- package/dist/tui-opentui/run-session-picker.js +28 -0
- package/dist/tui-opentui/run.d.ts +38 -0
- package/dist/tui-opentui/run.js +48 -0
- package/dist/tui-opentui/session-picker.d.ts +12 -0
- package/dist/tui-opentui/session-picker.js +120 -0
- package/dist/tui-opentui/theme.d.ts +89 -0
- package/dist/tui-opentui/theme.js +157 -0
- package/dist/tui-opentui/todos.d.ts +9 -0
- package/dist/tui-opentui/todos.js +45 -0
- package/dist/tui-opentui/trace-groups.d.ts +27 -0
- package/dist/tui-opentui/trace-groups.js +419 -0
- package/dist/tui-opentui/use-terminal-size.d.ts +4 -0
- package/dist/tui-opentui/use-terminal-size.js +5 -0
- package/dist/tui-opentui/welcome.d.ts +25 -0
- package/dist/tui-opentui/welcome.js +77 -0
- package/dist/types.d.ts +36 -2
- package/package.json +5 -1
package/dist/main.js
CHANGED
|
@@ -25,6 +25,9 @@ import { McpManager } from "./mcp/manager.js";
|
|
|
25
25
|
import { QuestionController } from "./question/index.js";
|
|
26
26
|
import { buildMemoryPrompt, formatMemoryStartupResult, recordMemoryCitations, runMemoryPhase2, runMemoryStartupPipeline, startMemoryStartupTask, } from "./memory/index.js";
|
|
27
27
|
import { basename } from "node:path";
|
|
28
|
+
import { normalizeSingleLine, truncateVisual } from "./text-display.js";
|
|
29
|
+
import { BUBBLE_WORDMARK } from "./tui/wordmark.js";
|
|
30
|
+
import { configureDebugTrace, summarizeAgentEventForTrace, summarizeTraceMessage, traceEvent, } from "./debug-trace.js";
|
|
28
31
|
async function main() {
|
|
29
32
|
const args = parseArgs(process.argv.slice(2));
|
|
30
33
|
if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
@@ -70,6 +73,7 @@ async function main() {
|
|
|
70
73
|
baseURL: defaultProvider.baseURL,
|
|
71
74
|
thinkingLevel: args.thinkingLevel,
|
|
72
75
|
promptCacheKey: sessionPromptCacheKey,
|
|
76
|
+
openAICodexAuth: registry.createOpenAICodexAuthAdapter(defaultProvider.id),
|
|
73
77
|
})
|
|
74
78
|
: createUnavailableProvider(unavailableProviderMessage);
|
|
75
79
|
const createProvider = (providerId, apiKey, baseURL) => createProviderInstance({
|
|
@@ -78,6 +82,7 @@ async function main() {
|
|
|
78
82
|
baseURL,
|
|
79
83
|
thinkingLevel: args.thinkingLevel,
|
|
80
84
|
promptCacheKey: sessionPromptCacheKey,
|
|
85
|
+
openAICodexAuth: registry.createOpenAICodexAuthAdapter(providerId),
|
|
81
86
|
});
|
|
82
87
|
const createProviderForRoute = async (route) => {
|
|
83
88
|
const providerId = route.providerId;
|
|
@@ -183,7 +188,7 @@ async function main() {
|
|
|
183
188
|
// - --resume (no name): show interactive picker
|
|
184
189
|
let sessionManager;
|
|
185
190
|
let resumedExistingSession = false;
|
|
186
|
-
// Resolved before any
|
|
191
|
+
// Resolved before any TUI render so picker and main TUI share the same value
|
|
187
192
|
// and we only run OSC 11 once.
|
|
188
193
|
let preResolvedTheme;
|
|
189
194
|
if (args.resume && !args.sessionName) {
|
|
@@ -195,13 +200,13 @@ async function main() {
|
|
|
195
200
|
else {
|
|
196
201
|
const themeConfig = userConfig.getTheme();
|
|
197
202
|
if (themeConfig.mode === "auto") {
|
|
198
|
-
const { detectTerminalTheme } = await import("./tui
|
|
203
|
+
const { detectTerminalTheme } = await import("./tui/detect-theme.js");
|
|
199
204
|
preResolvedTheme = await detectTerminalTheme();
|
|
200
205
|
}
|
|
201
206
|
else {
|
|
202
207
|
preResolvedTheme = themeConfig.mode;
|
|
203
208
|
}
|
|
204
|
-
const { runSessionPicker } = await import("./tui-
|
|
209
|
+
const { runSessionPicker } = await import("./tui-opentui/run-session-picker.js");
|
|
205
210
|
const picked = await runSessionPicker({
|
|
206
211
|
currentCwd: args.cwd,
|
|
207
212
|
currentSessions,
|
|
@@ -274,6 +279,25 @@ async function main() {
|
|
|
274
279
|
tools: tools.map((tool) => tool.name),
|
|
275
280
|
memoryPrompt,
|
|
276
281
|
});
|
|
282
|
+
const traceInfo = configureDebugTrace({
|
|
283
|
+
cwd: args.cwd,
|
|
284
|
+
sessionFile: sessionManager?.getSessionFile(),
|
|
285
|
+
provider: activeProviderId || "none",
|
|
286
|
+
model: activeModel || "none",
|
|
287
|
+
renderer: printMode ? "print" : "opentui-core",
|
|
288
|
+
});
|
|
289
|
+
if (traceInfo.enabled) {
|
|
290
|
+
traceEvent("run_start", {
|
|
291
|
+
tracePath: traceInfo.path,
|
|
292
|
+
rawEnabled: traceInfo.rawEnabled,
|
|
293
|
+
resumed: resumedExistingSession,
|
|
294
|
+
printMode,
|
|
295
|
+
mode: initialMode,
|
|
296
|
+
thinkingLevel: initialThinkingLevel,
|
|
297
|
+
tools: tools.length,
|
|
298
|
+
cwd: args.cwd,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
277
301
|
const budgetLedger = new BudgetLedger();
|
|
278
302
|
let sessionTitleUpdater;
|
|
279
303
|
const agent = new Agent({
|
|
@@ -299,6 +323,9 @@ async function main() {
|
|
|
299
323
|
if (message.role === "meta")
|
|
300
324
|
return;
|
|
301
325
|
sessionManager.appendMessage(message);
|
|
326
|
+
traceEvent("session_message_persisted", {
|
|
327
|
+
message: summarizeTraceMessage(message),
|
|
328
|
+
});
|
|
302
329
|
sessionTitleUpdater?.handlePersistedMessage(message);
|
|
303
330
|
if (message.role === "assistant") {
|
|
304
331
|
recordMemoryCitations(args.cwd, message.content);
|
|
@@ -402,6 +429,7 @@ async function main() {
|
|
|
402
429
|
process.exit(1);
|
|
403
430
|
}
|
|
404
431
|
for await (const event of agent.run(prompt, args.cwd)) {
|
|
432
|
+
traceEvent("print_agent_event", summarizeAgentEventForTrace(event));
|
|
405
433
|
if (event.type === "text_delta") {
|
|
406
434
|
process.stdout.write(event.content);
|
|
407
435
|
}
|
|
@@ -422,9 +450,9 @@ async function main() {
|
|
|
422
450
|
detectedTheme = preResolvedTheme;
|
|
423
451
|
}
|
|
424
452
|
else if (themeConfig.mode === "auto") {
|
|
425
|
-
// Probe before
|
|
426
|
-
// runtime
|
|
427
|
-
const { detectTerminalTheme } = await import("./tui
|
|
453
|
+
// Probe before OpenTUI owns stdin. OSC 11 needs raw mode, and the
|
|
454
|
+
// runtime renderer can consume the reply before startup code sees it.
|
|
455
|
+
const { detectTerminalTheme } = await import("./tui/detect-theme.js");
|
|
428
456
|
detectedTheme = await detectTerminalTheme();
|
|
429
457
|
}
|
|
430
458
|
else {
|
|
@@ -447,7 +475,7 @@ async function main() {
|
|
|
447
475
|
runMemorySummary,
|
|
448
476
|
runMemoryRefresh,
|
|
449
477
|
};
|
|
450
|
-
const { runTui } = await import("./tui
|
|
478
|
+
const { runTui } = await import("./tui/run.js");
|
|
451
479
|
await runTui(agent, args, {
|
|
452
480
|
...commonOptions,
|
|
453
481
|
themeMode: themeConfig.mode,
|
|
@@ -456,13 +484,62 @@ async function main() {
|
|
|
456
484
|
onThemeModeChange: (mode) => userConfig.setThemeMode(mode),
|
|
457
485
|
});
|
|
458
486
|
if (sessionManager) {
|
|
459
|
-
|
|
460
|
-
|
|
487
|
+
printOpenTuiExitSummary(sessionManager, {
|
|
488
|
+
resumed: resumedExistingSession,
|
|
489
|
+
theme: detectedTheme,
|
|
490
|
+
});
|
|
461
491
|
}
|
|
462
492
|
}
|
|
463
493
|
finally {
|
|
494
|
+
traceEvent("run_shutdown_start");
|
|
464
495
|
await shutdownRuntime();
|
|
496
|
+
traceEvent("run_shutdown_end");
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
function printOpenTuiExitSummary(sessionManager, options) {
|
|
500
|
+
if (!process.stdout.isTTY)
|
|
501
|
+
return;
|
|
502
|
+
const sessionName = basename(sessionManager.getSessionFile());
|
|
503
|
+
const sessionId = sessionName.replace(/\.jsonl$/, "");
|
|
504
|
+
const title = truncateVisual(normalizeSingleLine(sessionManager.getMetadata().title ?? ""), 64);
|
|
505
|
+
const sessionLabel = title || `${options.resumed ? "Session" : "New session"} - ${sessionId}`;
|
|
506
|
+
const continueCommand = `bubble --resume --session ${sessionName}`;
|
|
507
|
+
const colors = options.theme === "light"
|
|
508
|
+
? {
|
|
509
|
+
markMuted: chalk.hex("#8C8C8C"),
|
|
510
|
+
markStrong: chalk.hex("#1C1C1C"),
|
|
511
|
+
markBrand: chalk.hex("#8B4A00"),
|
|
512
|
+
label: chalk.hex("#6F7377"),
|
|
513
|
+
value: chalk.hex("#171717").bold,
|
|
514
|
+
}
|
|
515
|
+
: {
|
|
516
|
+
markMuted: chalk.hex("#9CA3AF"),
|
|
517
|
+
markStrong: chalk.hex("#F4F4F5"),
|
|
518
|
+
markBrand: chalk.hex("#F5A742"),
|
|
519
|
+
label: chalk.hex("#808080"),
|
|
520
|
+
value: chalk.hex("#EEEEEE").bold,
|
|
521
|
+
};
|
|
522
|
+
const label = (value) => colors.label(value.padEnd(10));
|
|
523
|
+
const logoColor = (tone) => {
|
|
524
|
+
switch (tone) {
|
|
525
|
+
case "brand": return colors.markBrand;
|
|
526
|
+
case "ink": return colors.markStrong;
|
|
527
|
+
case "stone": return colors.markMuted;
|
|
528
|
+
case "soft": return colors.label;
|
|
529
|
+
case "caption": return colors.label;
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
for (const line of BUBBLE_WORDMARK) {
|
|
533
|
+
if (line.segments) {
|
|
534
|
+
console.log(line.segments.map((segment) => logoColor(segment.tone)(segment.text)).join(""));
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
console.log(logoColor(line.tone ?? "caption")(line.text ?? ""));
|
|
538
|
+
}
|
|
465
539
|
}
|
|
540
|
+
console.log();
|
|
541
|
+
console.log(`${label("Session")}${colors.value(sessionLabel)}`);
|
|
542
|
+
console.log(`${label("Continue")}${colors.value(continueCommand)}`);
|
|
466
543
|
}
|
|
467
544
|
async function readPipedStdin() {
|
|
468
545
|
if (process.stdin.isTTY)
|
package/dist/model-catalog.js
CHANGED
|
@@ -25,6 +25,7 @@ const OPENAI_CHAT_LEVELS = ["off"];
|
|
|
25
25
|
const TOGGLE_THINKING_LEVELS = ["off", "medium"];
|
|
26
26
|
const DEEPSEEK_V4_LEVELS = ["high", "max"];
|
|
27
27
|
export const BUILTIN_MODELS = [
|
|
28
|
+
{ id: "gpt-5.5", name: "gpt-5.5", providerId: "openai-codex", reasoningLevels: ALL_OPENAI_LEVELS, contextWindow: 272000, toolOutputTokenLimit: 10000 },
|
|
28
29
|
{ id: "gpt-5.4", name: "gpt-5.4", providerId: "openai-codex", reasoningLevels: ALL_OPENAI_LEVELS, contextWindow: 272000 },
|
|
29
30
|
{ id: "gpt-5.4-mini", name: "gpt-5.4-mini", providerId: "openai-codex", reasoningLevels: ALL_OPENAI_LEVELS, contextWindow: 272000 },
|
|
30
31
|
{ id: "gpt-5.3-codex", name: "gpt-5.3-codex", providerId: "openai-codex", reasoningLevels: ALL_OPENAI_LEVELS, contextWindow: 272000 },
|
|
@@ -78,11 +78,18 @@ export function createDefaultHooks() {
|
|
|
78
78
|
}
|
|
79
79
|
ctx.state.evidenceTracker?.observe(ctx.toolCall, ctx.result);
|
|
80
80
|
ctx.state.governor?.afterToolResult(ctx.toolCall, ctx.result);
|
|
81
|
-
// Edit/write retry-escalation:
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
// Edit/write retry-escalation: models can spiral on "identical content"
|
|
82
|
+
// or "not found" errors. Nudge them to re-ground or switch strategy.
|
|
83
|
+
if (isMutationTool(ctx.toolCall.name) && ctx.result.isError) {
|
|
84
|
+
if (ctx.toolCall.name === "edit" && ctx.result.status === "no_match" && ctx.result.metadata?.kind === "edit") {
|
|
85
|
+
const path = typeof ctx.result.metadata.path === "string" ? ctx.result.metadata.path : "";
|
|
86
|
+
const reminded = ctx.state.editNoMatchReminderPaths ?? (ctx.state.editNoMatchReminderPaths = []);
|
|
87
|
+
if (path && !reminded.includes(path)) {
|
|
88
|
+
reminded.push(path);
|
|
89
|
+
const summary = ctx.result.content.split("\n")[0] || "";
|
|
90
|
+
ctx.queueReminder(buildEditRetryEscalationReminder(`Edit oldText did not match ${path}. ${summary}`));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
86
93
|
const hash = hashEditCall(ctx.toolCall);
|
|
87
94
|
const history = ctx.state.recentEditFailures ?? (ctx.state.recentEditFailures = []);
|
|
88
95
|
history.push(hash);
|
|
@@ -96,10 +103,11 @@ export function createDefaultHooks() {
|
|
|
96
103
|
ctx.queueReminder(buildEditRetryEscalationReminder(`Last failure: ${ctx.toolCall.name} on the same target with identical arguments. ${summary}`));
|
|
97
104
|
}
|
|
98
105
|
}
|
|
99
|
-
else if ((ctx.toolCall.name
|
|
106
|
+
else if (isMutationTool(ctx.toolCall.name) && !ctx.result.isError) {
|
|
100
107
|
// Successful mutation resets the dedup state so a later, unrelated
|
|
101
108
|
// failure won't fire the reminder spuriously.
|
|
102
109
|
ctx.state.recentEditFailures = [];
|
|
110
|
+
ctx.state.editNoMatchReminderPaths = [];
|
|
103
111
|
ctx.state.editRetryReminderSent = false;
|
|
104
112
|
}
|
|
105
113
|
// Redundant-Read detection moved into the read tool itself: it now
|
|
@@ -151,10 +159,13 @@ function markCodeChanged(state) {
|
|
|
151
159
|
state.codeChanged = true;
|
|
152
160
|
}
|
|
153
161
|
function isCodeWriteResult(_toolCall, result) {
|
|
154
|
-
if (result.isError || result.status === "blocked" || result.status === "command_error") {
|
|
162
|
+
if (result.isError || result.status === "blocked" || result.status === "cancelled" || result.status === "command_error") {
|
|
155
163
|
return false;
|
|
156
164
|
}
|
|
157
|
-
return result.metadata?.kind === "write" || result.metadata?.kind === "edit";
|
|
165
|
+
return result.metadata?.kind === "write" || result.metadata?.kind === "edit" || result.metadata?.kind === "patch";
|
|
166
|
+
}
|
|
167
|
+
function isMutationTool(name) {
|
|
168
|
+
return name === "edit" || name === "write" || name === "apply_patch";
|
|
158
169
|
}
|
|
159
170
|
function hasSubagentLifecycleActivity(toolCalls, toolResults) {
|
|
160
171
|
return toolCalls.some((toolCall) => isSubagentLifecycleTool(toolCall.name))
|
|
@@ -16,6 +16,7 @@ export interface TurnHookState {
|
|
|
16
16
|
codeChanged?: boolean;
|
|
17
17
|
smallTaskHintSent?: boolean;
|
|
18
18
|
recentEditFailures?: string[];
|
|
19
|
+
editNoMatchReminderPaths?: string[];
|
|
19
20
|
editRetryReminderSent?: boolean;
|
|
20
21
|
recentReadPaths?: string[];
|
|
21
22
|
redundantReadReminded?: Set<string>;
|
|
@@ -3,6 +3,7 @@ export const defaultToolSnippets = {
|
|
|
3
3
|
read: "Read the contents of a file",
|
|
4
4
|
bash: "Execute a bash command",
|
|
5
5
|
edit: "Apply targeted string replacements to a file",
|
|
6
|
+
apply_patch: "Apply a structured patch for multi-file or larger code changes",
|
|
6
7
|
write: "Write a new file or overwrite an existing one",
|
|
7
8
|
glob: "Find files by glob pattern without using bash",
|
|
8
9
|
grep: "Search file contents using regex",
|
|
@@ -23,6 +24,7 @@ export const defaultToolNames = [
|
|
|
23
24
|
"glob",
|
|
24
25
|
"bash",
|
|
25
26
|
"edit",
|
|
27
|
+
"apply_patch",
|
|
26
28
|
"write",
|
|
27
29
|
"grep",
|
|
28
30
|
"lsp",
|
|
@@ -31,12 +31,11 @@ export declare function buildWorkflowPhaseReminder(input: {
|
|
|
31
31
|
}): string;
|
|
32
32
|
export declare function buildTaskSummaryReminder(): string;
|
|
33
33
|
/**
|
|
34
|
-
* Fired when
|
|
35
|
-
*
|
|
36
|
-
* ones — can otherwise spiral on `No changes made: identical
|
|
37
|
-
* `oldText not found` because their internal reasoning convinces
|
|
38
|
-
* are typing the change correctly
|
|
39
|
-
* This nudge forces a strategy change.
|
|
34
|
+
* Fired when a file mutation failure suggests the model may be relying on stale
|
|
35
|
+
* local memory instead of the current file bytes. Models — especially
|
|
36
|
+
* thinking-heavy ones — can otherwise spiral on `No changes made: identical
|
|
37
|
+
* content` or `oldText not found` because their internal reasoning convinces
|
|
38
|
+
* them they are typing the change correctly.
|
|
40
39
|
*/
|
|
41
40
|
export declare function buildEditRetryEscalationReminder(reason: string): string;
|
|
42
41
|
/**
|
package/dist/prompt/reminders.js
CHANGED
|
@@ -178,23 +178,22 @@ Treat the task output as a bounded subtask result:
|
|
|
178
178
|
// validation scripts that found the bug but could never fix it. CC trusts the
|
|
179
179
|
// model to decide when verification is meaningful; we follow that.
|
|
180
180
|
/**
|
|
181
|
-
* Fired when
|
|
182
|
-
*
|
|
183
|
-
* ones — can otherwise spiral on `No changes made: identical
|
|
184
|
-
* `oldText not found` because their internal reasoning convinces
|
|
185
|
-
* are typing the change correctly
|
|
186
|
-
* This nudge forces a strategy change.
|
|
181
|
+
* Fired when a file mutation failure suggests the model may be relying on stale
|
|
182
|
+
* local memory instead of the current file bytes. Models — especially
|
|
183
|
+
* thinking-heavy ones — can otherwise spiral on `No changes made: identical
|
|
184
|
+
* content` or `oldText not found` because their internal reasoning convinces
|
|
185
|
+
* them they are typing the change correctly.
|
|
187
186
|
*/
|
|
188
187
|
export function buildEditRetryEscalationReminder(reason) {
|
|
189
188
|
return wrapInSystemReminder(`
|
|
190
|
-
|
|
189
|
+
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.
|
|
191
190
|
|
|
192
191
|
${reason}
|
|
193
192
|
|
|
194
|
-
Stop retrying
|
|
193
|
+
Stop retrying from memory. Pick one of:
|
|
195
194
|
- 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.
|
|
196
195
|
- 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.
|
|
197
|
-
- Use the write tool with
|
|
196
|
+
- 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.
|
|
198
197
|
- If you cannot determine the cause, ask the user for clarification.
|
|
199
198
|
`);
|
|
200
199
|
}
|
package/dist/prompt/runtime.js
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
const defaultGuidelines = [
|
|
7
7
|
"Ground decisions in the codebase: inspect relevant files, command output, or runtime state before making claims about behavior. Separate confirmed facts from inference when evidence is incomplete.",
|
|
8
8
|
"Choose the smallest coherent change. Edit only the files required for the requested change; do not refactor or improve adjacent code unprompted.",
|
|
9
|
-
"For modifications to existing code, read the file first. For brand-new files whose target path is known and does not exist, write directly without exploratory reading. Use edit for small targeted changes
|
|
9
|
+
"For modifications to existing code, read the file first. For brand-new files whose target path is known and does not exist, write directly without exploratory reading. Use edit for small targeted changes, apply_patch for related multi-file or larger structured changes, and write for intentional full-file replacement of an existing file. Never delete and recreate a file just to overwrite it.",
|
|
10
10
|
"Prefer structured tools (glob, grep, lsp, read) over bash for search and inspection. Do not repeat a near-identical search or re-read the same file unless new evidence changes the question.",
|
|
11
|
-
"If a tool fails, diagnose the error before switching tactics. Do not retry the identical call with identical arguments. After two equivalent failures, switch approach — re-read the file, use a different tool, rewrite the whole file with write
|
|
11
|
+
"If a tool fails, diagnose the error before switching tactics. Do not retry the identical call with identical arguments. After two equivalent failures, switch approach — re-read the file, use a different tool, rewrite the whole file with write, or ask the user.",
|
|
12
12
|
"Before reporting a task complete, verify it works when verification is meaningful and cheap — run the existing test, execute the script, check the output. If no test exists, the change is purely declarative (static HTML/markdown/config), or running the code is not practical, state that explicitly rather than inventing a verification step. Do not write throwaway validation scripts to prove correctness; if there is no real check to run, report the change and stop.",
|
|
13
13
|
];
|
|
14
14
|
export function buildRuntimePrompt(options = {}) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Provider, ReasoningEffort, ThinkingLevel, TokenUsage } from "./types.js";
|
|
2
|
+
import type { OAuthCredentials } from "./oauth/types.js";
|
|
2
3
|
export interface CodexModelDescriptor {
|
|
3
4
|
id: string;
|
|
4
5
|
displayName?: string;
|
|
@@ -11,6 +12,11 @@ export interface CodexModelDescriptor {
|
|
|
11
12
|
}
|
|
12
13
|
export declare function isOpenAICodexBaseUrl(baseURL: string): boolean;
|
|
13
14
|
export declare function getOpenAICodexFallbackModels(): string[];
|
|
15
|
+
export interface OpenAICodexAuthAdapter {
|
|
16
|
+
getCredentials: () => OAuthCredentials | undefined | Promise<OAuthCredentials | undefined>;
|
|
17
|
+
refreshCredentials: (current?: OAuthCredentials) => Promise<OAuthCredentials>;
|
|
18
|
+
isExpired?: (credentials: OAuthCredentials, graceMs: number) => boolean;
|
|
19
|
+
}
|
|
14
20
|
export declare function extractChatGptAccountId(accessToken: string): string | undefined;
|
|
15
21
|
export declare function createOpenAICodexProvider(options: {
|
|
16
22
|
providerId?: string;
|
|
@@ -18,6 +24,7 @@ export declare function createOpenAICodexProvider(options: {
|
|
|
18
24
|
baseURL: string;
|
|
19
25
|
thinkingLevel?: ThinkingLevel;
|
|
20
26
|
promptCacheKey?: string;
|
|
27
|
+
auth?: OpenAICodexAuthAdapter;
|
|
21
28
|
}): Provider;
|
|
22
29
|
export declare function normalizeOpenAICodexUsage(usage: any): TokenUsage;
|
|
23
30
|
export declare function buildOpenAICodexPromptCacheKey(input: {
|