@sema-agent/client-core 0.94.0 → 0.95.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 (255) hide show
  1. package/CHANGELOG.md +341 -0
  2. package/README.md +70 -47
  3. package/dist/adapt/arms.js +5 -6
  4. package/dist/adapt/panelTasks.js +4 -10
  5. package/dist/adapt/toolCards.js +2 -2
  6. package/dist/adapt/wireShapes.js +3 -8
  7. package/dist/adapter/activeRunSelfHeal.d.ts +8 -3
  8. package/dist/adapter/activeRunSelfHeal.js +20 -12
  9. package/dist/adapter/downstream/eventToSdkMessage.js +24 -17
  10. package/dist/adapter/downstream/terminalToSdkResult.js +11 -3
  11. package/dist/adapter/runStream.js +2 -1
  12. package/dist/adapter/types.d.ts +7 -0
  13. package/dist/adapter/types.js +15 -0
  14. package/dist/agentSession/backgroundView.js +5 -1
  15. package/dist/agentSession/engineAgentRegistryRead.js +2 -1
  16. package/dist/agentSession/engineSessionBackgroundStop.js +3 -2
  17. package/dist/agentsWireCaps.d.ts +2 -0
  18. package/dist/approvalDecidedByCapability.d.ts +0 -1
  19. package/dist/approvalDecidedByCapability.js +2 -7
  20. package/dist/approvalsStreamLiveCapability.d.ts +0 -1
  21. package/dist/approvalsStreamLiveCapability.js +2 -6
  22. package/dist/atMostOnce.d.ts +8 -0
  23. package/dist/atMostOnce.js +36 -0
  24. package/dist/attachmentSeatCapability.d.ts +0 -1
  25. package/dist/attachmentSeatCapability.js +0 -3
  26. package/dist/attachmentsWireCaps.js +11 -6
  27. package/dist/bashExitReading.d.ts +10 -0
  28. package/dist/bashExitReading.js +12 -0
  29. package/dist/callerCwdCapability.d.ts +0 -1
  30. package/dist/callerCwdCapability.js +2 -7
  31. package/dist/centerWiringCapability.d.ts +1 -3
  32. package/dist/centerWiringCapability.js +18 -21
  33. package/dist/classifierStatus.js +4 -4
  34. package/dist/cloudConfigWireCaps.js +43 -21
  35. package/dist/compensations.js +1 -1
  36. package/dist/cycleOrder.d.ts +15 -0
  37. package/dist/cycleOrder.js +45 -0
  38. package/dist/decideFailureNote.js +4 -1
  39. package/dist/decideReceipt.d.ts +2 -1
  40. package/dist/decideReceipt.js +41 -13
  41. package/dist/declared/supportFloorDeclared.d.ts +1 -0
  42. package/dist/declared/supportFloorDeclared.js +5 -0
  43. package/dist/deviceExecutorManagementCapability.d.ts +0 -1
  44. package/dist/deviceExecutorManagementCapability.js +7 -16
  45. package/dist/deviceRebind.d.ts +88 -0
  46. package/dist/deviceRebind.js +242 -0
  47. package/dist/displayUntrusted.d.ts +1 -0
  48. package/dist/displayUntrusted.js +313 -11
  49. package/dist/engineAgentAbsence.d.ts +2 -2
  50. package/dist/engineAgentPanelStore.d.ts +8 -18
  51. package/dist/engineAgentPanelStore.js +19 -537
  52. package/dist/engineCapReader.d.ts +6 -6
  53. package/dist/engineCapReader.js +30 -43
  54. package/dist/engineCapsCache.d.ts +7 -2
  55. package/dist/engineCapsCache.js +64 -26
  56. package/dist/engineCredentialRequirement.d.ts +9 -0
  57. package/dist/engineCredentialRequirement.js +23 -0
  58. package/dist/engineEnvSourcing.d.ts +53 -0
  59. package/dist/engineEnvSourcing.js +81 -0
  60. package/dist/engineErrorCodes.d.ts +14 -3
  61. package/dist/engineErrorCodes.js +31 -10
  62. package/dist/engineIdentity.js +2 -0
  63. package/dist/engineWireSdk.js +2 -2
  64. package/dist/executionLaneCapability.d.ts +2 -3
  65. package/dist/executionLaneCapability.js +18 -23
  66. package/dist/fileHistoryCaptureCapability.d.ts +0 -1
  67. package/dist/fileHistoryCaptureCapability.js +2 -6
  68. package/dist/fileHistoryLegNotice.d.ts +3 -2
  69. package/dist/fileHistoryLegNotice.js +51 -2
  70. package/dist/fleet/fleetLedger.d.ts +2 -0
  71. package/dist/fleet/fleetLedger.js +69 -52
  72. package/dist/fleet/fleetProjection.d.ts +0 -3
  73. package/dist/fleet/fleetProjection.js +9 -16
  74. package/dist/fleetAgentPanelProjection.d.ts +4 -17
  75. package/dist/fleetAgentPanelProjection.js +7 -197
  76. package/dist/fleetAgentRowLike.d.ts +16 -0
  77. package/dist/fleetAgentRowLike.js +1 -0
  78. package/dist/gateOutcome.js +30 -18
  79. package/dist/generated/sdkFactTables.d.ts +2 -0
  80. package/dist/generated/sdkFactTables.js +17 -0
  81. package/dist/generated/settingsSchemaFns.d.ts +10 -0
  82. package/dist/generated/settingsSchemaFns.js +22 -0
  83. package/dist/headlessPermissionModeWire.d.ts +1 -1
  84. package/dist/headlessPermissionModeWire.js +1 -1
  85. package/dist/headlessReconnectWire.js +49 -20
  86. package/dist/hitl/allowRuleShadow.d.ts +1 -1
  87. package/dist/hitl/allowRuleShadow.js +49 -8
  88. package/dist/hitl/approvalCompleteParked.d.ts +1 -0
  89. package/dist/hitl/approvalCompleteParked.js +14 -0
  90. package/dist/hitl/approvalEndingCause.d.ts +5 -0
  91. package/dist/hitl/approvalEndingCause.js +61 -0
  92. package/dist/hitl/approvalHolds.d.ts +12 -2
  93. package/dist/hitl/approvalHolds.js +53 -2
  94. package/dist/hitl/approvalsFeed.js +23 -11
  95. package/dist/hitl/askGateWire.d.ts +2 -1
  96. package/dist/hitl/askGateWire.js +26 -1
  97. package/dist/hitl/automaticAnswerVerdict.js +2 -2
  98. package/dist/hitl/crashConverged.js +2 -0
  99. package/dist/hitl/decisionConflict.d.ts +3 -0
  100. package/dist/hitl/decisionConflict.js +43 -0
  101. package/dist/hitl/decisionRefusal.d.ts +4 -0
  102. package/dist/hitl/decisionRefusal.js +26 -1
  103. package/dist/hitl/frameRouter.js +26 -43
  104. package/dist/hitl/gateLedger.d.ts +5 -0
  105. package/dist/hitl/gateLedger.js +21 -0
  106. package/dist/hitl/hitlBridge.d.ts +12 -1
  107. package/dist/hitl/hitlBridge.js +68 -53
  108. package/dist/hitl/hitlHostSurface.js +3 -11
  109. package/dist/hitl/livePendingAsk.d.ts +16 -6
  110. package/dist/hitl/livePendingAsk.js +36 -4
  111. package/dist/hitl/parkExpiryJudgements.d.ts +19 -0
  112. package/dist/hitl/parkExpiryJudgements.js +46 -0
  113. package/dist/hitl/parkResolver.d.ts +3 -7
  114. package/dist/hitl/parkResolver.js +50 -26
  115. package/dist/hitl/persistedRulesWire.js +5 -1
  116. package/dist/hitl/planReviewWire.d.ts +2 -2
  117. package/dist/hitl/planReviewWire.js +56 -30
  118. package/dist/hitl/replayedApproval.d.ts +8 -0
  119. package/dist/hitl/replayedApproval.js +125 -10
  120. package/dist/hitl/resumeRunningCard.d.ts +2 -0
  121. package/dist/hitl/resumeRunningCard.js +15 -4
  122. package/dist/hitl/toolApprovalWire.d.ts +24 -4
  123. package/dist/hitl/toolApprovalWire.js +115 -38
  124. package/dist/hooksWireCaps.js +38 -6
  125. package/dist/host.d.ts +5 -0
  126. package/dist/host.js +3 -1
  127. package/dist/hostCallbackGuard.d.ts +1 -0
  128. package/dist/hostCallbackGuard.js +25 -0
  129. package/dist/index.d.ts +30 -11
  130. package/dist/index.js +20 -8
  131. package/dist/interactiveInterrupt.d.ts +8 -0
  132. package/dist/interactiveInterrupt.js +54 -0
  133. package/dist/limitsWire.d.ts +5 -0
  134. package/dist/limitsWire.js +49 -3
  135. package/dist/liveModelCatalog.d.ts +95 -0
  136. package/dist/liveModelCatalog.js +297 -32
  137. package/dist/mcpEngineLeg.d.ts +25 -0
  138. package/dist/mcpEngineLeg.js +71 -15
  139. package/dist/mcpPanel.js +2 -1
  140. package/dist/mcpProbeCapability.d.ts +0 -1
  141. package/dist/mcpProbeCapability.js +2 -6
  142. package/dist/mcpReconnect.d.ts +1 -3
  143. package/dist/mcpReconnect.js +10 -5
  144. package/dist/memoryComplianceCapability.d.ts +0 -1
  145. package/dist/memoryComplianceCapability.js +2 -6
  146. package/dist/memoryOriginCapability.d.ts +0 -1
  147. package/dist/memoryOriginCapability.js +2 -6
  148. package/dist/memoryVerbsWire.d.ts +1 -1
  149. package/dist/memoryVerbsWire.js +4 -0
  150. package/dist/model/liveCatalogShape.d.ts +14 -0
  151. package/dist/model/liveCatalogShape.js +71 -0
  152. package/dist/model/modelSupplyRules.d.ts +1 -0
  153. package/dist/model/modelSupplyRules.js +30 -9
  154. package/dist/model/modelsPolicy.d.ts +21 -0
  155. package/dist/model/modelsPolicy.js +90 -0
  156. package/dist/modelIdentityResolvability.d.ts +1 -1
  157. package/dist/modelIdentityResolvability.js +5 -12
  158. package/dist/modelSwitchFallback.d.ts +69 -0
  159. package/dist/modelSwitchFallback.js +206 -0
  160. package/dist/notifications.js +12 -11
  161. package/dist/outputStyleSeatCapability.d.ts +0 -1
  162. package/dist/outputStyleSeatCapability.js +2 -7
  163. package/dist/ownKey.d.ts +6 -0
  164. package/dist/ownKey.js +48 -0
  165. package/dist/panelReduce.d.ts +114 -0
  166. package/dist/panelReduce.js +1135 -0
  167. package/dist/panelStateHolder.d.ts +17 -0
  168. package/dist/panelStateHolder.js +201 -0
  169. package/dist/peerLaneCapability.d.ts +0 -1
  170. package/dist/peerLaneCapability.js +2 -6
  171. package/dist/permissionModesCapability.d.ts +0 -1
  172. package/dist/permissionModesCapability.js +5 -10
  173. package/dist/permissionRulesWriteCapability.d.ts +0 -1
  174. package/dist/permissionRulesWriteCapability.js +2 -6
  175. package/dist/permissionsAllowGrantCapability.d.ts +0 -1
  176. package/dist/permissionsAllowGrantCapability.js +3 -9
  177. package/dist/permissionsDeclaration.d.ts +6 -0
  178. package/dist/permissionsDeclaration.js +45 -0
  179. package/dist/printToolResultFrame.js +2 -10
  180. package/dist/promptDelivery.js +0 -8
  181. package/dist/resumeRefusalCopy.d.ts +18 -1
  182. package/dist/resumeRefusalCopy.js +126 -15
  183. package/dist/rewindArchiveCapability.d.ts +2 -1
  184. package/dist/rewindArchiveCapability.js +15 -7
  185. package/dist/rewindEngineAction.d.ts +21 -0
  186. package/dist/rewindEngineAction.js +36 -0
  187. package/dist/rewindWireCaps.d.ts +2 -1
  188. package/dist/rewindWireCaps.js +1 -0
  189. package/dist/runTerminal.d.ts +0 -11
  190. package/dist/runTerminal.js +4 -27
  191. package/dist/sdkRegistryTransit.d.ts +2 -1
  192. package/dist/sdkRegistryTransit.js +1 -0
  193. package/dist/sealedKeyCapability.d.ts +0 -1
  194. package/dist/sealedKeyCapability.js +3 -9
  195. package/dist/seatContract.d.ts +54 -25
  196. package/dist/seatContract.js +394 -171
  197. package/dist/sessionIdSegment.d.ts +1 -0
  198. package/dist/sessionIdSegment.js +11 -0
  199. package/dist/sessionMemoryErase.d.ts +1 -1
  200. package/dist/sessionMemoryErase.js +6 -1
  201. package/dist/sessionMemoryStatus.js +3 -2
  202. package/dist/sessionPolicyCapability.d.ts +0 -1
  203. package/dist/sessionPolicyCapability.js +2 -6
  204. package/dist/sessionReadRedactionCapability.d.ts +0 -1
  205. package/dist/sessionReadRedactionCapability.js +2 -7
  206. package/dist/sessionSyncFileHistoryCapability.d.ts +0 -1
  207. package/dist/sessionSyncFileHistoryCapability.js +2 -6
  208. package/dist/skillToolListsCapability.d.ts +0 -1
  209. package/dist/skillToolListsCapability.js +2 -6
  210. package/dist/skillsWireCaps.d.ts +4 -0
  211. package/dist/skillsWireCaps.js +66 -1
  212. package/dist/sqlEngineCapability.d.ts +0 -1
  213. package/dist/sqlEngineCapability.js +9 -14
  214. package/dist/steerPriorityCapability.d.ts +0 -1
  215. package/dist/steerPriorityCapability.js +3 -9
  216. package/dist/storePostureCapability.d.ts +1 -1
  217. package/dist/storePostureCapability.js +16 -16
  218. package/dist/streamDrainingLegs.d.ts +6 -0
  219. package/dist/streamDrainingLegs.js +23 -0
  220. package/dist/subagent/engineCompactWire.js +13 -38
  221. package/dist/subagent/engineDelegatedPrompt.js +10 -6
  222. package/dist/subagent/engineSubagentOutput.d.ts +0 -1
  223. package/dist/subagent/engineSubagentOutput.js +5 -20
  224. package/dist/subagent/engineSubagentTail.d.ts +0 -1
  225. package/dist/subagent/engineSubagentTail.js +7 -32
  226. package/dist/subagentContentStore.js +3 -3
  227. package/dist/supportFloor.js +2 -5
  228. package/dist/terminalStatuses.d.ts +30 -0
  229. package/dist/terminalStatuses.js +67 -0
  230. package/dist/toolResult.d.ts +5 -0
  231. package/dist/toolResult.js +88 -36
  232. package/dist/types/engineState.d.ts +2 -1
  233. package/dist/usageAnalytics.d.ts +146 -0
  234. package/dist/usageAnalytics.js +311 -0
  235. package/dist/userInvokedSkillsCapability.d.ts +0 -1
  236. package/dist/userInvokedSkillsCapability.js +2 -6
  237. package/dist/webSearchBackendCapability.d.ts +0 -1
  238. package/dist/webSearchBackendCapability.js +3 -7
  239. package/dist/webSearchWireCaps.js +3 -11
  240. package/dist/wireFailureShape.d.ts +4 -0
  241. package/dist/wireFailureShape.js +42 -2
  242. package/dist/wireRefusalCopy.js +2 -18
  243. package/dist/workflowClient.js +7 -11
  244. package/dist/workflowsJournalCapability.d.ts +0 -1
  245. package/dist/workflowsJournalCapability.js +2 -6
  246. package/dist/writeProtectionCapability.d.ts +0 -1
  247. package/dist/writeProtectionCapability.js +5 -10
  248. package/docs/INTEGRATION-CLIENTS.md +1033 -26
  249. package/package.json +5 -5
  250. package/dist/engineCapsGenerationGuard.d.ts +0 -4
  251. package/dist/engineCapsGenerationGuard.js +0 -19
  252. package/dist/panelRunningHistory.d.ts +0 -22
  253. package/dist/panelRunningHistory.js +0 -82
  254. package/dist/registryConflict.d.ts +0 -3
  255. package/dist/registryConflict.js +0 -44
@@ -1,212 +1,22 @@
1
- import { isStaleEngineAgentPanelEnd, publishEngineAgentPanelAbsence, publishEngineAgentPanelEvent } from './engineAgentPanelStore.js';
2
- import { __resetPanelRunningHistoryForTests, notePanelRowRunning, panelRowUnknownToConsumer, panelRowEndedIdentity, holdNewbornTerminalRow, peekHeldNewbornTerminalRow, dropHeldNewbornTerminalRow } from './panelRunningHistory.js';
3
- import { rowIdTail } from './workflow.js';
4
- import { TERMINAL_FLEET_TASK_STATUSES } from './fleet/fleetProjection.js';
1
+ import { ABSENT_SETTLE_MS, panelPresenceSize } from './panelReduce.js';
2
+ import { panelStateNow, stepEnginePanel } from './panelStateHolder.js';
5
3
  import { DEFAULT_SESSION_KEY } from './sessionSlot.js';
6
- import { isTerminalNotSuccess } from './runTerminal.js';
7
- export const ABSENT_SETTLE_MS = 30_000;
8
- const SETTLED_RETENTION_MS = ABSENT_SETTLE_MS * 4;
9
- function currentToolKeyOf(ct) {
10
- if (!ct || typeof ct.toolName !== 'string' || ct.toolName.length === 0)
11
- return undefined;
12
- return `${ct.toolName}\u0000${ct.target ?? ''}`;
13
- }
14
- const seen = new Map();
15
- function seenFor(sessionKey) {
16
- let m = seen.get(sessionKey);
17
- if (!m) {
18
- m = new Map();
19
- seen.set(sessionKey, m);
20
- }
21
- return m;
22
- }
4
+ export { ABSENT_SETTLE_MS };
23
5
  export function resetFleetAgentPanelProjection() {
24
- seen.clear();
25
- __resetPanelRunningHistoryForTests();
6
+ stepEnginePanel({ kind: 'fleet-rows-reset' });
26
7
  }
27
8
  export function resetFleetAgentPanelProjectionFor(sessionKey) {
28
- seen.delete(sessionKey);
9
+ stepEnginePanel({ kind: 'fleet-rows-reset', sessionKey });
29
10
  }
30
11
  export function fleetAgentProjectionSize() {
31
12
  return fleetAgentProjectionSizeFor(DEFAULT_SESSION_KEY);
32
13
  }
33
14
  export function fleetAgentProjectionSizeFor(sessionKey) {
34
- return seen.get(sessionKey)?.size ?? 0;
15
+ return panelPresenceSize(panelStateNow(), sessionKey);
35
16
  }
36
17
  export function projectFleetAgentRows(rows, nowMs = Date.now()) {
37
18
  projectFleetAgentRowsFor(DEFAULT_SESSION_KEY, rows, nowMs);
38
19
  }
39
20
  export function projectFleetAgentRowsFor(sessionKey, rows, nowMs = Date.now()) {
40
- const seenMap = seenFor(sessionKey);
41
- const present = new Set();
42
- for (const row of rows) {
43
- if (!row?.id)
44
- continue;
45
- const taskId = rowIdTail(row.id);
46
- if (!taskId)
47
- continue;
48
- present.add(taskId);
49
- const status = row.status ?? 'running';
50
- const wireTokens = typeof row.tokens === 'number' && Number.isInteger(row.tokens) && row.tokens >= 0 ? row.tokens : undefined;
51
- const tokens = wireTokens;
52
- const toolUses = typeof row.toolUses === 'number' ? row.toolUses : undefined;
53
- const transcriptId = row.transcriptId || undefined;
54
- const parentToolCallId = typeof row.parentToolCallId === 'string' && row.parentToolCallId.length > 0 ? row.parentToolCallId : undefined;
55
- const startedAt = typeof row.startedAt === 'number' && Number.isFinite(row.startedAt) && row.startedAt > 0 ? row.startedAt : undefined;
56
- const cycleSeq = typeof row.cycleSeq === 'number' && Number.isInteger(row.cycleSeq) && row.cycleSeq >= 1 ? row.cycleSeq : undefined;
57
- const currentTool = currentToolKeyOf(row.currentTool) !== undefined ? row.currentTool : undefined;
58
- const currentToolKey = currentToolKeyOf(row.currentTool);
59
- const prev = seenMap.get(taskId);
60
- const rowIdentity = { ...(cycleSeq !== undefined ? { cycleSeq } : {}), ...(startedAt !== undefined ? { startedAt } : {}) };
61
- const prevIdentity = prev !== undefined
62
- ? { ...(prev.cycleSeq !== undefined ? { cycleSeq: prev.cycleSeq } : {}), ...(prev.startedAt !== undefined ? { startedAt: prev.startedAt } : {}) }
63
- : undefined;
64
- if (prev !== undefined && isStaleEngineAgentPanelEnd(rowIdentity, prevIdentity))
65
- continue;
66
- const rowNewerCycle = prev !== undefined && isStaleEngineAgentPanelEnd(prevIdentity, rowIdentity);
67
- const sameCycle = prev !== undefined &&
68
- !prev.settled &&
69
- (startedAt === undefined || prev.startedAt === undefined || prev.startedAt === startedAt) &&
70
- (cycleSeq === undefined || prev.cycleSeq === undefined || prev.cycleSeq === cycleSeq);
71
- const knownTokens = tokens !== undefined ? tokens : sameCycle ? prev?.tokens : undefined;
72
- const carryIdentity = prev !== undefined && !rowNewerCycle;
73
- const knownStartedAt = startedAt !== undefined ? startedAt : carryIdentity ? prev.startedAt : undefined;
74
- const knownCycleSeq = cycleSeq !== undefined ? cycleSeq : carryIdentity ? prev.cycleSeq : undefined;
75
- if (TERMINAL_FLEET_TASK_STATUSES.has(status)) {
76
- const newbornTerminal = prev === undefined && panelRowUnknownToConsumer(taskId);
77
- const settledThisCycle = prev?.settled === true && !rowNewerCycle;
78
- const endedRec = prev === undefined ? panelRowEndedIdentity(taskId) : undefined;
79
- const frameCycle = { ...(cycleSeq !== undefined ? { cycleSeq } : {}), ...(startedAt !== undefined ? { startedAt } : {}) };
80
- if (endedRec !== undefined && isStaleEngineAgentPanelEnd(frameCycle, endedRec))
81
- continue;
82
- const endedSameCycle = endedRec !== undefined && !isStaleEngineAgentPanelEnd(endedRec, frameCycle);
83
- if (newbornTerminal) {
84
- holdNewbornTerminalRow(taskId, {
85
- kind: 'fleet-row',
86
- taskId,
87
- ...(row.name ? { name: row.name } : {}),
88
- ...(row.description ? { description: row.description } : {}),
89
- ...(knownTokens !== undefined ? { totalTokens: knownTokens } : {}),
90
- ...(toolUses !== undefined ? { toolUses } : {}),
91
- ...(transcriptId !== undefined ? { transcriptId } : {}),
92
- ...(parentToolCallId !== undefined ? { parentToolCallId } : {}),
93
- ...(knownStartedAt !== undefined ? { startedAt: knownStartedAt } : {}),
94
- ...(knownCycleSeq !== undefined ? { cycleSeq: knownCycleSeq } : {}),
95
- });
96
- }
97
- else if (!settledThisCycle) {
98
- if (rowNewerCycle ||
99
- (cycleSeq !== undefined && prev?.cycleSeq !== cycleSeq) ||
100
- (tokens !== undefined && prev?.tokens !== tokens) ||
101
- (toolUses !== undefined && prev?.toolUses !== toolUses) ||
102
- (transcriptId !== undefined && prev?.transcriptId !== transcriptId) ||
103
- (parentToolCallId !== undefined && prev?.parentToolCallId !== parentToolCallId) ||
104
- (startedAt !== undefined && prev?.startedAt !== startedAt)) {
105
- publishEngineAgentPanelEvent({
106
- kind: 'fleet-row',
107
- taskId,
108
- ...(row.name ? { name: row.name } : {}),
109
- ...(row.description ? { description: row.description } : {}),
110
- ...(knownTokens !== undefined ? { totalTokens: knownTokens } : {}),
111
- ...(toolUses !== undefined ? { toolUses } : {}),
112
- ...(transcriptId !== undefined ? { transcriptId } : {}),
113
- ...(parentToolCallId !== undefined ? { parentToolCallId } : {}),
114
- ...(knownStartedAt !== undefined ? { startedAt: knownStartedAt } : {}),
115
- ...(knownCycleSeq !== undefined ? { cycleSeq: knownCycleSeq } : {}),
116
- });
117
- }
118
- if (!endedSameCycle)
119
- publishEngineAgentPanelEvent({
120
- kind: 'end',
121
- taskId,
122
- isError: isTerminalNotSuccess(status),
123
- ...(knownCycleSeq !== undefined ? { cycleSeq: knownCycleSeq } : {}),
124
- ...(knownStartedAt !== undefined ? { startedAt: knownStartedAt } : {}),
125
- });
126
- }
127
- seenMap.set(taskId, {
128
- status,
129
- tokens: knownTokens,
130
- toolUses: toolUses !== undefined ? toolUses : prev?.toolUses,
131
- transcriptId: transcriptId !== undefined ? transcriptId : prev?.transcriptId,
132
- parentToolCallId: parentToolCallId !== undefined ? parentToolCallId : prev?.parentToolCallId,
133
- startedAt: knownStartedAt,
134
- cycleSeq: knownCycleSeq,
135
- currentToolKey: currentToolKey !== undefined ? currentToolKey : prev?.currentToolKey,
136
- lastSeenAt: nowMs,
137
- settled: true,
138
- settledAtMs: prev?.settled ? prev.settledAtMs : nowMs,
139
- absentReportedAtMs: undefined,
140
- });
141
- continue;
142
- }
143
- const cycleComparable = (cycleSeq !== undefined && prev?.cycleSeq !== undefined) || (startedAt !== undefined && prev?.startedAt !== undefined);
144
- const staleSnapshotOverHeldRow = prev !== undefined && prev.settled === true && cycleComparable && !rowNewerCycle && peekHeldNewbornTerminalRow(taskId) !== undefined;
145
- if (staleSnapshotOverHeldRow)
146
- continue;
147
- if (!prev ||
148
- prev.settled ||
149
- prev.absentReportedAtMs !== undefined ||
150
- prev.status !== status ||
151
- prev.tokens !== knownTokens ||
152
- (toolUses !== undefined && prev.toolUses !== toolUses) ||
153
- (transcriptId !== undefined && prev.transcriptId !== transcriptId) ||
154
- (parentToolCallId !== undefined && prev.parentToolCallId !== parentToolCallId) ||
155
- (startedAt !== undefined && prev.startedAt !== startedAt) ||
156
- (cycleSeq !== undefined && prev.cycleSeq !== cycleSeq) ||
157
- (currentToolKey !== undefined && prev.currentToolKey !== currentToolKey)) {
158
- notePanelRowRunning(taskId);
159
- dropHeldNewbornTerminalRow(taskId);
160
- publishEngineAgentPanelEvent({
161
- kind: 'fleet-row',
162
- taskId,
163
- ...(row.name ? { name: row.name } : {}),
164
- ...(row.description ? { description: row.description } : {}),
165
- ...(knownTokens !== undefined ? { totalTokens: knownTokens } : {}),
166
- ...(toolUses !== undefined ? { toolUses } : {}),
167
- ...(transcriptId !== undefined ? { transcriptId } : {}),
168
- ...(parentToolCallId !== undefined ? { parentToolCallId } : {}),
169
- ...(knownStartedAt !== undefined ? { startedAt: knownStartedAt } : {}),
170
- ...(knownCycleSeq !== undefined ? { cycleSeq: knownCycleSeq } : {}),
171
- ...(currentTool !== undefined ? { currentTool } : {}),
172
- });
173
- }
174
- seenMap.set(taskId, {
175
- status,
176
- tokens: knownTokens,
177
- toolUses: toolUses !== undefined ? toolUses : prev?.toolUses,
178
- transcriptId: transcriptId !== undefined ? transcriptId : prev?.transcriptId,
179
- parentToolCallId: parentToolCallId !== undefined ? parentToolCallId : prev?.parentToolCallId,
180
- startedAt: knownStartedAt,
181
- cycleSeq: knownCycleSeq,
182
- currentToolKey: currentToolKey !== undefined ? currentToolKey : prev?.currentToolKey,
183
- lastSeenAt: nowMs,
184
- settled: false,
185
- settledAtMs: undefined,
186
- absentReportedAtMs: undefined,
187
- });
188
- }
189
- for (const [taskId, s] of seenMap) {
190
- if (present.has(taskId) || s.settled || s.absentReportedAtMs !== undefined)
191
- continue;
192
- const absentForMs = nowMs - s.lastSeenAt;
193
- if (absentForMs <= ABSENT_SETTLE_MS)
194
- continue;
195
- publishEngineAgentPanelAbsence({ taskId, lastSeenAtMs: s.lastSeenAt, absentForMs });
196
- seenMap.set(taskId, { ...s, absentReportedAtMs: nowMs });
197
- }
198
- for (const [taskId, s] of seenMap) {
199
- if (present.has(taskId))
200
- continue;
201
- if (s.settled) {
202
- const settledAt = s.settledAtMs ?? s.lastSeenAt;
203
- if (nowMs - settledAt > SETTLED_RETENTION_MS)
204
- seenMap.delete(taskId);
205
- }
206
- else if (s.absentReportedAtMs !== undefined && nowMs - s.absentReportedAtMs > SETTLED_RETENTION_MS) {
207
- seenMap.delete(taskId);
208
- }
209
- }
210
- if (seenMap.size === 0)
211
- seen.delete(sessionKey);
21
+ stepEnginePanel({ kind: 'fleet-rows', sessionKey, rows, nowMs });
212
22
  }
@@ -0,0 +1,16 @@
1
+ export interface FleetAgentRowLike {
2
+ id: string;
3
+ name?: string;
4
+ description?: string;
5
+ status?: string;
6
+ tokens?: number;
7
+ toolUses?: number;
8
+ transcriptId?: string;
9
+ currentTool?: {
10
+ toolName: string;
11
+ target?: string;
12
+ };
13
+ startedAt?: number;
14
+ cycleSeq?: number;
15
+ parentToolCallId?: string;
16
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,6 +1,7 @@
1
1
  import { ccToolDenialKindForSettledBy, isCcToolDenialKind, isGateDeniedByWord } from './gateVocabulary.js';
2
2
  import { GATE_PARKED_ERROR_CODE } from './engineErrorCodes.js';
3
3
  import { CORE_MODE_RELEASE_REASONS, CORE_SETTLEMENT_KINDS } from './generated/engineFactTables.js';
4
+ import { readAskOrigin } from './ownKey.js';
4
5
  function str(v) {
5
6
  return typeof v === 'string' && v.length > 0 ? v : undefined;
6
7
  }
@@ -32,7 +33,8 @@ function readSettlement(v) {
32
33
  const who = readWho(s.who);
33
34
  if (kind === undefined || who === undefined)
34
35
  return undefined;
35
- const when = typeof s.when === 'number' && Number.isFinite(s.when) ? s.when : undefined;
36
+ const whenRaw = s.when;
37
+ const when = typeof whenRaw === 'number' && Number.isFinite(whenRaw) ? whenRaw : undefined;
36
38
  const note = str(s.note);
37
39
  return {
38
40
  kind,
@@ -62,7 +64,7 @@ function readModeRelease(raw) {
62
64
  const classes = nonEmptyStringList(r.classes);
63
65
  if (mode === undefined || askClass === undefined || classes === undefined)
64
66
  return undefined;
65
- const origin = str(r.origin);
67
+ const origin = readAskOrigin(r);
66
68
  const mandate = str(r.mandate);
67
69
  const reason = str(r.reason);
68
70
  return {
@@ -94,7 +96,7 @@ function readClassifierRound(raw) {
94
96
  }
95
97
  return view;
96
98
  }
97
- export function gateOutcomeOf(frame) {
99
+ function readGateRecord(frame) {
98
100
  if (typeof frame !== 'object' || frame === null || Array.isArray(frame))
99
101
  return undefined;
100
102
  const g = frame.gate;
@@ -107,7 +109,9 @@ export function gateOutcomeOf(frame) {
107
109
  const dr = d;
108
110
  const dk = str(dr.kind);
109
111
  let disposition;
110
- const classifier = readClassifierRound(dr.classifier);
112
+ let causePresent = false;
113
+ const classifierRaw = dr.classifier;
114
+ const classifier = readClassifierRound(classifierRaw);
111
115
  if (dk === 'allowed') {
112
116
  const released = readModeRelease(dr.released);
113
117
  disposition = {
@@ -120,7 +124,9 @@ export function gateOutcomeOf(frame) {
120
124
  const deniedBy = str(dr.deniedBy);
121
125
  if (deniedBy === undefined)
122
126
  return undefined;
123
- const cause = str(dr.cause);
127
+ const causeRaw = dr.cause;
128
+ causePresent = causeRaw !== undefined;
129
+ const cause = str(causeRaw);
124
130
  const mode = str(dr.mode);
125
131
  const askClass = str(dr.askClass);
126
132
  const classes = nonEmptyStringList(dr.classes);
@@ -137,14 +143,23 @@ export function gateOutcomeOf(frame) {
137
143
  else {
138
144
  return undefined;
139
145
  }
140
- const settlement = readSettlement(raw.settlement);
141
- const origin = str(raw.origin);
146
+ const settlementRaw = raw.settlement;
147
+ const settlement = readSettlement(settlementRaw);
148
+ const origin = readAskOrigin(raw);
142
149
  return {
143
- disposition,
144
- ...(settlement !== undefined ? { settlement } : {}),
145
- ...(origin !== undefined ? { origin } : {}),
150
+ view: {
151
+ disposition,
152
+ ...(settlement !== undefined ? { settlement } : {}),
153
+ ...(origin !== undefined ? { origin } : {}),
154
+ },
155
+ settlementPresent: settlementRaw !== undefined,
156
+ causePresent,
157
+ classifierPresent: classifierRaw !== undefined,
146
158
  };
147
159
  }
160
+ export function gateOutcomeOf(frame) {
161
+ return readGateRecord(frame)?.view;
162
+ }
148
163
  export function isGateParkedToolEnd(frame) {
149
164
  if (typeof frame !== 'object' || frame === null || Array.isArray(frame))
150
165
  return false;
@@ -189,21 +204,19 @@ export function ccToolDenialKindForToolEnd(frame) {
189
204
  return stamped;
190
205
  if (frame._sema_denial_unclassified === true)
191
206
  return undefined;
192
- const outcome = gateOutcomeOf(frame);
207
+ const record = readGateRecord(frame);
208
+ const outcome = record?.view;
193
209
  const kind = outcome?.settlement?.kind;
194
210
  if (kind === 'human_refused')
195
211
  return ccToolDenialKindForSettledBy('human');
196
212
  if (kind === 'policy_refused')
197
213
  return ccToolDenialKindForSettledBy('policy');
198
- const rawGate = frame.gate;
199
- const settlementAbsent = typeof rawGate === 'object' && rawGate !== null && rawGate.settlement === undefined;
200
- if (outcome !== undefined && settlementAbsent && outcome.disposition.kind === 'denied') {
214
+ if (record !== undefined && outcome !== undefined && !record.settlementPresent && outcome.disposition.kind === 'denied') {
201
215
  const by = outcome.disposition.deniedBy;
202
216
  if (by === 'ask_resolution' || !isGateDeniedByWord(by))
203
217
  return undefined;
204
218
  if (DENIED_BY_MAY_CARRY_CAUSE.has(by)) {
205
- const rawDisposition = rawKeyOf(rawGate, 'disposition');
206
- if (rawKeyOf(rawDisposition, 'cause') !== undefined) {
219
+ if (record.causePresent) {
207
220
  const cause = outcome.disposition.cause;
208
221
  if (cause === 'unavailable')
209
222
  return 'automode-unavailable';
@@ -213,14 +226,13 @@ export function ccToolDenialKindForToolEnd(frame) {
213
226
  }
214
227
  if (by === 'classifier')
215
228
  return 'automode-blocked';
216
- if (rawKeyOf(rawDisposition, 'classifier') !== undefined)
229
+ if (record.classifierPresent)
217
230
  return undefined;
218
231
  }
219
232
  return ccToolDenialKindForSettledBy('policy');
220
233
  }
221
234
  return undefined;
222
235
  }
223
- const rawKeyOf = (holder, key) => typeof holder === 'object' && holder !== null ? holder[key] : undefined;
224
236
  const DENIED_BY_MAY_CARRY_CAUSE = new Set(['classifier', 'policy']);
225
237
  const _gateOutcomeShapePin = (w) => w;
226
238
  void _gateOutcomeShapePin;
@@ -1,3 +1,5 @@
1
1
  export declare const SDK_ABORT_CAUSES: readonly ["client_disconnected", "ask_park_grace_exhausted", "cancel_verb"];
2
2
  export declare const SDK_STATUS_DERIVED_COARSE_CODES: readonly ["request.rejected", "auth.unauthorized", "auth.forbidden", "not_found.resource", "request.payload_too_large", "request.unprocessable", "limit.rate_exceeded", "capability.unavailable", "internal.error"];
3
3
  export declare const SDK_DENY_REASON_ROUTING_FAILURE: "routing_failure_fail_closed";
4
+ export declare const SDK_ASK_ROW_ENDING_CAUSES: readonly ["routing_failure_fail_closed", "run_cancelled", "run_exited", "batch_aborted", "batch_sibling_withdrawn", "batch_bound_elsewhere", "adhoc_leg_no_durable_domain", "orphan_ttl_exceeded", "crashed_before_park", "shutdown_before_park"];
5
+ export declare const SDK_RUN_DEATH_VERDICT_CODES: readonly ["run.stalled", "run.orphaned", "approval.expired"];
@@ -15,3 +15,20 @@ export const SDK_STATUS_DERIVED_COARSE_CODES = Object.freeze([
15
15
  "internal.error",
16
16
  ]);
17
17
  export const SDK_DENY_REASON_ROUTING_FAILURE = "routing_failure_fail_closed";
18
+ export const SDK_ASK_ROW_ENDING_CAUSES = Object.freeze([
19
+ "routing_failure_fail_closed",
20
+ "run_cancelled",
21
+ "run_exited",
22
+ "batch_aborted",
23
+ "batch_sibling_withdrawn",
24
+ "batch_bound_elsewhere",
25
+ "adhoc_leg_no_durable_domain",
26
+ "orphan_ttl_exceeded",
27
+ "crashed_before_park",
28
+ "shutdown_before_park",
29
+ ]);
30
+ export const SDK_RUN_DEATH_VERDICT_CODES = Object.freeze([
31
+ "run.stalled",
32
+ "run.orphaned",
33
+ "approval.expired",
34
+ ]);
@@ -0,0 +1,10 @@
1
+ export declare const SETTINGS_AT_MENTION_MODES: readonly ["all", "none", "only"];
2
+ export type SettingsAtMention = {
3
+ readonly mode: Exclude<(typeof SETTINGS_AT_MENTION_MODES)[number], 'only'>;
4
+ } | {
5
+ readonly mode: 'only';
6
+ readonly models: readonly string[];
7
+ };
8
+ export declare const SETTINGS_IS_LOOPBACK_AUTH_HOST: (host: string) => boolean;
9
+ export declare const SETTINGS_IS_HIERARCHICAL_URL: (raw: string) => boolean;
10
+ export declare const SETTINGS_NARROW_AT_MENTION: (atMention: SettingsAtMention, available: ReadonlySet<string>) => SettingsAtMention;
@@ -0,0 +1,22 @@
1
+ export const SETTINGS_AT_MENTION_MODES = Object.freeze([
2
+ "all",
3
+ "none",
4
+ "only",
5
+ ]);
6
+ export const SETTINGS_IS_LOOPBACK_AUTH_HOST = function isLoopbackAuthHost(host) {
7
+ return host === "127.0.0.1" || host === "localhost" || host === "::1" || host === "[::1]" || host === "::ffff:127.0.0.1";
8
+ };
9
+ export const SETTINGS_IS_HIERARCHICAL_URL = function isHierarchicalUrl(raw) {
10
+ if (!URL.canParse(raw))
11
+ return false;
12
+ const u = new URL(raw);
13
+ return u.href.startsWith(`${u.protocol}//`);
14
+ };
15
+ export const SETTINGS_NARROW_AT_MENTION = function narrowAtMention(atMention, available) {
16
+ if (atMention.mode !== "only")
17
+ return atMention;
18
+ const kept = atMention.models.filter((n) => available.has(n));
19
+ if (kept.length === atMention.models.length)
20
+ return atMention;
21
+ return kept.length > 0 ? { mode: "only", models: kept } : { mode: "none" };
22
+ };
@@ -3,7 +3,7 @@ import { type PermissionModesReading } from './permissionModesCapability.js';
3
3
  import { type EnvLike } from './hostEnv.js';
4
4
  export declare const HEADLESS_PERMISSION_MODE_ENV = "SEMA_HEADLESS_PERMISSION_MODE";
5
5
  export declare const HEADLESS_PERMISSION_WIRE_MODES: readonly PermissionMode[];
6
- export declare function normalizeShellPermissionModeForWire(mode: string | undefined, _permissionModes?: PermissionModesReading): string | undefined;
6
+ export declare function normalizeShellPermissionModeForWire(mode: string | undefined): string | undefined;
7
7
  export type HeadlessPermissionResolution = {
8
8
  fields: PermissionModeField;
9
9
  source: 'flag' | 'env' | 'default';
@@ -7,7 +7,7 @@ import { readOwnDataValue } from './ownKey.js';
7
7
  export const HEADLESS_PERMISSION_MODE_ENV = 'SEMA_HEADLESS_PERMISSION_MODE';
8
8
  export const HEADLESS_PERMISSION_WIRE_MODES = [...PERMISSION_MODE_WORDS];
9
9
  const SHELL_INTERNAL_MODE_TO_WIRE = new Map([['manual', 'default']]);
10
- export function normalizeShellPermissionModeForWire(mode, _permissionModes) {
10
+ export function normalizeShellPermissionModeForWire(mode) {
11
11
  if (mode === undefined)
12
12
  return undefined;
13
13
  return SHELL_INTERNAL_MODE_TO_WIRE.get(mode) ?? mode;
@@ -1,23 +1,18 @@
1
1
  import { isSseIdleError, probeEngineAlive } from './sseIdleTriage.js';
2
2
  import { hostEnv } from './hostEnv.js';
3
+ import { isSdkRunFrameSynthesizable } from './terminalStatuses.js';
4
+ import { createStreamDrainingLegs } from './streamDrainingLegs.js';
5
+ import { abortableSleep } from './abortableSleep.js';
3
6
  export const HEADLESS_RECONNECT_MAX = 2;
4
7
  function debugLog(msg) {
5
8
  if (hostEnv().SEMA_DEBUG) {
6
9
  console.error(`[sema] headless reconnect: ${msg}`);
7
10
  }
8
11
  }
9
- const RUN_ROW_TERMINAL_STATUSES = new Set([
10
- 'completed',
11
- 'failed',
12
- 'timeout',
13
- 'blocked',
14
- 'suspended',
15
- 'needs_review',
16
- ]);
17
12
  export function terminalEventFromRunRow(row) {
18
13
  if (!row || typeof row.status !== 'string')
19
14
  return null;
20
- if (RUN_ROW_TERMINAL_STATUSES.has(row.status)) {
15
+ if (isSdkRunFrameSynthesizable(row.status)) {
21
16
  if (row.result && typeof row.result === 'object') {
22
17
  return { type: 'done', result: row.result };
23
18
  }
@@ -49,6 +44,15 @@ export async function* withHeadlessR1Reconnect(live, deps) {
49
44
  let lastSeenSeq;
50
45
  const probe = deps.probeAlive ?? probeEngineAlive;
51
46
  let isRecoverableTear;
47
+ const drains = createStreamDrainingLegs();
48
+ let drainReopenAfterMs;
49
+ const terminalFromRunRow = async (taskId) => {
50
+ const row = await deps.getRun(taskId).catch(() => null);
51
+ const terminal = terminalEventFromRunRow(row);
52
+ if (terminal)
53
+ debugLog(`run row terminal (${String(row?.status)}) — synthesizing the terminal frame (R1b)`);
54
+ return terminal;
55
+ };
52
56
  for (;;) {
53
57
  if (deps.signal?.aborted)
54
58
  throw originalTearErr;
@@ -59,25 +63,37 @@ export async function* withHeadlessR1Reconnect(live, deps) {
59
63
  const taskId = deps.getTaskId();
60
64
  if (!taskId)
61
65
  throw originalTearErr;
62
- if (attempts >= HEADLESS_RECONNECT_MAX) {
63
- debugLog(`budget exhausted (${attempts}/${HEADLESS_RECONNECT_MAX}) — falling to the honest death path`);
64
- throw originalTearErr;
66
+ if (drainReopenAfterMs !== undefined) {
67
+ const waitMs = drainReopenAfterMs;
68
+ drainReopenAfterMs = undefined;
69
+ await abortableSleep(waitMs, deps.signal ?? new AbortController().signal);
70
+ if (deps.signal?.aborted)
71
+ throw originalTearErr;
72
+ debugLog(`engine draining — reopening runs.events(${taskId}${lastSeenSeq !== undefined ? `, Last-Event-ID ${lastSeenSeq}` : ''}) after ${waitMs}ms (not counted against the reconnect budget)`);
73
+ }
74
+ else {
75
+ if (attempts >= HEADLESS_RECONNECT_MAX) {
76
+ debugLog(`budget exhausted (${attempts}/${HEADLESS_RECONNECT_MAX}) — falling to the honest death path`);
77
+ throw originalTearErr;
78
+ }
79
+ attempts++;
80
+ if (!(await probe(deps.baseUrl)))
81
+ throw originalTearErr;
82
+ debugLog(`attempt ${attempts}/${HEADLESS_RECONNECT_MAX} — switching to runs.events(${taskId}${lastSeenSeq !== undefined ? `, Last-Event-ID ${lastSeenSeq}` : ''})`);
65
83
  }
66
- attempts++;
67
- if (!(await probe(deps.baseUrl)))
68
- throw originalTearErr;
69
- debugLog(`attempt ${attempts}/${HEADLESS_RECONNECT_MAX} — switching to runs.events(${taskId}${lastSeenSeq !== undefined ? `, Last-Event-ID ${lastSeenSeq}` : ''})`);
70
84
  try {
71
85
  const cont = deps.openEvents(taskId, lastSeenSeq !== undefined ? String(lastSeenSeq) : undefined);
72
86
  for await (const ev of cont) {
73
87
  if (deps.signal?.aborted)
74
88
  return;
89
+ if (drains.see(ev))
90
+ continue;
75
91
  const rec = ev;
76
92
  const t = rec?.type;
77
93
  if (t === undefined)
78
94
  continue;
79
95
  if (t === 'error') {
80
- debugLog(`runs.events lane frame type=error code=${String(rec?.errorCode ?? rec?.code ?? '')} — SDK resumes with Last-Event-ID internally`);
96
+ debugLog(`runs.events lane frame type=error code=${String(rec?.errorCode ?? rec?.code ?? '')} — not forwarded (the cap frame is resumed inside the SDK)`);
81
97
  continue;
82
98
  }
83
99
  const seq = typeof rec?.id === 'string' && /^\d+$/.test(rec.id) ? Number(rec.id) : undefined;
@@ -94,16 +110,29 @@ export async function* withHeadlessR1Reconnect(live, deps) {
94
110
  }
95
111
  if (deps.signal?.aborted)
96
112
  return;
113
+ const drained = drains.legEnded('returned');
114
+ if (drained !== undefined) {
115
+ if ('reconnectAfterMs' in drained) {
116
+ drainReopenAfterMs = drained.reconnectAfterMs;
117
+ continue;
118
+ }
119
+ debugLog(`runs.events kept ending on drain end frames (${drained.giveUp}) — checking the run row`);
120
+ const terminal = await terminalFromRunRow(taskId);
121
+ if (terminal) {
122
+ yield terminal;
123
+ return;
124
+ }
125
+ continue;
126
+ }
97
127
  debugLog('runs.events continuation ended without a terminal frame');
98
128
  }
99
129
  catch (contErr) {
130
+ drains.legEnded('threw');
100
131
  debugLog(`runs.events continuation failed: ${contErr instanceof Error ? contErr.message : String(contErr)}`);
101
132
  if (deps.signal?.aborted)
102
133
  throw originalTearErr;
103
- const row = await deps.getRun(taskId).catch(() => null);
104
- const terminal = terminalEventFromRunRow(row);
134
+ const terminal = await terminalFromRunRow(taskId);
105
135
  if (terminal) {
106
- debugLog(`run row terminal (${String(row?.status)}) — synthesizing the terminal frame (R1b)`);
107
136
  yield terminal;
108
137
  return;
109
138
  }
@@ -18,4 +18,4 @@ export interface ApprovalAllowRuleShadow {
18
18
  export declare function readAllowRuleShadowFromWire(carrier: object | null | undefined, leg: 'frame' | 'row'): ApprovalAllowRuleShadow | undefined;
19
19
  export declare function readApprovalAllowRuleShadow(card: object | null | undefined): ApprovalAllowRuleShadow | undefined;
20
20
  export declare function approvalAllowRuleShadowed(card: object | null | undefined): boolean;
21
- export declare function legacyShadowedRuleTextOf(r: ApprovalAllowRuleShadow | undefined): string | undefined;
21
+ export declare function allowRuleShadowSentence(card: object | null | undefined): string | undefined;
@@ -1,4 +1,5 @@
1
1
  import { CORE_ALLOW_RULE_MEMBERS } from '../generated/engineFactTables.js';
2
+ import { quoteUntrustedForSentence } from '../displayUntrusted.js';
2
3
  const KNOWN_MEMBERS = new Set(CORE_ALLOW_RULE_MEMBERS);
3
4
  function freezeMember(m) {
4
5
  return Object.freeze({ ...m });
@@ -154,14 +155,54 @@ export function readApprovalAllowRuleShadow(card) {
154
155
  export function approvalAllowRuleShadowed(card) {
155
156
  return readApprovalAllowRuleShadow(card) !== undefined;
156
157
  }
157
- export function legacyShadowedRuleTextOf(r) {
158
- if (r === undefined)
158
+ const ENTRY_MAX = 4096;
159
+ const MEMBER_MAX = 64;
160
+ const ENTRIES_SHOWN = 5;
161
+ const UNREADABLE_LINE = 'An allow rule matched this call but cannot clear it; which rule could not be read, so this one is confirmed individually.';
162
+ function legacySingleEntry(shadow) {
163
+ if (shadow.unreadable === true || shadow.members.length !== 1)
159
164
  return undefined;
160
- for (const m of r.members) {
161
- if (m.member !== 'persisted' || m.entries === undefined)
162
- continue;
163
- const first = m.entries[0];
164
- return typeof first === 'string' && first.trim() !== '' ? first : undefined;
165
+ const m = shadow.members[0];
166
+ if (m.member !== 'persisted' || m.malformed === true || m.omitted !== undefined)
167
+ return undefined;
168
+ if (m.entries === undefined || m.entries.length !== 1)
169
+ return undefined;
170
+ return quoteUntrustedForSentence(m.entries[0], ENTRY_MAX);
171
+ }
172
+ function memberPhrase(m) {
173
+ const memberWord = m.member === undefined ? undefined : KNOWN_MEMBERS.has(m.member) ? m.member : quoteUntrustedForSentence(m.member, MEMBER_MAX);
174
+ const word = memberWord ?? 'an unrecognised source';
175
+ const entries = m.entries ?? [];
176
+ if (m.malformed === true && entries.length === 0)
177
+ return `${word} (could not be read)`;
178
+ const listed = entries
179
+ .map((e) => quoteUntrustedForSentence(e, ENTRY_MAX))
180
+ .filter((e) => e !== undefined)
181
+ .slice(0, ENTRIES_SHOWN);
182
+ const notListed = entries.length - listed.length + (m.omitted ?? 0);
183
+ const parts = [];
184
+ if (listed.length > 0)
185
+ parts.push(listed.join(', '));
186
+ if (notListed > 0)
187
+ parts.push(listed.length > 0 ? `and ${String(notListed)} more not listed` : `${String(notListed)} not listed`);
188
+ if (m.malformed === true)
189
+ parts.push('part of it could not be read');
190
+ return parts.length > 0 ? `${word}: ${parts.join('; ')}` : word;
191
+ }
192
+ export function allowRuleShadowSentence(card) {
193
+ const shadow = readApprovalAllowRuleShadow(card);
194
+ if (shadow === undefined)
195
+ return undefined;
196
+ try {
197
+ if (shadow.unreadable === true || shadow.members.length === 0)
198
+ return UNREADABLE_LINE;
199
+ const legacy = legacySingleEntry(shadow);
200
+ if (legacy !== undefined) {
201
+ return `Your saved permission rule ${legacy} still exists — it just cannot clear this particular call, so this one is confirmed individually.`;
202
+ }
203
+ return `Allow rules matched this call but cannot clear it, so this one is confirmed individually — ${shadow.members.map(memberPhrase).join(' · ')}.`;
204
+ }
205
+ catch {
206
+ return UNREADABLE_LINE;
165
207
  }
166
- return undefined;
167
208
  }
@@ -0,0 +1 @@
1
+ export declare function approvalCompleteParked(frame: unknown): boolean;