@sema-agent/client-core 0.90.0 → 0.92.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 (172) hide show
  1. package/CHANGELOG.md +365 -0
  2. package/README.md +48 -32
  3. package/dist/adapt/arms.js +13 -12
  4. package/dist/adapt/panelTasks.d.ts +0 -2
  5. package/dist/adapt/panelTasks.js +6 -26
  6. package/dist/adapt.js +2 -2
  7. package/dist/adapter/activeRunSelfHeal.d.ts +0 -2
  8. package/dist/adapter/activeRunSelfHeal.js +5 -27
  9. package/dist/adapter/downstream/eventToSdkMessage.js +4 -2
  10. package/dist/adapter/downstream/terminalToSdkResult.js +16 -7
  11. package/dist/adapter/downstream/wiringManifestView.d.ts +6 -0
  12. package/dist/adapter/downstream/wiringManifestView.js +16 -0
  13. package/dist/adapter/runStream.js +39 -20
  14. package/dist/askSurvivesStandingAnswer.d.ts +13 -0
  15. package/dist/askSurvivesStandingAnswer.js +53 -0
  16. package/dist/attachmentSeatCapability.d.ts +11 -0
  17. package/dist/attachmentSeatCapability.js +27 -0
  18. package/dist/attachmentsWireCaps.d.ts +9 -0
  19. package/dist/attachmentsWireCaps.js +74 -21
  20. package/dist/autoModeUnavailable.js +3 -2
  21. package/dist/cloudConfigWireCaps.js +5 -4
  22. package/dist/compensations.js +13 -17
  23. package/dist/controlRouter.d.ts +32 -25
  24. package/dist/controlRouter.js +166 -63
  25. package/dist/coreValuePorts.d.ts +4 -2
  26. package/dist/coreValuePorts.js +5 -0
  27. package/dist/decideFailureNote.js +7 -3
  28. package/dist/decideReceipt.d.ts +9 -0
  29. package/dist/decideReceipt.js +62 -1
  30. package/dist/disableSkillsWire.d.ts +1 -0
  31. package/dist/disableSkillsWire.js +8 -1
  32. package/dist/displayUntrusted.js +155 -5
  33. package/dist/effectiveFacts.d.ts +14 -4
  34. package/dist/effectiveFacts.js +45 -5
  35. package/dist/engineAgentAbsence.js +2 -1
  36. package/dist/engineCapReader.js +16 -10
  37. package/dist/engineCapsCache.d.ts +5 -1
  38. package/dist/engineCapsCache.js +69 -8
  39. package/dist/engineClosedSets.d.ts +2 -0
  40. package/dist/engineClosedSets.js +3 -0
  41. package/dist/engineErrorCodes.d.ts +17 -0
  42. package/dist/engineErrorCodes.js +47 -0
  43. package/dist/engineNoticeCodes.d.ts +14 -0
  44. package/dist/engineNoticeCodes.js +71 -0
  45. package/dist/executionLaneCapability.js +2 -0
  46. package/dist/gateOutcome.js +2 -17
  47. package/dist/gateVocabulary.d.ts +2 -4
  48. package/dist/gateVocabulary.js +3 -11
  49. package/dist/generated/engineFactTables.d.ts +27 -0
  50. package/dist/generated/engineFactTables.js +128 -0
  51. package/dist/generated/engineNoticeTables.js +3 -0
  52. package/dist/generated/settingsSchemaTables.d.ts +8 -0
  53. package/dist/generated/settingsSchemaTables.js +40 -0
  54. package/dist/hitl/allowRuleShadow.d.ts +21 -0
  55. package/dist/hitl/allowRuleShadow.js +167 -0
  56. package/dist/hitl/askClassPair.d.ts +14 -0
  57. package/dist/hitl/askClassPair.js +42 -0
  58. package/dist/hitl/askGateWire.d.ts +1 -1
  59. package/dist/hitl/askGateWire.js +2 -1
  60. package/dist/hitl/askSurvivesPosture.d.ts +5 -0
  61. package/dist/hitl/askSurvivesPosture.js +17 -0
  62. package/dist/hitl/automaticAnswerVerdict.d.ts +21 -0
  63. package/dist/hitl/automaticAnswerVerdict.js +143 -0
  64. package/dist/hitl/crashConverged.d.ts +8 -0
  65. package/dist/hitl/crashConverged.js +58 -1
  66. package/dist/hitl/crashConvergedNotice.d.ts +4 -0
  67. package/dist/hitl/crashConvergedNotice.js +57 -0
  68. package/dist/hitl/frameRouter.d.ts +2 -0
  69. package/dist/hitl/frameRouter.js +66 -14
  70. package/dist/hitl/gateLedger.d.ts +6 -0
  71. package/dist/hitl/gateLedger.js +34 -5
  72. package/dist/hitl/hitlBridge.d.ts +7 -4
  73. package/dist/hitl/hitlHostSurface.d.ts +2 -0
  74. package/dist/hitl/hitlHostSurface.js +7 -0
  75. package/dist/hitl/livePendingAsk.js +24 -5
  76. package/dist/hitl/parkResolver.d.ts +7 -2
  77. package/dist/hitl/parkResolver.js +21 -7
  78. package/dist/hitl/persistedRulesWire.d.ts +4 -1
  79. package/dist/hitl/persistedRulesWire.js +9 -2
  80. package/dist/hitl/planReviewWire.d.ts +1 -1
  81. package/dist/hitl/planReviewWire.js +19 -5
  82. package/dist/hitl/replayedApproval.d.ts +21 -0
  83. package/dist/hitl/replayedApproval.js +89 -0
  84. package/dist/hitl/sessionPolicyDeliverable.js +20 -2
  85. package/dist/hitl/sessionPolicyWire.d.ts +13 -0
  86. package/dist/hitl/sessionPolicyWire.js +75 -2
  87. package/dist/hitl/suspendedReopen.js +2 -2
  88. package/dist/hitl/toolApprovalWire.d.ts +9 -1
  89. package/dist/hitl/toolApprovalWire.js +40 -40
  90. package/dist/hookMatcherVerdict.d.ts +29 -0
  91. package/dist/hookMatcherVerdict.js +266 -0
  92. package/dist/humanTurn.d.ts +91 -0
  93. package/dist/humanTurn.js +252 -0
  94. package/dist/humanTurnRead.d.ts +105 -0
  95. package/dist/humanTurnRead.js +331 -0
  96. package/dist/index.d.ts +38 -11
  97. package/dist/index.js +27 -8
  98. package/dist/inheritEnvWire.js +4 -5
  99. package/dist/liveModelCatalog.d.ts +2 -0
  100. package/dist/liveModelCatalog.js +51 -3
  101. package/dist/mcpLiveness.js +2 -1
  102. package/dist/memoryVerbsWire.d.ts +1 -1
  103. package/dist/memoryVerbsWire.js +3 -0
  104. package/dist/model/catalog.js +2 -1
  105. package/dist/model/providerPresets.d.ts +3 -1
  106. package/dist/model/tierVocabulary.d.ts +2 -1
  107. package/dist/model/tierVocabulary.js +3 -8
  108. package/dist/peerFrames.js +2 -2
  109. package/dist/permissionRuleIssue.d.ts +1 -1
  110. package/dist/permissionRuleIssue.js +31 -9
  111. package/dist/permissionsAllowGrantCapability.d.ts +2 -1
  112. package/dist/permissionsAllowGrantCapability.js +11 -2
  113. package/dist/permissionsDeclaration.d.ts +29 -0
  114. package/dist/permissionsDeclaration.js +155 -66
  115. package/dist/pluginHooksWire.js +2 -11
  116. package/dist/promptDelivery.d.ts +11 -3
  117. package/dist/promptDelivery.js +164 -29
  118. package/dist/promptProfileWireCaps.d.ts +2 -1
  119. package/dist/protocolSegment.d.ts +9 -0
  120. package/dist/protocolSegment.js +28 -0
  121. package/dist/providerRejection.d.ts +1 -0
  122. package/dist/providerRejection.js +20 -0
  123. package/dist/readDenyTiers.d.ts +10 -0
  124. package/dist/readDenyTiers.js +29 -0
  125. package/dist/request/taskRequest.js +3 -17
  126. package/dist/resumeRefusalCopy.d.ts +11 -0
  127. package/dist/resumeRefusalCopy.js +61 -4
  128. package/dist/rewindArchiveCapability.js +4 -4
  129. package/dist/rewindWireCaps.d.ts +2 -1
  130. package/dist/rewindWireCaps.js +19 -2
  131. package/dist/ruleCompileVerdict.d.ts +2 -0
  132. package/dist/ruleCompileVerdict.js +13 -0
  133. package/dist/ruleRemovalConsequence.d.ts +2 -1
  134. package/dist/runCancelContext.d.ts +12 -0
  135. package/dist/runCancelContext.js +83 -1
  136. package/dist/seam.d.ts +2 -0
  137. package/dist/seam.js +1 -1
  138. package/dist/seatContract.d.ts +2 -1
  139. package/dist/sessionMemoryStatus.d.ts +2 -4
  140. package/dist/sessionSyncFileHistoryCapability.d.ts +18 -0
  141. package/dist/sessionSyncFileHistoryCapability.js +50 -0
  142. package/dist/skillToolListsCapability.d.ts +18 -0
  143. package/dist/skillToolListsCapability.js +50 -0
  144. package/dist/sqlEngineCapability.js +1 -8
  145. package/dist/steerPriorityCapability.d.ts +18 -0
  146. package/dist/steerPriorityCapability.js +80 -0
  147. package/dist/steerReceipt.d.ts +9 -0
  148. package/dist/steerReceipt.js +25 -0
  149. package/dist/subagent/engineSubagentSteer.js +2 -9
  150. package/dist/supportFloor.js +2 -2
  151. package/dist/taskRequestWords.d.ts +7 -1
  152. package/dist/taskRequestWords.js +18 -8
  153. package/dist/toolHistoryBadTurn.d.ts +1 -0
  154. package/dist/toolHistoryBadTurn.js +89 -0
  155. package/dist/toolHistoryMismatch.d.ts +2 -1
  156. package/dist/toolHistoryMismatch.js +19 -6
  157. package/dist/toolResult.d.ts +8 -0
  158. package/dist/toolResult.js +47 -3
  159. package/dist/types/engineState.d.ts +2 -12
  160. package/dist/userAttachmentEnvelope.d.ts +61 -0
  161. package/dist/userAttachmentEnvelope.js +204 -0
  162. package/dist/userInvokedSkillsCapability.d.ts +18 -0
  163. package/dist/userInvokedSkillsCapability.js +50 -0
  164. package/dist/wireFailureShape.d.ts +3 -0
  165. package/dist/wireFailureShape.js +8 -0
  166. package/dist/wireRefusalCopy.d.ts +6 -0
  167. package/dist/wireRefusalCopy.js +26 -3
  168. package/dist/workflowsJournalCapability.d.ts +18 -0
  169. package/dist/workflowsJournalCapability.js +50 -0
  170. package/dist/writeProtectionCapability.d.ts +2 -4
  171. package/docs/INTEGRATION-CLIENTS.md +1489 -16
  172. package/package.json +3 -3
@@ -4,6 +4,10 @@ import { isEngineRefusedToolName } from './sessionPolicyDeliverable.js';
4
4
  import { RULES_LEGACY_TOOL_NAME_WRITE_NOTE } from './refusedToolNameProse.js';
5
5
  import { RULES_LEGACY_TOOL_NAME, isRulesRefusalCode } from '../engineErrorCodes.js';
6
6
  import { rosterMountedNames } from './rosterMountedNames.js';
7
+ import { CORE_RULE_TOOL_NAME_VERDICTS } from '../generated/engineFactTables.js';
8
+ function isLegacyToolNameVerdict(v) {
9
+ return typeof v === 'string' && v !== 'current' && CORE_RULE_TOOL_NAME_VERDICTS.includes(v);
10
+ }
7
11
  const RULE_FIELD_PRESENCE = {
8
12
  toolAllow: true,
9
13
  toolDeny: true,
@@ -14,7 +18,7 @@ const RULE_FIELD_PRESENCE = {
14
18
  export const SESSION_POLICY_RULE_FIELDS = Object.freeze(Object.keys(RULE_FIELD_PRESENCE));
15
19
  const MAX_RULE_ENTRIES = 10_000;
16
20
  export function classifySessionPolicyFailure(e) {
17
- const { status, errorCode, message, bodyField, bodyName } = wireFailureShapeOf(e);
21
+ const { status, errorCode, message, bodyField, bodyName, bodyVerdict, bodyCurrentName } = wireFailureShapeOf(e);
18
22
  if (errorCode === undefined || errorCode === '')
19
23
  return { kind: 'no-verdict', message };
20
24
  if (errorCode === RULES_LEGACY_TOOL_NAME) {
@@ -23,6 +27,8 @@ export function classifySessionPolicyFailure(e) {
23
27
  message,
24
28
  ...(bodyField === 'toolDeny' || bodyField === 'toolAllow' ? { field: bodyField } : {}),
25
29
  ...(bodyName !== undefined && bodyName !== '' ? { name: bodyName } : {}),
30
+ ...(isLegacyToolNameVerdict(bodyVerdict) ? { verdict: bodyVerdict } : {}),
31
+ ...(bodyCurrentName !== undefined && bodyCurrentName !== '' ? { currentName: bodyCurrentName } : {}),
26
32
  };
27
33
  }
28
34
  if (isRulesRefusalCode(errorCode) && status !== undefined && status >= 400 && status < 500)
@@ -227,7 +233,7 @@ function unknownFrom(failure, leg) {
227
233
  }
228
234
  }
229
235
  function legacyToolNameOutcome(failure, prior, add) {
230
- const { field, name } = failure;
236
+ const { field, name, verdict, currentName } = failure;
231
237
  let alreadyInRecord;
232
238
  if (field !== undefined && name !== undefined) {
233
239
  const inPrior = Object.hasOwn(prior, field) ? prior[field] : undefined;
@@ -242,6 +248,8 @@ function legacyToolNameOutcome(failure, prior, add) {
242
248
  message: failure.message,
243
249
  ...(field !== undefined ? { field } : {}),
244
250
  ...(name !== undefined ? { name } : {}),
251
+ ...(verdict !== undefined ? { verdict } : {}),
252
+ ...(currentName !== undefined ? { currentName } : {}),
245
253
  ...(alreadyInRecord !== undefined ? { alreadyInRecord } : {}),
246
254
  };
247
255
  }
@@ -344,6 +352,69 @@ export function sessionPolicyTightenNotice(outcome) {
344
352
  }
345
353
  }
346
354
  }
355
+ const READ_NOTICE_GENERIC = 'This session’s rule record could not be shown, so what it holds is not known. Try reading it again.';
356
+ const READ_NOTICE_NO_TARGET = 'There is no engine connection for this session, so its rule record cannot be read and what it holds is not known. Look again once it is connected.';
357
+ function readFailureSentence(kind) {
358
+ switch (kind) {
359
+ case 'face-unavailable':
360
+ return 'This deployment does not keep per-session tool rules, so there is no session rule record to read.';
361
+ case 'loosen-forbidden':
362
+ return 'The engine answered this read with a refusal that only applies to relaxing rules, so this session’s rule record could not be read. Try again; if it keeps happening, whoever runs the engine needs to look at it.';
363
+ case 'store-corrupt':
364
+ return 'The engine could not establish which version this session’s rules are at, so it would not hand the record over. This needs an operator to look at the rule store.';
365
+ case 'cas-conflict':
366
+ return 'The engine reported a version clash on this session’s rule record, so it could not be read this time. Try again.';
367
+ case 'session-ownerless':
368
+ return 'This session has no owner, so it has no rule record to read.';
369
+ case 'session-not-found':
370
+ return 'This session is not readable from here — it may not exist, or it may belong to someone else — so its rule record cannot be shown. Check that you are connected to the engine and the session you meant, or continue in a new session.';
371
+ case 'request-rejected':
372
+ return 'The engine rejected the request to read this session’s rule record as malformed, so it could not be read. Sending the same request again will not change that; check which session was asked for.';
373
+ case 'legacy-tool-name':
374
+ return 'The engine answered this read with a refusal about a tool name it no longer accepts, so this session’s rule record could not be read. Try again; if it keeps happening, whoever runs the engine needs to look at it.';
375
+ case 'unauthorized':
376
+ return 'This call could not present a verifiable identity, so this session’s rule record could not be read.';
377
+ case 'error':
378
+ return 'The engine refused to hand over this session’s rule record for a reason this version has no wording for, so it could not be read. Try again; if it keeps happening, whoever runs the engine needs to look at it.';
379
+ case 'no-verdict':
380
+ return 'No clear answer came back from the engine when this session’s rule record was read, so what it holds is not known — that is not the same as holding nothing. Try again.';
381
+ }
382
+ }
383
+ function readMalformedSentence(why) {
384
+ switch (why) {
385
+ case 'envelope':
386
+ return 'The engine answered, but not in a form this version can read as a session rule record, so what the record holds is not known. Try again; if it keeps happening, this version and the engine may be out of step.';
387
+ case 'rev':
388
+ return 'The engine’s answer for this session’s rule record carried no usable version, so this version will not show it — a record it cannot place may still hold restrictions that are in effect. Try again.';
389
+ case 'rule_field':
390
+ return 'One of the lists in this session’s rule record came back in a form this version cannot read, so none of the record is shown rather than part of it — a partly shown record could leave out restrictions that are in effect.';
391
+ }
392
+ }
393
+ function ownRead(o, key) {
394
+ const d = Object.getOwnPropertyDescriptor(o, key);
395
+ return d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
396
+ }
397
+ export function sessionPolicyReadNotice(outcome) {
398
+ try {
399
+ if (outcome === null || typeof outcome !== 'object')
400
+ return READ_NOTICE_GENERIC;
401
+ const arm = ownRead(outcome, 'kind');
402
+ if (arm === 'failed') {
403
+ const failure = ownRead(outcome, 'failure');
404
+ if (failure === null || typeof failure !== 'object')
405
+ return READ_NOTICE_GENERIC;
406
+ return readFailureSentence(ownRead(failure, 'kind')) ?? READ_NOTICE_GENERIC;
407
+ }
408
+ if (arm === 'malformed')
409
+ return readMalformedSentence(ownRead(outcome, 'why')) ?? READ_NOTICE_GENERIC;
410
+ if (arm === 'no_target')
411
+ return READ_NOTICE_NO_TARGET;
412
+ return READ_NOTICE_GENERIC;
413
+ }
414
+ catch {
415
+ return READ_NOTICE_GENERIC;
416
+ }
417
+ }
347
418
  function withoutRefusedEntries(prior, mounted) {
348
419
  const REFUSABLE_NAME_FIELDS = ['toolDeny', 'toolAllow'];
349
420
  const isRefusable = (f) => REFUSABLE_NAME_FIELDS.includes(f);
@@ -425,6 +496,8 @@ async function attemptRemoval(facade, sessionId, mounted, opts) {
425
496
  message: failure.message,
426
497
  ...(failure.field !== undefined ? { field: failure.field } : {}),
427
498
  ...(failure.name !== undefined ? { name: failure.name } : {}),
499
+ ...(failure.verdict !== undefined ? { verdict: failure.verdict } : {}),
500
+ ...(failure.currentName !== undefined ? { currentName: failure.currentName } : {}),
428
501
  },
429
502
  };
430
503
  }
@@ -1,4 +1,4 @@
1
- import { RESUME_REOPEN_CODES, RESUME_ENV_FAILED, RESUME_TOOL_UNAVAILABLE } from '../engineErrorCodes.js';
1
+ import { RESUME_REOPEN_CODES, RESUME_ENV_FAILED, RESUME_TOOL_UNAVAILABLE, refusalSaysStillPending } from '../engineErrorCodes.js';
2
2
  const UNSTATED = Object.freeze({ kind: 'unstated' });
3
3
  const NOT_REOPENED = Object.freeze({ kind: 'not_reopened' });
4
4
  export function suspendedReopenOf(ev) {
@@ -25,7 +25,7 @@ export function resumeReopenFromError(err) {
25
25
  const code = err.errorCode;
26
26
  if (typeof code !== 'string' || !RESUME_REOPEN_CODES.includes(code))
27
27
  return null;
28
- if (err.retriable !== true)
28
+ if (!refusalSaysStillPending(err))
29
29
  return null;
30
30
  const reason = code.slice('resume.'.length);
31
31
  return { code, reason, retriable: true };
@@ -1,4 +1,5 @@
1
1
  import { type GateCurrentPending, type HitlFailureStage, type HitlSafetyCode, type HitlClientLike, type DecideOperationObserver } from './hitlBridge.js';
2
+ import { type AllowRuleShadowedWireMember, type ApprovalAllowRuleShadow } from './allowRuleShadow.js';
2
3
  import { type RuleStoreUnreadableKind } from '../gateVocabulary.js';
3
4
  import { type ApprovalMandateWord } from '../gateVocabulary.js';
4
5
  import type { ToolPermissionRequest } from '../seatContract.js';
@@ -37,6 +38,7 @@ export type FsApprovalOutcome = {
37
38
  reason: string;
38
39
  code?: 'no_pending' | undefined;
39
40
  errorCode?: string | undefined;
41
+ retriable?: true;
40
42
  safetyCode?: HitlSafetyCode | undefined;
41
43
  retryExhausted?: true;
42
44
  currentPending?: GateCurrentPending | undefined;
@@ -130,7 +132,10 @@ export interface ApprovalCardRequest {
130
132
  inputHasBidi?: true;
131
133
  hasBidiControls?: true;
132
134
  persistedRuleShadowed?: string;
135
+ allowRuleShadow?: ApprovalAllowRuleShadow;
133
136
  probeCause?: unknown;
137
+ probeReason?: string;
138
+ rememberRefused?: true;
134
139
  ruleEvidence?: unknown;
135
140
  requiresRealApproval?: true;
136
141
  irreversibleParkGate?: true;
@@ -179,7 +184,10 @@ export interface ToolApprovalFrame {
179
184
  denialLimitFallback?: DenialLimitFallback;
180
185
  ruleStoreUnreadable?: string;
181
186
  persistedRuleShadowed?: string;
187
+ allowRuleShadowed?: readonly AllowRuleShadowedWireMember[];
182
188
  probeCause?: unknown;
189
+ probeReason?: string;
190
+ rememberRefused?: true;
183
191
  ruleEvidence?: unknown;
184
192
  requiresRealApproval?: true;
185
193
  mandated?: true;
@@ -204,7 +212,7 @@ export interface ToolApprovalDelegation {
204
212
  depth: number;
205
213
  agentName?: string;
206
214
  }
207
- export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "ruleStoreUnreadable", "readRootCandidate", "mandated", "mandate", "origin", "askClass", "classes", "inputHasBidi", "parked", "persistedRuleShadowed", "probeCause", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
215
+ export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "ruleStoreUnreadable", "readRootCandidate", "mandated", "mandate", "origin", "askClass", "classes", "inputHasBidi", "parked", "persistedRuleShadowed", "allowRuleShadowed", "probeCause", "probeReason", "rememberRefused", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
208
216
  export interface ApprovalMandatedFacts {
209
217
  readonly mandated?: true;
210
218
  readonly ruleOffersAbsence?: string;
@@ -2,14 +2,16 @@ import { APPROVAL_CARD_PORT_KEY_MISMATCH_REASON, DEFAULT_DENY_REASON, DecideTran
2
2
  import { askParkRowArm } from './askParkRowRouting.js';
3
3
  import { hostLog } from '../host.js';
4
4
  import { readOwnNonEmptyString } from '../ownKey.js';
5
+ import { readAskClassPair } from './askClassPair.js';
6
+ import { legacyShadowedRuleTextOf, readAllowRuleShadowFromWire } from './allowRuleShadow.js';
5
7
  import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
6
8
  import { readEngineActiveBgTasks } from '../fleet/fleetLedger.js';
7
9
  import { observeCancelByDeny, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
8
10
  import { approvalCallKey, liveFrameCallKey } from './gateIdentity.js';
9
- import { isRuleStoreUnreadableKind } from '../gateVocabulary.js';
10
11
  import { isApprovalMandateWord } from '../gateVocabulary.js';
11
12
  import { denyAttributionRefusalFromError } from '../wireRefusalCopy.js';
12
13
  import { decideFailureNote } from '../decideFailureNote.js';
14
+ import { refusalSaysStillPending } from '../engineErrorCodes.js';
13
15
  import { readDecideReceipt } from '../decideReceipt.js';
14
16
  import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, PERSISTED_RULE_MANDATES, } from '@sema-agent/sdk';
15
17
  export function isFsApprovalGate(gate) {
@@ -190,9 +192,9 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
190
192
  const callKey = approvalCallKey(gatedCallId, taskId);
191
193
  const ruleOffersReadOnly = readRuleOfferSupply(pending.ruleOffers, pending.ruleSuggestions);
192
194
  const durableProbeCause = pending.riskDescriptor?.probeCause;
193
- const durableShadowedRule = pending.riskDescriptor?.shadowedRule;
195
+ const durableProbeReason = readProbeReason(pending.riskDescriptor);
196
+ const durableShadow = readAllowRuleShadowFromWire(pending, 'row');
194
197
  const durableRuleOffersAbsence = pending.ruleOffersAbsence;
195
- const durableRuleStoreUnreadable = readRuleStoreUnreadable(pending.ruleStoreUnreadable);
196
198
  deps.onPresented?.();
197
199
  const card = await surfaceApprovalCard({
198
200
  toolName,
@@ -215,12 +217,10 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
215
217
  ...(pending.hasBidiControls === true ? { hasBidiControls: true } : {}),
216
218
  ...(ruleOffersReadOnly !== undefined ? { ruleOffersReadOnly } : {}),
217
219
  ...(isWireRecordCarrier(durableProbeCause) ? { probeCause: durableProbeCause } : {}),
218
- ...(durableRuleStoreUnreadable !== undefined
219
- ? { ruleStoreUnreadable: durableRuleStoreUnreadable }
220
- : {}),
221
- ...(typeof durableShadowedRule === 'string' && durableShadowedRule.trim() !== ''
222
- ? { persistedRuleShadowed: durableShadowedRule }
223
- : {}),
220
+ ...(durableProbeReason !== undefined ? { probeReason: durableProbeReason } : {}),
221
+ ...(readRememberRefused(pending) ? { rememberRefused: true } : {}),
222
+ ...(durableShadow !== undefined ? { allowRuleShadow: durableShadow } : {}),
223
+ ...((t) => (t !== undefined ? { persistedRuleShadowed: t } : {}))(legacyShadowedRuleTextOf(durableShadow)),
224
224
  ...(typeof durableRuleOffersAbsence === 'string' && durableRuleOffersAbsence !== ''
225
225
  ? { ruleOffersAbsence: durableRuleOffersAbsence }
226
226
  : {}),
@@ -279,6 +279,7 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
279
279
  catch (e) {
280
280
  const currentPending = readDecideCurrentPending(e);
281
281
  const wireCode = readWireErrorCode(e);
282
+ const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
282
283
  const safetyCode = e instanceof HitlSafetyError ? e.code : undefined;
283
284
  return {
284
285
  kind: 'failed',
@@ -288,6 +289,7 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
288
289
  ...(e instanceof DecideTransportRetryExhaustedError ? { retryExhausted: true } : {}),
289
290
  ...(currentPending !== undefined ? { currentPending } : {}),
290
291
  ...(wireCode !== undefined ? { errorCode: wireCode } : {}),
292
+ ...(wireRetriable ? { retriable: true } : {}),
291
293
  ...(safetyCode !== undefined ? { safetyCode } : {}),
292
294
  };
293
295
  }
@@ -335,6 +337,7 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
335
337
  catch (e) {
336
338
  const currentPending = readDecideCurrentPending(e);
337
339
  const wireCode = readWireErrorCode(e);
340
+ const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
338
341
  const safetyCode = e instanceof HitlSafetyError ? e.code : undefined;
339
342
  const attributionRefusal = denyAttributionRefusalFromError(e, lastSendCarriedAttribution);
340
343
  return {
@@ -345,6 +348,7 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
345
348
  ...(e instanceof DecideTransportRetryExhaustedError ? { retryExhausted: true } : {}),
346
349
  ...(currentPending !== undefined ? { currentPending } : {}),
347
350
  ...(wireCode !== undefined ? { errorCode: wireCode } : {}),
351
+ ...(wireRetriable ? { retriable: true } : {}),
348
352
  ...(safetyCode !== undefined ? { safetyCode } : {}),
349
353
  ...(attributionRefusal !== null ? { denyAttributionRefusal: attributionRefusal } : {}),
350
354
  };
@@ -392,7 +396,10 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
392
396
  'inputHasBidi',
393
397
  'parked',
394
398
  'persistedRuleShadowed',
399
+ 'allowRuleShadowed',
395
400
  'probeCause',
401
+ 'probeReason',
402
+ 'rememberRefused',
396
403
  'ruleEvidence',
397
404
  'requiresRealApproval',
398
405
  'expiresInMs',
@@ -437,30 +444,6 @@ export function toolPermissionRequestAskBits(card) {
437
444
  ...(askClassPair ?? {}),
438
445
  };
439
446
  }
440
- function readAskClassPair(v) {
441
- if (typeof v !== 'object' || v === null)
442
- return undefined;
443
- const askClass = readOwnNonEmptyString(v, 'askClass');
444
- if (askClass === undefined || !Object.hasOwn(v, 'classes'))
445
- return undefined;
446
- const raw = v.classes;
447
- if (!Array.isArray(raw) || raw.length === 0)
448
- return undefined;
449
- const classes = [];
450
- const seen = new Set();
451
- for (let i = 0; i < raw.length; i++) {
452
- if (!Object.hasOwn(raw, i))
453
- return undefined;
454
- const c = raw[i];
455
- if (typeof c !== 'string' || c === '' || seen.has(c))
456
- return undefined;
457
- seen.add(c);
458
- classes.push(c);
459
- }
460
- if (classes[0] !== askClass)
461
- return undefined;
462
- return { askClass, classes: Object.freeze(classes) };
463
- }
464
447
  function hasOwnStrictTrue(v, key) {
465
448
  return Object.hasOwn(v, key) && v[key] === true;
466
449
  }
@@ -644,6 +627,25 @@ function pathFromGateMessage(message) {
644
627
  return undefined;
645
628
  return body.slice(0, close);
646
629
  }
630
+ function readProbeReason(v) {
631
+ try {
632
+ if (typeof v !== 'object' || v === null || !Object.hasOwn(v, 'probeReason'))
633
+ return undefined;
634
+ const raw = v.probeReason;
635
+ return typeof raw === 'string' && raw.trim() !== '' ? raw : undefined;
636
+ }
637
+ catch {
638
+ return undefined;
639
+ }
640
+ }
641
+ function readRememberRefused(v) {
642
+ try {
643
+ return typeof v === 'object' && v !== null && Object.hasOwn(v, 'rememberRefused') && v.rememberRefused === true;
644
+ }
645
+ catch {
646
+ return false;
647
+ }
648
+ }
647
649
  function isNonNegativeFinite(v) {
648
650
  return typeof v === 'number' && Number.isFinite(v) && v >= 0;
649
651
  }
@@ -669,9 +671,6 @@ export function readDenialLimitFallback(v) {
669
671
  autoDenyAfterMs: d.autoDenyAfterMs,
670
672
  };
671
673
  }
672
- function readRuleStoreUnreadable(v) {
673
- return isRuleStoreUnreadableKind(v) ? v : undefined;
674
- }
675
674
  function isRuleOfferMatch(v) {
676
675
  return typeof v === 'string' && RULE_OFFER_MATCHES.includes(v);
677
676
  }
@@ -857,6 +856,7 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
857
856
  const argsNotRealInput = argsFallback || lane?.argsUnavailable === true;
858
857
  const ruleOffers = readRuleOfferSupply(frame.ruleOffers, frame.ruleSuggestions);
859
858
  const denialLimitFallback = readDenialLimitFallback(frame.denialLimitFallback);
859
+ const frameShadow = readAllowRuleShadowFromWire(frame, 'frame');
860
860
  const card = await surfaceApprovalCard({
861
861
  toolName,
862
862
  args: args,
@@ -883,10 +883,11 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
883
883
  ? { delegation: frame.delegation }
884
884
  : {}),
885
885
  ...(ruleOffers !== undefined ? { ruleOffers } : {}),
886
- ...(typeof frame.persistedRuleShadowed === 'string' && frame.persistedRuleShadowed.trim() !== ''
887
- ? { persistedRuleShadowed: frame.persistedRuleShadowed }
888
- : {}),
886
+ ...((sh) => (sh !== undefined ? { allowRuleShadow: sh } : {}))(frameShadow),
887
+ ...((t) => (t !== undefined ? { persistedRuleShadowed: t } : {}))(legacyShadowedRuleTextOf(frameShadow)),
889
888
  ...(isWireRecordCarrier(frame.probeCause) ? { probeCause: frame.probeCause } : {}),
889
+ ...((r) => (r !== undefined ? { probeReason: r } : {}))(readProbeReason(frame)),
890
+ ...(readRememberRefused(frame) ? { rememberRefused: true } : {}),
890
891
  ...(isWireRecordCarrier(frame.ruleEvidence) ? { ruleEvidence: frame.ruleEvidence } : {}),
891
892
  ...(frame.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
892
893
  ...(hasOwnStrictTrue(frame, 'mandated') ? { mandated: true } : {}),
@@ -897,7 +898,6 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
897
898
  ? { ruleOffersAbsence: frame.ruleOffersAbsence }
898
899
  : {}),
899
900
  ...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
900
- ...((k) => (k !== undefined ? { ruleStoreUnreadable: k } : {}))(readRuleStoreUnreadable(frame.ruleStoreUnreadable)),
901
901
  ...((o) => (o !== undefined ? { origin: o } : {}))(readOwnNonEmptyString(frame, 'origin')),
902
902
  ...(readAskClassPair(frame) ?? {}),
903
903
  });
@@ -0,0 +1,29 @@
1
+ export type HookMatcherUnjudgedReason = 'event_out_of_scope' | 'not_a_string' | 'oversize' | 'invalid_pattern' | 'pattern_unsupported' | 'over_budget';
2
+ export type HookMatcherVerdict = {
3
+ readonly kind: 'matchesAll';
4
+ } | {
5
+ readonly kind: 'accepted';
6
+ readonly reading: 'names';
7
+ readonly names: readonly string[];
8
+ readonly aliases: readonly {
9
+ readonly alias: string;
10
+ readonly canonical: string;
11
+ }[];
12
+ } | {
13
+ readonly kind: 'accepted';
14
+ readonly reading: 'pattern';
15
+ readonly aliases: readonly {
16
+ readonly alias: string;
17
+ readonly canonical: string;
18
+ }[];
19
+ } | {
20
+ readonly kind: 'refused';
21
+ readonly reason: 'alias_without_canonical';
22
+ readonly reading: 'names' | 'pattern';
23
+ readonly alias: string;
24
+ readonly canonical: string;
25
+ } | {
26
+ readonly kind: 'unjudged';
27
+ readonly reason: HookMatcherUnjudgedReason;
28
+ };
29
+ export declare function hookMatcherVerdict(event: string, matcher: unknown): HookMatcherVerdict;
@@ -0,0 +1,266 @@
1
+ import { HOOK_TOOL_ALIASES } from './hookToolAliases.js';
2
+ import { SETTINGS_TOOL_NAME_MATCHER_EVENTS } from './generated/settingsSchemaTables.js';
3
+ const TOOL_NAME_MATCHER_EVENTS = new Set(SETTINGS_TOOL_NAME_MATCHER_EVENTS);
4
+ const MATCHER_MAX_CHARS = 8192;
5
+ const PATTERN_WORK_BUDGET = 10_000_000;
6
+ const PATTERN_MAX_DEPTH = 32;
7
+ const COUNTED_REPEAT_MAX = 1000;
8
+ function unjudged(reason) {
9
+ return Object.freeze({ kind: 'unjudged', reason });
10
+ }
11
+ function refused(reading, p) {
12
+ return Object.freeze({ kind: 'refused', reason: 'alias_without_canonical', reading, alias: p.alias, canonical: p.canonical });
13
+ }
14
+ function aliasPairs() {
15
+ return Object.entries(HOOK_TOOL_ALIASES).map(([alias, canonical]) => ({ alias, canonical }));
16
+ }
17
+ function literalNames(matcher) {
18
+ const names = [];
19
+ let first = -1;
20
+ let last = -1;
21
+ for (let i = 0; i <= matcher.length; i++) {
22
+ const c = i < matcher.length ? matcher.charCodeAt(i) : 0x7c;
23
+ if (c === 0x7c || c === 0x2c) {
24
+ if (first !== -1)
25
+ names.push(matcher.slice(first, last + 1));
26
+ first = -1;
27
+ last = -1;
28
+ continue;
29
+ }
30
+ if (c === 0x20)
31
+ continue;
32
+ const nameChar = (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || c === 0x5f || c === 0x2d;
33
+ if (!nameChar)
34
+ return undefined;
35
+ if (first === -1)
36
+ first = i;
37
+ last = i;
38
+ }
39
+ return names;
40
+ }
41
+ const UNSUPPORTED = Symbol('unsupported');
42
+ const SINGLE_CHAR_ESCAPES = 'dDwWsStnrfv^$\\.*+?()[]{}|/-';
43
+ function patternBound(src, n) {
44
+ let i = 0;
45
+ let depth = 0;
46
+ const fail = () => {
47
+ throw UNSUPPORTED;
48
+ };
49
+ const quantifier = () => {
50
+ const c = src[i];
51
+ let lo;
52
+ let hi;
53
+ if (c === '*') {
54
+ lo = 0;
55
+ hi = Infinity;
56
+ i++;
57
+ }
58
+ else if (c === '+') {
59
+ lo = 1;
60
+ hi = Infinity;
61
+ i++;
62
+ }
63
+ else if (c === '?') {
64
+ lo = 0;
65
+ hi = 1;
66
+ i++;
67
+ }
68
+ else if (c === '{') {
69
+ const q = /^\{(\d{1,4})(?:(,)(\d{0,4}))?\}/.exec(src.slice(i, i + 12));
70
+ if (q === null)
71
+ return fail();
72
+ lo = Number(q[1]);
73
+ hi = q[2] === undefined ? lo : q[3] === '' || q[3] === undefined ? Infinity : Number(q[3]);
74
+ if (lo > COUNTED_REPEAT_MAX || (hi !== Infinity && hi > COUNTED_REPEAT_MAX) || hi < lo)
75
+ fail();
76
+ i += q[0].length;
77
+ }
78
+ else
79
+ return undefined;
80
+ if (src[i] === '?')
81
+ i++;
82
+ return { lo, hi };
83
+ };
84
+ const term = () => {
85
+ const c = src[i];
86
+ if (c === '^' || c === '$') {
87
+ i++;
88
+ if (quantifier() !== undefined)
89
+ fail();
90
+ return [1, 1];
91
+ }
92
+ if (c === '\\' && (src[i + 1] === 'b' || src[i + 1] === 'B')) {
93
+ i += 2;
94
+ if (quantifier() !== undefined)
95
+ fail();
96
+ return [1, 1];
97
+ }
98
+ let paths = 1;
99
+ let steps = 1;
100
+ let single = true;
101
+ if (c === '(') {
102
+ i++;
103
+ if (src[i] === '?') {
104
+ if (src[i + 1] === ':')
105
+ i += 2;
106
+ else {
107
+ const named = /^\?<([A-Za-z_$][A-Za-z0-9_$]*)>/.exec(src.slice(i, i + 80));
108
+ if (named === null)
109
+ fail();
110
+ else
111
+ i += named[0].length;
112
+ }
113
+ }
114
+ if (++depth > PATTERN_MAX_DEPTH)
115
+ fail();
116
+ [paths, steps] = disjunction();
117
+ if (src[i] !== ')')
118
+ fail();
119
+ i++;
120
+ depth--;
121
+ single = false;
122
+ }
123
+ else if (c === '[') {
124
+ i++;
125
+ if (src[i] === '^')
126
+ i++;
127
+ for (;;) {
128
+ if (i >= src.length)
129
+ fail();
130
+ const d = src[i];
131
+ if (d === ']') {
132
+ i++;
133
+ break;
134
+ }
135
+ i += d === '\\' ? 2 : 1;
136
+ }
137
+ }
138
+ else if (c === '.')
139
+ i++;
140
+ else if (c === '\\') {
141
+ const e = src[i + 1];
142
+ if (e === undefined || !SINGLE_CHAR_ESCAPES.includes(e))
143
+ fail();
144
+ i += 2;
145
+ }
146
+ else if (c === undefined || '*+?{}]|)'.includes(c))
147
+ fail();
148
+ else
149
+ i++;
150
+ const q = quantifier();
151
+ if (q === undefined)
152
+ return [paths, steps];
153
+ if (single) {
154
+ if (q.lo > n)
155
+ return [1, n + 1];
156
+ const top = Math.min(q.hi, n);
157
+ return [top - q.lo + 1, top + 1];
158
+ }
159
+ const hiEff = Math.min(q.hi, Math.max(q.lo, n + 1));
160
+ if (q.lo > hiEff)
161
+ return [1, 1];
162
+ let sum = 0;
163
+ for (let k = q.lo; k <= hiEff; k++) {
164
+ sum += paths ** k;
165
+ if (sum === Infinity)
166
+ break;
167
+ }
168
+ return [sum, hiEff * steps + 1];
169
+ };
170
+ const alternative = () => {
171
+ let paths = 1;
172
+ let steps = 0;
173
+ while (i < src.length && src[i] !== '|' && src[i] !== ')') {
174
+ const [p, s] = term();
175
+ paths *= p;
176
+ steps += s;
177
+ }
178
+ return [paths, steps];
179
+ };
180
+ const disjunction = () => {
181
+ let [paths, steps] = alternative();
182
+ while (src[i] === '|') {
183
+ i++;
184
+ const [p, s] = alternative();
185
+ paths += p;
186
+ steps = Math.max(steps, s);
187
+ }
188
+ return [paths, steps];
189
+ };
190
+ try {
191
+ const r = disjunction();
192
+ if (i !== src.length)
193
+ fail();
194
+ return r;
195
+ }
196
+ catch (e) {
197
+ if (e === UNSUPPORTED)
198
+ return undefined;
199
+ throw e;
200
+ }
201
+ }
202
+ export function hookMatcherVerdict(event, matcher) {
203
+ if (typeof event !== 'string' || !TOOL_NAME_MATCHER_EVENTS.has(event))
204
+ return unjudged('event_out_of_scope');
205
+ if (matcher === undefined)
206
+ return Object.freeze({ kind: 'matchesAll' });
207
+ if (typeof matcher !== 'string')
208
+ return unjudged('not_a_string');
209
+ if (matcher.length > MATCHER_MAX_CHARS)
210
+ return unjudged('oversize');
211
+ if (matcher === '' || matcher === '*')
212
+ return Object.freeze({ kind: 'matchesAll' });
213
+ const pairs = aliasPairs();
214
+ const names = literalNames(matcher);
215
+ if (names !== undefined) {
216
+ const dead = pairs.find((p) => names.includes(p.alias) && !names.includes(p.canonical));
217
+ if (dead !== undefined)
218
+ return refused('names', dead);
219
+ return Object.freeze({
220
+ kind: 'accepted',
221
+ reading: 'names',
222
+ names: Object.freeze([...names]),
223
+ aliases: Object.freeze(pairs.filter((p) => names.includes(p.alias)).map((p) => Object.freeze({ ...p }))),
224
+ });
225
+ }
226
+ const src = `^(?:${matcher})$`;
227
+ let re;
228
+ try {
229
+ re = new RegExp(src);
230
+ }
231
+ catch {
232
+ return unjudged('invalid_pattern');
233
+ }
234
+ const subjects = pairs.flatMap((p) => [p.alias, p.canonical]);
235
+ const n = subjects.reduce((a, s) => Math.max(a, s.length), 0);
236
+ const bound = patternBound(src, n);
237
+ if (bound === undefined)
238
+ return unjudged('pattern_unsupported');
239
+ if (bound[0] * bound[1] * (n + 1) * subjects.length > PATTERN_WORK_BUDGET)
240
+ return unjudged('over_budget');
241
+ const selects = (name) => {
242
+ if (matcher === name)
243
+ return true;
244
+ try {
245
+ return re.test(name);
246
+ }
247
+ catch {
248
+ return undefined;
249
+ }
250
+ };
251
+ const selected = [];
252
+ for (const p of pairs) {
253
+ const a = selects(p.alias);
254
+ if (a === undefined)
255
+ return unjudged('invalid_pattern');
256
+ if (!a)
257
+ continue;
258
+ const c = selects(p.canonical);
259
+ if (c === undefined)
260
+ return unjudged('invalid_pattern');
261
+ if (!c)
262
+ return refused('pattern', p);
263
+ selected.push(p);
264
+ }
265
+ return Object.freeze({ kind: 'accepted', reading: 'pattern', aliases: Object.freeze(selected.map((p) => Object.freeze({ ...p }))) });
266
+ }