@oh-my-pi/pi-coding-agent 16.1.23 → 16.2.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 +82 -0
- package/dist/cli.js +3785 -3695
- package/dist/types/advisor/__tests__/emission-guard.test.d.ts +1 -0
- package/dist/types/advisor/emission-guard.d.ts +73 -0
- package/dist/types/advisor/index.d.ts +1 -0
- package/dist/types/advisor/runtime.d.ts +7 -0
- package/dist/types/advisor/watchdog.d.ts +13 -0
- package/dist/types/cli/flag-tables.d.ts +1 -0
- package/dist/types/cli/gallery-fixtures/search.d.ts +1 -1
- package/dist/types/collab/display-name.d.ts +3 -0
- package/dist/types/collab/host.d.ts +0 -2
- package/dist/types/commands/worktree.d.ts +0 -3
- package/dist/types/config/inline-tool-descriptors-mode.d.ts +13 -0
- package/dist/types/config/model-discovery.d.ts +6 -1
- package/dist/types/config/model-registry.d.ts +6 -2
- package/dist/types/config/model-resolver.d.ts +12 -0
- package/dist/types/config/models-config-schema.d.ts +29 -2
- package/dist/types/config/models-config.d.ts +22 -1
- package/dist/types/config/settings-schema.d.ts +91 -21
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/dap/config.d.ts +1 -1
- package/dist/types/edit/hashline/filesystem.d.ts +4 -2
- package/dist/types/edit/renderer.d.ts +4 -0
- package/dist/types/extensibility/extensions/types.d.ts +17 -17
- package/dist/types/extensibility/hooks/types.d.ts +10 -10
- package/dist/types/extensibility/plugins/marketplace/types.d.ts +1 -0
- package/dist/types/internal-urls/__tests__/ssh-protocol.test.d.ts +1 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/registry-helpers.d.ts +2 -0
- package/dist/types/internal-urls/router.d.ts +1 -1
- package/dist/types/internal-urls/ssh-protocol.d.ts +10 -0
- package/dist/types/internal-urls/types.d.ts +19 -2
- package/dist/types/irc/bus.d.ts +6 -0
- package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -0
- package/dist/types/modes/components/__tests__/move-overlay.test.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +7 -0
- package/dist/types/modes/components/move-overlay.d.ts +23 -0
- package/dist/types/modes/components/plugin-selector.d.ts +2 -1
- package/dist/types/modes/components/queue-mode-selector.d.ts +2 -1
- package/dist/types/modes/components/select-list-mouse-routing.d.ts +2 -0
- package/dist/types/modes/components/settings-defs.d.ts +4 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/show-images-selector.d.ts +2 -1
- package/dist/types/modes/components/status-line/component.d.ts +2 -0
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/theme-selector.d.ts +2 -1
- package/dist/types/modes/components/thinking-selector.d.ts +2 -1
- package/dist/types/modes/controllers/command-controller.d.ts +10 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +10 -1
- package/dist/types/modes/internal-url-autocomplete.d.ts +1 -1
- package/dist/types/modes/running-subagent-badge.d.ts +6 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +9 -1
- package/dist/types/sdk.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +8 -1
- package/dist/types/session/blob-store.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +13 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
- package/dist/types/ssh/__tests__/connection-manager-args.test.d.ts +1 -0
- package/dist/types/ssh/__tests__/file-transfer-posix-guard.test.d.ts +1 -0
- package/dist/types/ssh/connection-manager.d.ts +2 -0
- package/dist/types/ssh/file-transfer.d.ts +79 -0
- package/dist/types/ssh/utils.d.ts +6 -0
- package/dist/types/system-prompt.d.ts +3 -0
- package/dist/types/task/executor.d.ts +16 -0
- package/dist/types/tiny/text.d.ts +1 -1
- package/dist/types/tools/builtin-names.d.ts +5 -1
- package/dist/types/tools/{find.d.ts → glob.d.ts} +15 -15
- package/dist/types/tools/{search.d.ts → grep.d.ts} +14 -14
- package/dist/types/tools/index.d.ts +3 -3
- package/dist/types/tools/path-utils.d.ts +29 -0
- package/dist/types/tools/read.d.ts +2 -2
- package/dist/types/tools/render-utils.d.ts +8 -0
- package/dist/types/tools/renderers.d.ts +11 -0
- package/dist/types/tools/ssh.d.ts +2 -0
- package/dist/types/tools/todo.d.ts +0 -16
- package/dist/types/tools/write.d.ts +2 -2
- package/dist/types/utils/active-repo-context.d.ts +8 -0
- package/dist/types/utils/image-resize.d.ts +1 -0
- package/dist/types/utils/markit-cache.d.ts +23 -0
- package/dist/types/utils/markit.d.ts +5 -1
- package/dist/types/utils/prompt-path.d.ts +1 -0
- package/dist/types/web/search/providers/duckduckgo.d.ts +14 -0
- package/dist/types/web/search/providers/firecrawl.d.ts +28 -0
- package/dist/types/web/search/providers/tinyfish.d.ts +29 -0
- package/dist/types/web/search/providers/xai.d.ts +13 -0
- package/dist/types/web/search/types.d.ts +18 -2
- package/package.json +30 -15
- package/scripts/build-binary.ts +9 -9
- package/scripts/bundle-dist.ts +2 -2
- package/src/advisor/__tests__/advisor.test.ts +61 -4
- package/src/advisor/__tests__/emission-guard.test.ts +147 -0
- package/src/advisor/advise-tool.ts +1 -1
- package/src/advisor/emission-guard.ts +172 -0
- package/src/advisor/index.ts +1 -0
- package/src/advisor/runtime.ts +11 -0
- package/src/advisor/watchdog.ts +27 -1
- package/src/cli/args.ts +5 -2
- package/src/cli/auth-broker-cli.ts +17 -0
- package/src/cli/config-cli.ts +4 -0
- package/src/cli/flag-tables.ts +7 -4
- package/src/cli/gallery-cli.ts +14 -2
- package/src/cli/gallery-fixtures/edit.ts +60 -0
- package/src/cli/gallery-fixtures/fs.ts +17 -17
- package/src/cli/gallery-fixtures/search.ts +4 -4
- package/src/cli/usage-cli.ts +20 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/collab/display-name.ts +13 -0
- package/src/collab/guest.ts +5 -1
- package/src/collab/host.ts +2 -13
- package/src/commands/worktree.ts +6 -0
- package/src/config/inline-tool-descriptors-mode.ts +27 -0
- package/src/config/model-discovery.ts +77 -8
- package/src/config/model-registry.ts +89 -11
- package/src/config/model-resolver.ts +39 -1
- package/src/config/models-config-schema.ts +41 -5
- package/src/config/models-config.ts +4 -1
- package/src/config/settings-schema.ts +85 -27
- package/src/config/settings.ts +172 -1
- package/src/cursor.ts +1 -1
- package/src/dap/config.ts +152 -8
- package/src/dap/session.ts +1 -1
- package/src/discovery/claude-plugins.ts +3 -2
- package/src/discovery/helpers.ts +3 -1
- package/src/edit/hashline/diff.ts +14 -3
- package/src/edit/hashline/execute.ts +42 -6
- package/src/edit/hashline/filesystem.ts +38 -2
- package/src/edit/index.ts +1 -0
- package/src/edit/modes/patch.ts +11 -1
- package/src/edit/renderer.ts +140 -13
- package/src/eval/__tests__/agent-bridge.test.ts +101 -0
- package/src/eval/agent-bridge.ts +26 -3
- package/src/eval/js/tool-bridge.ts +2 -2
- package/src/export/html/index.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/export/html/tool-views.generated.js +20 -20
- package/src/extensibility/extensions/types.ts +22 -22
- package/src/extensibility/hooks/types.ts +11 -11
- package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +8 -3
- package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +25 -10
- package/src/extensibility/plugins/marketplace/manager.ts +22 -0
- package/src/extensibility/plugins/marketplace/types.ts +1 -0
- package/src/internal-urls/__tests__/ssh-protocol.test.ts +331 -0
- package/src/internal-urls/docs-index.generated.txt +2 -2
- package/src/internal-urls/docs-index.ts +2 -3
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/registry-helpers.ts +19 -4
- package/src/internal-urls/router.ts +5 -3
- package/src/internal-urls/ssh-protocol.ts +367 -0
- package/src/internal-urls/types.ts +19 -2
- package/src/irc/bus.ts +11 -3
- package/src/mcp/tool-bridge.ts +32 -2
- package/src/memories/index.ts +1 -1
- package/src/modes/acp/acp-agent.ts +14 -1
- package/src/modes/acp/acp-event-mapper.ts +91 -27
- package/src/modes/components/__tests__/move-overlay.test.ts +166 -0
- package/src/modes/components/agent-hub.ts +3 -0
- package/src/modes/components/agent-transcript-viewer.ts +9 -7
- package/src/modes/components/custom-editor.ts +90 -5
- package/src/modes/components/move-overlay.ts +282 -0
- package/src/modes/components/plan-review-overlay.ts +35 -35
- package/src/modes/components/plugin-selector.ts +6 -1
- package/src/modes/components/queue-mode-selector.ts +6 -1
- package/src/modes/components/select-list-mouse-routing.ts +35 -0
- package/src/modes/components/session-selector.ts +11 -10
- package/src/modes/components/settings-defs.ts +14 -1
- package/src/modes/components/settings-selector.ts +196 -29
- package/src/modes/components/show-images-selector.ts +6 -1
- package/src/modes/components/status-line/component.ts +108 -28
- package/src/modes/components/status-line/segments.ts +5 -1
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/theme-selector.ts +6 -1
- package/src/modes/components/thinking-selector.ts +6 -1
- package/src/modes/components/tool-execution.ts +25 -9
- package/src/modes/components/tree-selector.ts +5 -5
- package/src/modes/controllers/command-controller.ts +140 -47
- package/src/modes/controllers/event-controller.ts +59 -3
- package/src/modes/controllers/input-controller.ts +68 -2
- package/src/modes/controllers/selector-controller.ts +5 -2
- package/src/modes/controllers/streaming-reveal.ts +17 -0
- package/src/modes/controllers/tool-args-reveal.ts +1 -1
- package/src/modes/interactive-mode.ts +116 -50
- package/src/modes/internal-url-autocomplete.ts +17 -2
- package/src/modes/prompt-action-autocomplete.ts +3 -1
- package/src/modes/running-subagent-badge.ts +13 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +2 -13
- package/src/modes/setup-wizard/scenes/providers.ts +2 -1
- package/src/modes/setup-wizard/scenes/theme.ts +6 -12
- package/src/modes/setup-wizard/scenes/web-search.ts +8 -13
- package/src/modes/setup-wizard/wizard-overlay.ts +6 -5
- package/src/modes/theme/theme.ts +9 -1
- package/src/modes/types.ts +9 -1
- package/src/modes/utils/interactive-context-helpers.ts +1 -1
- package/src/priority.json +15 -0
- package/src/prompts/advisor/active-repo-watchdog.md +6 -0
- package/src/prompts/advisor/context-files.md +8 -0
- package/src/prompts/advisor/system.md +21 -7
- package/src/prompts/agents/designer.md +1 -1
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/agents/librarian.md +2 -2
- package/src/prompts/agents/plan.md +2 -2
- package/src/prompts/agents/reviewer.md +1 -1
- package/src/prompts/agents/task.md +2 -2
- package/src/prompts/system/active-repo-context.md +4 -0
- package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
- package/src/prompts/system/plan-mode-active.md +3 -3
- package/src/prompts/system/project-prompt.md +2 -2
- package/src/prompts/system/system-prompt.md +6 -6
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/checkpoint.md +1 -1
- package/src/prompts/tools/{find.md → glob.md} +1 -1
- package/src/prompts/tools/{search.md → grep.md} +3 -3
- package/src/prompts/tools/read.md +4 -2
- package/src/sdk.ts +65 -25
- package/src/session/agent-session.ts +519 -132
- package/src/session/blob-store.ts +24 -0
- package/src/session/session-history-format.ts +2 -2
- package/src/session/session-manager.ts +46 -0
- package/src/slash-commands/builtin-registry.ts +138 -20
- package/src/slash-commands/helpers/usage-report.ts +23 -2
- package/src/ssh/__tests__/connection-manager-args.test.ts +69 -0
- package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +68 -0
- package/src/ssh/connection-manager.ts +12 -15
- package/src/ssh/file-transfer.ts +209 -0
- package/src/ssh/utils.ts +24 -0
- package/src/system-prompt.ts +56 -28
- package/src/task/executor.ts +96 -36
- package/src/task/index.ts +3 -3
- package/src/task/render.ts +14 -13
- package/src/task/worktree.ts +38 -1
- package/src/tiny/text.ts +49 -4
- package/src/tiny/worker.ts +3 -3
- package/src/tools/bash.ts +2 -2
- package/src/tools/builtin-names.ts +26 -2
- package/src/tools/{find.ts → glob.ts} +48 -42
- package/src/tools/{search.ts → grep.ts} +311 -129
- package/src/tools/index.ts +13 -14
- package/src/tools/irc.ts +6 -2
- package/src/tools/path-utils.ts +64 -1
- package/src/tools/read.ts +35 -6
- package/src/tools/render-utils.ts +14 -0
- package/src/tools/renderers.ts +15 -4
- package/src/tools/ssh.ts +17 -2
- package/src/tools/todo.ts +4 -26
- package/src/tools/write.ts +25 -9
- package/src/utils/active-repo-context.ts +143 -0
- package/src/utils/image-resize.ts +88 -7
- package/src/utils/lang-from-path.ts +3 -3
- package/src/utils/markit-cache.ts +166 -0
- package/src/utils/markit.ts +86 -18
- package/src/utils/prompt-path.ts +3 -0
- package/src/utils/title-generator.ts +1 -1
- package/src/web/search/provider.ts +54 -34
- package/src/web/search/providers/duckduckgo.ts +140 -0
- package/src/web/search/providers/firecrawl.ts +144 -0
- package/src/web/search/providers/tinyfish.ts +159 -0
- package/src/web/search/providers/xai.ts +292 -0
- package/src/web/search/providers/zai.ts +142 -56
- package/src/web/search/types.ts +6 -2
|
@@ -202,7 +202,7 @@ const pathSegment: StatusLineSegment = {
|
|
|
202
202
|
render(ctx) {
|
|
203
203
|
const opts = ctx.options.path ?? {};
|
|
204
204
|
|
|
205
|
-
const projectDir = getProjectDir();
|
|
205
|
+
const projectDir = ctx.activeRepo?.cwd ?? getProjectDir();
|
|
206
206
|
const { scratch, relative } = classifyProjectDir(projectDir);
|
|
207
207
|
let pwd = projectDir;
|
|
208
208
|
|
|
@@ -213,6 +213,7 @@ const pathSegment: StatusLineSegment = {
|
|
|
213
213
|
pwd = stripDisplayRoot(pwd);
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
|
+
const repoSuffix = ctx.activeRepo ? ` ↳ ${ctx.activeRepo.relativeRepoRoot}` : "";
|
|
216
217
|
if (opts.abbreviate !== false) {
|
|
217
218
|
pwd = shortenPath(pwd);
|
|
218
219
|
}
|
|
@@ -223,6 +224,9 @@ const pathSegment: StatusLineSegment = {
|
|
|
223
224
|
const sliceLen = Math.max(0, maxLen - ellipsis.length);
|
|
224
225
|
pwd = `${ellipsis}${pwd.slice(-sliceLen)}`;
|
|
225
226
|
}
|
|
227
|
+
if (repoSuffix) {
|
|
228
|
+
pwd = `${pwd}${repoSuffix}`;
|
|
229
|
+
}
|
|
226
230
|
|
|
227
231
|
const showScratchIcon = scratch && opts.stripWorkPrefix !== false;
|
|
228
232
|
const icon = showScratchIcon ? theme.icon.scratchFolder : theme.icon.folder;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CollabSessionState } from "../../../collab/protocol";
|
|
2
2
|
import type { StatusLinePreset, StatusLineSegmentId, StatusLineSeparatorStyle } from "../../../config/settings-schema";
|
|
3
3
|
import type { AgentSession } from "../../../session/agent-session";
|
|
4
|
+
import type { ActiveRepoContext } from "../../../utils/active-repo-context";
|
|
4
5
|
|
|
5
6
|
export type { StatusLinePreset, StatusLineSegmentId, StatusLineSeparatorStyle };
|
|
6
7
|
|
|
@@ -47,6 +48,7 @@ export interface SegmentContext {
|
|
|
47
48
|
session: AgentSession;
|
|
48
49
|
/** Focused subagent id while the view is proxied at its session, undefined otherwise. */
|
|
49
50
|
focusedAgentId?: string | undefined;
|
|
51
|
+
activeRepo: ActiveRepoContext | null;
|
|
50
52
|
width: number;
|
|
51
53
|
options: StatusLineSegmentOptions;
|
|
52
54
|
planMode: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Container, type SelectItem, SelectList } from "@oh-my-pi/pi-tui";
|
|
1
|
+
import { Container, type SelectItem, SelectList, type SgrMouseEvent } from "@oh-my-pi/pi-tui";
|
|
2
2
|
import { getSelectListTheme } from "../../modes/theme/theme";
|
|
3
3
|
import { DynamicBorder } from "./dynamic-border";
|
|
4
|
+
import { routeSelectListMouseWithTopBorder } from "./select-list-mouse-routing";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Component that renders a theme selector.
|
|
@@ -60,4 +61,8 @@ export class ThemeSelectorComponent extends Container {
|
|
|
60
61
|
getSelectList(): SelectList {
|
|
61
62
|
return this.#selectList;
|
|
62
63
|
}
|
|
64
|
+
|
|
65
|
+
routeMouse(event: SgrMouseEvent, line: number, col: number): void {
|
|
66
|
+
routeSelectListMouseWithTopBorder(this.#selectList, event, line, col);
|
|
67
|
+
}
|
|
63
68
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Effort } from "@oh-my-pi/pi-ai";
|
|
2
|
-
import { Container, type SelectItem, SelectList } from "@oh-my-pi/pi-tui";
|
|
2
|
+
import { Container, type SelectItem, SelectList, type SgrMouseEvent } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { getSelectListTheme } from "../../modes/theme/theme";
|
|
4
4
|
import { getThinkingLevelMetadata } from "../../thinking";
|
|
5
5
|
import { DynamicBorder } from "./dynamic-border";
|
|
6
|
+
import { routeSelectListMouseWithTopBorder } from "./select-list-mouse-routing";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Component that renders a thinking level selector with borders
|
|
@@ -49,4 +50,8 @@ export class ThinkingSelectorComponent extends Container {
|
|
|
49
50
|
getSelectList(): SelectList {
|
|
50
51
|
return this.#selectList;
|
|
51
52
|
}
|
|
53
|
+
|
|
54
|
+
routeMouse(event: SgrMouseEvent, line: number, col: number): void {
|
|
55
|
+
routeSelectListMouseWithTopBorder(this.#selectList, event, line, col);
|
|
56
|
+
}
|
|
52
57
|
}
|
|
@@ -655,15 +655,31 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
655
655
|
// `provisionalPendingPreview` describes only the PENDING call preview
|
|
656
656
|
// (`renderCall`, before any result): the result render may re-anchor it
|
|
657
657
|
// wholesale, so its rows must never commit. Once a (streaming partial)
|
|
658
|
-
// result exists the result renderer is the live shape — its body
|
|
659
|
-
// top-anchored and grows append-only, and `deriveLiveCommitState`
|
|
660
|
-
// per-row durability — so the block is commit-stable like any
|
|
661
|
-
// stream. Gating the flag on the pending phase is what keeps a
|
|
662
|
-
// streaming eval/bash/ssh whose box outgrows the viewport from
|
|
663
|
-
// its head: while commit-unstable its scrolled-off top
|
|
664
|
-
// and repainted nowhere, so it read as truncated until
|
|
665
|
-
// flipped it stable.
|
|
666
|
-
|
|
658
|
+
// result exists the result renderer is usually the live shape — its body
|
|
659
|
+
// is top-anchored and grows append-only, and `deriveLiveCommitState`
|
|
660
|
+
// gates per-row durability — so the block is commit-stable like any
|
|
661
|
+
// settled stream. Gating the flag on the pending phase is what keeps a
|
|
662
|
+
// collapsed streaming eval/bash/ssh whose box outgrows the viewport from
|
|
663
|
+
// stranding its head: while commit-unstable its scrolled-off top
|
|
664
|
+
// committed nowhere and repainted nowhere, so it read as truncated until
|
|
665
|
+
// ctrl+o (expanded) flipped it stable.
|
|
666
|
+
//
|
|
667
|
+
// Renderers whose partial-result chrome (header glyph, frame state)
|
|
668
|
+
// differs from the final result render set `provisionalPartialResult`
|
|
669
|
+
// to opt out of stream-commit while `isPartial` holds: the ratchet
|
|
670
|
+
// would otherwise promote the stable partial chrome to native scrollback
|
|
671
|
+
// after `STABLE_PREFIX_COMMIT_FRAMES` and leave it stranded above the
|
|
672
|
+
// final frame once the chrome flips. Once the result settles
|
|
673
|
+
// (`isPartial === false`) the block is commit-stable again.
|
|
674
|
+
if (this.#result !== undefined) {
|
|
675
|
+
if (this.#isPartial) {
|
|
676
|
+
const tool = this.#tool as { provisionalPartialResult?: boolean } | undefined;
|
|
677
|
+
const provisionalPartialResult =
|
|
678
|
+
tool?.provisionalPartialResult ?? toolRenderers[this.#toolName]?.provisionalPartialResult;
|
|
679
|
+
if (provisionalPartialResult) return false;
|
|
680
|
+
}
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
667
683
|
const tool = this.#tool as { provisionalPendingPreview?: boolean | "collapsed" } | undefined;
|
|
668
684
|
const provisionalPendingPreview =
|
|
669
685
|
tool?.provisionalPendingPreview ?? toolRenderers[this.#toolName]?.provisionalPendingPreview;
|
|
@@ -15,8 +15,8 @@ import type { TreeFilterMode } from "../../config/settings-schema";
|
|
|
15
15
|
import { theme } from "../../modes/theme/theme";
|
|
16
16
|
import { matchesAppInterrupt, matchesSelectDown, matchesSelectUp } from "../../modes/utils/keybinding-matchers";
|
|
17
17
|
import type { SessionTreeNode } from "../../session/session-entries";
|
|
18
|
+
import { toPathList } from "../../tools/grep";
|
|
18
19
|
import { shortenPath } from "../../tools/render-utils";
|
|
19
|
-
import { toPathList } from "../../tools/search";
|
|
20
20
|
import { canonicalizeMessage } from "../../utils/thinking-display";
|
|
21
21
|
import { DynamicBorder } from "./dynamic-border";
|
|
22
22
|
import { centeredWindow, contentRowWidth, renderScrollableList } from "./selector-helpers";
|
|
@@ -740,7 +740,7 @@ class TreeList implements Component {
|
|
|
740
740
|
.slice(0, 50);
|
|
741
741
|
return `[bash: ${cmd}${rawCmd.length > 50 ? "..." : ""}]`;
|
|
742
742
|
}
|
|
743
|
-
case "
|
|
743
|
+
case "grep": {
|
|
744
744
|
const pattern = String(args.pattern || "");
|
|
745
745
|
const searchPathsInput =
|
|
746
746
|
typeof args.paths === "string" || Array.isArray(args.paths)
|
|
@@ -750,11 +750,11 @@ class TreeList implements Component {
|
|
|
750
750
|
: undefined;
|
|
751
751
|
const paths = toPathList(searchPathsInput);
|
|
752
752
|
const scope = paths.length > 0 ? paths.join(", ") : ".";
|
|
753
|
-
return `[
|
|
753
|
+
return `[grep: /${pattern}/ in ${shortenPath(scope)}]`;
|
|
754
754
|
}
|
|
755
|
-
case "
|
|
755
|
+
case "glob": {
|
|
756
756
|
const paths = Array.isArray(args.paths) ? args.paths.join(", ") : String(args.pattern || ".");
|
|
757
|
-
return `[
|
|
757
|
+
return `[glob: ${shortenPath(paths)}]`;
|
|
758
758
|
}
|
|
759
759
|
case "ls": {
|
|
760
760
|
const path = shortenPath(String(args.path || "."));
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getEnvApiKey,
|
|
7
7
|
getProviderDetails,
|
|
8
8
|
type ProviderDetails,
|
|
9
|
+
resolveUsedFraction,
|
|
9
10
|
type UsageLimit,
|
|
10
11
|
type UsageReport,
|
|
11
12
|
} from "@oh-my-pi/pi-ai";
|
|
@@ -30,6 +31,7 @@ import { BashExecutionComponent } from "../../modes/components/bash-execution";
|
|
|
30
31
|
import { BorderedLoader } from "../../modes/components/bordered-loader";
|
|
31
32
|
import { DynamicBorder } from "../../modes/components/dynamic-border";
|
|
32
33
|
import { EvalExecutionComponent } from "../../modes/components/eval-execution";
|
|
34
|
+
import { MoveOverlay, type MoveOverlayResult } from "../../modes/components/move-overlay";
|
|
33
35
|
import { TranscriptBlock } from "../../modes/components/transcript-container";
|
|
34
36
|
import { getMarkdownTheme, getSymbolTheme, theme } from "../../modes/theme/theme";
|
|
35
37
|
import type { InteractiveModeContext } from "../../modes/types";
|
|
@@ -40,6 +42,7 @@ import type { AsyncJobSnapshotItem } from "../../session/agent-session";
|
|
|
40
42
|
import type { AuthStorage, OAuthAccountIdentity } from "../../session/auth-storage";
|
|
41
43
|
import type { CompactMode } from "../../session/compact-modes";
|
|
42
44
|
import type { NewSessionOptions } from "../../session/session-entries";
|
|
45
|
+
import { SessionManager } from "../../session/session-manager";
|
|
43
46
|
import { formatShakeSummary, type ShakeMode, type ShakeResult } from "../../session/shake-types";
|
|
44
47
|
import { limitMatchesActiveAccount } from "../../slash-commands/helpers/active-oauth-account";
|
|
45
48
|
import { outputMeta } from "../../tools/output-meta";
|
|
@@ -910,13 +913,34 @@ export class CommandController {
|
|
|
910
913
|
]);
|
|
911
914
|
}
|
|
912
915
|
|
|
913
|
-
|
|
916
|
+
/**
|
|
917
|
+
* `/move` — switch to a fresh empty session in a different directory.
|
|
918
|
+
*
|
|
919
|
+
* With no `targetPath` (TUI only), opens an autocomplete overlay so the user
|
|
920
|
+
* can pick or type a directory. With a `targetPath`, resolves it directly.
|
|
921
|
+
* If the target directory does not exist, the user is asked whether to create
|
|
922
|
+
* it. A brand-new empty session is then started in the target directory and
|
|
923
|
+
* the current session is left behind (resumable via `/resume`).
|
|
924
|
+
*/
|
|
925
|
+
async handleMoveCommand(targetPath?: string): Promise<void> {
|
|
914
926
|
if (this.ctx.session.isStreaming) {
|
|
915
927
|
this.ctx.showWarning("Wait for the current response to finish or abort it before moving.");
|
|
916
928
|
return;
|
|
917
929
|
}
|
|
918
930
|
|
|
919
|
-
|
|
931
|
+
let input: string | undefined = targetPath?.trim() || undefined;
|
|
932
|
+
|
|
933
|
+
// No argument in TUI mode: open the path autocomplete overlay.
|
|
934
|
+
if (!input) {
|
|
935
|
+
const result = await this.ctx.showHookCustom<MoveOverlayResult | undefined>(
|
|
936
|
+
(_tui, _theme, _keybindings, done) => new MoveOverlay(this.ctx.sessionManager.getCwd(), done),
|
|
937
|
+
{ overlay: true },
|
|
938
|
+
);
|
|
939
|
+
if (!result) return; // cancelled
|
|
940
|
+
input = result.directory;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
const unquoted = stripOuterDoubleQuotes(input);
|
|
920
944
|
if (!unquoted) {
|
|
921
945
|
this.ctx.showError("Usage: /move <path>");
|
|
922
946
|
return;
|
|
@@ -925,29 +949,85 @@ export class CommandController {
|
|
|
925
949
|
const cwd = this.ctx.sessionManager.getCwd();
|
|
926
950
|
const resolvedPath = resolveToCwd(unquoted, cwd);
|
|
927
951
|
|
|
952
|
+
// If the directory doesn't exist, offer to create it.
|
|
953
|
+
let isDirectory: boolean;
|
|
928
954
|
try {
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
955
|
+
isDirectory = (await fs.stat(resolvedPath)).isDirectory();
|
|
956
|
+
} catch {
|
|
957
|
+
isDirectory = false;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
if (!isDirectory) {
|
|
961
|
+
const parentDir = path.dirname(resolvedPath);
|
|
962
|
+
let parentExists = false;
|
|
963
|
+
try {
|
|
964
|
+
parentExists = (await fs.stat(parentDir)).isDirectory();
|
|
965
|
+
} catch {
|
|
966
|
+
parentExists = false;
|
|
967
|
+
}
|
|
968
|
+
if (!parentExists) {
|
|
969
|
+
this.ctx.showError(`Cannot create "${path.basename(resolvedPath)}": parent directory does not exist`);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const confirmed = await this.ctx.showHookConfirm(
|
|
973
|
+
"Create directory?",
|
|
974
|
+
`"${path.basename(resolvedPath)}" does not exist. Create it?`,
|
|
975
|
+
);
|
|
976
|
+
if (!confirmed) return;
|
|
977
|
+
try {
|
|
978
|
+
await fs.mkdir(resolvedPath, { recursive: true });
|
|
979
|
+
} catch (err) {
|
|
980
|
+
this.ctx.showError(`Failed to create directory: ${err instanceof Error ? err.message : String(err)}`);
|
|
932
981
|
return;
|
|
933
982
|
}
|
|
934
|
-
} catch {
|
|
935
|
-
this.ctx.showError(`Directory does not exist: ${resolvedPath}`);
|
|
936
|
-
return;
|
|
937
983
|
}
|
|
938
984
|
|
|
985
|
+
let newSessionFile: string | undefined;
|
|
939
986
|
try {
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
this.ctx.
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
987
|
+
// Create a fresh empty session file in the target directory's session
|
|
988
|
+
// folder, then switch to it. The current session is left behind and
|
|
989
|
+
// remains resumable via /resume.
|
|
990
|
+
newSessionFile = SessionManager.createEmptySessionFile(resolvedPath);
|
|
991
|
+
const switched = await this.ctx.session.switchSession(newSessionFile);
|
|
992
|
+
if (!switched) {
|
|
993
|
+
await this.ctx.sessionManager.dropSession(newSessionFile);
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
948
996
|
} catch (err) {
|
|
997
|
+
if (newSessionFile) {
|
|
998
|
+
try {
|
|
999
|
+
await this.ctx.sessionManager.dropSession(newSessionFile);
|
|
1000
|
+
} catch (dropErr) {
|
|
1001
|
+
this.ctx.showError(
|
|
1002
|
+
`Move failed: ${err instanceof Error ? err.message : String(err)}; failed to remove empty session: ${dropErr instanceof Error ? dropErr.message : String(dropErr)}`,
|
|
1003
|
+
);
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
949
1007
|
this.ctx.showError(`Move failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1008
|
+
return;
|
|
950
1009
|
}
|
|
1010
|
+
|
|
1011
|
+
this.ctx.session.markMovedFromEmptySessionFile(newSessionFile!);
|
|
1012
|
+
await this.ctx.applyCwdChange(resolvedPath);
|
|
1013
|
+
|
|
1014
|
+
this.ctx.chatContainer.clear();
|
|
1015
|
+
this.ctx.pendingMessagesContainer.clear();
|
|
1016
|
+
this.ctx.compactionQueuedMessages = [];
|
|
1017
|
+
this.ctx.streamingComponent = undefined;
|
|
1018
|
+
this.ctx.streamingMessage = undefined;
|
|
1019
|
+
this.ctx.pendingTools.clear();
|
|
1020
|
+
this.ctx.statusLine.invalidate();
|
|
1021
|
+
this.ctx.statusLine.setSessionStartTime(Date.now());
|
|
1022
|
+
this.ctx.updateEditorTopBorder();
|
|
1023
|
+
this.ctx.updateEditorBorderColor();
|
|
1024
|
+
await this.ctx.reloadTodos();
|
|
1025
|
+
this.ctx.ui.requestRender(true, { clearScrollback: true });
|
|
1026
|
+
|
|
1027
|
+
this.ctx.present([
|
|
1028
|
+
new Spacer(1),
|
|
1029
|
+
new Text(`${theme.fg("accent", `${theme.status.success} Moved to ${resolvedPath}`)}`, 1, 1),
|
|
1030
|
+
]);
|
|
951
1031
|
}
|
|
952
1032
|
|
|
953
1033
|
async handleRenameCommand(title: string): Promise<void> {
|
|
@@ -1306,22 +1386,10 @@ export function renderProviderSection(details: ProviderDetails, uiTheme: Pick<ty
|
|
|
1306
1386
|
return `${lines.join("\n")}\n`;
|
|
1307
1387
|
}
|
|
1308
1388
|
|
|
1309
|
-
function resolveFraction(limit: UsageLimit): number | undefined {
|
|
1310
|
-
const amount = limit.amount;
|
|
1311
|
-
if (amount.usedFraction !== undefined) return amount.usedFraction;
|
|
1312
|
-
if (amount.used !== undefined && amount.limit !== undefined && amount.limit > 0) {
|
|
1313
|
-
return amount.used / amount.limit;
|
|
1314
|
-
}
|
|
1315
|
-
if (amount.unit === "percent" && amount.used !== undefined) {
|
|
1316
|
-
return amount.used / 100;
|
|
1317
|
-
}
|
|
1318
|
-
return undefined;
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
1389
|
function resolveProviderUsageTotal(reports: UsageReport[]): number {
|
|
1322
1390
|
return reports
|
|
1323
1391
|
.flatMap(report => report.limits)
|
|
1324
|
-
.map(limit =>
|
|
1392
|
+
.map(limit => resolveUsedFraction(limit) ?? 0)
|
|
1325
1393
|
.reduce((sum, value) => sum + value, 0);
|
|
1326
1394
|
}
|
|
1327
1395
|
|
|
@@ -1342,22 +1410,28 @@ function formatWindowSuffix(label: string, windowLabel: string, uiTheme: typeof
|
|
|
1342
1410
|
}
|
|
1343
1411
|
|
|
1344
1412
|
function formatAccountLabel(limit: UsageLimit, report: UsageReport, index: number): string {
|
|
1345
|
-
const email =
|
|
1346
|
-
if (email) return email;
|
|
1347
|
-
const accountId =
|
|
1413
|
+
const email = report.metadata?.email;
|
|
1414
|
+
if (typeof email === "string" && email) return email;
|
|
1415
|
+
const accountId =
|
|
1416
|
+
typeof report.metadata?.accountId === "string" && report.metadata.accountId
|
|
1417
|
+
? report.metadata.accountId
|
|
1418
|
+
: limit.scope.accountId || undefined;
|
|
1348
1419
|
if (accountId) return accountId;
|
|
1349
|
-
const projectId =
|
|
1420
|
+
const projectId =
|
|
1421
|
+
typeof report.metadata?.projectId === "string" && report.metadata.projectId
|
|
1422
|
+
? report.metadata.projectId
|
|
1423
|
+
: limit.scope.projectId || undefined;
|
|
1350
1424
|
if (projectId) return projectId;
|
|
1351
1425
|
return `account ${index + 1}`;
|
|
1352
1426
|
}
|
|
1353
1427
|
|
|
1354
1428
|
function formatUnlimitedReportLabel(report: UsageReport, index: number): string {
|
|
1355
|
-
const email = report.metadata?.email
|
|
1356
|
-
if (email) return email;
|
|
1357
|
-
const accountId = report.metadata?.accountId
|
|
1358
|
-
if (accountId) return accountId;
|
|
1359
|
-
const projectId = report.metadata?.projectId
|
|
1360
|
-
if (projectId) return projectId;
|
|
1429
|
+
const email = report.metadata?.email;
|
|
1430
|
+
if (typeof email === "string" && email) return email;
|
|
1431
|
+
const accountId = report.metadata?.accountId;
|
|
1432
|
+
if (typeof accountId === "string" && accountId) return accountId;
|
|
1433
|
+
const projectId = report.metadata?.projectId;
|
|
1434
|
+
if (typeof projectId === "string" && projectId) return projectId;
|
|
1361
1435
|
return `account ${index + 1}`;
|
|
1362
1436
|
}
|
|
1363
1437
|
|
|
@@ -1432,7 +1506,7 @@ function resolveAggregateStatus(limits: UsageLimit[]): UsageLimit["status"] {
|
|
|
1432
1506
|
|
|
1433
1507
|
function formatAggregateAmount(limits: UsageLimit[]): string {
|
|
1434
1508
|
const fractions = limits
|
|
1435
|
-
.map(limit =>
|
|
1509
|
+
.map(limit => resolveUsedFraction(limit))
|
|
1436
1510
|
.filter((value): value is number => value !== undefined);
|
|
1437
1511
|
if (fractions.length === limits.length && fractions.length > 0) {
|
|
1438
1512
|
const sum = fractions.reduce((total, value) => total + value, 0);
|
|
@@ -1489,7 +1563,7 @@ function resolveStatusColor(status: UsageLimit["status"]): "success" | "warning"
|
|
|
1489
1563
|
}
|
|
1490
1564
|
|
|
1491
1565
|
function renderUsageBar(limit: UsageLimit, uiTheme: typeof theme, barWidth: number): string {
|
|
1492
|
-
const fraction =
|
|
1566
|
+
const fraction = resolveUsedFraction(limit);
|
|
1493
1567
|
if (fraction === undefined) {
|
|
1494
1568
|
return uiTheme.fg("dim", "·".repeat(barWidth));
|
|
1495
1569
|
}
|
|
@@ -1597,9 +1671,11 @@ export function renderUsageReports(
|
|
|
1597
1671
|
const count = report.resetCredits?.availableCount ?? 0;
|
|
1598
1672
|
if (count <= 0) continue;
|
|
1599
1673
|
const label =
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1674
|
+
typeof report.metadata?.email === "string" && report.metadata.email
|
|
1675
|
+
? report.metadata.email
|
|
1676
|
+
: typeof report.metadata?.accountId === "string" && report.metadata.accountId
|
|
1677
|
+
? report.metadata.accountId
|
|
1678
|
+
: "account";
|
|
1603
1679
|
const isActive =
|
|
1604
1680
|
!!activeAccount &&
|
|
1605
1681
|
((!!activeAccount.accountId && activeAccount.accountId === report.metadata?.accountId) ||
|
|
@@ -1607,6 +1683,23 @@ export function renderUsageReports(
|
|
|
1607
1683
|
resetAccountLines.push(
|
|
1608
1684
|
` • ${label}: ${count} saved reset${count === 1 ? "" : "s"}${isActive ? " (active)" : ""}`,
|
|
1609
1685
|
);
|
|
1686
|
+
const credits = report.resetCredits?.credits;
|
|
1687
|
+
if (credits) {
|
|
1688
|
+
for (const credit of credits) {
|
|
1689
|
+
if (credit.expiresAt) {
|
|
1690
|
+
const expiryMs = Date.parse(credit.expiresAt);
|
|
1691
|
+
if (!Number.isNaN(expiryMs)) {
|
|
1692
|
+
const remaining = expiryMs - nowMs;
|
|
1693
|
+
const expiryDate = credit.expiresAt.slice(0, 10);
|
|
1694
|
+
if (remaining > 0) {
|
|
1695
|
+
resetAccountLines.push(` expires in ${formatDuration(remaining)} (${expiryDate})`);
|
|
1696
|
+
} else {
|
|
1697
|
+
resetAccountLines.push(` expired (${expiryDate})`);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1610
1703
|
}
|
|
1611
1704
|
if (resetAccountLines.length > 0) {
|
|
1612
1705
|
lines.push(
|
|
@@ -1619,7 +1712,7 @@ export function renderUsageReports(
|
|
|
1619
1712
|
const entries = group.limits.map((limit, index) => ({
|
|
1620
1713
|
limit,
|
|
1621
1714
|
report: group.reports[index],
|
|
1622
|
-
fraction:
|
|
1715
|
+
fraction: resolveUsedFraction(limit),
|
|
1623
1716
|
index,
|
|
1624
1717
|
}));
|
|
1625
1718
|
entries.sort((a, b) => {
|
|
@@ -1672,8 +1765,8 @@ export function renderUsageReports(
|
|
|
1672
1765
|
const unlimitedReports = providerReports.filter(report => report.limits.length === 0);
|
|
1673
1766
|
for (const report of unlimitedReports) {
|
|
1674
1767
|
const label = formatUnlimitedReportLabel(report, 0);
|
|
1675
|
-
const tier = report.metadata?.planType
|
|
1676
|
-
const tierSuffix = tier ? ` ${uiTheme.fg("dim", `(${tier})`)}` : "";
|
|
1768
|
+
const tier = report.metadata?.planType;
|
|
1769
|
+
const tierSuffix = typeof tier === "string" && tier ? ` ${uiTheme.fg("dim", `(${tier})`)}` : "";
|
|
1677
1770
|
lines.push(
|
|
1678
1771
|
`${uiTheme.fg("success", uiTheme.status.success)} ${label}${tierSuffix} ${uiTheme.fg("dim", "-- no limits")}`,
|
|
1679
1772
|
);
|
|
@@ -5,7 +5,7 @@ import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
|
|
|
5
5
|
import { extractTextContent } from "../../commit/utils";
|
|
6
6
|
import { settings } from "../../config/settings";
|
|
7
7
|
import { getFileSnapshotStore } from "../../edit/file-snapshot-store";
|
|
8
|
-
import
|
|
8
|
+
import { AssistantMessageComponent } from "../../modes/components/assistant-message";
|
|
9
9
|
import { detectCacheInvalidation } from "../../modes/components/cache-invalidation-marker";
|
|
10
10
|
import {
|
|
11
11
|
ReadToolGroupComponent,
|
|
@@ -86,12 +86,14 @@ export class EventController {
|
|
|
86
86
|
#lastTtsrNotification: TtsrNotificationComponent | undefined = undefined;
|
|
87
87
|
#streamingReveal: StreamingRevealController;
|
|
88
88
|
#toolArgsReveal: ToolArgsRevealController;
|
|
89
|
+
#prevHideThinking = false;
|
|
89
90
|
#handlers: AgentSessionEventHandlers;
|
|
91
|
+
#terminalProgressActive = false;
|
|
90
92
|
|
|
91
93
|
constructor(private ctx: InteractiveModeContext) {
|
|
92
94
|
this.#streamingReveal = new StreamingRevealController({
|
|
93
95
|
getSmoothStreaming: () => this.ctx.settings.get("display.smoothStreaming"),
|
|
94
|
-
getHideThinkingBlock: () => this.ctx.
|
|
96
|
+
getHideThinkingBlock: () => this.ctx.effectiveHideThinkingBlock,
|
|
95
97
|
getProseOnlyThinking: () => this.ctx.proseOnlyThinking,
|
|
96
98
|
requestRender: () => this.ctx.ui.requestRender(),
|
|
97
99
|
});
|
|
@@ -124,7 +126,27 @@ export class EventController {
|
|
|
124
126
|
thinking_level_changed: async () => {
|
|
125
127
|
this.ctx.statusLine.invalidate();
|
|
126
128
|
this.ctx.updateEditorBorderColor();
|
|
127
|
-
this.ctx.
|
|
129
|
+
const hideThinking = this.ctx.effectiveHideThinkingBlock;
|
|
130
|
+
// Only do the expensive full resetDisplay when the effective
|
|
131
|
+
// visibility actually changed. Auto-classification (e.g. high→medium)
|
|
132
|
+
// emits thinking_level_changed without changing visibility — a full
|
|
133
|
+
// terminal replay for those would be disruptive.
|
|
134
|
+
if (hideThinking === this.#prevHideThinking) {
|
|
135
|
+
this.ctx.ui.requestRender();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
this.#prevHideThinking = hideThinking;
|
|
139
|
+
// Propagate visibility to existing rendered messages.
|
|
140
|
+
for (const child of this.ctx.chatContainer.children) {
|
|
141
|
+
if (child instanceof AssistantMessageComponent) {
|
|
142
|
+
child.setHideThinkingBlock(hideThinking);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (this.ctx.streamingComponent && this.ctx.streamingMessage) {
|
|
146
|
+
this.ctx.streamingComponent.setHideThinkingBlock(hideThinking);
|
|
147
|
+
this.#streamingReveal.resyncVisibility();
|
|
148
|
+
}
|
|
149
|
+
this.ctx.ui.resetDisplay();
|
|
128
150
|
},
|
|
129
151
|
goal_updated: async () => {},
|
|
130
152
|
} satisfies AgentSessionEventHandlers;
|
|
@@ -134,6 +156,7 @@ export class EventController {
|
|
|
134
156
|
this.#streamingReveal.stop();
|
|
135
157
|
this.#toolArgsReveal.stop();
|
|
136
158
|
this.#cancelIdleCompaction();
|
|
159
|
+
this.#setTerminalProgress(false);
|
|
137
160
|
for (const timer of this.#ircExpiryTimers.values()) {
|
|
138
161
|
clearTimeout(timer);
|
|
139
162
|
}
|
|
@@ -248,6 +271,18 @@ export class EventController {
|
|
|
248
271
|
await run(event);
|
|
249
272
|
}
|
|
250
273
|
|
|
274
|
+
#setTerminalProgress(active: boolean): void {
|
|
275
|
+
if (active) {
|
|
276
|
+
if (this.#terminalProgressActive || this.ctx.settings?.get("terminal.showProgress") !== true) return;
|
|
277
|
+
this.ctx.ui.terminal.setProgress(true);
|
|
278
|
+
this.#terminalProgressActive = true;
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (!this.#terminalProgressActive) return;
|
|
282
|
+
this.ctx.ui.terminal.setProgress(false);
|
|
283
|
+
this.#terminalProgressActive = false;
|
|
284
|
+
}
|
|
285
|
+
|
|
251
286
|
async #handleAgentStart(_event: Extract<AgentSessionEvent, { type: "agent_start" }>): Promise<void> {
|
|
252
287
|
this.#lastIntent = undefined;
|
|
253
288
|
this.#readToolCallArgs.clear();
|
|
@@ -266,11 +301,13 @@ export class EventController {
|
|
|
266
301
|
this.ctx.statusContainer.clear();
|
|
267
302
|
}
|
|
268
303
|
this.#cancelIdleCompaction();
|
|
304
|
+
this.#setTerminalProgress(true);
|
|
269
305
|
this.ctx.ensureLoadingAnimation();
|
|
270
306
|
this.ctx.ui.requestRender();
|
|
271
307
|
}
|
|
272
308
|
|
|
273
309
|
async #handleMessageStart(event: Extract<AgentSessionEvent, { type: "message_start" }>): Promise<void> {
|
|
310
|
+
this.#ensureWorkingLoaderWhileStreaming();
|
|
274
311
|
if (event.message.role === "hookMessage" || event.message.role === "custom") {
|
|
275
312
|
const signature = `${event.message.role}:${event.message.customType}:${event.message.timestamp}`;
|
|
276
313
|
if (this.#renderedCustomMessages.has(signature)) {
|
|
@@ -513,6 +550,7 @@ export class EventController {
|
|
|
513
550
|
}
|
|
514
551
|
|
|
515
552
|
async #handleMessageUpdate(event: Extract<AgentSessionEvent, { type: "message_update" }>): Promise<void> {
|
|
553
|
+
this.#ensureWorkingLoaderWhileStreaming();
|
|
516
554
|
this.#vocalizeDelta(event);
|
|
517
555
|
if (this.ctx.streamingComponent && event.message.role === "assistant") {
|
|
518
556
|
this.ctx.streamingMessage = event.message;
|
|
@@ -737,6 +775,7 @@ export class EventController {
|
|
|
737
775
|
}
|
|
738
776
|
|
|
739
777
|
async #handleToolExecutionStart(event: Extract<AgentSessionEvent, { type: "tool_execution_start" }>): Promise<void> {
|
|
778
|
+
this.#ensureWorkingLoaderWhileStreaming();
|
|
740
779
|
this.#updateWorkingMessageFromIntent(event.intent);
|
|
741
780
|
this.#resolveDisplaceablePoll(event.toolName);
|
|
742
781
|
if (!this.ctx.pendingTools.has(event.toolCallId)) {
|
|
@@ -801,6 +840,7 @@ export class EventController {
|
|
|
801
840
|
async #handleToolExecutionUpdate(
|
|
802
841
|
event: Extract<AgentSessionEvent, { type: "tool_execution_update" }>,
|
|
803
842
|
): Promise<void> {
|
|
843
|
+
this.#ensureWorkingLoaderWhileStreaming();
|
|
804
844
|
const component = this.ctx.pendingTools.get(event.toolCallId);
|
|
805
845
|
if (component) {
|
|
806
846
|
const asyncState = (event.partialResult.details as { async?: { state?: string } } | undefined)?.async?.state;
|
|
@@ -929,6 +969,7 @@ export class EventController {
|
|
|
929
969
|
}
|
|
930
970
|
|
|
931
971
|
async #finishAgentEnd(): Promise<void> {
|
|
972
|
+
this.#setTerminalProgress(false);
|
|
932
973
|
this.#streamingReveal.stop();
|
|
933
974
|
this.#toolArgsReveal.flushAll();
|
|
934
975
|
if (this.ctx.loadingAnimation) {
|
|
@@ -989,6 +1030,17 @@ export class EventController {
|
|
|
989
1030
|
}
|
|
990
1031
|
}
|
|
991
1032
|
|
|
1033
|
+
/**
|
|
1034
|
+
* Restore the live "Working…" loader when a streaming event lands after a
|
|
1035
|
+
* transient status overlay cleared the container. Focus mode dispatches events
|
|
1036
|
+
* for `viewSession`, so key the reconciler on that session, not the main one.
|
|
1037
|
+
*/
|
|
1038
|
+
#ensureWorkingLoaderWhileStreaming(): void {
|
|
1039
|
+
if (!this.ctx.viewSession.isStreaming) return;
|
|
1040
|
+
if (this.ctx.autoCompactionLoader || this.ctx.retryLoader) return;
|
|
1041
|
+
this.ctx.ensureLoadingAnimation();
|
|
1042
|
+
}
|
|
1043
|
+
|
|
992
1044
|
/**
|
|
993
1045
|
* Trailing Esc hint for live maintenance loaders. While a subagent is
|
|
994
1046
|
* focused, Esc returns to main instead of cancelling its maintenance
|
|
@@ -1004,6 +1056,7 @@ export class EventController {
|
|
|
1004
1056
|
event: Extract<AgentSessionEvent, { type: "auto_compaction_start" }>,
|
|
1005
1057
|
): Promise<void> {
|
|
1006
1058
|
this.#cancelIdleCompaction();
|
|
1059
|
+
this.#setTerminalProgress(true);
|
|
1007
1060
|
this.#stopWorkingLoader();
|
|
1008
1061
|
this.ctx.statusContainer.clear();
|
|
1009
1062
|
const reasonText =
|
|
@@ -1035,6 +1088,7 @@ export class EventController {
|
|
|
1035
1088
|
|
|
1036
1089
|
async #handleAutoCompactionEnd(event: Extract<AgentSessionEvent, { type: "auto_compaction_end" }>): Promise<void> {
|
|
1037
1090
|
this.#cancelIdleCompaction();
|
|
1091
|
+
this.#setTerminalProgress(false);
|
|
1038
1092
|
if (this.ctx.autoCompactionLoader) {
|
|
1039
1093
|
this.ctx.autoCompactionLoader.stop();
|
|
1040
1094
|
this.ctx.autoCompactionLoader = undefined;
|
|
@@ -1096,6 +1150,7 @@ export class EventController {
|
|
|
1096
1150
|
this.ctx.showWarning("Auto context-full maintenance failed; continuing without maintenance");
|
|
1097
1151
|
}
|
|
1098
1152
|
await this.ctx.flushCompactionQueue({ willRetry: event.willRetry });
|
|
1153
|
+
this.#ensureWorkingLoaderWhileStreaming();
|
|
1099
1154
|
this.ctx.ui.requestRender();
|
|
1100
1155
|
}
|
|
1101
1156
|
|
|
@@ -1130,6 +1185,7 @@ export class EventController {
|
|
|
1130
1185
|
if (!event.success) {
|
|
1131
1186
|
this.ctx.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
|
|
1132
1187
|
}
|
|
1188
|
+
this.#ensureWorkingLoaderWhileStreaming();
|
|
1133
1189
|
this.ctx.ui.requestRender();
|
|
1134
1190
|
}
|
|
1135
1191
|
|