@sema-agent/client-core 0.110.0 → 0.112.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/CHANGELOG.md +244 -0
  2. package/README.md +59 -18
  3. package/dist/abortableSleep.d.ts +7 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/arms.js +25 -4
  6. package/dist/adapt/toolCards.d.ts +3 -1
  7. package/dist/adapt/toolCards.js +4 -2
  8. package/dist/adapter/activeRunSelfHeal.d.ts +39 -0
  9. package/dist/adapter/activeRunSelfHeal.js +171 -3
  10. package/dist/adapter/downstream/eventToSdkMessage.js +10 -10
  11. package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
  12. package/dist/adapter/downstream/terminalToSdkResult.js +11 -0
  13. package/dist/adapter/runStream.d.ts +5 -0
  14. package/dist/adapter/runStream.js +15 -3
  15. package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
  16. package/dist/attachmentRoute.d.ts +20 -0
  17. package/dist/attachmentRoute.js +39 -0
  18. package/dist/attachmentsWireCaps.js +1 -1
  19. package/dist/builtinOutputStyles.d.ts +12 -0
  20. package/dist/builtinOutputStyles.js +147 -0
  21. package/dist/checkpointRowVerdict.d.ts +3 -0
  22. package/dist/checkpointRowVerdict.js +14 -1
  23. package/dist/compactionStatus.d.ts +9 -0
  24. package/dist/compactionStatus.js +29 -5
  25. package/dist/consoleCaPin.d.ts +136 -0
  26. package/dist/consoleCaPin.js +827 -0
  27. package/dist/controlRouter.js +2 -1
  28. package/dist/coreValuePorts.d.ts +39 -1
  29. package/dist/coreValuePorts.js +10 -0
  30. package/dist/decideReceipt.d.ts +2 -1
  31. package/dist/deviceRebind.js +3 -3
  32. package/dist/displayUntrusted.d.ts +1 -0
  33. package/dist/displayUntrusted.js +6 -0
  34. package/dist/effectiveFacts.d.ts +9 -0
  35. package/dist/effectiveFacts.js +33 -0
  36. package/dist/engineAddress.d.ts +1 -0
  37. package/dist/engineAddress.js +4 -1
  38. package/dist/engineCredentialRelease.d.ts +54 -0
  39. package/dist/engineCredentialRelease.js +146 -0
  40. package/dist/engineErrorCodes.d.ts +1 -0
  41. package/dist/engineErrorCodes.js +1 -0
  42. package/dist/engineHealthProbeVerdict.d.ts +24 -0
  43. package/dist/engineHealthProbeVerdict.js +123 -0
  44. package/dist/engineNoticeCodes.js +5 -7
  45. package/dist/engineRepoint.d.ts +12 -5
  46. package/dist/engineRepoint.js +44 -19
  47. package/dist/engineRowRef.d.ts +3 -0
  48. package/dist/engineRowRef.js +11 -1
  49. package/dist/engineSwapCrossing.d.ts +57 -0
  50. package/dist/engineSwapCrossing.js +297 -0
  51. package/dist/engineUnavailable.d.ts +45 -0
  52. package/dist/engineUnavailable.js +496 -0
  53. package/dist/engineUrl.js +4 -2
  54. package/dist/eventLeg.js +3 -3
  55. package/dist/gateFailureCodes.js +2 -2
  56. package/dist/generated/sdkFactTables.d.ts +2 -1
  57. package/dist/generated/sdkFactTables.js +14 -0
  58. package/dist/generated/settingsSchemaFns.d.ts +1 -0
  59. package/dist/generated/settingsSchemaFns.js +3 -0
  60. package/dist/generated/settingsSchemaHooksClassifier.js +38 -9
  61. package/dist/generated/settingsSchemaTables.d.ts +56 -0
  62. package/dist/generated/settingsSchemaTables.js +19 -0
  63. package/dist/generated/settingsSchemaTypes.d.ts +23 -13
  64. package/dist/generated/settingsSchemaUrlShape.d.ts +1 -0
  65. package/dist/generated/settingsSchemaUrlShape.js +17 -0
  66. package/dist/hitl/approvalDelivery.d.ts +1 -0
  67. package/dist/hitl/approvalDelivery.js +1 -1
  68. package/dist/hitl/approvalResolution.d.ts +1 -1
  69. package/dist/hitl/approvalResolution.js +2 -1
  70. package/dist/hitl/askGateWire.js +5 -6
  71. package/dist/hitl/decisionRefusal.d.ts +6 -0
  72. package/dist/hitl/decisionRefusal.js +19 -10
  73. package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
  74. package/dist/hitl/decisionRefusalCarrier.js +348 -0
  75. package/dist/hitl/decisionSend.d.ts +7 -0
  76. package/dist/hitl/decisionSend.js +34 -12
  77. package/dist/hitl/editedApprovalAcceptance.js +1 -1
  78. package/dist/hitl/elicitationRefusal.d.ts +4 -0
  79. package/dist/hitl/elicitationRefusal.js +23 -0
  80. package/dist/hitl/frameRouter.d.ts +2 -2
  81. package/dist/hitl/frameRouter.js +8 -15
  82. package/dist/hitl/gateIdentity.d.ts +5 -0
  83. package/dist/hitl/gateIdentity.js +23 -0
  84. package/dist/hitl/hitlBridge.d.ts +6 -0
  85. package/dist/hitl/hitlBridge.js +10 -4
  86. package/dist/hitl/hitlHostSurface.d.ts +2 -1
  87. package/dist/hitl/hitlHostSurface.js +7 -2
  88. package/dist/hitl/localAllowRedemption.d.ts +70 -0
  89. package/dist/hitl/localAllowRedemption.js +276 -0
  90. package/dist/hitl/parkOwnership.js +2 -1
  91. package/dist/hitl/parkResolver.js +14 -4
  92. package/dist/hitl/persistedRulesWire.d.ts +16 -0
  93. package/dist/hitl/persistedRulesWire.js +70 -0
  94. package/dist/hitl/planReviewWire.d.ts +3 -0
  95. package/dist/hitl/planReviewWire.js +23 -6
  96. package/dist/hitl/policyDenyCause.d.ts +1 -1
  97. package/dist/hitl/policyDenyCause.js +1 -1
  98. package/dist/hitl/resumeRunningCard.d.ts +1 -0
  99. package/dist/hitl/resumeRunningCard.js +1 -0
  100. package/dist/hitl/screenCardCarriers.d.ts +15 -0
  101. package/dist/hitl/screenCardCarriers.js +41 -0
  102. package/dist/hitl/screenDecisionCards.d.ts +98 -0
  103. package/dist/hitl/screenDecisionCards.js +363 -0
  104. package/dist/hitl/sessionPolicyWire.js +2 -2
  105. package/dist/hitl/standingRows.d.ts +6 -2
  106. package/dist/hitl/standingRows.js +27 -2
  107. package/dist/hitl/toolApprovalWire.d.ts +30 -8
  108. package/dist/hitl/toolApprovalWire.js +264 -121
  109. package/dist/hooksReaderGenerations.js +1 -1
  110. package/dist/hooksWireCaps.js +3 -3
  111. package/dist/hostSettingNoticeArm.d.ts +3 -0
  112. package/dist/hostSettingNoticeArm.js +12 -0
  113. package/dist/hostTurnSettings.d.ts +3 -0
  114. package/dist/hostTurnSettings.js +38 -5
  115. package/dist/humanTurnRead.js +11 -1
  116. package/dist/i18n/messages.en.d.ts +292 -0
  117. package/dist/i18n/messages.en.js +298 -7
  118. package/dist/i18n/messages.zh.js +316 -6
  119. package/dist/i18n/translate.d.ts +3 -0
  120. package/dist/i18n/translate.js +78 -10
  121. package/dist/i18n/zh.d.ts +1 -0
  122. package/dist/i18n/zh.js +1 -0
  123. package/dist/index.d.ts +71 -20
  124. package/dist/index.js +44 -13
  125. package/dist/interactiveHalt.js +2 -1
  126. package/dist/interactiveInterrupt.js +20 -2
  127. package/dist/liveModelCatalog.d.ts +4 -0
  128. package/dist/liveModelCatalog.js +12 -3
  129. package/dist/localConfig/documents.d.ts +61 -0
  130. package/dist/localConfig/documents.js +51 -0
  131. package/dist/localConfig/kernel.d.ts +149 -0
  132. package/dist/localConfig/kernel.js +597 -0
  133. package/dist/localConfig/mcp/edit.d.ts +161 -0
  134. package/dist/localConfig/mcp/edit.js +846 -0
  135. package/dist/localConfig/mcp/servers.d.ts +165 -0
  136. package/dist/localConfig/mcp/servers.js +776 -0
  137. package/dist/localConfig/mcp/views.d.ts +170 -0
  138. package/dist/localConfig/mcp/views.js +429 -0
  139. package/dist/localConfig/modelPool.d.ts +262 -0
  140. package/dist/localConfig/modelPool.js +1489 -0
  141. package/dist/localConfig/modelPoolWords.d.ts +13 -0
  142. package/dist/localConfig/modelPoolWords.js +20 -0
  143. package/dist/localConfig/receiptShape.d.ts +7 -0
  144. package/dist/localConfig/receiptShape.js +135 -0
  145. package/dist/localConfig/settingsWrite.d.ts +127 -0
  146. package/dist/localConfig/settingsWrite.js +237 -0
  147. package/dist/localConfig/skills.d.ts +124 -0
  148. package/dist/localConfig/skills.js +397 -0
  149. package/dist/localConfig/skillsWords.d.ts +10 -0
  150. package/dist/localConfig/skillsWords.js +5 -0
  151. package/dist/localEngineSeatReading.d.ts +2 -2
  152. package/dist/localEngineSeatReading.js +5 -3
  153. package/dist/localEngineSpawn.d.ts +0 -7
  154. package/dist/localEngineSpawn.js +0 -92
  155. package/dist/localEngineStep.d.ts +2 -2
  156. package/dist/localEngineStep.js +2 -0
  157. package/dist/mcpProbeWire.js +2 -1
  158. package/dist/mcpReconnect.js +3 -3
  159. package/dist/mcpWireCaps.js +1 -1
  160. package/dist/memoryVerbsWire.js +3 -2
  161. package/dist/model/engineModelWords.d.ts +3 -0
  162. package/dist/model/engineModelWords.js +13 -0
  163. package/dist/model/onboardJudgement.d.ts +14 -4
  164. package/dist/model/onboardJudgement.js +91 -139
  165. package/dist/model/subagentModelWord.d.ts +21 -2
  166. package/dist/model/subagentModelWord.js +100 -29
  167. package/dist/modelCapabilityProbe.js +3 -40
  168. package/dist/modelConfigGapNotice.d.ts +20 -0
  169. package/dist/modelConfigGapNotice.js +102 -0
  170. package/dist/modsKernelIdentity.js +2 -47
  171. package/dist/notifications.d.ts +19 -0
  172. package/dist/notifications.js +114 -12
  173. package/dist/ownKey.d.ts +7 -0
  174. package/dist/ownKey.js +71 -8
  175. package/dist/pendingUserRow.d.ts +4 -0
  176. package/dist/pendingUserRow.js +16 -0
  177. package/dist/permissionsDeclaration.d.ts +3 -1
  178. package/dist/permissionsDeclaration.js +33 -9
  179. package/dist/planReviewCardFacts.d.ts +3 -0
  180. package/dist/planReviewCardFacts.js +52 -2
  181. package/dist/pluginHooksWire.d.ts +9 -1
  182. package/dist/pluginHooksWire.js +40 -6
  183. package/dist/printToolResultFrame.js +4 -2
  184. package/dist/promptBlockedCopy.js +12 -7
  185. package/dist/promptSuggestionBatch.d.ts +7 -0
  186. package/dist/promptSuggestionBatch.js +9 -0
  187. package/dist/promptSuggestionsTail.d.ts +39 -0
  188. package/dist/promptSuggestionsTail.js +116 -0
  189. package/dist/request/taskRequest.js +10 -6
  190. package/dist/requestCoordinate.d.ts +1 -0
  191. package/dist/requestCoordinate.js +13 -0
  192. package/dist/resumeRefusalCopy.d.ts +3 -1
  193. package/dist/resumeRefusalCopy.js +12 -4
  194. package/dist/rewindArchiveReading.js +4 -4
  195. package/dist/runCancelContext.d.ts +2 -1
  196. package/dist/runCancelContext.js +1 -0
  197. package/dist/runLedgerSnapshot.js +3 -3
  198. package/dist/runPermissionModeSwitch.js +3 -4
  199. package/dist/scenarioRefusal.js +11 -13
  200. package/dist/scenarioSkills.js +11 -6
  201. package/dist/seam.d.ts +16 -2
  202. package/dist/seam.js +10 -0
  203. package/dist/seatContract.d.ts +259 -9
  204. package/dist/seatContract.js +444 -15
  205. package/dist/sessionCostReading.d.ts +70 -0
  206. package/dist/sessionCostReading.js +150 -0
  207. package/dist/sessionHooksRoster.d.ts +4 -0
  208. package/dist/sessionHooksRoster.js +158 -0
  209. package/dist/sessionIdSegment.d.ts +1 -0
  210. package/dist/sessionIdSegment.js +4 -1
  211. package/dist/sessionMemoryErase.js +2 -1
  212. package/dist/sessionMemoryStatus.js +2 -2
  213. package/dist/sessionReadRedaction.d.ts +7 -0
  214. package/dist/sessionReadRedaction.js +26 -0
  215. package/dist/sessionSyncFileHistory.js +8 -9
  216. package/dist/settingsHooksSalvage.js +2 -1
  217. package/dist/sha256Bytes.d.ts +1 -0
  218. package/dist/sha256Bytes.js +46 -0
  219. package/dist/skillOverrideChain.d.ts +83 -0
  220. package/dist/skillOverrideChain.js +378 -0
  221. package/dist/skillsWireCaps.d.ts +3 -0
  222. package/dist/skillsWireCaps.js +16 -1
  223. package/dist/sseIdleTriage.js +2 -1
  224. package/dist/storeDurabilityWord.d.ts +15 -0
  225. package/dist/storeDurabilityWord.js +18 -0
  226. package/dist/storePostureCapability.d.ts +7 -3
  227. package/dist/storePostureCapability.js +69 -39
  228. package/dist/subagent/engineSubagentResume.js +2 -2
  229. package/dist/subagent/engineSubagentTail.js +2 -1
  230. package/dist/subagentContentStore.d.ts +11 -0
  231. package/dist/subagentContentStore.js +28 -2
  232. package/dist/toolResult.d.ts +1 -0
  233. package/dist/toolResult.js +2 -0
  234. package/dist/turnFailurePresentation.js +4 -10
  235. package/dist/userAttachmentEnvelope.d.ts +2 -1
  236. package/dist/userAttachmentEnvelope.js +10 -0
  237. package/dist/wireErrorTriage.js +2 -1
  238. package/dist/wireFailureShape.d.ts +4 -0
  239. package/dist/wireFailureShape.js +141 -19
  240. package/dist/wireRefusalCopy.d.ts +6 -5
  241. package/dist/wireRefusalCopy.js +49 -92
  242. package/docs/INTEGRATION-CLIENTS.md +2718 -17
  243. package/package.json +7 -3
@@ -448,6 +448,6 @@ export function mcpAssertionHeaderNotSentNotice(specs) {
448
448
  const parts = distinct.slice(0, ASSERTION_NOTICE_NAMES_SHOWN).map((x) => `"${x}"`);
449
449
  if (distinct.length > ASSERTION_NOTICE_NAMES_SHOWN)
450
450
  parts.push(`+${distinct.length - ASSERTION_NOTICE_NAMES_SHOWN} more`);
451
- return (`MCP servers that declare an assertion header (assertionHeader): ${parts.join(', ')} — server versions up to 7.147.0 accept this setting but do not send the header, ` +
451
+ return (`MCP servers that declare an assertion header (assertionHeader): ${parts.join(', ')} — server versions up to 7.149.0 accept this setting but do not send the header, ` +
452
452
  'and this client cannot tell whether the engine it uses sends it; a connector that requires the header will refuse requests from these servers');
453
453
  }
@@ -1,4 +1,5 @@
1
1
  import { wireFailureShapeOf } from './wireFailureShape.js';
2
+ import { CAPABILITY_OPERATOR_PRINCIPALS_REQUIRED, MEMORY_CONTROL_PLANE_CORRUPT } from './engineErrorCodes.js';
2
3
  import { memoryComplianceVerbsAvailable } from './memoryComplianceCapability.js';
3
4
  import { memoryOriginVerbsAvailable } from './memoryOriginCapability.js';
4
5
  import { readMemoryEraseReceipt, readMemoryOriginClearanceReceipt } from './memoryEntriesWire.js';
@@ -53,7 +54,7 @@ export function classifyMemoryFailure(e) {
53
54
  return refusedBy('unauthorized', message);
54
55
  case 'auth.operator_only':
55
56
  return refusedBy('operator_only', message);
56
- case 'capability.operator_principals_required':
57
+ case CAPABILITY_OPERATOR_PRINCIPALS_REQUIRED:
57
58
  return refusedBy('operators_unconfigured', message);
58
59
  case 'auth.service_token_required':
59
60
  return refusedBy('service_token_required', message);
@@ -94,7 +95,7 @@ export function classifyMemoryFailure(e) {
94
95
  case 'memory.target_outside_store':
95
96
  case 'memory.scope_home_not_directory':
96
97
  return unknownBy('store_form_refused', message);
97
- case 'internal.memory_control_plane_corrupt':
98
+ case MEMORY_CONTROL_PLANE_CORRUPT:
98
99
  return unknownBy('control_plane_corrupt', message);
99
100
  default:
100
101
  break;
@@ -0,0 +1,3 @@
1
+ export type EngineModelWordKind = 'tier' | 'cc-alias' | 'best' | 'default';
2
+ export declare const ENGINE_MODEL_WORDS: readonly string[];
3
+ export declare function engineModelWordKindOf(word: string): EngineModelWordKind | undefined;
@@ -0,0 +1,13 @@
1
+ import { CC_TIER_ALIASES, TIER_ORDER } from './tierVocabulary.js';
2
+ export const ENGINE_MODEL_WORDS = Object.freeze([...TIER_ORDER, ...Object.keys(CC_TIER_ALIASES), 'best', 'default']);
3
+ export function engineModelWordKindOf(word) {
4
+ if (typeof word !== 'string')
5
+ return undefined;
6
+ if (TIER_ORDER.includes(word))
7
+ return 'tier';
8
+ if (Object.hasOwn(CC_TIER_ALIASES, word))
9
+ return 'cc-alias';
10
+ if (word === 'best')
11
+ return 'best';
12
+ return word === 'default' ? 'default' : undefined;
13
+ }
@@ -1,7 +1,10 @@
1
- import type { ModelApiWord } from './providerPresets.js';
2
- import type { OnboardCatalogIssue, OnboardProbeResult, OnboardProbeVerdict, OnboardRefusal } from '../seatContract.js';
1
+ import { type ModelApiWord } from './providerPresets.js';
2
+ import type { ModelDiscoveryResult, OnboardCatalogIssue, OnboardProbeResult, OnboardProbeVerdict, OnboardRefusal } from '../seatContract.js';
3
+ import type { SettingsDocument } from '../host.js';
3
4
  import { type ModelProbeResult } from '../modelCapabilityProbe.js';
4
5
  import type { UiLanguage } from '../env/uiLanguage.js';
6
+ export declare function onboardKeyCarriedBy(text: string, key: string | undefined): boolean;
7
+ export declare function onboardRedactKey(text: string, key: string | undefined): string;
5
8
  export declare function onboardBaseUrlProblem(url: string, lang?: UiLanguage): string | undefined;
6
9
  export interface OnboardProbeTarget {
7
10
  readonly baseUrl: string;
@@ -42,6 +45,13 @@ export interface OnboardProbeDeps {
42
45
  readonly firstTokenMs?: number | undefined;
43
46
  }
44
47
  export declare function probeModelEndpoint(target: OnboardProbeTarget, deps?: OnboardProbeDeps): Promise<OnboardProbeResult>;
48
+ export interface OnboardDiscoveryTarget {
49
+ readonly baseUrl: string;
50
+ readonly api: ModelApiWord;
51
+ readonly apiKey?: string | undefined;
52
+ readonly auth?: 'bearer' | 'x-api-key' | undefined;
53
+ }
54
+ export declare function discoverEndpointModels(target: OnboardDiscoveryTarget, deps?: OnboardProbeDeps): Promise<ModelDiscoveryResult>;
45
55
  export type OnboardWriteVerdict = {
46
56
  readonly write: true;
47
57
  readonly unverified: boolean;
@@ -49,10 +59,11 @@ export type OnboardWriteVerdict = {
49
59
  readonly write: false;
50
60
  readonly refusal: 'probe_failed' | 'probe_unverified';
51
61
  };
52
- export declare function onboardWriteVerdict(verdict: OnboardProbeVerdict, acceptUnverified?: boolean): OnboardWriteVerdict;
62
+ export declare function onboardWriteVerdict(verdict: OnboardProbeVerdict | undefined, acceptUnverified?: boolean): OnboardWriteVerdict;
53
63
  export declare const KEY_SLOT_PREFIX = "SEMA_MODEL_KEY_";
54
64
  export declare function keyEnvNameForEntry(entryName: string): string;
55
65
  export declare function poolEntryNameOf(modelId: string, channel: string, taken: ReadonlySet<string>): string;
66
+ export declare function onboardEntryCredentialIssue(m: SettingsDocument, env: Readonly<Record<string, string | undefined>>): string | undefined;
56
67
  export interface OnboardPlanEntry {
57
68
  readonly modelId: string;
58
69
  readonly api: ModelApiWord;
@@ -90,5 +101,4 @@ export type OnboardWritePlan = {
90
101
  readonly refusal: Extract<OnboardRefusal, 'input' | 'catalog_unreadable' | 'credential_unpaired' | 'engine_is_remote' | 'env_catalog_present'>;
91
102
  readonly detail: string;
92
103
  };
93
- export declare function onboardWritePlan(input: OnboardWritePlanInput): OnboardWritePlan;
94
104
  export declare function onboardRefusalText(refusal: OnboardRefusal, lang?: UiLanguage): string;
@@ -1,6 +1,8 @@
1
+ import { inferFamily } from './providerPresets.js';
1
2
  import { ONBOARD_REFUSALS } from '../seatContract.js';
2
- import { joinRequestUrl, routeKeyOf } from '../coreValuePorts.js';
3
- import { applyProbeToEntry, probeModelLiveness, probeVerdictNotice } from '../modelCapabilityProbe.js';
3
+ import { adjudicateModelRoute, joinRequestUrl, routeKeyOf } from '../coreValuePorts.js';
4
+ import { settleWithinDeadline } from '../abortableSleep.js';
5
+ import { probeModelLiveness, probeVerdictNotice } from '../modelCapabilityProbe.js';
4
6
  import { ownDataField } from '../ownKey.js';
5
7
  import { msg, renderMessage } from '../i18n/translate.js';
6
8
  const field = (v, key) => {
@@ -57,6 +59,9 @@ function safeDecode(t) {
57
59
  return t;
58
60
  }
59
61
  }
62
+ export function onboardKeyCarriedBy(text, key) {
63
+ return carriesKey(text, key);
64
+ }
60
65
  function carriesKey(text, key) {
61
66
  if (key === undefined || key === '')
62
67
  return false;
@@ -69,6 +74,9 @@ function carriesKey(text, key) {
69
74
  function looksLikeSecret(val) {
70
75
  return /^(sk|ak|pk|rk)[-_]/i.test(val) || (val.length >= 20 && /[A-Za-z]/.test(val) && /[0-9]/.test(val) && /^[A-Za-z0-9._~+/=-]+$/.test(val));
71
76
  }
77
+ export function onboardRedactKey(text, key) {
78
+ return redact(text, key);
79
+ }
72
80
  function redact(text, key) {
73
81
  if (key === undefined || key === '')
74
82
  return text;
@@ -197,7 +205,71 @@ export async function probeModelEndpoint(target, deps) {
197
205
  });
198
206
  if (v.verdict === 'unreachable' && seen.redirected !== undefined)
199
207
  return out('unreachable', seen.redirected);
200
- return out(v.verdict, probeDetailOf(v));
208
+ const firstOutputMs = v.verdict === 'ok' && typeof v.waitedMs === 'number' && Number.isFinite(v.waitedMs) && v.waitedMs >= 0 ? Math.round(v.waitedMs) : undefined;
209
+ const r = out(v.verdict, probeDetailOf(v));
210
+ return firstOutputMs === undefined ? r : Object.freeze({ ...r, firstOutputMs });
211
+ }
212
+ const DISCOVERY_MAX = 512;
213
+ export async function discoverEndpointModels(target, deps) {
214
+ const apiKey = field(target, 'apiKey');
215
+ const key = typeof apiKey === 'string' && apiKey !== '' ? apiKey : undefined;
216
+ const fail = (verdict, detail) => Object.freeze({ ok: false, verdict, detail: capText(redact(detail, key?.trim() || undefined)) });
217
+ const baseUrl = field(target, 'baseUrl');
218
+ const api = field(target, 'api');
219
+ const auth = field(target, 'auth');
220
+ if (typeof baseUrl !== 'string' || (api !== 'anthropic-messages' && api !== 'openai-completions'))
221
+ return fail('other', en('onboard.probe.incomplete'));
222
+ const f = (field(deps, 'fetchImpl') ?? globalThis.fetch);
223
+ if (typeof f !== 'function')
224
+ return fail('unreachable', en('onboard.probe.noFetch'));
225
+ const root = baseUrl.trim();
226
+ let url;
227
+ try {
228
+ url = joinRequestUrl(root, 'models');
229
+ }
230
+ catch {
231
+ return fail('other', en('onboard.baseUrl.invalid'));
232
+ }
233
+ if (url === undefined)
234
+ return fail('other', en('onboard.probe.judgeAbsent'));
235
+ const t = field(deps, 'timeoutMs');
236
+ const budget = typeof t === 'number' && Number.isFinite(t) && t > 0 ? t : 10_000;
237
+ const headers = { ...(api === 'anthropic-messages' ? { 'anthropic-version': '2023-06-01' } : {}), ...keyHeaders(api, key, auth === 'bearer' || auth === 'x-api-key' ? auth : undefined) };
238
+ const settled = await settleWithinDeadline(budget, async (signal) => {
239
+ const res = await f(url, { method: 'GET', headers, redirect: 'manual', signal });
240
+ const redirected = redirectRefusal(res, url, key);
241
+ if (redirected !== undefined)
242
+ return fail('unreachable', redirected);
243
+ if (res.status === 401 || res.status === 403)
244
+ return fail('auth', en('model.pool.discover.status', { status: res.status }));
245
+ if (res.status < 200 || res.status >= 300)
246
+ return fail('other', en('model.pool.discover.status', { status: res.status }));
247
+ let body;
248
+ try {
249
+ body = JSON.parse(await res.text());
250
+ }
251
+ catch {
252
+ return fail('shape', en('model.pool.discover.notList'));
253
+ }
254
+ const data = field(body, 'data');
255
+ if (!Array.isArray(data))
256
+ return fail('shape', en('model.pool.discover.notList'));
257
+ const out = [];
258
+ for (let i = 0; i < data.length && out.length < DISCOVERY_MAX; i++) {
259
+ const id = field(field(data, String(i)), 'id');
260
+ if (typeof id !== 'string' || id.trim() === '' || id.length > 512 || carriesKey(id, key) || out.some((c) => c.id === id))
261
+ continue;
262
+ const fam = inferFamily(id);
263
+ const exact = fam !== undefined && fam.perModelCap !== undefined;
264
+ out.push(Object.freeze({ id, ...(exact ? { contextWindow: fam.contextWindow, maxTokens: fam.maxTokens } : {}) }));
265
+ }
266
+ return Object.freeze({ ok: true, candidates: Object.freeze(out) });
267
+ });
268
+ if (settled.kind === 'deadline')
269
+ return fail('unreachable', en('model.pool.discover.timeout'));
270
+ if (settled.kind === 'error')
271
+ return fail('unreachable', en('model.pool.discover.unreachable', { cause: settled.detail }));
272
+ return settled.value;
201
273
  }
202
274
  export function onboardWriteVerdict(verdict, acceptUnverified) {
203
275
  if (verdict === 'ok')
@@ -224,6 +296,9 @@ export function poolEntryNameOf(modelId, channel, taken) {
224
296
  candidate = `${name}-${channel}-${i}`;
225
297
  return candidate;
226
298
  }
299
+ export function onboardEntryCredentialIssue(m, env) {
300
+ return entryCredentialIssue(m, env);
301
+ }
227
302
  function entryCredentialIssue(m, env) {
228
303
  const name = typeof m['name'] === 'string' ? m['name'] : '?';
229
304
  if (m['sealedApiKey'] !== undefined && m['sealedApiKey'] !== null)
@@ -239,6 +314,19 @@ function entryCredentialIssue(m, env) {
239
314
  const api = typeof m['api'] === 'string' ? m['api'] : 'openai-completions';
240
315
  const rootKey = api === 'anthropic-messages' ? 'ANTHROPIC_BASE_URL' : 'MODEL_GATEWAY_BASEURL';
241
316
  const root = env[rootKey];
317
+ if (api !== 'anthropic-messages' && (typeof root !== 'string' || root.trim() === '')) {
318
+ let keyless;
319
+ try {
320
+ keyless = adjudicateModelRoute({ id: typeof m['id'] === 'string' ? m['id'] : name, baseUrl: base }, { source: 'absent' }, {});
321
+ }
322
+ catch {
323
+ keyless = undefined;
324
+ }
325
+ if (keyless === undefined)
326
+ return en('onboard.catalog.judgeAbsent', { name });
327
+ if (keyless.ok)
328
+ return undefined;
329
+ }
242
330
  let paired;
243
331
  try {
244
332
  const a = routeKeyOf(base);
@@ -262,142 +350,6 @@ function entryCredentialIssue(m, env) {
262
350
  const rootText = typeof root === 'string' && root !== '' ? `${rootKey} (${hostOf(root)})` : en('onboard.catalog.noRoot', { rootKey });
263
351
  return en('onboard.catalog.unpaired', { name, host: hostOf(base), root: rootText });
264
352
  }
265
- function migrateLegacyDefaultKey(doc) {
266
- const legacy = doc['default'];
267
- if (typeof legacy !== 'string' || legacy.length === 0)
268
- return doc;
269
- const { default: _legacy, ...rest } = doc;
270
- void _legacy;
271
- const prevRoles = rest['roles'];
272
- const roles = prevRoles !== null && typeof prevRoles === 'object' && !Array.isArray(prevRoles) ? { ...prevRoles } : {};
273
- roles['default'] = { model: legacy };
274
- return { ...rest, roles };
275
- }
276
- export function onboardWritePlan(input) {
277
- const rawKey = field(field(input, 'entry'), 'apiKey');
278
- const planKey = typeof rawKey === 'string' && rawKey !== '' ? rawKey : undefined;
279
- const say = (text) => redact(text, planKey);
280
- const refuse = (refusal, detail) => Object.freeze({ ok: false, refusal, detail: say(detail) });
281
- try {
282
- if (input.engineMode === 'cloud')
283
- return refuse('engine_is_remote', en('onboard.refusal.engineIsRemote'));
284
- const rawDoc = input.modelsDoc;
285
- if (rawDoc !== undefined && (rawDoc === null || typeof rawDoc !== 'object' || Array.isArray(rawDoc)))
286
- return refuse('catalog_unreadable', en('onboard.plan.notObject'));
287
- const doc = migrateLegacyDefaultKey(rawDoc === undefined ? {} : { ...rawDoc });
288
- const rawModels = doc['models'];
289
- if (rawModels !== undefined && !Array.isArray(rawModels))
290
- return refuse('catalog_unreadable', en('onboard.plan.modelsNotList'));
291
- const list = Array.isArray(rawModels) ? rawModels : [];
292
- if (list.some((m) => m === null || typeof m !== 'object' || Array.isArray(m)))
293
- return refuse('catalog_unreadable', en('onboard.plan.modelsItemNotObject'));
294
- const models = list.map((m) => ({ ...m }));
295
- const envModel = [input.effectiveEnv?.['MODEL_ID'], input.effectiveEnv?.['MODEL_CHEAP_ID']].find((v) => typeof v === 'string' && v.trim() !== '');
296
- if (envModel !== undefined && !models.some((m) => m['enabled'] !== false)) {
297
- return refuse('env_catalog_present', en('onboard.plan.envCatalogPresent', { model: envModel }));
298
- }
299
- const rawEnv = input.settingsEnv;
300
- if (rawEnv !== undefined && (rawEnv === null || typeof rawEnv !== 'object' || Array.isArray(rawEnv)))
301
- return refuse('catalog_unreadable', en('onboard.plan.envNotObject'));
302
- const prevEnv = rawEnv === undefined ? {} : { ...rawEnv };
303
- const e = input.entry;
304
- const isShellSlot = (ref) => typeof ref === 'string' && ref.startsWith(KEY_SLOT_PREFIX) && typeof prevEnv[ref] === 'string';
305
- const targetKey = routeKeyOf(e.baseUrl);
306
- if (targetKey === undefined)
307
- return refuse('credential_unpaired', en('onboard.catalog.judgeAbsent', { name: e.modelId }));
308
- const sameTarget = (m) => m['id'] === e.modelId && m['api'] === e.api && typeof m['baseUrl'] === 'string' && routeKeyOf(m['baseUrl']) === targetKey;
309
- const existingIdx = models.findIndex((m) => sameTarget(m) && m['enabled'] !== false && m['sealedApiKey'] === undefined && (m['apiKeyEnv'] === undefined || isShellSlot(m['apiKeyEnv'])));
310
- const taken = new Set(models.map((m) => (typeof m['name'] === 'string' ? m['name'] : '')).filter((n) => n !== ''));
311
- const name = existingIdx >= 0 ? String(models[existingIdx]['name']) : poolEntryNameOf(e.modelId, e.channel, taken);
312
- const existingRef = existingIdx >= 0 ? models[existingIdx]['apiKeyEnv'] : undefined;
313
- const sharedByOther = typeof existingRef === 'string' &&
314
- models.some((m, i) => i !== existingIdx && typeof m['apiKeyEnv'] === 'string' && m['apiKeyEnv'].toUpperCase() === existingRef.toUpperCase());
315
- let keyEnv;
316
- if (isShellSlot(existingRef) && !sharedByOther) {
317
- keyEnv = existingRef;
318
- }
319
- else {
320
- const used = new Set();
321
- for (const m of models)
322
- if (typeof m['apiKeyEnv'] === 'string')
323
- used.add(m['apiKeyEnv'].toUpperCase());
324
- for (const k of Object.keys(prevEnv))
325
- used.add(k.toUpperCase());
326
- for (const [k, v] of Object.entries(input.effectiveEnv ?? {}))
327
- if (typeof v === 'string')
328
- used.add(k.toUpperCase());
329
- const baseSlot = keyEnvNameForEntry(name);
330
- keyEnv = baseSlot;
331
- for (let i = 2; used.has(keyEnv.toUpperCase()); i++)
332
- keyEnv = `${baseSlot}_${i}`;
333
- }
334
- if (carriesKey(name, e.apiKey) || carriesKey(keyEnv, e.apiKey))
335
- return refuse('input', en('onboard.input.keyInFields'));
336
- const base = existingIdx >= 0 ? models[existingIdx] : {};
337
- let entry = {
338
- ...base,
339
- name,
340
- id: e.modelId,
341
- provider: e.api === 'anthropic-messages' ? 'anthropic' : 'gateway',
342
- api: e.api,
343
- baseUrl: e.baseUrl,
344
- contextWindow: typeof base['contextWindow'] === 'number' ? base['contextWindow'] : e.contextWindow,
345
- maxTokens: typeof base['maxTokens'] === 'number' ? base['maxTokens'] : e.maxTokens,
346
- apiKeyEnv: keyEnv,
347
- semaChannel: typeof base['semaChannel'] === 'string' ? base['semaChannel'] : e.channel,
348
- semaLabel: typeof base['semaLabel'] === 'string' ? base['semaLabel'] : e.providerName,
349
- semaProbe: input.probe.verdict,
350
- semaProbeAt: input.now,
351
- };
352
- if (input.thinking !== undefined) {
353
- const probed = applyProbeToEntry({ ...(typeof entry['reasoning'] === 'boolean' ? { reasoning: entry['reasoning'] } : {}), ...(entry['compat'] !== undefined ? { compat: entry['compat'] } : {}) }, input.thinking);
354
- const next = { ...entry };
355
- if (typeof probed.reasoning === 'boolean')
356
- next['reasoning'] = probed.reasoning;
357
- if (probed.compat !== undefined)
358
- next['compat'] = probed.compat;
359
- else
360
- delete next['compat'];
361
- entry = next;
362
- }
363
- const envView = { ...input.effectiveEnv, [keyEnv]: e.apiKey };
364
- const issue = entryCredentialIssue(entry, envView);
365
- if (issue !== undefined)
366
- return refuse('credential_unpaired', issue);
367
- if (existingIdx >= 0)
368
- models[existingIdx] = entry;
369
- else
370
- models.push(entry);
371
- const nextDoc = { ...doc, models };
372
- if (input.makeDefault) {
373
- const prevRoles = doc['roles'];
374
- const roles = prevRoles !== null && typeof prevRoles === 'object' && !Array.isArray(prevRoles) ? { ...prevRoles } : {};
375
- roles['default'] = { model: name };
376
- nextDoc['roles'] = roles;
377
- }
378
- const otherIssues = [];
379
- for (const m of models) {
380
- if (m['enabled'] === false || m['name'] === name)
381
- continue;
382
- const other = entryCredentialIssue(m, envView);
383
- if (other !== undefined) {
384
- otherIssues.push(Object.freeze({ name: typeof m['name'] === 'string' ? m['name'] : '?', modelId: typeof m['id'] === 'string' ? m['id'] : '?', issue: say(other) }));
385
- }
386
- }
387
- return Object.freeze({
388
- ok: true,
389
- entryName: name,
390
- keyEnv,
391
- updatedExisting: existingIdx >= 0,
392
- nextModelsDoc: nextDoc,
393
- nextSettingsEnv: { ...prevEnv, [keyEnv]: e.apiKey },
394
- otherIssues: Object.freeze(otherIssues),
395
- });
396
- }
397
- catch {
398
- return refuse('input', en('onboard.refusal.input'));
399
- }
400
- }
401
353
  const REFUSAL_KEYS = {
402
354
  input: 'onboard.refusal.input',
403
355
  probe_failed: 'onboard.refusal.probeFailed',
@@ -1,9 +1,9 @@
1
1
  import { type LiveModelCatalog } from '../liveModelCatalog.js';
2
2
  import type { UiLanguage } from '../env/uiLanguage.js';
3
- export type SubagentModelWordKind = 'inherit' | 'catalog' | 'tier' | 'cc-alias' | 'best' | 'model-id' | 'passthrough' | 'catalog-unknown' | 'ambiguous' | 'unbound';
3
+ export type SubagentModelWordKind = 'inherit' | 'catalog' | 'tier' | 'cc-alias' | 'best' | 'default' | 'model-id' | 'passthrough' | 'catalog-unknown' | 'ambiguous' | 'unbound';
4
4
  export type SubagentModelWordProblem = 'tiers-inert' | 'chain-unbound' | 'binding-not-in-catalog';
5
5
  export interface SubagentModelLocalEngine {
6
- readonly engine: 'started-by-this-process';
6
+ readonly engine: 'reads-this-config' | 'started-by-this-process';
7
7
  readonly tiers: Readonly<Record<string, string>> | undefined;
8
8
  readonly pool?: readonly {
9
9
  readonly name: string;
@@ -29,3 +29,22 @@ export interface SubagentModelWordReading {
29
29
  }
30
30
  export declare function resolveSubagentModelWord(input: SubagentModelWordInput | null | undefined): SubagentModelWordReading;
31
31
  export declare function subagentModelWordProblemLine(word: string, reading: SubagentModelWordReading, lang?: UiLanguage): string | undefined;
32
+ export declare function subagentModelCallRefusalReason(word: string, reading: SubagentModelWordReading, lang?: UiLanguage): string | undefined;
33
+ export type SubagentModelAdmission = {
34
+ readonly verdict: 'accepted';
35
+ readonly why: 'not_sent';
36
+ } | {
37
+ readonly verdict: 'accepted';
38
+ readonly why: 'served';
39
+ readonly model: string;
40
+ } | {
41
+ readonly verdict: 'refused';
42
+ readonly why: 'not_listed' | 'not_served' | 'disabled';
43
+ readonly model: string;
44
+ } | {
45
+ readonly verdict: 'unknown';
46
+ readonly why: 'catalog_absent' | 'catalog_unreadable' | 'catalog_incomplete' | 'reserved_word';
47
+ readonly model: string;
48
+ };
49
+ export declare function subagentModelAdmissionOf(input: SubagentModelWordInput | null | undefined): SubagentModelAdmission;
50
+ export declare function subagentModelAdmissionLine(word: string, admission: SubagentModelAdmission, lang?: UiLanguage): string | undefined;
@@ -1,8 +1,10 @@
1
1
  import { CC_TIER_ALIASES, TIER_ORDER } from './tierVocabulary.js';
2
- import { liveModelRowsOf } from '../liveModelCatalog.js';
2
+ import { ENGINE_MODEL_WORDS, engineModelWordKindOf } from './engineModelWords.js';
3
+ import { liveModelRowsOf, modelAcceptanceOf } from '../liveModelCatalog.js';
3
4
  import { quoteUntrustedForSentence } from '../displayUntrusted.js';
4
5
  import { msg, renderMessage } from '../i18n/translate.js';
5
- const STARTED_BY_THIS_PROCESS = 'started-by-this-process';
6
+ const READS_THIS_CONFIG = 'reads-this-config';
7
+ const LEGACY_LOCAL_ENGINE_WORD = 'started-by-this-process';
6
8
  function ownData(v, key) {
7
9
  if (v === null || typeof v !== 'object')
8
10
  return undefined;
@@ -46,7 +48,8 @@ function localOf(input) {
46
48
  const local = ownData(input, 'local');
47
49
  if (local === null || typeof local !== 'object')
48
50
  return undefined;
49
- return ownData(local, 'engine') === STARTED_BY_THIS_PROCESS ? local : undefined;
51
+ const engine = ownData(local, 'engine');
52
+ return engine === READS_THIS_CONFIG || engine === LEGACY_LOCAL_ENGINE_WORD ? local : undefined;
50
53
  }
51
54
  function catalogOf(input, local) {
52
55
  const live = ownData(input, 'liveCatalog');
@@ -115,14 +118,7 @@ function tiersOf(local) {
115
118
  }
116
119
  return out;
117
120
  }
118
- const BUILTIN_VOCAB = [...TIER_ORDER, ...Object.keys(CC_TIER_ALIASES), 'best'];
119
- function builtinKindOf(w) {
120
- if (TIER_ORDER.includes(w))
121
- return 'tier';
122
- if (Object.hasOwn(CC_TIER_ALIASES, w))
123
- return 'cc-alias';
124
- return w === 'best' ? 'best' : undefined;
125
- }
121
+ const builtinKindOf = engineModelWordKindOf;
126
122
  class BrokenBinding {
127
123
  }
128
124
  function landingOf(word, kind, tiers, cat) {
@@ -203,21 +199,22 @@ function resolveIn(input) {
203
199
  return { kind, model: w, landing: { name: l.name, via: l.via, source: 'local-config' }, label: `${l.name} (${label})` };
204
200
  };
205
201
  const asCatalog = (name) => ({ kind: 'catalog', model: name, label: name });
202
+ const asDefault = (w) => ({ kind: 'default', model: w, label: w });
203
+ const vocab = (w, b) => (b === 'default' ? asDefault(w) : tierClass(w, b));
206
204
  const classify = (k) => {
207
205
  if (cat?.nameSet.has(k))
208
206
  return asCatalog(k);
209
- const b = builtinKindOf(k);
210
- return tierClass(k, b ?? 'tier');
207
+ return vocab(k, builtinKindOf(k) ?? 'tier');
211
208
  };
212
209
  if (cat?.nameSet.has(word))
213
210
  return freezeReading(asCatalog(word));
214
211
  const exact = builtinKindOf(word);
215
212
  if (exact !== undefined)
216
- return freezeReading(tierClass(word, exact));
213
+ return freezeReading(vocab(word, exact));
217
214
  if (tiers?.has(word))
218
215
  return freezeReading(tierClass(word, 'tier'));
219
216
  const lower = word.toLowerCase();
220
- const space = new Set([...(cat?.names ?? []), ...BUILTIN_VOCAB, ...customTierKeys]);
217
+ const space = new Set([...(cat?.names ?? []), ...ENGINE_MODEL_WORDS, ...customTierKeys]);
221
218
  const ciMatches = [...space].filter((k) => k.toLowerCase() === lower);
222
219
  if (builtinKindOf(lower) !== undefined) {
223
220
  return freezeReading(ciMatches.length === 1 ? classify(ciMatches[0]) : { kind: 'ambiguous' });
@@ -241,21 +238,95 @@ export function resolveSubagentModelWord(input) {
241
238
  return freezeReading(word === '' ? { kind: 'inherit' } : { kind: 'catalog-unknown', model: word, label: word });
242
239
  }
243
240
  }
244
- export function subagentModelWordProblemLine(word, reading, lang) {
245
- const ref = problemRefOf(word, reading);
246
- return ref === undefined ? undefined : renderMessage(ref, lang);
247
- }
248
- function problemRefOf(word, reading) {
241
+ function wordCauseOf(reading) {
249
242
  const kind = ownData(reading, 'kind');
250
- if (kind !== 'unbound' && kind !== 'ambiguous')
251
- return undefined;
252
- const quoted = quoteUntrustedForSentence(typeof word === 'string' ? word.trim() : '', 80) ?? '""';
253
243
  if (kind === 'ambiguous')
254
- return msg('model.subagent.ambiguous', { word: quoted });
244
+ return 'ambiguous';
245
+ if (kind !== 'unbound')
246
+ return undefined;
255
247
  const problem = ownData(reading, 'problem');
256
- if (problem === 'tiers-inert')
257
- return msg('model.subagent.tiersInert', { word: quoted });
258
- if (problem === 'binding-not-in-catalog')
259
- return msg('model.subagent.bindingNotInCatalog', { word: quoted });
260
- return msg('model.subagent.chainUnbound', { word: quoted });
248
+ return problem === 'tiers-inert' || problem === 'binding-not-in-catalog' ? problem : 'chain-unbound';
249
+ }
250
+ const PROBLEM_LINE_KEYS = Object.freeze({
251
+ ambiguous: 'model.subagent.ambiguous',
252
+ 'tiers-inert': 'model.subagent.tiersInert',
253
+ 'chain-unbound': 'model.subagent.chainUnbound',
254
+ 'binding-not-in-catalog': 'model.subagent.bindingNotInCatalog',
255
+ });
256
+ const CALL_REFUSAL_KEYS = Object.freeze({
257
+ ambiguous: 'model.subagent.call.ambiguous',
258
+ 'tiers-inert': 'model.subagent.call.tiersInert',
259
+ 'chain-unbound': 'model.subagent.call.chainUnbound',
260
+ 'binding-not-in-catalog': 'model.subagent.call.bindingNotInCatalog',
261
+ });
262
+ const WORD_SHOWN_MAX = 80;
263
+ function quotedWord(word) {
264
+ return quoteUntrustedForSentence(typeof word === 'string' ? word.trim() : '', WORD_SHOWN_MAX) ?? '""';
265
+ }
266
+ export function subagentModelWordProblemLine(word, reading, lang) {
267
+ const cause = wordCauseOf(reading);
268
+ return cause === undefined ? undefined : renderMessage(msg(PROBLEM_LINE_KEYS[cause], { word: quotedWord(word) }), lang);
269
+ }
270
+ export function subagentModelCallRefusalReason(word, reading, lang) {
271
+ try {
272
+ const cause = wordCauseOf(reading);
273
+ if (cause === undefined)
274
+ return undefined;
275
+ const label = ownData(reading, 'label');
276
+ const tier = typeof label === 'string' && label.trim() !== '' ? label : word;
277
+ return renderMessage(msg(CALL_REFUSAL_KEYS[cause], { word: quotedWord(word), tier: quotedWord(tier) }), lang);
278
+ }
279
+ catch {
280
+ return undefined;
281
+ }
282
+ }
283
+ const notSent = () => Object.freeze({ verdict: 'accepted', why: 'not_sent' });
284
+ const isVocabKind = (k) => k === 'tier' || k === 'cc-alias' || k === 'best' || k === 'default';
285
+ function admissionIn(input) {
286
+ const reading = resolveIn(input);
287
+ const model = reading.model;
288
+ if (model === undefined)
289
+ return notSent();
290
+ const live = ownData(input, 'liveCatalog');
291
+ if (live === undefined || live === null)
292
+ return Object.freeze({ verdict: 'unknown', why: 'catalog_absent', model });
293
+ const a = modelAcceptanceOf(model, live);
294
+ if (a.verdict === 'accepted')
295
+ return Object.freeze({ verdict: 'accepted', why: 'served', model });
296
+ if (a.verdict === 'refused') {
297
+ if (a.why === 'not_at_mentionable')
298
+ return Object.freeze({ verdict: 'accepted', why: 'served', model });
299
+ if (a.why === 'disabled')
300
+ return Object.freeze({ verdict: 'refused', why: 'disabled', model });
301
+ return Object.freeze({ verdict: 'refused', why: isVocabKind(reading.kind) ? 'not_served' : 'not_listed', model });
302
+ }
303
+ return Object.freeze({ verdict: 'unknown', why: a.why === 'model_absent' ? 'catalog_unreadable' : a.why, model });
304
+ }
305
+ export function subagentModelAdmissionOf(input) {
306
+ try {
307
+ return admissionIn(input);
308
+ }
309
+ catch {
310
+ const w = ownData(input, 'word');
311
+ const word = typeof w === 'string' ? w.trim() : '';
312
+ return word === '' ? notSent() : Object.freeze({ verdict: 'unknown', why: 'catalog_unreadable', model: word });
313
+ }
314
+ }
315
+ const ADMISSION_LINE_KEYS = Object.freeze({
316
+ not_listed: 'model.subagent.refused.notListed',
317
+ not_served: 'model.subagent.refused.notServed',
318
+ disabled: 'model.subagent.refused.disabled',
319
+ });
320
+ export function subagentModelAdmissionLine(word, admission, lang) {
321
+ try {
322
+ if (ownData(admission, 'verdict') !== 'refused')
323
+ return undefined;
324
+ const why = ownData(admission, 'why');
325
+ if (why !== 'not_listed' && why !== 'not_served' && why !== 'disabled')
326
+ return undefined;
327
+ return renderMessage(msg(ADMISSION_LINE_KEYS[why], { word: quotedWord(word) }), lang);
328
+ }
329
+ catch {
330
+ return undefined;
331
+ }
261
332
  }
@@ -1,5 +1,5 @@
1
1
  import { displayUntrusted } from './displayUntrusted.js';
2
- import { abortableSleep } from './abortableSleep.js';
2
+ import { requestDeadline } from './abortableSleep.js';
3
3
  import { causeSlotProtected, walkCauseChain } from './engineWireFor.js';
4
4
  import { readThrownSlot } from './wireFailureShape.js';
5
5
  import { guardedRead, readOwnDataValue } from './ownKey.js';
@@ -522,45 +522,8 @@ function shownCause(e, redact) {
522
522
  const shown = displayUntrusted(masked, { max: 160 });
523
523
  return shown.trim() === '' ? undefined : shown;
524
524
  }
525
- const MAX_TIMER_MS = 2_147_483_647;
526
- function probeDeadline(ms, cancel) {
527
- const ctl = new AbortController();
528
- const stop = new AbortController();
529
- let expired = false;
530
- const gone = new Promise((_resolve, reject) => {
531
- const fire = () => reject(Object.assign(new Error('aborted'), { name: 'AbortError' }));
532
- if (ctl.signal.aborted)
533
- fire();
534
- else
535
- ctl.signal.addEventListener('abort', fire, { once: true });
536
- });
537
- gone.catch(() => undefined);
538
- void abortableSleep(Math.min(ms, MAX_TIMER_MS), stop.signal).then(() => {
539
- if (stop.signal.aborted)
540
- return;
541
- expired = true;
542
- ctl.abort();
543
- });
544
- const onCancel = () => ctl.abort();
545
- if (cancel !== undefined) {
546
- if (cancel.aborted)
547
- ctl.abort();
548
- else
549
- cancel.addEventListener('abort', onCancel, { once: true });
550
- }
551
- return {
552
- signal: ctl.signal,
553
- expired: () => expired,
554
- abort: () => {
555
- stop.abort();
556
- cancel?.removeEventListener('abort', onCancel);
557
- ctl.abort();
558
- },
559
- race: (p) => Promise.race([p, gone]),
560
- };
561
- }
562
525
  async function getCatalogText(fetchIt, url, headers, timeoutMs, cancel) {
563
- const d = probeDeadline(timeoutMs, cancel);
526
+ const d = requestDeadline(timeoutMs, cancel);
564
527
  try {
565
528
  const res = await d.race(fetchIt(url, { method: 'GET', headers, signal: d.signal }));
566
529
  let text = '';
@@ -577,7 +540,7 @@ async function getCatalogText(fetchIt, url, headers, timeoutMs, cancel) {
577
540
  }
578
541
  }
579
542
  async function postForFirstToken(fetchIt, api, url, path, headers, body, deadlineMs, cancel, now, redact) {
580
- const d = probeDeadline(deadlineMs, cancel);
543
+ const d = requestDeadline(deadlineMs, cancel);
581
544
  const startedAt = now();
582
545
  try {
583
546
  const res = await d.race(fetchIt(url, { method: 'POST', headers, body, signal: d.signal }));