@sema-agent/client-core 0.110.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 (176) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/README.md +50 -18
  3. package/dist/abortableSleep.d.ts +7 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/arms.js +25 -4
  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 +10 -10
  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/displayUntrusted.d.ts +1 -0
  26. package/dist/displayUntrusted.js +6 -0
  27. package/dist/engineHealthFacts.d.ts +11 -0
  28. package/dist/engineHealthFacts.js +21 -0
  29. package/dist/engineHealthProbeVerdict.d.ts +23 -0
  30. package/dist/engineHealthProbeVerdict.js +115 -0
  31. package/dist/engineNoticeCodes.js +5 -7
  32. package/dist/engineRepoint.d.ts +12 -5
  33. package/dist/engineRepoint.js +44 -19
  34. package/dist/engineRowRef.d.ts +3 -0
  35. package/dist/engineRowRef.js +11 -1
  36. package/dist/engineSwapCrossing.d.ts +57 -0
  37. package/dist/engineSwapCrossing.js +297 -0
  38. package/dist/engineUnavailable.d.ts +44 -0
  39. package/dist/engineUnavailable.js +492 -0
  40. package/dist/eventLeg.js +3 -3
  41. package/dist/generated/sdkFactTables.d.ts +1 -1
  42. package/dist/generated/sdkFactTables.js +1 -0
  43. package/dist/generated/settingsSchemaTables.d.ts +4 -0
  44. package/dist/generated/settingsSchemaTables.js +1 -0
  45. package/dist/hitl/approvalDelivery.d.ts +1 -0
  46. package/dist/hitl/approvalDelivery.js +1 -1
  47. package/dist/hitl/approvalResolution.d.ts +1 -1
  48. package/dist/hitl/approvalResolution.js +2 -1
  49. package/dist/hitl/askGateWire.js +5 -6
  50. package/dist/hitl/decisionRefusal.d.ts +2 -0
  51. package/dist/hitl/decisionRefusal.js +7 -8
  52. package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
  53. package/dist/hitl/decisionRefusalCarrier.js +348 -0
  54. package/dist/hitl/decisionSend.d.ts +7 -0
  55. package/dist/hitl/decisionSend.js +34 -12
  56. package/dist/hitl/editedApprovalAcceptance.js +1 -1
  57. package/dist/hitl/elicitationRefusal.d.ts +4 -0
  58. package/dist/hitl/elicitationRefusal.js +23 -0
  59. package/dist/hitl/frameRouter.d.ts +2 -2
  60. package/dist/hitl/frameRouter.js +8 -15
  61. package/dist/hitl/gateIdentity.d.ts +5 -0
  62. package/dist/hitl/gateIdentity.js +23 -0
  63. package/dist/hitl/hitlBridge.d.ts +6 -0
  64. package/dist/hitl/hitlBridge.js +10 -4
  65. package/dist/hitl/hitlHostSurface.d.ts +2 -1
  66. package/dist/hitl/hitlHostSurface.js +7 -2
  67. package/dist/hitl/localAllowRedemption.d.ts +67 -0
  68. package/dist/hitl/localAllowRedemption.js +256 -0
  69. package/dist/hitl/parkResolver.js +7 -4
  70. package/dist/hitl/persistedRulesWire.d.ts +16 -0
  71. package/dist/hitl/persistedRulesWire.js +70 -0
  72. package/dist/hitl/planReviewWire.d.ts +3 -0
  73. package/dist/hitl/planReviewWire.js +23 -6
  74. package/dist/hitl/policyDenyCause.d.ts +1 -1
  75. package/dist/hitl/policyDenyCause.js +1 -1
  76. package/dist/hitl/resumeRunningCard.d.ts +1 -0
  77. package/dist/hitl/resumeRunningCard.js +1 -0
  78. package/dist/hitl/screenCardCarriers.d.ts +15 -0
  79. package/dist/hitl/screenCardCarriers.js +41 -0
  80. package/dist/hitl/screenDecisionCards.d.ts +98 -0
  81. package/dist/hitl/screenDecisionCards.js +362 -0
  82. package/dist/hitl/standingRows.d.ts +3 -1
  83. package/dist/hitl/standingRows.js +18 -0
  84. package/dist/hitl/toolApprovalWire.d.ts +30 -8
  85. package/dist/hitl/toolApprovalWire.js +264 -121
  86. package/dist/hooksWireCaps.js +3 -3
  87. package/dist/hostSettingNoticeArm.d.ts +3 -0
  88. package/dist/hostSettingNoticeArm.js +12 -0
  89. package/dist/hostTurnSettings.d.ts +3 -0
  90. package/dist/hostTurnSettings.js +38 -5
  91. package/dist/humanTurnRead.js +11 -1
  92. package/dist/i18n/messages.en.d.ts +129 -0
  93. package/dist/i18n/messages.en.js +135 -7
  94. package/dist/i18n/messages.zh.js +142 -6
  95. package/dist/i18n/translate.d.ts +3 -0
  96. package/dist/i18n/translate.js +78 -10
  97. package/dist/i18n/zh.d.ts +1 -0
  98. package/dist/i18n/zh.js +1 -0
  99. package/dist/index.d.ts +43 -17
  100. package/dist/index.js +30 -12
  101. package/dist/interactiveHalt.js +2 -1
  102. package/dist/interactiveInterrupt.js +20 -2
  103. package/dist/liveModelCatalog.js +2 -2
  104. package/dist/localEngineSeatReading.d.ts +2 -2
  105. package/dist/localEngineSeatReading.js +5 -3
  106. package/dist/localEngineSpawn.d.ts +0 -7
  107. package/dist/localEngineSpawn.js +0 -92
  108. package/dist/localEngineStep.d.ts +2 -2
  109. package/dist/localEngineStep.js +2 -0
  110. package/dist/mcpReconnect.js +3 -3
  111. package/dist/mcpWireCaps.js +1 -1
  112. package/dist/model/engineModelWords.d.ts +3 -0
  113. package/dist/model/engineModelWords.js +13 -0
  114. package/dist/model/subagentModelWord.d.ts +21 -2
  115. package/dist/model/subagentModelWord.js +100 -29
  116. package/dist/modelCapabilityProbe.js +3 -40
  117. package/dist/notifications.d.ts +19 -0
  118. package/dist/notifications.js +114 -12
  119. package/dist/ownKey.d.ts +6 -0
  120. package/dist/ownKey.js +31 -0
  121. package/dist/pendingUserRow.d.ts +4 -0
  122. package/dist/pendingUserRow.js +16 -0
  123. package/dist/permissionsDeclaration.d.ts +1 -0
  124. package/dist/permissionsDeclaration.js +11 -1
  125. package/dist/planReviewCardFacts.d.ts +3 -0
  126. package/dist/planReviewCardFacts.js +52 -2
  127. package/dist/pluginHooksWire.d.ts +9 -1
  128. package/dist/pluginHooksWire.js +40 -6
  129. package/dist/printToolResultFrame.js +4 -2
  130. package/dist/promptBlockedCopy.js +11 -6
  131. package/dist/promptSuggestionBatch.d.ts +7 -0
  132. package/dist/promptSuggestionBatch.js +9 -0
  133. package/dist/promptSuggestionsTail.d.ts +39 -0
  134. package/dist/promptSuggestionsTail.js +116 -0
  135. package/dist/request/taskRequest.js +10 -6
  136. package/dist/requestCoordinate.d.ts +1 -0
  137. package/dist/requestCoordinate.js +13 -0
  138. package/dist/rewindArchiveReading.js +4 -4
  139. package/dist/runCancelContext.d.ts +2 -1
  140. package/dist/runCancelContext.js +1 -0
  141. package/dist/runPermissionModeSwitch.js +2 -2
  142. package/dist/scenarioRefusal.js +11 -13
  143. package/dist/scenarioSkills.js +11 -6
  144. package/dist/seam.d.ts +16 -2
  145. package/dist/seam.js +10 -0
  146. package/dist/seatContract.d.ts +109 -9
  147. package/dist/seatContract.js +218 -15
  148. package/dist/sessionCostReading.d.ts +70 -0
  149. package/dist/sessionCostReading.js +150 -0
  150. package/dist/sessionHooksRoster.d.ts +4 -0
  151. package/dist/sessionHooksRoster.js +158 -0
  152. package/dist/sessionMemoryStatus.js +2 -2
  153. package/dist/sessionReadRedaction.d.ts +7 -0
  154. package/dist/sessionReadRedaction.js +26 -0
  155. package/dist/sessionSyncFileHistory.js +8 -9
  156. package/dist/settingsHooksSalvage.js +2 -1
  157. package/dist/sseIdleTriage.js +2 -1
  158. package/dist/storeDurabilityWord.d.ts +15 -0
  159. package/dist/storeDurabilityWord.js +18 -0
  160. package/dist/storePostureCapability.d.ts +7 -3
  161. package/dist/storePostureCapability.js +69 -39
  162. package/dist/subagent/engineSubagentResume.js +2 -2
  163. package/dist/subagent/engineSubagentTail.js +2 -1
  164. package/dist/subagentContentStore.d.ts +11 -0
  165. package/dist/subagentContentStore.js +28 -2
  166. package/dist/toolResult.d.ts +1 -0
  167. package/dist/toolResult.js +2 -0
  168. package/dist/userAttachmentEnvelope.d.ts +2 -1
  169. package/dist/userAttachmentEnvelope.js +10 -0
  170. package/dist/wireErrorTriage.js +2 -1
  171. package/dist/wireFailureShape.d.ts +4 -0
  172. package/dist/wireFailureShape.js +141 -19
  173. package/dist/wireRefusalCopy.d.ts +6 -5
  174. package/dist/wireRefusalCopy.js +49 -92
  175. package/docs/INTEGRATION-CLIENTS.md +2118 -17
  176. package/package.json +6 -2
@@ -5,8 +5,9 @@ import { panelEndFact } from './panelReduce.js';
5
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';
9
- import { displayUntrusted } from './displayUntrusted.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';
10
11
  function escapeXml(v) {
11
12
  return v.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
12
13
  }
@@ -213,6 +214,7 @@ export function normalizeTaskNotification(raw) {
213
214
  ? n.lines.filter((l) => typeof l === 'string')
214
215
  : [];
215
216
  const recentSteps = deriveNotificationResidualLines(n);
217
+ const failureReason = failureReasonOf(status, n.error);
216
218
  return {
217
219
  taskId,
218
220
  status,
@@ -230,6 +232,7 @@ export function normalizeTaskNotification(raw) {
230
232
  ? { diagnostics: n.diagnostics }
231
233
  : {}),
232
234
  ...(typeof n.result === 'string' && n.result.length > 0 ? { result: n.result } : {}),
235
+ ...(failureReason !== undefined ? { error: failureReason } : {}),
233
236
  ...(typeof n.stoppedBy === 'string' && n.stoppedBy.length > 0 ? { stoppedBy: n.stoppedBy } : {}),
234
237
  ...(n.partial === true ? { partial: true } : {}),
235
238
  ...(typeof n.exitCode === 'number' ? { exitCode: n.exitCode } : {}),
@@ -422,6 +425,7 @@ export function enqueuePlanReviewOutcome(value, meta) {
422
425
  queuePortMisses++;
423
426
  return false;
424
427
  }
428
+ const questionId = meta === undefined ? undefined : guardedRead(() => readOwnDataNonEmptyString(meta, 'questionId'));
425
429
  p.enqueueMetaPrompt({
426
430
  value,
427
431
  mode: 'prompt',
@@ -436,12 +440,34 @@ export function enqueuePlanReviewOutcome(value, meta) {
436
440
  decision: meta.decision,
437
441
  effect: meta.effect,
438
442
  ...(meta.effect === 'still_parked' && meta.cause === 'stored_form_unsupported' ? { cause: meta.cause } : {}),
443
+ ...(questionId !== undefined ? { questionId } : {}),
439
444
  }),
440
445
  }
441
446
  : {}),
442
447
  });
443
448
  return true;
444
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
+ }
445
471
  export function dropQueuedNotificationsForRun(taskId) {
446
472
  const needle = `<${TASK_ID_TAG}>${escapeXml(taskId)}</${TASK_ID_TAG}>`;
447
473
  const p = port();
@@ -494,20 +520,25 @@ export function workflowRunTerminalReadingOf(body) {
494
520
  if (isCoreWorkflowRunParkedEnd(status, guardedRead(() => readOwnDataValue(snap, 'errorCode'))))
495
521
  return Object.freeze({ state: 'awaiting approval' });
496
522
  const result = guardedRead(() => readOwnDataValue(snap, 'result'));
497
- const error = status === 'failed' ? guardedRead(() => readOwnDataValue(snap, 'error')) : undefined;
498
- const shownError = typeof error === 'string' && error.length > 0 ? shownRunError(error) : '';
523
+ const shownError = failureReasonOf(status, guardedRead(() => readOwnDataValue(snap, 'error')));
524
+ const resultFull = guardedRead(() => readOwnDataValue(snap, 'resultFull'));
499
525
  return Object.freeze({
500
526
  state: status,
501
527
  ...(typeof result === 'string' && result.length > 0 ? { result } : {}),
502
- ...(shownError.length > 0 ? { error: shownError } : {}),
528
+ ...(shownError !== undefined ? { error: shownError } : {}),
503
529
  pointer: 'absent',
530
+ ...(typeof resultFull === 'string' && resultFull.length > 0 ? { truncated: true } : {}),
504
531
  });
505
532
  }
506
533
  const WORKFLOW_RUN_ERROR_MAX = 200;
507
534
  function shownRunError(raw) {
508
- const opts = { foldLines: false, escapeBackslashes: 'auto' };
509
- const whole = displayUntrusted(raw, opts);
510
- return whole.length <= WORKFLOW_RUN_ERROR_MAX ? whole : `${displayUntrusted(raw, { ...opts, max: WORKFLOW_RUN_ERROR_MAX - 1 })}…`;
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;
511
542
  }
512
543
  function workflowPollDecisionOf(delivered, reading) {
513
544
  if (delivered)
@@ -518,7 +549,13 @@ function workflowPollDecisionOf(delivered, reading) {
518
549
  return { act: 'wait', why: 'awaiting approval' };
519
550
  if (reading.state === 'unknown')
520
551
  return { act: 'wait', why: 'unknown' };
521
- return { act: 'emit', status: reading.state, ...(reading.result !== undefined ? { result: reading.result } : {}), ...(reading.error !== undefined ? { error: reading.error } : {}) };
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
+ };
522
559
  }
523
560
  function workflowPollCompletionSummary(status, error) {
524
561
  const head = `workflow ${status} (read from its run record; no handle to the full record is available)`;
@@ -826,7 +863,7 @@ async function tickWatchInner(now) {
826
863
  if (outstandingRuns.delete(runId))
827
864
  notifyOutstanding();
828
865
  if (decision.act === 'emit') {
829
- enqueueWorkflowCompletion({ runId, status: decision.status, summary: workflowPollCompletionSummary(decision.status, decision.error) }, { ...(decision.result !== undefined ? { result: decision.result } : {}), ...(decision.error !== undefined ? { error: decision.error } : {}) });
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 });
830
867
  }
831
868
  }
832
869
  catch {
@@ -890,9 +927,17 @@ export function enqueueEngineWorkflowNotification(raw) {
890
927
  traceNotif('workflow-notification dropped:payload-unreadable');
891
928
  return;
892
929
  }
893
- enqueueWorkflowCompletion(c, completionIdOnly(workflowCompletionExtrasOf(c)));
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));
894
939
  }
895
- function enqueueWorkflowCompletion(c, body) {
940
+ function enqueueWorkflowCompletion(c, body, poll) {
896
941
  if (notifiedRunIds.has(c.runId)) {
897
942
  workflowDedupDropped++;
898
943
  traceNotif(`workflow notification suppressed (run already notified) runId=${c.runId} status=${c.status}`);
@@ -900,11 +945,68 @@ function enqueueWorkflowCompletion(c, body) {
900
945
  }
901
946
  markRunNotified(c.runId);
902
947
  cardEnqueuedRunIds.add(c.runId);
948
+ if (poll !== undefined)
949
+ notePointerOwed(c.runId, poll.truncated);
903
950
  port()?.enqueuePendingNotification(taskNotificationQueueCommand({ taskId: c.runId, taskType: 'workflow', status: c.status, summary: c.summary, ...body }));
904
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);
1005
+ }
905
1006
  export function _resetEngineTaskNotificationForTest() {
906
1007
  notifiedRunIds.clear();
907
1008
  notifiedBgCycleHigh.clear();
1009
+ pointerOwedRuns.clear();
908
1010
  cardEnqueuedRunIds.clear();
909
1011
  outstandingRuns.clear();
910
1012
  outstandingBgTasks.clear();
package/dist/ownKey.d.ts CHANGED
@@ -79,3 +79,9 @@ export declare function workflowCompletionExtrasOf(c: object): {
79
79
  completionId?: string;
80
80
  diagnostics?: string;
81
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
@@ -453,3 +453,34 @@ export function workflowCompletionExtrasOf(c) {
453
453
  ...(diagnostics !== undefined ? { diagnostics } : {}),
454
454
  };
455
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
+ }
@@ -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
+ }
@@ -115,6 +115,7 @@ export declare function assembledRuleRows(input: PermissionsDeclarationInput, ca
115
115
  export interface PermissionsAllowKeptFacts {
116
116
  readonly problemCount?: number;
117
117
  readonly managedPolicyPending?: true;
118
+ readonly managedPolicyFetch?: 'in_progress' | 'failed' | 'unknown';
118
119
  readonly where?: string;
119
120
  }
120
121
  export declare function permissionsAllowKeptNotice(report: Pick<PermissionsDeclarationReport, 'kept'>, settingsRead: 'clean' | 'partial' | 'unreadable', facts?: PermissionsAllowKeptFacts): string | undefined;
@@ -543,11 +543,21 @@ export function permissionsAllowKeptNotice(report, settingsRead, facts) {
543
543
  const rawCount = readFact('problemCount');
544
544
  const rawWhere = readFact('where');
545
545
  const managedPending = readFact('managedPolicyPending') === true;
546
+ const rawFetch = readFact('managedPolicyFetch');
547
+ const fetchState = rawFetch === 'in_progress' || rawFetch === 'failed' ? rawFetch : 'unknown';
546
548
  const count = typeof rawCount === 'number' && Number.isInteger(rawCount) && rawCount >= 1 ? rawCount : undefined;
547
549
  const where = typeof rawWhere === 'string' && rawWhere.length >= 1 && rawWhere.length <= 200 && !/\p{Cc}/u.test(rawWhere) ? rawWhere : undefined;
548
550
  const head = `${String(held)} permissions.allow rule${held === 1 ? ' was' : 's were'} not sent to the engine for this run because `;
549
551
  if (settingsRead === 'partial' && managedPending && count === 1) {
550
- return (`${head}the managed policy for your account has not been received yet (sema keeps fetching it; allow rules apply once it arrives). ` +
552
+ if (fetchState === 'in_progress') {
553
+ return (`${head}the managed policy for your account has not been received yet (sema keeps fetching it; allow rules apply once it arrives). ` +
554
+ 'Run again once it has been received (deny and ask rules, and rules given with --allowedTools, are still sent).');
555
+ }
556
+ if (fetchState === 'failed') {
557
+ return (`${head}the managed policy for your account has not been received yet (this run will not fetch again; the next run retries). ` +
558
+ 'Run again to retry fetching it (deny and ask rules, and rules given with --allowedTools, are still sent).');
559
+ }
560
+ return (`${head}the managed policy for your account has not been received yet. ` +
551
561
  'Run again once it has been received (deny and ask rules, and rules given with --allowedTools, are still sent).');
552
562
  }
553
563
  const why = settingsRead === 'partial'
@@ -16,6 +16,7 @@ export interface PlanReviewCardFacts {
16
16
  readonly taskId: string;
17
17
  readonly checkpointId?: string;
18
18
  readonly body: PlanReviewBody;
19
+ readonly evidenceRevision?: number;
19
20
  }
20
21
  export declare function planReviewBodyOf(text: string): Exclude<PlanReviewBody, {
21
22
  kind: 'unknown';
@@ -91,10 +92,12 @@ export interface PlanBodyClaimLeg {
91
92
  }>;
92
93
  overflow: boolean;
93
94
  unreadable: boolean;
95
+ readonly seen: Set<string>;
94
96
  }
95
97
  export declare function openPlanBodyClaimLeg(): PlanBodyClaimLeg;
96
98
  export declare function stepPlanBodyClaim(leg: PlanBodyClaimLeg, frame: object, subFlow: boolean): void;
97
99
  export declare function planReviewParkFacts(leg: PlanBodyClaimLeg, result: {} | null | undefined): PlanReviewCardFacts | undefined;
100
+ export declare function planReviewClaimStep(leg: PlanBodyClaimLeg, frame: object, subFlow: boolean, seq: string | undefined): PlanReviewCardFacts | undefined;
98
101
  export interface PlanReviewFactsTaken {
99
102
  readonly sessionKey: string;
100
103
  readonly taskId: string;
@@ -3,6 +3,7 @@ import { readRunTerminal, runTerminalGateKind } from './runTerminal.js';
3
3
  import { SDK_PLAN_REVIEW_GATE_KINDS } from './generated/sdkFactTables.js';
4
4
  import { CORE_PRESENT_PLAN_TOOL_NAME } from './generated/engineFactTables.js';
5
5
  import { DEFAULT_SESSION_KEY } from './sessionSlot.js';
6
+ import { doneResultView } from './replayBody.js';
6
7
  const PLAN_BODY_MAX_LENGTH = 200_000;
7
8
  const PLAN_BODY_UNKNOWN_CAUSES = Object.freeze([
8
9
  'not_observed', 'gate_unidentified', 'gate_moved', 'unreadable', 'ambiguous_call', 'malformed_frame', 'conflicting_claims', 'not_presented', 'not_carried',
@@ -29,6 +30,13 @@ export function mintPlanReviewCardFacts(taskId, checkpointId, body) {
29
30
  const b = checkpointId === undefined ? unknownBody('gate_unidentified') : body;
30
31
  return Object.freeze({ taskId, ...(checkpointId !== undefined ? { checkpointId } : {}), body: Object.isFrozen(b) ? b : Object.freeze({ ...b }) });
31
32
  }
33
+ let evidenceRevisionSeq = 0;
34
+ function withEvidenceRevision(facts, evidenceRevision) {
35
+ return Object.freeze({ taskId: facts.taskId, ...(facts.checkpointId !== undefined ? { checkpointId: facts.checkpointId } : {}), body: facts.body, evidenceRevision });
36
+ }
37
+ function isEvidenceRevision(v) {
38
+ return typeof v === 'number' && Number.isSafeInteger(v) && v > 0;
39
+ }
32
40
  export function unknownBody(cause) {
33
41
  return Object.freeze({ kind: 'unknown', cause });
34
42
  }
@@ -59,7 +67,11 @@ export function planReviewCardFactsFrom(v) {
59
67
  const checkpointId = ck.present ? ck.value : undefined;
60
68
  if (checkpointId === undefined && !(body.kind === 'unknown' && body.cause === 'gate_unidentified'))
61
69
  return undefined;
62
- return mintPlanReviewCardFacts(taskId, checkpointId, body);
70
+ const eg = ownData(v, 'evidenceRevision');
71
+ if (eg.present && !isEvidenceRevision(eg.value))
72
+ return undefined;
73
+ const minted = mintPlanReviewCardFacts(taskId, checkpointId, body);
74
+ return eg.present ? withEvidenceRevision(minted, eg.value) : minted;
63
75
  }
64
76
  catch {
65
77
  return undefined;
@@ -167,6 +179,10 @@ export function commitPlanReviewFacts(ticket, facts, claim, highWater) {
167
179
  if (prev !== undefined && prev.checkpointId === checkpointId && (isConflict(prev.facts) || !samePlanClaim(prev.claim, claim))) {
168
180
  out = mintPlanReviewCardFacts(facts.taskId, checkpointId, unknownBody('conflicting_claims'));
169
181
  }
182
+ const reused = prev !== undefined && prev.checkpointId === checkpointId && prev.facts.evidenceRevision !== undefined && samePlanReviewCardFacts(prev.facts, out)
183
+ ? prev.facts.evidenceRevision
184
+ : undefined;
185
+ out = withEvidenceRevision(out, reused ?? ++evidenceRevisionSeq);
170
186
  rec.gate = { checkpointId, facts: out, claim, generation: ticket.generation, ...(highWater !== undefined ? { highWater } : {}) };
171
187
  return out;
172
188
  }
@@ -338,7 +354,7 @@ function foldMarker(rec, frame) {
338
354
  }
339
355
  const LEG_BUFFER_MAX = 256;
340
356
  export function openPlanBodyClaimLeg() {
341
- return { buffer: [], overflow: false, unreadable: false };
357
+ return { buffer: [], overflow: false, unreadable: false, seen: new Set() };
342
358
  }
343
359
  function bindLeg(leg, taskId) {
344
360
  const rec = recordFor(DEFAULT_SESSION_KEY, taskId);
@@ -425,6 +441,40 @@ export function planReviewParkFacts(leg, result) {
425
441
  return taskId === undefined ? undefined : mintPlanReviewCardFacts(taskId, checkpointId, unknownBody('unreadable'));
426
442
  }
427
443
  }
444
+ export function planReviewClaimStep(leg, frame, subFlow, seq) {
445
+ if (claimSeqDedupe(leg, frame, seq) === 'duplicate')
446
+ return undefined;
447
+ stepPlanBodyClaim(leg, frame, subFlow);
448
+ let result;
449
+ try {
450
+ if (frame.type !== 'done')
451
+ return undefined;
452
+ result = frame.result;
453
+ }
454
+ catch {
455
+ return undefined;
456
+ }
457
+ return planReviewParkFacts(leg, doneResultView(result).body);
458
+ }
459
+ function claimSeqDedupe(leg, frame, seq) {
460
+ try {
461
+ if (seq !== undefined && classOf(frame) !== 'other') {
462
+ if (leg.seen.has(seq))
463
+ return 'duplicate';
464
+ leg.seen.add(seq);
465
+ while (leg.seen.size > SETTLED_CALLS_MAX) {
466
+ const oldest = leg.seen.values().next();
467
+ if (oldest.done === true)
468
+ break;
469
+ leg.seen.delete(oldest.value);
470
+ }
471
+ }
472
+ return 'fresh';
473
+ }
474
+ catch {
475
+ return 'class_unreadable';
476
+ }
477
+ }
428
478
  function legRecord(leg, taskId) {
429
479
  return leg.taskId === undefined || leg.taskId === taskId ? bindLeg(leg, taskId) : recordFor(DEFAULT_SESSION_KEY, taskId);
430
480
  }
@@ -12,8 +12,10 @@ export declare const PLUGIN_HOOK_REASONS: readonly ["projected", "shell_event",
12
12
  export type PluginHookReason = (typeof PLUGIN_HOOK_REASONS)[number];
13
13
  export declare const PLUGIN_HOOK_EXCLUSION_REASONS: readonly ["workspace_untrusted", "governance_unreadable", "policy_disable_all", "startup_mode", "managed_plugins_only", "prompt_not_user_authored"];
14
14
  export type PluginHookExclusionReason = (typeof PLUGIN_HOOK_EXCLUSION_REASONS)[number];
15
- export declare const SETTINGS_HOOK_DROP_REASONS: readonly ["exec_form_unsupported", "mcp_tool_unsupported", "key_not_read", "source_not_authored", "workspace_untrusted", "governance_unreadable", "policy_disable_all"];
15
+ export declare const SETTINGS_HOOK_DROP_REASONS: readonly ["exec_form_unsupported", "mcp_tool_unsupported", "key_not_read", "source_not_authored", "workspace_untrusted", "governance_unreadable", "policy_disable_all", "no_executor"];
16
16
  export type SettingsHookDropReason = (typeof SETTINGS_HOOK_DROP_REASONS)[number];
17
+ export type HookEntryTypeWord = 'command' | 'prompt' | 'agent' | 'http' | 'mcp_tool';
18
+ export declare const HOOK_ENTRY_TYPE_WORDS: readonly ["command", "prompt", "agent", "http", "mcp_tool"];
17
19
  export type HookSettingsSource = 'policySettings' | 'userSettings' | 'projectSettings' | 'localSettings' | 'flagSettings';
18
20
  export interface PluginHookKey {
19
21
  readonly pluginId: string;
@@ -34,6 +36,7 @@ export interface SettingsHookDrop {
34
36
  readonly event: string;
35
37
  readonly groupIndex: number;
36
38
  readonly hookIndex: number;
39
+ readonly type?: HookEntryTypeWord;
37
40
  readonly reason: SettingsHookDropReason;
38
41
  readonly notRun: boolean;
39
42
  readonly unreadKeys?: readonly (readonly (string | number)[])[];
@@ -43,6 +46,7 @@ export interface SettingsHookVerdict {
43
46
  readonly event: string;
44
47
  readonly groupIndex: number;
45
48
  readonly hookIndex: number;
49
+ readonly type?: HookEntryTypeWord;
46
50
  readonly disposition: PluginHookDisposition;
47
51
  readonly reason: PluginHookReason;
48
52
  }
@@ -51,6 +55,7 @@ export interface SettingsHookExclusion {
51
55
  readonly event: string;
52
56
  readonly groupIndex: number;
53
57
  readonly hookIndex: number;
58
+ readonly type?: HookEntryTypeWord;
54
59
  readonly reason: PluginHookExclusionReason;
55
60
  }
56
61
  export interface SettingsHooksSalvageRow {
@@ -169,6 +174,9 @@ export declare function decideSessionHooks(input: SessionHookDecisionInput): {
169
174
  readonly unreadable: number;
170
175
  };
171
176
  export declare function filterSettingsHookGroups(source: HookSettingsSource, event: string, groups: readonly WireHookGroup[], execFormCapable: boolean, shellRunsType: (type: string | undefined) => boolean, judge: SettingsHookJudge, readerNotRead: readonly string[], mask?: HooksSalvageMask): SettingsHookFilterResult;
177
+ export declare function hookEntryTypeWordOf(entry: {} | null | undefined): {
178
+ readonly type?: HookEntryTypeWord;
179
+ };
172
180
  export interface SettingsHookFilterResult {
173
181
  readonly groups: WireHookGroup[];
174
182
  readonly drops: SettingsHookDrop[];
@@ -3,6 +3,7 @@ import { appendOwnElement, readOwnDataValue } from './ownKey.js';
3
3
  import { inSlotReadOf } from './wireFailureShape.js';
4
4
  import { msg, renderMessage } from './i18n/translate.js';
5
5
  import { SETTINGS_ENGINE_OWNED_HOOK_EVENTS } from './generated/settingsSchemaTables.js';
6
+ import { SETTINGS_HOOK_EVENTS } from './generated/settingsSchemaFns.js';
6
7
  import { HOOK_SETTINGS_SOURCE_TEXT, hooksEntryUnreadKeys, hooksGroupAuthoredSource, hooksGroupUnreadKeys, hooksKeyCoordinateText, hooksSalvageMaskOf, salvageSettingsHooksEntries, } from './settingsHooksSalvage.js';
7
8
  export const ENGINE_FIRED_HOOK_EVENTS = SETTINGS_ENGINE_OWNED_HOOK_EVENTS;
8
9
  export const PLUGIN_HOOK_DISPOSITIONS = ['engine', 'shell', 'not_run'];
@@ -49,7 +50,11 @@ export const SETTINGS_HOOK_DROP_REASONS = [
49
50
  'workspace_untrusted',
50
51
  'governance_unreadable',
51
52
  'policy_disable_all',
53
+ 'no_executor',
52
54
  ];
55
+ export const HOOK_ENTRY_TYPE_WORDS = Object.freeze(['command', 'prompt', 'agent', 'http', 'mcp_tool']);
56
+ const _hookEntryTypePins = [true, true, true, true];
57
+ void _hookEntryTypePins;
53
58
  export function hooksWirePlanShapeOf(v) {
54
59
  try {
55
60
  if (typeof v !== 'object' || v === null || Array.isArray(v))
@@ -144,6 +149,7 @@ function hookExecutorOf(x) {
144
149
  return judged(x.shellRuns ? 'shell' : 'not_run', why);
145
150
  }
146
151
  const ENGINE_EVENTS = new Set(ENGINE_FIRED_HOOK_EVENTS);
152
+ const SHELL_OWNED_EVENTS = new Set(SETTINGS_HOOK_EVENTS.filter((e) => !ENGINE_EVENTS.has(e)));
147
153
  const USER_CONFIG_REF_ONE = /\$\{user_config\.[^}]+\}/;
148
154
  const USER_CONFIG_REF_ALL = /\$\{user_config\.([^}]+)\}/g;
149
155
  function isRecord(v) {
@@ -493,10 +499,23 @@ export function filterSettingsHookGroups(source, event, groups, execFormCapable,
493
499
  const excluded = [];
494
500
  const out = [];
495
501
  let stripped = 0;
496
- const engineEvent = ENGINE_EVENTS.has(event);
502
+ const engineEvent = isEngineFiredHookEvent(event);
503
+ const shellOwned = !engineEvent && SHELL_OWNED_EVENTS.has(event);
497
504
  groups.forEach((group, groupIndex) => {
498
505
  if (mask !== undefined && mask.groupDropped(event, groupIndex))
499
506
  return;
507
+ if (!engineEvent) {
508
+ if (!shellOwned || !isRecord(group) || !Array.isArray(group.hooks))
509
+ return;
510
+ group.hooks.forEach((entry, hookIndex) => {
511
+ if (mask !== undefined && mask.entryDropped(event, groupIndex, hookIndex))
512
+ return;
513
+ if (shellRunsType(isRecord(entry) && typeof entry.type === 'string' ? entry.type : undefined))
514
+ return;
515
+ appendOwnElement(drops, { source, event, groupIndex, hookIndex, reason: 'no_executor', notRun: true });
516
+ });
517
+ return;
518
+ }
500
519
  if (!isRecord(group) || !Array.isArray(group.hooks)) {
501
520
  appendOwnElement(out, group);
502
521
  return;
@@ -522,27 +541,29 @@ export function filterSettingsHookGroups(source, event, groups, execFormCapable,
522
541
  const sourceNotAuthored = earlier === undefined && groupSourceAuthored && (authored === 'unreadable' || entryUnread !== undefined);
523
542
  const unreadKeys = earlier === undefined && !sourceNotAuthored ? unreadKeyPathsOf(event, groupIndex, hookIndex, groupUnread ?? [], entryUnread) : [];
524
543
  const reason = earlier ?? (sourceNotAuthored ? 'source_not_authored' : unreadKeys.length > 0 ? 'key_not_read' : undefined);
544
+ const typed = hookEntryTypeWordOf(entry);
525
545
  if (reason !== undefined) {
526
546
  appendOwnElement(drops, {
527
547
  source,
528
548
  event,
529
549
  groupIndex,
530
550
  hookIndex,
551
+ ...typed,
531
552
  reason,
532
- notRun: engineEvent && !shellRunsType(isRecord(entry) && typeof entry.type === 'string' ? entry.type : undefined),
553
+ notRun: !shellRunsType(isRecord(entry) && typeof entry.type === 'string' ? entry.type : undefined),
533
554
  ...(reason === 'key_not_read' ? { unreadKeys } : {}),
534
555
  });
535
556
  changed = true;
536
557
  return;
537
558
  }
538
- if (engineEvent && isRecord(entry) && judgeableSettingsEntry(entry)) {
559
+ if (isRecord(entry) && judgeableSettingsEntry(entry)) {
539
560
  const ex = judge(event, group, entry);
540
561
  if (ex.kind === 'excluded') {
541
- appendOwnElement(excluded, { source, event, groupIndex, hookIndex, reason: ex.reason });
562
+ appendOwnElement(excluded, { source, event, groupIndex, hookIndex, ...typed, reason: ex.reason });
542
563
  changed = true;
543
564
  return;
544
565
  }
545
- appendOwnElement(verdicts, { source, event, groupIndex, hookIndex, disposition: ex.disposition, reason: ex.reason });
566
+ appendOwnElement(verdicts, { source, event, groupIndex, hookIndex, ...typed, disposition: ex.disposition, reason: ex.reason });
546
567
  if (ex.disposition === 'shell') {
547
568
  changed = true;
548
569
  return;
@@ -567,6 +588,17 @@ export function filterSettingsHookGroups(source, event, groups, execFormCapable,
567
588
  });
568
589
  return { groups: out, drops, stripped, verdicts, excluded };
569
590
  }
591
+ export function hookEntryTypeWordOf(entry) {
592
+ try {
593
+ if (!isRecord(entry))
594
+ return {};
595
+ const t = readOwnDataValue(entry, 'type');
596
+ return typeof t === 'string' && HOOK_ENTRY_TYPE_WORDS.includes(t) ? { type: t } : {};
597
+ }
598
+ catch {
599
+ return {};
600
+ }
601
+ }
570
602
  function judgeableSettingsEntry(entry) {
571
603
  const type = entry.type;
572
604
  if (typeof type !== 'string' || !ENGINE_HOOK_TYPE_RUNS.has(type))
@@ -899,7 +931,7 @@ function settingsExclusionRowOf(v) {
899
931
  const REASON_TEXT = {
900
932
  projected: 'it is sent to the engine',
901
933
  shell_event: 'this client runs this event itself',
902
- no_executor: 'neither the engine nor this client runs this event',
934
+ no_executor: renderMessage(msg('config.hooks.plan.reason.noExecutor')),
903
935
  engine_not_local: 'the engine was not started by this client on this machine, so it cannot reach the plugin files',
904
936
  engine_locality_unknown: 'it is not known whether the engine was started by this client on this machine, so plugin hooks are not sent to it',
905
937
  engine_capability_absent: 'the connected engine does not support plugin hooks',
@@ -931,6 +963,7 @@ const DROP_TEXT = {
931
963
  workspace_untrusted: renderMessage(msg('config.hooks.plan.reason.closedUntrusted')),
932
964
  governance_unreadable: renderMessage(msg('config.hooks.plan.reason.closedGovernanceUnreadable')),
933
965
  policy_disable_all: renderMessage(msg('config.hooks.plan.reason.closedDisableAll')),
966
+ no_executor: REASON_TEXT.no_executor,
934
967
  };
935
968
  const SOURCE_TEXT = HOOK_SETTINGS_SOURCE_TEXT;
936
969
  export function pluginHookReasonText(reason) {
@@ -1011,6 +1044,7 @@ function namedPathsOfRow(d) {
1011
1044
  case 'workspace_untrusted':
1012
1045
  case 'governance_unreadable':
1013
1046
  case 'policy_disable_all':
1047
+ case 'no_executor':
1014
1048
  return [];
1015
1049
  }
1016
1050
  }
@@ -24,6 +24,8 @@ export function toolEndResultToUserFrame(arm) {
24
24
  }, undefined, parent === null ? 'leader' : 'subagent');
25
25
  const envelope = toolResultEnvelopeParts(outcome);
26
26
  const gateView = guardedRead(() => gateOutcomeOf(arm));
27
+ const denialKindRaw = arm._sema_denial_kind;
28
+ const denialKind = typeof denialKindRaw === 'string' ? denialKindRaw : undefined;
27
29
  const content = Array.isArray(outcome.body.content) ? outcome.body.content : arm.truncated === true ? `${text}\n…(truncated)` : text;
28
30
  return {
29
31
  type: 'user',
@@ -39,9 +41,9 @@ export function toolEndResultToUserFrame(arm) {
39
41
  ],
40
42
  },
41
43
  parent_tool_use_id: parent,
42
- ...(isCcToolDenialKind(arm._sema_denial_kind) ? { _sema_denial_kind: arm._sema_denial_kind } : {}),
44
+ ...(isCcToolDenialKind(denialKind) ? { _sema_denial_kind: denialKind } : {}),
43
45
  ...(gateView !== undefined ? { _sema_gate: gateView } : {}),
44
- ...toolResultRowFlagParts(arm, gateView),
46
+ ...toolResultRowFlagParts(arm, gateView, denialKind),
45
47
  ...(Object.hasOwn(envelope, 'tool_use_result') ? { tool_use_result: envelope.tool_use_result } : {}),
46
48
  ...(Object.hasOwn(envelope, '_sema_degraded') ? { _sema_degraded: envelope._sema_degraded } : {}),
47
49
  uuid: rowIdentity.uuid,