@sema-agent/client-core 0.112.1 → 0.113.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 (178) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +36 -23
  3. package/dist/adapt/arms.js +7 -1
  4. package/dist/adapt/textStream.d.ts +2 -0
  5. package/dist/adapt/textStream.js +27 -1
  6. package/dist/adapter/activeRunSelfHeal.d.ts +7 -5
  7. package/dist/adapter/activeRunSelfHeal.js +38 -66
  8. package/dist/adapter/downstream/eventToSdkMessage.js +4 -0
  9. package/dist/adapter/downstream/terminalToSdkResult.d.ts +8 -0
  10. package/dist/adapter/downstream/terminalToSdkResult.js +36 -1
  11. package/dist/adapter/runStream.d.ts +2 -12
  12. package/dist/adapter/runStream.js +46 -19
  13. package/dist/agentSession/engineAgentRegistryRead.js +2 -1
  14. package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
  15. package/dist/attachmentOutcome.d.ts +35 -0
  16. package/dist/attachmentOutcome.js +192 -0
  17. package/dist/attachmentPathPlan.d.ts +67 -0
  18. package/dist/attachmentPathPlan.js +203 -0
  19. package/dist/attachmentRoute.d.ts +9 -1
  20. package/dist/attachmentRoute.js +23 -2
  21. package/dist/declared/supportFloorDeclared.d.ts +1 -1
  22. package/dist/declared/supportFloorDeclared.js +1 -0
  23. package/dist/deviceRebind.js +2 -3
  24. package/dist/displayUntrusted.js +126 -19
  25. package/dist/engineAddress.d.ts +1 -1
  26. package/dist/engineAddress.js +9 -6
  27. package/dist/engineErrorCodes.d.ts +3 -0
  28. package/dist/engineErrorCodes.js +4 -0
  29. package/dist/engineNoticeCodes.d.ts +34 -4
  30. package/dist/engineNoticeCodes.js +133 -6
  31. package/dist/engineUnavailable.d.ts +4 -2
  32. package/dist/engineUnavailable.js +99 -17
  33. package/dist/fleet/fleetLedger.d.ts +9 -3
  34. package/dist/fleet/fleetLedger.js +112 -16
  35. package/dist/fleet/fleetProjection.d.ts +5 -0
  36. package/dist/fleet/fleetProjection.js +9 -1
  37. package/dist/gateVocabulary.d.ts +7 -0
  38. package/dist/gateVocabulary.js +40 -14
  39. package/dist/generated/engineFactTables.d.ts +4 -2
  40. package/dist/generated/engineFactTables.js +198 -2
  41. package/dist/generated/engineNoticeTables.js +56 -8
  42. package/dist/generated/settingsSchemaFns.d.ts +7 -0
  43. package/dist/generated/settingsSchemaFns.js +11 -0
  44. package/dist/generated/settingsSchemaHash.d.ts +1 -0
  45. package/dist/generated/settingsSchemaHash.js +58 -0
  46. package/dist/generated/settingsSchemaOwnKeyRead.js +6 -1
  47. package/dist/generated/settingsSchemaTables.d.ts +3 -2
  48. package/dist/generated/settingsSchemaTables.js +14 -0
  49. package/dist/generated/settingsSchemaTightenVerdict.d.ts +1 -0
  50. package/dist/generated/settingsSchemaTightenVerdict.js +125 -0
  51. package/dist/headlessPermissionModeWire.d.ts +7 -2
  52. package/dist/headlessPermissionModeWire.js +77 -8
  53. package/dist/hitl/approvalDecisionDoor.d.ts +24 -0
  54. package/dist/hitl/approvalDecisionDoor.js +77 -0
  55. package/dist/hitl/approvalDelivery.d.ts +6 -0
  56. package/dist/hitl/approvalDelivery.js +57 -7
  57. package/dist/hitl/approvalResolution.d.ts +6 -3
  58. package/dist/hitl/approvalResolution.js +24 -2
  59. package/dist/hitl/approvalsFeed.js +1 -1
  60. package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
  61. package/dist/hitl/decisionSend.d.ts +7 -4
  62. package/dist/hitl/decisionSend.js +90 -41
  63. package/dist/hitl/frameRouter.d.ts +3 -3
  64. package/dist/hitl/frameRouter.js +5 -0
  65. package/dist/hitl/hitlHostSurface.d.ts +3 -2
  66. package/dist/hitl/hitlHostSurface.js +5 -3
  67. package/dist/hitl/legacyApprovalShapes.d.ts +142 -0
  68. package/dist/hitl/legacyApprovalShapes.js +1 -0
  69. package/dist/hitl/livePendingAsk.d.ts +4 -4
  70. package/dist/hitl/livePendingAsk.js +5 -3
  71. package/dist/hitl/parkResolver.js +1 -1
  72. package/dist/hitl/persistedRulesWire.js +2 -2
  73. package/dist/hitl/planReviewCard.d.ts +2 -1
  74. package/dist/hitl/planReviewCard.js +64 -1
  75. package/dist/hitl/planReviewWire.d.ts +30 -0
  76. package/dist/hitl/planReviewWire.js +204 -65
  77. package/dist/hitl/sessionPolicyWire.js +16 -7
  78. package/dist/hitl/subagentSource.d.ts +1 -0
  79. package/dist/hitl/subagentSource.js +11 -0
  80. package/dist/hitl/toolApprovalWire.d.ts +34 -10
  81. package/dist/hitl/toolApprovalWire.js +182 -33
  82. package/dist/hookMatcherVerdict.d.ts +2 -1
  83. package/dist/hookMatcherVerdict.js +3 -2
  84. package/dist/hooksReaderGenerations.d.ts +8 -0
  85. package/dist/hooksReaderGenerations.js +49 -0
  86. package/dist/hooksWireCaps.js +43 -18
  87. package/dist/hostSettingsSource.d.ts +1 -1
  88. package/dist/hostSettingsSource.js +2 -2
  89. package/dist/hostTurnSettings.d.ts +2 -2
  90. package/dist/hostTurnSettings.js +87 -47
  91. package/dist/humanTurn.d.ts +2 -1
  92. package/dist/humanTurn.js +44 -13
  93. package/dist/humanTurnRead.d.ts +7 -0
  94. package/dist/humanTurnRead.js +23 -14
  95. package/dist/humanTurnSentences.js +3 -0
  96. package/dist/i18n/messages.en.d.ts +91 -5
  97. package/dist/i18n/messages.en.js +98 -12
  98. package/dist/i18n/messages.zh.js +108 -15
  99. package/dist/index.d.ts +29 -15
  100. package/dist/index.js +17 -9
  101. package/dist/liveModelCatalog.js +1 -0
  102. package/dist/liveQuestionStore.d.ts +2 -0
  103. package/dist/liveQuestionStore.js +7 -2
  104. package/dist/localConfig/kernel.d.ts +2 -1
  105. package/dist/localConfig/kernel.js +12 -18
  106. package/dist/localConfig/mcp/edit.js +25 -27
  107. package/dist/localConfig/mcp/servers.js +5 -8
  108. package/dist/localConfig/modelPool.d.ts +6 -1
  109. package/dist/localConfig/modelPool.js +104 -27
  110. package/dist/localConfig/modelPoolWords.d.ts +2 -0
  111. package/dist/localConfig/modelPoolWords.js +1 -0
  112. package/dist/localConfig/settingsWrite.js +1 -14
  113. package/dist/localConfig/skills.js +9 -16
  114. package/dist/localEngineSpawn.d.ts +29 -1
  115. package/dist/localEngineSpawn.js +62 -1
  116. package/dist/mcpLiveness.d.ts +1 -1
  117. package/dist/mcpProbeWire.js +2 -2
  118. package/dist/mcpReconnect.js +2 -2
  119. package/dist/model/catalogLoader.js +3 -17
  120. package/dist/model/onboardJudgement.d.ts +8 -0
  121. package/dist/model/onboardJudgement.js +8 -3
  122. package/dist/model/onboardSteps.d.ts +19 -0
  123. package/dist/model/onboardSteps.js +76 -0
  124. package/dist/notifications.js +6 -0
  125. package/dist/ownKey.d.ts +4 -0
  126. package/dist/ownKey.js +61 -0
  127. package/dist/pathShape.d.ts +14 -0
  128. package/dist/pathShape.js +44 -0
  129. package/dist/planReviewCardFacts.d.ts +3 -0
  130. package/dist/planReviewCardFacts.js +20 -3
  131. package/dist/pluginHooksWire.d.ts +20 -8
  132. package/dist/pluginHooksWire.js +204 -86
  133. package/dist/prepareRefusal.d.ts +2 -0
  134. package/dist/prepareRefusal.js +11 -1
  135. package/dist/request/taskRequest.js +3 -1
  136. package/dist/resultFrameKeys.d.ts +1 -0
  137. package/dist/resultFrameKeys.js +3 -0
  138. package/dist/resumeRefusalCopy.js +3 -1
  139. package/dist/retryStatus.d.ts +11 -1
  140. package/dist/retryStatus.js +15 -4
  141. package/dist/runTerminal.d.ts +1 -0
  142. package/dist/runTerminal.js +15 -1
  143. package/dist/sdkWireTransit.d.ts +1 -1
  144. package/dist/seatContract.d.ts +12 -3
  145. package/dist/seatContract.js +24 -13
  146. package/dist/sessionEngine.js +1 -1
  147. package/dist/sessionHooksRoster.js +8 -6
  148. package/dist/sessionIdSegment.d.ts +0 -1
  149. package/dist/sessionIdSegment.js +1 -3
  150. package/dist/sessionMemoryErase.js +2 -2
  151. package/dist/sessionMemoryStatus.js +2 -1
  152. package/dist/sessionOccupant.d.ts +29 -0
  153. package/dist/sessionOccupant.js +78 -0
  154. package/dist/settingsHooksSalvage.d.ts +4 -1
  155. package/dist/settingsHooksSalvage.js +22 -9
  156. package/dist/skillOverrideChain.d.ts +3 -0
  157. package/dist/skillOverrideChain.js +31 -4
  158. package/dist/skillsWireCaps.d.ts +16 -2
  159. package/dist/skillsWireCaps.js +47 -20
  160. package/dist/subagent/agentStateDetail.js +32 -8
  161. package/dist/subagent/engineCompactWire.js +48 -36
  162. package/dist/subagent/engineDelegatedPrompt.d.ts +3 -2
  163. package/dist/subagent/engineDelegatedPrompt.js +65 -28
  164. package/dist/subagentEditOutcomes.d.ts +9 -0
  165. package/dist/subagentEditOutcomes.js +43 -0
  166. package/dist/supportFloor.d.ts +2 -1
  167. package/dist/supportFloor.js +1 -1
  168. package/dist/toolResult.js +4 -2
  169. package/dist/toolRoster.d.ts +1 -0
  170. package/dist/toolRoster.js +1 -0
  171. package/dist/usageAnalytics.js +2 -1
  172. package/dist/userAttachmentEnvelope.d.ts +11 -0
  173. package/dist/userAttachmentEnvelope.js +7 -2
  174. package/dist/userAttachmentSeat.d.ts +7 -1
  175. package/dist/userAttachmentSeat.js +44 -7
  176. package/dist/wiringInstructionsAttribution.js +8 -2
  177. package/docs/INTEGRATION-CLIENTS.md +1588 -16
  178. package/package.json +5 -5
@@ -8,11 +8,11 @@ import { computeDeleteBlockers, computeDeleteWarnings, resolveEntryVision } from
8
8
  import { routeKeyOf } from '../coreValuePorts.js';
9
9
  import { applyProbeToEntry } from '../modelCapabilityProbe.js';
10
10
  import { KEY_SLOT_PREFIX, keyEnvNameForEntry, onboardWriteVerdict, poolEntryNameOf, onboardBaseUrlProblem, onboardEntryCredentialIssue, onboardKeyCarriedBy, onboardRedactKey, } from '../model/onboardJudgement.js';
11
- import { ownSnapshotRecord, ownSnapshotRecordFaithful, readOwnDataValue } from '../ownKey.js';
11
+ import { ownDataField, ownSnapshotRecord, ownSnapshotRecordFaithful, putOwnKey, readOwnDataValue, sameJson } from '../ownKey.js';
12
12
  import { msg, renderMessage } from '../i18n/translate.js';
13
13
  import { applyLocalConfigIntent, previewLocalConfigIntent } from './kernel.js';
14
14
  import { settingsWritePlan } from './settingsWrite.js';
15
- import { MODEL_POOL_CHANNEL_SOURCES, MODEL_POOL_EDIT_KINDS, MODEL_POOL_MAX_BATCH } from './modelPoolWords.js';
15
+ import { MODEL_POOL_CHANNEL_SOURCES, MODEL_POOL_EDIT_KINDS, MODEL_POOL_MAX_BATCH, MODEL_POOL_READINGS } from './modelPoolWords.js';
16
16
  export { MODEL_POOL_EDIT_KINDS, MODEL_POOL_CHANNEL_SOURCES, MODEL_POOL_CREDENTIAL_KINDS, MODEL_POOL_COMPLETENESS, MODEL_POOL_MAX_BATCH, MODEL_POOL_PROBE_STATES, MODEL_POOL_DEFAULT_KEEP_REASONS } from './modelPoolWords.js';
17
17
  const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
18
18
  function own(v, key) {
@@ -30,19 +30,6 @@ function cloneDoc(v) {
30
30
  return snap === undefined ? undefined : JSON.parse(JSON.stringify(snap));
31
31
  }
32
32
  const freezeDoc = (v) => ownSnapshotRecord(v) ?? Object.freeze({});
33
- function sameJson(a, b) {
34
- if (a === b)
35
- return true;
36
- if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
37
- return false;
38
- if (Array.isArray(a) !== Array.isArray(b))
39
- return false;
40
- if (Array.isArray(a) && Array.isArray(b))
41
- return a.length === b.length && a.every((x, i) => sameJson(x, b[i]));
42
- const ka = Object.keys(a);
43
- const kb = Object.keys(b);
44
- return ka.length === kb.length && ka.every((k) => Object.hasOwn(b, k) && sameJson(a[k], b[k]));
45
- }
46
33
  const en = (key, params) => renderMessage(msg(key, params));
47
34
  const strOf = (v) => (typeof v === 'string' && v !== '' ? v : undefined);
48
35
  const posInt = (v) => typeof v === 'number' && Number.isSafeInteger(v) && v > 0;
@@ -204,10 +191,16 @@ function shadowDisclosuresOf(settings) {
204
191
  const model = own(settings, 'model');
205
192
  return typeof model === 'string' && model.trim() !== '' ? Object.freeze([Object.freeze({ kind: 'shadowed_by', layer: 'userSettings', detail: 'model' })]) : Object.freeze([]);
206
193
  }
194
+ function shadowUnknownDisclosures() {
195
+ return Object.freeze([Object.freeze({ kind: 'shadow_unknown', layer: 'userSettings', detail: 'model' })]);
196
+ }
197
+ function shadowOf(settings, reading) {
198
+ return reading === 'unreadable' ? shadowUnknownDisclosures() : shadowDisclosuresOf(settings);
199
+ }
207
200
  const noneDefault = (disclosures) => Object.freeze({ kind: 'none', disclosures });
208
201
  const unknownDefault = (disclosures) => Object.freeze({ kind: 'unknown', disclosures });
209
202
  export function effectiveDefaultModelOf(input) {
210
- const disclosures = shadowDisclosuresOf(own(input, 'userSettings'));
203
+ const disclosures = shadowOf(own(input, 'userSettings'), own(input, 'userSettingsReading'));
211
204
  try {
212
205
  const raw = own(input, 'modelsDoc');
213
206
  if (raw === null || raw === undefined)
@@ -659,7 +652,7 @@ function planUpdate(edit, modelsRaw, settingsRaw) {
659
652
  m['maxTokens'] = max;
660
653
  return okPlan(modelsRaw, doc, settingsRaw, settingsOrEmpty(settingsRaw), {});
661
654
  }
662
- function planSetDefault(name, modelsRaw, settingsRaw) {
655
+ function planSetDefault(name, modelsRaw, settingsRaw, settingsReading) {
663
656
  if (typeof name !== 'string' || name === '')
664
657
  return refuse('invalid_request');
665
658
  const mread = modelsDocOf(modelsRaw);
@@ -673,7 +666,7 @@ function planSetDefault(name, modelsRaw, settingsRaw) {
673
666
  const roles = isRecord(doc['roles']) ? doc['roles'] : {};
674
667
  roles['default'] = { model: name };
675
668
  doc['roles'] = roles;
676
- return okPlan(modelsRaw, doc, settingsRaw, settingsOrEmpty(settingsRaw), { disclosures: shadowDisclosuresOf(settingsRaw) });
669
+ return okPlan(modelsRaw, doc, settingsRaw, settingsOrEmpty(settingsRaw), { disclosures: shadowOf(settingsRaw, settingsReading) });
677
670
  }
678
671
  function planImportEnv(modelsRaw, settingsRaw, env) {
679
672
  const mread = modelsDocOf(modelsRaw);
@@ -869,7 +862,7 @@ function envOf(v) {
869
862
  }
870
863
  return out;
871
864
  }
872
- function planWith(edit, modelsDoc, settingsDoc, effectiveEnv, pins, declineDefault = false) {
865
+ function planWith(edit, modelsDoc, settingsDoc, effectiveEnv, pins, declineDefault = false, settingsReading) {
873
866
  try {
874
867
  const kind = own(edit, 'kind');
875
868
  const env = envOf(effectiveEnv);
@@ -887,7 +880,7 @@ function planWith(edit, modelsDoc, settingsDoc, effectiveEnv, pins, declineDefau
887
880
  case 'remove':
888
881
  return planRemove(own(edit, 'name'), modelsDoc, settingsDoc);
889
882
  case 'setDefault':
890
- return planSetDefault(own(edit, 'name'), modelsDoc, settingsDoc);
883
+ return planSetDefault(own(edit, 'name'), modelsDoc, settingsDoc, settingsReading);
891
884
  case 'importEnvModels':
892
885
  return planImportEnv(modelsDoc, settingsDoc, env);
893
886
  case 'bindTier':
@@ -937,7 +930,7 @@ export function modelPoolIntentOf(edit, effectiveEnv, probe) {
937
930
  const settings = docFrom(input.fresh.userSettings);
938
931
  if (models === 'unreadable' || (settings === 'unreadable' && needsSettings))
939
932
  return undefined;
940
- return { models, settings: settings === 'unreadable' ? null : settings };
933
+ return { models, settings: settings === 'unreadable' ? null : settings, settingsReading: settings === 'unreadable' ? 'unreadable' : undefined };
941
934
  };
942
935
  const modelsStep = (role, withPins) => ({
943
936
  doc: 'models',
@@ -952,7 +945,7 @@ export function modelPoolIntentOf(edit, effectiveEnv, probe) {
952
945
  const pins = pinsRaw === undefined ? undefined : pinsOf(pinsRaw);
953
946
  if (pinsRaw !== undefined && pins === undefined)
954
947
  return { kind: 'refuse', reason: 'invalid_request' };
955
- const p = record(planWith(edit, r.models, r.settings, effectiveEnv, pins));
948
+ const p = record(planWith(edit, r.models, r.settings, effectiveEnv, pins, false, r.settingsReading));
956
949
  if (!p.ok)
957
950
  return refuseStep(p);
958
951
  const carry = { released: [...p.releasedKeySlots] };
@@ -1327,11 +1320,91 @@ function providersOf() {
1327
1320
  });
1328
1321
  }));
1329
1322
  }
1323
+ function envHasDroppedKeys(given, snap) {
1324
+ try {
1325
+ if (typeof given !== 'object' || given === null)
1326
+ return true;
1327
+ for (const k of Reflect.ownKeys(given)) {
1328
+ if (typeof k !== 'string' || Object.hasOwn(snap, k))
1329
+ continue;
1330
+ const d = Object.getOwnPropertyDescriptor(given, k);
1331
+ if (d === undefined || d.enumerable !== true || !Object.hasOwn(d, 'value') || d.value !== undefined)
1332
+ return true;
1333
+ }
1334
+ return false;
1335
+ }
1336
+ catch {
1337
+ return true;
1338
+ }
1339
+ }
1340
+ function viewEnvOf(readings) {
1341
+ let given;
1342
+ try {
1343
+ given = typeof readings === 'object' && readings !== null ? ownDataField(readings, 'effectiveEnv') : undefined;
1344
+ }
1345
+ catch {
1346
+ return { reading: 'unreadable', env: {} };
1347
+ }
1348
+ if (given === undefined)
1349
+ return { reading: 'absent', env: {} };
1350
+ const snap = ownSnapshotRecordFaithful(given);
1351
+ if (snap === undefined || envHasDroppedKeys(given, snap))
1352
+ return { reading: 'unreadable', env: {} };
1353
+ const env = {};
1354
+ for (const k of Object.keys(snap)) {
1355
+ const val = snap[k];
1356
+ if (typeof val !== 'string')
1357
+ return { reading: 'unreadable', env: {} };
1358
+ putOwnKey(env, k, val);
1359
+ }
1360
+ return { reading: 'read', env };
1361
+ }
1362
+ function viewSettingsOf(readings) {
1363
+ try {
1364
+ const sr = typeof readings === 'object' && readings !== null ? ownDataField(readings, 'userSettings') : undefined;
1365
+ if (sr === undefined)
1366
+ return { reading: 'absent', raw: null };
1367
+ if (typeof sr !== 'object' || sr === null)
1368
+ return { reading: 'unreadable', raw: null };
1369
+ const status = ownDataField(sr, 'status');
1370
+ if (status === 'absent')
1371
+ return { reading: 'absent', raw: null };
1372
+ if (status !== 'loaded')
1373
+ return { reading: 'unreadable', raw: null };
1374
+ const doc = ownDataField(sr, 'doc');
1375
+ if (typeof doc !== 'object' || doc === null || Array.isArray(doc))
1376
+ return { reading: 'unreadable', raw: null };
1377
+ const checked = settingsDocOf(doc);
1378
+ if (!checked.ok)
1379
+ return { reading: 'unreadable', raw: null };
1380
+ return { reading: 'read', raw: checked.doc };
1381
+ }
1382
+ catch {
1383
+ return { reading: 'unreadable', raw: null };
1384
+ }
1385
+ }
1386
+ export function modelPoolEnvReadingOf(view) {
1387
+ if (typeof view !== 'object' || view === null)
1388
+ return 'unreadable';
1389
+ try {
1390
+ const d = Object.getOwnPropertyDescriptor(view, 'effectiveEnv');
1391
+ if (d === undefined)
1392
+ return 'not_reported';
1393
+ if (!Object.hasOwn(d, 'value'))
1394
+ return 'unreadable';
1395
+ const word = d.value;
1396
+ if (word === undefined)
1397
+ return 'not_reported';
1398
+ return MODEL_POOL_READINGS.find((w) => w === word) ?? 'unreadable';
1399
+ }
1400
+ catch {
1401
+ return 'unreadable';
1402
+ }
1403
+ }
1330
1404
  export function modelPoolViewOf(readings) {
1331
- const env = envOf(own(readings, 'effectiveEnv'));
1405
+ const { reading: envReading, env } = viewEnvOf(readings);
1332
1406
  const mr = own(readings, 'models');
1333
- const sr = own(readings, 'userSettings');
1334
- const settingsRaw = own(sr, 'status') === 'loaded' ? own(sr, 'doc') : null;
1407
+ const { reading: settingsReading, raw: settingsRaw } = viewSettingsOf(readings);
1335
1408
  const settings = settingsDocOf(settingsRaw);
1336
1409
  const sdoc = settings.ok ? settings.doc : {};
1337
1410
  const legacyTierSettings = sdoc['semaTierGroups'] !== undefined || sdoc['semaRosters'] !== undefined;
@@ -1344,10 +1417,12 @@ export function modelPoolViewOf(readings) {
1344
1417
  entries: Object.freeze([]),
1345
1418
  roles: Object.freeze([]),
1346
1419
  tierGroups: Object.freeze([]),
1347
- defaultModel: catalog === 'absent' ? effectiveDefaultModelOf({ modelsDoc: null, userSettings: settingsRaw }) : unknownDefault(shadowDisclosuresOf(settingsRaw)),
1420
+ defaultModel: catalog === 'absent' ? effectiveDefaultModelOf({ modelsDoc: null, userSettings: settingsRaw, userSettingsReading: settingsReading }) : unknownDefault(shadowOf(settingsRaw, settingsReading)),
1348
1421
  legacyTierSettings,
1349
1422
  orphanKeySlots: catalog === 'absent' ? orphanKeySlotsOf([], settingsEnv).length : 0,
1350
1423
  envModels: Object.freeze(catalog === 'unreadable' ? [] : [env['MODEL_ID'], env['MODEL_CHEAP_ID']].filter((v) => typeof v === 'string' && v.trim() !== '')),
1424
+ effectiveEnv: envReading,
1425
+ userSettings: settingsReading,
1351
1426
  providers: providersOf(),
1352
1427
  });
1353
1428
  if (status === 'absent')
@@ -1363,7 +1438,7 @@ export function modelPoolViewOf(readings) {
1363
1438
  const items = Array.isArray(list) ? list : [];
1364
1439
  const { rows: good, unreadable } = catalogRowsOf(items);
1365
1440
  const doc = { ...raw, models: good };
1366
- const def = effectiveDefaultModelOf({ modelsDoc: raw, userSettings: settingsRaw });
1441
+ const def = effectiveDefaultModelOf({ modelsDoc: raw, userSettings: settingsRaw, userSettingsReading: settingsReading });
1367
1442
  const blockDoc = withDefaultFolded(doc);
1368
1443
  const groups = tierGroupsOf(raw).filter((g) => typeof g['name'] === 'string');
1369
1444
  const active = typeof raw['activeTierGroup'] === 'string' ? raw['activeTierGroup'] : undefined;
@@ -1452,6 +1527,8 @@ export function modelPoolViewOf(readings) {
1452
1527
  legacyTierSettings,
1453
1528
  orphanKeySlots: items.every(isRecord) ? orphanKeySlotsOf(items, settingsEnv).length : 0,
1454
1529
  envModels: Object.freeze(good.some(isEnabled) ? [] : [env['MODEL_ID'], env['MODEL_CHEAP_ID']].filter((v) => typeof v === 'string' && v.trim() !== '')),
1530
+ effectiveEnv: envReading,
1531
+ userSettings: settingsReading,
1455
1532
  providers: providersOf(),
1456
1533
  });
1457
1534
  }
@@ -11,3 +11,5 @@ export declare const MODEL_POOL_DEFAULT_KEEP_REASONS: readonly ["has_default", "
11
11
  export type ModelPoolDefaultKeepReason = (typeof MODEL_POOL_DEFAULT_KEEP_REASONS)[number];
12
12
  export declare const MODEL_POOL_COMPLETENESS: readonly ["complete", "catalog_incomplete"];
13
13
  export type ModelPoolCompleteness = (typeof MODEL_POOL_COMPLETENESS)[number];
14
+ export declare const MODEL_POOL_READINGS: readonly ["read", "absent", "unreadable"];
15
+ export type ModelPoolViewReading = (typeof MODEL_POOL_READINGS)[number];
@@ -18,3 +18,4 @@ export const MODEL_POOL_MAX_BATCH = 64;
18
18
  export const MODEL_POOL_PROBE_STATES = Object.freeze(['verified', 'unverified', 'unprobed']);
19
19
  export const MODEL_POOL_DEFAULT_KEEP_REASONS = Object.freeze(['has_default', 'existing_unknown', 'declined', 'nothing_added', 'none_verified']);
20
20
  export const MODEL_POOL_COMPLETENESS = Object.freeze(['complete', 'catalog_incomplete']);
21
+ export const MODEL_POOL_READINGS = Object.freeze(['read', 'absent', 'unreadable']);
@@ -2,7 +2,7 @@ import { SETTINGS_UNIT_PLACES, SETTINGS_WORKSPACE_TRUST_STATES } from '../genera
2
2
  import { SETTINGS_IS_RESERVED_MEMBER_NAME } from '../generated/settingsSchemaFns.js';
3
3
  import { layerAdmissionWord } from '../settingsLayerAdmission.js';
4
4
  import { KEY_SLOT_PREFIX } from '../model/onboardJudgement.js';
5
- import { ownSnapshotRecord, readOwnDataValue } from '../ownKey.js';
5
+ import { ownSnapshotRecord, readOwnDataValue, sameJson } from '../ownKey.js';
6
6
  import { SKILL_OVERRIDE_STATES } from '../skillOverrideChain.js';
7
7
  const unitKey = (unit) => SETTINGS_UNIT_PLACES[unit].path;
8
8
  const SKILL_OVERRIDE_WORDS = SKILL_OVERRIDE_STATES;
@@ -93,19 +93,6 @@ function listWith(current, name, present) {
93
93
  return list.filter((x) => x !== name);
94
94
  return list.includes(name) ? list : [...list, name];
95
95
  }
96
- function sameJson(a, b) {
97
- if (a === b)
98
- return true;
99
- if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
100
- return false;
101
- if (Array.isArray(a) !== Array.isArray(b))
102
- return false;
103
- if (Array.isArray(a) && Array.isArray(b))
104
- return a.length === b.length && a.every((x, i) => sameJson(x, b[i]));
105
- const ka = Object.keys(a);
106
- const kb = Object.keys(b);
107
- return ka.length === kb.length && ka.every((k) => Object.hasOwn(b, k) && sameJson(a[k], b[k]));
108
- }
109
96
  export function settingsWritePlan(input) {
110
97
  try {
111
98
  if (!isPlainRecord(input))
@@ -1,8 +1,8 @@
1
1
  import { SETTINGS_IS_RESERVED_MEMBER_NAME } from '../generated/settingsSchemaFns.js';
2
2
  import { readOwnDataValue, appendOwnElement } from '../ownKey.js';
3
- import { SKILL_OVERRIDE_STATES, effectiveOfShaped, lockOfShaped, lowerTierOf, ownValueIn, secondaryNamesOf, bareNameOf, shapeSkillLayers, shapeSkillSubject, stateAt, withLayerDoc, } from '../skillOverrideChain.js';
3
+ import { LIST_BUDGET, SKILL_OVERRIDE_STATES, effectiveOfShaped, lockOfShaped, lowerTierOf, ownValueIn, secondaryNamesOf, bareNameOf, shapeSkillLayers, shapeSkillSubject, stateAt, withLayerDoc, } from '../skillOverrideChain.js';
4
4
  import { settingsWritePlan } from './settingsWrite.js';
5
- import { isDotSegment } from '../sessionIdSegment.js';
5
+ import { pathShapeOf, PATH_LENGTH_LIMIT } from '../pathShape.js';
6
6
  import { slotReadOf } from '../wireFailureShape.js';
7
7
  import { SKILL_ROOT_LAYERS, SKILL_ROOT_KINDS, SKILL_OVERRIDE_SCOPES } from './skillsWords.js';
8
8
  function own(v, key) {
@@ -16,7 +16,6 @@ function own(v, key) {
16
16
  }
17
17
  }
18
18
  const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
19
- const LIST_BUDGET = 4096;
20
19
  function listOf(v) {
21
20
  if (!Array.isArray(v))
22
21
  return [];
@@ -30,24 +29,18 @@ function listOf(v) {
30
29
  }
31
30
  const TRUST_LAYERS = ['project', 'additional'];
32
31
  function pathFormOf(raw) {
33
- if (typeof raw !== 'string' || raw === '' || raw.length > 4096 || raw.includes('\u0000'))
32
+ if (typeof raw !== 'string' || raw.length > PATH_LENGTH_LIMIT)
34
33
  return undefined;
35
- const sep = raw.includes('\\') && !raw.includes('/') ? '\\' : '/';
36
- let floor;
37
- if (raw.startsWith('/'))
38
- floor = '/';
39
- else if (/^[A-Za-z]:[\\/]/.test(raw))
40
- floor = raw.slice(0, 3);
41
- else if (/^\\\\[^\\/]+[\\/][^\\/]+/.test(raw))
42
- floor = raw.match(/^\\\\[^\\/]+[\\/][^\\/]+/)[0];
43
- else
34
+ const shape = pathShapeOf(raw);
35
+ if (shape === undefined || shape.kind === 'relative' || shape.hasDotSegment || shape.hasEmptySegment)
36
+ return undefined;
37
+ if (shape.kind === 'unc' && shape.names.length > 0)
44
38
  return undefined;
39
+ const sep = raw.includes('\\') && !raw.includes('/') ? '\\' : '/';
40
+ const floor = shape.root;
45
41
  let path = raw;
46
42
  while (path.length > floor.length && (path.endsWith('/') || path.endsWith('\\')))
47
43
  path = path.slice(0, -1);
48
- const rest = path.slice(floor.length);
49
- if (rest !== '' && rest.split(/[\\/]/).some((seg) => seg === '' || isDotSegment(seg)))
50
- return undefined;
51
44
  return { sep, path, floor };
52
45
  }
53
46
  const joinPath = (f, ...segs) => (f.path === f.floor && /[\\/]$/.test(f.floor) ? f.path + segs.join(f.sep) : [f.path, ...segs].join(f.sep));
@@ -13,6 +13,9 @@ export type LocalEngineSpawnValue = {
13
13
  } | {
14
14
  readonly kind: 'derived';
15
15
  readonly from: LocalEngineSpawnDerived;
16
+ } | {
17
+ readonly kind: 'gated';
18
+ readonly value: string;
16
19
  };
17
20
  export interface LocalEngineSpawnEnvRow {
18
21
  readonly key: string;
@@ -25,6 +28,28 @@ export declare const LOCAL_ENGINE_SPAWN_SHAPE: Readonly<{
25
28
  readonly detachedSession: true;
26
29
  readonly stdio: "engine-log-file";
27
30
  }>;
31
+ export declare const LOCAL_ENGINE_SEED_REFUSALS: readonly ["policy-unreadable", "bind-not-acknowledged"];
32
+ export type LocalEngineSeedRefusal = (typeof LOCAL_ENGINE_SEED_REFUSALS)[number];
33
+ export type LocalEngineBindRefusal = Extract<LocalEngineSeedRefusal, 'bind-not-acknowledged'>;
34
+ export declare const LOCAL_ENGINE_BIND_DISCLOSURES: readonly ["bind-acknowledged"];
35
+ export type LocalEngineBindDisclosure = (typeof LOCAL_ENGINE_BIND_DISCLOSURES)[number];
36
+ export declare function localEngineSeedRefusalCodeOf(refusal: string): string | undefined;
37
+ export interface LocalEngineBindGateInput {
38
+ readonly requested?: string | undefined;
39
+ readonly acknowledged?: boolean | undefined;
40
+ }
41
+ export interface LocalEngineBindPass {
42
+ readonly kind: 'pass';
43
+ readonly handed: string | undefined;
44
+ readonly disclose: boolean;
45
+ }
46
+ export interface LocalEngineBindRefuse {
47
+ readonly kind: 'refuse';
48
+ readonly reason: LocalEngineBindRefusal;
49
+ }
50
+ export type LocalEngineBindGate = LocalEngineBindPass | LocalEngineBindRefuse;
51
+ export declare function localEngineBindGateOf(input: LocalEngineBindGateInput): LocalEngineBindGate;
52
+ export declare function localEngineBindSentence(gate: LocalEngineBindGate, switchKey: string, lang?: UiLanguage): string | undefined;
28
53
  export declare function localEngineOperatorSingleUserOf(inherited: unknown): 'true' | undefined;
29
54
  export interface LocalEngineBudgets {
30
55
  readonly storeLockWaitMs: number;
@@ -41,6 +66,7 @@ export interface LocalEngineSpawnEnvInput {
41
66
  readonly inherited: unknown;
42
67
  readonly paths: LocalEngineSpawnPaths;
43
68
  readonly hostBits: unknown;
69
+ readonly bind?: LocalEngineBindGate | undefined;
44
70
  }
45
71
  export interface LocalEngineSpawnApplied {
46
72
  readonly key: string;
@@ -72,6 +98,7 @@ export interface LocalEngineSeedInput {
72
98
  readonly layers: LocalEngineSettingsLayers;
73
99
  readonly topLevel: unknown;
74
100
  readonly platform: 'posix' | 'win32';
101
+ readonly bindAcknowledged?: boolean | undefined;
75
102
  }
76
103
  export interface LocalEngineSeedFrom {
77
104
  readonly key: string;
@@ -85,7 +112,8 @@ export interface LocalEngineSeed {
85
112
  readonly env: Readonly<Record<string, string>>;
86
113
  readonly from: readonly LocalEngineSeedFrom[];
87
114
  readonly dropped: readonly LocalEngineSeedDropped[];
88
- readonly refuse?: 'policy-unreadable';
115
+ readonly bind: LocalEngineBindGate;
116
+ readonly refuse?: LocalEngineSeedRefusal;
89
117
  }
90
118
  export declare function localEngineSeedEnv(input: LocalEngineSeedInput): LocalEngineSeed;
91
119
  export type LocalEnginePrincipal = {
@@ -67,7 +67,9 @@ function freezeRecord(rows) {
67
67
  Object.defineProperty(out, k, { value: v, enumerable: true, writable: true, configurable: true });
68
68
  return Object.freeze(out);
69
69
  }
70
+ const BIND_DEFAULT = '127.0.0.1';
70
71
  const lit = (value) => Object.freeze({ kind: 'literal', value });
72
+ const gated = (value) => Object.freeze({ kind: 'gated', value });
71
73
  const del = Object.freeze({ kind: 'delete' });
72
74
  const slot = (s) => Object.freeze({ kind: 'path', slot: s });
73
75
  const derived = (from) => Object.freeze({ kind: 'derived', from });
@@ -78,7 +80,8 @@ export const LOCAL_ENGINE_SPAWN_ENV = Object.freeze([
78
80
  row('ENGINE_AUTO_EXIT', lit('true'), true),
79
81
  row('SEMA_PARENT_PID', del, false),
80
82
  row('SEMA_ENGINE_PARENT_PID', del, false),
81
- row('BIND_HOST', lit('127.0.0.1'), true),
83
+ row('BIND_HOST', gated(BIND_DEFAULT), false),
84
+ row('BIND_HOST_INSECURE_SELF_SPAWN', del, false),
82
85
  row('DB_BACKEND', lit('local'), false),
83
86
  row('CONFIG_PROVIDER', lit('local'), false),
84
87
  row('REMOTE_EXEC', lit('host'), true),
@@ -94,6 +97,52 @@ export const LOCAL_ENGINE_SPAWN_ENV = Object.freeze([
94
97
  ]);
95
98
  export const LOCAL_ENGINE_HOST_BITS = Object.freeze(['PORT', 'SERVICE_AUTH_TOKENS', 'ELECTRON_RUN_AS_NODE', 'NODE_ENV', 'HOST_SHELL_PATH']);
96
99
  export const LOCAL_ENGINE_SPAWN_SHAPE = Object.freeze({ detachedSession: true, stdio: 'engine-log-file' });
100
+ export const LOCAL_ENGINE_SEED_REFUSALS = Object.freeze(['policy-unreadable', 'bind-not-acknowledged']);
101
+ export const LOCAL_ENGINE_BIND_DISCLOSURES = Object.freeze(['bind-acknowledged']);
102
+ export function localEngineSeedRefusalCodeOf(refusal) {
103
+ return LOCAL_ENGINE_SEED_REFUSALS.includes(refusal) ? refusal.replace(/-/g, '_') : undefined;
104
+ }
105
+ export function localEngineBindGateOf(input) {
106
+ const raw = own(input, 'requested');
107
+ const requested = typeof raw === 'string' ? raw.trim() : '';
108
+ if (requested === '' || requested === BIND_DEFAULT)
109
+ return Object.freeze({ kind: 'pass', handed: undefined, disclose: false });
110
+ if (own(input, 'acknowledged') === true)
111
+ return Object.freeze({ kind: 'pass', handed: requested, disclose: true });
112
+ return Object.freeze({ kind: 'refuse', reason: 'bind-not-acknowledged' });
113
+ }
114
+ function bindSentenceKeyOf(word) {
115
+ switch (word) {
116
+ case 'bind-not-acknowledged':
117
+ return 'localEngine.bindNotAcknowledged';
118
+ case 'bind-acknowledged':
119
+ return 'localEngine.bindAcknowledged';
120
+ }
121
+ }
122
+ export function localEngineBindSentence(gate, switchKey, lang) {
123
+ try {
124
+ const kind = own(gate, 'kind');
125
+ if (kind === 'refuse') {
126
+ if (own(gate, 'reason') !== 'bind-not-acknowledged')
127
+ return undefined;
128
+ if (typeof switchKey !== 'string' || !/^[^\s\p{C}]{1,128}$/u.test(switchKey))
129
+ return undefined;
130
+ return renderMessage(msg(bindSentenceKeyOf('bind-not-acknowledged'), { switchKey }), lang);
131
+ }
132
+ if (kind === 'pass' && own(gate, 'disclose') === true)
133
+ return renderMessage(msg(bindSentenceKeyOf(LOCAL_ENGINE_BIND_DISCLOSURES[0])), lang);
134
+ return undefined;
135
+ }
136
+ catch {
137
+ return undefined;
138
+ }
139
+ }
140
+ function bindHandedOf(verdict) {
141
+ if (own(verdict, 'kind') !== 'pass')
142
+ return undefined;
143
+ const handed = own(verdict, 'handed');
144
+ return typeof handed === 'string' && handed !== '' && handed === handed.trim() ? handed : undefined;
145
+ }
97
146
  const BOOL_TRUE = new Set(['true', '1', 'yes', 'on']);
98
147
  const OPERATOR_DECLARATION_KEYS = Object.freeze({ requirePrincipal: 'REQUIRE_PRINCIPAL', principals: 'OPERATOR_PRINCIPALS', singleUser: 'OPERATOR_SINGLE_USER' });
99
148
  export function localEngineOperatorSingleUserOf(inherited) {
@@ -152,6 +201,7 @@ export function localEngineSpawnEnv(input) {
152
201
  for (const [k, v] of envEntriesOf(own(input, 'inherited')) ?? [])
153
202
  env.set(k, v);
154
203
  const paths = own(input, 'paths');
204
+ const bindHanded = bindHandedOf(own(input, 'bind'));
155
205
  for (const r of LOCAL_ENGINE_SPAWN_ENV) {
156
206
  const inheritedValue = env.get(r.key);
157
207
  if (r.value.kind === 'delete') {
@@ -169,6 +219,11 @@ export function localEngineSpawnEnv(input) {
169
219
  let from = 'table';
170
220
  if (r.value.kind === 'literal')
171
221
  value = r.value.value;
222
+ else if (r.value.kind === 'gated') {
223
+ value = bindHanded ?? r.value.value;
224
+ if (bindHanded !== undefined)
225
+ from = 'user';
226
+ }
172
227
  else if (r.value.kind === 'path') {
173
228
  const p = own(paths, r.value.slot);
174
229
  value = typeof p === 'string' && p !== '' ? p : undefined;
@@ -311,11 +366,17 @@ export function localEngineSeedEnv(input) {
311
366
  dropped.push(Object.freeze({ key: 'WORKFLOW_SIZE_GUIDELINE', why: 'top-level-invalid' }));
312
367
  }
313
368
  }
369
+ const bindKey = canon('BIND_HOST');
370
+ const bind = localEngineBindGateOf({ requested: merged.get(bindKey)?.value, acknowledged: own(input, 'bindAcknowledged') === true });
371
+ merged.delete(bindKey);
372
+ if (refuse === undefined && bind.kind === 'refuse')
373
+ refuse = bind.reason;
314
374
  const rows = [...merged.values()];
315
375
  return Object.freeze({
316
376
  env: freezeRecord(rows.map((r) => [r.key, r.value])),
317
377
  from: Object.freeze(rows.map((r) => Object.freeze({ key: r.key, source: r.source }))),
318
378
  dropped: Object.freeze(dropped),
379
+ bind,
319
380
  ...(refuse !== undefined ? { refuse } : {}),
320
381
  });
321
382
  }
@@ -29,7 +29,7 @@ export interface McpEngineLegHealth {
29
29
  }
30
30
  export declare function mcpLivenessRollupOf(rows: readonly McpLivenessBearingRow[] | undefined): McpEngineLegHealth;
31
31
  export declare function mcpEngineLegHealthDetail(health: McpEngineLegHealth | undefined): string;
32
- export declare const MCP_FAILURE_KINDS: readonly ["connect_refused", "connection_failed", "connection_closed", "unauthorized", "http_status", "not_mcp_response", "spawn_failed", "timeout", "protocol", "invalid_config", "credential_unavailable", "host_refused", "unknown"];
32
+ export declare const MCP_FAILURE_KINDS: readonly ["connect_refused", "connection_failed", "connection_closed", "unauthorized", "http_status", "not_mcp_response", "spawn_failed", "timeout", "protocol", "invalid_config", "credential_unavailable", "host_refused", "revoked", "unknown"];
33
33
  export declare const MCP_AUTHORIZATION_REASONS: readonly ["rejected", "scope", "none"];
34
34
  export declare const MCP_FAILURE_UNAUTHORIZED: Extract<(typeof CORE_MCP_FAILURE_KINDS)[number], 'unauthorized'>;
35
35
  export interface McpAuthorizationView {
@@ -1,6 +1,6 @@
1
1
  import { projectMcpSection } from './adapter/downstream/eventToSdkMessage.js';
2
2
  import { wireFailureShapeOf } from './wireFailureShape.js';
3
- import { LIMIT_RATE_EXCEEDED } from './engineErrorCodes.js';
3
+ import { LIMIT_RATE_EXCEEDED, ROUTE_NOT_FOUND } from './engineErrorCodes.js';
4
4
  import { mcpProbeAvailable } from './mcpProbeCapability.js';
5
5
  import { scenarioRefusalFromError } from './scenarioRefusal.js';
6
6
  export const MCP_PROBE_REFUSAL_CAUSES = Object.freeze([
@@ -39,7 +39,7 @@ export function classifyMcpProbeFailure(e) {
39
39
  if (errorCode === undefined || errorCode === '')
40
40
  return unknownBy('no_verdict', message, retryAfterSec);
41
41
  switch (errorCode) {
42
- case 'not_found.route':
42
+ case ROUTE_NOT_FOUND:
43
43
  return refusedBy('face_absent', message);
44
44
  case 'capability.mcp_injection_required':
45
45
  return refusedBy('injection_not_honored', message);
@@ -1,5 +1,5 @@
1
1
  import { capForDisplay } from './fleetTaskDesc.js';
2
- import { STEERING_NOT_RUNNING } from './engineErrorCodes.js';
2
+ import { ROUTE_NOT_FOUND, STEERING_NOT_RUNNING } from './engineErrorCodes.js';
3
3
  import { ownDataField } from './ownKey.js';
4
4
  import { isSessionIdPathSegment } from './sessionIdSegment.js';
5
5
  import { inSlotReadOf, inValueOf, slotReadOf } from './wireFailureShape.js';
@@ -147,7 +147,7 @@ export function classifyMcpReconnectFailure(e) {
147
147
  return { kind: 'unsupported', reason: 'capability' };
148
148
  if (typeof code === 'string' && code.startsWith('feature.'))
149
149
  return { kind: 'unsupported', reason: 'feature' };
150
- if (code === 'not_found.route')
150
+ if (code === ROUTE_NOT_FOUND)
151
151
  return { kind: 'unsupported', reason: 'route' };
152
152
  if (code === 'not_found.session')
153
153
  return { kind: 'not_found' };
@@ -1,4 +1,6 @@
1
1
  import { hostEnv } from '../hostEnv.js';
2
+ import { sha256BytesHex } from '../sha256Bytes.js';
3
+ import { utf8Encode } from '../uuidV5.js';
2
4
  import { resolveModelCatalog, } from './catalog.js';
3
5
  export const DEFAULT_CATALOG_SOURCES = Object.freeze([
4
6
  'https://raw.githubusercontent.com/sema-agent/sema-model-catalog/main/dist/catalog.json',
@@ -61,18 +63,6 @@ export function catalogShaUrlFor(catalogUrl) {
61
63
  export function catalogCachePath(configHome) {
62
64
  return `${configHome.replace(/\/+$/, '')}/${CATALOG_CACHE_RELATIVE_PATH}`;
63
65
  }
64
- async function sha256Hex(text) {
65
- const g = globalThis;
66
- const subtle = g.crypto?.subtle;
67
- const Enc = g.TextEncoder;
68
- if (subtle === undefined || Enc === undefined)
69
- return null;
70
- const digest = await subtle.digest('SHA-256', new Enc().encode(text));
71
- let out = '';
72
- for (const b of new Uint8Array(digest))
73
- out += b.toString(16).padStart(2, '0');
74
- return out;
75
- }
76
66
  function parseShaFile(text) {
77
67
  const m = /\b([0-9a-fA-F]{64})\b/.exec(text);
78
68
  return m?.[1] === undefined ? null : m[1].toLowerCase();
@@ -202,14 +192,10 @@ export async function loadCatalogWithSources(opts) {
202
192
  let shaChecked = false;
203
193
  if (shaGot.outcome === 'ok' && shaGot.text !== undefined) {
204
194
  const expected = parseShaFile(shaGot.text);
205
- const actual = await sha256Hex(got.text);
206
195
  if (expected === null) {
207
196
  warnings.push(`catalog sha256 sidecar for ${finalUrl} is not a hex digest — payload accepted without the checksum`);
208
197
  }
209
- else if (actual === null) {
210
- warnings.push('no WebCrypto SHA-256 in this host — catalog payload accepted without the checksum');
211
- }
212
- else if (expected !== actual) {
198
+ else if (expected !== sha256BytesHex(utf8Encode(got.text))) {
213
199
  attempts.push({ url: src, outcome: 'sha-mismatch', detail: 'catalog.sha256 does not match the payload digest' });
214
200
  continue;
215
201
  }
@@ -39,6 +39,14 @@ export interface OnboardProbeResponse {
39
39
  text(): Promise<string>;
40
40
  }
41
41
  export type OnboardProbeFetch = (url: string, init: OnboardProbeRequestInit) => Promise<OnboardProbeResponse>;
42
+ export declare const ONBOARD_PROBE_BUDGETS: Readonly<{
43
+ firstTokenMs: 15000;
44
+ catalogMs: Readonly<{
45
+ 'anthropic-messages': 8000;
46
+ 'openai-completions': 10000;
47
+ }>;
48
+ discoveryMs: 10000;
49
+ }>;
42
50
  export interface OnboardProbeDeps {
43
51
  readonly fetchImpl?: OnboardProbeFetch | undefined;
44
52
  readonly timeoutMs?: number | undefined;
@@ -117,6 +117,11 @@ export function onboardBaseUrlProblem(url, lang) {
117
117
  return say('onboard.baseUrl.encoding');
118
118
  return undefined;
119
119
  }
120
+ export const ONBOARD_PROBE_BUDGETS = Object.freeze({
121
+ firstTokenMs: 15_000,
122
+ catalogMs: Object.freeze({ 'anthropic-messages': 8_000, 'openai-completions': 10_000 }),
123
+ discoveryMs: 10_000,
124
+ });
120
125
  function keyHeaders(api, key, auth) {
121
126
  const h = {};
122
127
  if (key !== undefined && key !== '') {
@@ -172,7 +177,7 @@ export async function probeModelEndpoint(target, deps) {
172
177
  return typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : undefined;
173
178
  };
174
179
  const timeoutMs = num('timeoutMs');
175
- const deadlineMs = num('firstTokenMs') ?? timeoutMs ?? 15_000;
180
+ const deadlineMs = num('firstTokenMs') ?? timeoutMs ?? ONBOARD_PROBE_BUDGETS.firstTokenMs;
176
181
  const root = baseUrl.trim();
177
182
  try {
178
183
  if (joinRequestUrl(root, 'models') === undefined)
@@ -199,7 +204,7 @@ export async function probeModelEndpoint(target, deps) {
199
204
  ...(modelId !== undefined ? { modelId } : {}),
200
205
  fetch: fetchWithKey,
201
206
  firstTokenBudgetMs: deadlineMs,
202
- catalogBudgetMs: timeoutMs ?? (api === 'anthropic-messages' ? 8000 : 10_000),
207
+ catalogBudgetMs: timeoutMs ?? ONBOARD_PROBE_BUDGETS.catalogMs[api],
203
208
  requestUrl: (path) => joinRequestUrl(root, path),
204
209
  redact: (text) => redact(text, redactKey),
205
210
  });
@@ -233,7 +238,7 @@ export async function discoverEndpointModels(target, deps) {
233
238
  if (url === undefined)
234
239
  return fail('other', en('onboard.probe.judgeAbsent'));
235
240
  const t = field(deps, 'timeoutMs');
236
- const budget = typeof t === 'number' && Number.isFinite(t) && t > 0 ? t : 10_000;
241
+ const budget = typeof t === 'number' && Number.isFinite(t) && t > 0 ? t : ONBOARD_PROBE_BUDGETS.discoveryMs;
237
242
  const headers = { ...(api === 'anthropic-messages' ? { 'anthropic-version': '2023-06-01' } : {}), ...keyHeaders(api, key, auth === 'bearer' || auth === 'x-api-key' ? auth : undefined) };
238
243
  const settled = await settleWithinDeadline(budget, async (signal) => {
239
244
  const res = await f(url, { method: 'GET', headers, redirect: 'manual', signal });