@gajae-code/coding-agent 0.11.10 → 0.12.0
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 +86 -2
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/cli.d.ts +8 -1
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/model-profile-contract.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +86 -3
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +43 -2
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +5 -12
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +17 -32
- package/dist/types/gjc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +4 -135
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/gjc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +112 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +5 -46
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +2 -1
- package/dist/types/gjc-runtime/team-store.d.ts +12 -0
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +7 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +8 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -5
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +22 -9
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/queue-pane.d.ts +8 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +7 -1
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/runtime/memory-domain.d.ts +9 -0
- package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
- package/dist/types/runtime/memory-guard.d.ts +39 -0
- package/dist/types/runtime/memory-limit.d.ts +11 -0
- package/dist/types/runtime-mcp/manager.d.ts +8 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/acp/final-text.d.ts +19 -0
- package/dist/types/sdk/acp/mcp.d.ts +23 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +7 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/index.d.ts +18 -2
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -1
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +23 -3
- package/dist/types/sdk/host/control/operations.d.ts +2 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +5 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/startup-capability.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +51 -19
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +27 -0
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +59 -13
- package/dist/types/session/streaming-output.d.ts +60 -5
- package/dist/types/skill-state/active-state.d.ts +0 -4
- package/dist/types/skill-state/workflow-hud.d.ts +3 -0
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/types.d.ts +36 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +17 -1
- package/dist/types/tools/output-meta.d.ts +21 -1
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -1
- package/dist/types/tools/resource-gc.d.ts +23 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/package.json +9 -13
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/generate-sdk-operation-inventory.ts +4 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/scripts/verify-gjc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/capability/index.ts +8 -0
- package/src/cli/args.ts +14 -8
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/read-cli.ts +2 -1
- package/src/cli.ts +49 -2
- package/src/commands/deep-interview.ts +29 -9
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/sdk.ts +91 -7
- package/src/commands/team.ts +1 -1
- package/src/config/model-profile-activation.ts +1 -0
- package/src/config/model-profile-contract.ts +13 -0
- package/src/config/model-profiles.ts +24 -0
- package/src/config/model-registry.ts +21 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +84 -3
- package/src/config/settings.ts +254 -108
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +229 -82
- package/src/defaults/gjc/skills/ralplan/SKILL.md +78 -26
- package/src/defaults/gjc/skills/team/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +26 -16
- package/src/eval/py/executor.ts +7 -2
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +42 -4
- package/src/extensibility/shared-events.ts +5 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +47 -68
- package/src/gjc-runtime/deep-interview-recorder.ts +227 -310
- package/src/gjc-runtime/deep-interview-runtime.ts +109 -270
- package/src/gjc-runtime/deep-interview-stage.ts +1016 -0
- package/src/gjc-runtime/deep-interview-state.ts +12 -921
- package/src/gjc-runtime/gc-render.ts +1 -0
- package/src/gjc-runtime/gc-runtime.ts +11 -1
- package/src/gjc-runtime/linux-proc.ts +70 -36
- package/src/gjc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/gjc-runtime/ralplan-runtime.ts +979 -71
- package/src/gjc-runtime/state-runtime.ts +50 -12
- package/src/gjc-runtime/state-writer.ts +70 -222
- package/src/gjc-runtime/team-launch.ts +34 -5
- package/src/gjc-runtime/team-runtime.ts +970 -33
- package/src/gjc-runtime/team-store.ts +32 -0
- package/src/gjc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/gjc-runtime/team-workers.ts +1 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +18 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +20 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +13 -7
- package/src/gjc-runtime/workflow-manifest.generated.json +97 -350
- package/src/gjc-runtime/workflow-manifest.ts +34 -179
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/hooks/skill-state.ts +1 -23
- package/src/internal-urls/docs-index.generated.ts +10 -11
- package/src/internal-urls/local-protocol.ts +23 -4
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/main.ts +9 -5
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +47 -0
- package/src/modes/acp/acp-agent.ts +597 -67
- package/src/modes/acp/acp-event-mapper.ts +106 -12
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +76 -5
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/oauth-selector.ts +42 -12
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/session-observer-overlay.ts +187 -32
- package/src/modes/components/settings-selector.ts +60 -18
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/components/tool-status-header.ts +1 -4
- package/src/modes/components/welcome.ts +18 -3
- package/src/modes/controllers/command-controller.ts +4 -0
- package/src/modes/controllers/event-controller.ts +109 -27
- package/src/modes/controllers/extension-ui-controller.ts +23 -12
- package/src/modes/controllers/input-controller.ts +62 -6
- package/src/modes/controllers/selector-controller.ts +83 -37
- package/src/modes/interactive-mode.ts +127 -29
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/runtime-init.ts +20 -2
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +94 -1
- package/src/prompts/agents/architect.md +9 -1
- package/src/prompts/agents/critic.md +9 -0
- package/src/prompts/agents/executor.md +1 -1
- package/src/prompts/system/system-prompt.md +0 -1
- package/src/prompts/tools/read.md +4 -2
- package/src/runtime/memory-domain.ts +66 -0
- package/src/runtime/memory-guard-contract.ts +73 -0
- package/src/runtime/memory-guard.ts +292 -0
- package/src/runtime/memory-limit.ts +50 -0
- package/src/runtime-mcp/manager.ts +26 -6
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/sdk/acp/adapter.ts +8 -1
- package/src/sdk/acp/final-text.ts +40 -0
- package/src/sdk/acp/mcp.ts +26 -0
- package/src/sdk/broker/lifecycle.ts +107 -7
- package/src/sdk/bus/chat-daemon-cli.ts +2 -2
- package/src/sdk/bus/chat-daemon-control.ts +4 -3
- package/src/sdk/bus/config.ts +1 -1
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/index.ts +503 -60
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/prompt-reconciliation.ts +2 -1
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/reconciliation-store.ts +224 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -4
- package/src/sdk/bus/telegram-daemon.ts +531 -53
- package/src/sdk/host/control/dispatch.ts +7 -1
- package/src/sdk/host/control/operations.ts +2 -1
- package/src/sdk/host/host.ts +25 -3
- package/src/sdk/host/query/handlers.ts +55 -0
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/protocol/operation-inventory.generated.json +76 -0
- package/src/sdk/protocol/operation-registry.ts +8 -2
- package/src/sdk/session.ts +25 -2
- package/src/sdk/startup-capability.ts +6 -0
- package/src/session/agent-session.ts +720 -190
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +40 -10
- package/src/session/internal/native-publish-outcome.ts +8 -3
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +1372 -42
- package/src/session/streaming-output.ts +262 -43
- package/src/setup/provider-onboarding.ts +19 -4
- package/src/skill-state/active-state.ts +0 -10
- package/src/skill-state/workflow-hud.ts +25 -2
- package/src/skill-state/workflow-mutation-guard.ts +65 -7
- package/src/task/executor.ts +11 -3
- package/src/task/index.ts +2 -0
- package/src/task/types.ts +22 -11
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/ask.ts +89 -69
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/fetch.ts +114 -23
- package/src/tools/output-meta.ts +176 -9
- package/src/tools/path-utils.ts +5 -1
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1542 -310
- package/src/tools/resource-gc.ts +665 -125
- package/src/tools/tool-result.ts +14 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/workflow/workflow-intent-diff.ts +4 -1
- package/dist/types/gjc-runtime/deep-interview-draft.d.ts +0 -8
- package/dist/types/gjc-runtime/deep-interview-payload.d.ts +0 -41
- package/dist/types/gjc-runtime/deep-interview-repair.d.ts +0 -17
- package/src/gjc-runtime/deep-interview-draft.ts +0 -843
- package/src/gjc-runtime/deep-interview-payload.ts +0 -262
- package/src/gjc-runtime/deep-interview-repair.ts +0 -1674
|
@@ -235,18 +235,16 @@ function getSavedUsageMode(): UsageMode {
|
|
|
235
235
|
return segmentOptions.usage?.mode === "remaining" ? "remaining" : "used";
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
function
|
|
238
|
+
function getUsageModeSettings(mode: string): StatusLineSegmentOptions {
|
|
239
239
|
const normalizedMode: UsageMode = mode === "remaining" ? "remaining" : "used";
|
|
240
240
|
const segmentOptions = settings.get("statusLine.segmentOptions") as StatusLineSegmentOptions;
|
|
241
|
-
|
|
241
|
+
return {
|
|
242
242
|
...segmentOptions,
|
|
243
243
|
usage: {
|
|
244
244
|
...(segmentOptions.usage ?? {}),
|
|
245
245
|
mode: normalizedMode,
|
|
246
246
|
},
|
|
247
247
|
};
|
|
248
|
-
settings.set("statusLine.segmentOptions", nextOptions as Record<string, unknown>);
|
|
249
|
-
return nextOptions;
|
|
250
248
|
}
|
|
251
249
|
|
|
252
250
|
function statusSegmentLabel(id: StatusLineSegmentId): string {
|
|
@@ -613,14 +611,17 @@ class StatusLineCustomEditor extends Container {
|
|
|
613
611
|
}
|
|
614
612
|
|
|
615
613
|
#save(): void {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
614
|
+
const saved = commitInteractiveSettings(this.callbacks, () => {
|
|
615
|
+
settings.set("statusLine.preset", "custom");
|
|
616
|
+
settings.set("statusLine.leftSegments", [...this.#draft.leftSegments]);
|
|
617
|
+
settings.set("statusLine.rightSegments", [...this.#draft.rightSegments]);
|
|
618
|
+
settings.set("statusLine.separator", this.#draft.separator);
|
|
619
|
+
settings.set(
|
|
620
|
+
"statusLine.segmentOptions",
|
|
621
|
+
cloneSegmentOptions(this.#draft.segmentOptions) as Record<string, unknown>,
|
|
622
|
+
);
|
|
623
|
+
});
|
|
624
|
+
if (!saved) return;
|
|
624
625
|
this.callbacks.onChange("statusLine.preset", "custom");
|
|
625
626
|
this.callbacks.onChange("statusLine.leftSegments", [...this.#draft.leftSegments]);
|
|
626
627
|
this.callbacks.onChange("statusLine.rightSegments", [...this.#draft.rightSegments]);
|
|
@@ -707,9 +708,29 @@ export interface SettingsCallbacks {
|
|
|
707
708
|
getStatusLinePreview?: (width?: number) => string;
|
|
708
709
|
/** Called when plugins change */
|
|
709
710
|
onPluginsChanged?: () => void;
|
|
711
|
+
/** Called when an interactive setting cannot be committed. */
|
|
712
|
+
onError?: (message: string) => void;
|
|
710
713
|
/** Called when settings panel is closed */
|
|
711
714
|
onCancel: () => void;
|
|
712
715
|
}
|
|
716
|
+
function commitInteractiveSettings(callbacks: SettingsCallbacks, commit: () => void): boolean {
|
|
717
|
+
if (!settings.canWriteDurableConfig()) {
|
|
718
|
+
callbacks.onError?.(
|
|
719
|
+
"Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
|
|
720
|
+
);
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
try {
|
|
724
|
+
commit();
|
|
725
|
+
return true;
|
|
726
|
+
} catch (error) {
|
|
727
|
+
if (!settings.canWriteDurableConfig()) {
|
|
728
|
+
callbacks.onError?.(error instanceof Error ? error.message : String(error));
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
throw error;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
713
734
|
|
|
714
735
|
/**
|
|
715
736
|
* Main tabbed settings selector component.
|
|
@@ -994,11 +1015,18 @@ export class SettingsSelectorComponent extends Container {
|
|
|
994
1015
|
// The shared pet commit policy rechecks capability immediately
|
|
995
1016
|
// before mutation and persists only on acceptance; the settings
|
|
996
1017
|
// surface must not persist ahead of that result.
|
|
997
|
-
|
|
1018
|
+
let accepted = false;
|
|
1019
|
+
if (
|
|
1020
|
+
!commitInteractiveSettings(this.callbacks, () => {
|
|
1021
|
+
accepted = this.callbacks.onPetCommit?.(value) ?? false;
|
|
1022
|
+
})
|
|
1023
|
+
) {
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
998
1026
|
done(accepted ? value : undefined);
|
|
999
1027
|
return;
|
|
1000
1028
|
}
|
|
1001
|
-
this.#setSettingValue(def.path, value);
|
|
1029
|
+
if (!commitInteractiveSettings(this.callbacks, () => this.#setSettingValue(def.path, value))) return;
|
|
1002
1030
|
this.callbacks.onChange(def.path, value);
|
|
1003
1031
|
done(value);
|
|
1004
1032
|
},
|
|
@@ -1031,7 +1059,7 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1031
1059
|
value => {
|
|
1032
1060
|
// Empty string clears the setting; undefined-typed string settings
|
|
1033
1061
|
// store "" which the browser.ts expandPath ignores (no-op fallback).
|
|
1034
|
-
this.#setSettingValue(def.path, value);
|
|
1062
|
+
if (!commitInteractiveSettings(this.callbacks, () => this.#setSettingValue(def.path, value))) return;
|
|
1035
1063
|
this.callbacks.onChange(def.path, value);
|
|
1036
1064
|
wrappedDone(value);
|
|
1037
1065
|
},
|
|
@@ -1089,7 +1117,15 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1089
1117
|
getSettingsListTheme(),
|
|
1090
1118
|
(id, newValue) => {
|
|
1091
1119
|
if (id === STATUS_LINE_USAGE_MODE_ID) {
|
|
1092
|
-
const segmentOptions =
|
|
1120
|
+
const segmentOptions = getUsageModeSettings(newValue);
|
|
1121
|
+
if (
|
|
1122
|
+
!commitInteractiveSettings(this.callbacks, () => {
|
|
1123
|
+
settings.set("statusLine.segmentOptions", segmentOptions as Record<string, unknown>);
|
|
1124
|
+
})
|
|
1125
|
+
) {
|
|
1126
|
+
this.#refreshCurrentTabItems(defs);
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1093
1129
|
this.callbacks.onChange("statusLine.segmentOptions", segmentOptions);
|
|
1094
1130
|
if (tabId === "appearance") {
|
|
1095
1131
|
this.#triggerStatusLinePreview();
|
|
@@ -1105,14 +1141,20 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1105
1141
|
|
|
1106
1142
|
if (def.type === "boolean") {
|
|
1107
1143
|
const boolValue = newValue === "true";
|
|
1108
|
-
settings.set(path, boolValue as never)
|
|
1144
|
+
if (!commitInteractiveSettings(this.callbacks, () => settings.set(path, boolValue as never))) {
|
|
1145
|
+
this.#refreshCurrentTabItems(defs);
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1109
1148
|
this.callbacks.onChange(path, boolValue);
|
|
1110
1149
|
|
|
1111
1150
|
if (tabId === "appearance") {
|
|
1112
1151
|
this.#triggerStatusLinePreview();
|
|
1113
1152
|
}
|
|
1114
1153
|
} else if (def.type === "enum") {
|
|
1115
|
-
settings.set(path, newValue as never)
|
|
1154
|
+
if (!commitInteractiveSettings(this.callbacks, () => settings.set(path, newValue as never))) {
|
|
1155
|
+
this.#refreshCurrentTabItems(defs);
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1116
1158
|
this.callbacks.onChange(path, newValue);
|
|
1117
1159
|
}
|
|
1118
1160
|
// Submenu/text types already persisted the value inside their own
|
|
@@ -129,6 +129,21 @@ function stabilizeStreamingPreviews(previews: PerFileDiffPreview[]): PerFileDiff
|
|
|
129
129
|
return changed ? next : previews;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
function previewsAreEqual(a: PerFileDiffPreview[] | undefined, b: PerFileDiffPreview[]): boolean {
|
|
133
|
+
return (
|
|
134
|
+
a?.length === b.length &&
|
|
135
|
+
a.every((preview, index) => {
|
|
136
|
+
const next = b[index];
|
|
137
|
+
return (
|
|
138
|
+
preview.path === next?.path &&
|
|
139
|
+
preview.diff === next.diff &&
|
|
140
|
+
preview.error === next.error &&
|
|
141
|
+
preview.firstChangedLine === next.firstChangedLine
|
|
142
|
+
);
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
132
147
|
function isEditLikeToolName(toolName: string): boolean {
|
|
133
148
|
return toolName === "edit" || toolName === "apply_patch";
|
|
134
149
|
}
|
|
@@ -144,6 +159,8 @@ export interface ToolExecutionOptions {
|
|
|
144
159
|
editFuzzyThreshold?: number;
|
|
145
160
|
editAllowFuzzy?: boolean;
|
|
146
161
|
hashlineAutoDropPureInsertDuplicates?: boolean;
|
|
162
|
+
/** Internal observer for visible asynchronous renderer mutations. */
|
|
163
|
+
onVisibleTranscriptMutation?: () => void;
|
|
147
164
|
}
|
|
148
165
|
|
|
149
166
|
export interface ToolExecutionHandle {
|
|
@@ -168,6 +185,11 @@ export interface ToolExecutionHandle {
|
|
|
168
185
|
* back to {@link setExpanded}.
|
|
169
186
|
*/
|
|
170
187
|
setManuallyExpanded?(expanded: boolean): void;
|
|
188
|
+
/**
|
|
189
|
+
* Internal capability for dispatchers that need exact synchronous visible-output
|
|
190
|
+
* detection. Optional so existing structural handles remain source compatible.
|
|
191
|
+
*/
|
|
192
|
+
consumeVisibleTranscriptChange?(): boolean;
|
|
171
193
|
}
|
|
172
194
|
|
|
173
195
|
/**
|
|
@@ -213,8 +235,11 @@ export class ToolExecutionComponent extends Container {
|
|
|
213
235
|
#argsIdentityVersion = 0;
|
|
214
236
|
#lastArgsReference: any;
|
|
215
237
|
#shareArgsWithRenderer = false;
|
|
216
|
-
// Cached converted images for Kitty protocol (which requires PNG), keyed by index
|
|
217
|
-
#convertedImages: Map<number, { data: string; mimeType: string }> = new Map();
|
|
238
|
+
// Cached converted images for Kitty protocol (which requires PNG), keyed by index and source.
|
|
239
|
+
#convertedImages: Map<number, { data: string; mimeType: string; source: string }> = new Map();
|
|
240
|
+
#imageConversionGenerations = new Map<number, number>();
|
|
241
|
+
#imageConversionsInFlight = new Map<number, string>();
|
|
242
|
+
|
|
218
243
|
// Spinner animation for partial task results
|
|
219
244
|
#spinnerFrame?: number;
|
|
220
245
|
#spinnerAnimation?: AnimationRegistration;
|
|
@@ -229,6 +254,10 @@ export class ToolExecutionComponent extends Container {
|
|
|
229
254
|
expanded: false,
|
|
230
255
|
isPartial: true,
|
|
231
256
|
};
|
|
257
|
+
#onVisibleTranscriptMutation?: () => void;
|
|
258
|
+
#visibleTranscriptChanged = false;
|
|
259
|
+
#visibleProjection = "";
|
|
260
|
+
#disposed = false;
|
|
232
261
|
|
|
233
262
|
constructor(
|
|
234
263
|
toolName: string,
|
|
@@ -246,6 +275,8 @@ export class ToolExecutionComponent extends Container {
|
|
|
246
275
|
this.#editFuzzyThreshold = options.editFuzzyThreshold;
|
|
247
276
|
this.#editAllowFuzzy = options.editAllowFuzzy;
|
|
248
277
|
this.#hashlineAutoDropPureInsertDuplicates = options.hashlineAutoDropPureInsertDuplicates;
|
|
278
|
+
this.#onVisibleTranscriptMutation = options.onVisibleTranscriptMutation;
|
|
279
|
+
|
|
249
280
|
this.#tool = tool;
|
|
250
281
|
this.#ui = ui;
|
|
251
282
|
this.#cwd = cwd;
|
|
@@ -273,18 +304,23 @@ export class ToolExecutionComponent extends Container {
|
|
|
273
304
|
this.#editMode = resolveEditModeForTool(toolName, tool);
|
|
274
305
|
|
|
275
306
|
this.#updateDisplay();
|
|
307
|
+
this.#visibleProjection = this.#captureLogicalVisibleProjection();
|
|
308
|
+
|
|
276
309
|
void this.#runPreviewDiff();
|
|
277
310
|
}
|
|
278
311
|
|
|
279
312
|
updateArgs(args: any, _toolCallId?: string): void {
|
|
313
|
+
const argsChanged = !Bun.deepEquals(this.#args, args);
|
|
314
|
+
if (!argsChanged && !this.#editMode) return;
|
|
280
315
|
if (args !== this.#lastArgsReference) {
|
|
281
316
|
this.#lastArgsReference = args;
|
|
282
317
|
this.#argsIdentityVersion += 1;
|
|
283
318
|
}
|
|
284
|
-
this.#args = this.#shareArgsWithRenderer ? args : cloneToolArgs(args);
|
|
319
|
+
this.#args = argsChanged ? (this.#shareArgsWithRenderer ? args : cloneToolArgs(args)) : this.#args;
|
|
285
320
|
this.#updateSpinnerAnimation();
|
|
286
321
|
void this.#runPreviewDiff();
|
|
287
322
|
this.#updateDisplay();
|
|
323
|
+
this.#markVisibleMutationIfChanged();
|
|
288
324
|
}
|
|
289
325
|
|
|
290
326
|
/**
|
|
@@ -295,6 +331,8 @@ export class ToolExecutionComponent extends Container {
|
|
|
295
331
|
this.#argsComplete = true;
|
|
296
332
|
this.#updateSpinnerAnimation();
|
|
297
333
|
void this.#runPreviewDiff();
|
|
334
|
+
this.#updateDisplay();
|
|
335
|
+
this.#markVisibleMutationIfChanged();
|
|
298
336
|
}
|
|
299
337
|
|
|
300
338
|
async #runPreviewDiff(): Promise<void> {
|
|
@@ -338,14 +376,17 @@ export class ToolExecutionComponent extends Container {
|
|
|
338
376
|
hashlineAutoDropPureInsertDuplicates: this.#hashlineAutoDropPureInsertDuplicates,
|
|
339
377
|
isStreaming,
|
|
340
378
|
});
|
|
341
|
-
if (controller.signal.aborted) return;
|
|
379
|
+
if (this.#disposed || controller.signal.aborted) return;
|
|
342
380
|
if (previews) {
|
|
343
|
-
|
|
381
|
+
const nextPreview = isStreaming ? stabilizeStreamingPreviews(previews) : previews;
|
|
382
|
+
const changed = !previewsAreEqual(this.#editDiffPreview, nextPreview);
|
|
383
|
+
this.#editDiffPreview = nextPreview;
|
|
344
384
|
this.#updateDisplay();
|
|
345
385
|
this.#ui.requestRender();
|
|
386
|
+
if (changed) this.#markVisibleMutationIfChanged(true);
|
|
346
387
|
}
|
|
347
388
|
} catch (err) {
|
|
348
|
-
if (controller.signal.aborted) return;
|
|
389
|
+
if (this.#disposed || controller.signal.aborted) return;
|
|
349
390
|
logger.warn("Edit preview diff failed", { tool: this.#toolName, error: String(err) });
|
|
350
391
|
}
|
|
351
392
|
}
|
|
@@ -359,8 +400,10 @@ export class ToolExecutionComponent extends Container {
|
|
|
359
400
|
isPartial = false,
|
|
360
401
|
_toolCallId?: string,
|
|
361
402
|
): void {
|
|
403
|
+
if (this.#isPartial === isPartial && Bun.deepEquals(this.#result, result)) return;
|
|
362
404
|
this.#textOutputCache = undefined;
|
|
363
405
|
this.#result = result;
|
|
406
|
+
this.#invalidateStaleKittyConversions();
|
|
364
407
|
this.#isPartial = isPartial;
|
|
365
408
|
// When tool is complete, ensure args are marked complete so spinner stops
|
|
366
409
|
if (!isPartial) {
|
|
@@ -370,6 +413,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
370
413
|
this.#updateDisplay();
|
|
371
414
|
// Convert non-PNG images to PNG for Kitty protocol (async)
|
|
372
415
|
this.#maybeConvertImagesForKitty();
|
|
416
|
+
this.#markVisibleMutationIfChanged();
|
|
373
417
|
}
|
|
374
418
|
|
|
375
419
|
/**
|
|
@@ -383,40 +427,101 @@ export class ToolExecutionComponent extends Container {
|
|
|
383
427
|
return [...contentImages, ...detailImages];
|
|
384
428
|
}
|
|
385
429
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
*/
|
|
390
|
-
#maybeConvertImagesForKitty(): void {
|
|
391
|
-
// Only needed for Kitty protocol
|
|
392
|
-
if (TERMINAL.imageProtocol !== ImageProtocol.Kitty) return;
|
|
393
|
-
if (!this.#result) return;
|
|
394
|
-
|
|
395
|
-
const imageBlocks = this.#getAllImageBlocks();
|
|
430
|
+
#imageSource(image: { data?: string; mimeType?: string }): string | undefined {
|
|
431
|
+
return image.data && image.mimeType ? `${image.mimeType}\u0000${image.data}` : undefined;
|
|
432
|
+
}
|
|
396
433
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (
|
|
434
|
+
#invalidateStaleKittyConversions(): void {
|
|
435
|
+
const images = this.#getAllImageBlocks();
|
|
436
|
+
const indices = new Set<number>();
|
|
437
|
+
for (let index = 0; index < images.length; index++) {
|
|
438
|
+
const source = this.#imageSource(images[index]);
|
|
439
|
+
if (!source) continue;
|
|
440
|
+
indices.add(index);
|
|
441
|
+
if (this.#convertedImages.get(index)?.source !== source) this.#convertedImages.delete(index);
|
|
442
|
+
if (this.#imageConversionsInFlight.get(index) !== source) {
|
|
443
|
+
this.#imageConversionsInFlight.delete(index);
|
|
444
|
+
this.#imageConversionGenerations.set(index, (this.#imageConversionGenerations.get(index) ?? 0) + 1);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
for (const index of this.#convertedImages.keys()) {
|
|
448
|
+
if (!indices.has(index)) this.#convertedImages.delete(index);
|
|
449
|
+
}
|
|
450
|
+
for (const index of this.#imageConversionsInFlight.keys()) {
|
|
451
|
+
if (!indices.has(index)) {
|
|
452
|
+
this.#imageConversionsInFlight.delete(index);
|
|
453
|
+
this.#imageConversionGenerations.set(index, (this.#imageConversionGenerations.get(index) ?? 0) + 1);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
403
457
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
458
|
+
/** Convert non-PNG images to PNG for Kitty graphics protocol. */
|
|
459
|
+
#maybeConvertImagesForKitty(): void {
|
|
460
|
+
if (TERMINAL.imageProtocol !== ImageProtocol.Kitty) return;
|
|
461
|
+
for (const [index, image] of this.#getAllImageBlocks().entries()) {
|
|
462
|
+
const source = this.#imageSource(image);
|
|
463
|
+
if (!source || image.mimeType === "image/png") continue;
|
|
464
|
+
if (
|
|
465
|
+
this.#convertedImages.get(index)?.source === source ||
|
|
466
|
+
this.#imageConversionsInFlight.get(index) === source
|
|
467
|
+
)
|
|
468
|
+
continue;
|
|
469
|
+
const generation = this.#imageConversionGenerations.get(index) ?? 0;
|
|
470
|
+
this.#imageConversionsInFlight.set(index, source);
|
|
471
|
+
new Bun.Image(Buffer.from(image.data!, "base64"))
|
|
407
472
|
.png()
|
|
408
473
|
.toBase64()
|
|
409
474
|
.then(data => {
|
|
410
|
-
|
|
475
|
+
if (
|
|
476
|
+
this.#disposed ||
|
|
477
|
+
this.#imageConversionGenerations.get(index) !== generation ||
|
|
478
|
+
this.#imageConversionsInFlight.get(index) !== source ||
|
|
479
|
+
this.#imageSource(this.#getAllImageBlocks()[index] ?? {}) !== source
|
|
480
|
+
)
|
|
481
|
+
return;
|
|
482
|
+
this.#imageConversionsInFlight.delete(index);
|
|
483
|
+
this.#convertedImages.set(index, { data, mimeType: "image/png", source });
|
|
411
484
|
this.#updateDisplay();
|
|
412
485
|
this.#ui.requestRender();
|
|
486
|
+
this.#markVisibleMutationIfChanged(true);
|
|
413
487
|
})
|
|
414
488
|
.catch(() => {
|
|
415
|
-
|
|
489
|
+
if (
|
|
490
|
+
this.#disposed ||
|
|
491
|
+
this.#imageConversionGenerations.get(index) !== generation ||
|
|
492
|
+
this.#imageConversionsInFlight.get(index) !== source
|
|
493
|
+
)
|
|
494
|
+
return;
|
|
495
|
+
this.#imageConversionsInFlight.delete(index);
|
|
416
496
|
});
|
|
417
497
|
}
|
|
418
498
|
}
|
|
419
499
|
|
|
500
|
+
#captureLogicalVisibleProjection(): string {
|
|
501
|
+
return this.render(10_000).join("\n");
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
#markVisibleMutationIfChanged(notify = false): void {
|
|
505
|
+
const projection = this.#captureLogicalVisibleProjection();
|
|
506
|
+
if (projection === this.#visibleProjection) return;
|
|
507
|
+
this.#visibleProjection = projection;
|
|
508
|
+
this.#markVisibleMutation(notify);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
#markVisibleMutation(notify = false): void {
|
|
512
|
+
if (notify) {
|
|
513
|
+
this.#onVisibleTranscriptMutation?.();
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
this.#visibleTranscriptChanged = true;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
consumeVisibleTranscriptChange(): boolean {
|
|
520
|
+
const changed = this.#visibleTranscriptChanged;
|
|
521
|
+
this.#visibleTranscriptChanged = false;
|
|
522
|
+
return changed;
|
|
523
|
+
}
|
|
524
|
+
|
|
420
525
|
/**
|
|
421
526
|
* Start or stop spinner animation based on whether this is a partial task result.
|
|
422
527
|
*/
|
|
@@ -430,6 +535,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
430
535
|
const needsSpinner = isStreamingArgs || isPartialTask;
|
|
431
536
|
if (needsSpinner && !this.#spinnerAnimation) {
|
|
432
537
|
this.#spinnerAnimation = registerAnimationCallback(() => {
|
|
538
|
+
if (this.#disposed) return;
|
|
433
539
|
const frameCount = theme.spinnerFrames.length;
|
|
434
540
|
if (frameCount === 0) return;
|
|
435
541
|
this.#spinnerFrame = ((this.#spinnerFrame ?? -1) + 1) % frameCount;
|
|
@@ -456,6 +562,11 @@ export class ToolExecutionComponent extends Container {
|
|
|
456
562
|
}
|
|
457
563
|
|
|
458
564
|
override dispose(): void {
|
|
565
|
+
if (this.#disposed) return;
|
|
566
|
+
this.#disposed = true;
|
|
567
|
+
this.#imageConversionGenerations.clear();
|
|
568
|
+
this.#imageConversionsInFlight.clear();
|
|
569
|
+
this.#convertedImages.clear();
|
|
459
570
|
this.stopAnimation();
|
|
460
571
|
super.dispose();
|
|
461
572
|
}
|
|
@@ -70,16 +70,13 @@ export interface StatusLineActionHint {
|
|
|
70
70
|
|
|
71
71
|
const ACTION_HINT_PRIORITY: readonly AppKeybinding[] = [
|
|
72
72
|
"app.message.sendNow",
|
|
73
|
-
"app.message.queue",
|
|
74
73
|
"app.message.followUp",
|
|
74
|
+
"app.message.queue",
|
|
75
75
|
"app.message.dequeue",
|
|
76
76
|
"app.commandPalette.open",
|
|
77
77
|
"app.plan.toggle",
|
|
78
78
|
"app.mode.cycle",
|
|
79
|
-
"app.thinking.cycle",
|
|
80
|
-
"app.model.select",
|
|
81
79
|
"app.model.cycleForward",
|
|
82
|
-
"app.history.search",
|
|
83
80
|
"app.session.togglePath",
|
|
84
81
|
"app.session.toggleSort",
|
|
85
82
|
"app.session.rename",
|
|
@@ -20,6 +20,7 @@ export interface WelcomeComponentOptions {
|
|
|
20
20
|
getViewportRows?: () => number | undefined;
|
|
21
21
|
getReservedBottomRows?: (termWidth: number) => number;
|
|
22
22
|
changelogMarkdown?: string;
|
|
23
|
+
rightGutterWidth?: number;
|
|
23
24
|
collapseChangelog?: boolean;
|
|
24
25
|
buildLabel?: string;
|
|
25
26
|
keyDisplayContext?: KeyDisplayContext;
|
|
@@ -110,7 +111,8 @@ export class WelcomeComponent implements Component {
|
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
render(termWidth: number): string[] {
|
|
113
|
-
const
|
|
114
|
+
const rightGutterWidth = this.#rightGutterWidth(termWidth);
|
|
115
|
+
const boxWidth = Math.max(0, termWidth - rightGutterWidth);
|
|
114
116
|
if (boxWidth < 4) {
|
|
115
117
|
return [];
|
|
116
118
|
}
|
|
@@ -245,7 +247,7 @@ export class WelcomeComponent implements Component {
|
|
|
245
247
|
lines.push(tl + titleStyled + theme.fg("dim", hChar.repeat(afterTitle)) + tr);
|
|
246
248
|
}
|
|
247
249
|
if (outputRows === 1) {
|
|
248
|
-
return lines;
|
|
250
|
+
return this.#withRightGutter(lines, rightGutterWidth);
|
|
249
251
|
}
|
|
250
252
|
|
|
251
253
|
if (showRightColumn) {
|
|
@@ -273,7 +275,7 @@ export class WelcomeComponent implements Component {
|
|
|
273
275
|
lines.push(bl + h.repeat(leftCol) + br);
|
|
274
276
|
}
|
|
275
277
|
|
|
276
|
-
return lines;
|
|
278
|
+
return this.#withRightGutter(lines, rightGutterWidth);
|
|
277
279
|
}
|
|
278
280
|
|
|
279
281
|
/** Center text within a given width */
|
|
@@ -295,6 +297,19 @@ export class WelcomeComponent implements Component {
|
|
|
295
297
|
}
|
|
296
298
|
return str + padding(width - visLen);
|
|
297
299
|
}
|
|
300
|
+
#rightGutterWidth(termWidth: number): number {
|
|
301
|
+
const configured = this.options.rightGutterWidth ?? 0;
|
|
302
|
+
if (!Number.isFinite(configured) || configured <= 0) return 0;
|
|
303
|
+
const gutterWidth = Math.floor(configured);
|
|
304
|
+
return Math.min(gutterWidth, Math.max(0, termWidth - 4));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
#withRightGutter(lines: string[], rightGutterWidth: number): string[] {
|
|
308
|
+
if (rightGutterWidth <= 0) return lines;
|
|
309
|
+
const gutter = padding(rightGutterWidth);
|
|
310
|
+
return lines.map(line => line + gutter);
|
|
311
|
+
}
|
|
312
|
+
|
|
298
313
|
#targetRows(termWidth: number): number | undefined {
|
|
299
314
|
const viewportRows = this.options.getViewportRows?.();
|
|
300
315
|
if (typeof viewportRows !== "number" || !Number.isFinite(viewportRows) || viewportRows <= 0) {
|
|
@@ -65,6 +65,8 @@ export function buildHelpMarkdown(keybindings: Pick<KeybindingsManager, "getAcce
|
|
|
65
65
|
const displayKey = (action: Keybinding): string => keybindings.getAccessibleDisplayString(action) || "Disabled";
|
|
66
66
|
const sessionNewKey = formatHotkeyMarkdownCode(displayKey("app.session.new"));
|
|
67
67
|
const selectModelKey = formatHotkeyMarkdownCode(displayKey("app.model.select"));
|
|
68
|
+
const queueMessageKey = formatHotkeyMarkdownCode(displayKey("app.message.queue"));
|
|
69
|
+
const dequeueMessageKey = formatHotkeyMarkdownCode(displayKey("app.message.dequeue"));
|
|
68
70
|
const autocompleteNavigationKeys = formatHotkeyMarkdownCode(
|
|
69
71
|
`${displayKey("tui.select.up")}/${displayKey("tui.select.down")}`,
|
|
70
72
|
false,
|
|
@@ -84,6 +86,8 @@ export function buildHelpMarkdown(keybindings: Pick<KeybindingsManager, "getAcce
|
|
|
84
86
|
"| Show session details | `/session info` |",
|
|
85
87
|
"| Delete current session transcript/artifacts | `/session delete` |",
|
|
86
88
|
`| Select a model | \`/model\` or ${selectModelKey} |`,
|
|
89
|
+
`| Queue a message for the next turn | ${queueMessageKey} |`,
|
|
90
|
+
`| Select or edit a queued message | ${dequeueMessageKey} |`,
|
|
87
91
|
"| Show all shortcuts | `?` on an empty prompt or `/hotkeys` |",
|
|
88
92
|
"",
|
|
89
93
|
"**Finding commands**",
|