@gajae-code/coding-agent 0.16.3 → 0.16.6
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 +113 -0
- package/dist/types/capability/types.d.ts +4 -0
- package/dist/types/cli/args.d.ts +2 -2
- package/dist/types/cli/customize-doctor.d.ts +22 -0
- package/dist/types/cli/root-flags.d.ts +4 -2
- package/dist/types/cli.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +1 -1
- package/dist/types/config/autorouting-tier-map.d.ts +3 -0
- package/dist/types/config/file-lock.d.ts +31 -3
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +9 -0
- package/dist/types/config/settings-schema.d.ts +29 -1
- package/dist/types/coordinator-mcp/question-state.d.ts +7 -0
- package/dist/types/coordinator-mcp/server.d.ts +7 -0
- package/dist/types/discovery/claude-plugins.d.ts +3 -1
- package/dist/types/discovery/helpers.d.ts +35 -0
- package/dist/types/exec/bash-executor.d.ts +17 -1
- package/dist/types/exec/bash-shell-guardian.d.ts +15 -0
- package/dist/types/exec/bash-shell-runtime-entry.d.ts +2 -0
- package/dist/types/exec/bash-shell-signals.d.ts +2 -0
- package/dist/types/exec/bash-shell-supervisor-entry.d.ts +1 -0
- package/dist/types/exec/bash-shell-supervisor.d.ts +10 -0
- package/dist/types/exec/bash-shell-worker-entry.d.ts +2 -0
- package/dist/types/exec/bash-shell-worker-protocol.d.ts +63 -0
- package/dist/types/exec/bash-shell-worker.d.ts +1 -0
- package/dist/types/exec/isolated-shell.d.ts +19 -0
- package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +9 -0
- package/dist/types/extensibility/extensions/function-hooks.d.ts +169 -0
- package/dist/types/extensibility/extensions/index.d.ts +1 -0
- package/dist/types/extensibility/extensions/runner.d.ts +46 -6
- package/dist/types/extensibility/extensions/types.d.ts +16 -0
- package/dist/types/extensibility/extensions/wrapper.d.ts +3 -1
- package/dist/types/extensibility/gjc-plugins/constrained-hooks.d.ts +35 -2
- package/dist/types/extensibility/gjc-plugins/mcp-policy.d.ts +16 -0
- package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -0
- package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
- package/dist/types/extensibility/hooks/types.d.ts +4 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +5 -2
- package/dist/types/extensibility/shared-events.d.ts +1 -1
- package/dist/types/extensibility/skill-management.d.ts +9 -4
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +4 -1
- package/dist/types/gjc-runtime/linux-proc.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +14 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +41 -3
- package/dist/types/gjc-runtime/ultragoal-succession.d.ts +251 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -2
- package/dist/types/modes/components/bash-execution.d.ts +1 -0
- package/dist/types/modes/components/execution-shared.d.ts +1 -0
- package/dist/types/modes/components/settings-selector.d.ts +6 -1
- package/dist/types/modes/components/status-line/command.d.ts +35 -0
- package/dist/types/modes/components/status-line/index.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +5 -0
- package/dist/types/modes/components/tool-status-header.d.ts +14 -0
- package/dist/types/modes/controllers/event-controller.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/types.d.ts +7 -0
- package/dist/types/runtime-mcp/client.d.ts +9 -1
- package/dist/types/runtime-mcp/manager.d.ts +7 -1
- package/dist/types/runtime-mcp/pool.d.ts +22 -3
- package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
- package/dist/types/runtime-mcp/types.d.ts +22 -0
- package/dist/types/sdk/broker/ensure.d.ts +32 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +0 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
- package/dist/types/sdk/broker/startup-failure.d.ts +4 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +12 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +17 -2
- package/dist/types/sdk/bus/conversation-store.d.ts +11 -0
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +8 -8
- package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/slack-live-provider.d.ts +6 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +12 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +8 -0
- package/dist/types/sdk/client/client.d.ts +6 -0
- package/dist/types/sdk/host/host.d.ts +3 -1
- package/dist/types/sdk/host/session-runtime.d.ts +26 -1
- package/dist/types/sdk/lifecycle/service.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +10 -2
- package/dist/types/sdk/session-api-key.d.ts +26 -0
- package/dist/types/sdk/session.d.ts +5 -1
- package/dist/types/sdk/transport/serve-cli.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +11 -0
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/setup/paseo/announce.d.ts +112 -0
- package/dist/types/setup/paseo/setup-deps.d.ts +2 -1
- package/dist/types/skill-state/canonical-skills.d.ts +4 -0
- package/dist/types/system-prompt.d.ts +10 -0
- package/dist/types/task/executor.d.ts +5 -1
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/fetch.d.ts +7 -0
- package/dist/types/tools/github-cache.d.ts +2 -1
- package/dist/types/tools/index.d.ts +10 -0
- package/dist/types/tools/path-utils.d.ts +2 -0
- package/dist/types/tools/search.d.ts +6 -0
- package/package.json +9 -7
- package/scripts/capture-status-line-custom-editor-showcase.ts +151 -0
- package/scripts/generate-sdk-operation-inventory.ts +11 -0
- package/scripts/verify-status-line-custom-editor-showcase.ts +309 -0
- package/src/capability/index.ts +14 -2
- package/src/capability/types.ts +4 -0
- package/src/cli/args.ts +20 -9
- package/src/cli/customize-doctor.ts +155 -12
- package/src/cli/root-flags.ts +5 -3
- package/src/cli.ts +22 -0
- package/src/commands/sdk.ts +74 -31
- package/src/config/autorouting-tier-map.ts +1 -0
- package/src/config/file-lock-gc.ts +40 -2
- package/src/config/file-lock.ts +379 -31
- package/src/config/model-preset-registry.ts +120 -19
- package/src/config/model-profiles.ts +40 -0
- package/src/config/model-registry.ts +73 -23
- package/src/config/model-resolver.ts +48 -0
- package/src/config/settings-schema.ts +33 -1
- package/src/config/settings.ts +1 -0
- package/src/coordinator-mcp/question-state.ts +33 -2
- package/src/coordinator-mcp/server.ts +477 -101
- package/src/crash/sanitize.ts +1 -1
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +11 -11
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +13 -2
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +22 -0
- package/src/discovery/builtin.ts +52 -55
- package/src/discovery/claude-plugins.ts +12 -4
- package/src/discovery/helpers.ts +307 -34
- package/src/exec/bash-executor.ts +330 -50
- package/src/exec/bash-shell-guardian.ts +388 -0
- package/src/exec/bash-shell-runtime-entry.ts +5 -0
- package/src/exec/bash-shell-signals.ts +29 -0
- package/src/exec/bash-shell-supervisor-entry.ts +3 -0
- package/src/exec/bash-shell-supervisor.ts +359 -0
- package/src/exec/bash-shell-worker-entry.ts +5 -0
- package/src/exec/bash-shell-worker-protocol.ts +36 -0
- package/src/exec/bash-shell-worker.ts +116 -0
- package/src/exec/isolated-shell.ts +393 -0
- package/src/extensibility/extensions/function-hooks-internal.ts +73 -0
- package/src/extensibility/extensions/function-hooks.ts +959 -0
- package/src/extensibility/extensions/index.ts +22 -0
- package/src/extensibility/extensions/loader.ts +53 -2
- package/src/extensibility/extensions/runner.ts +895 -264
- package/src/extensibility/extensions/types.ts +42 -0
- package/src/extensibility/extensions/wrapper.ts +56 -15
- package/src/extensibility/gjc-plugins/compiler.ts +228 -60
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +321 -23
- package/src/extensibility/gjc-plugins/installer.ts +13 -12
- package/src/extensibility/gjc-plugins/mcp-policy.ts +81 -52
- package/src/extensibility/gjc-plugins/paths.ts +13 -0
- package/src/extensibility/gjc-plugins/registry.ts +113 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +758 -16
- package/src/extensibility/gjc-plugins/runtime-quarantine.ts +20 -2
- package/src/extensibility/gjc-plugins/schema.ts +121 -4
- package/src/extensibility/gjc-plugins/types.ts +25 -0
- package/src/extensibility/hooks/loader.ts +133 -16
- package/src/extensibility/hooks/types.ts +28 -0
- package/src/extensibility/runtime-skill-discovery.ts +149 -190
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/skill-management.ts +200 -52
- package/src/extensibility/skills.ts +73 -25
- package/src/gjc-runtime/empty-delete-gc.ts +94 -4
- package/src/gjc-runtime/gc-runtime.ts +29 -0
- package/src/gjc-runtime/linux-proc.ts +14 -0
- package/src/gjc-runtime/repository-binding.ts +1 -1
- package/src/gjc-runtime/session-state-lock.ts +143 -10
- package/src/gjc-runtime/session-state-sidecar.ts +27 -11
- package/src/gjc-runtime/state-writer.ts +30 -29
- package/src/gjc-runtime/ultragoal-runtime.ts +189 -47
- package/src/gjc-runtime/ultragoal-succession.ts +1667 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +52 -1
- package/src/gjc-runtime/workflow-manifest.ts +16 -0
- package/src/harness-control-plane/session-lease.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +12 -12
- package/src/lsp/config.ts +10 -3
- package/src/lsp/index.ts +1 -1
- package/src/lsp/path-trust.ts +69 -14
- package/src/main.ts +78 -35
- package/src/modes/DESIGN.md +54 -0
- package/src/modes/acp/acp-agent.ts +16 -3
- package/src/modes/components/assistant-message.ts +22 -9
- package/src/modes/components/bash-execution.ts +4 -1
- package/src/modes/components/execution-shared.ts +4 -1
- package/src/modes/components/model-selector.ts +166 -33
- package/src/modes/components/session-observer-overlay.ts +1 -1
- package/src/modes/components/settings-selector.ts +1075 -235
- package/src/modes/components/status-line/command.ts +190 -0
- package/src/modes/components/status-line/index.ts +1 -0
- package/src/modes/components/status-line/segments.ts +32 -5
- package/src/modes/components/status-line/types.ts +5 -0
- package/src/modes/components/tool-status-header.ts +396 -22
- package/src/modes/controllers/command-controller.ts +3 -0
- package/src/modes/controllers/event-controller.ts +151 -7
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +6 -3
- package/src/modes/controllers/selector-controller.ts +21 -0
- package/src/modes/interactive-mode.ts +53 -6
- package/src/modes/print-mode.ts +5 -1
- package/src/modes/prompt-action-autocomplete.ts +54 -27
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/types.ts +17 -3
- package/src/modes/utils/ui-helpers.ts +2 -0
- package/src/prompts/system/system-prompt.md +8 -0
- package/src/prompts/tools/search.md +2 -0
- package/src/prompts/tools/subagent.md +1 -0
- package/src/runtime-mcp/client.ts +60 -17
- package/src/runtime-mcp/manager.ts +189 -14
- package/src/runtime-mcp/pool.test.ts +218 -0
- package/src/runtime-mcp/pool.ts +224 -23
- package/src/runtime-mcp/transports/stdio.ts +158 -24
- package/src/runtime-mcp/types.ts +24 -0
- package/src/sdk/broker/broker.ts +124 -32
- package/src/sdk/broker/ensure.ts +248 -41
- package/src/sdk/broker/lifecycle-ledger.ts +7 -17
- package/src/sdk/broker/lifecycle.ts +56 -6
- package/src/sdk/broker/startup-failure.ts +15 -6
- package/src/sdk/bus/chat-daemon-control.ts +3 -1
- package/src/sdk/bus/chat-daemon-runtime.ts +72 -6
- package/src/sdk/bus/chat-effect-journal.ts +132 -7
- package/src/sdk/bus/conversation-store.ts +29 -0
- package/src/sdk/bus/lifecycle-commands.ts +17 -11
- package/src/sdk/bus/slack-conversation.ts +1 -0
- package/src/sdk/bus/slack-daemon.ts +478 -49
- package/src/sdk/bus/slack-live-provider.ts +12 -0
- package/src/sdk/bus/slack-provider.ts +5 -0
- package/src/sdk/bus/telegram-daemon.ts +16 -2
- package/src/sdk/bus/topic-registry.ts +19 -0
- package/src/sdk/cli/master-cli.ts +1 -1
- package/src/sdk/cli/session-cli.ts +11 -8
- package/src/sdk/client/client.ts +66 -17
- package/src/sdk/host/host.ts +29 -9
- package/src/sdk/host/query/revision-store.ts +19 -13
- package/src/sdk/host/reverse-leases.ts +7 -4
- package/src/sdk/host/session-runtime.test.ts +52 -10
- package/src/sdk/host/session-runtime.ts +383 -44
- package/src/sdk/lifecycle/service.ts +4 -0
- package/src/sdk/protocol/operation-inventory.generated.json +77 -0
- package/src/sdk/router/session-router.ts +89 -38
- package/src/sdk/session-api-key.ts +94 -0
- package/src/sdk/session.ts +228 -55
- package/src/sdk/transport/relay.ts +35 -13
- package/src/sdk/transport/serve-cli.ts +3 -3
- package/src/session/agent-session.ts +240 -90
- package/src/session/contribution-prep.ts +214 -21
- package/src/session/fallback-chain-controller.ts +38 -0
- package/src/session/internal/managed-session-storage.ts +19 -5
- package/src/session/messages.ts +5 -0
- package/src/session/session-manager.ts +29 -5
- package/src/session-import/redact.ts +5 -1
- package/src/setup/paseo/announce.ts +456 -0
- package/src/setup/paseo/setup-deps.ts +15 -3
- package/src/skill-state/canonical-skills.ts +12 -0
- package/src/skills/skills.test.ts +41 -21
- package/src/slash-commands/builtin-registry.ts +1 -0
- package/src/system-prompt.ts +29 -3
- package/src/task/executor.ts +7 -1
- package/src/task/index.ts +21 -4
- package/src/tools/bash.ts +63 -19
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +29 -1
- package/src/tools/gh.ts +26 -3
- package/src/tools/github-cache.ts +68 -16
- package/src/tools/index.ts +10 -0
- package/src/tools/job.ts +21 -4
- package/src/tools/monitor.ts +8 -2
- package/src/tools/path-utils.ts +7 -0
- package/src/tools/read.ts +14 -2
- package/src/tools/render-utils.ts +1 -3
- package/src/tools/resource-gc.ts +28 -8
- package/src/tools/search.ts +116 -58
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +37 -2
- package/src/tools/subagent.ts +6 -3
- package/src/tools/tool-catalog.generated.ts +10 -3
- package/src/tools/ultragoal-ask-guard.ts +2 -2
- package/src/web/insane/bridge.ts +113 -23
- package/src/web/search/providers/perplexity.ts +102 -41
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as crypto from "node:crypto";
|
|
2
2
|
import * as fsSync from "node:fs";
|
|
3
|
+
import type { FileHandle } from "node:fs/promises";
|
|
3
4
|
import * as fs from "node:fs/promises";
|
|
4
5
|
import * as path from "node:path";
|
|
5
6
|
import type { Api, Model } from "@gajae-code/ai/core";
|
|
6
7
|
import type { NativeExactFileIdentity, NativeExactUnlinkResult } from "@gajae-code/natives";
|
|
7
|
-
import { getAgentDir, isEnoent } from "@gajae-code/utils";
|
|
8
|
+
import { $credentialEnv, getAgentDir, isEnoent } from "@gajae-code/utils";
|
|
8
9
|
import * as z from "zod/v4";
|
|
9
10
|
import { splitSelectorThinkingSuffix } from "../thinking";
|
|
10
11
|
import { withFileLock } from "./file-lock";
|
|
@@ -939,16 +940,80 @@ function validateGeneration(
|
|
|
939
940
|
return generation;
|
|
940
941
|
}
|
|
941
942
|
|
|
942
|
-
|
|
943
|
+
type RegistryFileStat = {
|
|
944
|
+
dev: number | bigint;
|
|
945
|
+
ino: number | bigint;
|
|
946
|
+
size: number | bigint;
|
|
947
|
+
isFile(): boolean;
|
|
948
|
+
isSymbolicLink(): boolean;
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
function registryFileOpenFlags(): number {
|
|
952
|
+
return (
|
|
953
|
+
fsSync.constants.O_RDONLY |
|
|
954
|
+
(process.platform === "win32" ? 0 : (fsSync.constants.O_NOFOLLOW ?? 0) | (fsSync.constants.O_NONBLOCK ?? 0))
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function sameRegistryFileIdentity(left: RegistryFileStat, right: RegistryFileStat): boolean {
|
|
959
|
+
return (
|
|
960
|
+
left.isFile() &&
|
|
961
|
+
!left.isSymbolicLink() &&
|
|
962
|
+
right.isFile() &&
|
|
963
|
+
!right.isSymbolicLink() &&
|
|
964
|
+
left.dev === right.dev &&
|
|
965
|
+
left.ino === right.ino
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
function readBoundedRegistryFdSync(fd: number): Buffer {
|
|
970
|
+
const chunks: Buffer[] = [];
|
|
971
|
+
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
972
|
+
let total = 0;
|
|
973
|
+
for (;;) {
|
|
974
|
+
const length = Math.min(buffer.length, MODEL_PRESET_REGISTRY_MAX_STATE_BYTES + 1 - total);
|
|
975
|
+
const bytesRead = fsSync.readSync(fd, buffer, 0, length, null);
|
|
976
|
+
if (bytesRead === 0) return Buffer.concat(chunks, total);
|
|
977
|
+
total += bytesRead;
|
|
978
|
+
chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
|
|
979
|
+
if (total > MODEL_PRESET_REGISTRY_MAX_STATE_BYTES) throw new Error("Registry cache is oversized.");
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function readRegistryBytesSync(file: string): Buffer | undefined {
|
|
984
|
+
let before: fsSync.Stats;
|
|
943
985
|
try {
|
|
944
|
-
|
|
945
|
-
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error("Registry cache path is not a regular file.");
|
|
946
|
-
if (stat.size > MODEL_PRESET_REGISTRY_MAX_STATE_BYTES) throw new Error("Registry cache is oversized.");
|
|
947
|
-
return JSON.parse(fsSync.readFileSync(file, "utf8"));
|
|
986
|
+
before = fsSync.lstatSync(file);
|
|
948
987
|
} catch (error) {
|
|
949
988
|
if (isEnoent(error)) return undefined;
|
|
950
989
|
throw error;
|
|
951
990
|
}
|
|
991
|
+
if (!before.isFile() || before.isSymbolicLink()) throw new Error("Registry cache path is not a regular file.");
|
|
992
|
+
if (before.size > MODEL_PRESET_REGISTRY_MAX_STATE_BYTES) throw new Error("Registry cache is oversized.");
|
|
993
|
+
let fd: number | undefined;
|
|
994
|
+
try {
|
|
995
|
+
fd = fsSync.openSync(file, registryFileOpenFlags());
|
|
996
|
+
const opened = fsSync.fstatSync(fd);
|
|
997
|
+
if (!sameRegistryFileIdentity(before, opened)) throw new Error("Registry cache path changed while opening.");
|
|
998
|
+
const bytes = readBoundedRegistryFdSync(fd);
|
|
999
|
+
const finalOpened = fsSync.fstatSync(fd);
|
|
1000
|
+
let after: fsSync.Stats;
|
|
1001
|
+
try {
|
|
1002
|
+
after = fsSync.lstatSync(file);
|
|
1003
|
+
} catch (error) {
|
|
1004
|
+
throw new Error("Registry cache path changed while reading.", { cause: error });
|
|
1005
|
+
}
|
|
1006
|
+
if (!sameRegistryFileIdentity(opened, finalOpened) || !sameRegistryFileIdentity(opened, after))
|
|
1007
|
+
throw new Error("Registry cache path changed while reading.");
|
|
1008
|
+
return bytes;
|
|
1009
|
+
} finally {
|
|
1010
|
+
if (fd !== undefined) fsSync.closeSync(fd);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
function readJsonSync(file: string): unknown | undefined {
|
|
1015
|
+
const bytes = readRegistryBytesSync(file);
|
|
1016
|
+
return bytes === undefined ? undefined : JSON.parse(bytes.toString("utf8"));
|
|
952
1017
|
}
|
|
953
1018
|
|
|
954
1019
|
/**
|
|
@@ -956,16 +1021,44 @@ function readJsonSync(file: string): unknown | undefined {
|
|
|
956
1021
|
* Keep the synchronous reader above for legacy startup paths until their
|
|
957
1022
|
* constructor-wide migration can be completed without changing public APIs.
|
|
958
1023
|
*/
|
|
959
|
-
async function
|
|
1024
|
+
async function readRegistryBytesBun(file: string): Promise<Buffer | undefined> {
|
|
1025
|
+
let before: fsSync.BigIntStats;
|
|
960
1026
|
try {
|
|
961
|
-
|
|
962
|
-
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error("Registry cache path is not a regular file.");
|
|
963
|
-
if (stat.size > BigInt(MODEL_PRESET_REGISTRY_MAX_STATE_BYTES)) throw new Error("Registry cache is oversized.");
|
|
964
|
-
return JSON.parse(await Bun.file(file).text());
|
|
1027
|
+
before = await fs.lstat(file, { bigint: true });
|
|
965
1028
|
} catch (error) {
|
|
966
1029
|
if (isEnoent(error)) return undefined;
|
|
967
1030
|
throw error;
|
|
968
1031
|
}
|
|
1032
|
+
if (!before.isFile() || before.isSymbolicLink()) throw new Error("Registry cache path is not a regular file.");
|
|
1033
|
+
if (before.size > BigInt(MODEL_PRESET_REGISTRY_MAX_STATE_BYTES)) throw new Error("Registry cache is oversized.");
|
|
1034
|
+
let handle: FileHandle | undefined;
|
|
1035
|
+
try {
|
|
1036
|
+
handle = await fs.open(file, registryFileOpenFlags());
|
|
1037
|
+
const opened = await handle.stat({ bigint: true });
|
|
1038
|
+
if (!sameRegistryFileIdentity(before, opened)) throw new Error("Registry cache path changed while opening.");
|
|
1039
|
+
const bunFileFromDescriptor = (Bun.file as unknown as (descriptor: number) => Bun.BunFile)(handle.fd);
|
|
1040
|
+
const bytes = Buffer.from(
|
|
1041
|
+
await bunFileFromDescriptor.slice(0, MODEL_PRESET_REGISTRY_MAX_STATE_BYTES + 1).arrayBuffer(),
|
|
1042
|
+
);
|
|
1043
|
+
if (bytes.length > MODEL_PRESET_REGISTRY_MAX_STATE_BYTES) throw new Error("Registry cache is oversized.");
|
|
1044
|
+
const finalOpened = await handle.stat({ bigint: true });
|
|
1045
|
+
let after: fsSync.BigIntStats;
|
|
1046
|
+
try {
|
|
1047
|
+
after = await fs.lstat(file, { bigint: true });
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
throw new Error("Registry cache path changed while reading.", { cause: error });
|
|
1050
|
+
}
|
|
1051
|
+
if (!sameRegistryFileIdentity(opened, finalOpened) || !sameRegistryFileIdentity(opened, after))
|
|
1052
|
+
throw new Error("Registry cache path changed while reading.");
|
|
1053
|
+
return bytes;
|
|
1054
|
+
} finally {
|
|
1055
|
+
await handle?.close();
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
async function readJsonBun(file: string): Promise<unknown | undefined> {
|
|
1060
|
+
const bytes = await readRegistryBytesBun(file);
|
|
1061
|
+
return bytes === undefined ? undefined : JSON.parse(bytes.toString("utf8"));
|
|
969
1062
|
}
|
|
970
1063
|
|
|
971
1064
|
function parseState(value: unknown): RegistryState {
|
|
@@ -1444,10 +1537,12 @@ function effectiveTrustedKeys(
|
|
|
1444
1537
|
return getModelPresetRegistryTestTrustedKeys(agentDir) ?? MODEL_PRESET_REGISTRY_TRUSTED_KEYS;
|
|
1445
1538
|
}
|
|
1446
1539
|
function effectiveManifestUrl(dependencies: ModelPresetRegistryDependencies): string {
|
|
1447
|
-
return
|
|
1540
|
+
return (
|
|
1541
|
+
dependencies.manifestUrl ?? $credentialEnv("GJC_MODEL_PRESET_REGISTRY_URL") ?? DEFAULT_MODEL_PRESET_REGISTRY_URL
|
|
1542
|
+
);
|
|
1448
1543
|
}
|
|
1449
1544
|
function environmentDisabled(): boolean {
|
|
1450
|
-
return /^(?:1|true|yes|on)$/i.test(
|
|
1545
|
+
return /^(?:1|true|yes|on)$/i.test($credentialEnv("GJC_MODEL_PRESET_REGISTRY_DISABLED") ?? "");
|
|
1451
1546
|
}
|
|
1452
1547
|
function assertHttpsUrl(raw: string, description: string): URL {
|
|
1453
1548
|
let url: URL;
|
|
@@ -1574,9 +1669,10 @@ function cloneAcceptedModelPresetRegistry(value: AcceptedModelPresetRegistry): A
|
|
|
1574
1669
|
|
|
1575
1670
|
function registryFileFingerprint(file: string): string {
|
|
1576
1671
|
try {
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1672
|
+
const bytes = readRegistryBytesSync(file);
|
|
1673
|
+
return bytes === undefined ? "absent" : sha256(bytes);
|
|
1674
|
+
} catch (error) {
|
|
1675
|
+
return isEnoent(error) ? "absent" : "rejected";
|
|
1580
1676
|
}
|
|
1581
1677
|
}
|
|
1582
1678
|
|
|
@@ -1593,9 +1689,10 @@ function acceptedRegistryFingerprint(
|
|
|
1593
1689
|
|
|
1594
1690
|
async function registryFileFingerprintAsync(file: string): Promise<string> {
|
|
1595
1691
|
try {
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1692
|
+
const bytes = await readRegistryBytesBun(file);
|
|
1693
|
+
return bytes === undefined ? "absent" : sha256(bytes);
|
|
1694
|
+
} catch (error) {
|
|
1695
|
+
return isEnoent(error) ? "absent" : "rejected";
|
|
1599
1696
|
}
|
|
1600
1697
|
}
|
|
1601
1698
|
|
|
@@ -1622,6 +1719,8 @@ export function loadAcceptedModelPresetRegistry(
|
|
|
1622
1719
|
if (!acceptedRegistryReadIsCacheable(dependencies) || environmentDisabled())
|
|
1623
1720
|
return loadAcceptedModelPresetRegistryUncached(agentDir, dependencies);
|
|
1624
1721
|
const fingerprint = acceptedRegistryFingerprint(agentDir, dependencies);
|
|
1722
|
+
if (fingerprint.split("|").includes("rejected"))
|
|
1723
|
+
return loadAcceptedModelPresetRegistryUncached(agentDir, dependencies);
|
|
1625
1724
|
const cached = acceptedRegistryReadCache.get(agentDir);
|
|
1626
1725
|
if (cached && cached.fingerprint === fingerprint) return cloneAcceptedModelPresetRegistry(cached.value);
|
|
1627
1726
|
const value = loadAcceptedModelPresetRegistryUncached(agentDir, dependencies);
|
|
@@ -1698,6 +1797,8 @@ export async function loadAcceptedModelPresetRegistryAsync(
|
|
|
1698
1797
|
if (!acceptedRegistryReadIsCacheable(dependencies) || environmentDisabled())
|
|
1699
1798
|
return loadAcceptedModelPresetRegistryAsyncUncached(agentDir, dependencies);
|
|
1700
1799
|
const fingerprint = await acceptedRegistryFingerprintAsync(agentDir, dependencies);
|
|
1800
|
+
if (fingerprint.split("|").includes("rejected"))
|
|
1801
|
+
return loadAcceptedModelPresetRegistryAsyncUncached(agentDir, dependencies);
|
|
1701
1802
|
const cached = acceptedRegistryReadCache.get(agentDir);
|
|
1702
1803
|
if (cached && cached.fingerprint === fingerprint) return cloneAcceptedModelPresetRegistry(cached.value);
|
|
1703
1804
|
const value = await loadAcceptedModelPresetRegistryAsyncUncached(agentDir, dependencies);
|
|
@@ -100,6 +100,27 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
100
100
|
critic: "openai-codex/gpt-5.6-luna:max",
|
|
101
101
|
architect: "openai-codex/gpt-5.6-luna:max",
|
|
102
102
|
}),
|
|
103
|
+
profile("astra-lite", ["openai-codex"], {
|
|
104
|
+
default: "openai-codex/gpt-6-astra:medium",
|
|
105
|
+
executor: "openai-codex/gpt-5.6-luna:xhigh",
|
|
106
|
+
planner: "openai-codex/gpt-6-astra:xhigh",
|
|
107
|
+
critic: "openai-codex/gpt-5.6-sol:high",
|
|
108
|
+
architect: "openai-codex/gpt-5.6-terra:xhigh",
|
|
109
|
+
}),
|
|
110
|
+
profile("astra-default", ["openai-codex"], {
|
|
111
|
+
default: "openai-codex/gpt-6-astra:medium",
|
|
112
|
+
executor: "openai-codex/gpt-5.6-luna:max",
|
|
113
|
+
planner: "openai-codex/gpt-6-astra:xhigh",
|
|
114
|
+
critic: "openai-codex/gpt-5.6-sol:xhigh",
|
|
115
|
+
architect: "openai-codex/gpt-5.6-terra:xhigh",
|
|
116
|
+
}),
|
|
117
|
+
profile("astra-heavy", ["openai-codex"], {
|
|
118
|
+
default: "openai-codex/gpt-6-astra:medium",
|
|
119
|
+
executor: "openai-codex/gpt-5.6-sol:max",
|
|
120
|
+
planner: "openai-codex/gpt-5.6-sol:max",
|
|
121
|
+
critic: "openai-codex/gpt-6-astra:max",
|
|
122
|
+
architect: "openai-codex/gpt-6-astra:max",
|
|
123
|
+
}),
|
|
103
124
|
profile("macos-omlx-fast", ["omlx"], {
|
|
104
125
|
default: "omlx/Qwen3.6-35B-A3B-4bit:low",
|
|
105
126
|
executor: "omlx/Qwen3.6-35B-A3B-4bit:low",
|
|
@@ -488,6 +509,20 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
488
509
|
critic: "anthropic/claude-opus-5:high",
|
|
489
510
|
architect: "openai-codex/gpt-5.6-sol:xhigh",
|
|
490
511
|
}),
|
|
512
|
+
profile("astra-fable", ["openai-codex", "anthropic"], {
|
|
513
|
+
default: "openai-codex/gpt-6-astra:medium",
|
|
514
|
+
executor: "openai-codex/gpt-5.6-luna:max",
|
|
515
|
+
planner: "openai-codex/gpt-6-astra:xhigh",
|
|
516
|
+
critic: "anthropic/claude-fable-5-1:xhigh",
|
|
517
|
+
architect: "anthropic/claude-fable-5-1:xhigh",
|
|
518
|
+
}),
|
|
519
|
+
profile("astra-fable-opus", ["openai-codex", "anthropic"], {
|
|
520
|
+
default: "openai-codex/gpt-6-astra:medium",
|
|
521
|
+
executor: "openai-codex/gpt-5.6-luna:max",
|
|
522
|
+
planner: "anthropic/claude-opus-5:medium",
|
|
523
|
+
critic: "anthropic/claude-fable-5-1:xhigh",
|
|
524
|
+
architect: "anthropic/claude-fable-5-1:xhigh",
|
|
525
|
+
}),
|
|
491
526
|
];
|
|
492
527
|
|
|
493
528
|
/**
|
|
@@ -521,6 +556,9 @@ const PROFILE_PRESENTATION: Record<string, ModelProfilePresentation> = {
|
|
|
521
556
|
"codex-medium": { displayName: "Codex Medium", providerGroup: "CODEX" },
|
|
522
557
|
"codex-pro": { displayName: "Codex Pro", providerGroup: "CODEX" },
|
|
523
558
|
lunamaxxing: { displayName: "LunaMaxxing", providerGroup: "CODEX" },
|
|
559
|
+
"astra-lite": { displayName: "ASTRA-Lite", providerGroup: "CODEX" },
|
|
560
|
+
"astra-default": { displayName: "ASTRA-Default", providerGroup: "CODEX" },
|
|
561
|
+
"astra-heavy": { displayName: "ASTRA-Heavy", providerGroup: "CODEX" },
|
|
524
562
|
opencodego: { displayName: "OpenCodeGo", providerGroup: "OPENCODEGO" },
|
|
525
563
|
"commandcode-goat": { displayName: "Command Code GOAT", providerGroup: "COMMAND CODE GOAT" },
|
|
526
564
|
"open-weights-glm": { displayName: "GLM", providerGroup: "OPEN WEIGHT MODELS (PROVIDER AGNOSTIC)" },
|
|
@@ -594,6 +632,8 @@ const PROFILE_PRESENTATION: Record<string, ModelProfilePresentation> = {
|
|
|
594
632
|
"opus-codex": { displayName: "Opus + Codex", providerGroup: "COMBOS" },
|
|
595
633
|
"codex-opencodego": { displayName: "Codex + OpenCodeGo", providerGroup: "COMBOS" },
|
|
596
634
|
"fable-opus-codex": { displayName: "Fable + Opus + Codex", providerGroup: "COMBOS" },
|
|
635
|
+
"astra-fable": { displayName: "ASTRA + Fable (5.1)", providerGroup: "COMBOS" },
|
|
636
|
+
"astra-fable-opus": { displayName: "ASTRA + Fable (5.1) + Opus", providerGroup: "COMBOS" },
|
|
597
637
|
"macos-omlx-fast": { displayName: "4-bit Fast (MoE measured 93.5 tok/s)", providerGroup: "macOS Local (oMLX)" },
|
|
598
638
|
"macos-omlx-balanced": {
|
|
599
639
|
displayName: "8-bit Balanced (MoE measured 71.1 tok/s)",
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
unregisterCustomApis,
|
|
41
41
|
} from "@gajae-code/ai/core";
|
|
42
42
|
import { fetchModelsDevPayload } from "@gajae-code/ai/provider-models/openai-compat";
|
|
43
|
+
import { isDirectXaiReasoningEffortModel } from "@gajae-code/ai/providers/openai-completions-compat";
|
|
43
44
|
import {
|
|
44
45
|
detectDiscoveredApiFamily,
|
|
45
46
|
resolveLoopbackOpenAIBaseUrl,
|
|
@@ -279,11 +280,7 @@ export const GJC_MODEL_ASSIGNMENT_TARGETS: Record<GjcModelAssignmentTargetId, Gj
|
|
|
279
280
|
|
|
280
281
|
export function requiresExplicitThinkingChoice(model: Model, role: GjcModelAssignmentTargetId | null): boolean {
|
|
281
282
|
if (!modelSupportsReasoningControl(model)) return false;
|
|
282
|
-
if (
|
|
283
|
-
model.provider === "openai" ||
|
|
284
|
-
model.provider === "openai-codex" ||
|
|
285
|
-
(model.provider === "xai" && (model.id === "grok-4.5" || model.id === "grok-4.6"))
|
|
286
|
-
)
|
|
283
|
+
if (model.provider === "openai" || model.provider === "openai-codex" || isDirectXaiReasoningEffortModel(model))
|
|
287
284
|
return true;
|
|
288
285
|
if (role === null) return false;
|
|
289
286
|
if (role === "default") return true;
|
|
@@ -1835,6 +1832,25 @@ export class ModelRegistry {
|
|
|
1835
1832
|
/**
|
|
1836
1833
|
* Reload models from disk (embedded + accepted registry + custom from models.yml).
|
|
1837
1834
|
*/
|
|
1835
|
+
async refreshStatic(): Promise<void> {
|
|
1836
|
+
if (this.#disposed) return;
|
|
1837
|
+
await this.#enqueueCatalogMutation(async () => {
|
|
1838
|
+
if (this.#disposed) return;
|
|
1839
|
+
this.#catalogRefreshGeneration++;
|
|
1840
|
+
this.#suspendRebuild();
|
|
1841
|
+
try {
|
|
1842
|
+
this.#reloadStaticModels();
|
|
1843
|
+
this.#suppressedSelectors.clear();
|
|
1844
|
+
this.#modelBindingsApplier.apply();
|
|
1845
|
+
} finally {
|
|
1846
|
+
this.#resumeRebuild();
|
|
1847
|
+
}
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
/**
|
|
1852
|
+
* Reload models from disk and refresh runtime provider discovery state.
|
|
1853
|
+
*/
|
|
1838
1854
|
async refresh(strategy: ModelRefreshStrategy = "online-if-uncached"): Promise<void> {
|
|
1839
1855
|
if (this.#disposed) return;
|
|
1840
1856
|
await this.#enqueueCatalogMutation(async () => {
|
|
@@ -1882,7 +1898,8 @@ export class ModelRegistry {
|
|
|
1882
1898
|
const refreshGeneration = this.#catalogRefreshGeneration;
|
|
1883
1899
|
this.#suspendRebuild();
|
|
1884
1900
|
try {
|
|
1885
|
-
this
|
|
1901
|
+
// A provider refresh only updates discovery for this provider.
|
|
1902
|
+
// Static catalog changes are serialized through refresh().
|
|
1886
1903
|
for (const selector of this.#suppressedSelectors.keys()) {
|
|
1887
1904
|
if (selector.startsWith(`${providerId}/`)) {
|
|
1888
1905
|
this.#suppressedSelectors.delete(selector);
|
|
@@ -3026,6 +3043,7 @@ export class ModelRegistry {
|
|
|
3026
3043
|
refreshGeneration = this.#catalogRefreshGeneration,
|
|
3027
3044
|
providerRefresh?: ProviderRefreshFence,
|
|
3028
3045
|
): Promise<void> {
|
|
3046
|
+
const profileAvailabilityEvidenceBefore = this.#profileAvailabilityEvidenceFingerprint();
|
|
3029
3047
|
const disabledProviders = getDisabledProviderIdsFromSettings(this.#settings);
|
|
3030
3048
|
const selectedDiscoverableProviders = (
|
|
3031
3049
|
providerFilter
|
|
@@ -3232,7 +3250,10 @@ export class ModelRegistry {
|
|
|
3232
3250
|
});
|
|
3233
3251
|
}
|
|
3234
3252
|
if (discovered.length === 0) {
|
|
3235
|
-
if (
|
|
3253
|
+
if (
|
|
3254
|
+
clearPublishedModelIds.size > 0 ||
|
|
3255
|
+
profileAvailabilityEvidenceBefore !== this.#profileAvailabilityEvidenceFingerprint()
|
|
3256
|
+
) {
|
|
3236
3257
|
this.#rebuildCanonicalIndex();
|
|
3237
3258
|
}
|
|
3238
3259
|
return;
|
|
@@ -3240,6 +3261,23 @@ export class ModelRegistry {
|
|
|
3240
3261
|
this.#mergeDiscoveredModels(discovered);
|
|
3241
3262
|
}
|
|
3242
3263
|
|
|
3264
|
+
#profileAvailabilityEvidenceFingerprint(): string {
|
|
3265
|
+
return JSON.stringify(
|
|
3266
|
+
[...this.#descriptorDiscoveryEvidence.entries()]
|
|
3267
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
3268
|
+
.map(([provider, evidence]) => [
|
|
3269
|
+
provider,
|
|
3270
|
+
evidence.fresh,
|
|
3271
|
+
evidence.profileFresh,
|
|
3272
|
+
evidence.authGeneration,
|
|
3273
|
+
evidence.endpoint,
|
|
3274
|
+
evidence.profileEndpoint,
|
|
3275
|
+
[...evidence.modelIds].sort(),
|
|
3276
|
+
evidence.profileModelIds === undefined ? undefined : [...evidence.profileModelIds].sort(),
|
|
3277
|
+
]),
|
|
3278
|
+
);
|
|
3279
|
+
}
|
|
3280
|
+
|
|
3243
3281
|
#mergeDiscoveredModels(discovered: readonly Model<Api>[]): void {
|
|
3244
3282
|
const discoveredModels = this.#applyHardcodedModelPolicies(
|
|
3245
3283
|
discovered.map(model =>
|
|
@@ -3537,8 +3575,9 @@ export class ModelRegistry {
|
|
|
3537
3575
|
// through discovery.type.
|
|
3538
3576
|
if (this.#configuredProviderIds.has("vllm")) configuredDiscoveryProviders.add("vllm");
|
|
3539
3577
|
if (this.#configuredProviderIds.has("sglang")) configuredDiscoveryProviders.add("sglang");
|
|
3540
|
-
const managerOptions =
|
|
3541
|
-
|
|
3578
|
+
const managerOptions = await this.#collectBuiltInModelManagerOptions(
|
|
3579
|
+
configuredDiscoveryProviders,
|
|
3580
|
+
providerFilter,
|
|
3542
3581
|
);
|
|
3543
3582
|
if (managerOptions.length === 0) {
|
|
3544
3583
|
return [];
|
|
@@ -3551,6 +3590,7 @@ export class ModelRegistry {
|
|
|
3551
3590
|
|
|
3552
3591
|
async #collectBuiltInModelManagerOptions(
|
|
3553
3592
|
excludedProviderIds: ReadonlySet<string> = new Set(),
|
|
3593
|
+
providerFilter?: ReadonlySet<string>,
|
|
3554
3594
|
): Promise<ModelManagerDiscoveryOptions[]> {
|
|
3555
3595
|
const specialProviderDescriptors: Array<{
|
|
3556
3596
|
providerId: string;
|
|
@@ -3594,10 +3634,16 @@ export class ModelRegistry {
|
|
|
3594
3634
|
];
|
|
3595
3635
|
const disabledProviders = getDisabledProviderIdsFromSettings(this.#settings);
|
|
3596
3636
|
const standardProviderDescriptors = PROVIDER_DESCRIPTORS.filter(
|
|
3597
|
-
descriptor =>
|
|
3637
|
+
descriptor =>
|
|
3638
|
+
!disabledProviders.has(descriptor.providerId) &&
|
|
3639
|
+
!excludedProviderIds.has(descriptor.providerId) &&
|
|
3640
|
+
(providerFilter === undefined || providerFilter.has(descriptor.providerId)),
|
|
3598
3641
|
);
|
|
3599
3642
|
const enabledSpecialProviderDescriptors = specialProviderDescriptors.filter(
|
|
3600
|
-
descriptor =>
|
|
3643
|
+
descriptor =>
|
|
3644
|
+
!disabledProviders.has(descriptor.providerId) &&
|
|
3645
|
+
!excludedProviderIds.has(descriptor.providerId) &&
|
|
3646
|
+
(providerFilter === undefined || providerFilter.has(descriptor.providerId)),
|
|
3601
3647
|
);
|
|
3602
3648
|
for (const descriptor of standardProviderDescriptors) {
|
|
3603
3649
|
if (!descriptor.allowUnauthenticated) continue;
|
|
@@ -4514,10 +4560,10 @@ export class ModelRegistry {
|
|
|
4514
4560
|
.get(model.provider.toLowerCase())
|
|
4515
4561
|
?.get(model.id.toLowerCase())?.thinking;
|
|
4516
4562
|
if (overrideThinking !== undefined) return { ...model, thinking: overrideThinking as ThinkingConfig };
|
|
4517
|
-
const
|
|
4518
|
-
candidate
|
|
4519
|
-
|
|
4520
|
-
|
|
4563
|
+
const hasDeclaredThinking = (candidate: CustomModelOverlay) =>
|
|
4564
|
+
candidate.provider === model.provider && candidate.id === model.id && candidate.thinking !== undefined;
|
|
4565
|
+
const overlay =
|
|
4566
|
+
this.#runtimeModelOverlays.find(hasDeclaredThinking) ?? this.#customModelOverlays.find(hasDeclaredThinking);
|
|
4521
4567
|
return overlay?.thinking === undefined ? model : { ...model, thinking: overlay.thinking };
|
|
4522
4568
|
}
|
|
4523
4569
|
/**
|
|
@@ -5169,15 +5215,20 @@ export class ModelRegistry {
|
|
|
5169
5215
|
* usable until live catalog evidence exists so offline startup is unchanged.
|
|
5170
5216
|
*/
|
|
5171
5217
|
getAvailableForProfileActivation(): Model<Api>[] {
|
|
5218
|
+
const bundledIdsByProvider = new Map<string, Set<string>>();
|
|
5172
5219
|
return this.getAvailable().filter(model => {
|
|
5173
5220
|
const evidence = this.#descriptorDiscoveryEvidence.get(model.provider);
|
|
5174
5221
|
if (!evidence?.profileFresh || evidence.profileModelIds === undefined) return true;
|
|
5175
5222
|
if (this.#hasCustomModelOverlay(model.provider, model.id)) return true;
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5223
|
+
let bundledModelIds = bundledIdsByProvider.get(model.provider);
|
|
5224
|
+
if (!bundledModelIds) {
|
|
5225
|
+
bundledModelIds = new Set(
|
|
5226
|
+
(getBundledModels(model.provider as Parameters<typeof getBundledModels>[0]) as Model<Api>[]).map(
|
|
5227
|
+
candidate => candidate.id,
|
|
5228
|
+
),
|
|
5229
|
+
);
|
|
5230
|
+
bundledIdsByProvider.set(model.provider, bundledModelIds);
|
|
5231
|
+
}
|
|
5181
5232
|
if (!bundledModelIds.has(model.id)) return true;
|
|
5182
5233
|
if (
|
|
5183
5234
|
evidence.authGeneration !== this.#getProviderEvidenceGeneration(model.provider) ||
|
|
@@ -5190,9 +5241,8 @@ export class ModelRegistry {
|
|
|
5190
5241
|
}
|
|
5191
5242
|
|
|
5192
5243
|
#hasCustomModelOverlay(provider: string, id: string): boolean {
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
);
|
|
5244
|
+
const matches = (overlay: CustomModelOverlay) => overlay.provider === provider && overlay.id === id;
|
|
5245
|
+
return this.#customModelOverlays.some(matches) || this.#runtimeModelOverlays.some(matches);
|
|
5196
5246
|
}
|
|
5197
5247
|
|
|
5198
5248
|
#hasFreshOrStaticModelEvidence(model: Model<Api>): boolean {
|
|
@@ -62,6 +62,54 @@ export function parseModelString(
|
|
|
62
62
|
: { provider, id: modelStr.slice(slashIdx + 1) };
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
export function resolveStartupModelRefreshSelectors(
|
|
66
|
+
options: {
|
|
67
|
+
model?: string;
|
|
68
|
+
provider?: string;
|
|
69
|
+
credential?: string;
|
|
70
|
+
continue?: boolean;
|
|
71
|
+
resume?: string | boolean;
|
|
72
|
+
hasStartupProfile: boolean;
|
|
73
|
+
},
|
|
74
|
+
settings: ModelRoleSettings,
|
|
75
|
+
): ModelSelectorValue | undefined {
|
|
76
|
+
if (options.credential) return undefined;
|
|
77
|
+
if (options.model) {
|
|
78
|
+
return options.provider && !options.model.toLowerCase().startsWith(`${options.provider.toLowerCase()}/`)
|
|
79
|
+
? `${options.provider}/${options.model}`
|
|
80
|
+
: options.model;
|
|
81
|
+
}
|
|
82
|
+
if (options.hasStartupProfile) return undefined;
|
|
83
|
+
if (options.continue || options.resume) return undefined;
|
|
84
|
+
return resolveConfiguredModelPatterns(settings.getModelRole("default"), settings);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function refreshMissingQualifiedModelProviders(
|
|
88
|
+
selectors: ModelSelectorValue | undefined,
|
|
89
|
+
modelRegistry: Pick<ModelRegistry, "getAvailable" | "getDiscoverableProviders" | "refreshProvider">,
|
|
90
|
+
): Promise<boolean> {
|
|
91
|
+
const refreshedProviders = new Set<string>();
|
|
92
|
+
for (const selector of normalizeModelSelectorValue(selectors)) {
|
|
93
|
+
const parsedSelector = parseModelString(selector);
|
|
94
|
+
if (!parsedSelector) continue;
|
|
95
|
+
const provider = modelRegistry
|
|
96
|
+
.getDiscoverableProviders()
|
|
97
|
+
.find(candidate => candidate.toLowerCase() === parsedSelector.provider.toLowerCase());
|
|
98
|
+
if (!provider || refreshedProviders.has(provider)) continue;
|
|
99
|
+
const availableModels = modelRegistry.getAvailable();
|
|
100
|
+
const rawModelId = selector.slice(selector.indexOf("/") + 1);
|
|
101
|
+
if (
|
|
102
|
+
resolveProviderModelReference(provider, rawModelId, availableModels) ||
|
|
103
|
+
resolveProviderModelReference(provider, parsedSelector.id, availableModels)
|
|
104
|
+
)
|
|
105
|
+
continue;
|
|
106
|
+
|
|
107
|
+
await modelRegistry.refreshProvider(provider, "online-if-uncached");
|
|
108
|
+
refreshedProviders.add(provider);
|
|
109
|
+
}
|
|
110
|
+
return refreshedProviders.size > 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
65
113
|
/**
|
|
66
114
|
* Format a model as "provider/modelId" string.
|
|
67
115
|
*/
|
|
@@ -111,7 +111,8 @@ export type StatusLineSegmentId =
|
|
|
111
111
|
| "cache_read"
|
|
112
112
|
| "cache_write"
|
|
113
113
|
| "session_name"
|
|
114
|
-
| "usage"
|
|
114
|
+
| "usage"
|
|
115
|
+
| "command";
|
|
115
116
|
|
|
116
117
|
/** Submenu choice metadata. */
|
|
117
118
|
export type SubmenuOption<V extends string = string> = {
|
|
@@ -1242,6 +1243,26 @@ export const SETTINGS_SCHEMA = {
|
|
|
1242
1243
|
},
|
|
1243
1244
|
},
|
|
1244
1245
|
|
|
1246
|
+
reasoningLanguage: {
|
|
1247
|
+
type: "enum",
|
|
1248
|
+
values: ["off", "english"] as const,
|
|
1249
|
+
default: "off",
|
|
1250
|
+
ui: {
|
|
1251
|
+
tab: "model",
|
|
1252
|
+
label: "Reasoning Language",
|
|
1253
|
+
description:
|
|
1254
|
+
"Optionally reason through development work in English while preserving the user's response language",
|
|
1255
|
+
options: [
|
|
1256
|
+
{ value: "off", label: "Off", description: "Do not add reasoning-language guidance" },
|
|
1257
|
+
{
|
|
1258
|
+
value: "english",
|
|
1259
|
+
label: "English",
|
|
1260
|
+
description: "Reason through technical work in English and answer in the user's language",
|
|
1261
|
+
},
|
|
1262
|
+
],
|
|
1263
|
+
},
|
|
1264
|
+
},
|
|
1265
|
+
|
|
1245
1266
|
hideThinkingBlock: {
|
|
1246
1267
|
type: "boolean",
|
|
1247
1268
|
default: false,
|
|
@@ -1749,6 +1770,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
1749
1770
|
},
|
|
1750
1771
|
},
|
|
1751
1772
|
|
|
1773
|
+
"paseo.autoImport": {
|
|
1774
|
+
type: "boolean",
|
|
1775
|
+
default: false,
|
|
1776
|
+
ui: {
|
|
1777
|
+
tab: "interaction",
|
|
1778
|
+
label: "Announce Sessions to Paseo",
|
|
1779
|
+
description:
|
|
1780
|
+
"Global-only operator opt-in. When enabled, and only when a GJC ACP provider is already registered in Paseo and its daemon is already running, import each interactive session into Paseo so it can be driven from there. Project settings cannot enable it. Never starts the Paseo daemon or removes imported entries; stale entries must be pruned in Paseo. Never runs when Paseo is absent.",
|
|
1781
|
+
},
|
|
1782
|
+
},
|
|
1783
|
+
|
|
1752
1784
|
"crashReport.nudge": {
|
|
1753
1785
|
type: "boolean",
|
|
1754
1786
|
default: true,
|
package/src/config/settings.ts
CHANGED
|
@@ -333,6 +333,7 @@ const PATH_SCOPED_ARRAY_SETTINGS = new Set<SettingPath>(["enabledModels", "disab
|
|
|
333
333
|
const GLOBAL_ONLY_SETTINGS = new Set<SettingPath>([
|
|
334
334
|
"crashReport.upstream",
|
|
335
335
|
"crashReport.upstreamDsn",
|
|
336
|
+
"paseo.autoImport",
|
|
336
337
|
"ui.language",
|
|
337
338
|
"telemetry.enabled",
|
|
338
339
|
]);
|
|
@@ -163,6 +163,8 @@ export interface PromptRequestV1 {
|
|
|
163
163
|
phase: "claimed" | "remote_started" | "accepted" | "linked" | "terminal" | "completed" | "uncertain";
|
|
164
164
|
runtime_receipt?: { accepted: true; command_id: string; turn_id: string };
|
|
165
165
|
coordinator_turn_id?: string;
|
|
166
|
+
/** Retain prompt and source turn until the delegate's outer response is durable. */
|
|
167
|
+
delegate_response_pending?: boolean;
|
|
166
168
|
safe_response?: Record<string, unknown>;
|
|
167
169
|
error_code?: PublicReason | "idempotency_conflict";
|
|
168
170
|
created_at: string;
|
|
@@ -244,7 +246,12 @@ export interface CoordinatorSessionTransactionV1 {
|
|
|
244
246
|
scheduler_applied_revision?: number;
|
|
245
247
|
scheduler_digest?: string;
|
|
246
248
|
};
|
|
247
|
-
recovery: {
|
|
249
|
+
recovery: {
|
|
250
|
+
prompt_watermark_at: string | null;
|
|
251
|
+
last_repaired_at: string | null;
|
|
252
|
+
/** One-shot authority for the initial delegate prompt, consumed atomically with its claim. */
|
|
253
|
+
initial_delegate_request?: { key_digest: string; request_digest: string };
|
|
254
|
+
};
|
|
248
255
|
}
|
|
249
256
|
interface CoordinatorSessionHistoryV1 {
|
|
250
257
|
schema_version: 1;
|
|
@@ -815,6 +822,7 @@ function assertTransaction(transaction: CoordinatorSessionTransactionV1, namespa
|
|
|
815
822
|
typeof request.canonical_prompt.text !== "string" ||
|
|
816
823
|
!safeId(request.sdk_idempotency_key) ||
|
|
817
824
|
!promptPhases.has(String(request.phase)) ||
|
|
825
|
+
(request.delegate_response_pending !== undefined && typeof request.delegate_response_pending !== "boolean") ||
|
|
818
826
|
!isTime(request.created_at) ||
|
|
819
827
|
!isTime(request.updated_at) ||
|
|
820
828
|
(request.coordinator_turn_id !== undefined &&
|
|
@@ -1194,6 +1202,12 @@ function assertTransaction(transaction: CoordinatorSessionTransactionV1, namespa
|
|
|
1194
1202
|
(typeof projection.scheduler_digest !== "string" || projection.scheduler_digest.length === 0)) ||
|
|
1195
1203
|
(recovery.prompt_watermark_at !== null && !isTime(recovery.prompt_watermark_at)) ||
|
|
1196
1204
|
(recovery.last_repaired_at !== null && !isTime(recovery.last_repaired_at)) ||
|
|
1205
|
+
(recovery.initial_delegate_request !== undefined &&
|
|
1206
|
+
(!isRecord(recovery.initial_delegate_request) ||
|
|
1207
|
+
typeof recovery.initial_delegate_request.key_digest !== "string" ||
|
|
1208
|
+
!/^[a-f0-9]{64}$/.test(recovery.initial_delegate_request.key_digest) ||
|
|
1209
|
+
typeof recovery.initial_delegate_request.request_digest !== "string" ||
|
|
1210
|
+
!/^[a-f0-9]{64}$/.test(recovery.initial_delegate_request.request_digest))) ||
|
|
1197
1211
|
(queue.selected_promotion !== null &&
|
|
1198
1212
|
(!isRecord(queue.selected_promotion) ||
|
|
1199
1213
|
!safeId(queue.selected_promotion.from_turn_id) ||
|
|
@@ -1837,7 +1851,15 @@ export async function commitCreationWal(
|
|
|
1837
1851
|
scheduler_applied_revision: 0,
|
|
1838
1852
|
scheduler_digest: digest(JSON.stringify({ session_id: session.session_id, revision: 1 })),
|
|
1839
1853
|
},
|
|
1840
|
-
recovery: {
|
|
1854
|
+
recovery: {
|
|
1855
|
+
prompt_watermark_at: null,
|
|
1856
|
+
last_repaired_at: null,
|
|
1857
|
+
// Only a newly created WAL may grant pre-dispatch authority. An
|
|
1858
|
+
// existing WAL above must never replenish a consumed/compacted claim.
|
|
1859
|
+
...(intent.kind === "delegate"
|
|
1860
|
+
? { initial_delegate_request: { key_digest: keyDigest, request_digest: request.request_digest } }
|
|
1861
|
+
: {}),
|
|
1862
|
+
},
|
|
1841
1863
|
};
|
|
1842
1864
|
assertTransaction(transaction, session.namespace_id, session.session_id);
|
|
1843
1865
|
await writeAtomic(transactionPath(paths, session.session_id), transaction);
|
|
@@ -2320,6 +2342,13 @@ export async function enumeratePublicDeliveries(
|
|
|
2320
2342
|
export function compactTransaction(transaction: CoordinatorSessionTransactionV1, now = Date.now()): void {
|
|
2321
2343
|
normalizeOutbox(transaction);
|
|
2322
2344
|
const old = (time: string): boolean => Date.parse(time) + RETENTION_MS < now;
|
|
2345
|
+
// Prompt acceptance/canonical finalization precedes the outer delegate receipt.
|
|
2346
|
+
// Neither retention nor pressure compaction may erase that recovery authority.
|
|
2347
|
+
const pinnedPrompts = Object.values(transaction.requests.prompts).filter(
|
|
2348
|
+
request => request.delegate_response_pending === true,
|
|
2349
|
+
);
|
|
2350
|
+
const pinnedRequests = new Set<object>(pinnedPrompts);
|
|
2351
|
+
const pinnedTurns = new Set(pinnedPrompts.map(request => request.coordinator_turn_id));
|
|
2323
2352
|
for (const [id, event] of Object.entries(transaction.outbox))
|
|
2324
2353
|
if (
|
|
2325
2354
|
event.emitted &&
|
|
@@ -2332,6 +2361,7 @@ export function compactTransaction(transaction: CoordinatorSessionTransactionV1,
|
|
|
2332
2361
|
for (const [id, request] of Object.entries(group))
|
|
2333
2362
|
if (
|
|
2334
2363
|
request.phase === "completed" &&
|
|
2364
|
+
!pinnedRequests.has(request) &&
|
|
2335
2365
|
old(request.updated_at) &&
|
|
2336
2366
|
JSON.stringify(transaction.canonical).includes(id) === false
|
|
2337
2367
|
)
|
|
@@ -2347,6 +2377,7 @@ export function compactTransaction(transaction: CoordinatorSessionTransactionV1,
|
|
|
2347
2377
|
for (const turnId of terminalTurnIds) {
|
|
2348
2378
|
const turn = transaction.canonical.turns[turnId];
|
|
2349
2379
|
if (!turn) continue;
|
|
2380
|
+
if (pinnedTurns.has(turnId)) continue;
|
|
2350
2381
|
// Both promotion endpoints stay addressable for later projection repair;
|
|
2351
2382
|
// deleting either would corrupt the WAL instead of completing at capacity.
|
|
2352
2383
|
if (
|