@gajae-code/coding-agent 0.12.0 → 0.12.2
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 +111 -0
- package/dist/types/async/job-manager.d.ts +17 -3
- package/dist/types/config/settings-schema.d.ts +56 -2
- package/dist/types/exa/mcp-client.d.ts +10 -1
- package/dist/types/exec/bash-executor.d.ts +45 -12
- package/dist/types/extensibility/extensions/types.d.ts +16 -2
- package/dist/types/extensibility/gjc-plugins/index.d.ts +12 -2
- package/dist/types/extensibility/gjc-plugins/installer.d.ts +63 -7
- package/dist/types/extensibility/gjc-plugins/lifecycle-reconciliation.d.ts +55 -0
- package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +64 -0
- package/dist/types/extensibility/gjc-plugins/runtime-quarantine.d.ts +67 -0
- package/dist/types/extensibility/gjc-plugins/session-validation.d.ts +3 -1
- package/dist/types/extensibility/gjc-plugins/types.d.ts +124 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/gjc-runtime/ralplan-review-conflicts.d.ts +114 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +23 -3
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +25 -0
- package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +43 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +10 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +37 -80
- package/dist/types/modes/acp/acp-agent.d.ts +2 -19
- package/dist/types/modes/components/gjc-bundle-settings.d.ts +34 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +16 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +1 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +18 -0
- package/dist/types/modes/components/settings-selector.d.ts +9 -0
- package/dist/types/modes/components/status-line/gh.d.ts +5 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +18 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +1 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +2 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +9 -0
- package/dist/types/modes/theme/theme.d.ts +3 -1
- package/dist/types/modes/types.d.ts +32 -0
- package/dist/types/reminders/star-reminder.d.ts +3 -13
- package/dist/types/runtime-mcp/manager.d.ts +1 -0
- package/dist/types/sdk/acp/adapter.d.ts +3 -1
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/attachment-registry.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/config.d.ts +6 -2
- package/dist/types/sdk/bus/index.d.ts +29 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +8 -1
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +7 -3
- package/dist/types/sdk/bus/rich-draft.d.ts +1 -0
- package/dist/types/sdk/bus/rich-render.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +165 -0
- package/dist/types/sdk/bus/telegram-cli.d.ts +12 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -1
- package/dist/types/sdk/bus/telegram-reference.d.ts +11 -1
- package/dist/types/sdk/bus/threaded-render.d.ts +3 -2
- package/dist/types/sdk/bus/topic-registry.d.ts +11 -1
- package/dist/types/sdk/host/query/revision-store.d.ts +2 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
- package/dist/types/sdk/prompt-status.d.ts +23 -3
- package/dist/types/sdk/session.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +40 -2
- package/dist/types/session/artifacts.d.ts +8 -0
- package/dist/types/session/blob-store.d.ts +57 -4
- package/dist/types/session/internal/managed-session-storage.d.ts +30 -4
- package/dist/types/session/session-manager.d.ts +23 -6
- package/dist/types/session/session-storage.d.ts +4 -0
- package/dist/types/session/streaming-output.d.ts +36 -7
- package/dist/types/setup/credential-import.d.ts +12 -0
- package/dist/types/skill-state/active-state.d.ts +12 -0
- package/dist/types/skill-state/workflow-hud.d.ts +6 -0
- package/dist/types/task/executor.d.ts +3 -2
- package/dist/types/task/index.d.ts +7 -1
- package/dist/types/task/output-manager.d.ts +1 -0
- package/dist/types/task/provider-retry-status.d.ts +8 -0
- package/dist/types/task/receipt.d.ts +9 -1
- package/dist/types/task/types.d.ts +23 -0
- package/dist/types/tools/ask-answer-registry.d.ts +2 -2
- package/dist/types/tools/bash-interactive.d.ts +4 -1
- package/dist/types/tools/bash.d.ts +3 -1
- package/dist/types/tools/image-gen.d.ts +4 -0
- package/dist/types/tools/index.d.ts +6 -0
- package/dist/types/tools/output-meta.d.ts +23 -2
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/read.d.ts +0 -6
- package/dist/types/tools/review.d.ts +5 -7
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/utils/gh.d.ts +13 -0
- package/dist/types/utils/image-resize.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +2 -0
- package/dist/types/web/search/providers/searxng.d.ts +7 -0
- package/dist/types/web/search/providers/xai.d.ts +2 -0
- package/package.json +10 -8
- package/scripts/acp-conformance-agent.ts +218 -0
- package/scripts/capture-gjc-bundle-settings.ts +306 -0
- package/scripts/dogfood-ralplan-review-conflicts.ts +272 -0
- package/scripts/generate-sdk-operation-inventory.ts +5 -0
- package/scripts/resident-memory-bench.ts +1341 -0
- package/scripts/run-acp-conformance.ts +290 -0
- package/src/async/job-manager.ts +99 -39
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/plugin-cli.ts +176 -18
- package/src/commands/team.ts +19 -1
- package/src/config/file-lock.ts +11 -2
- package/src/config/settings-schema.ts +53 -2
- package/src/coordinator-mcp/question-gate-codec.ts +1 -1
- package/src/coordinator-mcp/server.ts +5 -12
- package/src/debug/index.ts +42 -17
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -0
- package/src/defaults/gjc/skills/ralplan/SKILL.md +25 -20
- package/src/defaults/gjc/skills/team/SKILL.md +2 -0
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +39 -26
- package/src/defaults/gjc/skills/ultragoal/validation-batch-contracts.md +12 -0
- package/src/defaults/gjc-defaults.ts +4 -4
- package/src/exa/mcp-client.ts +12 -3
- package/src/exec/bash-executor.ts +144 -27
- package/src/extensibility/extensions/runner.ts +15 -0
- package/src/extensibility/extensions/types.ts +18 -1
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +8 -2
- package/src/extensibility/gjc-plugins/index.ts +19 -2
- package/src/extensibility/gjc-plugins/installer.ts +189 -50
- package/src/extensibility/gjc-plugins/lifecycle-reconciliation.ts +199 -0
- package/src/extensibility/gjc-plugins/lifecycle.ts +626 -0
- package/src/extensibility/gjc-plugins/observability.ts +3 -3
- package/src/extensibility/gjc-plugins/prompt-appendix.ts +5 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +12 -2
- package/src/extensibility/gjc-plugins/runtime-quarantine.ts +121 -0
- package/src/extensibility/gjc-plugins/schema.ts +84 -12
- package/src/extensibility/gjc-plugins/session-validation.ts +9 -3
- package/src/extensibility/gjc-plugins/types.ts +141 -0
- package/src/extensibility/gjc-plugins/validation.ts +9 -0
- package/src/gjc-runtime/launch-tmux.ts +25 -6
- package/src/gjc-runtime/launch-worktree.ts +103 -1
- package/src/gjc-runtime/ledger-event-renderer.ts +1 -0
- package/src/gjc-runtime/managed-owner-admission.ts +1 -1
- package/src/gjc-runtime/ralplan-review-conflicts.ts +472 -0
- package/src/gjc-runtime/ralplan-runtime.ts +491 -27
- package/src/gjc-runtime/team-launch.ts +8 -0
- package/src/gjc-runtime/team-runtime.ts +140 -32
- package/src/gjc-runtime/team-worker-heartbeat.ts +149 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +10 -0
- package/src/gjc-runtime/tmux-sessions.ts +58 -13
- package/src/gjc-runtime/ultragoal-evidence.ts +6 -74
- package/src/gjc-runtime/ultragoal-runtime.ts +635 -612
- package/src/gjc-runtime/workflow-manifest.generated.json +37 -99
- package/src/gjc-runtime/workflow-manifest.ts +21 -70
- package/src/internal-urls/docs-index.generated.ts +16 -16
- package/src/modes/DESIGN.md +104 -0
- package/src/modes/acp/acp-agent.ts +298 -186
- package/src/modes/components/gjc-bundle-settings.ts +508 -0
- package/src/modes/components/irc-sidebar.ts +30 -1
- package/src/modes/components/notifications-settings-editor.ts +12 -1
- package/src/modes/components/oauth-selector.ts +68 -30
- package/src/modes/components/session-observer-overlay.ts +305 -86
- package/src/modes/components/settings-selector.ts +56 -5
- package/src/modes/components/status-line/gh.ts +16 -0
- package/src/modes/components/tool-status-header.ts +3 -15
- package/src/modes/components/transcript-viewer-overlay.ts +99 -24
- package/src/modes/controllers/command-controller.ts +42 -34
- package/src/modes/controllers/event-controller.ts +27 -16
- package/src/modes/controllers/extension-ui-controller.ts +193 -34
- package/src/modes/controllers/selector-controller.ts +160 -22
- package/src/modes/interactive-mode.ts +189 -62
- package/src/modes/jobs-observer.ts +1 -1
- package/src/modes/runtime-init.ts +3 -0
- package/src/modes/theme/theme.ts +11 -9
- package/src/modes/types.ts +88 -0
- package/src/prompts/agent-fragments/ralplan-persistence.md +4 -3
- package/src/prompts/system/eager-todo.md +8 -4
- package/src/prompts/system/system-prompt.md +11 -4
- package/src/prompts/tools/bash.md +5 -5
- package/src/prompts/tools/search-tool-bm25.md +5 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/reminders/star-reminder.ts +4 -42
- package/src/runtime-mcp/manager.ts +19 -4
- package/src/sdk/acp/adapter.ts +22 -6
- package/src/sdk/broker/lifecycle.ts +18 -13
- package/src/sdk/broker/transport.ts +35 -3
- package/src/sdk/bus/attachment-registry.ts +5 -1
- package/src/sdk/bus/chat-daemon-cli.ts +4 -39
- package/src/sdk/bus/chat-daemon-control.ts +7 -3
- package/src/sdk/bus/config.ts +18 -2
- package/src/sdk/bus/index.ts +661 -126
- package/src/sdk/bus/kind-aware-reconciliation.ts +148 -73
- package/src/sdk/bus/lifecycle-commands.ts +4 -1
- package/src/sdk/bus/lifecycle-control-runtime.ts +403 -88
- package/src/sdk/bus/reconciliation-store.ts +95 -16
- package/src/sdk/bus/rich-draft.ts +1 -1
- package/src/sdk/bus/rich-render.ts +2 -2
- package/src/sdk/bus/telegram-adoption-intent.ts +574 -0
- package/src/sdk/bus/telegram-cli.ts +33 -8
- package/src/sdk/bus/telegram-daemon-cli.ts +1 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +12 -4
- package/src/sdk/bus/telegram-daemon.ts +916 -23
- package/src/sdk/bus/telegram-reference.ts +40 -3
- package/src/sdk/bus/threaded-render.ts +48 -3
- package/src/sdk/bus/topic-registry.ts +20 -0
- package/src/sdk/host/query/revision-store.ts +36 -2
- package/src/sdk/host/reverse-leases.ts +44 -7
- package/src/sdk/prompt-status.ts +16 -3
- package/src/sdk/protocol/operation-inventory.generated.json +33 -0
- package/src/sdk/session.ts +89 -9
- package/src/session/agent-session.ts +844 -173
- package/src/session/artifacts.ts +22 -0
- package/src/session/auth-broker-config.ts +10 -3
- package/src/session/blob-store.ts +915 -24
- package/src/session/internal/managed-session-scope.ts +200 -111
- package/src/session/internal/managed-session-storage.ts +492 -62
- package/src/session/session-manager.ts +1614 -435
- package/src/session/session-storage.ts +38 -45
- package/src/session/streaming-output.ts +238 -68
- package/src/setup/credential-import.ts +75 -13
- package/src/skill-state/active-state.ts +32 -0
- package/src/skill-state/workflow-hud.ts +17 -0
- package/src/skill-state/workflow-mutation-guard.ts +496 -2
- package/src/task/executor.ts +122 -39
- package/src/task/index.ts +280 -45
- package/src/task/output-manager.ts +24 -14
- package/src/task/provider-retry-status.ts +16 -0
- package/src/task/receipt.ts +34 -8
- package/src/task/render.ts +6 -2
- package/src/task/types.ts +108 -0
- package/src/tools/ask-answer-registry.ts +12 -6
- package/src/tools/ask.ts +86 -55
- package/src/tools/ast-edit.ts +1 -0
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/bash-interactive.ts +8 -3
- package/src/tools/bash.ts +503 -60
- package/src/tools/find.ts +1 -0
- package/src/tools/image-gen.ts +29 -5
- package/src/tools/index.ts +6 -0
- package/src/tools/output-meta.ts +108 -14
- package/src/tools/path-utils.ts +3 -2
- package/src/tools/read.ts +18 -0
- package/src/tools/review.ts +5 -7
- package/src/tools/search.ts +1 -0
- package/src/tools/subagent-render.ts +6 -2
- package/src/tools/subagent.ts +6 -0
- package/src/tools/telegram-send.ts +6 -3
- package/src/utils/gh.ts +39 -0
- package/src/utils/image-resize.ts +3 -0
- package/src/web/search/providers/anthropic.ts +4 -4
- package/src/web/search/providers/kimi.ts +15 -5
- package/src/web/search/providers/searxng.ts +47 -4
- package/src/web/search/providers/xai.ts +7 -2
- package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +0 -23
- package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +0 -30
- package/src/gjc-runtime/ultragoal-pipeline.ts +0 -306
|
@@ -43,7 +43,13 @@ import {
|
|
|
43
43
|
setTheme,
|
|
44
44
|
theme,
|
|
45
45
|
} from "../../modes/theme/theme";
|
|
46
|
-
import
|
|
46
|
+
import {
|
|
47
|
+
clearInteractiveActivityLoaders,
|
|
48
|
+
type InteractiveModeContext,
|
|
49
|
+
type OAuthSelectorOptions,
|
|
50
|
+
stopInteractiveActivityIndicator,
|
|
51
|
+
suspendInteractiveActivityIndicator,
|
|
52
|
+
} from "../../modes/types";
|
|
47
53
|
import { getNotificationConfig, isTelegramConfigured, maskToken } from "../../sdk/bus/config";
|
|
48
54
|
import {
|
|
49
55
|
clearTelegramActivationMarker,
|
|
@@ -130,6 +136,7 @@ import { CustomProviderWizardComponent, type CustomProviderWizardSubmit } from "
|
|
|
130
136
|
import { ExtensionDashboard } from "../components/extensions";
|
|
131
137
|
import type { PetMode } from "../components/gajae-pet-widget";
|
|
132
138
|
import { HistorySearchComponent } from "../components/history-search";
|
|
139
|
+
import { HookSelectorComponent } from "../components/hook-selector";
|
|
133
140
|
import { JobsOverlayComponent } from "../components/jobs-overlay";
|
|
134
141
|
import { ModelSelectorComponent } from "../components/model-selector";
|
|
135
142
|
import type {
|
|
@@ -166,7 +173,7 @@ import type { JobsObserver } from "../jobs-observer";
|
|
|
166
173
|
import type { SessionObserverRegistry } from "../session-observer-registry";
|
|
167
174
|
import type { TasksAggregator } from "../tasks-aggregator";
|
|
168
175
|
import type { TranscriptItemRegistry } from "../transcript-item-registry";
|
|
169
|
-
import { acquireResumeProgressLease } from "../utils/ui-helpers";
|
|
176
|
+
import { acquireResumeProgressLease, type ResumeProgressLease } from "../utils/ui-helpers";
|
|
170
177
|
|
|
171
178
|
const CALLBACK_SERVER_PROVIDERS = new Set<string>([
|
|
172
179
|
"anthropic",
|
|
@@ -196,7 +203,6 @@ export function buildStatusLineSettings(settingsInstance: Settings): StatusLineS
|
|
|
196
203
|
rightSegments: settingsInstance.get("statusLine.rightSegments"),
|
|
197
204
|
separator: settingsInstance.get("statusLine.separator"),
|
|
198
205
|
showHookStatus: settingsInstance.get("statusLine.showHookStatus"),
|
|
199
|
-
showActionHints: settingsInstance.get("statusLine.showActionHints"),
|
|
200
206
|
sessionAccent: settingsInstance.get("statusLine.sessionAccent"),
|
|
201
207
|
maxRows: settingsInstance.get("statusLine.maxRows"),
|
|
202
208
|
segmentOptions: settingsInstance.get("statusLine.segmentOptions"),
|
|
@@ -351,6 +357,7 @@ export function createNotificationsEditorOperations(
|
|
|
351
357
|
richDraftEnabled: config.richDraft.enabled,
|
|
352
358
|
toolActivityEnabled: config.toolActivity.enabled,
|
|
353
359
|
streamingEnabled: config.streaming.enabled,
|
|
360
|
+
sound: config.sound,
|
|
354
361
|
},
|
|
355
362
|
};
|
|
356
363
|
},
|
|
@@ -724,6 +731,7 @@ export function createNotificationsEditorOperations(
|
|
|
724
731
|
{ path: "notifications.redact", op: "set", value: preferences.redact },
|
|
725
732
|
{ path: "notifications.verbosity", op: "set", value: preferences.verbosity },
|
|
726
733
|
{ path: "notifications.sessionScope", op: "set", value: preferences.sessionScope },
|
|
734
|
+
{ path: "notifications.telegram.sound", op: "set", value: preferences.sound },
|
|
727
735
|
{ path: "notifications.telegram.rich.enabled", op: "set", value: preferences.richEnabled },
|
|
728
736
|
{ path: "notifications.telegram.richDraft.enabled", op: "set", value: preferences.richDraftEnabled },
|
|
729
737
|
{ path: "notifications.telegram.streaming.enabled", op: "set", value: preferences.streamingEnabled },
|
|
@@ -818,7 +826,9 @@ export class SelectorController {
|
|
|
818
826
|
* @param create Factory that receives a `done` callback and returns the component and focus target
|
|
819
827
|
*/
|
|
820
828
|
showSelector(create: (done: () => void) => { component: Component; focus: Component }): void {
|
|
829
|
+
if (this.ctx.isStopped?.()) return;
|
|
821
830
|
const done = () => {
|
|
831
|
+
if (this.ctx.isStopped?.()) return;
|
|
822
832
|
// Prefer the pet-aware composer restore (InteractiveMode.restoreComposer); fall back
|
|
823
833
|
// to a plain editor swap for contexts that predate it (e.g. lightweight test doubles).
|
|
824
834
|
if (typeof this.ctx.restoreComposer === "function") {
|
|
@@ -1280,6 +1290,8 @@ export class SelectorController {
|
|
|
1280
1290
|
availableThemes,
|
|
1281
1291
|
availableModelProfiles: [...this.ctx.session.modelRegistry.getModelProfiles().keys()],
|
|
1282
1292
|
cwd: getProjectDir(),
|
|
1293
|
+
gjcRuntimeSnapshot: this.ctx.session.gjcRuntimeSnapshot,
|
|
1294
|
+
gjcActivationGeneration: this.ctx.session.gjcActivationGeneration,
|
|
1283
1295
|
},
|
|
1284
1296
|
{
|
|
1285
1297
|
onChange: (id, value) => this.handleSettingChange(id, value),
|
|
@@ -1561,7 +1573,8 @@ export class SelectorController {
|
|
|
1561
1573
|
this.ctx.rebuildChatFromMessages("reconcile-same-transcript");
|
|
1562
1574
|
break;
|
|
1563
1575
|
case "theme": {
|
|
1564
|
-
setTheme(value as string, true).then(result => {
|
|
1576
|
+
setTheme(value as string, true, { shouldApply: () => !this.ctx.isStopped?.() }).then(result => {
|
|
1577
|
+
if (this.ctx.isStopped?.()) return;
|
|
1565
1578
|
this.ctx.statusLine.invalidate();
|
|
1566
1579
|
this.ctx.updateEditorTopBorder();
|
|
1567
1580
|
this.ctx.ui.invalidate();
|
|
@@ -1621,13 +1634,16 @@ export class SelectorController {
|
|
|
1621
1634
|
this.ctx.session.agent.repetitionPenalty = repetitionPenalty >= 0 ? repetitionPenalty : undefined;
|
|
1622
1635
|
break;
|
|
1623
1636
|
}
|
|
1637
|
+
case "statusLine.showActionHints": {
|
|
1638
|
+
this.ctx.updateEditorChrome();
|
|
1639
|
+
break;
|
|
1640
|
+
}
|
|
1624
1641
|
case "statusLinePreset":
|
|
1625
1642
|
case "statusLine.preset":
|
|
1626
1643
|
case "statusLineSeparator":
|
|
1627
1644
|
case "statusLine.separator":
|
|
1628
1645
|
case "statusLineShowHooks":
|
|
1629
1646
|
case "statusLine.showHookStatus":
|
|
1630
|
-
case "statusLine.showActionHints":
|
|
1631
1647
|
case "statusLine.sessionAccent":
|
|
1632
1648
|
case "statusLine.maxRows":
|
|
1633
1649
|
case "statusLine.leftSegments":
|
|
@@ -2133,12 +2149,14 @@ export class SelectorController {
|
|
|
2133
2149
|
|
|
2134
2150
|
// Set up escape handler and loader if summarizing
|
|
2135
2151
|
let summaryLoader: Loader | undefined;
|
|
2152
|
+
let releaseActivityIndicator: (() => void) | undefined;
|
|
2136
2153
|
const originalOnEscape = this.ctx.editor.onEscape;
|
|
2137
2154
|
|
|
2138
2155
|
if (wantsSummary) {
|
|
2139
2156
|
this.ctx.editor.onEscape = () => {
|
|
2140
2157
|
this.ctx.session.abortBranchSummary();
|
|
2141
2158
|
};
|
|
2159
|
+
releaseActivityIndicator = suspendInteractiveActivityIndicator(this.ctx);
|
|
2142
2160
|
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
2143
2161
|
summaryLoader = new Loader(
|
|
2144
2162
|
this.ctx.ui,
|
|
@@ -2156,6 +2174,7 @@ export class SelectorController {
|
|
|
2156
2174
|
summarize: wantsSummary,
|
|
2157
2175
|
customInstructions,
|
|
2158
2176
|
});
|
|
2177
|
+
if (this.ctx.isStopped?.()) return;
|
|
2159
2178
|
|
|
2160
2179
|
if (result.aborted) {
|
|
2161
2180
|
// Summarization aborted - re-show tree selector
|
|
@@ -2171,18 +2190,21 @@ export class SelectorController {
|
|
|
2171
2190
|
// Update UI — pass the context built by navigateTree to skip a second O(N) walk.
|
|
2172
2191
|
this.ctx.rebuildInitialMessages("reconcile-same-transcript", result.sessionContext);
|
|
2173
2192
|
await this.ctx.reloadTodos();
|
|
2193
|
+
if (this.ctx.isStopped?.()) return;
|
|
2174
2194
|
if (result.editorText && !this.ctx.editor.getText().trim()) {
|
|
2175
2195
|
this.ctx.editor.setText(result.editorText);
|
|
2176
2196
|
}
|
|
2177
2197
|
this.ctx.showStatus("Navigated to selected point");
|
|
2178
2198
|
} catch (error) {
|
|
2199
|
+
if (this.ctx.isStopped?.()) return;
|
|
2179
2200
|
this.ctx.showError(error instanceof Error ? error.message : String(error));
|
|
2180
2201
|
} finally {
|
|
2181
2202
|
if (summaryLoader) {
|
|
2182
2203
|
summaryLoader.stop();
|
|
2183
|
-
this.ctx.statusContainer.clear();
|
|
2204
|
+
if (!this.ctx.isStopped?.()) this.ctx.statusContainer.clear();
|
|
2184
2205
|
}
|
|
2185
|
-
this.ctx.editor.onEscape = originalOnEscape;
|
|
2206
|
+
if (!this.ctx.isStopped?.()) this.ctx.editor.onEscape = originalOnEscape;
|
|
2207
|
+
releaseActivityIndicator?.();
|
|
2186
2208
|
}
|
|
2187
2209
|
},
|
|
2188
2210
|
() => {
|
|
@@ -2201,6 +2223,7 @@ export class SelectorController {
|
|
|
2201
2223
|
|
|
2202
2224
|
async showSessionSelector(): Promise<void> {
|
|
2203
2225
|
const sessions = await this.ctx.sessionManager.listForResumePickerReadOnly();
|
|
2226
|
+
if (this.ctx.isStopped?.()) return;
|
|
2204
2227
|
this.showSelector(done => {
|
|
2205
2228
|
const selector = new SessionSelectorComponent(
|
|
2206
2229
|
sessions,
|
|
@@ -2235,12 +2258,9 @@ export class SelectorController {
|
|
|
2235
2258
|
});
|
|
2236
2259
|
}
|
|
2237
2260
|
|
|
2238
|
-
#clearTransientSessionUi(): void {
|
|
2239
|
-
if (this.ctx
|
|
2240
|
-
|
|
2241
|
-
this.ctx.loadingAnimation = undefined;
|
|
2242
|
-
}
|
|
2243
|
-
this.ctx.statusContainer.clear();
|
|
2261
|
+
#clearTransientSessionUi(options?: { restoreBackground?: boolean; clearSpecializedLoaders?: boolean }): void {
|
|
2262
|
+
if (options?.clearSpecializedLoaders) clearInteractiveActivityLoaders(this.ctx);
|
|
2263
|
+
stopInteractiveActivityIndicator(this.ctx, { restoreBackground: options?.restoreBackground });
|
|
2244
2264
|
this.ctx.pendingMessagesContainer.clear();
|
|
2245
2265
|
this.ctx.compactionQueuedMessages = [];
|
|
2246
2266
|
this.ctx.streamingComponent = undefined;
|
|
@@ -2273,6 +2293,7 @@ export class SelectorController {
|
|
|
2273
2293
|
}
|
|
2274
2294
|
|
|
2275
2295
|
const detached = await this.ctx.session.newSession();
|
|
2296
|
+
if (this.ctx.isStopped?.()) return false;
|
|
2276
2297
|
if (!detached) {
|
|
2277
2298
|
return false;
|
|
2278
2299
|
}
|
|
@@ -2280,28 +2301,50 @@ export class SelectorController {
|
|
|
2280
2301
|
|
|
2281
2302
|
this.#refreshSessionTerminalTitle();
|
|
2282
2303
|
|
|
2283
|
-
this.#clearTransientSessionUi();
|
|
2304
|
+
this.#clearTransientSessionUi({ clearSpecializedLoaders: true });
|
|
2284
2305
|
this.ctx.statusLine.invalidate();
|
|
2285
2306
|
this.ctx.statusLine.setSessionStartTime(Date.now());
|
|
2286
2307
|
this.ctx.updateEditorTopBorder();
|
|
2287
2308
|
this.ctx.updateEditorBorderColor();
|
|
2288
2309
|
this.ctx.rebuildInitialMessages("replace-identity");
|
|
2289
2310
|
await this.ctx.reloadTodos();
|
|
2311
|
+
if (this.ctx.isStopped?.()) return false;
|
|
2290
2312
|
this.ctx.ui.requestRender();
|
|
2291
2313
|
return true;
|
|
2292
2314
|
}
|
|
2293
2315
|
|
|
2316
|
+
/**
|
|
2317
|
+
* Guards `handleResumeSession` against overlapping resumes.
|
|
2318
|
+
*
|
|
2319
|
+
* `acquireResumeProgressLease` only owns the spinner, so it cannot serialize
|
|
2320
|
+
* two resumes, and the session picker dispatches resume through a
|
|
2321
|
+
* void-returning `onSelect` callback — a second selection would otherwise
|
|
2322
|
+
* start a concurrent transition whose rejection nobody awaits.
|
|
2323
|
+
*/
|
|
2324
|
+
#resumeInFlight = false;
|
|
2325
|
+
|
|
2294
2326
|
async handleResumeSession(sessionPath: string): Promise<void> {
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2327
|
+
if (this.#resumeInFlight) {
|
|
2328
|
+
this.ctx.showStatus("Resume already in progress");
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
this.#resumeInFlight = true;
|
|
2332
|
+
const releaseActivityIndicator = suspendInteractiveActivityIndicator(this.ctx);
|
|
2333
|
+
// Everything below runs inside the try so a synchronous failure (a throwing
|
|
2334
|
+
// session-manager accessor, an unavailable status rail) can never strand the
|
|
2335
|
+
// guard and silently disable every later resume.
|
|
2336
|
+
let progressLease: ResumeProgressLease | undefined;
|
|
2298
2337
|
try {
|
|
2338
|
+
const previousSessionId = this.ctx.sessionManager.getSessionId();
|
|
2339
|
+
progressLease = acquireResumeProgressLease(this.ctx);
|
|
2299
2340
|
await progressLease.committed;
|
|
2341
|
+
if (this.ctx.isStopped?.()) return;
|
|
2300
2342
|
const migrationPolicy =
|
|
2301
2343
|
this.ctx.settings?.get("session.directoryMigration") === "disabled" ? "disabled" : "copy-retain";
|
|
2302
2344
|
let writableSessionPath = sessionPath;
|
|
2303
2345
|
if (this.ctx.sessionManager.isManagedDestination()) {
|
|
2304
2346
|
const inspection = await SessionManager.inspectSessionTailReadOnly(sessionPath);
|
|
2347
|
+
if (this.ctx.isStopped?.()) return;
|
|
2305
2348
|
if (inspection.kind === "error")
|
|
2306
2349
|
throw new Error(`Could not inspect selected session: ${inspection.reason}`);
|
|
2307
2350
|
writableSessionPath = await this.ctx.sessionManager.prepareManagedCandidateForStrictAdoption(
|
|
@@ -2309,14 +2352,66 @@ export class SelectorController {
|
|
|
2309
2352
|
migrationPolicy,
|
|
2310
2353
|
inspection.identity,
|
|
2311
2354
|
);
|
|
2355
|
+
if (this.ctx.isStopped?.()) return;
|
|
2312
2356
|
}
|
|
2313
2357
|
// Switch session via AgentSession (emits hook and tool session events)
|
|
2314
|
-
|
|
2315
|
-
|
|
2358
|
+
let switched: boolean;
|
|
2359
|
+
let transitionMutationStarted = false;
|
|
2360
|
+
try {
|
|
2361
|
+
switched = await this.ctx.session.switchSession(writableSessionPath, {
|
|
2316
2362
|
transition: { origin: INTERACTIVE_SELECTOR_RESUME_ORIGIN },
|
|
2317
|
-
|
|
2318
|
-
|
|
2363
|
+
onTransitionMutationStarted: () => {
|
|
2364
|
+
transitionMutationStarted = true;
|
|
2365
|
+
},
|
|
2366
|
+
});
|
|
2367
|
+
if (this.ctx.isStopped?.()) return;
|
|
2368
|
+
} catch (error) {
|
|
2369
|
+
if (this.ctx.isStopped?.()) return;
|
|
2370
|
+
// `switchSession` opens with `#beginSessionTransition`, which throws
|
|
2371
|
+
// `{ code: "busy" }` while another transition (compaction, handoff, fork,
|
|
2372
|
+
// another switch, …) owns the session. Resume is a UI action dispatched
|
|
2373
|
+
// through a void callback, so report that as status instead of rejecting a
|
|
2374
|
+
// promise nobody awaits. Admission-busy never acquired the lease, so the
|
|
2375
|
+
// owner's transient state — including the `compactionQueuedMessages` that
|
|
2376
|
+
// hold input typed during compaction — must survive untouched.
|
|
2377
|
+
const typed = error as { code?: unknown } | undefined;
|
|
2378
|
+
if (typed?.code === "busy") {
|
|
2379
|
+
this.ctx.showStatus("Another session operation is already in progress");
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
// A rejection can occur either before the transition mutates the live session
|
|
2383
|
+
// (for example, a rejecting `session_before_switch` hook) or after disconnect/
|
|
2384
|
+
// abort has begun. Preserve the current session's transient UI in the former
|
|
2385
|
+
// case; reconcile only state invalidated by a transition that actually mutated.
|
|
2386
|
+
// Reconciliation stays best-effort so cleanup failure never replaces the
|
|
2387
|
+
// original switch failure.
|
|
2388
|
+
if (transitionMutationStarted) {
|
|
2389
|
+
try {
|
|
2390
|
+
progressLease.clear();
|
|
2391
|
+
this.#clearTransientSessionUi({ restoreBackground: false, clearSpecializedLoaders: true });
|
|
2392
|
+
} catch {
|
|
2393
|
+
logger.warn("Resume transient-UI reconciliation failed after a session switch error", {
|
|
2394
|
+
classification: "resume-cleanup-failed",
|
|
2395
|
+
});
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
throw error;
|
|
2399
|
+
}
|
|
2400
|
+
// `#clearTransientSessionUi` drops `compactionQueuedMessages`, so it must stay
|
|
2401
|
+
// untouched on the paths that never acquired the session-transition lease:
|
|
2402
|
+
// admission-busy above and managed-candidate preparation before it.
|
|
2403
|
+
//
|
|
2404
|
+
// A pre-mutation hook cancellation preserves the current session and all of
|
|
2405
|
+
// its foreground/retry UI. A rollback after disconnect/abort must clear it.
|
|
2406
|
+
if (!switched) {
|
|
2407
|
+
if (transitionMutationStarted) {
|
|
2408
|
+
progressLease.clear();
|
|
2409
|
+
this.#clearTransientSessionUi({ restoreBackground: false, clearSpecializedLoaders: true });
|
|
2410
|
+
}
|
|
2319
2411
|
return;
|
|
2412
|
+
}
|
|
2413
|
+
progressLease.clear();
|
|
2414
|
+
this.#clearTransientSessionUi({ restoreBackground: false, clearSpecializedLoaders: true });
|
|
2320
2415
|
const switchingToDifferentSession = previousSessionId !== this.ctx.sessionManager.getSessionId();
|
|
2321
2416
|
if (switchingToDifferentSession) this.ctx.resetIrcSidebarSession();
|
|
2322
2417
|
this.#refreshSessionTerminalTitle();
|
|
@@ -2326,12 +2421,55 @@ export class SelectorController {
|
|
|
2326
2421
|
switchingToDifferentSession ? "replace-identity" : "reconcile-same-transcript",
|
|
2327
2422
|
);
|
|
2328
2423
|
await this.ctx.reloadTodos();
|
|
2424
|
+
if (this.ctx.isStopped?.()) return;
|
|
2329
2425
|
this.ctx.showStatus("Resumed session");
|
|
2426
|
+
this.#maybePromptResumeModelChoice();
|
|
2330
2427
|
} finally {
|
|
2331
|
-
progressLease
|
|
2428
|
+
progressLease?.clear();
|
|
2429
|
+
this.#resumeInFlight = false;
|
|
2430
|
+
releaseActivityIndicator();
|
|
2332
2431
|
}
|
|
2333
2432
|
}
|
|
2334
2433
|
|
|
2434
|
+
/**
|
|
2435
|
+
* When `session.resumeModelBehavior` is "ask", offer a one-shot choice after
|
|
2436
|
+
* resuming: keep the model the session was last using, or switch to whatever
|
|
2437
|
+
* `modelRoles.default` currently resolves to. No-op if the setting is unset,
|
|
2438
|
+
* the two models already match, or either model can't be resolved.
|
|
2439
|
+
*/
|
|
2440
|
+
#maybePromptResumeModelChoice(): void {
|
|
2441
|
+
if (this.ctx.settings?.get("session.resumeModelBehavior") !== "ask") return;
|
|
2442
|
+
const sessionModel = this.ctx.session.model;
|
|
2443
|
+
const currentDefault = this.ctx.session.resolveConfiguredDefaultModel?.();
|
|
2444
|
+
if (!sessionModel || !currentDefault) return;
|
|
2445
|
+
if (sessionModel.provider === currentDefault.provider && sessionModel.id === currentDefault.id) return;
|
|
2446
|
+
|
|
2447
|
+
this.showSelector(done => {
|
|
2448
|
+
const selector = new HookSelectorComponent(
|
|
2449
|
+
`This session last used ${sessionModel.provider}/${sessionModel.id}.\n` +
|
|
2450
|
+
`Current default model is ${currentDefault.provider}/${currentDefault.id}.`,
|
|
2451
|
+
[`Keep ${sessionModel.id}`, `Use ${currentDefault.id}`],
|
|
2452
|
+
async (option: string) => {
|
|
2453
|
+
done();
|
|
2454
|
+
if (option === `Use ${currentDefault.id}`) {
|
|
2455
|
+
try {
|
|
2456
|
+
await this.ctx.session.setModel(currentDefault);
|
|
2457
|
+
this.ctx.showStatus(`Switched to ${currentDefault.provider}/${currentDefault.id}`);
|
|
2458
|
+
} catch (err) {
|
|
2459
|
+
this.ctx.showError(err instanceof Error ? err.message : String(err));
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
this.ctx.ui.requestRender();
|
|
2463
|
+
},
|
|
2464
|
+
() => {
|
|
2465
|
+
done();
|
|
2466
|
+
this.ctx.ui.requestRender();
|
|
2467
|
+
},
|
|
2468
|
+
);
|
|
2469
|
+
return { component: selector, focus: selector };
|
|
2470
|
+
});
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2335
2473
|
async handleSessionDeleteCommand(): Promise<void> {
|
|
2336
2474
|
const sessionFile = this.ctx.sessionManager.getSessionFile();
|
|
2337
2475
|
if (!sessionFile) {
|