@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ProviderControlDef, ProviderControlType, ProviderModule } from './contracts.js'
|
|
2
|
+
import { deriveAutoApproveModeRisk } from './auto-approve-modes.js'
|
|
2
3
|
import { providerHasOpenPanelSupport } from './open-panel-support.js'
|
|
3
4
|
|
|
4
5
|
const VALID_CAPABILITY_MEDIA_TYPES = new Set(['text', 'image', 'audio', 'video', 'resource'])
|
|
@@ -66,6 +67,7 @@ const KNOWN_PROVIDER_FIELDS = new Set<string>([
|
|
|
66
67
|
'providerVersion',
|
|
67
68
|
'status',
|
|
68
69
|
'details',
|
|
70
|
+
'autoApproveModes',
|
|
69
71
|
'modelLaunchArgs',
|
|
70
72
|
'modelOptions',
|
|
71
73
|
'thinkingLaunchArgs',
|
|
@@ -144,6 +146,7 @@ export function validateProviderDefinition(raw: unknown): ProviderValidationResu
|
|
|
144
146
|
validateCapabilities(provider as unknown as ProviderModule, controls, errors)
|
|
145
147
|
validateNativeHistory(provider.nativeHistory, errors)
|
|
146
148
|
validateMeshCoordinator(provider.meshCoordinator, errors)
|
|
149
|
+
validateAutoApproveModes(provider.autoApproveModes, errors)
|
|
147
150
|
|
|
148
151
|
for (const control of controls) {
|
|
149
152
|
validateControl(control as ProviderControlDef, errors)
|
|
@@ -160,6 +163,86 @@ export function validateProviderDefinition(raw: unknown): ProviderValidationResu
|
|
|
160
163
|
return { errors, warnings }
|
|
161
164
|
}
|
|
162
165
|
|
|
166
|
+
export function validateAutoApproveModes(raw: unknown, errors: string[]): void {
|
|
167
|
+
if (raw === undefined) return
|
|
168
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
169
|
+
errors.push('autoApproveModes must be an object')
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const config = raw as Record<string, unknown>
|
|
174
|
+
const defaultModeId = typeof config.default === 'string' ? config.default.trim() : ''
|
|
175
|
+
if (!defaultModeId) {
|
|
176
|
+
errors.push('autoApproveModes.default must be a non-empty string')
|
|
177
|
+
} else {
|
|
178
|
+
config.default = defaultModeId
|
|
179
|
+
}
|
|
180
|
+
if (!Array.isArray(config.modes) || config.modes.length === 0) {
|
|
181
|
+
errors.push('autoApproveModes.modes must be a non-empty array')
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const ids = new Set<string>()
|
|
186
|
+
for (const [index, rawMode] of config.modes.entries()) {
|
|
187
|
+
const prefix = `autoApproveModes.modes[${index}]`
|
|
188
|
+
if (!rawMode || typeof rawMode !== 'object' || Array.isArray(rawMode)) {
|
|
189
|
+
errors.push(`${prefix} must be an object`)
|
|
190
|
+
continue
|
|
191
|
+
}
|
|
192
|
+
const mode = rawMode as Record<string, unknown>
|
|
193
|
+
const id = typeof mode.id === 'string' ? mode.id.trim() : ''
|
|
194
|
+
if (!id) {
|
|
195
|
+
errors.push(`${prefix}.id must be a non-empty string`)
|
|
196
|
+
} else if (ids.has(id)) {
|
|
197
|
+
errors.push(`${prefix}.id must be unique (duplicate: ${id})`)
|
|
198
|
+
} else {
|
|
199
|
+
ids.add(id)
|
|
200
|
+
mode.id = id
|
|
201
|
+
}
|
|
202
|
+
if (typeof mode.label !== 'string' || !mode.label.trim()) {
|
|
203
|
+
errors.push(`${prefix}.label must be a non-empty string`)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const strategy = mode.strategy
|
|
207
|
+
if (!['pty-parse-default', 'launch-args', 'post-boot-command'].includes(String(strategy))) {
|
|
208
|
+
errors.push(`${prefix}.strategy must be one of: pty-parse-default, launch-args, post-boot-command`)
|
|
209
|
+
} else if (strategy === 'post-boot-command') {
|
|
210
|
+
errors.push(`${prefix}.strategy post-boot-command is reserved and unsupported in v1`)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const risk = mode.risk
|
|
214
|
+
if (!['safe', 'caution', 'dangerous'].includes(String(risk))) {
|
|
215
|
+
errors.push(`${prefix}.risk must be one of: safe, caution, dangerous`)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
for (const field of ['launchArgs', 'removeArgs'] as const) {
|
|
219
|
+
const value = mode[field]
|
|
220
|
+
if (value !== undefined && (!Array.isArray(value) || value.some((arg) => typeof arg !== 'string' || !arg.trim()))) {
|
|
221
|
+
errors.push(`${prefix}.${field} must be an array of non-empty strings when provided`)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (strategy === 'launch-args' && (!Array.isArray(mode.launchArgs) || mode.launchArgs.length === 0)) {
|
|
225
|
+
errors.push(`${prefix}.launchArgs must be a non-empty array for launch-args strategy`)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Mutate the validated provider object so downstream runtime/UI consumers see
|
|
229
|
+
// the effective risk. The runtime resolver repeats this derivation as defense-in-depth.
|
|
230
|
+
if (['safe', 'caution', 'dangerous'].includes(String(risk))
|
|
231
|
+
&& deriveAutoApproveModeRisk(mode as any) === 'dangerous') {
|
|
232
|
+
mode.risk = 'dangerous'
|
|
233
|
+
}
|
|
234
|
+
if (mode.risk === 'dangerous' && (typeof mode.warning !== 'string' || !mode.warning.trim())) {
|
|
235
|
+
errors.push(`${prefix}.warning is required for dangerous modes`)
|
|
236
|
+
} else if (mode.warning !== undefined && (typeof mode.warning !== 'string' || !mode.warning.trim())) {
|
|
237
|
+
errors.push(`${prefix}.warning must be a non-empty string when provided`)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (defaultModeId && !ids.has(defaultModeId)) {
|
|
242
|
+
errors.push(`autoApproveModes.default must reference an existing mode id: ${defaultModeId}`)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
163
246
|
function validateCapabilities(provider: ProviderModule, controls: ProviderControlDef[], errors: string[]): void {
|
|
164
247
|
const capabilities = provider.capabilities
|
|
165
248
|
if (provider.contractVersion === 2) {
|
|
@@ -119,6 +119,20 @@
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
},
|
|
122
|
+
"autoApproveModes": {
|
|
123
|
+
"type": "object",
|
|
124
|
+
"description": "Provider-specific auto-approve choices and their launch/runtime strategy.",
|
|
125
|
+
"required": ["default", "modes"],
|
|
126
|
+
"additionalProperties": false,
|
|
127
|
+
"properties": {
|
|
128
|
+
"default": { "type": "string", "minLength": 1 },
|
|
129
|
+
"modes": {
|
|
130
|
+
"type": "array",
|
|
131
|
+
"minItems": 1,
|
|
132
|
+
"items": { "$ref": "#/$defs/autoApproveMode" }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
122
136
|
"sendDelayMs": {
|
|
123
137
|
"type": "number",
|
|
124
138
|
"minimum": 0,
|
|
@@ -476,6 +490,36 @@
|
|
|
476
490
|
}
|
|
477
491
|
},
|
|
478
492
|
"$defs": {
|
|
493
|
+
"autoApproveMode": {
|
|
494
|
+
"type": "object",
|
|
495
|
+
"required": ["id", "label", "strategy", "risk"],
|
|
496
|
+
"additionalProperties": false,
|
|
497
|
+
"properties": {
|
|
498
|
+
"id": { "type": "string", "minLength": 1 },
|
|
499
|
+
"label": { "type": "string", "minLength": 1 },
|
|
500
|
+
"strategy": { "enum": ["pty-parse-default", "launch-args", "post-boot-command"] },
|
|
501
|
+
"risk": { "enum": ["safe", "caution", "dangerous"] },
|
|
502
|
+
"warning": { "type": "string", "minLength": 1 },
|
|
503
|
+
"launchArgs": {
|
|
504
|
+
"type": "array",
|
|
505
|
+
"items": { "type": "string", "minLength": 1 }
|
|
506
|
+
},
|
|
507
|
+
"removeArgs": {
|
|
508
|
+
"type": "array",
|
|
509
|
+
"items": { "type": "string", "minLength": 1 }
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
"allOf": [
|
|
513
|
+
{
|
|
514
|
+
"if": { "properties": { "strategy": { "const": "launch-args" } }, "required": ["strategy"] },
|
|
515
|
+
"then": { "required": ["launchArgs"], "properties": { "launchArgs": { "minItems": 1 } } }
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
"if": { "properties": { "risk": { "const": "dangerous" } }, "required": ["risk"] },
|
|
519
|
+
"then": { "required": ["warning"] }
|
|
520
|
+
}
|
|
521
|
+
]
|
|
522
|
+
},
|
|
479
523
|
"patternList": {
|
|
480
524
|
"type": "array",
|
|
481
525
|
"items": {
|
package/src/repo-mesh-types.ts
CHANGED
|
@@ -15,6 +15,8 @@ import type { GitRepoStatus, GitCompactSummary } from './git/git-types.js';
|
|
|
15
15
|
import type { MeshMissionSummary, MeshMissionSlimSummary } from './mesh/mesh-missions.js';
|
|
16
16
|
import type { MeshMagiActivitySummary } from './mesh/mesh-magi-status.js';
|
|
17
17
|
import type { MagiKindPanelMap, DifficultyBrainMap, NodeCapabilitySlot } from '@adhdev/mesh-shared';
|
|
18
|
+
import type { ProviderModule } from './providers/contracts.js';
|
|
19
|
+
import { deriveAutoApproveModeRisk } from './providers/auto-approve-modes.js';
|
|
18
20
|
|
|
19
21
|
// ─── Core Mesh Types ────────────────────────────
|
|
20
22
|
|
|
@@ -350,6 +352,8 @@ export interface RepoMeshPolicy {
|
|
|
350
352
|
* A node policy may override this per-node (RepoMeshNodePolicy.delegatedWorkerAutoApprove).
|
|
351
353
|
*/
|
|
352
354
|
delegatedWorkerAutoApprove?: boolean;
|
|
355
|
+
/** Explicit opt-in required before delegated workers may use a dangerous provider mode. */
|
|
356
|
+
delegatedWorkerDangerousModeAllow?: boolean;
|
|
353
357
|
/**
|
|
354
358
|
* MESH-SEND-KEYS (feature 3): opt-in to allow the coordinator to inject
|
|
355
359
|
* DESTRUCTIVE keys (CTRL_C / ESC) into a worker PTY via mesh_send_keys. These
|
|
@@ -505,6 +509,8 @@ export interface RepoMeshNodePolicy {
|
|
|
505
509
|
* precedence over the mesh-level policy for worker sessions launched onto this node.
|
|
506
510
|
*/
|
|
507
511
|
delegatedWorkerAutoApprove?: boolean;
|
|
512
|
+
/** Per-node override for dangerous delegated worker mode authorization. */
|
|
513
|
+
delegatedWorkerDangerousModeAllow?: boolean;
|
|
508
514
|
/**
|
|
509
515
|
* MESH-SEND-KEYS (feature 3): per-node override for
|
|
510
516
|
* RepoMeshPolicy.allowSendKeysDestructive.
|
|
@@ -550,6 +556,7 @@ export const DEFAULT_MESH_POLICY: RepoMeshPolicy = {
|
|
|
550
556
|
// any specific session manually; that override is preserved per-device.
|
|
551
557
|
spawnedSessionVisibility: 'hidden',
|
|
552
558
|
delegatedWorkerAutoApprove: true,
|
|
559
|
+
delegatedWorkerDangerousModeAllow: false,
|
|
553
560
|
sessionCleanupOnNodeRemove: 'preserve',
|
|
554
561
|
// MAGI auto-launches a worker session per pinned replica target with no idle
|
|
555
562
|
// session; those stay idle-LIVE after their turn. Default ON (stop_and_delete)
|
|
@@ -776,6 +783,13 @@ export function mergeAndNormalizePolicy(
|
|
|
776
783
|
} else {
|
|
777
784
|
delete policy.autoConvergeCodeChange;
|
|
778
785
|
}
|
|
786
|
+
// Dangerous delegated-worker provider modes are fail-closed and only persist
|
|
787
|
+
// when the mesh owner has explicitly opted in.
|
|
788
|
+
if (policy.delegatedWorkerDangerousModeAllow === true) {
|
|
789
|
+
policy.delegatedWorkerDangerousModeAllow = true;
|
|
790
|
+
} else {
|
|
791
|
+
delete policy.delegatedWorkerDangerousModeAllow;
|
|
792
|
+
}
|
|
779
793
|
// Coordinator idle-push policy: strict opt-in. Only persist the explicit
|
|
780
794
|
// 'auto_silent_on_dispatch' value; any other/invalid value normalizes to the
|
|
781
795
|
// 'always' default and is dropped so existing meshes.json stays byte-for-byte
|
|
@@ -789,23 +803,62 @@ export function mergeAndNormalizePolicy(
|
|
|
789
803
|
}
|
|
790
804
|
|
|
791
805
|
/**
|
|
792
|
-
* Resolve
|
|
793
|
-
*
|
|
794
|
-
*
|
|
795
|
-
* `autoApprove`; it wins over the global per-provider-type autoApprove config because the
|
|
796
|
-
* launch path merges the envelope as a settingsOverride on top of the provider defaults.
|
|
806
|
+
* Resolve delegated worker auto-approve. Legacy providers return a boolean. Providers
|
|
807
|
+
* with modes return the default mode id, except a dangerous default is downgraded to a
|
|
808
|
+
* non-dangerous PTY mode unless mesh/node policy explicitly opts in.
|
|
797
809
|
*/
|
|
798
810
|
export function resolveDelegatedWorkerAutoApprove(
|
|
799
|
-
meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove'> | null,
|
|
800
|
-
nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove'> | null,
|
|
801
|
-
|
|
811
|
+
meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null,
|
|
812
|
+
nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null,
|
|
813
|
+
provider?: Pick<ProviderModule, 'autoApproveModes'> | null,
|
|
814
|
+
): boolean | string {
|
|
815
|
+
let enabled = true;
|
|
802
816
|
if (typeof nodePolicy?.delegatedWorkerAutoApprove === 'boolean') {
|
|
803
|
-
|
|
817
|
+
enabled = nodePolicy.delegatedWorkerAutoApprove;
|
|
818
|
+
} else if (typeof meshPolicy?.delegatedWorkerAutoApprove === 'boolean') {
|
|
819
|
+
enabled = meshPolicy.delegatedWorkerAutoApprove;
|
|
804
820
|
}
|
|
805
|
-
if (
|
|
806
|
-
|
|
821
|
+
if (!enabled) return false;
|
|
822
|
+
|
|
823
|
+
const modes = provider?.autoApproveModes;
|
|
824
|
+
if (!modes) return true;
|
|
825
|
+
const defaultMode = modes.modes.find((mode) => mode.id === modes.default);
|
|
826
|
+
if (!defaultMode || defaultMode.strategy === 'post-boot-command') return false;
|
|
827
|
+
|
|
828
|
+
const dangerousAllowed = resolveDelegatedWorkerDangerousModeAllow(meshPolicy, nodePolicy);
|
|
829
|
+
if (deriveAutoApproveModeRisk(defaultMode) === 'dangerous' && !dangerousAllowed) {
|
|
830
|
+
const ptyFallback = modes!.modes.find((mode) =>
|
|
831
|
+
mode.strategy === 'pty-parse-default' && deriveAutoApproveModeRisk(mode) !== 'dangerous');
|
|
832
|
+
return ptyFallback?.id || false;
|
|
807
833
|
}
|
|
808
|
-
return
|
|
834
|
+
return defaultMode.id;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
export function resolveDelegatedWorkerDangerousModeAllow(
|
|
838
|
+
meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerDangerousModeAllow'> | null,
|
|
839
|
+
nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerDangerousModeAllow'> | null,
|
|
840
|
+
): boolean {
|
|
841
|
+
if (typeof nodePolicy?.delegatedWorkerDangerousModeAllow === 'boolean') {
|
|
842
|
+
return nodePolicy.delegatedWorkerDangerousModeAllow;
|
|
843
|
+
}
|
|
844
|
+
return meshPolicy?.delegatedWorkerDangerousModeAllow === true;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/** Shape a boolean-or-mode resolution for the settings precedence contract. */
|
|
848
|
+
export function delegatedWorkerAutoApproveSettings(
|
|
849
|
+
meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null,
|
|
850
|
+
nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null,
|
|
851
|
+
provider?: Pick<ProviderModule, 'autoApproveModes'> | null,
|
|
852
|
+
): {
|
|
853
|
+
autoApprove: boolean | undefined;
|
|
854
|
+
autoApproveMode: string | undefined;
|
|
855
|
+
delegatedWorkerDangerousModeAllow: boolean;
|
|
856
|
+
} {
|
|
857
|
+
const resolved = resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy, provider);
|
|
858
|
+
const delegatedWorkerDangerousModeAllow = resolveDelegatedWorkerDangerousModeAllow(meshPolicy, nodePolicy);
|
|
859
|
+
return typeof resolved === 'string'
|
|
860
|
+
? { autoApprove: undefined, autoApproveMode: resolved, delegatedWorkerDangerousModeAllow }
|
|
861
|
+
: { autoApprove: resolved, autoApproveMode: undefined, delegatedWorkerDangerousModeAllow };
|
|
809
862
|
}
|
|
810
863
|
|
|
811
864
|
/**
|
package/src/shared-types.ts
CHANGED
|
@@ -52,7 +52,7 @@ export type { ProviderErrorReason } from './providers/provider-instance.js';
|
|
|
52
52
|
// Local import for use in Managed*Entry types below
|
|
53
53
|
import type { ActiveChatData as _ActiveChatData, ProviderErrorReason as _ProviderErrorReason } from './providers/provider-instance.js';
|
|
54
54
|
import type { WorkspaceEntry } from './config/workspaces.js';
|
|
55
|
-
import type { ProviderMeshCoordinatorConfig, ProviderResumeCapability } from './providers/contracts.js';
|
|
55
|
+
import type { AutoApproveModesConfig, ProviderMeshCoordinatorConfig, ProviderResumeCapability } from './providers/contracts.js';
|
|
56
56
|
import type {
|
|
57
57
|
GitCompactSummary,
|
|
58
58
|
GitDiffSummary,
|
|
@@ -563,6 +563,8 @@ export interface AvailableProviderInfo {
|
|
|
563
563
|
lastVerification?: MachineProviderCheckResult;
|
|
564
564
|
/** Provider-declared Repo Mesh coordinator/MCP behavior. */
|
|
565
565
|
meshCoordinator?: ProviderMeshCoordinatorConfig;
|
|
566
|
+
/** Provider-declared auto-approve choices shown by session launch UIs. */
|
|
567
|
+
autoApproveModes?: AutoApproveModesConfig;
|
|
566
568
|
/** BRAIN-ROUTING: suggested model values for the new-session model dropdown. */
|
|
567
569
|
modelOptions?: string[];
|
|
568
570
|
/** BRAIN-ROUTING: reasoning-effort values for the new-session thinking dropdown. */
|
package/src/status/snapshot.ts
CHANGED
|
@@ -145,6 +145,7 @@ export function buildAvailableProviders(
|
|
|
145
145
|
lastDetection?: AvailableProviderInfo['lastDetection'];
|
|
146
146
|
lastVerification?: AvailableProviderInfo['lastVerification'];
|
|
147
147
|
meshCoordinator?: AvailableProviderInfo['meshCoordinator'];
|
|
148
|
+
autoApproveModes?: AvailableProviderInfo['autoApproveModes'];
|
|
148
149
|
_sourceTrust?: AvailableProviderInfo['trust'];
|
|
149
150
|
_sourceLayer?: AvailableProviderInfo['sourceLayer'];
|
|
150
151
|
_sourceName?: string | null;
|
|
@@ -183,6 +184,7 @@ export function buildAvailableProviders(
|
|
|
183
184
|
...(provider.lastDetection !== undefined ? { lastDetection: provider.lastDetection } : {}),
|
|
184
185
|
...(provider.lastVerification !== undefined ? { lastVerification: provider.lastVerification } : {}),
|
|
185
186
|
...(provider.meshCoordinator !== undefined ? { meshCoordinator: provider.meshCoordinator } : {}),
|
|
187
|
+
...(provider.autoApproveModes !== undefined ? { autoApproveModes: provider.autoApproveModes } : {}),
|
|
186
188
|
...(trust ? {
|
|
187
189
|
trust,
|
|
188
190
|
trustDescription: describeTrust(trust),
|