@gajae-code/coding-agent 0.16.4 → 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 +54 -0
- package/dist/types/capability/types.d.ts +4 -0
- package/dist/types/cli/customize-doctor.d.ts +22 -0
- package/dist/types/config/file-lock.d.ts +31 -3
- 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/helpers.d.ts +33 -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/types.d.ts +25 -0
- package/dist/types/extensibility/hooks/types.d.ts +4 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +4 -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 +12 -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/bash-execution.d.ts +1 -0
- package/dist/types/modes/components/execution-shared.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/types.d.ts +6 -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-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/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 +4 -2
- 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.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +2 -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 +4 -3
- package/dist/types/skill-state/canonical-skills.d.ts +4 -0
- package/dist/types/system-prompt.d.ts +8 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/github-cache.d.ts +2 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/package.json +9 -7
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/src/capability/index.ts +14 -2
- package/src/capability/types.ts +4 -0
- package/src/cli/customize-doctor.ts +155 -12
- package/src/cli.ts +22 -0
- package/src/commands/sdk.ts +74 -31
- package/src/config/file-lock-gc.ts +40 -2
- package/src/config/file-lock.ts +198 -14
- package/src/config/model-preset-registry.ts +115 -16
- package/src/config/model-registry.ts +16 -12
- 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/skills/ultragoal/SKILL.md +22 -0
- package/src/discovery/builtin.ts +50 -62
- package/src/discovery/helpers.ts +297 -32
- 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 +20 -2
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +321 -23
- package/src/extensibility/gjc-plugins/registry.ts +113 -1
- 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 +80 -47
- package/src/extensibility/skill-management.ts +200 -52
- package/src/extensibility/skills.ts +29 -9
- 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/session-state-lock.ts +51 -8
- 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 +6 -6
- 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 +6 -1
- package/src/modes/acp/acp-agent.ts +16 -3
- package/src/modes/components/bash-execution.ts +4 -1
- package/src/modes/components/execution-shared.ts +4 -1
- package/src/modes/components/session-observer-overlay.ts +1 -1
- package/src/modes/components/tool-status-header.ts +3 -1
- package/src/modes/controllers/command-controller.ts +1 -0
- package/src/modes/controllers/event-controller.ts +3 -2
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +6 -3
- package/src/modes/interactive-mode.ts +6 -5
- 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 +16 -3
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/tools/subagent.md +1 -0
- package/src/runtime-mcp/manager.ts +20 -2
- package/src/runtime-mcp/pool.test.ts +20 -11
- package/src/sdk/broker/broker.ts +119 -28
- 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 +35 -5
- 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 +387 -48
- 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 +2 -2
- 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 +50 -8
- package/src/sdk/host/session-runtime.ts +158 -51
- package/src/sdk/lifecycle/service.ts +4 -0
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/sdk/router/session-router.ts +89 -38
- package/src/sdk/session.ts +155 -26
- package/src/sdk/transport/relay.ts +35 -13
- package/src/session/agent-session.ts +117 -52
- 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 +49 -43
- package/src/setup/paseo/setup-deps.ts +1 -1
- package/src/skill-state/canonical-skills.ts +12 -0
- package/src/skills/skills.test.ts +41 -21
- package/src/system-prompt.ts +25 -3
- package/src/task/executor.ts +5 -0
- package/src/task/index.ts +4 -0
- package/src/tools/bash.ts +28 -11
- package/src/tools/gh.ts +26 -3
- package/src/tools/github-cache.ts +68 -16
- package/src/tools/index.ts +2 -0
- package/src/tools/job.ts +13 -2
- 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/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +15 -0
- package/src/tools/subagent.ts +3 -2
- package/src/tools/tool-catalog.generated.ts +2 -2
- package/src/tools/ultragoal-ask-guard.ts +2 -2
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
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
import { processIncarnation } from "./sdk/broker/process-incarnation";
|
|
69
69
|
import { newMasterAttestationEpoch, resolveSessionLocator, SessionIndex } from "./sdk/broker/session-index";
|
|
70
70
|
import type { AgentSession } from "./session/agent-session";
|
|
71
|
+
import { ManagedAppendIdentityMismatchError } from "./session/internal/managed-session-storage";
|
|
71
72
|
import { SessionMigrationBusyError } from "./session/internal/session-open-errors";
|
|
72
73
|
import {
|
|
73
74
|
type ResumeSessionIdentity,
|
|
@@ -551,7 +552,11 @@ export async function applyStartupModelProfiles(args: StartupModelProfileArgs):
|
|
|
551
552
|
|
|
552
553
|
async function exitForStartupModelProfileError(args: StartupModelProfileArgs, error: unknown): Promise<never> {
|
|
553
554
|
const message = error instanceof Error ? error.message : String(error);
|
|
554
|
-
process.stderr.write(
|
|
555
|
+
process.stderr.write(
|
|
556
|
+
error instanceof ManagedAppendIdentityMismatchError
|
|
557
|
+
? `${error.operatorMessage}\n`
|
|
558
|
+
: `${chalk.red(`Error: ${message}`)}\n`,
|
|
559
|
+
);
|
|
555
560
|
await args.session.dispose();
|
|
556
561
|
process.exit(1);
|
|
557
562
|
}
|
|
@@ -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,
|
|
@@ -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)));
|
|
@@ -272,7 +272,7 @@ export class SessionObserverOverlayComponent extends TranscriptViewerOverlay {
|
|
|
272
272
|
this.#cache = {
|
|
273
273
|
...cache,
|
|
274
274
|
completeOffset: cache.completeOffset + complete.bytes.length,
|
|
275
|
-
prefixDigest:
|
|
275
|
+
prefixDigest: crypto.createHash("sha256").update(snapshot.prefix).update(complete.bytes).digest("hex"),
|
|
276
276
|
entries,
|
|
277
277
|
model: parsed.model ?? cache.model,
|
|
278
278
|
...projection.state,
|
|
@@ -854,6 +854,8 @@ export class StatusLineComponent implements Component {
|
|
|
854
854
|
// treats a null value as hidden, so gating here is behavior-identical.
|
|
855
855
|
const prSegmentActive =
|
|
856
856
|
effectiveSettings.leftSegments.includes("pr") || effectiveSettings.rightSegments.includes("pr");
|
|
857
|
+
const gitSegmentActive =
|
|
858
|
+
effectiveSettings.leftSegments.includes("git") || effectiveSettings.rightSegments.includes("git");
|
|
857
859
|
const commandSegmentActive =
|
|
858
860
|
effectiveSettings.leftSegments.includes("command") || effectiveSettings.rightSegments.includes("command");
|
|
859
861
|
const commandOptions = commandSegmentActive ? this.#trustedCommandOptions() : undefined;
|
|
@@ -883,7 +885,7 @@ export class StatusLineComponent implements Component {
|
|
|
883
885
|
sessionStartTime: this.#sessionStartTime,
|
|
884
886
|
git: {
|
|
885
887
|
branch: this.#getCurrentBranch(),
|
|
886
|
-
status: this.#getGitStatus(),
|
|
888
|
+
status: gitSegmentActive ? this.#getGitStatus() : null,
|
|
887
889
|
pr: prSegmentActive ? (previewOnly ? (this.#cachedPr ?? null) : this.#lookupPr()) : null,
|
|
888
890
|
},
|
|
889
891
|
usage: this.#cachedUsage,
|
|
@@ -1233,6 +1233,7 @@ export class CommandController {
|
|
|
1233
1233
|
const meta = outputMeta().truncationFromSummary(result, { direction: "tail" }).get();
|
|
1234
1234
|
this.ctx.bashComponent.setComplete(result.exitCode, result.cancelled, {
|
|
1235
1235
|
output: result.output,
|
|
1236
|
+
signal: result.signal,
|
|
1236
1237
|
truncation: meta?.truncation,
|
|
1237
1238
|
});
|
|
1238
1239
|
}
|
|
@@ -16,6 +16,7 @@ import { ToolExecutionComponent, type ToolExecutionHandle } from "../../modes/co
|
|
|
16
16
|
import { TtsrNotificationComponent } from "../../modes/components/ttsr-notification";
|
|
17
17
|
import { getSymbolTheme, theme } from "../../modes/theme/theme";
|
|
18
18
|
import {
|
|
19
|
+
clearInteractiveActivityIndicator,
|
|
19
20
|
type InteractiveModeContext,
|
|
20
21
|
stopInteractiveActivityIndicator,
|
|
21
22
|
syncInteractiveActivityIndicator,
|
|
@@ -1104,7 +1105,7 @@ export class EventController {
|
|
|
1104
1105
|
this.ctx.editor.onEscape = () => {
|
|
1105
1106
|
this.ctx.session.abortCompaction();
|
|
1106
1107
|
};
|
|
1107
|
-
|
|
1108
|
+
clearInteractiveActivityIndicator(this.ctx);
|
|
1108
1109
|
const reasonText =
|
|
1109
1110
|
event.reason === "overflow" ? "Context overflow detected, " : event.reason === "idle" ? "Idle " : "";
|
|
1110
1111
|
const actionLabel = event.action === "handoff" ? "Auto-handoff" : "Auto context-full maintenance";
|
|
@@ -1205,7 +1206,7 @@ export class EventController {
|
|
|
1205
1206
|
this.ctx.session.abortRetry();
|
|
1206
1207
|
}
|
|
1207
1208
|
};
|
|
1208
|
-
|
|
1209
|
+
clearInteractiveActivityIndicator(this.ctx);
|
|
1209
1210
|
// Stop any prior retry loader/timer before installing a new one.
|
|
1210
1211
|
this.ctx.retryLoader?.stop();
|
|
1211
1212
|
this.#clearRetryCountdown();
|
|
@@ -292,6 +292,7 @@ export class ExtensionUiController {
|
|
|
292
292
|
const result = await session.executeBash(input.cmd, undefined, { excludeFromContext: true });
|
|
293
293
|
return {
|
|
294
294
|
exitCode: result.exitCode,
|
|
295
|
+
signal: result.signal,
|
|
295
296
|
cancelled: result.cancelled,
|
|
296
297
|
output: result.output,
|
|
297
298
|
truncated: result.truncated,
|
|
@@ -273,16 +273,19 @@ export class InputController {
|
|
|
273
273
|
|
|
274
274
|
#syncTranscriptTurnPosition(): readonly string[] {
|
|
275
275
|
const anchorIds = getUserMessageViewportAnchorIds(this.ctx.session.messages);
|
|
276
|
+
const previousAnchorIds = new Set(this.#transcriptTurnAnchorIds);
|
|
276
277
|
if (
|
|
277
278
|
anchorIds.length !== this.#transcriptTurnAnchorIds.length ||
|
|
278
|
-
anchorIds.some(id => !
|
|
279
|
+
anchorIds.some(id => !previousAnchorIds.has(id))
|
|
279
280
|
) {
|
|
280
281
|
const hadAnchors = this.#transcriptTurnAnchorIds.length > 0;
|
|
281
282
|
const currentAnchorId = this.#transcriptTurnAnchorIds[this.#transcriptTurnPosition];
|
|
282
|
-
const
|
|
283
|
+
const incomingAnchorIds = new Set(anchorIds);
|
|
284
|
+
const retainedAnchorIds = this.#transcriptTurnAnchorIds.filter(id => incomingAnchorIds.has(id));
|
|
285
|
+
const retainedAnchorIdSet = new Set(retainedAnchorIds);
|
|
283
286
|
this.#transcriptTurnAnchorIds = [
|
|
284
287
|
...retainedAnchorIds,
|
|
285
|
-
...anchorIds.filter(id => !
|
|
288
|
+
...anchorIds.filter(id => !retainedAnchorIdSet.has(id)),
|
|
286
289
|
];
|
|
287
290
|
const currentPosition = currentAnchorId ? this.#transcriptTurnAnchorIds.indexOf(currentAnchorId) : -1;
|
|
288
291
|
this.#transcriptTurnPosition =
|
|
@@ -1700,7 +1700,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1700
1700
|
}
|
|
1701
1701
|
}
|
|
1702
1702
|
this.#stopListeners.clear();
|
|
1703
|
-
this
|
|
1703
|
+
this.clearLoadingAnimation();
|
|
1704
1704
|
this.#suspendedActivityIndicator = undefined;
|
|
1705
1705
|
this.#petProtocolUnsubscribe?.();
|
|
1706
1706
|
this.#petProtocolUnsubscribe = undefined;
|
|
@@ -1996,7 +1996,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1996
1996
|
return tallyBackgroundActivity(this.session.getAsyncJobSnapshot()?.running ?? []);
|
|
1997
1997
|
}
|
|
1998
1998
|
|
|
1999
|
-
|
|
1999
|
+
/** Clear the status display without settling the foreground run during maintenance/retry. */
|
|
2000
|
+
clearLoadingAnimation(): void {
|
|
2000
2001
|
this.loadingAnimation?.stop();
|
|
2001
2002
|
this.loadingAnimation = undefined;
|
|
2002
2003
|
this.statusContainer.clear();
|
|
@@ -2007,7 +2008,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2007
2008
|
return;
|
|
2008
2009
|
const foregroundActive = this.#foregroundActivity || (!this.#foregroundTurnSettled && this.session.isStreaming);
|
|
2009
2010
|
if (!this.isInitialized && !foregroundActive) {
|
|
2010
|
-
this
|
|
2011
|
+
this.clearLoadingAnimation();
|
|
2011
2012
|
return;
|
|
2012
2013
|
}
|
|
2013
2014
|
const message = resolveActivityIndicatorMessage(
|
|
@@ -2016,7 +2017,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2016
2017
|
this.#pendingWorkingMessage ?? this.#defaultWorkingMessage,
|
|
2017
2018
|
);
|
|
2018
2019
|
if (!message) {
|
|
2019
|
-
this
|
|
2020
|
+
this.clearLoadingAnimation();
|
|
2020
2021
|
return;
|
|
2021
2022
|
}
|
|
2022
2023
|
if (!this.loadingAnimation) {
|
|
@@ -2047,7 +2048,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2047
2048
|
this.#foregroundActivity = false;
|
|
2048
2049
|
if (options?.foregroundSettled) this.#foregroundTurnSettled = true;
|
|
2049
2050
|
if (options?.restoreBackground === false) {
|
|
2050
|
-
this
|
|
2051
|
+
this.clearLoadingAnimation();
|
|
2051
2052
|
return;
|
|
2052
2053
|
}
|
|
2053
2054
|
this.syncActivityIndicator();
|
package/src/modes/print-mode.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { type AssistantMessage, type ImageContent, isContextOverflow } from "@ga
|
|
|
10
10
|
import { isKnownSinkPeerClosedError, logger, sanitizeText } from "@gajae-code/utils";
|
|
11
11
|
import { loadSlashCommands } from "../extensibility/slash-commands";
|
|
12
12
|
import type { AgentSession } from "../session/agent-session";
|
|
13
|
+
import { ManagedAppendIdentityMismatchError } from "../session/internal/managed-session-storage";
|
|
13
14
|
import { isSilentAbort } from "../session/messages";
|
|
14
15
|
import { resolveProviderSafetyStopHint } from "../session/provider-safety-stop-hint";
|
|
15
16
|
import { executeLocalHeadlessBuiltinSlashCommand } from "../slash-commands/builtin-registry";
|
|
@@ -388,7 +389,10 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
|
|
|
388
389
|
// Observe callback and stream errors from every preceding print-mode write.
|
|
389
390
|
await stdout.flush();
|
|
390
391
|
} catch (error) {
|
|
391
|
-
|
|
392
|
+
if (error instanceof ManagedAppendIdentityMismatchError) {
|
|
393
|
+
process.exitCode = 1;
|
|
394
|
+
await writeStderrAndQuiesce(`${error.operatorMessage}\n`);
|
|
395
|
+
} else failures.push(error);
|
|
392
396
|
} finally {
|
|
393
397
|
// The JSON subscriber remains live while disposal emits its final events.
|
|
394
398
|
try {
|
|
@@ -89,23 +89,37 @@ function getSlashCommandPriority(command: SlashCommand | undefined, item: Autoco
|
|
|
89
89
|
return ADVANCED_SLASH_COMMAND_PRIORITIES.get(item.value) ?? 0;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
interface SlashCommandMetadata {
|
|
93
|
+
command: SlashCommand;
|
|
94
|
+
index: number;
|
|
95
|
+
lowerName: string;
|
|
96
|
+
normalizedName: string;
|
|
97
|
+
normalizedDescription: string | undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
interface SkillCommandMetadata {
|
|
101
|
+
command: SlashCommand;
|
|
102
|
+
lowerSkillName: string;
|
|
103
|
+
searchTargets: string[];
|
|
104
|
+
collisionSearchTargets: string[];
|
|
105
|
+
}
|
|
106
|
+
|
|
92
107
|
function sortSlashCommandSuggestions(
|
|
93
108
|
suggestions: { items: AutocompleteItem[]; prefix: string } | null,
|
|
94
|
-
|
|
109
|
+
commandMetadata: ReadonlyMap<string, SlashCommandMetadata>,
|
|
95
110
|
): { items: AutocompleteItem[]; prefix: string } | null {
|
|
96
111
|
if (!suggestions) return null;
|
|
97
112
|
const query = suggestions.prefix.slice(1).toLowerCase();
|
|
98
113
|
const normalizedQuery = normalizeFuzzyText(query);
|
|
99
|
-
const commandIndexes = new Map(commands.map((command, index) => [command.name, index]));
|
|
100
|
-
const commandByName = new Map(commands.map(command => [command.name, command]));
|
|
101
114
|
const items = suggestions.items
|
|
102
115
|
.map((item, index) => {
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
const normalizedName = normalizeFuzzyText(item.value);
|
|
108
|
-
const normalizedDesc =
|
|
116
|
+
const metadata = commandMetadata.get(item.value);
|
|
117
|
+
const command = metadata?.command;
|
|
118
|
+
const commandIndex = metadata?.index ?? index;
|
|
119
|
+
const lowerName = metadata?.lowerName ?? item.value.toLowerCase();
|
|
120
|
+
const normalizedName = metadata?.normalizedName ?? normalizeFuzzyText(item.value);
|
|
121
|
+
const normalizedDesc =
|
|
122
|
+
metadata?.normalizedDescription ?? normalizeFuzzyText(item.description?.toLowerCase() ?? "");
|
|
109
123
|
const nameScore = autocompleteFuzzyMatch(normalizedQuery, normalizedName)
|
|
110
124
|
? autocompleteFuzzyScore(normalizedQuery, normalizedName)
|
|
111
125
|
: 0;
|
|
@@ -180,7 +194,8 @@ function isSlashTokenInsideInlineCodeSpan(
|
|
|
180
194
|
export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
181
195
|
#baseProvider: CombinedAutocompleteProvider;
|
|
182
196
|
#actions: PromptActionDefinition[];
|
|
183
|
-
#
|
|
197
|
+
#commandMetadata = new Map<string, SlashCommandMetadata>();
|
|
198
|
+
#skillCommandMetadata: SkillCommandMetadata[] = [];
|
|
184
199
|
#getPromptSuggestion: (() => string | null) | undefined;
|
|
185
200
|
|
|
186
201
|
constructor(
|
|
@@ -191,7 +206,28 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
|
191
206
|
) {
|
|
192
207
|
this.#baseProvider = new CombinedAutocompleteProvider(commands, basePath);
|
|
193
208
|
this.#actions = actions;
|
|
194
|
-
|
|
209
|
+
for (const [index, command] of commands.entries()) {
|
|
210
|
+
this.#commandMetadata.set(command.name, {
|
|
211
|
+
command,
|
|
212
|
+
index,
|
|
213
|
+
lowerName: command.name.toLowerCase(),
|
|
214
|
+
normalizedName: normalizeFuzzyText(command.name),
|
|
215
|
+
normalizedDescription:
|
|
216
|
+
command.description === undefined ? undefined : normalizeFuzzyText(command.description.toLowerCase()),
|
|
217
|
+
});
|
|
218
|
+
if (command.name.startsWith("skill:")) {
|
|
219
|
+
const skillName = command.name.slice("skill:".length);
|
|
220
|
+
const name = normalizeFuzzyText(command.name);
|
|
221
|
+
const alias = normalizeFuzzyText(`skill-${skillName}`);
|
|
222
|
+
const description = normalizeFuzzyText(command.description ?? "");
|
|
223
|
+
this.#skillCommandMetadata.push({
|
|
224
|
+
command,
|
|
225
|
+
lowerSkillName: skillName.toLowerCase(),
|
|
226
|
+
searchTargets: [name, alias, normalizeFuzzyText(skillName), description],
|
|
227
|
+
collisionSearchTargets: [name, alias, description],
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
195
231
|
this.#getPromptSuggestion = getPromptSuggestion;
|
|
196
232
|
}
|
|
197
233
|
|
|
@@ -244,7 +280,7 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
|
244
280
|
});
|
|
245
281
|
return sortSlashCommandSuggestions(
|
|
246
282
|
mergeAutocompleteSuggestions(baseSuggestions, skillCommandSuggestions),
|
|
247
|
-
this.#
|
|
283
|
+
this.#commandMetadata,
|
|
248
284
|
);
|
|
249
285
|
}
|
|
250
286
|
if (rawSlashPrefix && slashTokenInsideCode) {
|
|
@@ -334,7 +370,7 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
|
334
370
|
const skillCommandSuggestions = this.#getSkillCommandSuggestions(textBeforeCursor, { includeEmpty: false });
|
|
335
371
|
return sortSlashCommandSuggestions(
|
|
336
372
|
mergeAutocompleteSuggestions(baseSuggestions, skillCommandSuggestions),
|
|
337
|
-
this.#
|
|
373
|
+
this.#commandMetadata,
|
|
338
374
|
);
|
|
339
375
|
}
|
|
340
376
|
trySyncInlineReplace(textBeforeCursor: string): { replaceLen: number; insert: string } | null {
|
|
@@ -354,20 +390,11 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
|
354
390
|
query.startsWith("skill-") ? `skill:${query.slice("skill-".length)}` : query,
|
|
355
391
|
);
|
|
356
392
|
if (query.length > 0 && normalizedQuery.length === 0) return null;
|
|
357
|
-
const exactNonSkillCommand = this.#
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
.map(command => {
|
|
363
|
-
const skillName = command.name.slice("skill:".length);
|
|
364
|
-
if (exactNonSkillCommand && query === skillName.toLowerCase()) return null;
|
|
365
|
-
const searchTargets = [
|
|
366
|
-
command.name,
|
|
367
|
-
`skill-${skillName}`,
|
|
368
|
-
...(exactNonSkillCommand ? [] : [skillName]),
|
|
369
|
-
command.description ?? "",
|
|
370
|
-
].map(target => normalizeFuzzyText(target));
|
|
393
|
+
const exactNonSkillCommand = this.#commandMetadata.has(query) && !query.startsWith("skill:");
|
|
394
|
+
const items = this.#skillCommandMetadata
|
|
395
|
+
.map(({ command, lowerSkillName, searchTargets: allSearchTargets, collisionSearchTargets }) => {
|
|
396
|
+
if (exactNonSkillCommand && query === lowerSkillName) return null;
|
|
397
|
+
const searchTargets = exactNonSkillCommand ? collisionSearchTargets : allSearchTargets;
|
|
371
398
|
if (
|
|
372
399
|
!searchTargets.some(
|
|
373
400
|
target => autocompleteFuzzyMatch(normalizedQuery, target) || target.includes(normalizedQuery),
|
|
@@ -201,6 +201,7 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
201
201
|
},
|
|
202
202
|
getJobs: () => session.getAsyncJobSnapshot(),
|
|
203
203
|
onJobFold: listener => session.onJobFold(listener),
|
|
204
|
+
onSessionEvent: listener => session.subscribe(listener),
|
|
204
205
|
setSdkPermissionProvider: provider => session.setSdkPermissionProvider(provider),
|
|
205
206
|
setSdkClientBridge: bridge => session.setClientBridge(bridge),
|
|
206
207
|
sdkControl: async (operation, input) => {
|
|
@@ -325,6 +326,7 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
325
326
|
const result = await session.executeBash(input.cmd, undefined, { excludeFromContext: true });
|
|
326
327
|
return {
|
|
327
328
|
exitCode: result.exitCode,
|
|
329
|
+
signal: result.signal,
|
|
328
330
|
cancelled: result.cancelled,
|
|
329
331
|
output: result.output,
|
|
330
332
|
truncated: result.truncated,
|
package/src/modes/types.ts
CHANGED
|
@@ -74,6 +74,20 @@ export type ActivityIndicatorStopOptions = Readonly<{
|
|
|
74
74
|
foregroundSettled?: boolean;
|
|
75
75
|
}>;
|
|
76
76
|
|
|
77
|
+
export function clearInteractiveActivityIndicator(ctx: {
|
|
78
|
+
loadingAnimation?: Loader;
|
|
79
|
+
statusContainer?: PartialActivityStatusContainer;
|
|
80
|
+
clearLoadingAnimation?: () => void;
|
|
81
|
+
}): void {
|
|
82
|
+
if (ctx.clearLoadingAnimation) {
|
|
83
|
+
ctx.clearLoadingAnimation();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
ctx.loadingAnimation?.stop();
|
|
87
|
+
ctx.loadingAnimation = undefined;
|
|
88
|
+
ctx.statusContainer?.clear?.();
|
|
89
|
+
}
|
|
90
|
+
|
|
77
91
|
export function stopInteractiveActivityIndicator(
|
|
78
92
|
ctx: {
|
|
79
93
|
loadingAnimation?: Loader;
|
|
@@ -86,9 +100,7 @@ export function stopInteractiveActivityIndicator(
|
|
|
86
100
|
ctx.stopLoadingAnimation(options);
|
|
87
101
|
return;
|
|
88
102
|
}
|
|
89
|
-
ctx
|
|
90
|
-
ctx.loadingAnimation = undefined;
|
|
91
|
-
ctx.statusContainer?.clear?.();
|
|
103
|
+
clearInteractiveActivityIndicator(ctx);
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
export function clearInteractiveActivityLoaders(
|
|
@@ -285,6 +297,7 @@ export interface InteractiveModeContext {
|
|
|
285
297
|
setWorkingMessage(message?: string): void;
|
|
286
298
|
applyPendingWorkingMessage(): void;
|
|
287
299
|
ensureLoadingAnimation(): void;
|
|
300
|
+
clearLoadingAnimation(): void;
|
|
288
301
|
syncActivityIndicator(): void;
|
|
289
302
|
suspendActivityIndicator(): () => void;
|
|
290
303
|
stopLoadingAnimation(options?: ActivityIndicatorStopOptions): void;
|
|
@@ -530,6 +530,7 @@ export class UiHelpers {
|
|
|
530
530
|
component.appendOutput(message.output);
|
|
531
531
|
}
|
|
532
532
|
component.setComplete(message.exitCode, message.cancelled, {
|
|
533
|
+
signal: message.signal,
|
|
533
534
|
truncation: message.meta?.truncation,
|
|
534
535
|
});
|
|
535
536
|
addChatChild(this.ctx, component);
|
|
@@ -15,6 +15,7 @@ Inspect selected subagents by `ids`; omit `ids` to inspect current running subag
|
|
|
15
15
|
## `action: "await"`
|
|
16
16
|
Wait for selected subagents by `ids`; omit `ids` to wait for current running subagents.
|
|
17
17
|
- Always set `timeout_ms` when the result is not immediately required forever.
|
|
18
|
+
- `heartbeat_ms` accepts integer intervals from 100 to 60,000 milliseconds (for example, 10,000 for ten seconds); `0` disables heartbeat updates and omission retains the 500 ms default. Terminal completion is event-driven, not delayed until the next heartbeat.
|
|
18
19
|
- Await timeout only bounds this tool call's wait; it does not stop the subagent and is not a failure reason.
|
|
19
20
|
- On timeout, inspect progress and keep doing independent work. Never cancel just because an await timed out; cancel only if the subagent has actually failed, gone off-track, or become unrecoverably wrong.
|
|
20
21
|
- Completed results are receipt-first by default: bounded preview plus `agent://<id>` output ref when available, not full retained output.
|