@gajae-code/coding-agent 0.15.2 → 0.15.4

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/dist/types/config/autorouting-tier-map.d.ts +0 -9
  3. package/dist/types/config/file-lock.d.ts +7 -1
  4. package/dist/types/config/keybindings.d.ts +13 -3
  5. package/dist/types/config/model-registry.d.ts +5 -0
  6. package/dist/types/config/model-resolver.d.ts +1 -0
  7. package/dist/types/config/settings-schema.d.ts +65 -1
  8. package/dist/types/coordinator/contract.d.ts +1 -1
  9. package/dist/types/coordinator-mcp/question-state.d.ts +42 -2
  10. package/dist/types/extensibility/extensions/types.d.ts +2 -0
  11. package/dist/types/gjc-runtime/deep-interview-state.d.ts +12 -0
  12. package/dist/types/gjc-runtime/workflow-placeholder.d.ts +6 -0
  13. package/dist/types/main.d.ts +1 -1
  14. package/dist/types/modes/action-registry.d.ts +1 -0
  15. package/dist/types/modes/components/hook-selector.d.ts +1 -0
  16. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +1 -1
  17. package/dist/types/modes/components/welcome.d.ts +2 -0
  18. package/dist/types/modes/controllers/input-controller.d.ts +16 -1
  19. package/dist/types/modes/controllers/runtime-mcp-command-controller.d.ts +42 -0
  20. package/dist/types/modes/interactive-mode.d.ts +7 -0
  21. package/dist/types/modes/shared/oauth-url-copy.d.ts +9 -0
  22. package/dist/types/modes/types.d.ts +3 -0
  23. package/dist/types/modes/ui-language.d.ts +2 -1
  24. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +1 -0
  25. package/dist/types/sdk/broker/session-index.d.ts +18 -0
  26. package/dist/types/sdk/bus/index.d.ts +4 -1
  27. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -1
  28. package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
  29. package/dist/types/sdk/cli/session-cli.d.ts +1 -1
  30. package/dist/types/sdk/lifecycle/service.d.ts +32 -5
  31. package/dist/types/sdk/session.d.ts +5 -0
  32. package/dist/types/session/agent-session.d.ts +4 -0
  33. package/dist/types/session/fallback-chain-controller.d.ts +8 -0
  34. package/dist/types/slash-commands/helpers/aside.d.ts +54 -0
  35. package/dist/types/tools/ask.d.ts +9 -0
  36. package/dist/types/utils/clipboard.d.ts +9 -1
  37. package/package.json +7 -7
  38. package/scripts/generate-sdk-adapter-parity-manifest.ts +5 -2
  39. package/scripts/generate-sdk-operation-inventory.ts +3 -0
  40. package/src/commands/sdk.ts +2 -2
  41. package/src/config/autorouting-tier-map.ts +0 -9
  42. package/src/config/file-lock.ts +15 -1
  43. package/src/config/keybindings.ts +13 -3
  44. package/src/config/model-registry.ts +132 -21
  45. package/src/config/model-resolver.ts +2 -1
  46. package/src/config/models-config-schema.ts +2 -2
  47. package/src/config/settings-schema.ts +66 -1
  48. package/src/config/settings.ts +1 -1
  49. package/src/coordinator/contract.ts +1 -0
  50. package/src/coordinator-mcp/question-state.ts +235 -4
  51. package/src/coordinator-mcp/server.ts +402 -2
  52. package/src/dap/client.ts +19 -8
  53. package/src/deep-interview/render-middleware.ts +6 -2
  54. package/src/extensibility/extensions/types.ts +2 -0
  55. package/src/gjc-runtime/deep-interview-stage.ts +20 -1
  56. package/src/gjc-runtime/deep-interview-state.ts +3 -1
  57. package/src/gjc-runtime/ralplan-runtime.ts +6 -1
  58. package/src/gjc-runtime/session-state-lock.ts +11 -2
  59. package/src/gjc-runtime/ultragoal-runtime.ts +5 -0
  60. package/src/gjc-runtime/workflow-placeholder.ts +33 -0
  61. package/src/internal-urls/docs-index.generated.ts +11 -9
  62. package/src/lsp/client.ts +39 -4
  63. package/src/main.ts +42 -11
  64. package/src/modes/action-registry.ts +7 -0
  65. package/src/modes/components/hook-selector.ts +2 -1
  66. package/src/modes/components/iterm-pet-transport.ts +4 -2
  67. package/src/modes/components/runtime-mcp-add-wizard.ts +38 -1
  68. package/src/modes/components/settings-selector.ts +1 -0
  69. package/src/modes/components/tool-status-header.ts +149 -41
  70. package/src/modes/components/welcome.ts +8 -0
  71. package/src/modes/controllers/extension-ui-controller.ts +1 -0
  72. package/src/modes/controllers/goal-mode-controller.ts +7 -2
  73. package/src/modes/controllers/input-controller.ts +141 -7
  74. package/src/modes/controllers/plan-mode-controller.ts +12 -4
  75. package/src/modes/controllers/runtime-mcp-command-controller.ts +164 -20
  76. package/src/modes/controllers/selector-controller.ts +11 -2
  77. package/src/modes/interactive-mode.ts +88 -1
  78. package/src/modes/shared/oauth-url-copy.ts +33 -0
  79. package/src/modes/types.ts +3 -0
  80. package/src/modes/ui-language.ts +44 -3
  81. package/src/runtime-mcp/oauth-flow.ts +19 -8
  82. package/src/runtime-mcp/transports/stdio.ts +27 -13
  83. package/src/sdk/acp/adapter.ts +29 -2
  84. package/src/sdk/broker/broker.ts +26 -6
  85. package/src/sdk/broker/lifecycle-ledger.ts +28 -1
  86. package/src/sdk/broker/lifecycle.ts +664 -5
  87. package/src/sdk/broker/session-index.ts +246 -0
  88. package/src/sdk/broker/transport.ts +1 -0
  89. package/src/sdk/bus/index.ts +135 -38
  90. package/src/sdk/bus/natives-augment.d.ts +20 -1
  91. package/src/sdk/bus/telegram-daemon-contract.ts +4 -1
  92. package/src/sdk/bus/telegram-daemon.ts +3 -0
  93. package/src/sdk/cli/session-cli.ts +34 -7
  94. package/src/sdk/lifecycle/service.ts +166 -11
  95. package/src/sdk/mcp/server.ts +2 -1
  96. package/src/sdk/protocol/operation-inventory.generated.json +40 -0
  97. package/src/sdk/protocol/operation-registry.ts +2 -1
  98. package/src/sdk/session.ts +91 -7
  99. package/src/session/agent-session.ts +473 -66
  100. package/src/session/fallback-chain-controller.ts +17 -0
  101. package/src/setup/frictionless-onboarding.ts +4 -4
  102. package/src/slash-commands/aside-command.test.ts +366 -0
  103. package/src/slash-commands/builtin-registry.ts +19 -0
  104. package/src/slash-commands/helpers/aside.ts +299 -0
  105. package/src/tools/ask-contract.ts +54 -3
  106. package/src/tools/ask.ts +54 -6
  107. 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) result.maxTokens = override.maxTokens;
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(): Model<Api>[] {
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 = this.#discoveryManager.loadCached(providerConfig, this.#cacheDbPath, expectedProvenance);
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 = await this.#peekApiKeyForProvider(provider, {
2499
- ignoreCredentiallessFallback: optionalAuth,
2500
- refreshOAuth: true,
2501
- baseUrl: providerConfig.baseUrl,
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
- firstPositiveDiscoveryNumber(
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
- if (!this.#isCredentiallessProvider(provider)) return lookup();
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,
@@ -1928,6 +1940,52 @@ export const SETTINGS_SCHEMA = {
1928
1940
  },
1929
1941
  },
1930
1942
 
1943
+ "compaction.adaptive.enabled": {
1944
+ type: "boolean",
1945
+ default: false,
1946
+ ui: {
1947
+ tab: "context",
1948
+ label: "Adaptive Compaction",
1949
+ description: "Adjust compaction frequency by context size and call rate",
1950
+ },
1951
+ },
1952
+ "compaction.adaptive.baseThresholdPercent": {
1953
+ type: "number",
1954
+ default: 85,
1955
+ ui: {
1956
+ tab: "context",
1957
+ label: "Adaptive Base Threshold %",
1958
+ description: "Base context percentage used when adaptive compaction is enabled",
1959
+ },
1960
+ },
1961
+ "compaction.adaptive.aggression": {
1962
+ type: "number",
1963
+ default: 0.15,
1964
+ ui: {
1965
+ tab: "context",
1966
+ label: "Adaptive Aggression",
1967
+ description: "How strongly call rate lowers the compaction threshold; use 0 to 1",
1968
+ },
1969
+ },
1970
+ "compaction.adaptive.turnWindow": {
1971
+ type: "number",
1972
+ default: 15,
1973
+ ui: {
1974
+ tab: "context",
1975
+ label: "Call-Rate Window",
1976
+ description: "Minutes of recent calls considered by adaptive compaction",
1977
+ },
1978
+ },
1979
+ "compaction.adaptive.minThresholdPercent": {
1980
+ type: "number",
1981
+ default: 50,
1982
+ ui: {
1983
+ tab: "context",
1984
+ label: "Adaptive Floor %",
1985
+ description: "Lowest context percentage adaptive compaction may lower the threshold to",
1986
+ },
1987
+ },
1988
+
1931
1989
  "compaction.handoffSaveToDisk": {
1932
1990
  type: "boolean",
1933
1991
  default: false,
@@ -4320,6 +4378,13 @@ export interface CompactionSettings {
4320
4378
  strategy: "context-full" | "handoff" | "off";
4321
4379
  thresholdPercent: number;
4322
4380
  thresholdTokens: number;
4381
+ adaptive: {
4382
+ enabled: boolean;
4383
+ baseThresholdPercent: number;
4384
+ aggression: number;
4385
+ turnWindow: number;
4386
+ minThresholdPercent: number;
4387
+ };
4323
4388
  reserveTokens: number;
4324
4389
  keepRecentTokens: number;
4325
4390
  handoffSaveToDisk: boolean;
@@ -1174,7 +1174,7 @@ export class Settings implements NotificationSettingsReader {
1174
1174
  for (const key of Object.keys(SETTINGS_SCHEMA) as SettingPath[]) {
1175
1175
  if (key.startsWith(`${prefix}.`)) {
1176
1176
  const suffix = key.slice(prefix.length + 1);
1177
- result[suffix] = this.get(key);
1177
+ setByPath(result, suffix.split("."), this.get(key));
1178
1178
  }
1179
1179
  }
1180
1180
  return result as unknown as GroupTypeMap[G];
@@ -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",