@adhdev/daemon-core 0.8.29 → 0.8.31

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 (63) hide show
  1. package/dist/agent-stream/manager.d.ts +1 -0
  2. package/dist/agent-stream/provider-adapter.d.ts +1 -0
  3. package/dist/agent-stream/types.d.ts +3 -0
  4. package/dist/boot/daemon-lifecycle.d.ts +2 -1
  5. package/dist/cdp/manager.d.ts +2 -0
  6. package/dist/cli-adapter-types.d.ts +34 -5
  7. package/dist/cli-adapters/provider-cli-adapter.d.ts +4 -158
  8. package/dist/cli-adapters/provider-cli-config.d.ts +30 -0
  9. package/dist/cli-adapters/provider-cli-parse.d.ts +42 -0
  10. package/dist/cli-adapters/provider-cli-runtime.d.ts +29 -0
  11. package/dist/cli-adapters/provider-cli-shared.d.ts +158 -0
  12. package/dist/commands/handler.d.ts +4 -3
  13. package/dist/config/config.d.ts +4 -3
  14. package/dist/index.js +1033 -621
  15. package/dist/index.js.map +1 -1
  16. package/dist/index.mjs +1035 -624
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/providers/acp-provider-instance.d.ts +1 -0
  19. package/dist/providers/approval-utils.d.ts +7 -0
  20. package/dist/providers/cli-provider-instance.d.ts +2 -0
  21. package/dist/providers/contracts.d.ts +12 -1
  22. package/dist/providers/ide-provider-instance.d.ts +1 -0
  23. package/dist/providers/provider-loader.d.ts +3 -0
  24. package/dist/status/reporter.d.ts +2 -3
  25. package/dist/status/snapshot.d.ts +2 -1
  26. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  27. package/package.json +3 -1
  28. package/src/agent-stream/manager.ts +8 -2
  29. package/src/agent-stream/poller.ts +57 -6
  30. package/src/agent-stream/provider-adapter.ts +11 -7
  31. package/src/agent-stream/types.ts +3 -0
  32. package/src/boot/daemon-lifecycle.ts +7 -6
  33. package/src/cdp/initializer.ts +2 -2
  34. package/src/cdp/manager.ts +5 -0
  35. package/src/cdp/setup.ts +1 -1
  36. package/src/cli-adapter-types.ts +37 -5
  37. package/src/cli-adapters/provider-cli-adapter.ts +212 -795
  38. package/src/cli-adapters/provider-cli-config.ts +66 -0
  39. package/src/cli-adapters/provider-cli-parse.ts +202 -0
  40. package/src/cli-adapters/provider-cli-runtime.ts +142 -0
  41. package/src/cli-adapters/provider-cli-shared.ts +439 -0
  42. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +1 -1
  43. package/src/commands/cdp-commands.ts +6 -1
  44. package/src/commands/chat-commands.ts +45 -29
  45. package/src/commands/cli-manager.ts +28 -9
  46. package/src/commands/handler.ts +14 -10
  47. package/src/commands/router.ts +23 -10
  48. package/src/commands/stream-commands.ts +11 -5
  49. package/src/config/config.ts +4 -10
  50. package/src/daemon/dev-auto-implement.ts +22 -18
  51. package/src/daemon/dev-cli-debug.ts +59 -16
  52. package/src/daemon/dev-server.ts +67 -43
  53. package/src/providers/acp-provider-instance.ts +18 -3
  54. package/src/providers/approval-utils.ts +66 -0
  55. package/src/providers/cli-provider-instance.ts +32 -6
  56. package/src/providers/contracts.d.ts +1 -0
  57. package/src/providers/contracts.ts +15 -2
  58. package/src/providers/extension-provider-instance.ts +1 -1
  59. package/src/providers/ide-provider-instance.ts +67 -41
  60. package/src/providers/provider-loader.ts +110 -55
  61. package/src/providers/version-archive.ts +23 -5
  62. package/src/status/reporter.ts +18 -14
  63. package/src/status/snapshot.ts +5 -4
@@ -20,7 +20,7 @@ import * as fs from 'fs';
20
20
  import * as path from 'path';
21
21
  import * as os from 'os';
22
22
  import type { ProviderLoader } from '../providers/provider-loader.js';
23
- import type { ProviderCategory } from '../providers/contracts.js';
23
+ import type { ProviderCategory, ProviderModule, ProviderScripts, ProviderSettingDef } from '../providers/contracts.js';
24
24
  import type { ChildProcess } from 'child_process';
25
25
  import type { DaemonCdpManager } from '../cdp/manager.js';
26
26
  import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
@@ -34,6 +34,64 @@ import { handleAutoImplement, handleAutoImplCancel, handleAutoImplSSE } from './
34
34
 
35
35
  export const DEV_SERVER_PORT = 19280;
36
36
 
37
+ interface ProviderListEntry {
38
+ type: string;
39
+ name: string;
40
+ category: ProviderCategory;
41
+ icon: string | null;
42
+ displayName: string;
43
+ scripts?: string[];
44
+ inputMethod?: ProviderModule['inputMethod'] | null;
45
+ inputSelector?: string | null;
46
+ extensionId?: string | null;
47
+ cdpPorts?: [number, number] | [];
48
+ spawn?: ProviderModule['spawn'] | null;
49
+ auth?: ProviderModule['auth'] | null;
50
+ install?: string | null;
51
+ hasSettings?: boolean;
52
+ settingsCount?: number;
53
+ }
54
+
55
+ function getScriptNames(scripts?: ProviderScripts): string[] {
56
+ if (!scripts) return [];
57
+ return Object.entries(scripts)
58
+ .filter(([, value]) => typeof value === 'function')
59
+ .map(([name]) => name);
60
+ }
61
+
62
+ function toProviderListEntry(provider: ProviderModule): ProviderListEntry {
63
+ const base: ProviderListEntry = {
64
+ type: provider.type,
65
+ name: provider.name,
66
+ category: provider.category,
67
+ icon: provider.icon || null,
68
+ displayName: provider.displayName || provider.name,
69
+ };
70
+
71
+ if (provider.category === 'ide' || provider.category === 'extension') {
72
+ base.scripts = getScriptNames(provider.scripts);
73
+ base.inputMethod = provider.inputMethod || null;
74
+ base.inputSelector = provider.inputSelector || null;
75
+ base.extensionId = provider.extensionId || null;
76
+ base.cdpPorts = provider.cdpPorts || [];
77
+ }
78
+
79
+ if (provider.category === 'acp') {
80
+ base.spawn = provider.spawn || null;
81
+ base.auth = provider.auth || null;
82
+ base.install = provider.install || null;
83
+ base.hasSettings = !!provider.settings;
84
+ base.settingsCount = provider.settings ? Object.keys(provider.settings).length : 0;
85
+ }
86
+
87
+ if (provider.category === 'cli') {
88
+ base.spawn = provider.spawn || null;
89
+ base.install = provider.install || null;
90
+ }
91
+
92
+ return base;
93
+ }
94
+
37
95
  export class DevServer implements DevServerContext {
38
96
  private server: http.Server | null = null;
39
97
  public providerLoader: ProviderLoader;
@@ -212,41 +270,7 @@ export class DevServer implements DevServerContext {
212
270
  // ─── Handlers ───
213
271
 
214
272
  private async handleListProviders(_req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
215
- const providers = this.providerLoader.getAll().map(p => {
216
- const base: any = {
217
- type: p.type,
218
- name: p.name,
219
- category: p.category,
220
- icon: (p as any).icon || null,
221
- displayName: (p as any).displayName || p.name,
222
- };
223
-
224
- // IDE/Extension specific
225
- if (p.category === 'ide' || p.category === 'extension') {
226
- base.scripts = p.scripts ? Object.keys(p.scripts).filter(k => typeof (p.scripts as any)[k] === 'function') : [];
227
- base.inputMethod = p.inputMethod || null;
228
- base.inputSelector = (p as any).inputSelector || null;
229
- base.extensionId = p.extensionId || null;
230
- base.cdpPorts = (p as any).cdpPorts || [];
231
- }
232
-
233
- // ACP specific
234
- if (p.category === 'acp') {
235
- base.spawn = (p as any).spawn || null;
236
- base.auth = (p as any).auth || null;
237
- base.install = (p as any).install || null;
238
- base.hasSettings = !!(p as any).settings;
239
- base.settingsCount = (p as any).settings ? Object.keys((p as any).settings).length : 0;
240
- }
241
-
242
- // CLI specific
243
- if (p.category === 'cli') {
244
- base.spawn = (p as any).spawn || null;
245
- base.install = (p as any).install || null;
246
- }
247
-
248
- return base;
249
- });
273
+ const providers = this.providerLoader.getAll().map(toProviderListEntry);
250
274
  this.json(res, 200, { providers, count: providers.length });
251
275
  }
252
276
 
@@ -273,7 +297,7 @@ export class DevServer implements DevServerContext {
273
297
  return;
274
298
  }
275
299
 
276
- const spawn = (provider as any).spawn;
300
+ const spawn = provider.spawn;
277
301
  if (!spawn) {
278
302
  this.json(res, 400, { error: `Provider ${type} has no spawn config` });
279
303
  return;
@@ -330,7 +354,7 @@ export class DevServer implements DevServerContext {
330
354
  return;
331
355
  }
332
356
 
333
- const fn = (provider.scripts as any)?.[scriptName];
357
+ const fn = provider.scripts?.[scriptName];
334
358
  if (typeof fn !== 'function') {
335
359
  this.json(res, 400, { error: `Script '${scriptName}' not found in provider '${type}'`, available: provider.scripts ? Object.keys(provider.scripts) : [] });
336
360
  return;
@@ -446,7 +470,7 @@ export class DevServer implements DevServerContext {
446
470
  }));
447
471
  for (const cdp of this.cdpManagers.values()) {
448
472
  if (!cdp.isConnected) {
449
- (cdp as any)._targetId = null;
473
+ cdp.clearTargetId();
450
474
  }
451
475
  }
452
476
  this.json(res, 200, { reloaded: true, providers });
@@ -569,7 +593,7 @@ export class DevServer implements DevServerContext {
569
593
  this.sendSSE({ type: 'watch_error', error: `Provider '${this.watchScriptPath}' not found` });
570
594
  return;
571
595
  }
572
- const fn = (provider.scripts as any)?.[this.watchScriptName!];
596
+ const fn = provider.scripts?.[this.watchScriptName!];
573
597
  if (typeof fn !== 'function') {
574
598
  this.sendSSE({ type: 'watch_error', error: `Script '${this.watchScriptName}' not found` });
575
599
  return;
@@ -769,7 +793,7 @@ export class DevServer implements DevServerContext {
769
793
  // Settings validation
770
794
  if (config.settings) {
771
795
  for (const [key, val] of Object.entries(config.settings)) {
772
- const s = val as any;
796
+ const s = val as Partial<ProviderSettingDef>;
773
797
  if (!s.type) errors.push(`settings.${key}: missing type`);
774
798
  else if (!['boolean', 'number', 'string', 'select'].includes(s.type))
775
799
  errors.push(`settings.${key}: invalid type '${s.type}'`);
@@ -784,7 +808,7 @@ export class DevServer implements DevServerContext {
784
808
  if (config.cdpPorts && Array.isArray(config.cdpPorts)) {
785
809
  const allProviders = this.providerLoader.getAll();
786
810
  for (const port of config.cdpPorts) {
787
- const conflict = allProviders.find(p => p.type !== type && (p as any).cdpPorts?.includes(port));
811
+ const conflict = allProviders.find(p => p.type !== type && p.cdpPorts?.includes(port));
788
812
  if (conflict) warnings.push(`CDP port ${port} conflicts with provider '${conflict.type}'`);
789
813
  }
790
814
  }
@@ -801,7 +825,7 @@ export class DevServer implements DevServerContext {
801
825
  if (!message) { this.json(res, 400, { error: 'message required' }); return; }
802
826
  const provider = this.providerLoader.getMeta(type);
803
827
  if (!provider) { this.json(res, 404, { error: `Provider not found: ${type}` }); return; }
804
- const spawn = (provider as any).spawn;
828
+ const spawn = provider.spawn;
805
829
  if (!spawn) { this.json(res, 400, { error: `Provider ${type} has no spawn config` }); return; }
806
830
 
807
831
  const { spawn: spawnFn } = await import('child_process');
@@ -232,7 +232,7 @@ export class AcpProviderInstance implements ProviderInstance {
232
232
  ...(this.currentModel ? { model: this.currentModel } : {}),
233
233
  ...(this.currentMode ? { mode: this.currentMode } : {}),
234
234
  },
235
- providerControls: this.provider.controls as any,
235
+ providerControls: this.provider.controls,
236
236
  };
237
237
  }
238
238
 
@@ -600,8 +600,10 @@ export class AcpProviderInstance implements ProviderInstance {
600
600
  }
601
601
 
602
602
  // ─── Auto-approve: skip user confirmation ───
603
- if (this.settings.autoApprove) {
604
- this.log.info(`[${this.type}] Auto-approving: ${tc.title || tc.toolCallId}`);
603
+ if (this.settings.autoApprove !== false) {
604
+ const toolTitle = tc.title || tc.toolCallId || 'tool call';
605
+ this.log.info(`[${this.type}] Auto-approving: ${toolTitle}`);
606
+ this.appendSystemMessage(`Auto-approved: ${toolTitle}`);
605
607
  const allowOption = params.options.find(o => o.kind === 'allow_once') || params.options.find(o => o.kind === 'allow_always');
606
608
  if (allowOption) {
607
609
  return { outcome: { outcome: 'selected', optionId: allowOption.optionId } };
@@ -1143,6 +1145,19 @@ export class AcpProviderInstance implements ProviderInstance {
1143
1145
  if (this.events.length > 50) this.events = this.events.slice(-50);
1144
1146
  }
1145
1147
 
1148
+ private appendSystemMessage(content: string, timestamp = Date.now()): void {
1149
+ const normalizedContent = String(content || '').trim();
1150
+ if (!normalizedContent) return;
1151
+ this.messages.push({
1152
+ role: 'system',
1153
+ content: normalizedContent,
1154
+ timestamp,
1155
+ });
1156
+ if (this.messages.length > 200) {
1157
+ this.messages = this.messages.slice(-100);
1158
+ }
1159
+ }
1160
+
1146
1161
  private flushEvents(): ProviderEvent[] {
1147
1162
  const events = [...this.events];
1148
1163
  this.events = [];
@@ -0,0 +1,66 @@
1
+ import type { ProviderModule } from './contracts.js';
2
+
3
+ const DEFAULT_APPROVAL_POSITIVE_HINTS = [
4
+ 'run',
5
+ 'approve',
6
+ 'accept',
7
+ 'allow once',
8
+ 'always allow',
9
+ 'allow',
10
+ 'yes',
11
+ 'proceed',
12
+ 'continue',
13
+ 'confirm',
14
+ 'save',
15
+ 'ok',
16
+ 'trust',
17
+ ];
18
+
19
+ function normalizeApprovalLabel(value: string): string {
20
+ return String(value || '')
21
+ .toLowerCase()
22
+ .replace(/[^\p{L}\p{N}]+/gu, ' ')
23
+ .trim();
24
+ }
25
+
26
+ export function getApprovalPositiveHints(provider?: Pick<ProviderModule, 'approvalPositiveHints'> | null): string[] {
27
+ const customHints = Array.isArray(provider?.approvalPositiveHints)
28
+ ? provider.approvalPositiveHints
29
+ .map((hint) => normalizeApprovalLabel(String(hint || '')))
30
+ .filter(Boolean)
31
+ : [];
32
+ return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
33
+ }
34
+
35
+ export function pickApprovalButton(
36
+ buttons: string[] | null | undefined,
37
+ provider?: Pick<ProviderModule, 'approvalPositiveHints'> | null,
38
+ ): { index: number; label: string } {
39
+ const labels = (buttons || []).map((button) => String(button || '').trim()).filter(Boolean);
40
+ if (labels.length === 0) {
41
+ return { index: 0, label: 'Approve' };
42
+ }
43
+
44
+ const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
45
+ const hints = getApprovalPositiveHints(provider);
46
+
47
+ for (const hint of hints) {
48
+ const exactIndex = normalizedButtons.findIndex((label) => label === hint);
49
+ if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
50
+
51
+ const prefixIndex = normalizedButtons.findIndex((label) => label.startsWith(hint));
52
+ if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
53
+
54
+ const includeIndex = normalizedButtons.findIndex((label) => label.includes(hint));
55
+ if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
56
+ }
57
+
58
+ return { index: 0, label: labels[0] };
59
+ }
60
+
61
+ export function formatAutoApprovalMessage(modalMessage?: string, buttonLabel?: string): string {
62
+ const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ''}`];
63
+ const cleanMessage = String(modalMessage || '').trim();
64
+ if (cleanMessage) lines.push(cleanMessage);
65
+ return lines.join('\n');
66
+ }
@@ -20,6 +20,7 @@ import { ChatHistoryWriter, readChatHistory } from '../config/chat-history.js';
20
20
  import { LOG } from '../logging/logger.js';
21
21
  import type { ChatMessage } from '../types.js';
22
22
  import { extractProviderControlValues, normalizeProviderEffects } from './control-effects.js';
23
+ import { formatAutoApprovalMessage, pickApprovalButton } from './approval-utils.js';
23
24
 
24
25
  let CachedDatabaseSync: (new (path: string, options?: { readOnly?: boolean }) => {
25
26
  prepare(sql: string): { get(...params: Array<string | number>): unknown };
@@ -100,7 +101,7 @@ export class CliProviderInstance implements ProviderInstance {
100
101
  this.providerSessionId = options?.providerSessionId;
101
102
  this.launchMode = options?.launchMode || 'new';
102
103
  this.onProviderSessionResolved = options?.onProviderSessionResolved;
103
- this.adapter = new ProviderCliAdapter(provider as any as CliProviderModule, workingDir, cliArgs, transportFactory);
104
+ this.adapter = new ProviderCliAdapter(provider as CliProviderModule, workingDir, cliArgs, transportFactory);
104
105
  this.monitor = new StatusMonitor();
105
106
  this.historyWriter = new ChatHistoryWriter();
106
107
  }
@@ -249,6 +250,8 @@ export class CliProviderInstance implements ProviderInstance {
249
250
  getState(): ProviderState {
250
251
  const adapterStatus = this.adapter.getStatus();
251
252
  const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
253
+ const autoApproveActive = adapterStatus.status === 'waiting_approval' && this.shouldAutoApprove();
254
+ const visibleStatus = autoApproveActive ? 'generating' : adapterStatus.status;
252
255
  const parsedProviderSessionId = typeof parsedStatus?.providerSessionId === 'string'
253
256
  ? parsedStatus.providerSessionId.trim()
254
257
  : '';
@@ -297,14 +300,16 @@ export class CliProviderInstance implements ProviderInstance {
297
300
  type: this.type,
298
301
  name: this.provider.name,
299
302
  category: 'cli',
300
- status: adapterStatus.status,
303
+ status: visibleStatus,
301
304
  mode: this.presentationMode,
302
305
  activeChat: {
303
306
  id: `${this.type}_${this.workingDir}`,
304
307
  title: parsedStatus?.title || dirName,
305
- status: parsedStatus?.status || adapterStatus.status,
308
+ status: autoApproveActive && parsedStatus?.status === 'waiting_approval'
309
+ ? 'generating'
310
+ : (parsedStatus?.status || visibleStatus),
306
311
  messages: mergedMessages,
307
- activeModal: parsedStatus?.activeModal ?? adapterStatus.activeModal,
312
+ activeModal: autoApproveActive ? null : (parsedStatus?.activeModal ?? adapterStatus.activeModal),
308
313
  inputContent: '',
309
314
  },
310
315
  workspace: this.workingDir,
@@ -323,7 +328,7 @@ export class CliProviderInstance implements ProviderInstance {
323
328
  } : undefined,
324
329
  resume: this.provider.resume,
325
330
  controlValues: this.controlValues,
326
- providerControls: this.provider.controls as any,
331
+ providerControls: this.provider.controls,
327
332
  };
328
333
  }
329
334
 
@@ -375,7 +380,16 @@ export class CliProviderInstance implements ProviderInstance {
375
380
  const now = Date.now();
376
381
  const adapterStatus = this.adapter.getStatus();
377
382
  const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
378
- const newStatus = adapterStatus.status;
383
+ const rawStatus = adapterStatus.status;
384
+ const autoApproveActive = rawStatus === 'waiting_approval' && this.shouldAutoApprove();
385
+ if (autoApproveActive) {
386
+ const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(adapterStatus.activeModal?.buttons, this.provider);
387
+ this.recordAutoApproval(adapterStatus.activeModal?.message, buttonLabel, now);
388
+ setTimeout(() => {
389
+ this.adapter.resolveModal(buttonIndex);
390
+ }, 0);
391
+ }
392
+ const newStatus = autoApproveActive ? 'generating' : rawStatus;
379
393
  const dirName = this.workingDir.split('/').filter(Boolean).pop() || 'session';
380
394
  const chatTitle = `${this.provider.name} · ${dirName}`;
381
395
  const partial = this.adapter.getPartialResponse();
@@ -603,6 +617,18 @@ export class CliProviderInstance implements ProviderInstance {
603
617
  get cliType(): string { return this.type; }
604
618
  get cliName(): string { return this.provider.name; }
605
619
 
620
+ private shouldAutoApprove(): boolean {
621
+ return this.settings.autoApprove !== false;
622
+ }
623
+
624
+ private recordAutoApproval(modalMessage?: string, buttonLabel?: string, now = Date.now()): void {
625
+ this.appendRuntimeSystemMessage(
626
+ formatAutoApprovalMessage(modalMessage, buttonLabel),
627
+ `auto_approval:${now}:${buttonLabel || 'approve'}`,
628
+ now,
629
+ );
630
+ }
631
+
606
632
  recordApprovalSelection(buttonText: string): void {
607
633
  const cleanButton = String(buttonText || '').trim();
608
634
  if (!cleanButton) return;
@@ -253,6 +253,7 @@ export interface ProviderModule {
253
253
  };
254
254
  cleanOutput?: (raw: string, lastUserInput?: string) => string;
255
255
  resume?: ProviderResumeCapability;
256
+ approvalPositiveHints?: string[];
256
257
  scripts?: ProviderScripts;
257
258
  vscodeCommands?: {
258
259
  focusPanel?: string;
@@ -304,6 +304,13 @@ export interface CdpTargetFilter {
304
304
  titleExcludes?: string;
305
305
  }
306
306
 
307
+ export type ProviderVersionCommand = string | Partial<Record<string, string>>;
308
+
309
+ export interface ProviderCompatibilityEntry {
310
+ ideVersion: string;
311
+ scriptDir: string;
312
+ }
313
+
307
314
  export interface ProviderModule {
308
315
  /** Unique identifier (e.g. 'cline', 'cursor', 'gemini-cli') */
309
316
  type: string;
@@ -328,7 +335,7 @@ export interface ProviderModule {
328
335
  /** Install instructions (shown when command is missing) */
329
336
  install?: string;
330
337
  /** Custom version detection command (e.g. 'cursor --version', 'claude -v') */
331
- versionCommand?: string;
338
+ versionCommand?: ProviderVersionCommand;
332
339
  /** Versions tested by provider maintainer (informational) */
333
340
  testedVersions?: string[];
334
341
  /** Per-OS process names — used by launch.ts to detect/kill IDE processes */
@@ -371,6 +378,9 @@ export interface ProviderModule {
371
378
  // ─── Extension category only ───
372
379
  extensionId?: string;
373
380
  extensionIdPattern?: RegExp;
381
+ extensionIdPattern_flags?: string;
382
+ compatibility?: ProviderCompatibilityEntry[];
383
+ defaultScriptDir?: string;
374
384
 
375
385
  // ─── CLI category only ───
376
386
  binary?: string;
@@ -380,6 +390,7 @@ export interface ProviderModule {
380
390
  shell?: boolean;
381
391
  env?: Record<string, string>;
382
392
  };
393
+ approvalKeys?: Record<number, string>;
383
394
  patterns?: {
384
395
  prompt?: RegExp[];
385
396
  generating?: RegExp[];
@@ -388,8 +399,10 @@ export interface ProviderModule {
388
399
  };
389
400
  cleanOutput?: (raw: string, lastUserInput?: string) => string;
390
401
  resume?: ProviderResumeCapability;
391
- /** Session ID probe config — auto-discovers provider session ID from local SQLite DB */
402
+ /** Session ID probe config — auto-discovers provider session ID from local SQLite DB */
392
403
  sessionProbe?: ProviderSessionProbe;
404
+ /** Approval button priority hints used when auto-approve must pick a positive action */
405
+ approvalPositiveHints?: string[];
393
406
 
394
407
  // ─── CDP scripts (ide/extension category) ───
395
408
  scripts?: ProviderScripts;
@@ -92,7 +92,7 @@ export class ExtensionProviderInstance implements ProviderInstance {
92
92
  currentModel: this.currentModel || undefined,
93
93
  currentPlan: this.currentMode || undefined,
94
94
  controlValues: this.controlValues,
95
- providerControls: this.provider.controls as any,
95
+ providerControls: this.provider.controls,
96
96
  agentStreams: this.agentStreams,
97
97
  instanceId: this.instanceId,
98
98
  lastUpdated: Date.now(),