@sema-agent/client-core 0.109.0 → 0.111.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 +342 -0
  2. package/README.md +88 -32
  3. package/dist/abortableSleep.d.ts +7 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/arms.js +45 -11
  6. package/dist/adapt/toolCards.d.ts +3 -1
  7. package/dist/adapt/toolCards.js +4 -2
  8. package/dist/adapter/activeRunSelfHeal.d.ts +39 -0
  9. package/dist/adapter/activeRunSelfHeal.js +171 -3
  10. package/dist/adapter/downstream/eventToSdkMessage.js +20 -15
  11. package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
  12. package/dist/adapter/downstream/terminalToSdkResult.js +11 -0
  13. package/dist/adapter/runStream.d.ts +5 -0
  14. package/dist/adapter/runStream.js +15 -3
  15. package/dist/attachmentRoute.d.ts +20 -0
  16. package/dist/attachmentRoute.js +39 -0
  17. package/dist/attachmentsWireCaps.js +1 -1
  18. package/dist/builtinOutputStyles.d.ts +12 -0
  19. package/dist/builtinOutputStyles.js +147 -0
  20. package/dist/compactionStatus.d.ts +9 -0
  21. package/dist/compactionStatus.js +29 -5
  22. package/dist/controlRouter.js +2 -1
  23. package/dist/coreValuePorts.d.ts +39 -1
  24. package/dist/coreValuePorts.js +10 -0
  25. package/dist/declared/supportFloorDeclared.js +1 -1
  26. package/dist/displayUntrusted.d.ts +1 -0
  27. package/dist/displayUntrusted.js +138 -14
  28. package/dist/engineErrorCodes.d.ts +3 -0
  29. package/dist/engineErrorCodes.js +3 -0
  30. package/dist/engineHealthFacts.d.ts +20 -0
  31. package/dist/engineHealthFacts.js +42 -1
  32. package/dist/engineHealthProbeVerdict.d.ts +23 -0
  33. package/dist/engineHealthProbeVerdict.js +115 -0
  34. package/dist/engineInlineTaskStats.d.ts +4 -3
  35. package/dist/engineInlineTaskStats.js +18 -2
  36. package/dist/engineNoticeCodes.d.ts +10 -0
  37. package/dist/engineNoticeCodes.js +36 -6
  38. package/dist/engineRepoint.d.ts +147 -0
  39. package/dist/engineRepoint.js +310 -0
  40. package/dist/engineRowRef.d.ts +3 -0
  41. package/dist/engineRowRef.js +13 -1
  42. package/dist/engineSessionMessages.d.ts +2 -1
  43. package/dist/engineSessionMessages.js +5 -0
  44. package/dist/engineSwapCrossing.d.ts +57 -0
  45. package/dist/engineSwapCrossing.js +297 -0
  46. package/dist/engineUnavailable.d.ts +44 -0
  47. package/dist/engineUnavailable.js +492 -0
  48. package/dist/errorSupplementRoles.js +2 -1
  49. package/dist/eventLeg.js +3 -3
  50. package/dist/fleet/fleetProjection.js +3 -2
  51. package/dist/generated/engineFactTables.d.ts +2 -0
  52. package/dist/generated/engineFactTables.js +12 -0
  53. package/dist/generated/sdkFactTables.d.ts +2 -1
  54. package/dist/generated/sdkFactTables.js +5 -0
  55. package/dist/generated/settingsSchemaTables.d.ts +4 -0
  56. package/dist/generated/settingsSchemaTables.js +1 -0
  57. package/dist/hitl/approvalAskers.d.ts +6 -0
  58. package/dist/hitl/approvalAskers.js +30 -0
  59. package/dist/hitl/approvalCardReasons.js +6 -3
  60. package/dist/hitl/approvalDelivery.d.ts +1 -0
  61. package/dist/hitl/approvalDelivery.js +1 -1
  62. package/dist/hitl/approvalRequestFrameReading.d.ts +7 -0
  63. package/dist/hitl/approvalRequestFrameReading.js +11 -0
  64. package/dist/hitl/approvalResolution.d.ts +9 -2
  65. package/dist/hitl/approvalResolution.js +6 -1
  66. package/dist/hitl/askGateWire.js +5 -8
  67. package/dist/hitl/automaticAnswerVerdict.js +2 -2
  68. package/dist/hitl/decisionConflict.js +6 -3
  69. package/dist/hitl/decisionRefusal.d.ts +2 -0
  70. package/dist/hitl/decisionRefusal.js +8 -8
  71. package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
  72. package/dist/hitl/decisionRefusalCarrier.js +348 -0
  73. package/dist/hitl/decisionSend.d.ts +10 -0
  74. package/dist/hitl/decisionSend.js +57 -17
  75. package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
  76. package/dist/hitl/editedApprovalAcceptance.js +26 -0
  77. package/dist/hitl/elicitationRefusal.d.ts +4 -0
  78. package/dist/hitl/elicitationRefusal.js +23 -0
  79. package/dist/hitl/frameRouter.d.ts +2 -2
  80. package/dist/hitl/frameRouter.js +12 -33
  81. package/dist/hitl/gateIdentity.d.ts +5 -0
  82. package/dist/hitl/gateIdentity.js +23 -0
  83. package/dist/hitl/gateLedger.d.ts +0 -4
  84. package/dist/hitl/gateLedger.js +0 -19
  85. package/dist/hitl/hitlBridge.d.ts +7 -0
  86. package/dist/hitl/hitlBridge.js +14 -8
  87. package/dist/hitl/hitlHostSurface.d.ts +3 -1
  88. package/dist/hitl/hitlHostSurface.js +14 -4
  89. package/dist/hitl/hostWithdrawal.d.ts +15 -0
  90. package/dist/hitl/hostWithdrawal.js +38 -0
  91. package/dist/hitl/localAllowRedemption.d.ts +67 -0
  92. package/dist/hitl/localAllowRedemption.js +256 -0
  93. package/dist/hitl/parkResolver.js +32 -13
  94. package/dist/hitl/persistedRulesWire.d.ts +16 -0
  95. package/dist/hitl/persistedRulesWire.js +72 -2
  96. package/dist/hitl/planReviewWire.d.ts +3 -0
  97. package/dist/hitl/planReviewWire.js +37 -10
  98. package/dist/hitl/policyDenyCause.d.ts +1 -1
  99. package/dist/hitl/policyDenyCause.js +1 -1
  100. package/dist/hitl/resumeRunningCard.d.ts +1 -0
  101. package/dist/hitl/resumeRunningCard.js +1 -0
  102. package/dist/hitl/screenCardCarriers.d.ts +15 -0
  103. package/dist/hitl/screenCardCarriers.js +41 -0
  104. package/dist/hitl/screenDecisionCards.d.ts +98 -0
  105. package/dist/hitl/screenDecisionCards.js +362 -0
  106. package/dist/hitl/sessionPolicyWire.js +6 -4
  107. package/dist/hitl/standingRows.d.ts +4 -1
  108. package/dist/hitl/standingRows.js +33 -1
  109. package/dist/hitl/toolApprovalWire.d.ts +64 -15
  110. package/dist/hitl/toolApprovalWire.js +470 -125
  111. package/dist/hooksWireCaps.js +31 -1
  112. package/dist/hostSettingNoticeArm.d.ts +3 -0
  113. package/dist/hostSettingNoticeArm.js +12 -0
  114. package/dist/hostTurnSettings.d.ts +13 -4
  115. package/dist/hostTurnSettings.js +150 -10
  116. package/dist/humanTurnRead.js +11 -1
  117. package/dist/i18n/messages.en.d.ts +212 -0
  118. package/dist/i18n/messages.en.js +217 -5
  119. package/dist/i18n/messages.zh.js +235 -5
  120. package/dist/i18n/translate.d.ts +3 -0
  121. package/dist/i18n/translate.js +78 -10
  122. package/dist/i18n/zh.d.ts +1 -0
  123. package/dist/i18n/zh.js +1 -0
  124. package/dist/index.d.ts +56 -16
  125. package/dist/index.js +40 -12
  126. package/dist/inheritEnvCapability.js +2 -0
  127. package/dist/inputAuthor.d.ts +16 -0
  128. package/dist/inputAuthor.js +44 -0
  129. package/dist/interactiveHalt.js +2 -1
  130. package/dist/interactiveInterrupt.js +20 -2
  131. package/dist/interruptRefusalNote.d.ts +2 -0
  132. package/dist/interruptRefusalNote.js +17 -0
  133. package/dist/liveModelCatalog.d.ts +2 -0
  134. package/dist/liveModelCatalog.js +23 -9
  135. package/dist/liveQuestionStore.d.ts +4 -1
  136. package/dist/liveQuestionStore.js +12 -3
  137. package/dist/localEngineSeatReading.d.ts +2 -2
  138. package/dist/localEngineSeatReading.js +5 -3
  139. package/dist/localEngineSpawn.d.ts +0 -7
  140. package/dist/localEngineSpawn.js +0 -92
  141. package/dist/localEngineStep.d.ts +2 -2
  142. package/dist/localEngineStep.js +2 -0
  143. package/dist/mcpEngineLeg.d.ts +3 -0
  144. package/dist/mcpEngineLeg.js +38 -1
  145. package/dist/mcpPanel.js +2 -0
  146. package/dist/mcpReconnect.d.ts +9 -1
  147. package/dist/mcpReconnect.js +124 -19
  148. package/dist/mcpWireCaps.js +1 -1
  149. package/dist/model/engineModelWords.d.ts +3 -0
  150. package/dist/model/engineModelWords.js +13 -0
  151. package/dist/model/subagentModelWord.d.ts +21 -2
  152. package/dist/model/subagentModelWord.js +100 -29
  153. package/dist/modelCapabilityProbe.js +7 -43
  154. package/dist/modsFace.d.ts +8 -1
  155. package/dist/modsFace.js +27 -0
  156. package/dist/notifications.d.ts +36 -6
  157. package/dist/notifications.js +170 -20
  158. package/dist/ownKey.d.ts +6 -5
  159. package/dist/ownKey.js +31 -7
  160. package/dist/peerFrames.d.ts +40 -2
  161. package/dist/peerFrames.js +158 -3
  162. package/dist/pendingUserRow.d.ts +4 -0
  163. package/dist/pendingUserRow.js +16 -0
  164. package/dist/permissionsDeclaration.d.ts +1 -0
  165. package/dist/permissionsDeclaration.js +11 -1
  166. package/dist/planReviewCardFacts.d.ts +3 -0
  167. package/dist/planReviewCardFacts.js +52 -2
  168. package/dist/pluginHooksWire.d.ts +10 -1
  169. package/dist/pluginHooksWire.js +96 -7
  170. package/dist/printToolResultFrame.js +4 -2
  171. package/dist/promptBlockedCopy.js +11 -6
  172. package/dist/promptSuggestionBatch.d.ts +7 -0
  173. package/dist/promptSuggestionBatch.js +9 -0
  174. package/dist/promptSuggestionsTail.d.ts +39 -0
  175. package/dist/promptSuggestionsTail.js +116 -0
  176. package/dist/replayBody.js +10 -2
  177. package/dist/request/taskRequest.d.ts +5 -0
  178. package/dist/request/taskRequest.js +142 -21
  179. package/dist/requestCoordinate.d.ts +1 -0
  180. package/dist/requestCoordinate.js +13 -0
  181. package/dist/resumeRefusalCopy.d.ts +1 -3
  182. package/dist/resumeRefusalCopy.js +2 -1
  183. package/dist/retryStatus.d.ts +2 -2
  184. package/dist/retryStatus.js +2 -0
  185. package/dist/rewindArchiveReading.js +4 -4
  186. package/dist/runCancelContext.d.ts +2 -1
  187. package/dist/runCancelContext.js +1 -0
  188. package/dist/runPermissionModeSwitch.js +2 -2
  189. package/dist/scenarioRefusal.js +11 -13
  190. package/dist/scenarioSkills.js +11 -6
  191. package/dist/seam.d.ts +27 -2
  192. package/dist/seam.js +17 -1
  193. package/dist/seatContract.d.ts +189 -12
  194. package/dist/seatContract.js +401 -9
  195. package/dist/sessionBackground.js +2 -1
  196. package/dist/sessionBackgroundHandoff.js +2 -1
  197. package/dist/sessionCostReading.d.ts +70 -0
  198. package/dist/sessionCostReading.js +150 -0
  199. package/dist/sessionEngine.d.ts +2 -0
  200. package/dist/sessionEngine.js +111 -0
  201. package/dist/sessionHooksRoster.d.ts +4 -0
  202. package/dist/sessionHooksRoster.js +158 -0
  203. package/dist/sessionMemoryStatus.d.ts +2 -1
  204. package/dist/sessionMemoryStatus.js +2 -2
  205. package/dist/sessionReadRedaction.d.ts +7 -0
  206. package/dist/sessionReadRedaction.js +26 -0
  207. package/dist/sessionSyncFileHistory.d.ts +4 -0
  208. package/dist/sessionSyncFileHistory.js +27 -0
  209. package/dist/settingsHooksSalvage.js +2 -1
  210. package/dist/sseIdleTriage.d.ts +13 -1
  211. package/dist/sseIdleTriage.js +25 -6
  212. package/dist/storeDurabilityWord.d.ts +15 -0
  213. package/dist/storeDurabilityWord.js +18 -0
  214. package/dist/storePostureCapability.d.ts +7 -3
  215. package/dist/storePostureCapability.js +69 -39
  216. package/dist/subagent/engineSubagentResume.js +2 -2
  217. package/dist/subagent/engineSubagentTail.js +8 -1
  218. package/dist/subagentContentStore.d.ts +11 -0
  219. package/dist/subagentContentStore.js +28 -2
  220. package/dist/terminalStatuses.d.ts +1 -0
  221. package/dist/terminalStatuses.js +4 -1
  222. package/dist/toolResult.d.ts +1 -0
  223. package/dist/toolResult.js +2 -0
  224. package/dist/turnFailurePresentation.js +9 -2
  225. package/dist/typePins.d.ts +11 -0
  226. package/dist/usageAnalytics.d.ts +17 -0
  227. package/dist/usageAnalytics.js +44 -2
  228. package/dist/userAttachmentEnvelope.d.ts +2 -1
  229. package/dist/userAttachmentEnvelope.js +10 -0
  230. package/dist/wireErrorTriage.js +2 -1
  231. package/dist/wireFailureShape.d.ts +6 -5
  232. package/dist/wireFailureShape.js +150 -38
  233. package/dist/wireRefusalCopy.d.ts +16 -5
  234. package/dist/wireRefusalCopy.js +119 -86
  235. package/docs/INTEGRATION-CLIENTS.md +4228 -21
  236. package/package.json +7 -3
@@ -2,10 +2,12 @@ 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
- import { guardedRead, ownSnapshot, ownSnapshotRecord, ownSnapshotTyped, readOwnDataValue, workflowCompletionExtrasOf } from './ownKey.js';
8
+ import { guardedRead, ownSnapshot, ownSnapshotRecord, ownSnapshotTyped, readOwnDataNonEmptyString, readOwnDataValue, resupplyTruncatedFactsOf, resupplyTruncatedOf, workflowCompletionExtrasOf } from './ownKey.js';
9
+ import { msg, renderMessage } from './i18n/translate.js';
10
+ import { displayUntrustedCapped } from './displayUntrusted.js';
9
11
  function escapeXml(v) {
10
12
  return v.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
11
13
  }
@@ -102,6 +104,7 @@ export function freezeTaskNotificationFields(n) {
102
104
  ...(n.outputFile !== undefined ? { outputFile: n.outputFile } : {}),
103
105
  ...(n.diagnostics !== undefined ? { diagnostics: n.diagnostics } : {}),
104
106
  ...(n.result !== undefined ? { result: n.result } : {}),
107
+ ...(n.error !== undefined ? { error: n.error } : {}),
105
108
  ...(n.stoppedBy !== undefined ? { stoppedBy: n.stoppedBy } : {}),
106
109
  ...(n.partial !== undefined ? { partial: n.partial } : {}),
107
110
  ...(n.exitCode !== undefined ? { exitCode: n.exitCode } : {}),
@@ -128,7 +131,7 @@ export function taskNotificationOf(carrier) {
128
131
  if (typeof taskId !== 'string' || taskId.length === 0 || typeof status !== 'string' || typeof summary !== 'string')
129
132
  return undefined;
130
133
  const out = { taskId, status, summary };
131
- for (const k of ['taskType', 'toolUseId', 'outputFile', 'diagnostics', 'result', 'stoppedBy', 'externalSource']) {
134
+ for (const k of ['taskType', 'toolUseId', 'outputFile', 'diagnostics', 'result', 'error', 'stoppedBy', 'externalSource']) {
132
135
  const v = slot(k);
133
136
  if (typeof v === 'string')
134
137
  out[k] = v;
@@ -211,6 +214,7 @@ export function normalizeTaskNotification(raw) {
211
214
  ? n.lines.filter((l) => typeof l === 'string')
212
215
  : [];
213
216
  const recentSteps = deriveNotificationResidualLines(n);
217
+ const failureReason = failureReasonOf(status, n.error);
214
218
  return {
215
219
  taskId,
216
220
  status,
@@ -228,6 +232,7 @@ export function normalizeTaskNotification(raw) {
228
232
  ? { diagnostics: n.diagnostics }
229
233
  : {}),
230
234
  ...(typeof n.result === 'string' && n.result.length > 0 ? { result: n.result } : {}),
235
+ ...(failureReason !== undefined ? { error: failureReason } : {}),
231
236
  ...(typeof n.stoppedBy === 'string' && n.stoppedBy.length > 0 ? { stoppedBy: n.stoppedBy } : {}),
232
237
  ...(n.partial === true ? { partial: true } : {}),
233
238
  ...(typeof n.exitCode === 'number' ? { exitCode: n.exitCode } : {}),
@@ -420,6 +425,7 @@ export function enqueuePlanReviewOutcome(value, meta) {
420
425
  queuePortMisses++;
421
426
  return false;
422
427
  }
428
+ const questionId = meta === undefined ? undefined : guardedRead(() => readOwnDataNonEmptyString(meta, 'questionId'));
423
429
  p.enqueueMetaPrompt({
424
430
  value,
425
431
  mode: 'prompt',
@@ -434,12 +440,34 @@ export function enqueuePlanReviewOutcome(value, meta) {
434
440
  decision: meta.decision,
435
441
  effect: meta.effect,
436
442
  ...(meta.effect === 'still_parked' && meta.cause === 'stored_form_unsupported' ? { cause: meta.cause } : {}),
443
+ ...(questionId !== undefined ? { questionId } : {}),
437
444
  }),
438
445
  }
439
446
  : {}),
440
447
  });
441
448
  return true;
442
449
  }
450
+ export function resupplyTruncatedSentence(facts, lang) {
451
+ try {
452
+ const f = resupplyTruncatedFactsOf(facts);
453
+ if (f === undefined)
454
+ return undefined;
455
+ const count = f.droppedKeys.length;
456
+ return renderMessage(msg(count === 1 ? 'notice.resupply.truncated.one' : 'notice.resupply.truncated.other', count === 1 ? { bytes: f.bytes } : { bytes: f.bytes, count }), lang);
457
+ }
458
+ catch {
459
+ return undefined;
460
+ }
461
+ }
462
+ export function resupplyTruncatedArmBody(carrier, source, taskId) {
463
+ const facts = resupplyTruncatedOf(carrier);
464
+ if (facts === undefined)
465
+ return undefined;
466
+ const text = resupplyTruncatedSentence(facts);
467
+ if (text === undefined)
468
+ return undefined;
469
+ return { source, taskId, bytes: facts.bytes, droppedKeys: facts.droppedKeys, text };
470
+ }
443
471
  export function dropQueuedNotificationsForRun(taskId) {
444
472
  const needle = `<${TASK_ID_TAG}>${escapeXml(taskId)}</${TASK_ID_TAG}>`;
445
473
  const p = port();
@@ -478,6 +506,61 @@ const cardEnqueuedRunIds = new Set();
478
506
  export function isWorkflowCompletionCardEnqueued(runId) {
479
507
  return cardEnqueuedRunIds.has(runId);
480
508
  }
509
+ export function workflowRunTerminalReadingOf(body) {
510
+ const snap = probeAnswerSnapshot(body);
511
+ if (snap === null)
512
+ return Object.freeze({ state: 'unknown', why: 'body_unreadable' });
513
+ const status = guardedRead(() => readOwnDataValue(snap, 'status'));
514
+ if (typeof status !== 'string')
515
+ return Object.freeze({ state: 'unknown', why: 'status_unreadable' });
516
+ if (status === 'running')
517
+ return Object.freeze({ state: 'running' });
518
+ if (!isCoreWorkflowRunTerminal(status))
519
+ return Object.freeze({ state: 'unknown', why: 'status_unrecognized' });
520
+ if (isCoreWorkflowRunParkedEnd(status, guardedRead(() => readOwnDataValue(snap, 'errorCode'))))
521
+ return Object.freeze({ state: 'awaiting approval' });
522
+ const result = guardedRead(() => readOwnDataValue(snap, 'result'));
523
+ const shownError = failureReasonOf(status, guardedRead(() => readOwnDataValue(snap, 'error')));
524
+ const resultFull = guardedRead(() => readOwnDataValue(snap, 'resultFull'));
525
+ return Object.freeze({
526
+ state: status,
527
+ ...(typeof result === 'string' && result.length > 0 ? { result } : {}),
528
+ ...(shownError !== undefined ? { error: shownError } : {}),
529
+ pointer: 'absent',
530
+ ...(typeof resultFull === 'string' && resultFull.length > 0 ? { truncated: true } : {}),
531
+ });
532
+ }
533
+ const WORKFLOW_RUN_ERROR_MAX = 200;
534
+ function shownRunError(raw) {
535
+ return displayUntrustedCapped(raw, WORKFLOW_RUN_ERROR_MAX, { foldLines: false, escapeBackslashes: 'auto' });
536
+ }
537
+ function failureReasonOf(status, raw) {
538
+ if (status !== 'failed' || typeof raw !== 'string' || raw.length === 0)
539
+ return undefined;
540
+ const shown = shownRunError(raw);
541
+ return shown.length > 0 ? shown : undefined;
542
+ }
543
+ function workflowPollDecisionOf(delivered, reading) {
544
+ if (delivered)
545
+ return { act: 'stop', why: 'delivered' };
546
+ if (reading.state === 'running')
547
+ return { act: 'wait', why: 'running' };
548
+ if (reading.state === 'awaiting approval')
549
+ return { act: 'wait', why: 'awaiting approval' };
550
+ if (reading.state === 'unknown')
551
+ return { act: 'wait', why: 'unknown' };
552
+ return {
553
+ act: 'emit',
554
+ status: reading.state,
555
+ ...(reading.result !== undefined ? { result: reading.result } : {}),
556
+ ...(reading.error !== undefined ? { error: reading.error } : {}),
557
+ ...(reading.truncated === true ? { truncated: true } : {}),
558
+ };
559
+ }
560
+ function workflowPollCompletionSummary(status, error) {
561
+ const head = `workflow ${status} (read from its run record; no handle to the full record is available)`;
562
+ return error === undefined ? head : `${head} — failure reason from the run record: ${error}`;
563
+ }
481
564
  const outstandingRuns = new Map();
482
565
  const outstandingListeners = new Set();
483
566
  function notifyOutstanding() {
@@ -493,7 +576,7 @@ export function outstandingWorkflowCount() {
493
576
  return outstandingRuns.size;
494
577
  }
495
578
  export function outstandingDeliverableWorkflowCount() {
496
- return statusProbe ? outstandingRuns.size : 0;
579
+ return workflowRunReader ? outstandingRuns.size : 0;
497
580
  }
498
581
  export function subscribeOutstandingWorkflows(listener) {
499
582
  outstandingListeners.add(listener);
@@ -531,12 +614,12 @@ export function notificationDropCounters() {
531
614
  bgWatchCollectedUnprobed,
532
615
  };
533
616
  }
534
- let statusProbe = null;
617
+ let workflowRunReader = null;
535
618
  let watchTimer = null;
536
619
  const WATCH_INTERVAL_MS = 5000;
537
620
  const WATCH_TTL_MS = 2 * 60 * 60 * 1000;
538
- export function installWorkflowStatusProbe(probe) {
539
- statusProbe = probe;
621
+ export function installWorkflowRunReader(reader) {
622
+ workflowRunReader = reader;
540
623
  }
541
624
  const BG_FIRST_SEQ = 1;
542
625
  function normalizeBgSeq(seq) {
@@ -762,26 +845,25 @@ async function tickWatchInner(now) {
762
845
  bgProbeInFlight.delete(key);
763
846
  }
764
847
  }
765
- const probe = statusProbe;
848
+ const reader = workflowRunReader;
766
849
  for (const runId of [...outstandingRuns.keys()]) {
767
850
  if (notifiedRunIds.has(runId)) {
768
851
  if (outstandingRuns.delete(runId))
769
852
  notifyOutstanding();
770
853
  continue;
771
854
  }
772
- if (!probe)
855
+ if (!reader)
773
856
  continue;
774
857
  runProbeInFlight.set(runId, now);
775
858
  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
- });
859
+ const reading = workflowRunTerminalReadingOf(await withProbeDeadline(reader(runId), probeTimeoutMs()));
860
+ const decision = workflowPollDecisionOf(notifiedRunIds.has(runId), reading);
861
+ if (decision.act === 'wait')
862
+ continue;
863
+ if (outstandingRuns.delete(runId))
864
+ notifyOutstanding();
865
+ if (decision.act === 'emit') {
866
+ enqueueWorkflowCompletion({ runId, status: decision.status, summary: workflowPollCompletionSummary(decision.status, decision.error) }, { ...(decision.result !== undefined ? { result: decision.result } : {}), ...(decision.error !== undefined ? { error: decision.error } : {}) }, { truncated: decision.truncated === true });
785
867
  }
786
868
  }
787
869
  catch {
@@ -845,6 +927,17 @@ export function enqueueEngineWorkflowNotification(raw) {
845
927
  traceNotif('workflow-notification dropped:payload-unreadable');
846
928
  return;
847
929
  }
930
+ const extras = workflowCompletionExtrasOf(c);
931
+ if (notifiedRunIds.has(c.runId)) {
932
+ const supplement = takeWorkflowPointerSupplement(c.runId, c.status, extras.diagnostics, extras.completionId);
933
+ if (supplement !== undefined) {
934
+ port()?.enqueuePendingNotification(taskNotificationQueueCommand(supplement));
935
+ return;
936
+ }
937
+ }
938
+ enqueueWorkflowCompletion(c, completionIdOnly(extras));
939
+ }
940
+ function enqueueWorkflowCompletion(c, body, poll) {
848
941
  if (notifiedRunIds.has(c.runId)) {
849
942
  workflowDedupDropped++;
850
943
  traceNotif(`workflow notification suppressed (run already notified) runId=${c.runId} status=${c.status}`);
@@ -852,11 +945,68 @@ export function enqueueEngineWorkflowNotification(raw) {
852
945
  }
853
946
  markRunNotified(c.runId);
854
947
  cardEnqueuedRunIds.add(c.runId);
855
- port()?.enqueuePendingNotification(taskNotificationQueueCommand({ taskId: c.runId, taskType: 'workflow', status: c.status, summary: c.summary, ...completionIdOnly(workflowCompletionExtrasOf(c)) }));
948
+ if (poll !== undefined)
949
+ notePointerOwed(c.runId, poll.truncated);
950
+ port()?.enqueuePendingNotification(taskNotificationQueueCommand({ taskId: c.runId, taskType: 'workflow', status: c.status, summary: c.summary, ...body }));
951
+ }
952
+ const pointerOwedRuns = new Map();
953
+ const POINTER_OWED_MAX = 512;
954
+ function notePointerOwed(runId, truncated) {
955
+ pointerOwedRuns.delete(runId);
956
+ pointerOwedRuns.set(runId, { truncated });
957
+ while (pointerOwedRuns.size > POINTER_OWED_MAX) {
958
+ const oldest = pointerOwedRuns.keys().next();
959
+ if (oldest.done === true)
960
+ break;
961
+ pointerOwedRuns.delete(oldest.value);
962
+ }
963
+ }
964
+ const WORKFLOW_RECORD_REF_SENTENCE = /Full record \(the result and every agent's row\): read it with ReadToolResult \(ref "([A-Za-z0-9_.~-]{1,518})"\) before assuming an empty or unexpected result\./g;
965
+ export function workflowRecordRefOf(diagnostics) {
966
+ if (typeof diagnostics !== 'string' || diagnostics.length === 0)
967
+ return undefined;
968
+ try {
969
+ let ref;
970
+ for (const m of diagnostics.matchAll(WORKFLOW_RECORD_REF_SENTENCE))
971
+ ref = m[1];
972
+ return ref;
973
+ }
974
+ catch {
975
+ return undefined;
976
+ }
977
+ }
978
+ function workflowPointerSupplementSummary(ref, truncated) {
979
+ const head = `Full record of this workflow run (the result and every agent's row): read it with ReadToolResult (ref "${ref}")`;
980
+ return truncated ? `${head} — the result delivered earlier was cut short; the full text is in this record.` : `${head}.`;
981
+ }
982
+ export function takeWorkflowPointerSupplement(runId, status, diagnostics, completionId) {
983
+ try {
984
+ const owed = pointerOwedRuns.get(runId);
985
+ if (owed === undefined)
986
+ return undefined;
987
+ const ref = workflowRecordRefOf(diagnostics);
988
+ if (ref === undefined)
989
+ return undefined;
990
+ pointerOwedRuns.delete(runId);
991
+ return freezeTaskNotificationFields({
992
+ taskId: runId,
993
+ taskType: 'workflow',
994
+ status,
995
+ summary: workflowPointerSupplementSummary(ref, owed.truncated),
996
+ ...(completionId !== undefined ? { completionId } : {}),
997
+ });
998
+ }
999
+ catch {
1000
+ return undefined;
1001
+ }
1002
+ }
1003
+ export function settleWorkflowPointerOwed(runId) {
1004
+ pointerOwedRuns.delete(runId);
856
1005
  }
857
1006
  export function _resetEngineTaskNotificationForTest() {
858
1007
  notifiedRunIds.clear();
859
1008
  notifiedBgCycleHigh.clear();
1009
+ pointerOwedRuns.clear();
860
1010
  cardEnqueuedRunIds.clear();
861
1011
  outstandingRuns.clear();
862
1012
  outstandingBgTasks.clear();
@@ -867,7 +1017,7 @@ export function _resetEngineTaskNotificationForTest() {
867
1017
  bgProbeInFlight.clear();
868
1018
  runProbeInFlight.clear();
869
1019
  tickGeneration = 0;
870
- statusProbe = null;
1020
+ workflowRunReader = null;
871
1021
  bgStatusProbe = null;
872
1022
  tickStartedAtMs = null;
873
1023
  abandonedCount = 0;
package/dist/ownKey.d.ts CHANGED
@@ -39,11 +39,6 @@ export interface HumanInputAttachmentDeclinedByView {
39
39
  name: string;
40
40
  }
41
41
  export declare function humanInputAttachmentsOf(frame: {} | null | undefined): HumanInputAttachmentView[] | undefined;
42
- export interface HumanInputAuthorKeys {
43
- author?: string;
44
- extension?: string;
45
- }
46
- export declare function humanInputAuthorKeys(frame: {} | null | undefined): HumanInputAuthorKeys;
47
42
  export declare function markMcpSectionDroppedNamedRows(rows: object, dropped: ReadonlyMap<string, number>): void;
48
43
  export declare function mcpSectionDroppedNamedRowsOf(rows: object | null | undefined, name: string): number;
49
44
  export interface PromptManifestSectionRow {
@@ -84,3 +79,9 @@ export declare function workflowCompletionExtrasOf(c: object): {
84
79
  completionId?: string;
85
80
  diagnostics?: string;
86
81
  };
82
+ export interface ResupplyTruncatedFacts {
83
+ readonly bytes: number;
84
+ readonly droppedKeys: readonly string[];
85
+ }
86
+ export declare function resupplyTruncatedFactsOf(slot: {} | null | undefined): ResupplyTruncatedFacts | undefined;
87
+ export declare function resupplyTruncatedOf(carrier: object | null | undefined): ResupplyTruncatedFacts | undefined;
package/dist/ownKey.js CHANGED
@@ -193,13 +193,6 @@ export function humanInputAttachmentsOf(frame) {
193
193
  return undefined;
194
194
  }
195
195
  }
196
- export function humanInputAuthorKeys(frame) {
197
- if (typeof frame !== 'object' || frame === null || Array.isArray(frame))
198
- return {};
199
- const author = guardedRead(() => readOwnDataNonEmptyString(frame, 'author'));
200
- const extension = guardedRead(() => readOwnDataNonEmptyString(frame, 'extension'));
201
- return { ...(author !== undefined ? { author } : {}), ...(extension !== undefined ? { extension } : {}) };
202
- }
203
196
  const DROPPED_NAMED_ROWS = Symbol.for('@sema-agent/client-core/mcpSectionDroppedNamedRows');
204
197
  export function markMcpSectionDroppedNamedRows(rows, dropped) {
205
198
  if (dropped.size === 0)
@@ -460,3 +453,34 @@ export function workflowCompletionExtrasOf(c) {
460
453
  ...(diagnostics !== undefined ? { diagnostics } : {}),
461
454
  };
462
455
  }
456
+ const DROPPED_KEYS_MAX = 256;
457
+ const DROPPED_KEY_NAME_MAX = 128;
458
+ export function resupplyTruncatedFactsOf(slot) {
459
+ try {
460
+ if (typeof slot !== 'object' || slot === null || Array.isArray(slot))
461
+ return undefined;
462
+ const bytes = guardedRead(() => readOwnDataValue(slot, 'bytes'));
463
+ if (typeof bytes !== 'number' || !Number.isSafeInteger(bytes) || bytes <= 0)
464
+ return undefined;
465
+ const keys = guardedRead(() => readOwnDataValue(slot, 'droppedKeys'));
466
+ if (!Array.isArray(keys))
467
+ return undefined;
468
+ const length = guardedRead(() => keys.length);
469
+ if (typeof length !== 'number' || length < 1 || length > DROPPED_KEYS_MAX)
470
+ return undefined;
471
+ const names = [];
472
+ for (let i = 0; i < length; i++) {
473
+ const name = guardedRead(() => readOwnDataValue(keys, String(i)));
474
+ if (typeof name !== 'string' || name.length === 0 || name.length > DROPPED_KEY_NAME_MAX)
475
+ return undefined;
476
+ appendOwnElement(names, name);
477
+ }
478
+ return Object.freeze({ bytes, droppedKeys: Object.freeze(names) });
479
+ }
480
+ catch {
481
+ return undefined;
482
+ }
483
+ }
484
+ export function resupplyTruncatedOf(carrier) {
485
+ return resupplyTruncatedFactsOf(guardedRead(() => readOwnDataValue(carrier, 'payloadTruncated')));
486
+ }
@@ -1,6 +1,7 @@
1
+ import type { UiLanguage } from './env/uiLanguage.js';
1
2
  export declare const AGENT_MESSAGE_TAG = "agent-message";
2
3
  export declare const CROSS_SESSION_MESSAGE_TAG = "cross-session-message";
3
- export declare const PEER_FRAME_LANES: readonly ["agent_message", "cross_session_message", "cross_session_notice"];
4
+ export declare const PEER_FRAME_LANES: readonly ["agent_message", "cross_session_message", "cross_session_notice", "inbox_notice"];
4
5
  export declare const CROSS_SESSION_NOTICE_KINDS: readonly ["delivery_notice", "idle_notice"];
5
6
  export type CrossSessionNoticeKind = (typeof CROSS_SESSION_NOTICE_KINDS)[number];
6
7
  export declare const PEER_MODE_CLASSES: readonly ["bypass", "prompting"];
@@ -22,13 +23,50 @@ export type CrossSessionMessageFrame = {
22
23
  fromScope?: string;
23
24
  seq?: number;
24
25
  };
26
+ export type PeerNoticeFacts = {
27
+ state: string;
28
+ recipient?: string;
29
+ target?: string;
30
+ detail?: string;
31
+ dropReason?: string;
32
+ refSeq?: number;
33
+ finishedAt?: number;
34
+ };
25
35
  export type CrossSessionNoticeFrame = {
26
36
  lane: 'cross_session_notice';
27
37
  kind: CrossSessionNoticeKind;
28
38
  text: string;
39
+ notice?: PeerNoticeFacts;
40
+ };
41
+ export type InboxNoticeFrame = {
42
+ lane: 'inbox_notice';
43
+ pending: number;
44
+ delivered: number;
45
+ urgent?: number;
46
+ resupplied?: number;
29
47
  };
30
- export type PeerFrameProjection = AgentMessageFrame | CrossSessionMessageFrame | CrossSessionNoticeFrame;
48
+ export type PeerFrameProjection = AgentMessageFrame | CrossSessionMessageFrame | CrossSessionNoticeFrame | InboxNoticeFrame;
31
49
  export declare function classifyPeerNotification(raw: Record<string, unknown>): PeerFrameProjection | null;
50
+ export type SessionBoxNoticeFacts = {
51
+ noticeKind: 'inbox';
52
+ pending: number;
53
+ delivered: number;
54
+ urgent?: number;
55
+ resupplied?: number;
56
+ } | {
57
+ noticeKind: 'delivery' | 'idle';
58
+ state: string;
59
+ peerLabel?: string;
60
+ detail?: string;
61
+ dropReason?: string;
62
+ refSeq?: number;
63
+ finishedAt?: number;
64
+ };
65
+ export type SessionBoxNoticeBody = SessionBoxNoticeFacts & {
66
+ text: string;
67
+ };
68
+ export declare function sessionBoxNoticeSentence(facts: SessionBoxNoticeFacts, lang?: UiLanguage): string | undefined;
69
+ export declare function sessionBoxNoticeOf(p: PeerFrameProjection): SessionBoxNoticeBody | undefined;
32
70
  export declare function renderPeerFrameTranscriptText(p: PeerFrameProjection): string;
33
71
  export declare function parsePeerFrameText(text: string): AgentMessageFrame | CrossSessionMessageFrame | null;
34
72
  export declare function peerFrameDisplayName(p: AgentMessageFrame | CrossSessionMessageFrame): string;
@@ -1,4 +1,5 @@
1
- import { applyDisplayFace, PEER_NAME_FACE } from './displayUntrusted.js';
1
+ import { applyDisplayFace, displayUntrusted, PEER_NAME_FACE } from './displayUntrusted.js';
2
+ import { msg, renderMessage } from './i18n/translate.js';
2
3
  import { ownSnapshotRecord } from './ownKey.js';
3
4
  import { CORE_ENGINE_AUTHORITY_ENVELOPE_TAGS, CORE_PERMISSION_MODE_CLASSES } from './generated/engineFactTables.js';
4
5
  const WIRE_KEY_FROM = 'from';
@@ -12,6 +13,20 @@ const WIRE_KEY_AGENT_TYPE = 'agentType';
12
13
  const WIRE_KEY_AGENT_MESSAGE = 'agentMessage';
13
14
  const WIRE_KEY_CROSS_SESSION_MESSAGE = 'crossSessionMessage';
14
15
  const WIRE_KEY_CROSS_SESSION_NOTICE = 'crossSessionNotice';
16
+ const WIRE_KEY_INBOX_NOTICE = 'inboxNotice';
17
+ const WIRE_KEY_PENDING = 'pending';
18
+ const WIRE_KEY_DELIVERED = 'delivered';
19
+ const WIRE_KEY_URGENT = 'urgent';
20
+ const WIRE_KEY_RESUPPLIED = 'resupplied';
21
+ const WIRE_KEY_PEER_META = 'peerMeta';
22
+ const WIRE_KEY_NOTICE = 'notice';
23
+ const WIRE_KEY_STATE = 'state';
24
+ const WIRE_KEY_REF_SEQ = 'refSeq';
25
+ const WIRE_KEY_RECIPIENT = 'recipient';
26
+ const WIRE_KEY_TARGET = 'target';
27
+ const WIRE_KEY_DETAIL = 'detail';
28
+ const WIRE_KEY_DROP_REASON = 'dropReason';
29
+ const WIRE_KEY_FINISHED_AT = 'finishedAt';
15
30
  const WIRE_KEY_PROVENANCE = '_sema_provenance';
16
31
  const WIRE_KEY_FROM_SESSION = 'fromSession';
17
32
  const WIRE_KEY_FROM_NAME = 'fromName';
@@ -20,7 +35,7 @@ const WIRE_KEY_FROM_SCOPE = 'fromScope';
20
35
  const FROM_ATTR = WIRE_KEY_FROM;
21
36
  export const AGENT_MESSAGE_TAG = 'agent-message';
22
37
  export const CROSS_SESSION_MESSAGE_TAG = 'cross-session-message';
23
- export const PEER_FRAME_LANES = Object.freeze(['agent_message', 'cross_session_message', 'cross_session_notice']);
38
+ export const PEER_FRAME_LANES = Object.freeze(['agent_message', 'cross_session_message', 'cross_session_notice', 'inbox_notice']);
24
39
  export const CROSS_SESSION_NOTICE_KINDS = Object.freeze(['delivery_notice', 'idle_notice']);
25
40
  export const PEER_MODE_CLASSES = CORE_PERMISSION_MODE_CLASSES;
26
41
  function dataProp(o, key) {
@@ -43,6 +58,40 @@ function nonEmptyString(v) {
43
58
  function finiteNumber(v) {
44
59
  return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
45
60
  }
61
+ function countOf(v) {
62
+ return typeof v === 'number' && Number.isSafeInteger(v) && v >= 0 ? v : undefined;
63
+ }
64
+ function shownLabel(v, max) {
65
+ if (typeof v !== 'string')
66
+ return undefined;
67
+ const s = displayUntrusted(v, { blanks: true, max });
68
+ return s.length > 0 ? s : undefined;
69
+ }
70
+ const NOTICE_LABEL_MAX = 64;
71
+ const NOTICE_TEXT_MAX = 200;
72
+ function peerNoticeOf(n) {
73
+ const notice = dataProp(dataProp(dataProp(n, WIRE_KEY_PROVENANCE), WIRE_KEY_PEER_META), WIRE_KEY_NOTICE);
74
+ if (notice === null || typeof notice !== 'object')
75
+ return undefined;
76
+ const state = nonEmptyString(dataProp(notice, WIRE_KEY_STATE));
77
+ if (state === undefined)
78
+ return undefined;
79
+ const recipient = shownLabel(dataProp(notice, WIRE_KEY_RECIPIENT), NOTICE_LABEL_MAX);
80
+ const target = shownLabel(dataProp(notice, WIRE_KEY_TARGET), NOTICE_LABEL_MAX);
81
+ const detail = shownLabel(dataProp(notice, WIRE_KEY_DETAIL), NOTICE_TEXT_MAX);
82
+ const dropReason = shownLabel(dataProp(notice, WIRE_KEY_DROP_REASON), NOTICE_TEXT_MAX);
83
+ const refSeq = countOf(dataProp(notice, WIRE_KEY_REF_SEQ));
84
+ const finishedAt = countOf(dataProp(notice, WIRE_KEY_FINISHED_AT));
85
+ return {
86
+ state,
87
+ ...(recipient !== undefined ? { recipient } : {}),
88
+ ...(target !== undefined ? { target } : {}),
89
+ ...(detail !== undefined ? { detail } : {}),
90
+ ...(dropReason !== undefined ? { dropReason } : {}),
91
+ ...(refSeq !== undefined ? { refSeq } : {}),
92
+ ...(finishedAt !== undefined ? { finishedAt } : {}),
93
+ };
94
+ }
46
95
  function provenanceAgrees(n, lane, carrierFromRaw) {
47
96
  const prov = dataProp(n, WIRE_KEY_PROVENANCE);
48
97
  if (prov === null || typeof prov !== 'object')
@@ -112,7 +161,24 @@ export function classifyPeerNotification(raw) {
112
161
  const text = nonEmptyString(dataProp(crossSessionNotice, WIRE_KEY_TEXT));
113
162
  if (kind === undefined || text === undefined || !provenanceAgrees(n, 'cross_session_notice'))
114
163
  return null;
115
- return { lane: 'cross_session_notice', kind, text };
164
+ const notice = peerNoticeOf(n);
165
+ return { lane: 'cross_session_notice', kind, text, ...(notice !== undefined ? { notice } : {}) };
166
+ }
167
+ const inboxNotice = dataProp(n, WIRE_KEY_INBOX_NOTICE);
168
+ if (inboxNotice !== undefined) {
169
+ const pending = countOf(dataProp(inboxNotice, WIRE_KEY_PENDING));
170
+ const delivered = countOf(dataProp(inboxNotice, WIRE_KEY_DELIVERED));
171
+ if (pending === undefined || delivered === undefined || !provenanceAgrees(n, 'inbox_notice'))
172
+ return null;
173
+ const urgent = countOf(dataProp(inboxNotice, WIRE_KEY_URGENT));
174
+ const resupplied = countOf(dataProp(inboxNotice, WIRE_KEY_RESUPPLIED));
175
+ return {
176
+ lane: 'inbox_notice',
177
+ pending,
178
+ delivered,
179
+ ...(urgent !== undefined ? { urgent } : {}),
180
+ ...(resupplied !== undefined ? { resupplied } : {}),
181
+ };
116
182
  }
117
183
  return null;
118
184
  }
@@ -140,9 +206,98 @@ function attr(v) {
140
206
  function unattr(v) {
141
207
  return v.replace(/&quot;/g, '"').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
142
208
  }
209
+ const DELIVERY_SENTENCE_KEYS = Object.freeze({
210
+ delivered: 'notice.sessionBox.delivery.delivered',
211
+ held: 'notice.sessionBox.delivery.held',
212
+ held_by_policy: 'notice.sessionBox.delivery.heldByPolicy',
213
+ denied: 'notice.sessionBox.delivery.denied',
214
+ refused: 'notice.sessionBox.delivery.refused',
215
+ expired: 'notice.sessionBox.delivery.expired',
216
+ });
217
+ const IDLE_SENTENCE_KEYS = Object.freeze({
218
+ exited: 'notice.sessionBox.idle.exited',
219
+ unavailable: 'notice.sessionBox.idle.unavailable',
220
+ expired: 'notice.sessionBox.idle.expired',
221
+ });
222
+ function sentenceKeyOf(table, word) {
223
+ return Object.hasOwn(table, word) ? table[word] : undefined;
224
+ }
225
+ function joinParts(parts, lang) {
226
+ return parts.join(lang === 'zh' ? '' : ' ');
227
+ }
228
+ function inboxLine(pending, urgent, resupplied, lang) {
229
+ const parts = [renderMessage(pending === 1 ? msg('notice.sessionBox.inbox.one') : msg('notice.sessionBox.inbox.many', { pending }), lang)];
230
+ if (urgent !== undefined)
231
+ parts.push(renderMessage(msg('notice.sessionBox.inbox.urgent', { urgent }), lang));
232
+ if (resupplied !== undefined)
233
+ parts.push(renderMessage(msg('notice.sessionBox.inbox.resupplied', { resupplied }), lang));
234
+ return joinParts(parts, lang);
235
+ }
236
+ export function sessionBoxNoticeSentence(facts, lang) {
237
+ try {
238
+ const noticeKind = dataProp(facts, 'noticeKind');
239
+ if (noticeKind === 'inbox') {
240
+ const pending = countOf(dataProp(facts, WIRE_KEY_PENDING));
241
+ if (pending === undefined)
242
+ return undefined;
243
+ return inboxLine(pending, countOf(dataProp(facts, WIRE_KEY_URGENT)), countOf(dataProp(facts, WIRE_KEY_RESUPPLIED)), lang);
244
+ }
245
+ if (noticeKind !== 'delivery' && noticeKind !== 'idle')
246
+ return undefined;
247
+ const state = nonEmptyString(dataProp(facts, WIRE_KEY_STATE));
248
+ if (state === undefined)
249
+ return undefined;
250
+ const label = shownLabel(dataProp(facts, 'peerLabel'), NOTICE_LABEL_MAX);
251
+ const detail = shownLabel(dataProp(facts, WIRE_KEY_DETAIL), NOTICE_TEXT_MAX);
252
+ const dropReason = shownLabel(dataProp(facts, WIRE_KEY_DROP_REASON), NOTICE_TEXT_MAX);
253
+ if (noticeKind === 'delivery') {
254
+ const peer = label ?? renderMessage(msg('notice.sessionBox.peer.another'), lang);
255
+ if (state === 'dropped')
256
+ return renderMessage(dropReason !== undefined ? msg('notice.sessionBox.delivery.dropped', { peer, reason: dropReason }) : msg('notice.sessionBox.delivery.droppedPlain', { peer }), lang);
257
+ return renderMessage(msg(sentenceKeyOf(DELIVERY_SENTENCE_KEYS, state) ?? 'notice.sessionBox.delivery.other', { peer }), lang);
258
+ }
259
+ const peer = label ?? renderMessage(msg('notice.sessionBox.peer.anotherStart'), lang);
260
+ if (state === 'idle')
261
+ return renderMessage(detail !== undefined ? msg('notice.sessionBox.idle.finishedDetail', { peer, detail }) : msg('notice.sessionBox.idle.finished', { peer }), lang);
262
+ return renderMessage(msg(sentenceKeyOf(IDLE_SENTENCE_KEYS, state) ?? 'notice.sessionBox.idle.other', { peer }), lang);
263
+ }
264
+ catch {
265
+ return undefined;
266
+ }
267
+ }
268
+ export function sessionBoxNoticeOf(p) {
269
+ if (p.lane === 'inbox_notice') {
270
+ const facts = {
271
+ noticeKind: 'inbox',
272
+ pending: p.pending,
273
+ delivered: p.delivered,
274
+ ...(p.urgent !== undefined ? { urgent: p.urgent } : {}),
275
+ ...(p.resupplied !== undefined ? { resupplied: p.resupplied } : {}),
276
+ };
277
+ const text = sessionBoxNoticeSentence(facts);
278
+ return text === undefined ? undefined : { ...facts, text };
279
+ }
280
+ if (p.lane !== 'cross_session_notice' || p.notice === undefined)
281
+ return undefined;
282
+ const delivery = p.kind === 'delivery_notice';
283
+ const peerLabel = delivery ? (p.notice.recipient ?? p.notice.target) : (p.notice.target ?? p.notice.recipient);
284
+ const facts = {
285
+ noticeKind: delivery ? 'delivery' : 'idle',
286
+ state: p.notice.state,
287
+ ...(peerLabel !== undefined ? { peerLabel } : {}),
288
+ ...(p.notice.detail !== undefined ? { detail: p.notice.detail } : {}),
289
+ ...(p.notice.dropReason !== undefined ? { dropReason: p.notice.dropReason } : {}),
290
+ ...(p.notice.refSeq !== undefined ? { refSeq: p.notice.refSeq } : {}),
291
+ ...(p.notice.finishedAt !== undefined ? { finishedAt: p.notice.finishedAt } : {}),
292
+ };
293
+ const text = sessionBoxNoticeSentence(facts);
294
+ return text === undefined ? undefined : { ...facts, text };
295
+ }
143
296
  export function renderPeerFrameTranscriptText(p) {
144
297
  if (p.lane === 'cross_session_notice')
145
298
  return escapeEnvelopeTag(CROSS_SESSION_MESSAGE_TAG, escapeEnvelopeTag(AGENT_MESSAGE_TAG, neutralizeAuthorityTags(p.text)));
299
+ if (p.lane === 'inbox_notice')
300
+ return inboxLine(p.pending, p.urgent, p.resupplied, undefined);
146
301
  if (p.lane === 'agent_message') {
147
302
  const body = escapeEnvelopeTag(AGENT_MESSAGE_TAG, neutralizeAuthorityTags(p.body));
148
303
  return `<${AGENT_MESSAGE_TAG} ${FROM_ATTR}="${attr(p.from)}">\n${body}\n</${AGENT_MESSAGE_TAG}>`;
@@ -0,0 +1,4 @@
1
+ export declare const PENDING_USER_ROW_ID_PREFIX = "pending-";
2
+ export declare function pendingUserRowIdOf(uuid: string): string | undefined;
3
+ export declare function pendingUserRowUuidOf(id: string | null | undefined): string | undefined;
4
+ export declare function isPendingUserRowId(id: string | null | undefined): boolean;
@@ -0,0 +1,16 @@
1
+ export const PENDING_USER_ROW_ID_PREFIX = 'pending-';
2
+ const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
3
+ export function pendingUserRowIdOf(uuid) {
4
+ if (typeof uuid !== 'string' || !UUID_V4.test(uuid))
5
+ return undefined;
6
+ return PENDING_USER_ROW_ID_PREFIX + uuid;
7
+ }
8
+ export function pendingUserRowUuidOf(id) {
9
+ if (typeof id !== 'string' || !id.startsWith(PENDING_USER_ROW_ID_PREFIX))
10
+ return undefined;
11
+ const uuid = id.slice(PENDING_USER_ROW_ID_PREFIX.length);
12
+ return pendingUserRowIdOf(uuid) === id ? uuid : undefined;
13
+ }
14
+ export function isPendingUserRowId(id) {
15
+ return pendingUserRowUuidOf(id) !== undefined;
16
+ }