@adhdev/daemon-core 1.0.18-rc.17 → 1.0.18-rc.19
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/config/mesh-json-config.d.ts +62 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +629 -400
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +592 -370
- package/dist/index.mjs.map +1 -1
- package/dist/repo-mesh-types.d.ts +25 -3
- package/package.json +3 -3
- package/src/commands/med-family/mesh-crud.ts +155 -0
- package/src/config/mesh-json-config.ts +103 -0
- package/src/index.ts +18 -0
- package/src/mesh/mesh-event-forwarding.ts +12 -0
- package/src/mesh/mesh-queue-assignment.ts +53 -3
- package/src/repo-mesh-types.ts +54 -7
|
@@ -15,6 +15,7 @@ import type { MeshMissionSummary, MeshMissionSlimSummary } from './mesh/mesh-mis
|
|
|
15
15
|
import type { MeshMagiActivitySummary } from './mesh/mesh-magi-status.js';
|
|
16
16
|
import type { MagiKindPanelMap, DifficultyBrainMap, NodeCapabilitySlot } from '@adhdev/mesh-shared';
|
|
17
17
|
import type { ProviderModule } from './providers/contracts.js';
|
|
18
|
+
import type { RepoMeshDeclarativeConfig } from './config/mesh-json-config.js';
|
|
18
19
|
export interface RepoMesh {
|
|
19
20
|
id: string;
|
|
20
21
|
name: string;
|
|
@@ -530,13 +531,34 @@ export declare function normalizeAutoFastForwardPolicy(value: unknown): NonNulla
|
|
|
530
531
|
export declare function mergeAndNormalizePolicy(base: RepoMeshPolicy | undefined, patch: Partial<RepoMeshPolicy> | undefined): RepoMeshPolicy;
|
|
531
532
|
/**
|
|
532
533
|
* Resolve delegated worker auto-approve. Legacy providers return a boolean. Providers
|
|
533
|
-
* with modes return
|
|
534
|
+
* with modes return a mode id, except a dangerous mode is downgraded to a
|
|
534
535
|
* non-dangerous PTY mode unless mesh/node policy explicitly opts in.
|
|
536
|
+
*
|
|
537
|
+
* THREE EXPLICIT STAGES — do not collapse them; the ordering is a hard MAGI
|
|
538
|
+
* invariant:
|
|
539
|
+
*
|
|
540
|
+
* ① ENABLE gate (machine-local policy only): node boolean > mesh boolean.
|
|
541
|
+
* `enabled=false` returns `false` IMMEDIATELY, BEFORE any mode selection.
|
|
542
|
+
* The repo `mesh.json` providerDefaults has ZERO influence here — a
|
|
543
|
+
* node/mesh opt-out is never overridden by a repo-declared requested mode.
|
|
544
|
+
*
|
|
545
|
+
* ② MODE selection (only when enabled === true):
|
|
546
|
+
* task override [FUTURE — param reserved below, not yet wired] >
|
|
547
|
+
* repo mesh.json providerDefaults.autoApproveModes[providerType] >
|
|
548
|
+
* provider spec autoApproveModes.default.
|
|
549
|
+
* A repo-requested mode ID is adopted ONLY when it exists in the provider's
|
|
550
|
+
* own `autoApproveModes.modes`; an unknown/stale/typo'd ID is IGNORED and we
|
|
551
|
+
* fall back to the provider default (fail-closed: never coerce into a
|
|
552
|
+
* dangerous mode via a bad ID).
|
|
553
|
+
*
|
|
554
|
+
* ③ DANGEROUS gate: whichever mode stage ② picked, if it is dangerous and the
|
|
555
|
+
* machine-local delegatedWorkerDangerousModeAllow is not set, downgrade to a
|
|
556
|
+
* non-dangerous PTY-parse mode (or `false` if none exists).
|
|
535
557
|
*/
|
|
536
|
-
export declare function resolveDelegatedWorkerAutoApprove(meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null, nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null, provider?: Pick<ProviderModule, 'autoApproveModes'> | null): boolean | string;
|
|
558
|
+
export declare function resolveDelegatedWorkerAutoApprove(meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null, nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null, provider?: Pick<ProviderModule, 'autoApproveModes'> | null, repoConfig?: RepoMeshDeclarativeConfig | null, providerType?: string | null): boolean | string;
|
|
537
559
|
export declare function resolveDelegatedWorkerDangerousModeAllow(meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerDangerousModeAllow'> | null, nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerDangerousModeAllow'> | null): boolean;
|
|
538
560
|
/** 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): {
|
|
561
|
+
export declare function delegatedWorkerAutoApproveSettings(meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null, nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null, provider?: Pick<ProviderModule, 'autoApproveModes'> | null, repoConfig?: RepoMeshDeclarativeConfig | null, providerType?: string | null): {
|
|
540
562
|
autoApprove: boolean | undefined;
|
|
541
563
|
autoApproveMode: string | undefined;
|
|
542
564
|
delegatedWorkerDangerousModeAllow: boolean;
|
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.19",
|
|
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.19",
|
|
51
|
+
"@adhdev/session-host-core": "1.0.18-rc.19",
|
|
52
52
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
53
53
|
"ajv": "^8.20.0",
|
|
54
54
|
"ajv-formats": "^3.0.1",
|
|
@@ -399,6 +399,161 @@ export const meshCrudHandlers: Record<string, MedFamilyHandler> = {
|
|
|
399
399
|
}
|
|
400
400
|
},
|
|
401
401
|
|
|
402
|
+
// READ path for `.adhdev/mesh.json` — returns the currently-committed repo
|
|
403
|
+
// config (parsed + normalized) for a workspace so a UI can render/edit the
|
|
404
|
+
// existing declarative zones (notably `providerDefaults.autoApproveModes`)
|
|
405
|
+
// WITHOUT re-deriving them from the machine-local scaffold. Never writes.
|
|
406
|
+
// `config` is undefined when no repo file exists (sourceType 'unavailable') or
|
|
407
|
+
// it is unparseable (sourceType 'invalid', with the parse error surfaced).
|
|
408
|
+
read_mesh_json_config: async (_ctx: MedFamilyContext, args: any) => {
|
|
409
|
+
const workspace = typeof args?.workspace === 'string' && args.workspace.trim() ? args.workspace.trim() : process.cwd();
|
|
410
|
+
try {
|
|
411
|
+
const { loadRepoMeshJsonConfig } = await import('../../config/mesh-json-config.js');
|
|
412
|
+
const loaded = loadRepoMeshJsonConfig(workspace);
|
|
413
|
+
return {
|
|
414
|
+
success: true,
|
|
415
|
+
workspace,
|
|
416
|
+
sourceType: loaded.sourceType,
|
|
417
|
+
source: loaded.source,
|
|
418
|
+
...(loaded.path ? { path: loaded.path } : {}),
|
|
419
|
+
...(loaded.error ? { error: loaded.error } : {}),
|
|
420
|
+
config: loaded.config,
|
|
421
|
+
// Convenience projection so the UI does not have to reach into config.
|
|
422
|
+
providerDefaults: loaded.config?.providerDefaults,
|
|
423
|
+
};
|
|
424
|
+
} catch (e: any) {
|
|
425
|
+
return { success: false, error: e.message };
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
|
|
429
|
+
// Partial-edit WRITE path for `.adhdev/mesh.json` `providerDefaults` — a
|
|
430
|
+
// READ-MODIFY-WRITE that preserves operator hand-edits. Unlike
|
|
431
|
+
// write_mesh_json_config (which rebuilds the WHOLE file from the machine-local
|
|
432
|
+
// scaffold and can silently drop hand-edited zones), this parses the existing
|
|
433
|
+
// repo file, merges ONLY the providerDefaults.autoApproveModes zone, and
|
|
434
|
+
// re-serializes — coordinator prompt, operating notes and limits authored in
|
|
435
|
+
// the repo are carried through untouched. Defaults to dry-run.
|
|
436
|
+
//
|
|
437
|
+
// args: { workspace?, autoApproveModes: Record<providerType,modeId>, write?, merge? }
|
|
438
|
+
// merge=true (default): per-provider merge into the existing map; a modeId of
|
|
439
|
+
// '' | null removes that provider's entry. merge=false: REPLACE the whole
|
|
440
|
+
// autoApproveModes map with the supplied one.
|
|
441
|
+
set_mesh_provider_defaults: async (_ctx: MedFamilyContext, args: any) => {
|
|
442
|
+
const workspace = typeof args?.workspace === 'string' && args.workspace.trim() ? args.workspace.trim() : process.cwd();
|
|
443
|
+
const write = args?.write === true;
|
|
444
|
+
const merge = args?.merge !== false; // default true
|
|
445
|
+
const inputModes = args?.autoApproveModes;
|
|
446
|
+
if (inputModes !== undefined && (typeof inputModes !== 'object' || inputModes === null || Array.isArray(inputModes))) {
|
|
447
|
+
return { success: false, error: 'autoApproveModes must be an object (providerType → modeId) when provided' };
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
const {
|
|
451
|
+
loadRepoMeshJsonConfig,
|
|
452
|
+
normalizeRepoMeshDeclarativeConfig,
|
|
453
|
+
MESH_JSON_CONFIG_LOCATIONS,
|
|
454
|
+
} = await import('../../config/mesh-json-config.js');
|
|
455
|
+
const { existsSync, readFileSync, mkdirSync, writeFileSync } = await import('fs');
|
|
456
|
+
const { dirname, join } = await import('path');
|
|
457
|
+
const yaml = await import('js-yaml');
|
|
458
|
+
|
|
459
|
+
const relativePath = MESH_JSON_CONFIG_LOCATIONS[0];
|
|
460
|
+
|
|
461
|
+
// Read-modify-write: parse the EXISTING on-disk document (preferring the
|
|
462
|
+
// first existing json/yaml variant) so unrelated zones survive verbatim.
|
|
463
|
+
let baseDoc: Record<string, any> = { version: 1 };
|
|
464
|
+
let existingPath = join(workspace, relativePath);
|
|
465
|
+
let existedAsYaml = false;
|
|
466
|
+
for (const relative of MESH_JSON_CONFIG_LOCATIONS) {
|
|
467
|
+
const candidate = join(workspace, relative);
|
|
468
|
+
if (!existsSync(candidate)) continue;
|
|
469
|
+
try {
|
|
470
|
+
const text = readFileSync(candidate, 'utf-8');
|
|
471
|
+
const parsed = /\.json$/i.test(candidate) ? JSON.parse(text) : yaml.load(text);
|
|
472
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
473
|
+
baseDoc = parsed as Record<string, any>;
|
|
474
|
+
existingPath = candidate;
|
|
475
|
+
existedAsYaml = !/\.json$/i.test(candidate);
|
|
476
|
+
}
|
|
477
|
+
} catch (e: any) {
|
|
478
|
+
return { success: false, error: `existing ${relative} is unparseable, refusing to overwrite: ${e?.message || e}` };
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Merge ONLY the providerDefaults.autoApproveModes zone.
|
|
484
|
+
const existingPd = baseDoc.providerDefaults && typeof baseDoc.providerDefaults === 'object' && !Array.isArray(baseDoc.providerDefaults)
|
|
485
|
+
? baseDoc.providerDefaults as Record<string, any>
|
|
486
|
+
: {};
|
|
487
|
+
const existingModes = existingPd.autoApproveModes && typeof existingPd.autoApproveModes === 'object' && !Array.isArray(existingPd.autoApproveModes)
|
|
488
|
+
? { ...existingPd.autoApproveModes as Record<string, string> }
|
|
489
|
+
: {};
|
|
490
|
+
|
|
491
|
+
const nextModes: Record<string, string> = merge ? existingModes : {};
|
|
492
|
+
if (inputModes) {
|
|
493
|
+
for (const [providerType, modeId] of Object.entries(inputModes as Record<string, unknown>)) {
|
|
494
|
+
const type = typeof providerType === 'string' ? providerType.trim() : '';
|
|
495
|
+
if (!type) continue;
|
|
496
|
+
const id = typeof modeId === 'string' ? modeId.trim() : '';
|
|
497
|
+
if (id) nextModes[type] = id;
|
|
498
|
+
else delete nextModes[type]; // '' / null → remove this provider's entry
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const nextDoc: Record<string, any> = { ...baseDoc, version: 1 };
|
|
503
|
+
if (Object.keys(nextModes).length) {
|
|
504
|
+
nextDoc.providerDefaults = { ...existingPd, autoApproveModes: nextModes };
|
|
505
|
+
} else {
|
|
506
|
+
// No entries left → drop the zone entirely so we don't leave an empty stub.
|
|
507
|
+
if (nextDoc.providerDefaults) {
|
|
508
|
+
const { autoApproveModes, ...restPd } = nextDoc.providerDefaults;
|
|
509
|
+
if (Object.keys(restPd).length) nextDoc.providerDefaults = restPd;
|
|
510
|
+
else delete nextDoc.providerDefaults;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Validate the merged document before it ever touches disk.
|
|
515
|
+
const validation = normalizeRepoMeshDeclarativeConfig(nextDoc);
|
|
516
|
+
if (!validation.valid) {
|
|
517
|
+
return { success: false, error: `merged mesh.json is invalid: ${validation.errors.join('; ')}` };
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Serialize in the on-disk format (JSON unless the existing file was YAML).
|
|
521
|
+
const absolutePath = existingPath;
|
|
522
|
+
const serialized = existedAsYaml
|
|
523
|
+
? yaml.dump(nextDoc, { indent: 2 })
|
|
524
|
+
: `${JSON.stringify(nextDoc, null, 2)}\n`;
|
|
525
|
+
|
|
526
|
+
if (!write) {
|
|
527
|
+
return {
|
|
528
|
+
success: true,
|
|
529
|
+
written: false,
|
|
530
|
+
dryRun: true,
|
|
531
|
+
path: absolutePath,
|
|
532
|
+
relativePath,
|
|
533
|
+
merge,
|
|
534
|
+
providerDefaults: nextDoc.providerDefaults,
|
|
535
|
+
preview: serialized,
|
|
536
|
+
note: 'Dry-run: nothing written. Re-run with write=true to persist. Only the providerDefaults zone is merged; other repo zones are preserved.',
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
mkdirSync(dirname(absolutePath), { recursive: true });
|
|
541
|
+
writeFileSync(absolutePath, serialized, 'utf-8');
|
|
542
|
+
return {
|
|
543
|
+
success: true,
|
|
544
|
+
written: true,
|
|
545
|
+
dryRun: false,
|
|
546
|
+
path: absolutePath,
|
|
547
|
+
relativePath,
|
|
548
|
+
merge,
|
|
549
|
+
providerDefaults: nextDoc.providerDefaults,
|
|
550
|
+
note: 'Wrote providerDefaults into .adhdev/mesh.json (read-modify-write; other zones preserved). Commit it to the repo.',
|
|
551
|
+
};
|
|
552
|
+
} catch (e: any) {
|
|
553
|
+
return { success: false, error: e.message };
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
|
|
402
557
|
delete_mesh: async (_ctx: MedFamilyContext, args: any) => {
|
|
403
558
|
const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
|
|
404
559
|
if (!meshId) return { success: false, error: 'meshId required' };
|
|
@@ -19,6 +19,18 @@
|
|
|
19
19
|
* machine-local policy directly. The repo file shapes the coordinator prompt and
|
|
20
20
|
* operating notes, nothing else.
|
|
21
21
|
*
|
|
22
|
+
* `providerDefaults` IS NOT POLICY EITHER. It is a repo-shared DECLARATIVE
|
|
23
|
+
* "requested auto-approve mode" per providerType — the answer to "WHEN a delegated
|
|
24
|
+
* worker of type X is auto-approved, WHICH mode should it use by default". It has
|
|
25
|
+
* NO say over WHETHER auto-approve is enabled: the ENABLE decision (and the
|
|
26
|
+
* dangerous-mode opt-in) remains 100% machine-local (meshes.json /
|
|
27
|
+
* RepoMeshPolicy). A repo can declare `providerDefaults` freely; a machine that
|
|
28
|
+
* has delegatedWorkerAutoApprove=false still gets no auto-approve, and a dangerous
|
|
29
|
+
* requested mode is still downgraded to PTY parsing unless the machine opts in.
|
|
30
|
+
* The requested mode ID is validated at runtime against the provider spec — an
|
|
31
|
+
* unknown mode ID is IGNORED (fall back to the provider's own default), never
|
|
32
|
+
* silently coerced into a dangerous mode.
|
|
33
|
+
*
|
|
22
34
|
* The coordinator/operatingNotes merge is **in-memory only**: the on-disk
|
|
23
35
|
* machine-local `meshes.json` is never mutated by this module.
|
|
24
36
|
*
|
|
@@ -60,6 +72,19 @@ export interface RepoMeshDeclarativeLimits {
|
|
|
60
72
|
maxNotes?: number;
|
|
61
73
|
}
|
|
62
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Repo-shared declarative per-provider defaults. NOT policy — see the file header.
|
|
77
|
+
* `autoApproveModes` maps a providerType (e.g. "claude-cli") to the auto-approve
|
|
78
|
+
* mode ID that a delegated worker of that type should REQUEST when it is
|
|
79
|
+
* auto-approved. The map only influences WHICH mode is used, never WHETHER
|
|
80
|
+
* auto-approve is enabled (that stays machine-local). Mode IDs are validated
|
|
81
|
+
* against the live provider spec at resolve time; an unknown ID is ignored.
|
|
82
|
+
*/
|
|
83
|
+
export interface RepoMeshDeclarativeProviderDefaults {
|
|
84
|
+
/** providerType → requested auto-approve mode ID. */
|
|
85
|
+
autoApproveModes?: Record<string, string>;
|
|
86
|
+
}
|
|
87
|
+
|
|
63
88
|
/**
|
|
64
89
|
* Parsed + normalized `.adhdev/mesh.json` shape. Every field is optional except
|
|
65
90
|
* version so a repo can declare only the zone(s) it cares about. Policy is NOT a
|
|
@@ -70,6 +95,8 @@ export interface RepoMeshDeclarativeConfig {
|
|
|
70
95
|
coordinator?: RepoMeshDeclarativeCoordinatorConfig;
|
|
71
96
|
operatingNotes?: CoordinatorOperatingNote[];
|
|
72
97
|
limits?: RepoMeshDeclarativeLimits;
|
|
98
|
+
/** Repo-shared per-provider defaults (requested auto-approve mode). NOT policy. */
|
|
99
|
+
providerDefaults?: RepoMeshDeclarativeProviderDefaults;
|
|
73
100
|
}
|
|
74
101
|
|
|
75
102
|
export interface RepoMeshJsonConfigLoadResult {
|
|
@@ -132,6 +159,20 @@ export const MESH_JSON_CONFIG_SCHEMA = {
|
|
|
132
159
|
maxNotes: { type: 'number', minimum: 1 },
|
|
133
160
|
},
|
|
134
161
|
},
|
|
162
|
+
providerDefaults: {
|
|
163
|
+
type: 'object',
|
|
164
|
+
additionalProperties: false,
|
|
165
|
+
properties: {
|
|
166
|
+
// providerType → requested auto-approve mode ID. Mode IDs are
|
|
167
|
+
// validated against the live provider spec at resolve time; an
|
|
168
|
+
// unknown ID is ignored (provider default is used), so the schema
|
|
169
|
+
// only constrains the shape (string→string), not the ID values.
|
|
170
|
+
autoApproveModes: {
|
|
171
|
+
type: 'object',
|
|
172
|
+
additionalProperties: { type: 'string' },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
135
176
|
},
|
|
136
177
|
} as const;
|
|
137
178
|
|
|
@@ -226,6 +267,38 @@ export function normalizeRepoMeshDeclarativeConfig(parsed: unknown): {
|
|
|
226
267
|
}
|
|
227
268
|
}
|
|
228
269
|
|
|
270
|
+
// providerDefaults: shape-only normalization. We keep every string→string
|
|
271
|
+
// (providerType → requested mode ID) entry after trimming; we deliberately do
|
|
272
|
+
// NOT validate the mode IDs against any provider spec here — the normalizer has
|
|
273
|
+
// no provider registry, and (crucially) validity depends on the LIVE provider
|
|
274
|
+
// at resolve time. The runtime resolver (resolveDelegatedWorkerAutoApprove)
|
|
275
|
+
// checks the requested ID against provider.autoApproveModes.modes and falls
|
|
276
|
+
// back to the provider's own default when the ID is unknown. This keeps the
|
|
277
|
+
// config fail-closed: a stale/typo'd mode ID never coerces into a dangerous
|
|
278
|
+
// mode, it just means "no repo override → provider default".
|
|
279
|
+
if (parsed.providerDefaults !== undefined) {
|
|
280
|
+
if (isRecord(parsed.providerDefaults)) {
|
|
281
|
+
const pd: RepoMeshDeclarativeProviderDefaults = {};
|
|
282
|
+
const rawModes = (parsed.providerDefaults as Record<string, unknown>).autoApproveModes;
|
|
283
|
+
if (rawModes !== undefined) {
|
|
284
|
+
if (isRecord(rawModes)) {
|
|
285
|
+
const modes: Record<string, string> = {};
|
|
286
|
+
for (const [providerType, modeId] of Object.entries(rawModes)) {
|
|
287
|
+
const type = typeof providerType === 'string' ? providerType.trim() : '';
|
|
288
|
+
const id = typeof modeId === 'string' ? modeId.trim() : '';
|
|
289
|
+
if (type && id) modes[type] = id;
|
|
290
|
+
}
|
|
291
|
+
if (Object.keys(modes).length) pd.autoApproveModes = modes;
|
|
292
|
+
} else {
|
|
293
|
+
errors.push('providerDefaults.autoApproveModes must be an object when provided');
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (Object.keys(pd).length) config.providerDefaults = pd;
|
|
297
|
+
} else {
|
|
298
|
+
errors.push('providerDefaults must be an object when provided');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
229
302
|
return { valid: true, config, errors };
|
|
230
303
|
}
|
|
231
304
|
|
|
@@ -364,6 +437,21 @@ export function applyRepoMeshConfig<T extends Pick<LocalMeshEntry, 'coordinator'
|
|
|
364
437
|
* exported — it is machine-local only and has no place in mesh.json. Operating
|
|
365
438
|
* notes are intentionally NOT exported either: those are runtime ledger lessons,
|
|
366
439
|
* and a repo should declare baseline notes deliberately.
|
|
440
|
+
*
|
|
441
|
+
* `providerDefaults` is NOT auto-exported from the machine-local mesh (there is no
|
|
442
|
+
* machine-local source for it — it is a repo-authored declaration). To help an
|
|
443
|
+
* operator hand-author it, the scaffold carries a commented example shape:
|
|
444
|
+
*
|
|
445
|
+
* "providerDefaults": {
|
|
446
|
+
* "autoApproveModes": {
|
|
447
|
+
* "claude-cli": "accept-edits", // requested mode WHEN auto-approve is on
|
|
448
|
+
* "codex-cli": "auto" // (enable/dangerous opt-in stays machine-local)
|
|
449
|
+
* }
|
|
450
|
+
* }
|
|
451
|
+
*
|
|
452
|
+
* The example is surfaced via the scaffold's `_providerDefaultsExample` hint (JSON
|
|
453
|
+
* has no comments) rather than a live `providerDefaults` value, so serializing the
|
|
454
|
+
* scaffold never writes an unwanted requested-mode map into the repo file.
|
|
367
455
|
*/
|
|
368
456
|
export function buildMeshJsonConfigScaffold(
|
|
369
457
|
mesh: Pick<LocalMeshEntry, 'coordinator'>,
|
|
@@ -378,6 +466,21 @@ export function buildMeshJsonConfigScaffold(
|
|
|
378
466
|
return scaffold;
|
|
379
467
|
}
|
|
380
468
|
|
|
469
|
+
/**
|
|
470
|
+
* A copy-paste example of the `providerDefaults` zone for operators hand-authoring
|
|
471
|
+
* a repo `.adhdev/mesh.json`. Returned by the export/write commands as a separate
|
|
472
|
+
* hint field (never merged into the serialized scaffold) so the repo file stays
|
|
473
|
+
* clean. The mode IDs shown are illustrative — a repo should use IDs that exist in
|
|
474
|
+
* its own providers' specs; an unknown ID is ignored at resolve time and the
|
|
475
|
+
* provider's own default is used.
|
|
476
|
+
*/
|
|
477
|
+
export const MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE: RepoMeshDeclarativeProviderDefaults = {
|
|
478
|
+
autoApproveModes: {
|
|
479
|
+
'claude-cli': 'accept-edits',
|
|
480
|
+
'codex-cli': 'auto',
|
|
481
|
+
},
|
|
482
|
+
};
|
|
483
|
+
|
|
381
484
|
/** Serialize a scaffold to the canonical 2-space JSON draft text. */
|
|
382
485
|
export function serializeMeshJsonConfigScaffold(config: RepoMeshDeclarativeConfig): string {
|
|
383
486
|
return JSON.stringify(config, null, 2);
|
package/src/index.ts
CHANGED
|
@@ -163,6 +163,24 @@ export {
|
|
|
163
163
|
resolveAutoConvergeCodeChange,
|
|
164
164
|
} from './repo-mesh-types.js';
|
|
165
165
|
|
|
166
|
+
// ── Repo-shared declarative mesh config (.adhdev/mesh.json) ──
|
|
167
|
+
export {
|
|
168
|
+
loadRepoMeshJsonConfig,
|
|
169
|
+
normalizeRepoMeshDeclarativeConfig,
|
|
170
|
+
buildMeshJsonConfigScaffold,
|
|
171
|
+
serializeMeshJsonConfigScaffold,
|
|
172
|
+
MESH_JSON_CONFIG_LOCATIONS,
|
|
173
|
+
MESH_JSON_CONFIG_SCHEMA,
|
|
174
|
+
MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE,
|
|
175
|
+
} from './config/mesh-json-config.js';
|
|
176
|
+
export type {
|
|
177
|
+
RepoMeshDeclarativeConfig,
|
|
178
|
+
RepoMeshDeclarativeCoordinatorConfig,
|
|
179
|
+
RepoMeshDeclarativeLimits,
|
|
180
|
+
RepoMeshDeclarativeProviderDefaults,
|
|
181
|
+
RepoMeshJsonConfigLoadResult,
|
|
182
|
+
} from './config/mesh-json-config.js';
|
|
183
|
+
|
|
166
184
|
// ── Git Surface ──
|
|
167
185
|
export * from './git/index.js';
|
|
168
186
|
|
|
@@ -17,6 +17,7 @@ import { enqueueUnresolvedDelegateForward, nudgeUnresolvedForwardRetry } from '.
|
|
|
17
17
|
import { traceMeshEventStage, traceMeshEventDrop } from './mesh-event-trace.js';
|
|
18
18
|
import { getLastDisplayMessage } from '../status/snapshot.js';
|
|
19
19
|
import { delegatedWorkerAutoApproveSettings } from '../repo-mesh-types.js';
|
|
20
|
+
import { loadRepoMeshJsonConfig } from '../config/mesh-json-config.js';
|
|
20
21
|
import { meshNodeIdMatches, daemonIdsEquivalent, expandDaemonIdForms, sessionIdsEquivalent, withStatusProbeMarker, type MeshNodeIdentified } from '@adhdev/mesh-shared';
|
|
21
22
|
import {
|
|
22
23
|
findRecentTerminalLedgerEvidence,
|
|
@@ -1618,6 +1619,17 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
1618
1619
|
mesh?.policy,
|
|
1619
1620
|
node?.policy,
|
|
1620
1621
|
components.providerLoader?.getMeta(recoveryContext.failedProviderType),
|
|
1622
|
+
// Recovery relaunch: same repo-declared requested mode as the
|
|
1623
|
+
// primary path. node.workspace missing → null → provider default.
|
|
1624
|
+
(() => {
|
|
1625
|
+
const ws = typeof node?.workspace === 'string' && node.workspace.trim() ? node.workspace.trim() : '';
|
|
1626
|
+
if (!ws) return null;
|
|
1627
|
+
try {
|
|
1628
|
+
const r = loadRepoMeshJsonConfig(ws);
|
|
1629
|
+
return r.sourceType === 'repo_file' && r.config ? r.config : null;
|
|
1630
|
+
} catch { return null; }
|
|
1631
|
+
})(),
|
|
1632
|
+
recoveryContext.failedProviderType,
|
|
1621
1633
|
),
|
|
1622
1634
|
launchedByCoordinator: true,
|
|
1623
1635
|
}
|
|
@@ -14,6 +14,8 @@ 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
16
|
import { delegatedWorkerAutoApproveSettings, resolveProviderMaxParallel, resolveNodeSchedulingPriority, normalizeMeshSchedulingStrategy, resolveMaxParallelTasks, resolveMaxReadonlyParallelTasks, resolveCoordinatorIdlePushPolicy } from '../repo-mesh-types.js';
|
|
17
|
+
import { loadRepoMeshJsonConfig } from '../config/mesh-json-config.js';
|
|
18
|
+
import type { RepoMeshDeclarativeConfig } from '../config/mesh-json-config.js';
|
|
17
19
|
import type { RepoMeshSchedulingStrategy } from '../repo-mesh-types.js';
|
|
18
20
|
import { normalizeMeshNodeId, meshNodeIdMatches, daemonIdsEquivalent, canonicalDaemonId, expandDaemonIdForms, normalizeMeshWorkspaceForCompare, meshWorkspacesEquivalent, sessionIdsEquivalent, normalizeNodeCapabilitySlots, isMeshTaskDifficulty, withStatusProbeMarker, type MeshNodeIdentified, type NodeCapabilitySlot, type MeshTaskDifficulty } from '@adhdev/mesh-shared';
|
|
19
21
|
import { resolveNodeCapabilitySlots } from './mesh-node-slots.js';
|
|
@@ -90,6 +92,25 @@ export function __resetIdleAutoFastForwardForTests(): void {
|
|
|
90
92
|
autoFastForwardWorkspaceLease.clear();
|
|
91
93
|
}
|
|
92
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Load the repo-shared `.adhdev/mesh.json` for a node's workspace, tolerating a
|
|
97
|
+
* missing/invalid file (returns null → resolver falls back to provider-spec
|
|
98
|
+
* defaults, i.e. exactly the pre-providerDefaults behavior). Only the
|
|
99
|
+
* `providerDefaults` zone influences the delegated-worker MODE selection; it never
|
|
100
|
+
* touches the ENABLE decision. When a node carries no workspace path (should not
|
|
101
|
+
* happen for a launchable node, but be defensive), we skip the read entirely.
|
|
102
|
+
*/
|
|
103
|
+
function loadRepoConfigForNode(node: any): RepoMeshDeclarativeConfig | null {
|
|
104
|
+
const workspace = typeof node?.workspace === 'string' && node.workspace.trim() ? node.workspace.trim() : '';
|
|
105
|
+
if (!workspace) return null;
|
|
106
|
+
try {
|
|
107
|
+
const result = loadRepoMeshJsonConfig(workspace);
|
|
108
|
+
return result.sourceType === 'repo_file' && result.config ? result.config : null;
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
93
114
|
export function getMeshWithCache(components: DaemonComponents, meshId: string): any | undefined {
|
|
94
115
|
const localMesh = getMesh(meshId);
|
|
95
116
|
const cachedMesh = components.router?.getCachedInlineMesh(meshId);
|
|
@@ -708,6 +729,8 @@ export function tryAssignQueueTask(
|
|
|
708
729
|
mesh?.policy,
|
|
709
730
|
node?.policy,
|
|
710
731
|
components.providerLoader?.getMeta(providerType),
|
|
732
|
+
loadRepoConfigForNode(node),
|
|
733
|
+
providerType,
|
|
711
734
|
),
|
|
712
735
|
...(localDaemonId ? { meshCoordinatorDaemonId: localDaemonId } : {}),
|
|
713
736
|
// COMPLETION-PROPAGATION F5: (re)stamp the coordinator SESSION anchor from THIS
|
|
@@ -1698,8 +1721,20 @@ function liveSessionCountForNode(components: DaemonComponents, meshId: string, n
|
|
|
1698
1721
|
* free claimer, and is excluded — so a read-only auto-launch onto a busy-but-no-idle node
|
|
1699
1722
|
* is still allowed. A node with NO live mesh session at all (dead, or never launched) does
|
|
1700
1723
|
* not match, preserving the legitimate first-session spawn.
|
|
1724
|
+
*
|
|
1725
|
+
* PROVIDER-MATCH gate (DISPATCH-DEADLOCK-PROVIDER-MISMATCH): a live session only suppresses
|
|
1726
|
+
* this launch if it could ACTUALLY claim THIS task. A session whose providerType does not
|
|
1727
|
+
* satisfy the task's requiredTags (e.g. a claude-cli coordinator/worker session on a node,
|
|
1728
|
+
* while the pending task is required_tags: [provider=codex-cli]) is NOT a pending claimer for
|
|
1729
|
+
* this task — claimNextQueueTask's nodeSatisfiesRequiredTags gate would reject its claim. Left
|
|
1730
|
+
* unchecked, such a mismatched session made this gate return true for a task it can never claim,
|
|
1731
|
+
* so the required-provider worker never auto-launched AND no session could claim → nobody made
|
|
1732
|
+
* progress → permanent silent deadlock. Mirror the claim path: build the session's own
|
|
1733
|
+
* capability tags (its providerType pinned onto the node) and only count it as a pending claimer
|
|
1734
|
+
* when those tags satisfy task.requiredTags. `node` is passed in so the tag set reflects this
|
|
1735
|
+
* node's os/arch/worktree/converge context, matching claimNextQueueTask exactly.
|
|
1701
1736
|
*/
|
|
1702
|
-
function nodeHasLiveSessionPendingClaim(components: DaemonComponents, meshId: string, nodeId: string): boolean {
|
|
1737
|
+
function nodeHasLiveSessionPendingClaim(components: DaemonComponents, meshId: string, nodeId: string, task: MeshWorkQueueEntry, node: any): boolean {
|
|
1703
1738
|
// (A) AUTOLAUNCH-CLAIM-CHURN remote-awareness: a task whose auto-launch record targets this
|
|
1704
1739
|
// node and is still inside its await-claim window (base or backoff) already has a session on
|
|
1705
1740
|
// its way to claim — even when that session is REMOTE and thus invisible to the local
|
|
@@ -1738,7 +1773,20 @@ function nodeHasLiveSessionPendingClaim(components: DaemonComponents, meshId: st
|
|
|
1738
1773
|
if (isTerminalSessionStatus(status)) return false; // dead → no claimer here, allow launch
|
|
1739
1774
|
const sessionId = readNonEmptyString(state.instanceId);
|
|
1740
1775
|
if (sessionId && busySessionIds.has(sessionId)) return false; // busy with its own assigned task
|
|
1741
|
-
|
|
1776
|
+
// PROVIDER-MATCH gate (DISPATCH-DEADLOCK-PROVIDER-MISMATCH): only count this session as a
|
|
1777
|
+
// pending claimer if its own provider could satisfy THIS task's requiredTags. A session
|
|
1778
|
+
// whose providerType does not match (e.g. a claude-cli session while task requires
|
|
1779
|
+
// provider=codex-cli) can never claim this task via claimNextQueueTask's
|
|
1780
|
+
// nodeSatisfiesRequiredTags gate, so it must not suppress the required-provider launch —
|
|
1781
|
+
// otherwise the task deadlocks (mismatched session blocks launch, yet cannot claim). Mirror
|
|
1782
|
+
// the claim path: pin the session's providerType onto this node and check the tags.
|
|
1783
|
+
if (task.requiredTags?.length) {
|
|
1784
|
+
const sessionProviderType = state.type || readNonEmptyString(settings.providerType);
|
|
1785
|
+
if (sessionProviderType && !nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, sessionProviderType))) {
|
|
1786
|
+
return false; // provider mismatch → this session can't claim this task; not a pending claimer
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
return true; // live + unassigned + provider-capable → will claim the pending task itself
|
|
1742
1790
|
});
|
|
1743
1791
|
}
|
|
1744
1792
|
|
|
@@ -2250,7 +2298,7 @@ async function maybeAutoLaunchOneQueueSession(components: DaemonComponents, mesh
|
|
|
2250
2298
|
// busy-but-no-idle node is still allowed. Skip with a transient (non-actionable)
|
|
2251
2299
|
// reason so the coordinator is not paged; the 4s reconcile retries, and once the
|
|
2252
2300
|
// existing session goes terminal this gate clears and a legitimate launch proceeds.
|
|
2253
|
-
if (nodeHasLiveSessionPendingClaim(components, meshId, nodeId)) {
|
|
2301
|
+
if (nodeHasLiveSessionPendingClaim(components, meshId, nodeId, task, node)) {
|
|
2254
2302
|
markAutoLaunch(meshId, task.id, { status: 'skipped', reason: 'node_has_live_session_pending_claim', nodeId });
|
|
2255
2303
|
continue;
|
|
2256
2304
|
}
|
|
@@ -2327,6 +2375,8 @@ async function maybeAutoLaunchOneQueueSession(components: DaemonComponents, mesh
|
|
|
2327
2375
|
mesh?.policy,
|
|
2328
2376
|
node?.policy,
|
|
2329
2377
|
components.providerLoader?.getMeta(resolved.providerType),
|
|
2378
|
+
loadRepoConfigForNode(node),
|
|
2379
|
+
resolved.providerType,
|
|
2330
2380
|
),
|
|
2331
2381
|
launchedByCoordinator: true,
|
|
2332
2382
|
autoLaunchedForQueueTaskId: task.id,
|
package/src/repo-mesh-types.ts
CHANGED
|
@@ -17,6 +17,9 @@ import type { MeshMagiActivitySummary } from './mesh/mesh-magi-status.js';
|
|
|
17
17
|
import type { MagiKindPanelMap, DifficultyBrainMap, NodeCapabilitySlot } from '@adhdev/mesh-shared';
|
|
18
18
|
import type { ProviderModule } from './providers/contracts.js';
|
|
19
19
|
import { deriveAutoApproveModeRisk } from './providers/auto-approve-modes.js';
|
|
20
|
+
// Type-only import (no runtime cycle) — mesh-json-config imports types from this
|
|
21
|
+
// module, and this direction is `import type` so tsc erases it at emit.
|
|
22
|
+
import type { RepoMeshDeclarativeConfig } from './config/mesh-json-config.js';
|
|
20
23
|
|
|
21
24
|
// ─── Core Mesh Types ────────────────────────────
|
|
22
25
|
|
|
@@ -804,14 +807,40 @@ export function mergeAndNormalizePolicy(
|
|
|
804
807
|
|
|
805
808
|
/**
|
|
806
809
|
* Resolve delegated worker auto-approve. Legacy providers return a boolean. Providers
|
|
807
|
-
* with modes return
|
|
810
|
+
* with modes return a mode id, except a dangerous mode is downgraded to a
|
|
808
811
|
* non-dangerous PTY mode unless mesh/node policy explicitly opts in.
|
|
812
|
+
*
|
|
813
|
+
* THREE EXPLICIT STAGES — do not collapse them; the ordering is a hard MAGI
|
|
814
|
+
* invariant:
|
|
815
|
+
*
|
|
816
|
+
* ① ENABLE gate (machine-local policy only): node boolean > mesh boolean.
|
|
817
|
+
* `enabled=false` returns `false` IMMEDIATELY, BEFORE any mode selection.
|
|
818
|
+
* The repo `mesh.json` providerDefaults has ZERO influence here — a
|
|
819
|
+
* node/mesh opt-out is never overridden by a repo-declared requested mode.
|
|
820
|
+
*
|
|
821
|
+
* ② MODE selection (only when enabled === true):
|
|
822
|
+
* task override [FUTURE — param reserved below, not yet wired] >
|
|
823
|
+
* repo mesh.json providerDefaults.autoApproveModes[providerType] >
|
|
824
|
+
* provider spec autoApproveModes.default.
|
|
825
|
+
* A repo-requested mode ID is adopted ONLY when it exists in the provider's
|
|
826
|
+
* own `autoApproveModes.modes`; an unknown/stale/typo'd ID is IGNORED and we
|
|
827
|
+
* fall back to the provider default (fail-closed: never coerce into a
|
|
828
|
+
* dangerous mode via a bad ID).
|
|
829
|
+
*
|
|
830
|
+
* ③ DANGEROUS gate: whichever mode stage ② picked, if it is dangerous and the
|
|
831
|
+
* machine-local delegatedWorkerDangerousModeAllow is not set, downgrade to a
|
|
832
|
+
* non-dangerous PTY-parse mode (or `false` if none exists).
|
|
809
833
|
*/
|
|
810
834
|
export function resolveDelegatedWorkerAutoApprove(
|
|
811
835
|
meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null,
|
|
812
836
|
nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null,
|
|
813
837
|
provider?: Pick<ProviderModule, 'autoApproveModes'> | null,
|
|
838
|
+
repoConfig?: RepoMeshDeclarativeConfig | null,
|
|
839
|
+
// providerType is needed to look up the repo-declared requested mode; it is
|
|
840
|
+
// separate from `provider` because the caller resolves the spec independently.
|
|
841
|
+
providerType?: string | null,
|
|
814
842
|
): boolean | string {
|
|
843
|
+
// ── ① ENABLE gate — machine-local only. false short-circuits before mode. ──
|
|
815
844
|
let enabled = true;
|
|
816
845
|
if (typeof nodePolicy?.delegatedWorkerAutoApprove === 'boolean') {
|
|
817
846
|
enabled = nodePolicy.delegatedWorkerAutoApprove;
|
|
@@ -822,16 +851,32 @@ export function resolveDelegatedWorkerAutoApprove(
|
|
|
822
851
|
|
|
823
852
|
const modes = provider?.autoApproveModes;
|
|
824
853
|
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
854
|
|
|
855
|
+
// ── ② MODE selection (enabled only). Repo providerDefaults MAY override the
|
|
856
|
+
// provider spec default here — but only with a mode ID the spec knows. ──
|
|
857
|
+
// Future: a per-task override would slot in ahead of the repo default; the
|
|
858
|
+
// signature reserves that precedence but no task override is wired yet.
|
|
859
|
+
const requestedModeRaw = typeof providerType === 'string'
|
|
860
|
+
? repoConfig?.providerDefaults?.autoApproveModes?.[providerType.trim()]
|
|
861
|
+
: undefined;
|
|
862
|
+
const requestedModeId = typeof requestedModeRaw === 'string' && requestedModeRaw.trim()
|
|
863
|
+
? requestedModeRaw.trim()
|
|
864
|
+
: '';
|
|
865
|
+
const requestedMode = requestedModeId
|
|
866
|
+
? modes.modes.find((mode) => mode.id === requestedModeId)
|
|
867
|
+
: undefined;
|
|
868
|
+
const selectedMode = requestedMode
|
|
869
|
+
?? modes.modes.find((mode) => mode.id === modes.default);
|
|
870
|
+
if (!selectedMode || selectedMode.strategy === 'post-boot-command') return false;
|
|
871
|
+
|
|
872
|
+
// ── ③ DANGEROUS gate — downgrade a dangerous selection without machine opt-in. ──
|
|
828
873
|
const dangerousAllowed = resolveDelegatedWorkerDangerousModeAllow(meshPolicy, nodePolicy);
|
|
829
|
-
if (deriveAutoApproveModeRisk(
|
|
830
|
-
const ptyFallback = modes
|
|
874
|
+
if (deriveAutoApproveModeRisk(selectedMode) === 'dangerous' && !dangerousAllowed) {
|
|
875
|
+
const ptyFallback = modes.modes.find((mode) =>
|
|
831
876
|
mode.strategy === 'pty-parse-default' && deriveAutoApproveModeRisk(mode) !== 'dangerous');
|
|
832
877
|
return ptyFallback?.id || false;
|
|
833
878
|
}
|
|
834
|
-
return
|
|
879
|
+
return selectedMode.id;
|
|
835
880
|
}
|
|
836
881
|
|
|
837
882
|
export function resolveDelegatedWorkerDangerousModeAllow(
|
|
@@ -849,12 +894,14 @@ export function delegatedWorkerAutoApproveSettings(
|
|
|
849
894
|
meshPolicy?: Pick<RepoMeshPolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null,
|
|
850
895
|
nodePolicy?: Pick<RepoMeshNodePolicy, 'delegatedWorkerAutoApprove' | 'delegatedWorkerDangerousModeAllow'> | null,
|
|
851
896
|
provider?: Pick<ProviderModule, 'autoApproveModes'> | null,
|
|
897
|
+
repoConfig?: RepoMeshDeclarativeConfig | null,
|
|
898
|
+
providerType?: string | null,
|
|
852
899
|
): {
|
|
853
900
|
autoApprove: boolean | undefined;
|
|
854
901
|
autoApproveMode: string | undefined;
|
|
855
902
|
delegatedWorkerDangerousModeAllow: boolean;
|
|
856
903
|
} {
|
|
857
|
-
const resolved = resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy, provider);
|
|
904
|
+
const resolved = resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy, provider, repoConfig, providerType);
|
|
858
905
|
const delegatedWorkerDangerousModeAllow = resolveDelegatedWorkerDangerousModeAllow(meshPolicy, nodePolicy);
|
|
859
906
|
return typeof resolved === 'string'
|
|
860
907
|
? { autoApprove: undefined, autoApproveMode: resolved, delegatedWorkerDangerousModeAllow }
|