@sema-agent/client-core 0.96.0 → 0.97.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 (143) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.md +28 -22
  3. package/dist/adapter/activeRunSelfHeal.d.ts +0 -2
  4. package/dist/adapter/activeRunSelfHeal.js +7 -7
  5. package/dist/adapter/downstream/terminalToSdkResult.js +6 -2
  6. package/dist/agentSession/backgroundView.js +2 -15
  7. package/dist/agentsWireCaps.d.ts +1 -1
  8. package/dist/agentsWireCaps.js +16 -10
  9. package/dist/approvalDecidedByCapability.d.ts +2 -0
  10. package/dist/approvalDecidedByCapability.js +0 -1
  11. package/dist/approvalsStreamLiveCapability.d.ts +2 -0
  12. package/dist/approvalsStreamLiveCapability.js +3 -2
  13. package/dist/attachmentSeatCapability.d.ts +2 -0
  14. package/dist/attachmentSeatCapability.js +0 -1
  15. package/dist/callerCwdCapability.d.ts +2 -0
  16. package/dist/callerCwdCapability.js +0 -1
  17. package/dist/centerWiringCapability.d.ts +2 -0
  18. package/dist/centerWiringCapability.js +4 -3
  19. package/dist/cloudConfigWireCaps.d.ts +2 -2
  20. package/dist/cloudConfigWireCaps.js +192 -56
  21. package/dist/declared/supportFloorDeclared.js +3 -3
  22. package/dist/deviceExecutorManagementCapability.d.ts +2 -0
  23. package/dist/deviceExecutorManagementCapability.js +3 -2
  24. package/dist/displayUntrusted.d.ts +3 -0
  25. package/dist/displayUntrusted.js +15 -0
  26. package/dist/engineCapReader.d.ts +8 -2
  27. package/dist/engineCapReader.js +15 -7
  28. package/dist/engineCapsCache.d.ts +11 -3
  29. package/dist/engineCapsCache.js +54 -23
  30. package/dist/engineErrorCodes.d.ts +3 -0
  31. package/dist/engineErrorCodes.js +9 -1
  32. package/dist/engineNoticeCodes.js +2 -3
  33. package/dist/executionLaneCapability.d.ts +2 -0
  34. package/dist/executionLaneCapability.js +3 -2
  35. package/dist/fileHistoryCaptureCapability.d.ts +2 -0
  36. package/dist/fileHistoryCaptureCapability.js +34 -8
  37. package/dist/fileHistoryLegNotice.js +4 -0
  38. package/dist/fleet/fleetLedger.js +129 -105
  39. package/dist/gateOutcome.js +2 -4
  40. package/dist/gateVocabulary.d.ts +1 -6
  41. package/dist/gateVocabulary.js +2 -5
  42. package/dist/generated/sdkFactTables.d.ts +2 -0
  43. package/dist/generated/sdkFactTables.js +7 -0
  44. package/dist/generated/toolNameTables.d.ts +0 -1
  45. package/dist/generated/toolNameTables.js +0 -42
  46. package/dist/hitl/approvalDecisionNoteAudit.js +2 -2
  47. package/dist/hitl/approvalEndingCause.js +2 -7
  48. package/dist/hitl/approvalHolds.d.ts +16 -1
  49. package/dist/hitl/approvalHolds.js +52 -12
  50. package/dist/hitl/askGateWire.js +1 -0
  51. package/dist/hitl/automaticAnswerVerdict.js +5 -7
  52. package/dist/hitl/decisionConflict.js +4 -3
  53. package/dist/hitl/decisionRefusal.js +19 -12
  54. package/dist/hitl/frameRouter.d.ts +3 -0
  55. package/dist/hitl/frameRouter.js +52 -25
  56. package/dist/hitl/gateLedger.d.ts +0 -5
  57. package/dist/hitl/gateLedger.js +0 -21
  58. package/dist/hitl/hitlBridge.js +18 -4
  59. package/dist/hitl/parkResolver.js +7 -3
  60. package/dist/hitl/replayedApproval.d.ts +2 -3
  61. package/dist/hitl/replayedApproval.js +7 -16
  62. package/dist/hitl/sessionPolicyDeliverable.d.ts +0 -1
  63. package/dist/hitl/sessionPolicyDeliverable.js +15 -17
  64. package/dist/hitl/sessionPolicyWire.d.ts +2 -0
  65. package/dist/hitl/sessionPolicyWire.js +15 -3
  66. package/dist/hitl/standingRows.d.ts +1 -0
  67. package/dist/hitl/standingRows.js +38 -17
  68. package/dist/hitl/suspendedReopen.js +2 -1
  69. package/dist/hitl/toolApprovalWire.d.ts +8 -8
  70. package/dist/hitl/toolApprovalWire.js +50 -24
  71. package/dist/index.d.ts +6 -4
  72. package/dist/index.js +4 -3
  73. package/dist/liveModelCatalog.d.ts +13 -0
  74. package/dist/liveModelCatalog.js +56 -0
  75. package/dist/mcpProbeCapability.d.ts +2 -0
  76. package/dist/mcpProbeCapability.js +3 -2
  77. package/dist/mcpWireCaps.d.ts +1 -0
  78. package/dist/mcpWireCaps.js +4 -1
  79. package/dist/memoryComplianceCapability.d.ts +2 -0
  80. package/dist/memoryComplianceCapability.js +3 -2
  81. package/dist/memoryOriginCapability.d.ts +2 -0
  82. package/dist/memoryOriginCapability.js +3 -2
  83. package/dist/modelIdentityResolvability.js +2 -2
  84. package/dist/noticePresentation.js +2 -2
  85. package/dist/outputStyleSeatCapability.d.ts +2 -0
  86. package/dist/outputStyleSeatCapability.js +0 -1
  87. package/dist/peerLaneCapability.d.ts +2 -0
  88. package/dist/peerLaneCapability.js +3 -2
  89. package/dist/permissionModesCapability.d.ts +2 -0
  90. package/dist/permissionModesCapability.js +0 -1
  91. package/dist/permissionRulesWriteCapability.d.ts +2 -0
  92. package/dist/permissionRulesWriteCapability.js +3 -2
  93. package/dist/permissionsAllowGrantCapability.d.ts +2 -0
  94. package/dist/permissionsAllowGrantCapability.js +4 -2
  95. package/dist/permissionsDeclaration.js +19 -21
  96. package/dist/request/taskRequest.d.ts +1 -4
  97. package/dist/request/taskRequest.js +74 -19
  98. package/dist/resumeRefusalCopy.js +4 -4
  99. package/dist/rewindArchiveCapability.js +5 -0
  100. package/dist/rewindArchiveReading.d.ts +2 -0
  101. package/dist/rewindArchiveReading.js +0 -1
  102. package/dist/runTerminal.d.ts +33 -4
  103. package/dist/runTerminal.js +170 -15
  104. package/dist/sdkWireTransit.d.ts +2 -1
  105. package/dist/sdkWireTransit.js +1 -0
  106. package/dist/sealedKeyCapability.d.ts +2 -0
  107. package/dist/sealedKeyCapability.js +0 -1
  108. package/dist/seatContract.d.ts +7 -4
  109. package/dist/seatContract.js +34 -20
  110. package/dist/sessionPolicyCapability.d.ts +2 -0
  111. package/dist/sessionPolicyCapability.js +3 -2
  112. package/dist/sessionReadRedactionCapability.d.ts +2 -0
  113. package/dist/sessionReadRedactionCapability.js +0 -1
  114. package/dist/sessionSyncFileHistoryCapability.d.ts +2 -0
  115. package/dist/sessionSyncFileHistoryCapability.js +3 -2
  116. package/dist/skillToolListsCapability.d.ts +2 -0
  117. package/dist/skillToolListsCapability.js +3 -2
  118. package/dist/sqlEngineCapability.d.ts +2 -0
  119. package/dist/sqlEngineCapability.js +4 -3
  120. package/dist/steerPriorityCapability.d.ts +2 -0
  121. package/dist/steerPriorityCapability.js +3 -2
  122. package/dist/storePostureCapability.d.ts +2 -0
  123. package/dist/storePostureCapability.js +4 -3
  124. package/dist/toolHistoryBadTurn.js +3 -2
  125. package/dist/toolResult.d.ts +11 -0
  126. package/dist/toolResult.js +53 -0
  127. package/dist/userInvokedSkillsCapability.d.ts +2 -0
  128. package/dist/userInvokedSkillsCapability.js +3 -2
  129. package/dist/webSearchBackendCapability.d.ts +2 -0
  130. package/dist/webSearchBackendCapability.js +3 -2
  131. package/dist/wireErrorTriage.js +58 -34
  132. package/dist/wireFailureShape.d.ts +1 -1
  133. package/dist/wireFailureShape.js +76 -25
  134. package/dist/workflowsJournalCapability.d.ts +2 -0
  135. package/dist/workflowsJournalCapability.js +3 -2
  136. package/dist/writeProtectionCapability.d.ts +2 -0
  137. package/dist/writeProtectionCapability.js +3 -2
  138. package/docs/INTEGRATION-CLIENTS.md +533 -14
  139. package/package.json +4 -4
  140. package/dist/engineRefusedToolName.d.ts +0 -1
  141. package/dist/engineRefusedToolName.js +0 -8
  142. package/dist/engineWordRenames.d.ts +0 -8
  143. package/dist/engineWordRenames.js +0 -13
@@ -99,40 +99,48 @@ export function createFleetLedger(hooks = {}, opts = {}) {
99
99
  const wfMap = new Map();
100
100
  const rowMeta = new Map();
101
101
  const retained = new Map();
102
- const cycleWatermark = new Map();
103
- const CYCLE_WATERMARK_MAX = 512;
104
- function raiseCycleWatermark(tail, gen) {
102
+ const tails = new Map();
103
+ const TAIL_RECORD_MAX = 512;
104
+ function touchTail(tail) {
105
+ const prior = tails.get(tail);
106
+ tails.delete(tail);
107
+ if (prior === undefined && tails.size >= TAIL_RECORD_MAX) {
108
+ const held = new Set();
109
+ for (const id of taskMap.keys())
110
+ held.add(rowIdTail(id));
111
+ for (const id of retained.keys())
112
+ held.add(rowIdTail(id));
113
+ for (const candidate of tails.keys()) {
114
+ if (held.has(candidate))
115
+ continue;
116
+ tails.delete(candidate);
117
+ break;
118
+ }
119
+ }
120
+ const rec = prior ?? { gen: undefined, settled: undefined };
121
+ tails.set(tail, rec);
122
+ return rec;
123
+ }
124
+ function raiseTailGen(tail, gen) {
105
125
  if (gen === undefined || tail.length === 0)
106
126
  return;
107
- const prior = cycleWatermark.get(tail);
127
+ const prior = tails.get(tail)?.gen;
108
128
  if (prior !== undefined && cycleSeqOrder(prior, gen) !== 'before')
109
129
  return;
110
- cycleWatermark.delete(tail);
111
- if (cycleWatermark.size >= CYCLE_WATERMARK_MAX) {
112
- const oldest = cycleWatermark.keys().next().value;
113
- if (oldest !== undefined)
114
- cycleWatermark.delete(oldest);
115
- }
116
- cycleWatermark.set(tail, gen);
130
+ touchTail(tail).gen = gen;
131
+ }
132
+ function isFormerGeneration(tail, gen) {
133
+ return cycleSeqOrder(gen, tails.get(tail)?.gen) === 'before';
117
134
  }
118
- const settledTerminal = new Map();
119
- const SETTLED_TERMINAL_MAX = 512;
120
135
  function noteSettledTerminal(tail, row) {
121
136
  if (tail.length === 0 || typeof row.status !== 'string')
122
137
  return;
123
- const gen = wireCycleSeq(row);
124
- const recordedGen = settledTerminal.get(tail)?.cycleSeq;
125
- if (cycleSeqOrder(gen, recordedGen) === 'before')
126
- return;
127
- if (cycleSeqOrder(gen, cycleWatermark.get(tail)) === 'before')
128
- return;
129
- settledTerminal.delete(tail);
130
- if (settledTerminal.size >= SETTLED_TERMINAL_MAX) {
131
- const oldest = settledTerminal.keys().next().value;
132
- if (oldest !== undefined)
133
- settledTerminal.delete(oldest);
134
- }
135
- settledTerminal.set(tail, { status: row.status, cycleSeq: gen });
138
+ touchTail(tail).settled = { status: row.status, cycleSeq: wireCycleSeq(row) };
139
+ }
140
+ function withdrawSettledTerminal(tail) {
141
+ const rec = tails.get(tail);
142
+ if (rec !== undefined)
143
+ rec.settled = undefined;
136
144
  }
137
145
  let droppedMalformed = 0;
138
146
  let droppedUnknownFrame = 0;
@@ -167,11 +175,16 @@ export function createFleetLedger(hooks = {}, opts = {}) {
167
175
  ...(retired ? { retiredByNotification: true } : {}),
168
176
  };
169
177
  };
170
- function settleTerminalRow(row, receivedAtMs) {
178
+ function settleTerminalRow(row, tail, receivedAtMs) {
171
179
  taskMap.delete(row.id);
172
180
  rowMeta.delete(row.id);
181
+ for (const id of taskMap.keys()) {
182
+ if (rowIdTail(id) === tail) {
183
+ debug(`[fleet-frame] terminal row not retained: tail still in flight on an alias(row=${row.id} alias=${id})`);
184
+ return;
185
+ }
186
+ }
173
187
  retained.set(row.id, { row, expireAt: receivedAtMs + TERMINAL_RETAIN_MS });
174
- const tail = rowIdTail(row.id);
175
188
  for (const id of [...retained.keys()]) {
176
189
  if (id !== row.id && rowIdTail(id) === tail)
177
190
  retained.delete(id);
@@ -179,18 +192,32 @@ export function createFleetLedger(hooks = {}, opts = {}) {
179
192
  noteSettledTerminal(tail, row);
180
193
  }
181
194
  function admitRow(row, receivedAtMs) {
195
+ const tail = rowIdTail(row.id);
196
+ const gen = wireCycleSeq(row);
197
+ if (isFormerGeneration(tail, gen)) {
198
+ debug(`[fleet-frame] former-generation row ignored(row=${row.id} row-cycle=${gen} tail-cycle=${tails.get(tail)?.gen})`);
199
+ return;
200
+ }
201
+ raiseTailGen(tail, gen);
202
+ if (gen !== undefined) {
203
+ for (const [id, alias] of [...taskMap]) {
204
+ if (id !== row.id && rowIdTail(id) === tail && cycleSeqOrder(wireCycleSeq(alias), gen) === 'before') {
205
+ taskMap.delete(id);
206
+ rowMeta.delete(id);
207
+ }
208
+ }
209
+ }
182
210
  if (isSdkFleetTaskTerminal(row.status)) {
183
- settleTerminalRow(row, receivedAtMs);
211
+ settleTerminalRow(row, tail, receivedAtMs);
184
212
  return;
185
213
  }
186
214
  taskMap.set(row.id, row);
187
215
  rowMeta.set(row.id, { receivedAtMs });
188
- const tail = rowIdTail(row.id);
189
216
  for (const id of [...retained.keys()]) {
190
217
  if (rowIdTail(id) === tail)
191
218
  retained.delete(id);
192
219
  }
193
- settledTerminal.delete(tail);
220
+ withdrawSettledTerminal(tail);
194
221
  clearBgTerminalFacts(tail);
195
222
  }
196
223
  function forgetSettledTerminalRowOf(id) {
@@ -243,9 +270,6 @@ export function createFleetLedger(hooks = {}, opts = {}) {
243
270
  break;
244
271
  }
245
272
  case 'snapshot': {
246
- taskMap.clear();
247
- wfMap.clear();
248
- rowMeta.clear();
249
273
  const receivedAtMs = nowFn();
250
274
  const rows = [];
251
275
  for (const row of frame.tasks) {
@@ -256,12 +280,26 @@ export function createFleetLedger(hooks = {}, opts = {}) {
256
280
  }
257
281
  rows.push(row);
258
282
  }
283
+ const keepTails = new Set();
284
+ const currentTails = new Set();
285
+ for (const row of rows) {
286
+ const tail = rowIdTail(row.id);
287
+ if (isFormerGeneration(tail, wireCycleSeq(row)))
288
+ keepTails.add(tail);
289
+ else
290
+ currentTails.add(tail);
291
+ }
292
+ for (const tail of currentTails)
293
+ keepTails.delete(tail);
294
+ for (const id of [...taskMap.keys()]) {
295
+ if (keepTails.has(rowIdTail(id)))
296
+ continue;
297
+ taskMap.delete(id);
298
+ rowMeta.delete(id);
299
+ }
300
+ wfMap.clear();
259
301
  for (const row of rows)
260
- if (isSdkFleetTaskTerminal(row.status))
261
- admitRow(row, receivedAtMs);
262
- for (const row of rows)
263
- if (!isSdkFleetTaskTerminal(row.status))
264
- admitRow(row, receivedAtMs);
302
+ admitRow(row, receivedAtMs);
265
303
  for (const row of frame.workflows)
266
304
  wfMap.set(row.id, row);
267
305
  break;
@@ -275,7 +313,6 @@ export function createFleetLedger(hooks = {}, opts = {}) {
275
313
  }
276
314
  const receivedAtMs = nowFn();
277
315
  admitRow(row, receivedAtMs);
278
- raiseCycleWatermark(rowIdTail(row.id), wireCycleSeq(row));
279
316
  debug(`[fleet-row] id=${row.id} parent=${row.parentId ?? '-'} agentType=${row.agentType ?? '-'}` +
280
317
  ` agentName=${row.agentName ?? '-'} name=${row.name ?? '-'} status=${row.status ?? '-'}` +
281
318
  ` tokens=${row.tokens ?? '-'} parentToolCallId=${row.parentToolCallId ?? '-'}`);
@@ -420,79 +457,65 @@ export function createFleetLedger(hooks = {}, opts = {}) {
420
457
  const writtenIds = new Set();
421
458
  let applied = false;
422
459
  let washBlocked = false;
423
- let staleBlocked = false;
424
460
  const notifSeq = cycleSeqValue(n.seq);
425
- {
426
- const mark = cycleWatermark.get(n.taskId);
427
- if (cycleSeqOrder(notifSeq, mark) === 'before') {
428
- staleBlocked = true;
429
- debug(`[fleet-frame] bg_notification stale-cycle ignored(watermark=${mark} notif-seq=${notifSeq})`);
430
- }
431
- }
432
- for (const id of rowIds) {
433
- const known = taskMap.get(id);
434
- if (!known)
435
- continue;
436
- const heldGen = wireCycleSeq(known);
437
- if (cycleSeqOrder(notifSeq, heldGen) === 'before') {
438
- staleBlocked = true;
439
- debug(`[fleet-frame] bg_notification stale-cycle ignored(row=${id} row-cycle=${heldGen} notif-seq=${notifSeq})`);
440
- continue;
441
- }
442
- const started = known.startedAt;
443
- const meta = rowMeta.get(id);
444
- const frozenElapsed = typeof started === 'number' && started > 0 && started <= nowMs
445
- ? nowMs - started
446
- : meta !== undefined && isElapsedBase(known.elapsedMs)
447
- ? known.elapsedMs + Math.max(0, nowMs - meta.receivedAtMs)
448
- : known.elapsedMs;
449
- taskMap.delete(id);
450
- rowMeta.delete(id);
451
- retained.set(id, {
452
- row: {
453
- ...known,
454
- status: coerced,
455
- ...(typeof frozenElapsed === 'number' ? { elapsedMs: frozenElapsed } : {}),
456
- },
457
- expireAt: nowMs + TERMINAL_RETAIN_MS,
458
- retiredByNotification: true,
459
- });
460
- writtenIds.add(id);
461
- applied = true;
461
+ const staleBlocked = isFormerGeneration(notifTail, notifSeq);
462
+ if (staleBlocked) {
463
+ debug(`[fleet-frame] bg_notification stale-cycle ignored(tail-cycle=${tails.get(notifTail)?.gen} notif-seq=${notifSeq})`);
462
464
  }
463
- for (const [id, entry] of retained) {
464
- if (writtenIds.has(id))
465
- continue;
466
- if (rowIdTail(id) !== notifTail)
467
- continue;
468
- const retainedGen = wireCycleSeq(entry.row);
469
- if (cycleSeqOrder(notifSeq, retainedGen) === 'before') {
470
- staleBlocked = true;
471
- debug(`[fleet-frame] bg_notification stale-cycle ignored(retained=${id} row-cycle=${retainedGen} notif-seq=${notifSeq})`);
472
- continue;
473
- }
474
- const prior = entry.row.status ?? '';
475
- if (prior === n.status) {
465
+ if (!staleBlocked) {
466
+ for (const id of rowIds) {
467
+ const known = taskMap.get(id);
468
+ if (!known)
469
+ continue;
470
+ const started = known.startedAt;
471
+ const meta = rowMeta.get(id);
472
+ const frozenElapsed = typeof started === 'number' && started > 0 && started <= nowMs
473
+ ? nowMs - started
474
+ : meta !== undefined && isElapsedBase(known.elapsedMs)
475
+ ? known.elapsedMs + Math.max(0, nowMs - meta.receivedAtMs)
476
+ : known.elapsedMs;
477
+ taskMap.delete(id);
478
+ rowMeta.delete(id);
479
+ retained.set(id, {
480
+ row: {
481
+ ...known,
482
+ status: coerced,
483
+ ...(typeof frozenElapsed === 'number' ? { elapsedMs: frozenElapsed } : {}),
484
+ },
485
+ expireAt: nowMs + TERMINAL_RETAIN_MS,
486
+ retiredByNotification: true,
487
+ });
488
+ writtenIds.add(id);
476
489
  applied = true;
477
- continue;
478
490
  }
479
- if (isWashGreen(prior, n.status)) {
480
- washBlocked = true;
481
- debug(`[fleet-frame] bg_notification WASH-GREEN blocked(retained=${id} prior=${prior} notif=${n.status})`);
482
- continue;
491
+ for (const [id, entry] of retained) {
492
+ if (writtenIds.has(id))
493
+ continue;
494
+ if (rowIdTail(id) !== notifTail)
495
+ continue;
496
+ const prior = entry.row.status ?? '';
497
+ if (prior === n.status) {
498
+ applied = true;
499
+ continue;
500
+ }
501
+ if (isWashGreen(prior, n.status)) {
502
+ washBlocked = true;
503
+ debug(`[fleet-frame] bg_notification WASH-GREEN blocked(retained=${id} prior=${prior} notif=${n.status})`);
504
+ continue;
505
+ }
506
+ retained.set(id, {
507
+ row: { ...entry.row, status: coerced },
508
+ expireAt: nowMs + TERMINAL_RETAIN_MS,
509
+ ...(entry.retiredByNotification === true ? { retiredByNotification: true } : {}),
510
+ });
511
+ applied = true;
483
512
  }
484
- retained.set(id, {
485
- row: { ...entry.row, status: coerced },
486
- expireAt: nowMs + TERMINAL_RETAIN_MS,
487
- ...(entry.retiredByNotification === true ? { retiredByNotification: true } : {}),
488
- });
489
- applied = true;
490
513
  }
491
514
  const factsAccepted = !staleBlocked &&
492
515
  (applied ||
493
516
  (!washBlocked &&
494
517
  (() => {
495
- for (const prior of [getBgTerminalFacts(n.taskId), settledTerminal.get(notifTail)]) {
518
+ for (const prior of [getBgTerminalFacts(n.taskId), tails.get(notifTail)?.settled]) {
496
519
  if (prior === undefined)
497
520
  continue;
498
521
  const order = cycleSeqOrder(notifSeq, prior.cycleSeq);
@@ -506,7 +529,7 @@ export function createFleetLedger(hooks = {}, opts = {}) {
506
529
  return true;
507
530
  })()));
508
531
  if (factsAccepted)
509
- raiseCycleWatermark(n.taskId, notifSeq);
532
+ raiseTailGen(notifTail, notifSeq);
510
533
  if (factsAccepted) {
511
534
  recordBgTerminalFacts(n.taskId, {
512
535
  status: n.status,
@@ -633,7 +656,8 @@ export function createFleetLedger(hooks = {}, opts = {}) {
633
656
  wfMap.clear();
634
657
  rowMeta.clear();
635
658
  retained.clear();
636
- settledTerminal.clear();
659
+ for (const rec of tails.values())
660
+ rec.settled = undefined;
637
661
  contentAnchorSet = false;
638
662
  contentAnchor = undefined;
639
663
  debug(`[fleet-frame] 会话锚变更(${from ?? '-'} → ${to ?? '-'})⇒ 清上一会话的行/留存` +
@@ -1,7 +1,6 @@
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 { currentEngineWord } from './engineWordRenames.js';
5
4
  import { readAskOrigin, readOwnDataNonEmptyString, readOwnDataValue } from './ownKey.js';
6
5
  function str(v) {
7
6
  return typeof v === 'string' && v.length > 0 ? v : undefined;
@@ -19,9 +18,8 @@ export function modeReleaseReasonSentence(reason) {
19
18
  try {
20
19
  if (typeof reason !== 'string' || reason === '')
21
20
  return undefined;
22
- const word = currentEngineWord(reason);
23
- return Object.hasOwn(MODE_RELEASE_SENTENCE_BY_REASON, word)
24
- ? MODE_RELEASE_SENTENCE_BY_REASON[word]
21
+ return Object.hasOwn(MODE_RELEASE_SENTENCE_BY_REASON, reason)
22
+ ? MODE_RELEASE_SENTENCE_BY_REASON[reason]
25
23
  : MODE_RELEASE_UNRECOGNISED_SENTENCE;
26
24
  }
27
25
  catch {
@@ -1,5 +1,4 @@
1
1
  import { CORE_DENIED_BY_VALUES, CORE_PERSISTED_RULE_MANDATES } from './generated/engineFactTables.js';
2
- import { PRIOR_ENGINE_WORD_RENAMES, type PriorEngineWord } from './engineWordRenames.js';
3
2
  export declare const CC_TOOL_DENIAL_KINDS: readonly ["user-rejected", "permission-rule", "automode-blocked", "automode-unavailable", "automode-parsing-error", "interrupted", "cancelled"];
4
3
  export type CcToolDenialKind = (typeof CC_TOOL_DENIAL_KINDS)[number];
5
4
  export declare function isCcToolDenialKind(v: unknown): v is CcToolDenialKind;
@@ -11,11 +10,7 @@ export declare function isGateDeniedByWord(v: unknown): v is GateDeniedByWord;
11
10
  export declare function gateDeniedByDetail(deniedBy: unknown): string;
12
11
  export declare function askOriginDetail(origin: unknown): string;
13
12
  export declare function mandatedApprovalDetail(): string;
14
- type RetiredMandateWord = {
15
- [K in PriorEngineWord]: (typeof PRIOR_ENGINE_WORD_RENAMES)[K] extends (typeof CORE_PERSISTED_RULE_MANDATES)[number] ? K : never;
16
- }[PriorEngineWord];
17
- export declare const APPROVAL_MANDATE_WORDS: readonly ((typeof CORE_PERSISTED_RULE_MANDATES)[number] | RetiredMandateWord)[];
13
+ export declare const APPROVAL_MANDATE_WORDS: readonly (typeof CORE_PERSISTED_RULE_MANDATES)[number][];
18
14
  export type ApprovalMandateWord = (typeof APPROVAL_MANDATE_WORDS)[number];
19
15
  export declare function isApprovalMandateWord(v: unknown): v is ApprovalMandateWord;
20
16
  export declare function approvalMandateDetail(word: ApprovalMandateWord): string;
21
- export {};
@@ -1,5 +1,4 @@
1
1
  import { CORE_DENIED_BY_VALUES, CORE_PERSISTED_RULE_MANDATES } from './generated/engineFactTables.js';
2
- import { PRIOR_ENGINE_WORD_RENAMES } from './engineWordRenames.js';
3
2
  export const CC_TOOL_DENIAL_KINDS = Object.freeze([
4
3
  'user-rejected',
5
4
  'permission-rule',
@@ -72,8 +71,7 @@ export function askOriginDetail(origin) {
72
71
  export function mandatedApprovalDetail() {
73
72
  return 'no saved allow rule and no remembered answer can retire this question; each call is settled on its own — by you here or by an automatic check the deployment runs — and an answer covers only that call';
74
73
  }
75
- const RETIRED_MANDATE_WORDS = Object.freeze(Object.keys(PRIOR_ENGINE_WORD_RENAMES).filter((k) => CORE_PERSISTED_RULE_MANDATES.includes(PRIOR_ENGINE_WORD_RENAMES[k])));
76
- export const APPROVAL_MANDATE_WORDS = Object.freeze([...CORE_PERSISTED_RULE_MANDATES, ...RETIRED_MANDATE_WORDS]);
74
+ export const APPROVAL_MANDATE_WORDS = Object.freeze([...CORE_PERSISTED_RULE_MANDATES]);
77
75
  const _mandateWordsPin = true;
78
76
  void _mandateWordsPin;
79
77
  export function isApprovalMandateWord(v) {
@@ -86,9 +84,8 @@ const APPROVAL_MANDATE_SENTENCES = Object.freeze({
86
84
  probe_unanswered: 'the check run on this call did not answer (it timed out or failed; this client cannot tell which), so whether the call stays inside the directories allowed for this session is unknown',
87
85
  write_protection: "this change lands on a protected location (the agent's own configuration), which is confirmed one change at a time",
88
86
  target_unresolved: 'where this change lands on disk could not be determined (a link on the way could not be examined, or the target could not be resolved), so whether it lands on a guarded location is unknown',
89
- write_protection_unresolved: 'the protected-location check could not tell where this change lands, so whether it touches a protected location is unknown',
90
87
  });
91
- const APPROVAL_MANDATE_SET = new Set(Object.keys(APPROVAL_MANDATE_SENTENCES));
88
+ const APPROVAL_MANDATE_SET = new Set(APPROVAL_MANDATE_WORDS);
92
89
  export function approvalMandateDetail(word) {
93
90
  if (typeof word === 'string' && Object.hasOwn(APPROVAL_MANDATE_SENTENCES, word)) {
94
91
  return APPROVAL_MANDATE_SENTENCES[word];
@@ -3,3 +3,5 @@ export declare const SDK_STATUS_DERIVED_COARSE_CODES: readonly ["request.rejecte
3
3
  export declare const SDK_DENY_REASON_ROUTING_FAILURE: "routing_failure_fail_closed";
4
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
5
  export declare const SDK_RUN_DEATH_VERDICT_CODES: readonly ["run.stalled", "run.orphaned", "approval.expired"];
6
+ export declare const SDK_APPROVAL_DECIDE_GATE_KINDS: readonly ["human", "irreversible_ask"];
7
+ export declare const SDK_PLAN_REVIEW_GATE_KINDS: readonly ["plan_review"];
@@ -32,3 +32,10 @@ export const SDK_RUN_DEATH_VERDICT_CODES = Object.freeze([
32
32
  "run.orphaned",
33
33
  "approval.expired",
34
34
  ]);
35
+ export const SDK_APPROVAL_DECIDE_GATE_KINDS = Object.freeze([
36
+ "human",
37
+ "irreversible_ask",
38
+ ]);
39
+ export const SDK_PLAN_REVIEW_GATE_KINDS = Object.freeze([
40
+ "plan_review",
41
+ ]);
@@ -1,3 +1,2 @@
1
1
  export declare const CORE_PROTOCOL_PREFIXES: readonly ["mcp__", "a2a__"];
2
- export declare const CORE_RETIRED_TOOL_CURRENT_NAMES: ReadonlyMap<string, string | null>;
3
2
  export declare const CORE_SKILL_TOOL_NAME: "Skill";
@@ -1,44 +1,2 @@
1
1
  export const CORE_PROTOCOL_PREFIXES = ["mcp__", "a2a__"];
2
- export const CORE_RETIRED_TOOL_CURRENT_NAMES = new Map([
3
- ["read_file", "Read"],
4
- ["edit_file", "Edit"],
5
- ["write_file", "Write"],
6
- ["bash", "Bash"],
7
- ["bash_readonly", null],
8
- ["glob", "Glob"],
9
- ["grep", "Grep"],
10
- ["web_fetch", "WebFetch"],
11
- ["tool_search", "ToolSearch"],
12
- ["skill", "Skill"],
13
- ["present_plan", "ExitPlanMode"],
14
- ["enter_plan_mode", "EnterPlanMode"],
15
- ["spawn_subagent", "Agent"],
16
- ["CronCancel", "CronDelete"],
17
- ["lsp", "LSP"],
18
- ["submit_output", "StructuredOutput"],
19
- ["repo_map", "RepoMap"],
20
- ["read_tool_result", "ReadToolResult"],
21
- ["report_blocked", "ReportBlocked"],
22
- ["declare_done", "DeclareDone"],
23
- ["run_workflow", "Workflow"],
24
- ["RunWorkflow", "Workflow"],
25
- ["Task", "Agent"],
26
- ["BashOutput", "TaskOutput"],
27
- ["AgentOutputTool", "TaskOutput"],
28
- ["BashOutputTool", "TaskOutput"],
29
- ["AgentOutput", "TaskOutput"],
30
- ["WorkflowStatus", null],
31
- ["KillShell", "TaskStop"],
32
- ["KillBash", "TaskStop"],
33
- ["ListMcpResources", "ListMcpResourcesTool"],
34
- ["ReadMcpResource", "ReadMcpResourceTool"],
35
- ["ReadMcpResourceDir", "ReadMcpResourceDirTool"],
36
- ["MultiEdit", null],
37
- ["Fork", null],
38
- ["Sleep", null],
39
- ["Remember", null],
40
- ["remember", null],
41
- ["Recall", null],
42
- ["recall", null],
43
- ]);
44
2
  export const CORE_SKILL_TOOL_NAME = "Skill";
@@ -1,4 +1,4 @@
1
- import { displayUntrusted } from '../displayUntrusted.js';
1
+ import { displayUntrusted, markerSafeCut } from '../displayUntrusted.js';
2
2
  const NOTE_DISPLAY_MAX = 200;
3
3
  export const DECISION_NOTE_NOTICE_KEY = 'approval-decision-note';
4
4
  function cleanNote(raw) {
@@ -7,7 +7,7 @@ function cleanNote(raw) {
7
7
  .trim();
8
8
  if (flat === '')
9
9
  return undefined;
10
- return flat.length <= NOTE_DISPLAY_MAX ? flat : `${flat.slice(0, NOTE_DISPLAY_MAX - 1)}…`;
10
+ return flat.length <= NOTE_DISPLAY_MAX ? flat : `${flat.slice(0, markerSafeCut(flat, NOTE_DISPLAY_MAX - 1))}…`;
11
11
  }
12
12
  export function readDecisionNoteAudit(ack) {
13
13
  if (ack === null || typeof ack !== 'object')
@@ -1,5 +1,6 @@
1
1
  import { ASK_GONE_DENIED_WORD } from '../engineErrorCodes.js';
2
2
  import { SDK_ASK_ROW_ENDING_CAUSES } from '../generated/sdkFactTables.js';
3
+ import { guardedRead, readOwnDataValue } from '../ownKey.js';
3
4
  import { readThrownExtra } from '../wireFailureShape.js';
4
5
  export const ASK_ROW_ENDING_CAUSE_WORDS = SDK_ASK_ROW_ENDING_CAUSES;
5
6
  const _askRowCausePin = true;
@@ -27,13 +28,7 @@ export function askRowEndingCauseFor(state, word) {
27
28
  return undefined;
28
29
  }
29
30
  function ownDataSlot(o, key) {
30
- try {
31
- const slot = Object.getOwnPropertyDescriptor(o, key);
32
- return slot !== undefined && Object.hasOwn(slot, 'value') ? slot.value : undefined;
33
- }
34
- catch {
35
- return undefined;
36
- }
31
+ return guardedRead(() => readOwnDataValue(o, key));
37
32
  }
38
33
  export function approvalCompleteCauseOf(frame) {
39
34
  try {
@@ -1,4 +1,12 @@
1
1
  import type { ApprovalHolderIdentity, FsApprovalOutcome, ToolApprovalFrameOutcome } from './toolApprovalWire.js';
2
+ export declare const APPROVAL_SETTLED_HOWS: readonly ["answered", "interrupted", "closed_by_engine"];
3
+ export type ApprovalSettledHow = (typeof APPROVAL_SETTLED_HOWS)[number];
4
+ export declare const SETTLED_APPROVALS_MAX_ENTRIES = 4096;
5
+ export interface SettledApprovalEntry {
6
+ readonly how: ApprovalSettledHow;
7
+ readonly parked: boolean;
8
+ readonly outcome: ToolApprovalFrameOutcome;
9
+ }
2
10
  export interface DurableDecisionHold {
3
11
  readonly lane: 'durable';
4
12
  readonly outcome: Promise<FsApprovalOutcome>;
@@ -17,7 +25,7 @@ export interface LiveCardHold {
17
25
  };
18
26
  readonly cardSignal: AbortSignal;
19
27
  readonly interrupt: () => void;
20
- trailing?: Promise<void>;
28
+ trailing?: Promise<boolean>;
21
29
  }
22
30
  export type ApprovalHold = DurableDecisionHold | LiveCardHold;
23
31
  export interface LiveCardIdentity {
@@ -27,12 +35,14 @@ export interface LiveCardIdentity {
27
35
  readonly sessionId?: string;
28
36
  }
29
37
  export declare function liveCardHoldFor(who: ApprovalHolderIdentity): LiveCardHold | undefined;
38
+ export declare function settledForDurableLane(entry: SettledApprovalEntry | undefined): boolean;
30
39
  export declare function liveCardAnsweredHow(o: ToolApprovalFrameOutcome): 'answered' | 'interrupted' | undefined;
31
40
  export declare function awaitLiveCardHolder(hold: LiveCardHold, signal: AbortSignal | undefined): Promise<ToolApprovalFrameOutcome | undefined>;
32
41
  export interface ApprovalDecisionHoldTable {
33
42
  readonly inFlight: Map<string, ApprovalHold>;
34
43
  readonly settled: Map<string, DurableSettledHold>;
35
44
  readonly openReads: Set<number>;
45
+ readonly outcomes: Map<string, SettledApprovalEntry>;
36
46
  clock: number;
37
47
  }
38
48
  export interface DurableSettledHold {
@@ -40,6 +50,11 @@ export interface DurableSettledHold {
40
50
  readonly hold: DurableDecisionHold;
41
51
  }
42
52
  export declare const approvalDecisionHolds: ApprovalDecisionHoldTable;
53
+ export declare function settledApprovalOf(approvalId: string | undefined): SettledApprovalEntry | undefined;
54
+ export declare function clearSettledApprovals(): void;
55
+ export declare function anyApprovalSettled(): boolean;
56
+ export declare function noteEngineClosedApproval(approvalId: string, parked: boolean): void;
57
+ export declare function settledLiveCardOutcome(approvalId: string): ToolApprovalFrameOutcome | undefined;
43
58
  export declare function liveCardHoldOf(key: string): LiveCardHold | undefined;
44
59
  export declare function holdLiveCard(identity: LiveCardIdentity, callerSignal: AbortSignal | undefined, present: (hold: LiveCardHold) => Promise<ToolApprovalFrameOutcome>): Promise<ToolApprovalFrameOutcome>;
45
60
  export declare function joinLiveCard(hold: LiveCardHold, signal: AbortSignal | undefined): Promise<ToolApprovalFrameOutcome>;
@@ -1,6 +1,8 @@
1
1
  import { hostLog } from '../host.js';
2
2
  import { liveFrameCallKey } from './gateIdentity.js';
3
3
  import { approvalResolutionOf } from './approvalResolution.js';
4
+ export const APPROVAL_SETTLED_HOWS = Object.freeze(['answered', 'interrupted', 'closed_by_engine']);
5
+ export const SETTLED_APPROVALS_MAX_ENTRIES = 4096;
4
6
  const nonEmpty = (v) => typeof v === 'string' && v !== '';
5
7
  export function liveCardHoldFor(who) {
6
8
  if (nonEmpty(who.approvalId))
@@ -19,13 +21,8 @@ export function liveCardHoldFor(who) {
19
21
  }
20
22
  return undefined;
21
23
  }
22
- function liveCardSettledQuestion(hold, o) {
23
- const r = approvalResolutionOf(o);
24
- if (answeredHowOf(r) !== undefined)
25
- return true;
26
- if (r.kind === 'unsettled')
27
- return false;
28
- return hold.superseded !== undefined && !hold.superseded.parked;
24
+ export function settledForDurableLane(entry) {
25
+ return entry !== undefined && !entry.parked;
29
26
  }
30
27
  export function liveCardAnsweredHow(o) {
31
28
  return answeredHowOf(approvalResolutionOf(o));
@@ -47,9 +44,43 @@ export async function awaitLiveCardHolder(hold, signal) {
47
44
  finally {
48
45
  unsubscribe();
49
46
  }
50
- return liveCardSettledQuestion(hold, o) ? { ...o, joinedInFlight: true } : undefined;
47
+ const stillHeld = approvalDecisionHolds.inFlight.get(liveFrameCallKey(hold.identity.approvalId)) === hold;
48
+ return stillHeld || settledForDurableLane(settledApprovalOf(hold.identity.approvalId)) ? { ...o, joinedInFlight: true } : undefined;
49
+ }
50
+ export const approvalDecisionHolds = { inFlight: new Map(), settled: new Map(), openReads: new Set(), outcomes: new Map(), clock: 0 };
51
+ export function settledApprovalOf(approvalId) {
52
+ return typeof approvalId === 'string' && approvalId !== '' ? approvalDecisionHolds.outcomes.get(approvalId) : undefined;
53
+ }
54
+ export function clearSettledApprovals() {
55
+ approvalDecisionHolds.outcomes.clear();
56
+ }
57
+ export function anyApprovalSettled() {
58
+ return approvalDecisionHolds.outcomes.size > 0;
59
+ }
60
+ function noteSettled(approvalId, how, parked, outcome) {
61
+ if (typeof approvalId !== 'string' || approvalId === '')
62
+ return;
63
+ if (!APPROVAL_SETTLED_HOWS.includes(how))
64
+ return;
65
+ const t = approvalDecisionHolds.outcomes;
66
+ if (t.has(approvalId))
67
+ return;
68
+ t.set(approvalId, Object.freeze({ how, parked, outcome: Object.freeze({ ...outcome }) }));
69
+ while (t.size > SETTLED_APPROVALS_MAX_ENTRIES) {
70
+ const oldest = t.keys().next();
71
+ if (oldest.done === true)
72
+ break;
73
+ t.delete(oldest.value);
74
+ }
75
+ }
76
+ const ENGINE_CLOSED_OUTCOME = Object.freeze({ decision: 'unresolved', retracted: true });
77
+ export function noteEngineClosedApproval(approvalId, parked) {
78
+ noteSettled(approvalId, 'closed_by_engine', parked, ENGINE_CLOSED_OUTCOME);
79
+ }
80
+ export function settledLiveCardOutcome(approvalId) {
81
+ const e = settledApprovalOf(approvalId);
82
+ return e === undefined ? undefined : { ...e.outcome, joinedInFlight: true };
51
83
  }
52
- export const approvalDecisionHolds = { inFlight: new Map(), settled: new Map(), openReads: new Set(), clock: 0 };
53
84
  export function liveCardHoldOf(key) {
54
85
  const h = approvalDecisionHolds.inFlight.get(key);
55
86
  return h !== undefined && h.lane === 'live' ? h : undefined;
@@ -76,7 +107,7 @@ export function holdLiveCard(identity, callerSignal, present) {
76
107
  if (h.inFlight.get(key) === hold)
77
108
  h.inFlight.delete(key);
78
109
  };
79
- const settle = (give) => {
110
+ const settle = (give, onLanded) => {
80
111
  settled = true;
81
112
  unsubscribe();
82
113
  const trailing = hold.trailing;
@@ -86,7 +117,11 @@ export function holdLiveCard(identity, callerSignal, present) {
86
117
  return;
87
118
  }
88
119
  give();
89
- trailing.then(release, release);
120
+ trailing.then((landed) => {
121
+ if (landed === true)
122
+ onLanded?.();
123
+ release();
124
+ }, release);
90
125
  };
91
126
  let run;
92
127
  try {
@@ -95,7 +130,12 @@ export function holdLiveCard(identity, callerSignal, present) {
95
130
  catch (e) {
96
131
  run = Promise.reject(e);
97
132
  }
98
- run.then((o) => settle(() => resolveOutcome(o)), (e) => settle(() => rejectOutcome(e)));
133
+ run.then((o) => {
134
+ const how = liveCardAnsweredHow(o);
135
+ if (how === 'answered')
136
+ noteSettled(identity.approvalId, how, false, o);
137
+ settle(() => resolveOutcome(o), how === 'interrupted' ? () => noteSettled(identity.approvalId, how, false, o) : undefined);
138
+ }, (e) => settle(() => rejectOutcome(e)));
99
139
  return outcome;
100
140
  }
101
141
  export async function joinLiveCard(hold, signal) {