@gajae-code/coding-agent 0.12.0 → 0.12.1
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 +82 -1
- package/dist/types/async/job-manager.d.ts +17 -3
- package/dist/types/config/settings-schema.d.ts +54 -0
- package/dist/types/exa/mcp-client.d.ts +10 -1
- package/dist/types/exec/bash-executor.d.ts +45 -12
- package/dist/types/extensibility/extensions/types.d.ts +16 -2
- package/dist/types/extensibility/gjc-plugins/index.d.ts +12 -2
- package/dist/types/extensibility/gjc-plugins/installer.d.ts +63 -7
- package/dist/types/extensibility/gjc-plugins/lifecycle-reconciliation.d.ts +55 -0
- package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +64 -0
- package/dist/types/extensibility/gjc-plugins/runtime-quarantine.d.ts +67 -0
- package/dist/types/extensibility/gjc-plugins/session-validation.d.ts +3 -1
- package/dist/types/extensibility/gjc-plugins/types.d.ts +124 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +18 -0
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +25 -0
- package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +43 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +10 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +31 -80
- package/dist/types/modes/acp/acp-agent.d.ts +2 -19
- package/dist/types/modes/components/gjc-bundle-settings.d.ts +34 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +1 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +18 -0
- package/dist/types/modes/components/settings-selector.d.ts +9 -0
- package/dist/types/modes/components/status-line/gh.d.ts +5 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +18 -1
- package/dist/types/reminders/star-reminder.d.ts +3 -13
- package/dist/types/runtime-mcp/manager.d.ts +1 -0
- package/dist/types/sdk/acp/adapter.d.ts +3 -1
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/attachment-registry.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +4 -1
- package/dist/types/sdk/bus/config.d.ts +6 -2
- package/dist/types/sdk/bus/index.d.ts +29 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +8 -1
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +7 -3
- package/dist/types/sdk/bus/rich-draft.d.ts +1 -0
- package/dist/types/sdk/bus/rich-render.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +165 -0
- package/dist/types/sdk/bus/telegram-cli.d.ts +12 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +11 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -1
- package/dist/types/sdk/bus/telegram-reference.d.ts +11 -1
- package/dist/types/sdk/bus/threaded-render.d.ts +3 -2
- package/dist/types/sdk/bus/topic-registry.d.ts +11 -1
- package/dist/types/sdk/host/query/revision-store.d.ts +2 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
- package/dist/types/sdk/prompt-status.d.ts +23 -3
- package/dist/types/sdk/session.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +38 -2
- package/dist/types/session/artifacts.d.ts +8 -0
- package/dist/types/session/blob-store.d.ts +57 -4
- package/dist/types/session/internal/managed-session-storage.d.ts +8 -1
- package/dist/types/session/session-manager.d.ts +23 -6
- package/dist/types/session/session-storage.d.ts +4 -0
- package/dist/types/session/streaming-output.d.ts +36 -7
- package/dist/types/setup/credential-import.d.ts +12 -0
- package/dist/types/skill-state/active-state.d.ts +12 -0
- package/dist/types/skill-state/workflow-hud.d.ts +6 -0
- package/dist/types/task/executor.d.ts +3 -2
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/provider-retry-status.d.ts +8 -0
- package/dist/types/task/receipt.d.ts +7 -1
- package/dist/types/task/types.d.ts +23 -0
- package/dist/types/tools/ask-answer-registry.d.ts +2 -2
- package/dist/types/tools/bash-interactive.d.ts +4 -1
- package/dist/types/tools/bash.d.ts +3 -1
- package/dist/types/tools/image-gen.d.ts +4 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/output-meta.d.ts +23 -2
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/read.d.ts +0 -6
- package/dist/types/tools/review.d.ts +5 -7
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/utils/gh.d.ts +13 -0
- package/dist/types/utils/image-resize.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +2 -0
- package/dist/types/web/search/providers/searxng.d.ts +7 -0
- package/dist/types/web/search/providers/xai.d.ts +2 -0
- package/package.json +10 -8
- package/scripts/acp-conformance-agent.ts +218 -0
- package/scripts/capture-gjc-bundle-settings.ts +306 -0
- package/scripts/generate-sdk-operation-inventory.ts +3 -0
- package/scripts/resident-memory-bench.ts +1341 -0
- package/scripts/run-acp-conformance.ts +285 -0
- package/src/async/job-manager.ts +78 -20
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/plugin-cli.ts +176 -18
- package/src/commands/team.ts +19 -1
- package/src/config/file-lock.ts +11 -2
- package/src/config/settings-schema.ts +51 -0
- package/src/coordinator-mcp/question-gate-codec.ts +1 -1
- package/src/coordinator-mcp/server.ts +5 -12
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -0
- package/src/defaults/gjc/skills/ralplan/SKILL.md +19 -16
- package/src/defaults/gjc/skills/team/SKILL.md +2 -0
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +37 -24
- package/src/defaults/gjc/skills/ultragoal/validation-batch-contracts.md +12 -0
- package/src/defaults/gjc-defaults.ts +4 -4
- package/src/exa/mcp-client.ts +12 -3
- package/src/exec/bash-executor.ts +144 -27
- package/src/extensibility/extensions/runner.ts +15 -0
- package/src/extensibility/extensions/types.ts +18 -1
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +8 -2
- package/src/extensibility/gjc-plugins/index.ts +19 -2
- package/src/extensibility/gjc-plugins/installer.ts +189 -50
- package/src/extensibility/gjc-plugins/lifecycle-reconciliation.ts +199 -0
- package/src/extensibility/gjc-plugins/lifecycle.ts +626 -0
- package/src/extensibility/gjc-plugins/observability.ts +3 -3
- package/src/extensibility/gjc-plugins/prompt-appendix.ts +5 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +12 -2
- package/src/extensibility/gjc-plugins/runtime-quarantine.ts +121 -0
- package/src/extensibility/gjc-plugins/schema.ts +84 -12
- package/src/extensibility/gjc-plugins/session-validation.ts +9 -3
- package/src/extensibility/gjc-plugins/types.ts +141 -0
- package/src/extensibility/gjc-plugins/validation.ts +9 -0
- package/src/gjc-runtime/launch-tmux.ts +25 -6
- package/src/gjc-runtime/managed-owner-admission.ts +1 -1
- package/src/gjc-runtime/ralplan-runtime.ts +411 -17
- package/src/gjc-runtime/team-launch.ts +8 -0
- package/src/gjc-runtime/team-runtime.ts +140 -32
- package/src/gjc-runtime/team-worker-heartbeat.ts +149 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +10 -0
- package/src/gjc-runtime/tmux-sessions.ts +58 -13
- package/src/gjc-runtime/ultragoal-runtime.ts +401 -611
- package/src/gjc-runtime/workflow-manifest.generated.json +8 -99
- package/src/gjc-runtime/workflow-manifest.ts +4 -68
- package/src/internal-urls/docs-index.generated.ts +15 -15
- package/src/modes/DESIGN.md +51 -0
- package/src/modes/acp/acp-agent.ts +298 -186
- package/src/modes/components/gjc-bundle-settings.ts +508 -0
- package/src/modes/components/notifications-settings-editor.ts +12 -1
- package/src/modes/components/oauth-selector.ts +68 -30
- package/src/modes/components/session-observer-overlay.ts +305 -86
- package/src/modes/components/settings-selector.ts +56 -5
- package/src/modes/components/status-line/gh.ts +16 -0
- package/src/modes/components/tool-status-header.ts +3 -14
- package/src/modes/components/transcript-viewer-overlay.ts +99 -24
- package/src/modes/controllers/command-controller.ts +4 -0
- package/src/modes/controllers/extension-ui-controller.ts +7 -0
- package/src/modes/controllers/selector-controller.ts +45 -0
- package/src/modes/runtime-init.ts +3 -0
- package/src/prompts/agent-fragments/ralplan-persistence.md +4 -3
- package/src/prompts/system/eager-todo.md +8 -4
- package/src/prompts/system/system-prompt.md +11 -4
- package/src/prompts/tools/bash.md +5 -5
- package/src/prompts/tools/search-tool-bm25.md +5 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/reminders/star-reminder.ts +4 -42
- package/src/runtime-mcp/manager.ts +19 -4
- package/src/sdk/acp/adapter.ts +22 -6
- package/src/sdk/broker/transport.ts +35 -3
- package/src/sdk/bus/attachment-registry.ts +5 -1
- package/src/sdk/bus/chat-daemon-cli.ts +4 -39
- package/src/sdk/bus/chat-daemon-control.ts +6 -3
- package/src/sdk/bus/config.ts +18 -2
- package/src/sdk/bus/index.ts +661 -126
- package/src/sdk/bus/kind-aware-reconciliation.ts +148 -73
- package/src/sdk/bus/lifecycle-commands.ts +4 -1
- package/src/sdk/bus/lifecycle-control-runtime.ts +403 -88
- package/src/sdk/bus/reconciliation-store.ts +95 -16
- package/src/sdk/bus/rich-draft.ts +1 -1
- package/src/sdk/bus/rich-render.ts +2 -2
- package/src/sdk/bus/telegram-adoption-intent.ts +574 -0
- package/src/sdk/bus/telegram-cli.ts +33 -8
- package/src/sdk/bus/telegram-daemon-cli.ts +1 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +11 -4
- package/src/sdk/bus/telegram-daemon.ts +916 -23
- package/src/sdk/bus/telegram-reference.ts +40 -3
- package/src/sdk/bus/threaded-render.ts +48 -3
- package/src/sdk/bus/topic-registry.ts +20 -0
- package/src/sdk/host/query/revision-store.ts +36 -2
- package/src/sdk/host/reverse-leases.ts +44 -7
- package/src/sdk/prompt-status.ts +16 -3
- package/src/sdk/protocol/operation-inventory.generated.json +22 -0
- package/src/sdk/session.ts +88 -9
- package/src/session/agent-session.ts +822 -172
- package/src/session/artifacts.ts +22 -0
- package/src/session/auth-broker-config.ts +10 -3
- package/src/session/blob-store.ts +915 -24
- package/src/session/internal/managed-session-scope.ts +44 -6
- package/src/session/internal/managed-session-storage.ts +48 -6
- package/src/session/session-manager.ts +1614 -435
- package/src/session/session-storage.ts +9 -1
- package/src/session/streaming-output.ts +238 -68
- package/src/setup/credential-import.ts +75 -13
- package/src/skill-state/active-state.ts +32 -0
- package/src/skill-state/workflow-hud.ts +17 -0
- package/src/skill-state/workflow-mutation-guard.ts +496 -2
- package/src/task/executor.ts +122 -39
- package/src/task/index.ts +23 -14
- package/src/task/provider-retry-status.ts +16 -0
- package/src/task/receipt.ts +23 -7
- package/src/task/render.ts +6 -2
- package/src/task/types.ts +108 -0
- package/src/tools/ask-answer-registry.ts +12 -6
- package/src/tools/ask.ts +86 -55
- package/src/tools/ast-edit.ts +1 -0
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/bash-interactive.ts +8 -3
- package/src/tools/bash.ts +503 -60
- package/src/tools/find.ts +1 -0
- package/src/tools/image-gen.ts +29 -5
- package/src/tools/index.ts +4 -0
- package/src/tools/output-meta.ts +108 -14
- package/src/tools/path-utils.ts +3 -2
- package/src/tools/read.ts +18 -0
- package/src/tools/review.ts +5 -7
- package/src/tools/search.ts +1 -0
- package/src/tools/subagent-render.ts +6 -2
- package/src/tools/subagent.ts +6 -0
- package/src/tools/telegram-send.ts +6 -3
- package/src/utils/gh.ts +39 -0
- package/src/utils/image-resize.ts +3 -0
- package/src/web/search/providers/anthropic.ts +4 -4
- package/src/web/search/providers/kimi.ts +15 -5
- package/src/web/search/providers/searxng.ts +47 -4
- package/src/web/search/providers/xai.ts +7 -2
- package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +0 -23
- package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +0 -30
- package/src/gjc-runtime/ultragoal-pipeline.ts +0 -306
|
@@ -22,10 +22,12 @@ import type {
|
|
|
22
22
|
StatusLineSeparatorStyle,
|
|
23
23
|
} from "../../config/settings-schema";
|
|
24
24
|
import { SETTING_TABS, TAB_METADATA } from "../../config/settings-schema";
|
|
25
|
+
import type { GjcRuntimeSnapshotProvider } from "../../extensibility/gjc-plugins/runtime-quarantine";
|
|
25
26
|
import { getCurrentThemeName, getSelectListTheme, getSettingsListTheme, theme } from "../../modes/theme/theme";
|
|
26
27
|
import { matchesAppInterrupt } from "../../modes/utils/keybinding-matchers";
|
|
27
28
|
import { getTabBarTheme } from "../shared";
|
|
28
29
|
import { DynamicBorder } from "./dynamic-border";
|
|
30
|
+
import { GjcBundleSettingsComponent } from "./gjc-bundle-settings";
|
|
29
31
|
import {
|
|
30
32
|
type NotificationsEditorOperations,
|
|
31
33
|
NotificationsSettingsEditorComponent,
|
|
@@ -650,6 +652,7 @@ function getSettingsTabs(): Tab[] {
|
|
|
650
652
|
return { id, label: `${icon} ${meta.label}` };
|
|
651
653
|
}),
|
|
652
654
|
{ id: "plugins", label: `${theme.icon.package} Plugins` },
|
|
655
|
+
{ id: "gjc-bundles", label: `${theme.icon.package} GJC Bundles` },
|
|
653
656
|
];
|
|
654
657
|
}
|
|
655
658
|
|
|
@@ -672,6 +675,14 @@ export interface SettingsRuntimeContext {
|
|
|
672
675
|
petAvailable?: boolean;
|
|
673
676
|
/** Terminal environment used to select unavailable-pet guidance. Omitted in production to use Bun.env. */
|
|
674
677
|
terminalEnv?: NodeJS.ProcessEnv;
|
|
678
|
+
/**
|
|
679
|
+
* Runtime evidence published by the session for the current activation
|
|
680
|
+
* generation. Omitted when no session published one, in which case the GJC
|
|
681
|
+
* Bundles tab honestly reports runtime status as unavailable.
|
|
682
|
+
*/
|
|
683
|
+
gjcRuntimeSnapshot?: GjcRuntimeSnapshotProvider;
|
|
684
|
+
/** Activation generation the published snapshot must match to be merged. */
|
|
685
|
+
gjcActivationGeneration?: number;
|
|
675
686
|
}
|
|
676
687
|
|
|
677
688
|
/** Status line settings subset for preview */
|
|
@@ -740,10 +751,11 @@ export class SettingsSelectorComponent extends Container {
|
|
|
740
751
|
#tabBar: TabBar;
|
|
741
752
|
#currentList: SettingsList | null = null;
|
|
742
753
|
#pluginComponent: PluginSettingsComponent | null = null;
|
|
754
|
+
#gjcBundleComponent: GjcBundleSettingsComponent | null = null;
|
|
743
755
|
#notificationsEditor: NotificationsSettingsEditorComponent | null = null;
|
|
744
756
|
#statusPreviewContainer: Container | null = null;
|
|
745
757
|
#statusPreviewText: Text | null = null;
|
|
746
|
-
#currentTabId: SettingTab | "plugins" = "appearance";
|
|
758
|
+
#currentTabId: SettingTab | "plugins" | "gjc-bundles" = "appearance";
|
|
747
759
|
#textInputActive = false;
|
|
748
760
|
|
|
749
761
|
constructor(
|
|
@@ -759,7 +771,7 @@ export class SettingsSelectorComponent extends Container {
|
|
|
759
771
|
// Tab bar
|
|
760
772
|
this.#tabBar = new TabBar("Settings", getSettingsTabs(), getTabBarTheme());
|
|
761
773
|
this.#tabBar.onTabChange = () => {
|
|
762
|
-
this.#switchToTab(this.#tabBar.getActiveTab().id as SettingTab | "plugins");
|
|
774
|
+
this.#switchToTab(this.#tabBar.getActiveTab().id as SettingTab | "plugins" | "gjc-bundles");
|
|
763
775
|
};
|
|
764
776
|
|
|
765
777
|
this.addChild(this.#tabBar);
|
|
@@ -774,7 +786,7 @@ export class SettingsSelectorComponent extends Container {
|
|
|
774
786
|
this.addChild(new DynamicBorder());
|
|
775
787
|
}
|
|
776
788
|
|
|
777
|
-
#switchToTab(tabId: SettingTab | "plugins"): void {
|
|
789
|
+
#switchToTab(tabId: SettingTab | "plugins" | "gjc-bundles"): void {
|
|
778
790
|
if (this.#currentTabId === "notifications" && tabId !== "notifications" && !this.#disposeNotificationsEditor()) {
|
|
779
791
|
return;
|
|
780
792
|
}
|
|
@@ -789,6 +801,11 @@ export class SettingsSelectorComponent extends Container {
|
|
|
789
801
|
this.removeChild(this.#pluginComponent);
|
|
790
802
|
this.#pluginComponent = null;
|
|
791
803
|
}
|
|
804
|
+
if (this.#gjcBundleComponent) {
|
|
805
|
+
this.removeChild(this.#gjcBundleComponent);
|
|
806
|
+
this.#gjcBundleComponent.dispose();
|
|
807
|
+
this.#gjcBundleComponent = null;
|
|
808
|
+
}
|
|
792
809
|
if (this.#statusPreviewContainer) {
|
|
793
810
|
this.removeChild(this.#statusPreviewContainer);
|
|
794
811
|
this.#statusPreviewContainer = null;
|
|
@@ -801,6 +818,8 @@ export class SettingsSelectorComponent extends Container {
|
|
|
801
818
|
|
|
802
819
|
if (tabId === "plugins") {
|
|
803
820
|
this.#showPluginsTab();
|
|
821
|
+
} else if (tabId === "gjc-bundles") {
|
|
822
|
+
this.#showGjcBundlesTab();
|
|
804
823
|
} else if (tabId === "notifications") {
|
|
805
824
|
this.#showNotificationsTab();
|
|
806
825
|
} else {
|
|
@@ -1224,7 +1243,7 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1224
1243
|
|
|
1225
1244
|
/** Re-evaluate condition gates against the current settings and refresh the active list. */
|
|
1226
1245
|
#refreshCurrentTabItems(defs: SettingDef[]): void {
|
|
1227
|
-
if (this.#currentTabId === "plugins" || !this.#currentList) return;
|
|
1246
|
+
if (this.#currentTabId === "plugins" || this.#currentTabId === "gjc-bundles" || !this.#currentList) return;
|
|
1228
1247
|
this.#currentList.setItems(this.#buildItemsForTab(defs, this.#currentTabId));
|
|
1229
1248
|
}
|
|
1230
1249
|
|
|
@@ -1271,15 +1290,31 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1271
1290
|
});
|
|
1272
1291
|
this.addChild(this.#pluginComponent);
|
|
1273
1292
|
}
|
|
1293
|
+
#showGjcBundlesTab(): void {
|
|
1294
|
+
this.#gjcBundleComponent = new GjcBundleSettingsComponent(
|
|
1295
|
+
this.context.cwd,
|
|
1296
|
+
{
|
|
1297
|
+
onClose: () => this.callbacks.onCancel(),
|
|
1298
|
+
onBundlesChanged: () => this.callbacks.onPluginsChanged?.(),
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
runtimeSnapshotProvider: this.context.gjcRuntimeSnapshot,
|
|
1302
|
+
activationGeneration: this.context.gjcActivationGeneration,
|
|
1303
|
+
},
|
|
1304
|
+
);
|
|
1305
|
+
this.addChild(this.#gjcBundleComponent);
|
|
1306
|
+
}
|
|
1274
1307
|
|
|
1275
1308
|
getFocusComponent(): Component {
|
|
1276
|
-
return (this.#currentList || this.#pluginComponent || this.#notificationsEditor)!;
|
|
1309
|
+
return (this.#currentList || this.#pluginComponent || this.#gjcBundleComponent || this.#notificationsEditor)!;
|
|
1277
1310
|
}
|
|
1278
1311
|
|
|
1279
1312
|
override dispose(): void {
|
|
1280
1313
|
if (this.#notificationsEditor?.navigationLocked) return;
|
|
1281
1314
|
this.#notificationsEditor?.dispose();
|
|
1282
1315
|
this.#notificationsEditor = null;
|
|
1316
|
+
this.#gjcBundleComponent?.dispose();
|
|
1317
|
+
this.#gjcBundleComponent = null;
|
|
1283
1318
|
super.dispose();
|
|
1284
1319
|
}
|
|
1285
1320
|
|
|
@@ -1301,6 +1336,18 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1301
1336
|
this.#notificationsEditor.handleInput(data);
|
|
1302
1337
|
return;
|
|
1303
1338
|
}
|
|
1339
|
+
if (this.#gjcBundleComponent && this.#currentTabId === "gjc-bundles") {
|
|
1340
|
+
if (tabNavigation) {
|
|
1341
|
+
if (this.#gjcBundleComponent.navigationLocked) {
|
|
1342
|
+
this.#gjcBundleComponent.handleInput(data);
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
this.#tabBar.handleInput(data);
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
this.#gjcBundleComponent.handleInput(data);
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1304
1351
|
|
|
1305
1352
|
// Handle tab switching — but NOT when a text input is active, since
|
|
1306
1353
|
// arrow keys must reach the cursor and Tab must not switch tabs.
|
|
@@ -1319,6 +1366,10 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1319
1366
|
this.#pluginComponent.handleInput(data);
|
|
1320
1367
|
return;
|
|
1321
1368
|
}
|
|
1369
|
+
if (this.#gjcBundleComponent) {
|
|
1370
|
+
this.#gjcBundleComponent.handleInput(data);
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1322
1373
|
|
|
1323
1374
|
// Fallback for future top-level content that does not own cancellation.
|
|
1324
1375
|
if (matchesAppInterrupt(data)) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type RunGh, runGhDefault } from "../../../utils/gh";
|
|
2
|
+
|
|
3
|
+
const STATUS_LINE_GH_TIMEOUT_MS = 5_000;
|
|
4
|
+
|
|
5
|
+
export async function lookupCurrentPr(runGh: RunGh = runGhDefault): Promise<{ number: number; url: string } | null> {
|
|
6
|
+
try {
|
|
7
|
+
const result = await runGh(["pr", "view", "--json", "number,url"], { timeoutMs: STATUS_LINE_GH_TIMEOUT_MS });
|
|
8
|
+
if (result.exitCode !== 0 || result.timedOut) return null;
|
|
9
|
+
|
|
10
|
+
const pr = JSON.parse(result.stdout) as { number?: unknown; url?: unknown };
|
|
11
|
+
if (typeof pr.number !== "number" || typeof pr.url !== "string") return null;
|
|
12
|
+
return { number: pr.number, url: pr.url };
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -2,7 +2,6 @@ import * as fs from "node:fs";
|
|
|
2
2
|
|
|
3
3
|
import { type Component, truncateToWidth, visibleWidth } from "@gajae-code/tui";
|
|
4
4
|
import { formatCount, getProjectDir } from "@gajae-code/utils";
|
|
5
|
-
import { $ } from "bun";
|
|
6
5
|
import {
|
|
7
6
|
type AppKeybinding,
|
|
8
7
|
KEYBINDINGS,
|
|
@@ -20,6 +19,7 @@ import type { ActionRegistry, FocusDomain } from "../action-registry";
|
|
|
20
19
|
import { EMPTY_JOBS_SNAPSHOT, type JobsSnapshot } from "../jobs-observer";
|
|
21
20
|
import { sanitizeStatusText } from "../shared";
|
|
22
21
|
import { renderSkillHudBar } from "./skill-hud/render";
|
|
22
|
+
import { lookupCurrentPr } from "./status-line/gh";
|
|
23
23
|
import {
|
|
24
24
|
canReuseCachedPr,
|
|
25
25
|
createPrCacheContext,
|
|
@@ -416,19 +416,8 @@ export class StatusLineComponent implements Component {
|
|
|
416
416
|
};
|
|
417
417
|
try {
|
|
418
418
|
// Requires `gh repo set-default` to be configured; fails gracefully if not
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
setCachedPr(null);
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
const pr = JSON.parse(result.stdout.toString()) as { number: number; url: string };
|
|
425
|
-
if (typeof pr.number === "number") {
|
|
426
|
-
setCachedPr({ number: pr.number, url: pr.url });
|
|
427
|
-
} else {
|
|
428
|
-
setCachedPr(null);
|
|
429
|
-
}
|
|
430
|
-
} catch {
|
|
431
|
-
setCachedPr(null);
|
|
419
|
+
const pr = await lookupCurrentPr();
|
|
420
|
+
setCachedPr(pr);
|
|
432
421
|
} finally {
|
|
433
422
|
this.#prLookupInFlight = false;
|
|
434
423
|
if (this.#onBranchChange) {
|
|
@@ -54,6 +54,34 @@ export type TranscriptViewerOverlayOptions = {
|
|
|
54
54
|
|
|
55
55
|
type RenderedEntry = { lineStart: number; lineCount: number };
|
|
56
56
|
|
|
57
|
+
export const __transcriptViewerPerfCounters = {
|
|
58
|
+
enabled: false,
|
|
59
|
+
layoutCacheHits: 0,
|
|
60
|
+
layoutCacheMisses: 0,
|
|
61
|
+
refreshRuns: 0,
|
|
62
|
+
rebuildRuns: 0,
|
|
63
|
+
snapshot() {
|
|
64
|
+
return {
|
|
65
|
+
layoutCacheHits: this.layoutCacheHits,
|
|
66
|
+
layoutCacheMisses: this.layoutCacheMisses,
|
|
67
|
+
refreshRuns: this.refreshRuns,
|
|
68
|
+
rebuildRuns: this.rebuildRuns,
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
enable(): void {
|
|
72
|
+
this.enabled = true;
|
|
73
|
+
},
|
|
74
|
+
disable(): void {
|
|
75
|
+
this.enabled = false;
|
|
76
|
+
},
|
|
77
|
+
reset(): void {
|
|
78
|
+
this.layoutCacheHits = 0;
|
|
79
|
+
this.layoutCacheMisses = 0;
|
|
80
|
+
this.refreshRuns = 0;
|
|
81
|
+
this.rebuildRuns = 0;
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
57
85
|
/** Message-agnostic, modal transcript browser over resolved projection entries. */
|
|
58
86
|
export class TranscriptViewerOverlay extends Container {
|
|
59
87
|
#options: TranscriptViewerOverlayOptions;
|
|
@@ -73,6 +101,7 @@ export class TranscriptViewerOverlay extends Container {
|
|
|
73
101
|
#followTailPending = false;
|
|
74
102
|
#skipFollowTailOnce = false;
|
|
75
103
|
#followTailActive = false;
|
|
104
|
+
#layoutCache = new Map<string, Map<string, string[]>>();
|
|
76
105
|
|
|
77
106
|
constructor(options: TranscriptViewerOverlayOptions) {
|
|
78
107
|
super();
|
|
@@ -87,7 +116,11 @@ export class TranscriptViewerOverlay extends Container {
|
|
|
87
116
|
get isFullscreen(): boolean {
|
|
88
117
|
return this.#fullscreen;
|
|
89
118
|
}
|
|
90
|
-
refresh(identityMap?: ReadonlyMap<string, string
|
|
119
|
+
refresh(identityMap?: ReadonlyMap<string, string>, preserveLayoutCache = false): void {
|
|
120
|
+
if (__transcriptViewerPerfCounters.enabled) __transcriptViewerPerfCounters.refreshRuns++;
|
|
121
|
+
// Incremental sources explicitly invalidate same-id payload patches before requesting a
|
|
122
|
+
// preserving refresh. Other source changes conservatively drop all layout variants.
|
|
123
|
+
if (!preserveLayoutCache) this.#layoutCache.clear();
|
|
91
124
|
const previous = this.selectedEntryId;
|
|
92
125
|
const previousPosition = this.#selected;
|
|
93
126
|
const reconciledPrevious = previous ? (identityMap?.get(previous) ?? previous) : undefined;
|
|
@@ -109,9 +142,15 @@ export class TranscriptViewerOverlay extends Container {
|
|
|
109
142
|
this.#initialized = true;
|
|
110
143
|
this.#rebuild();
|
|
111
144
|
}
|
|
145
|
+
invalidateLayoutEntries(entryIds: readonly string[]): void {
|
|
146
|
+
for (const id of entryIds) this.#layoutCache.delete(id);
|
|
147
|
+
}
|
|
112
148
|
override render(width: number): string[] {
|
|
113
|
-
|
|
114
|
-
this
|
|
149
|
+
const nextWidth = Math.max(1, width);
|
|
150
|
+
const widthChanged = nextWidth !== this.#width;
|
|
151
|
+
this.#width = nextWidth;
|
|
152
|
+
if (widthChanged) this.#layoutCache.clear();
|
|
153
|
+
if (widthChanged || getMarkdownTheme() !== this.#mdTheme) this.#rebuild();
|
|
115
154
|
const defaultHeader = this.#fullscreen
|
|
116
155
|
? [theme.fg("accent", "Transcript block")]
|
|
117
156
|
: [theme.fg("accent", sanitizeText(this.#options.title ?? "Transcript"))];
|
|
@@ -179,6 +218,7 @@ export class TranscriptViewerOverlay extends Container {
|
|
|
179
218
|
if (this.#fullscreen) {
|
|
180
219
|
this.#fullscreen = false;
|
|
181
220
|
this.#scrollOffset = 0;
|
|
221
|
+
this.#rebuild();
|
|
182
222
|
this.#requestRender();
|
|
183
223
|
} else this.#options.onClose();
|
|
184
224
|
return;
|
|
@@ -208,6 +248,7 @@ export class TranscriptViewerOverlay extends Container {
|
|
|
208
248
|
this.#followTailActive = false;
|
|
209
249
|
this.#followTailPending = false;
|
|
210
250
|
this.#selected = 0;
|
|
251
|
+
this.#rebuild();
|
|
211
252
|
this.#scrollOffset = 0;
|
|
212
253
|
this.#requestRender();
|
|
213
254
|
return;
|
|
@@ -216,6 +257,7 @@ export class TranscriptViewerOverlay extends Container {
|
|
|
216
257
|
this.#followTailActive = true;
|
|
217
258
|
this.#followTailPending = true;
|
|
218
259
|
this.#selected = Math.max(0, count - 1);
|
|
260
|
+
this.#rebuild();
|
|
219
261
|
this.#scrollOffset = this.#lines.length;
|
|
220
262
|
this.#requestRender();
|
|
221
263
|
return;
|
|
@@ -228,6 +270,7 @@ export class TranscriptViewerOverlay extends Container {
|
|
|
228
270
|
this.#followTailActive = false;
|
|
229
271
|
this.#fullscreen = true;
|
|
230
272
|
this.#scrollOffset = 0;
|
|
273
|
+
this.#rebuild();
|
|
231
274
|
this.#requestRender();
|
|
232
275
|
return;
|
|
233
276
|
}
|
|
@@ -243,6 +286,7 @@ export class TranscriptViewerOverlay extends Container {
|
|
|
243
286
|
const entry = this.#entries[this.#selected];
|
|
244
287
|
if (entry?.rawViewable !== false) {
|
|
245
288
|
this.#raw.has(entry.id) ? this.#raw.delete(entry.id) : this.#raw.add(entry.id);
|
|
289
|
+
this.#rebuild();
|
|
246
290
|
this.#requestRender();
|
|
247
291
|
}
|
|
248
292
|
}
|
|
@@ -284,6 +328,7 @@ export class TranscriptViewerOverlay extends Container {
|
|
|
284
328
|
this.#followTailActive = false;
|
|
285
329
|
this.#followTailPending = false;
|
|
286
330
|
this.#selected = Math.max(0, Math.min(this.#selected + direction * 5, this.#entries.length - 1));
|
|
331
|
+
this.#rebuild();
|
|
287
332
|
this.#scrollOffset += direction * PAGE_SIZE;
|
|
288
333
|
this.#requestRender();
|
|
289
334
|
}
|
|
@@ -327,32 +372,58 @@ export class TranscriptViewerOverlay extends Container {
|
|
|
327
372
|
this.#options.requestRender?.();
|
|
328
373
|
}
|
|
329
374
|
#rebuild(): void {
|
|
375
|
+
if (__transcriptViewerPerfCounters.enabled) __transcriptViewerPerfCounters.rebuildRuns++;
|
|
376
|
+
// Theme is tracked by reference outside the variant key; a reference change clears
|
|
377
|
+
// the whole cache so plain and markdown layouts pick up new markdown colors.
|
|
378
|
+
const currentMdTheme = getMarkdownTheme();
|
|
379
|
+
if (currentMdTheme !== this.#mdTheme) {
|
|
380
|
+
this.#mdTheme = currentMdTheme;
|
|
381
|
+
this.#layoutCache.clear();
|
|
382
|
+
}
|
|
330
383
|
const lines: string[] = [];
|
|
331
384
|
this.#renderedEntries = [];
|
|
332
385
|
const display = this.#fullscreen ? this.#entries.slice(this.#selected, this.#selected + 1) : this.#entries;
|
|
333
386
|
const contentWidth = Math.max(1, this.#width - INDENT.length - 1);
|
|
334
387
|
for (const entry of display) {
|
|
335
388
|
const start = lines.length;
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
lines.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
389
|
+
const entryLines = this.#renderEntryLines(entry, contentWidth);
|
|
390
|
+
lines.push(...entryLines);
|
|
391
|
+
// Cumulative positions are recomputed from cached line lengths on every rebuild
|
|
392
|
+
// so PR2c can later separate virtual coordinates from materialized lines.
|
|
393
|
+
this.#renderedEntries.push({ lineStart: start, lineCount: entryLines.length });
|
|
394
|
+
}
|
|
395
|
+
this.#lines = lines.length ? lines : [theme.fg("dim", "No transcript entries yet.")];
|
|
396
|
+
}
|
|
397
|
+
#renderEntryLines(entry: TranscriptViewerEntry, contentWidth: number): string[] {
|
|
398
|
+
const selected = entry.id === this.selectedEntryId;
|
|
399
|
+
const expanded = this.#fullscreen || this.#expanded.has(entry.id);
|
|
400
|
+
const raw = this.#raw.has(entry.id);
|
|
401
|
+
const variantKey = `${expanded ? 1 : 0}|${raw ? 1 : 0}|${selected ? 1 : 0}|${contentWidth}`;
|
|
402
|
+
let variants = this.#layoutCache.get(entry.id);
|
|
403
|
+
if (variants) {
|
|
404
|
+
const cached = variants.get(variantKey);
|
|
405
|
+
if (cached) {
|
|
406
|
+
if (__transcriptViewerPerfCounters.enabled) __transcriptViewerPerfCounters.layoutCacheHits++;
|
|
407
|
+
return cached;
|
|
355
408
|
}
|
|
409
|
+
}
|
|
410
|
+
if (__transcriptViewerPerfCounters.enabled) __transcriptViewerPerfCounters.layoutCacheMisses++;
|
|
411
|
+
const lines: string[] = [];
|
|
412
|
+
lines.push("");
|
|
413
|
+
lines.push(
|
|
414
|
+
`${selected ? theme.fg("accent", "▶") : " "} ${theme.fg("muted", `[${sanitizeText(entry.label ?? entry.kind)}]`)}`,
|
|
415
|
+
);
|
|
416
|
+
if (
|
|
417
|
+
!raw &&
|
|
418
|
+
expanded &&
|
|
419
|
+
selected &&
|
|
420
|
+
entry.kind === "tool" &&
|
|
421
|
+
entry.richRenderEligible === true &&
|
|
422
|
+
entry.renderDescriptor
|
|
423
|
+
) {
|
|
424
|
+
for (const line of renderToolDisplayLines(entry.renderDescriptor, contentWidth, theme))
|
|
425
|
+
lines.push(`${INDENT}${line}`);
|
|
426
|
+
} else {
|
|
356
427
|
const text = sanitizeText(
|
|
357
428
|
(raw
|
|
358
429
|
? entry.payload.text
|
|
@@ -375,9 +446,13 @@ export class TranscriptViewerOverlay extends Container {
|
|
|
375
446
|
for (const line of preview) lines.push(`${INDENT}${truncateToWidth(line, contentWidth)}`);
|
|
376
447
|
if (text.split("\n").length > PREVIEW_LINES) lines.push(`${INDENT}${theme.fg("dim", "... more (Space)")}`);
|
|
377
448
|
}
|
|
378
|
-
this.#renderedEntries.push({ lineStart: start, lineCount: lines.length - start });
|
|
379
449
|
}
|
|
380
|
-
|
|
450
|
+
if (!variants) {
|
|
451
|
+
variants = new Map();
|
|
452
|
+
this.#layoutCache.set(entry.id, variants);
|
|
453
|
+
}
|
|
454
|
+
variants.set(variantKey, lines);
|
|
455
|
+
return lines;
|
|
381
456
|
}
|
|
382
457
|
#markdown(text: string, width: number): string[] {
|
|
383
458
|
return new Markdown(text, 0, 0, this.#mdTheme).render(width).map(line => `${INDENT}${line.trimEnd()}`);
|
|
@@ -1175,6 +1175,10 @@ export class CommandController {
|
|
|
1175
1175
|
}
|
|
1176
1176
|
this.ctx.showError(`Bash command failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1177
1177
|
}
|
|
1178
|
+
const bashComponent = this.ctx.bashComponent;
|
|
1179
|
+
if (isDeferred && bashComponent && this.ctx.pendingBashComponents.includes(bashComponent)) {
|
|
1180
|
+
this.ctx.pendingMessagesContainer.detachChild(bashComponent);
|
|
1181
|
+
}
|
|
1178
1182
|
|
|
1179
1183
|
this.ctx.bashComponent = undefined;
|
|
1180
1184
|
this.ctx.ui.requestRender();
|
|
@@ -416,7 +416,9 @@ export class ExtensionUiController {
|
|
|
416
416
|
const contextActions: ExtensionContextActions = {
|
|
417
417
|
getModel: () => this.ctx.session.model,
|
|
418
418
|
isIdle: () => !this.ctx.session.isStreaming,
|
|
419
|
+
getActivePromptHandle: () => this.ctx.session.activePromptHandle,
|
|
419
420
|
abort: () => this.ctx.session.abort(),
|
|
421
|
+
abortPromptAndWait: (handle, options) => this.ctx.session.abortPromptAndWait(handle, options),
|
|
420
422
|
hasPendingMessages: () => this.ctx.session.queuedMessageCount > 0,
|
|
421
423
|
getPendingMessageCounts: () => this.ctx.session.pendingMessageCounts,
|
|
422
424
|
getTranscript: () => this.ctx.session.getTranscript(),
|
|
@@ -468,6 +470,7 @@ export class ExtensionUiController {
|
|
|
468
470
|
getBranchCandidates: () => this.ctx.sessionManager.getTree(),
|
|
469
471
|
getExtensions: () => this.ctx.session.extensionRunner?.getExtensionPaths() ?? [],
|
|
470
472
|
setSdkPermissionProvider: provider => this.ctx.session.setSdkPermissionProvider(provider),
|
|
473
|
+
setSdkClientBridge: bridge => this.ctx.session.setClientBridge(bridge),
|
|
471
474
|
sdkControl: this.#sdkControl,
|
|
472
475
|
};
|
|
473
476
|
const commandActions: ExtensionCommandContextActions = {
|
|
@@ -715,7 +718,9 @@ export class ExtensionUiController {
|
|
|
715
718
|
const contextActions: ExtensionContextActions = {
|
|
716
719
|
getModel: () => this.ctx.session.model,
|
|
717
720
|
isIdle: () => !this.ctx.session.isStreaming,
|
|
721
|
+
getActivePromptHandle: () => this.ctx.session.activePromptHandle,
|
|
718
722
|
abort: () => this.ctx.session.abort(),
|
|
723
|
+
abortPromptAndWait: (handle, options) => this.ctx.session.abortPromptAndWait(handle, options),
|
|
719
724
|
hasPendingMessages: () => this.ctx.session.queuedMessageCount > 0,
|
|
720
725
|
getPendingMessageCounts: () => this.ctx.session.pendingMessageCounts,
|
|
721
726
|
getTranscript: () => this.ctx.session.getTranscript(),
|
|
@@ -764,6 +769,7 @@ export class ExtensionUiController {
|
|
|
764
769
|
getBranchCandidates: () => this.ctx.sessionManager.getTree(),
|
|
765
770
|
getExtensions: () => this.ctx.session.extensionRunner?.getExtensionPaths() ?? [],
|
|
766
771
|
setSdkPermissionProvider: provider => this.ctx.session.setSdkPermissionProvider(provider),
|
|
772
|
+
setSdkClientBridge: bridge => this.ctx.session.setClientBridge(bridge),
|
|
767
773
|
sdkControl: this.#sdkControl,
|
|
768
774
|
};
|
|
769
775
|
const commandActions: ExtensionCommandContextActions = {
|
|
@@ -935,6 +941,7 @@ export class ExtensionUiController {
|
|
|
935
941
|
sessionManager: createReadonlySessionManager(this.ctx.session.sessionManager),
|
|
936
942
|
modelRegistry: this.ctx.session.modelRegistry,
|
|
937
943
|
model: this.ctx.session.model,
|
|
944
|
+
getActivePromptHandle: () => this.ctx.session.activePromptHandle,
|
|
938
945
|
isIdle: () => !this.ctx.session.isStreaming,
|
|
939
946
|
hasPendingMessages: () => this.ctx.session.queuedMessageCount > 0,
|
|
940
947
|
getPendingMessageCounts: () => this.ctx.session.pendingMessageCounts,
|
|
@@ -130,6 +130,7 @@ import { CustomProviderWizardComponent, type CustomProviderWizardSubmit } from "
|
|
|
130
130
|
import { ExtensionDashboard } from "../components/extensions";
|
|
131
131
|
import type { PetMode } from "../components/gajae-pet-widget";
|
|
132
132
|
import { HistorySearchComponent } from "../components/history-search";
|
|
133
|
+
import { HookSelectorComponent } from "../components/hook-selector";
|
|
133
134
|
import { JobsOverlayComponent } from "../components/jobs-overlay";
|
|
134
135
|
import { ModelSelectorComponent } from "../components/model-selector";
|
|
135
136
|
import type {
|
|
@@ -351,6 +352,7 @@ export function createNotificationsEditorOperations(
|
|
|
351
352
|
richDraftEnabled: config.richDraft.enabled,
|
|
352
353
|
toolActivityEnabled: config.toolActivity.enabled,
|
|
353
354
|
streamingEnabled: config.streaming.enabled,
|
|
355
|
+
sound: config.sound,
|
|
354
356
|
},
|
|
355
357
|
};
|
|
356
358
|
},
|
|
@@ -724,6 +726,7 @@ export function createNotificationsEditorOperations(
|
|
|
724
726
|
{ path: "notifications.redact", op: "set", value: preferences.redact },
|
|
725
727
|
{ path: "notifications.verbosity", op: "set", value: preferences.verbosity },
|
|
726
728
|
{ path: "notifications.sessionScope", op: "set", value: preferences.sessionScope },
|
|
729
|
+
{ path: "notifications.telegram.sound", op: "set", value: preferences.sound },
|
|
727
730
|
{ path: "notifications.telegram.rich.enabled", op: "set", value: preferences.richEnabled },
|
|
728
731
|
{ path: "notifications.telegram.richDraft.enabled", op: "set", value: preferences.richDraftEnabled },
|
|
729
732
|
{ path: "notifications.telegram.streaming.enabled", op: "set", value: preferences.streamingEnabled },
|
|
@@ -1280,6 +1283,8 @@ export class SelectorController {
|
|
|
1280
1283
|
availableThemes,
|
|
1281
1284
|
availableModelProfiles: [...this.ctx.session.modelRegistry.getModelProfiles().keys()],
|
|
1282
1285
|
cwd: getProjectDir(),
|
|
1286
|
+
gjcRuntimeSnapshot: this.ctx.session.gjcRuntimeSnapshot,
|
|
1287
|
+
gjcActivationGeneration: this.ctx.session.gjcActivationGeneration,
|
|
1283
1288
|
},
|
|
1284
1289
|
{
|
|
1285
1290
|
onChange: (id, value) => this.handleSettingChange(id, value),
|
|
@@ -2327,11 +2332,51 @@ export class SelectorController {
|
|
|
2327
2332
|
);
|
|
2328
2333
|
await this.ctx.reloadTodos();
|
|
2329
2334
|
this.ctx.showStatus("Resumed session");
|
|
2335
|
+
this.#maybePromptResumeModelChoice();
|
|
2330
2336
|
} finally {
|
|
2331
2337
|
progressLease.clear();
|
|
2332
2338
|
}
|
|
2333
2339
|
}
|
|
2334
2340
|
|
|
2341
|
+
/**
|
|
2342
|
+
* When `session.resumeModelBehavior` is "ask", offer a one-shot choice after
|
|
2343
|
+
* resuming: keep the model the session was last using, or switch to whatever
|
|
2344
|
+
* `modelRoles.default` currently resolves to. No-op if the setting is unset,
|
|
2345
|
+
* the two models already match, or either model can't be resolved.
|
|
2346
|
+
*/
|
|
2347
|
+
#maybePromptResumeModelChoice(): void {
|
|
2348
|
+
if (this.ctx.settings?.get("session.resumeModelBehavior") !== "ask") return;
|
|
2349
|
+
const sessionModel = this.ctx.session.model;
|
|
2350
|
+
const currentDefault = this.ctx.session.resolveConfiguredDefaultModel?.();
|
|
2351
|
+
if (!sessionModel || !currentDefault) return;
|
|
2352
|
+
if (sessionModel.provider === currentDefault.provider && sessionModel.id === currentDefault.id) return;
|
|
2353
|
+
|
|
2354
|
+
this.showSelector(done => {
|
|
2355
|
+
const selector = new HookSelectorComponent(
|
|
2356
|
+
`This session last used ${sessionModel.provider}/${sessionModel.id}.\n` +
|
|
2357
|
+
`Current default model is ${currentDefault.provider}/${currentDefault.id}.`,
|
|
2358
|
+
[`Keep ${sessionModel.id}`, `Use ${currentDefault.id}`],
|
|
2359
|
+
async (option: string) => {
|
|
2360
|
+
done();
|
|
2361
|
+
if (option === `Use ${currentDefault.id}`) {
|
|
2362
|
+
try {
|
|
2363
|
+
await this.ctx.session.setModel(currentDefault);
|
|
2364
|
+
this.ctx.showStatus(`Switched to ${currentDefault.provider}/${currentDefault.id}`);
|
|
2365
|
+
} catch (err) {
|
|
2366
|
+
this.ctx.showError(err instanceof Error ? err.message : String(err));
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
this.ctx.ui.requestRender();
|
|
2370
|
+
},
|
|
2371
|
+
() => {
|
|
2372
|
+
done();
|
|
2373
|
+
this.ctx.ui.requestRender();
|
|
2374
|
+
},
|
|
2375
|
+
);
|
|
2376
|
+
return { component: selector, focus: selector };
|
|
2377
|
+
});
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2335
2380
|
async handleSessionDeleteCommand(): Promise<void> {
|
|
2336
2381
|
const sessionFile = this.ctx.sessionManager.getSessionFile();
|
|
2337
2382
|
if (!sessionFile) {
|
|
@@ -94,7 +94,9 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
94
94
|
{
|
|
95
95
|
getModel: () => session.model,
|
|
96
96
|
isIdle: () => !session.isStreaming,
|
|
97
|
+
getActivePromptHandle: () => session.activePromptHandle,
|
|
97
98
|
abort: () => session.abort(),
|
|
99
|
+
abortPromptAndWait: (handle, abortOptions) => session.abortPromptAndWait(handle, abortOptions),
|
|
98
100
|
hasPendingMessages: () => session.queuedMessageCount > 0,
|
|
99
101
|
getPendingMessageCounts: () => session.pendingMessageCounts,
|
|
100
102
|
getTranscript: () => session.getTranscript(),
|
|
@@ -168,6 +170,7 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
168
170
|
},
|
|
169
171
|
getJobs: () => session.getAsyncJobSnapshot(),
|
|
170
172
|
setSdkPermissionProvider: provider => session.setSdkPermissionProvider(provider),
|
|
173
|
+
setSdkClientBridge: bridge => session.setClientBridge(bridge),
|
|
171
174
|
sdkControl: async (operation, input) => {
|
|
172
175
|
switch (operation) {
|
|
173
176
|
case "model.set": {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Persistence (ralplan runs only):
|
|
2
|
-
- Only when the assignment references a ralplan stage or `stage_n`, persist the
|
|
2
|
+
- Only when the assignment references a ralplan stage or `stage_n`, it must also provide the ralplan owner `session_id` and `run_id`. If either identifier is missing, do not persist; return a compact error asking the caller to supply both.
|
|
3
|
+
- Persist the full artifact through:
|
|
3
4
|
|
|
4
|
-
gjc ralplan --write --stage {{stage}} --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json
|
|
5
|
+
gjc ralplan --write --session-id <owner-session-id> --run-id <run-id> --stage {{stage}} --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json
|
|
5
6
|
|
|
6
|
-
Use the assignment-provided `stage_n`;
|
|
7
|
+
Use the assignment-provided owner `session_id`, `run_id`, and `stage_n`; never substitute the role subagent's own session id. On a duplicate-write error retry with the incremented N. Return the write receipt (`session_id`, `run_id`, `path`, `sha256`, `stage`, `stage_n`) and the role's compact verdict only. Otherwise, do not call `gjc ralplan --write`; return the full result in `yield.result.data`.
|
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
Before substantive work, create a phased todo.
|
|
3
3
|
|
|
4
4
|
You MUST call `todo_write` first in this turn.
|
|
5
|
-
|
|
5
|
+
Your call MUST use the top-level `ops` array with exactly one `init` operation.
|
|
6
|
+
The `init` operation MUST contain `list`, whose entries each have a unique `phase` and an `items` array of task strings.
|
|
7
|
+
Use this canonical shape:
|
|
8
|
+
```json
|
|
9
|
+
{"ops":[{"op":"init","list":[{"phase":"Investigation","items":["Locate relevant source and existing tests","Reproduce failure with focused contract test"]},{"phase":"Implementation","items":["Apply root cause fix across callsites"]},{"phase":"Verification","items":["Run focused tests and static checks"]}]}]}
|
|
10
|
+
```
|
|
6
11
|
You MUST cover the entire request from investigation through implementation and verification — not just the next immediate step.
|
|
7
|
-
Task
|
|
8
|
-
|
|
9
|
-
You MUST keep exactly one task `in_progress` and all later tasks `pending`.
|
|
12
|
+
Task strings MUST be specific, unique labels of 5-10 words. A future turn MUST execute them without re-planning.
|
|
13
|
+
The tool automatically promotes the first task after initialization. Add extra context later with a separate `note` operation only when needed.
|
|
10
14
|
|
|
11
15
|
After `todo_write` succeeds, continue the request in the same turn.
|
|
12
16
|
Do not call `todo_write` again unless task state materially changed.
|
|
@@ -22,12 +22,13 @@ Optimize for correctness first, maintainability second, and brevity third. Prefe
|
|
|
22
22
|
- Clear, low-risk implementation requests use direct tools and focused verification; do not invoke workflows or role agents for ceremony. Small verification needs do not turn a clear request into a planning workflow.
|
|
23
23
|
- Ambiguous implementation asks with a missing target, scope, acceptance criteria, or safety boundary require clarification or the appropriate planning workflow before mutation.
|
|
24
24
|
- Informational questions are answer-only/read-only unless the user explicitly requests a change, command, or execution.
|
|
25
|
-
- Vague requirements use `/skill:deep-interview`: a requirements-only workflow that must not mutate product code. Its spec hands off as deep-interview → ralplan consensus →
|
|
26
|
-
- Clear work with non-trivial architecture or sequencing risk uses `/skill:ralplan --deliberate
|
|
25
|
+
- Vague requirements use `/skill:deep-interview`: a requirements-only workflow that must not mutate product code. Its spec hands off as deep-interview → ralplan consensus → final receipt → approval or valid non-off runtime admission → separately authorized execution.
|
|
26
|
+
- Clear work with non-trivial architecture or sequencing risk uses `/skill:ralplan --deliberate`; reconciliation must persist its final receipt before choosing approval or an admitted handoff. A valid non-off final runtime receipt enters the existing handoff chain; otherwise it stops pending approval.
|
|
27
27
|
- Use `/skill:ultragoal` for durable goal ledgers and `/skill:team` for approved coordinated persistent work.
|
|
28
28
|
- Delegate large implementation slices to `executor`; use `planner`, `architect`, or `critic` for bounded planning and review.
|
|
29
|
+
- An explicit user request to use a worktree (for example, "use worktree") overrides direct editing: delegate implementation through `task` with `isolated: true`. This is the in-session counterpart of launching `gjc --worktree`; if task isolation is unavailable, report that conflict instead of editing in the parent session.
|
|
29
30
|
- Active skills are authoritative: never ignore an invoked skill; read the full skill text and follow it exactly.
|
|
30
|
-
- Before explicit execution approval, planning and interview workflows NEVER edit product source, run mutating shell commands, commit, push, open PRs, or delegate implementation.
|
|
31
|
+
- Before explicit execution approval or a valid non-off ralplan final runtime receipt, planning and interview workflows NEVER edit product source, run mutating shell commands, commit, push, open PRs, or delegate implementation.
|
|
31
32
|
</routing>
|
|
32
33
|
</gjc-runtime>
|
|
33
34
|
{{/unless}}
|
|
@@ -82,8 +83,14 @@ Use tools whenever they materially improve correctness, completeness, or groundi
|
|
|
82
83
|
|
|
83
84
|
{{#if toolDiscoveryActive}}
|
|
84
85
|
<tool-discovery>
|
|
85
|
-
Use `{{toolRefs.search_tool_bm25}}` to activate hidden tools when a purpose-built capability would improve the task; then call the activated tool. Essential tools stay loaded up front.
|
|
86
|
+
Use `{{toolRefs.search_tool_bm25}}` to activate hidden tools when a purpose-built capability would improve the task; then call the activated tool. Activation makes a tool available, but does not perform its action. Essential tools stay loaded up front.
|
|
86
87
|
Discoverable capabilities include browser automation, scheduling, debugging, and external integrations.
|
|
88
|
+
{{#has tools "task"}}
|
|
89
|
+
For requested subagents, delegation, or independent parallel lanes, call `{{toolRefs.task}}` before saying agents are running.
|
|
90
|
+
{{else}}
|
|
91
|
+
For requested subagents, delegation, or independent parallel lanes, search for `subagent delegation` before saying agents are running.
|
|
92
|
+
{{/has}}
|
|
93
|
+
Do not claim a web search, browser action, integration, or subagent ran without that tool's result.
|
|
87
94
|
</tool-discovery>
|
|
88
95
|
{{/if}}
|
|
89
96
|
|