@gajae-code/coding-agent 0.16.1 → 0.16.4
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 +70 -0
- package/dist/types/cli/args.d.ts +2 -2
- 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/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/discovery/claude-plugins.d.ts +3 -1
- package/dist/types/discovery/helpers.d.ts +2 -0
- 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/runtime-skill-discovery.d.ts +1 -1
- package/dist/types/extensibility/shared-events.d.ts +1 -1
- package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -2
- 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 +1 -0
- package/dist/types/modes/types.d.ts +1 -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/bus/chat-daemon-runtime.d.ts +12 -0
- 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/cli/rows.d.ts +7 -0
- package/dist/types/sdk/host/events.d.ts +1 -0
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/session-runtime.d.ts +23 -0
- package/dist/types/sdk/session-api-key.d.ts +26 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +9 -0
- package/dist/types/setup/paseo/announce.d.ts +111 -0
- package/dist/types/setup/paseo/setup-deps.d.ts +2 -1
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +2 -1
- package/dist/types/tools/fetch.d.ts +7 -0
- package/dist/types/tools/index.d.ts +8 -2
- package/dist/types/tools/path-utils.d.ts +2 -0
- package/dist/types/tools/search.d.ts +6 -0
- package/dist/types/utils/external-editor.d.ts +5 -0
- package/package.json +7 -7
- package/scripts/capture-status-line-custom-editor-showcase.ts +151 -0
- package/scripts/generate-sdk-operation-inventory.ts +9 -0
- package/scripts/verify-status-line-custom-editor-showcase.ts +309 -0
- package/src/cli/args.ts +20 -9
- package/src/cli/root-flags.ts +5 -3
- package/src/config/autorouting-tier-map.ts +1 -0
- package/src/config/file-lock.ts +181 -17
- package/src/config/model-preset-registry.ts +5 -3
- package/src/config/model-profiles.ts +40 -0
- package/src/config/model-registry.ts +57 -11
- 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/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/discovery/builtin.ts +13 -4
- package/src/discovery/claude-plugins.ts +12 -4
- package/src/discovery/helpers.ts +10 -2
- package/src/extensibility/gjc-plugins/compiler.ts +208 -58
- 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/runtime-adapters.ts +758 -16
- package/src/extensibility/runtime-skill-discovery.ts +77 -151
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/skills.ts +44 -16
- package/src/gjc-runtime/repository-binding.ts +1 -1
- package/src/gjc-runtime/session-state-lock.ts +94 -6
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/main.ts +72 -34
- package/src/modes/DESIGN.md +54 -0
- package/src/modes/acp/acp-agent.ts +29 -14
- package/src/modes/components/assistant-message.ts +22 -9
- package/src/modes/components/model-selector.ts +166 -33
- 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 +393 -21
- package/src/modes/controllers/command-controller.ts +2 -0
- package/src/modes/controllers/event-controller.ts +148 -5
- package/src/modes/controllers/selector-controller.ts +21 -0
- package/src/modes/interactive-mode.ts +47 -1
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/system/system-prompt.md +8 -0
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/job.md +1 -1
- package/src/prompts/tools/search.md +2 -0
- package/src/runtime-mcp/client.ts +60 -17
- package/src/runtime-mcp/manager.ts +171 -14
- package/src/runtime-mcp/pool.test.ts +209 -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/acp/adapter.ts +1 -1
- package/src/sdk/broker/broker.ts +5 -4
- package/src/sdk/bus/chat-daemon-control.ts +1 -1
- package/src/sdk/bus/chat-daemon-runtime.ts +44 -1
- package/src/sdk/bus/slack-daemon.ts +92 -2
- package/src/sdk/bus/slack-live-provider.ts +12 -0
- package/src/sdk/bus/slack-provider.ts +5 -0
- package/src/sdk/cli/rows.ts +47 -2
- package/src/sdk/cli/session-cli.ts +47 -23
- package/src/sdk/host/events.ts +25 -2
- package/src/sdk/host/host.ts +4 -1
- package/src/sdk/host/session-runtime.test.ts +36 -25
- package/src/sdk/host/session-runtime.ts +279 -43
- package/src/sdk/protocol/operation-inventory.generated.json +66 -0
- package/src/sdk/router/session-router.ts +60 -37
- package/src/sdk/session-api-key.ts +94 -0
- package/src/sdk/session.ts +73 -30
- package/src/sdk/transport/serve-cli.ts +3 -3
- package/src/session/agent-session.ts +123 -38
- package/src/session/contribution-prep.ts +214 -21
- package/src/setup/paseo/announce.ts +450 -0
- package/src/setup/paseo/setup-deps.ts +15 -3
- package/src/slash-commands/builtin-registry.ts +1 -0
- package/src/system-prompt.ts +4 -0
- package/src/task/executor.ts +2 -1
- package/src/task/index.ts +17 -4
- package/src/tools/bash.ts +50 -21
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +29 -1
- package/src/tools/index.ts +8 -2
- package/src/tools/job.ts +8 -2
- package/src/tools/monitor.ts +8 -2
- package/src/tools/path-utils.ts +7 -0
- package/src/tools/search.ts +116 -58
- package/src/tools/skill.ts +22 -2
- package/src/tools/subagent.ts +3 -1
- package/src/tools/tool-catalog.generated.ts +9 -2
- package/src/utils/external-editor.ts +16 -2
- package/src/web/insane/bridge.ts +113 -23
- package/src/web/search/providers/perplexity.ts +102 -41
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,
|
|
@@ -1645,6 +1653,66 @@ export async function runRootCommand(
|
|
|
1645
1653
|
)
|
|
1646
1654
|
: undefined;
|
|
1647
1655
|
|
|
1656
|
+
if (parsedArgs.apiKey && parsedArgs.credential) {
|
|
1657
|
+
process.stderr.write(`${chalk.red("--api-key and --credential cannot be used together")}\n`);
|
|
1658
|
+
process.exit(1);
|
|
1659
|
+
}
|
|
1660
|
+
if (parsedArgs.credential && parsedArgs.preferCredential) {
|
|
1661
|
+
process.stderr.write(`${chalk.red("--credential and --prefer-credential cannot be used together")}\n`);
|
|
1662
|
+
process.exit(1);
|
|
1663
|
+
}
|
|
1664
|
+
if (parsedArgs.apiKey && parsedArgs.preferCredential) {
|
|
1665
|
+
process.stderr.write(`${chalk.red("--api-key and --prefer-credential cannot be used together")}\n`);
|
|
1666
|
+
process.exit(1);
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
let credentialSelector: CliCredentialSelector | undefined;
|
|
1670
|
+
let preferredCredentialSelector: CliCredentialSelector | undefined;
|
|
1671
|
+
try {
|
|
1672
|
+
credentialSelector = parsedArgs.credential ? parseCliCredentialSelector(parsedArgs.credential) : undefined;
|
|
1673
|
+
preferredCredentialSelector = parsedArgs.preferCredential
|
|
1674
|
+
? parseCliCredentialSelector(parsedArgs.preferCredential)
|
|
1675
|
+
: undefined;
|
|
1676
|
+
if (preferredCredentialSelector) {
|
|
1677
|
+
const provider =
|
|
1678
|
+
preferredCredentialSelector.provider ??
|
|
1679
|
+
authStorage.resolveRuntimePreferredCredentialSelectorProvider(preferredCredentialSelector.selector);
|
|
1680
|
+
authStorage.setRuntimePreferredCredentialSelector(provider, preferredCredentialSelector.selector);
|
|
1681
|
+
}
|
|
1682
|
+
} catch (error) {
|
|
1683
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1684
|
+
process.stderr.write(`${chalk.red(message)}\n`);
|
|
1685
|
+
process.exit(1);
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
const hasRootStartupProfile = Boolean(settingsInstance.get("modelProfile.default") || parsedArgs.mpreset);
|
|
1689
|
+
const startupModelSelectors = resolveStartupModelRefreshSelectors(
|
|
1690
|
+
{
|
|
1691
|
+
model: parsedArgs.model,
|
|
1692
|
+
provider: parsedArgs.provider,
|
|
1693
|
+
credential: parsedArgs.credential,
|
|
1694
|
+
continue: parsedArgs.continue,
|
|
1695
|
+
resume: parsedArgs.resume,
|
|
1696
|
+
hasStartupProfile: hasRootStartupProfile,
|
|
1697
|
+
},
|
|
1698
|
+
settingsInstance,
|
|
1699
|
+
);
|
|
1700
|
+
const explicitStartupModelSelector = parsedArgs.model
|
|
1701
|
+
? typeof startupModelSelectors === "string"
|
|
1702
|
+
? startupModelSelectors
|
|
1703
|
+
: parsedArgs.model
|
|
1704
|
+
: undefined;
|
|
1705
|
+
if (parsedArgs.apiKey && explicitStartupModelSelector) {
|
|
1706
|
+
const parsedModel = parseModelString(explicitStartupModelSelector);
|
|
1707
|
+
if (parsedModel) authStorage.setRuntimeApiKey(parsedModel.provider, parsedArgs.apiKey);
|
|
1708
|
+
}
|
|
1709
|
+
await logger.time(
|
|
1710
|
+
"refreshStartupModelProviders",
|
|
1711
|
+
refreshMissingQualifiedModelProviders,
|
|
1712
|
+
startupModelSelectors,
|
|
1713
|
+
modelRegistry,
|
|
1714
|
+
);
|
|
1715
|
+
|
|
1648
1716
|
let scopedModels: ScopedModel[] = [];
|
|
1649
1717
|
const modelPatterns = parsedArgs.models ?? settingsInstance.get("enabledModels");
|
|
1650
1718
|
const modelMatchPreferences = {
|
|
@@ -1779,7 +1847,6 @@ export async function runRootCommand(
|
|
|
1779
1847
|
if (isInteractive && sessionOptions.mcpConfigPath) {
|
|
1780
1848
|
sessionOptions.deferMcpConfigStartup = true;
|
|
1781
1849
|
}
|
|
1782
|
-
const hasRootStartupProfile = Boolean(settingsInstance.get("modelProfile.default") || parsedArgs.mpreset);
|
|
1783
1850
|
// ACP is not carved out: `gjc acp` is broker-backed and never builds a local
|
|
1784
1851
|
// session here, and the broker-launched lifecycle child defers memory startup
|
|
1785
1852
|
// unconditionally (createLifecycleAgentSession) so readiness never waits on
|
|
@@ -1790,37 +1857,8 @@ export async function runRootCommand(
|
|
|
1790
1857
|
const acpStartupOptions = mode === "acp" ? resolveAcpStartupOptions(parsedArgs, sessionOptions) : undefined;
|
|
1791
1858
|
|
|
1792
1859
|
// 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
|
-
}
|
|
1860
|
+
if (credentialSelector) sessionOptions.credentialSelector = credentialSelector;
|
|
1861
|
+
if (preferredCredentialSelector) sessionOptions.preferredCredentialSelector = preferredCredentialSelector;
|
|
1824
1862
|
if (parsedArgs.apiKey) {
|
|
1825
1863
|
if (!sessionOptions.model && !sessionOptions.modelPattern) {
|
|
1826
1864
|
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,
|
|
@@ -1699,9 +1699,13 @@ export class AcpAgent implements Agent {
|
|
|
1699
1699
|
try {
|
|
1700
1700
|
await record.adapter.ensureProviders();
|
|
1701
1701
|
} catch (error) {
|
|
1702
|
-
if (waiter.settled || record.
|
|
1702
|
+
if (waiter.settled || record.activePrompt !== waiter) {
|
|
1703
1703
|
this.#retiredPromptAcknowledgements.delete(params.sessionId);
|
|
1704
|
-
|
|
1704
|
+
return await response;
|
|
1705
|
+
}
|
|
1706
|
+
if (record.cancelRequested && waiter.cancelAcknowledged) {
|
|
1707
|
+
this.#retiredPromptAcknowledgements.delete(params.sessionId);
|
|
1708
|
+
await this.#settleCancelledPrompt(params.sessionId, record, waiter);
|
|
1705
1709
|
return await response;
|
|
1706
1710
|
}
|
|
1707
1711
|
if (record.activePrompt === waiter) {
|
|
@@ -1712,9 +1716,13 @@ export class AcpAgent implements Agent {
|
|
|
1712
1716
|
}
|
|
1713
1717
|
throw error;
|
|
1714
1718
|
}
|
|
1715
|
-
if (waiter.settled || record.
|
|
1719
|
+
if (waiter.settled || record.activePrompt !== waiter) {
|
|
1716
1720
|
this.#retiredPromptAcknowledgements.delete(params.sessionId);
|
|
1717
|
-
|
|
1721
|
+
return await response;
|
|
1722
|
+
}
|
|
1723
|
+
if (record.cancelRequested && waiter.cancelAcknowledged) {
|
|
1724
|
+
this.#retiredPromptAcknowledgements.delete(params.sessionId);
|
|
1725
|
+
await this.#settleCancelledPrompt(params.sessionId, record, waiter);
|
|
1718
1726
|
return await response;
|
|
1719
1727
|
}
|
|
1720
1728
|
|
|
@@ -1739,8 +1747,11 @@ export class AcpAgent implements Agent {
|
|
|
1739
1747
|
);
|
|
1740
1748
|
}
|
|
1741
1749
|
waiter.dispatched = true;
|
|
1742
|
-
if (waiter.settled || record.
|
|
1743
|
-
|
|
1750
|
+
if (waiter.settled || record.activePrompt !== waiter) {
|
|
1751
|
+
return await response;
|
|
1752
|
+
}
|
|
1753
|
+
if (record.cancelRequested && waiter.cancelAcknowledged) {
|
|
1754
|
+
await this.#settleCancelledPrompt(params.sessionId, record, waiter);
|
|
1744
1755
|
return await response;
|
|
1745
1756
|
}
|
|
1746
1757
|
|
|
@@ -1922,7 +1933,7 @@ export class AcpAgent implements Agent {
|
|
|
1922
1933
|
// return the semantically meaningful `cancelled` stop reason, so that Clients
|
|
1923
1934
|
// can reliably confirm the cancellation." Surfacing the transport's `busy`
|
|
1924
1935
|
// rejection instead would show the user a spurious error for their own cancel.
|
|
1925
|
-
if (record.cancelRequested) {
|
|
1936
|
+
if (record.cancelRequested && waiter.cancelAcknowledged) {
|
|
1926
1937
|
record.cancelRequested = false;
|
|
1927
1938
|
// The client's turn is settled by the return; the advisory idle publication
|
|
1928
1939
|
// must not gate it and must still be attempted so the running phase is
|
|
@@ -2006,11 +2017,6 @@ export class AcpAgent implements Agent {
|
|
|
2006
2017
|
}
|
|
2007
2018
|
waiter?.cancelAttemptResolve?.(true);
|
|
2008
2019
|
} catch (error) {
|
|
2009
|
-
if (waiter && !waiter.dispatched) {
|
|
2010
|
-
await this.#settleCancelledPrompt(params.sessionId, record, waiter);
|
|
2011
|
-
waiter.cancelAttemptResolve?.(true);
|
|
2012
|
-
return;
|
|
2013
|
-
}
|
|
2014
2020
|
if (!waiter) record.cancelRequested = false;
|
|
2015
2021
|
// Only the LAST in-flight attempt resolves the shared promise false;
|
|
2016
2022
|
// an earlier attempt may still acknowledge (review thread P2). After
|
|
@@ -2058,7 +2064,8 @@ export class AcpAgent implements Agent {
|
|
|
2058
2064
|
|
|
2059
2065
|
async #settleCancelledPrompt(id: string, record: SessionRecord, waiter: PromptWaiter): Promise<void> {
|
|
2060
2066
|
// The authoritative terminal wins whenever it arrives in time; this only runs
|
|
2061
|
-
// when nothing settled the prompt the
|
|
2067
|
+
// when nothing settled the prompt after the SDK acknowledged the cancellation.
|
|
2068
|
+
if (!waiter.cancelAcknowledged) return;
|
|
2062
2069
|
if (this.#sessions.get(id) !== record || record.activePrompt !== waiter || waiter.settled) return;
|
|
2063
2070
|
if (waiter.terminalReserved) return;
|
|
2064
2071
|
record.activePrompt = undefined;
|
|
@@ -2896,6 +2903,14 @@ export class AcpAgent implements Agent {
|
|
|
2896
2903
|
if (reconnected) {
|
|
2897
2904
|
const waiter = record.activePrompt;
|
|
2898
2905
|
if (waiter && !waiter.settled && !waiter.terminal) {
|
|
2906
|
+
// A successful abort acknowledgement is authoritative for this prompt even
|
|
2907
|
+
// when the SDK transport changes identity during the cancellation grace.
|
|
2908
|
+
// Treating the reconnect as owner loss would replace the acknowledged
|
|
2909
|
+
// cancellation with connection_closed and leave cancellation nondeterministic.
|
|
2910
|
+
if (record.cancelRequested && waiter.cancelAcknowledged) {
|
|
2911
|
+
await this.#settleCancelledPrompt(id, record, waiter);
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2899
2914
|
record.activePrompt = undefined;
|
|
2900
2915
|
record.busy = record.backgroundBusy;
|
|
2901
2916
|
clearPromptWatchdog(waiter);
|
|
@@ -3113,7 +3128,7 @@ export class AcpAgent implements Agent {
|
|
|
3113
3128
|
id: string,
|
|
3114
3129
|
notification: SessionNotification,
|
|
3115
3130
|
adapter: AcpSdkAdapter,
|
|
3116
|
-
|
|
3131
|
+
_publicationGeneration: number,
|
|
3117
3132
|
): void {
|
|
3118
3133
|
const prior = this.#failureDiagnosticTails.get(id) ?? Promise.resolve();
|
|
3119
3134
|
const task = prior.then(async () => {
|
|
@@ -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
|
|