@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
@@ -2,8 +2,6 @@ import type { ActiveRunBusySignal } from './runStream.js';
2
2
  import type { PlanReviewReopenTrigger } from '../hitl/planReviewWire.js';
3
3
  import { atMostOnceFailureClass } from '../atMostOnce.js';
4
4
  export declare const PLAN_REVIEW_GATE_KIND = "plan_review";
5
- export declare const PLAN_REVIEW_GATE_KINDS: readonly string[];
6
- export declare const ASK_PARK_GATE_KINDS: readonly string[];
7
5
  export type DurableDecideLeg = 'approval' | 'plan_review' | 'other_gate' | 'unreadable' | 'absent';
8
6
  export declare function durableDecideLegOf(row: {
9
7
  readonly gateKind?: string | null;
@@ -5,14 +5,13 @@ import { atMostOnceFailureClass } from '../atMostOnce.js';
5
5
  import { readSteerReceipt } from '../steerReceipt.js';
6
6
  import { resumeAttachReplayDisclosure, runningAttachOffered } from '../hitl/resumeRunningCard.js';
7
7
  import { isSdkRunTerminal, SDK_PARKED_RUN_STATUSES } from '../terminalStatuses.js';
8
+ import { SDK_APPROVAL_DECIDE_GATE_KINDS, SDK_PLAN_REVIEW_GATE_KINDS } from '../generated/sdkFactTables.js';
8
9
  export const PLAN_REVIEW_GATE_KIND = 'plan_review';
9
- const APPROVAL_LEG_GATE_KINDS = ['human', 'irreversible_ask'];
10
- const PLAN_LEG_GATE_KINDS = ['plan_review'];
10
+ const APPROVAL_LEG_GATE_KINDS = SDK_APPROVAL_DECIDE_GATE_KINDS;
11
+ const PLAN_LEG_GATE_KINDS = SDK_PLAN_REVIEW_GATE_KINDS;
11
12
  const CORE_GATE_KINDS = ['human', 'irreversible_ask', 'resource_limit', 'needs_review', 'plan_review', 'task_done'];
12
- const _durableDecideLegPins = [true, true, true, true, true, true];
13
+ const _durableDecideLegPins = [true, true, true, true, true, true, true, true, true, true];
13
14
  void _durableDecideLegPins;
14
- export const PLAN_REVIEW_GATE_KINDS = Object.freeze([...PLAN_LEG_GATE_KINDS]);
15
- export const ASK_PARK_GATE_KINDS = Object.freeze([...APPROVAL_LEG_GATE_KINDS]);
16
15
  function gateKindReadingOf(row) {
17
16
  let kind;
18
17
  try {
@@ -447,9 +446,10 @@ export async function attemptActiveRunSelfHeal(signal, runs, deps) {
447
446
  };
448
447
  }
449
448
  const gateKind = signal.pendingGate?.kind ?? null;
450
- if (gateKind !== null && PLAN_REVIEW_GATE_KINDS.includes(gateKind))
449
+ const gateLeg = gateKind !== null ? durableDecideLegOf({ gateKind }) : 'absent';
450
+ if (gateLeg === 'plan_review')
451
451
  return planReviewArm(taskId, signal, deps);
452
- if (gateKind !== null && ASK_PARK_GATE_KINDS.includes(gateKind))
452
+ if (gateLeg === 'approval')
453
453
  return askParkArm(taskId, signal, runs, deps);
454
454
  let status = signal.activeTaskStatus;
455
455
  if (status === null) {
@@ -6,12 +6,16 @@ import { readEffectiveReasoning, readEffectiveMemoryScopes } from '../../effecti
6
6
  import { isReviewPark, readRunTerminal, runTerminalCode, runTerminalGateToolName, } from '../../runTerminal.js';
7
7
  import { doneResultView } from '../../replayBody.js';
8
8
  import { toCcModelUsage } from './turnUsageToModelUsage.js';
9
- import { LIMITS_MAX_COST_EXCEEDED, LIMITS_MAX_TOKENS_EXCEEDED, LIMITS_MAX_TURNS_EXCEEDED, LIMITS_MAX_WALLTIME_EXCEEDED, OUTPUT_INVALID, isCorruptSessionCode, isRewindFamilyCode, } from '../../engineErrorCodes.js';
9
+ import { LIMITS_MAX_COST_EXCEEDED, LIMITS_MAX_TOKENS_EXCEEDED, LIMITS_MAX_TURNS_EXCEEDED, LIMITS_MAX_WALLTIME_EXCEEDED, OUTPUT_INVALID, isCorruptSessionCode, idempotencyPayloadMismatchContent, isIdempotencyPayloadMismatchCode, isRewindFamilyCode, } from '../../engineErrorCodes.js';
10
10
  import { corruptSessionContent } from '../../corruptSessionCopy.js';
11
11
  import { runBlockedText } from '../../promptBlockedCopy.js';
12
12
  import { abortCauseNoteOf, readAbortCause } from '../../runCancelContext.js';
13
13
  function withTerminalNote(text, code, frame) {
14
- const withCorrupt = isCorruptSessionCode(code) ? `${text}\n${corruptSessionContent()}` : text;
14
+ const withCorrupt = isCorruptSessionCode(code)
15
+ ? `${text}\n${corruptSessionContent()}`
16
+ : isIdempotencyPayloadMismatchCode(code)
17
+ ? `${text}\n${idempotencyPayloadMismatchContent()}`
18
+ : text;
15
19
  const abortNote = abortCauseNoteOf(frame);
16
20
  return abortNote === undefined ? withCorrupt : `${withCorrupt}\n${abortNote}`;
17
21
  }
@@ -122,7 +122,6 @@ export function createBackgroundView(client, opts) {
122
122
  let disposed = false;
123
123
  let current = null;
124
124
  let lastSerialized = null;
125
- let capsApprovals = null;
126
125
  const listeners = new Set();
127
126
  const abort = new AbortController();
128
127
  function notify() {
@@ -137,27 +136,16 @@ export function createBackgroundView(client, opts) {
137
136
  }
138
137
  }
139
138
  }
140
- async function probeCaps() {
141
- if (capsApprovals !== null)
142
- return;
143
- try {
144
- const caps = await client.capabilities({ signal: abort.signal });
145
- capsApprovals = caps.approvals === true;
146
- }
147
- catch (err) {
148
- debugLog(`caps probe unavailable (approvals 判别缺席,保持 ok 口径): ${String(err)}`);
149
- }
150
- }
151
139
  async function pullAssistant() {
152
140
  try {
153
141
  const { tasks } = await client.assistant.tasks({ signal: abort.signal });
154
142
  if (!Array.isArray(tasks))
155
143
  return { health: 'unavailable', tasks: [] };
156
- return { health: capsApprovals === false ? 'not-configured' : 'ok', tasks };
144
+ return { health: 'ok', tasks };
157
145
  }
158
146
  catch (err) {
159
147
  debugLog(`assistant.tasks degrade: ${String(err)}`);
160
- const health = capsApprovals === false || isNotImplemented(err)
148
+ const health = isNotImplemented(err)
161
149
  ? 'not-configured'
162
150
  : isReadCredentialRefusal(err)
163
151
  ? 'unauthorized'
@@ -183,7 +171,6 @@ export function createBackgroundView(client, opts) {
183
171
  }
184
172
  }
185
173
  async function pollOnce() {
186
- await probeCaps();
187
174
  const [assistant, fleet] = await Promise.all([pullAssistant(), pullFleet()]);
188
175
  if (disposed || abort.signal.aborted)
189
176
  return;
@@ -1,6 +1,7 @@
1
1
  import { type EngineProbeOpts, type WireAuthTokenSource } from './engineWireSdk.js';
2
2
  import type { ReasoningEffort } from './effortWire.js';
3
3
  import type { ToolRosterView } from './toolRoster.js';
4
+ import { type EngineToolDirectory } from './engineToolDirectory.js';
4
5
  export declare const MAX_TASK_AGENTS = 32;
5
6
  export declare const MAX_AGENT_NAME_CHARS = 64;
6
7
  export declare const MAX_AGENT_TEXT_CHARS = 4096;
@@ -61,6 +62,7 @@ export type ProjectAgentsDeps = {
61
62
  skillSpecs?: TaskAgentWireSkill[];
62
63
  toolRoster?: ToolRosterView | null;
63
64
  withholdRefusedDenyLists?: boolean;
65
+ toolDirectory?: EngineToolDirectory;
64
66
  agentSourceLabel?: (agentName: string) => string | undefined;
65
67
  };
66
68
  export type ProjectAgentsResult = {
@@ -68,7 +70,7 @@ export type ProjectAgentsResult = {
68
70
  warnings: string[];
69
71
  toolListFlaws?: readonly AgentToolListFlaw[];
70
72
  };
71
- export type AgentToolListFlawKind = 'retired' | 'unprefixed_namespace' | 'case_twin';
73
+ export type AgentToolListFlawKind = 'retired' | 'unprefixed_namespace' | 'unmintable_segment' | 'unjudged' | 'case_twin';
72
74
  export interface AgentToolListFlaw {
73
75
  readonly agent: string;
74
76
  readonly list: 'tools' | 'disallowedTools';
@@ -3,10 +3,11 @@ import { ownDataField } from './ownKey.js';
3
3
  import { makeEngineWireClient } from './engineWireSdk.js';
4
4
  import { frozenSet } from './frozenSet.js';
5
5
  import { settleWithinDeadline } from './abortableSleep.js';
6
- import { isEngineRefusedToolName } from './engineRefusedToolName.js';
7
- import { CORE_PROTOCOL_PREFIXES, CORE_RETIRED_TOOL_CURRENT_NAMES } from './generated/toolNameTables.js';
6
+ import { ruleToolNameVerdictOf } from './protocolSegment.js';
7
+ import { CORE_PROTOCOL_PREFIXES } from './generated/toolNameTables.js';
8
8
  import { rosterMountedNames } from './rosterMountedNames.js';
9
9
  import { capForDisplay } from './fleetTaskDesc.js';
10
+ import { deniedEntryToolName, mcpEntryReadingOf } from './engineToolDirectory.js';
10
11
  export const MAX_TASK_AGENTS = 32;
11
12
  export const MAX_AGENT_NAME_CHARS = 64;
12
13
  export const MAX_AGENT_TEXT_CHARS = 4_096;
@@ -43,32 +44,6 @@ const SOURCE_RANK = {
43
44
  function sourceRank(source) {
44
45
  return SOURCE_RANK[source] ?? 5;
45
46
  }
46
- const RETIRED_TABLE_CURRENT_NAMES = frozenSet([...CORE_RETIRED_TOOL_CURRENT_NAMES.values()].filter((v) => typeof v === 'string'));
47
- function unescapedIndexOf(s, ch, last) {
48
- for (let k = 0; k < s.length; k++) {
49
- const i = last ? s.length - 1 - k : k;
50
- if (s[i] !== ch)
51
- continue;
52
- let backslashes = 0;
53
- for (let j = i - 1; j >= 0 && s[j] === '\\'; j--)
54
- backslashes++;
55
- if (backslashes % 2 === 0)
56
- return i;
57
- }
58
- return -1;
59
- }
60
- function deniedEntryToolName(entry) {
61
- if (entry !== entry.trim())
62
- return undefined;
63
- const open = unescapedIndexOf(entry, '(', false);
64
- if (open === -1)
65
- return entry;
66
- const close = unescapedIndexOf(entry, ')', true);
67
- if (close === -1 || close <= open || close !== entry.length - 1)
68
- return entry;
69
- const name = entry.substring(0, open);
70
- return name.length === 0 ? entry : name.trim();
71
- }
72
47
  function toolNameFlawOf(entry, names, list) {
73
48
  if (typeof entry !== 'string' || entry.length === 0 || entry === '*')
74
49
  return undefined;
@@ -76,12 +51,15 @@ function toolNameFlawOf(entry, names, list) {
76
51
  if (name === undefined || name.length === 0 || name === '*' || names.has(name))
77
52
  return undefined;
78
53
  const named = name !== entry ? { toolName: name } : {};
79
- if (isEngineRefusedToolName(name)) {
80
- const current = CORE_RETIRED_TOOL_CURRENT_NAMES.get(name);
81
- if (current === undefined)
82
- return { kind: 'unprefixed_namespace', ...named };
83
- return typeof current === 'string' ? { kind: 'retired', currentName: current, ...named } : { kind: 'retired', ...named };
84
- }
54
+ const verdict = ruleToolNameVerdictOf(name);
55
+ if (verdict.kind === 'unknown')
56
+ return { kind: 'unjudged', ...named };
57
+ if (verdict.kind === 'unmintable_segment')
58
+ return { kind: 'unmintable_segment', ...named };
59
+ if (verdict.kind === 'unprefixed_namespace')
60
+ return { kind: 'unprefixed_namespace', ...named };
61
+ if (verdict.kind === 'retired')
62
+ return verdict.currentName !== undefined ? { kind: 'retired', currentName: verdict.currentName, ...named } : { kind: 'retired', ...named };
85
63
  if (CORE_PROTOCOL_PREFIXES.some((prefix) => name.startsWith(prefix)))
86
64
  return undefined;
87
65
  const folded = name.toLowerCase();
@@ -94,7 +72,7 @@ export function agentToolListFlaws(agents, roster) {
94
72
  if (!Array.isArray(agents) || agents.length === 0)
95
73
  return [];
96
74
  const mounted = rosterMountedNames(roster);
97
- const names = typeof mounted === 'object' ? mounted : RETIRED_TABLE_CURRENT_NAMES;
75
+ const names = typeof mounted === 'object' ? mounted : new Set();
98
76
  const out = [];
99
77
  for (const a of agents) {
100
78
  if (a === null || typeof a !== 'object' || typeof a.name !== 'string')
@@ -117,6 +95,10 @@ function toolListFlawPhrase(f) {
117
95
  return `${entry} is a retired tool name${f.currentName !== undefined ? ` (now ${JSON.stringify(f.currentName)})` : ' (no longer available under any name)'}`;
118
96
  if (f.kind === 'unprefixed_namespace')
119
97
  return `${entry} contains "__" but does not start with a tool protocol prefix`;
98
+ if (f.kind === 'unmintable_segment')
99
+ return `${entry} spells its server or agent peer part in a way the engine never uses when naming tools, so no tool answers to it`;
100
+ if (f.kind === 'unjudged')
101
+ return `${entry} could not be checked here, because the engine's tool-name check is not available in this client`;
120
102
  return `${entry} differs from the tool ${JSON.stringify(f.currentName ?? '')} only in letter case`;
121
103
  }
122
104
  export function projectAgentsForWire(defs, deps) {
@@ -176,8 +158,10 @@ export function projectAgentsForWire(defs, deps) {
176
158
  const denyTools = projectTools(def.disallowedTools, 'disallowedTools');
177
159
  if (denyTools)
178
160
  item.denyTools = denyTools;
179
- const flaws = agentToolListFlaws([item], deps.toolRoster);
180
- if (flaws.length > 0) {
161
+ let whoMemo;
162
+ const whoOf = () => {
163
+ if (whoMemo !== undefined)
164
+ return whoMemo;
181
165
  let source;
182
166
  try {
183
167
  source = deps.agentSourceLabel?.(name);
@@ -185,7 +169,14 @@ export function projectAgentsForWire(defs, deps) {
185
169
  catch {
186
170
  source = undefined;
187
171
  }
188
- const who = `agent "${name}"${typeof source === 'string' && source.length > 0 ? ` (${capForDisplay(source, 120)})` : ''}`;
172
+ whoMemo = `agent "${name}"${typeof source === 'string' && source.length > 0 ? ` (${capForDisplay(source, 120)})` : ''}`;
173
+ return whoMemo;
174
+ };
175
+ const flaws = agentToolListFlaws([item], deps.toolRoster);
176
+ const flawedEntries = new Set(flaws.map((f) => `${f.list}\u0000${f.entry}`));
177
+ const omittedAllowFlaws = new Set(flaws.filter((f) => f.list === 'tools' && f.kind !== 'unjudged').map((f) => f.entry));
178
+ if (flaws.length > 0) {
179
+ const who = whoOf();
189
180
  toolListFlaws.push(...flaws);
190
181
  const denyFlaws = flaws.filter(f => f.list === 'disallowedTools');
191
182
  const withhold = denyFlaws.length > 0 && deps.withholdRefusedDenyLists === true;
@@ -199,12 +190,49 @@ export function projectAgentsForWire(defs, deps) {
199
190
  for (const f of flaws) {
200
191
  if (f.list !== 'tools')
201
192
  continue;
202
- warnings.push(`agents wire: ${who}: its tools entry ${toolListFlawPhrase(f)} — tool names are matched exactly, so this entry does not give the agent that tool. Fix the entry in the agent definition.`);
193
+ warnings.push(f.kind === 'unjudged'
194
+ ? `agents wire: ${who}: its tools entry ${toolListFlawPhrase(f)} — tool names are matched exactly, so this entry does not give the agent that tool. Fix the entry in the agent definition.`
195
+ : `agents wire: ${who}: its tools entry ${toolListFlawPhrase(f)} — tool names are matched exactly, so no tool answers to this entry; it is omitted (newer engines refuse a request whose agent definitions name it). Fix the entry in the agent definition.`);
203
196
  }
204
197
  }
205
198
  if (withhold)
206
199
  continue;
207
200
  }
201
+ const filterList = (key, label) => {
202
+ const entries = item[key];
203
+ if (entries === undefined)
204
+ return;
205
+ const kept = [];
206
+ for (const entry of entries) {
207
+ if (label === 'tools' && omittedAllowFlaws.has(entry))
208
+ continue;
209
+ if (flawedEntries.has(`${label}\u0000${entry}`)) {
210
+ kept.push(entry);
211
+ continue;
212
+ }
213
+ const toolName = label === 'disallowedTools' ? deniedEntryToolName(entry) : entry;
214
+ const reading = toolName === undefined ? 'outside' : mcpEntryReadingOf(toolName, deps.toolDirectory);
215
+ if (reading === 'unanswered') {
216
+ warnings.push(`agents wire: ${whoOf()} ${label} entry ${JSON.stringify(entry)} names no tool this deployment answers to — omitted: no MCP server of that name is declared for this session, so there is nothing to ${label === 'tools' ? 'give' : 'hide'}, and newer engines refuse a request whose agent definitions name such a tool. Remove the entry or configure the server.`);
217
+ continue;
218
+ }
219
+ if (reading === 'unchecked') {
220
+ warnings.push(`agents wire: ${whoOf()} ${label} entry ${JSON.stringify(entry)} could not be checked against the MCP servers this session declares — sent as written; if no such server is declared, newer engines refuse the whole request until the entry is removed.`);
221
+ }
222
+ kept.push(entry);
223
+ }
224
+ if (key === 'allowTools') {
225
+ item.allowTools = kept;
226
+ }
227
+ else if (kept.length > 0) {
228
+ item.denyTools = kept;
229
+ }
230
+ else {
231
+ delete item.denyTools;
232
+ }
233
+ };
234
+ filterList('denyTools', 'disallowedTools');
235
+ filterList('allowTools', 'tools');
208
236
  if (def.memory !== undefined) {
209
237
  if (typeof def.memory === 'string' && AGENT_MEMORY_WORDS.has(def.memory)) {
210
238
  item.memory = { scopes: [`agent:${name}`], writeScope: `agent:${name}` };
@@ -1,5 +1,7 @@
1
1
  export type ApprovalDecidedByReading = {
2
2
  kind: 'unobserved';
3
+ } | {
4
+ kind: 'unreadable';
3
5
  } | {
4
6
  kind: 'not_reported';
5
7
  } | {
@@ -17,7 +17,6 @@ export function projectApprovalDecidedByCapability(caps) {
17
17
  const reader = createEngineCapReader({
18
18
  name: 'approvalDecidedBy',
19
19
  project: projectApprovalDecidedByCapability,
20
- makeUnobserved: () => ({ kind: 'unobserved' }),
21
20
  });
22
21
  export function noteEngineCapsForApprovalDecidedBy(baseUrl, caps, opts) {
23
22
  reader.note(baseUrl, caps, opts);
@@ -1,5 +1,7 @@
1
1
  export type ApprovalsStreamLiveReading = {
2
2
  kind: 'unobserved';
3
+ } | {
4
+ kind: 'unreadable';
3
5
  } | {
4
6
  kind: 'not_reported';
5
7
  } | {
@@ -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
  export function projectApprovalsStreamLiveCapability(caps) {
5
5
  try {
@@ -21,7 +21,6 @@ export function projectApprovalsStreamLiveCapability(caps) {
21
21
  const reader = createEngineCapReader({
22
22
  name: 'approvalsStreamLive',
23
23
  project: projectApprovalsStreamLiveCapability,
24
- makeUnobserved: () => ({ kind: 'unobserved' }),
25
24
  });
26
25
  export function noteEngineCapsForApprovalsStreamLive(baseUrl, caps, opts) {
27
26
  reader.note(baseUrl, caps, opts);
@@ -34,6 +33,8 @@ export function livePendingNeedsReconcile(reading) {
34
33
  }
35
34
  export function approvalsStreamLiveDoctorDetail(reading) {
36
35
  switch (reading.kind) {
36
+ case 'unreadable':
37
+ return unreadableReadingDetail('live approval push');
37
38
  case 'unobserved':
38
39
  return 'live approval push not observed — the engine reports it on /v1/capabilities; 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), so suspended asks are pulled by the reconcile cadence';
39
40
  case 'not_reported':
@@ -1,5 +1,7 @@
1
1
  export type AttachmentSeatReading = {
2
2
  readonly kind: 'unobserved';
3
+ } | {
4
+ readonly kind: 'unreadable';
3
5
  } | {
4
6
  readonly kind: 'not_reported';
5
7
  };
@@ -11,7 +11,6 @@ function projectAttachmentSeat(caps) {
11
11
  const reader = createEngineCapReader({
12
12
  name: 'attachmentSeat',
13
13
  project: projectAttachmentSeat,
14
- makeUnobserved: () => ({ kind: 'unobserved' }),
15
14
  });
16
15
  export function noteEngineCapsForAttachmentSeat(baseUrl, caps, opts) {
17
16
  reader.note(baseUrl, caps, opts);
@@ -1,5 +1,7 @@
1
1
  export type CallerCwdReading = {
2
2
  kind: 'unobserved';
3
+ } | {
4
+ kind: 'unreadable';
3
5
  } | {
4
6
  kind: 'not_reported';
5
7
  } | {
@@ -17,7 +17,6 @@ export function projectCallerCwdCapability(caps) {
17
17
  const reader = createEngineCapReader({
18
18
  name: 'callerCwd',
19
19
  project: projectCallerCwdCapability,
20
- makeUnobserved: () => ({ kind: 'unobserved' }),
21
20
  });
22
21
  export function observedCallerCwd(baseUrl = engineWireTarget()?.baseUrl) {
23
22
  return reader.observed(baseUrl);
@@ -19,6 +19,8 @@ export interface CenterWiringView {
19
19
  }
20
20
  export type CenterWiringReading = {
21
21
  kind: 'unobserved';
22
+ } | {
23
+ kind: 'unreadable';
22
24
  } | {
23
25
  kind: 'none';
24
26
  } | {
@@ -1,6 +1,6 @@
1
1
  import { capForDisplay } from './fleetTaskDesc.js';
2
2
  import { engineWireTarget } from './engineWireTarget.js';
3
- import { createEngineCapReader } from './engineCapReader.js';
3
+ import { createEngineCapReader, unreadableReadingDetail } from './engineCapReader.js';
4
4
  import { ownDataField } from './ownKey.js';
5
5
  const nonEmpty = (v) => typeof v === 'string' && v !== '';
6
6
  const isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
@@ -86,7 +86,6 @@ function projectCenterWiringOf(caps) {
86
86
  const reader = createEngineCapReader({
87
87
  name: 'centerWiring',
88
88
  project: projectCenterWiringCapability,
89
- makeUnobserved: () => ({ kind: 'unobserved' }),
90
89
  });
91
90
  export function observedCenterWiring(baseUrl = engineWireTarget()?.baseUrl) {
92
91
  return reader.observed(baseUrl);
@@ -96,7 +95,7 @@ function guarded(reading) {
96
95
  return undefined;
97
96
  try {
98
97
  const kind = reading.kind;
99
- if (kind === 'unobserved' || kind === 'none')
98
+ if (kind === 'unobserved' || kind === 'unreadable' || kind === 'none')
100
99
  return { kind };
101
100
  if (kind !== 'present')
102
101
  return undefined;
@@ -158,6 +157,8 @@ function failModeClause(v) {
158
157
  export function centerWiringDoctorDetail(reading) {
159
158
  const unobserved = 'center wiring not observed — the engine reports it on /v1/capabilities; this process has no usable capabilities reading cached for it, so whether this engine is connected to a configuration center is unknown';
160
159
  const r = guarded(reading);
160
+ if (r?.kind === 'unreadable')
161
+ return unreadableReadingDetail('center wiring');
161
162
  if (r?.kind === 'none') {
162
163
  return 'no configuration center on this deployment — the engine runs on its own local configuration; no quota lease or usage reporting goes through a center';
163
164
  }
@@ -20,7 +20,7 @@ export interface CloudModelRefIssue {
20
20
  readonly key: 'models' | 'roles' | 'atMention' | 'tierGroups' | 'activeTierGroup';
21
21
  readonly at?: string;
22
22
  readonly target?: string;
23
- readonly reason: 'dangling' | 'rejected' | 'unreadable' | 'unsupported' | 'duplicate' | 'shadowed';
23
+ readonly reason: 'dangling' | 'rejected' | 'unreadable' | 'unsupported' | 'duplicate' | 'shadowed' | 'unvouched';
24
24
  readonly outcome: 'dropped' | 'kept';
25
25
  }
26
26
  export interface CloudModelsDocOptions {
@@ -123,4 +123,4 @@ export interface CloudEffectiveNotice {
123
123
  readonly level: 'warn' | 'info';
124
124
  readonly text: string;
125
125
  }
126
- export declare function cloudEffectiveNotices(p: Pick<CloudProjection, 'effectiveWarnings' | 'budget' | 'runtimeCaps' | 'modelRefIssues'>): CloudEffectiveNotice[];
126
+ export declare function cloudEffectiveNotices(p: Pick<CloudProjection, 'effectiveWarnings' | 'budget' | 'runtimeCaps' | 'modelRefIssues' | 'droppedMcpServers'>): CloudEffectiveNotice[];