@sema-agent/client-core 0.88.0 → 0.90.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 (61) hide show
  1. package/CHANGELOG.md +208 -0
  2. package/README.md +27 -21
  3. package/dist/adapter/activeRunSelfHeal.js +4 -2
  4. package/dist/adapter/downstream/eventToSdkMessage.js +4 -2
  5. package/dist/adapter/downstream/wiringManifestView.d.ts +9 -0
  6. package/dist/adapter/downstream/wiringManifestView.js +27 -0
  7. package/dist/adapter/runStream.d.ts +2 -0
  8. package/dist/adapter/runStream.js +157 -32
  9. package/dist/attachmentsWireCaps.d.ts +13 -2
  10. package/dist/attachmentsWireCaps.js +25 -5
  11. package/dist/controlRouter.d.ts +7 -1
  12. package/dist/controlRouter.js +43 -1
  13. package/dist/coreValuePorts.d.ts +1 -0
  14. package/dist/disableSkillsWire.d.ts +17 -0
  15. package/dist/disableSkillsWire.js +52 -0
  16. package/dist/displayUntrusted.js +122 -55
  17. package/dist/engineCapReader.js +12 -1
  18. package/dist/engineNoticeCodes.d.ts +25 -9
  19. package/dist/engineNoticeCodes.js +161 -68
  20. package/dist/fileHistoryLegNotice.d.ts +20 -0
  21. package/dist/fileHistoryLegNotice.js +122 -0
  22. package/dist/gateOutcome.d.ts +1 -1
  23. package/dist/gateOutcome.js +1 -0
  24. package/dist/generated/engineFactTables.d.ts +1 -0
  25. package/dist/generated/engineFactTables.js +3 -0
  26. package/dist/generated/engineNoticeTables.d.ts +3 -0
  27. package/dist/generated/engineNoticeTables.js +107 -2
  28. package/dist/hitl/toolApprovalWire.d.ts +6 -2
  29. package/dist/hitl/toolApprovalWire.js +30 -0
  30. package/dist/hookToolAliases.d.ts +3 -0
  31. package/dist/hookToolAliases.js +10 -0
  32. package/dist/hooksWireCaps.js +80 -12
  33. package/dist/index.d.ts +14 -3
  34. package/dist/index.js +10 -3
  35. package/dist/permissionsAllowGrantCapability.d.ts +17 -0
  36. package/dist/permissionsAllowGrantCapability.js +75 -0
  37. package/dist/permissionsDeclaration.d.ts +25 -0
  38. package/dist/permissionsDeclaration.js +227 -0
  39. package/dist/pluginHooksWire.d.ts +46 -4
  40. package/dist/pluginHooksWire.js +277 -24
  41. package/dist/promptDelivery.d.ts +5 -2
  42. package/dist/promptDelivery.js +8 -3
  43. package/dist/providerRejection.d.ts +7 -0
  44. package/dist/providerRejection.js +58 -0
  45. package/dist/request/taskRequest.d.ts +2 -0
  46. package/dist/request/taskRequest.js +67 -9
  47. package/dist/runTerminal.d.ts +6 -0
  48. package/dist/runTerminal.js +23 -0
  49. package/dist/sdkRegistryTransit.d.ts +1 -0
  50. package/dist/sdkRegistryTransit.js +1 -0
  51. package/dist/seam.d.ts +3 -0
  52. package/dist/seatContract.d.ts +9 -1
  53. package/dist/seatContract.js +10 -0
  54. package/dist/subagent/engineSubagentSteer.d.ts +8 -1
  55. package/dist/subagent/engineSubagentSteer.js +26 -3
  56. package/dist/supportFloor.d.ts +10 -0
  57. package/dist/supportFloor.js +42 -0
  58. package/dist/taskRequestWords.d.ts +1 -0
  59. package/dist/taskRequestWords.js +14 -0
  60. package/docs/INTEGRATION-CLIENTS.md +896 -14
  61. package/package.json +3 -3
@@ -0,0 +1,75 @@
1
+ import { engineWireTarget } from './engineWireTarget.js';
2
+ import { createEngineCapReader } from './engineCapReader.js';
3
+ const ALLOW_GRANT_CAP_PATH = ['settings', 'permissions', 'allowGrant'];
4
+ export function projectPermissionsAllowGrantCapability(caps) {
5
+ if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
6
+ return undefined;
7
+ let node = caps;
8
+ for (let i = 0; i < ALLOW_GRANT_CAP_PATH.length; i++) {
9
+ const seg = ALLOW_GRANT_CAP_PATH[i];
10
+ if (!Object.hasOwn(node, seg))
11
+ return { kind: 'not_reported' };
12
+ const v = node[seg];
13
+ if (v === undefined)
14
+ return { kind: 'not_reported' };
15
+ if (i === ALLOW_GRANT_CAP_PATH.length - 1) {
16
+ return typeof v === 'boolean' ? { kind: 'present', grants: v } : undefined;
17
+ }
18
+ if (v === null || typeof v !== 'object' || Array.isArray(v))
19
+ return undefined;
20
+ node = v;
21
+ }
22
+ return undefined;
23
+ }
24
+ const reader = createEngineCapReader({
25
+ name: ALLOW_GRANT_CAP_PATH.join('.'),
26
+ project: projectPermissionsAllowGrantCapability,
27
+ makeUnobserved: () => ({ kind: 'unobserved' }),
28
+ });
29
+ export function noteEngineCapsForPermissionsAllowGrant(baseUrl, caps, opts) {
30
+ reader.note(baseUrl, caps, opts);
31
+ }
32
+ export function observedPermissionsAllowGrant(baseUrl = engineWireTarget()?.baseUrl) {
33
+ return reader.observed(baseUrl);
34
+ }
35
+ export function permissionsAllowGrantAvailable(reading) {
36
+ try {
37
+ if (reading === null || typeof reading !== 'object' || Array.isArray(reading))
38
+ return false;
39
+ if (!Object.hasOwn(reading, 'kind') || !Object.hasOwn(reading, 'grants'))
40
+ return false;
41
+ return reading.kind === 'present' && reading.grants === true;
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ function ownReadingField(reading, key) {
48
+ try {
49
+ if (reading === null || typeof reading !== 'object' || Array.isArray(reading))
50
+ return undefined;
51
+ return Object.hasOwn(reading, key) ? reading[key] : undefined;
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ }
57
+ export function permissionsAllowGrantDoctorDetail(reading) {
58
+ if (permissionsAllowGrantAvailable(reading)) {
59
+ return 'allow rules sent to the engine — it reads them as rules, not as an exclusive list: a matching call counts as answered for, and the engine still raises its own prompts where it requires them';
60
+ }
61
+ const kind = ownReadingField(reading, 'kind');
62
+ if (kind === 'present' && ownReadingField(reading, 'grants') === false) {
63
+ return 'allow rules kept on this side — this engine says it does not read allow rules as rules of that kind, so they are not sent and matching approvals are still answered here';
64
+ }
65
+ if (kind === 'not_reported') {
66
+ return 'allow rules kept on this side — this engine does not say how it reads allow rules; sending them to an engine that reads them as an exclusive list would block every tool not on it, so they are not sent and matching approvals are still answered here';
67
+ }
68
+ return 'allow rules kept on this side — no usable capabilities reading is cached for this engine (none received, or the last one was unreadable), so allow rules are not sent and matching approvals are still answered here';
69
+ }
70
+ export function forgetPermissionsAllowGrantReading(baseUrl) {
71
+ reader.forget(baseUrl);
72
+ }
73
+ export function __resetPermissionsAllowGrantReadingsForTests() {
74
+ reader.__resetForTests();
75
+ }
@@ -0,0 +1,25 @@
1
+ import { type PermissionsAllowGrantReading } from './permissionsAllowGrantCapability.js';
2
+ declare const RULE_TRUST_ORDER: readonly ["policySettings", "cliArg", "flagSettings", "session", "userSettings", "localSettings", "projectSettings"];
3
+ export type WireRuleOrigin = (typeof RULE_TRUST_ORDER)[number];
4
+ export interface WireRuleOrigins {
5
+ readonly deny?: ReadonlyMap<string, WireRuleOrigin>;
6
+ readonly ask?: ReadonlyMap<string, WireRuleOrigin>;
7
+ readonly allow?: ReadonlyMap<string, WireRuleOrigin>;
8
+ }
9
+ export interface PermissionsDeclarationInput {
10
+ readonly deny: readonly string[];
11
+ readonly ask: readonly string[];
12
+ readonly allow: readonly string[];
13
+ readonly origins?: WireRuleOrigins;
14
+ readonly cliDeny?: readonly string[];
15
+ }
16
+ export interface PermissionsDeclarationBuckets {
17
+ deny?: string[];
18
+ ask?: string[];
19
+ allow?: string[];
20
+ }
21
+ export interface PermissionsDeclaration {
22
+ permissions?: PermissionsDeclarationBuckets;
23
+ }
24
+ export declare function permissionsDeclaration(input: PermissionsDeclarationInput, caps: PermissionsAllowGrantReading): PermissionsDeclaration;
25
+ export {};
@@ -0,0 +1,227 @@
1
+ import { hasCoreValuePort, protocolOf, retiredToolNames, ruleToolGrammarOf, validatePermissionRules } from './coreValuePorts.js';
2
+ import { permissionsAllowGrantAvailable } from './permissionsAllowGrantCapability.js';
3
+ const RULE_TRUST_ORDER = [
4
+ 'policySettings',
5
+ 'cliArg',
6
+ 'flagSettings',
7
+ 'session',
8
+ 'userSettings',
9
+ 'localSettings',
10
+ 'projectSettings',
11
+ ];
12
+ function normalizeLegacyToolName(name) {
13
+ switch (name) {
14
+ case 'Task':
15
+ return 'Agent';
16
+ case 'KillShell':
17
+ return 'TaskStop';
18
+ case 'AgentOutputTool':
19
+ case 'BashOutputTool':
20
+ return 'TaskOutput';
21
+ default:
22
+ return name;
23
+ }
24
+ }
25
+ function unescapedIndex(str, char, fromEnd) {
26
+ for (let k = 0; k < str.length; k++) {
27
+ const i = fromEnd ? str.length - 1 - k : k;
28
+ if (str[i] !== char)
29
+ continue;
30
+ let backslashes = 0;
31
+ for (let j = i - 1; j >= 0 && str[j] === '\\'; j--)
32
+ backslashes++;
33
+ if (backslashes % 2 === 0)
34
+ return i;
35
+ }
36
+ return -1;
37
+ }
38
+ function unescapeRuleContent(content) {
39
+ return content.replace(/\\\(/g, '(').replace(/\\\)/g, ')').replace(/\\\\/g, '\\');
40
+ }
41
+ function parseRule(rule) {
42
+ const open = unescapedIndex(rule, '(', false);
43
+ if (open === -1)
44
+ return { toolName: normalizeLegacyToolName(rule) };
45
+ const close = unescapedIndex(rule, ')', true);
46
+ if (close === -1 || close <= open || close !== rule.length - 1)
47
+ return { toolName: normalizeLegacyToolName(rule) };
48
+ const toolName = rule.substring(0, open);
49
+ const rawContent = rule.substring(open + 1, close);
50
+ if (!toolName)
51
+ return { toolName: normalizeLegacyToolName(rule) };
52
+ if (rawContent === '' || rawContent === '*')
53
+ return { toolName: normalizeLegacyToolName(toolName) };
54
+ return { toolName: normalizeLegacyToolName(toolName), ruleContent: unescapeRuleContent(rawContent) };
55
+ }
56
+ function requireRulePorts() {
57
+ const required = ['MCP_NAMESPACE', 'RETIRED_TOOL_NAMES', 'protocolOf', 'ruleToolGrammarOf', 'validatePermissionRules'];
58
+ const missing = required.filter((p) => !hasCoreValuePort(p));
59
+ if (missing.length === 0)
60
+ return;
61
+ throw new Error(`permissionsDeclaration: 规则判据端口缺席(${missing.join(' / ')})—— 先用 installCoreValuePorts 装上 core 的同名实现。` +
62
+ '缺席不等于全部合格:不判就发会让整份请求被引擎拒收,不判就删会静默丢掉 deny 规则,所以这里拒绝装配。');
63
+ }
64
+ function engineWireSafe(name) {
65
+ if (name.includes('('))
66
+ return false;
67
+ if (retiredToolNames().has(name))
68
+ return false;
69
+ const ns = protocolOf(name);
70
+ if (ns === undefined)
71
+ return !name.includes('__');
72
+ return ns.parse(name) !== undefined;
73
+ }
74
+ function wholeServerRuleName(name) {
75
+ const read = name.trim();
76
+ const ns = protocolOf(read);
77
+ if (ns === undefined || !read.startsWith(ns.prefix))
78
+ return name;
79
+ const peer = read.slice(ns.prefix.length);
80
+ return peer !== '' && !peer.includes('__') && !peer.endsWith('_') ? `${read}__*` : name;
81
+ }
82
+ const RULE_ROOT = '/';
83
+ function compileVerdict(rule, behavior) {
84
+ try {
85
+ const issues = validatePermissionRules([{ rule, behavior }], { pathBases: { root: RULE_ROOT } });
86
+ if (!Array.isArray(issues))
87
+ return 'unknown';
88
+ return issues.length === 0 ? 'compiles' : 'refused';
89
+ }
90
+ catch {
91
+ return 'unknown';
92
+ }
93
+ }
94
+ function wireSubset(rules, behavior) {
95
+ const out = new Set();
96
+ let portsChecked = false;
97
+ for (const rule of rules) {
98
+ if (typeof rule !== 'string' || rule === '')
99
+ continue;
100
+ if (!portsChecked) {
101
+ requireRulePorts();
102
+ portsChecked = true;
103
+ }
104
+ const parsed = parseRule(rule);
105
+ if (parsed.ruleContent === undefined) {
106
+ const name = wholeServerRuleName(parsed.toolName);
107
+ if (!engineWireSafe(name))
108
+ continue;
109
+ if (out.has(name))
110
+ continue;
111
+ const verdict = compileVerdict(name, behavior);
112
+ if (verdict === 'refused' || (verdict === 'unknown' && behavior === 'allow'))
113
+ continue;
114
+ out.add(name);
115
+ continue;
116
+ }
117
+ if (!engineWireSafe(parsed.toolName))
118
+ continue;
119
+ if (ruleToolGrammarOf(parsed.toolName) === 'path' && parsed.ruleContent.startsWith('~/'))
120
+ continue;
121
+ const verdict = compileVerdict(rule, behavior);
122
+ if (verdict === 'compiles' || (verdict === 'unknown' && behavior !== 'allow'))
123
+ out.add(rule);
124
+ }
125
+ return [...out];
126
+ }
127
+ function projectBucketByTrust(raw, behavior, originOf, cliRules) {
128
+ const present = new Set(raw);
129
+ const groups = RULE_TRUST_ORDER.map(() => []);
130
+ const rankOf = (origin) => RULE_TRUST_ORDER.indexOf(origin);
131
+ if (originOf !== undefined) {
132
+ for (const [r, origin] of originOf) {
133
+ const rank = rankOf(origin);
134
+ if (rank >= 0 && typeof r === 'string' && present.has(r))
135
+ groups[rank].push(r);
136
+ }
137
+ }
138
+ const unknownOrigin = [...present].filter((r) => originOf === undefined || rankOf(originOf.get(r)) < 0);
139
+ const out = new Set();
140
+ const append = (list) => {
141
+ for (const r of list)
142
+ out.add(r);
143
+ };
144
+ RULE_TRUST_ORDER.forEach((origin, rank) => {
145
+ if (origin === 'cliArg')
146
+ append(cliRules);
147
+ append(wireSubset(groups[rank], behavior));
148
+ });
149
+ append(wireSubset(unknownOrigin, behavior));
150
+ return [...out];
151
+ }
152
+ const WHO = 'permissionsDeclaration';
153
+ function ownField(obj, key, where) {
154
+ if (Object.hasOwn(obj, key))
155
+ return obj[key];
156
+ if (key in obj)
157
+ throw new TypeError(`${WHO}: ${where}${key} 只在原型链上,不是自有字段 —— 拒绝读取(请交普通对象)。`);
158
+ return undefined;
159
+ }
160
+ function ruleList(v, key) {
161
+ if (v === undefined || v === null)
162
+ return undefined;
163
+ if (!Array.isArray(v))
164
+ throw new TypeError(`${WHO}: ${key} 必须是规则串数组或缺席 —— 收到 typeof=${typeof v}。`);
165
+ const out = [];
166
+ const n = v.length;
167
+ for (let i = 0; i < n; i++) {
168
+ if (!Object.hasOwn(v, i)) {
169
+ if (i in v)
170
+ throw new TypeError(`${WHO}: ${key}[${i}] 是空洞,而原型链上在这个下标有值 —— 拒绝读取(请交不带空洞的普通数组)。`);
171
+ continue;
172
+ }
173
+ const r = v[i];
174
+ if (typeof r === 'string')
175
+ out.push(r);
176
+ }
177
+ return out;
178
+ }
179
+ function originMap(v, key) {
180
+ if (v === undefined || v === null)
181
+ return undefined;
182
+ const ok = typeof v === 'object' &&
183
+ typeof v.get === 'function' &&
184
+ typeof v[Symbol.iterator] === 'function';
185
+ if (!ok)
186
+ throw new TypeError(`${WHO}: origins.${key} 必须是 Map(规则原串 → 来源)或缺席 —— 收到 typeof=${typeof v}。`);
187
+ const snap = new Map();
188
+ for (const entry of v) {
189
+ const [r, origin] = entry;
190
+ if (!snap.has(r))
191
+ snap.set(r, origin);
192
+ }
193
+ return snap;
194
+ }
195
+ export function permissionsDeclaration(input, caps) {
196
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
197
+ throw new TypeError(`${WHO}: 入参必须是对象 —— 收到 ${input === null ? 'null' : Array.isArray(input) ? 'array' : `typeof=${typeof input}`}。`);
198
+ }
199
+ const deny = ruleList(ownField(input, 'deny', ''), 'deny');
200
+ const ask = ruleList(ownField(input, 'ask', ''), 'ask');
201
+ const allow = ruleList(ownField(input, 'allow', ''), 'allow');
202
+ const cliDeny = ruleList(ownField(input, 'cliDeny', ''), 'cliDeny');
203
+ const originsRaw = ownField(input, 'origins', '');
204
+ let origins = {};
205
+ if (originsRaw !== undefined && originsRaw !== null) {
206
+ if (typeof originsRaw !== 'object' || Array.isArray(originsRaw)) {
207
+ throw new TypeError(`${WHO}: origins 必须是 { deny?, ask?, allow? } 对象或缺席 —— 收到 typeof=${typeof originsRaw}。`);
208
+ }
209
+ const od = originMap(ownField(originsRaw, 'deny', 'origins.'), 'deny');
210
+ const oa = originMap(ownField(originsRaw, 'ask', 'origins.'), 'ask');
211
+ const ol = originMap(ownField(originsRaw, 'allow', 'origins.'), 'allow');
212
+ origins = { ...(od ? { deny: od } : {}), ...(oa ? { ask: oa } : {}), ...(ol ? { allow: ol } : {}) };
213
+ }
214
+ const allowOn = permissionsAllowGrantAvailable(caps);
215
+ const cliDenyWire = cliDeny === undefined ? [] : wireSubset(cliDeny, 'deny');
216
+ const denyOut = projectBucketByTrust(deny ?? [], 'deny', origins.deny, cliDenyWire);
217
+ const askOut = projectBucketByTrust(ask ?? [], 'ask', origins.ask, []);
218
+ const allowOut = allowOn && allow !== undefined ? projectBucketByTrust(allow, 'allow', origins.allow, []) : [];
219
+ const permissions = {};
220
+ if (denyOut.length > 0)
221
+ permissions.deny = denyOut;
222
+ if (askOut.length > 0)
223
+ permissions.ask = askOut;
224
+ if (allowOut.length > 0)
225
+ permissions.allow = allowOut;
226
+ return Object.keys(permissions).length > 0 ? { permissions } : {};
227
+ }
@@ -4,9 +4,9 @@ export declare const ENGINE_FIRED_HOOK_EVENTS: readonly ["PreToolUse", "PostTool
4
4
  export type EngineFiredHookEvent = (typeof ENGINE_FIRED_HOOK_EVENTS)[number];
5
5
  export declare const PLUGIN_HOOK_DISPOSITIONS: readonly ["engine", "shell", "not_run"];
6
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"];
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
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"];
9
+ 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
10
  export type PluginHookExclusionReason = (typeof PLUGIN_HOOK_EXCLUSION_REASONS)[number];
11
11
  export declare const SETTINGS_HOOK_DROP_REASONS: readonly ["exec_form_unsupported", "mcp_tool_unsupported"];
12
12
  export type SettingsHookDropReason = (typeof SETTINGS_HOOK_DROP_REASONS)[number];
@@ -33,9 +33,25 @@ export interface SettingsHookDrop {
33
33
  readonly reason: SettingsHookDropReason;
34
34
  readonly notRun: boolean;
35
35
  }
36
+ export interface SettingsHookVerdict {
37
+ readonly source: HookSettingsSource;
38
+ readonly event: string;
39
+ readonly groupIndex: number;
40
+ readonly hookIndex: number;
41
+ readonly disposition: PluginHookDisposition;
42
+ readonly reason: PluginHookReason;
43
+ }
44
+ export interface SettingsHookExclusion {
45
+ readonly source: HookSettingsSource;
46
+ readonly event: string;
47
+ readonly groupIndex: number;
48
+ readonly hookIndex: number;
49
+ readonly reason: PluginHookExclusionReason;
50
+ }
36
51
  export interface HooksWireEngineFacts {
37
52
  readonly ownedByThisShell: boolean | null;
38
53
  readonly pluginHooksCapable: boolean | null;
54
+ readonly hooksCapable: boolean | null;
39
55
  }
40
56
  export type PluginHookReaderState = 'present' | 'absent' | 'failed';
41
57
  export interface HooksWirePlan {
@@ -44,19 +60,34 @@ export interface HooksWirePlan {
44
60
  readonly verdicts: readonly PluginHookVerdict[];
45
61
  readonly excluded: readonly PluginHookExclusion[];
46
62
  readonly settingsDropped: readonly SettingsHookDrop[];
63
+ readonly settingsVerdicts: readonly SettingsHookVerdict[];
64
+ readonly settingsExcluded: readonly SettingsHookExclusion[];
47
65
  readonly wire: WireHooksConfig | undefined;
48
66
  }
49
67
  export interface HooksWirePlanOptions {
50
68
  readonly baseUrl?: string;
51
69
  readonly engineOwnedByThisShell?: boolean;
52
70
  readonly shellHookEvents?: readonly string[];
71
+ readonly shellHookTypesByEvent?: Readonly<Record<string, readonly string[]>>;
72
+ readonly promptAuthoredByUser?: boolean;
53
73
  }
54
74
  export declare const ENGINE_HOOK_LIMITS: {
55
75
  readonly maxGroupsPerEvent: 16;
56
76
  readonly maxEntriesPerEvent: 32;
57
77
  readonly maxCommandChars: 8192;
78
+ readonly maxLlmEntriesPerEvent: 4;
79
+ };
80
+ export type ShellRunsHook = (event: string, type: string | undefined) => boolean;
81
+ type HookExecutor = {
82
+ readonly kind: 'excluded';
83
+ readonly reason: 'prompt_not_user_authored';
84
+ } | {
85
+ readonly kind: 'judged';
86
+ readonly disposition: PluginHookDisposition;
87
+ readonly reason: PluginHookReason;
58
88
  };
59
89
  type WireHookGroup = NonNullable<WireHooksConfig[string]>[number];
90
+ type WireHookEntry = WireHookGroup['hooks'][number];
60
91
  export declare function isEngineFiredHookEvent(event: string): boolean;
61
92
  export interface EventOccupancy {
62
93
  readonly groups: number;
@@ -68,7 +99,8 @@ export interface PluginHookDecisionInput {
68
99
  readonly exclusion: PluginHookExclusionReason | undefined;
69
100
  readonly managedOnly: boolean;
70
101
  readonly engine: HooksWireEngineFacts;
71
- readonly shellHookEvents: ReadonlySet<string>;
102
+ readonly shellRuns: ShellRunsHook;
103
+ readonly promptAuthoredByUser: boolean;
72
104
  readonly occupied: ReadonlyMap<string, EventOccupancy>;
73
105
  }
74
106
  export interface PluginHookDecision {
@@ -78,12 +110,16 @@ export interface PluginHookDecision {
78
110
  readonly unreadable: number;
79
111
  }
80
112
  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;
113
+ export declare function filterSettingsHookGroups(source: HookSettingsSource, event: string, groups: readonly WireHookGroup[], execFormCapable: boolean, shellRunsType: (type: string | undefined) => boolean, failLoudMcpTool: boolean, judge?: SettingsHookJudge): SettingsHookFilterResult;
82
114
  export interface SettingsHookFilterResult {
83
115
  readonly groups: WireHookGroup[];
84
116
  readonly drops: SettingsHookDrop[];
85
117
  readonly stripped: number;
118
+ readonly verdicts: SettingsHookVerdict[];
119
+ readonly excluded: SettingsHookExclusion[];
86
120
  }
121
+ export type SettingsHookJudge = (event: string, group: WireHookGroup, entry: WireHookEntry) => HookExecutor;
122
+ export declare function createSettingsHookJudge(engine: HooksWireEngineFacts, shellRuns: ShellRunsHook, promptAuthoredByUser: boolean): SettingsHookJudge;
87
123
  export interface SourcedSettingsHookGroup {
88
124
  readonly source: HookSettingsSource;
89
125
  readonly group: WireHookGroup;
@@ -94,6 +130,12 @@ export interface SettingsHookDedupResult {
94
130
  readonly removed: number;
95
131
  }
96
132
  export declare function pluginHookVerdictOf(plan: HooksWirePlan, pluginId: string, event: string, groupIndex: number, hookIndex: number): PluginHookVerdict | undefined;
133
+ export declare function settingsHooksKeptLocally<T>(plan: HooksWirePlan, event: string, group: {
134
+ readonly matcher?: string;
135
+ readonly hooks: readonly T[];
136
+ }, opts?: {
137
+ readonly engineCompaction?: 'performed' | 'not_performed';
138
+ }): readonly T[];
97
139
  export declare function pluginHookReasonText(reason: PluginHookReason): string;
98
140
  export declare function settingsHookDropText(reason: SettingsHookDropReason): string;
99
141
  export declare function pluginHookExecutorLabel(v: PluginHookVerdict): string;