@gajae-code/coding-agent 0.16.3 → 0.16.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 +113 -0
- package/dist/types/capability/types.d.ts +4 -0
- package/dist/types/cli/args.d.ts +2 -2
- package/dist/types/cli/customize-doctor.d.ts +22 -0
- package/dist/types/cli/root-flags.d.ts +4 -2
- package/dist/types/cli.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +1 -1
- package/dist/types/config/autorouting-tier-map.d.ts +3 -0
- package/dist/types/config/file-lock.d.ts +31 -3
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +9 -0
- package/dist/types/config/settings-schema.d.ts +29 -1
- package/dist/types/coordinator-mcp/question-state.d.ts +7 -0
- package/dist/types/coordinator-mcp/server.d.ts +7 -0
- package/dist/types/discovery/claude-plugins.d.ts +3 -1
- package/dist/types/discovery/helpers.d.ts +35 -0
- package/dist/types/exec/bash-executor.d.ts +17 -1
- package/dist/types/exec/bash-shell-guardian.d.ts +15 -0
- package/dist/types/exec/bash-shell-runtime-entry.d.ts +2 -0
- package/dist/types/exec/bash-shell-signals.d.ts +2 -0
- package/dist/types/exec/bash-shell-supervisor-entry.d.ts +1 -0
- package/dist/types/exec/bash-shell-supervisor.d.ts +10 -0
- package/dist/types/exec/bash-shell-worker-entry.d.ts +2 -0
- package/dist/types/exec/bash-shell-worker-protocol.d.ts +63 -0
- package/dist/types/exec/bash-shell-worker.d.ts +1 -0
- package/dist/types/exec/isolated-shell.d.ts +19 -0
- package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +9 -0
- package/dist/types/extensibility/extensions/function-hooks.d.ts +169 -0
- package/dist/types/extensibility/extensions/index.d.ts +1 -0
- package/dist/types/extensibility/extensions/runner.d.ts +46 -6
- package/dist/types/extensibility/extensions/types.d.ts +16 -0
- package/dist/types/extensibility/extensions/wrapper.d.ts +3 -1
- package/dist/types/extensibility/gjc-plugins/constrained-hooks.d.ts +35 -2
- package/dist/types/extensibility/gjc-plugins/mcp-policy.d.ts +16 -0
- package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -0
- package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
- package/dist/types/extensibility/hooks/types.d.ts +4 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +5 -2
- package/dist/types/extensibility/shared-events.d.ts +1 -1
- package/dist/types/extensibility/skill-management.d.ts +9 -4
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +4 -1
- package/dist/types/gjc-runtime/linux-proc.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +14 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +41 -3
- package/dist/types/gjc-runtime/ultragoal-succession.d.ts +251 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -2
- package/dist/types/modes/components/bash-execution.d.ts +1 -0
- package/dist/types/modes/components/execution-shared.d.ts +1 -0
- package/dist/types/modes/components/settings-selector.d.ts +6 -1
- package/dist/types/modes/components/status-line/command.d.ts +35 -0
- package/dist/types/modes/components/status-line/index.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +5 -0
- package/dist/types/modes/components/tool-status-header.d.ts +14 -0
- package/dist/types/modes/controllers/event-controller.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/types.d.ts +7 -0
- package/dist/types/runtime-mcp/client.d.ts +9 -1
- package/dist/types/runtime-mcp/manager.d.ts +7 -1
- package/dist/types/runtime-mcp/pool.d.ts +22 -3
- package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
- package/dist/types/runtime-mcp/types.d.ts +22 -0
- package/dist/types/sdk/broker/ensure.d.ts +32 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +0 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
- package/dist/types/sdk/broker/startup-failure.d.ts +4 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +12 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +17 -2
- package/dist/types/sdk/bus/conversation-store.d.ts +11 -0
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +8 -8
- package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/slack-live-provider.d.ts +6 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +12 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +8 -0
- package/dist/types/sdk/client/client.d.ts +6 -0
- package/dist/types/sdk/host/host.d.ts +3 -1
- package/dist/types/sdk/host/session-runtime.d.ts +26 -1
- package/dist/types/sdk/lifecycle/service.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +10 -2
- package/dist/types/sdk/session-api-key.d.ts +26 -0
- package/dist/types/sdk/session.d.ts +5 -1
- package/dist/types/sdk/transport/serve-cli.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +11 -0
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/setup/paseo/announce.d.ts +112 -0
- package/dist/types/setup/paseo/setup-deps.d.ts +2 -1
- package/dist/types/skill-state/canonical-skills.d.ts +4 -0
- package/dist/types/system-prompt.d.ts +10 -0
- package/dist/types/task/executor.d.ts +5 -1
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/fetch.d.ts +7 -0
- package/dist/types/tools/github-cache.d.ts +2 -1
- package/dist/types/tools/index.d.ts +10 -0
- package/dist/types/tools/path-utils.d.ts +2 -0
- package/dist/types/tools/search.d.ts +6 -0
- package/package.json +9 -7
- package/scripts/capture-status-line-custom-editor-showcase.ts +151 -0
- package/scripts/generate-sdk-operation-inventory.ts +11 -0
- package/scripts/verify-status-line-custom-editor-showcase.ts +309 -0
- package/src/capability/index.ts +14 -2
- package/src/capability/types.ts +4 -0
- package/src/cli/args.ts +20 -9
- package/src/cli/customize-doctor.ts +155 -12
- package/src/cli/root-flags.ts +5 -3
- package/src/cli.ts +22 -0
- package/src/commands/sdk.ts +74 -31
- package/src/config/autorouting-tier-map.ts +1 -0
- package/src/config/file-lock-gc.ts +40 -2
- package/src/config/file-lock.ts +379 -31
- package/src/config/model-preset-registry.ts +120 -19
- package/src/config/model-profiles.ts +40 -0
- package/src/config/model-registry.ts +73 -23
- package/src/config/model-resolver.ts +48 -0
- package/src/config/settings-schema.ts +33 -1
- package/src/config/settings.ts +1 -0
- package/src/coordinator-mcp/question-state.ts +33 -2
- package/src/coordinator-mcp/server.ts +477 -101
- package/src/crash/sanitize.ts +1 -1
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +11 -11
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +13 -2
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +22 -0
- package/src/discovery/builtin.ts +52 -55
- package/src/discovery/claude-plugins.ts +12 -4
- package/src/discovery/helpers.ts +307 -34
- package/src/exec/bash-executor.ts +330 -50
- package/src/exec/bash-shell-guardian.ts +388 -0
- package/src/exec/bash-shell-runtime-entry.ts +5 -0
- package/src/exec/bash-shell-signals.ts +29 -0
- package/src/exec/bash-shell-supervisor-entry.ts +3 -0
- package/src/exec/bash-shell-supervisor.ts +359 -0
- package/src/exec/bash-shell-worker-entry.ts +5 -0
- package/src/exec/bash-shell-worker-protocol.ts +36 -0
- package/src/exec/bash-shell-worker.ts +116 -0
- package/src/exec/isolated-shell.ts +393 -0
- package/src/extensibility/extensions/function-hooks-internal.ts +73 -0
- package/src/extensibility/extensions/function-hooks.ts +959 -0
- package/src/extensibility/extensions/index.ts +22 -0
- package/src/extensibility/extensions/loader.ts +53 -2
- package/src/extensibility/extensions/runner.ts +895 -264
- package/src/extensibility/extensions/types.ts +42 -0
- package/src/extensibility/extensions/wrapper.ts +56 -15
- package/src/extensibility/gjc-plugins/compiler.ts +228 -60
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +321 -23
- package/src/extensibility/gjc-plugins/installer.ts +13 -12
- package/src/extensibility/gjc-plugins/mcp-policy.ts +81 -52
- package/src/extensibility/gjc-plugins/paths.ts +13 -0
- package/src/extensibility/gjc-plugins/registry.ts +113 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +758 -16
- package/src/extensibility/gjc-plugins/runtime-quarantine.ts +20 -2
- package/src/extensibility/gjc-plugins/schema.ts +121 -4
- package/src/extensibility/gjc-plugins/types.ts +25 -0
- package/src/extensibility/hooks/loader.ts +133 -16
- package/src/extensibility/hooks/types.ts +28 -0
- package/src/extensibility/runtime-skill-discovery.ts +149 -190
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/skill-management.ts +200 -52
- package/src/extensibility/skills.ts +73 -25
- package/src/gjc-runtime/empty-delete-gc.ts +94 -4
- package/src/gjc-runtime/gc-runtime.ts +29 -0
- package/src/gjc-runtime/linux-proc.ts +14 -0
- package/src/gjc-runtime/repository-binding.ts +1 -1
- package/src/gjc-runtime/session-state-lock.ts +143 -10
- package/src/gjc-runtime/session-state-sidecar.ts +27 -11
- package/src/gjc-runtime/state-writer.ts +30 -29
- package/src/gjc-runtime/ultragoal-runtime.ts +189 -47
- package/src/gjc-runtime/ultragoal-succession.ts +1667 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +52 -1
- package/src/gjc-runtime/workflow-manifest.ts +16 -0
- package/src/harness-control-plane/session-lease.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +12 -12
- package/src/lsp/config.ts +10 -3
- package/src/lsp/index.ts +1 -1
- package/src/lsp/path-trust.ts +69 -14
- package/src/main.ts +78 -35
- package/src/modes/DESIGN.md +54 -0
- package/src/modes/acp/acp-agent.ts +16 -3
- package/src/modes/components/assistant-message.ts +22 -9
- package/src/modes/components/bash-execution.ts +4 -1
- package/src/modes/components/execution-shared.ts +4 -1
- package/src/modes/components/model-selector.ts +166 -33
- package/src/modes/components/session-observer-overlay.ts +1 -1
- package/src/modes/components/settings-selector.ts +1075 -235
- package/src/modes/components/status-line/command.ts +190 -0
- package/src/modes/components/status-line/index.ts +1 -0
- package/src/modes/components/status-line/segments.ts +32 -5
- package/src/modes/components/status-line/types.ts +5 -0
- package/src/modes/components/tool-status-header.ts +396 -22
- package/src/modes/controllers/command-controller.ts +3 -0
- package/src/modes/controllers/event-controller.ts +151 -7
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +6 -3
- package/src/modes/controllers/selector-controller.ts +21 -0
- package/src/modes/interactive-mode.ts +53 -6
- package/src/modes/print-mode.ts +5 -1
- package/src/modes/prompt-action-autocomplete.ts +54 -27
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/types.ts +17 -3
- package/src/modes/utils/ui-helpers.ts +2 -0
- package/src/prompts/system/system-prompt.md +8 -0
- package/src/prompts/tools/search.md +2 -0
- package/src/prompts/tools/subagent.md +1 -0
- package/src/runtime-mcp/client.ts +60 -17
- package/src/runtime-mcp/manager.ts +189 -14
- package/src/runtime-mcp/pool.test.ts +218 -0
- package/src/runtime-mcp/pool.ts +224 -23
- package/src/runtime-mcp/transports/stdio.ts +158 -24
- package/src/runtime-mcp/types.ts +24 -0
- package/src/sdk/broker/broker.ts +124 -32
- package/src/sdk/broker/ensure.ts +248 -41
- package/src/sdk/broker/lifecycle-ledger.ts +7 -17
- package/src/sdk/broker/lifecycle.ts +56 -6
- package/src/sdk/broker/startup-failure.ts +15 -6
- package/src/sdk/bus/chat-daemon-control.ts +3 -1
- package/src/sdk/bus/chat-daemon-runtime.ts +72 -6
- package/src/sdk/bus/chat-effect-journal.ts +132 -7
- package/src/sdk/bus/conversation-store.ts +29 -0
- package/src/sdk/bus/lifecycle-commands.ts +17 -11
- package/src/sdk/bus/slack-conversation.ts +1 -0
- package/src/sdk/bus/slack-daemon.ts +478 -49
- package/src/sdk/bus/slack-live-provider.ts +12 -0
- package/src/sdk/bus/slack-provider.ts +5 -0
- package/src/sdk/bus/telegram-daemon.ts +16 -2
- package/src/sdk/bus/topic-registry.ts +19 -0
- package/src/sdk/cli/master-cli.ts +1 -1
- package/src/sdk/cli/session-cli.ts +11 -8
- package/src/sdk/client/client.ts +66 -17
- package/src/sdk/host/host.ts +29 -9
- package/src/sdk/host/query/revision-store.ts +19 -13
- package/src/sdk/host/reverse-leases.ts +7 -4
- package/src/sdk/host/session-runtime.test.ts +52 -10
- package/src/sdk/host/session-runtime.ts +383 -44
- package/src/sdk/lifecycle/service.ts +4 -0
- package/src/sdk/protocol/operation-inventory.generated.json +77 -0
- package/src/sdk/router/session-router.ts +89 -38
- package/src/sdk/session-api-key.ts +94 -0
- package/src/sdk/session.ts +228 -55
- package/src/sdk/transport/relay.ts +35 -13
- package/src/sdk/transport/serve-cli.ts +3 -3
- package/src/session/agent-session.ts +240 -90
- package/src/session/contribution-prep.ts +214 -21
- package/src/session/fallback-chain-controller.ts +38 -0
- package/src/session/internal/managed-session-storage.ts +19 -5
- package/src/session/messages.ts +5 -0
- package/src/session/session-manager.ts +29 -5
- package/src/session-import/redact.ts +5 -1
- package/src/setup/paseo/announce.ts +456 -0
- package/src/setup/paseo/setup-deps.ts +15 -3
- package/src/skill-state/canonical-skills.ts +12 -0
- package/src/skills/skills.test.ts +41 -21
- package/src/slash-commands/builtin-registry.ts +1 -0
- package/src/system-prompt.ts +29 -3
- package/src/task/executor.ts +7 -1
- package/src/task/index.ts +21 -4
- package/src/tools/bash.ts +63 -19
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +29 -1
- package/src/tools/gh.ts +26 -3
- package/src/tools/github-cache.ts +68 -16
- package/src/tools/index.ts +10 -0
- package/src/tools/job.ts +21 -4
- package/src/tools/monitor.ts +8 -2
- package/src/tools/path-utils.ts +7 -0
- package/src/tools/read.ts +14 -2
- package/src/tools/render-utils.ts +1 -3
- package/src/tools/resource-gc.ts +28 -8
- package/src/tools/search.ts +116 -58
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +37 -2
- package/src/tools/subagent.ts +6 -3
- package/src/tools/tool-catalog.generated.ts +10 -3
- package/src/tools/ultragoal-ask-guard.ts +2 -2
- package/src/web/insane/bridge.ts +113 -23
- package/src/web/search/providers/perplexity.ts +102 -41
package/src/lsp/config.ts
CHANGED
|
@@ -336,14 +336,21 @@ export function resolveCommand(command: string, cwd: string): string | null {
|
|
|
336
336
|
return piUtils.$which(command);
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
/**
|
|
339
|
+
/**
|
|
340
|
+
* Resolve an LSP executable without consulting project-controlled bin directories.
|
|
341
|
+
*
|
|
342
|
+
* Trust is evaluated on both the discovered and canonical paths, but the returned
|
|
343
|
+
* path is the discovered one: multiplexed launchers such as rustup pick the proxied
|
|
344
|
+
* tool from argv[0], so `~/.cargo/bin/rust-analyzer -> rustup` must be spawned as
|
|
345
|
+
* `rust-analyzer`, not as the resolved `rustup` binary.
|
|
346
|
+
*/
|
|
340
347
|
function resolveTrustedLspCommand(command: string, cwd: string): string | null {
|
|
341
348
|
if (!path.isAbsolute(command) && (command.includes("/") || command.includes("\\"))) return null;
|
|
342
349
|
const discovered = path.isAbsolute(command) ? command : piUtils.$which(command);
|
|
343
350
|
if (!discovered) return null;
|
|
344
351
|
if (isProjectControlledPath(discovered, cwd)) return null;
|
|
345
|
-
|
|
346
|
-
return
|
|
352
|
+
if (!canonicalExistingPath(discovered)) return null;
|
|
353
|
+
return discovered;
|
|
347
354
|
}
|
|
348
355
|
|
|
349
356
|
interface ConfigSource {
|
package/src/lsp/index.ts
CHANGED
|
@@ -1160,7 +1160,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1160
1160
|
|
|
1161
1161
|
const serverStatus =
|
|
1162
1162
|
servers.length > 0
|
|
1163
|
-
? `
|
|
1163
|
+
? `Configured language servers: ${servers.join(", ")}`
|
|
1164
1164
|
: "No language servers configured for this project";
|
|
1165
1165
|
|
|
1166
1166
|
const output = lspmuxStatus ? `${serverStatus}\n${lspmuxStatus}` : serverStatus;
|
package/src/lsp/path-trust.ts
CHANGED
|
@@ -59,29 +59,84 @@ function findProjectTrustRoot(start: string, stopPaths: ReadonlySet<string>): st
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
function isProjectMarkerRoot(root: string): boolean {
|
|
63
|
+
return fs.existsSync(path.join(root, ".git")) || isDirectory(path.join(root, CONFIG_DIR_NAME));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Spellings of `candidate` that differ only in how HOME is named: the path as
|
|
68
|
+
* given, plus the same path with any ancestor that is HOME under another name
|
|
69
|
+
* replaced by canonical HOME. The suffix beneath HOME is kept as spelled, and an
|
|
70
|
+
* alias that lives inside a project (`repo/home-link -> HOME`) is project
|
|
71
|
+
* content, not a HOME spelling, so it is never expanded.
|
|
72
|
+
*/
|
|
73
|
+
function candidateSpellings(candidate: string, canonicalHome: string, projectRoot: string | undefined): string[] {
|
|
74
|
+
const resolved = path.resolve(candidate);
|
|
75
|
+
const spellings = [resolved];
|
|
76
|
+
const normalizedHome = normalizePathForComparison(canonicalHome);
|
|
77
|
+
let current = path.dirname(resolved);
|
|
78
|
+
for (;;) {
|
|
79
|
+
if (
|
|
80
|
+
normalizePathForComparison(current) !== normalizedHome &&
|
|
81
|
+
(projectRoot === undefined || !pathIsLexicallyWithin(projectRoot, current)) &&
|
|
82
|
+
normalizePathForComparison(canonicalPath(current)) === normalizedHome
|
|
83
|
+
) {
|
|
84
|
+
spellings.push(path.join(canonicalHome, path.relative(current, resolved)));
|
|
85
|
+
}
|
|
86
|
+
const parent = path.dirname(current);
|
|
87
|
+
if (parent === current) return spellings;
|
|
88
|
+
current = parent;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
62
92
|
export function isProjectControlledPath(candidate: string, cwd: string): boolean {
|
|
63
93
|
const home = os.homedir();
|
|
64
|
-
const
|
|
94
|
+
const canonicalHome = canonicalPath(home);
|
|
95
|
+
const stopPaths = new Set([path.resolve(home), canonicalHome].map(normalizePathForComparison));
|
|
65
96
|
const lexicalTrustRoot = findProjectTrustRoot(cwd, stopPaths);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
97
|
+
// A trust root outside HOME must not claim user executables inside HOME
|
|
98
|
+
// (e.g. ~/.gjc/bin); a root under HOME still owns what it contains. Each check
|
|
99
|
+
// judges home scope on the same view of the path it inspects, without
|
|
100
|
+
// dereferencing the candidate's final component: mixing views let a project
|
|
101
|
+
// link into HOME, or a HOME link into the project, escape rejection. HOME may
|
|
102
|
+
// be spelled lexically or canonically on either side, so the lexical check
|
|
103
|
+
// runs over every HOME-alias spelling of both the candidate and the root.
|
|
104
|
+
if (lexicalTrustRoot !== undefined) {
|
|
105
|
+
const trustRootIsHomeScoped = pathIsWithin(canonicalHome, lexicalTrustRoot);
|
|
106
|
+
// A root under HOME may legitimately be reached through a HOME alias, and a
|
|
107
|
+
// bare fallback root (no project marker) owns nothing on its own; a real
|
|
108
|
+
// project root outside HOME owns every alias directory it contains.
|
|
109
|
+
const aliasBoundary =
|
|
110
|
+
!trustRootIsHomeScoped && isProjectMarkerRoot(lexicalTrustRoot) ? lexicalTrustRoot : undefined;
|
|
111
|
+
const rootSpellings = candidateSpellings(lexicalTrustRoot, canonicalHome, undefined);
|
|
112
|
+
const spellings = candidateSpellings(candidate, canonicalHome, aliasBoundary);
|
|
113
|
+
// Every spelling names the same location, so home scope is a property of
|
|
114
|
+
// the location: one spelling under HOME makes all of them HOME-scoped.
|
|
115
|
+
const candidateIsHomeScoped = spellings.some(
|
|
116
|
+
spelling => pathIsLexicallyWithin(home, spelling) || pathIsLexicallyWithin(canonicalHome, spelling),
|
|
117
|
+
);
|
|
118
|
+
if (
|
|
119
|
+
(!candidateIsHomeScoped || trustRootIsHomeScoped) &&
|
|
120
|
+
spellings.some(spelling => rootSpellings.some(root => pathIsLexicallyWithin(root, spelling)))
|
|
121
|
+
) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
73
125
|
|
|
126
|
+
const canonicalCandidate = canonicalPath(candidate);
|
|
127
|
+
const canonicalCandidateParent = canonicalParentPath(candidate);
|
|
128
|
+
const parentIsHomeScoped = pathIsLexicallyWithin(canonicalHome, canonicalCandidateParent);
|
|
129
|
+
const targetIsHomeScoped = pathIsWithin(canonicalHome, canonicalCandidate);
|
|
74
130
|
const canonicalTrustRoots = new Set<string>();
|
|
75
131
|
if (lexicalTrustRoot !== undefined) canonicalTrustRoots.add(canonicalPath(lexicalTrustRoot));
|
|
76
132
|
const canonicalTrustRoot = findProjectTrustRoot(canonicalPath(cwd), stopPaths);
|
|
77
133
|
if (canonicalTrustRoot !== undefined) canonicalTrustRoots.add(canonicalPath(canonicalTrustRoot));
|
|
78
134
|
for (const trustRoot of canonicalTrustRoots) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
)
|
|
84
|
-
return true;
|
|
135
|
+
const trustRootIsHomeScoped = pathIsLexicallyWithin(canonicalHome, trustRoot);
|
|
136
|
+
const parentOwned =
|
|
137
|
+
pathIsLexicallyWithin(trustRoot, canonicalCandidateParent) && (!parentIsHomeScoped || trustRootIsHomeScoped);
|
|
138
|
+
const targetOwned = pathIsWithin(trustRoot, canonicalCandidate) && (!targetIsHomeScoped || trustRootIsHomeScoped);
|
|
139
|
+
if (parentOwned || targetOwned) return true;
|
|
85
140
|
}
|
|
86
141
|
return false;
|
|
87
142
|
}
|
package/src/main.ts
CHANGED
|
@@ -31,7 +31,15 @@ import { selectSession } from "./cli/session-picker";
|
|
|
31
31
|
import { findConfigFile } from "./config";
|
|
32
32
|
import { activateModelProfile, ModelProfileCredentialError } from "./config/model-profile-activation";
|
|
33
33
|
import { ModelRegistry, ModelsConfigFile } from "./config/model-registry";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
parseModelString,
|
|
36
|
+
refreshMissingQualifiedModelProviders,
|
|
37
|
+
resolveCliModel,
|
|
38
|
+
resolveModelRoleValue,
|
|
39
|
+
resolveModelScope,
|
|
40
|
+
resolveStartupModelRefreshSelectors,
|
|
41
|
+
type ScopedModel,
|
|
42
|
+
} from "./config/model-resolver";
|
|
35
43
|
import { selectorHead } from "./config/model-selector-value";
|
|
36
44
|
import { getDefault, type SettingPath, Settings, settings } from "./config/settings";
|
|
37
45
|
import { resolveMachineLocalUpdateChannel, type UpdateChannel } from "./config/update-channel";
|
|
@@ -49,7 +57,7 @@ import type { PrintModeOptions } from "./modes/print-mode";
|
|
|
49
57
|
import { initTheme, stopThemeWatcher } from "./modes/theme/theme";
|
|
50
58
|
import type { SubmittedUserInput } from "./modes/types";
|
|
51
59
|
import { applyCliRuntimeApiKeyOverride } from "./runtime-api-key";
|
|
52
|
-
import { parseCliCredentialSelector } from "./runtime-credential-selector";
|
|
60
|
+
import { type CliCredentialSelector, parseCliCredentialSelector } from "./runtime-credential-selector";
|
|
53
61
|
import type { MCPManager } from "./runtime-mcp";
|
|
54
62
|
import {
|
|
55
63
|
type CreateAgentSessionOptions,
|
|
@@ -60,6 +68,7 @@ import {
|
|
|
60
68
|
import { processIncarnation } from "./sdk/broker/process-incarnation";
|
|
61
69
|
import { newMasterAttestationEpoch, resolveSessionLocator, SessionIndex } from "./sdk/broker/session-index";
|
|
62
70
|
import type { AgentSession } from "./session/agent-session";
|
|
71
|
+
import { ManagedAppendIdentityMismatchError } from "./session/internal/managed-session-storage";
|
|
63
72
|
import { SessionMigrationBusyError } from "./session/internal/session-open-errors";
|
|
64
73
|
import {
|
|
65
74
|
type ResumeSessionIdentity,
|
|
@@ -543,7 +552,11 @@ export async function applyStartupModelProfiles(args: StartupModelProfileArgs):
|
|
|
543
552
|
|
|
544
553
|
async function exitForStartupModelProfileError(args: StartupModelProfileArgs, error: unknown): Promise<never> {
|
|
545
554
|
const message = error instanceof Error ? error.message : String(error);
|
|
546
|
-
process.stderr.write(
|
|
555
|
+
process.stderr.write(
|
|
556
|
+
error instanceof ManagedAppendIdentityMismatchError
|
|
557
|
+
? `${error.operatorMessage}\n`
|
|
558
|
+
: `${chalk.red(`Error: ${message}`)}\n`,
|
|
559
|
+
);
|
|
547
560
|
await args.session.dispose();
|
|
548
561
|
process.exit(1);
|
|
549
562
|
}
|
|
@@ -1645,6 +1658,66 @@ export async function runRootCommand(
|
|
|
1645
1658
|
)
|
|
1646
1659
|
: undefined;
|
|
1647
1660
|
|
|
1661
|
+
if (parsedArgs.apiKey && parsedArgs.credential) {
|
|
1662
|
+
process.stderr.write(`${chalk.red("--api-key and --credential cannot be used together")}\n`);
|
|
1663
|
+
process.exit(1);
|
|
1664
|
+
}
|
|
1665
|
+
if (parsedArgs.credential && parsedArgs.preferCredential) {
|
|
1666
|
+
process.stderr.write(`${chalk.red("--credential and --prefer-credential cannot be used together")}\n`);
|
|
1667
|
+
process.exit(1);
|
|
1668
|
+
}
|
|
1669
|
+
if (parsedArgs.apiKey && parsedArgs.preferCredential) {
|
|
1670
|
+
process.stderr.write(`${chalk.red("--api-key and --prefer-credential cannot be used together")}\n`);
|
|
1671
|
+
process.exit(1);
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
let credentialSelector: CliCredentialSelector | undefined;
|
|
1675
|
+
let preferredCredentialSelector: CliCredentialSelector | undefined;
|
|
1676
|
+
try {
|
|
1677
|
+
credentialSelector = parsedArgs.credential ? parseCliCredentialSelector(parsedArgs.credential) : undefined;
|
|
1678
|
+
preferredCredentialSelector = parsedArgs.preferCredential
|
|
1679
|
+
? parseCliCredentialSelector(parsedArgs.preferCredential)
|
|
1680
|
+
: undefined;
|
|
1681
|
+
if (preferredCredentialSelector) {
|
|
1682
|
+
const provider =
|
|
1683
|
+
preferredCredentialSelector.provider ??
|
|
1684
|
+
authStorage.resolveRuntimePreferredCredentialSelectorProvider(preferredCredentialSelector.selector);
|
|
1685
|
+
authStorage.setRuntimePreferredCredentialSelector(provider, preferredCredentialSelector.selector);
|
|
1686
|
+
}
|
|
1687
|
+
} catch (error) {
|
|
1688
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1689
|
+
process.stderr.write(`${chalk.red(message)}\n`);
|
|
1690
|
+
process.exit(1);
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
const hasRootStartupProfile = Boolean(settingsInstance.get("modelProfile.default") || parsedArgs.mpreset);
|
|
1694
|
+
const startupModelSelectors = resolveStartupModelRefreshSelectors(
|
|
1695
|
+
{
|
|
1696
|
+
model: parsedArgs.model,
|
|
1697
|
+
provider: parsedArgs.provider,
|
|
1698
|
+
credential: parsedArgs.credential,
|
|
1699
|
+
continue: parsedArgs.continue,
|
|
1700
|
+
resume: parsedArgs.resume,
|
|
1701
|
+
hasStartupProfile: hasRootStartupProfile,
|
|
1702
|
+
},
|
|
1703
|
+
settingsInstance,
|
|
1704
|
+
);
|
|
1705
|
+
const explicitStartupModelSelector = parsedArgs.model
|
|
1706
|
+
? typeof startupModelSelectors === "string"
|
|
1707
|
+
? startupModelSelectors
|
|
1708
|
+
: parsedArgs.model
|
|
1709
|
+
: undefined;
|
|
1710
|
+
if (parsedArgs.apiKey && explicitStartupModelSelector) {
|
|
1711
|
+
const parsedModel = parseModelString(explicitStartupModelSelector);
|
|
1712
|
+
if (parsedModel) authStorage.setRuntimeApiKey(parsedModel.provider, parsedArgs.apiKey);
|
|
1713
|
+
}
|
|
1714
|
+
await logger.time(
|
|
1715
|
+
"refreshStartupModelProviders",
|
|
1716
|
+
refreshMissingQualifiedModelProviders,
|
|
1717
|
+
startupModelSelectors,
|
|
1718
|
+
modelRegistry,
|
|
1719
|
+
);
|
|
1720
|
+
|
|
1648
1721
|
let scopedModels: ScopedModel[] = [];
|
|
1649
1722
|
const modelPatterns = parsedArgs.models ?? settingsInstance.get("enabledModels");
|
|
1650
1723
|
const modelMatchPreferences = {
|
|
@@ -1779,7 +1852,6 @@ export async function runRootCommand(
|
|
|
1779
1852
|
if (isInteractive && sessionOptions.mcpConfigPath) {
|
|
1780
1853
|
sessionOptions.deferMcpConfigStartup = true;
|
|
1781
1854
|
}
|
|
1782
|
-
const hasRootStartupProfile = Boolean(settingsInstance.get("modelProfile.default") || parsedArgs.mpreset);
|
|
1783
1855
|
// ACP is not carved out: `gjc acp` is broker-backed and never builds a local
|
|
1784
1856
|
// session here, and the broker-launched lifecycle child defers memory startup
|
|
1785
1857
|
// unconditionally (createLifecycleAgentSession) so readiness never waits on
|
|
@@ -1790,37 +1862,8 @@ export async function runRootCommand(
|
|
|
1790
1862
|
const acpStartupOptions = mode === "acp" ? resolveAcpStartupOptions(parsedArgs, sessionOptions) : undefined;
|
|
1791
1863
|
|
|
1792
1864
|
// Handle CLI credential selection and --api-key as runtime overrides (not persisted).
|
|
1793
|
-
if (
|
|
1794
|
-
|
|
1795
|
-
process.exit(1);
|
|
1796
|
-
}
|
|
1797
|
-
if (parsedArgs.credential && parsedArgs.preferCredential) {
|
|
1798
|
-
process.stderr.write(`${chalk.red("--credential and --prefer-credential cannot be used together")}\n`);
|
|
1799
|
-
process.exit(1);
|
|
1800
|
-
}
|
|
1801
|
-
if (parsedArgs.apiKey && parsedArgs.preferCredential) {
|
|
1802
|
-
process.stderr.write(`${chalk.red("--api-key and --prefer-credential cannot be used together")}\n`);
|
|
1803
|
-
process.exit(1);
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
if (parsedArgs.credential) {
|
|
1807
|
-
try {
|
|
1808
|
-
sessionOptions.credentialSelector = parseCliCredentialSelector(parsedArgs.credential);
|
|
1809
|
-
} catch (error) {
|
|
1810
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1811
|
-
process.stderr.write(`${chalk.red(message)}\n`);
|
|
1812
|
-
process.exit(1);
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
if (parsedArgs.preferCredential) {
|
|
1816
|
-
try {
|
|
1817
|
-
sessionOptions.preferredCredentialSelector = parseCliCredentialSelector(parsedArgs.preferCredential);
|
|
1818
|
-
} catch (error) {
|
|
1819
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1820
|
-
process.stderr.write(`${chalk.red(message)}\n`);
|
|
1821
|
-
process.exit(1);
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1865
|
+
if (credentialSelector) sessionOptions.credentialSelector = credentialSelector;
|
|
1866
|
+
if (preferredCredentialSelector) sessionOptions.preferredCredentialSelector = preferredCredentialSelector;
|
|
1824
1867
|
if (parsedArgs.apiKey) {
|
|
1825
1868
|
if (!sessionOptions.model && !sessionOptions.modelPattern) {
|
|
1826
1869
|
process.stderr.write(
|
package/src/modes/DESIGN.md
CHANGED
|
@@ -347,6 +347,60 @@ CJK/Latin todo, IRC, status, and BTW text break at semantic phrase boundaries,
|
|
|
347
347
|
not inside a phase/action/status label, short identifier, or grapheme cluster.
|
|
348
348
|
Those defects, width overflow, overlap, hidden focus/cursor, and lost anchors
|
|
349
349
|
are blocking visual-QA failures.
|
|
350
|
+
|
|
351
|
+
### Status Line Custom Editor simulation
|
|
352
|
+
|
|
353
|
+
The Appearance tab's Status Line Custom Editor is a spatial editor, not a
|
|
354
|
+
generic setting row list. It renders a simulated statusbar with left/right
|
|
355
|
+
segment areas, a hidden-segment palette, visible Separator and segment-option
|
|
356
|
+
controls, and Confirm/Exit actions. It must not render legacy `Segment`,
|
|
357
|
+
`Move left`, or `Move right` rows.
|
|
358
|
+
|
|
359
|
+
The editor owns transient state locally: focused statusbar slot, focused palette
|
|
360
|
+
item, focused root control, open visible-choice panel, picked segment, floating
|
|
361
|
+
ghost, empty origin slot, and overflow warning. Durable settings remain only the
|
|
362
|
+
existing `statusLine.leftSegments`, `statusLine.rightSegments`,
|
|
363
|
+
`statusLine.separator`, and `statusLine.segmentOptions` keys; Confirm persists
|
|
364
|
+
through the existing settings guard, while Exit or root-state Escape restores
|
|
365
|
+
the previous preview and closes.
|
|
366
|
+
|
|
367
|
+
Focus variants are `statusbar`, `palette`, `separator-control`,
|
|
368
|
+
`option-control(path)`, `choice(target, focusedValue, returnFocus)`, `confirm`,
|
|
369
|
+
and `exit`. Left/Right is owned by the editor while active and never switches
|
|
370
|
+
settings tabs. Enter selects/deselects, picks/drops, applies a focused choice,
|
|
371
|
+
or activates Confirm/Exit according to focus. Up/Down traverses palette,
|
|
372
|
+
visible root controls, actions, and choice candidates. Escape inside `choice`
|
|
373
|
+
returns to the opener without mutation; Escape everywhere else cancels and
|
|
374
|
+
restores. Delete only hides a focused visible statusbar segment.
|
|
375
|
+
|
|
376
|
+
Separator and every segment option are visible-choice controls, never cycle-only
|
|
377
|
+
rows. Descriptor data owns labels, allowed values, visible current-value
|
|
378
|
+
formatting, and typed draft application. Separator choices are Powerline, Thin
|
|
379
|
+
chevron, Slash, Pipe, Block, None, and ASCII. Boolean options show `true` and
|
|
380
|
+
`false`; enum options show their union values; `path.maxLength` shows `16`,
|
|
381
|
+
`24`, `32`, `40`, `50`, `60`, and `80`.
|
|
382
|
+
|
|
383
|
+
At constrained width the simulated statusbar renders as two rows and emits an
|
|
384
|
+
explicit overflow/wrap warning. ANSI-aware cell measurement remains mandatory;
|
|
385
|
+
ASCII/no-color output must retain textual focus, ghost/origin, selected choice,
|
|
386
|
+
and warning affordances.
|
|
387
|
+
|
|
388
|
+
Deterministic evidence belongs to
|
|
389
|
+
`test/fixtures/tui/status-line-custom-editor-showcase.ts`, captured by
|
|
390
|
+
`scripts/capture-status-line-custom-editor-showcase.ts --out
|
|
391
|
+
.gjc/qa/status-line-custom-editor-<run>` and verified by
|
|
392
|
+
`scripts/verify-status-line-custom-editor-showcase.ts --root
|
|
393
|
+
.gjc/qa/status-line-custom-editor-<run> --require-independent-review`. The
|
|
394
|
+
canonical states are `root-statusbar`, `picked-origin-slot`,
|
|
395
|
+
`palette-exact-insert`, `separator-control`, `separator-choice-focused`,
|
|
396
|
+
`separator-choice-applied`, `option-boolean-choice-focused`,
|
|
397
|
+
`option-enum-choice-focused`, `option-numeric-choice-focused`,
|
|
398
|
+
`option-choice-applied`, `exit-restored`, `confirm-persisted`,
|
|
399
|
+
`overflow-two-row-warning`, and `narrow-cjk`. Each state records
|
|
400
|
+
`terminal.txt`, `terminal-ansi.txt`, `terminal.html`, and `metadata.json`, plus
|
|
401
|
+
the root manifest. Missing state witnesses, ANSI/plain mismatch, invalid
|
|
402
|
+
metadata, and absent independent review are blocking visual-QA failures.
|
|
403
|
+
|
|
350
404
|
### Sticky viewport deterministic visual QA
|
|
351
405
|
|
|
352
406
|
`test/fixtures/tui/sticky-viewport-showcase.ts` is a fixed-clock, no-network,
|
|
@@ -101,6 +101,16 @@ const MAX_PROMPT_FRAME_BYTES = 256 * 1024;
|
|
|
101
101
|
* envelope. A canonical-length UUID keeps the measurement equal to the real frame.
|
|
102
102
|
*/
|
|
103
103
|
const PROMPT_FRAME_ID_PLACEHOLDER = "00000000-0000-4000-8000-000000000000";
|
|
104
|
+
/**
|
|
105
|
+
* Readiness budget every ACP session launch requests, independent of MCP.
|
|
106
|
+
*
|
|
107
|
+
* Host cold start costs the same whether or not the client declared MCP servers,
|
|
108
|
+
* so leaving the MCP-less path on the broker's 10s default made it the only ACP
|
|
109
|
+
* launch that fails under ordinary concurrency: a second host starting beside the
|
|
110
|
+
* first measurably crosses that deadline, and the broker then reports the launch
|
|
111
|
+
* as `terminal_uncertain`, which an external client surfaces as a dead provider.
|
|
112
|
+
*/
|
|
113
|
+
const ACP_SESSION_READINESS_TIMEOUT_MS = ACP_MCP_LIFECYCLE_TIMEOUT_MS;
|
|
104
114
|
|
|
105
115
|
type JsonObject = Record<string, unknown>;
|
|
106
116
|
interface PromptWaiter {
|
|
@@ -1362,7 +1372,8 @@ export class AcpAgent implements Agent {
|
|
|
1362
1372
|
cwd: params.cwd,
|
|
1363
1373
|
target: { path: params.cwd },
|
|
1364
1374
|
...(this.#startupOptions?.modelPreset ? { modelPreset: this.#startupOptions.modelPreset } : {}),
|
|
1365
|
-
|
|
1375
|
+
readinessTimeoutMs: ACP_SESSION_READINESS_TIMEOUT_MS,
|
|
1376
|
+
...(mcpServers.length > 0 ? { mcpServers } : {}),
|
|
1366
1377
|
},
|
|
1367
1378
|
randomUUID(),
|
|
1368
1379
|
mcpServers,
|
|
@@ -1418,7 +1429,8 @@ export class AcpAgent implements Agent {
|
|
|
1418
1429
|
sourceSessionId: params.sessionId,
|
|
1419
1430
|
sourceSessionPath: source,
|
|
1420
1431
|
target: { path: params.cwd },
|
|
1421
|
-
|
|
1432
|
+
readinessTimeoutMs: ACP_SESSION_READINESS_TIMEOUT_MS,
|
|
1433
|
+
...(mcpServers.length > 0 ? { mcpServers } : {}),
|
|
1422
1434
|
},
|
|
1423
1435
|
randomUUID(),
|
|
1424
1436
|
mcpServers,
|
|
@@ -2268,7 +2280,8 @@ export class AcpAgent implements Agent {
|
|
|
2268
2280
|
sessionId: id,
|
|
2269
2281
|
sessionPath: saved,
|
|
2270
2282
|
target: { path: cwd },
|
|
2271
|
-
|
|
2283
|
+
readinessTimeoutMs: ACP_SESSION_READINESS_TIMEOUT_MS,
|
|
2284
|
+
...(mcpServers.length > 0 ? { mcpServers } : {}),
|
|
2272
2285
|
},
|
|
2273
2286
|
randomUUID(),
|
|
2274
2287
|
mcpServers,
|
|
@@ -106,16 +106,24 @@ export class AssistantMessageComponent extends Container {
|
|
|
106
106
|
#toolImagesRevision = 0;
|
|
107
107
|
#convertedToolImagesRevision = 0;
|
|
108
108
|
#disposed = false;
|
|
109
|
+
// Cached Markdown children can outlive their attachment to this assistant.
|
|
110
|
+
// Session/live-parent validity belongs to the owner, not the paint lifetime.
|
|
111
|
+
readonly #requestRepaint?: () => void;
|
|
109
112
|
constructor(
|
|
110
113
|
message?: AssistantMessage,
|
|
111
114
|
private hideThinkingBlock = false,
|
|
112
|
-
|
|
115
|
+
onImageUpdate?: () => void,
|
|
113
116
|
viewportAnchorId?: string,
|
|
114
117
|
private readonly onVisibleMutation?: () => void,
|
|
115
118
|
private readonly hintSession?: ProviderSafetyStopHintSession,
|
|
116
119
|
) {
|
|
117
120
|
super();
|
|
118
121
|
this.#viewportAnchorId = viewportAnchorId;
|
|
122
|
+
if (onImageUpdate) {
|
|
123
|
+
this.#requestRepaint = () => {
|
|
124
|
+
if (!this.#disposed) onImageUpdate();
|
|
125
|
+
};
|
|
126
|
+
}
|
|
119
127
|
|
|
120
128
|
this.#contentContainer = new Container();
|
|
121
129
|
this.addChild(this.#contentContainer);
|
|
@@ -172,6 +180,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
172
180
|
}
|
|
173
181
|
|
|
174
182
|
#notifyVisibleMutation(): void {
|
|
183
|
+
if (this.#disposed) return;
|
|
175
184
|
const after = this.#visibleContentSignature();
|
|
176
185
|
if (after === this.#visibleSignature) return;
|
|
177
186
|
this.#visibleSignature = after;
|
|
@@ -186,6 +195,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
186
195
|
}
|
|
187
196
|
|
|
188
197
|
override invalidate(): void {
|
|
198
|
+
if (this.#disposed) return;
|
|
189
199
|
super.invalidate();
|
|
190
200
|
if (this.#lastMessage) {
|
|
191
201
|
this.updateContent(this.#lastMessage, { streaming: this.#lastStreaming });
|
|
@@ -193,13 +203,14 @@ export class AssistantMessageComponent extends Container {
|
|
|
193
203
|
}
|
|
194
204
|
|
|
195
205
|
setHideThinkingBlock(hide: boolean): void {
|
|
206
|
+
if (this.#disposed) return;
|
|
196
207
|
if (this.hideThinkingBlock === hide) return;
|
|
197
208
|
this.hideThinkingBlock = hide;
|
|
198
209
|
this.#notifyVisibleMutation();
|
|
199
210
|
}
|
|
200
211
|
|
|
201
212
|
setToolResultImages(toolCallId: string, images: ImageContent[]): void {
|
|
202
|
-
if (!toolCallId) return;
|
|
213
|
+
if (this.#disposed || !toolCallId) return;
|
|
203
214
|
const validImages = images.filter(img => img.type === "image" && img.data && img.mimeType);
|
|
204
215
|
const previousImages = this.#toolImagesByCallId.get(toolCallId);
|
|
205
216
|
if (
|
|
@@ -268,7 +279,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
268
279
|
this.#convertedKittyImages.set(key, { type: "image", data, mimeType: "image/png", source });
|
|
269
280
|
this.#convertedToolImagesRevision += 1;
|
|
270
281
|
if (this.#lastMessage) this.updateContent(this.#lastMessage, { streaming: this.#lastStreaming });
|
|
271
|
-
this
|
|
282
|
+
this.#requestRepaint?.();
|
|
272
283
|
})
|
|
273
284
|
.catch(() => {
|
|
274
285
|
if (
|
|
@@ -283,6 +294,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
283
294
|
}
|
|
284
295
|
|
|
285
296
|
setUsageInfo(usage: Usage): void {
|
|
297
|
+
if (this.#disposed) return;
|
|
286
298
|
this.#usageInfo = usage;
|
|
287
299
|
if (this.#lastMessage) {
|
|
288
300
|
this.updateContent(this.#lastMessage, { streaming: this.#lastStreaming });
|
|
@@ -311,7 +323,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
311
323
|
const cached = this.#contentBlocksCache.get(content);
|
|
312
324
|
if (cached?.source === content.text) {
|
|
313
325
|
if (cached.component instanceof Markdown) {
|
|
314
|
-
cached.component.setOnStaleThrottle(this
|
|
326
|
+
cached.component.setOnStaleThrottle(this.#requestRepaint);
|
|
315
327
|
cached.component.setStreaming(streaming);
|
|
316
328
|
}
|
|
317
329
|
return cached.component;
|
|
@@ -322,14 +334,14 @@ export class AssistantMessageComponent extends Container {
|
|
|
322
334
|
// instead of constructing a new one each chunk; combined with the markdown
|
|
323
335
|
// per-code-block highlight cache, appends no longer re-highlight the whole prefix.
|
|
324
336
|
if (!deepInterview && cached && cached.component instanceof Markdown) {
|
|
325
|
-
cached.component.setOnStaleThrottle(this
|
|
337
|
+
cached.component.setOnStaleThrottle(this.#requestRepaint);
|
|
326
338
|
cached.component.setText(trimmed, { streaming });
|
|
327
339
|
cached.source = content.text;
|
|
328
340
|
return cached.component;
|
|
329
341
|
}
|
|
330
342
|
const component = deepInterview ?? new Markdown(trimmed, 1, 0, getMarkdownTheme());
|
|
331
343
|
if (component instanceof Markdown) {
|
|
332
|
-
component.setOnStaleThrottle(this
|
|
344
|
+
component.setOnStaleThrottle(this.#requestRepaint);
|
|
333
345
|
component.setStreaming(streaming);
|
|
334
346
|
}
|
|
335
347
|
this.#contentBlocksCache.set(content, { source: content.text, component });
|
|
@@ -341,14 +353,14 @@ export class AssistantMessageComponent extends Container {
|
|
|
341
353
|
const cached = this.#contentBlocksCache.get(content);
|
|
342
354
|
if (cached?.source === content.thinking) {
|
|
343
355
|
if (cached.component instanceof Markdown) {
|
|
344
|
-
cached.component.setOnStaleThrottle(this
|
|
356
|
+
cached.component.setOnStaleThrottle(this.#requestRepaint);
|
|
345
357
|
cached.component.setStreaming(streaming);
|
|
346
358
|
}
|
|
347
359
|
return cached.component as Markdown;
|
|
348
360
|
}
|
|
349
361
|
const trimmed = elideRunawayThinkingRepetition(content.thinking.trim());
|
|
350
362
|
if (cached?.component instanceof Markdown) {
|
|
351
|
-
cached.component.setOnStaleThrottle(this
|
|
363
|
+
cached.component.setOnStaleThrottle(this.#requestRepaint);
|
|
352
364
|
cached.component.setText(trimmed, { streaming });
|
|
353
365
|
cached.source = content.thinking;
|
|
354
366
|
return cached.component;
|
|
@@ -357,7 +369,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
357
369
|
color: (text: string) => theme.fg("thinkingText", text),
|
|
358
370
|
italic: true,
|
|
359
371
|
});
|
|
360
|
-
component.setOnStaleThrottle(this
|
|
372
|
+
component.setOnStaleThrottle(this.#requestRepaint);
|
|
361
373
|
component.setStreaming(streaming);
|
|
362
374
|
this.#contentBlocksCache.set(content, { source: content.thinking, component });
|
|
363
375
|
this.#reusableChildren.add(component);
|
|
@@ -437,6 +449,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
437
449
|
}
|
|
438
450
|
|
|
439
451
|
updateContent(message: AssistantMessage, options?: AssistantMessageUpdateOptions): void {
|
|
452
|
+
if (this.#disposed) return;
|
|
440
453
|
this.#lastMessage = message;
|
|
441
454
|
this.#lastStreaming = options?.streaming ?? false;
|
|
442
455
|
|
|
@@ -43,6 +43,7 @@ export class BashExecutionComponent extends Container {
|
|
|
43
43
|
#outputLines: string[] = [];
|
|
44
44
|
#status: ExecutionStatus = "running";
|
|
45
45
|
#exitCode: number | undefined = undefined;
|
|
46
|
+
#signal: string | undefined = undefined;
|
|
46
47
|
#loader: Loader;
|
|
47
48
|
#truncation?: TruncationMeta;
|
|
48
49
|
#expanded = false;
|
|
@@ -127,9 +128,10 @@ export class BashExecutionComponent extends Container {
|
|
|
127
128
|
setComplete(
|
|
128
129
|
exitCode: number | undefined,
|
|
129
130
|
cancelled: boolean,
|
|
130
|
-
options?: { output?: string; truncation?: TruncationMeta },
|
|
131
|
+
options?: { output?: string; signal?: string; truncation?: TruncationMeta },
|
|
131
132
|
): void {
|
|
132
133
|
this.#exitCode = exitCode;
|
|
134
|
+
this.#signal = options?.signal;
|
|
133
135
|
this.#status = resolveExecutionStatus(exitCode, cancelled);
|
|
134
136
|
this.#truncation = options?.truncation;
|
|
135
137
|
if (options?.output !== undefined) {
|
|
@@ -215,6 +217,7 @@ export class BashExecutionComponent extends Container {
|
|
|
215
217
|
const footer = buildStatusFooter({
|
|
216
218
|
status: this.#status,
|
|
217
219
|
exitCode: this.#exitCode,
|
|
220
|
+
signal: this.#signal,
|
|
218
221
|
truncation: this.#truncation,
|
|
219
222
|
hiddenLineCount,
|
|
220
223
|
suppressHiddenCount: hasSixelOutput && !fallbackActive,
|
|
@@ -68,6 +68,7 @@ export function createCollapsedPreview(previewText: string, previewLines: number
|
|
|
68
68
|
export function buildStatusFooter(opts: {
|
|
69
69
|
status: ExecutionStatus;
|
|
70
70
|
exitCode: number | undefined;
|
|
71
|
+
signal?: string;
|
|
71
72
|
truncation: TruncationMeta | undefined;
|
|
72
73
|
hiddenLineCount: number;
|
|
73
74
|
/** Suppress the "… N more lines" hint (used when sixel passthrough renders the full output). */
|
|
@@ -81,7 +82,9 @@ export function buildStatusFooter(opts: {
|
|
|
81
82
|
if (opts.status === "cancelled") {
|
|
82
83
|
parts.push(theme.fg("warning", "(cancelled)"));
|
|
83
84
|
} else if (opts.status === "error") {
|
|
84
|
-
parts.push(
|
|
85
|
+
parts.push(
|
|
86
|
+
theme.fg("error", opts.signal ? `(${opts.signal}, exit ${opts.exitCode})` : `(exit ${opts.exitCode})`),
|
|
87
|
+
);
|
|
85
88
|
}
|
|
86
89
|
if (opts.truncation) {
|
|
87
90
|
parts.push(theme.fg("warning", formatTruncationMetaNotice(opts.truncation)));
|