@gajae-code/coding-agent 0.15.4 → 0.15.6
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 +46 -2
- package/dist/types/async/job-manager.d.ts +83 -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/update-cli.d.ts +27 -5
- package/dist/types/commands/auth-gateway.d.ts +3 -0
- package/dist/types/commands/gc.d.ts +13 -0
- package/dist/types/commands/model-presets.d.ts +21 -0
- package/dist/types/config/autorouting-tier-map.d.ts +99 -0
- package/dist/types/config/file-lock.d.ts +49 -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 +11 -5
- package/dist/types/config/settings-schema.d.ts +9 -0
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -5
- package/dist/types/coordinator-mcp/projection-scan.d.ts +46 -0
- package/dist/types/extensibility/extensions/types.d.ts +4 -4
- package/dist/types/extensibility/shared-events.d.ts +2 -0
- 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/session-state-lock.d.ts +35 -6
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/main.d.ts +3 -1
- package/dist/types/modes/components/jobs-overlay-model.d.ts +4 -2
- package/dist/types/modes/jobs-observer.d.ts +36 -0
- package/dist/types/sdk/broker/broker.d.ts +3 -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 +8 -1
- package/dist/types/sdk/cli/session-cli.d.ts +42 -1
- package/dist/types/sdk/client/discovery.d.ts +1 -0
- package/dist/types/sdk/host/host.d.ts +2 -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 +19 -1
- 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 +9 -0
- package/dist/types/session/account-inventory.d.ts +1 -0
- package/dist/types/session/agent-session.d.ts +19 -5
- package/dist/types/session/fold-coordinator.d.ts +201 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +4 -3
- 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/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/index.d.ts +6 -5
- package/dist/types/tools/path-utils.d.ts +5 -6
- package/dist/types/tools/tmux-self-injection-guard.d.ts +15 -0
- 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 +3 -3
- package/src/async/job-manager.ts +671 -39
- 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 +3 -0
- package/src/cli/model-presets-cli.ts +100 -0
- package/src/cli/update-cli.ts +142 -6
- package/src/cli.ts +6 -2
- package/src/commands/auth-gateway.ts +12 -8
- package/src/commands/gc.ts +19 -0
- package/src/commands/model-presets.ts +24 -0
- package/src/commands/sdk.ts +5 -0
- package/src/config/autorouting-tier-map.ts +33 -0
- package/src/config/file-lock-gc.ts +9 -4
- package/src/config/file-lock.ts +775 -59
- 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 +2799 -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 +1028 -123
- package/src/config/settings-schema.ts +11 -0
- package/src/config/settings.ts +6 -1
- package/src/coordinator-mcp/model-preset.ts +32 -17
- package/src/coordinator-mcp/policy.ts +1 -1
- package/src/coordinator-mcp/projection-scan.ts +571 -0
- package/src/coordinator-mcp/server.ts +19 -24
- package/src/coordinator-mcp/session-reaper.ts +9 -3
- package/src/extensibility/extensions/loader.ts +0 -1
- package/src/extensibility/extensions/types.ts +4 -2
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +1 -0
- package/src/extensibility/shared-events.ts +2 -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.ts +44 -7
- package/src/gjc-runtime/ralplan-runtime.ts +14 -1
- package/src/gjc-runtime/session-state-lock.ts +1242 -141
- package/src/gjc-runtime/session-state-sidecar.ts +76 -3
- package/src/gjc-runtime/tmux-sessions.ts +145 -48
- package/src/index.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/lsp/config.ts +4 -3
- package/src/lsp/path-trust.ts +12 -6
- package/src/main.ts +9 -2
- package/src/modes/acp/acp-agent.ts +11 -5
- package/src/modes/components/jobs-overlay-model.ts +99 -16
- package/src/modes/components/model-selector.ts +158 -18
- package/src/modes/components/status-line/segments.ts +6 -1
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +23 -6
- package/src/modes/interactive-mode.ts +14 -1
- package/src/modes/jobs-observer.ts +149 -16
- package/src/modes/prompt-action-autocomplete.ts +26 -47
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/utils/injected-user-submission.ts +15 -1
- 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 +48 -21
- package/src/sdk/broker/lifecycle.ts +32 -6
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +11 -1
- package/src/sdk/bus/kind-aware-reconciliation.ts +46 -8
- package/src/sdk/bus/prompt-reconciliation.ts +33 -6
- package/src/sdk/bus/slack-conversation.ts +5 -1
- package/src/sdk/bus/telegram-daemon-contract.ts +8 -1
- package/src/sdk/cli/session-cli.ts +161 -5
- package/src/sdk/client/discovery.ts +3 -1
- package/src/sdk/host/control/dispatch.ts +3 -0
- package/src/sdk/host/host.ts +7 -0
- 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 +1240 -107
- package/src/sdk/host/session-runtime.ts +1029 -115
- 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 +2 -2
- package/src/sdk/protocol/operation-registry.ts +4 -1
- package/src/sdk/router/session-router.ts +297 -111
- package/src/sdk/session.ts +83 -12
- package/src/session/account-inventory.ts +27 -11
- package/src/session/agent-session.ts +887 -95
- package/src/session/fold-coordinator.ts +433 -0
- package/src/session/internal/managed-session-storage.ts +64 -26
- 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 +29 -10
- 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 +142 -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 +786 -67
- package/src/tools/index.ts +7 -5
- package/src/tools/path-utils.ts +77 -29
- package/src/tools/tmux-self-injection-guard.ts +359 -0
- 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
|
@@ -3,6 +3,7 @@ import type { PtySession as NativePtySession, PtyRunResult } from "@gajae-code/n
|
|
|
3
3
|
import {
|
|
4
4
|
type Component,
|
|
5
5
|
extractPrintableText,
|
|
6
|
+
getKeybindings,
|
|
6
7
|
matchesKey,
|
|
7
8
|
padding,
|
|
8
9
|
parseKey,
|
|
@@ -10,7 +11,7 @@ import {
|
|
|
10
11
|
truncateToWidth,
|
|
11
12
|
visibleWidth,
|
|
12
13
|
} from "@gajae-code/tui";
|
|
13
|
-
import { sanitizeText } from "@gajae-code/utils";
|
|
14
|
+
import { logger, sanitizeText } from "@gajae-code/utils";
|
|
14
15
|
import type { Terminal as XtermTerminalType } from "@xterm/headless";
|
|
15
16
|
import { Settings } from "../config/settings";
|
|
16
17
|
import { NON_INTERACTIVE_ENV } from "../exec/non-interactive-env";
|
|
@@ -115,6 +116,7 @@ class BashInteractiveOverlayComponent implements Component {
|
|
|
115
116
|
#writeOffset = 0;
|
|
116
117
|
#flushResolvers: Array<() => void> = [];
|
|
117
118
|
#writing = false;
|
|
119
|
+
#onFoldKey: () => boolean = () => false;
|
|
118
120
|
|
|
119
121
|
constructor(
|
|
120
122
|
private readonly command: string,
|
|
@@ -131,10 +133,16 @@ class BashInteractiveOverlayComponent implements Component {
|
|
|
131
133
|
});
|
|
132
134
|
}
|
|
133
135
|
|
|
134
|
-
setHandlers(
|
|
136
|
+
setHandlers(
|
|
137
|
+
onInput: (data: string) => void,
|
|
138
|
+
onDismiss: () => void,
|
|
139
|
+
onDispose: () => void,
|
|
140
|
+
onFoldKey: () => boolean,
|
|
141
|
+
): void {
|
|
135
142
|
this.#onInput = onInput;
|
|
136
143
|
this.#onDismiss = onDismiss;
|
|
137
144
|
this.#onDispose = onDispose;
|
|
145
|
+
this.#onFoldKey = onFoldKey;
|
|
138
146
|
}
|
|
139
147
|
|
|
140
148
|
appendOutput(chunk: string): void {
|
|
@@ -199,6 +207,9 @@ class BashInteractiveOverlayComponent implements Component {
|
|
|
199
207
|
}
|
|
200
208
|
|
|
201
209
|
handleInput(data: string): void {
|
|
210
|
+
if (this.#state === "running" && getKeybindings().matches(data, "app.tool.backgroundFold")) {
|
|
211
|
+
if (this.#onFoldKey()) return;
|
|
212
|
+
}
|
|
202
213
|
if (this.#state === "running" && (matchesKey(data, "escape") || matchesKey(data, "esc"))) {
|
|
203
214
|
this.#onDismiss();
|
|
204
215
|
return;
|
|
@@ -293,6 +304,25 @@ class BashInteractiveOverlayComponent implements Component {
|
|
|
293
304
|
}
|
|
294
305
|
}
|
|
295
306
|
|
|
307
|
+
/** Live controls handed to the caller once the PTY run owns its session. */
|
|
308
|
+
export interface InteractivePtyControls {
|
|
309
|
+
/**
|
|
310
|
+
* Detach the overlay and settle the foreground call with `foldResult`, leaving
|
|
311
|
+
* the process running and still writing into the sink. Idempotent.
|
|
312
|
+
*/
|
|
313
|
+
detachObserver: (foldResult: BashInteractiveResult) => "resolved" | "already-settled";
|
|
314
|
+
/**
|
|
315
|
+
* Resolves with the run's real summary when the process actually ends,
|
|
316
|
+
* regardless of whether the foreground was folded. A folded run is delivered
|
|
317
|
+
* from this, so its result can never be silently dropped.
|
|
318
|
+
*/
|
|
319
|
+
terminalCompletion: Promise<BashInteractiveResult>;
|
|
320
|
+
/** Kill the owned process during owner teardown; the observer never calls this. */
|
|
321
|
+
kill: () => void;
|
|
322
|
+
/** Stop forwarding the foreground abort signal after ownership transfers on fold. */
|
|
323
|
+
detachForegroundCancellation: () => void;
|
|
324
|
+
}
|
|
325
|
+
|
|
296
326
|
export async function runInteractiveBashPty(
|
|
297
327
|
ui: NonNullable<AgentToolContext["ui"]>,
|
|
298
328
|
options: {
|
|
@@ -307,6 +337,11 @@ export async function runInteractiveBashPty(
|
|
|
307
337
|
spillThreshold?: number;
|
|
308
338
|
headBytes?: number;
|
|
309
339
|
settings?: Settings;
|
|
340
|
+
/** Receives live controls once the session is running; used to register a fold. */
|
|
341
|
+
onControls?: (controls: InteractivePtyControls) => void;
|
|
342
|
+
onFoldKey?: () => boolean;
|
|
343
|
+
onDismiss?: () => void;
|
|
344
|
+
onOutput?: (chunk: string) => void;
|
|
310
345
|
},
|
|
311
346
|
): Promise<BashInteractiveResult> {
|
|
312
347
|
const settings = options.settings ?? (await Settings.init());
|
|
@@ -322,89 +357,151 @@ export async function runInteractiveBashPty(
|
|
|
322
357
|
const { default: xterm } = await import("@xterm/headless");
|
|
323
358
|
const XtermTerminal = xterm.Terminal;
|
|
324
359
|
const PtySession = await ptySessionNative();
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
360
|
+
|
|
361
|
+
// Ownership inversion: this runner owns the session and the sink from t0,
|
|
362
|
+
// started OUTSIDE ui.custom. The overlay is only an observer view, so
|
|
363
|
+
// dismissing it — or never creating it — can never kill the process.
|
|
364
|
+
// Seed the real host geometry so the first frame renders at the user's size;
|
|
365
|
+
// the overlay keeps resizing the session as it lays out. Fallbacks apply only
|
|
366
|
+
// when the host exposes no terminal (tests, pipes).
|
|
367
|
+
const initialPtySize = {
|
|
368
|
+
cols: Math.max(20, (process.stdout.columns ?? 80) - 2),
|
|
369
|
+
rows: Math.max(5, (process.stdout.rows ?? 24) - 4),
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const session = new PtySession();
|
|
373
|
+
const processAbortController = new AbortController();
|
|
374
|
+
const forwardForegroundAbort = () => processAbortController.abort();
|
|
375
|
+
if (options.signal?.aborted) processAbortController.abort();
|
|
376
|
+
else options.signal?.addEventListener("abort", forwardForegroundAbort, { once: true });
|
|
377
|
+
let observer: BashInteractiveOverlayComponent | undefined;
|
|
378
|
+
let settleForeground: ((result: BashInteractiveResult) => void) | undefined;
|
|
379
|
+
let settled = false;
|
|
380
|
+
const foreground = Promise.withResolvers<BashInteractiveResult>();
|
|
381
|
+
const terminal = Promise.withResolvers<BashInteractiveResult>();
|
|
382
|
+
|
|
383
|
+
const settle = (result: BashInteractiveResult): "resolved" | "already-settled" => {
|
|
384
|
+
if (settled) return "already-settled";
|
|
385
|
+
settled = true;
|
|
386
|
+
// Prefer ui.custom's own `done` so the overlay tears down through its normal
|
|
387
|
+
// path; fall back to the outer promise when no overlay was ever attached.
|
|
388
|
+
if (settleForeground) settleForeground(result);
|
|
389
|
+
else foreground.resolve(result);
|
|
390
|
+
return "resolved";
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
let finished = false;
|
|
394
|
+
const finalize = (run: PtyRunResult): void => {
|
|
395
|
+
if (finished) return;
|
|
396
|
+
finished = true;
|
|
397
|
+
observer?.setComplete({ exitCode: run.exitCode, cancelled: run.cancelled, timedOut: run.timedOut });
|
|
398
|
+
void (async () => {
|
|
399
|
+
await observer?.flushOutput();
|
|
400
|
+
const summary = await sink.dump();
|
|
401
|
+
const outcome: BashInteractiveResult = {
|
|
402
|
+
exitCode: run.exitCode,
|
|
403
|
+
cancelled: run.cancelled,
|
|
404
|
+
timedOut: run.timedOut,
|
|
405
|
+
...summary,
|
|
351
406
|
};
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
},
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const normalizedChunk = normalizeCaptureChunk(chunk);
|
|
396
|
-
sink.push(normalizedChunk);
|
|
397
|
-
tui.requestRender();
|
|
398
|
-
},
|
|
399
|
-
)
|
|
400
|
-
.then(finalize)
|
|
401
|
-
.catch(error => {
|
|
402
|
-
sink.push(`PTY error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
403
|
-
finalize({ exitCode: undefined, cancelled: false, timedOut: false });
|
|
404
|
-
});
|
|
405
|
-
return component;
|
|
407
|
+
options.signal?.removeEventListener("abort", forwardForegroundAbort);
|
|
408
|
+
// Publish the real outcome BEFORE settling: a folded run's foreground is
|
|
409
|
+
// already gone, and this is the only path that can deliver its result.
|
|
410
|
+
terminal.resolve(outcome);
|
|
411
|
+
settle(outcome);
|
|
412
|
+
})();
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
// The original deadline rides on the run itself, so a folded command expires
|
|
416
|
+
// exactly when it would have in the foreground.
|
|
417
|
+
void session
|
|
418
|
+
.start(
|
|
419
|
+
{
|
|
420
|
+
command: options.command,
|
|
421
|
+
cwd: options.cwd,
|
|
422
|
+
timeoutMs: options.timeoutMs,
|
|
423
|
+
env: { ...NON_INTERACTIVE_ENV, ...options.env },
|
|
424
|
+
signal: processAbortController.signal,
|
|
425
|
+
cols: initialPtySize.cols,
|
|
426
|
+
rows: initialPtySize.rows,
|
|
427
|
+
shell: resolvedShell,
|
|
428
|
+
},
|
|
429
|
+
(err, chunk) => {
|
|
430
|
+
if (finished || err || !chunk) return;
|
|
431
|
+
// The sink is fed unconditionally: output stays continuous across a
|
|
432
|
+
// fold, when no overlay exists, and after the overlay is disposed.
|
|
433
|
+
const normalizedChunk = normalizeCaptureChunk(chunk);
|
|
434
|
+
sink.push(normalizedChunk);
|
|
435
|
+
options.onOutput?.(normalizedChunk);
|
|
436
|
+
observer?.appendOutput(chunk);
|
|
437
|
+
},
|
|
438
|
+
)
|
|
439
|
+
.then(finalize)
|
|
440
|
+
.catch(error => {
|
|
441
|
+
sink.push(`PTY error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
442
|
+
finalize({ exitCode: undefined, cancelled: false, timedOut: false });
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
options.onControls?.({
|
|
446
|
+
terminalCompletion: terminal.promise,
|
|
447
|
+
kill: () => session.kill(),
|
|
448
|
+
detachForegroundCancellation: () => {
|
|
449
|
+
options.signal?.removeEventListener("abort", forwardForegroundAbort);
|
|
406
450
|
},
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
451
|
+
detachObserver: (foldResult: BashInteractiveResult) => {
|
|
452
|
+
const outcome = settle(foldResult);
|
|
453
|
+
if (outcome === "resolved") observer = undefined;
|
|
454
|
+
return outcome;
|
|
455
|
+
},
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Folded (or already finished) before any view existed: there is nothing to
|
|
459
|
+
// attach an observer to, so never await an overlay that will never be settled.
|
|
460
|
+
if (settled) return await foreground.promise;
|
|
461
|
+
|
|
462
|
+
try {
|
|
463
|
+
const overlayResult = await Promise.race([
|
|
464
|
+
foreground.promise,
|
|
465
|
+
ui.custom<BashInteractiveResult>(
|
|
466
|
+
(tui, uiTheme, _keybindings, done) => {
|
|
467
|
+
settleForeground = done;
|
|
468
|
+
const component = new BashInteractiveOverlayComponent(
|
|
469
|
+
options.command,
|
|
470
|
+
uiTheme,
|
|
471
|
+
() => tui.terminal.rows,
|
|
472
|
+
XtermTerminal,
|
|
473
|
+
);
|
|
474
|
+
component.setSession(session);
|
|
475
|
+
// Observer view only: stdin still reaches the process while attached,
|
|
476
|
+
// Escape explicitly force-kills, while disposal never kills the work.
|
|
477
|
+
component.setHandlers(
|
|
478
|
+
data => {
|
|
479
|
+
try {
|
|
480
|
+
session.write(data);
|
|
481
|
+
} catch {
|
|
482
|
+
// ignore writes after the command exits
|
|
483
|
+
}
|
|
484
|
+
},
|
|
485
|
+
options.onDismiss ?? (() => session.kill()),
|
|
486
|
+
() => {},
|
|
487
|
+
options.onFoldKey ?? (() => false),
|
|
488
|
+
);
|
|
489
|
+
observer = component;
|
|
490
|
+
return component;
|
|
491
|
+
},
|
|
492
|
+
{ overlay: true },
|
|
493
|
+
),
|
|
494
|
+
]);
|
|
495
|
+
return overlayResult;
|
|
496
|
+
} catch (error) {
|
|
497
|
+
// Overlay creation or view init failed. The process is already running and
|
|
498
|
+
// owned here, so surface the failure without orphaning it: the run keeps
|
|
499
|
+
// writing to the sink and settles the foreground on its own.
|
|
500
|
+
logger.warn("Interactive PTY overlay unavailable; continuing without the view", {
|
|
501
|
+
error: error instanceof Error ? error.message : String(error),
|
|
502
|
+
});
|
|
503
|
+
observer = undefined;
|
|
504
|
+
settleForeground = undefined;
|
|
505
|
+
return await foreground.promise;
|
|
506
|
+
}
|
|
410
507
|
}
|