@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
|
@@ -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,
|
|
@@ -128,6 +129,15 @@ type AgentSessionEventHandlers = {
|
|
|
128
129
|
[E in AgentSessionEventKind]: (event: Extract<AgentSessionEvent, { type: E }>) => Promise<void>;
|
|
129
130
|
};
|
|
130
131
|
|
|
132
|
+
interface AssistantTextPresentation {
|
|
133
|
+
component: AssistantMessageComponent;
|
|
134
|
+
latestMessage: AssistantMessage;
|
|
135
|
+
sessionIdentity: InteractiveModeContext["session"];
|
|
136
|
+
sessionEpoch: number;
|
|
137
|
+
paintEpoch: number;
|
|
138
|
+
cancel: () => void;
|
|
139
|
+
}
|
|
140
|
+
|
|
131
141
|
export class EventController {
|
|
132
142
|
#lastReadGroup: ReadToolGroupComponent | undefined = undefined;
|
|
133
143
|
#lastThinkingCount = 0;
|
|
@@ -146,6 +156,16 @@ export class EventController {
|
|
|
146
156
|
#visibleTranscriptChanged = false;
|
|
147
157
|
#handlingEvent = false;
|
|
148
158
|
#eventQueue: Promise<void> = Promise.resolve();
|
|
159
|
+
#disposed = false;
|
|
160
|
+
#assistantTextSuspended = false;
|
|
161
|
+
#sessionPresentationEpoch = 0;
|
|
162
|
+
#paintEpoch = 0;
|
|
163
|
+
#pendingAssistantText: AssistantTextPresentation | undefined;
|
|
164
|
+
#assistantTextMutationScope: AssistantTextPresentation | undefined;
|
|
165
|
+
#assistantLifetimes = new WeakMap<
|
|
166
|
+
AssistantMessageComponent,
|
|
167
|
+
{ sessionIdentity: InteractiveModeContext["session"]; sessionEpoch: number }
|
|
168
|
+
>();
|
|
149
169
|
|
|
150
170
|
constructor(private ctx: InteractiveModeContext) {
|
|
151
171
|
this.#handlers = {
|
|
@@ -177,6 +197,8 @@ export class EventController {
|
|
|
177
197
|
}
|
|
178
198
|
|
|
179
199
|
dispose(): void {
|
|
200
|
+
this.#disposed = true;
|
|
201
|
+
this.resetAssistantTextPresentation();
|
|
180
202
|
this.#cancelIdleCompaction();
|
|
181
203
|
this.#clearRetryCountdown();
|
|
182
204
|
if (this.ctx.retryEscapeHandler) {
|
|
@@ -199,6 +221,104 @@ export class EventController {
|
|
|
199
221
|
this.clearIrcExpiryTimers();
|
|
200
222
|
}
|
|
201
223
|
|
|
224
|
+
#cancelAssistantTextPresentation(): void {
|
|
225
|
+
const pending = this.#pendingAssistantText;
|
|
226
|
+
this.#pendingAssistantText = undefined;
|
|
227
|
+
pending?.cancel();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Temporary TUI stop discards scheduling, not historical assistant callback lifetimes. */
|
|
231
|
+
suspendAssistantTextPresentation(): void {
|
|
232
|
+
this.#assistantTextSuspended = true;
|
|
233
|
+
this.#paintEpoch += 1;
|
|
234
|
+
this.#cancelAssistantTextPresentation();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Rearm before the first restarted frame, even without a new provider event. */
|
|
238
|
+
resumeAssistantTextPresentation(): void {
|
|
239
|
+
this.#cancelAssistantTextPresentation();
|
|
240
|
+
this.#assistantTextSuspended = false;
|
|
241
|
+
const component = this.ctx.streamingComponent;
|
|
242
|
+
const message = this.ctx.streamingMessage;
|
|
243
|
+
if (component && message && this.#isLiveAssistant(component)) {
|
|
244
|
+
this.#queueAssistantText(component, message);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Session/transcript replacement invalidates callbacks before removing old children. */
|
|
249
|
+
resetAssistantTextPresentation(): void {
|
|
250
|
+
this.#sessionPresentationEpoch += 1;
|
|
251
|
+
this.#cancelAssistantTextPresentation();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
#isLiveAssistant(component: AssistantMessageComponent): boolean {
|
|
255
|
+
const lifetime = this.#assistantLifetimes.get(component);
|
|
256
|
+
return (
|
|
257
|
+
!this.#disposed &&
|
|
258
|
+
!this.ctx.isStopped?.() &&
|
|
259
|
+
lifetime !== undefined &&
|
|
260
|
+
lifetime.sessionIdentity === this.ctx.session &&
|
|
261
|
+
lifetime.sessionEpoch === this.#sessionPresentationEpoch &&
|
|
262
|
+
this.ctx.chatContainer.hasLiveChild(component)
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
#isValidAssistantText(record: AssistantTextPresentation): boolean {
|
|
267
|
+
return (
|
|
268
|
+
!this.#assistantTextSuspended &&
|
|
269
|
+
this.#isLiveAssistant(record.component) &&
|
|
270
|
+
record.sessionIdentity === this.ctx.session &&
|
|
271
|
+
record.sessionEpoch === this.#sessionPresentationEpoch &&
|
|
272
|
+
record.paintEpoch === this.#paintEpoch &&
|
|
273
|
+
record.component === this.ctx.streamingComponent
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
#queueAssistantText(component: AssistantMessageComponent, latestMessage: AssistantMessage): void {
|
|
278
|
+
if (this.#assistantTextSuspended || !this.#isLiveAssistant(component)) return;
|
|
279
|
+
const pending = this.#pendingAssistantText;
|
|
280
|
+
if (pending && this.#isValidAssistantText(pending)) {
|
|
281
|
+
pending.latestMessage = latestMessage;
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
this.#cancelAssistantTextPresentation();
|
|
285
|
+
const record: AssistantTextPresentation = {
|
|
286
|
+
component,
|
|
287
|
+
latestMessage,
|
|
288
|
+
sessionIdentity: this.ctx.session,
|
|
289
|
+
sessionEpoch: this.#sessionPresentationEpoch,
|
|
290
|
+
paintEpoch: this.#paintEpoch,
|
|
291
|
+
cancel: () => {},
|
|
292
|
+
};
|
|
293
|
+
this.#pendingAssistantText = record;
|
|
294
|
+
record.cancel = this.ctx.ui.enqueueBeforeRender(() => this.#flushAssistantText(record));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
#flushAssistantText(record = this.#pendingAssistantText): void {
|
|
298
|
+
if (!record || this.#pendingAssistantText !== record) return;
|
|
299
|
+
this.#cancelAssistantTextPresentation();
|
|
300
|
+
if (!this.#isValidAssistantText(record)) return;
|
|
301
|
+
const previousScope = this.#assistantTextMutationScope;
|
|
302
|
+
this.#assistantTextMutationScope = record;
|
|
303
|
+
try {
|
|
304
|
+
record.component.updateContent(record.latestMessage, { streaming: true });
|
|
305
|
+
} finally {
|
|
306
|
+
this.#assistantTextMutationScope = previousScope;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
#observeAssistantMutation(component: AssistantMessageComponent): void {
|
|
311
|
+
if (!this.#isLiveAssistant(component)) return;
|
|
312
|
+
const scope = this.#assistantTextMutationScope;
|
|
313
|
+
if (scope?.component === component && this.#isValidAssistantText(scope)) {
|
|
314
|
+
// Only this synchronous projection publishes before frame source selection.
|
|
315
|
+
// An unrelated awaited event retains its own dirty flag and settlement semantics.
|
|
316
|
+
this.ctx.recordVisibleTranscriptMutation?.();
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
this.#observeVisibleTranscriptMutation();
|
|
320
|
+
}
|
|
321
|
+
|
|
202
322
|
#recordVisibleTranscriptMutation(): void {
|
|
203
323
|
this.#visibleTranscriptChanged = true;
|
|
204
324
|
}
|
|
@@ -292,9 +412,10 @@ export class EventController {
|
|
|
292
412
|
}
|
|
293
413
|
|
|
294
414
|
async #dispatchEvent(event: AgentSessionEvent): Promise<void> {
|
|
415
|
+
if (this.#disposed) return;
|
|
295
416
|
if (this.ctx.isStopped?.()) return;
|
|
296
417
|
if (!this.ctx.isInitialized) await this.ctx.init();
|
|
297
|
-
if (this.ctx.isStopped?.()) return;
|
|
418
|
+
if (this.#disposed || this.ctx.isStopped?.()) return;
|
|
298
419
|
this.#visibleTranscriptChanged = false;
|
|
299
420
|
this.#handlingEvent = true;
|
|
300
421
|
try {
|
|
@@ -462,18 +583,26 @@ export class EventController {
|
|
|
462
583
|
this.ctx.ui.requestRender();
|
|
463
584
|
this.#recordVisibleTranscriptMutation();
|
|
464
585
|
} else if (event.message.role === "assistant") {
|
|
586
|
+
this.#cancelAssistantTextPresentation();
|
|
465
587
|
this.#lastThinkingCount = 0;
|
|
466
588
|
this.#resetReadGroup();
|
|
467
589
|
this.#toolIntentCache.clear();
|
|
468
590
|
this.#thinkingContentIndices.clear();
|
|
469
|
-
|
|
591
|
+
const component: AssistantMessageComponent = new AssistantMessageComponent(
|
|
470
592
|
undefined,
|
|
471
593
|
this.ctx.hideThinkingBlock,
|
|
472
|
-
() =>
|
|
594
|
+
() => {
|
|
595
|
+
if (this.#isLiveAssistant(component)) this.ctx.ui.requestRender();
|
|
596
|
+
},
|
|
473
597
|
this.ctx.getAssistantViewportAnchorId?.(event.message),
|
|
474
|
-
() => this.#
|
|
598
|
+
() => this.#observeAssistantMutation(component),
|
|
475
599
|
this.ctx.session,
|
|
476
600
|
);
|
|
601
|
+
this.#assistantLifetimes.set(component, {
|
|
602
|
+
sessionIdentity: this.ctx.session,
|
|
603
|
+
sessionEpoch: this.#sessionPresentationEpoch,
|
|
604
|
+
});
|
|
605
|
+
this.ctx.streamingComponent = component;
|
|
477
606
|
this.ctx.streamingMessage = event.message;
|
|
478
607
|
addChatChild(this.ctx, this.ctx.streamingComponent);
|
|
479
608
|
this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage, { streaming: true });
|
|
@@ -605,8 +734,20 @@ export class EventController {
|
|
|
605
734
|
transferSessionMessageIdentity([this.ctx.streamingMessage], [event.message]);
|
|
606
735
|
}
|
|
607
736
|
this.ctx.streamingMessage = event.message;
|
|
608
|
-
this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage, { streaming: true });
|
|
609
737
|
const contentIndex = event.assistantMessageEvent?.contentIndex;
|
|
738
|
+
if (
|
|
739
|
+
event.assistantMessageEvent?.type === "text_delta" &&
|
|
740
|
+
typeof contentIndex === "number" &&
|
|
741
|
+
Number.isInteger(contentIndex) &&
|
|
742
|
+
contentIndex >= 0 &&
|
|
743
|
+
contentIndex < event.message.content.length &&
|
|
744
|
+
event.message.content[contentIndex]?.type === "text"
|
|
745
|
+
) {
|
|
746
|
+
this.#queueAssistantText(this.ctx.streamingComponent, event.message);
|
|
747
|
+
} else {
|
|
748
|
+
this.#cancelAssistantTextPresentation();
|
|
749
|
+
this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage, { streaming: true });
|
|
750
|
+
}
|
|
610
751
|
const changedContent =
|
|
611
752
|
typeof contentIndex === "number" && contentIndex >= 0
|
|
612
753
|
? this.ctx.streamingMessage.content[contentIndex]
|
|
@@ -714,6 +855,7 @@ export class EventController {
|
|
|
714
855
|
|
|
715
856
|
async #handleMessageEnd(event: Extract<AgentSessionEvent, { type: "message_end" }>): Promise<void> {
|
|
716
857
|
if (event.message.role === "user") return;
|
|
858
|
+
if (event.message.role === "assistant") this.#cancelAssistantTextPresentation();
|
|
717
859
|
if (this.ctx.streamingComponent && event.message.role === "assistant") {
|
|
718
860
|
if (this.ctx.streamingMessage?.role === "assistant") {
|
|
719
861
|
transferSessionMessageIdentity([this.ctx.streamingMessage], [event.message]);
|
|
@@ -916,6 +1058,8 @@ export class EventController {
|
|
|
916
1058
|
}
|
|
917
1059
|
|
|
918
1060
|
async #handleAgentEnd(event: Extract<AgentSessionEvent, { type: "agent_end" }>): Promise<void> {
|
|
1061
|
+
this.#flushAssistantText();
|
|
1062
|
+
this.#cancelAssistantTextPresentation();
|
|
919
1063
|
this.ctx.setWorkingMessage(undefined);
|
|
920
1064
|
stopInteractiveActivityIndicator(this.ctx, { foregroundSettled: true });
|
|
921
1065
|
if (this.ctx.streamingComponent) {
|
|
@@ -961,7 +1105,7 @@ export class EventController {
|
|
|
961
1105
|
this.ctx.editor.onEscape = () => {
|
|
962
1106
|
this.ctx.session.abortCompaction();
|
|
963
1107
|
};
|
|
964
|
-
|
|
1108
|
+
clearInteractiveActivityIndicator(this.ctx);
|
|
965
1109
|
const reasonText =
|
|
966
1110
|
event.reason === "overflow" ? "Context overflow detected, " : event.reason === "idle" ? "Idle " : "";
|
|
967
1111
|
const actionLabel = event.action === "handoff" ? "Auto-handoff" : "Auto context-full maintenance";
|
|
@@ -1062,7 +1206,7 @@ export class EventController {
|
|
|
1062
1206
|
this.ctx.session.abortRetry();
|
|
1063
1207
|
}
|
|
1064
1208
|
};
|
|
1065
|
-
|
|
1209
|
+
clearInteractiveActivityIndicator(this.ctx);
|
|
1066
1210
|
// Stop any prior retry loader/timer before installing a new one.
|
|
1067
1211
|
this.ctx.retryLoader?.stop();
|
|
1068
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 =
|
|
@@ -1976,6 +1976,22 @@ export class SelectorController {
|
|
|
1976
1976
|
width ?? this.ctx.editor.getTopBorderAvailableWidth(this.ctx.ui.terminal.columns);
|
|
1977
1977
|
return this.ctx.statusLine.getPreviewContent(availableWidth);
|
|
1978
1978
|
},
|
|
1979
|
+
getStatusLinePreviewForSettings: (previewSettings, width?: number) => {
|
|
1980
|
+
const availableWidth =
|
|
1981
|
+
width ?? this.ctx.editor.getTopBorderAvailableWidth(this.ctx.ui.terminal.columns);
|
|
1982
|
+
return this.ctx.statusLine.getPreviewContentForSettings(availableWidth, {
|
|
1983
|
+
...buildStatusLineSettings(settings),
|
|
1984
|
+
...previewSettings,
|
|
1985
|
+
});
|
|
1986
|
+
},
|
|
1987
|
+
getStatusLinePreviewPartsForSettings: (previewSettings, width?: number) => {
|
|
1988
|
+
const availableWidth =
|
|
1989
|
+
width ?? this.ctx.editor.getTopBorderAvailableWidth(this.ctx.ui.terminal.columns);
|
|
1990
|
+
return this.ctx.statusLine.getPreviewPartsForSettings(availableWidth, {
|
|
1991
|
+
...buildStatusLineSettings(settings),
|
|
1992
|
+
...previewSettings,
|
|
1993
|
+
});
|
|
1994
|
+
},
|
|
1979
1995
|
onPluginsChanged: () => {
|
|
1980
1996
|
this.ctx.ui.requestRender();
|
|
1981
1997
|
},
|
|
@@ -2982,6 +2998,8 @@ export class SelectorController {
|
|
|
2982
2998
|
this.ctx.ui.requestRender();
|
|
2983
2999
|
try {
|
|
2984
3000
|
await mgr.uninstallPlugin(pluginId, scope);
|
|
3001
|
+
await this.ctx.session.reloadSkills();
|
|
3002
|
+
await this.ctx.refreshSlashCommandState();
|
|
2985
3003
|
this.ctx.showStatus(`Uninstalled ${pluginId}`);
|
|
2986
3004
|
} catch (err) {
|
|
2987
3005
|
this.ctx.showStatus(`Uninstall failed: ${err}`);
|
|
@@ -3019,6 +3037,8 @@ export class SelectorController {
|
|
|
3019
3037
|
try {
|
|
3020
3038
|
const force = installedIds.has(`${name}@${marketplace}`);
|
|
3021
3039
|
await mgr.installPlugin(name, marketplace, { force });
|
|
3040
|
+
await this.ctx.session.reloadSkills();
|
|
3041
|
+
await this.ctx.refreshSlashCommandState();
|
|
3022
3042
|
this.ctx.showStatus(`Installed ${name} from ${marketplace}`);
|
|
3023
3043
|
} catch (err) {
|
|
3024
3044
|
this.ctx.showStatus(`Install failed: ${err}`);
|
|
@@ -3245,6 +3265,7 @@ export class SelectorController {
|
|
|
3245
3265
|
}
|
|
3246
3266
|
|
|
3247
3267
|
#clearTransientSessionUi(options?: { restoreBackground?: boolean; clearSpecializedLoaders?: boolean }): void {
|
|
3268
|
+
this.ctx.resetAssistantTextPresentation();
|
|
3248
3269
|
if (options?.clearSpecializedLoaders) clearInteractiveActivityLoaders(this.ctx);
|
|
3249
3270
|
stopInteractiveActivityIndicator(this.ctx, {
|
|
3250
3271
|
restoreBackground: options?.restoreBackground,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
Text,
|
|
14
14
|
TUI,
|
|
15
15
|
} from "@gajae-code/tui";
|
|
16
|
-
import { APP_NAME, adjustHsv, getProjectDir, logger, postmortem, sanitizeText } from "@gajae-code/utils";
|
|
16
|
+
import { APP_NAME, adjustHsv, getAgentDir, getProjectDir, logger, postmortem, sanitizeText } from "@gajae-code/utils";
|
|
17
17
|
import chalk from "chalk";
|
|
18
18
|
import { AsyncJobManager } from "../async";
|
|
19
19
|
import {
|
|
@@ -49,6 +49,7 @@ import type { AgentSession, AgentSessionEvent, AsyncJobSnapshotItem } from "../s
|
|
|
49
49
|
import type { HistoryStorage } from "../session/history-storage";
|
|
50
50
|
import type { SessionContext, SessionManager } from "../session/session-manager";
|
|
51
51
|
import { getRecentSessions, getSessionMessageEntryId } from "../session/session-manager";
|
|
52
|
+
import { createDefaultPaseoAnnounceDependencies, startPaseoAnnouncement } from "../setup/paseo/announce";
|
|
52
53
|
import type { LspStartupServerInfo } from "../tools";
|
|
53
54
|
import { formatPhaseDisplayName } from "../tools/todo-write";
|
|
54
55
|
import { copyToClipboard } from "../utils/clipboard";
|
|
@@ -745,6 +746,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
745
746
|
version: this.#version,
|
|
746
747
|
focusDomain: "composer",
|
|
747
748
|
keyDisplayContext: this.#keyDisplayContext,
|
|
749
|
+
onUpdate: () => {
|
|
750
|
+
this.updateEditorChrome();
|
|
751
|
+
this.ui.requestRender();
|
|
752
|
+
},
|
|
748
753
|
});
|
|
749
754
|
this.statusLine.setAutoCompactEnabled(session.autoCompactionEnabled);
|
|
750
755
|
|
|
@@ -776,6 +781,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
776
781
|
this.#btwController = new BtwController(this);
|
|
777
782
|
this.#extensionUiController = new ExtensionUiController(this);
|
|
778
783
|
this.#eventController = new EventController(this);
|
|
784
|
+
this.ui.setRenderPreparationLifecycleCallbacks({
|
|
785
|
+
invalidate: () => this.#eventController.suspendAssistantTextPresentation(),
|
|
786
|
+
beforeStart: () => this.#eventController.resumeAssistantTextPresentation(),
|
|
787
|
+
});
|
|
779
788
|
this.#commandController = new CommandController(this);
|
|
780
789
|
this.#todoCommandController = new TodoCommandController(this);
|
|
781
790
|
this.#selectorController = new SelectorController(this);
|
|
@@ -1065,6 +1074,37 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1065
1074
|
crashRelayTimer.unref?.();
|
|
1066
1075
|
}
|
|
1067
1076
|
|
|
1077
|
+
// Make this session visible in Paseo. Interactive-only by construction: a
|
|
1078
|
+
// `gjc acp` provider process never builds an InteractiveMode, so an imported
|
|
1079
|
+
// session can never announce itself back into Paseo.
|
|
1080
|
+
if (this.settings.getGlobal("paseo.autoImport") === true) {
|
|
1081
|
+
const sessionId = this.sessionManager.getSessionId();
|
|
1082
|
+
const announcement = startPaseoAnnouncement(
|
|
1083
|
+
{
|
|
1084
|
+
sessionId,
|
|
1085
|
+
cwd: this.sessionManager.getCwd(),
|
|
1086
|
+
isEnabled: () => this.settings.getGlobal("paseo.autoImport") === true,
|
|
1087
|
+
onOutcome: outcome => {
|
|
1088
|
+
if (this.#stopped || outcome.kind === "skipped") return;
|
|
1089
|
+
this.showStatus(
|
|
1090
|
+
outcome.kind === "failed"
|
|
1091
|
+
? `Paseo import failed: ${outcome.detail}`
|
|
1092
|
+
: `Controllable in Paseo as ${outcome.providerKey}`,
|
|
1093
|
+
{ dim: true },
|
|
1094
|
+
);
|
|
1095
|
+
},
|
|
1096
|
+
},
|
|
1097
|
+
createDefaultPaseoAnnounceDependencies(getAgentDir()),
|
|
1098
|
+
);
|
|
1099
|
+
const stopWatchingSettings = this.settings.onChanged(path => {
|
|
1100
|
+
if (path === "paseo.autoImport" && this.settings.getGlobal(path) !== true) announcement.cancel();
|
|
1101
|
+
});
|
|
1102
|
+
this.#stopListeners.add(() => {
|
|
1103
|
+
stopWatchingSettings();
|
|
1104
|
+
announcement.cancel();
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1068
1108
|
if (starReminderGate.schedule) {
|
|
1069
1109
|
scheduleLaunchStarReminderAfterFirstRender({
|
|
1070
1110
|
confirm: (title, message) =>
|
|
@@ -1517,6 +1557,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1517
1557
|
|
|
1518
1558
|
rebuildChatFromMessages(policy: TranscriptRebuildPolicy): void {
|
|
1519
1559
|
prepareTranscriptRebuild(this.ui, policy);
|
|
1560
|
+
this.resetAssistantTextPresentation();
|
|
1520
1561
|
this.chatContainer.clear();
|
|
1521
1562
|
const context = this.session.buildDisplaySessionContext();
|
|
1522
1563
|
this.renderSessionContext(context);
|
|
@@ -1649,6 +1690,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1649
1690
|
stop(): void {
|
|
1650
1691
|
const wasInitialized = this.isInitialized;
|
|
1651
1692
|
this.#stopped = true;
|
|
1693
|
+
this.ui.setRenderPreparationLifecycleCallbacks(undefined);
|
|
1652
1694
|
this.#inputController.discardDeferredSubmission();
|
|
1653
1695
|
for (const listener of this.#stopListeners) {
|
|
1654
1696
|
try {
|
|
@@ -1658,7 +1700,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1658
1700
|
}
|
|
1659
1701
|
}
|
|
1660
1702
|
this.#stopListeners.clear();
|
|
1661
|
-
this
|
|
1703
|
+
this.clearLoadingAnimation();
|
|
1662
1704
|
this.#suspendedActivityIndicator = undefined;
|
|
1663
1705
|
this.#petProtocolUnsubscribe?.();
|
|
1664
1706
|
this.#petProtocolUnsubscribe = undefined;
|
|
@@ -1954,7 +1996,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1954
1996
|
return tallyBackgroundActivity(this.session.getAsyncJobSnapshot()?.running ?? []);
|
|
1955
1997
|
}
|
|
1956
1998
|
|
|
1957
|
-
|
|
1999
|
+
/** Clear the status display without settling the foreground run during maintenance/retry. */
|
|
2000
|
+
clearLoadingAnimation(): void {
|
|
1958
2001
|
this.loadingAnimation?.stop();
|
|
1959
2002
|
this.loadingAnimation = undefined;
|
|
1960
2003
|
this.statusContainer.clear();
|
|
@@ -1965,7 +2008,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1965
2008
|
return;
|
|
1966
2009
|
const foregroundActive = this.#foregroundActivity || (!this.#foregroundTurnSettled && this.session.isStreaming);
|
|
1967
2010
|
if (!this.isInitialized && !foregroundActive) {
|
|
1968
|
-
this
|
|
2011
|
+
this.clearLoadingAnimation();
|
|
1969
2012
|
return;
|
|
1970
2013
|
}
|
|
1971
2014
|
const message = resolveActivityIndicatorMessage(
|
|
@@ -1974,7 +2017,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1974
2017
|
this.#pendingWorkingMessage ?? this.#defaultWorkingMessage,
|
|
1975
2018
|
);
|
|
1976
2019
|
if (!message) {
|
|
1977
|
-
this
|
|
2020
|
+
this.clearLoadingAnimation();
|
|
1978
2021
|
return;
|
|
1979
2022
|
}
|
|
1980
2023
|
if (!this.loadingAnimation) {
|
|
@@ -2005,7 +2048,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2005
2048
|
this.#foregroundActivity = false;
|
|
2006
2049
|
if (options?.foregroundSettled) this.#foregroundTurnSettled = true;
|
|
2007
2050
|
if (options?.restoreBackground === false) {
|
|
2008
|
-
this
|
|
2051
|
+
this.clearLoadingAnimation();
|
|
2009
2052
|
return;
|
|
2010
2053
|
}
|
|
2011
2054
|
this.syncActivityIndicator();
|
|
@@ -2077,6 +2120,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2077
2120
|
return this.#uiHelpers.isKnownSlashCommand(text);
|
|
2078
2121
|
}
|
|
2079
2122
|
|
|
2123
|
+
resetAssistantTextPresentation(): void {
|
|
2124
|
+
this.#eventController.resetAssistantTextPresentation();
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2080
2127
|
/** Advances the sticky-viewport source only for semantic transcript output. */
|
|
2081
2128
|
recordVisibleTranscriptMutation(): void {
|
|
2082
2129
|
const identity = `session:${this.sessionManager.getSessionId()}`;
|
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,
|