@sema-agent/client-core 0.98.0 → 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/CHANGELOG.md +376 -0
  2. package/README.md +61 -35
  3. package/dist/adapt/arms.js +120 -55
  4. package/dist/adapt/panelTasks.d.ts +3 -2
  5. package/dist/adapt/panelTasks.js +34 -11
  6. package/dist/adapt/textStream.d.ts +1 -1
  7. package/dist/adapt/textStream.js +51 -28
  8. package/dist/adapter/activeRunSelfHeal.d.ts +11 -3
  9. package/dist/adapter/activeRunSelfHeal.js +210 -294
  10. package/dist/adapter/downstream/eventToSdkMessage.d.ts +25 -6
  11. package/dist/adapter/downstream/eventToSdkMessage.js +176 -12
  12. package/dist/adapter/downstream/terminalToSdkResult.js +10 -0
  13. package/dist/adapter/downstream/wiringManifestView.d.ts +3 -1
  14. package/dist/adapter/downstream/wiringManifestView.js +1 -0
  15. package/dist/adapter/runStream.js +82 -40
  16. package/dist/adapter/types.d.ts +2 -0
  17. package/dist/adapter/types.js +2 -1
  18. package/dist/agentSession/engineSessionBackgroundStop.js +16 -8
  19. package/dist/agentsWireCaps.d.ts +7 -3
  20. package/dist/agentsWireCaps.js +133 -39
  21. package/dist/cloudConfigWireCaps.d.ts +10 -1
  22. package/dist/cloudConfigWireCaps.js +181 -65
  23. package/dist/compactionStatus.d.ts +34 -0
  24. package/dist/compactionStatus.js +196 -0
  25. package/dist/controlRouter.d.ts +4 -2
  26. package/dist/controlRouter.js +39 -51
  27. package/dist/coreValuePorts.d.ts +26 -1
  28. package/dist/coreValuePorts.js +10 -0
  29. package/dist/corruptSessionCopy.d.ts +2 -1
  30. package/dist/corruptSessionCopy.js +3 -3
  31. package/dist/decideFailureNote.js +3 -3
  32. package/dist/declared/supportFloorDeclared.js +1 -1
  33. package/dist/detachWire.js +3 -2
  34. package/dist/devicesFace.d.ts +34 -0
  35. package/dist/devicesFace.js +90 -0
  36. package/dist/displayUntrusted.d.ts +1 -0
  37. package/dist/displayUntrusted.js +153 -5
  38. package/dist/engineAgentPanelStore.d.ts +14 -4
  39. package/dist/engineAgentPanelStore.js +14 -0
  40. package/dist/engineCapReader.d.ts +2 -1
  41. package/dist/engineCapReader.js +7 -5
  42. package/dist/engineCapsCache.d.ts +8 -0
  43. package/dist/engineCapsCache.js +59 -6
  44. package/dist/engineCapsChange.d.ts +2 -0
  45. package/dist/engineCapsChange.js +9 -0
  46. package/dist/engineErrorCodes.d.ts +11 -2
  47. package/dist/engineErrorCodes.js +11 -12
  48. package/dist/engineRowRef.js +4 -0
  49. package/dist/engineSessionMessages.d.ts +1 -0
  50. package/dist/engineSessionMessages.js +7 -1
  51. package/dist/engineTargetSeat.d.ts +27 -0
  52. package/dist/engineTargetSeat.js +199 -0
  53. package/dist/engineToolDirectory.d.ts +3 -0
  54. package/dist/engineToolDirectory.js +4 -2
  55. package/dist/engineWireFor.js +3 -8
  56. package/dist/env/uiLanguage.d.ts +2 -1
  57. package/dist/env/uiLanguage.js +3 -2
  58. package/dist/eventLeg.d.ts +7 -5
  59. package/dist/eventLeg.js +68 -49
  60. package/dist/fileHistoryCaptureCapability.d.ts +2 -4
  61. package/dist/fileHistoryCaptureCapability.js +12 -22
  62. package/dist/fileHistoryLegNotice.d.ts +2 -1
  63. package/dist/fileHistoryLegNotice.js +3 -3
  64. package/dist/frameBinding.d.ts +3 -0
  65. package/dist/frameBinding.js +12 -0
  66. package/dist/gateOutcome.js +16 -2
  67. package/dist/gateVocabulary.d.ts +8 -7
  68. package/dist/gateVocabulary.js +71 -73
  69. package/dist/generated/engineFactTables.d.ts +18 -1
  70. package/dist/generated/engineFactTables.js +56 -0
  71. package/dist/generated/engineNoticeTables.js +12 -3
  72. package/dist/generated/settingsSchemaFns.d.ts +18 -0
  73. package/dist/generated/settingsSchemaFns.js +70 -0
  74. package/dist/generated/settingsSchemaHooksClassifier.d.ts +2 -0
  75. package/dist/generated/settingsSchemaHooksClassifier.js +254 -0
  76. package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -0
  77. package/dist/generated/settingsSchemaMcpRequestEntry.js +162 -0
  78. package/dist/generated/settingsSchemaTables.d.ts +2 -0
  79. package/dist/generated/settingsSchemaTables.js +9 -0
  80. package/dist/generated/settingsSchemaTypes.d.ts +133 -0
  81. package/dist/generated/settingsSchemaTypes.js +1 -0
  82. package/dist/hitl/allowRuleShadow.d.ts +2 -1
  83. package/dist/hitl/allowRuleShadow.js +18 -15
  84. package/dist/hitl/approvalCardReasons.d.ts +3 -2
  85. package/dist/hitl/approvalCardReasons.js +29 -30
  86. package/dist/hitl/approvalHolds.d.ts +17 -0
  87. package/dist/hitl/approvalHolds.js +114 -29
  88. package/dist/hitl/askClassPair.d.ts +2 -2
  89. package/dist/hitl/askClassPair.js +16 -0
  90. package/dist/hitl/askGateWire.js +2 -1
  91. package/dist/hitl/askSettlementLine.d.ts +14 -4
  92. package/dist/hitl/askSettlementLine.js +67 -47
  93. package/dist/hitl/attributionLadder.d.ts +23 -0
  94. package/dist/hitl/attributionLadder.js +67 -0
  95. package/dist/hitl/automaticAnswerVerdict.d.ts +2 -1
  96. package/dist/hitl/automaticAnswerVerdict.js +8 -8
  97. package/dist/hitl/crashConvergedNotice.d.ts +5 -4
  98. package/dist/hitl/crashConvergedNotice.js +28 -41
  99. package/dist/hitl/decisionLedger.d.ts +55 -0
  100. package/dist/hitl/decisionLedger.js +156 -0
  101. package/dist/hitl/decisionRefusal.d.ts +1 -0
  102. package/dist/hitl/decisionRefusal.js +1 -1
  103. package/dist/hitl/decisionSend.d.ts +113 -0
  104. package/dist/hitl/decisionSend.js +579 -0
  105. package/dist/hitl/frameRouter.js +7 -8
  106. package/dist/hitl/hitlBridge.d.ts +1 -0
  107. package/dist/hitl/hitlBridge.js +21 -9
  108. package/dist/hitl/persistedRulesWire.d.ts +6 -5
  109. package/dist/hitl/persistedRulesWire.js +55 -73
  110. package/dist/hitl/planReviewWire.d.ts +5 -0
  111. package/dist/hitl/planReviewWire.js +53 -76
  112. package/dist/hitl/sessionPolicyDeliverable.d.ts +2 -0
  113. package/dist/hitl/sessionPolicyDeliverable.js +49 -9
  114. package/dist/hitl/sessionPolicyWire.d.ts +2 -1
  115. package/dist/hitl/sessionPolicyWire.js +23 -24
  116. package/dist/hitl/suspendedReopen.d.ts +2 -1
  117. package/dist/hitl/suspendedReopen.js +7 -10
  118. package/dist/hitl/toolApprovalWire.d.ts +17 -5
  119. package/dist/hitl/toolApprovalWire.js +120 -147
  120. package/dist/hooksWireCaps.js +145 -56
  121. package/dist/host.d.ts +21 -0
  122. package/dist/hostTurnSettings.d.ts +110 -0
  123. package/dist/hostTurnSettings.js +692 -0
  124. package/dist/humanTurn.d.ts +15 -1
  125. package/dist/humanTurn.js +100 -12
  126. package/dist/humanTurnRead.d.ts +2 -0
  127. package/dist/humanTurnRead.js +22 -1
  128. package/dist/i18n/messages.en.d.ts +713 -0
  129. package/dist/i18n/messages.en.js +713 -0
  130. package/dist/i18n/messages.zh.js +854 -1
  131. package/dist/index.d.ts +45 -13
  132. package/dist/index.js +27 -9
  133. package/dist/inheritEnvCapability.d.ts +4 -2
  134. package/dist/inheritEnvCapability.js +12 -5
  135. package/dist/inheritEnvWire.d.ts +3 -2
  136. package/dist/inheritEnvWire.js +14 -16
  137. package/dist/injectionLaneNotice.d.ts +5 -0
  138. package/dist/injectionLaneNotice.js +46 -0
  139. package/dist/interactiveHalt.js +17 -6
  140. package/dist/liveModelCatalog.d.ts +2 -1
  141. package/dist/liveModelCatalog.js +14 -8
  142. package/dist/mcpPanel.d.ts +10 -1
  143. package/dist/mcpPanel.js +42 -13
  144. package/dist/mcpProbeCapability.d.ts +2 -1
  145. package/dist/mcpProbeCapability.js +7 -11
  146. package/dist/mcpProbeWire.d.ts +3 -1
  147. package/dist/mcpProbeWire.js +5 -0
  148. package/dist/mcpReconnect.js +2 -2
  149. package/dist/mcpToolToggles.d.ts +6 -0
  150. package/dist/mcpToolToggles.js +46 -0
  151. package/dist/mcpWireCaps.d.ts +43 -3
  152. package/dist/mcpWireCaps.js +194 -59
  153. package/dist/mcpWireTransport.d.ts +4 -0
  154. package/dist/mcpWireTransport.js +18 -0
  155. package/dist/memoryEntriesWire.js +5 -10
  156. package/dist/model/onboardJudgement.d.ts +93 -0
  157. package/dist/model/onboardJudgement.js +415 -0
  158. package/dist/model/tierVocabulary.d.ts +2 -0
  159. package/dist/model/tierVocabulary.js +17 -5
  160. package/dist/modelCapabilityProbe.d.ts +65 -0
  161. package/dist/modelCapabilityProbe.js +601 -0
  162. package/dist/modelIdentityResolvability.d.ts +5 -0
  163. package/dist/modelIdentityResolvability.js +49 -22
  164. package/dist/ownKey.d.ts +33 -0
  165. package/dist/ownKey.js +109 -0
  166. package/dist/panelReduce.d.ts +11 -1
  167. package/dist/panelReduce.js +95 -16
  168. package/dist/panelStateHolder.d.ts +1 -0
  169. package/dist/panelStateHolder.js +10 -1
  170. package/dist/permissionRuleIssue.d.ts +2 -1
  171. package/dist/permissionRuleIssue.js +23 -20
  172. package/dist/permissionRulesWriteCapability.d.ts +2 -1
  173. package/dist/permissionRulesWriteCapability.js +8 -6
  174. package/dist/pluginHooksWire.d.ts +46 -3
  175. package/dist/pluginHooksWire.js +220 -12
  176. package/dist/prepareRefusal.d.ts +8 -0
  177. package/dist/prepareRefusal.js +72 -0
  178. package/dist/printModelStatus.d.ts +63 -0
  179. package/dist/printModelStatus.js +155 -0
  180. package/dist/request/taskRequest.d.ts +16 -10
  181. package/dist/request/taskRequest.js +195 -58
  182. package/dist/resumeRefusalCopy.d.ts +14 -9
  183. package/dist/resumeRefusalCopy.js +89 -144
  184. package/dist/retryStatus.d.ts +55 -0
  185. package/dist/retryStatus.js +155 -19
  186. package/dist/ruleRefusalSentence.d.ts +4 -0
  187. package/dist/ruleRefusalSentence.js +58 -0
  188. package/dist/sandboxWire.d.ts +2 -1
  189. package/dist/sandboxWire.js +55 -21
  190. package/dist/scenarioRefusal.d.ts +10 -0
  191. package/dist/scenarioRefusal.js +111 -0
  192. package/dist/seam.d.ts +48 -9
  193. package/dist/seam.js +54 -5
  194. package/dist/seatContract.d.ts +272 -14
  195. package/dist/seatContract.js +711 -23
  196. package/dist/selfOrchestrationDenial.js +7 -19
  197. package/dist/sessionIdSegment.d.ts +6 -0
  198. package/dist/sessionIdSegment.js +24 -0
  199. package/dist/sessionMemoryStatus.js +6 -11
  200. package/dist/sessionPolicyCapability.d.ts +2 -1
  201. package/dist/sessionPolicyCapability.js +8 -6
  202. package/dist/settingsCredentialKeyPaths.d.ts +8 -0
  203. package/dist/settingsCredentialKeyPaths.js +219 -0
  204. package/dist/settingsHooksSalvage.d.ts +44 -0
  205. package/dist/settingsHooksSalvage.js +211 -0
  206. package/dist/skillToolListsCapability.d.ts +2 -1
  207. package/dist/skillToolListsCapability.js +8 -6
  208. package/dist/skillsWireCaps.d.ts +0 -3
  209. package/dist/skillsWireCaps.js +1 -2
  210. package/dist/sseIdleTriage.js +3 -2
  211. package/dist/subagent/engineSubagentResume.js +15 -9
  212. package/dist/subagentContentStore.d.ts +4 -0
  213. package/dist/subagentContentStore.js +157 -27
  214. package/dist/toolHistoryMismatch.js +3 -2
  215. package/dist/toolRoster.d.ts +3 -2
  216. package/dist/toolRoster.js +21 -20
  217. package/dist/toolsFlagNames.d.ts +8 -0
  218. package/dist/toolsFlagNames.js +110 -0
  219. package/dist/turnFailurePresentation.d.ts +12 -1
  220. package/dist/turnFailurePresentation.js +220 -15
  221. package/dist/usageAnalytics.js +6 -11
  222. package/dist/userAttachmentEnvelope.d.ts +1 -0
  223. package/dist/userAttachmentEnvelope.js +37 -7
  224. package/dist/userAttachmentSeat.d.ts +21 -0
  225. package/dist/userAttachmentSeat.js +75 -0
  226. package/dist/userInvokedSkillsCapability.d.ts +11 -1
  227. package/dist/userInvokedSkillsCapability.js +31 -7
  228. package/dist/wireErrorTriage.d.ts +7 -0
  229. package/dist/wireErrorTriage.js +23 -1
  230. package/dist/wireFailureShape.js +2 -1
  231. package/dist/wireRefusalCopy.d.ts +62 -16
  232. package/dist/wireRefusalCopy.js +307 -148
  233. package/dist/workflowsJournalCapability.d.ts +2 -1
  234. package/dist/workflowsJournalCapability.js +8 -6
  235. package/docs/INTEGRATION-CLIENTS.md +951 -42
  236. package/package.json +5 -5
@@ -1,7 +1,9 @@
1
1
  import { capForDisplay, escapeDisplayControlChars } from './fleetTaskDesc.js';
2
+ import { msg, renderMessage } from './i18n/translate.js';
2
3
  export const PERMISSION_RULE_ISSUE_CODES = Object.freeze([
3
4
  'invalid.empty_tool',
4
5
  'invalid.paren',
6
+ 'invalid.tool_name',
5
7
  'invalid.param_split',
6
8
  'invalid.primary_field',
7
9
  'invalid.cap_exceeded',
@@ -19,24 +21,25 @@ export const PERMISSION_RULE_ISSUE_CODES = Object.freeze([
19
21
  export const RETIRED_PERMISSION_RULE_ISSUE_CODES = Object.freeze(['unsupported.unknown_param']);
20
22
  const ISSUE_MESSAGE_MAX = 200;
21
23
  const ISSUE_SENTENCES = Object.freeze({
22
- 'invalid.empty_tool': 'this rule is invalid: it names no tool',
23
- 'invalid.paren': 'this rule is invalid: its parentheses do not parse (escape a literal one as \\( or \\))',
24
- 'invalid.param_split': 'this rule is invalid: its parentheses do not hold a parameter rule written name:pattern',
25
- 'invalid.primary_field': "this rule is invalid: it patterns the tool's primary field, which matches the whole trimmed string rather than a prefix",
26
- 'invalid.cap_exceeded': 'this rule set is invalid: it is over one of the deployment caps (rule count, rule length, pattern length, or star count); this client cannot tell which from the code alone',
27
- 'invalid.behavior': 'this rule is invalid: its behavior is neither deny nor ask, so it would never fire',
28
- 'invalid.command_rule': 'this rule is invalid: the command pattern was rejected by the command-rule validator',
29
- 'invalid.entry_type': 'this allow list is invalid: it must be a list of rule strings, and it or one of its entries is not a string',
30
- 'unsupported.allow': 'an allow rule is not supported in a rule set that only says deny or ask — a standing approval belongs on the allow list instead',
31
- 'unsupported.param_unknown': 'this parameter rule is not supported: the tool carries no such parameter, so it could never match',
32
- 'unsupported.param_non_scalar': 'this parameter rule is not supported: that parameter holds an object or an array, so it could never match',
33
- 'unsupported.tool_unknown': "this rule is not supported on this leg: the resolver it supplied does not know the tool, so the rule's face cannot be checked",
34
- 'unsupported.path_base': 'this path rule is not supported as spelled: the base it is relative to is missing or is not a usable absolute path, so it would reach nothing and is refused rather than compiled',
35
- 'unsupported.mcp_paren': 'a parenthesised parameter rule is not supported on an MCP tool name',
36
- 'unsupported.covering_paren': 'a parenthesised parameter rule is not supported on a covering protocol name — the parameter grammar is keyed by an exact tool name, so no mounted tool could ever bear it',
24
+ 'invalid.empty_tool': 'rules.issue.emptyTool',
25
+ 'invalid.paren': 'rules.issue.paren',
26
+ 'invalid.tool_name': 'rules.issue.toolName',
27
+ 'invalid.param_split': 'rules.issue.paramSplit',
28
+ 'invalid.primary_field': 'rules.issue.primaryField',
29
+ 'invalid.cap_exceeded': 'rules.issue.capExceeded',
30
+ 'invalid.behavior': 'rules.issue.behavior',
31
+ 'invalid.command_rule': 'rules.issue.commandRule',
32
+ 'invalid.entry_type': 'rules.issue.entryType',
33
+ 'unsupported.allow': 'rules.issue.allowRule',
34
+ 'unsupported.param_unknown': 'rules.issue.paramUnknown',
35
+ 'unsupported.param_non_scalar': 'rules.issue.paramNonScalar',
36
+ 'unsupported.tool_unknown': 'rules.issue.toolUnknown',
37
+ 'unsupported.path_base': 'rules.issue.pathBase',
38
+ 'unsupported.mcp_paren': 'rules.issue.mcpParen',
39
+ 'unsupported.covering_paren': 'rules.issue.coveringParen',
37
40
  });
38
- const PARAM_SPLIT_WITH_COLON = 'this rule is invalid: a parameter rule is written name:pattern, and the name before the colon or the pattern after it is empty';
39
- const PARAM_SPLIT_NO_COLON = 'this rule is invalid: there is no colon inside the parentheses, so this is not a parameter rule (a parameter rule is written name:pattern)';
41
+ const PARAM_SPLIT_WITH_COLON = 'rules.issue.paramSplitWithColon';
42
+ const PARAM_SPLIT_NO_COLON = 'rules.issue.paramSplitNoColon';
40
43
  function colonInsideParens(rule) {
41
44
  if (typeof rule !== 'string')
42
45
  return undefined;
@@ -51,7 +54,7 @@ function colonInsideParens(rule) {
51
54
  }
52
55
  return undefined;
53
56
  }
54
- export function permissionRuleIssueDetail(code, message, rule) {
57
+ export function permissionRuleIssueDetail(code, message, rule, lang) {
55
58
  const colon = code === 'invalid.param_split' ? colonInsideParens(rule) : undefined;
56
59
  const known = colon !== undefined
57
60
  ? colon ? PARAM_SPLIT_WITH_COLON : PARAM_SPLIT_NO_COLON
@@ -61,7 +64,7 @@ export function permissionRuleIssueDetail(code, message, rule) {
61
64
  : '';
62
65
  const codeWord = typeof code === 'string' && code.length > 0 ? escapeDisplayControlChars(code) : '(no code)';
63
66
  const head = known !== undefined
64
- ? known
65
- : `this rule was refused with ${codeWord}, a code newer than this client`;
67
+ ? renderMessage(msg(known), lang)
68
+ : renderMessage(msg('rules.issue.newerCode', { code: codeWord }), lang);
66
69
  return text.length > 0 ? `${head} — ${text}` : head;
67
70
  }
@@ -1,3 +1,4 @@
1
+ import type { UiLanguage } from './env/uiLanguage.js';
1
2
  export type PermissionRulesWriteReading = {
2
3
  kind: 'unobserved';
3
4
  } | {
@@ -12,4 +13,4 @@ export type PermissionRulesWriteReading = {
12
13
  export declare function projectPermissionRulesWriteCapability(caps: unknown): PermissionRulesWriteReading | undefined;
13
14
  export declare function observedPermissionRulesWrite(baseUrl?: string | undefined): PermissionRulesWriteReading;
14
15
  export declare function permissionRulesWriteAvailable(reading: PermissionRulesWriteReading): boolean;
15
- export declare function permissionRulesWriteDoctorDetail(reading: PermissionRulesWriteReading): string;
16
+ export declare function permissionRulesWriteDoctorDetail(reading: PermissionRulesWriteReading, lang?: UiLanguage): string;
@@ -1,5 +1,6 @@
1
1
  import { engineWireTarget } from './engineWireTarget.js';
2
2
  import { createEngineCapReader, unreadableReadingDetail } from './engineCapReader.js';
3
+ import { msg, renderMessage } from './i18n/translate.js';
3
4
  import { ownDataField } from './ownKey.js';
4
5
  export function projectPermissionRulesWriteCapability(caps) {
5
6
  try {
@@ -28,17 +29,18 @@ export function observedPermissionRulesWrite(baseUrl = engineWireTarget()?.baseU
28
29
  export function permissionRulesWriteAvailable(reading) {
29
30
  return reading.kind === 'present';
30
31
  }
31
- export function permissionRulesWriteDoctorDetail(reading) {
32
+ export function permissionRulesWriteDoctorDetail(reading, lang) {
33
+ const subject = msg('notice.capability.subject.permissionRulesWrite');
32
34
  switch (reading.kind) {
33
35
  case 'unreadable':
34
- return unreadableReadingDetail('permission-rule write entry');
36
+ return unreadableReadingDetail(renderMessage(subject, lang), lang);
35
37
  case 'unobserved':
36
- return 'permission-rule write entry not observed — the engine reports it on /v1/capabilities (permissionRulesWrite); this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)';
38
+ return renderMessage(msg('notice.capability.unobserved', { subject, key: 'permissionRulesWrite' }), lang);
37
39
  case 'not_reported':
38
- return 'permission-rule write entry not reported by this engine — this binary predates the entry itself; hide it rather than calling it';
40
+ return renderMessage(msg('notice.capability.permissionRulesWrite.notReported'), lang);
39
41
  case 'absent':
40
- return 'permission-rule write entry unreachable for this call — either the rules lane is not open on this deployment, or this call cannot bring a verifiable identity; the engine reports one bit for both, so this client cannot tell which';
42
+ return renderMessage(msg('notice.capability.permissionRulesWrite.absent'), lang);
41
43
  case 'present':
42
- return 'permission-rule write entry reachable for this call — a per-call attempt still answers for itself whether the direction, text, and scope are accepted';
44
+ return renderMessage(msg('notice.capability.permissionRulesWrite.present'), lang);
43
45
  }
44
46
  }
@@ -1,10 +1,11 @@
1
1
  import type { WireHooksConfig } from './finalVerifyWire.js';
2
- import type { PluginHookSource } from './host.js';
2
+ import type { PluginHookSource, SessionHookOrigin, SessionHookSource } from './host.js';
3
+ import { type HooksSalvageDrop, type HooksSalvageMask, type HooksSalvageRejection } from './settingsHooksSalvage.js';
3
4
  export declare const ENGINE_FIRED_HOOK_EVENTS: readonly ["PreToolUse", "PostToolUse", "PostToolUseFailure", "PostToolBatch", "PreCompact", "PostCompact", "Stop", "StopFailure", "UserPromptSubmit"];
4
5
  export type EngineFiredHookEvent = (typeof ENGINE_FIRED_HOOK_EVENTS)[number];
5
6
  export declare const PLUGIN_HOOK_DISPOSITIONS: readonly ["engine", "shell", "not_run"];
6
7
  export type PluginHookDisposition = (typeof PLUGIN_HOOK_DISPOSITIONS)[number];
7
- export declare const PLUGIN_HOOK_REASONS: readonly ["projected", "shell_event", "no_executor", "engine_not_local", "engine_locality_unknown", "engine_capability_absent", "engine_capability_unknown", "not_command", "sensitive_options", "engine_limit", "shell_form_user_config", "user_config_unset", "malformed_entry", "engine_hooks_off", "engine_hooks_unknown", "engine_type_skipped", "engine_llm_limit", "engine_unconfirmed", "engine_elsewhere"];
8
+ export declare const PLUGIN_HOOK_REASONS: readonly ["projected", "shell_event", "no_executor", "engine_not_local", "engine_locality_unknown", "engine_capability_absent", "engine_capability_unknown", "not_command", "sensitive_options", "engine_limit", "shell_form_user_config", "user_config_unset", "malformed_entry", "engine_hooks_off", "engine_hooks_unknown", "engine_type_skipped", "engine_llm_limit", "engine_unconfirmed", "engine_elsewhere", "hooks_module", "scope_unsupported", "source_incomplete"];
8
9
  export type PluginHookReason = (typeof PLUGIN_HOOK_REASONS)[number];
9
10
  export declare const PLUGIN_HOOK_EXCLUSION_REASONS: readonly ["workspace_untrusted", "governance_unreadable", "policy_disable_all", "startup_mode", "managed_plugins_only", "prompt_not_user_authored"];
10
11
  export type PluginHookExclusionReason = (typeof PLUGIN_HOOK_EXCLUSION_REASONS)[number];
@@ -48,6 +49,28 @@ export interface SettingsHookExclusion {
48
49
  readonly hookIndex: number;
49
50
  readonly reason: PluginHookExclusionReason;
50
51
  }
52
+ export interface SettingsHooksSalvageRow {
53
+ readonly source: HookSettingsSource;
54
+ readonly outcome: 'loaded-partial' | 'rejected';
55
+ readonly dropped: readonly HooksSalvageDrop[];
56
+ readonly rejections: readonly HooksSalvageRejection[];
57
+ readonly notice: string;
58
+ }
59
+ export interface PluginHookModuleVerdict {
60
+ readonly pluginId: string;
61
+ readonly pluginName: string;
62
+ readonly disposition: PluginHookDisposition;
63
+ readonly reason: PluginHookReason;
64
+ }
65
+ export interface SessionHookVerdict {
66
+ readonly origin: SessionHookOrigin;
67
+ readonly name: string;
68
+ readonly event: string;
69
+ readonly groupIndex: number;
70
+ readonly hookIndex: number;
71
+ readonly disposition: PluginHookDisposition;
72
+ readonly reason: PluginHookReason;
73
+ }
51
74
  export interface HooksWireEngineFacts {
52
75
  readonly ownedByThisShell: boolean | null;
53
76
  readonly pluginHooksCapable: boolean | null;
@@ -62,6 +85,11 @@ export interface HooksWirePlan {
62
85
  readonly settingsDropped: readonly SettingsHookDrop[];
63
86
  readonly settingsVerdicts: readonly SettingsHookVerdict[];
64
87
  readonly settingsExcluded: readonly SettingsHookExclusion[];
88
+ readonly settingsSalvage: readonly SettingsHooksSalvageRow[];
89
+ readonly moduleVerdicts: readonly PluginHookModuleVerdict[];
90
+ readonly sessionVerdicts: readonly SessionHookVerdict[];
91
+ readonly sessionReader: PluginHookReaderState;
92
+ readonly closedBy: Extract<PluginHookExclusionReason, 'workspace_untrusted' | 'governance_unreadable' | 'policy_disable_all'> | null;
65
93
  readonly wire: WireHooksConfig | undefined;
66
94
  }
67
95
  export interface HooksWirePlanOptions {
@@ -70,7 +98,9 @@ export interface HooksWirePlanOptions {
70
98
  readonly shellHookEvents?: readonly string[];
71
99
  readonly shellHookTypesByEvent?: Readonly<Record<string, readonly string[]>>;
72
100
  readonly promptAuthoredByUser?: boolean;
101
+ readonly suppressedLocally?: readonly string[];
73
102
  }
103
+ export declare function hooksWirePlanShapeOf(v: unknown): 'plan' | 'not' | 'threw';
74
104
  export declare const ENGINE_HOOK_LIMITS: {
75
105
  readonly maxGroupsPerEvent: 16;
76
106
  readonly maxEntriesPerEvent: 32;
@@ -102,15 +132,27 @@ export interface PluginHookDecisionInput {
102
132
  readonly shellRuns: ShellRunsHook;
103
133
  readonly promptAuthoredByUser: boolean;
104
134
  readonly occupied: ReadonlyMap<string, EventOccupancy>;
135
+ readonly suppressedLocally?: ReadonlySet<string>;
105
136
  }
106
137
  export interface PluginHookDecision {
107
138
  readonly verdicts: readonly PluginHookVerdict[];
139
+ readonly modules: readonly PluginHookModuleVerdict[];
108
140
  readonly excluded: readonly PluginHookExclusion[];
109
141
  readonly projected: ReadonlyMap<string, readonly WireHookGroup[]>;
110
142
  readonly unreadable: number;
111
143
  }
112
144
  export declare function decidePluginHooks(input: PluginHookDecisionInput): PluginHookDecision;
113
- export declare function filterSettingsHookGroups(source: HookSettingsSource, event: string, groups: readonly WireHookGroup[], execFormCapable: boolean, shellRunsType: (type: string | undefined) => boolean, judge: SettingsHookJudge): SettingsHookFilterResult;
145
+ export interface SessionHookDecisionInput {
146
+ readonly sources: readonly SessionHookSource[];
147
+ readonly engine: HooksWireEngineFacts;
148
+ readonly shellRuns: ShellRunsHook;
149
+ readonly promptAuthoredByUser: boolean;
150
+ }
151
+ export declare function decideSessionHooks(input: SessionHookDecisionInput): {
152
+ readonly verdicts: readonly SessionHookVerdict[];
153
+ readonly unreadable: number;
154
+ };
155
+ export declare function filterSettingsHookGroups(source: HookSettingsSource, event: string, groups: readonly WireHookGroup[], execFormCapable: boolean, shellRunsType: (type: string | undefined) => boolean, judge: SettingsHookJudge, mask?: HooksSalvageMask): SettingsHookFilterResult;
114
156
  export interface SettingsHookFilterResult {
115
157
  readonly groups: WireHookGroup[];
116
158
  readonly drops: SettingsHookDrop[];
@@ -140,4 +182,5 @@ export declare function pluginHookReasonText(reason: PluginHookReason): string;
140
182
  export declare function settingsHookDropText(reason: SettingsHookDropReason): string;
141
183
  export declare function pluginHookExecutorLabel(v: PluginHookVerdict): string;
142
184
  export declare function hooksNotRunNotice(plan: HooksWirePlan): readonly string[];
185
+ export declare function userPromptSubmitExecutorOf(plan: HooksWirePlan): 'shell' | 'engine' | 'unknown' | 'none';
143
186
  export {};
@@ -1,5 +1,6 @@
1
1
  import { displayUntrusted } from './displayUntrusted.js';
2
2
  import { SETTINGS_ENGINE_OWNED_HOOK_EVENTS } from './generated/settingsSchemaTables.js';
3
+ import { HOOK_SETTINGS_SOURCE_TEXT, hooksSalvageMaskOf, salvageSettingsHooksEntries, } from './settingsHooksSalvage.js';
3
4
  export const ENGINE_FIRED_HOOK_EVENTS = SETTINGS_ENGINE_OWNED_HOOK_EVENTS;
4
5
  export const PLUGIN_HOOK_DISPOSITIONS = ['engine', 'shell', 'not_run'];
5
6
  export const PLUGIN_HOOK_REASONS = [
@@ -22,6 +23,9 @@ export const PLUGIN_HOOK_REASONS = [
22
23
  'engine_llm_limit',
23
24
  'engine_unconfirmed',
24
25
  'engine_elsewhere',
26
+ 'hooks_module',
27
+ 'scope_unsupported',
28
+ 'source_incomplete',
25
29
  ];
26
30
  export const PLUGIN_HOOK_EXCLUSION_REASONS = [
27
31
  'workspace_untrusted',
@@ -32,6 +36,43 @@ export const PLUGIN_HOOK_EXCLUSION_REASONS = [
32
36
  'prompt_not_user_authored',
33
37
  ];
34
38
  export const SETTINGS_HOOK_DROP_REASONS = ['exec_form_unsupported', 'mcp_tool_unsupported'];
39
+ export function hooksWirePlanShapeOf(v) {
40
+ try {
41
+ if (typeof v !== 'object' || v === null || Array.isArray(v))
42
+ return 'not';
43
+ const o = v;
44
+ for (const k of PLAN_KEYS)
45
+ if (!Object.hasOwn(o, k) && !LATER_PLAN_KEYS.has(k))
46
+ return 'not';
47
+ const reader = o.pluginReader;
48
+ const engineFacts = o.engine;
49
+ const wire = o.wire;
50
+ const arrayOrAbsent = (k) => !Object.hasOwn(o, k) || Array.isArray(o[k]);
51
+ return Array.isArray(o.verdicts) &&
52
+ Array.isArray(o.excluded) &&
53
+ Array.isArray(o.settingsDropped) &&
54
+ Array.isArray(o.settingsVerdicts) &&
55
+ Array.isArray(o.settingsExcluded) &&
56
+ arrayOrAbsent('settingsSalvage') &&
57
+ arrayOrAbsent('moduleVerdicts') &&
58
+ arrayOrAbsent('sessionVerdicts') &&
59
+ (!Object.hasOwn(o, 'sessionReader') || o.sessionReader === 'present' || o.sessionReader === 'absent' || o.sessionReader === 'failed') &&
60
+ (!Object.hasOwn(o, 'closedBy') || o.closedBy === null || o.closedBy === 'workspace_untrusted' || o.closedBy === 'governance_unreadable' || o.closedBy === 'policy_disable_all') &&
61
+ (reader === 'present' || reader === 'absent' || reader === 'failed') &&
62
+ typeof engineFacts === 'object' &&
63
+ engineFacts !== null &&
64
+ (wire === undefined || (typeof wire === 'object' && wire !== null && !Array.isArray(wire)))
65
+ ? 'plan'
66
+ : 'not';
67
+ }
68
+ catch {
69
+ return 'threw';
70
+ }
71
+ }
72
+ const PLAN_KEYS = ['engine', 'pluginReader', 'verdicts', 'excluded', 'settingsDropped', 'settingsVerdicts', 'settingsExcluded', 'settingsSalvage', 'moduleVerdicts', 'sessionVerdicts', 'sessionReader', 'closedBy', 'wire'];
73
+ const LATER_PLAN_KEYS = new Set(['settingsSalvage', 'moduleVerdicts', 'sessionVerdicts', 'sessionReader', 'closedBy']);
74
+ const _planKeysPin = [true, true];
75
+ void _planKeysPin;
35
76
  export const ENGINE_HOOK_LIMITS = { maxGroupsPerEvent: 16, maxEntriesPerEvent: 32, maxCommandChars: 8192, maxLlmEntriesPerEvent: 4 };
36
77
  const ENGINE_HOOK_TYPE_RUNS = new Set(['command', 'http', 'prompt', 'agent']);
37
78
  const COMPACTION_EVENTS = new Set(['PreCompact', 'PostCompact']);
@@ -39,6 +80,8 @@ const judged = (disposition, reason) => ({ kind: 'judged', disposition, reason }
39
80
  function hookExecutorOf(x) {
40
81
  if (x.event === 'UserPromptSubmit' && !x.promptAuthoredByUser)
41
82
  return { kind: 'excluded', reason: 'prompt_not_user_authored' };
83
+ if (x.source === 'session')
84
+ return judged(x.shellRuns ? 'shell' : 'not_run', 'scope_unsupported');
42
85
  if (x.source === 'settings') {
43
86
  const why = x.powershell
44
87
  ? { reason: 'engine_type_skipped', certain: true }
@@ -172,15 +215,25 @@ export function occupancyOf(wire) {
172
215
  export function decidePluginHooks(input) {
173
216
  const verdicts = [];
174
217
  const excluded = [];
218
+ const modules = [];
175
219
  const candidates = new Map();
176
220
  let unreadable = 0;
177
221
  const fallback = (event, type, reason) => input.shellRuns(event, typeof type === 'string' ? type : undefined) ? { disposition: 'shell', reason } : { disposition: 'not_run', reason };
178
222
  input.plugins.forEach((p, pluginIndex) => {
179
- if (!isRecord(p) || !isRecord(p.hooks)) {
223
+ if (!isRecord(p)) {
224
+ unreadable++;
225
+ return;
226
+ }
227
+ if (p.hooksModule === true && input.exclusion === undefined && !(input.managedOnly && p.managed !== true)) {
228
+ modules.push({ pluginId: typeof p.id === 'string' ? p.id : '', pluginName: typeof p.name === 'string' ? p.name : '', disposition: 'not_run', reason: 'hooks_module' });
229
+ }
230
+ if (!isRecord(p.hooks)) {
180
231
  unreadable++;
181
232
  return;
182
233
  }
183
234
  const src = p;
235
+ const sv = salvageSettingsHooksEntries({ hooks: p.hooks });
236
+ const pieceMask = hooksSalvageMaskOf(sv.outcome === 'loaded-partial' ? sv.dropped : []);
184
237
  const sourceOk = nonEmptyString(p.id) && nonEmptyString(p.name) && nonEmptyString(p.root) && nonEmptyString(p.dataDir);
185
238
  const pluginId = typeof p.id === 'string' ? p.id : '';
186
239
  const pluginName = typeof p.name === 'string' ? p.name : '';
@@ -207,7 +260,15 @@ export function decidePluginHooks(input) {
207
260
  return;
208
261
  }
209
262
  const entryType = isRecord(entry) && typeof entry.type === 'string' ? entry.type : undefined;
210
- const shape = sourceOk ? classifyEntry(entry, src.options) : { kind: 'malformed' };
263
+ if (!sourceOk) {
264
+ verdicts.push({ ...key, disposition: 'not_run', reason: 'source_incomplete' });
265
+ return;
266
+ }
267
+ if (!pieceMask.eventDropped(event) && pieceMask.entryDropped(event, groupIndex, hookIndex)) {
268
+ verdicts.push({ ...key, disposition: 'not_run', reason: 'malformed_entry' });
269
+ return;
270
+ }
271
+ const shape = classifyEntry(entry, src.options);
211
272
  if (shape.kind === 'malformed') {
212
273
  verdicts.push({ ...key, disposition: 'not_run', reason: 'malformed_entry' });
213
274
  return;
@@ -299,7 +360,79 @@ export function decidePluginHooks(input) {
299
360
  }
300
361
  projected.set(event, groupsOut);
301
362
  }
302
- return { verdicts, excluded, projected, unreadable };
363
+ const suppressed = input.suppressedLocally;
364
+ if (suppressed !== undefined && suppressed.size > 0) {
365
+ for (const v of verdicts) {
366
+ if (v.disposition !== 'shell' || !suppressed.has(v.pluginId))
367
+ continue;
368
+ v.disposition = 'not_run';
369
+ if (!ENGINE_EVENTS.has(v.event))
370
+ v.reason = 'no_executor';
371
+ }
372
+ }
373
+ return { verdicts, excluded, modules, projected, unreadable };
374
+ }
375
+ const SESSION_HOOK_ORIGINS = new Set(['skill', 'agent', 'sdk_host']);
376
+ const LOCAL_REGISTRATION_TYPES = new Set(['callback', 'function']);
377
+ const isLocalRegistration = (e) => isRecord(e) && typeof e.type === 'string' && LOCAL_REGISTRATION_TYPES.has(e.type);
378
+ const JUDGE_STAND_IN = Object.freeze({ type: 'command', command: 'true' });
379
+ function sessionHooksForJudge(hooks) {
380
+ const out = {};
381
+ for (const [event, groups] of Object.entries(hooks)) {
382
+ out[event] = Array.isArray(groups)
383
+ ? groups.map((g) => isRecord(g) && Array.isArray(g.hooks) && g.hooks.some(isLocalRegistration)
384
+ ? { ...g, hooks: g.hooks.map((e) => (isLocalRegistration(e) ? JUDGE_STAND_IN : e)) }
385
+ : g)
386
+ : groups;
387
+ }
388
+ return out;
389
+ }
390
+ export function decideSessionHooks(input) {
391
+ const out = [];
392
+ let unreadable = 0;
393
+ for (const src of input.sources) {
394
+ if (!isRecord(src) || typeof src.origin !== 'string' || !SESSION_HOOK_ORIGINS.has(src.origin) || !isRecord(src.hooks)) {
395
+ unreadable++;
396
+ continue;
397
+ }
398
+ const origin = src.origin;
399
+ const name = typeof src.name === 'string' ? src.name : '';
400
+ const sv = salvageSettingsHooksEntries({ hooks: sessionHooksForJudge(src.hooks) });
401
+ const rejectedWhole = sv.outcome === 'rejected';
402
+ const mask = hooksSalvageMaskOf(sv.outcome === 'loaded-partial' ? sv.dropped : []);
403
+ for (const [event, groups] of Object.entries(src.hooks)) {
404
+ if (!Array.isArray(groups)) {
405
+ unreadable++;
406
+ continue;
407
+ }
408
+ ;
409
+ groups.forEach((group, groupIndex) => {
410
+ if (!isRecord(group) || !Array.isArray(group.hooks)) {
411
+ unreadable++;
412
+ return;
413
+ }
414
+ ;
415
+ group.hooks.forEach((entry, hookIndex) => {
416
+ const key = { origin, name, event, groupIndex, hookIndex };
417
+ const type = isRecord(entry) && typeof entry.type === 'string' ? entry.type : undefined;
418
+ if (rejectedWhole || (!mask.eventDropped(event) && mask.entryDropped(event, groupIndex, hookIndex)) || type === undefined) {
419
+ out.push({ ...key, disposition: 'not_run', reason: 'malformed_entry' });
420
+ return;
421
+ }
422
+ const shellRuns = input.shellRuns(event, type);
423
+ if (!ENGINE_EVENTS.has(event)) {
424
+ out.push({ ...key, ...(shellRuns ? { disposition: 'shell', reason: 'shell_event' } : { disposition: 'not_run', reason: 'no_executor' }) });
425
+ return;
426
+ }
427
+ const ex = hookExecutorOf({ source: 'session', event, powershell: false, llmPosition: 0, engine: input.engine, shellRuns, promptAuthoredByUser: input.promptAuthoredByUser });
428
+ if (ex.kind === 'excluded')
429
+ return;
430
+ out.push({ ...key, disposition: ex.disposition, reason: ex.reason });
431
+ });
432
+ });
433
+ }
434
+ }
435
+ return { verdicts: out, unreadable };
303
436
  }
304
437
  const SHELL_INERT_COMMAND = /^[A-Za-z0-9_./:+-]+$/;
305
438
  function carriesArgs(entry) {
@@ -312,7 +445,7 @@ function carriesArgs(entry) {
312
445
  }
313
446
  return true;
314
447
  }
315
- export function filterSettingsHookGroups(source, event, groups, execFormCapable, shellRunsType, judge) {
448
+ export function filterSettingsHookGroups(source, event, groups, execFormCapable, shellRunsType, judge, mask) {
316
449
  const drops = [];
317
450
  const verdicts = [];
318
451
  const excluded = [];
@@ -320,6 +453,8 @@ export function filterSettingsHookGroups(source, event, groups, execFormCapable,
320
453
  let stripped = 0;
321
454
  const engineEvent = ENGINE_EVENTS.has(event);
322
455
  groups.forEach((group, groupIndex) => {
456
+ if (mask !== undefined && mask.groupDropped(event, groupIndex))
457
+ return;
323
458
  if (!isRecord(group) || !Array.isArray(group.hooks)) {
324
459
  out.push(group);
325
460
  return;
@@ -327,6 +462,10 @@ export function filterSettingsHookGroups(source, event, groups, execFormCapable,
327
462
  const kept = [];
328
463
  let changed = false;
329
464
  group.hooks.forEach((entry, hookIndex) => {
465
+ if (mask !== undefined && mask.entryDropped(event, groupIndex, hookIndex)) {
466
+ changed = true;
467
+ return;
468
+ }
330
469
  const reason = !isRecord(entry)
331
470
  ? undefined
332
471
  : entry.type === 'mcp_tool'
@@ -555,7 +694,7 @@ export function settingsHooksKeptLocally(plan, event, group, opts) {
555
694
  const planRec = plan;
556
695
  if (!isRecord(planRec))
557
696
  return hooks;
558
- const excludedList = Object.hasOwn(planRec, 'settingsExcluded') ? planRec.settingsExcluded : [];
697
+ const excludedList = Object.hasOwn(planRec, 'settingsExcluded') ? planRec.settingsExcluded : undefined;
559
698
  const wire = ownField(planRec, 'wire');
560
699
  if (!Array.isArray(excludedList) || (wire !== undefined && !isRecord(wire)))
561
700
  return hooks;
@@ -665,18 +804,15 @@ const REASON_TEXT = {
665
804
  engine_llm_limit: 'the engine may skip prompt or agent hooks after the first four on one event',
666
805
  engine_unconfirmed: 'it is sent to the engine, which is expected to run it, but this client cannot confirm that',
667
806
  engine_elsewhere: 'it is not confirmed that the engine was started by this client on this machine, so this client runs it itself',
807
+ hooks_module: 'hooks registered in code by a plugin module run neither in the engine nor in this client',
808
+ scope_unsupported: 'hooks registered for a skill, an agent or by the host application are not sent to the engine, and this client does not run this event',
809
+ source_incomplete: 'the plugin was reported without its id, name, root folder or data folder',
668
810
  };
669
811
  const DROP_TEXT = {
670
812
  exec_form_unsupported: 'exec-form hooks (with "args") are sent to the engine only once it reports support for them',
671
813
  mcp_tool_unsupported: 'MCP tool hooks are not supported by the engine',
672
814
  };
673
- const SOURCE_TEXT = {
674
- policySettings: 'Managed settings',
675
- userSettings: 'User settings',
676
- projectSettings: 'Project settings',
677
- localSettings: 'Local settings',
678
- flagSettings: 'Flag settings',
679
- };
815
+ const SOURCE_TEXT = HOOK_SETTINGS_SOURCE_TEXT;
680
816
  export function pluginHookReasonText(reason) {
681
817
  return REASON_TEXT[reason];
682
818
  }
@@ -786,5 +922,77 @@ export function hooksNotRunNotice(plan) {
786
922
  const events = row.events.map((e) => noticeLabel(e)).join(', ');
787
923
  lines.push(`${SOURCE_TEXT[row.source]}: ${events} ${row.count === 1 ? 'hook' : 'hooks'} will not run — ${pluginHookReasonText(row.reason)}.`);
788
924
  }
925
+ const moduleRows = plan.moduleVerdicts;
926
+ for (const raw of Array.isArray(moduleRows) ? moduleRows : []) {
927
+ if (!isRecord(raw) || ownField(raw, 'disposition') !== 'not_run')
928
+ continue;
929
+ const reason = ownField(raw, 'reason');
930
+ const name = ownField(raw, 'pluginName');
931
+ if (typeof reason !== 'string' || !REASON_SET.has(reason))
932
+ continue;
933
+ lines.push(`Plugin "${noticeLabel(typeof name === 'string' ? name : '') || '(unnamed)'}": hooks module will not run — ${pluginHookReasonText(reason)}.`);
934
+ }
935
+ const sessionRows = plan.sessionVerdicts;
936
+ const bySession = new Map();
937
+ for (const raw of Array.isArray(sessionRows) ? sessionRows : []) {
938
+ if (!isRecord(raw) || ownField(raw, 'disposition') !== 'not_run')
939
+ continue;
940
+ const origin = ownField(raw, 'origin');
941
+ const name = ownField(raw, 'name');
942
+ const reason = ownField(raw, 'reason');
943
+ const event = ownField(raw, 'event');
944
+ if (typeof origin !== 'string' || !SESSION_HOOK_ORIGINS.has(origin) || typeof reason !== 'string' || !REASON_SET.has(reason) || typeof event !== 'string')
945
+ continue;
946
+ const nm = typeof name === 'string' ? name : '';
947
+ const k = `${origin}\u0000${nm}\u0000${reason}`;
948
+ const row = bySession.get(k) ?? { origin: origin, name: nm, reason: reason, events: [], count: 0 };
949
+ if (!row.events.includes(event))
950
+ row.events.push(event);
951
+ row.count++;
952
+ bySession.set(k, row);
953
+ }
954
+ for (const row of bySession.values()) {
955
+ const events = row.events.map((e) => noticeLabel(e)).join(', ');
956
+ const who = row.origin === 'sdk_host' ? 'Host application hooks' : `${row.origin === 'skill' ? 'Skill' : 'Agent'} "${noticeLabel(row.name) || '(unnamed)'}"`;
957
+ lines.push(`${who}: ${events} ${row.count === 1 ? 'hook' : 'hooks'} will not run — ${pluginHookReasonText(row.reason)}.`);
958
+ }
959
+ const salvageRows = plan.settingsSalvage;
960
+ for (const row of Array.isArray(salvageRows) ? salvageRows : []) {
961
+ const notice = isRecord(row) ? ownField(row, 'notice') : undefined;
962
+ if (typeof notice === 'string' && notice.length > 0)
963
+ lines.push(notice);
964
+ }
789
965
  return lines;
790
966
  }
967
+ export function userPromptSubmitExecutorOf(plan) {
968
+ try {
969
+ const p = plan;
970
+ if (!isRecord(p))
971
+ return 'unknown';
972
+ const verdicts = ownField(p, 'verdicts');
973
+ if (!Array.isArray(verdicts))
974
+ return 'unknown';
975
+ const rows = [...verdicts];
976
+ const settingsRows = ownField(p, 'settingsVerdicts');
977
+ if (!Array.isArray(settingsRows))
978
+ return 'unknown';
979
+ rows.push(...settingsRows);
980
+ const sessionRows = ownField(p, 'sessionVerdicts');
981
+ if (sessionRows !== undefined) {
982
+ if (!Array.isArray(sessionRows))
983
+ return 'unknown';
984
+ rows.push(...sessionRows);
985
+ }
986
+ const ups = rows.filter((r) => isRecord(r) && ownField(r, 'event') === 'UserPromptSubmit').map((r) => ownField(r, 'disposition'));
987
+ if (ups.length === 0)
988
+ return 'none';
989
+ if (ups.some((d) => d === 'engine'))
990
+ return 'engine';
991
+ if (ups.every((d) => d === 'shell'))
992
+ return 'shell';
993
+ return 'unknown';
994
+ }
995
+ catch {
996
+ return 'unknown';
997
+ }
998
+ }
@@ -0,0 +1,8 @@
1
+ import { CONFIG_AGENT_UNKNOWN_TOOL, CONFIG_IMAGE_INVALID, CONFIG_LEGACY_TOOL_NAME, CONFIG_TOOL_FACE_CONTROL, PROMPT_ASSEMBLED_ROLE_REFUSED, USER_ATTACHMENT_INVALID_ERROR_CODE, USER_INVOKED_SKILL_UNKNOWN_ERROR_CODE } from './engineErrorCodes.js';
2
+ export type PrepareRefusalCode = typeof CONFIG_LEGACY_TOOL_NAME | typeof CONFIG_IMAGE_INVALID | typeof CONFIG_AGENT_UNKNOWN_TOOL | typeof PROMPT_ASSEMBLED_ROLE_REFUSED | typeof USER_ATTACHMENT_INVALID_ERROR_CODE | typeof USER_INVOKED_SKILL_UNKNOWN_ERROR_CODE | typeof CONFIG_TOOL_FACE_CONTROL;
3
+ export interface PrepareRefusalNoteFacts {
4
+ readonly index?: number;
5
+ }
6
+ export declare function prepareRefusalNoteOf(code: string | undefined, facts?: PrepareRefusalNoteFacts): string | undefined;
7
+ export declare function prepareRefusalCodeOf(failure: {} | null | undefined): PrepareRefusalCode | undefined;
8
+ export declare function imageInvalidFailureNoteOf(code: string | undefined): string | undefined;
@@ -0,0 +1,72 @@
1
+ import { CONFIG_AGENT_UNKNOWN_TOOL, CONFIG_IMAGE_INVALID, CONFIG_LEGACY_TOOL_NAME, CONFIG_TOOL_FACE_CONTROL, PROMPT_ASSEMBLED_ROLE_REFUSED, USER_ATTACHMENT_INVALID_ERROR_CODE, USER_INVOKED_SKILL_UNKNOWN_ERROR_CODE, } from './engineErrorCodes.js';
2
+ import { REFUSED_NAME_CLASSES } from './hitl/refusedToolNameProse.js';
3
+ import { readOwnDataValue } from './ownKey.js';
4
+ import { effectiveWireErrorCode } from './wireErrorTriage.js';
5
+ const nonNegInt = (v) => (typeof v === 'number' && Number.isSafeInteger(v) && v >= 0 ? v : undefined);
6
+ const PREPARE_REFUSAL_NOTES = Object.freeze({
7
+ [CONFIG_LEGACY_TOOL_NAME]: () => `The engine refused this run at setup: a tool rule applied to it names a tool the engine refuses — ${REFUSED_NAME_CLASSES} (the engine's own error text names the entry). ` +
8
+ 'Every later run fails the same way until that entry is removed from wherever it is set: this session’s rule record, the settings sent with the run, a parent session’s rule record, or the deployment’s own tool rules.',
9
+ [CONFIG_IMAGE_INVALID]: () => 'The engine refused this run at setup: an image sent with the message was not accepted — the engine’s own error text says which image and what is wrong with it. ' +
10
+ 'Remove or replace that image, then send the message again.',
11
+ [CONFIG_AGENT_UNKNOWN_TOOL]: () => 'The engine refused this run at setup: a subagent tool list (in a subagent definition sent with it, or on a delegation tool) is not a list of tool names, or has an entry that names no tool or uses a form that list does not accept — ' +
12
+ 'the engine’s own error text says which list, and which entry when it is one entry · Sending the same request again is refused the same way; fix what it names, then send it',
13
+ [PROMPT_ASSEMBLED_ROLE_REFUSED]: () => 'The engine refused this run at setup: the role text sent with it contains the engine’s own prompt sections word for word — the engine’s own error text names the field · ' +
14
+ 'Send only your own role text (the engine assembles the rest), then send it again',
15
+ [USER_ATTACHMENT_INVALID_ERROR_CODE]: (facts) => {
16
+ const index = nonNegInt(facts.index);
17
+ return index !== undefined
18
+ ? `The engine refused attachment ${index + 1} of this message: it is in a form the engine does not accept, or larger than it takes · Your draft is kept · Remove or replace that attachment, then send it again`
19
+ : 'The engine refused the files attached to this message: together with the message they are more than it can take in one request on this model, or one of them is in a form it does not accept · ' +
20
+ 'Your draft is kept · Remove some attachments or attach smaller parts, then send it again';
21
+ },
22
+ [USER_INVOKED_SKILL_UNKNOWN_ERROR_CODE]: () => 'The engine refused this run at setup: a skill named for it is not one this run has loaded, or skills are turned off for this run · ' +
23
+ 'Your draft is kept · Check the skill name, or load that skill, then send it again',
24
+ [CONFIG_TOOL_FACE_CONTROL]: () => 'The engine refused this run at setup: its tool set could not be put together as asked — usually a tool list on the request or one it inherits (allowed or excluded tools) names something no tool answers to, ' +
25
+ 'uses a form those lists do not accept, or excludes a tool this run depends on; it can also be the deployment’s own tool mounts — the engine’s own error text says what it refused and where · ' +
26
+ 'Sending the same request again is refused the same way; fix what it names (or ask whoever operates the engine, if it names the deployment’s tools), then send it',
27
+ });
28
+ function isPrepareRefusalCode(code) {
29
+ return typeof code === 'string' && Object.hasOwn(PREPARE_REFUSAL_NOTES, code);
30
+ }
31
+ export function prepareRefusalNoteOf(code, facts) {
32
+ try {
33
+ if (!isPrepareRefusalCode(code))
34
+ return undefined;
35
+ let index;
36
+ try {
37
+ index = facts === undefined || facts === null ? undefined : facts.index;
38
+ }
39
+ catch {
40
+ index = undefined;
41
+ }
42
+ const i = nonNegInt(index);
43
+ return PREPARE_REFUSAL_NOTES[code](i !== undefined ? { index: i } : {});
44
+ }
45
+ catch {
46
+ return undefined;
47
+ }
48
+ }
49
+ export function prepareRefusalCodeOf(failure) {
50
+ try {
51
+ if (typeof failure !== 'object' || failure === null)
52
+ return undefined;
53
+ const kind = readOwnDataValue(failure, 'kind');
54
+ const plane = readOwnDataValue(failure, 'plane');
55
+ if (kind === 'failed' && (plane === 'cause' || plane === 'flat')) {
56
+ const code = readOwnDataValue(failure, 'code');
57
+ return isPrepareRefusalCode(code) ? code : undefined;
58
+ }
59
+ if (readOwnDataValue(failure, 'type') === 'result') {
60
+ const code = readOwnDataValue(failure, '_sema_error_code');
61
+ return isPrepareRefusalCode(code) ? code : undefined;
62
+ }
63
+ const code = effectiveWireErrorCode(failure);
64
+ return isPrepareRefusalCode(code) ? code : undefined;
65
+ }
66
+ catch {
67
+ return undefined;
68
+ }
69
+ }
70
+ export function imageInvalidFailureNoteOf(code) {
71
+ return code === CONFIG_IMAGE_INVALID ? prepareRefusalNoteOf(code) : undefined;
72
+ }