@gajae-code/coding-agent 0.15.2 → 0.15.3
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 +37 -1
- package/dist/types/config/autorouting-tier-map.d.ts +0 -9
- package/dist/types/config/file-lock.d.ts +7 -1
- package/dist/types/config/keybindings.d.ts +13 -3
- package/dist/types/config/model-registry.d.ts +5 -0
- package/dist/types/config/model-resolver.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +13 -1
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/question-state.d.ts +42 -2
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +12 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/modes/action-registry.d.ts +1 -0
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +1 -1
- package/dist/types/modes/components/welcome.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +16 -1
- package/dist/types/modes/controllers/runtime-mcp-command-controller.d.ts +42 -0
- package/dist/types/modes/interactive-mode.d.ts +7 -0
- package/dist/types/modes/shared/oauth-url-copy.d.ts +9 -0
- package/dist/types/modes/types.d.ts +3 -0
- package/dist/types/modes/ui-language.d.ts +2 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +1 -0
- package/dist/types/sdk/broker/session-index.d.ts +18 -0
- package/dist/types/sdk/bus/index.d.ts +4 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
- package/dist/types/sdk/cli/session-cli.d.ts +1 -1
- package/dist/types/sdk/lifecycle/service.d.ts +32 -5
- package/dist/types/sdk/session.d.ts +5 -0
- package/dist/types/session/agent-session.d.ts +4 -0
- package/dist/types/session/fallback-chain-controller.d.ts +8 -0
- package/dist/types/slash-commands/helpers/aside.d.ts +54 -0
- package/dist/types/tools/ask.d.ts +8 -0
- package/dist/types/utils/clipboard.d.ts +9 -1
- package/package.json +7 -7
- package/scripts/generate-sdk-adapter-parity-manifest.ts +5 -2
- package/scripts/generate-sdk-operation-inventory.ts +3 -0
- package/src/commands/sdk.ts +2 -2
- package/src/config/autorouting-tier-map.ts +0 -9
- package/src/config/file-lock.ts +15 -1
- package/src/config/keybindings.ts +13 -3
- package/src/config/model-registry.ts +132 -21
- package/src/config/model-resolver.ts +2 -1
- package/src/config/models-config-schema.ts +2 -2
- package/src/config/settings-schema.ts +13 -1
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/question-state.ts +235 -4
- package/src/coordinator-mcp/server.ts +402 -2
- package/src/dap/client.ts +19 -8
- package/src/gjc-runtime/deep-interview-stage.ts +20 -1
- package/src/gjc-runtime/deep-interview-state.ts +3 -1
- package/src/gjc-runtime/session-state-lock.ts +11 -2
- package/src/internal-urls/docs-index.generated.ts +8 -7
- package/src/lsp/client.ts +39 -4
- package/src/main.ts +42 -11
- package/src/modes/action-registry.ts +7 -0
- package/src/modes/components/iterm-pet-transport.ts +4 -2
- package/src/modes/components/runtime-mcp-add-wizard.ts +38 -1
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/tool-status-header.ts +149 -41
- package/src/modes/components/welcome.ts +8 -0
- package/src/modes/controllers/goal-mode-controller.ts +7 -2
- package/src/modes/controllers/input-controller.ts +141 -7
- package/src/modes/controllers/plan-mode-controller.ts +12 -4
- package/src/modes/controllers/runtime-mcp-command-controller.ts +164 -20
- package/src/modes/controllers/selector-controller.ts +11 -2
- package/src/modes/interactive-mode.ts +88 -1
- package/src/modes/shared/oauth-url-copy.ts +33 -0
- package/src/modes/types.ts +3 -0
- package/src/modes/ui-language.ts +44 -3
- package/src/runtime-mcp/oauth-flow.ts +19 -8
- package/src/runtime-mcp/transports/stdio.ts +27 -13
- package/src/sdk/acp/adapter.ts +29 -2
- package/src/sdk/broker/broker.ts +26 -6
- package/src/sdk/broker/lifecycle-ledger.ts +28 -1
- package/src/sdk/broker/lifecycle.ts +664 -5
- package/src/sdk/broker/session-index.ts +246 -0
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/bus/index.ts +135 -38
- package/src/sdk/bus/natives-augment.d.ts +20 -1
- package/src/sdk/bus/telegram-daemon-contract.ts +4 -1
- package/src/sdk/bus/telegram-daemon.ts +3 -0
- package/src/sdk/cli/session-cli.ts +34 -7
- package/src/sdk/lifecycle/service.ts +166 -11
- package/src/sdk/mcp/server.ts +2 -1
- package/src/sdk/protocol/operation-inventory.generated.json +40 -0
- package/src/sdk/protocol/operation-registry.ts +2 -1
- package/src/sdk/session.ts +91 -7
- package/src/session/agent-session.ts +403 -56
- package/src/session/fallback-chain-controller.ts +17 -0
- package/src/setup/frictionless-onboarding.ts +4 -4
- package/src/slash-commands/aside-command.test.ts +366 -0
- package/src/slash-commands/builtin-registry.ts +19 -0
- package/src/slash-commands/helpers/aside.ts +299 -0
- package/src/tools/ask.ts +8 -0
- package/src/utils/clipboard.ts +37 -28
|
@@ -16,12 +16,14 @@ import {
|
|
|
16
16
|
enrichModelThinking,
|
|
17
17
|
getBundledModels,
|
|
18
18
|
getBundledProviders,
|
|
19
|
+
getEnvApiKey,
|
|
19
20
|
googleAntigravityModelManagerOptions,
|
|
20
21
|
googleGeminiCliModelManagerOptions,
|
|
21
22
|
isCodexGpt56Tier,
|
|
22
23
|
isKnownProvider,
|
|
23
24
|
type Model,
|
|
24
25
|
type ModelManagerOptions,
|
|
26
|
+
type ModelMaxTokensSource,
|
|
25
27
|
type ModelRefreshStrategy,
|
|
26
28
|
type ModelRequestTransform,
|
|
27
29
|
modelSupportsReasoningControl,
|
|
@@ -47,6 +49,10 @@ function isVllmNoAuthToken(provider: string, apiKey: string | undefined): boolea
|
|
|
47
49
|
return provider === "vllm" && apiKey === VLLM_DEFAULT_LOCAL_TOKEN;
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
function normalizeVllmApiKey(provider: string, apiKey: string | undefined): string | undefined {
|
|
53
|
+
return isVllmNoAuthToken(provider, apiKey) ? getEnvApiKey(provider) : apiKey;
|
|
54
|
+
}
|
|
55
|
+
|
|
50
56
|
import { registerOAuthProvider, unregisterOAuthProviders } from "@gajae-code/ai/utils/oauth";
|
|
51
57
|
import type { OAuthCredentials, OAuthLoginCallbacks } from "@gajae-code/ai/utils/oauth/types";
|
|
52
58
|
import { $pickCredentialEnv, $rotatingCredentialEnv, isRecord, logger } from "@gajae-code/utils";
|
|
@@ -996,7 +1002,10 @@ function applyModelOverride(model: Model<Api>, override: ModelOverride): Model<A
|
|
|
996
1002
|
override: override.contextWindow,
|
|
997
1003
|
});
|
|
998
1004
|
}
|
|
999
|
-
if (override.maxTokens !== undefined
|
|
1005
|
+
if (override.maxTokens !== undefined && Number.isSafeInteger(override.maxTokens) && override.maxTokens > 0) {
|
|
1006
|
+
result.maxTokens = override.maxTokens;
|
|
1007
|
+
result.maxTokensSource = "configured";
|
|
1008
|
+
}
|
|
1000
1009
|
if (override.contextPromotionTarget !== undefined) result.contextPromotionTarget = override.contextPromotionTarget;
|
|
1001
1010
|
if (override.wireModelId !== undefined) result.wireModelId = override.wireModelId;
|
|
1002
1011
|
result.requestTransform = mergeRequestTransform(model.requestTransform, override.requestTransform);
|
|
@@ -1074,6 +1083,7 @@ type CustomModelOverlay = {
|
|
|
1074
1083
|
cost?: { input: number; output: number; cacheRead: number; cacheWrite: number };
|
|
1075
1084
|
contextWindow?: number;
|
|
1076
1085
|
maxTokens?: number;
|
|
1086
|
+
maxTokensSource?: ModelMaxTokensSource;
|
|
1077
1087
|
headers?: Record<string, string>;
|
|
1078
1088
|
compat?: Model<Api>["compat"];
|
|
1079
1089
|
contextPromotionTarget?: string;
|
|
@@ -1150,6 +1160,10 @@ function buildCustomModelOverlay(
|
|
|
1150
1160
|
cost: modelDef.cost,
|
|
1151
1161
|
contextWindow: modelDef.contextWindow,
|
|
1152
1162
|
maxTokens: modelDef.maxTokens,
|
|
1163
|
+
maxTokensSource:
|
|
1164
|
+
modelDef.maxTokens !== undefined && Number.isSafeInteger(modelDef.maxTokens) && modelDef.maxTokens > 0
|
|
1165
|
+
? "configured"
|
|
1166
|
+
: undefined,
|
|
1153
1167
|
headers: mergeCustomModelHeaders(providerHeaders, modelDef.headers, authHeader, providerApiKey),
|
|
1154
1168
|
compat: mergeCompat(providerCompat, modelDef.compat),
|
|
1155
1169
|
requestTransform: mergeRequestTransform(providerRequestTransform, modelDef.requestTransform),
|
|
@@ -1284,6 +1298,8 @@ function finalizeCustomModel(model: CustomModelOverlay, options: CustomModelBuil
|
|
|
1284
1298
|
contextWindow:
|
|
1285
1299
|
resolvedModel.contextWindow ?? reference?.contextWindow ?? (options.useDefaults ? 128000 : undefined),
|
|
1286
1300
|
maxTokens: resolvedModel.maxTokens ?? reference?.maxTokens ?? (options.useDefaults ? 16384 : undefined),
|
|
1301
|
+
maxTokensSource:
|
|
1302
|
+
resolvedModel.maxTokensSource ?? (reference?.maxTokensSource === "configured" ? "configured" : undefined),
|
|
1287
1303
|
headers: resolvedModel.headers,
|
|
1288
1304
|
compat: mergeCompat(reference?.compat, resolvedModel.compat),
|
|
1289
1305
|
contextPromotionTarget: resolvedModel.contextPromotionTarget,
|
|
@@ -1507,6 +1523,68 @@ export class ModelRegistry {
|
|
|
1507
1523
|
}
|
|
1508
1524
|
}
|
|
1509
1525
|
|
|
1526
|
+
admitCachedProviderForStoredLiteralCredential(providerId: string, selector: AuthCredentialSelector): boolean {
|
|
1527
|
+
const cachedModels = this.#loadCachedProviderModelsForStoredLiteralCredential(providerId, selector);
|
|
1528
|
+
if (!cachedModels || cachedModels.length === 0) return false;
|
|
1529
|
+
|
|
1530
|
+
this.#suspendRebuild();
|
|
1531
|
+
try {
|
|
1532
|
+
this.#mergeDiscoveredModels(cachedModels);
|
|
1533
|
+
this.#rebuildProviderActivity();
|
|
1534
|
+
this.#modelBindingsApplier.apply();
|
|
1535
|
+
return true;
|
|
1536
|
+
} finally {
|
|
1537
|
+
this.#resumeRebuild();
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
/** @internal Validate an existing startup model without mutating a caller-supplied registry. */
|
|
1542
|
+
validateModelForStoredLiteralCredential(
|
|
1543
|
+
providerId: string,
|
|
1544
|
+
modelId: string,
|
|
1545
|
+
selector: AuthCredentialSelector,
|
|
1546
|
+
): boolean {
|
|
1547
|
+
if (!this.find(providerId, modelId)) return false;
|
|
1548
|
+
if (this.#resolveStaticModelReference(providerId, modelId)) return true;
|
|
1549
|
+
const cachedModels = this.#loadCachedProviderModelsForStoredLiteralCredential(providerId, selector, false);
|
|
1550
|
+
return cachedModels ? resolveProviderModelReference(providerId, modelId, cachedModels) !== undefined : false;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
/** @internal Whether startup must establish credential-scoped cache provenance for this exact target. */
|
|
1554
|
+
requiresStoredLiteralCredentialCacheAdmission(providerId: string, modelId: string): boolean {
|
|
1555
|
+
return !this.find(providerId, modelId) || !this.#resolveStaticModelReference(providerId, modelId);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
#loadCachedProviderModelsForStoredLiteralCredential(
|
|
1559
|
+
providerId: string,
|
|
1560
|
+
selector: AuthCredentialSelector,
|
|
1561
|
+
publishDiscoveryState = true,
|
|
1562
|
+
): Model<Api>[] | undefined {
|
|
1563
|
+
const supportsDiscovery =
|
|
1564
|
+
this.#discoveryManager.providerIds().has(providerId) ||
|
|
1565
|
+
PROVIDER_DESCRIPTORS.some(descriptor => descriptor.providerId === providerId);
|
|
1566
|
+
if (!supportsDiscovery) return undefined;
|
|
1567
|
+
const authEvidence = this.authStorage.getStoredLiteralApiKeyEvidenceGeneration(providerId, selector);
|
|
1568
|
+
if (!authEvidence) return undefined;
|
|
1569
|
+
return [
|
|
1570
|
+
...this.#loadCachedStandardProviderModels(providerId, authEvidence),
|
|
1571
|
+
...this.#loadCachedDiscoverableModels(providerId, authEvidence, publishDiscoveryState),
|
|
1572
|
+
];
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
#resolveStaticModelReference(providerId: string, modelId: string): Model<Api> | undefined {
|
|
1576
|
+
const normalizedProvider = providerId.toLowerCase();
|
|
1577
|
+
const matchingActivity = [...this.#providerActivity.entries()].filter(
|
|
1578
|
+
([candidateProvider]) => candidateProvider.toLowerCase() === normalizedProvider,
|
|
1579
|
+
);
|
|
1580
|
+
if (matchingActivity.length !== 1) return undefined;
|
|
1581
|
+
const [canonicalProvider, activity] = matchingActivity[0];
|
|
1582
|
+
const staticModels = this.#models.filter(
|
|
1583
|
+
model => model.provider === canonicalProvider && activity.staticModelIds.has(model.id),
|
|
1584
|
+
);
|
|
1585
|
+
return resolveProviderModelReference(providerId, modelId, staticModels);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1510
1588
|
#getStaticLoadEnvironmentFingerprint(): string {
|
|
1511
1589
|
const providerBaseUrlEnvKeys = new Set(
|
|
1512
1590
|
[
|
|
@@ -1744,6 +1822,7 @@ export class ModelRegistry {
|
|
|
1744
1822
|
cost: customModel.cost ?? existingModel.cost,
|
|
1745
1823
|
contextWindow: customModel.contextWindow ?? existingModel.contextWindow,
|
|
1746
1824
|
maxTokens: customModel.maxTokens ?? existingModel.maxTokens,
|
|
1825
|
+
maxTokensSource: customModel.maxTokensSource ?? existingModel.maxTokensSource,
|
|
1747
1826
|
// Same-id custom definitions replace bundled transport behavior. Provider-level
|
|
1748
1827
|
// headers/compat were already folded into customModel during parsing; do not
|
|
1749
1828
|
// re-merge bundled transport metadata here.
|
|
@@ -1762,16 +1841,17 @@ export class ModelRegistry {
|
|
|
1762
1841
|
return merged;
|
|
1763
1842
|
}
|
|
1764
1843
|
|
|
1765
|
-
#loadCachedStandardProviderModels(): Model<Api>[] {
|
|
1844
|
+
#loadCachedStandardProviderModels(providerId?: string, authEvidence?: string): Model<Api>[] {
|
|
1766
1845
|
const configuredDiscoveryProviders = new Set(this.#discoveryManager.providers.map(provider => provider.provider));
|
|
1767
1846
|
const cachedModels: Model<Api>[] = [];
|
|
1768
1847
|
for (const descriptor of PROVIDER_DESCRIPTORS) {
|
|
1848
|
+
if (providerId && descriptor.providerId !== providerId) continue;
|
|
1769
1849
|
if (configuredDiscoveryProviders.has(descriptor.providerId)) {
|
|
1770
1850
|
continue;
|
|
1771
1851
|
}
|
|
1772
1852
|
const cache = readModelCache<Api>(descriptor.providerId, 24 * 60 * 60 * 1000, Date.now, this.#cacheDbPath);
|
|
1773
1853
|
const expectedProvenance = fingerprintDescriptorDiscoveryProvenance(
|
|
1774
|
-
this.#getProviderEvidenceGeneration(descriptor.providerId),
|
|
1854
|
+
authEvidence ?? this.#getProviderEvidenceGeneration(descriptor.providerId),
|
|
1775
1855
|
this.#normalizeDiscoveryEvidenceEndpoint(this.#getProviderBaseUrlForDiscovery(descriptor.providerId) ?? ""),
|
|
1776
1856
|
);
|
|
1777
1857
|
if (
|
|
@@ -1796,22 +1876,29 @@ export class ModelRegistry {
|
|
|
1796
1876
|
return cachedModels;
|
|
1797
1877
|
}
|
|
1798
1878
|
|
|
1799
|
-
#loadCachedDiscoverableModels(
|
|
1879
|
+
#loadCachedDiscoverableModels(
|
|
1880
|
+
providerId?: string,
|
|
1881
|
+
authEvidence?: string,
|
|
1882
|
+
publishDiscoveryState = true,
|
|
1883
|
+
): Model<Api>[] {
|
|
1800
1884
|
const cachedModels: Model<Api>[] = [];
|
|
1801
1885
|
for (const providerConfig of this.#discoveryManager.providers) {
|
|
1886
|
+
if (providerId && providerConfig.provider !== providerId) continue;
|
|
1802
1887
|
let expectedProvenance: string | undefined;
|
|
1803
1888
|
try {
|
|
1804
1889
|
const effectiveConfig = this.#effectiveDiscoveryProviderConfig(providerConfig);
|
|
1805
1890
|
expectedProvenance = fingerprintConfiguredDiscoveryRequestShape(
|
|
1806
1891
|
effectiveConfig,
|
|
1807
|
-
this.#getProviderEvidenceGeneration(effectiveConfig.provider),
|
|
1892
|
+
authEvidence ?? this.#getProviderEvidenceGeneration(effectiveConfig.provider),
|
|
1808
1893
|
this.#normalizeDiscoveryEvidenceEndpoint(effectiveConfig.baseUrl ?? ""),
|
|
1809
1894
|
);
|
|
1810
1895
|
} catch {
|
|
1811
1896
|
// A context that cannot be fully derived cannot vouch for a cache row.
|
|
1812
1897
|
expectedProvenance = undefined;
|
|
1813
1898
|
}
|
|
1814
|
-
const models =
|
|
1899
|
+
const models = publishDiscoveryState
|
|
1900
|
+
? this.#discoveryManager.loadCached(providerConfig, this.#cacheDbPath, expectedProvenance)
|
|
1901
|
+
: this.#readCachedDiscoverableModels(providerConfig, expectedProvenance);
|
|
1815
1902
|
// Cache rows persist sanitized transport metadata (no headers), so a
|
|
1816
1903
|
// rebooted registry re-derives the provider transport override from the
|
|
1817
1904
|
// same source the live publish path uses — mirroring the cached
|
|
@@ -1834,6 +1921,22 @@ export class ModelRegistry {
|
|
|
1834
1921
|
return cachedModels;
|
|
1835
1922
|
}
|
|
1836
1923
|
|
|
1924
|
+
#readCachedDiscoverableModels(
|
|
1925
|
+
providerConfig: DiscoveryProviderConfig,
|
|
1926
|
+
expectedProvenance: string | undefined,
|
|
1927
|
+
): Model<Api>[] {
|
|
1928
|
+
const cache = readModelCache<Api>(providerConfig.provider, 24 * 60 * 60 * 1000, Date.now, this.#cacheDbPath);
|
|
1929
|
+
if (
|
|
1930
|
+
cache === null ||
|
|
1931
|
+
cache.dynamicModelIds === undefined ||
|
|
1932
|
+
expectedProvenance === undefined ||
|
|
1933
|
+
cache.dynamicModelProvenance !== expectedProvenance
|
|
1934
|
+
) {
|
|
1935
|
+
return [];
|
|
1936
|
+
}
|
|
1937
|
+
return applyFinalCodexGpt56ContextCap(cache.models);
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1837
1940
|
#applyProviderCompat(compat: Model<Api>["compat"] | undefined, models: Model<Api>[]): Model<Api>[] {
|
|
1838
1941
|
if (!compat) return models;
|
|
1839
1942
|
return models.map(model => ({ ...model, compat: mergeCompat(model.compat, compat) }));
|
|
@@ -2452,6 +2555,10 @@ export class ModelRegistry {
|
|
|
2452
2555
|
}
|
|
2453
2556
|
return;
|
|
2454
2557
|
}
|
|
2558
|
+
this.#mergeDiscoveredModels(discovered);
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
#mergeDiscoveredModels(discovered: readonly Model<Api>[]): void {
|
|
2455
2562
|
const discoveredModels = this.#applyHardcodedModelPolicies(
|
|
2456
2563
|
discovered.map(model =>
|
|
2457
2564
|
mergeDiscoveredModel(
|
|
@@ -2495,11 +2602,14 @@ export class ModelRegistry {
|
|
|
2495
2602
|
if (optionalAuth && isCurrentPreflight()) this.#credentiallessAuthFallbackProviders.delete(provider);
|
|
2496
2603
|
let apiKey: string | undefined;
|
|
2497
2604
|
try {
|
|
2498
|
-
apiKey =
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2605
|
+
apiKey = normalizeVllmApiKey(
|
|
2606
|
+
provider,
|
|
2607
|
+
await this.#peekApiKeyForProvider(provider, {
|
|
2608
|
+
ignoreCredentiallessFallback: optionalAuth,
|
|
2609
|
+
refreshOAuth: true,
|
|
2610
|
+
baseUrl: providerConfig.baseUrl,
|
|
2611
|
+
}),
|
|
2612
|
+
);
|
|
2503
2613
|
const currentAuthConfigurationGeneration = this.authStorage.getProviderConfigurationGeneration(provider);
|
|
2504
2614
|
if (preflightAuthConfigurationGeneration !== currentAuthConfigurationGeneration) {
|
|
2505
2615
|
const currentOAuthRefreshGeneration = this.authStorage.getProviderOAuthRefreshGeneration(provider);
|
|
@@ -3287,6 +3397,11 @@ export class ModelRegistry {
|
|
|
3287
3397
|
if (!isRecord(item) || typeof item.id !== "string" || !item.id.trim()) continue;
|
|
3288
3398
|
const id = item.id;
|
|
3289
3399
|
const referenceModel = resolveCustomModelReference(id);
|
|
3400
|
+
const discoveredMaxTokens = firstPositiveDiscoveryNumber(
|
|
3401
|
+
item.max_completion_tokens,
|
|
3402
|
+
item.max_tokens,
|
|
3403
|
+
item.max_output_tokens,
|
|
3404
|
+
);
|
|
3290
3405
|
const api = this.#resolveDiscoveredModelApi(providerConfig, id);
|
|
3291
3406
|
discovered.push(
|
|
3292
3407
|
enrichModelThinking({
|
|
@@ -3309,14 +3424,8 @@ export class ModelRegistry {
|
|
|
3309
3424
|
referenceModel?.contextWindow,
|
|
3310
3425
|
UNK_CONTEXT_WINDOW,
|
|
3311
3426
|
) ?? UNK_CONTEXT_WINDOW,
|
|
3312
|
-
maxTokens:
|
|
3313
|
-
|
|
3314
|
-
item.max_completion_tokens,
|
|
3315
|
-
item.max_tokens,
|
|
3316
|
-
item.max_output_tokens,
|
|
3317
|
-
referenceModel?.maxTokens,
|
|
3318
|
-
UNK_MAX_TOKENS,
|
|
3319
|
-
) ?? UNK_MAX_TOKENS,
|
|
3427
|
+
maxTokens: discoveredMaxTokens ?? referenceModel?.maxTokens ?? UNK_MAX_TOKENS,
|
|
3428
|
+
maxTokensSource: discoveredMaxTokens === undefined ? referenceModel?.maxTokensSource : "discovered",
|
|
3320
3429
|
headers: providerConfig.headers,
|
|
3321
3430
|
compat: mergeCompat(
|
|
3322
3431
|
{
|
|
@@ -4367,9 +4476,11 @@ export class ModelRegistry {
|
|
|
4367
4476
|
}
|
|
4368
4477
|
|
|
4369
4478
|
async #getApiKeyOrNoAuth(provider: string, lookup: () => Promise<string | undefined>): Promise<string | undefined> {
|
|
4370
|
-
|
|
4371
|
-
if (!this.#optionalAuthProviders.has(provider)) return kNoAuth;
|
|
4479
|
+
const credentialless = this.#isCredentiallessProvider(provider);
|
|
4480
|
+
if (credentialless && !this.#optionalAuthProviders.has(provider)) return kNoAuth;
|
|
4372
4481
|
const apiKey = await lookup();
|
|
4482
|
+
if (isVllmNoAuthToken(provider, apiKey)) return normalizeVllmApiKey(provider, apiKey) ?? kNoAuth;
|
|
4483
|
+
if (!credentialless) return apiKey;
|
|
4373
4484
|
if (apiKey !== undefined) {
|
|
4374
4485
|
this.#credentiallessAuthFallbackProviders.delete(provider);
|
|
4375
4486
|
return apiKey;
|
|
@@ -1025,6 +1025,7 @@ export interface ModelChainResolutionOptions {
|
|
|
1025
1025
|
aliasIntent?: "preset-equivalent" | "reject";
|
|
1026
1026
|
canonicalSessionId?: string | null;
|
|
1027
1027
|
credentialSessionId?: string;
|
|
1028
|
+
signal?: AbortSignal;
|
|
1028
1029
|
}
|
|
1029
1030
|
|
|
1030
1031
|
export async function resolveModelChainWithAuth(
|
|
@@ -1080,7 +1081,7 @@ export async function resolveModelChainWithAuth(
|
|
|
1080
1081
|
break;
|
|
1081
1082
|
}
|
|
1082
1083
|
}
|
|
1083
|
-
const key = await modelRegistry.getApiKey(candidate.model, credentialSessionId);
|
|
1084
|
+
const key = await modelRegistry.getApiKey(candidate.model, credentialSessionId, { signal: options?.signal });
|
|
1084
1085
|
if (isAuthenticatedOrKeyless(key)) {
|
|
1085
1086
|
return { ...candidate, activeIndex, skips };
|
|
1086
1087
|
}
|
|
@@ -158,7 +158,7 @@ const ModelDefinitionSchema = z
|
|
|
158
158
|
.optional(),
|
|
159
159
|
premiumMultiplier: z.number().optional(),
|
|
160
160
|
contextWindow: z.number().optional(),
|
|
161
|
-
maxTokens: z.number().optional(),
|
|
161
|
+
maxTokens: z.number().int().finite().positive().max(Number.MAX_SAFE_INTEGER).optional(),
|
|
162
162
|
headers: z.record(z.string(), z.string()).optional(),
|
|
163
163
|
compat: ModelCompatSchema.optional(),
|
|
164
164
|
contextPromotionTarget: z.string().min(1).optional(),
|
|
@@ -185,7 +185,7 @@ export const ModelOverrideSchema = z
|
|
|
185
185
|
.optional(),
|
|
186
186
|
premiumMultiplier: z.number().optional(),
|
|
187
187
|
contextWindow: z.number().optional(),
|
|
188
|
-
maxTokens: z.number().optional(),
|
|
188
|
+
maxTokens: z.number().int().finite().positive().max(Number.MAX_SAFE_INTEGER).optional(),
|
|
189
189
|
headers: z.record(z.string(), z.string()).optional(),
|
|
190
190
|
compat: ModelCompatSchema.optional(),
|
|
191
191
|
contextPromotionTarget: z.string().min(1).optional(),
|
|
@@ -745,7 +745,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
745
745
|
// ────────────────────────────────────────────────────────────────────────
|
|
746
746
|
"ui.language": {
|
|
747
747
|
type: "enum",
|
|
748
|
-
values: ["en", "ko"] as const,
|
|
748
|
+
values: ["en", "ko", "ja"] as const,
|
|
749
749
|
default: "en",
|
|
750
750
|
ui: {
|
|
751
751
|
tab: "appearance",
|
|
@@ -754,6 +754,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
754
754
|
options: [
|
|
755
755
|
{ value: "en", label: "English" },
|
|
756
756
|
{ value: "ko", label: "Korean (한국어)" },
|
|
757
|
+
{ value: "ja", label: "Japanese (日本語)" },
|
|
757
758
|
],
|
|
758
759
|
},
|
|
759
760
|
},
|
|
@@ -1682,6 +1683,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
1682
1683
|
},
|
|
1683
1684
|
},
|
|
1684
1685
|
|
|
1686
|
+
"startup.skipLogoAnimation": {
|
|
1687
|
+
type: "boolean",
|
|
1688
|
+
default: false,
|
|
1689
|
+
ui: {
|
|
1690
|
+
tab: "interaction",
|
|
1691
|
+
label: "Skip Startup Logo Animation",
|
|
1692
|
+
description:
|
|
1693
|
+
"Skip the animated startup logo sweep; the welcome surface, its resting logo, and all information rows still render. Applies at the next interactive start.",
|
|
1694
|
+
},
|
|
1695
|
+
},
|
|
1696
|
+
|
|
1685
1697
|
"startup.checkUpdate": {
|
|
1686
1698
|
type: "boolean",
|
|
1687
1699
|
default: true,
|
|
@@ -12,6 +12,7 @@ export const COORDINATOR_MCP_TOOL_NAMES = [
|
|
|
12
12
|
"gjc_coordinator_watch_events",
|
|
13
13
|
"gjc_coordinator_register_session",
|
|
14
14
|
"gjc_coordinator_start_session",
|
|
15
|
+
"gjc_coordinator_retire_start_session",
|
|
15
16
|
"gjc_coordinator_activate_session",
|
|
16
17
|
"gjc_coordinator_stop_session",
|
|
17
18
|
"gjc_coordinator_send_prompt",
|
|
@@ -287,18 +287,51 @@ export interface CreationRequestV1 {
|
|
|
287
287
|
key_digest: string;
|
|
288
288
|
request_digest: string;
|
|
289
289
|
tool: string;
|
|
290
|
-
phase: "claimed" | "remote_started" | "wal_committed" | "projected" | "completed" | "uncertain";
|
|
290
|
+
phase: "claimed" | "remote_started" | "wal_committed" | "projected" | "completed" | "uncertain" | "retired";
|
|
291
291
|
canonical_create_intent: CanonicalCreateIntentV1 | null;
|
|
292
292
|
remote_create_key: string;
|
|
293
293
|
session_id: string | null;
|
|
294
294
|
endpoint_incarnation: string | null;
|
|
295
295
|
sidecar_verifier: { key_id: string; public_key: string } | null;
|
|
296
|
+
/** Retirement is staged before the broker effect so a post-effect crash can replay safely. */
|
|
297
|
+
retirement_intent?: CreationRetirementIntentV1;
|
|
296
298
|
wal_revision?: number;
|
|
297
299
|
wal_digest?: string;
|
|
298
300
|
safe_response?: Record<string, unknown>;
|
|
299
301
|
created_at: string;
|
|
300
302
|
updated_at: string;
|
|
301
303
|
}
|
|
304
|
+
export interface CreationRetirementProofV1 {
|
|
305
|
+
session_id: string;
|
|
306
|
+
cwd: string;
|
|
307
|
+
state_root: string;
|
|
308
|
+
endpoint_generation: number;
|
|
309
|
+
endpoint_mtime_ms: number;
|
|
310
|
+
process_incarnation: string;
|
|
311
|
+
host_incarnation: string;
|
|
312
|
+
lifecycle_request_id: string;
|
|
313
|
+
remote_create_key: string;
|
|
314
|
+
}
|
|
315
|
+
export interface CreationRetirementBrokerProofV1 {
|
|
316
|
+
session_id: string;
|
|
317
|
+
retired: true;
|
|
318
|
+
ledger_state: "terminal_error";
|
|
319
|
+
index_type: "session_closed";
|
|
320
|
+
state_root: string;
|
|
321
|
+
endpoint_generation: number;
|
|
322
|
+
endpoint_mtime_ms: number;
|
|
323
|
+
process_incarnation: string;
|
|
324
|
+
host_incarnation: string;
|
|
325
|
+
lifecycle_request_id: string;
|
|
326
|
+
remote_create_key: string;
|
|
327
|
+
}
|
|
328
|
+
export interface CreationRetirementIntentV1 {
|
|
329
|
+
phase: "intent" | "pre_effect_rejected" | "broker_retired";
|
|
330
|
+
proof: CreationRetirementProofV1;
|
|
331
|
+
retirement_key_digest?: string;
|
|
332
|
+
broker_proof?: CreationRetirementBrokerProofV1;
|
|
333
|
+
updated_at: string;
|
|
334
|
+
}
|
|
302
335
|
export interface NamespaceDeletionEntryV1 {
|
|
303
336
|
deletion_id: string;
|
|
304
337
|
session_id: string;
|
|
@@ -2395,21 +2428,219 @@ export async function repairProjections(
|
|
|
2395
2428
|
});
|
|
2396
2429
|
}
|
|
2397
2430
|
|
|
2431
|
+
function sameCreationRetirementProof(left: CreationRetirementProofV1, right: CreationRetirementProofV1): boolean {
|
|
2432
|
+
return canonicalJson(left) === canonicalJson(right);
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
function assertCreationRetirementProofMatches(
|
|
2436
|
+
request: CreationRequestV1,
|
|
2437
|
+
proof: CreationRetirementProofV1,
|
|
2438
|
+
includeStaged = true,
|
|
2439
|
+
): void {
|
|
2440
|
+
if (
|
|
2441
|
+
!proof.session_id ||
|
|
2442
|
+
!proof.cwd ||
|
|
2443
|
+
!proof.state_root ||
|
|
2444
|
+
!Number.isSafeInteger(proof.endpoint_generation) ||
|
|
2445
|
+
proof.endpoint_generation <= 0 ||
|
|
2446
|
+
!Number.isFinite(proof.endpoint_mtime_ms) ||
|
|
2447
|
+
proof.endpoint_mtime_ms <= 0 ||
|
|
2448
|
+
!proof.process_incarnation ||
|
|
2449
|
+
!proof.host_incarnation ||
|
|
2450
|
+
!proof.lifecycle_request_id ||
|
|
2451
|
+
!proof.remote_create_key
|
|
2452
|
+
)
|
|
2453
|
+
throw new Error("invalid_input");
|
|
2454
|
+
if (request.remote_create_key !== proof.remote_create_key) throw new Error("idempotency_conflict");
|
|
2455
|
+
if (request.session_id !== null && request.session_id !== proof.session_id) throw new Error("idempotency_conflict");
|
|
2456
|
+
const intent = request.canonical_create_intent;
|
|
2457
|
+
if (intent) {
|
|
2458
|
+
const session = intent.session;
|
|
2459
|
+
if (
|
|
2460
|
+
session.session_id !== proof.session_id ||
|
|
2461
|
+
path.resolve(session.cwd) !== path.resolve(proof.cwd) ||
|
|
2462
|
+
path.resolve(session.cwd, ".gjc", "state") !== path.resolve(proof.state_root) ||
|
|
2463
|
+
session.broker.endpoint_generation !== proof.endpoint_generation ||
|
|
2464
|
+
intent.kind === "register" ||
|
|
2465
|
+
intent.remote_create_key !== proof.remote_create_key
|
|
2466
|
+
)
|
|
2467
|
+
throw new Error("idempotency_conflict");
|
|
2468
|
+
}
|
|
2469
|
+
const staged = includeStaged ? request.retirement_intent : undefined;
|
|
2470
|
+
if (staged && !sameCreationRetirementProof(staged.proof, proof)) throw new Error("idempotency_conflict");
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
/** Claims retirement under the creation receipt before any broker effect. */
|
|
2474
|
+
export async function recordCreationRetirementIntent(
|
|
2475
|
+
paths: CoordinatorStatePaths,
|
|
2476
|
+
keyDigest: string,
|
|
2477
|
+
proof: CreationRetirementProofV1,
|
|
2478
|
+
retirementKeyDigest?: string,
|
|
2479
|
+
): Promise<CreationRequestV1> {
|
|
2480
|
+
return await withNamespaceRegistry(paths, async registry => {
|
|
2481
|
+
const request = registry.creations[keyDigest];
|
|
2482
|
+
if (!request) throw new Error("state_corrupt");
|
|
2483
|
+
if (request.phase !== "remote_started" && request.phase !== "uncertain" && request.phase !== "retired")
|
|
2484
|
+
throw new Error("retire_not_allowed");
|
|
2485
|
+
assertCreationRetirementProofMatches(request, proof, request.retirement_intent?.phase !== "pre_effect_rejected");
|
|
2486
|
+
if (
|
|
2487
|
+
request.retirement_intent?.phase !== "pre_effect_rejected" &&
|
|
2488
|
+
retirementKeyDigest &&
|
|
2489
|
+
request.retirement_intent?.retirement_key_digest &&
|
|
2490
|
+
request.retirement_intent.retirement_key_digest !== retirementKeyDigest
|
|
2491
|
+
)
|
|
2492
|
+
throw new Error("idempotency_conflict");
|
|
2493
|
+
if (!request.retirement_intent) {
|
|
2494
|
+
request.retirement_intent = {
|
|
2495
|
+
phase: "intent",
|
|
2496
|
+
proof,
|
|
2497
|
+
...(retirementKeyDigest ? { retirement_key_digest: retirementKeyDigest } : {}),
|
|
2498
|
+
updated_at: new Date().toISOString(),
|
|
2499
|
+
};
|
|
2500
|
+
request.updated_at = new Date().toISOString();
|
|
2501
|
+
} else if (request.retirement_intent.phase === "pre_effect_rejected") {
|
|
2502
|
+
request.retirement_intent.phase = "intent";
|
|
2503
|
+
request.retirement_intent.proof = proof;
|
|
2504
|
+
if (retirementKeyDigest) request.retirement_intent.retirement_key_digest = retirementKeyDigest;
|
|
2505
|
+
request.retirement_intent.updated_at = new Date().toISOString();
|
|
2506
|
+
request.updated_at = request.retirement_intent.updated_at;
|
|
2507
|
+
} else if (retirementKeyDigest && !request.retirement_intent.retirement_key_digest) {
|
|
2508
|
+
request.retirement_intent.retirement_key_digest = retirementKeyDigest;
|
|
2509
|
+
request.retirement_intent.updated_at = new Date().toISOString();
|
|
2510
|
+
request.updated_at = new Date().toISOString();
|
|
2511
|
+
}
|
|
2512
|
+
return request;
|
|
2513
|
+
});
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
/** Replaces only a pre-effect retirement proof after an explicit broker rejection. */
|
|
2517
|
+
export async function replaceCreationRetirementIntent(
|
|
2518
|
+
paths: CoordinatorStatePaths,
|
|
2519
|
+
keyDigest: string,
|
|
2520
|
+
proof: CreationRetirementProofV1,
|
|
2521
|
+
retirementKeyDigest: string,
|
|
2522
|
+
): Promise<CreationRequestV1> {
|
|
2523
|
+
return await withNamespaceRegistry(paths, async registry => {
|
|
2524
|
+
const request = registry.creations[keyDigest];
|
|
2525
|
+
if (!request) throw new Error("state_corrupt");
|
|
2526
|
+
if (request.phase !== "remote_started" && request.phase !== "uncertain" && request.phase !== "retired")
|
|
2527
|
+
throw new Error("retire_not_allowed");
|
|
2528
|
+
const staged = request.retirement_intent;
|
|
2529
|
+
if (!staged || staged.phase !== "intent" || staged.broker_proof) throw new Error("state_corrupt");
|
|
2530
|
+
if (staged.retirement_key_digest !== retirementKeyDigest) throw new Error("idempotency_conflict");
|
|
2531
|
+
assertCreationRetirementProofMatches(request, proof, false);
|
|
2532
|
+
staged.phase = "pre_effect_rejected";
|
|
2533
|
+
staged.updated_at = new Date().toISOString();
|
|
2534
|
+
request.updated_at = staged.updated_at;
|
|
2535
|
+
return request;
|
|
2536
|
+
});
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
/** Records the bounded broker proof before receipt advancement or idempotency sealing. */
|
|
2540
|
+
export async function recordCreationRetirementBrokerProof(
|
|
2541
|
+
paths: CoordinatorStatePaths,
|
|
2542
|
+
keyDigest: string,
|
|
2543
|
+
proof: CreationRetirementProofV1,
|
|
2544
|
+
brokerProof: CreationRetirementBrokerProofV1,
|
|
2545
|
+
): Promise<CreationRequestV1> {
|
|
2546
|
+
return await withNamespaceRegistry(paths, async registry => {
|
|
2547
|
+
const request = registry.creations[keyDigest];
|
|
2548
|
+
if (!request) throw new Error("state_corrupt");
|
|
2549
|
+
if (request.phase !== "remote_started" && request.phase !== "uncertain" && request.phase !== "retired")
|
|
2550
|
+
throw new Error("retire_not_allowed");
|
|
2551
|
+
assertCreationRetirementProofMatches(request, proof);
|
|
2552
|
+
if (
|
|
2553
|
+
brokerProof.session_id !== proof.session_id ||
|
|
2554
|
+
brokerProof.state_root !== proof.state_root ||
|
|
2555
|
+
brokerProof.endpoint_generation !== proof.endpoint_generation ||
|
|
2556
|
+
brokerProof.endpoint_mtime_ms !== proof.endpoint_mtime_ms ||
|
|
2557
|
+
brokerProof.process_incarnation !== proof.process_incarnation ||
|
|
2558
|
+
brokerProof.host_incarnation !== proof.host_incarnation ||
|
|
2559
|
+
brokerProof.lifecycle_request_id !== proof.lifecycle_request_id ||
|
|
2560
|
+
brokerProof.remote_create_key !== proof.remote_create_key ||
|
|
2561
|
+
brokerProof.retired !== true ||
|
|
2562
|
+
brokerProof.ledger_state !== "terminal_error" ||
|
|
2563
|
+
brokerProof.index_type !== "session_closed"
|
|
2564
|
+
)
|
|
2565
|
+
throw new Error("protocol_error");
|
|
2566
|
+
const staged = request.retirement_intent;
|
|
2567
|
+
if (staged?.broker_proof) {
|
|
2568
|
+
if (canonicalJson(staged.broker_proof) !== canonicalJson(brokerProof)) throw new Error("state_corrupt");
|
|
2569
|
+
return request;
|
|
2570
|
+
}
|
|
2571
|
+
if (!staged) {
|
|
2572
|
+
request.retirement_intent = {
|
|
2573
|
+
phase: "broker_retired",
|
|
2574
|
+
proof,
|
|
2575
|
+
broker_proof: brokerProof,
|
|
2576
|
+
updated_at: new Date().toISOString(),
|
|
2577
|
+
};
|
|
2578
|
+
} else {
|
|
2579
|
+
staged.phase = "broker_retired";
|
|
2580
|
+
staged.broker_proof = brokerProof;
|
|
2581
|
+
staged.updated_at = new Date().toISOString();
|
|
2582
|
+
}
|
|
2583
|
+
request.updated_at = new Date().toISOString();
|
|
2584
|
+
return request;
|
|
2585
|
+
});
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2398
2588
|
/** Advances a creation receipt only after its WAL or projection authority exists. */
|
|
2589
|
+
export async function assertCreationRetirementIdentity(
|
|
2590
|
+
paths: CoordinatorStatePaths,
|
|
2591
|
+
keyDigest: string,
|
|
2592
|
+
proof: CreationRetirementProofV1,
|
|
2593
|
+
): Promise<CreationRequestV1> {
|
|
2594
|
+
return await withNamespaceRegistry(paths, async registry => {
|
|
2595
|
+
const request = registry.creations[keyDigest];
|
|
2596
|
+
if (!request) throw new Error("state_corrupt");
|
|
2597
|
+
if (request.phase !== "remote_started" && request.phase !== "uncertain" && request.phase !== "retired")
|
|
2598
|
+
throw new Error("retire_not_allowed");
|
|
2599
|
+
assertCreationRetirementProofMatches(request, proof, request.retirement_intent?.phase !== "pre_effect_rejected");
|
|
2600
|
+
return request;
|
|
2601
|
+
});
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2399
2604
|
export async function advanceCreationReceipt(
|
|
2400
2605
|
paths: CoordinatorStatePaths,
|
|
2401
2606
|
keyDigest: string,
|
|
2402
|
-
phase: "projected" | "completed" | "uncertain",
|
|
2607
|
+
phase: "projected" | "completed" | "uncertain" | "retired",
|
|
2403
2608
|
safeResponse?: Record<string, unknown>,
|
|
2609
|
+
proof?: CreationRetirementProofV1,
|
|
2404
2610
|
): Promise<void> {
|
|
2405
2611
|
await withNamespaceRegistry(paths, async registry => {
|
|
2406
2612
|
const request = registry.creations[keyDigest];
|
|
2407
2613
|
if (!request) throw new Error("state_corrupt");
|
|
2408
|
-
if (request
|
|
2409
|
-
if (
|
|
2614
|
+
if (proof) assertCreationRetirementProofMatches(request, proof);
|
|
2615
|
+
if (request.phase === phase) {
|
|
2616
|
+
if (phase === "retired" && safeResponse) {
|
|
2617
|
+
request.safe_response = safeResponse;
|
|
2618
|
+
if (request.retirement_intent) request.retirement_intent.updated_at = new Date().toISOString();
|
|
2619
|
+
request.updated_at = new Date().toISOString();
|
|
2620
|
+
}
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
if (
|
|
2624
|
+
phase === "retired" &&
|
|
2625
|
+
(request.phase === "completed" || request.phase === "retired" || request.phase === "claimed")
|
|
2626
|
+
)
|
|
2627
|
+
throw new Error("state_corrupt");
|
|
2628
|
+
if (
|
|
2629
|
+
phase !== "uncertain" &&
|
|
2630
|
+
phase !== "retired" &&
|
|
2631
|
+
request.phase !== "wal_committed" &&
|
|
2632
|
+
request.phase !== "projected"
|
|
2633
|
+
)
|
|
2410
2634
|
throw new Error("state_corrupt");
|
|
2411
2635
|
request.phase = phase;
|
|
2412
2636
|
request.safe_response = safeResponse;
|
|
2637
|
+
if (phase === "retired" && proof && !request.retirement_intent) {
|
|
2638
|
+
request.retirement_intent = {
|
|
2639
|
+
phase: "intent",
|
|
2640
|
+
proof,
|
|
2641
|
+
updated_at: new Date().toISOString(),
|
|
2642
|
+
};
|
|
2643
|
+
}
|
|
2413
2644
|
request.updated_at = new Date().toISOString();
|
|
2414
2645
|
});
|
|
2415
2646
|
}
|