@sema-agent/client-core 0.108.0 → 0.110.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 (179) hide show
  1. package/CHANGELOG.md +294 -0
  2. package/README.md +77 -40
  3. package/dist/adapt/arms.js +41 -13
  4. package/dist/adapt/textStream.d.ts +2 -0
  5. package/dist/adapt/textStream.js +37 -16
  6. package/dist/adapter/activeRunSelfHeal.js +1 -1
  7. package/dist/adapter/downstream/eventToSdkMessage.js +11 -5
  8. package/dist/agentSession/engineAgentRegistryRead.js +2 -0
  9. package/dist/bashExitReading.d.ts +12 -0
  10. package/dist/bashExitReading.js +44 -0
  11. package/dist/checkpointRowVerdict.d.ts +1 -1
  12. package/dist/checkpointRowVerdict.js +4 -2
  13. package/dist/compensations.js +1 -1
  14. package/dist/declared/supportFloorDeclared.js +1 -1
  15. package/dist/displayUntrusted.js +132 -14
  16. package/dist/engineAgentAbsence.d.ts +2 -0
  17. package/dist/engineAgentAbsence.js +28 -0
  18. package/dist/engineErrorCodes.d.ts +7 -0
  19. package/dist/engineErrorCodes.js +7 -0
  20. package/dist/engineHealthFacts.d.ts +26 -0
  21. package/dist/engineHealthFacts.js +63 -4
  22. package/dist/engineInlineTaskStats.d.ts +4 -3
  23. package/dist/engineInlineTaskStats.js +18 -2
  24. package/dist/engineNoticeCodes.d.ts +138 -0
  25. package/dist/engineNoticeCodes.js +662 -55
  26. package/dist/engineRepoint.d.ts +140 -0
  27. package/dist/engineRepoint.js +285 -0
  28. package/dist/engineRowRef.js +2 -0
  29. package/dist/engineSessionMessages.d.ts +2 -1
  30. package/dist/engineSessionMessages.js +5 -0
  31. package/dist/errorSupplementRoles.js +5 -1
  32. package/dist/fleet/fleetProjection.js +3 -2
  33. package/dist/fleetTaskDesc.js +3 -3
  34. package/dist/generated/engineFactTables.d.ts +3 -0
  35. package/dist/generated/engineFactTables.js +18 -0
  36. package/dist/generated/engineNoticeTables.js +3 -0
  37. package/dist/generated/sdkFactTables.d.ts +1 -0
  38. package/dist/generated/sdkFactTables.js +4 -0
  39. package/dist/hitl/approvalAskers.d.ts +6 -0
  40. package/dist/hitl/approvalAskers.js +30 -0
  41. package/dist/hitl/approvalCardReasons.js +25 -4
  42. package/dist/hitl/approvalRequestFrameReading.d.ts +21 -0
  43. package/dist/hitl/approvalRequestFrameReading.js +90 -0
  44. package/dist/hitl/approvalResolution.d.ts +8 -1
  45. package/dist/hitl/approvalResolution.js +4 -0
  46. package/dist/hitl/approvalsFeed.js +1 -1
  47. package/dist/hitl/askGateWire.js +0 -2
  48. package/dist/hitl/askParkRowRouting.js +5 -2
  49. package/dist/hitl/automaticAnswerVerdict.d.ts +5 -2
  50. package/dist/hitl/automaticAnswerVerdict.js +18 -4
  51. package/dist/hitl/decisionConflict.d.ts +2 -1
  52. package/dist/hitl/decisionConflict.js +10 -4
  53. package/dist/hitl/decisionRefusal.js +1 -0
  54. package/dist/hitl/decisionSend.d.ts +3 -0
  55. package/dist/hitl/decisionSend.js +29 -11
  56. package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
  57. package/dist/hitl/editedApprovalAcceptance.js +26 -0
  58. package/dist/hitl/frameRouter.js +13 -19
  59. package/dist/hitl/gateLedger.d.ts +0 -4
  60. package/dist/hitl/gateLedger.js +0 -19
  61. package/dist/hitl/hitlBridge.d.ts +5 -0
  62. package/dist/hitl/hitlBridge.js +13 -9
  63. package/dist/hitl/hitlHostSurface.d.ts +1 -0
  64. package/dist/hitl/hitlHostSurface.js +7 -2
  65. package/dist/hitl/hostWithdrawal.d.ts +15 -0
  66. package/dist/hitl/hostWithdrawal.js +38 -0
  67. package/dist/hitl/livePendingAsk.js +17 -16
  68. package/dist/hitl/localAllowRule.js +2 -1
  69. package/dist/hitl/noAllowCause.d.ts +1 -1
  70. package/dist/hitl/noAllowCause.js +4 -2
  71. package/dist/hitl/parkExpiryJudgements.js +26 -1
  72. package/dist/hitl/parkOwnership.d.ts +2 -0
  73. package/dist/hitl/parkOwnership.js +48 -8
  74. package/dist/hitl/parkResolver.js +32 -16
  75. package/dist/hitl/parkedUnresumable.d.ts +13 -0
  76. package/dist/hitl/parkedUnresumable.js +80 -0
  77. package/dist/hitl/permissionRuleScope.d.ts +4 -0
  78. package/dist/hitl/permissionRuleScope.js +31 -0
  79. package/dist/hitl/persistedRulesWire.d.ts +2 -0
  80. package/dist/hitl/persistedRulesWire.js +13 -12
  81. package/dist/hitl/planReviewWire.js +14 -4
  82. package/dist/hitl/sessionAllowRoute.js +5 -4
  83. package/dist/hitl/sessionPolicyWire.js +6 -4
  84. package/dist/hitl/standingRows.d.ts +2 -5
  85. package/dist/hitl/standingRows.js +30 -15
  86. package/dist/hitl/toolApprovalWire.d.ts +42 -9
  87. package/dist/hitl/toolApprovalWire.js +267 -44
  88. package/dist/hooksWireCaps.js +35 -4
  89. package/dist/hostSettingsSource.js +2 -0
  90. package/dist/hostTurnSettings.d.ts +10 -4
  91. package/dist/hostTurnSettings.js +112 -5
  92. package/dist/i18n/messages.en.d.ts +251 -0
  93. package/dist/i18n/messages.en.js +254 -2
  94. package/dist/i18n/messages.zh.js +264 -2
  95. package/dist/index.d.ts +48 -12
  96. package/dist/index.js +29 -7
  97. package/dist/inheritEnvCapability.js +2 -0
  98. package/dist/inputAuthor.d.ts +16 -0
  99. package/dist/inputAuthor.js +44 -0
  100. package/dist/interruptRefusalNote.d.ts +2 -0
  101. package/dist/interruptRefusalNote.js +17 -0
  102. package/dist/liveModelCatalog.d.ts +2 -0
  103. package/dist/liveModelCatalog.js +21 -7
  104. package/dist/liveQuestionStore.d.ts +4 -1
  105. package/dist/liveQuestionStore.js +12 -3
  106. package/dist/localEngineJudge.d.ts +75 -0
  107. package/dist/localEngineJudge.js +242 -0
  108. package/dist/localEngineOccupancy.d.ts +88 -0
  109. package/dist/localEngineOccupancy.js +269 -0
  110. package/dist/localEngineSeatReading.d.ts +29 -0
  111. package/dist/localEngineSeatReading.js +294 -0
  112. package/dist/localEngineSpawn.d.ts +113 -0
  113. package/dist/localEngineSpawn.js +486 -0
  114. package/dist/localEngineStep.d.ts +286 -0
  115. package/dist/localEngineStep.js +1086 -0
  116. package/dist/localEngineStopLadder.d.ts +29 -0
  117. package/dist/localEngineStopLadder.js +139 -0
  118. package/dist/localEngineSwapCard.d.ts +43 -0
  119. package/dist/localEngineSwapCard.js +205 -0
  120. package/dist/localEngineTiming.d.ts +8 -0
  121. package/dist/localEngineTiming.js +8 -0
  122. package/dist/mcpEngineLeg.d.ts +3 -0
  123. package/dist/mcpEngineLeg.js +38 -1
  124. package/dist/mcpPanel.js +2 -0
  125. package/dist/mcpReconnect.d.ts +9 -1
  126. package/dist/mcpReconnect.js +132 -20
  127. package/dist/mcpWireCaps.js +2 -2
  128. package/dist/memoryEntriesWire.d.ts +1 -1
  129. package/dist/memoryEntriesWire.js +5 -0
  130. package/dist/modelCapabilityProbe.js +4 -3
  131. package/dist/modsFace.d.ts +8 -1
  132. package/dist/modsFace.js +27 -0
  133. package/dist/noticePresentation.d.ts +2 -0
  134. package/dist/noticePresentation.js +79 -1
  135. package/dist/notifications.d.ts +17 -6
  136. package/dist/notifications.js +67 -19
  137. package/dist/ownKey.d.ts +5 -0
  138. package/dist/ownKey.js +8 -1
  139. package/dist/peerFrames.d.ts +40 -2
  140. package/dist/peerFrames.js +158 -3
  141. package/dist/pluginHooksWire.d.ts +2 -1
  142. package/dist/pluginHooksWire.js +56 -1
  143. package/dist/replayBody.js +10 -2
  144. package/dist/request/taskRequest.d.ts +5 -0
  145. package/dist/request/taskRequest.js +138 -21
  146. package/dist/resumeRefusalCopy.d.ts +1 -3
  147. package/dist/resumeRefusalCopy.js +2 -1
  148. package/dist/retryStatus.d.ts +2 -2
  149. package/dist/retryStatus.js +2 -0
  150. package/dist/ruleReadings.js +1 -1
  151. package/dist/seam.d.ts +24 -2
  152. package/dist/seam.js +15 -2
  153. package/dist/seatContract.d.ts +125 -10
  154. package/dist/seatContract.js +373 -7
  155. package/dist/sessionBackground.js +4 -1
  156. package/dist/sessionBackgroundHandoff.js +2 -1
  157. package/dist/sessionEngine.d.ts +2 -0
  158. package/dist/sessionEngine.js +111 -0
  159. package/dist/sessionMemoryStatus.d.ts +2 -1
  160. package/dist/sessionSyncFileHistory.d.ts +4 -0
  161. package/dist/sessionSyncFileHistory.js +28 -0
  162. package/dist/sseIdleTriage.d.ts +13 -1
  163. package/dist/sseIdleTriage.js +24 -6
  164. package/dist/subagent/engineSubagentTail.js +6 -0
  165. package/dist/subagentContentStore.d.ts +12 -0
  166. package/dist/subagentContentStore.js +72 -11
  167. package/dist/terminalStatuses.d.ts +1 -0
  168. package/dist/terminalStatuses.js +4 -1
  169. package/dist/toolResult.js +18 -5
  170. package/dist/turnFailurePresentation.js +9 -2
  171. package/dist/typePins.d.ts +11 -0
  172. package/dist/usageAnalytics.d.ts +17 -0
  173. package/dist/usageAnalytics.js +44 -2
  174. package/dist/wireFailureShape.d.ts +22 -5
  175. package/dist/wireFailureShape.js +47 -26
  176. package/dist/wireRefusalCopy.d.ts +31 -1
  177. package/dist/wireRefusalCopy.js +167 -2
  178. package/docs/INTEGRATION-CLIENTS.md +3269 -21
  179. package/package.json +5 -5
@@ -1,12 +1,13 @@
1
1
  import { capForDisplay } from './fleetTaskDesc.js';
2
2
  import { ownDataField } from './ownKey.js';
3
3
  import { isSessionIdPathSegment } from './sessionIdSegment.js';
4
+ import { inSlotReadOf, inValueOf, slotReadOf } from './wireFailureShape.js';
4
5
  import { readMcpAuthorization } from './mcpLiveness.js';
6
+ import { msg, renderMessage } from './i18n/translate.js';
5
7
  export const MCP_RECONNECT_OUTCOMES = Object.freeze(['accepted', 'refused', 'unsupported']);
6
8
  export const MCP_RECONNECT_TRANSACTION_NOTICE = 'Reconnecting closes the current connection before dialing again. On a healthy server this is a transaction, ' +
7
9
  'not a refresh: a failed re-dial leaves the server down and withdraws its tools from the model until a later re-dial succeeds.';
8
- export const MCP_RECONNECT_UNSETTLED_NOTICE = 'The MCP re-dial did not settle within the time this request waits, so there is no result yet: the re-dial was not cancelled and may still be running. ' +
9
- "Check the server's state before re-dialing it again — a second request that arrives after the dial has settled closes the connection and dials once more.";
10
+ export const MCP_RECONNECT_UNSETTLED_NOTICE = renderMessage(msg('session.mcpReconnect.unsettled'));
10
11
  const UNSETTLED_CODE = 'state.mcp_reconnect_unsettled';
11
12
  const _sdkClientIsAnInjection = [true];
12
13
  void _sdkClientIsAnInjection;
@@ -82,8 +83,11 @@ export function projectMcpReconnectResult(body) {
82
83
  added,
83
84
  removed,
84
85
  };
85
- if ('toolNames' in body && body.toolNames !== undefined) {
86
- const toolNames = stringList(body.toolNames);
86
+ const names = inValueOf(body, 'toolNames');
87
+ if (names.state === 'unproven')
88
+ return undefined;
89
+ if (names.state === 'present' && names.value !== undefined) {
90
+ const toolNames = stringList(names.value);
87
91
  if (toolNames === undefined)
88
92
  return undefined;
89
93
  view.toolNames = toolNames;
@@ -93,8 +97,11 @@ export function projectMcpReconnectResult(body) {
93
97
  const status = projectStatus(body.status);
94
98
  if (status !== undefined)
95
99
  view.status = status;
96
- if ('listingIncomplete' in body && body.listingIncomplete !== undefined) {
97
- const li = projectListingIncomplete(body.listingIncomplete);
100
+ const incomplete = inValueOf(body, 'listingIncomplete');
101
+ if (incomplete.state === 'unproven')
102
+ return undefined;
103
+ if (incomplete.state === 'present' && incomplete.value !== undefined) {
104
+ const li = projectListingIncomplete(incomplete.value);
98
105
  if (li === undefined)
99
106
  return undefined;
100
107
  view.listingIncomplete = li;
@@ -116,7 +123,7 @@ function unsettledVerdict(carrier) {
116
123
  }
117
124
  function isUnsettledBody(raw) {
118
125
  try {
119
- return isRecord(raw) && !('outcome' in raw) && raw.errorCode === UNSETTLED_CODE;
126
+ return isRecord(raw) && inSlotReadOf(raw, 'outcome').state === 'absent' && raw.errorCode === UNSETTLED_CODE;
120
127
  }
121
128
  catch {
122
129
  return false;
@@ -189,23 +196,128 @@ export function projectMcpReconnectCapability(caps) {
189
196
  return undefined;
190
197
  }
191
198
  }
192
- export function mcpReconnectOutcomeDetail(view) {
199
+ function reasonTailOf(reason, lang) {
200
+ return reason === undefined ? '' : renderMessage(msg('session.mcpReconnect.reasonTail', { reason }), lang);
201
+ }
202
+ function outcomeSentence(f, lang) {
203
+ switch (f.outcome) {
204
+ case 'accepted':
205
+ return renderMessage(msg(f.toolCount === 1 ? 'session.mcpReconnect.accepted.one' : 'session.mcpReconnect.accepted.other', { server: f.name, toolCount: f.toolCount, added: f.added, removed: f.removed }), lang);
206
+ case 'refused': {
207
+ const why = reasonTailOf(f.reason, lang);
208
+ if (f.roster === undefined)
209
+ return renderMessage(msg('session.mcpReconnect.refused.untouched', { server: f.name, why }), lang);
210
+ if (f.roster === 0)
211
+ return renderMessage(msg('session.mcpReconnect.refused.noTools', { server: f.name, why }), lang);
212
+ return renderMessage(msg(f.roster === 1 ? 'session.mcpReconnect.refused.listed.one' : 'session.mcpReconnect.refused.listed.other', { server: f.name, toolCount: f.roster, why }), lang);
213
+ }
214
+ case 'unsupported':
215
+ return renderMessage(msg('session.mcpReconnect.undeclared', { server: f.name, why: reasonTailOf(f.reason, lang) }), lang);
216
+ }
217
+ }
218
+ export function mcpReconnectOutcomeDetail(view, lang) {
193
219
  const name = capForDisplay(view.server, RECONNECT_WORD_MAX);
194
220
  switch (view.outcome) {
195
221
  case 'accepted':
196
- return `MCP server ${name} was re-dialed: ${view.toolCount} tool${view.toolCount === 1 ? '' : 's'} mounted for this run (${view.added.length} added, ${view.removed.length} removed)`;
222
+ return outcomeSentence({ outcome: 'accepted', name, toolCount: view.toolCount, added: view.added.length, removed: view.removed.length }, lang);
223
+ case 'refused':
224
+ return outcomeSentence({ outcome: 'refused', name, roster: view.toolNames?.length, reason: view.reason }, lang);
225
+ case 'unsupported':
226
+ return outcomeSentence({ outcome: 'unsupported', name, reason: view.reason }, lang);
227
+ }
228
+ }
229
+ function verdictDetailKey(kind) {
230
+ switch (kind) {
231
+ case 'not_running':
232
+ return 'session.mcpReconnect.notRunning';
233
+ case 'unsupported':
234
+ return 'session.mcpReconnect.unsupported';
235
+ case 'not_found':
236
+ return 'session.mcpReconnect.notFound';
237
+ case 'invalid':
238
+ return 'session.mcpReconnect.invalid';
239
+ case 'failed':
240
+ return 'session.mcpReconnect.failed';
241
+ case 'no_session':
242
+ return 'session.mcpReconnect.noSession';
243
+ case 'unavailable':
244
+ return 'session.mcpReconnect.unavailable';
245
+ case 'unsettled':
246
+ return 'session.mcpReconnect.unsettled';
247
+ default:
248
+ return undefined;
249
+ }
250
+ }
251
+ function fieldOf(o, key) {
252
+ const s = slotReadOf(o, key);
253
+ return s.presence === 'present' ? { kind: 'value', value: s.value } : s.presence === 'absent' ? { kind: 'absent' } : { kind: 'unreadable' };
254
+ }
255
+ function okOutcomeFactsOf(verdict, server) {
256
+ const slot = fieldOf(verdict, 'result');
257
+ if (slot.kind !== 'value')
258
+ return undefined;
259
+ const r = slot.value;
260
+ if (r === null || typeof r !== 'object' || Array.isArray(r))
261
+ return undefined;
262
+ const outcome = fieldOf(r, 'outcome');
263
+ const echoed = fieldOf(r, 'server');
264
+ if (outcome.kind !== 'value' || echoed.kind !== 'value' || echoed.value !== server)
265
+ return undefined;
266
+ const rs = fieldOf(r, 'reason');
267
+ if (rs.kind === 'unreadable' || (rs.kind === 'value' && typeof rs.value !== 'string'))
268
+ return undefined;
269
+ const reason = rs.kind === 'value' ? rs.value : undefined;
270
+ const name = capForDisplay(server, RECONNECT_WORD_MAX);
271
+ switch (outcome.value) {
272
+ case 'unsupported':
273
+ return { outcome: 'unsupported', name, reason };
274
+ case 'accepted': {
275
+ const toolCount = fieldOf(r, 'toolCount');
276
+ const added = fieldOf(r, 'added');
277
+ const removed = fieldOf(r, 'removed');
278
+ if (toolCount.kind !== 'value' || added.kind !== 'value' || removed.kind !== 'value')
279
+ return undefined;
280
+ const n = toolCount.value;
281
+ const addedList = stringList(added.value);
282
+ const removedList = stringList(removed.value);
283
+ if (typeof n !== 'number' || !Number.isInteger(n) || n < 0 || addedList === undefined || removedList === undefined)
284
+ return undefined;
285
+ return { outcome: 'accepted', name, toolCount: n, added: addedList.length, removed: removedList.length };
286
+ }
197
287
  case 'refused': {
198
- const why = view.reason !== undefined ? ` — ${view.reason}` : '';
199
- if (view.toolNames === undefined) {
200
- return `MCP server ${name} was NOT re-dialed; its connection and tools were left untouched${why}`;
201
- }
202
- if (view.toolNames.length === 0) {
203
- return `MCP server ${name} was NOT re-dialed; this run's tool list has no tools from it right now (the dial failed, or this run's tool settings let none of its tools through)${why}`;
204
- }
205
- return `MCP server ${name} was NOT re-dialed; the engine reports ${view.toolNames.length} tool${view.toolNames.length === 1 ? '' : 's'} for it after this attempt${why}`;
288
+ const names = fieldOf(r, 'toolNames');
289
+ if (names.kind === 'unreadable')
290
+ return undefined;
291
+ if (names.kind === 'absent')
292
+ return { outcome: 'refused', name, roster: undefined, reason };
293
+ const roster = stringList(names.value);
294
+ if (roster === undefined)
295
+ return undefined;
296
+ return { outcome: 'refused', name, roster: roster.length, reason };
206
297
  }
207
- case 'unsupported':
208
- return (`This run declared no MCP servers, so there is nothing to re-dial (${name} is not part of this run)` +
209
- (view.reason !== undefined ? ` — ${view.reason}` : ''));
298
+ default:
299
+ return undefined;
300
+ }
301
+ }
302
+ export function mcpReconnectVerdictDetail(verdict, server, lang) {
303
+ try {
304
+ if (verdict === null || typeof verdict !== 'object')
305
+ return undefined;
306
+ const kindSlot = slotReadOf(verdict, 'kind');
307
+ if (kindSlot.presence !== 'present' || typeof kindSlot.value !== 'string')
308
+ return undefined;
309
+ if (!nonEmpty(server))
310
+ return undefined;
311
+ if (kindSlot.value === 'ok') {
312
+ const facts = okOutcomeFactsOf(verdict, server);
313
+ return facts === undefined ? undefined : outcomeSentence(facts, lang);
314
+ }
315
+ const key = verdictDetailKey(kindSlot.value);
316
+ if (key === undefined)
317
+ return undefined;
318
+ return renderMessage(msg(key, { server: capForDisplay(server, RECONNECT_WORD_MAX) }), lang);
319
+ }
320
+ catch {
321
+ return undefined;
210
322
  }
211
323
  }
@@ -329,7 +329,7 @@ function dropNotice(name, reason, problem, opts, path, transportKind) {
329
329
  : reason === 'elicitation_invalid'
330
330
  ? msg('config.mcp.elicitationInvalid', { name: quoted, where })
331
331
  : reason === 'allow_tools_invalid'
332
- ? msg('config.mcp.allowToolsInvalid', { name: quoted, where, problem: capForDisplay(problem ?? '', DROP_PROBLEM_SHOWN) })
332
+ ? msg('config.mcp.allowToolsInvalid', { name: quoted, where, problem: problem ?? '' })
333
333
  : reason === 'identity_headers_both_set'
334
334
  ? msg('config.mcp.identityHeadersBothSet', { name: quoted, where })
335
335
  : reason === 'transport_invalid'
@@ -448,6 +448,6 @@ export function mcpAssertionHeaderNotSentNotice(specs) {
448
448
  const parts = distinct.slice(0, ASSERTION_NOTICE_NAMES_SHOWN).map((x) => `"${x}"`);
449
449
  if (distinct.length > ASSERTION_NOTICE_NAMES_SHOWN)
450
450
  parts.push(`+${distinct.length - ASSERTION_NOTICE_NAMES_SHOWN} more`);
451
- return (`MCP servers that declare an assertion header (assertionHeader): ${parts.join(', ')} — server versions up to 7.144.0 accept this setting but do not send the header, ` +
451
+ return (`MCP servers that declare an assertion header (assertionHeader): ${parts.join(', ')} — server versions up to 7.147.0 accept this setting but do not send the header, ` +
452
452
  'and this client cannot tell whether the engine it uses sends it; a connector that requires the header will refuse requests from these servers');
453
453
  }
@@ -61,7 +61,7 @@ export interface MemoryEraseConflictRow {
61
61
  id: string;
62
62
  reason: string;
63
63
  }
64
- declare const MEMORY_ERASE_CARRIER_CLASSES: readonly ["projection", "shadow", "indexLine", "quarantineCopy", "planArchive", "planWorkbook", "evidenceBinding", "backendDerived"];
64
+ declare const MEMORY_ERASE_CARRIER_CLASSES: readonly ["projection", "shadow", "indexLine", "quarantineCopy", "clearanceCustody", "planArchive", "planWorkbook", "evidenceBinding", "backendDerived"];
65
65
  export type MemoryEraseCarrierClass = (typeof MEMORY_ERASE_CARRIER_CLASSES)[number];
66
66
  export type MemoryEraseCarrierReading = {
67
67
  removed: number;
@@ -174,6 +174,7 @@ const MEMORY_ERASE_CARRIER_CLASSES = Object.freeze([
174
174
  'shadow',
175
175
  'indexLine',
176
176
  'quarantineCopy',
177
+ 'clearanceCustody',
177
178
  'planArchive',
178
179
  'planWorkbook',
179
180
  'evidenceBinding',
@@ -237,6 +238,10 @@ function eraseResidualsOf(o, lane) {
237
238
  return null;
238
239
  const carriers = {};
239
240
  for (const cls of MEMORY_ERASE_CARRIER_CLASSES) {
241
+ if (!Object.hasOwn(rawCarriers, cls)) {
242
+ carriers[cls] = { unverified: true };
243
+ continue;
244
+ }
240
245
  const answer = carrierAnswerOf(readThrownSlot(rawCarriers, cls, 'data'));
241
246
  if (answer === null)
242
247
  return null;
@@ -128,15 +128,16 @@ export async function probeModelCapability(entry, send) {
128
128
  evidence: { baseline, attempts, lowEffortAccepted: !low.refused },
129
129
  };
130
130
  }
131
+ const _capabilityBitsPin = true;
132
+ void _capabilityBitsPin;
131
133
  export function applyProbeToEntry(entry, result) {
132
134
  if (result.verdict === 'inconclusive')
133
135
  return entry;
134
136
  const compatIn = typeof entry.compat === 'object' && entry.compat !== null && !Array.isArray(entry.compat)
135
137
  ? entry.compat
136
138
  : undefined;
137
- if (result.verdict === 'no_reasoning') {
138
- return { ...entry, reasoning: false };
139
- }
139
+ if (result.verdict === 'no_reasoning')
140
+ return entry;
140
141
  if (result.verdict === 'disabled_via' && typeof result.thinkingFormat === 'string') {
141
142
  return { ...entry, reasoning: true, compat: { ...compatIn, thinkingFormat: result.thinkingFormat } };
142
143
  }
@@ -1,8 +1,15 @@
1
- import type { WiringManifestModRefusedView } from './adapter/downstream/eventToSdkMessage.js';
1
+ import type { WiringManifestModRefusedView, WiringManifestModView } from './adapter/downstream/eventToSdkMessage.js';
2
+ import type { SessionModsView } from './seatContract.js';
3
+ import type { DeviceUnreadable } from './deviceRebind.js';
2
4
  import type { EngineNoticeChromeEvent } from './seam.js';
3
5
  import type { UiLanguage } from './env/uiLanguage.js';
6
+ import { type ApprovalRequestedBy } from './hitl/approvalAskers.js';
4
7
  export declare const MOD_LOADER_VERBS: readonly ["list", "load", "unload", "status"];
5
8
  export type ModLoaderVerb = (typeof MOD_LOADER_VERBS)[number];
6
9
  export declare function readModRegistrationRefusedNotice(ev: EngineNoticeChromeEvent): WiringManifestModRefusedView | undefined;
10
+ export declare function sessionModsOf(manifest: {
11
+ readonly mods?: readonly WiringManifestModView[];
12
+ } | null | undefined): SessionModsView | DeviceUnreadable;
7
13
  export declare function modCardSentence(input: object | null | undefined, lang?: UiLanguage): string | undefined;
14
+ export declare function approvalRequestedBySentence(by: ApprovalRequestedBy | null | undefined, lang?: UiLanguage): string | undefined;
8
15
  export declare function modLoaderFailureSentence(name: string, error: unknown, lang?: UiLanguage): string;
package/dist/modsFace.js CHANGED
@@ -4,6 +4,7 @@ import { displayUntrusted } from './displayUntrusted.js';
4
4
  import { WIRE_FAILURE_UNRENDERABLE_TEXT, wireFailureTextsOf } from './wireFailureShape.js';
5
5
  import { gateOutcomeOf, isDeniedGate, rewriteExtensionNamesOf, toolInputRewriteOf } from './gateOutcome.js';
6
6
  import { guardedRead, readOwnDataNonEmptyString, readOwnDataValue, toolResultBlockOf } from './ownKey.js';
7
+ import { requestedByViewOf } from './hitl/approvalAskers.js';
7
8
  const isCardRecordObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
8
9
  export const MOD_LOADER_VERBS = Object.freeze(['list', 'load', 'unload', 'status']);
9
10
  const modRowOf = (r) => modRowsOf([r])?.[0];
@@ -21,6 +22,18 @@ export function readModRegistrationRefusedNotice(ev) {
21
22
  return undefined;
22
23
  }
23
24
  }
25
+ export function sessionModsOf(manifest) {
26
+ try {
27
+ if (!isCardRecordObject(manifest))
28
+ return sessionModsUnreadable();
29
+ const rows = modRowsOf(readOwnDataValue(manifest, 'mods'));
30
+ return rows === undefined ? sessionModsUnreadable() : Object.freeze({ rows: Object.freeze(rows.slice()) });
31
+ }
32
+ catch {
33
+ return sessionModsUnreadable();
34
+ }
35
+ }
36
+ const sessionModsUnreadable = () => Object.freeze({ kind: 'unreadable', why: renderMessage(msg('config.mods.session.unreadable')) });
24
37
  const MOD_NAME_SHOWN_MAX = 64;
25
38
  const MOD_REASON_SHOWN_MAX = 160;
26
39
  const shownName = (s) => displayUntrusted(s, { blanks: true, max: MOD_NAME_SHOWN_MAX });
@@ -54,6 +67,20 @@ export function modCardSentence(input, lang) {
54
67
  return undefined;
55
68
  }
56
69
  }
70
+ export function approvalRequestedBySentence(by, lang) {
71
+ try {
72
+ const view = requestedByViewOf(by);
73
+ if (view === undefined)
74
+ return undefined;
75
+ const name = shownName(view.name);
76
+ if (name.trim() === '')
77
+ return undefined;
78
+ return renderMessage(msg('config.mods.request.by', { by: name }), lang);
79
+ }
80
+ catch {
81
+ return undefined;
82
+ }
83
+ }
57
84
  export function modLoaderFailureSentence(name, error, lang) {
58
85
  const shown = guardedRead(() => (typeof name === 'string' ? shownName(name) : undefined)) ?? '';
59
86
  const { reason } = wireFailureTextsOf(error);
@@ -6,7 +6,9 @@ export interface CutForDisplayResult {
6
6
  readonly shown: string;
7
7
  readonly truncated: boolean;
8
8
  }
9
+ export declare function pairSafeCut(text: string, cut: number): number;
9
10
  export declare function cutForDisplay(text: string, budget: number, opts?: CutForDisplayOptions): CutForDisplayResult;
11
+ export declare function elideMiddleForDisplay(text: string, budget: number): string;
10
12
  export type NoticePresentationAudience = 'user' | 'operator' | 'unknown';
11
13
  export type NoticeOutlet = 'present' | 'absent' | 'unknown';
12
14
  export type NoticeOccurrence = {
@@ -69,6 +69,14 @@ function pickCut(src, limit, floor, boundary) {
69
69
  return clause;
70
70
  return word >= floor ? word : -1;
71
71
  }
72
+ export function pairSafeCut(text, cut) {
73
+ if (cut <= 0 || cut >= text.length || text.charAt(cut) !== '\\' || text.charAt(cut - 1) !== '\\')
74
+ return cut;
75
+ let start = cut - 1;
76
+ while (start > 0 && text.charAt(start - 1) === '\\')
77
+ start--;
78
+ return (cut - start) % 2 === 1 ? cut - 1 : cut;
79
+ }
72
80
  export function cutForDisplay(text, budget, opts) {
73
81
  const src = typeof text === 'string' ? text : (guardedRead(() => String(text ?? '')) ?? '');
74
82
  if (typeof budget !== 'number' || !Number.isFinite(budget))
@@ -83,9 +91,79 @@ export function cutForDisplay(text, budget, opts) {
83
91
  const head = capDisplay(src, room, 'escape');
84
92
  const floor = Math.ceil(head.length / CUT_KEEP_DIVISOR);
85
93
  const at = pickCut(src, head.length, floor, boundary);
86
- const kept = (at === -1 ? head : src.slice(0, at)).trimEnd();
94
+ const kept = src.slice(0, pairSafeCut(src, at === -1 ? head.length : at)).trimEnd();
87
95
  return { shown: `${kept}${mark}`, truncated: true };
88
96
  }
97
+ const ELISION_MARK = '…';
98
+ const ELISION_MIN_ROOM = 8;
99
+ const ELISION_SOFT_BACKOFF = 24;
100
+ const CLUSTER_EXTENDER = /^[\p{M}‍︀-️\u{1F3FB}-\u{1F3FF}\u{E0020}-\u{E007F}]/u;
101
+ const REGIONAL_INDICATOR = /^[\u{1F1E6}-\u{1F1FF}]$/u;
102
+ const RTL_LETTER = /^[\p{Script=Arabic}\p{Script=Hebrew}\p{Script=Syriac}\p{Script=Thaana}\p{Script=Nko}\p{Script=Samaritan}\p{Script=Mandaic}]$/u;
103
+ function codePointBefore(text, i) {
104
+ return text.slice(Math.max(0, i - 2), i).match(/[\s\S]$/u)?.[0] ?? '';
105
+ }
106
+ function codePointAfter(text, i) {
107
+ const cp = i < text.length ? text.codePointAt(i) : undefined;
108
+ return cp === undefined ? '' : String.fromCodePoint(cp);
109
+ }
110
+ function hardSafeBoundary(text, i) {
111
+ return i <= 0 || i >= text.length || (capDisplay(text, i, 'escape').length === i && pairSafeCut(text, i) === i);
112
+ }
113
+ function calmBoundary(text, i) {
114
+ const before = codePointBefore(text, i);
115
+ const after = codePointAfter(text, i);
116
+ if (before === '' || after === '')
117
+ return true;
118
+ if (before === '‍' || CLUSTER_EXTENDER.test(after))
119
+ return false;
120
+ if (RTL_LETTER.test(before) || RTL_LETTER.test(after))
121
+ return false;
122
+ if (REGIONAL_INDICATOR.test(before) && REGIONAL_INDICATOR.test(after)) {
123
+ let run = 0;
124
+ for (let j = i; j > 0;) {
125
+ const cp = codePointBefore(text, j);
126
+ if (!REGIONAL_INDICATOR.test(cp))
127
+ break;
128
+ run++;
129
+ j -= cp.length;
130
+ }
131
+ return run % 2 === 0;
132
+ }
133
+ return true;
134
+ }
135
+ function settleOnCalm(text, at, step) {
136
+ for (let k = 0; k <= ELISION_SOFT_BACKOFF; k++) {
137
+ const i = at + step * k;
138
+ if (i < 0 || i > text.length)
139
+ break;
140
+ if (hardSafeBoundary(text, i) && calmBoundary(text, i))
141
+ return i;
142
+ }
143
+ return at;
144
+ }
145
+ export function elideMiddleForDisplay(text, budget) {
146
+ const src = typeof text === 'string' ? text : (guardedRead(() => String(text ?? '')) ?? '');
147
+ if (typeof budget !== 'number' || !Number.isFinite(budget))
148
+ return src;
149
+ const limit = Math.max(0, Math.floor(budget));
150
+ if (src.length <= limit)
151
+ return src;
152
+ const room = limit - ELISION_MARK.length;
153
+ if (room < ELISION_MIN_ROOM)
154
+ return cutForDisplay(src, limit).shown;
155
+ const headRoom = Math.ceil(room / 2);
156
+ const tailRoom = room - headRoom;
157
+ let head = headRoom;
158
+ while (head > 0 && !hardSafeBoundary(src, head))
159
+ head--;
160
+ let tail = src.length - tailRoom;
161
+ while (tail < src.length && !hardSafeBoundary(src, tail))
162
+ tail++;
163
+ head = settleOnCalm(src, head, -1);
164
+ tail = settleOnCalm(src, tail, 1);
165
+ return `${src.slice(0, head).trimEnd()}${ELISION_MARK}${src.slice(tail)}`;
166
+ }
89
167
  export const NOTICE_SEAT_CLASS = Object.freeze({
90
168
  user: Object.freeze({ present: 'persistent+hint', unknown: 'persistent+hint', absent: 'persistent' }),
91
169
  operator: Object.freeze({ present: 'transient', unknown: 'transient', absent: 'diagnostic' }),
@@ -9,6 +9,7 @@ export interface TaskNotificationFields {
9
9
  outputFile?: string;
10
10
  diagnostics?: string;
11
11
  result?: string;
12
+ error?: string;
12
13
  stoppedBy?: string;
13
14
  partial?: boolean;
14
15
  exitCode?: number;
@@ -93,11 +94,21 @@ export declare function noteWorkflowCompletionCardEnqueued(runId: string): void;
93
94
  export declare function listNotifiedRuns(): string[];
94
95
  export declare function listWorkflowCompletionCardsEnqueued(): string[];
95
96
  export declare function isWorkflowCompletionCardEnqueued(runId: string): boolean;
96
- type WorkflowStatusProbe = (runId: string) => Promise<{
97
- terminal: boolean;
98
- status: 'completed' | 'failed';
99
- summary: string;
100
- } | null>;
97
+ export type WorkflowRunReader = (runId: string) => Promise<unknown>;
98
+ export type WorkflowRunTerminalReading = {
99
+ readonly state: 'running';
100
+ } | {
101
+ readonly state: 'awaiting approval';
102
+ } | {
103
+ readonly state: 'completed' | 'failed';
104
+ readonly result?: string;
105
+ readonly error?: string;
106
+ readonly pointer: 'absent';
107
+ } | {
108
+ readonly state: 'unknown';
109
+ readonly why: 'body_unreadable' | 'status_unreadable' | 'status_unrecognized';
110
+ };
111
+ export declare function workflowRunTerminalReadingOf(body: unknown): WorkflowRunTerminalReading;
101
112
  export declare function outstandingWorkflowCount(): number;
102
113
  export declare function outstandingDeliverableWorkflowCount(): number;
103
114
  export declare function subscribeOutstandingWorkflows(listener: () => void): () => void;
@@ -111,7 +122,7 @@ export interface NotificationDropCounters {
111
122
  bgWatchCollectedUnprobed: number;
112
123
  }
113
124
  export declare function notificationDropCounters(): NotificationDropCounters;
114
- export declare function installWorkflowStatusProbe(probe: WorkflowStatusProbe): void;
125
+ export declare function installWorkflowRunReader(reader: WorkflowRunReader | null): void;
115
126
  type BgTaskStatusProbe = (taskId: string) => Promise<{
116
127
  terminal: boolean;
117
128
  status: string;
@@ -2,10 +2,11 @@ import { hostEnv } from './hostEnv.js';
2
2
  import { unrefTimer } from './unrefTimer.js';
3
3
  import { clearEnginePanelTaskResidentByWire, publishEngineAgentPanelEvent, } from './engineAgentPanelStore.js';
4
4
  import { panelEndFact } from './panelReduce.js';
5
- import { isCoreTaskNotificationTerminal, isCoreTaskNotificationTerminalNotSuccess } from './terminalStatuses.js';
5
+ import { isCoreTaskNotificationTerminal, isCoreTaskNotificationTerminalNotSuccess, isCoreWorkflowRunParkedEnd, isCoreWorkflowRunTerminal } from './terminalStatuses.js';
6
6
  import { cycleSeqValue } from './cycleOrder.js';
7
7
  import { CORE_ENGINE_AUTHORITY_ENVELOPE_TAGS } from './generated/engineFactTables.js';
8
8
  import { guardedRead, ownSnapshot, ownSnapshotRecord, ownSnapshotTyped, readOwnDataValue, workflowCompletionExtrasOf } from './ownKey.js';
9
+ import { displayUntrusted } from './displayUntrusted.js';
9
10
  function escapeXml(v) {
10
11
  return v.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
11
12
  }
@@ -102,6 +103,7 @@ export function freezeTaskNotificationFields(n) {
102
103
  ...(n.outputFile !== undefined ? { outputFile: n.outputFile } : {}),
103
104
  ...(n.diagnostics !== undefined ? { diagnostics: n.diagnostics } : {}),
104
105
  ...(n.result !== undefined ? { result: n.result } : {}),
106
+ ...(n.error !== undefined ? { error: n.error } : {}),
105
107
  ...(n.stoppedBy !== undefined ? { stoppedBy: n.stoppedBy } : {}),
106
108
  ...(n.partial !== undefined ? { partial: n.partial } : {}),
107
109
  ...(n.exitCode !== undefined ? { exitCode: n.exitCode } : {}),
@@ -128,7 +130,7 @@ export function taskNotificationOf(carrier) {
128
130
  if (typeof taskId !== 'string' || taskId.length === 0 || typeof status !== 'string' || typeof summary !== 'string')
129
131
  return undefined;
130
132
  const out = { taskId, status, summary };
131
- for (const k of ['taskType', 'toolUseId', 'outputFile', 'diagnostics', 'result', 'stoppedBy', 'externalSource']) {
133
+ for (const k of ['taskType', 'toolUseId', 'outputFile', 'diagnostics', 'result', 'error', 'stoppedBy', 'externalSource']) {
132
134
  const v = slot(k);
133
135
  if (typeof v === 'string')
134
136
  out[k] = v;
@@ -478,6 +480,50 @@ const cardEnqueuedRunIds = new Set();
478
480
  export function isWorkflowCompletionCardEnqueued(runId) {
479
481
  return cardEnqueuedRunIds.has(runId);
480
482
  }
483
+ export function workflowRunTerminalReadingOf(body) {
484
+ const snap = probeAnswerSnapshot(body);
485
+ if (snap === null)
486
+ return Object.freeze({ state: 'unknown', why: 'body_unreadable' });
487
+ const status = guardedRead(() => readOwnDataValue(snap, 'status'));
488
+ if (typeof status !== 'string')
489
+ return Object.freeze({ state: 'unknown', why: 'status_unreadable' });
490
+ if (status === 'running')
491
+ return Object.freeze({ state: 'running' });
492
+ if (!isCoreWorkflowRunTerminal(status))
493
+ return Object.freeze({ state: 'unknown', why: 'status_unrecognized' });
494
+ if (isCoreWorkflowRunParkedEnd(status, guardedRead(() => readOwnDataValue(snap, 'errorCode'))))
495
+ return Object.freeze({ state: 'awaiting approval' });
496
+ const result = guardedRead(() => readOwnDataValue(snap, 'result'));
497
+ const error = status === 'failed' ? guardedRead(() => readOwnDataValue(snap, 'error')) : undefined;
498
+ const shownError = typeof error === 'string' && error.length > 0 ? shownRunError(error) : '';
499
+ return Object.freeze({
500
+ state: status,
501
+ ...(typeof result === 'string' && result.length > 0 ? { result } : {}),
502
+ ...(shownError.length > 0 ? { error: shownError } : {}),
503
+ pointer: 'absent',
504
+ });
505
+ }
506
+ const WORKFLOW_RUN_ERROR_MAX = 200;
507
+ function shownRunError(raw) {
508
+ const opts = { foldLines: false, escapeBackslashes: 'auto' };
509
+ const whole = displayUntrusted(raw, opts);
510
+ return whole.length <= WORKFLOW_RUN_ERROR_MAX ? whole : `${displayUntrusted(raw, { ...opts, max: WORKFLOW_RUN_ERROR_MAX - 1 })}…`;
511
+ }
512
+ function workflowPollDecisionOf(delivered, reading) {
513
+ if (delivered)
514
+ return { act: 'stop', why: 'delivered' };
515
+ if (reading.state === 'running')
516
+ return { act: 'wait', why: 'running' };
517
+ if (reading.state === 'awaiting approval')
518
+ return { act: 'wait', why: 'awaiting approval' };
519
+ if (reading.state === 'unknown')
520
+ return { act: 'wait', why: 'unknown' };
521
+ return { act: 'emit', status: reading.state, ...(reading.result !== undefined ? { result: reading.result } : {}), ...(reading.error !== undefined ? { error: reading.error } : {}) };
522
+ }
523
+ function workflowPollCompletionSummary(status, error) {
524
+ const head = `workflow ${status} (read from its run record; no handle to the full record is available)`;
525
+ return error === undefined ? head : `${head} — failure reason from the run record: ${error}`;
526
+ }
481
527
  const outstandingRuns = new Map();
482
528
  const outstandingListeners = new Set();
483
529
  function notifyOutstanding() {
@@ -493,7 +539,7 @@ export function outstandingWorkflowCount() {
493
539
  return outstandingRuns.size;
494
540
  }
495
541
  export function outstandingDeliverableWorkflowCount() {
496
- return statusProbe ? outstandingRuns.size : 0;
542
+ return workflowRunReader ? outstandingRuns.size : 0;
497
543
  }
498
544
  export function subscribeOutstandingWorkflows(listener) {
499
545
  outstandingListeners.add(listener);
@@ -531,12 +577,12 @@ export function notificationDropCounters() {
531
577
  bgWatchCollectedUnprobed,
532
578
  };
533
579
  }
534
- let statusProbe = null;
580
+ let workflowRunReader = null;
535
581
  let watchTimer = null;
536
582
  const WATCH_INTERVAL_MS = 5000;
537
583
  const WATCH_TTL_MS = 2 * 60 * 60 * 1000;
538
- export function installWorkflowStatusProbe(probe) {
539
- statusProbe = probe;
584
+ export function installWorkflowRunReader(reader) {
585
+ workflowRunReader = reader;
540
586
  }
541
587
  const BG_FIRST_SEQ = 1;
542
588
  function normalizeBgSeq(seq) {
@@ -762,26 +808,25 @@ async function tickWatchInner(now) {
762
808
  bgProbeInFlight.delete(key);
763
809
  }
764
810
  }
765
- const probe = statusProbe;
811
+ const reader = workflowRunReader;
766
812
  for (const runId of [...outstandingRuns.keys()]) {
767
813
  if (notifiedRunIds.has(runId)) {
768
814
  if (outstandingRuns.delete(runId))
769
815
  notifyOutstanding();
770
816
  continue;
771
817
  }
772
- if (!probe)
818
+ if (!reader)
773
819
  continue;
774
820
  runProbeInFlight.set(runId, now);
775
821
  try {
776
- const res = probeAnswerSnapshot(await withProbeDeadline(probe(runId), probeTimeoutMs()));
777
- if (res?.terminal) {
778
- if (outstandingRuns.delete(runId))
779
- notifyOutstanding();
780
- enqueueEngineWorkflowNotification({
781
- runId,
782
- status: res.status,
783
- summary: res.summary,
784
- });
822
+ const reading = workflowRunTerminalReadingOf(await withProbeDeadline(reader(runId), probeTimeoutMs()));
823
+ const decision = workflowPollDecisionOf(notifiedRunIds.has(runId), reading);
824
+ if (decision.act === 'wait')
825
+ continue;
826
+ if (outstandingRuns.delete(runId))
827
+ notifyOutstanding();
828
+ if (decision.act === 'emit') {
829
+ enqueueWorkflowCompletion({ runId, status: decision.status, summary: workflowPollCompletionSummary(decision.status, decision.error) }, { ...(decision.result !== undefined ? { result: decision.result } : {}), ...(decision.error !== undefined ? { error: decision.error } : {}) });
785
830
  }
786
831
  }
787
832
  catch {
@@ -845,6 +890,9 @@ export function enqueueEngineWorkflowNotification(raw) {
845
890
  traceNotif('workflow-notification dropped:payload-unreadable');
846
891
  return;
847
892
  }
893
+ enqueueWorkflowCompletion(c, completionIdOnly(workflowCompletionExtrasOf(c)));
894
+ }
895
+ function enqueueWorkflowCompletion(c, body) {
848
896
  if (notifiedRunIds.has(c.runId)) {
849
897
  workflowDedupDropped++;
850
898
  traceNotif(`workflow notification suppressed (run already notified) runId=${c.runId} status=${c.status}`);
@@ -852,7 +900,7 @@ export function enqueueEngineWorkflowNotification(raw) {
852
900
  }
853
901
  markRunNotified(c.runId);
854
902
  cardEnqueuedRunIds.add(c.runId);
855
- port()?.enqueuePendingNotification(taskNotificationQueueCommand({ taskId: c.runId, taskType: 'workflow', status: c.status, summary: c.summary, ...completionIdOnly(workflowCompletionExtrasOf(c)) }));
903
+ port()?.enqueuePendingNotification(taskNotificationQueueCommand({ taskId: c.runId, taskType: 'workflow', status: c.status, summary: c.summary, ...body }));
856
904
  }
857
905
  export function _resetEngineTaskNotificationForTest() {
858
906
  notifiedRunIds.clear();
@@ -867,7 +915,7 @@ export function _resetEngineTaskNotificationForTest() {
867
915
  bgProbeInFlight.clear();
868
916
  runProbeInFlight.clear();
869
917
  tickGeneration = 0;
870
- statusProbe = null;
918
+ workflowRunReader = null;
871
919
  bgStatusProbe = null;
872
920
  tickStartedAtMs = null;
873
921
  abandonedCount = 0;