@oh-my-pi/pi-coding-agent 15.10.0 → 15.10.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/CHANGELOG.md +75 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/model-registry.d.ts +17 -1
- package/dist/types/config/settings-schema.d.ts +9 -0
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +3 -0
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +15 -5
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +18 -5
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +7 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +5 -9
- package/dist/types/tools/search.d.ts +4 -0
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +3 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/package.json +9 -9
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/dry-balance-cli.ts +52 -17
- package/src/cli/gallery-cli.ts +4 -1
- package/src/cli/gallery-fixtures/misc.ts +29 -0
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +33 -9
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/model-registry.ts +25 -2
- package/src/config/settings-schema.ts +10 -0
- package/src/config/settings.ts +20 -2
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +40 -54
- package/src/edit/renderer.ts +82 -78
- package/src/eval/__tests__/llm-bridge.test.ts +90 -31
- package/src/eval/llm-bridge.ts +8 -3
- package/src/goals/tools/goal-tool.ts +36 -26
- package/src/internal-urls/docs-index.generated.ts +6 -6
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +9 -7
- package/src/memories/index.ts +12 -5
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +23 -0
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/tips.txt +1 -0
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +68 -88
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +1 -2
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +57 -55
- package/src/modes/controllers/event-controller.ts +41 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +124 -119
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +23 -25
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/interactive-mode.ts +169 -94
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +18 -7
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/sdk.ts +11 -37
- package/src/session/agent-session.ts +82 -6
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +13 -5
- package/src/slash-commands/builtin-registry.ts +36 -9
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +5 -2
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +212 -147
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +50 -6
- package/src/tools/debug.ts +20 -8
- package/src/tools/fetch.ts +297 -7
- package/src/tools/find.ts +44 -30
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +8 -1
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/plan-mode-guard.ts +21 -39
- package/src/tools/read.ts +23 -16
- package/src/tools/render-utils.ts +21 -37
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +80 -78
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +118 -52
- package/src/tools/write.ts +81 -62
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +9 -1
- package/src/utils/enhanced-paste.ts +202 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
modelsAreEqual,
|
|
21
21
|
type UsageReport,
|
|
22
22
|
} from "@oh-my-pi/pi-ai";
|
|
23
|
-
import type { Component, EditorTheme, SlashCommand } from "@oh-my-pi/pi-tui";
|
|
23
|
+
import type { Component, EditorTheme, LoaderMessageColorFn, OverlayHandle, SlashCommand } from "@oh-my-pi/pi-tui";
|
|
24
24
|
import {
|
|
25
25
|
Container,
|
|
26
26
|
clearRenderCache,
|
|
@@ -65,12 +65,7 @@ import { BUILTIN_SLASH_COMMANDS, loadSlashCommands } from "../extensibility/slas
|
|
|
65
65
|
import type { Goal, GoalModeState } from "../goals/state";
|
|
66
66
|
import { resolveLocalUrlToPath } from "../internal-urls";
|
|
67
67
|
import { LSP_STARTUP_EVENT_CHANNEL, type LspStartupEvent } from "../lsp/startup-events";
|
|
68
|
-
import {
|
|
69
|
-
humanizePlanTitle,
|
|
70
|
-
type PlanApprovalDetails,
|
|
71
|
-
renameApprovedPlanFile,
|
|
72
|
-
resolvePlanTitle,
|
|
73
|
-
} from "../plan-mode/approved-plan";
|
|
68
|
+
import { humanizePlanTitle, type PlanApprovalDetails, resolveApprovedPlan } from "../plan-mode/approved-plan";
|
|
74
69
|
import planModeApprovedPrompt from "../prompts/system/plan-mode-approved.md" with { type: "text" };
|
|
75
70
|
import planModeCompactInstructionsPrompt from "../prompts/system/plan-mode-compact-instructions.md" with {
|
|
76
71
|
type: "text",
|
|
@@ -94,6 +89,7 @@ import { getSessionAccentAnsi, getSessionAccentHex } from "../utils/session-colo
|
|
|
94
89
|
import { popTerminalTitle, pushTerminalTitle, setSessionTerminalTitle } from "../utils/title-generator";
|
|
95
90
|
import type { AssistantMessageComponent } from "./components/assistant-message";
|
|
96
91
|
import type { BashExecutionComponent } from "./components/bash-execution";
|
|
92
|
+
import { ChatBlock, type ChatBlockHost } from "./components/chat-block";
|
|
97
93
|
import { CustomEditor } from "./components/custom-editor";
|
|
98
94
|
import { DynamicBorder } from "./components/dynamic-border";
|
|
99
95
|
import { ErrorBannerComponent } from "./components/error-banner";
|
|
@@ -101,6 +97,7 @@ import type { EvalExecutionComponent } from "./components/eval-execution";
|
|
|
101
97
|
import type { HookEditorComponent } from "./components/hook-editor";
|
|
102
98
|
import type { HookInputComponent } from "./components/hook-input";
|
|
103
99
|
import type { HookSelectorComponent, HookSelectorSlider } from "./components/hook-selector";
|
|
100
|
+
import { PlanReviewOverlay } from "./components/plan-review-overlay";
|
|
104
101
|
import { StatusLineComponent } from "./components/status-line";
|
|
105
102
|
import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
106
103
|
import { TranscriptContainer } from "./components/transcript-container";
|
|
@@ -114,6 +111,7 @@ import { MCPCommandController } from "./controllers/mcp-command-controller";
|
|
|
114
111
|
import { OmfgController } from "./controllers/omfg-controller";
|
|
115
112
|
import { SelectorController } from "./controllers/selector-controller";
|
|
116
113
|
import { SSHCommandController } from "./controllers/ssh-command-controller";
|
|
114
|
+
import { TanCommandController } from "./controllers/tan-command-controller";
|
|
117
115
|
import { TodoCommandController } from "./controllers/todo-command-controller";
|
|
118
116
|
import {
|
|
119
117
|
consumeLoopLimitIteration,
|
|
@@ -250,20 +248,6 @@ export interface InteractiveModeOptions {
|
|
|
250
248
|
initialMessages?: string[];
|
|
251
249
|
}
|
|
252
250
|
|
|
253
|
-
/**
|
|
254
|
-
* Plan-review preview block. Once rendered it is static (a one-shot Markdown of
|
|
255
|
-
* the plan file), so even while it sits as the live bottom block beneath the
|
|
256
|
-
* approval selector its scrolled-off head is safe to commit to native
|
|
257
|
-
* scrollback. Reporting append-only lets an over-tall plan + selector commit the
|
|
258
|
-
* plan's head instead of clipping it — without this a plain {@link Container} is
|
|
259
|
-
* deferred and a long plan is cut off the top on ED3-risk terminals.
|
|
260
|
-
*/
|
|
261
|
-
class PlanReviewBlock extends Container {
|
|
262
|
-
isTranscriptBlockAppendOnly(): boolean {
|
|
263
|
-
return true;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
251
|
export class InteractiveMode implements InteractiveModeContext {
|
|
268
252
|
session: AgentSession;
|
|
269
253
|
sessionManager: SessionManager;
|
|
@@ -287,7 +271,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
287
271
|
statusLine: StatusLineComponent;
|
|
288
272
|
|
|
289
273
|
isInitialized = false;
|
|
290
|
-
isBackgrounded = false;
|
|
291
274
|
isBashMode = false;
|
|
292
275
|
toolOutputExpanded = false;
|
|
293
276
|
todoExpanded = false;
|
|
@@ -355,12 +338,14 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
355
338
|
#planModePreviousModelState: { model: Model; thinkingLevel?: ThinkingLevel } | undefined;
|
|
356
339
|
#pendingModelSwitch: { model: Model; thinkingLevel?: ThinkingLevel } | undefined;
|
|
357
340
|
#planModeHasEntered = false;
|
|
358
|
-
#
|
|
341
|
+
#planReviewOverlay: PlanReviewOverlay | undefined;
|
|
342
|
+
#planReviewOverlayHandle: OverlayHandle | undefined;
|
|
359
343
|
readonly lspServers: LspStartupServerInfo[] | undefined = undefined;
|
|
360
344
|
mcpManager?: import("../mcp").MCPManager;
|
|
361
345
|
readonly #toolUiContextSetter: (uiContext: ExtensionUIContext, hasUI: boolean) => void;
|
|
362
346
|
|
|
363
347
|
readonly #btwController: BtwController;
|
|
348
|
+
readonly #tanCommandController: TanCommandController;
|
|
364
349
|
readonly #omfgController: OmfgController;
|
|
365
350
|
readonly #commandController: CommandController;
|
|
366
351
|
readonly #todoCommandController: TodoCommandController;
|
|
@@ -379,6 +364,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
379
364
|
#eventBus?: EventBus;
|
|
380
365
|
#eventBusUnsubscribers: Array<() => void> = [];
|
|
381
366
|
#welcomeComponent?: WelcomeComponent;
|
|
367
|
+
readonly #chatHost: ChatBlockHost = { requestRender: () => this.ui.requestRender() };
|
|
382
368
|
|
|
383
369
|
constructor(
|
|
384
370
|
session: AgentSession,
|
|
@@ -484,6 +470,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
484
470
|
|
|
485
471
|
this.#uiHelpers = new UiHelpers(this);
|
|
486
472
|
this.#btwController = new BtwController(this);
|
|
473
|
+
this.#tanCommandController = new TanCommandController(this);
|
|
487
474
|
this.#omfgController = new OmfgController(this);
|
|
488
475
|
this.#extensionUiController = new ExtensionUiController(this);
|
|
489
476
|
this.#eventController = new EventController(this);
|
|
@@ -613,8 +600,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
613
600
|
// Load initial todos
|
|
614
601
|
await this.#loadTodoList();
|
|
615
602
|
|
|
616
|
-
// Start the UI
|
|
617
|
-
|
|
603
|
+
// Start the UI. Cold `omp` launch opts into clearing on the first paint so
|
|
604
|
+
// the initial welcome frame does not append over the previous run's scrollback.
|
|
605
|
+
this.ui.start({ clearScrollback: options.clearInitialTerminalHistory === true });
|
|
618
606
|
pushTerminalTitle();
|
|
619
607
|
setSessionTerminalTitle(this.sessionManager.getSessionName(), this.sessionManager.getCwd());
|
|
620
608
|
this.updateEditorBorderColor();
|
|
@@ -1536,22 +1524,15 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1536
1524
|
if (!state?.enabled) {
|
|
1537
1525
|
throw new ToolError("Plan mode is not active.");
|
|
1538
1526
|
}
|
|
1539
|
-
const planFilePath =
|
|
1540
|
-
const planContent = await this.#readPlanFile(planFilePath);
|
|
1541
|
-
if (planContent === null) {
|
|
1542
|
-
throw new ToolError(
|
|
1543
|
-
`Plan file not found at ${planFilePath}. Write the finalized plan to ${planFilePath} before requesting approval.`,
|
|
1544
|
-
);
|
|
1545
|
-
}
|
|
1546
|
-
const normalized = resolvePlanTitle({
|
|
1527
|
+
const { planFilePath, title } = await resolveApprovedPlan({
|
|
1547
1528
|
suppliedTitle: extra?.title,
|
|
1548
|
-
|
|
1549
|
-
|
|
1529
|
+
statePlanFilePath: state.planFilePath,
|
|
1530
|
+
readPlan: url => this.#readPlanFile(url),
|
|
1531
|
+
listPlanFiles: () => this.#listLocalPlanFiles(),
|
|
1550
1532
|
});
|
|
1551
1533
|
const details: PlanApprovalDetails = {
|
|
1552
1534
|
planFilePath,
|
|
1553
|
-
|
|
1554
|
-
title: normalized.title,
|
|
1535
|
+
title,
|
|
1555
1536
|
planExists: true,
|
|
1556
1537
|
};
|
|
1557
1538
|
return {
|
|
@@ -1691,22 +1672,87 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1691
1672
|
}
|
|
1692
1673
|
}
|
|
1693
1674
|
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1675
|
+
/** `local://` URLs of plan files in the session-local root, newest first.
|
|
1676
|
+
* A fallback for `resolveApprovedPlan` when the agent dropped `extra.title`,
|
|
1677
|
+
* so the plan it wrote is still found by scanning recent `*-plan.md` files. */
|
|
1678
|
+
async #listLocalPlanFiles(): Promise<string[]> {
|
|
1679
|
+
const localRoot = this.#resolvePlanFilePath("local://");
|
|
1680
|
+
try {
|
|
1681
|
+
const entries = await fs.readdir(localRoot, { withFileTypes: true });
|
|
1682
|
+
const plans = await Promise.all(
|
|
1683
|
+
entries
|
|
1684
|
+
.filter(entry => entry.isFile() && /plan\.md$/i.test(entry.name))
|
|
1685
|
+
.map(async name => {
|
|
1686
|
+
const stat = await fs.stat(path.join(localRoot, name.name)).catch(() => null);
|
|
1687
|
+
return { url: `local://${name.name}`, mtime: stat?.mtimeMs ?? 0 };
|
|
1688
|
+
}),
|
|
1689
|
+
);
|
|
1690
|
+
return plans.sort((a, b) => b.mtime - a.mtime).map(plan => plan.url);
|
|
1691
|
+
} catch {
|
|
1692
|
+
return [];
|
|
1707
1693
|
}
|
|
1708
|
-
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
showPlanReview(
|
|
1697
|
+
planContent: string,
|
|
1698
|
+
title: string,
|
|
1699
|
+
options: string[],
|
|
1700
|
+
dialogOptions?: {
|
|
1701
|
+
helpText?: string;
|
|
1702
|
+
disabledIndices?: number[];
|
|
1703
|
+
onExternalEditor?: () => void;
|
|
1704
|
+
onPlanEdited?: (content: string) => void;
|
|
1705
|
+
onFeedbackChange?: (feedback: string) => void;
|
|
1706
|
+
initialIndex?: number;
|
|
1707
|
+
},
|
|
1708
|
+
extra?: { slider?: HookSelectorSlider },
|
|
1709
|
+
): Promise<string | undefined> {
|
|
1710
|
+
this.#hidePlanReview();
|
|
1711
|
+
const { promise, resolve } = Promise.withResolvers<string | undefined>();
|
|
1712
|
+
let settled = false;
|
|
1713
|
+
const finish = (choice: string | undefined): void => {
|
|
1714
|
+
if (settled) return;
|
|
1715
|
+
settled = true;
|
|
1716
|
+
this.#hidePlanReview();
|
|
1717
|
+
this.ui.requestRender();
|
|
1718
|
+
resolve(choice);
|
|
1719
|
+
};
|
|
1720
|
+
const overlay = new PlanReviewOverlay(
|
|
1721
|
+
planContent,
|
|
1722
|
+
{
|
|
1723
|
+
promptTitle: title,
|
|
1724
|
+
options,
|
|
1725
|
+
disabledIndices: dialogOptions?.disabledIndices,
|
|
1726
|
+
helpText: dialogOptions?.helpText,
|
|
1727
|
+
initialIndex: dialogOptions?.initialIndex,
|
|
1728
|
+
slider: extra?.slider,
|
|
1729
|
+
externalEditorLabel: this.keybindings.getDisplayString("app.editor.external") || undefined,
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
onPick: choice => finish(choice),
|
|
1733
|
+
onCancel: () => finish(undefined),
|
|
1734
|
+
onExternalEditor: dialogOptions?.onExternalEditor,
|
|
1735
|
+
onPlanEdited: dialogOptions?.onPlanEdited,
|
|
1736
|
+
onFeedbackChange: dialogOptions?.onFeedbackChange,
|
|
1737
|
+
},
|
|
1738
|
+
);
|
|
1739
|
+
this.#planReviewOverlay = overlay;
|
|
1740
|
+
this.#planReviewOverlayHandle = this.ui.showOverlay(overlay, {
|
|
1741
|
+
anchor: "bottom-center",
|
|
1742
|
+
width: "100%",
|
|
1743
|
+
maxHeight: "100%",
|
|
1744
|
+
margin: 0,
|
|
1745
|
+
fullscreen: true,
|
|
1746
|
+
});
|
|
1747
|
+
this.ui.setFocus(overlay);
|
|
1709
1748
|
this.ui.requestRender();
|
|
1749
|
+
return promise;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
#hidePlanReview(): void {
|
|
1753
|
+
this.#planReviewOverlayHandle?.hide();
|
|
1754
|
+
this.#planReviewOverlayHandle = undefined;
|
|
1755
|
+
this.#planReviewOverlay = undefined;
|
|
1710
1756
|
}
|
|
1711
1757
|
|
|
1712
1758
|
#getEditorTerminalPath(): string | null {
|
|
@@ -1728,14 +1774,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1728
1774
|
}
|
|
1729
1775
|
}
|
|
1730
1776
|
|
|
1731
|
-
#getPlanReviewHelpText(): string {
|
|
1732
|
-
const externalEditorKey = this.keybindings.getDisplayString("app.editor.external");
|
|
1733
|
-
if (!externalEditorKey) {
|
|
1734
|
-
return "up/down navigate enter select esc cancel";
|
|
1735
|
-
}
|
|
1736
|
-
return `up/down navigate enter select ${externalEditorKey.toLowerCase()} open in editor esc cancel`;
|
|
1737
|
-
}
|
|
1738
|
-
|
|
1739
1777
|
#getPlanApprovalContextUsage(): ContextUsage | undefined {
|
|
1740
1778
|
const executionModel = this.#planModePreviousModelState?.model ?? this.session.model;
|
|
1741
1779
|
const contextWindow = executionModel?.contextWindow;
|
|
@@ -1794,7 +1832,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1794
1832
|
});
|
|
1795
1833
|
if (result !== null) {
|
|
1796
1834
|
await Bun.write(resolvedPath, result);
|
|
1797
|
-
this.#
|
|
1835
|
+
this.#planReviewOverlay?.setPlanContent(result);
|
|
1798
1836
|
this.showStatus("Plan updated in external editor.");
|
|
1799
1837
|
}
|
|
1800
1838
|
} catch (error) {
|
|
@@ -1826,19 +1864,12 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1826
1864
|
planContent: string,
|
|
1827
1865
|
options: {
|
|
1828
1866
|
planFilePath: string;
|
|
1829
|
-
finalPlanFilePath: string;
|
|
1830
1867
|
title: string;
|
|
1831
1868
|
preserveContext?: boolean;
|
|
1832
1869
|
compactBeforeExecute?: boolean;
|
|
1833
1870
|
executionModel?: ResolvedRoleModel;
|
|
1834
1871
|
},
|
|
1835
1872
|
): Promise<void> {
|
|
1836
|
-
await renameApprovedPlanFile({
|
|
1837
|
-
planFilePath: options.planFilePath,
|
|
1838
|
-
finalPlanFilePath: options.finalPlanFilePath,
|
|
1839
|
-
getArtifactsDir: () => this.sessionManager.getArtifactsDir(),
|
|
1840
|
-
getSessionId: () => this.sessionManager.getSessionId(),
|
|
1841
|
-
});
|
|
1842
1873
|
const previousTools = this.#planModePreviousTools ?? this.session.getActiveToolNames();
|
|
1843
1874
|
|
|
1844
1875
|
// Mark the pending abort caused by the plan-mode → compaction transition as
|
|
@@ -1857,8 +1888,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1857
1888
|
if (!options.preserveContext) {
|
|
1858
1889
|
await this.handleClearCommand();
|
|
1859
1890
|
// The new session has a fresh local:// root — persist the approved plan there
|
|
1860
|
-
// so `local://<
|
|
1861
|
-
const newLocalPath = resolveLocalUrlToPath(options.
|
|
1891
|
+
// so `local://<slug>-plan.md` resolves correctly in the execution session.
|
|
1892
|
+
const newLocalPath = resolveLocalUrlToPath(options.planFilePath, {
|
|
1862
1893
|
getArtifactsDir: () => this.sessionManager.getArtifactsDir(),
|
|
1863
1894
|
getSessionId: () => this.sessionManager.getSessionId(),
|
|
1864
1895
|
});
|
|
@@ -1872,7 +1903,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1872
1903
|
// Cancellation skips the synthetic-prompt dispatch (operator's explicit
|
|
1873
1904
|
// abort is honored); failure proceeds best-effort — approval intent stands.
|
|
1874
1905
|
const compactionPrompt = prompt.render(planModeCompactInstructionsPrompt, {
|
|
1875
|
-
planFilePath: options.
|
|
1906
|
+
planFilePath: options.planFilePath,
|
|
1876
1907
|
});
|
|
1877
1908
|
// Pin the plan reference path BEFORE compaction so any user messages
|
|
1878
1909
|
// queued during the compaction await (which `handleCompactCommand`
|
|
@@ -1880,7 +1911,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1880
1911
|
// approved plan in `#buildPlanReferenceMessage`. Reassignment after
|
|
1881
1912
|
// the try/finally is idempotent and kept for the !compactBeforeExecute
|
|
1882
1913
|
// branch.
|
|
1883
|
-
this.session.setPlanReferencePath(options.
|
|
1914
|
+
this.session.setPlanReferencePath(options.planFilePath);
|
|
1884
1915
|
compactOutcome = await this.handleCompactCommand(compactionPrompt);
|
|
1885
1916
|
}
|
|
1886
1917
|
} finally {
|
|
@@ -1896,7 +1927,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1896
1927
|
if (previousTools.length > 0) {
|
|
1897
1928
|
await this.session.setActiveToolsByName(previousTools);
|
|
1898
1929
|
}
|
|
1899
|
-
this.session.setPlanReferencePath(options.
|
|
1930
|
+
this.session.setPlanReferencePath(options.planFilePath);
|
|
1900
1931
|
|
|
1901
1932
|
if (compactOutcome === "cancelled") {
|
|
1902
1933
|
// Explicit abort: honor it. `executeCompaction` already surfaced
|
|
@@ -1933,7 +1964,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1933
1964
|
this.session.markPlanReferenceSent();
|
|
1934
1965
|
const planModePrompt = prompt.render(planModeApprovedPrompt, {
|
|
1935
1966
|
planContent,
|
|
1936
|
-
|
|
1967
|
+
planFilePath: options.planFilePath,
|
|
1937
1968
|
contextPreserved: options.preserveContext === true,
|
|
1938
1969
|
});
|
|
1939
1970
|
await this.session.prompt(planModePrompt, { synthetic: true });
|
|
@@ -2223,7 +2254,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2223
2254
|
return;
|
|
2224
2255
|
}
|
|
2225
2256
|
|
|
2226
|
-
this.#renderPlanPreview(planContent, { append: true });
|
|
2227
2257
|
const contextUsage = this.#getPlanApprovalContextUsage();
|
|
2228
2258
|
const keepContextLabel = this.#formatKeepContextLabel(contextUsage);
|
|
2229
2259
|
const keepContextDisabled = this.#isKeepContextDisabled(contextUsage);
|
|
@@ -2253,23 +2283,40 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2253
2283
|
},
|
|
2254
2284
|
}
|
|
2255
2285
|
: undefined;
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
const
|
|
2286
|
+
// The overlay now owns the dynamic, focus-aware help line; the caller only
|
|
2287
|
+
// supplies the trailing cancel hint.
|
|
2288
|
+
const helpText = "esc cancel";
|
|
2289
|
+
// In-overlay edits (section deletes/undo) and section annotations. Deletes
|
|
2290
|
+
// update `editedContent` (and mirror to disk); annotations build `feedback`
|
|
2291
|
+
// that the Refine branch re-prompts the model with.
|
|
2292
|
+
let editedContent: string | undefined;
|
|
2293
|
+
let feedback = "";
|
|
2294
|
+
|
|
2295
|
+
const choice = await this.showPlanReview(
|
|
2296
|
+
planContent,
|
|
2259
2297
|
"Plan mode - next step",
|
|
2260
2298
|
["Approve and execute", "Approve and compact context", keepContextLabel, "Refine plan"],
|
|
2261
2299
|
{
|
|
2262
2300
|
helpText,
|
|
2263
2301
|
onExternalEditor: () => void this.#openPlanInExternalEditor(planFilePath),
|
|
2302
|
+
onPlanEdited: content => {
|
|
2303
|
+
editedContent = content;
|
|
2304
|
+
void Bun.write(this.#resolvePlanFilePath(planFilePath), content);
|
|
2305
|
+
},
|
|
2306
|
+
onFeedbackChange: value => {
|
|
2307
|
+
feedback = value;
|
|
2308
|
+
},
|
|
2264
2309
|
disabledIndices: keepContextDisabled ? [PLAN_KEEP_CONTEXT_OPTION_INDEX] : undefined,
|
|
2265
2310
|
},
|
|
2266
2311
|
{ slider },
|
|
2267
2312
|
);
|
|
2268
2313
|
|
|
2269
2314
|
if (choice === "Approve and execute" || choice === "Approve and compact context" || choice === keepContextLabel) {
|
|
2270
|
-
const finalPlanFilePath = details.finalPlanFilePath || planFilePath;
|
|
2271
2315
|
try {
|
|
2272
|
-
|
|
2316
|
+
// Prefer in-overlay edits (already in memory) over a disk re-read; the
|
|
2317
|
+
// `onPlanEdited` write is fire-and-forget, so reading the file here could
|
|
2318
|
+
// race ahead of it.
|
|
2319
|
+
const latestPlanContent = editedContent ?? (await this.#readPlanFile(planFilePath));
|
|
2273
2320
|
if (!latestPlanContent) {
|
|
2274
2321
|
this.showError(`Plan file not found at ${planFilePath}`);
|
|
2275
2322
|
return;
|
|
@@ -2287,7 +2334,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2287
2334
|
cycle && selectedTierIndex !== cycle.currentIndex ? cycle.models[selectedTierIndex] : undefined;
|
|
2288
2335
|
await this.#approvePlan(latestPlanContent, {
|
|
2289
2336
|
planFilePath,
|
|
2290
|
-
finalPlanFilePath,
|
|
2291
2337
|
title: details.title,
|
|
2292
2338
|
preserveContext: choice !== "Approve and execute",
|
|
2293
2339
|
compactBeforeExecute: choice === "Approve and compact context",
|
|
@@ -2300,6 +2346,16 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2300
2346
|
}
|
|
2301
2347
|
return;
|
|
2302
2348
|
}
|
|
2349
|
+
|
|
2350
|
+
if (choice === "Refine plan") {
|
|
2351
|
+
// Section annotations entered in the overlay become a refinement prompt
|
|
2352
|
+
// re-submitted to the model. With no annotations, fall back to today's
|
|
2353
|
+
// behavior: close the overlay and let the operator type their own.
|
|
2354
|
+
if (feedback.trim() && this.onInputCallback) {
|
|
2355
|
+
this.onInputCallback(this.startPendingSubmission({ text: feedback }));
|
|
2356
|
+
}
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2303
2359
|
}
|
|
2304
2360
|
|
|
2305
2361
|
/**
|
|
@@ -2453,9 +2509,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2453
2509
|
initializeHookRunner(uiContext: ExtensionUIContext, hasUI: boolean): void {
|
|
2454
2510
|
this.#extensionUiController.initializeHookRunner(uiContext, hasUI);
|
|
2455
2511
|
}
|
|
2456
|
-
createBackgroundUiContext(): ExtensionUIContext {
|
|
2457
|
-
return this.#extensionUiController.createBackgroundUiContext();
|
|
2458
|
-
}
|
|
2459
2512
|
|
|
2460
2513
|
setEditorComponent(
|
|
2461
2514
|
factory: ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => CustomEditor) | undefined,
|
|
@@ -2497,12 +2550,26 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2497
2550
|
this.ui.requestRender();
|
|
2498
2551
|
}
|
|
2499
2552
|
|
|
2500
|
-
//
|
|
2501
|
-
|
|
2502
|
-
|
|
2553
|
+
// UI helpers
|
|
2554
|
+
present(content: Component | readonly Component[]): void {
|
|
2555
|
+
if (Array.isArray(content)) {
|
|
2556
|
+
for (const item of content) this.#mountChatChild(item);
|
|
2557
|
+
} else {
|
|
2558
|
+
this.#mountChatChild(content as Component);
|
|
2559
|
+
}
|
|
2560
|
+
this.ui.requestRender();
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
#mountChatChild(item: Component): void {
|
|
2564
|
+
this.chatContainer.addChild(item);
|
|
2565
|
+
if (item instanceof ChatBlock) item.mount(this.#chatHost);
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
resetTranscript(): void {
|
|
2569
|
+
this.chatContainer.dispose();
|
|
2570
|
+
this.chatContainer.clear();
|
|
2503
2571
|
}
|
|
2504
2572
|
|
|
2505
|
-
// UI helpers
|
|
2506
2573
|
showStatus(message: string, options?: { dim?: boolean }): void {
|
|
2507
2574
|
this.#uiHelpers.showStatus(message, options);
|
|
2508
2575
|
}
|
|
@@ -2522,7 +2589,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2522
2589
|
}
|
|
2523
2590
|
|
|
2524
2591
|
showPinnedError(message: string): void {
|
|
2525
|
-
if (this.isBackgrounded) return;
|
|
2526
2592
|
this.errorBannerContainer.clear();
|
|
2527
2593
|
this.errorBannerContainer.addChild(new ErrorBannerComponent(message));
|
|
2528
2594
|
this.ui.requestRender();
|
|
@@ -2593,13 +2659,18 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2593
2659
|
ensureLoadingAnimation(): void {
|
|
2594
2660
|
if (!this.loadingAnimation) {
|
|
2595
2661
|
this.statusContainer.clear();
|
|
2662
|
+
const messageColorFn = ((message: string) =>
|
|
2663
|
+
renderWorkingMessage(message, this.#getWorkingMessageAccent())) as LoaderMessageColorFn & {
|
|
2664
|
+
animated: true;
|
|
2665
|
+
};
|
|
2666
|
+
messageColorFn.animated = true;
|
|
2596
2667
|
this.loadingAnimation = new Loader(
|
|
2597
2668
|
this.ui,
|
|
2598
2669
|
spinner => {
|
|
2599
2670
|
const accent = this.#getWorkingMessageAccent();
|
|
2600
2671
|
return accent ? `${accent.main}${spinner}\x1b[39m` : theme.fg("accent", spinner);
|
|
2601
2672
|
},
|
|
2602
|
-
|
|
2673
|
+
messageColorFn,
|
|
2603
2674
|
this.#defaultWorkingMessage,
|
|
2604
2675
|
getSymbolTheme().spinnerFrames,
|
|
2605
2676
|
);
|
|
@@ -2751,7 +2822,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2751
2822
|
this.#omfgController.dispose();
|
|
2752
2823
|
this.#extensionUiController.clearExtensionTerminalInputListeners();
|
|
2753
2824
|
this.clearPinnedError();
|
|
2754
|
-
this.#
|
|
2825
|
+
this.#hidePlanReview();
|
|
2755
2826
|
}
|
|
2756
2827
|
|
|
2757
2828
|
handleClearCommand(): Promise<void> {
|
|
@@ -2759,6 +2830,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2759
2830
|
return this.#commandController.handleClearCommand();
|
|
2760
2831
|
}
|
|
2761
2832
|
|
|
2833
|
+
handleFreshCommand(): Promise<void> {
|
|
2834
|
+
return this.#commandController.handleFreshCommand();
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2762
2837
|
handleDropCommand(): Promise<void> {
|
|
2763
2838
|
this.#prepareSessionSwitch();
|
|
2764
2839
|
return this.#commandController.handleDropCommand();
|
|
@@ -2994,10 +3069,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2994
3069
|
this.#inputController.handleDequeue();
|
|
2995
3070
|
}
|
|
2996
3071
|
|
|
2997
|
-
handleBackgroundCommand(): void {
|
|
2998
|
-
this.#inputController.handleBackgroundCommand();
|
|
2999
|
-
}
|
|
3000
|
-
|
|
3001
3072
|
handleImagePaste(): Promise<boolean> {
|
|
3002
3073
|
return this.#inputController.handleImagePaste();
|
|
3003
3074
|
}
|
|
@@ -3006,6 +3077,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3006
3077
|
return this.#btwController.start(question);
|
|
3007
3078
|
}
|
|
3008
3079
|
|
|
3080
|
+
handleTanCommand(work: string): Promise<void> {
|
|
3081
|
+
return this.#tanCommandController.start(work);
|
|
3082
|
+
}
|
|
3083
|
+
|
|
3009
3084
|
hasActiveBtw(): boolean {
|
|
3010
3085
|
return this.#btwController.hasActiveRequest();
|
|
3011
3086
|
}
|
|
@@ -406,7 +406,7 @@
|
|
|
406
406
|
}
|
|
407
407
|
},
|
|
408
408
|
"spinnerFrames": {
|
|
409
|
-
"description": "Override the spinner frames. Use a flat array to set both `status` and `activity`, or an object to override each independently. Frames are advanced ~12.5fps for status spinners and ~
|
|
409
|
+
"description": "Override the spinner frames. Use a flat array to set both `status` and `activity`, or an object to override each independently. Frames are advanced ~12.5fps for status spinners and ~30fps for activity spinners.",
|
|
410
410
|
"oneOf": [
|
|
411
411
|
{
|
|
412
412
|
"type": "array",
|
package/src/modes/theme/theme.ts
CHANGED
|
@@ -95,6 +95,7 @@ export type SymbolKey =
|
|
|
95
95
|
| "icon.pause"
|
|
96
96
|
| "icon.loop"
|
|
97
97
|
| "icon.folder"
|
|
98
|
+
| "icon.search"
|
|
98
99
|
| "icon.scratchFolder"
|
|
99
100
|
| "icon.file"
|
|
100
101
|
| "icon.git"
|
|
@@ -264,6 +265,7 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
264
265
|
"icon.pause": "⏸",
|
|
265
266
|
"icon.loop": "↻",
|
|
266
267
|
"icon.folder": "📁",
|
|
268
|
+
"icon.search": "🔍",
|
|
267
269
|
"icon.scratchFolder": "🗑",
|
|
268
270
|
"icon.file": "📄",
|
|
269
271
|
"icon.git": "⎇",
|
|
@@ -488,6 +490,7 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
488
490
|
"icon.loop": "\uf021",
|
|
489
491
|
// pick: | alt:
|
|
490
492
|
"icon.folder": "\uf115",
|
|
493
|
+
"icon.search": "\uf002",
|
|
491
494
|
// pick: | alt:
|
|
492
495
|
"icon.scratchFolder": "\uf014",
|
|
493
496
|
// pick: | alt:
|
|
@@ -701,6 +704,7 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
701
704
|
"icon.pause": "||",
|
|
702
705
|
"icon.loop": "loop",
|
|
703
706
|
"icon.folder": "[D]",
|
|
707
|
+
"icon.search": "[/]",
|
|
704
708
|
"icon.scratchFolder": "[T]",
|
|
705
709
|
"icon.file": "[F]",
|
|
706
710
|
"icon.git": "git:",
|
|
@@ -2439,10 +2443,10 @@ function getHighlightColors(t: Theme): NativeHighlightColors {
|
|
|
2439
2443
|
* switch (which always reassigns `theme`) must invalidate every entry.
|
|
2440
2444
|
*
|
|
2441
2445
|
* Why this exists: animated tool blocks (eval/bash) repaint their box on every
|
|
2442
|
-
* ~
|
|
2443
|
-
* Without memoization each frame re-
|
|
2444
|
-
* Rust FFI — ~26ms for 100 lines, ~40ms for 150 —
|
|
2445
|
-
* budget and starving the spinner/render timers (the "TUI freeze").
|
|
2446
|
+
* ~33ms border-shimmer frame, and markdown re-lexes on every streamed delta.
|
|
2447
|
+
* Without memoization each frame can re-tokenize an unchanged code body through
|
|
2448
|
+
* the Rust FFI — ~26ms for 100 lines, ~40ms for 150 — consuming or overrunning
|
|
2449
|
+
* the 33ms frame budget and starving the spinner/render timers (the "TUI freeze").
|
|
2446
2450
|
*/
|
|
2447
2451
|
const HIGHLIGHT_CACHE_MAX = 256;
|
|
2448
2452
|
const highlightCache = new LRUCache<string, string>({ max: HIGHLIGHT_CACHE_MAX });
|
package/src/modes/types.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
import type { CompactOptions } from "../extensibility/extensions/types";
|
|
15
15
|
import type { MCPManager } from "../mcp";
|
|
16
16
|
import type { PlanApprovalDetails } from "../plan-mode/approved-plan";
|
|
17
|
-
import type { AgentSession
|
|
17
|
+
import type { AgentSession } from "../session/agent-session";
|
|
18
18
|
import type { HistoryStorage } from "../session/history-storage";
|
|
19
19
|
import type { SessionContext, SessionManager } from "../session/session-manager";
|
|
20
20
|
import type { ShakeMode } from "../session/shake-types";
|
|
@@ -63,6 +63,7 @@ export type TodoPhase = {
|
|
|
63
63
|
|
|
64
64
|
export interface InteractiveModeInitOptions {
|
|
65
65
|
suppressWelcomeIntro?: boolean;
|
|
66
|
+
clearInitialTerminalHistory?: boolean;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
export type InteractiveSelectorDialogOptions = ExtensionUIDialogOptions & Pick<HookSelectorOptions, "disabledIndices">;
|
|
@@ -95,7 +96,6 @@ export interface InteractiveModeContext {
|
|
|
95
96
|
|
|
96
97
|
// State
|
|
97
98
|
isInitialized: boolean;
|
|
98
|
-
isBackgrounded: boolean;
|
|
99
99
|
isBashMode: boolean;
|
|
100
100
|
toolOutputExpanded: boolean;
|
|
101
101
|
todoExpanded: boolean;
|
|
@@ -149,15 +149,25 @@ export interface InteractiveModeContext {
|
|
|
149
149
|
// Extension UI integration
|
|
150
150
|
setToolUIContext(uiContext: ExtensionUIContext, hasUI: boolean): void;
|
|
151
151
|
initializeHookRunner(uiContext: ExtensionUIContext, hasUI: boolean): void;
|
|
152
|
-
createBackgroundUiContext(): ExtensionUIContext;
|
|
153
152
|
setEditorComponent(
|
|
154
153
|
factory: ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => CustomEditor) | undefined,
|
|
155
154
|
): void;
|
|
156
155
|
|
|
157
|
-
// Event handling
|
|
158
|
-
handleBackgroundEvent(event: AgentSessionEvent): Promise<void>;
|
|
159
|
-
|
|
160
156
|
// UI helpers
|
|
157
|
+
/**
|
|
158
|
+
* Mount transcript content and repaint once. The single sink for "show this in
|
|
159
|
+
* chat": producers build and return a `Component` (or a `ChatBlock` carrying
|
|
160
|
+
* its own lifecycle) and hand it here instead of touching `chatContainer` /
|
|
161
|
+
* `ui.requestRender()` directly. `ChatBlock`s are mounted (their `onMount`
|
|
162
|
+
* runs) so their timers/subscriptions start.
|
|
163
|
+
*/
|
|
164
|
+
present(content: Component | readonly Component[]): void;
|
|
165
|
+
/**
|
|
166
|
+
* Dispose every live block in the transcript (stopping timers/subscriptions)
|
|
167
|
+
* and clear it. Used before a full rebuild so animated/streaming blocks do not
|
|
168
|
+
* leak.
|
|
169
|
+
*/
|
|
170
|
+
resetTranscript(): void;
|
|
161
171
|
showStatus(message: string, options?: { dim?: boolean }): void;
|
|
162
172
|
showError(message: string): void;
|
|
163
173
|
showPinnedError(message: string): void;
|
|
@@ -233,6 +243,7 @@ export interface InteractiveModeContext {
|
|
|
233
243
|
handleDumpCommand(): void;
|
|
234
244
|
handleDebugTranscriptCommand(): Promise<void>;
|
|
235
245
|
handleClearCommand(): Promise<void>;
|
|
246
|
+
handleFreshCommand(): Promise<void>;
|
|
236
247
|
handleDropCommand(): Promise<void>;
|
|
237
248
|
handleForkCommand(): Promise<void>;
|
|
238
249
|
handleBashCommand(command: string, excludeFromContext?: boolean): Promise<void>;
|
|
@@ -278,9 +289,9 @@ export interface InteractiveModeContext {
|
|
|
278
289
|
handleCtrlD(): void;
|
|
279
290
|
handleCtrlZ(): void;
|
|
280
291
|
handleDequeue(): void;
|
|
281
|
-
handleBackgroundCommand(): void;
|
|
282
292
|
handleImagePaste(): Promise<boolean>;
|
|
283
293
|
handleBtwCommand(question: string): Promise<void>;
|
|
294
|
+
handleTanCommand(work: string): Promise<void>;
|
|
284
295
|
hasActiveBtw(): boolean;
|
|
285
296
|
handleBtwEscape(): boolean;
|
|
286
297
|
handleOmfgCommand(complaint: string): Promise<void>;
|