@gajae-code/coding-agent 0.15.5 → 0.16.0
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 +85 -1
- package/dist/types/async/job-manager.d.ts +83 -0
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/auth-gateway-cli.d.ts +17 -0
- package/dist/types/cli/model-presets-cli.d.ts +9 -0
- package/dist/types/cli/root-flags.d.ts +14 -0
- package/dist/types/cli/update-cli.d.ts +15 -0
- package/dist/types/cli.d.ts +7 -0
- package/dist/types/commands/auth-gateway.d.ts +3 -0
- package/dist/types/commands/gc.d.ts +13 -0
- package/dist/types/commands/launch.d.ts +10 -0
- package/dist/types/commands/model-presets.d.ts +21 -0
- package/dist/types/config/autorouting-tier-map.d.ts +120 -0
- package/dist/types/config/file-lock.d.ts +54 -2
- package/dist/types/config/internal/model-preset-registry-test-state.d.ts +4 -0
- package/dist/types/config/internal/model-preset-registry-test-support.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +3 -2
- package/dist/types/config/model-preset-registry.d.ts +368 -0
- package/dist/types/config/model-profile-activation.d.ts +39 -0
- package/dist/types/config/model-profiles.d.ts +5 -3
- package/dist/types/config/model-registry.d.ts +17 -5
- package/dist/types/config/settings-schema.d.ts +19 -1
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -5
- package/dist/types/coordinator-mcp/projection-scan.d.ts +54 -0
- package/dist/types/crash/record-loader.d.ts +15 -0
- package/dist/types/extensibility/extensions/types.d.ts +2 -5
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +58 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +35 -6
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +72 -1
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/main.d.ts +3 -1
- package/dist/types/master-mode/context.d.ts +21 -0
- package/dist/types/master-mode/first-request.d.ts +34 -0
- package/dist/types/master-mode/snapshot.d.ts +32 -0
- package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
- package/dist/types/modes/components/jobs-overlay-model.d.ts +4 -2
- package/dist/types/modes/components/oauth-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
- package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
- package/dist/types/modes/components/status-line/segments.d.ts +10 -0
- package/dist/types/modes/jobs-observer.d.ts +36 -0
- package/dist/types/modes/print-mode.d.ts +17 -0
- package/dist/types/modes/ui-language.d.ts +7 -1
- package/dist/types/sdk/broker/broker.d.ts +80 -1
- package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
- package/dist/types/sdk/broker/index.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +26 -0
- package/dist/types/sdk/broker/master-capability.d.ts +10 -0
- package/dist/types/sdk/broker/session-index.d.ts +40 -11
- package/dist/types/sdk/broker/session-scope.d.ts +72 -0
- package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
- package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
- package/dist/types/sdk/broker/state-version.d.ts +4 -1
- package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
- package/dist/types/sdk/bus/index.d.ts +6 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +3 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +4 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/master-cli.d.ts +63 -0
- package/dist/types/sdk/cli/rows.d.ts +4 -3
- package/dist/types/sdk/cli/session-cli.d.ts +18 -2
- package/dist/types/sdk/client/client.d.ts +3 -2
- package/dist/types/sdk/client/discovery.d.ts +1 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +2 -0
- package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
- package/dist/types/sdk/host/host.d.ts +10 -0
- package/dist/types/sdk/host/model-pin.d.ts +7 -2
- package/dist/types/sdk/host/sdk-run-capability.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +67 -1
- package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
- package/dist/types/sdk/lifecycle/service.d.ts +15 -3
- package/dist/types/sdk/lifecycle-session.d.ts +2 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +11 -1
- package/dist/types/sdk/protocol/adapter-validation.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +16 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -1
- package/dist/types/session/account-inventory.d.ts +1 -0
- package/dist/types/session/agent-session.d.ts +24 -7
- package/dist/types/session/credential-store-errors.d.ts +1 -0
- package/dist/types/session/fold-coordinator.d.ts +201 -0
- package/dist/types/session/sdk-run-capability-internal.d.ts +7 -0
- package/dist/types/session/sdk-run-capability.d.ts +2 -0
- package/dist/types/session/session-manager.d.ts +65 -0
- package/dist/types/session/yield-queue.d.ts +5 -4
- package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
- package/dist/types/telemetry/control.d.ts +11 -0
- package/dist/types/telemetry/events.d.ts +20 -0
- package/dist/types/telemetry/index.d.ts +3 -0
- package/dist/types/telemetry/transport.d.ts +22 -0
- package/dist/types/tools/bash-interactive.d.ts +23 -0
- package/dist/types/tools/bash.d.ts +4 -0
- package/dist/types/tools/gh.d.ts +8 -0
- package/dist/types/tools/index.d.ts +10 -5
- package/dist/types/web/search/providers/base.d.ts +9 -4
- package/dist/types/web/search/providers/xai.d.ts +2 -1
- package/dist/types/web/search/types.d.ts +14 -0
- package/package.json +10 -7
- package/scripts/generate-hotkeys-docs.ts +4 -0
- package/scripts/generate-sdk-operation-inventory.ts +9 -3
- package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
- package/src/async/job-manager.ts +671 -39
- package/src/cli/accounts-cli.ts +9 -5
- package/src/cli/args.ts +17 -0
- package/src/cli/auth-broker-cli.ts +40 -12
- package/src/cli/auth-gateway-cli.ts +273 -97
- package/src/cli/config-cli.ts +2 -2
- package/src/cli/fast-help.ts +4 -0
- package/src/cli/model-presets-cli.ts +100 -0
- package/src/cli/root-flags.ts +5 -0
- package/src/cli/update-cli.ts +117 -16
- package/src/cli.ts +6 -2
- package/src/commands/auth-gateway.ts +12 -8
- package/src/commands/gc.ts +19 -0
- package/src/commands/launch.ts +86 -18
- package/src/commands/model-presets.ts +24 -0
- package/src/commands/sdk.ts +113 -21
- package/src/config/autorouting-tier-map.ts +44 -0
- package/src/config/file-lock-gc.ts +9 -4
- package/src/config/file-lock.ts +890 -99
- package/src/config/internal/model-preset-registry-test-state.ts +28 -0
- package/src/config/internal/model-preset-registry-test-support.ts +11 -0
- package/src/config/keybindings.ts +6 -2
- package/src/config/model-preset-registry.ts +2795 -0
- package/src/config/model-profile-activation.ts +115 -28
- package/src/config/model-profile-contract.ts +4 -1
- package/src/config/model-profiles.ts +56 -14
- package/src/config/model-registry.ts +1044 -122
- package/src/config/model-resolver.ts +5 -0
- package/src/config/settings-schema.ts +19 -1
- package/src/config/settings.ts +11 -1
- package/src/coordinator-mcp/model-preset.ts +32 -17
- package/src/coordinator-mcp/projection-scan.ts +592 -0
- package/src/coordinator-mcp/question-state.ts +9 -11
- package/src/coordinator-mcp/server.ts +111 -42
- package/src/coordinator-mcp/session-reaper.ts +9 -3
- package/src/crash/index-store.ts +2 -0
- package/src/crash/record-loader.ts +10 -0
- package/src/extensibility/extensions/loader.ts +0 -1
- package/src/extensibility/extensions/types.ts +2 -3
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +1 -0
- package/src/gjc-runtime/empty-delete-gc.ts +301 -0
- package/src/gjc-runtime/gc-render.ts +29 -0
- package/src/gjc-runtime/gc-runtime.ts +174 -11
- package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
- package/src/gjc-runtime/session-state-lock.ts +1321 -147
- package/src/gjc-runtime/session-state-sidecar.ts +1004 -25
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -2
- package/src/gjc-runtime/tmux-sessions.ts +252 -13
- package/src/hooks/skill-state.ts +6 -0
- package/src/index.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/internal-urls/issue-pr-protocol.ts +4 -2
- package/src/lsp/config.ts +4 -3
- package/src/lsp/path-trust.ts +12 -6
- package/src/main.ts +66 -4
- package/src/master-mode/context.ts +50 -0
- package/src/master-mode/first-request.ts +65 -0
- package/src/master-mode/snapshot.ts +205 -0
- package/src/modes/acp/acp-agent.ts +616 -161
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
- package/src/modes/components/jobs-overlay-model.ts +99 -16
- package/src/modes/components/model-selector.ts +158 -18
- package/src/modes/components/oauth-selector.ts +6 -1
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/model-name.ts +53 -0
- package/src/modes/components/status-line/priority-row.ts +107 -0
- package/src/modes/components/status-line/segments.ts +29 -10
- package/src/modes/components/tool-status-header.ts +151 -15
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +51 -6
- package/src/modes/controllers/selector-controller.ts +191 -96
- package/src/modes/interactive-mode.ts +14 -1
- package/src/modes/jobs-observer.ts +149 -16
- package/src/modes/print-mode.ts +64 -0
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/ui-language.ts +49 -2
- package/src/modes/utils/injected-user-submission.ts +15 -1
- package/src/prompts/system/master-mode.md +28 -0
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/fold-receipt.md +3 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +17 -2
- package/src/sdk/broker/broker.ts +1671 -34
- package/src/sdk/broker/endpoint-authority.ts +86 -0
- package/src/sdk/broker/ensure.ts +5 -0
- package/src/sdk/broker/index.ts +3 -0
- package/src/sdk/broker/lifecycle.ts +217 -46
- package/src/sdk/broker/master-capability.ts +232 -0
- package/src/sdk/broker/session-index.ts +121 -17
- package/src/sdk/broker/session-scope.ts +272 -0
- package/src/sdk/broker/spawn-authority.ts +1031 -0
- package/src/sdk/broker/spawn-substrate.ts +548 -0
- package/src/sdk/broker/state-version.ts +26 -14
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/broker/worktree-occupancy.ts +90 -0
- package/src/sdk/bus/chat-daemon-control.ts +5 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +46 -5
- package/src/sdk/bus/index.ts +51 -4
- package/src/sdk/bus/kind-aware-reconciliation.ts +36 -6
- package/src/sdk/bus/prompt-reconciliation.ts +28 -4
- package/src/sdk/bus/slack-conversation.ts +5 -1
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/master-cli.ts +259 -0
- package/src/sdk/cli/rows.ts +11 -8
- package/src/sdk/cli/session-cli.ts +276 -6
- package/src/sdk/client/client.ts +14 -8
- package/src/sdk/client/discovery.ts +3 -1
- package/src/sdk/host/control/dispatch.ts +101 -37
- package/src/sdk/host/control/index.ts +2 -0
- package/src/sdk/host/control/operations.ts +2 -0
- package/src/sdk/host/control/runtime-gate.ts +203 -11
- package/src/sdk/host/host.ts +27 -2
- package/src/sdk/host/model-pin.ts +185 -25
- package/src/sdk/host/sdk-run-capability.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +581 -43
- package/src/sdk/host/session-runtime.ts +655 -101
- package/src/sdk/lifecycle/broker-client.ts +28 -0
- package/src/sdk/lifecycle/service.ts +279 -20
- package/src/sdk/lifecycle-session.ts +10 -2
- package/src/sdk/prompt-deadline-manager.ts +16 -1
- package/src/sdk/protocol/adapter-validation.ts +27 -0
- package/src/sdk/protocol/operation-inventory.generated.json +53 -2
- package/src/sdk/protocol/operation-registry.ts +17 -4
- package/src/sdk/router/session-router.ts +294 -154
- package/src/sdk/session.ts +440 -167
- package/src/sdk/startup-capability.ts +1 -0
- package/src/session/account-inventory.ts +27 -11
- package/src/session/agent-session.ts +1292 -180
- package/src/session/blob-store.ts +15 -5
- package/src/session/credential-store-errors.ts +2 -0
- package/src/session/fold-coordinator.ts +433 -0
- package/src/session/internal/managed-session-storage.ts +153 -78
- package/src/session/sdk-run-capability-internal.ts +15 -0
- package/src/session/sdk-run-capability.ts +2 -0
- package/src/session/session-manager.ts +342 -28
- package/src/session/yield-queue.ts +24 -10
- package/src/setup/frictionless-onboarding.ts +2 -0
- package/src/setup/provider-presets.json +1 -1
- package/src/slash-commands/helpers/credential-switch.ts +6 -1
- package/src/task/executor.ts +19 -3
- package/src/telemetry/control.ts +26 -0
- package/src/telemetry/events.ts +1268 -0
- package/src/telemetry/index.ts +3 -0
- package/src/telemetry/transport.ts +87 -0
- package/src/tools/bash-interactive.ts +182 -85
- package/src/tools/bash.ts +1028 -109
- package/src/tools/gh.ts +104 -48
- package/src/tools/github-cache.ts +2 -2
- package/src/tools/index.ts +11 -5
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/web/search/provider.ts +120 -11
- package/src/web/search/providers/anthropic.ts +89 -17
- package/src/web/search/providers/base.ts +9 -4
- package/src/web/search/providers/gemini.ts +87 -15
- package/src/web/search/providers/openai-compatible.ts +23 -8
- package/src/web/search/providers/xai.ts +98 -12
- package/src/web/search/types.ts +16 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrow-width survival row for the status rail.
|
|
3
|
+
*
|
|
4
|
+
* The rail evicts segments continuously as the terminal narrows. Without this
|
|
5
|
+
* row the last thing left at very small widths is the `…+N` overflow marker —
|
|
6
|
+
* the three things a user actually needs (how much context is left, whether a
|
|
7
|
+
* goal is running, which model is answering) are exactly what the generic
|
|
8
|
+
* eviction throws away first or last by accident.
|
|
9
|
+
*
|
|
10
|
+
* This row keeps them, in priority order, by stepping each item down its own
|
|
11
|
+
* ladder instead of dropping it:
|
|
12
|
+
*
|
|
13
|
+
* full form -> shortened form -> single glyph -> dropped
|
|
14
|
+
*
|
|
15
|
+
* Context % is never dropped and is the last survivor; the model name is
|
|
16
|
+
* dropped first of the three. The row is rendered as bare colored text with no
|
|
17
|
+
* group padding or end caps, so — like the overflow marker — it can survive at
|
|
18
|
+
* widths where a normal segment group cannot.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { visibleWidth } from "@gajae-code/tui";
|
|
22
|
+
import { formatNumber } from "@gajae-code/utils";
|
|
23
|
+
import { theme } from "../../theme/theme";
|
|
24
|
+
import { getContextUsageLevel, getContextUsageThemeColor } from "./context-thresholds";
|
|
25
|
+
import { shortenModelId } from "./model-name";
|
|
26
|
+
import { goalStatusDisplay } from "./segments";
|
|
27
|
+
import type { SegmentContext } from "./types";
|
|
28
|
+
|
|
29
|
+
const SEPARATOR = "·";
|
|
30
|
+
|
|
31
|
+
/** Which priority items the active layout is supposed to be carrying. */
|
|
32
|
+
export interface PriorityItemSet {
|
|
33
|
+
context: boolean;
|
|
34
|
+
goal: boolean;
|
|
35
|
+
model: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function contextForms(ctx: SegmentContext): { full: string; short: string } | null {
|
|
39
|
+
const window = ctx.contextWindow;
|
|
40
|
+
if (!(window > 0)) return null;
|
|
41
|
+
|
|
42
|
+
const raw = ctx.contextPercent;
|
|
43
|
+
const pct = typeof raw === "number" && Number.isFinite(raw) ? raw : null;
|
|
44
|
+
const color = pct === null ? "statusLineContext" : getContextUsageThemeColor(getContextUsageLevel(pct, window));
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
full: theme.fg(color, `${pct === null ? "?" : `${pct.toFixed(1)}%`}/${formatNumber(window)}`),
|
|
48
|
+
short: theme.fg(color, pct === null ? "?" : `${Math.round(pct)}%`),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function goalForms(ctx: SegmentContext): { label: string; glyph: string } | null {
|
|
53
|
+
const display = goalStatusDisplay(ctx);
|
|
54
|
+
if (!display) return null;
|
|
55
|
+
|
|
56
|
+
const glyph = display.icon || "G";
|
|
57
|
+
return {
|
|
58
|
+
label: theme.fg(display.color, display.icon ? `${display.icon} Goal` : "Goal"),
|
|
59
|
+
glyph: theme.fg(display.color, glyph),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function modelForms(ctx: SegmentContext): { short: string; glyph: string } {
|
|
64
|
+
const model = ctx.session.state.model;
|
|
65
|
+
const name = shortenModelId(model?.id ?? model?.name);
|
|
66
|
+
return {
|
|
67
|
+
short: theme.fg("statusLineModel", name),
|
|
68
|
+
glyph: theme.fg("statusLineModel", theme.icon.model || name.slice(0, 1)),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Widest priority row that fits `width`, or null when even the minimal form
|
|
74
|
+
* cannot fit (callers then fall back to the overflow marker).
|
|
75
|
+
*
|
|
76
|
+
* Returns null when neither context nor goal is available: the model name alone
|
|
77
|
+
* is the lowest-priority item and never justifies replacing the normal rail.
|
|
78
|
+
*/
|
|
79
|
+
export function buildPriorityRow(ctx: SegmentContext, width: number, include: PriorityItemSet): string | null {
|
|
80
|
+
if (width <= 0) return null;
|
|
81
|
+
|
|
82
|
+
const context = include.context ? contextForms(ctx) : null;
|
|
83
|
+
const goal = include.goal ? goalForms(ctx) : null;
|
|
84
|
+
const model = include.model ? modelForms(ctx) : null;
|
|
85
|
+
if (!context && !goal) return null;
|
|
86
|
+
|
|
87
|
+
// Ordered widest-first. Every item degrades through its own forms before any
|
|
88
|
+
// item is dropped, and drops run lowest-priority first (model, then goal).
|
|
89
|
+
const ladder: readonly (readonly (string | null)[])[] = [
|
|
90
|
+
[context?.full ?? null, goal?.label ?? null, model?.short ?? null],
|
|
91
|
+
[context?.full ?? null, goal?.glyph ?? null, model?.short ?? null],
|
|
92
|
+
[context?.full ?? null, goal?.glyph ?? null, model?.glyph ?? null],
|
|
93
|
+
[context?.short ?? null, goal?.glyph ?? null, model?.glyph ?? null],
|
|
94
|
+
[context?.short ?? null, goal?.glyph ?? null, null],
|
|
95
|
+
[context?.short ?? null, null, null],
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const separator = theme.fg("dim", SEPARATOR);
|
|
99
|
+
for (const candidate of ladder) {
|
|
100
|
+
const parts = candidate.filter((part): part is string => part !== null);
|
|
101
|
+
if (parts.length === 0) continue;
|
|
102
|
+
const row = parts.join(separator);
|
|
103
|
+
if (visibleWidth(row) <= width) return row;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
@@ -8,6 +8,7 @@ import { shortenPath } from "../../../tools/render-utils";
|
|
|
8
8
|
import { getSessionAccentAnsi, getSessionAccentHex } from "../../../utils/session-color";
|
|
9
9
|
import { sanitizeStatusText } from "../../shared";
|
|
10
10
|
import { getContextUsageLevel, getContextUsageThemeColor } from "./context-thresholds";
|
|
11
|
+
import { shortenModelId } from "./model-name";
|
|
11
12
|
import type { RenderedSegment, SegmentContext, StatusLineSegment, StatusLineSegmentId } from "./types";
|
|
12
13
|
|
|
13
14
|
export type { SegmentContext } from "./types";
|
|
@@ -99,10 +100,10 @@ const modelSegment: StatusLineSegment = {
|
|
|
99
100
|
const state = ctx.session.state;
|
|
100
101
|
const opts = ctx.options.model ?? {};
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
// Shortest recognizable id-derived form (`sonnet-4.5`), the same label the
|
|
104
|
+
// footer renders, so a narrow rail never spends cells on a provider prefix
|
|
105
|
+
// or a snapshot date.
|
|
106
|
+
const modelName = shortenModelId(state.model?.id ?? state.model?.name);
|
|
106
107
|
|
|
107
108
|
let content = withIcon(theme.icon.model, modelName);
|
|
108
109
|
|
|
@@ -148,9 +149,16 @@ function formatGoalUsage(current: number): string {
|
|
|
148
149
|
return used;
|
|
149
150
|
}
|
|
150
151
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Icon and color for the active goal indicator, or null when goal mode is not
|
|
154
|
+
* showing. Shared with the narrow-width priority row so the compact glyph keeps
|
|
155
|
+
* the same status color as the full label.
|
|
156
|
+
*/
|
|
157
|
+
export function goalStatusDisplay(ctx: SegmentContext): { icon: string; color: ThemeColor } | null {
|
|
158
|
+
const mode = ctx.goalMode;
|
|
159
|
+
if (!mode || (!mode.enabled && !mode.paused)) return null;
|
|
160
|
+
|
|
161
|
+
const status = ctx.session.getGoalModeState?.()?.goal?.status ?? (mode.paused ? "paused" : "active");
|
|
154
162
|
|
|
155
163
|
let icon: string = theme.icon.goal;
|
|
156
164
|
let color: ThemeColor = "accent";
|
|
@@ -170,13 +178,19 @@ function renderGoalMode(ctx: SegmentContext, mode: { enabled: boolean; paused: b
|
|
|
170
178
|
default:
|
|
171
179
|
break;
|
|
172
180
|
}
|
|
181
|
+
return { icon, color };
|
|
182
|
+
}
|
|
173
183
|
|
|
174
|
-
|
|
184
|
+
function renderGoalMode(ctx: SegmentContext, mode: { enabled: boolean; paused: boolean }): RenderedSegment {
|
|
185
|
+
const display = goalStatusDisplay(ctx) ?? { icon: theme.icon.goal, color: "accent" as ThemeColor };
|
|
186
|
+
const goal = ctx.session.getGoalModeState?.()?.goal;
|
|
187
|
+
|
|
188
|
+
const parts: string[] = [withIcon(display.icon, "Goal")];
|
|
175
189
|
const showUsage = ctx.session.settings.get("goal.statusInFooter") === true;
|
|
176
190
|
if (showUsage && goal) {
|
|
177
191
|
parts.push(formatGoalUsage(goal.tokensUsed));
|
|
178
192
|
}
|
|
179
|
-
return { content: theme.fg(color, parts.join(" ")), visible:
|
|
193
|
+
return { content: theme.fg(display.color, parts.join(" ")), visible: mode.enabled || mode.paused };
|
|
180
194
|
}
|
|
181
195
|
|
|
182
196
|
const modeSegment: StatusLineSegment = {
|
|
@@ -307,7 +321,9 @@ const jobsSegment: StatusLineSegment = {
|
|
|
307
321
|
id: "jobs",
|
|
308
322
|
render(ctx) {
|
|
309
323
|
const { jobs } = ctx;
|
|
310
|
-
const
|
|
324
|
+
const foldedJobCount = jobs.foldedJobs?.filter(job => job.backgrounded).length ?? 0;
|
|
325
|
+
const visible =
|
|
326
|
+
jobs.activeMonitorCount > 0 || jobs.activeCronCount > 0 || foldedJobCount > 0 || jobs.worstState === "failed";
|
|
311
327
|
if (!visible) {
|
|
312
328
|
return { content: "", visible: false };
|
|
313
329
|
}
|
|
@@ -318,6 +334,9 @@ const jobsSegment: StatusLineSegment = {
|
|
|
318
334
|
if (jobs.activeCronCount > 0) {
|
|
319
335
|
parts.push(withIcon(theme.icon.time, `${jobs.activeCronCount}`));
|
|
320
336
|
}
|
|
337
|
+
if (foldedJobCount > 0) {
|
|
338
|
+
parts.push(withIcon(theme.icon.pause || theme.icon.agents, `${foldedJobCount} folded`));
|
|
339
|
+
}
|
|
321
340
|
if (parts.length === 0) {
|
|
322
341
|
// Nothing active but a failure is unacknowledged — keep a drill-in marker.
|
|
323
342
|
parts.push(withIcon(theme.icon.warning, "jobs"));
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
resolveCurrentBranch,
|
|
29
29
|
} from "./status-line/git-utils";
|
|
30
30
|
import { getPreset } from "./status-line/presets";
|
|
31
|
+
import { buildPriorityRow, type PriorityItemSet } from "./status-line/priority-row";
|
|
31
32
|
import { renderSegment, type SegmentContext } from "./status-line/segments";
|
|
32
33
|
import { getSeparator } from "./status-line/separators";
|
|
33
34
|
import { calculateTokensPerSecond } from "./status-line/token-rate";
|
|
@@ -132,6 +133,8 @@ interface CollectedStatusSegments {
|
|
|
132
133
|
left: string[];
|
|
133
134
|
leftSegIds: StatusLineSegmentId[];
|
|
134
135
|
right: string[];
|
|
136
|
+
/** Parallel to `right`; null for action hints, job counts and the version tag. */
|
|
137
|
+
rightSegIds: (StatusLineSegmentId | null)[];
|
|
135
138
|
previewHighlightSegment: StatusLineSegmentId | undefined;
|
|
136
139
|
sessionAccent: boolean | undefined;
|
|
137
140
|
leftSepWidth: number;
|
|
@@ -819,6 +822,7 @@ export class StatusLineComponent implements Component {
|
|
|
819
822
|
}
|
|
820
823
|
|
|
821
824
|
const right: string[] = [];
|
|
825
|
+
const rightSegIds: (StatusLineSegmentId | null)[] = [];
|
|
822
826
|
const actionHints =
|
|
823
827
|
effectiveSettings.showActionHints === false
|
|
824
828
|
? []
|
|
@@ -833,9 +837,13 @@ export class StatusLineComponent implements Component {
|
|
|
833
837
|
const rendered = renderSegment(segId, ctx);
|
|
834
838
|
if (rendered.visible && rendered.content) {
|
|
835
839
|
right.push(highlightSegment(segId, rendered.content));
|
|
840
|
+
rightSegIds.push(segId);
|
|
836
841
|
}
|
|
837
842
|
}
|
|
838
|
-
|
|
843
|
+
for (const hint of actionHints) {
|
|
844
|
+
right.push(hint.content);
|
|
845
|
+
rightSegIds.push(null);
|
|
846
|
+
}
|
|
839
847
|
|
|
840
848
|
const runningBackgroundJobs =
|
|
841
849
|
this.session.getAsyncJobSnapshot()?.running.filter(job => job.metadata?.monitor !== true).length ?? 0;
|
|
@@ -843,9 +851,11 @@ export class StatusLineComponent implements Component {
|
|
|
843
851
|
const icon = theme.icon.agents ? `${theme.icon.agents} ` : "";
|
|
844
852
|
const label = `${formatCount("job", runningBackgroundJobs)} running`;
|
|
845
853
|
right.push(theme.fg("statusLineSubagents", `${icon}${label}`));
|
|
854
|
+
rightSegIds.push(null);
|
|
846
855
|
}
|
|
847
856
|
if (this.#version) {
|
|
848
857
|
right.push(theme.fg("dim", `v${this.#version}`));
|
|
858
|
+
rightSegIds.push(null);
|
|
849
859
|
}
|
|
850
860
|
|
|
851
861
|
return {
|
|
@@ -857,6 +867,7 @@ export class StatusLineComponent implements Component {
|
|
|
857
867
|
left,
|
|
858
868
|
leftSegIds,
|
|
859
869
|
right,
|
|
870
|
+
rightSegIds,
|
|
860
871
|
previewHighlightSegment,
|
|
861
872
|
sessionAccent: effectiveSettings.sessionAccent,
|
|
862
873
|
leftSepWidth: visibleWidth(separatorDef.left),
|
|
@@ -872,22 +883,108 @@ export class StatusLineComponent implements Component {
|
|
|
872
883
|
return Math.max(1, Math.min(3, Math.trunc(raw)));
|
|
873
884
|
}
|
|
874
885
|
|
|
886
|
+
/**
|
|
887
|
+
* Which priority items this layout is carrying, and where.
|
|
888
|
+
*
|
|
889
|
+
* Context % has no segment of its own in most presets — it rides inside the
|
|
890
|
+
* `model` segment — so `model` inherits the context rank whenever it does.
|
|
891
|
+
* Items whose data is absent (no context window, no active goal) carry no
|
|
892
|
+
* priority at all, which keeps a rail with nothing to protect evicting
|
|
893
|
+
* exactly as it always did.
|
|
894
|
+
*/
|
|
895
|
+
#priorityItems(seg: CollectedStatusSegments): {
|
|
896
|
+
include: PriorityItemSet;
|
|
897
|
+
inlineContextPct: boolean;
|
|
898
|
+
} {
|
|
899
|
+
const collected = (id: StatusLineSegmentId): boolean =>
|
|
900
|
+
seg.leftSegIds.includes(id) || seg.rightSegIds.includes(id);
|
|
901
|
+
const inlineContextPct =
|
|
902
|
+
seg.ctx.options.model?.showContextPercent !== false && seg.ctx.contextPctSegmentActive !== true;
|
|
903
|
+
const goalMode = seg.ctx.goalMode;
|
|
904
|
+
|
|
905
|
+
return {
|
|
906
|
+
inlineContextPct,
|
|
907
|
+
include: {
|
|
908
|
+
context:
|
|
909
|
+
seg.ctx.contextWindow > 0 && (collected("context_pct") || (collected("model") && inlineContextPct)),
|
|
910
|
+
goal: collected("mode") && goalMode !== null && (goalMode.enabled || goalMode.paused),
|
|
911
|
+
model: collected("model"),
|
|
912
|
+
},
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Eviction rank: 0 is ordinary telemetry and goes first, 3 is context % and
|
|
918
|
+
* goes last. The model name outranks telemetry but loses to the goal
|
|
919
|
+
* indicator, matching context % > goal > model.
|
|
920
|
+
*/
|
|
921
|
+
#priorityRanker(seg: CollectedStatusSegments): (id: StatusLineSegmentId | null) => number {
|
|
922
|
+
const { include, inlineContextPct } = this.#priorityItems(seg);
|
|
923
|
+
const anyPriority = include.context || include.goal;
|
|
924
|
+
|
|
925
|
+
return (id: StatusLineSegmentId | null): number => {
|
|
926
|
+
if (id === null || !anyPriority) return 0;
|
|
927
|
+
if (id === "context_pct") return include.context ? 3 : 0;
|
|
928
|
+
if (id === "model") {
|
|
929
|
+
if (include.context && inlineContextPct) return 3;
|
|
930
|
+
return include.model ? 1 : 0;
|
|
931
|
+
}
|
|
932
|
+
if (id === "mode") return include.goal ? 2 : 0;
|
|
933
|
+
return 0;
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Priority row for a layout that could not keep everything the user needs on
|
|
939
|
+
* screen, or null when the normal rail already carries context %, the goal
|
|
940
|
+
* indicator and the model name.
|
|
941
|
+
*
|
|
942
|
+
* Context % has no segment of its own in most presets — it rides inside the
|
|
943
|
+
* `model` segment — so losing `model` silently loses the highest-priority
|
|
944
|
+
* item too. Survival is therefore resolved per item, not per segment.
|
|
945
|
+
*/
|
|
946
|
+
#priorityRowFor(
|
|
947
|
+
seg: CollectedStatusSegments,
|
|
948
|
+
survivingLeftIds: readonly (StatusLineSegmentId | null)[],
|
|
949
|
+
survivingRightIds: readonly (StatusLineSegmentId | null)[],
|
|
950
|
+
width: number,
|
|
951
|
+
): string | null {
|
|
952
|
+
const survived = (id: StatusLineSegmentId): boolean =>
|
|
953
|
+
survivingLeftIds.includes(id) || survivingRightIds.includes(id);
|
|
954
|
+
|
|
955
|
+
const { include, inlineContextPct } = this.#priorityItems(seg);
|
|
956
|
+
if (!include.context && !include.goal) return null;
|
|
957
|
+
|
|
958
|
+
const contextLost = include.context && !survived("context_pct") && !(inlineContextPct && survived("model"));
|
|
959
|
+
const goalLost = include.goal && !survived("mode");
|
|
960
|
+
const modelLost = include.model && !survived("model");
|
|
961
|
+
if (!contextLost && !goalLost && !modelLost) return null;
|
|
962
|
+
|
|
963
|
+
return buildPriorityRow(seg.ctx, width, include);
|
|
964
|
+
}
|
|
965
|
+
|
|
875
966
|
#buildStatusLine(width: number, precollected?: CollectedStatusSegments): string {
|
|
876
967
|
const seg = precollected ?? this.#collectStatusSegments(width, this.#resolveSettings());
|
|
877
968
|
const { ctx, separatorDef, bgAnsi, fgAnsi, sepAnsi, previewHighlightSegment } = seg;
|
|
878
969
|
const { leftSepWidth, rightSepWidth, leftCapWidth, rightCapWidth } = seg;
|
|
879
970
|
const topFillWidth = Math.max(0, width);
|
|
880
971
|
|
|
972
|
+
const rank = this.#priorityRanker(seg);
|
|
973
|
+
|
|
881
974
|
/**
|
|
882
|
-
* Evict against `budget
|
|
883
|
-
*
|
|
884
|
-
*
|
|
885
|
-
*
|
|
975
|
+
* Evict against `budget`: shrink the path first, then drop the
|
|
976
|
+
* lowest-priority segment still standing — right side before left, tail
|
|
977
|
+
* first within a side, which is the historical order for everything that
|
|
978
|
+
* carries no priority. Context %, the goal indicator and the model name
|
|
979
|
+
* outrank ordinary telemetry and are therefore the last to go. Runs from
|
|
980
|
+
* the original segment lists each time so the marker reservation can be
|
|
981
|
+
* recomputed without compounding evictions.
|
|
886
982
|
*/
|
|
887
983
|
const layout = (budget: number) => {
|
|
888
984
|
const left = [...seg.left];
|
|
889
985
|
const leftIds = [...seg.leftSegIds];
|
|
890
986
|
const right = [...seg.right];
|
|
987
|
+
const rightIds = [...seg.rightSegIds];
|
|
891
988
|
let leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
892
989
|
let rightWidth = this.#groupWidth(right, rightCapWidth, rightSepWidth);
|
|
893
990
|
const totalWidth = () => leftWidth + rightWidth + (left.length > 0 && right.length > 0 ? 1 : 0);
|
|
@@ -905,19 +1002,42 @@ export class StatusLineComponent implements Component {
|
|
|
905
1002
|
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
906
1003
|
}
|
|
907
1004
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1005
|
+
// Lowest rank loses first; equal ranks fall back to the historical
|
|
1006
|
+
// right-then-left, tail-first order.
|
|
1007
|
+
while (totalWidth() > budget && (left.length > 0 || right.length > 0)) {
|
|
1008
|
+
let victimSide: "left" | "right" | null = null;
|
|
1009
|
+
let victimIndex = -1;
|
|
1010
|
+
let victimRank = Number.POSITIVE_INFINITY;
|
|
1011
|
+
for (let index = right.length - 1; index >= 0; index -= 1) {
|
|
1012
|
+
const candidate = rank(rightIds[index]);
|
|
1013
|
+
if (candidate < victimRank) {
|
|
1014
|
+
victimRank = candidate;
|
|
1015
|
+
victimSide = "right";
|
|
1016
|
+
victimIndex = index;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
for (let index = left.length - 1; index >= 0; index -= 1) {
|
|
1020
|
+
const candidate = rank(leftIds[index]);
|
|
1021
|
+
if (candidate < victimRank) {
|
|
1022
|
+
victimRank = candidate;
|
|
1023
|
+
victimSide = "left";
|
|
1024
|
+
victimIndex = index;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
if (victimSide === null) break;
|
|
1028
|
+
if (victimSide === "right") {
|
|
1029
|
+
right.splice(victimIndex, 1);
|
|
1030
|
+
rightIds.splice(victimIndex, 1);
|
|
1031
|
+
rightWidth = this.#groupWidth(right, rightCapWidth, rightSepWidth);
|
|
1032
|
+
} else {
|
|
1033
|
+
left.splice(victimIndex, 1);
|
|
1034
|
+
leftIds.splice(victimIndex, 1);
|
|
1035
|
+
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1036
|
+
}
|
|
916
1037
|
dropped += 1;
|
|
917
|
-
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
918
1038
|
}
|
|
919
1039
|
}
|
|
920
|
-
return { left, right, leftWidth, rightWidth, dropped };
|
|
1040
|
+
return { left, right, leftIds, rightIds, leftWidth, rightWidth, dropped };
|
|
921
1041
|
};
|
|
922
1042
|
|
|
923
1043
|
// First pass with the full budget establishes whether anything is lost at
|
|
@@ -939,6 +1059,11 @@ export class StatusLineComponent implements Component {
|
|
|
939
1059
|
}
|
|
940
1060
|
}
|
|
941
1061
|
const marker = this.#renderOverflowMarker(placed.dropped, reserved);
|
|
1062
|
+
// Nothing survived that the user actually needs? Spend the rail on the
|
|
1063
|
+
// priority row instead of an overflow marker. Suppressing the marker here
|
|
1064
|
+
// is deliberate: at these widths its cells are worth more as context %.
|
|
1065
|
+
const priorityRow = this.#priorityRowFor(seg, placed.leftIds, placed.rightIds, topFillWidth);
|
|
1066
|
+
if (priorityRow !== null) return priorityRow;
|
|
942
1067
|
|
|
943
1068
|
const leftGroup = this.#renderStatusGroup(placed.left, "left", separatorDef, bgAnsi, fgAnsi, sepAnsi);
|
|
944
1069
|
const rightGroup = this.#renderStatusGroup(placed.right, "right", separatorDef, bgAnsi, fgAnsi, sepAnsi);
|
|
@@ -1078,6 +1203,17 @@ export class StatusLineComponent implements Component {
|
|
|
1078
1203
|
if (row.length === 0) packedRows.splice(index, 1);
|
|
1079
1204
|
}
|
|
1080
1205
|
|
|
1206
|
+
// Wrapping is supposed to be the lossless path. When even `maxRows` rows
|
|
1207
|
+
// cannot hold everything, the priority row beats a partial rail plus a
|
|
1208
|
+
// count of what is missing.
|
|
1209
|
+
if (dropped > 0) {
|
|
1210
|
+
const priorityRow = this.#priorityRowFor(seg, [], [], topFillWidth);
|
|
1211
|
+
if (priorityRow !== null) {
|
|
1212
|
+
this.#renderedRowsCache = { key: cacheKey, rows: [priorityRow] };
|
|
1213
|
+
return [priorityRow];
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1081
1217
|
// Reserve marker cells on whichever row ends up last, evicting from its
|
|
1082
1218
|
// tail until the cue fits. Never opens a new row: that would contradict
|
|
1083
1219
|
// maxRows. Emptying that row is fine -- the marker then occupies it alone.
|
|
@@ -312,7 +312,7 @@ export class ExtensionUiController {
|
|
|
312
312
|
session.retryNow();
|
|
313
313
|
return { retried: true, immediate: true };
|
|
314
314
|
case "bash.background":
|
|
315
|
-
if (!session.requestForegroundBashBackground())
|
|
315
|
+
if (!(await session.requestForegroundBashBackground()))
|
|
316
316
|
throw Object.assign(new Error("The active bash command cannot be moved to a managed background job."), {
|
|
317
317
|
code: "not_foldable",
|
|
318
318
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { type AgentMessage, ThinkingLevel } from "@gajae-code/agent-core";
|
|
4
|
-
import { type AutocompleteProvider, matchesKey, type SlashCommand } from "@gajae-code/tui";
|
|
4
|
+
import { type AutocompleteProvider, getKeybindings, matchesKey, type SlashCommand } from "@gajae-code/tui";
|
|
5
5
|
import { $pickenv, logger, sanitizeText } from "@gajae-code/utils";
|
|
6
6
|
import { type AppKeybinding, KEYBINDINGS } from "../../config/keybindings";
|
|
7
7
|
import { isSettingsInitialized, settings } from "../../config/settings";
|
|
@@ -420,6 +420,34 @@ export class InputController {
|
|
|
420
420
|
if (this.ctx.cancelPendingSubmission()) {
|
|
421
421
|
return true;
|
|
422
422
|
}
|
|
423
|
+
if (options.processes && this.ctx.session.isBashRunning) {
|
|
424
|
+
this.ctx.session.abortBash();
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
if (options.processes && this.ctx.session.isEvalRunning) {
|
|
428
|
+
this.ctx.session.abortEval();
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
// A queued steer on a live stream is owned by the steering branch below,
|
|
432
|
+
// which silently consumes it and auto-continues. This branch counts the same
|
|
433
|
+
// queue as cancellable work, and a streaming turn always has the indicator
|
|
434
|
+
// mounted, so without this hand-off it short-circuited first and turned the
|
|
435
|
+
// documented steer-on-interrupt into a loud abort that dumped the steer back
|
|
436
|
+
// into the composer. `cancelPendingSubmission()` above still wins: a
|
|
437
|
+
// submission that has not started yet is cancelled before the stream owns
|
|
438
|
+
// the key, and one that has started returns false there by design.
|
|
439
|
+
if (
|
|
440
|
+
options.streaming === true &&
|
|
441
|
+
this.ctx.session.isStreaming &&
|
|
442
|
+
this.ctx.session.hasQueuedSteering &&
|
|
443
|
+
!this.ctx.session.isBashRunning &&
|
|
444
|
+
!this.ctx.session.isEvalRunning &&
|
|
445
|
+
!this.#steerConsumePending
|
|
446
|
+
) {
|
|
447
|
+
this.#steerConsumePending = true;
|
|
448
|
+
void this.#abortInteractive({ silent: true });
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
423
451
|
// Only count queues this handler can actually drain. The aggregate
|
|
424
452
|
// `queuedMessageCount` also counts hidden next-turn context, which
|
|
425
453
|
// `restoreQueuedMessagesToEditor()` never returns and which survives turn
|
|
@@ -1855,19 +1883,36 @@ export class InputController {
|
|
|
1855
1883
|
const now = Date.now();
|
|
1856
1884
|
if (now - this.#lastBackgroundFoldKeyTime > BACKGROUND_FOLD_DOUBLE_PRESS_MS) {
|
|
1857
1885
|
this.#lastBackgroundFoldKeyTime = now;
|
|
1858
|
-
|
|
1886
|
+
const foldKeys = getKeybindings().getKeys("app.tool.backgroundFold");
|
|
1887
|
+
this.ctx.showStatus(
|
|
1888
|
+
`Press ${foldKeys.join(" / ")} again to fold supported foreground bash into a background job`,
|
|
1889
|
+
);
|
|
1859
1890
|
return true;
|
|
1860
1891
|
}
|
|
1861
1892
|
this.#lastBackgroundFoldKeyTime = 0;
|
|
1862
1893
|
|
|
1863
|
-
|
|
1894
|
+
try {
|
|
1895
|
+
const request = this.ctx.session.requestForegroundBashBackground?.() ?? Promise.resolve(false);
|
|
1896
|
+
void request
|
|
1897
|
+
.then(backgrounded => {
|
|
1898
|
+
if (backgrounded) {
|
|
1899
|
+
this.ctx.showStatus("Folding foreground bash into a quiet background job…");
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
this.ctx.showWarning(
|
|
1903
|
+
"No supported foreground tool can be folded. Use managed async bash/auto-background; raw Ctrl+Z/bg is not supported inside the TUI.",
|
|
1904
|
+
);
|
|
1905
|
+
})
|
|
1906
|
+
.catch(() => {
|
|
1907
|
+
this.ctx.showWarning(
|
|
1908
|
+
"No supported foreground tool can be folded. Use managed async bash/auto-background; raw Ctrl+Z/bg is not supported inside the TUI.",
|
|
1909
|
+
);
|
|
1910
|
+
});
|
|
1911
|
+
} catch {
|
|
1864
1912
|
this.ctx.showWarning(
|
|
1865
1913
|
"No supported foreground tool can be folded. Use managed async bash/auto-background; raw Ctrl+Z/bg is not supported inside the TUI.",
|
|
1866
1914
|
);
|
|
1867
|
-
return true;
|
|
1868
1915
|
}
|
|
1869
|
-
|
|
1870
|
-
this.ctx.showStatus("Folding foreground bash into a quiet background job…");
|
|
1871
1916
|
return true;
|
|
1872
1917
|
}
|
|
1873
1918
|
|