@sema-agent/client-core 0.96.0 → 0.97.1

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 (145) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/README.md +29 -22
  3. package/dist/adapter/activeRunSelfHeal.d.ts +0 -2
  4. package/dist/adapter/activeRunSelfHeal.js +7 -7
  5. package/dist/adapter/downstream/terminalToSdkResult.js +6 -2
  6. package/dist/agentSession/backgroundView.js +2 -15
  7. package/dist/agentsWireCaps.d.ts +3 -1
  8. package/dist/agentsWireCaps.js +67 -39
  9. package/dist/approvalDecidedByCapability.d.ts +2 -0
  10. package/dist/approvalDecidedByCapability.js +0 -1
  11. package/dist/approvalsStreamLiveCapability.d.ts +2 -0
  12. package/dist/approvalsStreamLiveCapability.js +3 -2
  13. package/dist/attachmentSeatCapability.d.ts +2 -0
  14. package/dist/attachmentSeatCapability.js +0 -1
  15. package/dist/callerCwdCapability.d.ts +2 -0
  16. package/dist/callerCwdCapability.js +0 -1
  17. package/dist/centerWiringCapability.d.ts +2 -0
  18. package/dist/centerWiringCapability.js +4 -3
  19. package/dist/cloudConfigWireCaps.d.ts +2 -2
  20. package/dist/cloudConfigWireCaps.js +192 -56
  21. package/dist/declared/supportFloorDeclared.js +3 -3
  22. package/dist/deviceExecutorManagementCapability.d.ts +2 -0
  23. package/dist/deviceExecutorManagementCapability.js +3 -2
  24. package/dist/displayUntrusted.d.ts +3 -0
  25. package/dist/displayUntrusted.js +15 -0
  26. package/dist/engineCapReader.d.ts +8 -2
  27. package/dist/engineCapReader.js +15 -7
  28. package/dist/engineCapsCache.d.ts +11 -3
  29. package/dist/engineCapsCache.js +54 -23
  30. package/dist/engineErrorCodes.d.ts +3 -0
  31. package/dist/engineErrorCodes.js +9 -1
  32. package/dist/engineNoticeCodes.js +2 -3
  33. package/dist/engineToolDirectory.d.ts +27 -0
  34. package/dist/engineToolDirectory.js +178 -0
  35. package/dist/executionLaneCapability.d.ts +2 -0
  36. package/dist/executionLaneCapability.js +3 -2
  37. package/dist/fileHistoryCaptureCapability.d.ts +2 -0
  38. package/dist/fileHistoryCaptureCapability.js +34 -8
  39. package/dist/fileHistoryLegNotice.js +4 -0
  40. package/dist/fleet/fleetLedger.js +129 -105
  41. package/dist/gateOutcome.js +2 -4
  42. package/dist/gateVocabulary.d.ts +1 -6
  43. package/dist/gateVocabulary.js +2 -5
  44. package/dist/generated/sdkFactTables.d.ts +2 -0
  45. package/dist/generated/sdkFactTables.js +7 -0
  46. package/dist/generated/toolNameTables.d.ts +0 -1
  47. package/dist/generated/toolNameTables.js +0 -42
  48. package/dist/hitl/approvalDecisionNoteAudit.js +2 -2
  49. package/dist/hitl/approvalEndingCause.js +2 -7
  50. package/dist/hitl/approvalHolds.d.ts +16 -1
  51. package/dist/hitl/approvalHolds.js +52 -12
  52. package/dist/hitl/askGateWire.js +1 -0
  53. package/dist/hitl/automaticAnswerVerdict.js +5 -7
  54. package/dist/hitl/decisionConflict.js +4 -3
  55. package/dist/hitl/decisionRefusal.js +19 -12
  56. package/dist/hitl/frameRouter.d.ts +3 -0
  57. package/dist/hitl/frameRouter.js +52 -25
  58. package/dist/hitl/gateLedger.d.ts +0 -5
  59. package/dist/hitl/gateLedger.js +0 -21
  60. package/dist/hitl/hitlBridge.js +18 -4
  61. package/dist/hitl/parkResolver.js +7 -3
  62. package/dist/hitl/replayedApproval.d.ts +2 -3
  63. package/dist/hitl/replayedApproval.js +7 -16
  64. package/dist/hitl/sessionPolicyDeliverable.d.ts +0 -1
  65. package/dist/hitl/sessionPolicyDeliverable.js +15 -17
  66. package/dist/hitl/sessionPolicyWire.d.ts +2 -0
  67. package/dist/hitl/sessionPolicyWire.js +15 -3
  68. package/dist/hitl/standingRows.d.ts +1 -0
  69. package/dist/hitl/standingRows.js +38 -17
  70. package/dist/hitl/suspendedReopen.js +2 -1
  71. package/dist/hitl/toolApprovalWire.d.ts +8 -8
  72. package/dist/hitl/toolApprovalWire.js +50 -24
  73. package/dist/index.d.ts +8 -4
  74. package/dist/index.js +5 -3
  75. package/dist/liveModelCatalog.d.ts +13 -0
  76. package/dist/liveModelCatalog.js +56 -0
  77. package/dist/mcpProbeCapability.d.ts +2 -0
  78. package/dist/mcpProbeCapability.js +3 -2
  79. package/dist/mcpWireCaps.d.ts +1 -0
  80. package/dist/mcpWireCaps.js +4 -1
  81. package/dist/memoryComplianceCapability.d.ts +2 -0
  82. package/dist/memoryComplianceCapability.js +3 -2
  83. package/dist/memoryOriginCapability.d.ts +2 -0
  84. package/dist/memoryOriginCapability.js +3 -2
  85. package/dist/modelIdentityResolvability.js +2 -2
  86. package/dist/noticePresentation.js +2 -2
  87. package/dist/outputStyleSeatCapability.d.ts +2 -0
  88. package/dist/outputStyleSeatCapability.js +0 -1
  89. package/dist/peerLaneCapability.d.ts +2 -0
  90. package/dist/peerLaneCapability.js +3 -2
  91. package/dist/permissionModesCapability.d.ts +2 -0
  92. package/dist/permissionModesCapability.js +0 -1
  93. package/dist/permissionRulesWriteCapability.d.ts +2 -0
  94. package/dist/permissionRulesWriteCapability.js +3 -2
  95. package/dist/permissionsAllowGrantCapability.d.ts +2 -0
  96. package/dist/permissionsAllowGrantCapability.js +4 -2
  97. package/dist/permissionsDeclaration.js +19 -21
  98. package/dist/request/taskRequest.d.ts +1 -4
  99. package/dist/request/taskRequest.js +74 -19
  100. package/dist/resumeRefusalCopy.js +4 -4
  101. package/dist/rewindArchiveCapability.js +5 -0
  102. package/dist/rewindArchiveReading.d.ts +2 -0
  103. package/dist/rewindArchiveReading.js +0 -1
  104. package/dist/runTerminal.d.ts +33 -4
  105. package/dist/runTerminal.js +170 -15
  106. package/dist/sdkWireTransit.d.ts +2 -1
  107. package/dist/sdkWireTransit.js +1 -0
  108. package/dist/sealedKeyCapability.d.ts +2 -0
  109. package/dist/sealedKeyCapability.js +0 -1
  110. package/dist/seatContract.d.ts +7 -4
  111. package/dist/seatContract.js +34 -20
  112. package/dist/sessionPolicyCapability.d.ts +2 -0
  113. package/dist/sessionPolicyCapability.js +3 -2
  114. package/dist/sessionReadRedactionCapability.d.ts +2 -0
  115. package/dist/sessionReadRedactionCapability.js +0 -1
  116. package/dist/sessionSyncFileHistoryCapability.d.ts +2 -0
  117. package/dist/sessionSyncFileHistoryCapability.js +3 -2
  118. package/dist/skillToolListsCapability.d.ts +2 -0
  119. package/dist/skillToolListsCapability.js +3 -2
  120. package/dist/sqlEngineCapability.d.ts +2 -0
  121. package/dist/sqlEngineCapability.js +4 -3
  122. package/dist/steerPriorityCapability.d.ts +2 -0
  123. package/dist/steerPriorityCapability.js +3 -2
  124. package/dist/storePostureCapability.d.ts +2 -0
  125. package/dist/storePostureCapability.js +4 -3
  126. package/dist/toolHistoryBadTurn.js +3 -2
  127. package/dist/toolResult.d.ts +11 -0
  128. package/dist/toolResult.js +53 -0
  129. package/dist/userInvokedSkillsCapability.d.ts +2 -0
  130. package/dist/userInvokedSkillsCapability.js +3 -2
  131. package/dist/webSearchBackendCapability.d.ts +2 -0
  132. package/dist/webSearchBackendCapability.js +3 -2
  133. package/dist/wireErrorTriage.js +58 -34
  134. package/dist/wireFailureShape.d.ts +1 -1
  135. package/dist/wireFailureShape.js +76 -25
  136. package/dist/workflowsJournalCapability.d.ts +2 -0
  137. package/dist/workflowsJournalCapability.js +3 -2
  138. package/dist/writeProtectionCapability.d.ts +2 -0
  139. package/dist/writeProtectionCapability.js +3 -2
  140. package/docs/INTEGRATION-CLIENTS.md +585 -14
  141. package/package.json +4 -4
  142. package/dist/engineRefusedToolName.d.ts +0 -1
  143. package/dist/engineRefusedToolName.js +0 -8
  144. package/dist/engineWordRenames.d.ts +0 -8
  145. package/dist/engineWordRenames.js +0 -13
@@ -3,6 +3,7 @@ import { toCappedSpec } from './skillsWireCaps.js';
3
3
  import { TIER_ORDER } from './model/tierVocabulary.js';
4
4
  import { SETTINGS_NARROW_AT_MENTION } from './generated/settingsSchemaFns.js';
5
5
  import { modelsPolicyOf } from './model/modelsPolicy.js';
6
+ import { guardedRead, readOwnDataNonEmptyString, readOwnDataValue } from './ownKey.js';
6
7
  import { SETTINGS_BUDGET_NUMBER_KEYS, SETTINGS_BUDGET_PER_MODEL_NUMBER_KEYS, SETTINGS_BUDGET_STRING_KEYS, SETTINGS_TIER_GROUP_NAME_RE, } from './generated/settingsSchemaTables.js';
7
8
  const asRec = (v) => (typeof v === 'object' && v !== null && !Array.isArray(v) ? v : undefined);
8
9
  const asArr = (v) => (Array.isArray(v) ? v : []);
@@ -96,6 +97,7 @@ export function cloudMcpToSpecs(servers, env) {
96
97
  ...(allowTools.length > 0 ? { allowTools } : {}),
97
98
  ...(s.elicitation === true ? { elicitation: true } : {}),
98
99
  ...(s.toolFaces !== undefined ? { toolFaces: s.toolFaces } : {}),
100
+ ...(s.toolAxes !== undefined ? { toolAxes: s.toolAxes } : {}),
99
101
  });
100
102
  }
101
103
  return { specs, missingEnv: [...new Set(missingEnv)], droppedServers };
@@ -140,6 +142,9 @@ const TIER_GROUP_NAME = SETTINGS_TIER_GROUP_NAME_RE;
140
142
  const setOwn = (o, k, v) => {
141
143
  Object.defineProperty(o, k, { value: v, enumerable: true, writable: true, configurable: true });
142
144
  };
145
+ function modelsDocPositionOf(m, key) {
146
+ return guardedRead(() => (Object.hasOwn(m, key) ? { value: m[key] } : 'absent')) ?? 'unreadable';
147
+ }
143
148
  function modelsDocOf(models, opts) {
144
149
  const m = asRec(models);
145
150
  if (!m || !Array.isArray(m.models))
@@ -169,6 +174,7 @@ function modelsDocOf(models, opts) {
169
174
  };
170
175
  const refOf = (v) => (typeof v === 'string' && v !== '' ? { target: v } : {});
171
176
  const reasonOf = (v) => typeof v === 'string' && v !== '' ? (rawNames.has(v) ? 'rejected' : 'dangling') : 'unreadable';
177
+ const modelRefVerdict = (v) => names.has(v) ? (accept === undefined ? 'unvouched' : 'accepted') : reasonOf(v);
172
178
  let written = stripped;
173
179
  let catalogKept = false;
174
180
  if (acceptedFlags && stripped.length > 0) {
@@ -191,41 +197,57 @@ function modelsDocOf(models, opts) {
191
197
  drop({ key: 'roles', at: 'default', reason: 'unreadable' });
192
198
  const dm = policy.defaultModel;
193
199
  let defaultRole;
194
- if (accept && dm !== undefined && dm.from === 'default') {
195
- if (names.has(dm.name))
200
+ let defaultWinner;
201
+ if (dm !== undefined && dm.from === 'default') {
202
+ const verdict = modelRefVerdict(dm.name);
203
+ if (verdict === 'accepted')
196
204
  defaultRole = { model: dm.name };
197
205
  else
198
- drop({ key: 'roles', at: 'default', target: dm.name, reason: reasonOf(dm.name) });
206
+ drop({ key: 'roles', at: 'default', target: dm.name, reason: verdict });
207
+ if (verdict === 'accepted' || verdict === 'unvouched')
208
+ defaultWinner = dm.name;
199
209
  }
200
210
  const roles = asRec(m.roles);
201
211
  const keptRoles = {};
212
+ let sameModelDefaultRole;
202
213
  for (const [role, t] of Object.entries(roles ?? {})) {
203
- if (role === 'default' && defaultRole !== undefined) {
204
- let same = false;
214
+ if (role === 'default' && defaultWinner !== undefined) {
215
+ const tr = asRec(t);
216
+ let model;
217
+ let snapshot;
205
218
  try {
206
- same = JSON.stringify(t) === JSON.stringify(defaultRole);
219
+ model = tr && Object.hasOwn(tr, 'model') ? tr.model : undefined;
220
+ if (tr !== undefined && typeof model === 'string' && model === defaultWinner) {
221
+ snapshot = {};
222
+ for (const [k, v] of Object.entries(tr))
223
+ setOwn(snapshot, k, v);
224
+ setOwn(snapshot, 'model', model);
225
+ }
207
226
  }
208
227
  catch {
209
- same = false;
228
+ model = undefined;
229
+ snapshot = undefined;
210
230
  }
211
- if (!same) {
212
- const tr = asRec(t);
213
- const model = tr && Object.hasOwn(tr, 'model') ? tr.model : undefined;
231
+ if (snapshot !== undefined)
232
+ sameModelDefaultRole = snapshot;
233
+ else
214
234
  drop({ key: 'roles', at: 'default', ...refOf(model), reason: 'shadowed' });
215
- }
216
235
  continue;
217
236
  }
218
237
  const tr = asRec(t);
219
238
  const model = tr && Object.hasOwn(tr, 'model') ? tr.model : undefined;
220
- if (typeof model === 'string' && !names.has(model)) {
221
- drop({ key: 'roles', at: role, ...refOf(model), reason: reasonOf(model) });
222
- continue;
239
+ if (typeof model === 'string') {
240
+ const verdict = modelRefVerdict(model);
241
+ if (verdict !== 'accepted') {
242
+ drop({ key: 'roles', at: role, ...refOf(model), reason: verdict });
243
+ continue;
244
+ }
223
245
  }
224
246
  setOwn(keptRoles, role, t);
225
247
  }
226
248
  const rolesOut = {};
227
249
  if (defaultRole !== undefined)
228
- setOwn(rolesOut, 'default', defaultRole);
250
+ setOwn(rolesOut, 'default', sameModelDefaultRole ?? defaultRole);
229
251
  else if (Object.hasOwn(keptRoles, 'default'))
230
252
  setOwn(rolesOut, 'default', keptRoles.default);
231
253
  for (const [k, v] of Object.entries(keptRoles))
@@ -260,53 +282,64 @@ function modelsDocOf(models, opts) {
260
282
  }
261
283
  }
262
284
  let groupNames;
263
- const rawGroupNames = new Set((Array.isArray(m.tierGroups) ? m.tierGroups : []).map((g) => asStr(asRec(g)?.name)).filter((n) => !!n));
264
- if (accept && Object.hasOwn(m, 'tierGroups')) {
265
- if (!Array.isArray(m.tierGroups)) {
266
- drop({ key: 'tierGroups', reason: 'unreadable' });
285
+ if (accept) {
286
+ const tg = modelsDocPositionOf(m, 'tierGroups');
287
+ const atg = modelsDocPositionOf(m, 'activeTierGroup');
288
+ const tgValue = typeof tg === 'object' ? tg.value : undefined;
289
+ const rawGroupNames = new Set((Array.isArray(tgValue) ? tgValue : []).map((g) => asStr(asRec(g)?.name)).filter((n) => !!n));
290
+ if (tg === 'unreadable') {
291
+ drop({ key: 'tierGroups', at: 'models', reason: 'unreadable' });
267
292
  }
268
- else {
269
- const groups = [];
270
- groupNames = new Set();
271
- for (const raw of m.tierGroups) {
272
- const g = asRec(raw);
273
- const name = g ? asStr(g.name) : undefined;
274
- if (!g || !name || !TIER_GROUP_NAME.test(name)) {
275
- drop({ key: 'tierGroups', ...(name ? { at: name } : {}), reason: 'unreadable' });
276
- continue;
277
- }
278
- if (groupNames.has(name)) {
279
- drop({ key: 'tierGroups', at: name, reason: 'duplicate' });
280
- continue;
281
- }
282
- groupNames.add(name);
283
- const tiers = {};
284
- if (Object.hasOwn(g, 'tiers')) {
285
- const t = asRec(g.tiers);
286
- if (!t)
287
- drop({ key: 'tierGroups', at: `${name}.tiers`, reason: 'unreadable' });
288
- else {
289
- for (const [tier, model] of Object.entries(t)) {
290
- if (!TIER_WORDS.includes(tier))
291
- drop({ key: 'tierGroups', at: `${name}.${tier}`, ...refOf(model), reason: 'unsupported' });
292
- else if (typeof model === 'string' && names.has(model))
293
- setOwn(tiers, tier, model);
294
- else
295
- drop({ key: 'tierGroups', at: `${name}.${tier}`, ...refOf(model), reason: reasonOf(model) });
293
+ else if (tg !== 'absent') {
294
+ if (!Array.isArray(tgValue)) {
295
+ drop({ key: 'tierGroups', reason: 'unreadable' });
296
+ }
297
+ else {
298
+ const groups = [];
299
+ groupNames = new Set();
300
+ for (const raw of tgValue) {
301
+ const g = asRec(raw);
302
+ const name = g ? asStr(g.name) : undefined;
303
+ if (!g || !name || !TIER_GROUP_NAME.test(name)) {
304
+ drop({ key: 'tierGroups', ...(name ? { at: name } : {}), reason: 'unreadable' });
305
+ continue;
306
+ }
307
+ if (groupNames.has(name)) {
308
+ drop({ key: 'tierGroups', at: name, reason: 'duplicate' });
309
+ continue;
310
+ }
311
+ groupNames.add(name);
312
+ const tiers = {};
313
+ if (Object.hasOwn(g, 'tiers')) {
314
+ const t = asRec(g.tiers);
315
+ if (!t)
316
+ drop({ key: 'tierGroups', at: `${name}.tiers`, reason: 'unreadable' });
317
+ else {
318
+ for (const [tier, model] of Object.entries(t)) {
319
+ if (!TIER_WORDS.includes(tier))
320
+ drop({ key: 'tierGroups', at: `${name}.${tier}`, ...refOf(model), reason: 'unsupported' });
321
+ else if (typeof model === 'string' && names.has(model))
322
+ setOwn(tiers, tier, model);
323
+ else
324
+ drop({ key: 'tierGroups', at: `${name}.${tier}`, ...refOf(model), reason: reasonOf(model) });
325
+ }
296
326
  }
297
327
  }
328
+ groups.push({ name, tiers, ...(typeof g.notes === 'string' ? { notes: g.notes } : {}) });
298
329
  }
299
- groups.push({ name, tiers, ...(typeof g.notes === 'string' ? { notes: g.notes } : {}) });
330
+ doc.tierGroups = groups;
300
331
  }
301
- doc.tierGroups = groups;
302
332
  }
303
- }
304
- if (accept && Object.hasOwn(m, 'activeTierGroup')) {
305
- const a = m.activeTierGroup;
306
- if (typeof a === 'string' && groupNames?.has(a))
307
- doc.activeTierGroup = a;
308
- else
309
- drop({ key: 'activeTierGroup', ...refOf(a), reason: typeof a === 'string' && a !== '' ? (rawGroupNames.has(a) ? 'rejected' : 'dangling') : 'unreadable' });
333
+ if (atg === 'unreadable') {
334
+ drop({ key: 'activeTierGroup', at: 'models', reason: 'unreadable' });
335
+ }
336
+ else if (atg !== 'absent') {
337
+ const a = atg.value;
338
+ if (typeof a === 'string' && groupNames?.has(a))
339
+ doc.activeTierGroup = a;
340
+ else
341
+ drop({ key: 'activeTierGroup', ...refOf(a), reason: typeof a === 'string' && a !== '' ? (rawGroupNames.has(a) ? 'rejected' : 'dangling') : 'unreadable' });
342
+ }
310
343
  }
311
344
  return { doc, issues };
312
345
  }
@@ -566,6 +599,8 @@ function droppedRefNotice(d) {
566
599
  case 'roles':
567
600
  if (d.at === 'default' && d.reason === 'unreadable')
568
601
  return { level: 'warn', text: 'cloud config: the default model setting could not be read, so it was left out — the local engine picks its default from roles and tiers instead' };
602
+ if (d.reason === 'unvouched')
603
+ return { level: 'warn', text: `cloud config: role "${d.at ?? ''}" points at model "${t ?? ''}", but this client was given no way to check that the local engine can use that model — the role was left out and falls back along the engine's role chain` };
569
604
  if (d.reason === 'shadowed')
570
605
  return { level: 'warn', text: `cloud config: the "${d.at ?? ''}" role${t === undefined ? '' : ` ("${t}")`} is overridden by the default model setting served to you — only one default model can be set, so that role entry was left out` };
571
606
  return { level: 'warn', text: `cloud config: role "${d.at ?? ''}" ${t === undefined ? 'names no readable model' : `points at model "${t}", which ${notServed}`} — the role was left out and falls back along the engine's role chain` };
@@ -582,11 +617,111 @@ function droppedRefNotice(d) {
582
617
  return { level: 'warn', text: `cloud config: tier "${d.at ?? ''}" is bound to model "${t ?? ''}", which ${notServed} — that tier was left unbound` };
583
618
  if (d.reason === 'unsupported')
584
619
  return { level: 'warn', text: `cloud config: tier "${d.at ?? ''}" is not a tier the local engine reads (${TIER_WORDS.join(', ')}) — that binding was left out` };
620
+ if (d.at === 'models')
621
+ return { level: 'warn', text: 'cloud config: the tier group settings served to you could not be read by this client — they were left out, so no tier groups are set on the local engine' };
585
622
  return { level: 'warn', text: `cloud config: a tier group setting${d.at === undefined ? '' : ` ("${d.at}")`} could not be read and was left out` };
586
623
  case 'activeTierGroup':
587
624
  return { level: 'warn', text: `cloud config: ${t === undefined ? 'the active tier group setting could not be read' : `the active tier group "${t}" is not among the readable tier groups`} — no tier table is active on the local engine` };
588
625
  }
589
626
  }
627
+ const MCP_DROP_SENTENCES = {
628
+ 'missing-field': {
629
+ level: 'warn',
630
+ text: (who) => `cloud config: MCP server entries left out because a required setting is missing or unreadable (a name, a transport, or its command or address): ${who}`,
631
+ },
632
+ 'unknown-transport': {
633
+ level: 'warn',
634
+ text: (who) => `cloud config: MCP server entries left out because their transport is not one this client supports: ${who}`,
635
+ },
636
+ 'principal-and-assertion-header': {
637
+ level: 'warn',
638
+ text: (who) => `cloud config: MCP server entries left out because they set both principalHeader and assertionHeader, which name the same identity header — keep only one of the two: ${who}`,
639
+ },
640
+ capped: {
641
+ level: 'warn',
642
+ text: (who) => `cloud config: MCP server entries left out because more than ${MCP_CAPS.items} were served to you and this client passes at most ${MCP_CAPS.items} to the engine: ${who}`,
643
+ },
644
+ disabled: {
645
+ level: 'info',
646
+ text: (who) => `cloud config: MCP server entries switched off in the served settings, so they are not started here: ${who}`,
647
+ },
648
+ };
649
+ const MCP_DROP_UNRECOGNISED = (who) => `cloud config: MCP server entries left out for a reason this client does not recognise: ${who}`;
650
+ const MCP_DROP_LIST_UNREADABLE = 'cloud config: the list of MCP server entries left out could not be read by this client — servers missing from the local tool list may have been left out without notice';
651
+ const MCP_DROP_NAMES_SHOWN = 5;
652
+ function mcpDropWho(names, unnamed) {
653
+ const distinct = [...new Set(names)];
654
+ const parts = distinct.slice(0, MCP_DROP_NAMES_SHOWN).map((n) => `"${n}"`);
655
+ if (distinct.length > MCP_DROP_NAMES_SHOWN)
656
+ parts.push(`+${distinct.length - MCP_DROP_NAMES_SHOWN} more`);
657
+ if (unnamed > 0)
658
+ parts.push(`${unnamed} without a readable name`);
659
+ return parts.join(', ');
660
+ }
661
+ function guarded(read) {
662
+ try {
663
+ return read();
664
+ }
665
+ catch {
666
+ return undefined;
667
+ }
668
+ }
669
+ function droppedMcpNotices(carrier) {
670
+ const listUnreadable = [{ level: 'warn', text: MCP_DROP_LIST_UNREADABLE }];
671
+ let desc;
672
+ try {
673
+ desc = Object.getOwnPropertyDescriptor(carrier, 'droppedMcpServers');
674
+ }
675
+ catch {
676
+ return listUnreadable;
677
+ }
678
+ if (desc === undefined)
679
+ return [];
680
+ if (!Object.hasOwn(desc, 'value'))
681
+ return listUnreadable;
682
+ const rows = desc.value;
683
+ let count = 0;
684
+ try {
685
+ if (!Array.isArray(rows))
686
+ return listUnreadable;
687
+ count = rows.length;
688
+ }
689
+ catch {
690
+ return listUnreadable;
691
+ }
692
+ const known = new Map();
693
+ const unrecognised = { names: [], unnamed: 0 };
694
+ for (let i = 0; i < count; i++) {
695
+ const row = guarded(() => readOwnDataValue(rows, String(i)));
696
+ const rowObj = typeof row === 'object' && row !== null ? row : undefined;
697
+ const reason = guarded(() => {
698
+ const raw = readOwnDataValue(rowObj, 'reason');
699
+ return typeof raw === 'string' && Object.hasOwn(MCP_DROP_SENTENCES, raw) ? raw : undefined;
700
+ });
701
+ const name = guarded(() => readOwnDataNonEmptyString(rowObj, 'name'));
702
+ let bucket = unrecognised;
703
+ if (reason !== undefined) {
704
+ let b = known.get(reason);
705
+ if (b === undefined)
706
+ known.set(reason, (b = { names: [], unnamed: 0 }));
707
+ bucket = b;
708
+ }
709
+ if (name === undefined)
710
+ bucket.unnamed++;
711
+ else
712
+ bucket.names.push(name);
713
+ }
714
+ const out = [];
715
+ for (const reason of Object.keys(MCP_DROP_SENTENCES)) {
716
+ const b = known.get(reason);
717
+ if (b !== undefined)
718
+ out.push({ level: MCP_DROP_SENTENCES[reason].level, text: MCP_DROP_SENTENCES[reason].text(mcpDropWho(b.names, b.unnamed)) });
719
+ }
720
+ if (unrecognised.names.length + unrecognised.unnamed > 0) {
721
+ out.push({ level: 'warn', text: MCP_DROP_UNRECOGNISED(mcpDropWho(unrecognised.names, unrecognised.unnamed)) });
722
+ }
723
+ return out;
724
+ }
590
725
  function budgetNotice(r) {
591
726
  switch (r.kind) {
592
727
  case 'configured': {
@@ -654,6 +789,7 @@ export function cloudEffectiveNotices(p) {
654
789
  'the catalog is kept as sent, and a locally started engine rejects the cloud model settings: at startup it uses its own environment catalog instead, and on a later refresh it keeps the previous configuration',
655
790
  });
656
791
  }
792
+ out.push(...droppedMcpNotices(p));
657
793
  out.push(budgetNotice(p.budget));
658
794
  out.push(runtimeCapsNotice(p.runtimeCaps));
659
795
  return out;
@@ -1,5 +1,5 @@
1
1
  export const DECLARED_SUPPORT_FLOOR = Object.freeze({
2
- server: "7.125.0",
3
- core: "7.48.0",
4
- sdk: "20.0.0",
2
+ server: "7.126.0",
3
+ core: "7.49.5",
4
+ sdk: "21.1.0",
5
5
  });
@@ -1,5 +1,7 @@
1
1
  export type DeviceExecutorManagementReading = {
2
2
  kind: 'unobserved';
3
+ } | {
4
+ kind: 'unreadable';
3
5
  } | {
4
6
  kind: 'not_reported';
5
7
  why: 'capability_absent';
@@ -1,5 +1,5 @@
1
1
  import { engineWireTarget } from './engineWireTarget.js';
2
- import { createEngineCapReader } from './engineCapReader.js';
2
+ import { createEngineCapReader, unreadableReadingDetail } from './engineCapReader.js';
3
3
  import { ownDataField } from './ownKey.js';
4
4
  import { capForDisplay } from './fleetTaskDesc.js';
5
5
  export function projectDeviceExecutorManagementCapability(caps) {
@@ -43,7 +43,6 @@ export function projectDeviceExecutorManagementCapability(caps) {
43
43
  const reader = createEngineCapReader({
44
44
  name: 'deviceExecutor.management',
45
45
  project: projectDeviceExecutorManagementCapability,
46
- makeUnobserved: () => ({ kind: 'unobserved' }),
47
46
  });
48
47
  export function noteEngineCapsForDeviceExecutorManagement(baseUrl, caps, opts) {
49
48
  reader.note(baseUrl, caps, opts);
@@ -83,6 +82,8 @@ function laneFactsSuffix(r) {
83
82
  }
84
83
  export function deviceExecutorManagementDoctorDetail(reading) {
85
84
  switch (reading.kind) {
85
+ case 'unreadable':
86
+ return unreadableReadingDetail('device management face');
86
87
  case 'unobserved':
87
88
  return 'device management face not observed — the engine reports it on /v1/capabilities (deviceExecutor.management); this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)';
88
89
  case 'not_reported':
@@ -33,6 +33,9 @@ export interface DisplayUntrustedMarked {
33
33
  export declare const ESCAPE_FACE: DisplayFace;
34
34
  export declare const LABEL_FACE: DisplayFace;
35
35
  export declare const PEER_NAME_FACE: DisplayFace;
36
+ export declare const REDACTION_MARKER_MAX_LENGTH: number;
37
+ export declare const DISPLAY_CAP_BACKOFF_MAX: number;
38
+ export declare function markerSafeCut(text: string, cut: number): number;
36
39
  export declare function capDisplay(text: string, max: number | undefined, mark: DisplayMark): string;
37
40
  export declare function paintAndCap(text: string, face: DisplayFace, max?: number): string;
38
41
  export declare function credentialWashPasses(text: string, opts?: DisplayUntrustedOptions | null): {
@@ -4326,6 +4326,7 @@ function capPaired(t, max) {
4326
4326
  if (id >= 0)
4327
4327
  while (cut > 0 && t.ids[cut - 1] === id)
4328
4328
  cut--;
4329
+ cut = markerSafeCut(t.text, cut);
4329
4330
  if (cut > 0) {
4330
4331
  const code = t.text.charCodeAt(cut - 1);
4331
4332
  if (code >= 0xd800 && code <= 0xdbff)
@@ -4370,6 +4371,19 @@ export const ESCAPE_FACE = Object.freeze({ ...publicFace({ foldLines: false }) }
4370
4371
  export const LABEL_FACE = Object.freeze({ ...publicFace({}) });
4371
4372
  export const PEER_NAME_FACE = Object.freeze({ ...publicFace({ mark: 'space' }) });
4372
4373
  const ESCAPE_TOKEN_AT = /^\\u(?:[0-9A-F]{4}|\{[0-9A-F]{1,6}\})/;
4374
+ export const REDACTION_MARKER_MAX_LENGTH = REDACTION_MARKER_PREFIX.length + 2 * (1 + 16) + 1;
4375
+ export const DISPLAY_CAP_BACKOFF_MAX = REDACTION_MARKER_MAX_LENGTH;
4376
+ export function markerSafeCut(text, cut) {
4377
+ if (cut <= 0 || cut >= text.length)
4378
+ return cut;
4379
+ const open = text.lastIndexOf('\u00ab', cut - 1);
4380
+ if (open < 0 || cut - open >= REDACTION_MARKER_MAX_LENGTH)
4381
+ return cut;
4382
+ const close = text.indexOf('\u00bb', open);
4383
+ if (close < cut || close - open + 1 > REDACTION_MARKER_MAX_LENGTH)
4384
+ return cut;
4385
+ return MARKER_RE.test(text.slice(open, close + 1)) ? open : cut;
4386
+ }
4373
4387
  export function capDisplay(text, max, mark) {
4374
4388
  if (typeof max !== 'number' || !Number.isFinite(max))
4375
4389
  return text;
@@ -4387,6 +4401,7 @@ export function capDisplay(text, max, mark) {
4387
4401
  break;
4388
4402
  }
4389
4403
  }
4404
+ cut = markerSafeCut(text, cut);
4390
4405
  if (cut > 0) {
4391
4406
  const code = text.charCodeAt(cut - 1);
4392
4407
  if (code >= 0xd800 && code <= 0xdbff)
@@ -1,16 +1,22 @@
1
1
  interface EngineCapReaderSpec<R> {
2
2
  name: string;
3
3
  project: (caps: unknown) => R | undefined;
4
- makeUnobserved: () => R;
5
4
  }
5
+ type ReaderUnobserved = {
6
+ kind: 'unobserved';
7
+ };
8
+ type ReaderUnreadable = {
9
+ kind: 'unreadable';
10
+ };
6
11
  interface EngineCapReader<R> {
7
12
  note(baseUrl: string, caps: unknown, opts?: {
8
13
  generation?: number;
9
14
  }): void;
10
- observed(baseUrl: string | undefined): R;
15
+ observed(baseUrl: string | undefined): R | ReaderUnobserved | ReaderUnreadable;
11
16
  __resetForTests(): void;
12
17
  }
13
18
  export declare function createEngineCapReader<R>(spec: EngineCapReaderSpec<R>): EngineCapReader<R>;
19
+ export declare function unreadableReadingDetail(subject?: string): string;
14
20
  export type StoreSeat = {
15
21
  kind: 'object';
16
22
  store: object;
@@ -1,5 +1,7 @@
1
1
  import { engineCapsLandingOf } from './engineCapsCache.js';
2
2
  import { ownDataField } from './ownKey.js';
3
+ const unobserved = () => ({ kind: 'unobserved' });
4
+ const unreadable = () => ({ kind: 'unreadable' });
3
5
  function freezeReading(reading, seen = new Set()) {
4
6
  if (reading === null || typeof reading !== 'object' || seen.has(reading))
5
7
  return reading;
@@ -18,15 +20,17 @@ export function createEngineCapReader(spec) {
18
20
  note(_baseUrl, _caps, _opts) { },
19
21
  observed(baseUrl) {
20
22
  if (typeof baseUrl !== 'string' || baseUrl === '')
21
- return spec.makeUnobserved();
23
+ return unobserved();
22
24
  const landing = engineCapsLandingOf(baseUrl);
23
- if (landing === undefined)
24
- return spec.makeUnobserved();
25
+ if (landing.kind === 'not_landed')
26
+ return unobserved();
27
+ if (landing.kind === 'unreadable')
28
+ return unreadable();
25
29
  const hit = memo.get(landing);
26
30
  if (hit === PROJECTING)
27
- return spec.makeUnobserved();
31
+ return unobserved();
28
32
  if (hit !== undefined)
29
- return hit.reading === undefined ? spec.makeUnobserved() : hit.reading;
33
+ return hit.reading === undefined ? unreadable() : hit.reading;
30
34
  const mine = memo;
31
35
  mine.set(landing, PROJECTING);
32
36
  let reading;
@@ -38,17 +42,21 @@ export function createEngineCapReader(spec) {
38
42
  }
39
43
  if (engineCapsLandingOf(baseUrl) !== landing) {
40
44
  mine.delete(landing);
41
- return spec.makeUnobserved();
45
+ return unobserved();
42
46
  }
43
47
  const done = { reading: reading === undefined ? undefined : freezeReading(reading) };
44
48
  mine.set(landing, done);
45
- return done.reading === undefined ? spec.makeUnobserved() : done.reading;
49
+ return done.reading === undefined ? unreadable() : done.reading;
46
50
  },
47
51
  __resetForTests() {
48
52
  memo = new WeakMap();
49
53
  },
50
54
  };
51
55
  }
56
+ export function unreadableReadingDetail(subject) {
57
+ const head = typeof subject === 'string' && subject.length > 0 ? `${subject} could not be read` : 'could not be read';
58
+ return `${head} — the engine's /v1/capabilities report arrived, but this entry is not in a form this client reads, so it is shown as unknown, not as absent; probing the same engine again will not change it while the engine reports it this way`;
59
+ }
52
60
  export function storeSeatOf(caps) {
53
61
  try {
54
62
  const store = ownDataField(caps, 'store');
@@ -1,7 +1,13 @@
1
+ export type EngineCapsLanding = {
2
+ readonly kind: 'landed';
3
+ readonly caps: Readonly<Record<string, unknown>>;
4
+ } | {
5
+ readonly kind: 'not_landed';
6
+ } | {
7
+ readonly kind: 'unreadable';
8
+ };
1
9
  export declare function engineCapsGeneration(baseUrl: string): number;
2
- export declare function engineCapsLandingOf(baseUrl: string): {
3
- readonly caps: object;
4
- } | undefined;
10
+ export declare function engineCapsLandingOf(baseUrl: string | undefined): EngineCapsLanding;
5
11
  export declare function kickEngineCapsProbe(baseUrl: string, probe: () => Promise<unknown>): void;
6
12
  export declare function engineCapsSettled(baseUrl: string | undefined, opts?: {
7
13
  withinMs?: number;
@@ -30,6 +36,8 @@ export type EngineCapValueState = {
30
36
  export declare function engineCapValue(baseUrl: string | undefined, key: string): EngineCapValueState;
31
37
  export declare function engineCapString(baseUrl: string | undefined, key: string): string | undefined;
32
38
  export declare function invalidateEngineCaps(baseUrl: string | undefined, probe?: () => Promise<unknown>): void;
39
+ export type EngineCapsProbeRotation = 'started' | 'registered' | 'ignored';
40
+ export declare function rotateEngineCapsProbe(baseUrl: string | undefined, probe: () => Promise<unknown>): EngineCapsProbeRotation;
33
41
  export declare function refreshEngineCapsOnce(baseUrl: string | undefined, opts?: {
34
42
  withinMs?: number;
35
43
  }): Promise<'settled' | 'timed_out' | 'no_probe'>;