@sema-agent/client-core 0.98.0 → 0.100.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 (236) hide show
  1. package/CHANGELOG.md +376 -0
  2. package/README.md +61 -35
  3. package/dist/adapt/arms.js +120 -55
  4. package/dist/adapt/panelTasks.d.ts +3 -2
  5. package/dist/adapt/panelTasks.js +34 -11
  6. package/dist/adapt/textStream.d.ts +1 -1
  7. package/dist/adapt/textStream.js +51 -28
  8. package/dist/adapter/activeRunSelfHeal.d.ts +11 -3
  9. package/dist/adapter/activeRunSelfHeal.js +210 -294
  10. package/dist/adapter/downstream/eventToSdkMessage.d.ts +25 -6
  11. package/dist/adapter/downstream/eventToSdkMessage.js +176 -12
  12. package/dist/adapter/downstream/terminalToSdkResult.js +10 -0
  13. package/dist/adapter/downstream/wiringManifestView.d.ts +3 -1
  14. package/dist/adapter/downstream/wiringManifestView.js +1 -0
  15. package/dist/adapter/runStream.js +82 -40
  16. package/dist/adapter/types.d.ts +2 -0
  17. package/dist/adapter/types.js +2 -1
  18. package/dist/agentSession/engineSessionBackgroundStop.js +16 -8
  19. package/dist/agentsWireCaps.d.ts +7 -3
  20. package/dist/agentsWireCaps.js +133 -39
  21. package/dist/cloudConfigWireCaps.d.ts +10 -1
  22. package/dist/cloudConfigWireCaps.js +181 -65
  23. package/dist/compactionStatus.d.ts +34 -0
  24. package/dist/compactionStatus.js +196 -0
  25. package/dist/controlRouter.d.ts +4 -2
  26. package/dist/controlRouter.js +39 -51
  27. package/dist/coreValuePorts.d.ts +26 -1
  28. package/dist/coreValuePorts.js +10 -0
  29. package/dist/corruptSessionCopy.d.ts +2 -1
  30. package/dist/corruptSessionCopy.js +3 -3
  31. package/dist/decideFailureNote.js +3 -3
  32. package/dist/declared/supportFloorDeclared.js +1 -1
  33. package/dist/detachWire.js +3 -2
  34. package/dist/devicesFace.d.ts +34 -0
  35. package/dist/devicesFace.js +90 -0
  36. package/dist/displayUntrusted.d.ts +1 -0
  37. package/dist/displayUntrusted.js +153 -5
  38. package/dist/engineAgentPanelStore.d.ts +14 -4
  39. package/dist/engineAgentPanelStore.js +14 -0
  40. package/dist/engineCapReader.d.ts +2 -1
  41. package/dist/engineCapReader.js +7 -5
  42. package/dist/engineCapsCache.d.ts +8 -0
  43. package/dist/engineCapsCache.js +59 -6
  44. package/dist/engineCapsChange.d.ts +2 -0
  45. package/dist/engineCapsChange.js +9 -0
  46. package/dist/engineErrorCodes.d.ts +11 -2
  47. package/dist/engineErrorCodes.js +11 -12
  48. package/dist/engineRowRef.js +4 -0
  49. package/dist/engineSessionMessages.d.ts +1 -0
  50. package/dist/engineSessionMessages.js +7 -1
  51. package/dist/engineTargetSeat.d.ts +27 -0
  52. package/dist/engineTargetSeat.js +199 -0
  53. package/dist/engineToolDirectory.d.ts +3 -0
  54. package/dist/engineToolDirectory.js +4 -2
  55. package/dist/engineWireFor.js +3 -8
  56. package/dist/env/uiLanguage.d.ts +2 -1
  57. package/dist/env/uiLanguage.js +3 -2
  58. package/dist/eventLeg.d.ts +7 -5
  59. package/dist/eventLeg.js +68 -49
  60. package/dist/fileHistoryCaptureCapability.d.ts +2 -4
  61. package/dist/fileHistoryCaptureCapability.js +12 -22
  62. package/dist/fileHistoryLegNotice.d.ts +2 -1
  63. package/dist/fileHistoryLegNotice.js +3 -3
  64. package/dist/frameBinding.d.ts +3 -0
  65. package/dist/frameBinding.js +12 -0
  66. package/dist/gateOutcome.js +16 -2
  67. package/dist/gateVocabulary.d.ts +8 -7
  68. package/dist/gateVocabulary.js +71 -73
  69. package/dist/generated/engineFactTables.d.ts +18 -1
  70. package/dist/generated/engineFactTables.js +56 -0
  71. package/dist/generated/engineNoticeTables.js +12 -3
  72. package/dist/generated/settingsSchemaFns.d.ts +18 -0
  73. package/dist/generated/settingsSchemaFns.js +70 -0
  74. package/dist/generated/settingsSchemaHooksClassifier.d.ts +2 -0
  75. package/dist/generated/settingsSchemaHooksClassifier.js +254 -0
  76. package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -0
  77. package/dist/generated/settingsSchemaMcpRequestEntry.js +162 -0
  78. package/dist/generated/settingsSchemaTables.d.ts +2 -0
  79. package/dist/generated/settingsSchemaTables.js +9 -0
  80. package/dist/generated/settingsSchemaTypes.d.ts +133 -0
  81. package/dist/generated/settingsSchemaTypes.js +1 -0
  82. package/dist/hitl/allowRuleShadow.d.ts +2 -1
  83. package/dist/hitl/allowRuleShadow.js +18 -15
  84. package/dist/hitl/approvalCardReasons.d.ts +3 -2
  85. package/dist/hitl/approvalCardReasons.js +29 -30
  86. package/dist/hitl/approvalHolds.d.ts +17 -0
  87. package/dist/hitl/approvalHolds.js +114 -29
  88. package/dist/hitl/askClassPair.d.ts +2 -2
  89. package/dist/hitl/askClassPair.js +16 -0
  90. package/dist/hitl/askGateWire.js +2 -1
  91. package/dist/hitl/askSettlementLine.d.ts +14 -4
  92. package/dist/hitl/askSettlementLine.js +67 -47
  93. package/dist/hitl/attributionLadder.d.ts +23 -0
  94. package/dist/hitl/attributionLadder.js +67 -0
  95. package/dist/hitl/automaticAnswerVerdict.d.ts +2 -1
  96. package/dist/hitl/automaticAnswerVerdict.js +8 -8
  97. package/dist/hitl/crashConvergedNotice.d.ts +5 -4
  98. package/dist/hitl/crashConvergedNotice.js +28 -41
  99. package/dist/hitl/decisionLedger.d.ts +55 -0
  100. package/dist/hitl/decisionLedger.js +156 -0
  101. package/dist/hitl/decisionRefusal.d.ts +1 -0
  102. package/dist/hitl/decisionRefusal.js +1 -1
  103. package/dist/hitl/decisionSend.d.ts +113 -0
  104. package/dist/hitl/decisionSend.js +579 -0
  105. package/dist/hitl/frameRouter.js +7 -8
  106. package/dist/hitl/hitlBridge.d.ts +1 -0
  107. package/dist/hitl/hitlBridge.js +21 -9
  108. package/dist/hitl/persistedRulesWire.d.ts +6 -5
  109. package/dist/hitl/persistedRulesWire.js +55 -73
  110. package/dist/hitl/planReviewWire.d.ts +5 -0
  111. package/dist/hitl/planReviewWire.js +53 -76
  112. package/dist/hitl/sessionPolicyDeliverable.d.ts +2 -0
  113. package/dist/hitl/sessionPolicyDeliverable.js +49 -9
  114. package/dist/hitl/sessionPolicyWire.d.ts +2 -1
  115. package/dist/hitl/sessionPolicyWire.js +23 -24
  116. package/dist/hitl/suspendedReopen.d.ts +2 -1
  117. package/dist/hitl/suspendedReopen.js +7 -10
  118. package/dist/hitl/toolApprovalWire.d.ts +17 -5
  119. package/dist/hitl/toolApprovalWire.js +120 -147
  120. package/dist/hooksWireCaps.js +145 -56
  121. package/dist/host.d.ts +21 -0
  122. package/dist/hostTurnSettings.d.ts +110 -0
  123. package/dist/hostTurnSettings.js +692 -0
  124. package/dist/humanTurn.d.ts +15 -1
  125. package/dist/humanTurn.js +100 -12
  126. package/dist/humanTurnRead.d.ts +2 -0
  127. package/dist/humanTurnRead.js +22 -1
  128. package/dist/i18n/messages.en.d.ts +713 -0
  129. package/dist/i18n/messages.en.js +713 -0
  130. package/dist/i18n/messages.zh.js +854 -1
  131. package/dist/index.d.ts +45 -13
  132. package/dist/index.js +27 -9
  133. package/dist/inheritEnvCapability.d.ts +4 -2
  134. package/dist/inheritEnvCapability.js +12 -5
  135. package/dist/inheritEnvWire.d.ts +3 -2
  136. package/dist/inheritEnvWire.js +14 -16
  137. package/dist/injectionLaneNotice.d.ts +5 -0
  138. package/dist/injectionLaneNotice.js +46 -0
  139. package/dist/interactiveHalt.js +17 -6
  140. package/dist/liveModelCatalog.d.ts +2 -1
  141. package/dist/liveModelCatalog.js +14 -8
  142. package/dist/mcpPanel.d.ts +10 -1
  143. package/dist/mcpPanel.js +42 -13
  144. package/dist/mcpProbeCapability.d.ts +2 -1
  145. package/dist/mcpProbeCapability.js +7 -11
  146. package/dist/mcpProbeWire.d.ts +3 -1
  147. package/dist/mcpProbeWire.js +5 -0
  148. package/dist/mcpReconnect.js +2 -2
  149. package/dist/mcpToolToggles.d.ts +6 -0
  150. package/dist/mcpToolToggles.js +46 -0
  151. package/dist/mcpWireCaps.d.ts +43 -3
  152. package/dist/mcpWireCaps.js +194 -59
  153. package/dist/mcpWireTransport.d.ts +4 -0
  154. package/dist/mcpWireTransport.js +18 -0
  155. package/dist/memoryEntriesWire.js +5 -10
  156. package/dist/model/onboardJudgement.d.ts +93 -0
  157. package/dist/model/onboardJudgement.js +415 -0
  158. package/dist/model/tierVocabulary.d.ts +2 -0
  159. package/dist/model/tierVocabulary.js +17 -5
  160. package/dist/modelCapabilityProbe.d.ts +65 -0
  161. package/dist/modelCapabilityProbe.js +601 -0
  162. package/dist/modelIdentityResolvability.d.ts +5 -0
  163. package/dist/modelIdentityResolvability.js +49 -22
  164. package/dist/ownKey.d.ts +33 -0
  165. package/dist/ownKey.js +109 -0
  166. package/dist/panelReduce.d.ts +11 -1
  167. package/dist/panelReduce.js +95 -16
  168. package/dist/panelStateHolder.d.ts +1 -0
  169. package/dist/panelStateHolder.js +10 -1
  170. package/dist/permissionRuleIssue.d.ts +2 -1
  171. package/dist/permissionRuleIssue.js +23 -20
  172. package/dist/permissionRulesWriteCapability.d.ts +2 -1
  173. package/dist/permissionRulesWriteCapability.js +8 -6
  174. package/dist/pluginHooksWire.d.ts +46 -3
  175. package/dist/pluginHooksWire.js +220 -12
  176. package/dist/prepareRefusal.d.ts +8 -0
  177. package/dist/prepareRefusal.js +72 -0
  178. package/dist/printModelStatus.d.ts +63 -0
  179. package/dist/printModelStatus.js +155 -0
  180. package/dist/request/taskRequest.d.ts +16 -10
  181. package/dist/request/taskRequest.js +195 -58
  182. package/dist/resumeRefusalCopy.d.ts +14 -9
  183. package/dist/resumeRefusalCopy.js +89 -144
  184. package/dist/retryStatus.d.ts +55 -0
  185. package/dist/retryStatus.js +155 -19
  186. package/dist/ruleRefusalSentence.d.ts +4 -0
  187. package/dist/ruleRefusalSentence.js +58 -0
  188. package/dist/sandboxWire.d.ts +2 -1
  189. package/dist/sandboxWire.js +55 -21
  190. package/dist/scenarioRefusal.d.ts +10 -0
  191. package/dist/scenarioRefusal.js +111 -0
  192. package/dist/seam.d.ts +48 -9
  193. package/dist/seam.js +54 -5
  194. package/dist/seatContract.d.ts +272 -14
  195. package/dist/seatContract.js +711 -23
  196. package/dist/selfOrchestrationDenial.js +7 -19
  197. package/dist/sessionIdSegment.d.ts +6 -0
  198. package/dist/sessionIdSegment.js +24 -0
  199. package/dist/sessionMemoryStatus.js +6 -11
  200. package/dist/sessionPolicyCapability.d.ts +2 -1
  201. package/dist/sessionPolicyCapability.js +8 -6
  202. package/dist/settingsCredentialKeyPaths.d.ts +8 -0
  203. package/dist/settingsCredentialKeyPaths.js +219 -0
  204. package/dist/settingsHooksSalvage.d.ts +44 -0
  205. package/dist/settingsHooksSalvage.js +211 -0
  206. package/dist/skillToolListsCapability.d.ts +2 -1
  207. package/dist/skillToolListsCapability.js +8 -6
  208. package/dist/skillsWireCaps.d.ts +0 -3
  209. package/dist/skillsWireCaps.js +1 -2
  210. package/dist/sseIdleTriage.js +3 -2
  211. package/dist/subagent/engineSubagentResume.js +15 -9
  212. package/dist/subagentContentStore.d.ts +4 -0
  213. package/dist/subagentContentStore.js +157 -27
  214. package/dist/toolHistoryMismatch.js +3 -2
  215. package/dist/toolRoster.d.ts +3 -2
  216. package/dist/toolRoster.js +21 -20
  217. package/dist/toolsFlagNames.d.ts +8 -0
  218. package/dist/toolsFlagNames.js +110 -0
  219. package/dist/turnFailurePresentation.d.ts +12 -1
  220. package/dist/turnFailurePresentation.js +220 -15
  221. package/dist/usageAnalytics.js +6 -11
  222. package/dist/userAttachmentEnvelope.d.ts +1 -0
  223. package/dist/userAttachmentEnvelope.js +37 -7
  224. package/dist/userAttachmentSeat.d.ts +21 -0
  225. package/dist/userAttachmentSeat.js +75 -0
  226. package/dist/userInvokedSkillsCapability.d.ts +11 -1
  227. package/dist/userInvokedSkillsCapability.js +31 -7
  228. package/dist/wireErrorTriage.d.ts +7 -0
  229. package/dist/wireErrorTriage.js +23 -1
  230. package/dist/wireFailureShape.js +2 -1
  231. package/dist/wireRefusalCopy.d.ts +62 -16
  232. package/dist/wireRefusalCopy.js +307 -148
  233. package/dist/workflowsJournalCapability.d.ts +2 -1
  234. package/dist/workflowsJournalCapability.js +8 -6
  235. package/docs/INTEGRATION-CLIENTS.md +951 -42
  236. package/package.json +5 -5
@@ -3,7 +3,7 @@ import { eventToSdkMessage, turnEndUsage } from './downstream/eventToSdkMessage.
3
3
  import { turnUsageToModelUsage } from './downstream/turnUsageToModelUsage.js';
4
4
  import { readRunCostFacts, terminalToSdkResult, TOOL_INPUT_JOIN_MAX_CALLS } from './downstream/terminalToSdkResult.js';
5
5
  import { terminalRunIdOf, terminalUuidOf } from '../engineRowRef.js';
6
- import { coerceOutput, publishSubagentContentEvent } from '../subagentContentStore.js';
6
+ import { publishSubagentContentEvent, subagentContentEventOf } from '../subagentContentStore.js';
7
7
  import { OUTPUT_INVALID, activeRunBusyCarrierOf, classifyActiveRunBusy, isCorruptSessionCode, isLimitsExceededCode } from '../engineErrorCodes.js';
8
8
  import { doneResultView, isReplayedTerminal } from '../replayBody.js';
9
9
  import { readFrameStringKey } from '../ownKey.js';
@@ -13,11 +13,25 @@ import { classifyToolHistoryMismatch, noteToolHistoryTerminal, openToolHistoryGe
13
13
  import { isReviewPark, readRunTerminal, runTerminalCode } from '../runTerminal.js';
14
14
  import { parkHeldCallIdOf } from '../engineRowRef.js';
15
15
  import { providerRejectionResend } from '../providerRejection.js';
16
- import { washCredentials } from '../displayUntrusted.js';
16
+ import { displayUntrusted, washCredentials } from '../displayUntrusted.js';
17
17
  import { PROMPT_BLOCKED_BY_HOOK_ROW_LEAD, runBlockedText } from '../promptBlockedCopy.js';
18
18
  import { ccToolDenialKindForToolEnd, gateDeniedBy, gateOutcomeOf } from '../gateOutcome.js';
19
19
  import { resumeReopenContent, resumeReopenFromError, suspendedReopenOf } from '../hitl/suspendedReopen.js';
20
20
  import { isCcToolDenialKind, isCcToolDenialKindADenial, isGateDeniedByWord } from '../gateVocabulary.js';
21
+ const ENGINE_TURN_USAGE_KEYS = Object.freeze(['inputTokens', 'totalInputTokens', 'outputTokens', 'cacheReadTokens', 'cacheWriteTokens', 'costMicroUsd']);
22
+ const _engineTurnUsageKeysPin = true;
23
+ void _engineTurnUsageKeysPin;
24
+ function closedEngineTurnUsage(raw) {
25
+ const out = {};
26
+ let unknownKeys = 0;
27
+ for (const k of Object.keys(raw)) {
28
+ if (ENGINE_TURN_USAGE_KEYS.includes(k))
29
+ out[k] = raw[k];
30
+ else
31
+ unknownKeys++;
32
+ }
33
+ return { usage: out, unknownKeys };
34
+ }
21
35
  const mainLane = () => ({ lane: 'main' });
22
36
  function emitChromeFireAndForget(ctx, event) {
23
37
  try {
@@ -111,7 +125,23 @@ export function isModelOutputErrorRowText(text) {
111
125
  return text.trimStart().startsWith(`${MODEL_OUTPUT_ERROR_PREFIX}:`);
112
126
  }
113
127
  export const OUTCOME_UNKNOWN_ROW_PREFIX = 'Outcome unknown';
114
- function syntheticTerminalRow(ctx, identity, replayed, text, historyKey, resend) {
128
+ function apiErrorRowBitsOf(read) {
129
+ try {
130
+ const status = read.carrier.status;
131
+ const providerCondition = read.detail !== undefined && ownDataBit(read.detail, 'kind') === PROVIDER_REJECTED_DETAIL ? nonEmptyStr(ownDataBit(read.detail, 'providerCondition')) : undefined;
132
+ const errorCode = nonEmptyStr(read.carrier.code);
133
+ const bits = {
134
+ ...(typeof status === 'number' && Number.isInteger(status) && status >= 100 && status <= 999 ? { status } : {}),
135
+ ...(providerCondition !== undefined ? { providerCondition } : {}),
136
+ ...(errorCode !== undefined ? { errorCode } : {}),
137
+ };
138
+ return Object.keys(bits).length > 0 ? bits : undefined;
139
+ }
140
+ catch {
141
+ return undefined;
142
+ }
143
+ }
144
+ function syntheticTerminalRow(ctx, identity, replayed, text, historyKey, resend, apiError, owner) {
115
145
  return stamp(ctx, {
116
146
  uuid: identity.runId === undefined ? undefined : terminalUuidOf(identity.runId, identity.label),
117
147
  session_id: undefined,
@@ -121,9 +151,38 @@ function syntheticTerminalRow(ctx, identity, replayed, text, historyKey, resend)
121
151
  _sema_api_error_message: true,
122
152
  ...(historyKey !== undefined ? { _sema_tool_history_mismatch: historyKey } : {}),
123
153
  ...(resend !== undefined ? { _sema_provider_rejection_resend: { resend: resend.resend, text: resend.text, kind: resend.kind } } : {}),
154
+ ...(apiError?.status !== undefined ? { _sema_api_error_status: apiError.status } : {}),
155
+ ...(apiError?.providerCondition !== undefined ? { _sema_provider_condition: apiError.providerCondition } : {}),
156
+ ...(apiError?.errorCode !== undefined ? { _sema_api_error_code: apiError.errorCode } : {}),
124
157
  ...(replayed ? { _sema_replay: true } : {}),
158
+ ...(owner !== undefined ? { _sema_terminal_owner: owner } : {}),
125
159
  });
126
160
  }
161
+ function withTerminalOwner(frame, owner) {
162
+ return owner === undefined ? frame : { ...frame, _sema_terminal_owner: owner };
163
+ }
164
+ function terminalOwnerOfStream(ctx, busy, busyHandle, runId) {
165
+ const hostString = (k) => {
166
+ try {
167
+ const d = Object.getOwnPropertyDescriptor(ctx, k);
168
+ return d !== undefined && 'value' in d && typeof d.value === 'string' && d.value.length > 0 ? d.value : undefined;
169
+ }
170
+ catch {
171
+ return undefined;
172
+ }
173
+ };
174
+ const followed = hostString('followedRun');
175
+ if (followed !== undefined)
176
+ return { kind: 'followed-run', taskId: followed, ...(runId !== undefined ? { runId } : {}) };
177
+ if (!busy)
178
+ return undefined;
179
+ const requestId = hostString('requestId');
180
+ return {
181
+ kind: 'request-not-sent',
182
+ ...(requestId !== undefined ? { requestId } : {}),
183
+ ...(typeof busyHandle === 'string' && busyHandle.length > 0 ? { heldBy: busyHandle } : {}),
184
+ };
185
+ }
127
186
  function engineResultOf(result) {
128
187
  const v = doneResultView(result);
129
188
  return v.replay ? (v.engine ?? result) : result;
@@ -233,7 +292,7 @@ function ownTerminalSnapshot(result) {
233
292
  const ts = ownDataCopy(t, ['kind', 'code', 'message']);
234
293
  const detail = plainObjectOf(ownDataBit(t, 'detail'));
235
294
  if (detail !== undefined)
236
- ts.detail = ownDataCopy(detail, ['kind', 'status', 'code']);
295
+ ts.detail = ownDataCopy(detail, ['kind', 'status', 'code', 'providerCondition']);
237
296
  snap.terminal = ts;
238
297
  return snap;
239
298
  }
@@ -314,12 +373,10 @@ function apiErrorTerminalOfRowFrame(ev, facts) {
314
373
  }
315
374
  const reportedDroppedTypes = new Set();
316
375
  const DROPPED_TYPE_MEMO_CAP = 64;
317
- const DROPPED_TYPE_DISPLAY_CAP = 60;
318
- function sanitizeFrameType(type) {
319
- const flattened = type.replace(/[\u0000-\u001f\u007f-\u009f]/g, '\uFFFD');
320
- return flattened.length > DROPPED_TYPE_DISPLAY_CAP
321
- ? `${flattened.slice(0, DROPPED_TYPE_DISPLAY_CAP)}…`
322
- : flattened;
376
+ const DROPPED_TYPE_DISPLAY_MAX = 60;
377
+ function droppedTypeForLog(type) {
378
+ const full = displayUntrusted(type);
379
+ return full.length > DROPPED_TYPE_DISPLAY_MAX ? `${displayUntrusted(type, { max: DROPPED_TYPE_DISPLAY_MAX })}…` : full;
323
380
  }
324
381
  const DROPPED_WHY_SENTENCE = Object.freeze({
325
382
  duplicate_seq: 'a frame with this event id was already consumed on this run stream, so it was dropped as a replay (durable idempotency, contract 02 §1.1). ' +
@@ -348,7 +405,7 @@ function reportDroppedFrame(why, type, ctx) {
348
405
  return;
349
406
  if (reportedDroppedTypes.size < DROPPED_TYPE_MEMO_CAP)
350
407
  reportedDroppedTypes.add(type);
351
- console.error(`[client-core] dropped an engine frame (${why}): type="${sanitizeFrameType(type)}" — ${droppedWhySentence(why)}`);
408
+ console.error(`[client-core] dropped an engine frame (${why}): type="${droppedTypeForLog(type)}" — ${droppedWhySentence(why)}`);
352
409
  }
353
410
  export function _resetDroppedFrameReportForTest() {
354
411
  reportedDroppedTypes.clear();
@@ -512,27 +569,9 @@ async function* runStreamInner(events, ctx, handle = {}) {
512
569
  }
513
570
  }
514
571
  }
515
- if ((ev.type === 'text_delta' ||
516
- ev.type === 'reasoning_delta' ||
517
- ev.type === 'text' ||
518
- ev.type === 'reasoning' ||
519
- ev.type === 'tool_start' ||
520
- ev.type === 'tool_end') &&
521
- typeof ev.parentToolCallId === 'string') {
522
- const sub = ev;
523
- publishSubagentContentEvent({
524
- type: sub.type,
525
- taskId: sub.taskId ?? sub.parentToolCallId,
526
- parentToolCallId: sub.parentToolCallId,
527
- delta: sub.delta,
528
- text: sub.text,
529
- eventId: sub.eventId,
530
- toolCallId: sub.toolCallId,
531
- toolName: sub.toolName,
532
- args: sub.args,
533
- output: coerceOutput(sub.output),
534
- isError: sub.isError,
535
- });
572
+ const sub = subagentContentEventOf(ev);
573
+ if (sub !== undefined) {
574
+ publishSubagentContentEvent(sub);
536
575
  if (!(ctx.lane === 'print' && (sub.type === 'tool_start' || sub.type === 'tool_end')))
537
576
  continue;
538
577
  }
@@ -546,10 +585,10 @@ async function* runStreamInner(events, ctx, handle = {}) {
546
585
  const responseModelRaw = turnEndLoose.responseModel;
547
586
  const responseModelWord = typeof responseModelRaw === 'string' && responseModelRaw.length > 0 ? responseModelRaw : undefined;
548
587
  const rawUsage = ev.usage;
549
- const engineUsage = typeof rawUsage === 'object' && rawUsage !== null && !Array.isArray(rawUsage)
550
- ? rawUsage
551
- : undefined;
552
- const usage = engineUsage !== undefined ? turnUsageToModelUsage(engineUsage) : undefined;
588
+ const closedUsage = typeof rawUsage === 'object' && rawUsage !== null && !Array.isArray(rawUsage) ? closedEngineTurnUsage(rawUsage) : undefined;
589
+ const engineUsage = closedUsage?.usage;
590
+ const usageUnknownKeys = closedUsage !== undefined && closedUsage.unknownKeys > 0 ? { engineUsageUnknownKeys: closedUsage.unknownKeys } : {};
591
+ const usage = closedUsage !== undefined ? turnUsageToModelUsage(rawUsage) : undefined;
553
592
  const measured = turnEndUsage(ev);
554
593
  if (engineUsage === undefined || usage === undefined) {
555
594
  usageMissingObserved = true;
@@ -577,6 +616,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
577
616
  laneProof: mainLane(),
578
617
  usage,
579
618
  engineUsage,
619
+ ...usageUnknownKeys,
580
620
  ...(usageMissing ? { usageMissing: true } : {}),
581
621
  ...(stopWord !== undefined ? { stopReason: stopWord } : {}),
582
622
  ...(responseModelWord !== undefined ? { responseModel: responseModelWord } : {}),
@@ -600,6 +640,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
600
640
  parentToolCallId: parent,
601
641
  usage,
602
642
  engineUsage,
643
+ ...usageUnknownKeys,
603
644
  ...(usageMissing ? { usageMissing: true } : {}),
604
645
  ...(stopWord !== undefined ? { stopReason: stopWord } : {}),
605
646
  ...(responseModelWord !== undefined ? { responseModel: responseModelWord } : {}),
@@ -640,6 +681,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
640
681
  const doneTerminal = rowFacts.doneTerminal;
641
682
  const terminalRunId = terminalRunIdOf(ev);
642
683
  const replayed = isReplayedTerminal(ev);
684
+ const owner = terminalOwnerOfStream(ctx, rowFacts.busy.busy, rowFacts.busy.handle, terminalRunId);
643
685
  const errText = rowFacts.errText;
644
686
  if (errText) {
645
687
  const busy = rowFacts.busy;
@@ -680,7 +722,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
680
722
  : '';
681
723
  const resend = historyTerminal.detail !== undefined ? providerRejectionResend(historyTerminal.detail, historyTerminal.carrier) : undefined;
682
724
  const resendLine = resend !== undefined ? `\n${resend.text}` : '';
683
- yield syntheticTerminalRow(ctx, { runId: terminalRunId, label: 'terminal-row' }, replayed, `${sessionUnreadable ? `${rowText}\n${corruptSessionContent()}` : rowText}${abortNote !== undefined ? `\n${abortNote}` : ''}${rewindLine}${resendLine}`, historyMismatch === null ? undefined : toolHistoryMismatchRowKey(historyMismatch, historyRepeat, rewindOffered), resend);
725
+ yield syntheticTerminalRow(ctx, { runId: terminalRunId, label: 'terminal-row' }, replayed, `${sessionUnreadable ? `${rowText}\n${corruptSessionContent()}` : rowText}${abortNote !== undefined ? `\n${abortNote}` : ''}${rewindLine}${resendLine}`, historyMismatch === null ? undefined : toolHistoryMismatchRowKey(historyMismatch, historyRepeat, rewindOffered), resend, arm === 'apiError' ? apiErrorRowBitsOf(historyTerminal) : undefined, owner);
684
726
  }
685
727
  else {
686
728
  const echoedSid = ev.type === 'done' ? engineSessionIdOf(engineResultOf(ev.result)) : undefined;
@@ -706,7 +748,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
706
748
  emitChromeFireAndForget(ctx, { kind: 'run_cost_reconciled', laneProof: mainLane(), ...costFacts.reconcile });
707
749
  }
708
750
  }
709
- const resultFrame = terminalToSdkResult(ev, ctx, {
751
+ const resultFrame = withTerminalOwner(terminalToSdkResult(ev, ctx, {
710
752
  usageMissingObserved,
711
753
  nestedUsageByTask,
712
754
  toolInputByCallId,
@@ -716,13 +758,13 @@ async function* runStreamInner(events, ctx, handle = {}) {
716
758
  streamSawRunOpen,
717
759
  deniedToolNameByCallId,
718
760
  deniedCallsOverflowed,
719
- });
761
+ }), owner);
720
762
  const outcome = resultFrame.
721
763
  _sema_outcome;
722
764
  if (outcome === 'unknown') {
723
765
  const errs = resultFrame.errors;
724
766
  const sentence = Array.isArray(errs) && typeof errs[0] === 'string' ? errs[0] : 'the run ended without a terminal record this client can read';
725
- yield syntheticTerminalRow(ctx, { runId: terminalRunId, label: 'outcome-unknown' }, replayed, `${OUTCOME_UNKNOWN_ROW_PREFIX}: ${sentence}`);
767
+ yield syntheticTerminalRow(ctx, { runId: terminalRunId, label: 'outcome-unknown' }, replayed, `${OUTCOME_UNKNOWN_ROW_PREFIX}: ${sentence}`, undefined, undefined, undefined, owner);
726
768
  }
727
769
  yield terminalFactOf(ev, doneTerminal);
728
770
  yield resultFrame;
@@ -23,6 +23,8 @@ export interface EmitContext {
23
23
  emitChrome?(event: ChromeEvent): void | Promise<void>;
24
24
  onDroppedFrame?(info: DroppedFrameInfo): void;
25
25
  boundRun?(frame: AgentEvent): EngineBoundRun | undefined;
26
+ requestId?: string;
27
+ followedRun?: string;
26
28
  }
27
29
  export declare function stamp<T extends {
28
30
  uuid?: string | undefined;
@@ -1,5 +1,6 @@
1
1
  export function eventSeq(ev) {
2
- return ev.id;
2
+ const id = ev.id;
3
+ return typeof id === 'string' && id.length > 0 ? id : undefined;
3
4
  }
4
5
  export function uuid() {
5
6
  const g = typeof globalThis !== 'undefined' ? globalThis.crypto : undefined;
@@ -4,7 +4,7 @@ import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, noAnswerDetailF
4
4
  import { engineWireDebugEnabled } from '../engineWireTarget.js';
5
5
  import { hostLog } from '../host.js';
6
6
  import { isSessionIdPathSegment } from '../sessionIdSegment.js';
7
- import { wireFailureTextsOf } from '../wireFailureShape.js';
7
+ import { readThrownSlot, readThrownStatusSlot, THROWN_SLOT_UNREADABLE, wireFailureTextsOf } from '../wireFailureShape.js';
8
8
  import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
9
9
  const NOTHING_SENT = ' — nothing sent';
10
10
  const DETAIL_BAD_SESSION = `the session id must be a non-empty string that is not a dot segment${NOTHING_SENT}`;
@@ -155,13 +155,16 @@ function refusedWhyOf(rawCode, status) {
155
155
  return undefined;
156
156
  }
157
157
  function diagOf(e) {
158
- const o = e;
159
- const rawStatus = o.status ?? o.statusCode;
158
+ const rawStatus = readThrownStatusSlot(e);
159
+ const rawCode = readThrownSlot(e, 'errorCode', 'data');
160
+ if (rawStatus === THROWN_SLOT_UNREADABLE || rawCode === THROWN_SLOT_UNREADABLE)
161
+ return undefined;
160
162
  const status = typeof rawStatus === 'number' && Number.isInteger(rawStatus) ? rawStatus : undefined;
161
- const errorCode = typeof o.errorCode === 'string' && o.errorCode.length > 0 ? o.errorCode : undefined;
162
- const ra = o.retryAfterMs;
163
+ const errorCode = typeof rawCode === 'string' && rawCode.length > 0 ? rawCode : undefined;
164
+ const ra = readThrownSlot(e, 'retryAfterMs', 'data');
163
165
  const retryAfterMs = typeof ra === 'number' && Number.isFinite(ra) && ra >= 0 ? ra : undefined;
164
- const message = typeof o.message === 'string' && o.message.length > 0 ? o.message : undefined;
166
+ const rawMessage = readThrownSlot(e, 'message');
167
+ const message = typeof rawMessage === 'string' && rawMessage.length > 0 ? rawMessage : undefined;
165
168
  return {
166
169
  ...(status !== undefined ? { status } : {}),
167
170
  ...(errorCode !== undefined ? { errorCode } : {}),
@@ -169,6 +172,10 @@ function diagOf(e) {
169
172
  ...(message !== undefined ? { message } : {}),
170
173
  };
171
174
  }
175
+ function unreadableFailure() {
176
+ note('error (the failure could not be read)');
177
+ return { kind: 'error', detail: 'the stop request failed (the error could not be read)' };
178
+ }
172
179
  function classifyFailure(e) {
173
180
  try {
174
181
  if (typeof e !== 'object' || e === null) {
@@ -176,6 +183,8 @@ function classifyFailure(e) {
176
183
  return { kind: 'error', detail: `the stop request failed: ${wireFailureTextsOf(e).reason.slice(0, 200)}` };
177
184
  }
178
185
  const d = diagOf(e);
186
+ if (d === undefined)
187
+ return unreadableFailure();
179
188
  if (d.status === undefined && d.errorCode === undefined) {
180
189
  const noAnswer = noAnswerDetailFrom(e);
181
190
  if (noAnswer !== null) {
@@ -200,8 +209,7 @@ function classifyFailure(e) {
200
209
  return { kind: 'error', detail: (d.message ?? 'the stop request failed').slice(0, 300), ...diag };
201
210
  }
202
211
  catch {
203
- note('error (the failure could not be read)');
204
- return { kind: 'error', detail: 'the stop request failed (the error could not be read)' };
212
+ return unreadableFailure();
205
213
  }
206
214
  }
207
215
  export async function stopEngineSessionBackground(sessionId, body, opts) {
@@ -3,9 +3,6 @@ import type { ReasoningEffort } from './effortWire.js';
3
3
  import type { ToolRosterView } from './toolRoster.js';
4
4
  import { type AssemblyRefusal } from './wireFailureShape.js';
5
5
  export declare const MAX_TASK_AGENTS = 32;
6
- export declare const MAX_AGENT_TEXT_CHARS = 4096;
7
- export declare const MAX_AGENT_SYSTEM_PROMPT_CHARS = 65536;
8
- export declare const MAX_AGENT_SKILL_CONTENT_CHARS = 1048576;
9
6
  export declare const TASK_AGENT_WIRE_FIELDS: readonly ["name", "whenToUse", "whenToUseLean", "allowTools", "denyTools", "skills", "background", "isolation", "model", "thinking", "systemPrompt", "maxTurns", "memory", "observer", "observerMessage"];
10
7
  export type WireThinkingLevel = ReasoningEffort;
11
8
  export type TaskAgentWireSkill = {
@@ -49,6 +46,12 @@ export type AgentDefLike = {
49
46
  source: string;
50
47
  getSystemPrompt?: (arg?: unknown) => string;
51
48
  };
49
+ export declare const REFUSED_PROJECT_SERVER_CAUSES: readonly ["rejected", "not_approved", "disabled", "policy_blocked", "plugin_only", "malformed_config"];
50
+ export type RefusedProjectServerCause = (typeof REFUSED_PROJECT_SERVER_CAUSES)[number];
51
+ export interface RefusedProjectServer {
52
+ readonly name: string;
53
+ readonly cause: RefusedProjectServerCause;
54
+ }
52
55
  export type ProjectAgentsDeps = {
53
56
  resolveModel: (frontmatterModel: string | undefined) => {
54
57
  model: string;
@@ -58,6 +61,7 @@ export type ProjectAgentsDeps = {
58
61
  skillSpecs?: TaskAgentWireSkill[];
59
62
  toolRoster?: ToolRosterView | null;
60
63
  agentSourceLabel?: (agentName: string) => string | undefined;
64
+ refusedProjectServers?: readonly RefusedProjectServer[];
61
65
  };
62
66
  export type ProjectAgentsResult = {
63
67
  agents: TaskAgentWireItem[] | undefined;
@@ -8,11 +8,10 @@ import { CORE_PROTOCOL_PREFIXES } from './generated/toolNameTables.js';
8
8
  import { rosterMountedNames } from './rosterMountedNames.js';
9
9
  import { capForDisplay } from './fleetTaskDesc.js';
10
10
  import { deniedEntryToolName } from './engineToolDirectory.js';
11
- import { assemblyRefusal } from './wireFailureShape.js';
11
+ import { hasCoreValuePort, mcpNamespace, mintNamespacePrefix } from './coreValuePorts.js';
12
+ import { msg, renderMessage } from './i18n/translate.js';
13
+ import { assemblyRefusal, withAssemblyRefusal } from './wireFailureShape.js';
12
14
  export const MAX_TASK_AGENTS = 32;
13
- export const MAX_AGENT_TEXT_CHARS = 4_096;
14
- export const MAX_AGENT_SYSTEM_PROMPT_CHARS = 65_536;
15
- export const MAX_AGENT_SKILL_CONTENT_CHARS = 1_048_576;
16
15
  export const TASK_AGENT_WIRE_FIELDS = [
17
16
  'name',
18
17
  'whenToUse',
@@ -31,6 +30,7 @@ export const TASK_AGENT_WIRE_FIELDS = [
31
30
  'observerMessage',
32
31
  ];
33
32
  export const AGENT_MEMORY_WORDS = frozenSet(['user', 'project', 'local']);
33
+ export const REFUSED_PROJECT_SERVER_CAUSES = Object.freeze(['rejected', 'not_approved', 'disabled', 'policy_blocked', 'plugin_only', 'malformed_config']);
34
34
  const SOURCE_RANK = {
35
35
  policySettings: 0,
36
36
  flagSettings: 1,
@@ -98,8 +98,76 @@ function toolListFlawPhrase(f) {
98
98
  return `${entry} could not be checked here, because the engine's tool-name check is not available in this client`;
99
99
  return `${entry} differs from the tool ${JSON.stringify(f.currentName ?? '')} only in letter case`;
100
100
  }
101
+ function listShapeWord(v) {
102
+ try {
103
+ if (v === null)
104
+ return 'null';
105
+ if (typeof v === 'string')
106
+ return 'a string';
107
+ if (typeof v === 'object')
108
+ return 'an object';
109
+ return `a ${typeof v}`;
110
+ }
111
+ catch {
112
+ return 'unreadable';
113
+ }
114
+ }
115
+ function toolListShapeRefusal(agent, key, v) {
116
+ return withAssemblyRefusal(new TypeError(`projectAgentsForWire: agent "${capForDisplay(agent, 80)}" has ${key} written as ${listShapeWord(v)}, but it must be an array of tool-name strings — ` +
117
+ 'refusing to assemble the agents list (a list dropped silently would give the agent every tool, or remove a deny rule). Fix the definition.'), 'input_malformed');
118
+ }
119
+ const REFUSED_SERVER_PHRASE_KEYS = Object.freeze({
120
+ rejected: ['config.agentsWire.refusedServer.cause.rejected', 'config.agentsWire.refusedServer.way.rejected'],
121
+ not_approved: ['config.agentsWire.refusedServer.cause.notApproved', 'config.agentsWire.refusedServer.way.notApproved'],
122
+ disabled: ['config.agentsWire.refusedServer.cause.disabled', 'config.agentsWire.refusedServer.way.disabled'],
123
+ policy_blocked: ['config.agentsWire.refusedServer.cause.policyBlocked', 'config.agentsWire.refusedServer.way.policyBlocked'],
124
+ plugin_only: ['config.agentsWire.refusedServer.cause.pluginOnly', 'config.agentsWire.refusedServer.way.pluginOnly'],
125
+ malformed_config: ['config.agentsWire.refusedServer.cause.malformedConfig', 'config.agentsWire.refusedServer.way.malformedConfig'],
126
+ });
127
+ const REFUSED_SERVER_UNKNOWN_PHRASE = ['config.agentsWire.refusedServer.cause.unknown', 'config.agentsWire.refusedServer.way.unknown'];
128
+ const REFUSED_SERVER_NAME_MAX = 80;
129
+ function refusedServerIndexOf(list) {
130
+ try {
131
+ if (!Array.isArray(list) || list.length === 0)
132
+ return [];
133
+ if (!hasCoreValuePort('protocolOf') || !hasCoreValuePort('mintNamespacePrefix'))
134
+ return [];
135
+ const ns = mcpNamespace();
136
+ if (ns === undefined)
137
+ return [];
138
+ const out = [];
139
+ const seen = new Set();
140
+ for (const row of list) {
141
+ try {
142
+ if (row === null || typeof row !== 'object')
143
+ continue;
144
+ const name = row.name;
145
+ const cause = row.cause;
146
+ if (typeof name !== 'string' || name === '' || seen.has(name))
147
+ continue;
148
+ const prefix = mintNamespacePrefix(ns, name);
149
+ if (typeof prefix !== 'string' || prefix.length === 0)
150
+ continue;
151
+ seen.add(name);
152
+ const known = typeof cause === 'string' && Object.hasOwn(REFUSED_SERVER_PHRASE_KEYS, cause) ? REFUSED_SERVER_PHRASE_KEYS[cause] : undefined;
153
+ out.push({ name, prefix, phrase: known ?? REFUSED_SERVER_UNKNOWN_PHRASE });
154
+ }
155
+ catch {
156
+ continue;
157
+ }
158
+ }
159
+ return out;
160
+ }
161
+ catch {
162
+ return [];
163
+ }
164
+ }
165
+ function entryNamesServer(entry, prefix) {
166
+ return entry.startsWith(prefix) || entry === prefix.slice(0, -2);
167
+ }
101
168
  export function projectAgentsForWire(defs, deps) {
102
169
  const warnings = [];
170
+ const refusedServers = refusedServerIndexOf(deps.refusedProjectServers);
103
171
  const customs = defs
104
172
  .filter(d => d.source !== 'built-in')
105
173
  .slice()
@@ -121,25 +189,24 @@ export function projectAgentsForWire(defs, deps) {
121
189
  continue;
122
190
  seen.add(name);
123
191
  const item = { name };
124
- if (typeof def.whenToUse === 'string' && def.whenToUse.length > 0) {
125
- item.whenToUse =
126
- def.whenToUse.length > MAX_AGENT_TEXT_CHARS
127
- ? def.whenToUse.slice(0, MAX_AGENT_TEXT_CHARS)
128
- : def.whenToUse;
129
- if (def.whenToUse.length > MAX_AGENT_TEXT_CHARS) {
130
- warnings.push(`agents wire: agent "${name}" description truncated to ${MAX_AGENT_TEXT_CHARS} chars`);
131
- }
132
- }
192
+ if (typeof def.whenToUse === 'string' && def.whenToUse.length > 0)
193
+ item.whenToUse = def.whenToUse;
194
+ const rawTools = def.tools;
195
+ const rawDeny = def.disallowedTools;
196
+ if (rawTools !== undefined && !Array.isArray(rawTools))
197
+ throw toolListShapeRefusal(name, 'tools', rawTools);
198
+ if (rawDeny !== undefined && !Array.isArray(rawDeny))
199
+ throw toolListShapeRefusal(name, 'disallowedTools', rawDeny);
133
200
  const toolEntries = (list, label) => {
134
201
  const entries = list.filter((t) => typeof t === 'string' && t.length > 0);
135
202
  if (entries.length !== list.length)
136
203
  warnings.push(`agents wire: agent "${name}" ${label} had invalid entries — dropped`);
137
204
  return entries;
138
205
  };
139
- if (Array.isArray(def.tools))
140
- item.allowTools = toolEntries(def.tools, 'tools');
141
- if (Array.isArray(def.disallowedTools)) {
142
- const denyTools = toolEntries(def.disallowedTools, 'disallowedTools');
206
+ if (Array.isArray(rawTools))
207
+ item.allowTools = toolEntries(rawTools, 'tools');
208
+ if (Array.isArray(rawDeny)) {
209
+ const denyTools = toolEntries(rawDeny, 'disallowedTools');
143
210
  if (denyTools.length > 0)
144
211
  item.denyTools = denyTools;
145
212
  }
@@ -177,7 +244,30 @@ export function projectAgentsForWire(defs, deps) {
177
244
  for (const f of flaws) {
178
245
  if (f.list !== 'tools')
179
246
  continue;
180
- warnings.push(`agents wire: ${who}: its tools entry ${toolListFlawPhrase(f)} — sent as written; tool names are matched exactly, so this entry gives the agent nothing unless this run has a tool with exactly that name. Fix the entry in the agent definition.`);
247
+ warnings.push(renderMessage(msg('config.agentsWire.toolEntry.sentence', { who, phrase: toolListFlawPhrase(f) })));
248
+ }
249
+ }
250
+ if (refusedServers.length > 0 && item.allowTools !== undefined) {
251
+ const told = new Set();
252
+ const mounted = rosterMountedNames(deps.toolRoster);
253
+ const mountedNames = typeof mounted === 'object' ? mounted : undefined;
254
+ for (const entry of item.allowTools) {
255
+ if (mountedNames?.has(entry) === true)
256
+ continue;
257
+ const hit = refusedServers.find((r) => entryNamesServer(entry, r.prefix));
258
+ if (hit === undefined)
259
+ continue;
260
+ const sentence = renderMessage(msg('config.agentsWire.refusedServer.sentence', {
261
+ who: whoOf(),
262
+ entry: JSON.stringify(entry),
263
+ server: JSON.stringify(capForDisplay(hit.name, REFUSED_SERVER_NAME_MAX)),
264
+ cause: msg(hit.phrase[0]),
265
+ way: msg(hit.phrase[1]),
266
+ }));
267
+ if (told.has(sentence))
268
+ continue;
269
+ told.add(sentence);
270
+ warnings.push(sentence);
181
271
  }
182
272
  }
183
273
  if (def.memory !== undefined) {
@@ -199,7 +289,7 @@ export function projectAgentsForWire(defs, deps) {
199
289
  if (typeof skillName !== 'string' || skillName.length === 0)
200
290
  continue;
201
291
  const spec = byName.get(skillName.toLowerCase());
202
- if (spec && spec.content.length > 0 && spec.content.length <= MAX_AGENT_SKILL_CONTENT_CHARS) {
292
+ if (spec && spec.content.length > 0) {
203
293
  joined.push(spec);
204
294
  }
205
295
  else {
@@ -236,13 +326,8 @@ export function projectAgentsForWire(defs, deps) {
236
326
  catch {
237
327
  prompt = undefined;
238
328
  }
239
- if (typeof prompt === 'string' && prompt.length > 0) {
240
- if (prompt.length > MAX_AGENT_SYSTEM_PROMPT_CHARS) {
241
- warnings.push(`agents wire: agent "${name}" system prompt truncated to ${MAX_AGENT_SYSTEM_PROMPT_CHARS} chars`);
242
- prompt = prompt.slice(0, MAX_AGENT_SYSTEM_PROMPT_CHARS);
243
- }
329
+ if (typeof prompt === 'string' && prompt.length > 0)
244
330
  item.systemPrompt = prompt;
245
- }
246
331
  }
247
332
  if (def.maxTurns !== undefined)
248
333
  item.maxTurns = def.maxTurns;
@@ -291,6 +376,12 @@ let preparedKey = null;
291
376
  let preparedCapsGen = null;
292
377
  let preparedPromise = null;
293
378
  let payload;
379
+ function setPayload(agents, warnings) {
380
+ payload = { agents, warnings: Object.freeze([...warnings]) };
381
+ }
382
+ function clearPayload() {
383
+ payload = undefined;
384
+ }
294
385
  let currentGeneration = null;
295
386
  function concludeGeneration(generation, outcome) {
296
387
  if (generation !== null && preparedPromise === generation && currentGeneration !== null)
@@ -326,7 +417,7 @@ function watchCapsVerdict(baseUrl) {
326
417
  void engineCapsSettled(baseUrl).then(() => {
327
418
  if (preparedKey !== baseUrl || capsVerdictAt(baseUrl) !== false)
328
419
  return;
329
- payload = undefined;
420
+ clearPayload();
330
421
  concludeGeneration(preparedPromise, 'ok');
331
422
  currentGeneration?.settle();
332
423
  });
@@ -336,12 +427,10 @@ function applyProjectionIfCurrent(generation, projected) {
336
427
  return false;
337
428
  const accepted = capsVerdictAt(preparedKey) !== false;
338
429
  if (accepted) {
339
- for (const w of projected.warnings)
340
- warnOnce(w);
341
- payload = projected.agents;
430
+ setPayload(projected.agents, projected.warnings);
342
431
  }
343
432
  else {
344
- payload = undefined;
433
+ clearPayload();
345
434
  }
346
435
  concludeGeneration(generation, 'ok');
347
436
  currentGeneration?.settle();
@@ -352,7 +441,7 @@ async function runPrepare(cfg, projector, generation) {
352
441
  const [, projected] = await Promise.all([capsSettledAt(cfg.baseUrl), projector()]);
353
442
  if (capsVerdictAt(cfg.baseUrl) === false) {
354
443
  if (preparedKey === cfg.baseUrl) {
355
- payload = undefined;
444
+ clearPayload();
356
445
  concludeGeneration(generation, 'ok');
357
446
  if (projected.agents) {
358
447
  warnOnce(`agents wire: this engine does not support per-task custom agents (capabilities.taskAgents is not true: the engine does not report it, or this deployment turns it off, for example a multi-tenant one; this client cannot tell which) — ${projected.agents.length} custom agent definition(s) will NOT be sent`);
@@ -362,13 +451,14 @@ async function runPrepare(cfg, projector, generation) {
362
451
  }
363
452
  if (!applyProjectionIfCurrent(generation, projected))
364
453
  return;
365
- if (payload && hostEnv().SEMA_DEBUG) {
366
- console.error(`[sema] agents wire: ${payload.length} custom agent(s) → TaskRequest.agents (${payload.map(a => a.name).join(', ')})`);
454
+ const sent = payload?.agents;
455
+ if (sent && hostEnv().SEMA_DEBUG) {
456
+ console.error(`[sema] agents wire: ${sent.length} custom agent(s) → TaskRequest.agents (${sent.map(a => a.name).join(', ')})`);
367
457
  }
368
458
  }
369
459
  catch (e) {
370
460
  if (preparedPromise === generation)
371
- payload = undefined;
461
+ clearPayload();
372
462
  concludeGeneration(generation, 'failed');
373
463
  if (hostEnv().SEMA_DEBUG) {
374
464
  console.error('[sema] agents wire prepare failed (the readiness gate refuses this turn):', e);
@@ -403,7 +493,7 @@ export function invalidateTaskAgentsWire() {
403
493
  const projector = activeProjector;
404
494
  if (!preparedPromise || !projector)
405
495
  return Promise.resolve();
406
- payload = undefined;
496
+ clearPayload();
407
497
  return startGeneration(generation => runReproject(projector, generation));
408
498
  }
409
499
  async function runReproject(projector, generation) {
@@ -434,7 +524,7 @@ export async function awaitTaskAgentsWire(timeoutMs = 4000) {
434
524
  return taskAgentsReady();
435
525
  const projector = activeProjector;
436
526
  if (currentGeneration?.outcome === 'failed' && projector !== null) {
437
- payload = undefined;
527
+ clearPayload();
438
528
  void startGeneration(generation => runReproject(projector, generation));
439
529
  }
440
530
  let timeoutHandle;
@@ -493,10 +583,14 @@ function kickTaskAgentsRefresh() {
493
583
  export function taskAgentsField() {
494
584
  const base = preparedKey;
495
585
  if (capsVerdictAt(base) === false) {
496
- payload = undefined;
586
+ clearPayload();
497
587
  return {};
498
588
  }
499
- const current = payload;
589
+ const held = payload;
590
+ const current = held?.agents;
591
+ if (held !== undefined)
592
+ for (const w of held.warnings)
593
+ warnOnce(w);
500
594
  kickTaskAgentsRefresh();
501
595
  return current ? { agents: current } : {};
502
596
  }
@@ -506,6 +600,6 @@ export function _resetAgentsWireForTest() {
506
600
  preparedCapsGen = null;
507
601
  preparedPromise = null;
508
602
  currentGeneration = null;
509
- payload = undefined;
603
+ clearPayload();
510
604
  emittedWarnings.clear();
511
605
  }