@adhdev/daemon-core 1.0.18-rc.16 → 1.0.18-rc.18

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/index.mjs CHANGED
@@ -25,6 +25,62 @@ var __copyProps = (to, from, except, desc) => {
25
25
  };
26
26
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
27
 
28
+ // src/providers/auto-approve-modes.ts
29
+ function isKnownDangerousLaunchArg(arg) {
30
+ const normalized = arg.trim();
31
+ if (KNOWN_DANGEROUS_LAUNCH_ARGS.has(normalized)) return true;
32
+ return normalized.startsWith("--dangerously-skip-permissions=") || normalized.startsWith("--dangerously-bypass-approvals-and-sandbox=");
33
+ }
34
+ function deriveAutoApproveModeRisk(mode) {
35
+ return Array.isArray(mode.launchArgs) && mode.launchArgs.some(isKnownDangerousLaunchArg) ? "dangerous" : mode.risk;
36
+ }
37
+ function inactiveMode(modeId = "") {
38
+ return { active: false, strategy: "pty-parse-default", modeId };
39
+ }
40
+ function resolveConfiguredMode(provider, mode, settings) {
41
+ if (mode.strategy === "post-boot-command") return inactiveMode(mode.id);
42
+ if (settings?.launchedByCoordinator === true && settings.delegatedWorkerDangerousModeAllow !== true && deriveAutoApproveModeRisk(mode) === "dangerous") {
43
+ const fallback = provider.autoApproveModes?.modes.find((candidate) => candidate.strategy === "pty-parse-default" && deriveAutoApproveModeRisk(candidate) !== "dangerous");
44
+ return fallback ? { active: true, strategy: fallback.strategy, modeId: fallback.id } : inactiveMode(mode.id);
45
+ }
46
+ return { active: true, strategy: mode.strategy, modeId: mode.id };
47
+ }
48
+ function resolveProviderAutoApproveMode(provider, settings) {
49
+ const config = provider.autoApproveModes;
50
+ const explicitModeId = settings?.autoApproveMode;
51
+ if (typeof explicitModeId === "string") {
52
+ const mode = config?.modes.find((candidate) => candidate.id === explicitModeId);
53
+ if (!mode) return inactiveMode(explicitModeId);
54
+ return resolveConfiguredMode(provider, mode, settings);
55
+ }
56
+ const explicitLegacy = settings?.autoApprove;
57
+ const providerLegacyDefault = provider.settings?.autoApprove?.default;
58
+ const legacyActive = typeof explicitLegacy === "boolean" ? explicitLegacy : typeof providerLegacyDefault === "boolean" ? providerLegacyDefault : false;
59
+ if (!legacyActive) return inactiveMode();
60
+ if (!config) {
61
+ return { active: true, strategy: "pty-parse-default", modeId: "legacy" };
62
+ }
63
+ const defaultMode = config.modes.find((mode) => mode.id === config.default);
64
+ if (!defaultMode) return inactiveMode(config.default);
65
+ return resolveConfiguredMode(provider, defaultMode, settings);
66
+ }
67
+ function findProviderAutoApproveMode(provider, modeId) {
68
+ return provider?.autoApproveModes?.modes.find((mode) => mode.id === modeId);
69
+ }
70
+ var KNOWN_DANGEROUS_LAUNCH_ARGS;
71
+ var init_auto_approve_modes = __esm({
72
+ "src/providers/auto-approve-modes.ts"() {
73
+ "use strict";
74
+ KNOWN_DANGEROUS_LAUNCH_ARGS = /* @__PURE__ */ new Set([
75
+ "--dangerously-skip-permissions",
76
+ "--dangerously-bypass-approvals-and-sandbox",
77
+ "bypassPermissions",
78
+ "sandbox_mode=danger-full-access",
79
+ "approval_policy=never"
80
+ ]);
81
+ }
82
+ });
83
+
28
84
  // src/repo-mesh-types.ts
29
85
  function normalizeMeshSchedulingStrategy(value) {
30
86
  if (typeof value !== "string") return DEFAULT_MESH_SCHEDULING_STRATEGY;
@@ -111,6 +167,11 @@ function mergeAndNormalizePolicy(base, patch) {
111
167
  } else {
112
168
  delete policy.autoConvergeCodeChange;
113
169
  }
170
+ if (policy.delegatedWorkerDangerousModeAllow === true) {
171
+ policy.delegatedWorkerDangerousModeAllow = true;
172
+ } else {
173
+ delete policy.delegatedWorkerDangerousModeAllow;
174
+ }
114
175
  if (policy.coordinatorIdlePushPolicy === "auto_silent_on_dispatch") {
115
176
  policy.coordinatorIdlePushPolicy = "auto_silent_on_dispatch";
116
177
  } else {
@@ -118,14 +179,38 @@ function mergeAndNormalizePolicy(base, patch) {
118
179
  }
119
180
  return policy;
120
181
  }
121
- function resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy) {
182
+ function resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy, provider, repoConfig, providerType) {
183
+ let enabled = true;
122
184
  if (typeof nodePolicy?.delegatedWorkerAutoApprove === "boolean") {
123
- return nodePolicy.delegatedWorkerAutoApprove;
124
- }
125
- if (typeof meshPolicy?.delegatedWorkerAutoApprove === "boolean") {
126
- return meshPolicy.delegatedWorkerAutoApprove;
127
- }
128
- return true;
185
+ enabled = nodePolicy.delegatedWorkerAutoApprove;
186
+ } else if (typeof meshPolicy?.delegatedWorkerAutoApprove === "boolean") {
187
+ enabled = meshPolicy.delegatedWorkerAutoApprove;
188
+ }
189
+ if (!enabled) return false;
190
+ const modes = provider?.autoApproveModes;
191
+ if (!modes) return true;
192
+ const requestedModeRaw = typeof providerType === "string" ? repoConfig?.providerDefaults?.autoApproveModes?.[providerType.trim()] : void 0;
193
+ const requestedModeId = typeof requestedModeRaw === "string" && requestedModeRaw.trim() ? requestedModeRaw.trim() : "";
194
+ const requestedMode = requestedModeId ? modes.modes.find((mode) => mode.id === requestedModeId) : void 0;
195
+ const selectedMode = requestedMode ?? modes.modes.find((mode) => mode.id === modes.default);
196
+ if (!selectedMode || selectedMode.strategy === "post-boot-command") return false;
197
+ const dangerousAllowed = resolveDelegatedWorkerDangerousModeAllow(meshPolicy, nodePolicy);
198
+ if (deriveAutoApproveModeRisk(selectedMode) === "dangerous" && !dangerousAllowed) {
199
+ const ptyFallback = modes.modes.find((mode) => mode.strategy === "pty-parse-default" && deriveAutoApproveModeRisk(mode) !== "dangerous");
200
+ return ptyFallback?.id || false;
201
+ }
202
+ return selectedMode.id;
203
+ }
204
+ function resolveDelegatedWorkerDangerousModeAllow(meshPolicy, nodePolicy) {
205
+ if (typeof nodePolicy?.delegatedWorkerDangerousModeAllow === "boolean") {
206
+ return nodePolicy.delegatedWorkerDangerousModeAllow;
207
+ }
208
+ return meshPolicy?.delegatedWorkerDangerousModeAllow === true;
209
+ }
210
+ function delegatedWorkerAutoApproveSettings(meshPolicy, nodePolicy, provider, repoConfig, providerType) {
211
+ const resolved = resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy, provider, repoConfig, providerType);
212
+ const delegatedWorkerDangerousModeAllow = resolveDelegatedWorkerDangerousModeAllow(meshPolicy, nodePolicy);
213
+ return typeof resolved === "string" ? { autoApprove: void 0, autoApproveMode: resolved, delegatedWorkerDangerousModeAllow } : { autoApprove: resolved, autoApproveMode: void 0, delegatedWorkerDangerousModeAllow };
129
214
  }
130
215
  function resolveAllowSendKeysDestructive(meshPolicy, nodePolicy) {
131
216
  if (typeof nodePolicy?.allowSendKeysDestructive === "boolean") {
@@ -155,6 +240,7 @@ var MESH_SCHEDULING_STRATEGIES, DEFAULT_MESH_SCHEDULING_STRATEGY, MESH_CONVERGE_
155
240
  var init_repo_mesh_types = __esm({
156
241
  "src/repo-mesh-types.ts"() {
157
242
  "use strict";
243
+ init_auto_approve_modes();
158
244
  MESH_SCHEDULING_STRATEGIES = [
159
245
  "first_eligible",
160
246
  "least_loaded",
@@ -182,6 +268,7 @@ var init_repo_mesh_types = __esm({
182
268
  // any specific session manually; that override is preserved per-device.
183
269
  spawnedSessionVisibility: "hidden",
184
270
  delegatedWorkerAutoApprove: true,
271
+ delegatedWorkerDangerousModeAllow: false,
185
272
  sessionCleanupOnNodeRemove: "preserve",
186
273
  // MAGI auto-launches a worker session per pinned replica target with no idle
187
274
  // session; those stay idle-LIVE after their turn. Default ON (stop_and_delete)
@@ -221,6 +308,267 @@ var init_repo_mesh_types = __esm({
221
308
  }
222
309
  });
223
310
 
311
+ // src/config/mesh-json-config.ts
312
+ var mesh_json_config_exports = {};
313
+ __export(mesh_json_config_exports, {
314
+ MESH_JSON_CONFIG_LOCATIONS: () => MESH_JSON_CONFIG_LOCATIONS,
315
+ MESH_JSON_CONFIG_SCHEMA: () => MESH_JSON_CONFIG_SCHEMA,
316
+ MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE: () => MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE,
317
+ applyRepoMeshConfig: () => applyRepoMeshConfig,
318
+ buildMeshJsonConfigScaffold: () => buildMeshJsonConfigScaffold,
319
+ loadRepoMeshJsonConfig: () => loadRepoMeshJsonConfig,
320
+ mergeEffectiveCoordinatorConfig: () => mergeEffectiveCoordinatorConfig,
321
+ mergeEffectiveOperatingNotes: () => mergeEffectiveOperatingNotes,
322
+ normalizeRepoMeshDeclarativeConfig: () => normalizeRepoMeshDeclarativeConfig,
323
+ serializeMeshJsonConfigScaffold: () => serializeMeshJsonConfigScaffold
324
+ });
325
+ import { existsSync, readFileSync } from "fs";
326
+ import { join } from "path";
327
+ import * as yaml from "js-yaml";
328
+ function isRecord(value) {
329
+ return !!value && typeof value === "object" && !Array.isArray(value);
330
+ }
331
+ function parseConfigText(path46, text) {
332
+ if (/\.json$/i.test(path46)) return JSON.parse(text);
333
+ return yaml.load(text);
334
+ }
335
+ function normalizeOperatingNote(value) {
336
+ if (!isRecord(value)) return null;
337
+ const text = typeof value.text === "string" ? value.text.trim() : "";
338
+ if (!text) return null;
339
+ const category = value.category === "provider_quirk" || value.category === "pattern_to_avoid" || value.category === "recovery_lesson" ? value.category : void 0;
340
+ return {
341
+ text,
342
+ ...category ? { category } : {},
343
+ ...typeof value.createdAt === "string" ? { createdAt: value.createdAt } : {},
344
+ ...typeof value.sourceCoordinator === "string" ? { sourceCoordinator: value.sourceCoordinator } : {},
345
+ // Operating-notes lifecycle: a repo-declared note may pin itself or set an
346
+ // explicit expiry, same as a runtime-recorded one. Phase 2 (b)/(c): it may
347
+ // also declare supersedes/subjectKey to retire an earlier note or group
348
+ // same-subject notes for folding.
349
+ ...value.pinned === true ? { pinned: true } : {},
350
+ ...typeof value.expiresAt === "string" ? { expiresAt: value.expiresAt } : {},
351
+ ...typeof value.supersedes === "string" ? { supersedes: value.supersedes } : {},
352
+ ...typeof value.subjectKey === "string" ? { subjectKey: value.subjectKey } : {}
353
+ };
354
+ }
355
+ function normalizeRepoMeshDeclarativeConfig(parsed) {
356
+ const errors = [];
357
+ if (!isRecord(parsed)) return { valid: false, errors: ["config must be an object"] };
358
+ if (parsed.version !== 1) {
359
+ return { valid: false, errors: [`version must be 1 (got ${JSON.stringify(parsed.version)})`] };
360
+ }
361
+ const config = { version: 1 };
362
+ if (parsed.coordinator !== void 0) {
363
+ if (isRecord(parsed.coordinator)) {
364
+ const coord = {};
365
+ const c = parsed.coordinator;
366
+ if (typeof c.systemPromptOverride === "string") coord.systemPromptOverride = c.systemPromptOverride;
367
+ if (typeof c.systemPromptAppend === "string") coord.systemPromptAppend = c.systemPromptAppend;
368
+ if (Number.isFinite(Number(c.maxPromptChars))) coord.maxPromptChars = Number(c.maxPromptChars);
369
+ config.coordinator = coord;
370
+ } else {
371
+ errors.push("coordinator must be an object when provided");
372
+ }
373
+ }
374
+ if (parsed.operatingNotes !== void 0) {
375
+ if (Array.isArray(parsed.operatingNotes)) {
376
+ const notes = parsed.operatingNotes.map(normalizeOperatingNote).filter((n) => n !== null);
377
+ if (notes.length) config.operatingNotes = notes;
378
+ } else {
379
+ errors.push("operatingNotes must be an array when provided");
380
+ }
381
+ }
382
+ if (parsed.limits !== void 0) {
383
+ if (isRecord(parsed.limits)) {
384
+ const limits = {};
385
+ if (Number.isFinite(Number(parsed.limits.maxNoteChars))) limits.maxNoteChars = Number(parsed.limits.maxNoteChars);
386
+ if (Number.isFinite(Number(parsed.limits.maxNotes))) limits.maxNotes = Number(parsed.limits.maxNotes);
387
+ if (Object.keys(limits).length) config.limits = limits;
388
+ } else {
389
+ errors.push("limits must be an object when provided");
390
+ }
391
+ }
392
+ if (parsed.providerDefaults !== void 0) {
393
+ if (isRecord(parsed.providerDefaults)) {
394
+ const pd = {};
395
+ const rawModes = parsed.providerDefaults.autoApproveModes;
396
+ if (rawModes !== void 0) {
397
+ if (isRecord(rawModes)) {
398
+ const modes = {};
399
+ for (const [providerType, modeId] of Object.entries(rawModes)) {
400
+ const type = typeof providerType === "string" ? providerType.trim() : "";
401
+ const id = typeof modeId === "string" ? modeId.trim() : "";
402
+ if (type && id) modes[type] = id;
403
+ }
404
+ if (Object.keys(modes).length) pd.autoApproveModes = modes;
405
+ } else {
406
+ errors.push("providerDefaults.autoApproveModes must be an object when provided");
407
+ }
408
+ }
409
+ if (Object.keys(pd).length) config.providerDefaults = pd;
410
+ } else {
411
+ errors.push("providerDefaults must be an object when provided");
412
+ }
413
+ }
414
+ return { valid: true, config, errors };
415
+ }
416
+ function loadRepoMeshJsonConfig(workspace) {
417
+ const bases = [];
418
+ const ws = typeof workspace === "string" ? workspace.trim() : "";
419
+ if (ws) bases.push(ws);
420
+ let cwd = "";
421
+ try {
422
+ cwd = process.cwd();
423
+ } catch {
424
+ }
425
+ if (cwd && cwd !== ws) bases.push(cwd);
426
+ for (const base of bases) {
427
+ for (const relative5 of MESH_JSON_CONFIG_LOCATIONS) {
428
+ const configPath = join(base, relative5);
429
+ if (!existsSync(configPath)) continue;
430
+ try {
431
+ const parsed = parseConfigText(configPath, readFileSync(configPath, "utf-8"));
432
+ const result = normalizeRepoMeshDeclarativeConfig(parsed);
433
+ if (!result.valid || !result.config) {
434
+ return { source: relative5, sourceType: "invalid", path: configPath, error: result.errors.join("; ") };
435
+ }
436
+ return { config: result.config, source: relative5, sourceType: "repo_file", path: configPath };
437
+ } catch (error) {
438
+ return { source: relative5, sourceType: "invalid", path: configPath, error: error?.message || String(error) };
439
+ }
440
+ }
441
+ }
442
+ return {
443
+ source: "unavailable",
444
+ sourceType: "unavailable",
445
+ error: `No repo mesh config found. Checked: ${MESH_JSON_CONFIG_LOCATIONS.join(", ")}`
446
+ };
447
+ }
448
+ function mergeEffectiveCoordinatorConfig(repoCoord, localCoord) {
449
+ const out = { ...localCoord || {} };
450
+ const localOverride = localCoord?.systemPromptOverride?.trim();
451
+ const repoOverride = repoCoord?.systemPromptOverride?.trim();
452
+ if (localOverride) {
453
+ out.systemPromptOverride = localCoord.systemPromptOverride;
454
+ } else if (repoOverride) {
455
+ out.systemPromptOverride = repoCoord.systemPromptOverride;
456
+ } else {
457
+ delete out.systemPromptOverride;
458
+ }
459
+ const repoAppend = repoCoord?.systemPromptAppend?.trim() ? repoCoord.systemPromptAppend.trim() : "";
460
+ const localAppendRaw = localCoord?.systemPromptAppend ?? localCoord?.systemPromptSuffix;
461
+ const localAppend = localAppendRaw?.trim() ? localAppendRaw.trim() : "";
462
+ const stacked = [repoAppend, localAppend].filter(Boolean).join("\n\n");
463
+ if (stacked) {
464
+ out.systemPromptAppend = stacked;
465
+ delete out.systemPromptSuffix;
466
+ }
467
+ return out;
468
+ }
469
+ function mergeEffectiveOperatingNotes(repoNotes, ledgerNotes) {
470
+ const usable = (notes) => Array.isArray(notes) ? notes.filter((n) => n && typeof n.text === "string" && n.text.trim()) : [];
471
+ const repo = usable(repoNotes);
472
+ const ledger = usable(ledgerNotes);
473
+ const ledgerTexts = new Set(ledger.map((n) => n.text.trim()));
474
+ const repoKept = repo.filter((n) => !ledgerTexts.has(n.text.trim()));
475
+ const merged = [...repoKept, ...ledger];
476
+ return merged.length ? merged : void 0;
477
+ }
478
+ function applyRepoMeshConfig(mesh, repoConfig) {
479
+ if (!repoConfig) return mesh;
480
+ return {
481
+ ...mesh,
482
+ coordinator: mergeEffectiveCoordinatorConfig(repoConfig.coordinator, mesh.coordinator)
483
+ };
484
+ }
485
+ function buildMeshJsonConfigScaffold(mesh) {
486
+ const scaffold = { version: 1 };
487
+ const coord = {};
488
+ const override = mesh.coordinator?.systemPromptOverride;
489
+ if (typeof override === "string" && override.trim()) coord.systemPromptOverride = override;
490
+ const append = mesh.coordinator?.systemPromptAppend ?? mesh.coordinator?.systemPromptSuffix;
491
+ if (typeof append === "string" && append.trim()) coord.systemPromptAppend = append;
492
+ if (Object.keys(coord).length) scaffold.coordinator = coord;
493
+ return scaffold;
494
+ }
495
+ function serializeMeshJsonConfigScaffold(config) {
496
+ return JSON.stringify(config, null, 2);
497
+ }
498
+ var MESH_JSON_CONFIG_LOCATIONS, MESH_JSON_CONFIG_SCHEMA, MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE;
499
+ var init_mesh_json_config = __esm({
500
+ "src/config/mesh-json-config.ts"() {
501
+ "use strict";
502
+ MESH_JSON_CONFIG_LOCATIONS = [
503
+ ".adhdev/mesh.json",
504
+ ".adhdev/mesh.yaml",
505
+ ".adhdev/mesh.yml"
506
+ ];
507
+ MESH_JSON_CONFIG_SCHEMA = {
508
+ $schema: "https://json-schema.org/draft/2020-12/schema",
509
+ title: "ADHDev Repo Mesh Declarative Config",
510
+ type: "object",
511
+ additionalProperties: false,
512
+ required: ["version"],
513
+ properties: {
514
+ version: { const: 1 },
515
+ coordinator: {
516
+ type: "object",
517
+ additionalProperties: false,
518
+ properties: {
519
+ systemPromptOverride: { type: "string" },
520
+ systemPromptAppend: { type: "string" },
521
+ maxPromptChars: { type: "number", minimum: 1 }
522
+ }
523
+ },
524
+ operatingNotes: {
525
+ type: "array",
526
+ maxItems: 200,
527
+ items: {
528
+ type: "object",
529
+ additionalProperties: false,
530
+ required: ["text"],
531
+ properties: {
532
+ text: { type: "string", minLength: 1 },
533
+ category: { enum: ["provider_quirk", "pattern_to_avoid", "recovery_lesson"] },
534
+ createdAt: { type: "string" },
535
+ sourceCoordinator: { type: "string" }
536
+ }
537
+ }
538
+ },
539
+ limits: {
540
+ type: "object",
541
+ additionalProperties: false,
542
+ properties: {
543
+ maxNoteChars: { type: "number", minimum: 1 },
544
+ maxNotes: { type: "number", minimum: 1 }
545
+ }
546
+ },
547
+ providerDefaults: {
548
+ type: "object",
549
+ additionalProperties: false,
550
+ properties: {
551
+ // providerType → requested auto-approve mode ID. Mode IDs are
552
+ // validated against the live provider spec at resolve time; an
553
+ // unknown ID is ignored (provider default is used), so the schema
554
+ // only constrains the shape (string→string), not the ID values.
555
+ autoApproveModes: {
556
+ type: "object",
557
+ additionalProperties: { type: "string" }
558
+ }
559
+ }
560
+ }
561
+ }
562
+ };
563
+ MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE = {
564
+ autoApproveModes: {
565
+ "claude-cli": "accept-edits",
566
+ "codex-cli": "auto"
567
+ }
568
+ };
569
+ }
570
+ });
571
+
224
572
  // src/git/git-executor.ts
225
573
  var git_executor_exports = {};
226
574
  __export(git_executor_exports, {
@@ -437,10 +785,10 @@ function readInjected(value) {
437
785
  }
438
786
  function getDaemonBuildInfo() {
439
787
  if (cached) return cached;
440
- const commit = readInjected(true ? "74f34080bcd7c145e55fd7e5fd492a40a4942bc4" : void 0) ?? "unknown";
441
- const commitShort = readInjected(true ? "74f34080" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
442
- const version = readInjected(true ? "1.0.18-rc.16" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
443
- const builtAt = readInjected(true ? "2026-07-23T02:29:46.281Z" : void 0);
788
+ const commit = readInjected(true ? "56b83deb250a8ea89ba1a7ec587720e165d20062" : void 0) ?? "unknown";
789
+ const commitShort = readInjected(true ? "56b83deb" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
790
+ const version = readInjected(true ? "1.0.18-rc.18" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
791
+ const builtAt = readInjected(true ? "2026-07-23T06:00:17.638Z" : void 0);
444
792
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
445
793
  return cached;
446
794
  }
@@ -452,17 +800,17 @@ var init_build_info = __esm({
452
800
  });
453
801
 
454
802
  // src/git/change-impact-config.ts
455
- import { existsSync, readdirSync, readFileSync, statSync } from "fs";
456
- import { join } from "path";
457
- import * as yaml from "js-yaml";
458
- function isRecord(value) {
803
+ import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
804
+ import { join as join2 } from "path";
805
+ import * as yaml2 from "js-yaml";
806
+ function isRecord2(value) {
459
807
  return !!value && typeof value === "object" && !Array.isArray(value);
460
808
  }
461
809
  function isStringArray(value) {
462
810
  return Array.isArray(value) && value.every((v) => typeof v === "string" && v.length > 0);
463
811
  }
464
812
  function validateTarget(value, key2, errors) {
465
- if (!isRecord(value)) {
813
+ if (!isRecord2(value)) {
466
814
  errors.push(`impactTargets.${key2} must be an object`);
467
815
  return void 0;
468
816
  }
@@ -478,7 +826,7 @@ function validateTarget(value, key2, errors) {
478
826
  }
479
827
  function validateChangeImpactConfig(raw, source = "inline") {
480
828
  const errors = [];
481
- if (!isRecord(raw)) {
829
+ if (!isRecord2(raw)) {
482
830
  return { valid: false, errors: [`${source}: config must be an object`] };
483
831
  }
484
832
  const config = {};
@@ -495,7 +843,7 @@ function validateChangeImpactConfig(raw, source = "inline") {
495
843
  else errors.push("nonRuntimeRootFilePatterns must be an array of non-empty strings");
496
844
  }
497
845
  if (raw.impactTargets !== void 0) {
498
- if (!isRecord(raw.impactTargets)) {
846
+ if (!isRecord2(raw.impactTargets)) {
499
847
  errors.push("impactTargets must be an object");
500
848
  } else {
501
849
  const targets = {};
@@ -517,23 +865,23 @@ function validateChangeImpactConfig(raw, source = "inline") {
517
865
  }
518
866
  return { valid: errors.length === 0, errors, config: errors.length === 0 ? config : void 0 };
519
867
  }
520
- function parseConfigText(path46, text) {
868
+ function parseConfigText2(path46, text) {
521
869
  if (/\.json$/i.test(path46)) return JSON.parse(text);
522
- return yaml.load(text);
870
+ return yaml2.load(text);
523
871
  }
524
872
  function loadChangeImpactConfig(repoRoot) {
525
873
  for (const relative5 of CHANGE_IMPACT_CONFIG_LOCATIONS) {
526
- const configPath = join(repoRoot, relative5);
527
- if (!existsSync(configPath)) continue;
874
+ const configPath = join2(repoRoot, relative5);
875
+ if (!existsSync2(configPath)) continue;
528
876
  try {
529
- const text = readFileSync(configPath, "utf-8");
877
+ const text = readFileSync2(configPath, "utf-8");
530
878
  let mtimeMs = 0;
531
879
  try {
532
880
  mtimeMs = statSync(configPath).mtimeMs;
533
881
  } catch {
534
882
  mtimeMs = text.length;
535
883
  }
536
- const parsed = parseConfigText(configPath, text);
884
+ const parsed = parseConfigText2(configPath, text);
537
885
  const validation = validateChangeImpactConfig(parsed, relative5);
538
886
  if (!validation.valid) {
539
887
  return {
@@ -602,10 +950,10 @@ function suggestChangeImpactConfig(repoRoot) {
602
950
  const daemon = /* @__PURE__ */ new Set();
603
951
  const web = /* @__PURE__ */ new Set();
604
952
  const unclassified = [];
605
- const roots = ["packages", join("oss", "packages")];
953
+ const roots = ["packages", join2("oss", "packages")];
606
954
  for (const rel of roots) {
607
- const packagesRoot = join(repoRoot, rel);
608
- if (!existsSync(packagesRoot)) continue;
955
+ const packagesRoot = join2(repoRoot, rel);
956
+ if (!existsSync2(packagesRoot)) continue;
609
957
  for (const name of listPackageDirs(packagesRoot)) {
610
958
  if (/(^web[-.]|[-.]web$)/i.test(name) || /dashboard|frontend|ui$/i.test(name)) {
611
959
  web.add(name);
@@ -693,7 +1041,7 @@ var init_change_impact_config = __esm({
693
1041
  });
694
1042
 
695
1043
  // src/git/git-status.ts
696
- import { join as join2 } from "path";
1044
+ import { join as join3 } from "path";
697
1045
  function statusCacheKey(workspace, options) {
698
1046
  const includeSubmodules = options.includeSubmodules !== false;
699
1047
  const refreshUpstream = options.refreshUpstream === true;
@@ -876,7 +1224,7 @@ async function refineVerdictThroughSubmodules(repoPath, fromRef, toRef, options,
876
1224
  }
877
1225
  let subVerdict;
878
1226
  try {
879
- subVerdict = await classifyChangedPackages(join2(repoPath, subPath), range.from, range.to, {
1227
+ subVerdict = await classifyChangedPackages(join3(repoPath, subPath), range.from, range.to, {
880
1228
  ...options,
881
1229
  // Do not force the root's injected config onto the submodule — let it resolve
882
1230
  // its own .adhdev/change-impact.* (or fall back to defaults).
@@ -1641,8 +1989,8 @@ __export(config_exports, {
1641
1989
  updateConfig: () => updateConfig
1642
1990
  });
1643
1991
  import { homedir } from "os";
1644
- import { join as join3 } from "path";
1645
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync, chmodSync } from "fs";
1992
+ import { join as join4 } from "path";
1993
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync, chmodSync } from "fs";
1646
1994
  import { randomUUID } from "crypto";
1647
1995
  function resolveProviderSourceMode(providerSourceMode, legacyDisableUpstream) {
1648
1996
  if (providerSourceMode === "normal" || providerSourceMode === "no-upstream") {
@@ -1739,25 +2087,25 @@ function ensureMachineId(config) {
1739
2087
  }
1740
2088
  function getConfigDir() {
1741
2089
  const override = process.env.ADHDEV_CONFIG_DIR;
1742
- const dir = override && override.trim() ? override.trim() : join3(homedir(), ".adhdev");
1743
- if (!existsSync2(dir)) {
2090
+ const dir = override && override.trim() ? override.trim() : join4(homedir(), ".adhdev");
2091
+ if (!existsSync3(dir)) {
1744
2092
  mkdirSync(dir, { recursive: true });
1745
2093
  }
1746
2094
  return dir;
1747
2095
  }
1748
2096
  function getDaemonDataDir() {
1749
- const dir = join3(getConfigDir(), "daemon");
1750
- if (!existsSync2(dir)) {
2097
+ const dir = join4(getConfigDir(), "daemon");
2098
+ if (!existsSync3(dir)) {
1751
2099
  mkdirSync(dir, { recursive: true });
1752
2100
  }
1753
2101
  return dir;
1754
2102
  }
1755
2103
  function getConfigPath() {
1756
- return join3(getConfigDir(), "config.json");
2104
+ return join4(getConfigDir(), "config.json");
1757
2105
  }
1758
2106
  function migrateStateToStateFile(raw) {
1759
- const statePath = join3(getConfigDir(), "state.json");
1760
- if (existsSync2(statePath)) return;
2107
+ const statePath = join4(getConfigDir(), "state.json");
2108
+ if (existsSync3(statePath)) return;
1761
2109
  const recentActivity = Array.isArray(raw.recentActivity) ? raw.recentActivity : [];
1762
2110
  const savedProviderSessions = Array.isArray(raw.savedProviderSessions) ? raw.savedProviderSessions : [];
1763
2111
  const legacySessionReads = isPlainObject(raw.recentSessionReads) ? raw.recentSessionReads : {};
@@ -1781,7 +2129,7 @@ function migrateStateToStateFile(raw) {
1781
2129
  }
1782
2130
  function loadConfig() {
1783
2131
  const configPath = getConfigPath();
1784
- if (!existsSync2(configPath)) {
2132
+ if (!existsSync3(configPath)) {
1785
2133
  const initialized = ensureMachineId({ ...DEFAULT_CONFIG });
1786
2134
  try {
1787
2135
  saveConfig(initialized.config);
@@ -1790,7 +2138,7 @@ function loadConfig() {
1790
2138
  return initialized.config;
1791
2139
  }
1792
2140
  try {
1793
- const raw = readFileSync2(configPath, "utf-8");
2141
+ const raw = readFileSync3(configPath, "utf-8");
1794
2142
  const parsed = JSON.parse(raw);
1795
2143
  migrateStateToStateFile(parsed);
1796
2144
  const normalizedInput = normalizeConfig(parsed);
@@ -1812,7 +2160,7 @@ function saveConfig(config) {
1812
2160
  const configPath = getConfigPath();
1813
2161
  const dir = getConfigDir();
1814
2162
  const normalized = normalizeConfig(config);
1815
- if (!existsSync2(dir)) {
2163
+ if (!existsSync3(dir)) {
1816
2164
  mkdirSync(dir, { recursive: true, mode: 448 });
1817
2165
  }
1818
2166
  writeFileSync(configPath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
@@ -1888,7 +2236,7 @@ __export(git_worktree_exports, {
1888
2236
  });
1889
2237
  import * as path4 from "path";
1890
2238
  import { mkdir } from "fs/promises";
1891
- import { existsSync as existsSync3 } from "fs";
2239
+ import { existsSync as existsSync4 } from "fs";
1892
2240
  import { execFile as execFile2 } from "child_process";
1893
2241
  import { promisify as promisify2 } from "util";
1894
2242
  function resolveWorktreePath(repoRoot, meshName, branch) {
@@ -1981,7 +2329,7 @@ async function createWorktree(opts) {
1981
2329
  const { repoRoot, branch, baseBranch, meshName } = opts;
1982
2330
  const remote = (opts.remote || "origin").trim() || "origin";
1983
2331
  const targetDir = opts.targetDir || resolveWorktreePath(repoRoot, meshName, branch);
1984
- if (existsSync3(targetDir)) {
2332
+ if (existsSync4(targetDir)) {
1985
2333
  throw new Error(`Worktree target directory already exists: ${targetDir}`);
1986
2334
  }
1987
2335
  await mkdir(path4.dirname(targetDir), { recursive: true });
@@ -2006,7 +2354,7 @@ async function createWorktree(opts) {
2006
2354
  } catch (error) {
2007
2355
  const stderr = typeof error.stderr === "string" ? error.stderr : "";
2008
2356
  if (/already exists/i.test(stderr)) {
2009
- if (existsSync3(targetDir)) {
2357
+ if (existsSync4(targetDir)) {
2010
2358
  throw new Error(`Worktree target directory was created concurrently: ${targetDir}`);
2011
2359
  }
2012
2360
  throw new Error(`Branch '${branch}' already exists or is checked out in another worktree`);
@@ -2021,7 +2369,7 @@ async function createWorktree(opts) {
2021
2369
  };
2022
2370
  }
2023
2371
  async function removeWorktree(repoRoot, worktreePath, opts = {}) {
2024
- if (!existsSync3(worktreePath)) {
2372
+ if (!existsSync4(worktreePath)) {
2025
2373
  await pruneWorktrees(repoRoot);
2026
2374
  return { success: true, removedPath: worktreePath };
2027
2375
  }
@@ -3276,17 +3624,17 @@ __export(mesh_config_exports, {
3276
3624
  updateMesh: () => updateMesh,
3277
3625
  updateNode: () => updateNode
3278
3626
  });
3279
- import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
3280
- import { join as join6 } from "path";
3627
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
3628
+ import { join as join7 } from "path";
3281
3629
  import { randomBytes, randomUUID as randomUUID3 } from "crypto";
3282
3630
  function getMeshConfigPath() {
3283
- return join6(getConfigDir(), "meshes.json");
3631
+ return join7(getConfigDir(), "meshes.json");
3284
3632
  }
3285
3633
  function loadMeshConfig() {
3286
3634
  const path46 = getMeshConfigPath();
3287
- if (!existsSync5(path46)) return { meshes: [] };
3635
+ if (!existsSync6(path46)) return { meshes: [] };
3288
3636
  try {
3289
- const raw = JSON.parse(readFileSync3(path46, "utf-8"));
3637
+ const raw = JSON.parse(readFileSync4(path46, "utf-8"));
3290
3638
  if (!raw || !Array.isArray(raw.meshes)) return { meshes: [] };
3291
3639
  const config = raw;
3292
3640
  const migrated = migrateLoadedMeshConfig(config);
@@ -4890,8 +5238,8 @@ var init_contracts = __esm({
4890
5238
  });
4891
5239
 
4892
5240
  // src/mesh/mesh-events-pending.ts
4893
- import { appendFileSync, existsSync as existsSync7, readFileSync as readFileSync4, renameSync as renameSync2, statSync as statSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
4894
- import { join as join8 } from "path";
5241
+ import { appendFileSync, existsSync as existsSync8, readFileSync as readFileSync5, renameSync as renameSync2, statSync as statSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
5242
+ import { join as join9 } from "path";
4895
5243
  import { randomUUID as randomUUID5 } from "crypto";
4896
5244
  function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
4897
5245
  return expandDaemonIdForms(coordinatorDaemonId);
@@ -5114,9 +5462,9 @@ function getPendingEventsPath(meshId, coordinatorDaemonId) {
5114
5462
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
5115
5463
  if (coordinatorDaemonId) {
5116
5464
  const safeDaemon = coordinatorDaemonId.replace(/[^a-zA-Z0-9_-]/g, "_");
5117
- return join8(getLedgerDir(), `${safe}-${safeDaemon}.pending-events.jsonl`);
5465
+ return join9(getLedgerDir(), `${safe}-${safeDaemon}.pending-events.jsonl`);
5118
5466
  }
5119
- return join8(getLedgerDir(), `${safe}.pending-events.jsonl`);
5467
+ return join9(getLedgerDir(), `${safe}.pending-events.jsonl`);
5120
5468
  }
5121
5469
  function readPendingMeshCoordinatorEventsFromDisk(meshId, coordinatorDaemonId) {
5122
5470
  if (!meshId) return [];
@@ -5125,9 +5473,9 @@ function readPendingMeshCoordinatorEventsFromDisk(meshId, coordinatorDaemonId) {
5125
5473
  const paths = primaryDaemonId ? [getPendingEventsPath(meshId, primaryDaemonId), getPendingEventsPath(meshId)] : [getPendingEventsPath(meshId)];
5126
5474
  const events = [];
5127
5475
  for (const path46 of paths) {
5128
- if (!existsSync7(path46)) continue;
5476
+ if (!existsSync8(path46)) continue;
5129
5477
  try {
5130
- const raw = readFileSync4(path46, "utf-8");
5478
+ const raw = readFileSync5(path46, "utf-8");
5131
5479
  const parsed = raw.split("\n").filter(Boolean).flatMap((line) => {
5132
5480
  try {
5133
5481
  return [JSON.parse(line)];
@@ -5217,9 +5565,9 @@ function prunePendingMeshCoordinatorEventsRetention() {
5217
5565
  }
5218
5566
  function trimPendingEventsIfNeeded(path46) {
5219
5567
  try {
5220
- if (!existsSync7(path46)) return;
5568
+ if (!existsSync8(path46)) return;
5221
5569
  if (statSync4(path46).size <= MAX_PENDING_EVENTS_BYTES) return;
5222
- const lines = readFileSync4(path46, "utf-8").split("\n").filter(Boolean);
5570
+ const lines = readFileSync5(path46, "utf-8").split("\n").filter(Boolean);
5223
5571
  if (lines.length <= MAX_PENDING_EVENTS_KEEP) return;
5224
5572
  const dropped = lines.slice(0, lines.length - MAX_PENDING_EVENTS_KEEP);
5225
5573
  for (const line of dropped) {
@@ -5386,7 +5734,7 @@ function atomicDrainFile(path46) {
5386
5734
  return null;
5387
5735
  }
5388
5736
  try {
5389
- const content = readFileSync4(tmpPath, "utf-8");
5737
+ const content = readFileSync5(tmpPath, "utf-8");
5390
5738
  try {
5391
5739
  unlinkSync2(tmpPath);
5392
5740
  } catch {
@@ -5409,7 +5757,7 @@ function selectiveDrainFile(path46, predicate) {
5409
5757
  }
5410
5758
  let content;
5411
5759
  try {
5412
- content = readFileSync4(tmpPath, "utf-8");
5760
+ content = readFileSync5(tmpPath, "utf-8");
5413
5761
  } catch {
5414
5762
  try {
5415
5763
  unlinkSync2(tmpPath);
@@ -5440,7 +5788,7 @@ function selectiveDrainFile(path46, predicate) {
5440
5788
  unlinkSync2(tmpPath);
5441
5789
  } catch {
5442
5790
  try {
5443
- if (existsSync7(tmpPath) && !existsSync7(path46)) renameSync2(tmpPath, path46);
5791
+ if (existsSync8(tmpPath) && !existsSync8(path46)) renameSync2(tmpPath, path46);
5444
5792
  } catch {
5445
5793
  }
5446
5794
  return [];
@@ -5668,7 +6016,7 @@ function clearPendingMeshCoordinatorEvents(meshId, coordinatorDaemonId) {
5668
6016
  }
5669
6017
  const paths = coordinatorDaemonId ? [getPendingEventsPath(meshId, coordinatorDaemonId), getPendingEventsPath(meshId)] : [getPendingEventsPath(meshId)];
5670
6018
  for (const path46 of paths) {
5671
- if (existsSync7(path46)) try {
6019
+ if (existsSync8(path46)) try {
5672
6020
  unlinkSync2(path46);
5673
6021
  } catch {
5674
6022
  }
@@ -7018,8 +7366,8 @@ var init_mesh_work_queue = __esm({
7018
7366
  });
7019
7367
 
7020
7368
  // src/mesh/mesh-runtime-store.ts
7021
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync3, statSync as statSync5, unlinkSync as unlinkSync3 } from "fs";
7022
- import { dirname as dirname2, join as join9 } from "path";
7369
+ import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync6, renameSync as renameSync3, statSync as statSync5, unlinkSync as unlinkSync3 } from "fs";
7370
+ import { dirname as dirname2, join as join10 } from "path";
7023
7371
  function loadDatabaseCtor() {
7024
7372
  if (DatabaseCtor) return DatabaseCtor;
7025
7373
  DatabaseCtor = loadBetterSqlite3();
@@ -7029,13 +7377,13 @@ function safeMeshId(meshId) {
7029
7377
  return meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
7030
7378
  }
7031
7379
  function legacyQueuePath(meshId) {
7032
- return join9(getLedgerDir(), `${safeMeshId(meshId)}.queue.json`);
7380
+ return join10(getLedgerDir(), `${safeMeshId(meshId)}.queue.json`);
7033
7381
  }
7034
7382
  function cleanupStrayRootRuntimeDb(canonicalPath) {
7035
7383
  try {
7036
- const strayPath = join9(getConfigDir(), "mesh-runtime.db");
7384
+ const strayPath = join10(getConfigDir(), "mesh-runtime.db");
7037
7385
  if (strayPath === canonicalPath) return;
7038
- if (!existsSync8(strayPath)) return;
7386
+ if (!existsSync9(strayPath)) return;
7039
7387
  if (statSync5(strayPath).size !== 0) return;
7040
7388
  unlinkSync3(strayPath);
7041
7389
  if (!loggedStrayCleanup) {
@@ -7051,16 +7399,16 @@ function cleanupStrayRootRuntimeDb(canonicalPath) {
7051
7399
  }
7052
7400
  function meshRuntimeStorePath() {
7053
7401
  const dir = getLedgerDir();
7054
- const nextPath = join9(dir, "mesh-runtime.db");
7402
+ const nextPath = join10(dir, "mesh-runtime.db");
7055
7403
  cleanupStrayRootRuntimeDb(nextPath);
7056
- if (existsSync8(nextPath)) return nextPath;
7057
- const legacyPath = join9(dir, "beads.db");
7058
- if (!existsSync8(legacyPath)) return nextPath;
7404
+ if (existsSync9(nextPath)) return nextPath;
7405
+ const legacyPath = join10(dir, "beads.db");
7406
+ if (!existsSync9(legacyPath)) return nextPath;
7059
7407
  try {
7060
7408
  renameSync3(legacyPath, nextPath);
7061
7409
  for (const suffix of ["-wal", "-shm"]) {
7062
7410
  const legacyCompanion = `${legacyPath}${suffix}`;
7063
- if (existsSync8(legacyCompanion)) {
7411
+ if (existsSync9(legacyCompanion)) {
7064
7412
  renameSync3(legacyCompanion, `${nextPath}${suffix}`);
7065
7413
  }
7066
7414
  }
@@ -7072,7 +7420,7 @@ function meshRuntimeStorePath() {
7072
7420
  `Legacy beads.db\u2192mesh-runtime.db migration failed; using existing DB in-place to avoid data loss: ${err?.message || err}`
7073
7421
  );
7074
7422
  }
7075
- return existsSync8(nextPath) ? nextPath : legacyPath;
7423
+ return existsSync9(nextPath) ? nextPath : legacyPath;
7076
7424
  }
7077
7425
  return nextPath;
7078
7426
  }
@@ -7126,7 +7474,7 @@ var init_mesh_runtime_store = __esm({
7126
7474
  // 50 MB
7127
7475
  constructor(dbPath) {
7128
7476
  const dir = dirname2(dbPath);
7129
- if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
7477
+ if (!existsSync9(dir)) mkdirSync4(dir, { recursive: true });
7130
7478
  this.dbPath = dbPath;
7131
7479
  this.db = new (loadDatabaseCtor())(dbPath);
7132
7480
  this.db.pragma("journal_mode = WAL");
@@ -7505,7 +7853,7 @@ var init_mesh_runtime_store = __esm({
7505
7853
  this.walWriteCounter = 0;
7506
7854
  try {
7507
7855
  const walPath = `${this.dbPath}-wal`;
7508
- if (!existsSync8(walPath)) return;
7856
+ if (!existsSync9(walPath)) return;
7509
7857
  const size = statSync5(walPath).size;
7510
7858
  if (size < _MeshRuntimeStore.WAL_MAX_BYTES) return;
7511
7859
  process.stderr.write(
@@ -7522,9 +7870,9 @@ var init_mesh_runtime_store = __esm({
7522
7870
  const count = this.db.prepare("SELECT COUNT(*) AS count FROM mesh_queue WHERE mesh_id = ?").get(meshId);
7523
7871
  if (count.count > 0) return;
7524
7872
  const path46 = legacyQueuePath(meshId);
7525
- if (!existsSync8(path46)) return;
7873
+ if (!existsSync9(path46)) return;
7526
7874
  try {
7527
- const entries = JSON.parse(readFileSync5(path46, "utf-8"));
7875
+ const entries = JSON.parse(readFileSync6(path46, "utf-8"));
7528
7876
  if (!Array.isArray(entries)) return;
7529
7877
  const insert = this.db.prepare(`
7530
7878
  INSERT OR REPLACE INTO mesh_queue (
@@ -8955,8 +9303,8 @@ __export(mesh_ledger_exports, {
8955
9303
  readOperatingNotes: () => readOperatingNotes,
8956
9304
  tombstoneOperatingNote: () => tombstoneOperatingNote
8957
9305
  });
8958
- import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync6, statSync as statSync6, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "fs";
8959
- import { join as join10 } from "path";
9306
+ import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync7, statSync as statSync6, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "fs";
9307
+ import { join as join11 } from "path";
8960
9308
  import { randomUUID as randomUUID8 } from "crypto";
8961
9309
  import { EventEmitter } from "events";
8962
9310
  function isIntentionalCleanupStopEntry(entry) {
@@ -8979,35 +9327,35 @@ function isNoteExpired(note, now) {
8979
9327
  return now - created >= ttlDays * MS_PER_DAY;
8980
9328
  }
8981
9329
  function getLedgerDir() {
8982
- const dir = join10(getConfigDir(), LEDGER_DIR_NAME);
8983
- if (!existsSync9(dir)) {
9330
+ const dir = join11(getConfigDir(), LEDGER_DIR_NAME);
9331
+ if (!existsSync10(dir)) {
8984
9332
  mkdirSync5(dir, { recursive: true, mode: 448 });
8985
9333
  }
8986
9334
  return dir;
8987
9335
  }
8988
9336
  function getLedgerPath(meshId) {
8989
9337
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
8990
- return join10(getLedgerDir(), `${safe}.jsonl`);
9338
+ return join11(getLedgerDir(), `${safe}.jsonl`);
8991
9339
  }
8992
9340
  function getRotatedPath(meshId, index) {
8993
9341
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
8994
- return join10(getLedgerDir(), `${safe}.${index}.jsonl`);
9342
+ return join11(getLedgerDir(), `${safe}.${index}.jsonl`);
8995
9343
  }
8996
9344
  function getArchivePath(meshId) {
8997
9345
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
8998
- return join10(getLedgerDir(), `${safe}.archive.jsonl`);
9346
+ return join11(getLedgerDir(), `${safe}.archive.jsonl`);
8999
9347
  }
9000
9348
  function getRotatedArchivePath(meshId, index) {
9001
9349
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
9002
- return join10(getLedgerDir(), `${safe}.archive.${index}.jsonl`);
9350
+ return join11(getLedgerDir(), `${safe}.archive.${index}.jsonl`);
9003
9351
  }
9004
9352
  function getArchivedCountsPath(meshId) {
9005
9353
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
9006
- return join10(getLedgerDir(), `${safe}.archived-counts.json`);
9354
+ return join11(getLedgerDir(), `${safe}.archived-counts.json`);
9007
9355
  }
9008
9356
  function rotateArchiveFile(meshId, archivePath) {
9009
9357
  let index = 1;
9010
- while (existsSync9(getRotatedArchivePath(meshId, index))) {
9358
+ while (existsSync10(getRotatedArchivePath(meshId, index))) {
9011
9359
  index++;
9012
9360
  if (index > 5) break;
9013
9361
  }
@@ -9021,9 +9369,9 @@ function rotateArchiveFile(meshId, archivePath) {
9021
9369
  }
9022
9370
  function readArchivedCounts(meshId) {
9023
9371
  const path46 = getArchivedCountsPath(meshId);
9024
- if (!existsSync9(path46)) return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: "" };
9372
+ if (!existsSync10(path46)) return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: "" };
9025
9373
  try {
9026
- return JSON.parse(readFileSync6(path46, "utf-8"));
9374
+ return JSON.parse(readFileSync7(path46, "utf-8"));
9027
9375
  } catch {
9028
9376
  return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: "" };
9029
9377
  }
@@ -9062,7 +9410,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
9062
9410
  }
9063
9411
  function compactLedger(meshId) {
9064
9412
  const filePath = getLedgerPath(meshId);
9065
- if (!existsSync9(filePath)) return { archivedCount: 0, retainedCount: 0 };
9413
+ if (!existsSync10(filePath)) return { archivedCount: 0, retainedCount: 0 };
9066
9414
  const cutoff = Date.now() - ARCHIVE_TERMINAL_OLDER_THAN_MS;
9067
9415
  const entries = readLedgerEntries(meshId);
9068
9416
  const keep = [];
@@ -9077,7 +9425,7 @@ function compactLedger(meshId) {
9077
9425
  if (archive.length === 0) return { archivedCount: 0, retainedCount: keep.length };
9078
9426
  const archivePath = getArchivePath(meshId);
9079
9427
  try {
9080
- if (existsSync9(archivePath) && statSync6(archivePath).size > 50 * 1024 * 1024) {
9428
+ if (existsSync10(archivePath) && statSync6(archivePath).size > 50 * 1024 * 1024) {
9081
9429
  rotateArchiveFile(meshId, archivePath);
9082
9430
  }
9083
9431
  const archiveLines = archive.map((e) => JSON.stringify(e)).join("\n") + "\n";
@@ -9268,7 +9616,7 @@ function appendLedgerEntry(meshId, partial) {
9268
9616
  ...partial
9269
9617
  };
9270
9618
  const filePath = getLedgerPath(meshId);
9271
- if (existsSync9(filePath)) {
9619
+ if (existsSync10(filePath)) {
9272
9620
  try {
9273
9621
  const stat2 = statSync6(filePath);
9274
9622
  if (stat2.size >= MAX_FILE_SIZE_BYTES) {
@@ -9456,10 +9804,10 @@ function appendRemoteLedgerEntries(meshId, entries) {
9456
9804
  }
9457
9805
  function readLedgerFile(meshId) {
9458
9806
  const filePath = getLedgerPath(meshId);
9459
- if (!existsSync9(filePath)) return [];
9807
+ if (!existsSync10(filePath)) return [];
9460
9808
  let content;
9461
9809
  try {
9462
- content = readFileSync6(filePath, "utf-8");
9810
+ content = readFileSync7(filePath, "utf-8");
9463
9811
  } catch {
9464
9812
  return [];
9465
9813
  }
@@ -9724,7 +10072,7 @@ function getSessionRecoveryContext(meshId, opts) {
9724
10072
  }
9725
10073
  function rotateLedgerFile(meshId, currentPath) {
9726
10074
  let index = 1;
9727
- while (existsSync9(getRotatedPath(meshId, index))) {
10075
+ while (existsSync10(getRotatedPath(meshId, index))) {
9728
10076
  index++;
9729
10077
  if (index > 10) break;
9730
10078
  }
@@ -10729,10 +11077,10 @@ __export(mesh_coordinator_exports, {
10729
11077
  resolveMeshCoordinatorSetup: () => resolveMeshCoordinatorSetup,
10730
11078
  stripCoordinatorWrapperFile: () => stripCoordinatorWrapperFile
10731
11079
  });
10732
- import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
11080
+ import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
10733
11081
  import * as os4 from "os";
10734
11082
  import { DEFAULT_SESSION_HOST_COLS, DEFAULT_SESSION_HOST_ROWS } from "@adhdev/session-host-core";
10735
- import { basename as basename2, isAbsolute as isAbsolute4, join as join12, resolve as resolve7 } from "path";
11083
+ import { basename as basename2, isAbsolute as isAbsolute4, join as join13, resolve as resolve7 } from "path";
10736
11084
  function isHermesProvider(provider, cliType) {
10737
11085
  const type = cliType?.trim() || provider?.type?.trim() || "";
10738
11086
  return type === HERMES_CLI_TYPE;
@@ -10752,7 +11100,7 @@ function resolveHermesMeshCoordinatorSetup(options) {
10752
11100
  reason: "Could not resolve the ADHDev MCP server entrypoint and a Node runtime with WebSocket support for daemon IPC mode"
10753
11101
  };
10754
11102
  }
10755
- const configPath = join12(resolveHermesCoordinatorHome(options.meshId, options.workspace), "config.yaml");
11103
+ const configPath = join13(resolveHermesCoordinatorHome(options.meshId, options.workspace), "config.yaml");
10756
11104
  if (!configPath.trim()) {
10757
11105
  return createHermesManualMeshCoordinatorSetup(options.meshId, options.workspace);
10758
11106
  }
@@ -10899,14 +11247,14 @@ function resolveHermesCoordinatorHome(meshId, workspace) {
10899
11247
  const key2 = `${meshId || "mesh"}
10900
11248
  ${resolve7(workspace || os4.tmpdir())}`;
10901
11249
  const hash = shortHash(key2);
10902
- return join12(os4.tmpdir(), `adhdev-hermes-mesh-coordinator-${hash}`);
11250
+ return join13(os4.tmpdir(), `adhdev-hermes-mesh-coordinator-${hash}`);
10903
11251
  }
10904
11252
  function resolveMcpConfigPath(configPath, workspace) {
10905
11253
  const trimmed = configPath.trim();
10906
11254
  if (trimmed === "~") return os4.homedir();
10907
- if (trimmed.startsWith("~/")) return join12(os4.homedir(), trimmed.slice(2));
11255
+ if (trimmed.startsWith("~/")) return join13(os4.homedir(), trimmed.slice(2));
10908
11256
  if (isAbsolute4(trimmed)) return trimmed;
10909
- return join12(workspace, trimmed);
11257
+ return join13(workspace, trimmed);
10910
11258
  }
10911
11259
  function resolveAdhdevMcpServerLaunch(options) {
10912
11260
  const directEntryPath = resolveAdhdevMcpEntryPath(options.adhdevMcpEntryPath);
@@ -10977,7 +11325,7 @@ function applyInjectionRule(systemPrompt, injection, ctx) {
10977
11325
  }
10978
11326
  case "context_file": {
10979
11327
  if (!injection.path) return {};
10980
- const target = isAbsolute4(injection.path) ? injection.path : join12(ctx.workspace, injection.path);
11328
+ const target = isAbsolute4(injection.path) ? injection.path : join13(ctx.workspace, injection.path);
10981
11329
  const wrapper = injection.wrapper && injection.wrapper.includes("{prompt}") ? injection.wrapper : "{prompt}";
10982
11330
  const managedNote = "> _Managed by adhdev mesh coordinator \u2014 do not hand-edit this block. Changes inside the sentinels are overwritten on next coordinator launch._";
10983
11331
  const promptWithNote = `${managedNote}
@@ -10986,8 +11334,8 @@ ${systemPrompt}`;
10986
11334
  const rendered = wrapper.replace(/\{prompt\}/g, promptWithNote);
10987
11335
  const sentinel = wrapper.split("{prompt}")[0].trim();
10988
11336
  try {
10989
- if (existsSync10(target)) {
10990
- const existing = readFileSync8(target, "utf-8");
11337
+ if (existsSync11(target)) {
11338
+ const existing = readFileSync9(target, "utf-8");
10991
11339
  if (sentinel && existing.includes(sentinel)) {
10992
11340
  const closing = wrapper.split("{prompt}")[1]?.trim();
10993
11341
  const safeOpen = sentinel.replace(/[.+^${}()|[\]\\]/g, "\\$&");
@@ -11017,8 +11365,8 @@ function stripCoordinatorWrapperFile(filePath) {
11017
11365
  const OPEN = "<!-- adhdev-mesh-coordinator-prompt -->";
11018
11366
  const CLOSE = "<!-- /adhdev-mesh-coordinator-prompt -->";
11019
11367
  try {
11020
- if (!existsSync10(filePath)) return;
11021
- const existing = readFileSync8(filePath, "utf-8");
11368
+ if (!existsSync11(filePath)) return;
11369
+ const existing = readFileSync9(filePath, "utf-8");
11022
11370
  const openIdx = existing.indexOf(OPEN);
11023
11371
  if (openIdx < 0) return;
11024
11372
  const closeIdx = existing.indexOf(CLOSE, openIdx);
@@ -11121,16 +11469,16 @@ var init_mesh_coordinator = __esm({
11121
11469
  });
11122
11470
 
11123
11471
  // src/mesh/coordinator-registry.ts
11124
- import { join as join13 } from "path";
11125
- import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
11472
+ import { join as join14 } from "path";
11473
+ import { existsSync as existsSync12, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
11126
11474
  function getRegistryPath() {
11127
- return join13(getDaemonDataDir(), "mesh-coordinators.json");
11475
+ return join14(getDaemonDataDir(), "mesh-coordinators.json");
11128
11476
  }
11129
11477
  function loadMeshCoordinatorRegistry() {
11130
11478
  const path46 = getRegistryPath();
11131
- if (!existsSync11(path46)) return;
11479
+ if (!existsSync12(path46)) return;
11132
11480
  try {
11133
- const raw = JSON.parse(readFileSync9(path46, "utf-8"));
11481
+ const raw = JSON.parse(readFileSync10(path46, "utf-8"));
11134
11482
  if (!Array.isArray(raw)) return;
11135
11483
  _registry.clear();
11136
11484
  for (const entry of raw) {
@@ -11186,9 +11534,9 @@ var init_coordinator_registry = __esm({
11186
11534
  });
11187
11535
 
11188
11536
  // src/mesh/refine-config.ts
11189
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
11190
- import { join as join14 } from "path";
11191
- import * as yaml2 from "js-yaml";
11537
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
11538
+ import { join as join15 } from "path";
11539
+ import * as yaml3 from "js-yaml";
11192
11540
  function isMeshConfigRecord(value) {
11193
11541
  return !!value && typeof value === "object" && !Array.isArray(value);
11194
11542
  }
@@ -11272,14 +11620,14 @@ function validateMeshRefineConfig(config, source = "inline") {
11272
11620
  const rejectedCommands = [];
11273
11621
  const deprecationWarnings = [];
11274
11622
  let bootstrapMode = "inherit";
11275
- if (!isRecord2(config)) return { valid: false, errors: ["config must be an object"], bootstrapCommands, commands, rejectedCommands, bootstrapMode, deprecationWarnings };
11623
+ if (!isRecord3(config)) return { valid: false, errors: ["config must be an object"], bootstrapCommands, commands, rejectedCommands, bootstrapMode, deprecationWarnings };
11276
11624
  if (config.version !== 1) errors.push("version must be 1");
11277
11625
  if (config.allowAutoPublishSubmoduleMainCommits !== void 0 && typeof config.allowAutoPublishSubmoduleMainCommits !== "boolean") {
11278
11626
  errors.push("allowAutoPublishSubmoduleMainCommits must be a boolean when provided");
11279
11627
  }
11280
11628
  const validation = config.validation;
11281
- if (validation !== void 0 && !isRecord2(validation)) errors.push("validation must be an object");
11282
- const rawBootstrapMode = isRecord2(validation) ? validation.bootstrap : void 0;
11629
+ if (validation !== void 0 && !isRecord3(validation)) errors.push("validation must be an object");
11630
+ const rawBootstrapMode = isRecord3(validation) ? validation.bootstrap : void 0;
11283
11631
  if (rawBootstrapMode !== void 0) {
11284
11632
  if (rawBootstrapMode === "inherit" || rawBootstrapMode === "skip") {
11285
11633
  bootstrapMode = rawBootstrapMode;
@@ -11287,8 +11635,8 @@ function validateMeshRefineConfig(config, source = "inline") {
11287
11635
  errors.push("validation.bootstrap must be 'inherit' or 'skip' when provided");
11288
11636
  }
11289
11637
  }
11290
- const rawCommands = isRecord2(validation) ? validation.commands : void 0;
11291
- const rawBootstrapCommands = isRecord2(validation) ? validation.bootstrapCommands : void 0;
11638
+ const rawCommands = isRecord3(validation) ? validation.commands : void 0;
11639
+ const rawBootstrapCommands = isRecord3(validation) ? validation.bootstrapCommands : void 0;
11292
11640
  if (rawCommands !== void 0 && !Array.isArray(rawCommands)) errors.push("validation.commands must be an array");
11293
11641
  if (rawBootstrapCommands !== void 0 && !Array.isArray(rawBootstrapCommands)) errors.push("validation.bootstrapCommands must be an array");
11294
11642
  if (Array.isArray(rawBootstrapCommands) && rawBootstrapCommands.length > 0) {
@@ -11311,9 +11659,9 @@ function validateMeshRefineConfig(config, source = "inline") {
11311
11659
  if (rejectedCommands.length) errors.push("one or more validation commands are invalid");
11312
11660
  return { valid: errors.length === 0, errors, bootstrapCommands, commands, rejectedCommands, bootstrapMode, deprecationWarnings };
11313
11661
  }
11314
- function parseConfigText2(path46, text) {
11662
+ function parseConfigText3(path46, text) {
11315
11663
  if (/\.json$/i.test(path46)) return JSON.parse(text);
11316
- return yaml2.load(text);
11664
+ return yaml3.load(text);
11317
11665
  }
11318
11666
  function loadMeshRefineConfig(mesh, workspace) {
11319
11667
  const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
@@ -11324,10 +11672,10 @@ function loadMeshRefineConfig(mesh, workspace) {
11324
11672
  return { config: inline, source: "mesh.policy.refineConfig", sourceType: "mesh_policy" };
11325
11673
  }
11326
11674
  for (const relative5 of MESH_REFINE_CONFIG_LOCATIONS) {
11327
- const configPath = join14(workspace, relative5);
11328
- if (!existsSync12(configPath)) continue;
11675
+ const configPath = join15(workspace, relative5);
11676
+ if (!existsSync13(configPath)) continue;
11329
11677
  try {
11330
- const parsed = parseConfigText2(configPath, readFileSync10(configPath, "utf-8"));
11678
+ const parsed = parseConfigText3(configPath, readFileSync11(configPath, "utf-8"));
11331
11679
  const validation = validateMeshRefineConfig(parsed, relative5);
11332
11680
  if (!validation.valid) return { source: relative5, sourceType: "invalid", path: configPath, error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
11333
11681
  return { config: parsed, source: relative5, sourceType: "repo_file", path: configPath };
@@ -11343,20 +11691,20 @@ function loadMeshRefineConfig(mesh, workspace) {
11343
11691
  }
11344
11692
  function readPackageScripts(workspace) {
11345
11693
  try {
11346
- const parsed = JSON.parse(readFileSync10(join14(workspace, "package.json"), "utf-8"));
11347
- return isRecord2(parsed?.scripts) ? parsed.scripts : {};
11694
+ const parsed = JSON.parse(readFileSync11(join15(workspace, "package.json"), "utf-8"));
11695
+ return isRecord3(parsed?.scripts) ? parsed.scripts : {};
11348
11696
  } catch {
11349
11697
  return {};
11350
11698
  }
11351
11699
  }
11352
11700
  function collectProjectContextSuggestions(mesh) {
11353
11701
  const commands = mesh?.projectContext?.commands;
11354
- if (!isRecord2(commands)) return [];
11702
+ if (!isRecord3(commands)) return [];
11355
11703
  const suggestions = [];
11356
11704
  for (const category of MESH_REFINE_VALIDATION_CATEGORIES) {
11357
11705
  const entries = Array.isArray(commands[category]) ? commands[category] : [];
11358
11706
  for (const entry of entries) {
11359
- if (isRecord2(entry) && typeof entry.command === "string") suggestions.push({ command: entry.command, category });
11707
+ if (isRecord3(entry) && typeof entry.command === "string") suggestions.push({ command: entry.command, category });
11360
11708
  }
11361
11709
  }
11362
11710
  return suggestions;
@@ -11418,7 +11766,7 @@ function resolveMeshRefineValidationPlan(mesh, workspace) {
11418
11766
  unavailableReason: validation.commands.length ? void 0 : "validation_unavailable: repo mesh/refine config has no validation.commands"
11419
11767
  };
11420
11768
  }
11421
- var MESH_REFINE_VALIDATION_CATEGORIES, MESH_REFINE_VALIDATION_SCOPES, MESH_REFINE_CONFIG_LOCATIONS, MESH_REFINE_CONFIG_SCHEMA, SHELL_METACHAR_RE, SAFE_TOKEN_RE, SAFE_WIN32_EXEC_TOKEN_RE, isRecord2;
11769
+ var MESH_REFINE_VALIDATION_CATEGORIES, MESH_REFINE_VALIDATION_SCOPES, MESH_REFINE_CONFIG_LOCATIONS, MESH_REFINE_CONFIG_SCHEMA, SHELL_METACHAR_RE, SAFE_TOKEN_RE, SAFE_WIN32_EXEC_TOKEN_RE, isRecord3;
11422
11770
  var init_refine_config = __esm({
11423
11771
  "src/mesh/refine-config.ts"() {
11424
11772
  "use strict";
@@ -11512,13 +11860,13 @@ var init_refine_config = __esm({
11512
11860
  SHELL_METACHAR_RE = /[;&|<>`$\n\r'"]/;
11513
11861
  SAFE_TOKEN_RE = /^[A-Za-z0-9_@./:=+-]+$/;
11514
11862
  SAFE_WIN32_EXEC_TOKEN_RE = /^[A-Za-z0-9_@./:=+\\-]+$/;
11515
- isRecord2 = isMeshConfigRecord;
11863
+ isRecord3 = isMeshConfigRecord;
11516
11864
  }
11517
11865
  });
11518
11866
 
11519
11867
  // src/cli-adapters/resolve-executable.ts
11520
11868
  import { execFileSync } from "child_process";
11521
- import { existsSync as existsSync13 } from "fs";
11869
+ import { existsSync as existsSync14 } from "fs";
11522
11870
  import * as path10 from "path";
11523
11871
  function resolveWin32GlobalBin(trimmed) {
11524
11872
  if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\")) {
@@ -11534,7 +11882,7 @@ function resolveWin32GlobalBin(trimmed) {
11534
11882
  if (!dir) continue;
11535
11883
  for (const ext of WIN_EXEC_EXT) {
11536
11884
  const full = path10.join(dir, trimmed + ext);
11537
- if (existsSync13(full)) return full;
11885
+ if (existsSync14(full)) return full;
11538
11886
  }
11539
11887
  }
11540
11888
  return null;
@@ -11551,7 +11899,7 @@ function resolveWin32Executable(command) {
11551
11899
  if (process.platform !== "win32") return command;
11552
11900
  const trimmed = (command || "").trim();
11553
11901
  if (!trimmed) return command;
11554
- if (path10.isAbsolute(trimmed) && existsSync13(trimmed)) return trimmed;
11902
+ if (path10.isAbsolute(trimmed) && existsSync14(trimmed)) return trimmed;
11555
11903
  try {
11556
11904
  const out = execFileSync("where", [trimmed], {
11557
11905
  encoding: "utf8",
@@ -11627,12 +11975,12 @@ __export(worktree_bootstrap_config_exports, {
11627
11975
  shouldDeferDispatchForBootstrap: () => shouldDeferDispatchForBootstrap,
11628
11976
  validateMeshWorktreeBootstrapConfig: () => validateMeshWorktreeBootstrapConfig
11629
11977
  });
11630
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
11631
- import { join as join16, resolve as pathResolve } from "path";
11978
+ import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
11979
+ import { join as join17, resolve as pathResolve } from "path";
11632
11980
  import { execFile as execFile3, execFileSync as execFileSync2 } from "child_process";
11633
11981
  import { createHash as createHash2 } from "crypto";
11634
11982
  import { promisify as promisify3 } from "util";
11635
- import * as yaml3 from "js-yaml";
11983
+ import * as yaml4 from "js-yaml";
11636
11984
  function getRegisteredSubmodulePaths(workspace) {
11637
11985
  const paths = /* @__PURE__ */ new Set();
11638
11986
  try {
@@ -11747,7 +12095,7 @@ function isWorktreeBootstrapStaleRunning(node, nowMs = Date.now()) {
11747
12095
  if (!Number.isFinite(startedMs)) return false;
11748
12096
  if (nowMs - startedMs <= WORKTREE_BOOTSTRAP_STALE_RUNNING_MS) return false;
11749
12097
  const workspace = typeof node?.workspace === "string" ? node.workspace.trim() : "";
11750
- if (!workspace || !existsSync14(workspace)) return false;
12098
+ if (!workspace || !existsSync15(workspace)) return false;
11751
12099
  try {
11752
12100
  const out = execFileSync2(resolveWin32Executable("git"), ["status", "--porcelain"], {
11753
12101
  cwd: workspace,
@@ -11768,9 +12116,9 @@ function shouldDeferDispatchForBootstrap(node, nowMs = Date.now()) {
11768
12116
  if (node?.worktreeBootstrap?.status !== "running") return false;
11769
12117
  return !isWorktreeBootstrapStaleRunning(node, nowMs);
11770
12118
  }
11771
- function parseConfigText3(path46, text) {
12119
+ function parseConfigText4(path46, text) {
11772
12120
  if (/\.json$/i.test(path46)) return JSON.parse(text);
11773
- return yaml3.load(text);
12121
+ return yaml4.load(text);
11774
12122
  }
11775
12123
  function truncateOutput(value) {
11776
12124
  const text = typeof value === "string" ? value : value == null ? "" : String(value);
@@ -11810,10 +12158,10 @@ function loadMeshWorktreeBootstrapConfig(mesh, workspace) {
11810
12158
  return { config: inline, source: "mesh.policy.worktreeBootstrapConfig", sourceType: "mesh_policy" };
11811
12159
  }
11812
12160
  for (const relative5 of MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS) {
11813
- const configPath = join16(workspace, relative5);
11814
- if (!existsSync14(configPath)) continue;
12161
+ const configPath = join17(workspace, relative5);
12162
+ if (!existsSync15(configPath)) continue;
11815
12163
  try {
11816
- const parsed = parseConfigText3(configPath, readFileSync11(configPath, "utf-8"));
12164
+ const parsed = parseConfigText4(configPath, readFileSync12(configPath, "utf-8"));
11817
12165
  const validation = validateMeshWorktreeBootstrapConfig(parsed, relative5);
11818
12166
  if (!validation.valid) return { source: relative5, sourceType: "invalid", path: configPath, error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
11819
12167
  return { config: parsed, source: relative5, sourceType: "repo_file", path: configPath };
@@ -11826,9 +12174,9 @@ function loadMeshWorktreeBootstrapConfig(mesh, workspace) {
11826
12174
  function computeStaleInputsDigest(workspace, staleInputs) {
11827
12175
  const digest = {};
11828
12176
  for (const relative5 of staleInputs ?? []) {
11829
- const filePath = join16(workspace, relative5);
12177
+ const filePath = join17(workspace, relative5);
11830
12178
  try {
11831
- digest[relative5] = createHash2("sha256").update(readFileSync11(filePath)).digest("hex");
12179
+ digest[relative5] = createHash2("sha256").update(readFileSync12(filePath)).digest("hex");
11832
12180
  } catch {
11833
12181
  digest[relative5] = "absent";
11834
12182
  }
@@ -11899,10 +12247,10 @@ async function runMeshWorktreeBootstrap(mesh, workspace) {
11899
12247
  staleInputs: loaded.config.staleInputs
11900
12248
  };
11901
12249
  const staleInputPaths = loaded.config.staleInputs ?? [];
11902
- const initiallyAbsent = staleInputPaths.filter((p) => !existsSync14(join16(workspace, p)));
12250
+ const initiallyAbsent = staleInputPaths.filter((p) => !existsSync15(join17(workspace, p)));
11903
12251
  for (const command of validation.commands) {
11904
12252
  if (initiallyAbsent.length > 0) {
11905
- const appearedNow = initiallyAbsent.filter((p) => existsSync14(join16(workspace, p)));
12253
+ const appearedNow = initiallyAbsent.filter((p) => existsSync15(join17(workspace, p)));
11906
12254
  if (appearedNow.length > 0) {
11907
12255
  state.status = "stale";
11908
12256
  state.completedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -12025,224 +12373,6 @@ var init_worktree_bootstrap_config = __esm({
12025
12373
  }
12026
12374
  });
12027
12375
 
12028
- // src/config/mesh-json-config.ts
12029
- var mesh_json_config_exports = {};
12030
- __export(mesh_json_config_exports, {
12031
- MESH_JSON_CONFIG_LOCATIONS: () => MESH_JSON_CONFIG_LOCATIONS,
12032
- MESH_JSON_CONFIG_SCHEMA: () => MESH_JSON_CONFIG_SCHEMA,
12033
- applyRepoMeshConfig: () => applyRepoMeshConfig,
12034
- buildMeshJsonConfigScaffold: () => buildMeshJsonConfigScaffold,
12035
- loadRepoMeshJsonConfig: () => loadRepoMeshJsonConfig,
12036
- mergeEffectiveCoordinatorConfig: () => mergeEffectiveCoordinatorConfig,
12037
- mergeEffectiveOperatingNotes: () => mergeEffectiveOperatingNotes,
12038
- normalizeRepoMeshDeclarativeConfig: () => normalizeRepoMeshDeclarativeConfig,
12039
- serializeMeshJsonConfigScaffold: () => serializeMeshJsonConfigScaffold
12040
- });
12041
- import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
12042
- import { join as join17 } from "path";
12043
- import * as yaml4 from "js-yaml";
12044
- function isRecord3(value) {
12045
- return !!value && typeof value === "object" && !Array.isArray(value);
12046
- }
12047
- function parseConfigText4(path46, text) {
12048
- if (/\.json$/i.test(path46)) return JSON.parse(text);
12049
- return yaml4.load(text);
12050
- }
12051
- function normalizeOperatingNote(value) {
12052
- if (!isRecord3(value)) return null;
12053
- const text = typeof value.text === "string" ? value.text.trim() : "";
12054
- if (!text) return null;
12055
- const category = value.category === "provider_quirk" || value.category === "pattern_to_avoid" || value.category === "recovery_lesson" ? value.category : void 0;
12056
- return {
12057
- text,
12058
- ...category ? { category } : {},
12059
- ...typeof value.createdAt === "string" ? { createdAt: value.createdAt } : {},
12060
- ...typeof value.sourceCoordinator === "string" ? { sourceCoordinator: value.sourceCoordinator } : {},
12061
- // Operating-notes lifecycle: a repo-declared note may pin itself or set an
12062
- // explicit expiry, same as a runtime-recorded one. Phase 2 (b)/(c): it may
12063
- // also declare supersedes/subjectKey to retire an earlier note or group
12064
- // same-subject notes for folding.
12065
- ...value.pinned === true ? { pinned: true } : {},
12066
- ...typeof value.expiresAt === "string" ? { expiresAt: value.expiresAt } : {},
12067
- ...typeof value.supersedes === "string" ? { supersedes: value.supersedes } : {},
12068
- ...typeof value.subjectKey === "string" ? { subjectKey: value.subjectKey } : {}
12069
- };
12070
- }
12071
- function normalizeRepoMeshDeclarativeConfig(parsed) {
12072
- const errors = [];
12073
- if (!isRecord3(parsed)) return { valid: false, errors: ["config must be an object"] };
12074
- if (parsed.version !== 1) {
12075
- return { valid: false, errors: [`version must be 1 (got ${JSON.stringify(parsed.version)})`] };
12076
- }
12077
- const config = { version: 1 };
12078
- if (parsed.coordinator !== void 0) {
12079
- if (isRecord3(parsed.coordinator)) {
12080
- const coord = {};
12081
- const c = parsed.coordinator;
12082
- if (typeof c.systemPromptOverride === "string") coord.systemPromptOverride = c.systemPromptOverride;
12083
- if (typeof c.systemPromptAppend === "string") coord.systemPromptAppend = c.systemPromptAppend;
12084
- if (Number.isFinite(Number(c.maxPromptChars))) coord.maxPromptChars = Number(c.maxPromptChars);
12085
- config.coordinator = coord;
12086
- } else {
12087
- errors.push("coordinator must be an object when provided");
12088
- }
12089
- }
12090
- if (parsed.operatingNotes !== void 0) {
12091
- if (Array.isArray(parsed.operatingNotes)) {
12092
- const notes = parsed.operatingNotes.map(normalizeOperatingNote).filter((n) => n !== null);
12093
- if (notes.length) config.operatingNotes = notes;
12094
- } else {
12095
- errors.push("operatingNotes must be an array when provided");
12096
- }
12097
- }
12098
- if (parsed.limits !== void 0) {
12099
- if (isRecord3(parsed.limits)) {
12100
- const limits = {};
12101
- if (Number.isFinite(Number(parsed.limits.maxNoteChars))) limits.maxNoteChars = Number(parsed.limits.maxNoteChars);
12102
- if (Number.isFinite(Number(parsed.limits.maxNotes))) limits.maxNotes = Number(parsed.limits.maxNotes);
12103
- if (Object.keys(limits).length) config.limits = limits;
12104
- } else {
12105
- errors.push("limits must be an object when provided");
12106
- }
12107
- }
12108
- return { valid: true, config, errors };
12109
- }
12110
- function loadRepoMeshJsonConfig(workspace) {
12111
- const bases = [];
12112
- const ws = typeof workspace === "string" ? workspace.trim() : "";
12113
- if (ws) bases.push(ws);
12114
- let cwd = "";
12115
- try {
12116
- cwd = process.cwd();
12117
- } catch {
12118
- }
12119
- if (cwd && cwd !== ws) bases.push(cwd);
12120
- for (const base of bases) {
12121
- for (const relative5 of MESH_JSON_CONFIG_LOCATIONS) {
12122
- const configPath = join17(base, relative5);
12123
- if (!existsSync15(configPath)) continue;
12124
- try {
12125
- const parsed = parseConfigText4(configPath, readFileSync12(configPath, "utf-8"));
12126
- const result = normalizeRepoMeshDeclarativeConfig(parsed);
12127
- if (!result.valid || !result.config) {
12128
- return { source: relative5, sourceType: "invalid", path: configPath, error: result.errors.join("; ") };
12129
- }
12130
- return { config: result.config, source: relative5, sourceType: "repo_file", path: configPath };
12131
- } catch (error) {
12132
- return { source: relative5, sourceType: "invalid", path: configPath, error: error?.message || String(error) };
12133
- }
12134
- }
12135
- }
12136
- return {
12137
- source: "unavailable",
12138
- sourceType: "unavailable",
12139
- error: `No repo mesh config found. Checked: ${MESH_JSON_CONFIG_LOCATIONS.join(", ")}`
12140
- };
12141
- }
12142
- function mergeEffectiveCoordinatorConfig(repoCoord, localCoord) {
12143
- const out = { ...localCoord || {} };
12144
- const localOverride = localCoord?.systemPromptOverride?.trim();
12145
- const repoOverride = repoCoord?.systemPromptOverride?.trim();
12146
- if (localOverride) {
12147
- out.systemPromptOverride = localCoord.systemPromptOverride;
12148
- } else if (repoOverride) {
12149
- out.systemPromptOverride = repoCoord.systemPromptOverride;
12150
- } else {
12151
- delete out.systemPromptOverride;
12152
- }
12153
- const repoAppend = repoCoord?.systemPromptAppend?.trim() ? repoCoord.systemPromptAppend.trim() : "";
12154
- const localAppendRaw = localCoord?.systemPromptAppend ?? localCoord?.systemPromptSuffix;
12155
- const localAppend = localAppendRaw?.trim() ? localAppendRaw.trim() : "";
12156
- const stacked = [repoAppend, localAppend].filter(Boolean).join("\n\n");
12157
- if (stacked) {
12158
- out.systemPromptAppend = stacked;
12159
- delete out.systemPromptSuffix;
12160
- }
12161
- return out;
12162
- }
12163
- function mergeEffectiveOperatingNotes(repoNotes, ledgerNotes) {
12164
- const usable = (notes) => Array.isArray(notes) ? notes.filter((n) => n && typeof n.text === "string" && n.text.trim()) : [];
12165
- const repo = usable(repoNotes);
12166
- const ledger = usable(ledgerNotes);
12167
- const ledgerTexts = new Set(ledger.map((n) => n.text.trim()));
12168
- const repoKept = repo.filter((n) => !ledgerTexts.has(n.text.trim()));
12169
- const merged = [...repoKept, ...ledger];
12170
- return merged.length ? merged : void 0;
12171
- }
12172
- function applyRepoMeshConfig(mesh, repoConfig) {
12173
- if (!repoConfig) return mesh;
12174
- return {
12175
- ...mesh,
12176
- coordinator: mergeEffectiveCoordinatorConfig(repoConfig.coordinator, mesh.coordinator)
12177
- };
12178
- }
12179
- function buildMeshJsonConfigScaffold(mesh) {
12180
- const scaffold = { version: 1 };
12181
- const coord = {};
12182
- const override = mesh.coordinator?.systemPromptOverride;
12183
- if (typeof override === "string" && override.trim()) coord.systemPromptOverride = override;
12184
- const append = mesh.coordinator?.systemPromptAppend ?? mesh.coordinator?.systemPromptSuffix;
12185
- if (typeof append === "string" && append.trim()) coord.systemPromptAppend = append;
12186
- if (Object.keys(coord).length) scaffold.coordinator = coord;
12187
- return scaffold;
12188
- }
12189
- function serializeMeshJsonConfigScaffold(config) {
12190
- return JSON.stringify(config, null, 2);
12191
- }
12192
- var MESH_JSON_CONFIG_LOCATIONS, MESH_JSON_CONFIG_SCHEMA;
12193
- var init_mesh_json_config = __esm({
12194
- "src/config/mesh-json-config.ts"() {
12195
- "use strict";
12196
- MESH_JSON_CONFIG_LOCATIONS = [
12197
- ".adhdev/mesh.json",
12198
- ".adhdev/mesh.yaml",
12199
- ".adhdev/mesh.yml"
12200
- ];
12201
- MESH_JSON_CONFIG_SCHEMA = {
12202
- $schema: "https://json-schema.org/draft/2020-12/schema",
12203
- title: "ADHDev Repo Mesh Declarative Config",
12204
- type: "object",
12205
- additionalProperties: false,
12206
- required: ["version"],
12207
- properties: {
12208
- version: { const: 1 },
12209
- coordinator: {
12210
- type: "object",
12211
- additionalProperties: false,
12212
- properties: {
12213
- systemPromptOverride: { type: "string" },
12214
- systemPromptAppend: { type: "string" },
12215
- maxPromptChars: { type: "number", minimum: 1 }
12216
- }
12217
- },
12218
- operatingNotes: {
12219
- type: "array",
12220
- maxItems: 200,
12221
- items: {
12222
- type: "object",
12223
- additionalProperties: false,
12224
- required: ["text"],
12225
- properties: {
12226
- text: { type: "string", minLength: 1 },
12227
- category: { enum: ["provider_quirk", "pattern_to_avoid", "recovery_lesson"] },
12228
- createdAt: { type: "string" },
12229
- sourceCoordinator: { type: "string" }
12230
- }
12231
- }
12232
- },
12233
- limits: {
12234
- type: "object",
12235
- additionalProperties: false,
12236
- properties: {
12237
- maxNoteChars: { type: "number", minimum: 1 },
12238
- maxNotes: { type: "number", minimum: 1 }
12239
- }
12240
- }
12241
- }
12242
- };
12243
- }
12244
- });
12245
-
12246
12376
  // src/mesh/mesh-fast-forward.ts
12247
12377
  async function fastForwardMeshNode(args) {
12248
12378
  const workspace = typeof args.workspace === "string" ? args.workspace.trim() : "";
@@ -16576,6 +16706,16 @@ function __resetIdleAutoFastForwardForTests() {
16576
16706
  continuousAutoFastForwardLastScan.clear();
16577
16707
  autoFastForwardWorkspaceLease.clear();
16578
16708
  }
16709
+ function loadRepoConfigForNode(node) {
16710
+ const workspace = typeof node?.workspace === "string" && node.workspace.trim() ? node.workspace.trim() : "";
16711
+ if (!workspace) return null;
16712
+ try {
16713
+ const result = loadRepoMeshJsonConfig(workspace);
16714
+ return result.sourceType === "repo_file" && result.config ? result.config : null;
16715
+ } catch {
16716
+ return null;
16717
+ }
16718
+ }
16579
16719
  function getMeshWithCache(components, meshId) {
16580
16720
  const localMesh = getMesh(meshId);
16581
16721
  const cachedMesh = components.router?.getCachedInlineMesh(meshId);
@@ -16835,7 +16975,13 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
16835
16975
  meshNodeFor: meshId,
16836
16976
  meshNodeId: nodeId,
16837
16977
  launchedByCoordinator: true,
16838
- autoApprove: resolveDelegatedWorkerAutoApprove(mesh?.policy, node?.policy),
16978
+ ...delegatedWorkerAutoApproveSettings(
16979
+ mesh?.policy,
16980
+ node?.policy,
16981
+ components.providerLoader?.getMeta(providerType),
16982
+ loadRepoConfigForNode(node),
16983
+ providerType
16984
+ ),
16839
16985
  ...localDaemonId ? { meshCoordinatorDaemonId: localDaemonId } : {},
16840
16986
  // COMPLETION-PROPAGATION F5: (re)stamp the coordinator SESSION anchor from THIS
16841
16987
  // task's sourceCoordinatorSessionId with PRIORITY — a manually-launched (or reused)
@@ -17273,10 +17419,10 @@ function liveSessionCountForNode(components, meshId, nodeId) {
17273
17419
  }
17274
17420
  return count;
17275
17421
  }
17276
- function nodeHasLiveSessionPendingClaim(components, meshId, nodeId) {
17422
+ function nodeHasLiveSessionPendingClaim(components, meshId, nodeId, task, node) {
17277
17423
  if (inWindowAutoLaunchSessionIdsForNode(meshId, nodeId).length > 0) return true;
17278
17424
  const busySessionIds = new Set(
17279
- getQueue(meshId, { status: ["assigned"] }).filter((task) => daemonIdsEquivalent(task.assignedNodeId, nodeId)).map((task) => readNonEmptyString(task.assignedSessionId)).filter(Boolean)
17425
+ getQueue(meshId, { status: ["assigned"] }).filter((task2) => daemonIdsEquivalent(task2.assignedNodeId, nodeId)).map((task2) => readNonEmptyString(task2.assignedSessionId)).filter(Boolean)
17280
17426
  );
17281
17427
  return components.instanceManager.getByCategory("cli").some((inst) => {
17282
17428
  const state = inst.getState();
@@ -17289,6 +17435,12 @@ function nodeHasLiveSessionPendingClaim(components, meshId, nodeId) {
17289
17435
  if (isTerminalSessionStatus(status)) return false;
17290
17436
  const sessionId = readNonEmptyString(state.instanceId);
17291
17437
  if (sessionId && busySessionIds.has(sessionId)) return false;
17438
+ if (task.requiredTags?.length) {
17439
+ const sessionProviderType = state.type || readNonEmptyString(settings.providerType);
17440
+ if (sessionProviderType && !nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, sessionProviderType))) {
17441
+ return false;
17442
+ }
17443
+ }
17292
17444
  return true;
17293
17445
  });
17294
17446
  }
@@ -17569,7 +17721,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
17569
17721
  sweepExpiredCooldowns();
17570
17722
  continue;
17571
17723
  }
17572
- if (nodeHasLiveSessionPendingClaim(components, meshId, nodeId)) {
17724
+ if (nodeHasLiveSessionPendingClaim(components, meshId, nodeId, task, node)) {
17573
17725
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_has_live_session_pending_claim", nodeId });
17574
17726
  continue;
17575
17727
  }
@@ -17608,8 +17760,14 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
17608
17760
  spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
17609
17761
  // Coordinator-dispatched worker: auto-approve unless mesh/node policy
17610
17762
  // opts out (default true). Lands in settingsOverride and beats the
17611
- // global per-provider-type autoApprove config (see shouldAutoApprove).
17612
- autoApprove: resolveDelegatedWorkerAutoApprove(mesh?.policy, node?.policy),
17763
+ // global per-provider-type boolean/mode through explicit opposite-key clearing.
17764
+ ...delegatedWorkerAutoApproveSettings(
17765
+ mesh?.policy,
17766
+ node?.policy,
17767
+ components.providerLoader?.getMeta(resolved.providerType),
17768
+ loadRepoConfigForNode(node),
17769
+ resolved.providerType
17770
+ ),
17613
17771
  launchedByCoordinator: true,
17614
17772
  autoLaunchedForQueueTaskId: task.id
17615
17773
  };
@@ -18073,6 +18231,7 @@ var init_mesh_queue_assignment = __esm({
18073
18231
  init_mesh_event_trace();
18074
18232
  init_mesh_warmup_deadline();
18075
18233
  init_repo_mesh_types();
18234
+ init_mesh_json_config();
18076
18235
  init_dist();
18077
18236
  init_mesh_node_slots();
18078
18237
  init_mesh_events_stale();
@@ -20449,6 +20608,7 @@ function buildAvailableProviders(providerLoader) {
20449
20608
  ...provider.lastDetection !== void 0 ? { lastDetection: provider.lastDetection } : {},
20450
20609
  ...provider.lastVerification !== void 0 ? { lastVerification: provider.lastVerification } : {},
20451
20610
  ...provider.meshCoordinator !== void 0 ? { meshCoordinator: provider.meshCoordinator } : {},
20611
+ ...provider.autoApproveModes !== void 0 ? { autoApproveModes: provider.autoApproveModes } : {},
20452
20612
  ...trust ? {
20453
20613
  trust,
20454
20614
  trustDescription: describeTrust2(trust),
@@ -21627,7 +21787,24 @@ function injectMeshSystemMessage(components, args) {
21627
21787
  spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
21628
21788
  // Coordinator-dispatched recovery relaunch: same auto-approve
21629
21789
  // policy as the primary worker launch path.
21630
- autoApprove: resolveDelegatedWorkerAutoApprove(mesh?.policy, node?.policy),
21790
+ ...delegatedWorkerAutoApproveSettings(
21791
+ mesh?.policy,
21792
+ node?.policy,
21793
+ components.providerLoader?.getMeta(recoveryContext.failedProviderType),
21794
+ // Recovery relaunch: same repo-declared requested mode as the
21795
+ // primary path. node.workspace missing → null → provider default.
21796
+ (() => {
21797
+ const ws = typeof node?.workspace === "string" && node.workspace.trim() ? node.workspace.trim() : "";
21798
+ if (!ws) return null;
21799
+ try {
21800
+ const r = loadRepoMeshJsonConfig(ws);
21801
+ return r.sourceType === "repo_file" && r.config ? r.config : null;
21802
+ } catch {
21803
+ return null;
21804
+ }
21805
+ })(),
21806
+ recoveryContext.failedProviderType
21807
+ ),
21631
21808
  launchedByCoordinator: true
21632
21809
  }
21633
21810
  }).catch((e) => LOG.error("MeshRecovery", `Failed to auto-relaunch session for ${node.id}: ${e?.message}`));
@@ -22017,6 +22194,7 @@ var init_mesh_event_forwarding = __esm({
22017
22194
  init_mesh_event_trace();
22018
22195
  init_snapshot();
22019
22196
  init_repo_mesh_types();
22197
+ init_mesh_json_config();
22020
22198
  init_dist();
22021
22199
  init_mesh_events_stale();
22022
22200
  init_mesh_task_inflight();
@@ -24287,6 +24465,20 @@ var init_provider_schema = __esm({
24287
24465
  }
24288
24466
  }
24289
24467
  },
24468
+ autoApproveModes: {
24469
+ type: "object",
24470
+ description: "Provider-specific auto-approve choices and their launch/runtime strategy.",
24471
+ required: ["default", "modes"],
24472
+ additionalProperties: false,
24473
+ properties: {
24474
+ default: { type: "string", minLength: 1 },
24475
+ modes: {
24476
+ type: "array",
24477
+ minItems: 1,
24478
+ items: { $ref: "#/$defs/autoApproveMode" }
24479
+ }
24480
+ }
24481
+ },
24290
24482
  sendDelayMs: {
24291
24483
  type: "number",
24292
24484
  minimum: 0,
@@ -24660,6 +24852,36 @@ var init_provider_schema = __esm({
24660
24852
  }
24661
24853
  },
24662
24854
  $defs: {
24855
+ autoApproveMode: {
24856
+ type: "object",
24857
+ required: ["id", "label", "strategy", "risk"],
24858
+ additionalProperties: false,
24859
+ properties: {
24860
+ id: { type: "string", minLength: 1 },
24861
+ label: { type: "string", minLength: 1 },
24862
+ strategy: { enum: ["pty-parse-default", "launch-args", "post-boot-command"] },
24863
+ risk: { enum: ["safe", "caution", "dangerous"] },
24864
+ warning: { type: "string", minLength: 1 },
24865
+ launchArgs: {
24866
+ type: "array",
24867
+ items: { type: "string", minLength: 1 }
24868
+ },
24869
+ removeArgs: {
24870
+ type: "array",
24871
+ items: { type: "string", minLength: 1 }
24872
+ }
24873
+ },
24874
+ allOf: [
24875
+ {
24876
+ if: { properties: { strategy: { const: "launch-args" } }, required: ["strategy"] },
24877
+ then: { required: ["launchArgs"], properties: { launchArgs: { minItems: 1 } } }
24878
+ },
24879
+ {
24880
+ if: { properties: { risk: { const: "dangerous" } }, required: ["risk"] },
24881
+ then: { required: ["warning"] }
24882
+ }
24883
+ ]
24884
+ },
24663
24885
  patternList: {
24664
24886
  type: "array",
24665
24887
  items: {
@@ -30768,6 +30990,7 @@ function detectClaudeAskUserQuestionPromptFromJson(value, providerType = "claude
30768
30990
 
30769
30991
  // src/index.ts
30770
30992
  init_repo_mesh_types();
30993
+ init_mesh_json_config();
30771
30994
 
30772
30995
  // src/git/index.ts
30773
30996
  init_git_executor();
@@ -49005,6 +49228,7 @@ function formatMarkerTimestamp(timestamp) {
49005
49228
  }
49006
49229
 
49007
49230
  // src/providers/cli-provider-instance.ts
49231
+ init_auto_approve_modes();
49008
49232
  function approvalModalSignature(message, affirmativeAnchor) {
49009
49233
  return [typeof message === "string" ? message.trim() : "", affirmativeAnchor].join("::");
49010
49234
  }
@@ -50985,7 +51209,7 @@ var CliProviderInstance = class _CliProviderInstance {
50985
51209
  * resolveModal, so a plain turn that never saw an approval always returns false.
50986
51210
  */
50987
51211
  inApprovalResumeGrace(now = Date.now()) {
50988
- if (!this.isAutonomousMeshSession() || !this.shouldAutoApprove()) return false;
51212
+ if (!this.isAutonomousMeshSession() || !this.shouldUsePtyAutoApprove()) return false;
50989
51213
  const resolvedAt = typeof this.adapter?.lastApprovalResolvedAt === "number" ? this.adapter.lastApprovalResolvedAt : 0;
50990
51214
  if (resolvedAt <= 0) return false;
50991
51215
  return now - resolvedAt < _CliProviderInstance.APPROVAL_RESUME_GRACE_MS;
@@ -51090,7 +51314,7 @@ var CliProviderInstance = class _CliProviderInstance {
51090
51314
  return;
51091
51315
  }
51092
51316
  const latestStatus = this.adapter.getStatus({ allowParse: false });
51093
- const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
51317
+ const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldUsePtyAutoApprove();
51094
51318
  const latestVisibleStatus = latestAutoApproveActive || this.autoApproveBusy ? "generating" : latestStatus.status;
51095
51319
  LOG.debug("CLI", `[${this.type}] flush attempt: adapterStatus=${latestStatus.status} latestVisible=${latestVisibleStatus} generatingStartedAt=${this.generatingStartedAt} isWaitingForResponse=${!!this.adapter?.isWaitingForResponse} hasPartial=${!!this.adapter.getPartialResponse?.()}`);
51096
51320
  if (latestVisibleStatus !== "idle") {
@@ -51360,7 +51584,7 @@ var CliProviderInstance = class _CliProviderInstance {
51360
51584
  * session, where a human answers the prompt, is returned untouched).
51361
51585
  */
51362
51586
  stabilizeFlappingApprovalStatus(adapterStatus, now = Date.now()) {
51363
- if (!this.isAutonomousMeshSession() || !this.shouldAutoApprove()) return adapterStatus;
51587
+ if (!this.isAutonomousMeshSession() || !this.shouldUsePtyAutoApprove()) return adapterStatus;
51364
51588
  const rawStatus = adapterStatus?.status;
51365
51589
  const resolvedAt = typeof this.adapter?.lastApprovalResolvedAt === "number" ? this.adapter.lastApprovalResolvedAt : 0;
51366
51590
  if (rawStatus === "waiting_approval") {
@@ -51390,7 +51614,11 @@ var CliProviderInstance = class _CliProviderInstance {
51390
51614
  return adapterStatus;
51391
51615
  }
51392
51616
  maybeAutoApproveStatus(adapterStatus, now = Date.now()) {
51393
- if (adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove() && this.manualAttendance.isAttended(now)) {
51617
+ if (!this.shouldUsePtyAutoApprove()) {
51618
+ this.resetPtyAutoApproveState();
51619
+ return false;
51620
+ }
51621
+ if (adapterStatus?.status === "waiting_approval" && this.shouldUsePtyAutoApprove() && this.manualAttendance.isAttended(now)) {
51394
51622
  this.lastAutoApprovalSignature = "";
51395
51623
  this.pendingAutoApprovalSignature = "";
51396
51624
  this.pendingAutoApprovalSince = 0;
@@ -51405,7 +51633,7 @@ var CliProviderInstance = class _CliProviderInstance {
51405
51633
  }, this.manualAttendance.remainingMs(now) + 20);
51406
51634
  return false;
51407
51635
  }
51408
- const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
51636
+ const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldUsePtyAutoApprove();
51409
51637
  if (!autoApproveActive) {
51410
51638
  this.lastAutoApprovalSignature = "";
51411
51639
  if (this.pendingAutoApprovalSince) {
@@ -52100,15 +52328,34 @@ ${buttons.join("\n")}`;
52100
52328
  get cliName() {
52101
52329
  return this.provider.name;
52102
52330
  }
52331
+ resolveAutoApproveMode() {
52332
+ return resolveProviderAutoApproveMode(this.provider, this.settings);
52333
+ }
52334
+ /** Legacy boolean view retained for internal/test compatibility. */
52103
52335
  shouldAutoApprove() {
52104
- if (typeof this.settings.autoApprove === "boolean") {
52105
- return this.settings.autoApprove;
52336
+ return this.resolveAutoApproveMode().active;
52337
+ }
52338
+ shouldUsePtyAutoApprove() {
52339
+ const resolved = this.resolveAutoApproveMode();
52340
+ return this.shouldAutoApprove() && resolved.strategy === "pty-parse-default";
52341
+ }
52342
+ resetPtyAutoApproveState() {
52343
+ this.lastAutoApprovalSignature = "";
52344
+ this.pendingAutoApprovalSignature = "";
52345
+ this.pendingAutoApprovalSince = 0;
52346
+ this.autoApproveInactiveSince = 0;
52347
+ this.autoApproveMaskSince = 0;
52348
+ this.stalledApprovalNudgeEpisode = 0;
52349
+ this.autoApproveLastModalSeenAt = 0;
52350
+ this.autoApproveBusy = false;
52351
+ if (this.autoApproveSettleTimer) {
52352
+ clearTimeout(this.autoApproveSettleTimer);
52353
+ this.autoApproveSettleTimer = null;
52106
52354
  }
52107
- const providerDefault = this.provider.settings?.autoApprove?.default;
52108
- if (typeof providerDefault === "boolean") {
52109
- return providerDefault;
52355
+ if (this.autoApproveBusyTimer) {
52356
+ clearTimeout(this.autoApproveBusyTimer);
52357
+ this.autoApproveBusyTimer = null;
52110
52358
  }
52111
- return false;
52112
52359
  }
52113
52360
  /** @see ProviderInstance.noteManualInteraction */
52114
52361
  noteManualInteraction(now = Date.now(), opts) {
@@ -52124,7 +52371,7 @@ ${buttons.join("\n")}`;
52124
52371
  * CLI-specific modal text.
52125
52372
  */
52126
52373
  autoApproveEffectivelyActive(status, now = Date.now()) {
52127
- return status === "waiting_approval" && this.shouldAutoApprove() && !this.manualAttendance.isAttended(now);
52374
+ return status === "waiting_approval" && this.shouldUsePtyAutoApprove() && !this.manualAttendance.isAttended(now);
52128
52375
  }
52129
52376
  // STATUS-MISMATCH: true once the current auto-approve episode has been masking
52130
52377
  // waiting_approval behind `generating` for longer than AUTO_APPROVE_MASK_STALL_MS without
@@ -52134,7 +52381,7 @@ ${buttons.join("\n")}`;
52134
52381
  // maybeAutoApproveStatus (driven by getState + the recheck timer during a waiting episode);
52135
52382
  // this read is side-effect-free so getStatusMetadata can consult it too.
52136
52383
  autoApproveMaskStalled(now = Date.now()) {
52137
- return this.autoApproveMaskSince > 0 && now - this.autoApproveMaskSince > _CliProviderInstance.AUTO_APPROVE_MASK_STALL_MS;
52384
+ return this.shouldUsePtyAutoApprove() && this.autoApproveMaskSince > 0 && now - this.autoApproveMaskSince > _CliProviderInstance.AUTO_APPROVE_MASK_STALL_MS;
52138
52385
  }
52139
52386
  /**
52140
52387
  * NOTIF-APPROVAL-MASKED (Q1b): surface a delegated worker's STALLED auto-approve modal
@@ -52158,6 +52405,7 @@ ${buttons.join("\n")}`;
52158
52405
  * mask-clock value) so a modal that flaps between parsed/unparsed states is announced once.
52159
52406
  */
52160
52407
  maybeEmitStalledApprovalNudge(adapterStatus, now) {
52408
+ if (!this.shouldUsePtyAutoApprove()) return;
52161
52409
  if (!this.isMeshWorkerSession()) return;
52162
52410
  if (adapterStatus?.status !== "waiting_approval") return;
52163
52411
  if (!this.autoApproveMaskStalled(now)) return;
@@ -53703,6 +53951,7 @@ function shouldRestoreHostedRuntime(record, managerTag) {
53703
53951
  }
53704
53952
 
53705
53953
  // src/commands/cli-manager.ts
53954
+ init_auto_approve_modes();
53706
53955
  function isExplicitCommand(command) {
53707
53956
  const trimmed = command.trim();
53708
53957
  return path30.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
@@ -53929,6 +54178,24 @@ function expandThinkingLaunchArgs(template, level, levelMap) {
53929
54178
  const mapped = levelMap && typeof levelMap[raw] === "string" && levelMap[raw].trim() ? levelMap[raw].trim() : raw;
53930
54179
  return template.map((part) => part.includes("{{level}}") ? part.replace("{{level}}", mapped) : part);
53931
54180
  }
54181
+ function matchesRemovedLaunchArg(arg, removeArg) {
54182
+ return arg === removeArg || removeArg.startsWith("--") && arg.startsWith(`${removeArg}=`);
54183
+ }
54184
+ function applyAutoApproveModeLaunchArgs(provider, cliArgs, settings) {
54185
+ if (!provider) return { provider, cliArgs };
54186
+ const resolved = resolveProviderAutoApproveMode(provider, settings);
54187
+ if (!resolved.active || resolved.strategy !== "launch-args") return { provider, cliArgs };
54188
+ const mode = findProviderAutoApproveMode(provider, resolved.modeId);
54189
+ if (!mode || !Array.isArray(mode.launchArgs) || mode.launchArgs.length === 0) return { provider, cliArgs };
54190
+ const removeArgs = Array.isArray(mode.removeArgs) ? mode.removeArgs : [];
54191
+ const baseArgs = provider.spawn?.args;
54192
+ const filteredBaseArgs = Array.isArray(baseArgs) && removeArgs.length > 0 ? baseArgs.filter((arg) => !removeArgs.some((removeArg) => matchesRemovedLaunchArg(arg, removeArg))) : baseArgs;
54193
+ const launchProvider = filteredBaseArgs === baseArgs ? provider : { ...provider, spawn: { ...provider.spawn, args: filteredBaseArgs } };
54194
+ return {
54195
+ provider: launchProvider,
54196
+ cliArgs: [...mode.launchArgs, ...cliArgs || []]
54197
+ };
54198
+ }
53932
54199
  function readSubcommandSessionId(args, subcommands) {
53933
54200
  const resumeIndex = args.findIndex((arg) => subcommands.includes(arg));
53934
54201
  if (resumeIndex < 0) return void 0;
@@ -54359,22 +54626,30 @@ Run 'adhdev doctor' for detailed diagnostics.`
54359
54626
  if (provider) {
54360
54627
  console.log(colorize("cyan", ` \u{1F4E6} Using provider: ${provider.name} (${provider.type})`));
54361
54628
  }
54362
- const modelLaunchArgs = expandModelLaunchArgs(provider?.modelLaunchArgs, initialModel);
54363
- const cliArgsWithModel = modelLaunchArgs ? [...modelLaunchArgs, ...cliArgs || []] : cliArgs;
54629
+ const launchSettings = {
54630
+ ...this.providerLoader.getSettings(normalizedType),
54631
+ ...options?.settingsOverride || {}
54632
+ };
54633
+ const versionResolvedProvider = provider ? this.providerLoader.resolve(cliType, { version: cliInfo.version }) || provider : void 0;
54634
+ const autoApproveLaunch = applyAutoApproveModeLaunchArgs(versionResolvedProvider, cliArgs, launchSettings);
54635
+ const launchProvider = autoApproveLaunch.provider || provider;
54636
+ const cliArgsWithAutoApprove = autoApproveLaunch.cliArgs;
54637
+ const modelLaunchArgs = expandModelLaunchArgs(launchProvider?.modelLaunchArgs, initialModel);
54638
+ const cliArgsWithModel = modelLaunchArgs ? [...modelLaunchArgs, ...cliArgsWithAutoApprove || []] : cliArgsWithAutoApprove;
54364
54639
  if (initialModel && !modelLaunchArgs) {
54365
54640
  LOG.warn("CLI", `[${normalizedType}] initialModel='${initialModel}' requested but provider declares no modelLaunchArgs template \u2014 launching without model selection.`);
54366
54641
  }
54367
54642
  const initialThinkingLevel = options?.initialThinkingLevel;
54368
- const thinkingLaunchArgs = expandThinkingLaunchArgs(provider?.thinkingLaunchArgs, initialThinkingLevel, provider?.thinkingLevelMap);
54643
+ const thinkingLaunchArgs = expandThinkingLaunchArgs(launchProvider?.thinkingLaunchArgs, initialThinkingLevel, launchProvider?.thinkingLevelMap);
54369
54644
  const cliArgsWithBrain = thinkingLaunchArgs ? [...thinkingLaunchArgs, ...cliArgsWithModel || []] : cliArgsWithModel;
54370
54645
  if (initialThinkingLevel && !thinkingLaunchArgs) {
54371
54646
  LOG.warn("CLI", `[${normalizedType}] initialThinkingLevel='${initialThinkingLevel}' requested but provider declares no thinkingLaunchArgs template \u2014 launching without thinking-level selection.`);
54372
54647
  }
54373
- const sessionBinding = resolveCliSessionBinding(provider, normalizedType, cliArgsWithBrain, options?.resumeSessionId);
54648
+ const sessionBinding = resolveCliSessionBinding(launchProvider, normalizedType, cliArgsWithBrain, options?.resumeSessionId);
54374
54649
  const resolvedCliArgs = sessionBinding.cliArgs;
54375
54650
  const instanceManager = this.deps.getInstanceManager();
54376
- if (provider && instanceManager) {
54377
- const resolvedProvider = this.providerLoader.resolve(cliType, { version: cliInfo.version }) || provider;
54651
+ if (launchProvider && instanceManager) {
54652
+ const resolvedProvider = launchProvider;
54378
54653
  await this.registerCliInstance(
54379
54654
  key2,
54380
54655
  normalizedType,
@@ -54382,7 +54657,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
54382
54657
  resolvedDir,
54383
54658
  resolvedCliArgs,
54384
54659
  resolvedProvider,
54385
- { ...this.providerLoader.getSettings(normalizedType), ...options?.settingsOverride || {} },
54660
+ launchSettings,
54386
54661
  false,
54387
54662
  {
54388
54663
  providerSessionId: sessionBinding.providerSessionId,
@@ -55221,6 +55496,7 @@ init_hash();
55221
55496
  init_logger();
55222
55497
 
55223
55498
  // src/providers/provider-schema.ts
55499
+ init_auto_approve_modes();
55224
55500
  init_open_panel_support();
55225
55501
  var VALID_CAPABILITY_MEDIA_TYPES = /* @__PURE__ */ new Set(["text", "image", "audio", "video", "resource"]);
55226
55502
  var VALID_INPUT_STRATEGIES2 = /* @__PURE__ */ new Set(["native", "native_acp", "resource_link", "text_fallback", "paste", "upload"]);
@@ -55286,6 +55562,7 @@ var KNOWN_PROVIDER_FIELDS = /* @__PURE__ */ new Set([
55286
55562
  "providerVersion",
55287
55563
  "status",
55288
55564
  "details",
55565
+ "autoApproveModes",
55289
55566
  "modelLaunchArgs",
55290
55567
  "modelOptions",
55291
55568
  "thinkingLaunchArgs",
@@ -55347,6 +55624,7 @@ function validateProviderDefinition(raw) {
55347
55624
  validateCapabilities(provider, controls, errors);
55348
55625
  validateNativeHistory(provider.nativeHistory, errors);
55349
55626
  validateMeshCoordinator(provider.meshCoordinator, errors);
55627
+ validateAutoApproveModes(provider.autoApproveModes, errors);
55350
55628
  for (const control of controls) {
55351
55629
  validateControl(control, errors);
55352
55630
  }
@@ -55355,6 +55633,75 @@ function validateProviderDefinition(raw) {
55355
55633
  }
55356
55634
  return { errors, warnings };
55357
55635
  }
55636
+ function validateAutoApproveModes(raw, errors) {
55637
+ if (raw === void 0) return;
55638
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
55639
+ errors.push("autoApproveModes must be an object");
55640
+ return;
55641
+ }
55642
+ const config = raw;
55643
+ const defaultModeId = typeof config.default === "string" ? config.default.trim() : "";
55644
+ if (!defaultModeId) {
55645
+ errors.push("autoApproveModes.default must be a non-empty string");
55646
+ } else {
55647
+ config.default = defaultModeId;
55648
+ }
55649
+ if (!Array.isArray(config.modes) || config.modes.length === 0) {
55650
+ errors.push("autoApproveModes.modes must be a non-empty array");
55651
+ return;
55652
+ }
55653
+ const ids = /* @__PURE__ */ new Set();
55654
+ for (const [index, rawMode] of config.modes.entries()) {
55655
+ const prefix = `autoApproveModes.modes[${index}]`;
55656
+ if (!rawMode || typeof rawMode !== "object" || Array.isArray(rawMode)) {
55657
+ errors.push(`${prefix} must be an object`);
55658
+ continue;
55659
+ }
55660
+ const mode = rawMode;
55661
+ const id = typeof mode.id === "string" ? mode.id.trim() : "";
55662
+ if (!id) {
55663
+ errors.push(`${prefix}.id must be a non-empty string`);
55664
+ } else if (ids.has(id)) {
55665
+ errors.push(`${prefix}.id must be unique (duplicate: ${id})`);
55666
+ } else {
55667
+ ids.add(id);
55668
+ mode.id = id;
55669
+ }
55670
+ if (typeof mode.label !== "string" || !mode.label.trim()) {
55671
+ errors.push(`${prefix}.label must be a non-empty string`);
55672
+ }
55673
+ const strategy = mode.strategy;
55674
+ if (!["pty-parse-default", "launch-args", "post-boot-command"].includes(String(strategy))) {
55675
+ errors.push(`${prefix}.strategy must be one of: pty-parse-default, launch-args, post-boot-command`);
55676
+ } else if (strategy === "post-boot-command") {
55677
+ errors.push(`${prefix}.strategy post-boot-command is reserved and unsupported in v1`);
55678
+ }
55679
+ const risk = mode.risk;
55680
+ if (!["safe", "caution", "dangerous"].includes(String(risk))) {
55681
+ errors.push(`${prefix}.risk must be one of: safe, caution, dangerous`);
55682
+ }
55683
+ for (const field of ["launchArgs", "removeArgs"]) {
55684
+ const value = mode[field];
55685
+ if (value !== void 0 && (!Array.isArray(value) || value.some((arg) => typeof arg !== "string" || !arg.trim()))) {
55686
+ errors.push(`${prefix}.${field} must be an array of non-empty strings when provided`);
55687
+ }
55688
+ }
55689
+ if (strategy === "launch-args" && (!Array.isArray(mode.launchArgs) || mode.launchArgs.length === 0)) {
55690
+ errors.push(`${prefix}.launchArgs must be a non-empty array for launch-args strategy`);
55691
+ }
55692
+ if (["safe", "caution", "dangerous"].includes(String(risk)) && deriveAutoApproveModeRisk(mode) === "dangerous") {
55693
+ mode.risk = "dangerous";
55694
+ }
55695
+ if (mode.risk === "dangerous" && (typeof mode.warning !== "string" || !mode.warning.trim())) {
55696
+ errors.push(`${prefix}.warning is required for dangerous modes`);
55697
+ } else if (mode.warning !== void 0 && (typeof mode.warning !== "string" || !mode.warning.trim())) {
55698
+ errors.push(`${prefix}.warning must be a non-empty string when provided`);
55699
+ }
55700
+ }
55701
+ if (defaultModeId && !ids.has(defaultModeId)) {
55702
+ errors.push(`autoApproveModes.default must reference an existing mode id: ${defaultModeId}`);
55703
+ }
55704
+ }
55358
55705
  function validateCapabilities(provider, controls, errors) {
55359
55706
  const capabilities = provider.capabilities;
55360
55707
  if (provider.contractVersion === 2) {
@@ -59964,6 +60311,139 @@ var meshCrudHandlers = {
59964
60311
  return { success: false, error: e.message };
59965
60312
  }
59966
60313
  },
60314
+ // READ path for `.adhdev/mesh.json` — returns the currently-committed repo
60315
+ // config (parsed + normalized) for a workspace so a UI can render/edit the
60316
+ // existing declarative zones (notably `providerDefaults.autoApproveModes`)
60317
+ // WITHOUT re-deriving them from the machine-local scaffold. Never writes.
60318
+ // `config` is undefined when no repo file exists (sourceType 'unavailable') or
60319
+ // it is unparseable (sourceType 'invalid', with the parse error surfaced).
60320
+ read_mesh_json_config: async (_ctx, args) => {
60321
+ const workspace = typeof args?.workspace === "string" && args.workspace.trim() ? args.workspace.trim() : process.cwd();
60322
+ try {
60323
+ const { loadRepoMeshJsonConfig: loadRepoMeshJsonConfig2 } = await Promise.resolve().then(() => (init_mesh_json_config(), mesh_json_config_exports));
60324
+ const loaded = loadRepoMeshJsonConfig2(workspace);
60325
+ return {
60326
+ success: true,
60327
+ workspace,
60328
+ sourceType: loaded.sourceType,
60329
+ source: loaded.source,
60330
+ ...loaded.path ? { path: loaded.path } : {},
60331
+ ...loaded.error ? { error: loaded.error } : {},
60332
+ config: loaded.config,
60333
+ // Convenience projection so the UI does not have to reach into config.
60334
+ providerDefaults: loaded.config?.providerDefaults
60335
+ };
60336
+ } catch (e) {
60337
+ return { success: false, error: e.message };
60338
+ }
60339
+ },
60340
+ // Partial-edit WRITE path for `.adhdev/mesh.json` `providerDefaults` — a
60341
+ // READ-MODIFY-WRITE that preserves operator hand-edits. Unlike
60342
+ // write_mesh_json_config (which rebuilds the WHOLE file from the machine-local
60343
+ // scaffold and can silently drop hand-edited zones), this parses the existing
60344
+ // repo file, merges ONLY the providerDefaults.autoApproveModes zone, and
60345
+ // re-serializes — coordinator prompt, operating notes and limits authored in
60346
+ // the repo are carried through untouched. Defaults to dry-run.
60347
+ //
60348
+ // args: { workspace?, autoApproveModes: Record<providerType,modeId>, write?, merge? }
60349
+ // merge=true (default): per-provider merge into the existing map; a modeId of
60350
+ // '' | null removes that provider's entry. merge=false: REPLACE the whole
60351
+ // autoApproveModes map with the supplied one.
60352
+ set_mesh_provider_defaults: async (_ctx, args) => {
60353
+ const workspace = typeof args?.workspace === "string" && args.workspace.trim() ? args.workspace.trim() : process.cwd();
60354
+ const write = args?.write === true;
60355
+ const merge = args?.merge !== false;
60356
+ const inputModes = args?.autoApproveModes;
60357
+ if (inputModes !== void 0 && (typeof inputModes !== "object" || inputModes === null || Array.isArray(inputModes))) {
60358
+ return { success: false, error: "autoApproveModes must be an object (providerType \u2192 modeId) when provided" };
60359
+ }
60360
+ try {
60361
+ const {
60362
+ loadRepoMeshJsonConfig: loadRepoMeshJsonConfig2,
60363
+ normalizeRepoMeshDeclarativeConfig: normalizeRepoMeshDeclarativeConfig2,
60364
+ MESH_JSON_CONFIG_LOCATIONS: MESH_JSON_CONFIG_LOCATIONS2
60365
+ } = await Promise.resolve().then(() => (init_mesh_json_config(), mesh_json_config_exports));
60366
+ const { existsSync: existsSync57, readFileSync: readFileSync44, mkdirSync: mkdirSync23, writeFileSync: writeFileSync25 } = await import("fs");
60367
+ const { dirname: dirname19, join: join54 } = await import("path");
60368
+ const yaml6 = await import("js-yaml");
60369
+ const relativePath = MESH_JSON_CONFIG_LOCATIONS2[0];
60370
+ let baseDoc = { version: 1 };
60371
+ let existingPath = join54(workspace, relativePath);
60372
+ let existedAsYaml = false;
60373
+ for (const relative5 of MESH_JSON_CONFIG_LOCATIONS2) {
60374
+ const candidate = join54(workspace, relative5);
60375
+ if (!existsSync57(candidate)) continue;
60376
+ try {
60377
+ const text = readFileSync44(candidate, "utf-8");
60378
+ const parsed = /\.json$/i.test(candidate) ? JSON.parse(text) : yaml6.load(text);
60379
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
60380
+ baseDoc = parsed;
60381
+ existingPath = candidate;
60382
+ existedAsYaml = !/\.json$/i.test(candidate);
60383
+ }
60384
+ } catch (e) {
60385
+ return { success: false, error: `existing ${relative5} is unparseable, refusing to overwrite: ${e?.message || e}` };
60386
+ }
60387
+ break;
60388
+ }
60389
+ const existingPd = baseDoc.providerDefaults && typeof baseDoc.providerDefaults === "object" && !Array.isArray(baseDoc.providerDefaults) ? baseDoc.providerDefaults : {};
60390
+ const existingModes = existingPd.autoApproveModes && typeof existingPd.autoApproveModes === "object" && !Array.isArray(existingPd.autoApproveModes) ? { ...existingPd.autoApproveModes } : {};
60391
+ const nextModes = merge ? existingModes : {};
60392
+ if (inputModes) {
60393
+ for (const [providerType, modeId] of Object.entries(inputModes)) {
60394
+ const type = typeof providerType === "string" ? providerType.trim() : "";
60395
+ if (!type) continue;
60396
+ const id = typeof modeId === "string" ? modeId.trim() : "";
60397
+ if (id) nextModes[type] = id;
60398
+ else delete nextModes[type];
60399
+ }
60400
+ }
60401
+ const nextDoc = { ...baseDoc, version: 1 };
60402
+ if (Object.keys(nextModes).length) {
60403
+ nextDoc.providerDefaults = { ...existingPd, autoApproveModes: nextModes };
60404
+ } else {
60405
+ if (nextDoc.providerDefaults) {
60406
+ const { autoApproveModes, ...restPd } = nextDoc.providerDefaults;
60407
+ if (Object.keys(restPd).length) nextDoc.providerDefaults = restPd;
60408
+ else delete nextDoc.providerDefaults;
60409
+ }
60410
+ }
60411
+ const validation = normalizeRepoMeshDeclarativeConfig2(nextDoc);
60412
+ if (!validation.valid) {
60413
+ return { success: false, error: `merged mesh.json is invalid: ${validation.errors.join("; ")}` };
60414
+ }
60415
+ const absolutePath = existingPath;
60416
+ const serialized = existedAsYaml ? yaml6.dump(nextDoc, { indent: 2 }) : `${JSON.stringify(nextDoc, null, 2)}
60417
+ `;
60418
+ if (!write) {
60419
+ return {
60420
+ success: true,
60421
+ written: false,
60422
+ dryRun: true,
60423
+ path: absolutePath,
60424
+ relativePath,
60425
+ merge,
60426
+ providerDefaults: nextDoc.providerDefaults,
60427
+ preview: serialized,
60428
+ note: "Dry-run: nothing written. Re-run with write=true to persist. Only the providerDefaults zone is merged; other repo zones are preserved."
60429
+ };
60430
+ }
60431
+ mkdirSync23(dirname19(absolutePath), { recursive: true });
60432
+ writeFileSync25(absolutePath, serialized, "utf-8");
60433
+ return {
60434
+ success: true,
60435
+ written: true,
60436
+ dryRun: false,
60437
+ path: absolutePath,
60438
+ relativePath,
60439
+ merge,
60440
+ providerDefaults: nextDoc.providerDefaults,
60441
+ note: "Wrote providerDefaults into .adhdev/mesh.json (read-modify-write; other zones preserved). Commit it to the repo."
60442
+ };
60443
+ } catch (e) {
60444
+ return { success: false, error: e.message };
60445
+ }
60446
+ },
59967
60447
  delete_mesh: async (_ctx, args) => {
59968
60448
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
59969
60449
  if (!meshId) return { success: false, error: "meshId required" };
@@ -76096,6 +76576,9 @@ export {
76096
76576
  MAX_LEDGER_SLICE_LIMIT,
76097
76577
  MESH_CONVERGE_FAST_FORWARD_TAG,
76098
76578
  MESH_CONVERGE_REFINE_TAG,
76579
+ MESH_JSON_CONFIG_LOCATIONS,
76580
+ MESH_JSON_CONFIG_SCHEMA,
76581
+ MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE,
76099
76582
  MESH_MAX_PARALLEL_TASKS_MAX,
76100
76583
  MESH_MAX_PARALLEL_TASKS_MIN,
76101
76584
  MESH_MISSION_LIST_HISTORY_ID_LIMIT,
@@ -76155,6 +76638,7 @@ export {
76155
76638
  buildMeshActiveWorkSummary,
76156
76639
  buildMeshAsyncRefineJobs,
76157
76640
  buildMeshHostRequiredFailure,
76641
+ buildMeshJsonConfigScaffold,
76158
76642
  buildMeshLedgerReconciliationEvidence,
76159
76643
  buildMeshLedgerReplicaEvidence,
76160
76644
  buildMeshMagiActivity,
@@ -76205,6 +76689,7 @@ export {
76205
76689
  createSessionDelivery,
76206
76690
  createWorktree,
76207
76691
  daemonIdsEquivalent,
76692
+ delegatedWorkerAutoApproveSettings,
76208
76693
  deleteDirectDispatchesByTaskId,
76209
76694
  deleteMesh,
76210
76695
  deriveMeshNodeHealthFromGit,
@@ -76318,6 +76803,7 @@ export {
76318
76803
  loadMeshCoordinatorRegistry,
76319
76804
  loadMeshRefineConfig,
76320
76805
  loadMeshWorktreeBootstrapConfig,
76806
+ loadRepoMeshJsonConfig,
76321
76807
  loadRepoSettings,
76322
76808
  loadState,
76323
76809
  logCommand,
@@ -76358,6 +76844,7 @@ export {
76358
76844
  normalizeMeshWorkerResult,
76359
76845
  normalizeMessageParts,
76360
76846
  normalizeRepoIdentity,
76847
+ normalizeRepoMeshDeclarativeConfig,
76361
76848
  normalizeSessionModalFields,
76362
76849
  parsePorcelainV2Status,
76363
76850
  parseProviderSourceConfigUpdate,
@@ -76404,6 +76891,7 @@ export {
76404
76891
  resolveCurrentGlobalInstallSurface,
76405
76892
  resolveDebugRuntimeConfig,
76406
76893
  resolveDelegatedWorkerAutoApprove,
76894
+ resolveDelegatedWorkerDangerousModeAllow,
76407
76895
  resolveDeliveryDecision,
76408
76896
  resolveEffectiveMeshNodeHealth,
76409
76897
  resolveGitRepository,
@@ -76427,6 +76915,7 @@ export {
76427
76915
  runMeshWorktreeBootstrap,
76428
76916
  saveConfig,
76429
76917
  saveState,
76918
+ serializeMeshJsonConfigScaffold,
76430
76919
  serializeV2EnvelopeToWire,
76431
76920
  setDebugRuntimeConfig,
76432
76921
  setLogLevel,