@adhdev/daemon-core 1.0.18-rc.16 → 1.0.18-rc.17
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.
- package/dist/commands/cli-manager.d.ts +9 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +302 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +300 -33
- package/dist/index.mjs.map +1 -1
- package/dist/providers/auto-approve-modes.d.ts +14 -0
- package/dist/providers/cli-provider-instance.d.ts +4 -0
- package/dist/providers/contracts.d.ts +17 -0
- package/dist/providers/provider-schema.d.ts +1 -0
- package/dist/repo-mesh-types.d.ts +16 -6
- package/dist/shared-types.d.ts +3 -1
- package/package.json +3 -3
- package/src/commands/cli-manager.ts +54 -8
- package/src/index.ts +3 -1
- package/src/mesh/mesh-event-forwarding.ts +6 -2
- package/src/mesh/mesh-queue-assignment.ts +13 -6
- package/src/providers/auto-approve-modes.ts +97 -0
- package/src/providers/cli-provider-instance.ts +45 -13
- package/src/providers/contracts.ts +25 -1
- package/src/providers/provider-schema.ts +83 -0
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +44 -0
- package/src/repo-mesh-types.ts +65 -12
- package/src/shared-types.ts +3 -1
- package/src/status/snapshot.ts +2 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AutoApproveMode, AutoApproveModeRisk, AutoApproveModeStrategy, ProviderModule } from './contracts.js';
|
|
2
|
+
export interface ResolvedAutoApproveMode {
|
|
3
|
+
active: boolean;
|
|
4
|
+
strategy: AutoApproveModeStrategy;
|
|
5
|
+
modeId: string;
|
|
6
|
+
}
|
|
7
|
+
/** Runtime defense-in-depth for provider definitions that bypass schema validation. */
|
|
8
|
+
export declare function deriveAutoApproveModeRisk(mode: Pick<AutoApproveMode, 'risk' | 'launchArgs'>): AutoApproveModeRisk;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve new mode settings before the legacy boolean. A stale/unknown explicit
|
|
11
|
+
* mode id fails closed instead of falling through to an enabled legacy setting.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveProviderAutoApproveMode(provider: ProviderModule, settings: Record<string, unknown> | undefined): ResolvedAutoApproveMode;
|
|
14
|
+
export declare function findProviderAutoApproveMode(provider: ProviderModule | undefined, modeId: string): AutoApproveMode | undefined;
|
|
@@ -810,7 +810,11 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
810
810
|
getAdapter(): ProviderCliAdapter;
|
|
811
811
|
get cliType(): string;
|
|
812
812
|
get cliName(): string;
|
|
813
|
+
private resolveAutoApproveMode;
|
|
814
|
+
/** Legacy boolean view retained for internal/test compatibility. */
|
|
813
815
|
private shouldAutoApprove;
|
|
816
|
+
private shouldUsePtyAutoApprove;
|
|
817
|
+
private resetPtyAutoApproveState;
|
|
814
818
|
/** @see ProviderInstance.noteManualInteraction */
|
|
815
819
|
noteManualInteraction(now?: number, opts?: {
|
|
816
820
|
passive?: boolean;
|
|
@@ -389,6 +389,21 @@ export interface ProviderCompatibilityEntry {
|
|
|
389
389
|
ideVersion: string;
|
|
390
390
|
scriptDir: string;
|
|
391
391
|
}
|
|
392
|
+
export type AutoApproveModeStrategy = 'pty-parse-default' | 'launch-args' | 'post-boot-command';
|
|
393
|
+
export type AutoApproveModeRisk = 'safe' | 'caution' | 'dangerous';
|
|
394
|
+
export interface AutoApproveMode {
|
|
395
|
+
id: string;
|
|
396
|
+
label: string;
|
|
397
|
+
strategy: AutoApproveModeStrategy;
|
|
398
|
+
risk: AutoApproveModeRisk;
|
|
399
|
+
warning?: string;
|
|
400
|
+
launchArgs?: string[];
|
|
401
|
+
removeArgs?: string[];
|
|
402
|
+
}
|
|
403
|
+
export interface AutoApproveModesConfig {
|
|
404
|
+
default: string;
|
|
405
|
+
modes: AutoApproveMode[];
|
|
406
|
+
}
|
|
392
407
|
export interface ProviderModule {
|
|
393
408
|
/** Unique identifier (e.g. 'cline', 'cursor', 'gemini-cli') */
|
|
394
409
|
type: string;
|
|
@@ -418,6 +433,8 @@ export interface ProviderModule {
|
|
|
418
433
|
status?: string;
|
|
419
434
|
/** Inventory/support detail string maintained in adhdev-providers */
|
|
420
435
|
details?: string;
|
|
436
|
+
/** Provider-specific auto-approve choices and their launch/runtime strategy. */
|
|
437
|
+
autoApproveModes?: AutoApproveModesConfig;
|
|
421
438
|
/** Install instructions (shown when command is missing) */
|
|
422
439
|
install?: string;
|
|
423
440
|
/** Custom version detection command (e.g. 'cursor --version', 'claude -v') */
|
|
@@ -14,6 +14,7 @@ import type { GitRepoStatus, GitCompactSummary } from './git/git-types.js';
|
|
|
14
14
|
import type { MeshMissionSummary, MeshMissionSlimSummary } from './mesh/mesh-missions.js';
|
|
15
15
|
import type { MeshMagiActivitySummary } from './mesh/mesh-magi-status.js';
|
|
16
16
|
import type { MagiKindPanelMap, DifficultyBrainMap, NodeCapabilitySlot } from '@adhdev/mesh-shared';
|
|
17
|
+
import type { ProviderModule } from './providers/contracts.js';
|
|
17
18
|
export interface RepoMesh {
|
|
18
19
|
id: string;
|
|
19
20
|
name: string;
|
|
@@ -255,6 +256,8 @@ export interface RepoMeshPolicy {
|
|
|
255
256
|
* A node policy may override this per-node (RepoMeshNodePolicy.delegatedWorkerAutoApprove).
|
|
256
257
|
*/
|
|
257
258
|
delegatedWorkerAutoApprove?: boolean;
|
|
259
|
+
/** Explicit opt-in required before delegated workers may use a dangerous provider mode. */
|
|
260
|
+
delegatedWorkerDangerousModeAllow?: boolean;
|
|
258
261
|
/**
|
|
259
262
|
* MESH-SEND-KEYS (feature 3): opt-in to allow the coordinator to inject
|
|
260
263
|
* DESTRUCTIVE keys (CTRL_C / ESC) into a worker PTY via mesh_send_keys. These
|
|
@@ -406,6 +409,8 @@ export interface RepoMeshNodePolicy {
|
|
|
406
409
|
* precedence over the mesh-level policy for worker sessions launched onto this node.
|
|
407
410
|
*/
|
|
408
411
|
delegatedWorkerAutoApprove?: boolean;
|
|
412
|
+
/** Per-node override for dangerous delegated worker mode authorization. */
|
|
413
|
+
delegatedWorkerDangerousModeAllow?: boolean;
|
|
409
414
|
/**
|
|
410
415
|
* MESH-SEND-KEYS (feature 3): per-node override for
|
|
411
416
|
* RepoMeshPolicy.allowSendKeysDestructive.
|
|
@@ -524,13 +529,18 @@ export declare function normalizeAutoFastForwardPolicy(value: unknown): NonNulla
|
|
|
524
529
|
*/
|
|
525
530
|
export declare function mergeAndNormalizePolicy(base: RepoMeshPolicy | undefined, patch: Partial<RepoMeshPolicy> | undefined): RepoMeshPolicy;
|
|
526
531
|
/**
|
|
527
|
-
* Resolve
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
* `autoApprove`; it wins over the global per-provider-type autoApprove config because the
|
|
531
|
-
* launch path merges the envelope as a settingsOverride on top of the provider defaults.
|
|
532
|
+
* Resolve delegated worker auto-approve. Legacy providers return a boolean. Providers
|
|
533
|
+
* with modes return the default mode id, except a dangerous default is downgraded to a
|
|
534
|
+
* non-dangerous PTY mode unless mesh/node policy explicitly opts in.
|
|
532
535
|
*/
|
|
533
|
-
export declare function resolveDelegatedWorkerAutoApprove(meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove'> | null, nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove'> | null): boolean;
|
|
536
|
+
export declare function resolveDelegatedWorkerAutoApprove(meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null, nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null, provider?: Pick<ProviderModule, 'autoApproveModes'> | null): boolean | string;
|
|
537
|
+
export declare function resolveDelegatedWorkerDangerousModeAllow(meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerDangerousModeAllow'> | null, nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerDangerousModeAllow'> | null): boolean;
|
|
538
|
+
/** Shape a boolean-or-mode resolution for the settings precedence contract. */
|
|
539
|
+
export declare function delegatedWorkerAutoApproveSettings(meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null, nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null, provider?: Pick<ProviderModule, 'autoApproveModes'> | null): {
|
|
540
|
+
autoApprove: boolean | undefined;
|
|
541
|
+
autoApproveMode: string | undefined;
|
|
542
|
+
delegatedWorkerDangerousModeAllow: boolean;
|
|
543
|
+
};
|
|
534
544
|
/**
|
|
535
545
|
* MESH-SEND-KEYS (feature 3): resolve whether DESTRUCTIVE key injection
|
|
536
546
|
* (CTRL_C/ESC via mesh_send_keys) is permitted for a node. Node policy overrides
|
package/dist/shared-types.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type { ProviderState, ProviderStatus, ActiveChatData, IdeProviderState, C
|
|
|
13
13
|
export type { ProviderErrorReason } from './providers/provider-instance.js';
|
|
14
14
|
import type { ActiveChatData as _ActiveChatData, ProviderErrorReason as _ProviderErrorReason } from './providers/provider-instance.js';
|
|
15
15
|
import type { WorkspaceEntry } from './config/workspaces.js';
|
|
16
|
-
import type { ProviderMeshCoordinatorConfig, ProviderResumeCapability } from './providers/contracts.js';
|
|
16
|
+
import type { AutoApproveModesConfig, ProviderMeshCoordinatorConfig, ProviderResumeCapability } from './providers/contracts.js';
|
|
17
17
|
import type { GitCompactSummary, GitWorkspaceUpdate, WorkspaceGitSubscriptionParams } from './git/git-types.js';
|
|
18
18
|
import type { InteractivePrompt } from './providers/types/interactive-prompt.js';
|
|
19
19
|
export type { GitCommandName, GitCompactSummary, GitDiffSummary, GitFailureReason, GitFileChange, GitFileChangeStatus, GitRepoIdentity, GitRepoStatus, GitSnapshot, GitSnapshotCompareSummary, GitSnapshotReason, GitWorkspaceUpdate, WorkspaceGitSubscriptionParams, } from './git/git-types.js';
|
|
@@ -428,6 +428,8 @@ export interface AvailableProviderInfo {
|
|
|
428
428
|
lastVerification?: MachineProviderCheckResult;
|
|
429
429
|
/** Provider-declared Repo Mesh coordinator/MCP behavior. */
|
|
430
430
|
meshCoordinator?: ProviderMeshCoordinatorConfig;
|
|
431
|
+
/** Provider-declared auto-approve choices shown by session launch UIs. */
|
|
432
|
+
autoApproveModes?: AutoApproveModesConfig;
|
|
431
433
|
/** BRAIN-ROUTING: suggested model values for the new-session model dropdown. */
|
|
432
434
|
modelOptions?: string[];
|
|
433
435
|
/** BRAIN-ROUTING: reasoning-effort values for the new-session thinking dropdown. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "1.0.18-rc.
|
|
3
|
+
"version": "1.0.18-rc.17",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"author": "vilmire",
|
|
48
48
|
"license": "AGPL-3.0-or-later",
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@adhdev/mesh-shared": "1.0.18-rc.
|
|
51
|
-
"@adhdev/session-host-core": "1.0.18-rc.
|
|
50
|
+
"@adhdev/mesh-shared": "1.0.18-rc.17",
|
|
51
|
+
"@adhdev/session-host-core": "1.0.18-rc.17",
|
|
52
52
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
53
53
|
"ajv": "^8.20.0",
|
|
54
54
|
"ajv-formats": "^3.0.1",
|
|
@@ -34,6 +34,7 @@ import type { SessionRegistry } from '../sessions/registry.js';
|
|
|
34
34
|
import type { ProviderInstance } from '../providers/provider-instance.js';
|
|
35
35
|
import { LOG } from '../logging/logger.js';
|
|
36
36
|
import { shouldRestoreHostedRuntime } from './hosted-runtime-restore.js';
|
|
37
|
+
import { findProviderAutoApproveMode, resolveProviderAutoApproveMode } from '../providers/auto-approve-modes.js';
|
|
37
38
|
|
|
38
39
|
// ─── external dependency interface ──────────────────────────
|
|
39
40
|
|
|
@@ -452,6 +453,40 @@ export function expandThinkingLaunchArgs(
|
|
|
452
453
|
return template.map((part) => part.includes('{{level}}') ? part.replace('{{level}}', mapped) : part);
|
|
453
454
|
}
|
|
454
455
|
|
|
456
|
+
function matchesRemovedLaunchArg(arg: string, removeArg: string): boolean {
|
|
457
|
+
return arg === removeArg || (removeArg.startsWith('--') && arg.startsWith(`${removeArg}=`));
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Apply a selected launch-args auto-approve mode without mutating provider metadata.
|
|
462
|
+
* removeArgs only targets provider-owned base spawn.args; launchArgs are prepended to
|
|
463
|
+
* per-launch args beside model/thinking args, making conflict removal order-independent.
|
|
464
|
+
*/
|
|
465
|
+
export function applyAutoApproveModeLaunchArgs(
|
|
466
|
+
provider: ProviderModule | undefined,
|
|
467
|
+
cliArgs: string[] | undefined,
|
|
468
|
+
settings: Record<string, unknown> | undefined,
|
|
469
|
+
): { provider: ProviderModule | undefined; cliArgs: string[] | undefined } {
|
|
470
|
+
if (!provider) return { provider, cliArgs };
|
|
471
|
+
const resolved = resolveProviderAutoApproveMode(provider, settings);
|
|
472
|
+
if (!resolved.active || resolved.strategy !== 'launch-args') return { provider, cliArgs };
|
|
473
|
+
const mode = findProviderAutoApproveMode(provider, resolved.modeId);
|
|
474
|
+
if (!mode || !Array.isArray(mode.launchArgs) || mode.launchArgs.length === 0) return { provider, cliArgs };
|
|
475
|
+
|
|
476
|
+
const removeArgs = Array.isArray(mode.removeArgs) ? mode.removeArgs : [];
|
|
477
|
+
const baseArgs = provider.spawn?.args;
|
|
478
|
+
const filteredBaseArgs = Array.isArray(baseArgs) && removeArgs.length > 0
|
|
479
|
+
? baseArgs.filter((arg) => !removeArgs.some((removeArg) => matchesRemovedLaunchArg(arg, removeArg)))
|
|
480
|
+
: baseArgs;
|
|
481
|
+
const launchProvider = filteredBaseArgs === baseArgs
|
|
482
|
+
? provider
|
|
483
|
+
: { ...provider, spawn: { ...provider.spawn!, args: filteredBaseArgs } };
|
|
484
|
+
return {
|
|
485
|
+
provider: launchProvider,
|
|
486
|
+
cliArgs: [...mode.launchArgs, ...(cliArgs || [])],
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
455
490
|
function readSubcommandSessionId(args: string[], subcommands: string[]): string | undefined {
|
|
456
491
|
const resumeIndex = args.findIndex((arg) => subcommands.includes(arg));
|
|
457
492
|
if (resumeIndex < 0) return undefined;
|
|
@@ -1048,6 +1083,17 @@ export class DaemonCliManager {
|
|
|
1048
1083
|
console.log(colorize('cyan', ` 📦 Using provider: ${provider.name} (${provider.type})`));
|
|
1049
1084
|
}
|
|
1050
1085
|
|
|
1086
|
+
const launchSettings = {
|
|
1087
|
+
...this.providerLoader.getSettings(normalizedType),
|
|
1088
|
+
...(options?.settingsOverride || {}),
|
|
1089
|
+
};
|
|
1090
|
+
const versionResolvedProvider = provider
|
|
1091
|
+
? (this.providerLoader.resolve(cliType, { version: cliInfo.version }) || provider)
|
|
1092
|
+
: undefined;
|
|
1093
|
+
const autoApproveLaunch = applyAutoApproveModeLaunchArgs(versionResolvedProvider, cliArgs, launchSettings);
|
|
1094
|
+
const launchProvider = autoApproveLaunch.provider || provider;
|
|
1095
|
+
const cliArgsWithAutoApprove = autoApproveLaunch.cliArgs;
|
|
1096
|
+
|
|
1051
1097
|
// ─── Model axis (MAGI kind-panel): expand initialModel → launch args ───
|
|
1052
1098
|
// For a plain CLI provider the model is selected at spawn time via the manifest's
|
|
1053
1099
|
// modelLaunchArgs template ('{{model}}' → the requested model). ACP providers took
|
|
@@ -1055,10 +1101,10 @@ export class DaemonCliManager {
|
|
|
1055
1101
|
// or no requested model, is a no-op — model selection is best-effort and must never
|
|
1056
1102
|
// fail a launch. The model args are prepended so a caller's explicit cliArgs (e.g. a
|
|
1057
1103
|
// resume flag) still win positionally where order matters.
|
|
1058
|
-
const modelLaunchArgs = expandModelLaunchArgs(
|
|
1104
|
+
const modelLaunchArgs = expandModelLaunchArgs(launchProvider?.modelLaunchArgs, initialModel);
|
|
1059
1105
|
const cliArgsWithModel = modelLaunchArgs
|
|
1060
|
-
? [...modelLaunchArgs, ...(
|
|
1061
|
-
:
|
|
1106
|
+
? [...modelLaunchArgs, ...(cliArgsWithAutoApprove || [])]
|
|
1107
|
+
: cliArgsWithAutoApprove;
|
|
1062
1108
|
if (initialModel && !modelLaunchArgs) {
|
|
1063
1109
|
LOG.warn('CLI', `[${normalizedType}] initialModel='${initialModel}' requested but provider declares no modelLaunchArgs template — launching without model selection.`);
|
|
1064
1110
|
}
|
|
@@ -1069,7 +1115,7 @@ export class DaemonCliManager {
|
|
|
1069
1115
|
// Best-effort; a provider with no template (or no requested level) is a no-op. ACP
|
|
1070
1116
|
// providers route thinking through setConfigOption('thought_level') above.
|
|
1071
1117
|
const initialThinkingLevel = options?.initialThinkingLevel;
|
|
1072
|
-
const thinkingLaunchArgs = expandThinkingLaunchArgs(
|
|
1118
|
+
const thinkingLaunchArgs = expandThinkingLaunchArgs(launchProvider?.thinkingLaunchArgs, initialThinkingLevel, launchProvider?.thinkingLevelMap);
|
|
1073
1119
|
const cliArgsWithBrain = thinkingLaunchArgs
|
|
1074
1120
|
? [...thinkingLaunchArgs, ...(cliArgsWithModel || [])]
|
|
1075
1121
|
: cliArgsWithModel;
|
|
@@ -1078,13 +1124,13 @@ export class DaemonCliManager {
|
|
|
1078
1124
|
}
|
|
1079
1125
|
|
|
1080
1126
|
// ─── Resolve launch options → provider session binding ───
|
|
1081
|
-
const sessionBinding = resolveCliSessionBinding(
|
|
1127
|
+
const sessionBinding = resolveCliSessionBinding(launchProvider, normalizedType, cliArgsWithBrain, options?.resumeSessionId);
|
|
1082
1128
|
const resolvedCliArgs = sessionBinding.cliArgs;
|
|
1083
1129
|
|
|
1084
1130
|
// If InstanceManager exists, manage as CliProviderInstance unified
|
|
1085
1131
|
const instanceManager = this.deps.getInstanceManager();
|
|
1086
|
-
if (
|
|
1087
|
-
const resolvedProvider =
|
|
1132
|
+
if (launchProvider && instanceManager) {
|
|
1133
|
+
const resolvedProvider = launchProvider;
|
|
1088
1134
|
await this.registerCliInstance(
|
|
1089
1135
|
key,
|
|
1090
1136
|
normalizedType,
|
|
@@ -1092,7 +1138,7 @@ export class DaemonCliManager {
|
|
|
1092
1138
|
resolvedDir,
|
|
1093
1139
|
resolvedCliArgs,
|
|
1094
1140
|
resolvedProvider,
|
|
1095
|
-
|
|
1141
|
+
launchSettings,
|
|
1096
1142
|
false,
|
|
1097
1143
|
{
|
|
1098
1144
|
providerSessionId: sessionBinding.providerSessionId,
|
package/src/index.ts
CHANGED
|
@@ -143,6 +143,8 @@ export type {
|
|
|
143
143
|
export {
|
|
144
144
|
DEFAULT_MESH_POLICY,
|
|
145
145
|
resolveDelegatedWorkerAutoApprove,
|
|
146
|
+
delegatedWorkerAutoApproveSettings,
|
|
147
|
+
resolveDelegatedWorkerDangerousModeAllow,
|
|
146
148
|
resolveAllowSendKeysDestructive,
|
|
147
149
|
resolveMagiSessionCleanupMode,
|
|
148
150
|
magiAutoLaunchedSessionCleanupDecision,
|
|
@@ -489,7 +491,7 @@ export { ProviderInstanceManager } from './providers/provider-instance-manager.j
|
|
|
489
491
|
export { IdeProviderInstance } from './providers/ide-provider-instance.js';
|
|
490
492
|
export { CliProviderInstance } from './providers/cli-provider-instance.js';
|
|
491
493
|
export { AcpProviderInstance } from './providers/acp-provider-instance.js';
|
|
492
|
-
export type { ProviderModule, CdpTargetFilter, ProviderResumeCapability, InputEnvelope, InputPart, MessagePart, ReadChatTurnStatus, ControlListResult, ControlSetResult, ControlInvokeResult } from './providers/contracts.js';
|
|
494
|
+
export type { ProviderModule, AutoApproveMode, AutoApproveModesConfig, AutoApproveModeRisk, AutoApproveModeStrategy, CdpTargetFilter, ProviderResumeCapability, InputEnvelope, InputPart, MessagePart, ReadChatTurnStatus, ControlListResult, ControlSetResult, ControlInvokeResult } from './providers/contracts.js';
|
|
493
495
|
export type { ProviderSourceConfigSnapshot, ProviderSourceConfigUpdate } from './config/provider-source-config.js';
|
|
494
496
|
export { parseProviderSourceConfigUpdate } from './config/provider-source-config.js';
|
|
495
497
|
export { normalizeInputEnvelope, normalizeMessageParts, flattenMessageParts } from './providers/io-contracts.js';
|
|
@@ -16,7 +16,7 @@ import { resolveMeshHostStatus } from './mesh-host-ownership.js';
|
|
|
16
16
|
import { enqueueUnresolvedDelegateForward, nudgeUnresolvedForwardRetry } from './mesh-unresolved-forward-outbox.js';
|
|
17
17
|
import { traceMeshEventStage, traceMeshEventDrop } from './mesh-event-trace.js';
|
|
18
18
|
import { getLastDisplayMessage } from '../status/snapshot.js';
|
|
19
|
-
import {
|
|
19
|
+
import { delegatedWorkerAutoApproveSettings } from '../repo-mesh-types.js';
|
|
20
20
|
import { meshNodeIdMatches, daemonIdsEquivalent, expandDaemonIdForms, sessionIdsEquivalent, withStatusProbeMarker, type MeshNodeIdentified } from '@adhdev/mesh-shared';
|
|
21
21
|
import {
|
|
22
22
|
findRecentTerminalLedgerEvidence,
|
|
@@ -1614,7 +1614,11 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
1614
1614
|
spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || 'hidden',
|
|
1615
1615
|
// Coordinator-dispatched recovery relaunch: same auto-approve
|
|
1616
1616
|
// policy as the primary worker launch path.
|
|
1617
|
-
|
|
1617
|
+
...delegatedWorkerAutoApproveSettings(
|
|
1618
|
+
mesh?.policy,
|
|
1619
|
+
node?.policy,
|
|
1620
|
+
components.providerLoader?.getMeta(recoveryContext.failedProviderType),
|
|
1621
|
+
),
|
|
1618
1622
|
launchedByCoordinator: true,
|
|
1619
1623
|
}
|
|
1620
1624
|
}).catch((e: any) => LOG.error('MeshRecovery', `Failed to auto-relaunch session for ${node.id}: ${e?.message}`));
|
|
@@ -13,7 +13,7 @@ import { createSessionDelivery, updateSessionDeliveryStatus } from './mesh-deliv
|
|
|
13
13
|
import { MeshRuntimeStore } from './mesh-runtime-store.js';
|
|
14
14
|
import { traceMeshEventDrop } from './mesh-event-trace.js';
|
|
15
15
|
import { awaitWithWarmupDeadline, resolveWarmupDeadlineOpts } from './mesh-warmup-deadline.js';
|
|
16
|
-
import {
|
|
16
|
+
import { delegatedWorkerAutoApproveSettings, resolveProviderMaxParallel, resolveNodeSchedulingPriority, normalizeMeshSchedulingStrategy, resolveMaxParallelTasks, resolveMaxReadonlyParallelTasks, resolveCoordinatorIdlePushPolicy } from '../repo-mesh-types.js';
|
|
17
17
|
import type { RepoMeshSchedulingStrategy } from '../repo-mesh-types.js';
|
|
18
18
|
import { normalizeMeshNodeId, meshNodeIdMatches, daemonIdsEquivalent, canonicalDaemonId, expandDaemonIdForms, normalizeMeshWorkspaceForCompare, meshWorkspacesEquivalent, sessionIdsEquivalent, normalizeNodeCapabilitySlots, isMeshTaskDifficulty, withStatusProbeMarker, type MeshNodeIdentified, type NodeCapabilitySlot, type MeshTaskDifficulty } from '@adhdev/mesh-shared';
|
|
19
19
|
import { resolveNodeCapabilitySlots } from './mesh-node-slots.js';
|
|
@@ -690,7 +690,7 @@ export function tryAssignQueueTask(
|
|
|
690
690
|
if (inst && typeof inst.updateSettings === 'function') {
|
|
691
691
|
// Adopting a (possibly manually-opened) local session as a worker: apply the
|
|
692
692
|
// delegated-worker auto-approve policy here too, so a session that was launched
|
|
693
|
-
// without
|
|
693
|
+
// without an auto-approve boolean/mode still resolves the delegated policy once dispatched
|
|
694
694
|
// to it (the "approval notification fires only for certain delegated sessions"
|
|
695
695
|
// case). updateSettings preserves runtime mesh keys; passing autoApprove keeps it.
|
|
696
696
|
//
|
|
@@ -704,7 +704,11 @@ export function tryAssignQueueTask(
|
|
|
704
704
|
meshNodeFor: meshId,
|
|
705
705
|
meshNodeId: nodeId,
|
|
706
706
|
launchedByCoordinator: true,
|
|
707
|
-
|
|
707
|
+
...delegatedWorkerAutoApproveSettings(
|
|
708
|
+
mesh?.policy,
|
|
709
|
+
node?.policy,
|
|
710
|
+
components.providerLoader?.getMeta(providerType),
|
|
711
|
+
),
|
|
708
712
|
...(localDaemonId ? { meshCoordinatorDaemonId: localDaemonId } : {}),
|
|
709
713
|
// COMPLETION-PROPAGATION F5: (re)stamp the coordinator SESSION anchor from THIS
|
|
710
714
|
// task's sourceCoordinatorSessionId with PRIORITY — a manually-launched (or reused)
|
|
@@ -2318,8 +2322,12 @@ async function maybeAutoLaunchOneQueueSession(components: DaemonComponents, mesh
|
|
|
2318
2322
|
spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || 'hidden',
|
|
2319
2323
|
// Coordinator-dispatched worker: auto-approve unless mesh/node policy
|
|
2320
2324
|
// opts out (default true). Lands in settingsOverride and beats the
|
|
2321
|
-
// global per-provider-type
|
|
2322
|
-
|
|
2325
|
+
// global per-provider-type boolean/mode through explicit opposite-key clearing.
|
|
2326
|
+
...delegatedWorkerAutoApproveSettings(
|
|
2327
|
+
mesh?.policy,
|
|
2328
|
+
node?.policy,
|
|
2329
|
+
components.providerLoader?.getMeta(resolved.providerType),
|
|
2330
|
+
),
|
|
2323
2331
|
launchedByCoordinator: true,
|
|
2324
2332
|
autoLaunchedForQueueTaskId: task.id,
|
|
2325
2333
|
};
|
|
@@ -2984,4 +2992,3 @@ export function runIdleMaintenanceThenAssignQueue(components: DaemonComponents,
|
|
|
2984
2992
|
});
|
|
2985
2993
|
});
|
|
2986
2994
|
}
|
|
2987
|
-
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AutoApproveMode,
|
|
3
|
+
AutoApproveModeRisk,
|
|
4
|
+
AutoApproveModeStrategy,
|
|
5
|
+
ProviderModule,
|
|
6
|
+
} from './contracts.js';
|
|
7
|
+
|
|
8
|
+
const KNOWN_DANGEROUS_LAUNCH_ARGS = new Set([
|
|
9
|
+
'--dangerously-skip-permissions',
|
|
10
|
+
'--dangerously-bypass-approvals-and-sandbox',
|
|
11
|
+
'bypassPermissions',
|
|
12
|
+
'sandbox_mode=danger-full-access',
|
|
13
|
+
'approval_policy=never',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
export interface ResolvedAutoApproveMode {
|
|
17
|
+
active: boolean;
|
|
18
|
+
strategy: AutoApproveModeStrategy;
|
|
19
|
+
modeId: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isKnownDangerousLaunchArg(arg: string): boolean {
|
|
23
|
+
const normalized = arg.trim();
|
|
24
|
+
if (KNOWN_DANGEROUS_LAUNCH_ARGS.has(normalized)) return true;
|
|
25
|
+
return normalized.startsWith('--dangerously-skip-permissions=')
|
|
26
|
+
|| normalized.startsWith('--dangerously-bypass-approvals-and-sandbox=');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Runtime defense-in-depth for provider definitions that bypass schema validation. */
|
|
30
|
+
export function deriveAutoApproveModeRisk(mode: Pick<AutoApproveMode, 'risk' | 'launchArgs'>): AutoApproveModeRisk {
|
|
31
|
+
return Array.isArray(mode.launchArgs) && mode.launchArgs.some(isKnownDangerousLaunchArg)
|
|
32
|
+
? 'dangerous'
|
|
33
|
+
: mode.risk;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function inactiveMode(modeId = ''): ResolvedAutoApproveMode {
|
|
37
|
+
return { active: false, strategy: 'pty-parse-default', modeId };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolveConfiguredMode(
|
|
41
|
+
provider: ProviderModule,
|
|
42
|
+
mode: AutoApproveMode,
|
|
43
|
+
settings: Record<string, unknown> | undefined,
|
|
44
|
+
): ResolvedAutoApproveMode {
|
|
45
|
+
if (mode.strategy === 'post-boot-command') return inactiveMode(mode.id);
|
|
46
|
+
if (settings?.launchedByCoordinator === true
|
|
47
|
+
&& settings.delegatedWorkerDangerousModeAllow !== true
|
|
48
|
+
&& deriveAutoApproveModeRisk(mode) === 'dangerous') {
|
|
49
|
+
const fallback = provider.autoApproveModes?.modes.find((candidate) =>
|
|
50
|
+
candidate.strategy === 'pty-parse-default'
|
|
51
|
+
&& deriveAutoApproveModeRisk(candidate) !== 'dangerous');
|
|
52
|
+
return fallback
|
|
53
|
+
? { active: true, strategy: fallback.strategy, modeId: fallback.id }
|
|
54
|
+
: inactiveMode(mode.id);
|
|
55
|
+
}
|
|
56
|
+
return { active: true, strategy: mode.strategy, modeId: mode.id };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolve new mode settings before the legacy boolean. A stale/unknown explicit
|
|
61
|
+
* mode id fails closed instead of falling through to an enabled legacy setting.
|
|
62
|
+
*/
|
|
63
|
+
export function resolveProviderAutoApproveMode(
|
|
64
|
+
provider: ProviderModule,
|
|
65
|
+
settings: Record<string, unknown> | undefined,
|
|
66
|
+
): ResolvedAutoApproveMode {
|
|
67
|
+
const config = provider.autoApproveModes;
|
|
68
|
+
const explicitModeId = settings?.autoApproveMode;
|
|
69
|
+
if (typeof explicitModeId === 'string') {
|
|
70
|
+
const mode = config?.modes.find((candidate) => candidate.id === explicitModeId);
|
|
71
|
+
if (!mode) return inactiveMode(explicitModeId);
|
|
72
|
+
return resolveConfiguredMode(provider, mode, settings);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const explicitLegacy = settings?.autoApprove;
|
|
76
|
+
const providerLegacyDefault = provider.settings?.autoApprove?.default;
|
|
77
|
+
const legacyActive = typeof explicitLegacy === 'boolean'
|
|
78
|
+
? explicitLegacy
|
|
79
|
+
: typeof providerLegacyDefault === 'boolean'
|
|
80
|
+
? providerLegacyDefault
|
|
81
|
+
: false;
|
|
82
|
+
if (!legacyActive) return inactiveMode();
|
|
83
|
+
|
|
84
|
+
if (!config) {
|
|
85
|
+
return { active: true, strategy: 'pty-parse-default', modeId: 'legacy' };
|
|
86
|
+
}
|
|
87
|
+
const defaultMode = config.modes.find((mode) => mode.id === config.default);
|
|
88
|
+
if (!defaultMode) return inactiveMode(config.default);
|
|
89
|
+
return resolveConfiguredMode(provider, defaultMode, settings);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function findProviderAutoApproveMode(
|
|
93
|
+
provider: ProviderModule | undefined,
|
|
94
|
+
modeId: string,
|
|
95
|
+
): AutoApproveMode | undefined {
|
|
96
|
+
return provider?.autoApproveModes?.modes.find((mode) => mode.id === modeId);
|
|
97
|
+
}
|
|
@@ -75,6 +75,7 @@ import type {
|
|
|
75
75
|
} from './cli-provider-instance-types.js';
|
|
76
76
|
import { mergeConversationMessages, buildExternalTranscriptProbe } from './cli-provider-transcript-merge.js';
|
|
77
77
|
import { getEffectDedupKey, formatApprovalRequestMessage, formatMarkerTimestamp } from './cli-provider-effect-format.js';
|
|
78
|
+
import { resolveProviderAutoApproveMode, type ResolvedAutoApproveMode } from './auto-approve-modes.js';
|
|
78
79
|
|
|
79
80
|
// Re-export moved public symbols so existing importers (index.ts, tests) keep
|
|
80
81
|
// their `./cli-provider-instance.js` path. Pure move — no behavior change.
|
|
@@ -2780,7 +2781,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
2780
2781
|
* resolveModal, so a plain turn that never saw an approval always returns false.
|
|
2781
2782
|
*/
|
|
2782
2783
|
private inApprovalResumeGrace(now = Date.now()): boolean {
|
|
2783
|
-
if (!this.isAutonomousMeshSession() || !this.
|
|
2784
|
+
if (!this.isAutonomousMeshSession() || !this.shouldUsePtyAutoApprove()) return false;
|
|
2784
2785
|
const resolvedAt = typeof (this.adapter as any)?.lastApprovalResolvedAt === 'number'
|
|
2785
2786
|
? (this.adapter as any).lastApprovalResolvedAt as number
|
|
2786
2787
|
: 0;
|
|
@@ -2897,7 +2898,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
2897
2898
|
}
|
|
2898
2899
|
|
|
2899
2900
|
const latestStatus = this.adapter.getStatus({ allowParse: false });
|
|
2900
|
-
const latestAutoApproveActive = latestStatus.status === 'waiting_approval' && this.
|
|
2901
|
+
const latestAutoApproveActive = latestStatus.status === 'waiting_approval' && this.shouldUsePtyAutoApprove();
|
|
2901
2902
|
const latestVisibleStatus = latestAutoApproveActive || this.autoApproveBusy ? 'generating' : latestStatus.status;
|
|
2902
2903
|
LOG.debug('CLI', `[${this.type}] flush attempt: adapterStatus=${latestStatus.status} latestVisible=${latestVisibleStatus} generatingStartedAt=${this.generatingStartedAt} isWaitingForResponse=${!!(this.adapter as any)?.isWaitingForResponse} hasPartial=${!!this.adapter.getPartialResponse?.()}`);
|
|
2903
2904
|
if (latestVisibleStatus !== 'idle') {
|
|
@@ -3300,7 +3301,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
3300
3301
|
private stabilizeFlappingApprovalStatus(adapterStatus: any, now = Date.now()): any {
|
|
3301
3302
|
// Only autonomous auto-approving mesh sessions are subject to the delegated flap;
|
|
3302
3303
|
// never overlay for attended/foreground/non-mesh sessions.
|
|
3303
|
-
if (!this.isAutonomousMeshSession() || !this.
|
|
3304
|
+
if (!this.isAutonomousMeshSession() || !this.shouldUsePtyAutoApprove()) return adapterStatus;
|
|
3304
3305
|
|
|
3305
3306
|
const rawStatus = adapterStatus?.status;
|
|
3306
3307
|
const resolvedAt = typeof (this.adapter as any)?.lastApprovalResolvedAt === 'number'
|
|
@@ -3347,6 +3348,13 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
3347
3348
|
}
|
|
3348
3349
|
|
|
3349
3350
|
private maybeAutoApproveStatus(adapterStatus: any, now = Date.now()): boolean {
|
|
3351
|
+
// launch-args modes grant approval in the CLI process itself and therefore
|
|
3352
|
+
// must never enter the PTY modal parser/fire/settle/mask/nudge subsystem.
|
|
3353
|
+
// post-boot-command is reserved and resolves inactive in v1.
|
|
3354
|
+
if (!this.shouldUsePtyAutoApprove()) {
|
|
3355
|
+
this.resetPtyAutoApproveState();
|
|
3356
|
+
return false;
|
|
3357
|
+
}
|
|
3350
3358
|
// Manual-attendance suppression (provider-common): when a human is
|
|
3351
3359
|
// Manual-attendance suppression (provider-common): when a human is
|
|
3352
3360
|
// actively driving this session from the dashboard, hold auto-approve so
|
|
@@ -3358,7 +3366,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
3358
3366
|
// would otherwise never re-drive this decision. Background mesh workers
|
|
3359
3367
|
// are never attended, so their delegated auto-approve is untouched.
|
|
3360
3368
|
if (adapterStatus?.status === 'waiting_approval'
|
|
3361
|
-
&& this.
|
|
3369
|
+
&& this.shouldUsePtyAutoApprove()
|
|
3362
3370
|
&& this.manualAttendance.isAttended(now)) {
|
|
3363
3371
|
this.lastAutoApprovalSignature = '';
|
|
3364
3372
|
this.pendingAutoApprovalSignature = '';
|
|
@@ -3376,7 +3384,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
3376
3384
|
}, this.manualAttendance.remainingMs(now) + 20);
|
|
3377
3385
|
return false;
|
|
3378
3386
|
}
|
|
3379
|
-
const autoApproveActive = adapterStatus?.status === 'waiting_approval' && this.
|
|
3387
|
+
const autoApproveActive = adapterStatus?.status === 'waiting_approval' && this.shouldUsePtyAutoApprove();
|
|
3380
3388
|
// Guard re-entry: onStatusChange/getState can observe the same modal multiple
|
|
3381
3389
|
// times while the PTY absorbs the approval key. Without this flag, repeated
|
|
3382
3390
|
// snapshots would write stray keys into the input once the modal dismisses.
|
|
@@ -4652,15 +4660,37 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
4652
4660
|
get cliType(): string { return this.type; }
|
|
4653
4661
|
get cliName(): string { return this.provider.name; }
|
|
4654
4662
|
|
|
4663
|
+
private resolveAutoApproveMode(): ResolvedAutoApproveMode {
|
|
4664
|
+
return resolveProviderAutoApproveMode(this.provider, this.settings);
|
|
4665
|
+
}
|
|
4666
|
+
|
|
4667
|
+
/** Legacy boolean view retained for internal/test compatibility. */
|
|
4655
4668
|
private shouldAutoApprove(): boolean {
|
|
4656
|
-
|
|
4657
|
-
|
|
4669
|
+
return this.resolveAutoApproveMode().active;
|
|
4670
|
+
}
|
|
4671
|
+
|
|
4672
|
+
private shouldUsePtyAutoApprove(): boolean {
|
|
4673
|
+
const resolved = this.resolveAutoApproveMode();
|
|
4674
|
+
return this.shouldAutoApprove() && resolved.strategy === 'pty-parse-default';
|
|
4675
|
+
}
|
|
4676
|
+
|
|
4677
|
+
private resetPtyAutoApproveState(): void {
|
|
4678
|
+
this.lastAutoApprovalSignature = '';
|
|
4679
|
+
this.pendingAutoApprovalSignature = '';
|
|
4680
|
+
this.pendingAutoApprovalSince = 0;
|
|
4681
|
+
this.autoApproveInactiveSince = 0;
|
|
4682
|
+
this.autoApproveMaskSince = 0;
|
|
4683
|
+
this.stalledApprovalNudgeEpisode = 0;
|
|
4684
|
+
this.autoApproveLastModalSeenAt = 0;
|
|
4685
|
+
this.autoApproveBusy = false;
|
|
4686
|
+
if (this.autoApproveSettleTimer) {
|
|
4687
|
+
clearTimeout(this.autoApproveSettleTimer);
|
|
4688
|
+
this.autoApproveSettleTimer = null;
|
|
4658
4689
|
}
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4690
|
+
if (this.autoApproveBusyTimer) {
|
|
4691
|
+
clearTimeout(this.autoApproveBusyTimer);
|
|
4692
|
+
this.autoApproveBusyTimer = null;
|
|
4662
4693
|
}
|
|
4663
|
-
return false;
|
|
4664
4694
|
}
|
|
4665
4695
|
|
|
4666
4696
|
/** @see ProviderInstance.noteManualInteraction */
|
|
@@ -4687,7 +4717,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
4687
4717
|
*/
|
|
4688
4718
|
private autoApproveEffectivelyActive(status: string | undefined, now = Date.now()): boolean {
|
|
4689
4719
|
return status === 'waiting_approval'
|
|
4690
|
-
&& this.
|
|
4720
|
+
&& this.shouldUsePtyAutoApprove()
|
|
4691
4721
|
&& !this.manualAttendance.isAttended(now);
|
|
4692
4722
|
}
|
|
4693
4723
|
|
|
@@ -4699,7 +4729,8 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
4699
4729
|
// maybeAutoApproveStatus (driven by getState + the recheck timer during a waiting episode);
|
|
4700
4730
|
// this read is side-effect-free so getStatusMetadata can consult it too.
|
|
4701
4731
|
private autoApproveMaskStalled(now = Date.now()): boolean {
|
|
4702
|
-
return this.
|
|
4732
|
+
return this.shouldUsePtyAutoApprove()
|
|
4733
|
+
&& this.autoApproveMaskSince > 0
|
|
4703
4734
|
&& now - this.autoApproveMaskSince > CliProviderInstance.AUTO_APPROVE_MASK_STALL_MS;
|
|
4704
4735
|
}
|
|
4705
4736
|
|
|
@@ -4725,6 +4756,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
4725
4756
|
* mask-clock value) so a modal that flaps between parsed/unparsed states is announced once.
|
|
4726
4757
|
*/
|
|
4727
4758
|
private maybeEmitStalledApprovalNudge(adapterStatus: any, now: number): void {
|
|
4759
|
+
if (!this.shouldUsePtyAutoApprove()) return;
|
|
4728
4760
|
if (!this.isMeshWorkerSession()) return;
|
|
4729
4761
|
if (adapterStatus?.status !== 'waiting_approval') return;
|
|
4730
4762
|
if (!this.autoApproveMaskStalled(now)) return;
|
|
@@ -488,6 +488,28 @@ export interface ProviderCompatibilityEntry {
|
|
|
488
488
|
scriptDir: string;
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
+
export type AutoApproveModeStrategy =
|
|
492
|
+
| 'pty-parse-default'
|
|
493
|
+
| 'launch-args'
|
|
494
|
+
| 'post-boot-command';
|
|
495
|
+
|
|
496
|
+
export type AutoApproveModeRisk = 'safe' | 'caution' | 'dangerous';
|
|
497
|
+
|
|
498
|
+
export interface AutoApproveMode {
|
|
499
|
+
id: string;
|
|
500
|
+
label: string;
|
|
501
|
+
strategy: AutoApproveModeStrategy;
|
|
502
|
+
risk: AutoApproveModeRisk;
|
|
503
|
+
warning?: string;
|
|
504
|
+
launchArgs?: string[];
|
|
505
|
+
removeArgs?: string[];
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export interface AutoApproveModesConfig {
|
|
509
|
+
default: string;
|
|
510
|
+
modes: AutoApproveMode[];
|
|
511
|
+
}
|
|
512
|
+
|
|
491
513
|
export interface ProviderModule {
|
|
492
514
|
/** Unique identifier (e.g. 'cline', 'cursor', 'gemini-cli') */
|
|
493
515
|
type: string;
|
|
@@ -519,7 +541,9 @@ export interface ProviderModule {
|
|
|
519
541
|
status?: string;
|
|
520
542
|
/** Inventory/support detail string maintained in adhdev-providers */
|
|
521
543
|
details?: string;
|
|
522
|
-
|
|
544
|
+
/** Provider-specific auto-approve choices and their launch/runtime strategy. */
|
|
545
|
+
autoApproveModes?: AutoApproveModesConfig;
|
|
546
|
+
/** Install instructions (shown when command is missing) */
|
|
523
547
|
install?: string;
|
|
524
548
|
/** Custom version detection command (e.g. 'cursor --version', 'claude -v') */
|
|
525
549
|
versionCommand?: ProviderVersionCommand;
|