@gajae-code/coding-agent 0.12.21 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -2
- package/dist/types/cli/args.d.ts +5 -2
- package/dist/types/cli/root-flags.d.ts +246 -0
- package/dist/types/cli.d.ts +11 -19
- package/dist/types/commands/daemon.d.ts +0 -30
- package/dist/types/commands/launch.d.ts +11 -19
- package/dist/types/commands/quick-lane.d.ts +32 -0
- package/dist/types/commands/sdk.d.ts +21 -15
- package/dist/types/config/file-lock-gc.d.ts +2 -0
- package/dist/types/config/model-equivalence.d.ts +20 -0
- package/dist/types/config/model-profile-activation.d.ts +62 -17
- package/dist/types/config/model-profiles.d.ts +10 -0
- package/dist/types/config/model-registry.d.ts +68 -1
- package/dist/types/config/model-resolver.d.ts +15 -4
- package/dist/types/config/models-config-schema.d.ts +7 -2
- package/dist/types/config/provider-selection-policy.d.ts +62 -0
- package/dist/types/config/settings-schema.d.ts +36 -78
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/edit/renderer.d.ts +5 -2
- package/dist/types/edit/streaming.d.ts +13 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -0
- package/dist/types/extensibility/extensions/compact-handler.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +19 -5
- package/dist/types/internal-urls/local-protocol.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +0 -2
- package/dist/types/modes/components/provider-order-context.d.ts +45 -0
- package/dist/types/modes/components/provider-order-editor.d.ts +24 -0
- package/dist/types/modes/components/settings-defs.d.ts +5 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/quick-lane/classify.d.ts +40 -0
- package/dist/types/runtime/stt-settings-setup.d.ts +18 -0
- package/dist/types/sdk/broker/broker.d.ts +56 -2
- package/dist/types/sdk/broker/operation-receipt-ledger.d.ts +25 -0
- package/dist/types/sdk/broker/session-index.d.ts +83 -2
- package/dist/types/sdk/broker/state-version.d.ts +1 -1
- package/dist/types/sdk/bus/notification-service.d.ts +6 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +8 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +26 -0
- package/dist/types/sdk/cli/rows.d.ts +98 -0
- package/dist/types/sdk/cli/session-cli.d.ts +35 -3
- package/dist/types/sdk/elevation/allowlist.d.ts +20 -0
- package/dist/types/sdk/elevation/audit.d.ts +93 -0
- package/dist/types/sdk/elevation/capability.d.ts +3 -0
- package/dist/types/sdk/elevation/digest.d.ts +72 -0
- package/dist/types/sdk/elevation/dispatch-receipt.d.ts +85 -0
- package/dist/types/sdk/elevation/grant-ledger.d.ts +163 -0
- package/dist/types/sdk/elevation/owner.d.ts +48 -0
- package/dist/types/sdk/guides/cache.d.ts +98 -0
- package/dist/types/sdk/guides/catalog.d.ts +156 -0
- package/dist/types/sdk/guides/cli.d.ts +39 -0
- package/dist/types/sdk/guides/manifest.d.ts +66 -0
- package/dist/types/sdk/guides/verify.d.ts +66 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +8 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/query/cursor.d.ts +10 -0
- package/dist/types/sdk/host/query/handlers.d.ts +21 -0
- package/dist/types/sdk/host/session-runtime.d.ts +9 -0
- package/dist/types/sdk/mcp/server.d.ts +2 -2
- package/dist/types/sdk/prompt-status.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +4 -0
- package/dist/types/sdk/transport/index.d.ts +1 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +20 -1
- package/dist/types/session/agent-session.d.ts +32 -18
- package/dist/types/session/blob-store.d.ts +13 -7
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +25 -1
- package/dist/types/session/internal/session-memory-sidecar.d.ts +5 -5
- package/dist/types/session/session-manager.d.ts +139 -10
- package/dist/types/session/session-storage.d.ts +48 -0
- package/dist/types/setup/provider-onboarding.d.ts +7 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +14 -0
- package/dist/types/stt/setup.d.ts +2 -0
- package/dist/types/task/executor.d.ts +10 -2
- package/dist/types/tools/browser/runtime-diagnostics.d.ts +65 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -0
- package/dist/types/tools/browser.d.ts +2 -0
- package/dist/types/tools/image-gen.d.ts +14 -18
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/resource-gc.d.ts +2 -0
- package/dist/types/utils/pasted-image-path.d.ts +6 -0
- package/package.json +9 -9
- package/scripts/generate-sdk-operation-inventory.ts +8 -0
- package/scripts/run-test-manifest.ts +61 -21
- package/scripts/verify-gjc-sdk-canonicalization.ts +9 -4
- package/src/cli/args.ts +62 -9
- package/src/cli/completion-cli.ts +1 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/root-flags.ts +100 -0
- package/src/cli/setup-cli.ts +6 -2
- package/src/cli.ts +42 -65
- package/src/commands/acp.ts +1 -1
- package/src/commands/daemon.ts +2 -31
- package/src/commands/gc.ts +1 -1
- package/src/commands/launch.ts +8 -129
- package/src/commands/quick-lane.ts +51 -0
- package/src/commands/sdk.ts +200 -26
- package/src/commands/setup.ts +4 -2
- package/src/config/file-lock-gc.ts +4 -1
- package/src/config/model-equivalence.ts +76 -7
- package/src/config/model-profile-activation.ts +1115 -265
- package/src/config/model-profiles.ts +134 -2
- package/src/config/model-registry.ts +352 -50
- package/src/config/model-resolver.ts +289 -38
- package/src/config/models-config-schema.ts +11 -4
- package/src/config/provider-selection-policy.ts +146 -0
- package/src/config/settings-schema.ts +50 -77
- package/src/config/settings.ts +40 -0
- package/src/coordinator-mcp/server.ts +32 -2
- package/src/edit/renderer.ts +175 -223
- package/src/edit/streaming.ts +108 -1
- package/src/export/html/index.ts +57 -7
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/extensions/compact-handler.ts +3 -2
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/gjc-runtime/gc-runtime.ts +379 -20
- package/src/gjc-runtime/launch-worktree.ts +5 -1
- package/src/gjc-runtime/team-runtime.ts +59 -2
- package/src/gjc-runtime/tmux-owner-isolation.ts +19 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +16 -1
- package/src/internal-urls/docs-index.generated.ts +17 -13
- package/src/internal-urls/local-protocol.ts +1 -0
- package/src/main.ts +40 -3
- package/src/memories/index.ts +4 -2
- package/src/modes/acp/acp-agent.ts +235 -91
- package/src/modes/components/model-selector.ts +206 -42
- package/src/modes/components/provider-order-context.ts +197 -0
- package/src/modes/components/provider-order-editor.ts +441 -0
- package/src/modes/components/settings-defs.ts +21 -1
- package/src/modes/components/settings-selector.ts +45 -2
- package/src/modes/components/tool-execution.ts +47 -15
- package/src/modes/controllers/command-controller.ts +6 -3
- package/src/modes/controllers/extension-ui-controller.ts +2 -0
- package/src/modes/controllers/input-controller.ts +73 -2
- package/src/modes/controllers/selector-controller.ts +260 -153
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +18 -0
- package/src/prompts/tools/browser.md +1 -0
- package/src/quick-lane/classify.ts +257 -0
- package/src/rlm/index.ts +1 -1
- package/src/runtime/stt-settings-setup.ts +57 -0
- package/src/sdk/acp/adapter.ts +2 -1
- package/src/sdk/broker/broker.ts +780 -12
- package/src/sdk/broker/lifecycle.ts +70 -29
- package/src/sdk/broker/operation-receipt-ledger.ts +144 -0
- package/src/sdk/broker/session-index.ts +463 -48
- package/src/sdk/broker/state-version.ts +13 -9
- package/src/sdk/broker/transport.ts +14 -1
- package/src/sdk/bus/index.ts +34 -13
- package/src/sdk/bus/kind-aware-reconciliation.ts +21 -2
- package/src/sdk/bus/notification-service.ts +47 -1
- package/src/sdk/bus/prompt-reconciliation.ts +22 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +4 -1
- package/src/sdk/bus/telegram-daemon.ts +54 -3
- package/src/sdk/cli/rows.ts +247 -0
- package/src/sdk/cli/session-cli.ts +930 -79
- package/src/sdk/elevation/allowlist.ts +54 -0
- package/src/sdk/elevation/audit.ts +208 -0
- package/src/sdk/elevation/capability.ts +41 -0
- package/src/sdk/elevation/digest.ts +129 -0
- package/src/sdk/elevation/dispatch-receipt.ts +184 -0
- package/src/sdk/elevation/grant-ledger.ts +1142 -0
- package/src/sdk/elevation/owner.ts +109 -0
- package/src/sdk/guides/cache.ts +363 -0
- package/src/sdk/guides/catalog.ts +554 -0
- package/src/sdk/guides/cli.ts +235 -0
- package/src/sdk/guides/manifest.ts +152 -0
- package/src/sdk/guides/verify.ts +178 -0
- package/src/sdk/host/control/dispatch.ts +49 -1
- package/src/sdk/host/control/index.ts +1 -0
- package/src/sdk/host/control/operations.ts +8 -1
- package/src/sdk/host/query/cursor.ts +47 -3
- package/src/sdk/host/query/handlers.ts +155 -2
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +391 -1
- package/src/sdk/host/session-runtime.ts +308 -73
- package/src/sdk/host/surface-policy.ts +3 -0
- package/src/sdk/host/websocket-transport.ts +7 -1
- package/src/sdk/mcp/server.ts +88 -15
- package/src/sdk/prompt-status.ts +8 -0
- package/src/sdk/protocol/operation-inventory.generated.json +62 -0
- package/src/sdk/protocol/operation-registry.ts +3 -2
- package/src/sdk/session.ts +87 -39
- package/src/sdk/transport/index.ts +1 -0
- package/src/sdk/transport/relay.ts +3 -0
- package/src/sdk/transport/serve-cli.ts +111 -24
- package/src/secrets/obfuscator.ts +92 -12
- package/src/session/agent-session.ts +322 -93
- package/src/session/blob-store.ts +60 -8
- package/src/session/fallback-chain-controller.ts +35 -0
- package/src/session/internal/managed-session-scope.ts +25 -3
- package/src/session/internal/managed-session-storage.ts +574 -73
- package/src/session/internal/session-memory-sidecar.ts +19 -29
- package/src/session/session-manager.ts +2166 -743
- package/src/session/session-storage.ts +715 -48
- package/src/setup/provider-onboarding.ts +19 -3
- package/src/setup/provider-presets.json +3 -1
- package/src/slash-commands/builtin-registry.ts +85 -3
- package/src/stt/downloader.ts +2 -3
- package/src/stt/setup.ts +29 -4
- package/src/task/executor.ts +22 -18
- package/src/task/index.ts +24 -1
- package/src/tools/archive-reader.ts +11 -2
- package/src/tools/browser/runtime-diagnostics.ts +249 -0
- package/src/tools/browser/tab-protocol.ts +4 -0
- package/src/tools/browser/tab-supervisor.ts +5 -0
- package/src/tools/browser/tab-worker.ts +36 -1
- package/src/tools/browser.ts +7 -0
- package/src/tools/image-gen.ts +110 -166
- package/src/tools/index.ts +2 -0
- package/src/tools/read.ts +1 -1
- package/src/tools/resource-gc.ts +6 -1
- package/src/tools/tool-catalog.generated.ts +5 -1
- package/src/utils/pasted-image-path.ts +93 -0
- package/src/web/search/index.ts +3 -3
|
@@ -47,12 +47,18 @@ interface ProviderPreset {
|
|
|
47
47
|
compatibility: ProviderCompatibility;
|
|
48
48
|
api: ProviderSetupApi;
|
|
49
49
|
providerId: string;
|
|
50
|
-
baseUrl
|
|
50
|
+
baseUrl?: string;
|
|
51
51
|
apiKeyEnv: string;
|
|
52
52
|
models?: readonly string[];
|
|
53
53
|
modelApi?: Readonly<Record<string, ProviderSetupApi>>;
|
|
54
54
|
compat?: ProviderCompatConfig;
|
|
55
55
|
discovery?: ProviderDiscovery;
|
|
56
|
+
/**
|
|
57
|
+
* Parameterized presets (proxy gateways) do not hardcode a base URL or an
|
|
58
|
+
* unoverridable apiKeyEnv: the caller must supply `--base-url` and may
|
|
59
|
+
* override `--api-key-env`. `baseUrl` is absent for these presets.
|
|
60
|
+
*/
|
|
61
|
+
parameterized?: boolean;
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
const PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
@@ -144,11 +150,16 @@ function resolvePresetInput(input: ProviderSetupInput): {
|
|
|
144
150
|
`Provider preset '${preset.id}' is ${preset.compatibility}-compatible; omit --compat or use '${preset.compatibility}'.`,
|
|
145
151
|
);
|
|
146
152
|
}
|
|
147
|
-
if (preset && input.baseUrl !== undefined) {
|
|
153
|
+
if (preset && input.baseUrl !== undefined && !preset.parameterized) {
|
|
148
154
|
throw new Error(
|
|
149
155
|
`Provider preset '${preset.id}' uses a fixed base URL; omit --base-url or use --compat openai for a custom provider.`,
|
|
150
156
|
);
|
|
151
157
|
}
|
|
158
|
+
if (preset?.parameterized && !input.baseUrl) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Provider preset '${preset.id}' requires --base-url <url> (your proxy endpoint). Use --compat openai for a fully custom provider instead.`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
152
163
|
if (preset && input.models && input.models.length > 0) {
|
|
153
164
|
const catalogMode =
|
|
154
165
|
preset.models && preset.models.length > 0 ? "uses fixed model ids" : "discovers models automatically";
|
|
@@ -156,7 +167,12 @@ function resolvePresetInput(input: ProviderSetupInput): {
|
|
|
156
167
|
`Provider preset '${preset.id}' ${catalogMode}; omit --model or use --compat openai for a custom provider.`,
|
|
157
168
|
);
|
|
158
169
|
}
|
|
159
|
-
if (
|
|
170
|
+
if (
|
|
171
|
+
preset &&
|
|
172
|
+
input.apiKeyEnv !== undefined &&
|
|
173
|
+
!preset.parameterized &&
|
|
174
|
+
input.apiKeyEnv.trim() !== preset.apiKeyEnv
|
|
175
|
+
) {
|
|
160
176
|
throw new Error(
|
|
161
177
|
`Provider preset '${preset.id}' uses ${preset.apiKeyEnv}; omit --api-key-env or use --compat openai for a custom provider.`,
|
|
162
178
|
);
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
{"id":"glm","aliases":["zai","z-ai","bigmodel"],"name":"GLM / zAI","description":"OpenAI-compatible GLM endpoint from zAI/BigModel","compatibility":"openai","api":"openai-completions","providerId":"glm-proxy","baseUrl":"https://api.z.ai/api/paas/v4","apiKeyEnv":"ZAI_API_KEY","models":["glm-4.6"],"compat":{"supportsDeveloperRole":false,"supportsReasoningEffort":false,"thinkingFormat":"zai","reasoningContentField":"reasoning_content"}},
|
|
5
5
|
{"id":"alibaba-token-plan","aliases":["alibaba","token-plan"],"name":"Alibaba Token Plan","description":"OpenAI-compatible Alibaba Token Plan endpoint","compatibility":"openai","api":"openai-completions","providerId":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","apiKeyEnv":"ALIBABA_TOKEN_PLAN_API_KEY","models":["qwen3.8-max-preview","qwen3.8-max","glm-5.2","deepseek-v4-pro"],"modelApi":{"qwen3.8-max-preview":"openai-responses","qwen3.8-max":"openai-responses","glm-5.2":"openai-completions","deepseek-v4-pro":"openai-completions"},"compat":{"supportsDeveloperRole":false}},
|
|
6
6
|
{"id":"cline-pass","aliases":["clinepass","cline"],"name":"ClinePass","description":"ClinePass subscription models discovered from Cline's upstream models.dev catalog","compatibility":"openai","api":"openai-completions","providerId":"cline-pass","baseUrl":"https://api.cline.bot/api/v1","apiKeyEnv":"CLINE_API_KEY","discovery":{"type":"models-dev","modelsDevProvider":"cline-pass"},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"reasoningContentField":"reasoning"}},
|
|
7
|
-
{"id":"commandcode-goat","aliases":["commandcode","command-code","goat"],"name":"Command Code GOAT","description":"Command Code GOAT coding-plan models discovered from its OpenAI-compatible Provider API","compatibility":"openai","api":"openai-completions","providerId":"commandcode-goat","baseUrl":"https://api.commandcode.ai/provider/v1","apiKeyEnv":"CMD_API_KEY","discovery":{"type":"openai-models-list","apiByModelPrefix":{"claude-":"anthropic-messages"}},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"reasoningContentField":"reasoning_content"}}
|
|
7
|
+
{"id":"commandcode-goat","aliases":["commandcode","command-code","goat"],"name":"Command Code GOAT","description":"Command Code GOAT coding-plan models discovered from its OpenAI-compatible Provider API","compatibility":"openai","api":"openai-completions","providerId":"commandcode-goat","baseUrl":"https://api.commandcode.ai/provider/v1","apiKeyEnv":"CMD_API_KEY","discovery":{"type":"openai-models-list","apiByModelPrefix":{"claude-":"anthropic-messages"}},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"reasoningContentField":"reasoning_content"}},
|
|
8
|
+
{"id":"litellm","aliases":["litellm-proxy"],"name":"LiteLLM Proxy","description":"OpenAI-compatible LiteLLM proxy endpoint (user-supplied base URL) with live model discovery","compatibility":"openai","api":"openai-completions","providerId":"litellm","parameterized":true,"apiKeyEnv":"LITELLM_API_KEY","discovery":{"type":"openai-models-list"}},
|
|
9
|
+
{"id":"openai-compatible-proxy","aliases":["openai-proxy","compatible-proxy","custom-proxy"],"name":"OpenAI-Compatible Proxy","description":"Generic OpenAI-compatible gateway (OpenRouter, OpenCode Go, custom) with live model discovery","compatibility":"openai","api":"openai-completions","providerId":"openai-compatible-proxy","parameterized":true,"apiKeyEnv":"OPENAI_COMPATIBLE_API_KEY","discovery":{"type":"openai-models-list"}}
|
|
8
10
|
]
|
|
@@ -6,7 +6,8 @@ import { getOAuthProviders } from "@gajae-code/ai/utils/oauth";
|
|
|
6
6
|
import { PET_SKINS, type PetMode, Spacer, Text } from "@gajae-code/tui";
|
|
7
7
|
import { setProjectDir } from "@gajae-code/utils";
|
|
8
8
|
import { jobElapsedMs } from "../async";
|
|
9
|
-
import { materializeActiveModelProfileAssignments } from "../config/model-profile-activation";
|
|
9
|
+
import { activateModelProfile, materializeActiveModelProfileAssignments } from "../config/model-profile-activation";
|
|
10
|
+
import { formatModelProfileDisplayLabel } from "../config/model-profiles";
|
|
10
11
|
import {
|
|
11
12
|
GJC_MODEL_ASSIGNMENT_TARGET_IDS,
|
|
12
13
|
GJC_MODEL_ASSIGNMENT_TARGETS,
|
|
@@ -243,6 +244,54 @@ function parseModelCommandArgs(args: string): ParsedModelCommandArgs {
|
|
|
243
244
|
}
|
|
244
245
|
return { kind: "assign", targetId: "default", selector: args.trim() };
|
|
245
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* Optional namespace prefix accepted on `/model <preset>` selectors so users
|
|
249
|
+
* can disambiguate preset names from model ids with `gajae-code/<preset>`.
|
|
250
|
+
* The bare preset name remains the canonical form.
|
|
251
|
+
*/
|
|
252
|
+
const MODEL_PRESET_NAMESPACE_PREFIX = "gajae-code/";
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Resolve a `/model <selector>` argument against the merged model-profile
|
|
256
|
+
* registry. Returns the canonical profile name when `selector` is either a
|
|
257
|
+
* bare preset name (`codex-medium`) or a namespaced one
|
|
258
|
+
* (`gajae-code/codex-medium`); returns `undefined` otherwise so the caller
|
|
259
|
+
* falls through to ordinary model resolution.
|
|
260
|
+
*
|
|
261
|
+
* Only selectors that do NOT look like `provider/model` references are
|
|
262
|
+
* considered, so `/model anthropic/claude-...` is never hijacked by a preset.
|
|
263
|
+
*/
|
|
264
|
+
export function resolvePresetSelector(
|
|
265
|
+
selector: string,
|
|
266
|
+
modelRegistry: { getModelProfile?: (name: string) => unknown; getError?: () => unknown },
|
|
267
|
+
): string | undefined {
|
|
268
|
+
const trimmed = selector.trim();
|
|
269
|
+
if (!trimmed) return undefined;
|
|
270
|
+
|
|
271
|
+
// Reject `provider/model` references outright: even if a preset happened to
|
|
272
|
+
// be named `anthropic/claude`, the slash form is a model selector, not a
|
|
273
|
+
// preset shortcut. The only accepted slash form is the `gajae-code/`
|
|
274
|
+
// namespace prefix.
|
|
275
|
+
if (trimmed.includes("/")) {
|
|
276
|
+
if (!trimmed.toLowerCase().startsWith(MODEL_PRESET_NAMESPACE_PREFIX)) return undefined;
|
|
277
|
+
const stripped = trimmed.slice(MODEL_PRESET_NAMESPACE_PREFIX.length);
|
|
278
|
+
if (!stripped || stripped.includes("/")) return undefined;
|
|
279
|
+
return matchProfileName(stripped, modelRegistry);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return matchProfileName(trimmed, modelRegistry);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function matchProfileName(
|
|
286
|
+
candidate: string,
|
|
287
|
+
modelRegistry: { getModelProfile?: (name: string) => unknown; getError?: () => unknown },
|
|
288
|
+
): string | undefined {
|
|
289
|
+
// A registry load error means we cannot trust the profile index; fall
|
|
290
|
+
// through to model resolution rather than guessing. A registry without a
|
|
291
|
+
// getModelProfile surface (e.g. a minimal test fixture) has no presets.
|
|
292
|
+
if (modelRegistry.getError?.()) return undefined;
|
|
293
|
+
return modelRegistry.getModelProfile?.(candidate) ? candidate : undefined;
|
|
294
|
+
}
|
|
246
295
|
|
|
247
296
|
function splitExplicitThinkingSelector(selector: string): { baseSelector: string; thinkingLevel?: ThinkingLevel } {
|
|
248
297
|
const trimmed = selector.trim();
|
|
@@ -280,6 +329,8 @@ function resolveModelCommandSelectionFromAvailable(
|
|
|
280
329
|
const matchPreferences = { usageOrder: runtime.settings.getStorage()?.getModelUsageOrder() };
|
|
281
330
|
const resolved = parseModelPattern(selector, availableModels, matchPreferences, {
|
|
282
331
|
modelRegistry: runtime.session.modelRegistry,
|
|
332
|
+
sessionId: runtime.session.sessionId,
|
|
333
|
+
credentialSessionId: runtime.session.credentialSessionId,
|
|
283
334
|
});
|
|
284
335
|
if (!resolved.model) {
|
|
285
336
|
return undefined;
|
|
@@ -389,7 +440,7 @@ function formatModelAssignmentSuccess(
|
|
|
389
440
|
return `Role-agent models set to ${selector} for EXECUTOR, ARCHITECT, PLANNER, CRITIC.`;
|
|
390
441
|
}
|
|
391
442
|
if (targetId === "all-targets") {
|
|
392
|
-
return `All model targets set to ${selector} for DEFAULT, EXECUTOR, ARCHITECT, PLANNER, CRITIC.`;
|
|
443
|
+
return `All model targets set to ${selector} for DEFAULT, EXECUTOR, ARCHITECT, PLANNER, CRITIC, IMAGE.`;
|
|
393
444
|
}
|
|
394
445
|
if (targetId === "default") return `Default model set to ${selector}.`;
|
|
395
446
|
return `${targetId} agent model set to ${selector}.`;
|
|
@@ -400,6 +451,7 @@ function modelSelectionUsage(runtime: SlashCommandRuntime, currentModelLine?: st
|
|
|
400
451
|
currentModelLine,
|
|
401
452
|
formatModelAssignmentSummary(runtime),
|
|
402
453
|
"Use /model <model> for DEFAULT, or /model <target> <model[:effort]> for EXECUTOR, ARCHITECT, PLANNER, or CRITIC.",
|
|
454
|
+
"Use /model <preset> or /model gajae-code/<preset> to activate a known model profile.",
|
|
403
455
|
formatModelOnboardingGuidance(),
|
|
404
456
|
]
|
|
405
457
|
.filter((line): line is string => Boolean(line))
|
|
@@ -745,6 +797,36 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
745
797
|
runtime,
|
|
746
798
|
);
|
|
747
799
|
}
|
|
800
|
+
// Preset shortcut: when the selector names a known model profile
|
|
801
|
+
// (optionally `gajae-code/`-prefixed) and no explicit role target
|
|
802
|
+
// was given, activate the profile immediately instead of treating
|
|
803
|
+
// the preset name as a model id and failing with "Unknown model".
|
|
804
|
+
// `/model <role> <preset>` keeps assigning to the named role.
|
|
805
|
+
if (parsedArgs.targetId === "default") {
|
|
806
|
+
const presetName = resolvePresetSelector(modelId, runtime.session.modelRegistry);
|
|
807
|
+
if (presetName) {
|
|
808
|
+
try {
|
|
809
|
+
const profileLabel = formatModelProfileDisplayLabel(
|
|
810
|
+
runtime.session.modelRegistry.getModelProfile(presetName) ?? { name: presetName },
|
|
811
|
+
);
|
|
812
|
+
await activateModelProfile(
|
|
813
|
+
{
|
|
814
|
+
session: runtime.session,
|
|
815
|
+
modelRegistry: runtime.session.modelRegistry,
|
|
816
|
+
settings: runtime.settings,
|
|
817
|
+
profileName: presetName,
|
|
818
|
+
},
|
|
819
|
+
{ persistDefault: false },
|
|
820
|
+
);
|
|
821
|
+
await runtime.output(`Model profile: ${profileLabel}`);
|
|
822
|
+
await runtime.notifyTitleChanged?.();
|
|
823
|
+
await runtime.notifyConfigChanged?.();
|
|
824
|
+
return commandConsumed();
|
|
825
|
+
} catch (err) {
|
|
826
|
+
return usage(`Failed to activate model profile: ${errorMessage(err)}`, runtime);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
748
830
|
const resolution = await resolveModelCommandSelection(runtime, modelId);
|
|
749
831
|
if (!resolution.ok) {
|
|
750
832
|
return usage(modelSelectionUsage(runtime, resolution.failure.message), runtime);
|
|
@@ -756,7 +838,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
756
838
|
if (includesRoleAgent) {
|
|
757
839
|
const apiKey = await runtime.session.modelRegistry.getApiKey(
|
|
758
840
|
selection.model,
|
|
759
|
-
runtime.session.
|
|
841
|
+
runtime.session.credentialSessionId,
|
|
760
842
|
);
|
|
761
843
|
if (!apiKey) {
|
|
762
844
|
throw new Error(`No API key for ${selection.model.provider}/${selection.model.id}`);
|
package/src/stt/downloader.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { $which, logger } from "@gajae-code/utils";
|
|
2
2
|
import { $ } from "bun";
|
|
3
|
+
import { getRecorderInstallHint } from "./setup";
|
|
3
4
|
import { resolvePython } from "./transcriber";
|
|
4
5
|
|
|
5
6
|
export interface DownloadProgress {
|
|
@@ -32,9 +33,7 @@ async function ensureRecordingTool(options?: EnsureOptions): Promise<void> {
|
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
throw new Error(
|
|
36
|
-
"No audio recording tool found. Install SoX: sudo apt install sox, or FFmpeg: sudo apt install ffmpeg",
|
|
37
|
-
);
|
|
36
|
+
throw new Error(`No audio recording tool found. ${getRecorderInstallHint()}`);
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
// ── Python whisper ─────────────────────────────────────────────────
|
package/src/stt/setup.ts
CHANGED
|
@@ -1,7 +1,34 @@
|
|
|
1
1
|
import { detectRecordingTools } from "./recorder";
|
|
2
2
|
import { resolvePython } from "./transcriber";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
export function getRecorderInstallHint(platform: NodeJS.Platform = process.platform): string {
|
|
5
|
+
if (platform === "win32") return "PowerShell fallback available. For better quality: install SoX or FFmpeg.";
|
|
6
|
+
if (platform === "darwin") return "Install a recorder with Homebrew: brew install sox (or brew install ffmpeg)";
|
|
7
|
+
return "Install a recorder: sudo apt install sox (or sudo apt install ffmpeg)";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function formatSTTUsage(
|
|
11
|
+
platform: NodeJS.Platform = process.platform,
|
|
12
|
+
terminalProgram: string | undefined = Bun.env.TERM_PROGRAM,
|
|
13
|
+
): string {
|
|
14
|
+
const lines = [
|
|
15
|
+
"Enable STT: gjc config set stt.enabled true",
|
|
16
|
+
"You can also enable it in /settings > Interaction > Speech-to-Text.",
|
|
17
|
+
"In the composer, press Alt+H to start recording, then press Alt+H again to stop and transcribe.",
|
|
18
|
+
"The transcription is inserted into the composer for review before you send it.",
|
|
19
|
+
"Shortcut fallback: press Ctrl+P and select Toggle speech-to-text; repeat to stop and transcribe.",
|
|
20
|
+
];
|
|
21
|
+
if (platform === "darwin") {
|
|
22
|
+
lines.push("On macOS, Alt+H is Option+H. Your terminal must forward Option as Meta/Esc.");
|
|
23
|
+
if (terminalProgram?.toLowerCase().includes("ghostty")) {
|
|
24
|
+
lines.push(
|
|
25
|
+
"Ghostty: set macos-option-as-alt = true in its config, then reload the config or restart Ghostty.",
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
lines.push("Run /hotkeys inside GJC to confirm the active shortcut.");
|
|
30
|
+
return lines.join("\n");
|
|
31
|
+
}
|
|
5
32
|
|
|
6
33
|
export interface STTDependencyStatus {
|
|
7
34
|
recorder: { available: boolean; tool: string | null; installHint: string };
|
|
@@ -11,9 +38,7 @@ export interface STTDependencyStatus {
|
|
|
11
38
|
|
|
12
39
|
export async function checkDependencies(): Promise<STTDependencyStatus> {
|
|
13
40
|
const recorderTools = detectRecordingTools();
|
|
14
|
-
const recorderHint =
|
|
15
|
-
? "PowerShell fallback available. For better quality: install SoX or FFmpeg."
|
|
16
|
-
: "Install SoX: sudo apt install sox, or FFmpeg: sudo apt install ffmpeg";
|
|
41
|
+
const recorderHint = getRecorderInstallHint();
|
|
17
42
|
|
|
18
43
|
const pythonCmd = resolvePython();
|
|
19
44
|
const pythonHint = "Install Python 3.8+ from https://python.org";
|
package/src/task/executor.ts
CHANGED
|
@@ -40,7 +40,7 @@ import type { AgentSession, AgentSessionEvent, ForkContextSeed } from "../sessio
|
|
|
40
40
|
import type { ArtifactManager } from "../session/artifacts";
|
|
41
41
|
import type { AuthStorage } from "../session/auth-storage";
|
|
42
42
|
import { SKILL_PROMPT_MESSAGE_TYPE } from "../session/messages";
|
|
43
|
-
import { SessionManager } from "../session/session-manager";
|
|
43
|
+
import { SessionManager, type SessionMemoryMode } from "../session/session-manager";
|
|
44
44
|
import { FileSessionStorage } from "../session/session-storage";
|
|
45
45
|
import { truncateTail } from "../session/streaming-output";
|
|
46
46
|
// Ensure mandatory subagent result extraction is available even when a session is mocked.
|
|
@@ -214,7 +214,15 @@ export interface ExecutorOptions {
|
|
|
214
214
|
* if the resolved subagent model has no working credentials. See #985.
|
|
215
215
|
*/
|
|
216
216
|
parentActiveModelPattern?: string;
|
|
217
|
+
/**
|
|
218
|
+
* Whether the live parent session has an active model profile. When set,
|
|
219
|
+
* persisted `task.agentModelOverrides` may resolve through preset-equivalent
|
|
220
|
+
* aliases (bare profile aliases re-resolve to an equivalent provider variant).
|
|
221
|
+
* Manual/direct parents (no active profile) keep exact resolution.
|
|
222
|
+
*/
|
|
223
|
+
parentActiveModelProfile?: string;
|
|
217
224
|
parentSessionId?: string;
|
|
225
|
+
parentCredentialSessionId?: string;
|
|
218
226
|
thinkingLevel?: ThinkingLevel;
|
|
219
227
|
outputSchema?: unknown;
|
|
220
228
|
/** Parent task recursion depth (0 = top-level, 1 = first child, etc.) */
|
|
@@ -288,7 +296,7 @@ export class ManagedTaskPersistence {
|
|
|
288
296
|
throw new Error("Managed task persistence authority is unavailable");
|
|
289
297
|
}
|
|
290
298
|
|
|
291
|
-
async openSession(cwd: string, sessionMemoryMode:
|
|
299
|
+
async openSession(cwd: string, sessionMemoryMode: SessionMemoryMode = "shadow"): Promise<SessionManager> {
|
|
292
300
|
const store = this.#artifacts.getManagedStore();
|
|
293
301
|
if (!store) throw new Error("Managed task persistence authority is unavailable");
|
|
294
302
|
this.#artifacts.assertManagedBinding();
|
|
@@ -1513,8 +1521,11 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1513
1521
|
options.parentActiveModelPattern,
|
|
1514
1522
|
modelRegistry,
|
|
1515
1523
|
settings,
|
|
1516
|
-
|
|
1517
|
-
{
|
|
1524
|
+
options.parentCredentialSessionId ?? options.parentSessionId,
|
|
1525
|
+
{
|
|
1526
|
+
managedFallback: true,
|
|
1527
|
+
...(options.parentActiveModelProfile ? { aliasIntent: "preset-equivalent" } : {}),
|
|
1528
|
+
},
|
|
1518
1529
|
canonicalChildScope,
|
|
1519
1530
|
),
|
|
1520
1531
|
);
|
|
@@ -1664,6 +1675,8 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1664
1675
|
providerSessionId: canonicalChildScope,
|
|
1665
1676
|
model,
|
|
1666
1677
|
thinkingLevel: effectiveThinkingLevel,
|
|
1678
|
+
activeModelProfile: options.parentActiveModelProfile,
|
|
1679
|
+
credentialSessionId: options.parentCredentialSessionId ?? options.parentSessionId,
|
|
1667
1680
|
modelSubstitution:
|
|
1668
1681
|
modelSubstitutionWarning?.reason === "auth_unavailable" && requestedModel
|
|
1669
1682
|
? { requestedModel, reason: modelSubstitutionWarning.reason }
|
|
@@ -1733,20 +1746,10 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1733
1746
|
);
|
|
1734
1747
|
|
|
1735
1748
|
activeSession = session;
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
// parent selector so its request is never charged to override index zero.
|
|
1741
|
-
session.setConfiguredModelChain(
|
|
1742
|
-
"default",
|
|
1743
|
-
parentFallbackSelector ? [parentFallbackSelector] : modelPatterns,
|
|
1744
|
-
"subagent",
|
|
1745
|
-
agent.name,
|
|
1746
|
-
true,
|
|
1747
|
-
);
|
|
1748
|
-
if (activeIndex !== undefined && !parentFallbackSelector) {
|
|
1749
|
-
session.seedDefaultFallbackResolution(activeIndex, skips);
|
|
1749
|
+
const configuredChildChain = parentFallbackSelector ? [parentFallbackSelector] : modelPatterns;
|
|
1750
|
+
session.setConfiguredModelChain("default", configuredChildChain, "subagent", agent.name, true);
|
|
1751
|
+
if (!parentFallbackSelector) {
|
|
1752
|
+
session.seedDefaultFallbackResolution(activeIndex ?? 0, skips);
|
|
1750
1753
|
}
|
|
1751
1754
|
const liveSubagentId = options.subagentId ?? id;
|
|
1752
1755
|
const manager = AsyncJobManager.instance();
|
|
@@ -1871,6 +1874,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1871
1874
|
},
|
|
1872
1875
|
{
|
|
1873
1876
|
getModel: () => session.model,
|
|
1877
|
+
getCredentialSessionId: () => session.credentialSessionId,
|
|
1874
1878
|
isIdle: () => !session.isStreaming,
|
|
1875
1879
|
getActivePromptHandle: () => session.activePromptHandle,
|
|
1876
1880
|
abort: () => session.abort(),
|
package/src/task/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type { Model, Usage } from "@gajae-code/ai/core";
|
|
|
21
21
|
import { $pickenv, prompt, Snowflake } from "@gajae-code/utils";
|
|
22
22
|
import type { ToolSession } from "..";
|
|
23
23
|
import { AsyncJobManager, OwnerSubagentShutdownError, type ResumeRunner, type SubagentRunOutcome } from "../async";
|
|
24
|
+
import { resolveProfileBindings } from "../config/model-profiles";
|
|
24
25
|
import { resolveAgentModelPatterns } from "../config/model-resolver";
|
|
25
26
|
import type { Theme } from "../modes/theme/theme";
|
|
26
27
|
import planModeSubagentPrompt from "../prompts/system/plan-mode-subagent.md" with { type: "text" };
|
|
@@ -1642,6 +1643,20 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1642
1643
|
const agentModelOverrides = this.session.settings.get("task.agentModelOverrides");
|
|
1643
1644
|
const settingsModelOverride = agentModelOverrides[agentName];
|
|
1644
1645
|
const parentActiveModelPattern = this.session.getActiveModelString?.();
|
|
1646
|
+
// A live active model profile claims persisted agent overrides: bare
|
|
1647
|
+
// profile aliases may re-resolve to an equivalent provider variant at
|
|
1648
|
+
// dispatch. Manual/direct sessions (no active profile) stay exact.
|
|
1649
|
+
const parentActiveModelProfile = (
|
|
1650
|
+
this.session as { getActiveModelProfile?: () => string | undefined }
|
|
1651
|
+
).getActiveModelProfile?.();
|
|
1652
|
+
const parentProfileDefinition = parentActiveModelProfile
|
|
1653
|
+
? this.session.modelRegistry?.getModelProfile?.(parentActiveModelProfile)
|
|
1654
|
+
: undefined;
|
|
1655
|
+
const parentOwnedModelProfile =
|
|
1656
|
+
parentProfileDefinition &&
|
|
1657
|
+
Object.hasOwn(resolveProfileBindings(parentProfileDefinition).agentModelOverrides, agentName)
|
|
1658
|
+
? parentActiveModelProfile
|
|
1659
|
+
: undefined;
|
|
1645
1660
|
const modelOverride = resolveAgentModelPatterns({
|
|
1646
1661
|
settingsOverride: settingsModelOverride,
|
|
1647
1662
|
agentModel: effectiveAgent.model,
|
|
@@ -1985,7 +2000,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1985
2000
|
taskDepth,
|
|
1986
2001
|
modelOverride,
|
|
1987
2002
|
parentActiveModelPattern,
|
|
2003
|
+
parentActiveModelProfile: parentOwnedModelProfile,
|
|
1988
2004
|
parentSessionId: this.session.getSessionId?.() ?? undefined,
|
|
2005
|
+
parentCredentialSessionId:
|
|
2006
|
+
this.session.getCredentialSessionId?.() ?? this.session.getSessionId?.() ?? undefined,
|
|
1989
2007
|
thinkingLevel: thinkingLevelOverride,
|
|
1990
2008
|
outputSchema: effectiveOutputSchema,
|
|
1991
2009
|
sessionFile: taskSessionFile,
|
|
@@ -2060,7 +2078,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2060
2078
|
taskDepth,
|
|
2061
2079
|
modelOverride,
|
|
2062
2080
|
parentActiveModelPattern,
|
|
2081
|
+
parentActiveModelProfile: parentOwnedModelProfile,
|
|
2063
2082
|
parentSessionId: this.session.getSessionId?.() ?? undefined,
|
|
2083
|
+
parentCredentialSessionId:
|
|
2084
|
+
this.session.getCredentialSessionId?.() ?? this.session.getSessionId?.() ?? undefined,
|
|
2064
2085
|
thinkingLevel: thinkingLevelOverride,
|
|
2065
2086
|
outputSchema: effectiveOutputSchema,
|
|
2066
2087
|
sessionFile: taskSessionFile,
|
|
@@ -2117,7 +2138,9 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2117
2138
|
diff,
|
|
2118
2139
|
this.session.modelRegistry!,
|
|
2119
2140
|
this.session.settings,
|
|
2120
|
-
this.session.
|
|
2141
|
+
this.session.getCredentialSessionId?.() ??
|
|
2142
|
+
this.session.getSessionId?.() ??
|
|
2143
|
+
undefined,
|
|
2121
2144
|
);
|
|
2122
2145
|
}
|
|
2123
2146
|
: undefined;
|
|
@@ -156,10 +156,19 @@ async function readTarEntries(bytes: Uint8Array): Promise<ArchiveIndexEntry[]> {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
async function readZipEntries(bytes: Uint8Array): Promise<ArchiveIndexEntry[]> {
|
|
159
|
-
const {
|
|
159
|
+
const { unzip } = await loadFflate();
|
|
160
|
+
const result = Promise.withResolvers<Record<string, Uint8Array>>();
|
|
161
|
+
unzip(bytes, (error, files) => {
|
|
162
|
+
if (error) {
|
|
163
|
+
result.reject(error);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
result.resolve(files);
|
|
167
|
+
});
|
|
168
|
+
|
|
160
169
|
let files: Record<string, Uint8Array>;
|
|
161
170
|
try {
|
|
162
|
-
files =
|
|
171
|
+
files = await result.promise;
|
|
163
172
|
} catch (error) {
|
|
164
173
|
throw new ToolError(error instanceof Error ? error.message : String(error));
|
|
165
174
|
}
|