@sema-agent/client-core 0.83.0 → 0.83.2

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.
@@ -1,10 +1,15 @@
1
- import { engineCapNestedTrue } from './engineCapsCache.js';
1
+ import { engineCapNestedTrue, engineCapState } from './engineCapsCache.js';
2
+ import { displayUntrusted } from './displayUntrusted.js';
2
3
  import { collapseLabel } from './fleetTaskDesc.js';
3
4
  import { hostLog, hostSettings } from './host.js';
5
+ import { decidePluginHooks, filterSettingsHookGroups, occupancyOf, settingsHookDropText, } from './pluginHooksWire.js';
4
6
  import { MAX_HOOK_NOTICE_TEXT_CHARS } from './notifications.js';
5
7
  import { goalStopHookMatcher, __resetGoalStopHookForTests } from './goalStopHook.js';
6
8
  export { GOAL_STOP_HOOK_WIRE_ENV, CC_STOP_SEMANTICS_MIN_SERVER, ccStopSemanticsFromVersion, engineCcStopSemantics, isGoalStopHookWireArmed, setWireSessionStopHook, getWireSessionStopHook, getWireSessionStopHookCcSemantics, buildGoalStopHookPrompt, } from './goalStopHook.js';
7
9
  const EDITABLE_HOOK_SOURCES = ['userSettings', 'projectSettings', 'localSettings'];
10
+ const ALL_HOOK_SOURCES = ['policySettings', ...EDITABLE_HOOK_SOURCES];
11
+ const _hookSourceListsPin = [true, true];
12
+ void _hookSourceListsPin;
8
13
  function hooksLockedToPluginOnly(policyValue) {
9
14
  if (policyValue === true)
10
15
  return true;
@@ -42,19 +47,137 @@ function hooksOfSource(source) {
42
47
  return null;
43
48
  }
44
49
  }
45
- export function hooksForWire() {
50
+ let warnedNoPluginReader = new WeakSet();
51
+ const NO_PLUGIN_READER_WARNING = 'client-core: the installed settings port does not implement enabledPluginHooks() — ' +
52
+ 'plugin hooks are not sent to the engine and are not reported as not running. ' +
53
+ 'Implement SettingsPort.enabledPluginHooks() to list the enabled plugins and their hooks.';
54
+ let warnedNoPlanDrops = new WeakMap();
55
+ function noPlanDropWarning(event, reason) {
56
+ return (`client-core: ${displayUntrusted(event, { credentialUrls: false, credentialWords: false })} hooks from settings are not sent to the engine (${reason}), so they do not run there — ` +
57
+ `${settingsHookDropText(reason)}. Build the request with hooksForWire({ plan }) from hooksWirePlan() and show ` +
58
+ 'hooksNotRunNotice(plan) to tell the user which hooks will not run.');
59
+ }
60
+ function guardPluginTraversal(run) {
61
+ try {
62
+ return run();
63
+ }
64
+ catch (err) {
65
+ hostLog('debug', `hooksWireCaps: plugin hook data threw while being read — no plugin hooks projected this turn: ${String(err)}`);
66
+ return undefined;
67
+ }
68
+ }
69
+ function readPluginHooks(settings, warnIfAbsent) {
70
+ try {
71
+ if (typeof settings.enabledPluginHooks === 'function') {
72
+ const r = settings.enabledPluginHooks();
73
+ if (typeof r === 'object' && r !== null && Array.isArray(r.plugins)) {
74
+ return { state: 'present', reading: r };
75
+ }
76
+ hostLog('debug', 'hooksWireCaps: enabledPluginHooks() returned a malformed reading — no plugin hooks projected this turn');
77
+ return { state: 'failed' };
78
+ }
79
+ }
80
+ catch (err) {
81
+ hostLog('debug', `hooksWireCaps: enabledPluginHooks() threw — no plugin hooks projected this turn: ${String(err)}`);
82
+ return { state: 'failed' };
83
+ }
84
+ if (warnIfAbsent && !warnedNoPluginReader.has(settings)) {
85
+ warnedNoPluginReader.add(settings);
86
+ hostLog('warn', NO_PLUGIN_READER_WARNING);
87
+ }
88
+ return { state: 'absent' };
89
+ }
90
+ const NO_PLAN_ENGINE = { ownedByThisShell: null, pluginHooksCapable: null };
91
+ export function hooksForWire(opts = {}) {
92
+ const given = opts?.plan;
93
+ if (isHooksWirePlan(given))
94
+ return given.wire;
95
+ if (given !== undefined && given !== null) {
96
+ hostLog('debug', 'hooksForWire: opts.plan is not a plan from hooksWirePlan() — treated as absent (settings hooks only)');
97
+ }
98
+ if (isHooksWirePlan(opts))
99
+ return opts.wire;
100
+ return buildHooksWirePlan(NO_PLAN_ENGINE, new Set(), false).wire;
101
+ }
102
+ function isHooksWirePlan(v) {
103
+ try {
104
+ if (typeof v !== 'object' || v === null || Array.isArray(v))
105
+ return false;
106
+ const o = v;
107
+ for (const k of PLAN_KEYS)
108
+ if (!Object.hasOwn(o, k))
109
+ return false;
110
+ const reader = o.pluginReader;
111
+ const engineFacts = o.engine;
112
+ const wire = o.wire;
113
+ return (Array.isArray(o.verdicts) &&
114
+ Array.isArray(o.excluded) &&
115
+ Array.isArray(o.settingsDropped) &&
116
+ (reader === 'present' || reader === 'absent' || reader === 'failed') &&
117
+ typeof engineFacts === 'object' &&
118
+ engineFacts !== null &&
119
+ (wire === undefined || (typeof wire === 'object' && wire !== null && !Array.isArray(wire))));
120
+ }
121
+ catch (err) {
122
+ hostLog('debug', `hooksForWire: a value offered as a plan threw while being read — treated as not a plan: ${String(err)}`);
123
+ return false;
124
+ }
125
+ }
126
+ const PLAN_KEYS = ['engine', 'pluginReader', 'verdicts', 'excluded', 'settingsDropped', 'wire'];
127
+ const _planKeysPin = [true, true];
128
+ void _planKeysPin;
129
+ export function hooksWirePlan(opts = {}) {
130
+ const o = opts ?? {};
131
+ const owned = o.engineOwnedByThisShell;
132
+ const engine = {
133
+ ownedByThisShell: owned === true ? true : owned === false ? false : null,
134
+ pluginHooksCapable: engineCapState(o.baseUrl, 'taskSettings') === 'unprobed'
135
+ ? null
136
+ : engineCapNestedTrue(o.baseUrl, 'taskSettings', 'pluginHooks'),
137
+ };
138
+ const shellEvents = new Set();
139
+ if (Array.isArray(o.shellHookEvents)) {
140
+ for (const e of o.shellHookEvents)
141
+ if (typeof e === 'string')
142
+ shellEvents.add(e);
143
+ }
144
+ return buildHooksWirePlan(engine, shellEvents, true);
145
+ }
146
+ function buildHooksWirePlan(engine, shellEvents, withPlugins) {
147
+ const settingsDropped = [];
148
+ let settingsStripped = 0;
149
+ const planOf = (pluginReader, wire, verdicts = [], excluded = []) => ({ engine, pluginReader, verdicts, excluded, settingsDropped, wire });
150
+ const closedPlan = (settings, reason) => {
151
+ if (!withPlugins)
152
+ return planOf('absent', undefined);
153
+ const read = readPluginHooks(settings, false);
154
+ if (read.reading === undefined)
155
+ return planOf(read.state, undefined);
156
+ const reading = read.reading;
157
+ const d = guardPluginTraversal(() => decidePluginHooks({
158
+ plugins: reading.plugins,
159
+ exclusion: reason,
160
+ managedOnly: false,
161
+ engine,
162
+ shellHookEvents: shellEvents,
163
+ occupied: new Map(),
164
+ }));
165
+ if (d === undefined)
166
+ return planOf('failed', undefined);
167
+ return planOf(read.state, undefined, d.verdicts, d.excluded);
168
+ };
46
169
  const settings = hostSettings();
47
170
  if (settings === undefined)
48
- return undefined;
171
+ return planOf('absent', undefined);
49
172
  try {
50
173
  if (settings.shouldSkipHookDueToTrust()) {
51
174
  hostLog('debug', 'hooksWireCaps: workspace not trusted — no hooks projected to the engine');
52
- return undefined;
175
+ return closedPlan(settings, 'workspace_untrusted');
53
176
  }
54
177
  }
55
178
  catch (err) {
56
179
  hostLog('debug', `hooksWireCaps: workspace trust check threw — fail-closed (no hooks projected to the engine): ${String(err)}`);
57
- return undefined;
180
+ return closedPlan(settings, 'governance_unreadable');
58
181
  }
59
182
  let policy = null;
60
183
  try {
@@ -62,25 +185,25 @@ export function hooksForWire() {
62
185
  }
63
186
  catch (err) {
64
187
  hostLog('debug', `hooksWireCaps: policySettings read threw — fail-closed (no hooks projected to the engine): ${String(err)}`);
65
- return undefined;
188
+ return closedPlan(settings, 'governance_unreadable');
66
189
  }
67
190
  if (policy?.disableAllHooks === true) {
68
191
  hostLog('debug', 'hooksWireCaps: disableAllHooks (managed) — no hooks projected to the engine');
69
- return undefined;
192
+ return closedPlan(settings, 'policy_disable_all');
70
193
  }
71
194
  const pluginOnlyHooks = hooksLockedToPluginOnly(policy?.strictPluginOnlyCustomization);
72
195
  if (pluginOnlyHooks) {
73
196
  hostLog('debug', 'hooksWireCaps: strictPluginOnlyCustomization locks the hooks surface (managed) — only policySettings hooks projected to the engine');
74
197
  }
75
198
  const managedByPolicy = policy?.allowManagedHooksOnly === true || pluginOnlyHooks;
76
- const disabledByNonManaged = !managedByPolicy && nonManagedDisableAllHooks(settings);
199
+ let mergedMemo;
200
+ const mergedDisable = () => (mergedMemo ??= nonManagedDisableAllHooks(settings));
201
+ const disabledByNonManaged = !managedByPolicy && mergedDisable();
77
202
  if (disabledByNonManaged) {
78
203
  hostLog('debug', 'hooksWireCaps: disableAllHooks (merged, non-managed) — only policySettings hooks projected to the engine');
79
204
  }
80
205
  const managedOnly = managedByPolicy || disabledByNonManaged;
81
- const sources = managedOnly
82
- ? ['policySettings']
83
- : ['policySettings', ...EDITABLE_HOOK_SOURCES];
206
+ const sources = managedOnly ? ['policySettings'] : ALL_HOOK_SOURCES;
84
207
  const merged = {};
85
208
  for (const source of sources) {
86
209
  const hooks = hooksOfSource(source);
@@ -89,18 +212,76 @@ export function hooksForWire() {
89
212
  for (const [event, groups] of Object.entries(hooks)) {
90
213
  if (!Array.isArray(groups) || groups.length === 0)
91
214
  continue;
92
- merged[event] = [...(merged[event] ?? []), ...groups];
215
+ const kept = filterSettingsHookGroups(source, event, groups, engine.pluginHooksCapable === true, shellEvents.has(event), !withPlugins);
216
+ settingsDropped.push(...kept.drops);
217
+ settingsStripped += kept.stripped;
218
+ if (kept.groups.length === 0)
219
+ continue;
220
+ merged[event] = [...(merged[event] ?? []), ...kept.groups];
93
221
  }
94
222
  }
95
223
  const goalMatcher = goalStopHookMatcher(managedOnly);
96
224
  if (goalMatcher !== null) {
97
225
  merged['Stop'] = [...(merged['Stop'] ?? []), goalMatcher];
98
226
  }
99
- return Object.keys(merged).length > 0 ? merged : undefined;
227
+ if (settingsDropped.length > 0) {
228
+ hostLog('debug', `hooksWireCaps: ${settingsDropped.length} settings hook entr${settingsDropped.length === 1 ? 'y' : 'ies'} not sent to the engine ` +
229
+ `(${[...new Set(settingsDropped.map((d) => d.reason))].join(', ')}) — see hooksWirePlan().settingsDropped`);
230
+ }
231
+ if (settingsStripped > 0) {
232
+ hostLog('debug', `hooksWireCaps: ${settingsStripped} settings hook entr${settingsStripped === 1 ? 'y' : 'ies'} carried a plugin key — ` +
233
+ 'stripped before sending (plugin context is only minted for entries from the plugin reader)');
234
+ }
235
+ const wireOf = () => (Object.keys(merged).length > 0 ? merged : undefined);
236
+ if (!withPlugins) {
237
+ let seen = warnedNoPlanDrops.get(settings);
238
+ for (const d of settingsDropped) {
239
+ if (!d.notRun)
240
+ continue;
241
+ const key = `${d.event}\u0000${d.reason}`;
242
+ if (seen?.has(key) === true)
243
+ continue;
244
+ if (seen === undefined) {
245
+ seen = new Set();
246
+ warnedNoPlanDrops.set(settings, seen);
247
+ }
248
+ seen.add(key);
249
+ hostLog('warn', noPlanDropWarning(d.event, d.reason));
250
+ }
251
+ return planOf('absent', wireOf());
252
+ }
253
+ const read = readPluginHooks(settings, true);
254
+ if (read.reading === undefined)
255
+ return planOf(read.state, wireOf());
256
+ const reading = read.reading;
257
+ const occupied = occupancyOf(merged);
258
+ const d = guardPluginTraversal(() => {
259
+ const plugins = reading.plugins;
260
+ const startupMode = reading.suppressedByStartupMode === true;
261
+ const managedOnlyPlugins = !startupMode && plugins.length > 0 && (policy?.allowManagedHooksOnly === true || mergedDisable());
262
+ return decidePluginHooks({
263
+ plugins,
264
+ exclusion: startupMode ? 'startup_mode' : undefined,
265
+ managedOnly: managedOnlyPlugins,
266
+ engine,
267
+ shellHookEvents: shellEvents,
268
+ occupied,
269
+ });
270
+ });
271
+ if (d === undefined)
272
+ return planOf('failed', wireOf());
273
+ if (d.unreadable > 0) {
274
+ hostLog('debug', `hooksWireCaps: ${d.unreadable} plugin hook source(s) or group(s) unreadable — skipped`);
275
+ }
276
+ for (const [event, groups] of d.projected)
277
+ merged[event] = [...(merged[event] ?? []), ...groups];
278
+ return planOf('present', wireOf(), d.verdicts, d.excluded);
100
279
  }
101
280
  export function __resetHooksWireCapsForTests() {
102
281
  __resetGoalStopHookForTests();
103
282
  warnedNoMergedReader = new WeakSet();
283
+ warnedNoPluginReader = new WeakSet();
284
+ warnedNoPlanDrops = new WeakMap();
104
285
  }
105
286
  export function fleetHookFailureNoticeCapable(baseUrl) {
106
287
  return engineCapNestedTrue(baseUrl, 'fleet', 'hookFailureNotice');
package/dist/host.d.ts CHANGED
@@ -1,10 +1,34 @@
1
1
  import type { AdapterLogLevel, TimerHandle } from './seam.js';
2
+ import type { WireHooksConfig } from './finalVerifyWire.js';
2
3
  import { type NotificationQueuePort } from './notifications.js';
3
4
  export type SettingsDocument = Record<string, unknown>;
4
5
  export interface SettingsPort {
5
6
  getSettingsForSource(source: string): SettingsDocument | null;
6
7
  shouldSkipHookDueToTrust(hook?: unknown): boolean;
7
8
  mergedDisableAllHooks?(): boolean | undefined;
9
+ enabledPluginHooks?(): PluginHooksReading;
10
+ }
11
+ export type PluginHookOptionValue = string | number | boolean;
12
+ export interface PluginHookSensitiveOption {
13
+ readonly sensitive: true;
14
+ }
15
+ export interface PluginHookPlainOption {
16
+ readonly sensitive: false;
17
+ readonly value?: PluginHookOptionValue;
18
+ }
19
+ export type PluginHookOption = PluginHookSensitiveOption | PluginHookPlainOption;
20
+ export interface PluginHookSource {
21
+ readonly id: string;
22
+ readonly name: string;
23
+ readonly root: string;
24
+ readonly dataDir: string;
25
+ readonly managed: boolean;
26
+ readonly options?: Readonly<Record<string, PluginHookOption>>;
27
+ readonly hooks: WireHooksConfig;
28
+ }
29
+ export interface PluginHooksReading {
30
+ readonly plugins: readonly PluginHookSource[];
31
+ readonly suppressedByStartupMode?: boolean;
8
32
  }
9
33
  export interface FsPort {
10
34
  getScratchpadDir(): string | undefined;
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export * from './sdkWireTransit.js';
14
14
  export { SEMA_SEGMENT_ID_KEY } from './adapt/textStream.js';
15
15
  export * from './subagentContentStore.js';
16
16
  export * from './engineAgentPanelStore.js';
17
+ export * from './engineAgentAbsence.js';
17
18
  export * from './fleetAgentPanelProjection.js';
18
19
  export * from './liveQuestionStore.js';
19
20
  export * from './engineInlineTaskStats.js';
@@ -56,6 +57,8 @@ export * from './modelCapabilityProbe.js';
56
57
  export * from './classifierStatus.js';
57
58
  export * from './engineToolLabelStore.js';
58
59
  export * from './fleetTaskDesc.js';
60
+ export { displayUntrusted } from './displayUntrusted.js';
61
+ export type { DisplayUntrustedOptions, DisplayMark } from './displayUntrusted.js';
59
62
  export type * from './types/engineState.js';
60
63
  export * from './hostEnv.js';
61
64
  export * from './unrefTimer.js';
@@ -135,6 +138,7 @@ export * from './detachWire.js';
135
138
  export * from './workflowMonitor.js';
136
139
  export * from './workflowClient.js';
137
140
  export * from './hooksWireCaps.js';
141
+ export { ENGINE_FIRED_HOOK_EVENTS, PLUGIN_HOOK_DISPOSITIONS, PLUGIN_HOOK_REASONS, PLUGIN_HOOK_EXCLUSION_REASONS, SETTINGS_HOOK_DROP_REASONS, isEngineFiredHookEvent, pluginHookVerdictOf, pluginHookReasonText, settingsHookDropText, pluginHookExecutorLabel, hooksNotRunNotice, type EngineFiredHookEvent, type PluginHookDisposition, type PluginHookReason, type PluginHookExclusionReason, type SettingsHookDropReason, type HookSettingsSource, type PluginHookKey, type PluginHookVerdict, type PluginHookExclusion, type SettingsHookDrop, type HooksWireEngineFacts, type PluginHookReaderState, type HooksWirePlan, type HooksWirePlanOptions, } from './pluginHooksWire.js';
138
142
  export * from './liveInitToolFace.js';
139
143
  export * from './printInitToolFace.js';
140
144
  export * from './model/providerPresets.js';
@@ -160,8 +164,10 @@ export * from './hitl/resumeRunningCard.js';
160
164
  export * from './hitl/persistedRulesWire.js';
161
165
  export * from './hitl/localAllowRule.js';
162
166
  export * from './hitl/sessionPolicyWire.js';
167
+ export * from './hitl/sessionPolicyDeliverable.js';
163
168
  export * from './hitl/approvalsFeed.js';
164
169
  export * from './hitl/livePendingAsk.js';
170
+ export * from './hitl/askSurvivesPosture.js';
165
171
  export * from './hitl/crashConverged.js';
166
172
  export * from './interactiveHalt.js';
167
173
  export * from './compensations.js';
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ export * from './sdkWireTransit.js';
14
14
  export { SEMA_SEGMENT_ID_KEY } from './adapt/textStream.js';
15
15
  export * from './subagentContentStore.js';
16
16
  export * from './engineAgentPanelStore.js';
17
+ export * from './engineAgentAbsence.js';
17
18
  export * from './fleetAgentPanelProjection.js';
18
19
  export * from './liveQuestionStore.js';
19
20
  export * from './engineInlineTaskStats.js';
@@ -56,6 +57,7 @@ export * from './modelCapabilityProbe.js';
56
57
  export * from './classifierStatus.js';
57
58
  export * from './engineToolLabelStore.js';
58
59
  export * from './fleetTaskDesc.js';
60
+ export { displayUntrusted } from './displayUntrusted.js';
59
61
  export * from './hostEnv.js';
60
62
  export * from './unrefTimer.js';
61
63
  export * from './controlRouter.js';
@@ -133,6 +135,7 @@ export * from './detachWire.js';
133
135
  export * from './workflowMonitor.js';
134
136
  export * from './workflowClient.js';
135
137
  export * from './hooksWireCaps.js';
138
+ export { ENGINE_FIRED_HOOK_EVENTS, PLUGIN_HOOK_DISPOSITIONS, PLUGIN_HOOK_REASONS, PLUGIN_HOOK_EXCLUSION_REASONS, SETTINGS_HOOK_DROP_REASONS, isEngineFiredHookEvent, pluginHookVerdictOf, pluginHookReasonText, settingsHookDropText, pluginHookExecutorLabel, hooksNotRunNotice, } from './pluginHooksWire.js';
136
139
  export * from './liveInitToolFace.js';
137
140
  export * from './printInitToolFace.js';
138
141
  export * from './model/providerPresets.js';
@@ -158,8 +161,10 @@ export * from './hitl/resumeRunningCard.js';
158
161
  export * from './hitl/persistedRulesWire.js';
159
162
  export * from './hitl/localAllowRule.js';
160
163
  export * from './hitl/sessionPolicyWire.js';
164
+ export * from './hitl/sessionPolicyDeliverable.js';
161
165
  export * from './hitl/approvalsFeed.js';
162
166
  export * from './hitl/livePendingAsk.js';
167
+ export * from './hitl/askSurvivesPosture.js';
163
168
  export * from './hitl/crashConverged.js';
164
169
  export * from './interactiveHalt.js';
165
170
  export * from './compensations.js';
package/dist/ownKey.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export declare function putOwnKey<T>(table: Record<string, T>, key: string, value: T): void;
2
+ export declare function readOwnNonEmptyString(v: object | null | undefined, key: string): string | undefined;
package/dist/ownKey.js CHANGED
@@ -1,3 +1,9 @@
1
1
  export function putOwnKey(table, key, value) {
2
2
  Object.defineProperty(table, key, { value, enumerable: true, writable: true, configurable: true });
3
3
  }
4
+ export function readOwnNonEmptyString(v, key) {
5
+ if (typeof v !== 'object' || v === null || !Object.hasOwn(v, key))
6
+ return undefined;
7
+ const raw = v[key];
8
+ return typeof raw === 'string' && raw !== '' ? raw : undefined;
9
+ }
@@ -1,3 +1,4 @@
1
+ import { applyDisplayFace, LEGACY_NAME_FACE } from './displayUntrusted.js';
1
2
  const WIRE_KEY_FROM = 'from';
2
3
  const WIRE_KEY_BODY = 'body';
3
4
  const WIRE_KEY_KIND = 'kind';
@@ -124,7 +125,7 @@ function neutralizeAuthorityTags(text) {
124
125
  function cleanName(v) {
125
126
  if (typeof v !== 'string')
126
127
  return undefined;
127
- const t = v.replace(/[\u0000-\u001f\u007f\u2028\u2029]+/g, ' ').replace(/\s+/g, ' ').trim();
128
+ const t = applyDisplayFace(v, LEGACY_NAME_FACE);
128
129
  return t.length > 0 ? t : undefined;
129
130
  }
130
131
  function escapeEnvelopeTag(tag, text) {
@@ -0,0 +1,92 @@
1
+ import type { WireHooksConfig } from './finalVerifyWire.js';
2
+ import type { PluginHookSource } from './host.js';
3
+ export declare const ENGINE_FIRED_HOOK_EVENTS: readonly ["PreToolUse", "PostToolUse", "PostToolUseFailure", "PostToolBatch", "PreCompact", "PostCompact", "Stop", "StopFailure", "UserPromptSubmit"];
4
+ export type EngineFiredHookEvent = (typeof ENGINE_FIRED_HOOK_EVENTS)[number];
5
+ export declare const PLUGIN_HOOK_DISPOSITIONS: readonly ["engine", "shell", "not_run"];
6
+ 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"];
8
+ export type PluginHookReason = (typeof PLUGIN_HOOK_REASONS)[number];
9
+ export declare const PLUGIN_HOOK_EXCLUSION_REASONS: readonly ["workspace_untrusted", "governance_unreadable", "policy_disable_all", "startup_mode", "managed_plugins_only"];
10
+ export type PluginHookExclusionReason = (typeof PLUGIN_HOOK_EXCLUSION_REASONS)[number];
11
+ export declare const SETTINGS_HOOK_DROP_REASONS: readonly ["exec_form_unsupported", "mcp_tool_unsupported"];
12
+ export type SettingsHookDropReason = (typeof SETTINGS_HOOK_DROP_REASONS)[number];
13
+ export type HookSettingsSource = 'policySettings' | 'userSettings' | 'projectSettings' | 'localSettings';
14
+ export interface PluginHookKey {
15
+ readonly pluginId: string;
16
+ readonly pluginName: string;
17
+ readonly event: string;
18
+ readonly groupIndex: number;
19
+ readonly hookIndex: number;
20
+ }
21
+ export interface PluginHookVerdict extends PluginHookKey {
22
+ readonly disposition: PluginHookDisposition;
23
+ readonly reason: PluginHookReason;
24
+ }
25
+ export interface PluginHookExclusion extends PluginHookKey {
26
+ readonly reason: PluginHookExclusionReason;
27
+ }
28
+ export interface SettingsHookDrop {
29
+ readonly source: HookSettingsSource;
30
+ readonly event: string;
31
+ readonly groupIndex: number;
32
+ readonly hookIndex: number;
33
+ readonly reason: SettingsHookDropReason;
34
+ readonly notRun: boolean;
35
+ }
36
+ export interface HooksWireEngineFacts {
37
+ readonly ownedByThisShell: boolean | null;
38
+ readonly pluginHooksCapable: boolean | null;
39
+ }
40
+ export type PluginHookReaderState = 'present' | 'absent' | 'failed';
41
+ export interface HooksWirePlan {
42
+ readonly engine: HooksWireEngineFacts;
43
+ readonly pluginReader: PluginHookReaderState;
44
+ readonly verdicts: readonly PluginHookVerdict[];
45
+ readonly excluded: readonly PluginHookExclusion[];
46
+ readonly settingsDropped: readonly SettingsHookDrop[];
47
+ readonly wire: WireHooksConfig | undefined;
48
+ }
49
+ export interface HooksWirePlanOptions {
50
+ readonly baseUrl?: string;
51
+ readonly engineOwnedByThisShell?: boolean;
52
+ readonly shellHookEvents?: readonly string[];
53
+ }
54
+ export declare const ENGINE_HOOK_LIMITS: {
55
+ readonly maxGroupsPerEvent: 16;
56
+ readonly maxEntriesPerEvent: 32;
57
+ readonly maxCommandChars: 8192;
58
+ };
59
+ type WireHookGroup = NonNullable<WireHooksConfig[string]>[number];
60
+ export declare function isEngineFiredHookEvent(event: string): boolean;
61
+ export interface EventOccupancy {
62
+ readonly groups: number;
63
+ readonly entries: number;
64
+ }
65
+ export declare function occupancyOf(wire: WireHooksConfig): Map<string, EventOccupancy>;
66
+ export interface PluginHookDecisionInput {
67
+ readonly plugins: readonly PluginHookSource[];
68
+ readonly exclusion: PluginHookExclusionReason | undefined;
69
+ readonly managedOnly: boolean;
70
+ readonly engine: HooksWireEngineFacts;
71
+ readonly shellHookEvents: ReadonlySet<string>;
72
+ readonly occupied: ReadonlyMap<string, EventOccupancy>;
73
+ }
74
+ export interface PluginHookDecision {
75
+ readonly verdicts: readonly PluginHookVerdict[];
76
+ readonly excluded: readonly PluginHookExclusion[];
77
+ readonly projected: ReadonlyMap<string, readonly WireHookGroup[]>;
78
+ readonly unreadable: number;
79
+ }
80
+ export declare function decidePluginHooks(input: PluginHookDecisionInput): PluginHookDecision;
81
+ export declare function filterSettingsHookGroups(source: HookSettingsSource, event: string, groups: readonly WireHookGroup[], execFormCapable: boolean, hostFires: boolean, failLoudMcpTool: boolean): SettingsHookFilterResult;
82
+ export interface SettingsHookFilterResult {
83
+ readonly groups: WireHookGroup[];
84
+ readonly drops: SettingsHookDrop[];
85
+ readonly stripped: number;
86
+ }
87
+ export declare function pluginHookVerdictOf(plan: HooksWirePlan, pluginId: string, event: string, groupIndex: number, hookIndex: number): PluginHookVerdict | undefined;
88
+ export declare function pluginHookReasonText(reason: PluginHookReason): string;
89
+ export declare function settingsHookDropText(reason: SettingsHookDropReason): string;
90
+ export declare function pluginHookExecutorLabel(v: PluginHookVerdict): string;
91
+ export declare function hooksNotRunNotice(plan: HooksWirePlan): readonly string[];
92
+ export {};