@chrisromp/copilot-bridge 0.8.5 → 0.9.1

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.
@@ -0,0 +1,302 @@
1
+ /**
2
+ * hooks-loader.ts — Discover and load session hooks from plugins, user config, and workspace.
3
+ *
4
+ * Uses the official CLI hooks.json format:
5
+ * {
6
+ * "version": 1,
7
+ * "hooks": {
8
+ * "preToolUse": [
9
+ * { "type": "command", "bash": "./scripts/guard.sh", "cwd": ".", "timeoutSec": 10 }
10
+ * ]
11
+ * }
12
+ * }
13
+ *
14
+ * Hooks are shell commands. Input is piped as JSON to stdin, output read from stdout.
15
+ * Multiple hooks per type run in sequence; for preToolUse, first "deny" wins.
16
+ *
17
+ * Discovery order (lowest → highest priority, later entries append):
18
+ * 1. Plugin hooks: ~/.copilot/installed-plugins/.../hooks.json
19
+ * 2. User hooks: ~/.copilot/hooks.json
20
+ * 3. Workspace hooks: <workspace>/.github/hooks/hooks.json, <workspace>/.github/hooks.json, <workspace>/hooks.json
21
+ */
22
+ import * as fs from 'node:fs';
23
+ import * as path from 'node:path';
24
+ import { spawn } from 'node:child_process';
25
+ import { createLogger } from '../logger.js';
26
+ const log = createLogger('hooks');
27
+ /** CLI hook type names → SDK SessionHooks keys */
28
+ const HOOK_TYPE_MAP = {
29
+ preToolUse: 'onPreToolUse',
30
+ postToolUse: 'onPostToolUse',
31
+ userPromptSubmitted: 'onUserPromptSubmitted',
32
+ sessionStart: 'onSessionStart',
33
+ sessionEnd: 'onSessionEnd',
34
+ errorOccurred: 'onErrorOccurred',
35
+ };
36
+ const VALID_HOOK_TYPES = new Set(Object.keys(HOOK_TYPE_MAP));
37
+ /**
38
+ * Discover all hooks.json files in priority order (lowest first).
39
+ */
40
+ function discoverHooksFiles(workingDirectory, options) {
41
+ const home = process.env.HOME;
42
+ const results = [];
43
+ // 1. Plugin hooks (lowest priority)
44
+ if (home) {
45
+ const pluginsDir = path.join(home, '.copilot', 'installed-plugins');
46
+ if (fs.existsSync(pluginsDir)) {
47
+ const walk = (dir, depth) => {
48
+ if (depth > 3)
49
+ return;
50
+ try {
51
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
52
+ const full = path.join(dir, entry.name);
53
+ if (entry.isFile() && entry.name === 'hooks.json') {
54
+ results.push({ file: full, baseDir: dir });
55
+ }
56
+ else if (entry.isDirectory()) {
57
+ walk(full, depth + 1);
58
+ }
59
+ }
60
+ }
61
+ catch { /* permission errors */ }
62
+ };
63
+ walk(pluginsDir, 0);
64
+ }
65
+ }
66
+ // 2. User hooks
67
+ if (home) {
68
+ const userHooks = path.join(home, '.copilot', 'hooks.json');
69
+ if (fs.existsSync(userHooks)) {
70
+ results.push({ file: userHooks, baseDir: path.join(home, '.copilot') });
71
+ }
72
+ }
73
+ // 3. Workspace hooks (only if explicitly allowed — executes arbitrary code)
74
+ if (options?.allowWorkspaceHooks) {
75
+ const wsGithubHooks = path.join(workingDirectory, '.github', 'hooks', 'hooks.json');
76
+ if (fs.existsSync(wsGithubHooks)) {
77
+ results.push({ file: wsGithubHooks, baseDir: path.join(workingDirectory, '.github', 'hooks') });
78
+ }
79
+ const wsGithub = path.join(workingDirectory, '.github', 'hooks.json');
80
+ if (fs.existsSync(wsGithub)) {
81
+ results.push({ file: wsGithub, baseDir: path.join(workingDirectory, '.github') });
82
+ }
83
+ const wsRoot = path.join(workingDirectory, 'hooks.json');
84
+ if (fs.existsSync(wsRoot)) {
85
+ results.push({ file: wsRoot, baseDir: workingDirectory });
86
+ }
87
+ }
88
+ return results;
89
+ }
90
+ /**
91
+ * Parse a hooks.json file and return validated hook commands per type.
92
+ */
93
+ function parseHooksConfig(filePath) {
94
+ const result = new Map();
95
+ try {
96
+ const raw = JSON.parse(fs.readFileSync(filePath, 'utf8'));
97
+ const hooks = raw.hooks;
98
+ if (typeof hooks !== 'object' || hooks === null) {
99
+ log.warn(`Invalid hooks.json format (missing "hooks" key): ${filePath}`);
100
+ return result;
101
+ }
102
+ for (const [hookType, commands] of Object.entries(hooks)) {
103
+ if (!VALID_HOOK_TYPES.has(hookType)) {
104
+ log.warn(`Unknown hook type "${hookType}" in ${filePath}, skipping`);
105
+ continue;
106
+ }
107
+ if (!Array.isArray(commands)) {
108
+ log.warn(`Hook type "${hookType}" must be an array in ${filePath}, skipping`);
109
+ continue;
110
+ }
111
+ const valid = [];
112
+ for (const cmd of commands) {
113
+ if (!cmd || typeof cmd !== 'object' || cmd.type !== 'command' || (!cmd.bash && !cmd.powershell)) {
114
+ log.warn(`Invalid hook command for "${hookType}" in ${filePath}, skipping`);
115
+ continue;
116
+ }
117
+ valid.push(cmd);
118
+ }
119
+ if (valid.length > 0) {
120
+ result.set(hookType, valid);
121
+ }
122
+ }
123
+ }
124
+ catch (err) {
125
+ log.warn(`Failed to parse ${filePath}: ${err}`);
126
+ }
127
+ return result;
128
+ }
129
+ /**
130
+ * Execute a hook command by spawning a shell process (async, non-blocking).
131
+ * Input is piped as JSON to stdin, output parsed from stdout.
132
+ */
133
+ async function executeHookCommand(cmd, input, baseDir) {
134
+ const shell = cmd.bash ? 'bash' : 'powershell';
135
+ const script = cmd.bash ?? cmd.powershell;
136
+ const cwd = cmd.cwd ? path.resolve(baseDir, cmd.cwd) : baseDir;
137
+ const timeoutMs = (cmd.timeoutSec ?? 30) * 1000;
138
+ return new Promise((resolve) => {
139
+ let resolved = false;
140
+ const done = (value) => {
141
+ if (resolved)
142
+ return;
143
+ resolved = true;
144
+ clearTimeout(timer);
145
+ resolve(value);
146
+ };
147
+ const child = spawn(shell, ['-c', script], {
148
+ cwd,
149
+ env: { ...process.env, ...cmd.env },
150
+ stdio: ['pipe', 'pipe', 'pipe'],
151
+ });
152
+ const timer = setTimeout(() => {
153
+ log.warn(`Hook command timed out after ${cmd.timeoutSec ?? 30}s: ${script}`);
154
+ child.kill('SIGKILL');
155
+ done(undefined);
156
+ }, timeoutMs);
157
+ let stdout = '';
158
+ let stderr = '';
159
+ child.stdout.on('data', (data) => { stdout += data.toString(); });
160
+ child.stderr.on('data', (data) => { stderr += data.toString(); });
161
+ child.on('close', (code, signal) => {
162
+ if (signal) {
163
+ done(undefined);
164
+ return;
165
+ }
166
+ if (code !== 0) {
167
+ log.warn(`Hook command failed (exit ${code}): ${script}${stderr ? ' — ' + stderr.trim() : ''}`);
168
+ done(undefined);
169
+ return;
170
+ }
171
+ const trimmed = stdout.trim();
172
+ if (!trimmed) {
173
+ done(undefined);
174
+ return;
175
+ }
176
+ try {
177
+ done(JSON.parse(trimmed));
178
+ }
179
+ catch {
180
+ log.warn(`Hook command returned invalid JSON: ${script}`);
181
+ done(undefined);
182
+ }
183
+ });
184
+ child.on('error', (err) => {
185
+ log.warn(`Hook command failed: ${script} — ${err.message}`);
186
+ done(undefined);
187
+ });
188
+ child.stdin.write(JSON.stringify(input));
189
+ child.stdin.end();
190
+ });
191
+ }
192
+ /**
193
+ * Build a SessionHooks callback that runs all commands for a given hook type.
194
+ * For preToolUse: deny > ask > allow precedence. First "deny" or "ask" short-circuits.
195
+ */
196
+ function buildHookCallback(hookType, allCommands) {
197
+ return async (input, _invocation) => {
198
+ log.debug(`Hook callback invoked: ${hookType} (${allCommands.length} command(s)), tool=${input.toolName ?? 'n/a'}`);
199
+ let mergedResult = undefined;
200
+ for (const { cmd, baseDir } of allCommands) {
201
+ const result = await executeHookCommand(cmd, input, baseDir);
202
+ if (!result)
203
+ continue;
204
+ if (!mergedResult) {
205
+ mergedResult = result;
206
+ }
207
+ else {
208
+ Object.assign(mergedResult, result);
209
+ }
210
+ // For preToolUse, deny and ask short-circuit (deny > ask > allow)
211
+ if (hookType === 'preToolUse') {
212
+ if (result.permissionDecision === 'deny' || result.permissionDecision === 'ask') {
213
+ return mergedResult;
214
+ }
215
+ }
216
+ }
217
+ return mergedResult;
218
+ };
219
+ }
220
+ /**
221
+ * Discover and load all hooks for a given workspace.
222
+ * Returns a SessionHooks object ready to pass to the SDK, or undefined if no hooks found.
223
+ */
224
+ export async function loadHooks(workingDirectory, options) {
225
+ const files = discoverHooksFiles(workingDirectory, options);
226
+ if (files.length === 0)
227
+ return undefined;
228
+ // Collect all commands per hook type across all files (all sources append)
229
+ const commandsByType = new Map();
230
+ for (const { file, baseDir } of files) {
231
+ const config = parseHooksConfig(file);
232
+ for (const [hookType, commands] of config) {
233
+ const existing = commandsByType.get(hookType) ?? [];
234
+ for (const cmd of commands) {
235
+ existing.push({ cmd, baseDir });
236
+ }
237
+ commandsByType.set(hookType, existing);
238
+ }
239
+ log.debug(`Loaded hooks config from ${file} (${config.size} hook type(s))`);
240
+ }
241
+ if (commandsByType.size === 0)
242
+ return undefined;
243
+ const hooks = {};
244
+ for (const [hookType, commands] of commandsByType) {
245
+ const sdkKey = HOOK_TYPE_MAP[hookType];
246
+ if (!sdkKey)
247
+ continue;
248
+ hooks[sdkKey] = buildHookCallback(hookType, commands);
249
+ log.info(`Registered ${hookType} hook (${commands.length} command(s))`);
250
+ }
251
+ return Object.keys(hooks).length > 0 ? hooks : undefined;
252
+ }
253
+ /**
254
+ * Return metadata about configured hooks without executing them.
255
+ * Used by /tools to show which hooks are active.
256
+ */
257
+ export function getHooksInfo(workingDirectory, options) {
258
+ const files = discoverHooksFiles(workingDirectory, options);
259
+ if (files.length === 0)
260
+ return [];
261
+ const home = process.env.HOME ?? '';
262
+ // Accumulate command counts per hook type per source
263
+ const info = new Map();
264
+ for (const { file } of files) {
265
+ const config = parseHooksConfig(file);
266
+ const normalized = file.split(path.sep).join('/');
267
+ let source = 'user';
268
+ if (normalized.includes('installed-plugins'))
269
+ source = 'plugin';
270
+ else if (home && normalized.includes(home.split(path.sep).join('/') + '/.copilot/'))
271
+ source = 'user';
272
+ else
273
+ source = 'workspace';
274
+ for (const [hookType, commands] of config) {
275
+ const existing = info.get(hookType);
276
+ if (existing) {
277
+ existing.commandCount += commands.length;
278
+ // Higher-priority source wins for display
279
+ existing.source = source;
280
+ }
281
+ else {
282
+ info.set(hookType, { source, commandCount: commands.length });
283
+ }
284
+ }
285
+ }
286
+ return [...info.entries()]
287
+ .map(([hookType, { source, commandCount }]) => ({ hookType, source, commandCount }))
288
+ .sort((a, b) => a.hookType.localeCompare(b.hookType));
289
+ }
290
+ /**
291
+ * Merge two SessionHooks objects. The override hooks take precedence.
292
+ */
293
+ export function mergeHooks(base, override) {
294
+ if (!base && !override)
295
+ return undefined;
296
+ if (!base)
297
+ return override;
298
+ if (!override)
299
+ return base;
300
+ return { ...base, ...override };
301
+ }
302
+ //# sourceMappingURL=hooks-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-loader.js","sourceRoot":"","sources":["../../src/core/hooks-loader.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { CopilotBridge } from './bridge.js';
2
2
  import { type ChannelPrefs } from '../state/store.js';
3
3
  import { type InterAgentContext } from './inter-agent.js';
4
4
  import type { McpServerInfo } from './command-handler.js';
5
+ import { type HookInfo } from './hooks-loader.js';
5
6
  import type { ChannelAdapter } from '../types.js';
6
7
  /** Custom tools auto-approved without interactive prompt (they enforce workspace boundaries internally). */
7
8
  export declare const BRIDGE_CUSTOM_TOOLS: string[];
@@ -20,9 +21,17 @@ export declare class SessionManager {
20
21
  private lastMessageUserIds;
21
22
  private sessionMcpServers;
22
23
  private sessionSkillDirs;
24
+ private workspaceHooks;
23
25
  private sendFileHandler;
24
26
  private getAdapterForChannel;
25
27
  constructor(bridge: CopilotBridge);
28
+ /** Resolve hooks for a workspace, caching the result. */
29
+ private resolveHooks;
30
+ /**
31
+ * Wrap loaded hooks so that preToolUse "ask" decisions trigger the bridge's
32
+ * interactive permission prompt instead of being ignored by the CLI.
33
+ */
34
+ private wrapHooksWithAsk;
26
35
  /** Register a handler for session events (streaming, tool calls, etc.) */
27
36
  onSessionEvent(handler: SessionEventHandler): void;
28
37
  /** Register handler for the send_file custom tool. */
@@ -42,7 +51,10 @@ export declare class SessionManager {
42
51
  description: string;
43
52
  source: string;
44
53
  pending?: boolean;
54
+ disabled?: boolean;
45
55
  }[];
56
+ /** Get info about configured hooks for a channel's workspace. */
57
+ getHooksInfo(channelId: string): HookInfo[];
46
58
  /** List tools the SDK CLI process has available (via server RPC). */
47
59
  listSessionTools(channelId: string): Promise<{
48
60
  name: string;
@@ -110,6 +122,8 @@ export declare class SessionManager {
110
122
  resolveUserInput(channelId: string, answer: string): boolean;
111
123
  /** Check if channel has a pending permission request. */
112
124
  hasPendingPermission(channelId: string): boolean;
125
+ /** Check if the current pending permission is from a hook (no remember allowed). */
126
+ isHookPermission(channelId: string): boolean;
113
127
  /** Get the current session ID for a channel (if any). */
114
128
  getSessionId(channelId: string): string | undefined;
115
129
  /** Abort the current turn for a channel's session. */
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/core/session-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/core/session-manager.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@ import { addJob, removeJob, pauseJob, resumeJob, listJobs, formatInTimezone } fr
9
9
  import { canCall, createContext, extendContext, getBotWorkspaceMap, buildWorkspacePrompt, buildCallerPrompt, resolveAgentDefinition, } from './inter-agent.js';
10
10
  import { createLogger } from '../logger.js';
11
11
  import { tryWithFallback, isModelError, buildFallbackChain } from './model-fallback.js';
12
+ import { loadHooks, getHooksInfo } from './hooks-loader.js';
12
13
  const log = createLogger('session');
13
14
  /** Custom tools auto-approved without interactive prompt (they enforce workspace boundaries internally). */
14
15
  export const BRIDGE_CUSTOM_TOOLS = ['send_file', 'show_file_in_chat', 'ask_agent', 'schedule'];
@@ -311,6 +312,8 @@ export class SessionManager {
311
312
  sessionMcpServers = new Map(); // channelId → server names
312
313
  // Skill directories that were passed to the session at creation/resume time
313
314
  sessionSkillDirs = new Map(); // channelId → skill dir paths
315
+ // Loaded session hooks per workspace (cached after first load)
316
+ workspaceHooks = new Map();
314
317
  // Handler for send_file tool (set by index.ts, calls adapter.sendFile)
315
318
  sendFileHandler = null;
316
319
  getAdapterForChannel = null;
@@ -319,6 +322,74 @@ export class SessionManager {
319
322
  this.mcpServers = loadMcpServers();
320
323
  ensureWorkspacesDir();
321
324
  }
325
+ /** Resolve hooks for a workspace, caching the result. */
326
+ async resolveHooks(workingDirectory) {
327
+ const cached = this.workspaceHooks.get(workingDirectory);
328
+ if (cached !== undefined || this.workspaceHooks.has(workingDirectory))
329
+ return cached;
330
+ const allowWorkspaceHooks = getConfig().defaults.allowWorkspaceHooks ?? false;
331
+ const hooks = await loadHooks(workingDirectory, { allowWorkspaceHooks });
332
+ this.workspaceHooks.set(workingDirectory, hooks);
333
+ return hooks;
334
+ }
335
+ /**
336
+ * Wrap loaded hooks so that preToolUse "ask" decisions trigger the bridge's
337
+ * interactive permission prompt instead of being ignored by the CLI.
338
+ */
339
+ wrapHooksWithAsk(hooks, channelId) {
340
+ if (!hooks?.onPreToolUse)
341
+ return hooks;
342
+ const originalPreToolUse = hooks.onPreToolUse;
343
+ const wrappedPreToolUse = async (input, invocation) => {
344
+ const result = await originalPreToolUse(input, invocation);
345
+ if (!result || result.permissionDecision !== 'ask')
346
+ return result;
347
+ // Convert "ask" into an interactive permission prompt
348
+ const reason = result.permissionDecisionReason ?? 'Hook requires confirmation';
349
+ const toolName = input.toolName ?? 'unknown';
350
+ return new Promise((resolve) => {
351
+ const entry = {
352
+ sessionId: invocation.sessionId,
353
+ channelId,
354
+ toolName: `hook:${toolName}`,
355
+ serverName: undefined,
356
+ fromHook: true,
357
+ hookReason: reason,
358
+ toolInput: input.toolArgs,
359
+ commands: [],
360
+ resolve: (decision) => {
361
+ if (decision.kind === 'approved') {
362
+ resolve({ ...result, permissionDecision: 'allow' });
363
+ }
364
+ else {
365
+ resolve({ ...result, permissionDecision: 'deny', permissionDecisionReason: reason });
366
+ }
367
+ },
368
+ createdAt: Date.now(),
369
+ };
370
+ let queue = this.pendingPermissions.get(channelId);
371
+ if (!queue) {
372
+ queue = [];
373
+ this.pendingPermissions.set(channelId, queue);
374
+ }
375
+ queue.push(entry);
376
+ if (queue.length === 1) {
377
+ this.eventHandler?.(invocation.sessionId, channelId, {
378
+ type: 'bridge.permission_request',
379
+ data: {
380
+ toolName: `hook:${toolName}`,
381
+ serverName: undefined,
382
+ input: input.toolArgs,
383
+ commands: [],
384
+ hookReason: reason,
385
+ fromHook: true,
386
+ },
387
+ });
388
+ }
389
+ });
390
+ };
391
+ return { ...hooks, onPreToolUse: wrappedPreToolUse };
392
+ }
322
393
  /** Register a handler for session events (streaming, tool calls, etc.) */
323
394
  onSessionEvent(handler) {
324
395
  this.eventHandler = handler;
@@ -380,6 +451,8 @@ export class SessionManager {
380
451
  const workingDirectory = this.resolveWorkingDirectory(channelId);
381
452
  const dirs = discoverSkillDirectories(workingDirectory);
382
453
  const sessionDirs = this.sessionSkillDirs.get(channelId);
454
+ const prefs = getChannelPrefs(channelId);
455
+ const disabledSet = new Set(prefs?.disabledSkills ?? []);
383
456
  const skills = [];
384
457
  const home = process.env.HOME;
385
458
  for (const dir of dirs) {
@@ -409,10 +482,16 @@ export class SessionManager {
409
482
  }
410
483
  catch { /* skip */ }
411
484
  }
412
- skills.push({ name, description, source, pending: sessionDirs ? !sessionDirs.has(dir) : undefined });
485
+ skills.push({ name, description, source, pending: sessionDirs ? !sessionDirs.has(dir) : undefined, disabled: disabledSet.has(name) });
413
486
  }
414
487
  return skills.sort((a, b) => a.name.localeCompare(b.name));
415
488
  }
489
+ /** Get info about configured hooks for a channel's workspace. */
490
+ getHooksInfo(channelId) {
491
+ const workingDirectory = this.resolveWorkingDirectory(channelId);
492
+ const allowWorkspaceHooks = getConfig().defaults.allowWorkspaceHooks ?? false;
493
+ return getHooksInfo(workingDirectory, { allowWorkspaceHooks });
494
+ }
416
495
  /** List tools the SDK CLI process has available (via server RPC). */
417
496
  async listSessionTools(channelId) {
418
497
  // Ensure there's an active session so the CLI process is running
@@ -735,6 +814,7 @@ export class SessionManager {
735
814
  threadedReplies: storedPrefs?.threadedReplies ?? configChannel.threadedReplies,
736
815
  permissionMode: storedPrefs?.permissionMode ?? configChannel.permissionMode,
737
816
  reasoningEffort: storedPrefs?.reasoningEffort ?? configChannel.reasoningEffort ?? null,
817
+ disabledSkills: storedPrefs?.disabledSkills,
738
818
  };
739
819
  }
740
820
  /** Get model info (for checking capabilities like reasoning effort). */
@@ -812,7 +892,7 @@ export class SessionManager {
812
892
  if (!queue || queue.length === 0)
813
893
  return false;
814
894
  const pending = queue.shift();
815
- if (remember) {
895
+ if (remember && !pending.fromHook) {
816
896
  const action = allow ? 'allow' : 'deny';
817
897
  if (pending.serverName) {
818
898
  // MCP tool: save at server level so all tools on this server are covered
@@ -844,6 +924,8 @@ export class SessionManager {
844
924
  serverName: next.serverName,
845
925
  input: next.toolInput,
846
926
  commands: next.commands,
927
+ fromHook: next.fromHook,
928
+ hookReason: next.hookReason,
847
929
  },
848
930
  });
849
931
  }
@@ -878,6 +960,11 @@ export class SessionManager {
878
960
  const queue = this.pendingPermissions.get(channelId);
879
961
  return !!queue && queue.length > 0;
880
962
  }
963
+ /** Check if the current pending permission is from a hook (no remember allowed). */
964
+ isHookPermission(channelId) {
965
+ const queue = this.pendingPermissions.get(channelId);
966
+ return !!queue && queue.length > 0 && !!queue[0].fromHook;
967
+ }
881
968
  /** Get the current session ID for a channel (if any). */
882
969
  getSessionId(channelId) {
883
970
  return this.channelSessions.get(channelId) ?? getChannelSession(channelId) ?? undefined;
@@ -949,6 +1036,7 @@ export class SessionManager {
949
1036
  const reasoningEffort = prefs.reasoningEffort;
950
1037
  const skillDirectories = discoverSkillDirectories(workingDirectory);
951
1038
  const customTools = this.buildCustomTools(channelId);
1039
+ const disabledSkills = prefs.disabledSkills?.length ? prefs.disabledSkills : undefined;
952
1040
  // Resolve fallback configuration
953
1041
  const configChannel = getChannelConfig(channelId);
954
1042
  const configFallbacks = configChannel.fallbackModels ?? getConfig().defaults.fallbackModels;
@@ -962,6 +1050,11 @@ export class SessionManager {
962
1050
  log.warn('Failed to fetch model list for fallback resolution');
963
1051
  }
964
1052
  const resolvedMcpServers = this.resolveMcpServers(workingDirectory);
1053
+ const rawHooks = await this.resolveHooks(workingDirectory);
1054
+ const hooks = this.wrapHooksWithAsk(rawHooks, channelId);
1055
+ if (hooks) {
1056
+ log.debug(`Hooks resolved for session create: ${Object.keys(hooks).join(', ')}`);
1057
+ }
965
1058
  const createWithModel = async (model) => {
966
1059
  return withWorkspaceEnv(workingDirectory, () => this.bridge.createSession({
967
1060
  model,
@@ -970,9 +1063,11 @@ export class SessionManager {
970
1063
  reasoningEffort: reasoningEffort ?? undefined,
971
1064
  mcpServers: resolvedMcpServers,
972
1065
  skillDirectories: skillDirectories.length > 0 ? skillDirectories : undefined,
1066
+ disabledSkills,
973
1067
  onPermissionRequest: (request, invocation) => this.handlePermissionRequest(channelId, request, invocation),
974
1068
  onUserInputRequest: (request, invocation) => this.handleUserInputRequest(channelId, request, invocation),
975
1069
  tools: customTools.length > 0 ? customTools : undefined,
1070
+ hooks,
976
1071
  }));
977
1072
  };
978
1073
  const { result: session, usedModel, didFallback } = await tryWithFallback(prefs.model, availableModels, configFallbacks, createWithModel);
@@ -1004,7 +1099,13 @@ export class SessionManager {
1004
1099
  const reasoningEffort = prefs.reasoningEffort;
1005
1100
  const skillDirectories = discoverSkillDirectories(workingDirectory);
1006
1101
  const customTools = this.buildCustomTools(channelId);
1102
+ const disabledSkills = prefs.disabledSkills?.length ? prefs.disabledSkills : undefined;
1007
1103
  const mcpServers = this.resolveMcpServers(workingDirectory);
1104
+ const rawHooks = await this.resolveHooks(workingDirectory);
1105
+ const hooks = this.wrapHooksWithAsk(rawHooks, channelId);
1106
+ if (hooks) {
1107
+ log.debug(`Hooks resolved for session resume: ${Object.keys(hooks).join(', ')}`);
1108
+ }
1008
1109
  const session = await withWorkspaceEnv(workingDirectory, () => this.bridge.resumeSession(sessionId, {
1009
1110
  onPermissionRequest: (request, invocation) => this.handlePermissionRequest(channelId, request, invocation),
1010
1111
  onUserInputRequest: (request, invocation) => this.handleUserInputRequest(channelId, request, invocation),
@@ -1013,7 +1114,9 @@ export class SessionManager {
1013
1114
  reasoningEffort: reasoningEffort ?? undefined,
1014
1115
  mcpServers,
1015
1116
  skillDirectories: skillDirectories.length > 0 ? skillDirectories : undefined,
1117
+ disabledSkills,
1016
1118
  tools: customTools.length > 0 ? customTools : undefined,
1119
+ hooks,
1017
1120
  }));
1018
1121
  this.sessionMcpServers.set(channelId, new Set(Object.keys(mcpServers)));
1019
1122
  this.sessionSkillDirs.set(channelId, new Set(skillDirectories));
@@ -1050,6 +1153,7 @@ export class SessionManager {
1050
1153
  }
1051
1154
  const defaultConfigDir = process.env.HOME ? `${process.env.HOME}/.copilot` : undefined;
1052
1155
  const skillDirectories = discoverSkillDirectories(targetWorkspace);
1156
+ const hooks = await this.resolveHooks(targetWorkspace);
1053
1157
  // Build ephemeral permission handler
1054
1158
  const ephemeralPermissionHandler = this.buildEphemeralPermissionHandler(opts);
1055
1159
  // Build custom tools for ephemeral session (ask_agent with propagated context)
@@ -1065,6 +1169,7 @@ export class SessionManager {
1065
1169
  onPermissionRequest: ephemeralPermissionHandler,
1066
1170
  systemMessage: { content: systemParts.filter(Boolean).join('\n\n') },
1067
1171
  tools: ephemeralTools.length > 0 ? ephemeralTools : undefined,
1172
+ hooks,
1068
1173
  }));
1069
1174
  // Send message and wait for idle
1070
1175
  const response = await this.sendAndWaitForIdle(session, opts.message, timeout);