@gajae-code/coding-agent 0.17.2 → 0.17.4
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 +194 -0
- package/dist/types/cli/daemon-cli.d.ts +3 -1
- package/dist/types/cli/github-release.d.ts +17 -0
- package/dist/types/cli/public-command-entry.d.ts +56 -0
- package/dist/types/cli/public-command-errors.d.ts +126 -0
- package/dist/types/cli/public-command-evidence.d.ts +99 -0
- package/dist/types/cli/public-command-help.d.ts +65 -0
- package/dist/types/cli/public-command-registry.d.ts +69 -0
- package/dist/types/cli/session-picker.d.ts +1 -1
- package/dist/types/cli/setup-cli.d.ts +1 -0
- package/dist/types/cli/skills-cli.d.ts +5 -0
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/commands/daemon.d.ts +4 -50
- package/dist/types/commands/sdk.d.ts +20 -37
- package/dist/types/commands/setup.d.ts +3 -0
- package/dist/types/commands/skills.d.ts +9 -0
- package/dist/types/config/autorouting-tier-map.d.ts +18 -0
- package/dist/types/config/file-lock.d.ts +9 -1
- package/dist/types/config/model-profile-activation.d.ts +45 -1
- package/dist/types/config/model-registry.d.ts +11 -2
- package/dist/types/config/settings-schema.d.ts +11 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +6 -1
- package/dist/types/daemon/operator-contract.d.ts +5 -1
- package/dist/types/eval/js/worker-protocol.d.ts +2 -0
- package/dist/types/extensibility/extensions/types.d.ts +12 -0
- package/dist/types/extensibility/gjc-plugins/registry.d.ts +4 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +18 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -1
- package/dist/types/gjc-runtime/session-state-lock.d.ts +61 -0
- package/dist/types/internal-urls/embedded-protocol.d.ts +9 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/parse.d.ts +9 -1
- package/dist/types/main.d.ts +22 -0
- package/dist/types/modes/acp/acp-agent.d.ts +55 -1
- package/dist/types/modes/acp/startup-provenance.d.ts +16 -0
- package/dist/types/modes/components/custom-provider-wizard.d.ts +19 -1
- package/dist/types/modes/components/session-selector.d.ts +5 -2
- package/dist/types/modes/components/user-message-selector.d.ts +5 -2
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +11 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +14 -1
- package/dist/types/modes/types.d.ts +2 -2
- package/dist/types/runtime/process-lifecycle.d.ts +2 -0
- package/dist/types/runtime-mcp/manager.d.ts +56 -0
- package/dist/types/runtime-mcp/pool.d.ts +17 -0
- package/dist/types/runtime-mcp/redaction.d.ts +20 -0
- package/dist/types/runtime-mcp/startup-policy.d.ts +6 -0
- package/dist/types/runtime-mcp/tool-bridge.d.ts +9 -0
- package/dist/types/runtime-mcp/transports/http.d.ts +11 -0
- package/dist/types/sdk/broker/broker.d.ts +13 -2
- package/dist/types/sdk/broker/identity.d.ts +3 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +23 -3
- package/dist/types/sdk/broker/lifecycle.d.ts +15 -8
- package/dist/types/sdk/broker/runtime.d.ts +1 -1
- package/dist/types/sdk/broker/session-index.d.ts +22 -3
- package/dist/types/sdk/broker/spawn-authority.d.ts +4 -0
- package/dist/types/sdk/broker/stderr-drainer.d.ts +13 -0
- package/dist/types/sdk/bus/index.d.ts +19 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +5 -0
- package/dist/types/sdk/cli/master-cli.d.ts +6 -10
- package/dist/types/sdk/cli/rows.d.ts +19 -1
- package/dist/types/sdk/cli/session-cli.d.ts +22 -5
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/guides/cli.d.ts +3 -4
- package/dist/types/sdk/guides/verify.d.ts +2 -0
- package/dist/types/sdk/host/host.d.ts +9 -0
- package/dist/types/sdk/host/session-runtime.d.ts +31 -0
- package/dist/types/sdk/host/types.d.ts +10 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/prompt-deadline-flush.d.ts +83 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +37 -0
- package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -0
- package/dist/types/sdk/router/session-router.d.ts +2 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +48 -4
- package/dist/types/sdk/transport/stdio.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +168 -21
- package/dist/types/session/fallback-chain-controller.d.ts +6 -6
- package/dist/types/session/fold-coordinator.d.ts +17 -3
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -1
- package/dist/types/session/session-manager.d.ts +64 -1
- package/dist/types/session/session-work-lease.d.ts +14 -0
- package/dist/types/setup/model-onboarding-guidance.d.ts +1 -1
- package/dist/types/setup/provider-onboarding.d.ts +81 -1
- package/dist/types/tools/bash.d.ts +37 -11
- package/dist/types/tools/job.d.ts +1 -1
- package/dist/types/tools/steer-fold.d.ts +31 -0
- package/dist/types/utils/git.d.ts +28 -2
- package/dist/types/utils/mupdf-embedded.d.ts +2 -0
- package/dist/types/utils/mupdf.d.ts +4 -0
- package/dist/types/utils/session-title-generation.d.ts +6 -0
- package/dist/types/utils/title-generator.d.ts +11 -2
- package/dist/types/web/scrapers/types.d.ts +3 -0
- package/package.json +18 -10
- package/scripts/build-binary.ts +9 -3
- package/scripts/compile-args.ts +0 -9
- package/scripts/embed-mupdf.ts +52 -0
- package/scripts/generate-sdk-operation-inventory.ts +31 -1
- package/scripts/vendor-markit.ts +196 -0
- package/src/async/job-manager.ts +22 -1
- package/src/cli/daemon-cli.ts +53 -18
- package/src/cli/github-release.ts +87 -5
- package/src/cli/mcp-cli.ts +38 -4
- package/src/cli/public-command-entry.ts +450 -0
- package/src/cli/public-command-errors.ts +581 -0
- package/src/cli/public-command-evidence.ts +717 -0
- package/src/cli/public-command-help.ts +360 -0
- package/src/cli/public-command-registry.ts +535 -0
- package/src/cli/session-picker.ts +2 -1
- package/src/cli/setup-cli.ts +9 -1
- package/src/cli/skills-cli.ts +46 -2
- package/src/cli.ts +61 -1
- package/src/commands/daemon.ts +65 -69
- package/src/commands/sdk.ts +175 -117
- package/src/commands/setup.ts +2 -0
- package/src/commands/skills.ts +11 -0
- package/src/config/autorouting-tier-map.ts +14 -0
- package/src/config/file-lock.ts +147 -51
- package/src/config/model-profile-activation.ts +334 -43
- package/src/config/model-registry.ts +183 -25
- package/src/config/settings-schema.ts +26 -2
- package/src/coordinator-mcp/policy.ts +10 -5
- package/src/coordinator-mcp/question-gate-codec.ts +17 -0
- package/src/coordinator-mcp/question-state.ts +175 -8
- package/src/coordinator-mcp/server.ts +507 -81
- package/src/coordinator-mcp/session-reaper.ts +6 -4
- package/src/crash/record-loader.ts +6 -2
- package/src/daemon/operator-contract.ts +26 -12
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
- package/src/eval/js/context-manager.ts +8 -8
- package/src/eval/js/worker-core.ts +9 -4
- package/src/eval/js/worker-protocol.ts +2 -0
- package/src/extensibility/extensions/runner.ts +52 -4
- package/src/extensibility/extensions/types.ts +10 -0
- package/src/extensibility/gjc-plugins/registry.ts +196 -37
- package/src/extensibility/runtime-skill-discovery.ts +61 -3
- package/src/extensibility/shared-events.ts +3 -1
- package/src/gjc-runtime/session-state-lock.ts +165 -24
- package/src/gjc-runtime/ultragoal-succession.ts +31 -3
- package/src/internal-urls/docs-index.generated.ts +20 -19
- package/src/internal-urls/embedded-protocol.ts +83 -0
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/parse.ts +21 -1
- package/src/internal-urls/router.ts +4 -2
- package/src/main.ts +123 -55
- package/src/modes/acp/acp-agent.ts +408 -35
- package/src/modes/acp/startup-provenance.ts +59 -0
- package/src/modes/components/custom-provider-wizard.ts +308 -7
- package/src/modes/components/oauth-selector.ts +9 -0
- package/src/modes/components/session-selector.ts +47 -4
- package/src/modes/components/user-message-selector.ts +57 -15
- package/src/modes/controllers/command-controller.ts +58 -32
- package/src/modes/controllers/extension-ui-controller.ts +33 -7
- package/src/modes/controllers/input-controller.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +161 -15
- package/src/modes/interactive-mode.ts +21 -7
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +124 -7
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +16 -2
- package/src/modes/types.ts +2 -2
- package/src/prompts/agent-fragments/browser-aside.md +1 -0
- package/src/prompts/system/title-system.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/job.md +1 -1
- package/src/prompts/tools/read.md +2 -0
- package/src/runtime/network-prewarm-service.test.ts +36 -1
- package/src/runtime/network-prewarm-service.ts +28 -3
- package/src/runtime/process-lifecycle.ts +22 -11
- package/src/runtime-mcp/config.ts +16 -1
- package/src/runtime-mcp/manager.ts +689 -55
- package/src/runtime-mcp/pool.test.ts +31 -0
- package/src/runtime-mcp/pool.ts +304 -28
- package/src/runtime-mcp/redaction.ts +42 -0
- package/src/runtime-mcp/startup-policy.ts +7 -0
- package/src/runtime-mcp/tool-bridge.ts +71 -0
- package/src/runtime-mcp/transports/http.ts +108 -25
- package/src/sdk/acp/adapter.ts +27 -8
- package/src/sdk/broker/broker.ts +378 -52
- package/src/sdk/broker/identity.ts +9 -1
- package/src/sdk/broker/lifecycle-ledger.ts +95 -31
- package/src/sdk/broker/lifecycle.ts +690 -141
- package/src/sdk/broker/runtime.ts +1 -1
- package/src/sdk/broker/session-index.ts +97 -4
- package/src/sdk/broker/spawn-authority.ts +35 -7
- package/src/sdk/broker/stderr-drainer.ts +59 -0
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/index.ts +690 -80
- package/src/sdk/bus/telegram-daemon-cli.ts +31 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -3
- package/src/sdk/cli/master-cli.ts +47 -36
- package/src/sdk/cli/rows.ts +30 -7
- package/src/sdk/cli/session-cli.ts +687 -184
- package/src/sdk/client/client.ts +18 -0
- package/src/sdk/guides/catalog.ts +2 -2
- package/src/sdk/guides/cli.ts +20 -62
- package/src/sdk/guides/verify.ts +14 -0
- package/src/sdk/host/host.ts +49 -5
- package/src/sdk/host/session-runtime.test.ts +138 -2
- package/src/sdk/host/session-runtime.ts +311 -97
- package/src/sdk/host/types.ts +12 -1
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +84 -0
- package/src/sdk/host/websocket-transport.ts +40 -6
- package/src/sdk/index.ts +11 -0
- package/src/sdk/prompt-deadline-flush.ts +334 -0
- package/src/sdk/prompt-deadline-manager.ts +133 -1
- package/src/sdk/prompt-tool-boundary.ts +121 -0
- package/src/sdk/protocol/operation-inventory.generated.json +106 -0
- package/src/sdk/router/session-router.ts +9 -1
- package/src/sdk/session.ts +182 -7
- package/src/sdk/startup-capability.ts +1 -1
- package/src/sdk/transport/relay.ts +71 -6
- package/src/sdk/transport/serve-cli.ts +397 -42
- package/src/sdk/transport/socket.ts +12 -11
- package/src/sdk/transport/stdio.ts +7 -3
- package/src/session/agent-session.ts +2437 -400
- package/src/session/fallback-chain-controller.ts +6 -7
- package/src/session/fold-coordinator.ts +24 -5
- package/src/session/internal/managed-session-scope.ts +58 -18
- package/src/session/internal/managed-session-storage.ts +304 -11
- package/src/session/session-manager.ts +382 -44
- package/src/session/session-work-lease.ts +34 -0
- package/src/setup/model-onboarding-guidance.ts +1 -1
- package/src/setup/provider-onboarding.ts +397 -24
- package/src/slash-commands/builtin-registry.ts +258 -14
- package/src/task/index.ts +14 -2
- package/src/tools/bash.ts +98 -78
- package/src/tools/fetch.ts +119 -20
- package/src/tools/job.ts +151 -25
- package/src/tools/output-meta.ts +15 -2
- package/src/tools/path-utils.ts +27 -3
- package/src/tools/read.ts +3 -3
- package/src/tools/steer-fold.ts +107 -0
- package/src/tools/tool-catalog.generated.ts +29 -2
- package/src/tools/tool-errors.ts +4 -0
- package/src/utils/git.ts +53 -3
- package/src/utils/markit.ts +55 -41
- package/src/utils/mupdf-assets.d.ts +4 -0
- package/src/utils/mupdf-embedded.ts +4 -0
- package/src/utils/mupdf.ts +129 -0
- package/src/utils/session-title-generation.ts +29 -0
- package/src/utils/title-generator.ts +46 -2
- package/src/web/scrapers/types.ts +24 -3
- package/vendor/markit-ai/LICENSE +21 -0
- package/vendor/markit-ai/MANIFEST.json +412 -0
- package/vendor/markit-ai/dist/commands/config.d.ts +4 -0
- package/vendor/markit-ai/dist/commands/config.js +139 -0
- package/vendor/markit-ai/dist/commands/convert.d.ts +6 -0
- package/vendor/markit-ai/dist/commands/convert.js +113 -0
- package/vendor/markit-ai/dist/commands/formats.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/formats.js +92 -0
- package/vendor/markit-ai/dist/commands/init.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/init.js +29 -0
- package/vendor/markit-ai/dist/commands/onboard.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/onboard.js +61 -0
- package/vendor/markit-ai/dist/commands/plugin.d.ts +4 -0
- package/vendor/markit-ai/dist/commands/plugin.js +58 -0
- package/vendor/markit-ai/dist/config.d.ts +26 -0
- package/vendor/markit-ai/dist/config.js +42 -0
- package/vendor/markit-ai/dist/converters/audio.d.ts +7 -0
- package/vendor/markit-ai/dist/converters/audio.js +104 -0
- package/vendor/markit-ai/dist/converters/csv.d.ts +7 -0
- package/vendor/markit-ai/dist/converters/csv.js +83 -0
- package/vendor/markit-ai/dist/converters/docx.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/docx.js +57 -0
- package/vendor/markit-ai/dist/converters/epub.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/epub.js +128 -0
- package/vendor/markit-ai/dist/converters/github.d.ts +18 -0
- package/vendor/markit-ai/dist/converters/github.js +148 -0
- package/vendor/markit-ai/dist/converters/html.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/html.js +30 -0
- package/vendor/markit-ai/dist/converters/image.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/image.js +124 -0
- package/vendor/markit-ai/dist/converters/ipynb.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/ipynb.js +75 -0
- package/vendor/markit-ai/dist/converters/iwork.d.ts +20 -0
- package/vendor/markit-ai/dist/converters/iwork.js +391 -0
- package/vendor/markit-ai/dist/converters/json.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/json.js +21 -0
- package/vendor/markit-ai/dist/converters/pdf/columns.d.ts +35 -0
- package/vendor/markit-ai/dist/converters/pdf/columns.js +93 -0
- package/vendor/markit-ai/dist/converters/pdf/extract.d.ts +19 -0
- package/vendor/markit-ai/dist/converters/pdf/extract.js +514 -0
- package/vendor/markit-ai/dist/converters/pdf/grid.d.ts +25 -0
- package/vendor/markit-ai/dist/converters/pdf/grid.js +795 -0
- package/vendor/markit-ai/dist/converters/pdf/headers.d.ts +24 -0
- package/vendor/markit-ai/dist/converters/pdf/headers.js +108 -0
- package/vendor/markit-ai/dist/converters/pdf/index.d.ts +19 -0
- package/vendor/markit-ai/dist/converters/pdf/index.js +136 -0
- package/vendor/markit-ai/dist/converters/pdf/mupdf-loader.js +9 -0
- package/vendor/markit-ai/dist/converters/pdf/render.d.ts +24 -0
- package/vendor/markit-ai/dist/converters/pdf/render.js +513 -0
- package/vendor/markit-ai/dist/converters/pdf/types.d.ts +75 -0
- package/vendor/markit-ai/dist/converters/pdf/types.js +1 -0
- package/vendor/markit-ai/dist/converters/plain-text.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/plain-text.js +87 -0
- package/vendor/markit-ai/dist/converters/pptx.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/pptx.js +289 -0
- package/vendor/markit-ai/dist/converters/rss.d.ts +11 -0
- package/vendor/markit-ai/dist/converters/rss.js +138 -0
- package/vendor/markit-ai/dist/converters/wikipedia.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/wikipedia.js +33 -0
- package/vendor/markit-ai/dist/converters/xlsx.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/xlsx.js +147 -0
- package/vendor/markit-ai/dist/converters/xml.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/xml.js +18 -0
- package/vendor/markit-ai/dist/converters/yaml.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/yaml.js +17 -0
- package/vendor/markit-ai/dist/converters/zip.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/zip.js +57 -0
- package/vendor/markit-ai/dist/index.d.ts +30 -0
- package/vendor/markit-ai/dist/index.js +26 -0
- package/vendor/markit-ai/dist/main.d.ts +2 -0
- package/vendor/markit-ai/dist/main.js +191 -0
- package/vendor/markit-ai/dist/markit.d.ts +45 -0
- package/vendor/markit-ai/dist/markit.js +267 -0
- package/vendor/markit-ai/dist/plugins/api.d.ts +7 -0
- package/vendor/markit-ai/dist/plugins/api.js +42 -0
- package/vendor/markit-ai/dist/plugins/index.d.ts +4 -0
- package/vendor/markit-ai/dist/plugins/index.js +3 -0
- package/vendor/markit-ai/dist/plugins/installer.d.ts +25 -0
- package/vendor/markit-ai/dist/plugins/installer.js +176 -0
- package/vendor/markit-ai/dist/plugins/loader.d.ts +6 -0
- package/vendor/markit-ai/dist/plugins/loader.js +61 -0
- package/vendor/markit-ai/dist/plugins/types.d.ts +25 -0
- package/vendor/markit-ai/dist/plugins/types.js +1 -0
- package/vendor/markit-ai/dist/providers/anthropic.d.ts +2 -0
- package/vendor/markit-ai/dist/providers/anthropic.js +47 -0
- package/vendor/markit-ai/dist/providers/index.d.ts +21 -0
- package/vendor/markit-ai/dist/providers/index.js +56 -0
- package/vendor/markit-ai/dist/providers/openai.d.ts +2 -0
- package/vendor/markit-ai/dist/providers/openai.js +76 -0
- package/vendor/markit-ai/dist/providers/types.d.ts +26 -0
- package/vendor/markit-ai/dist/providers/types.js +1 -0
- package/vendor/markit-ai/dist/types.d.ts +36 -0
- package/vendor/markit-ai/dist/types.js +1 -0
- package/vendor/markit-ai/dist/utils/exit-codes.d.ts +4 -0
- package/vendor/markit-ai/dist/utils/exit-codes.js +4 -0
- package/vendor/markit-ai/dist/utils/output.d.ts +22 -0
- package/vendor/markit-ai/dist/utils/output.js +31 -0
- package/vendor/markit-ai/dist/utils/turndown.d.ts +8 -0
- package/vendor/markit-ai/dist/utils/turndown.js +70 -0
- package/vendor/markit-ai/package.json +74 -0
- package/vendor/markit-ai.patch +49 -0
|
@@ -14,9 +14,10 @@ interface UserMessageItem {
|
|
|
14
14
|
*/
|
|
15
15
|
class UserMessageList implements Component {
|
|
16
16
|
#selectedIndex: number = 0;
|
|
17
|
+
#selectionInFlight = false;
|
|
17
18
|
onSelect?: (entryId: string) => void;
|
|
18
19
|
onCancel?: () => void;
|
|
19
|
-
#
|
|
20
|
+
#availableRows = Number.POSITIVE_INFINITY;
|
|
20
21
|
|
|
21
22
|
constructor(private readonly messages: UserMessageItem[]) {
|
|
22
23
|
// Store messages in chronological order (oldest to newest)
|
|
@@ -24,6 +25,10 @@ class UserMessageList implements Component {
|
|
|
24
25
|
this.#selectedIndex = Math.max(0, this.messages.length - 1);
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
setAvailableRows(rows: number): void {
|
|
29
|
+
this.#availableRows = Math.max(3, rows);
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
invalidate(): void {
|
|
28
33
|
// No cached state to invalidate currently
|
|
29
34
|
}
|
|
@@ -36,12 +41,15 @@ class UserMessageList implements Component {
|
|
|
36
41
|
return lines;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
//
|
|
44
|
+
// Each entry uses two content rows plus a separator. Leave one row for
|
|
45
|
+
// the scroll position whenever the full history does not fit.
|
|
46
|
+
const needsScrollIndicator = this.messages.length * 3 > this.#availableRows;
|
|
47
|
+
const maxVisible = Math.max(1, Math.floor((this.#availableRows - (needsScrollIndicator ? 1 : 0)) / 3));
|
|
40
48
|
const startIndex = Math.max(
|
|
41
49
|
0,
|
|
42
|
-
Math.min(this.#selectedIndex - Math.floor(
|
|
50
|
+
Math.min(this.#selectedIndex - Math.floor(maxVisible / 2), this.messages.length - maxVisible),
|
|
43
51
|
);
|
|
44
|
-
const endIndex = Math.min(startIndex +
|
|
52
|
+
const endIndex = Math.min(startIndex + maxVisible, this.messages.length);
|
|
45
53
|
|
|
46
54
|
// Render visible messages (2 lines per message + blank line)
|
|
47
55
|
for (let i = startIndex; i < endIndex; i++) {
|
|
@@ -77,6 +85,7 @@ class UserMessageList implements Component {
|
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
handleInput(keyData: string): void {
|
|
88
|
+
if (this.#selectionInFlight) return;
|
|
80
89
|
// Up arrow - go to previous (older) message, wrap to bottom when at top
|
|
81
90
|
if (matchesKey(keyData, "up")) {
|
|
82
91
|
this.#selectedIndex = this.#selectedIndex === 0 ? this.messages.length - 1 : this.#selectedIndex - 1;
|
|
@@ -89,6 +98,7 @@ class UserMessageList implements Component {
|
|
|
89
98
|
else if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
90
99
|
const selected = this.messages[this.#selectedIndex];
|
|
91
100
|
if (selected && this.onSelect) {
|
|
101
|
+
this.#selectionInFlight = true;
|
|
92
102
|
this.onSelect(selected.id);
|
|
93
103
|
}
|
|
94
104
|
}
|
|
@@ -102,21 +112,43 @@ class UserMessageList implements Component {
|
|
|
102
112
|
}
|
|
103
113
|
|
|
104
114
|
/**
|
|
105
|
-
* Component that renders a
|
|
115
|
+
* Component that renders a prompt selector for creating a new session
|
|
106
116
|
*/
|
|
107
117
|
export class UserMessageSelectorComponent extends Container {
|
|
108
118
|
#messageList: UserMessageList;
|
|
109
|
-
|
|
110
|
-
|
|
119
|
+
#header: Container;
|
|
120
|
+
#footer: Container;
|
|
121
|
+
|
|
122
|
+
constructor(
|
|
123
|
+
messages: UserMessageItem[],
|
|
124
|
+
onSelect: (entryId: string) => void,
|
|
125
|
+
onCancel: () => void,
|
|
126
|
+
private readonly getViewportRows: () => number,
|
|
127
|
+
) {
|
|
111
128
|
super();
|
|
112
129
|
|
|
113
130
|
// Add header
|
|
114
|
-
this
|
|
115
|
-
this.addChild(new
|
|
116
|
-
this.addChild(new Text(theme.
|
|
117
|
-
this.addChild(
|
|
118
|
-
|
|
119
|
-
|
|
131
|
+
this.#header = new Container();
|
|
132
|
+
this.#header.addChild(new Spacer(1));
|
|
133
|
+
this.#header.addChild(new Text(theme.bold("Fork from Prompt"), 1, 0));
|
|
134
|
+
this.#header.addChild(
|
|
135
|
+
new Text(
|
|
136
|
+
theme.fg(
|
|
137
|
+
"muted",
|
|
138
|
+
"Creates a new session with history before the prompt; shared files stay in the same cwd.",
|
|
139
|
+
),
|
|
140
|
+
1,
|
|
141
|
+
0,
|
|
142
|
+
),
|
|
143
|
+
);
|
|
144
|
+
this.#header.addChild(
|
|
145
|
+
new Text(theme.fg("muted", "The selected prompt is restored for editing, not submitted."), 1, 0),
|
|
146
|
+
);
|
|
147
|
+
this.#header.addChild(new Text(theme.fg("muted", "↑/↓ move · Enter select · Esc cancel"), 1, 0));
|
|
148
|
+
this.#header.addChild(new Spacer(1));
|
|
149
|
+
this.#header.addChild(new DynamicBorder());
|
|
150
|
+
this.#header.addChild(new Spacer(1));
|
|
151
|
+
this.addChild(this.#header);
|
|
120
152
|
|
|
121
153
|
// Create message list
|
|
122
154
|
this.#messageList = new UserMessageList(messages);
|
|
@@ -126,8 +158,10 @@ export class UserMessageSelectorComponent extends Container {
|
|
|
126
158
|
this.addChild(this.#messageList);
|
|
127
159
|
|
|
128
160
|
// Add bottom border
|
|
129
|
-
this
|
|
130
|
-
this.addChild(new
|
|
161
|
+
this.#footer = new Container();
|
|
162
|
+
this.#footer.addChild(new Spacer(1));
|
|
163
|
+
this.#footer.addChild(new DynamicBorder());
|
|
164
|
+
this.addChild(this.#footer);
|
|
131
165
|
|
|
132
166
|
// Auto-cancel if no messages
|
|
133
167
|
if (messages.length === 0) {
|
|
@@ -135,6 +169,14 @@ export class UserMessageSelectorComponent extends Container {
|
|
|
135
169
|
}
|
|
136
170
|
}
|
|
137
171
|
|
|
172
|
+
render(width: number): string[] {
|
|
173
|
+
const chromeRows = this.#header.render(width).length + this.#footer.render(width).length;
|
|
174
|
+
// Reserve the pinned status/composer rows outside the overlay. Recompute
|
|
175
|
+
// against current terminal rows and wrapped header height on every render.
|
|
176
|
+
this.#messageList.setAvailableRows(this.getViewportRows() - chromeRows - 2);
|
|
177
|
+
return super.render(width);
|
|
178
|
+
}
|
|
179
|
+
|
|
138
180
|
getMessageList(): UserMessageList {
|
|
139
181
|
return this.#messageList;
|
|
140
182
|
}
|
|
@@ -38,7 +38,16 @@ import { replaceTabs } from "../../tools/render-utils";
|
|
|
38
38
|
import { getDisplayChangelogEntries } from "../../utils/changelog";
|
|
39
39
|
import { copyToClipboard } from "../../utils/clipboard";
|
|
40
40
|
import { openPath } from "../../utils/open";
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
beginSessionTitleGeneration,
|
|
43
|
+
invalidateSessionTitleGeneration,
|
|
44
|
+
isSessionTitleGenerationCurrent,
|
|
45
|
+
} from "../../utils/session-title-generation";
|
|
46
|
+
import {
|
|
47
|
+
buildConversationTitleInput,
|
|
48
|
+
generateSessionTitle,
|
|
49
|
+
setSessionTerminalTitle,
|
|
50
|
+
} from "../../utils/title-generator";
|
|
42
51
|
import { addChatChild, prepareTranscriptRebuild, syncPendingExecutionComponents } from "../utils/ui-helpers";
|
|
43
52
|
|
|
44
53
|
type HindsightModule = typeof import("../../hindsight");
|
|
@@ -84,6 +93,7 @@ export function buildHelpMarkdown(keybindings: Pick<KeybindingsManager, "getAcce
|
|
|
84
93
|
"| Resume another session | `/resume` |",
|
|
85
94
|
"| Show session details | `/session info` |",
|
|
86
95
|
"| Delete current session transcript/artifacts | `/session delete` |",
|
|
96
|
+
"| Fork from an earlier prompt into a new session | `/fork` |",
|
|
87
97
|
`| Select a model | \`/model\` or ${selectModelKey} |`,
|
|
88
98
|
`| Queue a message for the next turn | ${queueMessageKey} |`,
|
|
89
99
|
`| Select or edit a queued message | ${dequeueMessageKey} |`,
|
|
@@ -1109,31 +1119,7 @@ export class CommandController {
|
|
|
1109
1119
|
}
|
|
1110
1120
|
|
|
1111
1121
|
async handleForkCommand(): Promise<void> {
|
|
1112
|
-
|
|
1113
|
-
this.ctx.showWarning("Wait for the current response to finish or abort it before forking.");
|
|
1114
|
-
return;
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
const success = await this.ctx.session.fork();
|
|
1118
|
-
if (this.ctx.isStopped?.()) return;
|
|
1119
|
-
if (!success) {
|
|
1120
|
-
this.ctx.showError("Fork failed (session not persisted or cancelled)");
|
|
1121
|
-
return;
|
|
1122
|
-
}
|
|
1123
|
-
clearInteractiveActivityLoaders(this.ctx);
|
|
1124
|
-
stopInteractiveActivityIndicator(this.ctx, { foregroundSettled: true });
|
|
1125
|
-
this.ctx.resetIrcSidebarSession();
|
|
1126
|
-
|
|
1127
|
-
this.ctx.statusLine.invalidate();
|
|
1128
|
-
this.ctx.updateEditorTopBorder();
|
|
1129
|
-
|
|
1130
|
-
const sessionFile = this.ctx.session.sessionFile;
|
|
1131
|
-
const shortPath = sessionFile ? sessionFile.split("/").pop() : "new session";
|
|
1132
|
-
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
1133
|
-
this.ctx.chatContainer.addChild(
|
|
1134
|
-
new Text(`${theme.fg("accent", `${theme.status.success} Session forked to ${shortPath}`)}`, 1, 1),
|
|
1135
|
-
);
|
|
1136
|
-
this.ctx.ui.requestRender();
|
|
1122
|
+
this.ctx.showUserMessageSelector();
|
|
1137
1123
|
}
|
|
1138
1124
|
|
|
1139
1125
|
async handleMoveCommand(targetPath: string): Promise<void> {
|
|
@@ -1186,23 +1172,63 @@ export class CommandController {
|
|
|
1186
1172
|
}
|
|
1187
1173
|
}
|
|
1188
1174
|
|
|
1189
|
-
async handleRenameCommand(title
|
|
1175
|
+
async handleRenameCommand(title?: string): Promise<void> {
|
|
1190
1176
|
try {
|
|
1177
|
+
if (!title?.trim()) {
|
|
1178
|
+
const generation = beginSessionTitleGeneration(this.ctx.sessionManager);
|
|
1179
|
+
const input = buildConversationTitleInput(this.ctx.session.messages);
|
|
1180
|
+
if (!input) {
|
|
1181
|
+
this.ctx.showError("Nothing to summarize yet — pass a title: /rename <title>");
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
const generated = await generateSessionTitle(
|
|
1186
|
+
input,
|
|
1187
|
+
this.ctx.session.modelRegistry,
|
|
1188
|
+
this.ctx.settings,
|
|
1189
|
+
this.ctx.session.credentialSessionId,
|
|
1190
|
+
this.ctx.session.model,
|
|
1191
|
+
provider => this.ctx.session.agent.metadataForProvider(provider),
|
|
1192
|
+
);
|
|
1193
|
+
if (
|
|
1194
|
+
!isSessionTitleGenerationCurrent(this.ctx.sessionManager, generation) ||
|
|
1195
|
+
buildConversationTitleInput(this.ctx.session.messages) !== input
|
|
1196
|
+
)
|
|
1197
|
+
return;
|
|
1198
|
+
if (!generated) {
|
|
1199
|
+
this.ctx.showError("Could not generate a session title — pass one: /rename <title>");
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
const stored = await this.ctx.sessionManager.setSessionName(generated, "user");
|
|
1204
|
+
if (!stored) {
|
|
1205
|
+
this.ctx.showError("Session name cannot be empty.");
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
this.#finishRename();
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
invalidateSessionTitleGeneration(this.ctx.sessionManager);
|
|
1191
1213
|
const stored = await this.ctx.sessionManager.setSessionName(title, "user");
|
|
1192
1214
|
if (!stored) {
|
|
1193
1215
|
this.ctx.showError("Session name cannot be empty.");
|
|
1194
1216
|
return;
|
|
1195
1217
|
}
|
|
1196
|
-
|
|
1197
|
-
setSessionTerminalTitle(name, this.ctx.sessionManager.getCwd());
|
|
1198
|
-
this.ctx.statusLine.invalidate();
|
|
1199
|
-
this.ctx.updateEditorBorderColor();
|
|
1200
|
-
this.ctx.showStatus(`Session renamed to "${name}".`);
|
|
1218
|
+
this.#finishRename();
|
|
1201
1219
|
} catch (err) {
|
|
1202
1220
|
this.ctx.showError(`Rename failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1203
1221
|
}
|
|
1204
1222
|
}
|
|
1205
1223
|
|
|
1224
|
+
#finishRename(): void {
|
|
1225
|
+
const name = this.ctx.sessionManager.getSessionName()!;
|
|
1226
|
+
setSessionTerminalTitle(name, this.ctx.sessionManager.getCwd());
|
|
1227
|
+
this.ctx.statusLine.invalidate();
|
|
1228
|
+
this.ctx.updateEditorBorderColor();
|
|
1229
|
+
this.ctx.showStatus(`Session renamed to "${name}".`);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1206
1232
|
async handleBashCommand(command: string, excludeFromContext = false): Promise<void> {
|
|
1207
1233
|
const isDeferred = this.ctx.session.isStreaming;
|
|
1208
1234
|
const component = new BashExecutionComponent(command, this.ctx.ui, excludeFromContext);
|
|
@@ -146,31 +146,55 @@ export class ExtensionUiController {
|
|
|
146
146
|
const widgetsBelow = [...this.#hookWidgetsBelow.entries()];
|
|
147
147
|
const activeHookCustomComponent = this.#activeHookCustomComponent;
|
|
148
148
|
const activeHookCustomOverlay = this.#activeHookCustomOverlay;
|
|
149
|
+
const activeHookCustomCancel = this.#activeHookCustomCancel;
|
|
149
150
|
return () => {
|
|
151
|
+
const errors: unknown[] = [];
|
|
152
|
+
const attempt = (cleanup: () => void): void => {
|
|
153
|
+
try {
|
|
154
|
+
cleanup();
|
|
155
|
+
} catch (error) {
|
|
156
|
+
errors.push(error);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
150
159
|
for (const unsubscribe of terminalInputUnsubscribers) {
|
|
151
|
-
unsubscribe
|
|
152
|
-
|
|
160
|
+
if (!this.#extensionTerminalInputUnsubscribers.delete(unsubscribe)) continue;
|
|
161
|
+
attempt(unsubscribe);
|
|
153
162
|
}
|
|
154
163
|
let widgetsChanged = false;
|
|
155
164
|
for (const [key, widget] of widgetsAbove) {
|
|
156
165
|
if (this.#hookWidgetsAbove.get(key) !== widget) continue;
|
|
157
166
|
this.#hookWidgetsAbove.delete(key);
|
|
158
|
-
widget.dispose?.();
|
|
167
|
+
attempt(() => widget.dispose?.());
|
|
159
168
|
widgetsChanged = true;
|
|
160
169
|
}
|
|
161
170
|
for (const [key, widget] of widgetsBelow) {
|
|
162
171
|
if (this.#hookWidgetsBelow.get(key) !== widget) continue;
|
|
163
172
|
this.#hookWidgetsBelow.delete(key);
|
|
164
|
-
widget.dispose?.();
|
|
173
|
+
attempt(() => widget.dispose?.());
|
|
165
174
|
widgetsChanged = true;
|
|
166
175
|
}
|
|
167
176
|
if (
|
|
168
177
|
this.#activeHookCustomComponent === activeHookCustomComponent &&
|
|
169
|
-
this.#activeHookCustomOverlay === activeHookCustomOverlay
|
|
178
|
+
this.#activeHookCustomOverlay === activeHookCustomOverlay &&
|
|
179
|
+
this.#activeHookCustomCancel === activeHookCustomCancel
|
|
170
180
|
) {
|
|
171
|
-
this.#
|
|
181
|
+
this.#activeHookCustomComponent = undefined;
|
|
182
|
+
this.#activeHookCustomOverlay = undefined;
|
|
183
|
+
this.#activeHookCustomCancel = undefined;
|
|
184
|
+
attempt(() => activeHookCustomCancel?.());
|
|
185
|
+
attempt(() => activeHookCustomComponent?.dispose?.());
|
|
186
|
+
attempt(() => activeHookCustomOverlay?.hide());
|
|
187
|
+
}
|
|
188
|
+
if (widgetsChanged) {
|
|
189
|
+
attempt(() =>
|
|
190
|
+
this.#renderHookWidgetContainer(this.ctx.hookWidgetContainerAbove, this.#hookWidgetsAbove, true),
|
|
191
|
+
);
|
|
192
|
+
attempt(() =>
|
|
193
|
+
this.#renderHookWidgetContainer(this.ctx.hookWidgetContainerBelow, this.#hookWidgetsBelow, false),
|
|
194
|
+
);
|
|
195
|
+
attempt(() => this.ctx.ui.requestRender());
|
|
172
196
|
}
|
|
173
|
-
if (
|
|
197
|
+
if (errors.length > 0) throw new AggregateError(errors, "Previous session extension UI cleanup failed");
|
|
174
198
|
};
|
|
175
199
|
}
|
|
176
200
|
|
|
@@ -551,6 +575,7 @@ export class ExtensionUiController {
|
|
|
551
575
|
getCredentialSessionId: () => this.ctx.session.credentialSessionId,
|
|
552
576
|
isIdle: () => !this.ctx.session.isStreaming,
|
|
553
577
|
getActivePromptHandle: () => this.ctx.session.activePromptHandle,
|
|
578
|
+
getSessionWorkLease: () => this.ctx.session.sessionWorkLease,
|
|
554
579
|
abort: () => this.ctx.session.abort(),
|
|
555
580
|
abortPromptAndWait: (handle, options) => this.ctx.session.abortPromptAndWait(handle, options),
|
|
556
581
|
hasPendingMessages: () => this.ctx.session.queuedMessageCount > 0,
|
|
@@ -781,6 +806,7 @@ export class ExtensionUiController {
|
|
|
781
806
|
getCredentialSessionId: () => this.ctx.session.credentialSessionId,
|
|
782
807
|
isIdle: () => !this.ctx.session.isStreaming,
|
|
783
808
|
getActivePromptHandle: () => this.ctx.session.activePromptHandle,
|
|
809
|
+
getSessionWorkLease: () => this.ctx.session.sessionWorkLease,
|
|
784
810
|
abort: () => this.ctx.session.abort(),
|
|
785
811
|
abortPromptAndWait: (handle, options) => this.ctx.session.abortPromptAndWait(handle, options),
|
|
786
812
|
hasPendingMessages: () => this.ctx.session.queuedMessageCount > 0,
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
locatePastedImageReferenceAroundCursor,
|
|
34
34
|
parsePastedImagePaths,
|
|
35
35
|
} from "../../utils/pasted-image-path";
|
|
36
|
+
import { invalidateSessionTitleGeneration } from "../../utils/session-title-generation";
|
|
36
37
|
import { generateSessionTitle, setSessionTerminalTitle } from "../../utils/title-generator";
|
|
37
38
|
import { ActionRegistry, APP_ACTION_METADATA } from "../action-registry";
|
|
38
39
|
import { CommandPalette, type CommandPaletteAction, type CommandPaletteEntry } from "../components/command-palette";
|
|
@@ -1146,6 +1147,7 @@ export class InputController {
|
|
|
1146
1147
|
|
|
1147
1148
|
// Queue input during compaction
|
|
1148
1149
|
if (this.ctx.session.isCompacting) {
|
|
1150
|
+
invalidateSessionTitleGeneration(this.ctx.sessionManager);
|
|
1149
1151
|
if ((inputImages?.length ?? 0) > 0) {
|
|
1150
1152
|
this.ctx.showStatus("Compaction in progress. Retry after it completes to send images.");
|
|
1151
1153
|
return;
|
|
@@ -1159,6 +1161,7 @@ export class InputController {
|
|
|
1159
1161
|
// prompt to run after the active turn completes (in submission order).
|
|
1160
1162
|
// This handles extension commands (execute immediately), prompt template expansion, and queueing
|
|
1161
1163
|
if (this.ctx.session.isStreaming) {
|
|
1164
|
+
invalidateSessionTitleGeneration(this.ctx.sessionManager);
|
|
1162
1165
|
if (this.#canModifyComposer(composer)) {
|
|
1163
1166
|
this.ctx.editor.addToHistory(text);
|
|
1164
1167
|
this.ctx.editor.setText("");
|
|
@@ -1183,6 +1186,7 @@ export class InputController {
|
|
|
1183
1186
|
}
|
|
1184
1187
|
|
|
1185
1188
|
// Normal message submission
|
|
1189
|
+
invalidateSessionTitleGeneration(this.ctx.sessionManager);
|
|
1186
1190
|
// First, move any pending bash components to chat
|
|
1187
1191
|
this.ctx.flushPendingBashComponents();
|
|
1188
1192
|
|
|
@@ -155,7 +155,12 @@ import {
|
|
|
155
155
|
MODEL_ONBOARDING_PROVIDER_PRESET_COMMAND,
|
|
156
156
|
MODEL_ONBOARDING_SETUP_COMMAND,
|
|
157
157
|
} from "../../setup/model-onboarding-guidance";
|
|
158
|
-
import {
|
|
158
|
+
import {
|
|
159
|
+
addApiCompatibleProvider,
|
|
160
|
+
formatProviderSetupResult,
|
|
161
|
+
probeOpenAIModelsList,
|
|
162
|
+
reloadAndRefreshDiscoveryCatalog,
|
|
163
|
+
} from "../../setup/provider-onboarding";
|
|
159
164
|
import { getThinkingLevelMetadata } from "../../thinking";
|
|
160
165
|
import {
|
|
161
166
|
isConfigurableSearchProviderId,
|
|
@@ -1821,15 +1826,48 @@ export class SelectorController {
|
|
|
1821
1826
|
this.showSelector(done => {
|
|
1822
1827
|
let wizard: CustomProviderWizardComponent;
|
|
1823
1828
|
const submit = async (input: CustomProviderWizardSubmit): Promise<void> => {
|
|
1829
|
+
// Component-owned generation: revision or cancel after submit
|
|
1830
|
+
// start invalidates this attempt. Both completion branches
|
|
1831
|
+
// check it so a stale refresh cannot report success or
|
|
1832
|
+
// rerender over inputs the user has since revised.
|
|
1833
|
+
const generation = wizard.currentSubmitGeneration();
|
|
1834
|
+
const isCurrentSubmission = (): boolean => wizard.isSubmitCurrent(generation);
|
|
1824
1835
|
try {
|
|
1825
|
-
const result = await addApiCompatibleProvider(
|
|
1826
|
-
|
|
1836
|
+
const result = await addApiCompatibleProvider({
|
|
1837
|
+
...input,
|
|
1838
|
+
authStorage: this.ctx.session.modelRegistry.authStorage,
|
|
1839
|
+
});
|
|
1840
|
+
let recoveryHint: string | null = null;
|
|
1841
|
+
if (result.discoveryEnabled && !result.preset) {
|
|
1842
|
+
// A discovery-only add has no static models and no cache yet,
|
|
1843
|
+
// so an offline reload alone leaves an empty catalog. Refresh
|
|
1844
|
+
// only the new provider online; a hint is surfaced instead
|
|
1845
|
+
// of unconditional success when it stays unavailable.
|
|
1846
|
+
recoveryHint = await reloadAndRefreshDiscoveryCatalog(
|
|
1847
|
+
this.ctx.session.modelRegistry,
|
|
1848
|
+
result.providerId,
|
|
1849
|
+
this.ctx.session.credentialSessionId,
|
|
1850
|
+
);
|
|
1851
|
+
} else {
|
|
1852
|
+
await this.ctx.session.modelRegistry.refresh("offline", this.ctx.session.credentialSessionId);
|
|
1853
|
+
}
|
|
1827
1854
|
await this.ctx.notifyConfigChanged?.();
|
|
1828
|
-
|
|
1855
|
+
// A late settle after Esc-cancel or a mid-submit revision
|
|
1856
|
+
// must not drive completion UI: done() restores the
|
|
1857
|
+
// composer and could close a selector the user opened
|
|
1858
|
+
// after dismissal, and a stale refresh must not report
|
|
1859
|
+
// success over revised inputs.
|
|
1860
|
+
if (!isCurrentSubmission()) return;
|
|
1861
|
+
this.ctx.showStatus(
|
|
1862
|
+
recoveryHint
|
|
1863
|
+
? `${formatProviderSetupResult(result)}\n${recoveryHint}`
|
|
1864
|
+
: formatProviderSetupResult(result),
|
|
1865
|
+
);
|
|
1829
1866
|
wizard.complete();
|
|
1830
1867
|
done();
|
|
1831
1868
|
this.ctx.ui.requestRender();
|
|
1832
1869
|
} catch (err) {
|
|
1870
|
+
if (!isCurrentSubmission()) return;
|
|
1833
1871
|
const message = err instanceof Error ? err.message : String(err);
|
|
1834
1872
|
wizard.setSubmitError(`Provider setup failed: ${message}`);
|
|
1835
1873
|
}
|
|
@@ -1843,6 +1881,9 @@ export class SelectorController {
|
|
|
1843
1881
|
this.ctx.ui.requestRender();
|
|
1844
1882
|
},
|
|
1845
1883
|
() => this.ctx.ui.requestRender(),
|
|
1884
|
+
{
|
|
1885
|
+
discoverModels: async request => probeOpenAIModelsList(request),
|
|
1886
|
+
},
|
|
1846
1887
|
);
|
|
1847
1888
|
return { component: wizard, focus: wizard };
|
|
1848
1889
|
});
|
|
@@ -3070,36 +3111,138 @@ export class SelectorController {
|
|
|
3070
3111
|
});
|
|
3071
3112
|
}
|
|
3072
3113
|
|
|
3073
|
-
|
|
3074
|
-
|
|
3114
|
+
#forkBusyReason(): string | undefined {
|
|
3115
|
+
if (this.ctx.session.isStreaming) {
|
|
3116
|
+
return "Wait for the current response to finish or abort it before forking.";
|
|
3117
|
+
}
|
|
3118
|
+
if (this.ctx.session.isCompacting) {
|
|
3119
|
+
return "Wait for compaction to finish or abort it before forking.";
|
|
3120
|
+
}
|
|
3121
|
+
if (this.ctx.hasPendingSubmission()) {
|
|
3122
|
+
return "Wait for the pending message to be submitted or cancel it before forking.";
|
|
3123
|
+
}
|
|
3124
|
+
if (this.ctx.bashComponent || this.ctx.pythonComponent) {
|
|
3125
|
+
return "Wait for the foreground command to finish or cancel it before forking.";
|
|
3126
|
+
}
|
|
3127
|
+
return undefined;
|
|
3128
|
+
}
|
|
3075
3129
|
|
|
3130
|
+
showUserMessageSelector(onCommitted?: () => void): void {
|
|
3131
|
+
const openedSessionId = this.ctx.sessionManager.getSessionId();
|
|
3132
|
+
const openedSessionFile = this.ctx.sessionManager.getSessionFile();
|
|
3133
|
+
if (!openedSessionFile) {
|
|
3134
|
+
this.ctx.showError("Fork requires a persisted session");
|
|
3135
|
+
return;
|
|
3136
|
+
}
|
|
3137
|
+
const busyReason = this.#forkBusyReason();
|
|
3138
|
+
if (busyReason) {
|
|
3139
|
+
this.ctx.showWarning(busyReason);
|
|
3140
|
+
return;
|
|
3141
|
+
}
|
|
3142
|
+
|
|
3143
|
+
const userMessages = this.ctx.session.getUserMessagesForBranching();
|
|
3076
3144
|
if (userMessages.length === 0) {
|
|
3077
3145
|
this.ctx.showStatus("No messages to branch from");
|
|
3078
3146
|
return;
|
|
3079
3147
|
}
|
|
3080
3148
|
|
|
3149
|
+
this.ctx.goalModeController.cancelContinuation();
|
|
3150
|
+
// Close the side-chat composer before the picker takes ownership, including
|
|
3151
|
+
// paths that cancel, fail, or stop while branch() is pending.
|
|
3152
|
+
if (this.ctx.hasActiveBtw()) this.ctx.handleBtwEscape();
|
|
3153
|
+
|
|
3081
3154
|
this.showSelector(done => {
|
|
3155
|
+
let selectionInFlight = false;
|
|
3156
|
+
const closeWithoutBranch = (message: string, error = false): void => {
|
|
3157
|
+
done();
|
|
3158
|
+
if (error) {
|
|
3159
|
+
this.ctx.showError(message);
|
|
3160
|
+
} else {
|
|
3161
|
+
this.ctx.showStatus(message);
|
|
3162
|
+
}
|
|
3163
|
+
this.ctx.goalModeController.scheduleContinuation();
|
|
3164
|
+
this.ctx.ui.requestRender();
|
|
3165
|
+
};
|
|
3082
3166
|
const selector = new UserMessageSelectorComponent(
|
|
3083
3167
|
userMessages.map(m => ({ id: m.entryId, text: m.text })),
|
|
3084
3168
|
async entryId => {
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
this.ctx.
|
|
3169
|
+
if (selectionInFlight) return;
|
|
3170
|
+
selectionInFlight = true;
|
|
3171
|
+
|
|
3172
|
+
if (
|
|
3173
|
+
this.ctx.sessionManager.getSessionId() !== openedSessionId ||
|
|
3174
|
+
this.ctx.sessionManager.getSessionFile() !== openedSessionFile
|
|
3175
|
+
) {
|
|
3176
|
+
closeWithoutBranch("Fork cancelled because the active session changed", true);
|
|
3177
|
+
return;
|
|
3178
|
+
}
|
|
3179
|
+
const busyReason = this.#forkBusyReason();
|
|
3180
|
+
if (busyReason) {
|
|
3181
|
+
closeWithoutBranch(busyReason);
|
|
3090
3182
|
return;
|
|
3091
3183
|
}
|
|
3092
|
-
this.ctx.resetIrcSidebarSession();
|
|
3093
3184
|
|
|
3094
|
-
|
|
3095
|
-
|
|
3185
|
+
let selectedText = userMessages.find(message => message.entryId === entryId)!.text;
|
|
3186
|
+
let branchFailure: unknown;
|
|
3187
|
+
try {
|
|
3188
|
+
const result = await this.ctx.session.branch(entryId);
|
|
3189
|
+
if (this.ctx.isStopped?.()) return;
|
|
3190
|
+
if (result.cancelled) {
|
|
3191
|
+
closeWithoutBranch("Fork cancelled");
|
|
3192
|
+
return;
|
|
3193
|
+
}
|
|
3194
|
+
selectedText = result.selectedText;
|
|
3195
|
+
} catch (error) {
|
|
3196
|
+
if (this.ctx.isStopped?.()) return;
|
|
3197
|
+
const committed =
|
|
3198
|
+
this.ctx.sessionManager.getSessionId() !== openedSessionId ||
|
|
3199
|
+
this.ctx.sessionManager.getSessionFile() !== openedSessionFile;
|
|
3200
|
+
if (!committed) {
|
|
3201
|
+
closeWithoutBranch(`Fork failed: ${error instanceof Error ? error.message : String(error)}`, true);
|
|
3202
|
+
return;
|
|
3203
|
+
}
|
|
3204
|
+
// The successor is already durable. Never describe this as a
|
|
3205
|
+
// cancelled transition or roll it back to the parent's UI.
|
|
3206
|
+
branchFailure = error;
|
|
3207
|
+
}
|
|
3208
|
+
|
|
3209
|
+
try {
|
|
3210
|
+
onCommitted?.();
|
|
3211
|
+
} catch (error) {
|
|
3212
|
+
branchFailure ??= error;
|
|
3213
|
+
}
|
|
3214
|
+
try {
|
|
3215
|
+
this.ctx.resetIrcSidebarSession();
|
|
3216
|
+
this.ctx.resetObserverRegistry();
|
|
3217
|
+
this.#refreshSessionTerminalTitle();
|
|
3218
|
+
this.ctx.statusLine.invalidate();
|
|
3219
|
+
this.ctx.statusLine.setSessionStartTime(Date.now());
|
|
3220
|
+
this.ctx.updateEditorTopBorder();
|
|
3221
|
+
this.ctx.updateEditorBorderColor();
|
|
3222
|
+
this.ctx.rebuildInitialMessages("replace-identity");
|
|
3223
|
+
await this.ctx.reloadTodos();
|
|
3224
|
+
} catch (error) {
|
|
3225
|
+
branchFailure ??= error;
|
|
3226
|
+
}
|
|
3227
|
+
if (this.ctx.isStopped?.()) return;
|
|
3228
|
+
this.ctx.editor.setText(selectedText);
|
|
3096
3229
|
done();
|
|
3097
|
-
|
|
3230
|
+
if (branchFailure !== undefined) {
|
|
3231
|
+
this.ctx.showError(
|
|
3232
|
+
`Fork created, but session setup failed: ${branchFailure instanceof Error ? branchFailure.message : String(branchFailure)}`,
|
|
3233
|
+
);
|
|
3234
|
+
} else {
|
|
3235
|
+
this.ctx.showStatus("Forked to a new session; edit the selected prompt to continue");
|
|
3236
|
+
}
|
|
3237
|
+
// Do not schedule goal continuation over the selected prompt draft.
|
|
3238
|
+
this.ctx.ui.requestRender();
|
|
3098
3239
|
},
|
|
3099
3240
|
() => {
|
|
3100
3241
|
done();
|
|
3242
|
+
this.ctx.goalModeController.scheduleContinuation();
|
|
3101
3243
|
this.ctx.ui.requestRender();
|
|
3102
3244
|
},
|
|
3245
|
+
() => this.ctx.ui.terminal.rows,
|
|
3103
3246
|
);
|
|
3104
3247
|
return { component: selector, focus: selector.getMessageList() };
|
|
3105
3248
|
});
|
|
@@ -3274,6 +3417,9 @@ export class SelectorController {
|
|
|
3274
3417
|
});
|
|
3275
3418
|
}
|
|
3276
3419
|
},
|
|
3420
|
+
undefined,
|
|
3421
|
+
undefined,
|
|
3422
|
+
(session, starred) => this.ctx.sessionManager.setSessionStarredForPicker(session, starred),
|
|
3277
3423
|
);
|
|
3278
3424
|
selector.setOnRequestRender(() => this.ctx.ui.requestRender());
|
|
3279
3425
|
return { component: selector, focus: selector };
|
|
@@ -2265,10 +2265,24 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2265
2265
|
}
|
|
2266
2266
|
|
|
2267
2267
|
#prepareSessionSwitch(cleanupPreviousSessionUi?: () => void): void {
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2268
|
+
const errors: unknown[] = [];
|
|
2269
|
+
try {
|
|
2270
|
+
this.#btwController.dispose();
|
|
2271
|
+
} catch (error) {
|
|
2272
|
+
errors.push(error);
|
|
2273
|
+
}
|
|
2274
|
+
try {
|
|
2275
|
+
if (cleanupPreviousSessionUi) cleanupPreviousSessionUi();
|
|
2276
|
+
else this.#extensionUiController.clearExtensionTerminalInputListeners();
|
|
2277
|
+
} catch (error) {
|
|
2278
|
+
errors.push(error);
|
|
2279
|
+
}
|
|
2280
|
+
try {
|
|
2281
|
+
this.#planModeController.clearReview();
|
|
2282
|
+
} catch (error) {
|
|
2283
|
+
errors.push(error);
|
|
2284
|
+
}
|
|
2285
|
+
if (errors.length > 0) throw new AggregateError(errors, "Previous session UI cleanup failed");
|
|
2272
2286
|
}
|
|
2273
2287
|
|
|
2274
2288
|
async handleClearCommand(): Promise<boolean> {
|
|
@@ -2291,7 +2305,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2291
2305
|
}
|
|
2292
2306
|
|
|
2293
2307
|
handleForkCommand(): Promise<void> {
|
|
2294
|
-
this.#btwController.dispose();
|
|
2295
2308
|
return this.#commandController.handleForkCommand();
|
|
2296
2309
|
}
|
|
2297
2310
|
|
|
@@ -2299,7 +2312,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2299
2312
|
return this.#commandController.handleMoveCommand(targetPath);
|
|
2300
2313
|
}
|
|
2301
2314
|
|
|
2302
|
-
handleRenameCommand(title
|
|
2315
|
+
handleRenameCommand(title?: string): Promise<void> {
|
|
2303
2316
|
return this.#commandController.handleRenameCommand(title);
|
|
2304
2317
|
}
|
|
2305
2318
|
|
|
@@ -2564,7 +2577,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2564
2577
|
}
|
|
2565
2578
|
|
|
2566
2579
|
showUserMessageSelector(): void {
|
|
2567
|
-
this.#
|
|
2580
|
+
const cleanupPreviousSessionUi = this.#extensionUiController.captureSessionUiCleanup();
|
|
2581
|
+
this.#selectorController.showUserMessageSelector(() => this.#prepareSessionSwitch(cleanupPreviousSessionUi));
|
|
2568
2582
|
}
|
|
2569
2583
|
|
|
2570
2584
|
showTreeSelector(): void {
|
|
@@ -123,6 +123,7 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
123
123
|
getCredentialSessionId: () => session.credentialSessionId,
|
|
124
124
|
isIdle: () => !session.isStreaming,
|
|
125
125
|
getActivePromptHandle: () => session.activePromptHandle,
|
|
126
|
+
getSessionWorkLease: () => session.sessionWorkLease,
|
|
126
127
|
abort: () => session.abort(),
|
|
127
128
|
abortPromptAndWait: (handle, abortOptions) => session.abortPromptAndWait(handle, abortOptions),
|
|
128
129
|
hasPendingMessages: () => session.queuedMessageCount > 0,
|