@gajae-code/coding-agent 0.18.7 → 0.18.8
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 +279 -1
- package/dist/types/async/job-manager.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/capability/skill.d.ts +2 -0
- package/dist/types/capability/types.d.ts +2 -0
- package/dist/types/cli/public-command-errors.d.ts +3 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/autorouting-tier-map.d.ts +348 -9
- package/dist/types/config/file-lock.d.ts +1 -0
- package/dist/types/config/model-bindings-applier.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +11 -3
- package/dist/types/config/model-registry.d.ts +4 -9
- package/dist/types/config/skill-settings-defaults.d.ts +2 -0
- package/dist/types/cursor-delete-path.d.ts +1 -0
- package/dist/types/cursor-mcp-name.d.ts +8 -0
- package/dist/types/customization/inventory.d.ts +2 -0
- package/dist/types/debug/report-bundle-parent.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
- package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
- package/dist/types/discovery/helpers.d.ts +6 -1
- package/dist/types/export/html/css-color.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +7 -0
- package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
- package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
- package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
- package/dist/types/extensibility/skill-management.d.ts +3 -1
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
- package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
- package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +11 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
- package/dist/types/internal-urls/skill-path.d.ts +1 -0
- package/dist/types/lsp/workspace-path.d.ts +1 -0
- package/dist/types/modes/acp/acp-agent.d.ts +7 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -0
- package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
- package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
- package/dist/types/modes/controllers/input-controller.d.ts +9 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
- package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
- package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
- package/dist/types/progress/collect-project-progress.d.ts +38 -0
- package/dist/types/progress/progress-contract.d.ts +154 -0
- package/dist/types/progress/progress-snapshot.d.ts +11 -0
- package/dist/types/progress/project-progress.d.ts +141 -0
- package/dist/types/progress/render-progress.d.ts +28 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +1 -0
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
- package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
- package/dist/types/runtime-mcp/types.d.ts +6 -0
- package/dist/types/sdk/acp/adapter.d.ts +1 -1
- package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
- package/dist/types/sdk/broker/broker.d.ts +18 -1
- package/dist/types/sdk/broker/session-index.d.ts +12 -6
- package/dist/types/sdk/bus/index.d.ts +12 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +3 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +2 -0
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +8 -0
- package/dist/types/session/agent-session.d.ts +38 -9
- package/dist/types/session/artifacts.d.ts +2 -2
- package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +26 -1
- package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
- package/dist/types/session/internal/task-artifact-owner-access.d.ts +2 -0
- package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
- package/dist/types/session/session-manager.d.ts +31 -5
- package/dist/types/session/session-retirement.d.ts +31 -7
- package/dist/types/session/session-storage.d.ts +62 -2
- package/dist/types/session/task-artifact-owner-codec.d.ts +2 -0
- package/dist/types/setup/credential-auto-import.d.ts +1 -1
- package/dist/types/setup/credential-import.d.ts +6 -4
- package/dist/types/skill-state/active-state.d.ts +38 -0
- package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/tools/bash-skill-urls.d.ts +1 -1
- package/dist/types/tools/index.d.ts +3 -0
- package/dist/types/tools/python.d.ts +9 -1
- package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
- package/dist/types/tools/restricted-cwd.d.ts +1 -0
- package/dist/types/tools/todo-write.d.ts +6 -0
- package/dist/types/utils/tools-checksum.d.ts +7 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +21 -0
- package/scripts/verify-managed-empty-stop.ts +28 -0
- package/src/async/job-manager.ts +228 -122
- package/src/capability/index.ts +17 -0
- package/src/capability/mcp.ts +2 -0
- package/src/capability/skill.ts +2 -0
- package/src/capability/types.ts +2 -0
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/customize-doctor.ts +15 -2
- package/src/cli/public-command-errors.ts +21 -1
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/skills-cli.ts +5 -0
- package/src/commands/deep-interview.ts +47 -8
- package/src/commands/sdk.ts +28 -8
- package/src/config/autorouting-tier-map.ts +117 -7
- package/src/config/file-lock.ts +307 -68
- package/src/config/mcp-schema.json +4 -0
- package/src/config/model-bindings-applier.ts +20 -0
- package/src/config/model-profile-activation.ts +240 -46
- package/src/config/model-registry.ts +47 -153
- package/src/config/skill-settings-defaults.ts +7 -0
- package/src/coordinator-mcp/policy.ts +3 -2
- package/src/cursor-delete-path.ts +10 -0
- package/src/cursor-mcp-name.ts +15 -0
- package/src/cursor.ts +13 -2
- package/src/customization/inventory.ts +3 -0
- package/src/debug/report-bundle-parent.ts +22 -0
- package/src/debug/report-bundle.ts +3 -0
- package/src/debug/system-info.ts +13 -1
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
- package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
- package/src/defaults/gjc-defaults.test.ts +18 -1
- package/src/defaults/gjc-defaults.ts +3 -0
- package/src/defaults/gjc-skills.generated.ts +58 -36
- package/src/discovery/agents.ts +6 -1
- package/src/discovery/builtin.ts +10 -0
- package/src/discovery/helpers.ts +159 -20
- package/src/discovery/mcp-json.ts +6 -0
- package/src/discovery/ssh.ts +1 -1
- package/src/discovery/substitute-plugin-root.ts +3 -1
- package/src/edit/file-read-cache.ts +1 -1
- package/src/eval/py/executor.ts +380 -120
- package/src/export/html/css-color.ts +9 -0
- package/src/export/html/index.ts +5 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +73 -8
- package/src/extensibility/plugins/manager.ts +3 -2
- package/src/extensibility/plugins/marketplace/cache.ts +15 -0
- package/src/extensibility/plugins/marketplace/manager.ts +11 -3
- package/src/extensibility/plugins/plugin-link-path.ts +12 -0
- package/src/extensibility/runtime-skill-discovery.ts +20 -2
- package/src/extensibility/skill-management.ts +6 -2
- package/src/extensibility/skills.ts +6 -1
- package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
- package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
- package/src/gjc-runtime/deep-interview-stage.ts +98 -21
- package/src/gjc-runtime/gc-runtime.ts +206 -9
- package/src/gjc-runtime/launch-worktree.ts +14 -4
- package/src/gjc-runtime/ralplan-runtime.ts +447 -52
- package/src/gjc-runtime/state-argv.ts +2 -0
- package/src/gjc-runtime/state-migrations.ts +91 -29
- package/src/gjc-runtime/state-runtime.ts +3716 -135
- package/src/gjc-runtime/state-writer.ts +83 -8
- package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
- package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
- package/src/gjc-runtime/workflow-manifest.ts +54 -21
- package/src/harness-control-plane/operate.ts +12 -4
- package/src/harness-control-plane/owner.ts +5 -1
- package/src/hashline/parser.ts +48 -9
- package/src/hashline/recovery.ts +13 -4
- package/src/internal-urls/docs-index.generated.ts +18 -17
- package/src/internal-urls/skill-path.ts +19 -0
- package/src/internal-urls/skill-protocol.ts +2 -0
- package/src/lsp/edits.ts +6 -0
- package/src/lsp/workspace-path.ts +51 -0
- package/src/main.ts +195 -31
- package/src/modes/acp/acp-agent.ts +88 -4
- package/src/modes/components/custom-editor.ts +14 -0
- package/src/modes/components/customization/customization-dashboard.ts +12 -0
- package/src/modes/components/extensions/extension-dashboard.ts +2 -2
- package/src/modes/components/extensions/state-manager.ts +15 -5
- package/src/modes/components/model-selector.ts +1 -4
- package/src/modes/components/provider-onboarding-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +4 -0
- package/src/modes/controllers/event-controller.ts +11 -4
- package/src/modes/controllers/input-controller.ts +74 -29
- package/src/modes/controllers/runtime-mcp-command-controller.ts +8 -3
- package/src/modes/controllers/selector-controller.ts +28 -15
- package/src/modes/interactive-mode.ts +4 -0
- package/src/modes/runtime-init.ts +4 -0
- package/src/modes/utils/abort-message.ts +3 -1
- package/src/modes/utils/interrupt-ownership.ts +12 -0
- package/src/modes/utils/keybinding-matchers.ts +4 -0
- package/src/modes/utils/macos-option-forwarding.ts +80 -0
- package/src/progress/collect-project-progress.ts +153 -0
- package/src/progress/progress-contract.ts +172 -0
- package/src/progress/progress-snapshot.ts +140 -0
- package/src/progress/project-progress.ts +446 -0
- package/src/progress/render-progress.ts +209 -0
- package/src/prompts/system/master-mode.md +1 -0
- package/src/prompts/tools/skill-discovery.md +1 -1
- package/src/runtime-mcp/config-writer.ts +10 -6
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/oauth-flow.ts +5 -0
- package/src/runtime-mcp/oauth-public-url.ts +8 -0
- package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
- package/src/runtime-mcp/smithery-registry.ts +22 -3
- package/src/runtime-mcp/transports/http.ts +2 -2
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk/acp/adapter.ts +93 -8
- package/src/sdk/broker/broker-exit.ts +52 -23
- package/src/sdk/broker/broker.ts +75 -6
- package/src/sdk/broker/ensure.ts +2 -0
- package/src/sdk/broker/lifecycle.ts +711 -66
- package/src/sdk/broker/session-index.ts +191 -81
- package/src/sdk/bus/chat-daemon-cli.ts +38 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +238 -43
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
- package/src/sdk/cli/session-cli.ts +17 -3
- package/src/sdk/client/client.ts +28 -8
- package/src/sdk/host/query/handlers.ts +9 -1
- package/src/sdk/host/session-runtime.test.ts +2647 -294
- package/src/sdk/host/session-runtime.ts +426 -109
- package/src/sdk/host/surface-policy.ts +1 -0
- package/src/sdk/index.ts +8 -0
- package/src/sdk/lifecycle-session.ts +3 -1
- package/src/sdk/prompt-deadline-manager.ts +15 -0
- package/src/sdk/protocol/operation-inventory.generated.json +146 -0
- package/src/sdk/protocol/operation-registry.ts +9 -2
- package/src/sdk/router/session-router.ts +75 -12
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +67 -38
- package/src/session/agent-session.ts +623 -224
- package/src/session/artifacts.ts +174 -70
- package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
- package/src/session/internal/managed-session-scope.ts +3457 -462
- package/src/session/internal/managed-session-storage.ts +376 -61
- package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
- package/src/session/internal/task-artifact-owner-access.ts +19 -3
- package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
- package/src/session/session-manager.ts +939 -133
- package/src/session/session-retirement.ts +573 -14
- package/src/session/session-storage.ts +417 -14
- package/src/session/task-artifact-owner-codec.ts +71 -8
- package/src/session/task-artifact-owner-retirement.ts +21 -19
- package/src/setup/credential-auto-import.ts +12 -3
- package/src/setup/credential-import.ts +176 -5
- package/src/setup/paseo/announce.ts +6 -1
- package/src/skill-state/active-state.ts +132 -2
- package/src/slash-commands/builtin-registry.ts +39 -9
- package/src/slash-commands/helpers/progress-report.ts +23 -0
- package/src/stt/setup.ts +3 -5
- package/src/task/commands.ts +3 -1
- package/src/task/executor.ts +4 -0
- package/src/task/index.ts +6 -0
- package/src/tools/ask.ts +205 -6
- package/src/tools/ast-edit.ts +4 -0
- package/src/tools/bash-allowed-prefixes.ts +15 -0
- package/src/tools/bash-skill-urls.ts +56 -3
- package/src/tools/bash.ts +48 -79
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/eval.ts +56 -36
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/python.ts +187 -66
- package/src/tools/recipe/index.ts +9 -0
- package/src/tools/recipe/recipe-guard.ts +19 -0
- package/src/tools/restricted-cwd.ts +10 -0
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +28 -1
- package/src/tools/todo-write.ts +10 -1
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/tools/write.ts +1 -0
- package/src/utils/tools-checksum.ts +34 -0
- package/src/utils/tools-manager.ts +24 -6
- package/src/web/search/providers/codex.ts +3 -2
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { Container, matchesKey, type SelectItem, SelectList, Text } from "@gajae-code/tui";
|
|
22
22
|
import { sanitizeText } from "@gajae-code/utils";
|
|
23
|
+
import { resolveGlobalUserSkillLinkTrust } from "../../../config/skill-settings-defaults";
|
|
23
24
|
import { type CustomizationInventory, loadCustomizationInventory } from "../../../customization/inventory";
|
|
24
25
|
import {
|
|
25
26
|
removeHookFile,
|
|
@@ -47,6 +48,7 @@ import { ImportWizard } from "./import-wizard";
|
|
|
47
48
|
/** Minimal settings slice the dashboard reads and writes. */
|
|
48
49
|
export interface CustomizationSettingsSlice {
|
|
49
50
|
get(key: string): unknown;
|
|
51
|
+
getGlobal?(key: string): unknown;
|
|
50
52
|
set?(key: string, value: unknown): void;
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -150,6 +152,16 @@ export class CustomizationDashboard extends Container {
|
|
|
150
152
|
cwd: this.#cwd,
|
|
151
153
|
home: this.#homeDir,
|
|
152
154
|
policy: this.#skillPolicy(),
|
|
155
|
+
allowExternalUserSkillSymlinks: resolveGlobalUserSkillLinkTrust({
|
|
156
|
+
trustUserSkills:
|
|
157
|
+
typeof this.#settings?.getGlobal === "function"
|
|
158
|
+
? (this.#settings.getGlobal("skills.trustUserSkills") as boolean | undefined)
|
|
159
|
+
: undefined,
|
|
160
|
+
enablePiUser:
|
|
161
|
+
typeof this.#settings?.getGlobal === "function"
|
|
162
|
+
? (this.#settings.getGlobal("skills.enablePiUser") as boolean | undefined)
|
|
163
|
+
: undefined,
|
|
164
|
+
}),
|
|
153
165
|
disabledExtensions: this.#getStringArray("disabledExtensions"),
|
|
154
166
|
});
|
|
155
167
|
}
|
|
@@ -59,7 +59,7 @@ export class ExtensionDashboard extends Container {
|
|
|
59
59
|
async #init(): Promise<void> {
|
|
60
60
|
const sm = this.settings ?? (await Settings.init());
|
|
61
61
|
const disabledIds = sm ? ((sm.get("disabledExtensions") as string[]) ?? []) : [];
|
|
62
|
-
this.#state = await createInitialState(this.cwd, disabledIds);
|
|
62
|
+
this.#state = await createInitialState(this.cwd, disabledIds, sm);
|
|
63
63
|
|
|
64
64
|
// Calculate max visible items based on terminal height
|
|
65
65
|
// Reserve ~10 lines for header, tabs, help text, borders
|
|
@@ -193,7 +193,7 @@ export class ExtensionDashboard extends Container {
|
|
|
193
193
|
|
|
194
194
|
const sm = this.settings ?? Settings.instance;
|
|
195
195
|
const disabledIds = sm ? ((sm.get("disabledExtensions") as string[]) ?? []) : [];
|
|
196
|
-
const nextState = await refreshState(this.#state, this.cwd, disabledIds);
|
|
196
|
+
const nextState = await refreshState(this.#state, this.cwd, disabledIds, sm);
|
|
197
197
|
if (refreshToken !== this.#refreshToken) return;
|
|
198
198
|
this.#state = nextState;
|
|
199
199
|
|
|
@@ -14,6 +14,7 @@ import type { Skill } from "../../../capability/skill";
|
|
|
14
14
|
import type { SlashCommand } from "../../../capability/slash-command";
|
|
15
15
|
import type { CustomTool } from "../../../capability/tool";
|
|
16
16
|
import type { SourceMeta } from "../../../capability/types";
|
|
17
|
+
import type { Settings } from "../../../config/settings";
|
|
17
18
|
import {
|
|
18
19
|
disableProvider,
|
|
19
20
|
enableProvider,
|
|
@@ -43,7 +44,11 @@ export interface ExtensionSettingsManager {
|
|
|
43
44
|
/**
|
|
44
45
|
* Load all extensions from all capabilities.
|
|
45
46
|
*/
|
|
46
|
-
export async function loadAllExtensions(
|
|
47
|
+
export async function loadAllExtensions(
|
|
48
|
+
cwd?: string,
|
|
49
|
+
disabledIds?: string[],
|
|
50
|
+
settings?: Settings,
|
|
51
|
+
): Promise<Extension[]> {
|
|
47
52
|
const extensions: Extension[] = [];
|
|
48
53
|
const disabledExtensions = new Set<string>(disabledIds ?? []);
|
|
49
54
|
|
|
@@ -97,7 +102,7 @@ export async function loadAllExtensions(cwd?: string, disabledIds?: string[]): P
|
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
|
|
100
|
-
const loadOpts = cwd ? { cwd, includeDisabled: true } : { includeDisabled: true };
|
|
105
|
+
const loadOpts = cwd ? { cwd, includeDisabled: true, settings } : { includeDisabled: true, settings };
|
|
101
106
|
|
|
102
107
|
// Load skills
|
|
103
108
|
try {
|
|
@@ -553,8 +558,12 @@ export function applyDisabledExtensionsToState(state: DashboardState, disabledId
|
|
|
553
558
|
/**
|
|
554
559
|
* Create initial dashboard state.
|
|
555
560
|
*/
|
|
556
|
-
export async function createInitialState(
|
|
557
|
-
|
|
561
|
+
export async function createInitialState(
|
|
562
|
+
cwd?: string,
|
|
563
|
+
disabledIds?: string[],
|
|
564
|
+
settings?: Settings,
|
|
565
|
+
): Promise<DashboardState> {
|
|
566
|
+
const extensions = await loadAllExtensions(cwd, disabledIds, settings);
|
|
558
567
|
const tabs = buildProviderTabs(extensions);
|
|
559
568
|
const tabFiltered = extensions; // "all" tab by default
|
|
560
569
|
const searchFiltered = tabFiltered;
|
|
@@ -592,8 +601,9 @@ export async function refreshState(
|
|
|
592
601
|
state: DashboardState,
|
|
593
602
|
cwd?: string,
|
|
594
603
|
disabledIds?: string[],
|
|
604
|
+
settings?: Settings,
|
|
595
605
|
): Promise<DashboardState> {
|
|
596
|
-
const extensions = await loadAllExtensions(cwd, disabledIds);
|
|
606
|
+
const extensions = await loadAllExtensions(cwd, disabledIds, settings);
|
|
597
607
|
const tabs = buildProviderTabs(extensions);
|
|
598
608
|
|
|
599
609
|
// Get current provider from tabs
|
|
@@ -1369,10 +1369,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
1369
1369
|
}
|
|
1370
1370
|
|
|
1371
1371
|
#getProfileAvailableModels(): Model[] {
|
|
1372
|
-
|
|
1373
|
-
return typeof getAvailableForProfileActivation === "function"
|
|
1374
|
-
? getAvailableForProfileActivation.call(this.#modelRegistry)
|
|
1375
|
-
: this.#modelRegistry.getAvailable();
|
|
1372
|
+
return this.#modelRegistry.getAvailable();
|
|
1376
1373
|
}
|
|
1377
1374
|
|
|
1378
1375
|
#createProfileResolutionRegistry(availableModels: readonly Model[]) {
|
|
@@ -30,7 +30,7 @@ const PROVIDER_ONBOARDING_OPTIONS: ProviderOnboardingOption[] = [
|
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
label: "Import existing credentials",
|
|
33
|
-
description: "Detect and import Claude Code / Codex CLI logins already on this machine.",
|
|
33
|
+
description: "Detect and import Claude Code / Codex CLI / Kiro CLI logins already on this machine.",
|
|
34
34
|
action: "import-credentials",
|
|
35
35
|
},
|
|
36
36
|
];
|
|
@@ -109,6 +109,10 @@ const modelSegment: StatusLineSegment = {
|
|
|
109
109
|
const modelName = shortenModelId(state.model?.id ?? state.model?.name);
|
|
110
110
|
|
|
111
111
|
let content = withIcon(theme.icon.model, modelName);
|
|
112
|
+
const unavailableProfile = ctx.session.getUnavailableModelProfile?.();
|
|
113
|
+
if (unavailableProfile) {
|
|
114
|
+
content = `${theme.fg("warning", `profile unavailable: ${sanitizeStatusText(unavailableProfile)}`)} ${content}`;
|
|
115
|
+
}
|
|
112
116
|
|
|
113
117
|
if (ctx.session.isFastModeActive() && theme.icon.fast) {
|
|
114
118
|
content += ` ${theme.icon.fast}`;
|
|
@@ -60,6 +60,8 @@ export const __eventControllerPerfCounters = {
|
|
|
60
60
|
const COMPLETION_NOTIFY_COMMAND_TIMEOUT_MS = 10_000;
|
|
61
61
|
const TEXTLESS_LENGTH_WARNING =
|
|
62
62
|
'Response ended with stopReason "length" before producing visible text or a tool call. Check the model\'s output and context token limits (maxTokens in models.yml) or lower the reasoning level (/reasoning), then retry.';
|
|
63
|
+
const TEXTLESS_INCOMPLETE_WARNING =
|
|
64
|
+
"Response ended before producing visible text or a tool call. See the provider diagnostic for the reason.";
|
|
63
65
|
|
|
64
66
|
function isTextlessLengthStop(message: AssistantMessage | undefined): boolean {
|
|
65
67
|
return (
|
|
@@ -1090,7 +1092,7 @@ export class EventController {
|
|
|
1090
1092
|
event.stopReason !== "maintenance" &&
|
|
1091
1093
|
isTextlessLengthStop(lastAssistant)
|
|
1092
1094
|
) {
|
|
1093
|
-
this.ctx.showWarning(TEXTLESS_LENGTH_WARNING);
|
|
1095
|
+
this.ctx.showWarning(lastAssistant?.errorMessage ? TEXTLESS_INCOMPLETE_WARNING : TEXTLESS_LENGTH_WARNING);
|
|
1094
1096
|
}
|
|
1095
1097
|
this.sendCompletionNotification();
|
|
1096
1098
|
this.ctx.promptSuggestion?.onAgentEnd();
|
|
@@ -1372,15 +1374,20 @@ export class EventController {
|
|
|
1372
1374
|
|
|
1373
1375
|
const sessionName = this.ctx.sessionManager.getSessionName();
|
|
1374
1376
|
const textlessLengthStop = isTextlessLengthStop(last);
|
|
1377
|
+
const incompleteTitle = last?.errorMessage ? "Response incomplete" : "Output limit reached";
|
|
1375
1378
|
const title = textlessLengthStop
|
|
1376
1379
|
? sessionName
|
|
1377
|
-
? `${sessionName}:
|
|
1378
|
-
:
|
|
1380
|
+
? `${sessionName}: ${incompleteTitle}`
|
|
1381
|
+
: incompleteTitle
|
|
1379
1382
|
: sessionName
|
|
1380
1383
|
? `${sessionName}: Complete`
|
|
1381
1384
|
: "Complete";
|
|
1382
1385
|
const summary = summaryFromMessage(last, 1000);
|
|
1383
|
-
const body = textlessLengthStop
|
|
1386
|
+
const body = textlessLengthStop
|
|
1387
|
+
? last?.errorMessage
|
|
1388
|
+
? TEXTLESS_INCOMPLETE_WARNING
|
|
1389
|
+
: TEXTLESS_LENGTH_WARNING
|
|
1390
|
+
: (summary ?? "Complete");
|
|
1384
1391
|
const sessionManager = this.ctx.sessionManager as { getCwd?: () => string; getSessionId?: () => string };
|
|
1385
1392
|
const payload: CompletionNotifyPayload = {
|
|
1386
1393
|
type: "agent-turn-complete",
|
|
@@ -40,7 +40,9 @@ import { CommandPalette, type CommandPaletteAction, type CommandPaletteEntry } f
|
|
|
40
40
|
import type { PasteTextContext } from "../components/custom-editor";
|
|
41
41
|
import { QueuePaneComponent } from "../components/queue-pane";
|
|
42
42
|
import { type QueuedMessageMoveDirection, QueuedMessageSelectorComponent } from "../components/queued-message-selector";
|
|
43
|
+
import { focusedUiOwnsInterrupt } from "../utils/interrupt-ownership";
|
|
43
44
|
import { matchesAppInterrupt } from "../utils/keybinding-matchers";
|
|
45
|
+
import { createUnforwardedOptionChordWarner } from "../utils/macos-option-forwarding";
|
|
44
46
|
|
|
45
47
|
const QUEUE_SELECTOR_NAVIGATION_ACTIONS = [
|
|
46
48
|
"tui.select.up",
|
|
@@ -95,6 +97,8 @@ function isExpandable(obj: unknown): obj is Expandable {
|
|
|
95
97
|
export class InputController {
|
|
96
98
|
readonly actionRegistry: ActionRegistry<void>;
|
|
97
99
|
readonly #loadPastedImageBatch: typeof loadPastedImageBatch;
|
|
100
|
+
/** Session id with an automatic-title request pending, if any. */
|
|
101
|
+
#autoTitleInFlightSessionId: string | undefined;
|
|
98
102
|
#deferredSubmission?: {
|
|
99
103
|
text: string;
|
|
100
104
|
images?: InteractiveModeContext["pendingImages"];
|
|
@@ -546,15 +550,23 @@ export class InputController {
|
|
|
546
550
|
if (!isInterruptKey && !isClearKey) {
|
|
547
551
|
return undefined;
|
|
548
552
|
}
|
|
549
|
-
|
|
553
|
+
// Listeners run before focused handleInput. Yield interrupt/back to the
|
|
554
|
+
// focused UI before touching BTW, maintenance, retries or workflow state.
|
|
555
|
+
// Clear (Ctrl+C by default) remains a global abort, even if the same
|
|
556
|
+
// key is also configured as interrupt.
|
|
557
|
+
if (isInterruptKey && !isClearKey && focusedUiOwnsInterrupt(this.ctx)) {
|
|
558
|
+
this.#resetEscapeGestures();
|
|
559
|
+
return undefined;
|
|
560
|
+
}
|
|
561
|
+
if (isClearKey && this.ctx.hasActiveBtw() && this.ctx.handleBtwEscape()) {
|
|
550
562
|
this.#resetEscapeGestures();
|
|
551
563
|
return { consume: true };
|
|
552
564
|
}
|
|
553
|
-
if (isClearKey &&
|
|
565
|
+
if (isClearKey && this.ctx.hookSelector?.hasActiveInlineInput?.() === true) {
|
|
554
566
|
this.#resetEscapeGestures();
|
|
555
567
|
return undefined;
|
|
556
568
|
}
|
|
557
|
-
if (isClearKey
|
|
569
|
+
if (isClearKey) {
|
|
558
570
|
if (this.#handlePendingSteerInterrupt()) return { consume: true };
|
|
559
571
|
if (
|
|
560
572
|
this.#handleCancellableWorkEscape({
|
|
@@ -807,6 +819,12 @@ export class InputController {
|
|
|
807
819
|
this.ctx.editor.setActionKeys("app.message.queue", this.ctx.keybindings.getKeys("app.message.queue"));
|
|
808
820
|
this.ctx.editor.onQueue = () => this.#executeAction("app.message.queue");
|
|
809
821
|
this.#registerCommandPaletteAction("app.message.queue", queue, true);
|
|
822
|
+
this.ctx.editor.onUnforwardedOptionChord ??= createUnforwardedOptionChordWarner({
|
|
823
|
+
platform: process.platform,
|
|
824
|
+
terminalProgram: Bun.env.TERM_PROGRAM,
|
|
825
|
+
formatKeyHint: key => this.ctx.keybindings.formatKeyHint(key),
|
|
826
|
+
showWarning: message => this.ctx.showWarning(message),
|
|
827
|
+
});
|
|
810
828
|
|
|
811
829
|
this.ctx.editor.onViewportPageScroll = direction => this.ctx.ui.scrollViewportPages(direction);
|
|
812
830
|
this.ctx.editor.onViewportFollowLive = () => {
|
|
@@ -1163,6 +1181,7 @@ export class InputController {
|
|
|
1163
1181
|
return;
|
|
1164
1182
|
}
|
|
1165
1183
|
this.ctx.queueCompactionMessage(text, "steer", composer);
|
|
1184
|
+
this.#titleQueuedCompactionText(text);
|
|
1166
1185
|
return;
|
|
1167
1186
|
}
|
|
1168
1187
|
|
|
@@ -1172,6 +1191,9 @@ export class InputController {
|
|
|
1172
1191
|
// This handles extension commands (execute immediately), prompt template expansion, and queueing
|
|
1173
1192
|
if (this.ctx.session.isStreaming) {
|
|
1174
1193
|
invalidateSessionTitleGeneration(this.ctx.sessionManager);
|
|
1194
|
+
// A turn started by a skill command has no user message yet, so the
|
|
1195
|
+
// first typed message can arrive here instead of the idle path.
|
|
1196
|
+
this.maybeGenerateSessionTitle(text);
|
|
1175
1197
|
if (this.#canModifyComposer(composer)) {
|
|
1176
1198
|
this.ctx.editor.addToHistory(text);
|
|
1177
1199
|
this.ctx.editor.setText("");
|
|
@@ -1200,32 +1222,7 @@ export class InputController {
|
|
|
1200
1222
|
// First, move any pending bash components to chat
|
|
1201
1223
|
this.ctx.flushPendingBashComponents();
|
|
1202
1224
|
|
|
1203
|
-
|
|
1204
|
-
const hasUserMessages = this.ctx.session.messages.some((m: AgentMessage) => m.role === "user");
|
|
1205
|
-
if (!hasUserMessages && !this.ctx.sessionManager.getSessionName() && !$pickenv("GJC_NO_TITLE", "PI_NO_TITLE")) {
|
|
1206
|
-
const registry = this.ctx.session.modelRegistry;
|
|
1207
|
-
generateSessionTitle(
|
|
1208
|
-
text,
|
|
1209
|
-
registry,
|
|
1210
|
-
this.ctx.settings,
|
|
1211
|
-
this.ctx.session.credentialSessionId,
|
|
1212
|
-
this.ctx.session.model,
|
|
1213
|
-
provider => this.ctx.session.agent.metadataForProvider(provider),
|
|
1214
|
-
)
|
|
1215
|
-
.then(async title => {
|
|
1216
|
-
if (title) {
|
|
1217
|
-
const applied = await this.ctx.sessionManager.setSessionName(title, "auto");
|
|
1218
|
-
if (applied) {
|
|
1219
|
-
setSessionTerminalTitle(
|
|
1220
|
-
this.ctx.sessionManager.getSessionName()!,
|
|
1221
|
-
this.ctx.sessionManager.getCwd(),
|
|
1222
|
-
);
|
|
1223
|
-
this.ctx.updateEditorBorderColor();
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
})
|
|
1227
|
-
.catch(() => {});
|
|
1228
|
-
}
|
|
1225
|
+
this.maybeGenerateSessionTitle(text);
|
|
1229
1226
|
|
|
1230
1227
|
if (this.ctx.onInputCallback) {
|
|
1231
1228
|
// Include any pending images from clipboard paste
|
|
@@ -1628,6 +1625,51 @@ export class InputController {
|
|
|
1628
1625
|
return this.ctx.settings.get("busyPromptMode") === "steer" ? "steer" : "followUp";
|
|
1629
1626
|
}
|
|
1630
1627
|
|
|
1628
|
+
/**
|
|
1629
|
+
* Generate the automatic session title from the first user message. Called
|
|
1630
|
+
* from the editor submit and queue-shortcut paths (idle and streaming) and
|
|
1631
|
+
* from interactive startup messages; it is a no-op once the session has a
|
|
1632
|
+
* user message, a name, or a title request already in flight. The result is
|
|
1633
|
+
* bound to the session that requested it: a completion that lands after the
|
|
1634
|
+
* session was replaced (New Session, switch, resume) is discarded.
|
|
1635
|
+
*/
|
|
1636
|
+
maybeGenerateSessionTitle(text: string): void {
|
|
1637
|
+
if (this.ctx.sessionManager.getSessionName()) return;
|
|
1638
|
+
const sessionId = this.ctx.sessionManager.getSessionId();
|
|
1639
|
+
if (this.#autoTitleInFlightSessionId === sessionId) return;
|
|
1640
|
+
if ($pickenv("GJC_NO_TITLE", "PI_NO_TITLE")) return;
|
|
1641
|
+
if (this.ctx.session.messages.some((m: AgentMessage) => m.role === "user")) return;
|
|
1642
|
+
// Keep locally intercepted command arguments away from the title model.
|
|
1643
|
+
if (this.ctx.session.isLocallyHandledSlashCommand(text)) return;
|
|
1644
|
+
this.#autoTitleInFlightSessionId = sessionId;
|
|
1645
|
+
generateSessionTitle(
|
|
1646
|
+
text,
|
|
1647
|
+
this.ctx.session.modelRegistry,
|
|
1648
|
+
this.ctx.settings,
|
|
1649
|
+
this.ctx.session.credentialSessionId,
|
|
1650
|
+
this.ctx.session.model,
|
|
1651
|
+
provider => this.ctx.session.agent.metadataForProvider(provider),
|
|
1652
|
+
)
|
|
1653
|
+
.then(async title => {
|
|
1654
|
+
if (!title || this.ctx.sessionManager.getSessionId() !== sessionId) return;
|
|
1655
|
+
const applied = await this.ctx.sessionManager.setSessionName(title, "auto");
|
|
1656
|
+
if (applied) {
|
|
1657
|
+
setSessionTerminalTitle(this.ctx.sessionManager.getSessionName()!, this.ctx.sessionManager.getCwd());
|
|
1658
|
+
this.ctx.updateEditorBorderColor();
|
|
1659
|
+
}
|
|
1660
|
+
})
|
|
1661
|
+
.catch(() => {})
|
|
1662
|
+
.finally(() => {
|
|
1663
|
+
if (this.#autoTitleInFlightSessionId === sessionId) this.#autoTitleInFlightSessionId = undefined;
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
/** Title text accepted into the compaction queue; skill commands replay through the skill path and are not titled. */
|
|
1668
|
+
#titleQueuedCompactionText(text: string): void {
|
|
1669
|
+
if (parseSkillInvocations(text, this.ctx.skillCommands ?? new Map()).length > 0) return;
|
|
1670
|
+
this.maybeGenerateSessionTitle(text);
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1631
1673
|
/**
|
|
1632
1674
|
* Dispatch skill slash invocation(s) (`/skill:<name>`) through custom messages
|
|
1633
1675
|
* using the supplied `streamingBehavior`. Returns true if the text contains a
|
|
@@ -1736,6 +1778,7 @@ export class InputController {
|
|
|
1736
1778
|
return;
|
|
1737
1779
|
}
|
|
1738
1780
|
this.ctx.queueCompactionMessage(text, "followUp");
|
|
1781
|
+
this.#titleQueuedCompactionText(text);
|
|
1739
1782
|
return;
|
|
1740
1783
|
}
|
|
1741
1784
|
|
|
@@ -1750,6 +1793,7 @@ export class InputController {
|
|
|
1750
1793
|
followUpQueuePolicy: "sequential",
|
|
1751
1794
|
}),
|
|
1752
1795
|
);
|
|
1796
|
+
this.maybeGenerateSessionTitle(text);
|
|
1753
1797
|
this.ctx.updatePendingMessagesDisplay();
|
|
1754
1798
|
this.ctx.ui.requestRender();
|
|
1755
1799
|
return;
|
|
@@ -1761,6 +1805,7 @@ export class InputController {
|
|
|
1761
1805
|
return;
|
|
1762
1806
|
}
|
|
1763
1807
|
|
|
1808
|
+
this.maybeGenerateSessionTitle(text);
|
|
1764
1809
|
if (this.ctx.session.isStreaming) {
|
|
1765
1810
|
this.ctx.editor.addToHistory(text);
|
|
1766
1811
|
this.ctx.editor.setText("");
|
|
@@ -44,7 +44,8 @@ import { parseCommandArgs } from "../shared";
|
|
|
44
44
|
import { buildOAuthLoginAnchor, createOAuthUrlCopyLease } from "../shared/oauth-url-copy";
|
|
45
45
|
import { theme } from "../theme/theme";
|
|
46
46
|
import type { InteractiveModeContext } from "../types";
|
|
47
|
-
import {
|
|
47
|
+
import { focusedUiOwnsInterrupt } from "../utils/interrupt-ownership";
|
|
48
|
+
import { matchesAppClear, matchesAppInterrupt } from "../utils/keybinding-matchers";
|
|
48
49
|
import { groupBySource, parseRemoveArgs, readScopeFlag, showCommandMessage } from "./command-controller-shared";
|
|
49
50
|
|
|
50
51
|
function withTimeout<T>(promise: Promise<T>, timeoutMs: number, message: string): Promise<T> {
|
|
@@ -645,7 +646,9 @@ export class MCPCommandController {
|
|
|
645
646
|
const cancellationUnsubscribe =
|
|
646
647
|
signal === undefined && typeof this.ctx.ui.addInputListener === "function"
|
|
647
648
|
? this.ctx.ui.addInputListener(data => {
|
|
648
|
-
|
|
649
|
+
const isClearKey = data === "\x03" || matchesAppClear(data);
|
|
650
|
+
if (!isClearKey && !matchesAppInterrupt(data)) return;
|
|
651
|
+
if (!isClearKey && focusedUiOwnsInterrupt(this.ctx)) return;
|
|
649
652
|
userController.abort(new Error("OAuth flow cancelled"));
|
|
650
653
|
return { consume: true };
|
|
651
654
|
})
|
|
@@ -1839,7 +1842,9 @@ export class MCPCommandController {
|
|
|
1839
1842
|
const oauthUrlCopyLease = createOAuthUrlCopyLease(this.ctx);
|
|
1840
1843
|
const abortController = new AbortController();
|
|
1841
1844
|
const interruptUnsubscribe = this.ctx.ui.addInputListener?.(data => {
|
|
1842
|
-
|
|
1845
|
+
const isClearKey = data === "\x03" || matchesAppClear(data);
|
|
1846
|
+
if (!isClearKey && !matchesAppInterrupt(data)) return;
|
|
1847
|
+
if (!isClearKey && focusedUiOwnsInterrupt(this.ctx)) return;
|
|
1843
1848
|
abortController.abort(new Error("Smithery authorization cancelled."));
|
|
1844
1849
|
return { consume: true };
|
|
1845
1850
|
});
|
|
@@ -1612,7 +1612,7 @@ export class SelectorController {
|
|
|
1612
1612
|
}
|
|
1613
1613
|
|
|
1614
1614
|
async #handleCredentialImport(): Promise<void> {
|
|
1615
|
-
this.ctx.showStatus("Scanning for existing Claude Code / Codex CLI credentials…");
|
|
1615
|
+
this.ctx.showStatus("Scanning for existing Claude Code / Codex CLI / Kiro CLI credentials…");
|
|
1616
1616
|
const preview = await runExternalCredentialAutoImport({
|
|
1617
1617
|
authStorage: {
|
|
1618
1618
|
importCredentialIfAbsent: async () => ({
|
|
@@ -2494,17 +2494,20 @@ export class SelectorController {
|
|
|
2494
2494
|
* default) then refresh the status surfaces. Rethrows so callers surface errors.
|
|
2495
2495
|
*/
|
|
2496
2496
|
async #applyModelProfile(profileName: string, persistDefault: boolean): Promise<void> {
|
|
2497
|
+
this.ctx.session.markUserModelSelection();
|
|
2497
2498
|
const profileLabel = formatModelProfileDisplayLabel(
|
|
2498
2499
|
this.ctx.session.modelRegistry.getModelProfile(profileName) ?? { name: profileName },
|
|
2499
2500
|
);
|
|
2500
|
-
await
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2501
|
+
await this.ctx.session.withSdkControlMutation(() =>
|
|
2502
|
+
activateModelProfile(
|
|
2503
|
+
{
|
|
2504
|
+
session: this.ctx.session,
|
|
2505
|
+
modelRegistry: this.ctx.session.modelRegistry,
|
|
2506
|
+
settings: this.ctx.settings,
|
|
2507
|
+
profileName,
|
|
2508
|
+
},
|
|
2509
|
+
{ persistDefault },
|
|
2510
|
+
),
|
|
2508
2511
|
);
|
|
2509
2512
|
this.ctx.statusLine.invalidate();
|
|
2510
2513
|
this.ctx.updateEditorBorderColor();
|
|
@@ -2774,8 +2777,15 @@ export class SelectorController {
|
|
|
2774
2777
|
return;
|
|
2775
2778
|
}
|
|
2776
2779
|
const { model, role, thinkingLevel, selector: selectedSelector } = selection;
|
|
2780
|
+
if (
|
|
2781
|
+
(role !== null && role !== "default") ||
|
|
2782
|
+
selection.roles?.some(targetRole => targetRole !== "default")
|
|
2783
|
+
) {
|
|
2784
|
+
this.ctx.session.markUserModelSelection();
|
|
2785
|
+
}
|
|
2777
2786
|
if (role === null) {
|
|
2778
2787
|
// Temporary: update agent state but don't persist to settings
|
|
2788
|
+
this.ctx.session.markUserModelSelection();
|
|
2779
2789
|
await this.ctx.session.setModelTemporary(model, thinkingLevel, {
|
|
2780
2790
|
cause: "temporary-operation",
|
|
2781
2791
|
reason: "other",
|
|
@@ -3677,12 +3687,15 @@ export class SelectorController {
|
|
|
3677
3687
|
return;
|
|
3678
3688
|
}
|
|
3679
3689
|
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3690
|
+
this.ctx.session.markUserModelSelection();
|
|
3691
|
+
await this.ctx.session.withSdkControlMutation(() =>
|
|
3692
|
+
activateModelProfile({
|
|
3693
|
+
session: this.ctx.session,
|
|
3694
|
+
modelRegistry: this.ctx.session.modelRegistry,
|
|
3695
|
+
settings: this.ctx.settings,
|
|
3696
|
+
profileName: recommendedProfile.name,
|
|
3697
|
+
}),
|
|
3698
|
+
);
|
|
3686
3699
|
}
|
|
3687
3700
|
|
|
3688
3701
|
async #handleOAuthLogin(providerId: string, options?: OAuthSelectorOptions): Promise<void> {
|
|
@@ -2642,6 +2642,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2642
2642
|
this.#inputController.handleBackgroundCommand();
|
|
2643
2643
|
}
|
|
2644
2644
|
|
|
2645
|
+
maybeGenerateSessionTitle(text: string): void {
|
|
2646
|
+
this.#inputController.maybeGenerateSessionTitle(text);
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2645
2649
|
handleImagePaste(): Promise<boolean> {
|
|
2646
2650
|
return this.#inputController.handleImagePaste();
|
|
2647
2651
|
}
|
|
@@ -11,6 +11,8 @@ import { ThinkingLevel } from "@gajae-code/agent-core";
|
|
|
11
11
|
import { runExtensionCompact, runExtensionSetModel } from "../extensibility/extensions/compact-handler";
|
|
12
12
|
import { getSessionSlashCommands } from "../extensibility/extensions/get-commands-handler";
|
|
13
13
|
import type { ExtensionError, ExtensionUIContext } from "../extensibility/extensions/types";
|
|
14
|
+
import { buildSessionProjectProgress } from "../progress/collect-project-progress";
|
|
15
|
+
import { toProjectProgressSnapshot } from "../progress/progress-snapshot";
|
|
14
16
|
import {
|
|
15
17
|
parseSyntheticModelId,
|
|
16
18
|
resolveSyntheticModelSelection,
|
|
@@ -201,6 +203,8 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
201
203
|
return { bytes: new Uint8Array(await file.slice(start, end).arrayBuffer()), totalBytes: file.size };
|
|
202
204
|
},
|
|
203
205
|
getJobs: () => session.getAsyncJobSnapshot(),
|
|
206
|
+
getProjectProgress: async () =>
|
|
207
|
+
toProjectProgressSnapshot(await buildSessionProjectProgress(session, session.sessionManager)),
|
|
204
208
|
onJobFold: listener => session.onJobFold(listener),
|
|
205
209
|
onSessionEvent: listener => session.subscribe(listener),
|
|
206
210
|
setSdkPermissionProvider: provider => session.setSdkPermissionProvider(provider),
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getStreamIdleTimeoutRemediationMs } from "@gajae-code/ai/utils/idle-iterator";
|
|
2
|
+
|
|
1
3
|
const STREAM_IDLE_TIMEOUT_PATTERN = /\bstream stalled while waiting for the next event\b/i;
|
|
2
4
|
const GENERIC_ABORT_PATTERN = /^Request was aborted\.?$/i;
|
|
3
5
|
const ABORT_DISPLAY_LABEL_PATTERN = /^(?:Operation aborted|Aborted after \d+ retry attempts?)(?::|$)/;
|
|
@@ -37,5 +39,5 @@ function normalizeAbortCause(errorMessage: string | undefined): string {
|
|
|
37
39
|
function streamIdleTimeoutHint(cause: string): string {
|
|
38
40
|
if (!STREAM_IDLE_TIMEOUT_PATTERN.test(cause)) return "";
|
|
39
41
|
const separator = /[.!?]$/.test(cause) ? " " : ". ";
|
|
40
|
-
return `${separator}Hint: set PI_STREAM_IDLE_TIMEOUT_MS
|
|
42
|
+
return `${separator}Hint: set PI_STREAM_IDLE_TIMEOUT_MS=${getStreamIdleTimeoutRemediationMs()} for slow reasoning/proxy streams, or PI_STREAM_IDLE_TIMEOUT_MS=0 to disable the watchdog.`;
|
|
41
43
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { InteractiveModeContext } from "../types";
|
|
2
|
+
|
|
3
|
+
/** Ordinary focused UI owns interrupt/back before background work does. */
|
|
4
|
+
export function focusedUiOwnsInterrupt(
|
|
5
|
+
ctx: Pick<InteractiveModeContext, "ui" | "editor" | "hookSelector" | "hookInput" | "hookEditor">,
|
|
6
|
+
): boolean {
|
|
7
|
+
const focused = ctx.ui.getFocusedComponent?.();
|
|
8
|
+
if (!focused?.handleInput || focused === ctx.editor) return false;
|
|
9
|
+
// Non-inline hook prompts deliberately retain workflow-level interruption.
|
|
10
|
+
if (focused === ctx.hookSelector) return ctx.hookSelector.hasActiveInlineInput();
|
|
11
|
+
return focused !== ctx.hookInput && focused !== ctx.hookEditor;
|
|
12
|
+
}
|
|
@@ -16,6 +16,10 @@ export function matchesAppInterrupt(data: string): boolean {
|
|
|
16
16
|
return matchesKey(data, "escape") || matchesKey(data, "esc");
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export function matchesAppClear(data: string): boolean {
|
|
20
|
+
return getKeybindings().matches(data, "app.clear");
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
export function matchesSelectCancel(data: string): boolean {
|
|
20
24
|
return getKeybindings().matches(data, "tui.select.cancel");
|
|
21
25
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { KeyId } from "@gajae-code/tui";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Characters macOS composes for Option+<letter> on the US/ABC layout when the
|
|
5
|
+
* terminal sends Option as text instead of Alt/Meta. Dead-key letters (e, i, n,
|
|
6
|
+
* u) compose nothing on their own and are absent.
|
|
7
|
+
*/
|
|
8
|
+
const MACOS_OPTION_COMPOSED_KEYS: ReadonlyMap<string, KeyId> = new Map<string, KeyId>([
|
|
9
|
+
["å", "alt+a"],
|
|
10
|
+
["∫", "alt+b"],
|
|
11
|
+
["ç", "alt+c"],
|
|
12
|
+
["∂", "alt+d"],
|
|
13
|
+
["ƒ", "alt+f"],
|
|
14
|
+
["©", "alt+g"],
|
|
15
|
+
["˙", "alt+h"],
|
|
16
|
+
["∆", "alt+j"],
|
|
17
|
+
["˚", "alt+k"],
|
|
18
|
+
["¬", "alt+l"],
|
|
19
|
+
["µ", "alt+m"],
|
|
20
|
+
["ø", "alt+o"],
|
|
21
|
+
["π", "alt+p"],
|
|
22
|
+
["œ", "alt+q"],
|
|
23
|
+
["®", "alt+r"],
|
|
24
|
+
["ß", "alt+s"],
|
|
25
|
+
["†", "alt+t"],
|
|
26
|
+
["√", "alt+v"],
|
|
27
|
+
["∑", "alt+w"],
|
|
28
|
+
["≈", "alt+x"],
|
|
29
|
+
["¥", "alt+y"],
|
|
30
|
+
["Ω", "alt+z"],
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
/** The Option chord that composes `text` on the macOS US/ABC layout, if any. */
|
|
34
|
+
export function macosOptionChordForComposedText(text: string): KeyId | undefined {
|
|
35
|
+
return MACOS_OPTION_COMPOSED_KEYS.get(text);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Terminal-specific instruction for sending Option as Alt/Meta, when the terminal is known. */
|
|
39
|
+
export function macosOptionForwardingInstruction(terminalProgram: string | undefined): string | undefined {
|
|
40
|
+
const program = terminalProgram?.toLowerCase() ?? "";
|
|
41
|
+
if (program.includes("ghostty")) {
|
|
42
|
+
return "Ghostty: set macos-option-as-alt = true in its config, then reload the config or restart Ghostty.";
|
|
43
|
+
}
|
|
44
|
+
if (program === "apple_terminal") {
|
|
45
|
+
return "Terminal.app: enable Settings > Profiles > Keyboard > Use Option as Meta key.";
|
|
46
|
+
}
|
|
47
|
+
if (program === "iterm.app") {
|
|
48
|
+
return "iTerm2: set Settings > Profiles > Keys > Left and Right Option key to Esc+.";
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface UnforwardedOptionChordWarnerOptions {
|
|
54
|
+
platform: NodeJS.Platform;
|
|
55
|
+
terminalProgram: string | undefined;
|
|
56
|
+
formatKeyHint: (key: KeyId) => string;
|
|
57
|
+
showWarning: (message: string) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Builds the composer callback that explains, once, why a bound Option shortcut
|
|
62
|
+
* produced a character instead of firing. Returns undefined off macOS, where the
|
|
63
|
+
* composition table does not apply.
|
|
64
|
+
*/
|
|
65
|
+
export function createUnforwardedOptionChordWarner(
|
|
66
|
+
options: UnforwardedOptionChordWarnerOptions,
|
|
67
|
+
): ((key: KeyId, text: string) => void) | undefined {
|
|
68
|
+
if (options.platform !== "darwin") return undefined;
|
|
69
|
+
let warned = false;
|
|
70
|
+
return (key, text) => {
|
|
71
|
+
if (warned) return;
|
|
72
|
+
warned = true;
|
|
73
|
+
const instruction =
|
|
74
|
+
macosOptionForwardingInstruction(options.terminalProgram) ??
|
|
75
|
+
"Configure the terminal to send Option as Alt/Meta (Esc+).";
|
|
76
|
+
options.showWarning(
|
|
77
|
+
`${options.formatKeyHint(key)} arrived as "${text}": the terminal is sending Option as text, so Option shortcuts cannot reach GJC. ${instruction} Or remap the shortcut to a Control chord in keybindings.json.`,
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
}
|