@gajae-code/coding-agent 0.16.4 → 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 +54 -0
- package/dist/types/capability/types.d.ts +4 -0
- package/dist/types/cli/customize-doctor.d.ts +22 -0
- package/dist/types/config/file-lock.d.ts +31 -3
- 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/helpers.d.ts +33 -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/types.d.ts +25 -0
- package/dist/types/extensibility/hooks/types.d.ts +4 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +4 -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 +12 -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/bash-execution.d.ts +1 -0
- package/dist/types/modes/components/execution-shared.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/types.d.ts +6 -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-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/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 +4 -2
- 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.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +2 -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 +4 -3
- package/dist/types/skill-state/canonical-skills.d.ts +4 -0
- package/dist/types/system-prompt.d.ts +8 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/github-cache.d.ts +2 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/package.json +9 -7
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/src/capability/index.ts +14 -2
- package/src/capability/types.ts +4 -0
- package/src/cli/customize-doctor.ts +155 -12
- package/src/cli.ts +22 -0
- package/src/commands/sdk.ts +74 -31
- package/src/config/file-lock-gc.ts +40 -2
- package/src/config/file-lock.ts +198 -14
- package/src/config/model-preset-registry.ts +115 -16
- package/src/config/model-registry.ts +16 -12
- 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/skills/ultragoal/SKILL.md +22 -0
- package/src/discovery/builtin.ts +50 -62
- package/src/discovery/helpers.ts +297 -32
- 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 +20 -2
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +321 -23
- package/src/extensibility/gjc-plugins/registry.ts +113 -1
- 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 +80 -47
- package/src/extensibility/skill-management.ts +200 -52
- package/src/extensibility/skills.ts +29 -9
- 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/session-state-lock.ts +51 -8
- 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 +6 -6
- 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 +6 -1
- package/src/modes/acp/acp-agent.ts +16 -3
- package/src/modes/components/bash-execution.ts +4 -1
- package/src/modes/components/execution-shared.ts +4 -1
- package/src/modes/components/session-observer-overlay.ts +1 -1
- package/src/modes/components/tool-status-header.ts +3 -1
- package/src/modes/controllers/command-controller.ts +1 -0
- package/src/modes/controllers/event-controller.ts +3 -2
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +6 -3
- package/src/modes/interactive-mode.ts +6 -5
- 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 +16 -3
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/tools/subagent.md +1 -0
- package/src/runtime-mcp/manager.ts +20 -2
- package/src/runtime-mcp/pool.test.ts +20 -11
- package/src/sdk/broker/broker.ts +119 -28
- 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 +35 -5
- 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 +387 -48
- 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 +2 -2
- 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 +50 -8
- package/src/sdk/host/session-runtime.ts +158 -51
- package/src/sdk/lifecycle/service.ts +4 -0
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/sdk/router/session-router.ts +89 -38
- package/src/sdk/session.ts +155 -26
- package/src/sdk/transport/relay.ts +35 -13
- package/src/session/agent-session.ts +117 -52
- 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 +49 -43
- package/src/setup/paseo/setup-deps.ts +1 -1
- package/src/skill-state/canonical-skills.ts +12 -0
- package/src/skills/skills.test.ts +41 -21
- package/src/system-prompt.ts +25 -3
- package/src/task/executor.ts +5 -0
- package/src/task/index.ts +4 -0
- package/src/tools/bash.ts +28 -11
- package/src/tools/gh.ts +26 -3
- package/src/tools/github-cache.ts +68 -16
- package/src/tools/index.ts +2 -0
- package/src/tools/job.ts +13 -2
- 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/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +15 -0
- package/src/tools/subagent.ts +3 -2
- package/src/tools/tool-catalog.generated.ts +2 -2
- package/src/tools/ultragoal-ask-guard.ts +2 -2
|
@@ -1,5 +1,6 @@
|
|
|
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";
|
|
@@ -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 {
|
|
@@ -1576,9 +1669,10 @@ function cloneAcceptedModelPresetRegistry(value: AcceptedModelPresetRegistry): A
|
|
|
1576
1669
|
|
|
1577
1670
|
function registryFileFingerprint(file: string): string {
|
|
1578
1671
|
try {
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1672
|
+
const bytes = readRegistryBytesSync(file);
|
|
1673
|
+
return bytes === undefined ? "absent" : sha256(bytes);
|
|
1674
|
+
} catch (error) {
|
|
1675
|
+
return isEnoent(error) ? "absent" : "rejected";
|
|
1582
1676
|
}
|
|
1583
1677
|
}
|
|
1584
1678
|
|
|
@@ -1595,9 +1689,10 @@ function acceptedRegistryFingerprint(
|
|
|
1595
1689
|
|
|
1596
1690
|
async function registryFileFingerprintAsync(file: string): Promise<string> {
|
|
1597
1691
|
try {
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1692
|
+
const bytes = await readRegistryBytesBun(file);
|
|
1693
|
+
return bytes === undefined ? "absent" : sha256(bytes);
|
|
1694
|
+
} catch (error) {
|
|
1695
|
+
return isEnoent(error) ? "absent" : "rejected";
|
|
1601
1696
|
}
|
|
1602
1697
|
}
|
|
1603
1698
|
|
|
@@ -1624,6 +1719,8 @@ export function loadAcceptedModelPresetRegistry(
|
|
|
1624
1719
|
if (!acceptedRegistryReadIsCacheable(dependencies) || environmentDisabled())
|
|
1625
1720
|
return loadAcceptedModelPresetRegistryUncached(agentDir, dependencies);
|
|
1626
1721
|
const fingerprint = acceptedRegistryFingerprint(agentDir, dependencies);
|
|
1722
|
+
if (fingerprint.split("|").includes("rejected"))
|
|
1723
|
+
return loadAcceptedModelPresetRegistryUncached(agentDir, dependencies);
|
|
1627
1724
|
const cached = acceptedRegistryReadCache.get(agentDir);
|
|
1628
1725
|
if (cached && cached.fingerprint === fingerprint) return cloneAcceptedModelPresetRegistry(cached.value);
|
|
1629
1726
|
const value = loadAcceptedModelPresetRegistryUncached(agentDir, dependencies);
|
|
@@ -1700,6 +1797,8 @@ export async function loadAcceptedModelPresetRegistryAsync(
|
|
|
1700
1797
|
if (!acceptedRegistryReadIsCacheable(dependencies) || environmentDisabled())
|
|
1701
1798
|
return loadAcceptedModelPresetRegistryAsyncUncached(agentDir, dependencies);
|
|
1702
1799
|
const fingerprint = await acceptedRegistryFingerprintAsync(agentDir, dependencies);
|
|
1800
|
+
if (fingerprint.split("|").includes("rejected"))
|
|
1801
|
+
return loadAcceptedModelPresetRegistryAsyncUncached(agentDir, dependencies);
|
|
1703
1802
|
const cached = acceptedRegistryReadCache.get(agentDir);
|
|
1704
1803
|
if (cached && cached.fingerprint === fingerprint) return cloneAcceptedModelPresetRegistry(cached.value);
|
|
1705
1804
|
const value = await loadAcceptedModelPresetRegistryAsyncUncached(agentDir, dependencies);
|
|
@@ -4560,10 +4560,10 @@ export class ModelRegistry {
|
|
|
4560
4560
|
.get(model.provider.toLowerCase())
|
|
4561
4561
|
?.get(model.id.toLowerCase())?.thinking;
|
|
4562
4562
|
if (overrideThinking !== undefined) return { ...model, thinking: overrideThinking as ThinkingConfig };
|
|
4563
|
-
const
|
|
4564
|
-
candidate
|
|
4565
|
-
|
|
4566
|
-
|
|
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);
|
|
4567
4567
|
return overlay?.thinking === undefined ? model : { ...model, thinking: overlay.thinking };
|
|
4568
4568
|
}
|
|
4569
4569
|
/**
|
|
@@ -5215,15 +5215,20 @@ export class ModelRegistry {
|
|
|
5215
5215
|
* usable until live catalog evidence exists so offline startup is unchanged.
|
|
5216
5216
|
*/
|
|
5217
5217
|
getAvailableForProfileActivation(): Model<Api>[] {
|
|
5218
|
+
const bundledIdsByProvider = new Map<string, Set<string>>();
|
|
5218
5219
|
return this.getAvailable().filter(model => {
|
|
5219
5220
|
const evidence = this.#descriptorDiscoveryEvidence.get(model.provider);
|
|
5220
5221
|
if (!evidence?.profileFresh || evidence.profileModelIds === undefined) return true;
|
|
5221
5222
|
if (this.#hasCustomModelOverlay(model.provider, model.id)) return true;
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
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
|
+
}
|
|
5227
5232
|
if (!bundledModelIds.has(model.id)) return true;
|
|
5228
5233
|
if (
|
|
5229
5234
|
evidence.authGeneration !== this.#getProviderEvidenceGeneration(model.provider) ||
|
|
@@ -5236,9 +5241,8 @@ export class ModelRegistry {
|
|
|
5236
5241
|
}
|
|
5237
5242
|
|
|
5238
5243
|
#hasCustomModelOverlay(provider: string, id: string): boolean {
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
);
|
|
5244
|
+
const matches = (overlay: CustomModelOverlay) => overlay.provider === provider && overlay.id === id;
|
|
5245
|
+
return this.#customModelOverlays.some(matches) || this.#runtimeModelOverlays.some(matches);
|
|
5242
5246
|
}
|
|
5243
5247
|
|
|
5244
5248
|
#hasFreshOrStaticModelEvidence(model: Model<Api>): boolean {
|
|
@@ -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 (
|