@adhdev/daemon-core 0.9.82-rc.327 → 0.9.82-rc.329

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/cli-adapters/provider-cli-shared.d.ts +1 -1
  2. package/dist/git/change-impact-config.d.ts +159 -0
  3. package/dist/git/git-status.d.ts +14 -0
  4. package/dist/git/index.d.ts +2 -0
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.js +1140 -607
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +1090 -564
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/mesh/mesh-active-work.d.ts +66 -0
  11. package/dist/mesh/mesh-events-stale.d.ts +1 -1
  12. package/dist/providers/status-monitor.d.ts +7 -7
  13. package/dist/shared-types.d.ts +1 -1
  14. package/package.json +2 -2
  15. package/src/agent-stream/provider-adapter.ts +1 -1
  16. package/src/cli-adapters/provider-cli-adapter.ts +2 -2
  17. package/src/cli-adapters/provider-cli-shared.ts +1 -1
  18. package/src/commands/chat-commands.ts +1 -1
  19. package/src/commands/cli-manager.ts +1 -1
  20. package/src/commands/router.ts +46 -0
  21. package/src/git/change-impact-config.ts +354 -0
  22. package/src/git/git-status.ts +182 -16
  23. package/src/git/index.ts +16 -0
  24. package/src/index.ts +2 -2
  25. package/src/mesh/mesh-active-work.ts +154 -0
  26. package/src/mesh/mesh-events-coordinator.ts +13 -12
  27. package/src/mesh/mesh-events-stale.ts +4 -4
  28. package/src/mesh/mesh-events-utils.ts +3 -3
  29. package/src/mesh/mesh-reconcile-loop.ts +144 -1
  30. package/src/providers/acp-provider-instance.ts +6 -5
  31. package/src/providers/cli-provider-instance.ts +14 -10
  32. package/src/providers/extension-provider-instance.ts +7 -6
  33. package/src/providers/ide-provider-instance.ts +10 -6
  34. package/src/providers/read-chat-contract.ts +1 -1
  35. package/src/providers/status-monitor.d.ts +7 -7
  36. package/src/providers/status-monitor.ts +37 -22
  37. package/src/shared-types.ts +3 -0
  38. package/src/status/reporter.ts +1 -1
package/dist/index.js CHANGED
@@ -313,10 +313,10 @@ function readInjected(value) {
313
313
  }
314
314
  function getDaemonBuildInfo() {
315
315
  if (cached) return cached;
316
- const commit = readInjected(true ? "993e3922de8abe554822450e2b4c26c2c9f5f64a" : void 0) ?? "unknown";
317
- const commitShort = readInjected(true ? "993e3922" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
318
- const version = readInjected(true ? "0.9.82-rc.327" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
319
- const builtAt = readInjected(true ? "2026-06-19T12:31:25.870Z" : void 0);
316
+ const commit = readInjected(true ? "9277ba79593a0feae0e17de0204856825a199ebe" : void 0) ?? "unknown";
317
+ const commitShort = readInjected(true ? "9277ba79" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
318
+ const version = readInjected(true ? "0.9.82-rc.329" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
319
+ const builtAt = readInjected(true ? "2026-06-19T15:56:44.269Z" : void 0);
320
320
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
321
321
  return cached;
322
322
  }
@@ -327,6 +327,247 @@ var init_build_info = __esm({
327
327
  }
328
328
  });
329
329
 
330
+ // src/git/change-impact-config.ts
331
+ function isRecord(value) {
332
+ return !!value && typeof value === "object" && !Array.isArray(value);
333
+ }
334
+ function isStringArray(value) {
335
+ return Array.isArray(value) && value.every((v) => typeof v === "string" && v.length > 0);
336
+ }
337
+ function validateTarget(value, key, errors) {
338
+ if (!isRecord(value)) {
339
+ errors.push(`impactTargets.${key} must be an object`);
340
+ return void 0;
341
+ }
342
+ const { recommendedCommand } = value;
343
+ if (typeof recommendedCommand !== "string" || !recommendedCommand.length) {
344
+ errors.push(`impactTargets.${key}.recommendedCommand must be a non-empty string`);
345
+ return void 0;
346
+ }
347
+ for (const k of Object.keys(value)) {
348
+ if (k !== "recommendedCommand") errors.push(`impactTargets.${key}.${k} is not a recognized field (only recommendedCommand)`);
349
+ }
350
+ return { recommendedCommand };
351
+ }
352
+ function validateChangeImpactConfig(raw, source = "inline") {
353
+ const errors = [];
354
+ if (!isRecord(raw)) {
355
+ return { valid: false, errors: [`${source}: config must be an object`] };
356
+ }
357
+ const config = {};
358
+ if (raw.daemonRuntimePackages !== void 0) {
359
+ if (isStringArray(raw.daemonRuntimePackages)) config.daemonRuntimePackages = [...raw.daemonRuntimePackages];
360
+ else errors.push("daemonRuntimePackages must be an array of non-empty strings");
361
+ }
362
+ if (raw.webOnlyPackages !== void 0) {
363
+ if (isStringArray(raw.webOnlyPackages)) config.webOnlyPackages = [...raw.webOnlyPackages];
364
+ else errors.push("webOnlyPackages must be an array of non-empty strings");
365
+ }
366
+ if (raw.nonRuntimeRootFilePatterns !== void 0) {
367
+ if (isStringArray(raw.nonRuntimeRootFilePatterns)) config.nonRuntimeRootFilePatterns = [...raw.nonRuntimeRootFilePatterns];
368
+ else errors.push("nonRuntimeRootFilePatterns must be an array of non-empty strings");
369
+ }
370
+ if (raw.impactTargets !== void 0) {
371
+ if (!isRecord(raw.impactTargets)) {
372
+ errors.push("impactTargets must be an object");
373
+ } else {
374
+ const targets = {};
375
+ for (const key of Object.keys(raw.impactTargets)) {
376
+ if (key !== "daemon" && key !== "web" && key !== "none") {
377
+ errors.push(`impactTargets.${key} is not a recognized impact kind (daemon|web|none)`);
378
+ continue;
379
+ }
380
+ const target = validateTarget(raw.impactTargets[key], key, errors);
381
+ if (target) targets[key] = target;
382
+ }
383
+ if (Object.keys(targets).length) config.impactTargets = targets;
384
+ }
385
+ }
386
+ for (const key of Object.keys(raw)) {
387
+ if (!["daemonRuntimePackages", "webOnlyPackages", "nonRuntimeRootFilePatterns", "impactTargets"].includes(key)) {
388
+ errors.push(`unknown config key '${key}'`);
389
+ }
390
+ }
391
+ return { valid: errors.length === 0, errors, config: errors.length === 0 ? config : void 0 };
392
+ }
393
+ function parseConfigText(path42, text) {
394
+ if (/\.json$/i.test(path42)) return JSON.parse(text);
395
+ return yaml.load(text);
396
+ }
397
+ function loadChangeImpactConfig(repoRoot) {
398
+ for (const relative5 of CHANGE_IMPACT_CONFIG_LOCATIONS) {
399
+ const configPath = (0, import_path.join)(repoRoot, relative5);
400
+ if (!(0, import_fs.existsSync)(configPath)) continue;
401
+ try {
402
+ const text = (0, import_fs.readFileSync)(configPath, "utf-8");
403
+ let mtimeMs = 0;
404
+ try {
405
+ mtimeMs = (0, import_fs.statSync)(configPath).mtimeMs;
406
+ } catch {
407
+ mtimeMs = text.length;
408
+ }
409
+ const parsed = parseConfigText(configPath, text);
410
+ const validation = validateChangeImpactConfig(parsed, relative5);
411
+ if (!validation.valid) {
412
+ return {
413
+ source: relative5,
414
+ sourceType: "invalid",
415
+ path: configPath,
416
+ error: validation.errors.join("; "),
417
+ sourceKey: `invalid:${configPath}:${mtimeMs}`
418
+ };
419
+ }
420
+ return {
421
+ config: validation.config,
422
+ source: relative5,
423
+ sourceType: "repo_file",
424
+ path: configPath,
425
+ sourceKey: `file:${configPath}:${mtimeMs}`
426
+ };
427
+ } catch (error) {
428
+ return {
429
+ source: relative5,
430
+ sourceType: "invalid",
431
+ path: configPath,
432
+ error: error?.message || String(error),
433
+ sourceKey: `error:${configPath}`
434
+ };
435
+ }
436
+ }
437
+ return {
438
+ source: "unavailable",
439
+ sourceType: "unavailable",
440
+ error: `No change-impact config found. Checked: ${CHANGE_IMPACT_CONFIG_LOCATIONS.join(", ")}`,
441
+ sourceKey: "unavailable"
442
+ };
443
+ }
444
+ function globToRegExp(pattern) {
445
+ let out = "";
446
+ for (let i = 0; i < pattern.length; i++) {
447
+ const ch = pattern[i];
448
+ if (ch === "*") {
449
+ if (pattern[i + 1] === "*") {
450
+ out += ".*";
451
+ i++;
452
+ if (pattern[i + 1] === "/") i++;
453
+ } else {
454
+ out += "[^/]*";
455
+ }
456
+ } else if (ch === "?") {
457
+ out += "[^/]";
458
+ } else if (".+^${}()|[]\\".includes(ch)) {
459
+ out += "\\" + ch;
460
+ } else {
461
+ out += ch;
462
+ }
463
+ }
464
+ return new RegExp(`^${out}$`);
465
+ }
466
+ function listPackageDirs(packagesRoot) {
467
+ try {
468
+ return (0, import_fs.readdirSync)(packagesRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
469
+ } catch {
470
+ return [];
471
+ }
472
+ }
473
+ function suggestChangeImpactConfig(repoRoot) {
474
+ const notes = [];
475
+ const daemon = /* @__PURE__ */ new Set();
476
+ const web = /* @__PURE__ */ new Set();
477
+ const unclassified = [];
478
+ const roots = ["packages", (0, import_path.join)("oss", "packages")];
479
+ for (const rel of roots) {
480
+ const packagesRoot = (0, import_path.join)(repoRoot, rel);
481
+ if (!(0, import_fs.existsSync)(packagesRoot)) continue;
482
+ for (const name of listPackageDirs(packagesRoot)) {
483
+ if (/(^web[-.]|[-.]web$)/i.test(name) || /dashboard|frontend|ui$/i.test(name)) {
484
+ web.add(name);
485
+ } else {
486
+ daemon.add(name);
487
+ }
488
+ }
489
+ }
490
+ const daemonRuntimePackages = [...daemon].sort();
491
+ const webOnlyPackages = [...web].sort();
492
+ if (!daemonRuntimePackages.length && !webOnlyPackages.length) {
493
+ notes.push("No packages/ or oss/packages/ directories found \u2014 defaulting to an empty draft you should fill in by hand.");
494
+ } else {
495
+ if (daemonRuntimePackages.length) notes.push(`Classified ${daemonRuntimePackages.length} package(s) as daemon-runtime (change \u2192 rebuild/redeploy + restart).`);
496
+ if (webOnlyPackages.length) notes.push(`Classified ${webOnlyPackages.length} web-* package(s) as web-only (change \u2192 web redeploy, no daemon restart).`);
497
+ notes.push("Heuristic only: confirm each package actually matches its bucket before saving.");
498
+ }
499
+ const nonRuntimeRootFilePatterns = [
500
+ "*.md",
501
+ "docs/**",
502
+ "LICENSE",
503
+ "LICENSE.*",
504
+ ".gitignore"
505
+ ];
506
+ notes.push("nonRuntimeRootFilePatterns lists root files that demonstrably cannot change daemon runtime behavior; extend with your repo markers.");
507
+ const suggestedConfig = {
508
+ ...daemonRuntimePackages.length ? { daemonRuntimePackages } : {},
509
+ ...webOnlyPackages.length ? { webOnlyPackages } : {},
510
+ nonRuntimeRootFilePatterns,
511
+ impactTargets: {
512
+ daemon: { recommendedCommand: "rebuild + redeploy the daemon, then restart it" },
513
+ web: { recommendedCommand: "redeploy the web app (no daemon restart required)" },
514
+ none: { recommendedCommand: "no action required" }
515
+ }
516
+ };
517
+ return {
518
+ suggestedConfig,
519
+ notes,
520
+ discoveredPackages: { daemon: daemonRuntimePackages, web: webOnlyPackages, unclassified }
521
+ };
522
+ }
523
+ var import_fs, import_path, yaml, CHANGE_IMPACT_CONFIG_LOCATIONS, CHANGE_IMPACT_CONFIG_SCHEMA;
524
+ var init_change_impact_config = __esm({
525
+ "src/git/change-impact-config.ts"() {
526
+ "use strict";
527
+ import_fs = require("fs");
528
+ import_path = require("path");
529
+ yaml = __toESM(require("js-yaml"));
530
+ CHANGE_IMPACT_CONFIG_LOCATIONS = [
531
+ ".adhdev/change-impact.json",
532
+ ".adhdev/change-impact.yaml",
533
+ ".adhdev/change-impact.yml",
534
+ ".adhdev/repo-mesh-change-impact.json",
535
+ ".adhdev/repo-mesh-change-impact.yaml",
536
+ ".adhdev/repo-mesh-change-impact.yml"
537
+ ];
538
+ CHANGE_IMPACT_CONFIG_SCHEMA = {
539
+ $schema: "https://json-schema.org/draft/2020-12/schema",
540
+ title: "ADHDev Change Impact Config",
541
+ type: "object",
542
+ additionalProperties: false,
543
+ properties: {
544
+ daemonRuntimePackages: { type: "array", items: { type: "string", minLength: 1 } },
545
+ webOnlyPackages: { type: "array", items: { type: "string", minLength: 1 } },
546
+ nonRuntimeRootFilePatterns: { type: "array", items: { type: "string", minLength: 1 } },
547
+ impactTargets: {
548
+ type: "object",
549
+ additionalProperties: false,
550
+ properties: {
551
+ daemon: { $ref: "#/$defs/target" },
552
+ web: { $ref: "#/$defs/target" },
553
+ none: { $ref: "#/$defs/target" }
554
+ }
555
+ }
556
+ },
557
+ $defs: {
558
+ target: {
559
+ type: "object",
560
+ additionalProperties: false,
561
+ required: ["recommendedCommand"],
562
+ properties: {
563
+ recommendedCommand: { type: "string", minLength: 1 }
564
+ }
565
+ }
566
+ }
567
+ };
568
+ }
569
+ });
570
+
330
571
  // src/git/git-status.ts
331
572
  function isTransientGitFailure(error) {
332
573
  return error.reason === "timeout" || error.reason === "git_command_failed";
@@ -402,16 +643,34 @@ async function collectGitRepoStatus(repo, includeSubmodules, lastCheckedAt, opti
402
643
  ...daemonBuildBehind ? { daemonBuildBehind } : {}
403
644
  };
404
645
  }
405
- function isNonRuntimeRootFile(file) {
646
+ function resolveChangeImpactPolicy(config) {
647
+ const daemonRuntimePackages = new Set(
648
+ config?.daemonRuntimePackages && config.daemonRuntimePackages.length ? config.daemonRuntimePackages : DEFAULT_DAEMON_RUNTIME_PACKAGES
649
+ );
650
+ const webOnlyPackages = new Set(
651
+ config?.webOnlyPackages && config.webOnlyPackages.length ? config.webOnlyPackages : DEFAULT_WEB_ONLY_PACKAGES
652
+ );
653
+ const nonRuntimeRootFilePatterns = (config?.nonRuntimeRootFilePatterns || []).map(globToRegExp);
654
+ const impactTargets = {
655
+ daemon: config?.impactTargets?.daemon ?? DEFAULT_IMPACT_TARGETS.daemon,
656
+ web: config?.impactTargets?.web ?? DEFAULT_IMPACT_TARGETS.web,
657
+ none: config?.impactTargets?.none ?? DEFAULT_IMPACT_TARGETS.none
658
+ };
659
+ return { daemonRuntimePackages, webOnlyPackages, nonRuntimeRootFilePatterns, impactTargets };
660
+ }
661
+ function isNonRuntimeRootFile(file, policy) {
406
662
  const base = file.slice(file.lastIndexOf("/") + 1);
407
663
  if (/^\.(?:verify|marker|converge|ff-verify|patch-equiv|live-verify)\b/i.test(base)) return true;
408
664
  if (/(?:^|\/)docs\//i.test(file)) return true;
409
665
  if (/^(?:README|CHANGELOG|LICENSE|NOTICE|AUTHORS|CONTRIBUTING|CODEOWNERS)(?:\.[A-Za-z0-9]+)?$/i.test(base)) {
410
666
  return true;
411
667
  }
668
+ for (const re of policy.nonRuntimeRootFilePatterns) {
669
+ if (re.test(file)) return true;
670
+ }
412
671
  return false;
413
672
  }
414
- async function classifyDaemonBuildChange(repoPath, buildCommit, options) {
673
+ async function classifyDaemonBuildChange(repoPath, buildCommit, options, policy) {
415
674
  try {
416
675
  const diff = await runGit(repoPath, ["diff", "--name-only", `${buildCommit}..HEAD`], options);
417
676
  const files = diff.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
@@ -423,21 +682,47 @@ async function classifyDaemonBuildChange(repoPath, buildCommit, options) {
423
682
  for (const file of files) {
424
683
  const match = file.match(/(?:^|\/)packages\/([^/]+)\//);
425
684
  if (!match) {
426
- if (!isNonRuntimeRootFile(file)) sawRuntimeAmbiguousNonPackage = true;
685
+ if (!isNonRuntimeRootFile(file, policy)) sawRuntimeAmbiguousNonPackage = true;
427
686
  continue;
428
687
  }
429
688
  pkgs.add(match[1]);
430
689
  }
431
690
  const affectedPackages = [...pkgs].sort();
432
- const allBenign = !sawRuntimeAmbiguousNonPackage && affectedPackages.every((p) => WEB_ONLY_PACKAGES.has(p) && !DAEMON_RUNTIME_PACKAGES.has(p));
691
+ const allBenign = !sawRuntimeAmbiguousNonPackage && affectedPackages.every((p) => policy.webOnlyPackages.has(p) && !policy.daemonRuntimePackages.has(p));
433
692
  return { isDaemonAffecting: !allBenign, affectedPackages };
434
693
  } catch {
435
694
  return { isDaemonAffecting: true, affectedPackages: [] };
436
695
  }
437
696
  }
697
+ function resolveChangeImpactConfigForRepo(repoRoot, options) {
698
+ if (options.changeImpactConfig === null) {
699
+ return { config: null, sourceKey: "forced-default" };
700
+ }
701
+ if (options.changeImpactConfig !== void 0) {
702
+ let key = "injected";
703
+ try {
704
+ key = `injected:${JSON.stringify(options.changeImpactConfig)}`;
705
+ } catch {
706
+ }
707
+ return { config: options.changeImpactConfig, sourceKey: key };
708
+ }
709
+ if (!repoRoot) {
710
+ return { config: null, sourceKey: "no-repo-root" };
711
+ }
712
+ const loaded = loadChangeImpactConfig(repoRoot);
713
+ const cached2 = changeImpactConfigCache.get(repoRoot);
714
+ if (cached2 && cached2.sourceKey === loaded.sourceKey) {
715
+ return { config: cached2.config, sourceKey: loaded.sourceKey };
716
+ }
717
+ const config = loaded.sourceType === "repo_file" ? loaded.config ?? null : null;
718
+ changeImpactConfigCache.set(repoRoot, { sourceKey: loaded.sourceKey, config });
719
+ return { config, sourceKey: loaded.sourceKey };
720
+ }
438
721
  async function detectDaemonBuildBehind(repo, submodules, options) {
439
722
  const build = options.daemonBuildInfo ?? getDaemonBuildInfo();
440
723
  if (!build.commit || build.commit === "unknown") return void 0;
724
+ const { config, sourceKey: configKey } = resolveChangeImpactConfigForRepo(repo.repoRoot, options);
725
+ const policy = resolveChangeImpactPolicy(config);
441
726
  const scopes = [
442
727
  { scope: "root", repoPath: repo.repoRoot || repo.workspace }
443
728
  ];
@@ -451,11 +736,15 @@ async function detectDaemonBuildBehind(repo, submodules, options) {
451
736
  const head = headResult.stdout.trim();
452
737
  if (!head || head === build.commit) continue;
453
738
  await runGit(repoPath, ["merge-base", "--is-ancestor", build.commit, "HEAD"], options);
454
- const { isDaemonAffecting, affectedPackages } = await classifyDaemonBuildChange(
455
- repoPath,
456
- build.commit,
457
- options
458
- );
739
+ const evalKey = `${repoPath}\0${build.commit}\0${head}\0${configKey}`;
740
+ let evaluated = changeImpactEvalCache.get(evalKey);
741
+ if (!evaluated) {
742
+ evaluated = await classifyDaemonBuildChange(repoPath, build.commit, options, policy);
743
+ changeImpactEvalCache.set(evalKey, evaluated);
744
+ }
745
+ const { isDaemonAffecting, affectedPackages } = evaluated;
746
+ const kind = isDaemonAffecting ? "daemon" : affectedPackages.length > 0 ? "web" : "none";
747
+ const target = policy.impactTargets[kind];
459
748
  const scopeLabel = scope === "root" ? "workspace" : scope;
460
749
  const benignDetail = affectedPackages.length > 0 ? `only web packages changed (${affectedPackages.join(", ")})` : "only non-runtime files changed (markers/docs)";
461
750
  const warning = isDaemonAffecting ? `Live daemon was built from ${build.commitShort} which is behind ${scopeLabel} HEAD ${head.slice(0, 7)}. Merged code is NOT live until the daemon is rebuilt/redeployed and restarted \u2014 a local dist rebuild alone does not update a cloud daemon.` : `Live daemon was built from ${build.commitShort} which is behind ${scopeLabel} HEAD ${head.slice(0, 7)}, but ${benignDetail}. Daemon restart NOT required \u2014 redeploy the web app to reflect the change.`;
@@ -466,6 +755,8 @@ async function detectDaemonBuildBehind(repo, submodules, options) {
466
755
  scope,
467
756
  isDaemonAffecting,
468
757
  ...affectedPackages && affectedPackages.length > 0 ? { affectedPackages } : {},
758
+ recommendedAction: kind,
759
+ recommendedCommand: target.recommendedCommand,
469
760
  warning
470
761
  };
471
762
  } catch {
@@ -729,14 +1020,17 @@ async function enrichSubmoduleWorktreeStatus(repo, submodule, options) {
729
1020
  submodule.error = formatGitError(error);
730
1021
  }
731
1022
  }
732
- var lastKnownGoodStatus, DAEMON_RUNTIME_PACKAGES, WEB_ONLY_PACKAGES;
1023
+ var lastKnownGoodStatus, changeImpactEvalCache, changeImpactConfigCache, DEFAULT_DAEMON_RUNTIME_PACKAGES, DEFAULT_WEB_ONLY_PACKAGES, DEFAULT_IMPACT_TARGETS;
733
1024
  var init_git_status = __esm({
734
1025
  "src/git/git-status.ts"() {
735
1026
  "use strict";
736
1027
  init_git_executor();
737
1028
  init_build_info();
1029
+ init_change_impact_config();
738
1030
  lastKnownGoodStatus = /* @__PURE__ */ new Map();
739
- DAEMON_RUNTIME_PACKAGES = /* @__PURE__ */ new Set([
1031
+ changeImpactEvalCache = /* @__PURE__ */ new Map();
1032
+ changeImpactConfigCache = /* @__PURE__ */ new Map();
1033
+ DEFAULT_DAEMON_RUNTIME_PACKAGES = [
740
1034
  "daemon-core",
741
1035
  "daemon-standalone",
742
1036
  "session-host-core",
@@ -746,13 +1040,24 @@ var init_git_status = __esm({
746
1040
  "terminal-mux-cli",
747
1041
  "ghostty-vt-node",
748
1042
  "mcp-server"
749
- ]);
750
- WEB_ONLY_PACKAGES = /* @__PURE__ */ new Set([
1043
+ ];
1044
+ DEFAULT_WEB_ONLY_PACKAGES = [
751
1045
  "web-core",
752
1046
  "web-standalone",
753
1047
  "web-devconsole",
754
1048
  "terminal-render-web"
755
- ]);
1049
+ ];
1050
+ DEFAULT_IMPACT_TARGETS = {
1051
+ daemon: {
1052
+ recommendedCommand: "Redeploy + restart the daemon (a local dist rebuild alone does not update a cloud daemon)."
1053
+ },
1054
+ web: {
1055
+ recommendedCommand: "Redeploy the web app (no daemon restart required)."
1056
+ },
1057
+ none: {
1058
+ recommendedCommand: "No action required."
1059
+ }
1060
+ };
756
1061
  }
757
1062
  });
758
1063
 
@@ -1311,25 +1616,25 @@ function ensureMachineId(config) {
1311
1616
  }
1312
1617
  function getConfigDir() {
1313
1618
  const override = process.env.ADHDEV_CONFIG_DIR;
1314
- const dir = override && override.trim() ? override.trim() : (0, import_path.join)((0, import_os.homedir)(), ".adhdev");
1315
- if (!(0, import_fs.existsSync)(dir)) {
1316
- (0, import_fs.mkdirSync)(dir, { recursive: true });
1619
+ const dir = override && override.trim() ? override.trim() : (0, import_path2.join)((0, import_os.homedir)(), ".adhdev");
1620
+ if (!(0, import_fs2.existsSync)(dir)) {
1621
+ (0, import_fs2.mkdirSync)(dir, { recursive: true });
1317
1622
  }
1318
1623
  return dir;
1319
1624
  }
1320
1625
  function getDaemonDataDir() {
1321
- const dir = (0, import_path.join)(getConfigDir(), "daemon");
1322
- if (!(0, import_fs.existsSync)(dir)) {
1323
- (0, import_fs.mkdirSync)(dir, { recursive: true });
1626
+ const dir = (0, import_path2.join)(getConfigDir(), "daemon");
1627
+ if (!(0, import_fs2.existsSync)(dir)) {
1628
+ (0, import_fs2.mkdirSync)(dir, { recursive: true });
1324
1629
  }
1325
1630
  return dir;
1326
1631
  }
1327
1632
  function getConfigPath() {
1328
- return (0, import_path.join)(getConfigDir(), "config.json");
1633
+ return (0, import_path2.join)(getConfigDir(), "config.json");
1329
1634
  }
1330
1635
  function migrateStateToStateFile(raw) {
1331
- const statePath = (0, import_path.join)(getConfigDir(), "state.json");
1332
- if ((0, import_fs.existsSync)(statePath)) return;
1636
+ const statePath = (0, import_path2.join)(getConfigDir(), "state.json");
1637
+ if ((0, import_fs2.existsSync)(statePath)) return;
1333
1638
  const recentActivity = Array.isArray(raw.recentActivity) ? raw.recentActivity : [];
1334
1639
  const savedProviderSessions = Array.isArray(raw.savedProviderSessions) ? raw.savedProviderSessions : [];
1335
1640
  const legacySessionReads = isPlainObject(raw.recentSessionReads) ? raw.recentSessionReads : {};
@@ -1349,11 +1654,11 @@ function migrateStateToStateFile(raw) {
1349
1654
  sessionReads: mergedReads,
1350
1655
  sessionReadMarkers: cleanedMarkers
1351
1656
  };
1352
- (0, import_fs.writeFileSync)(statePath, JSON.stringify(state, null, 2), { encoding: "utf-8", mode: 384 });
1657
+ (0, import_fs2.writeFileSync)(statePath, JSON.stringify(state, null, 2), { encoding: "utf-8", mode: 384 });
1353
1658
  }
1354
1659
  function loadConfig() {
1355
1660
  const configPath = getConfigPath();
1356
- if (!(0, import_fs.existsSync)(configPath)) {
1661
+ if (!(0, import_fs2.existsSync)(configPath)) {
1357
1662
  const initialized = ensureMachineId({ ...DEFAULT_CONFIG });
1358
1663
  try {
1359
1664
  saveConfig(initialized.config);
@@ -1362,7 +1667,7 @@ function loadConfig() {
1362
1667
  return initialized.config;
1363
1668
  }
1364
1669
  try {
1365
- const raw = (0, import_fs.readFileSync)(configPath, "utf-8");
1670
+ const raw = (0, import_fs2.readFileSync)(configPath, "utf-8");
1366
1671
  const parsed = JSON.parse(raw);
1367
1672
  migrateStateToStateFile(parsed);
1368
1673
  const normalizedInput = normalizeConfig(parsed);
@@ -1384,12 +1689,12 @@ function saveConfig(config) {
1384
1689
  const configPath = getConfigPath();
1385
1690
  const dir = getConfigDir();
1386
1691
  const normalized = normalizeConfig(config);
1387
- if (!(0, import_fs.existsSync)(dir)) {
1388
- (0, import_fs.mkdirSync)(dir, { recursive: true, mode: 448 });
1692
+ if (!(0, import_fs2.existsSync)(dir)) {
1693
+ (0, import_fs2.mkdirSync)(dir, { recursive: true, mode: 448 });
1389
1694
  }
1390
- (0, import_fs.writeFileSync)(configPath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
1695
+ (0, import_fs2.writeFileSync)(configPath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
1391
1696
  try {
1392
- (0, import_fs.chmodSync)(configPath, 384);
1697
+ (0, import_fs2.chmodSync)(configPath, 384);
1393
1698
  } catch {
1394
1699
  }
1395
1700
  }
@@ -1416,13 +1721,13 @@ function isSetupComplete() {
1416
1721
  function resetConfig() {
1417
1722
  saveConfig({ ...DEFAULT_CONFIG });
1418
1723
  }
1419
- var import_os, import_path, import_fs, import_crypto, DEFAULT_CONFIG, MACHINE_ID_PREFIX;
1724
+ var import_os, import_path2, import_fs2, import_crypto, DEFAULT_CONFIG, MACHINE_ID_PREFIX;
1420
1725
  var init_config = __esm({
1421
1726
  "src/config/config.ts"() {
1422
1727
  "use strict";
1423
1728
  import_os = require("os");
1424
- import_path = require("path");
1425
- import_fs = require("fs");
1729
+ import_path2 = require("path");
1730
+ import_fs2 = require("fs");
1426
1731
  import_crypto = require("crypto");
1427
1732
  DEFAULT_CONFIG = {
1428
1733
  serverUrl: "https://api.adhf.dev",
@@ -1541,13 +1846,13 @@ __export(mesh_config_exports, {
1541
1846
  updateNode: () => updateNode
1542
1847
  });
1543
1848
  function getMeshConfigPath() {
1544
- return (0, import_path2.join)(getConfigDir(), "meshes.json");
1849
+ return (0, import_path3.join)(getConfigDir(), "meshes.json");
1545
1850
  }
1546
1851
  function loadMeshConfig() {
1547
1852
  const path42 = getMeshConfigPath();
1548
- if (!(0, import_fs2.existsSync)(path42)) return { meshes: [] };
1853
+ if (!(0, import_fs3.existsSync)(path42)) return { meshes: [] };
1549
1854
  try {
1550
- const raw = JSON.parse((0, import_fs2.readFileSync)(path42, "utf-8"));
1855
+ const raw = JSON.parse((0, import_fs3.readFileSync)(path42, "utf-8"));
1551
1856
  if (!raw || !Array.isArray(raw.meshes)) return { meshes: [] };
1552
1857
  const config = raw;
1553
1858
  const migrated = migrateLoadedMeshConfig(config);
@@ -1597,7 +1902,7 @@ function normalizeCapabilityTags(value) {
1597
1902
  }
1598
1903
  function saveMeshConfig(config) {
1599
1904
  const path42 = getMeshConfigPath();
1600
- (0, import_fs2.writeFileSync)(path42, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
1905
+ (0, import_fs3.writeFileSync)(path42, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
1601
1906
  }
1602
1907
  function normalizeRepoIdentity(remoteUrl) {
1603
1908
  let identity = remoteUrl.trim();
@@ -1957,12 +2262,12 @@ function updateNode(meshId, nodeId, opts) {
1957
2262
  saveMeshConfig(config);
1958
2263
  return node;
1959
2264
  }
1960
- var import_fs2, import_path2, import_crypto3, SESSION_CLEANUP_MODES, SPAWNED_SESSION_VISIBILITY_MODES;
2265
+ var import_fs3, import_path3, import_crypto3, SESSION_CLEANUP_MODES, SPAWNED_SESSION_VISIBILITY_MODES;
1961
2266
  var init_mesh_config = __esm({
1962
2267
  "src/config/mesh-config.ts"() {
1963
2268
  "use strict";
1964
- import_fs2 = require("fs");
1965
- import_path2 = require("path");
2269
+ import_fs3 = require("fs");
2270
+ import_path3 = require("path");
1966
2271
  import_crypto3 = require("crypto");
1967
2272
  init_config();
1968
2273
  init_repo_mesh_types();
@@ -2259,41 +2564,41 @@ function isIntentionalCleanupStopEntry(entry) {
2259
2564
  return payload.intentional === true && (payload.reason === "operator_cleanup" || payload.intentionalStopReason === "operator_cleanup" || payload.source === "mesh_cleanup_sessions" || payload.source === "mesh_remove_node");
2260
2565
  }
2261
2566
  function getLedgerDir() {
2262
- const dir = (0, import_path3.join)(getConfigDir(), LEDGER_DIR_NAME);
2263
- if (!(0, import_fs3.existsSync)(dir)) {
2264
- (0, import_fs3.mkdirSync)(dir, { recursive: true, mode: 448 });
2567
+ const dir = (0, import_path4.join)(getConfigDir(), LEDGER_DIR_NAME);
2568
+ if (!(0, import_fs4.existsSync)(dir)) {
2569
+ (0, import_fs4.mkdirSync)(dir, { recursive: true, mode: 448 });
2265
2570
  }
2266
2571
  return dir;
2267
2572
  }
2268
2573
  function getLedgerPath(meshId) {
2269
2574
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
2270
- return (0, import_path3.join)(getLedgerDir(), `${safe}.jsonl`);
2575
+ return (0, import_path4.join)(getLedgerDir(), `${safe}.jsonl`);
2271
2576
  }
2272
2577
  function getRotatedPath(meshId, index) {
2273
2578
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
2274
- return (0, import_path3.join)(getLedgerDir(), `${safe}.${index}.jsonl`);
2579
+ return (0, import_path4.join)(getLedgerDir(), `${safe}.${index}.jsonl`);
2275
2580
  }
2276
2581
  function getArchivePath(meshId) {
2277
2582
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
2278
- return (0, import_path3.join)(getLedgerDir(), `${safe}.archive.jsonl`);
2583
+ return (0, import_path4.join)(getLedgerDir(), `${safe}.archive.jsonl`);
2279
2584
  }
2280
2585
  function getRotatedArchivePath(meshId, index) {
2281
2586
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
2282
- return (0, import_path3.join)(getLedgerDir(), `${safe}.archive.${index}.jsonl`);
2587
+ return (0, import_path4.join)(getLedgerDir(), `${safe}.archive.${index}.jsonl`);
2283
2588
  }
2284
2589
  function getArchivedCountsPath(meshId) {
2285
2590
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
2286
- return (0, import_path3.join)(getLedgerDir(), `${safe}.archived-counts.json`);
2591
+ return (0, import_path4.join)(getLedgerDir(), `${safe}.archived-counts.json`);
2287
2592
  }
2288
2593
  function rotateArchiveFile(meshId, archivePath) {
2289
2594
  let index = 1;
2290
- while ((0, import_fs3.existsSync)(getRotatedArchivePath(meshId, index))) {
2595
+ while ((0, import_fs4.existsSync)(getRotatedArchivePath(meshId, index))) {
2291
2596
  index++;
2292
2597
  if (index > 5) break;
2293
2598
  }
2294
2599
  if (index > 5) index = 5;
2295
2600
  try {
2296
- (0, import_fs3.renameSync)(archivePath, getRotatedArchivePath(meshId, index));
2601
+ (0, import_fs4.renameSync)(archivePath, getRotatedArchivePath(meshId, index));
2297
2602
  } catch (e) {
2298
2603
  process.stderr.write(`[adhdev-mesh] Archive rotation failed for mesh ${meshId}: ${e?.message || e}
2299
2604
  `);
@@ -2301,9 +2606,9 @@ function rotateArchiveFile(meshId, archivePath) {
2301
2606
  }
2302
2607
  function readArchivedCounts(meshId) {
2303
2608
  const path42 = getArchivedCountsPath(meshId);
2304
- if (!(0, import_fs3.existsSync)(path42)) return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: "" };
2609
+ if (!(0, import_fs4.existsSync)(path42)) return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: "" };
2305
2610
  try {
2306
- return JSON.parse((0, import_fs3.readFileSync)(path42, "utf-8"));
2611
+ return JSON.parse((0, import_fs4.readFileSync)(path42, "utf-8"));
2307
2612
  } catch {
2308
2613
  return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: "" };
2309
2614
  }
@@ -2319,7 +2624,7 @@ function updateArchivedCounts(meshId, archived) {
2319
2624
  counts.totalArchived += archived.length;
2320
2625
  counts.lastArchivedAt = (/* @__PURE__ */ new Date()).toISOString();
2321
2626
  try {
2322
- (0, import_fs3.writeFileSync)(getArchivedCountsPath(meshId), JSON.stringify(counts), { encoding: "utf-8", mode: 384 });
2627
+ (0, import_fs4.writeFileSync)(getArchivedCountsPath(meshId), JSON.stringify(counts), { encoding: "utf-8", mode: 384 });
2323
2628
  } catch {
2324
2629
  }
2325
2630
  }
@@ -2342,7 +2647,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
2342
2647
  }
2343
2648
  function compactLedger(meshId) {
2344
2649
  const filePath = getLedgerPath(meshId);
2345
- if (!(0, import_fs3.existsSync)(filePath)) return { archivedCount: 0, retainedCount: 0 };
2650
+ if (!(0, import_fs4.existsSync)(filePath)) return { archivedCount: 0, retainedCount: 0 };
2346
2651
  const cutoff = Date.now() - ARCHIVE_TERMINAL_OLDER_THAN_MS;
2347
2652
  const entries = readLedgerEntries(meshId);
2348
2653
  const keep = [];
@@ -2357,11 +2662,11 @@ function compactLedger(meshId) {
2357
2662
  if (archive.length === 0) return { archivedCount: 0, retainedCount: keep.length };
2358
2663
  const archivePath = getArchivePath(meshId);
2359
2664
  try {
2360
- if ((0, import_fs3.existsSync)(archivePath) && (0, import_fs3.statSync)(archivePath).size > 50 * 1024 * 1024) {
2665
+ if ((0, import_fs4.existsSync)(archivePath) && (0, import_fs4.statSync)(archivePath).size > 50 * 1024 * 1024) {
2361
2666
  rotateArchiveFile(meshId, archivePath);
2362
2667
  }
2363
2668
  const archiveLines = archive.map((e) => JSON.stringify(e)).join("\n") + "\n";
2364
- (0, import_fs3.appendFileSync)(archivePath, archiveLines, { encoding: "utf-8", mode: 384 });
2669
+ (0, import_fs4.appendFileSync)(archivePath, archiveLines, { encoding: "utf-8", mode: 384 });
2365
2670
  updateArchivedCounts(meshId, archive);
2366
2671
  } catch (e) {
2367
2672
  process.stderr.write(`[adhdev-mesh] Ledger archive write failed for mesh ${meshId}: ${e?.message || e}
@@ -2370,7 +2675,7 @@ function compactLedger(meshId) {
2370
2675
  }
2371
2676
  try {
2372
2677
  const keepLines = keep.length ? keep.map((e) => JSON.stringify(e)).join("\n") + "\n" : "";
2373
- (0, import_fs3.writeFileSync)(filePath, keepLines, { encoding: "utf-8", mode: 384 });
2678
+ (0, import_fs4.writeFileSync)(filePath, keepLines, { encoding: "utf-8", mode: 384 });
2374
2679
  invalidateLedgerCache(meshId);
2375
2680
  } catch (e) {
2376
2681
  process.stderr.write(`[adhdev-mesh] Ledger compaction rewrite failed for mesh ${meshId}: ${e?.message || e}
@@ -2507,9 +2812,9 @@ function appendLedgerEntry(meshId, partial) {
2507
2812
  ...partial
2508
2813
  };
2509
2814
  const filePath = getLedgerPath(meshId);
2510
- if ((0, import_fs3.existsSync)(filePath)) {
2815
+ if ((0, import_fs4.existsSync)(filePath)) {
2511
2816
  try {
2512
- const stat2 = (0, import_fs3.statSync)(filePath);
2817
+ const stat2 = (0, import_fs4.statSync)(filePath);
2513
2818
  if (stat2.size >= MAX_FILE_SIZE_BYTES) {
2514
2819
  rotateLedgerFile(meshId, filePath);
2515
2820
  } else if (stat2.size >= COMPACT_THRESHOLD_BYTES) {
@@ -2534,7 +2839,7 @@ function appendLedgerEntry(meshId, partial) {
2534
2839
  }
2535
2840
  try {
2536
2841
  const line = JSON.stringify(entry) + "\n";
2537
- (0, import_fs3.appendFileSync)(filePath, line, { encoding: "utf-8", mode: 384 });
2842
+ (0, import_fs4.appendFileSync)(filePath, line, { encoding: "utf-8", mode: 384 });
2538
2843
  invalidateLedgerCache(meshId);
2539
2844
  meshLedgerEvents.emit("append", meshId, entry);
2540
2845
  return entry;
@@ -2593,7 +2898,7 @@ function appendRemoteLedgerEntries(meshId, entries) {
2593
2898
  }
2594
2899
  try {
2595
2900
  const lines = validEntries.map((e) => JSON.stringify(e)).join("\n") + "\n";
2596
- (0, import_fs3.appendFileSync)(ledgerPath, lines, { encoding: "utf-8", mode: 384 });
2901
+ (0, import_fs4.appendFileSync)(ledgerPath, lines, { encoding: "utf-8", mode: 384 });
2597
2902
  invalidateLedgerCache(meshId);
2598
2903
  for (const entry of validEntries) {
2599
2904
  meshLedgerEvents.emit("append", meshId, entry);
@@ -2605,10 +2910,10 @@ function appendRemoteLedgerEntries(meshId, entries) {
2605
2910
  }
2606
2911
  function readLedgerFile(meshId) {
2607
2912
  const filePath = getLedgerPath(meshId);
2608
- if (!(0, import_fs3.existsSync)(filePath)) return [];
2913
+ if (!(0, import_fs4.existsSync)(filePath)) return [];
2609
2914
  let content;
2610
2915
  try {
2611
- content = (0, import_fs3.readFileSync)(filePath, "utf-8");
2916
+ content = (0, import_fs4.readFileSync)(filePath, "utf-8");
2612
2917
  } catch {
2613
2918
  return [];
2614
2919
  }
@@ -2869,24 +3174,24 @@ function getSessionRecoveryContext(meshId, opts) {
2869
3174
  }
2870
3175
  function rotateLedgerFile(meshId, currentPath) {
2871
3176
  let index = 1;
2872
- while ((0, import_fs3.existsSync)(getRotatedPath(meshId, index))) {
3177
+ while ((0, import_fs4.existsSync)(getRotatedPath(meshId, index))) {
2873
3178
  index++;
2874
3179
  if (index > 10) break;
2875
3180
  }
2876
3181
  if (index > 10) index = 10;
2877
3182
  try {
2878
- (0, import_fs3.renameSync)(currentPath, getRotatedPath(meshId, index));
3183
+ (0, import_fs4.renameSync)(currentPath, getRotatedPath(meshId, index));
2879
3184
  } catch (e) {
2880
3185
  process.stderr.write(`[adhdev-mesh] Ledger rotation failed for mesh ${meshId}: ${e?.message || e}. File will continue to grow.
2881
3186
  `);
2882
3187
  }
2883
3188
  }
2884
- var import_fs3, import_path3, import_crypto4, import_events, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, COMPACT_THRESHOLD_BYTES, ARCHIVE_TERMINAL_OLDER_THAN_MS, RECENT_FAILURE_WINDOW_MS, ARCHIVABLE_KINDS, DEFAULT_LEDGER_SLICE_LIMIT, MAX_LEDGER_SLICE_LIMIT, meshLedgerEvents, ledgerReadCache, LEDGER_CACHE_TTL_MS, ledgerImportStoreRef, ledgerImportDone;
3189
+ var import_fs4, import_path4, import_crypto4, import_events, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, COMPACT_THRESHOLD_BYTES, ARCHIVE_TERMINAL_OLDER_THAN_MS, RECENT_FAILURE_WINDOW_MS, ARCHIVABLE_KINDS, DEFAULT_LEDGER_SLICE_LIMIT, MAX_LEDGER_SLICE_LIMIT, meshLedgerEvents, ledgerReadCache, LEDGER_CACHE_TTL_MS, ledgerImportStoreRef, ledgerImportDone;
2885
3190
  var init_mesh_ledger = __esm({
2886
3191
  "src/mesh/mesh-ledger.ts"() {
2887
3192
  "use strict";
2888
- import_fs3 = require("fs");
2889
- import_path3 = require("path");
3193
+ import_fs4 = require("fs");
3194
+ import_path4 = require("path");
2890
3195
  import_crypto4 = require("crypto");
2891
3196
  init_config();
2892
3197
  import_events = require("events");
@@ -3554,32 +3859,32 @@ function safeMeshId(meshId) {
3554
3859
  return meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
3555
3860
  }
3556
3861
  function legacyQueuePath(meshId) {
3557
- return (0, import_path4.join)(getLedgerDir(), `${safeMeshId(meshId)}.queue.json`);
3862
+ return (0, import_path5.join)(getLedgerDir(), `${safeMeshId(meshId)}.queue.json`);
3558
3863
  }
3559
3864
  function meshRuntimeStorePath() {
3560
3865
  const dir = getLedgerDir();
3561
- const nextPath = (0, import_path4.join)(dir, "mesh-runtime.db");
3562
- if ((0, import_fs4.existsSync)(nextPath)) return nextPath;
3563
- const legacyPath = (0, import_path4.join)(dir, "beads.db");
3564
- if (!(0, import_fs4.existsSync)(legacyPath)) return nextPath;
3866
+ const nextPath = (0, import_path5.join)(dir, "mesh-runtime.db");
3867
+ if ((0, import_fs5.existsSync)(nextPath)) return nextPath;
3868
+ const legacyPath = (0, import_path5.join)(dir, "beads.db");
3869
+ if (!(0, import_fs5.existsSync)(legacyPath)) return nextPath;
3565
3870
  try {
3566
- (0, import_fs4.renameSync)(legacyPath, nextPath);
3871
+ (0, import_fs5.renameSync)(legacyPath, nextPath);
3567
3872
  for (const suffix of ["-wal", "-shm"]) {
3568
3873
  const legacyCompanion = `${legacyPath}${suffix}`;
3569
- if ((0, import_fs4.existsSync)(legacyCompanion)) {
3570
- (0, import_fs4.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
3874
+ if ((0, import_fs5.existsSync)(legacyCompanion)) {
3875
+ (0, import_fs5.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
3571
3876
  }
3572
3877
  }
3573
3878
  } catch {
3574
3879
  }
3575
3880
  return nextPath;
3576
3881
  }
3577
- var import_fs4, import_path4, import_module, import_meta, DatabaseCtor, MeshRuntimeStore;
3882
+ var import_fs5, import_path5, import_module, import_meta, DatabaseCtor, MeshRuntimeStore;
3578
3883
  var init_mesh_runtime_store = __esm({
3579
3884
  "src/mesh/mesh-runtime-store.ts"() {
3580
3885
  "use strict";
3581
- import_fs4 = require("fs");
3582
- import_path4 = require("path");
3886
+ import_fs5 = require("fs");
3887
+ import_path5 = require("path");
3583
3888
  import_module = require("module");
3584
3889
  init_mesh_ledger();
3585
3890
  init_mesh_work_queue();
@@ -3595,8 +3900,8 @@ var init_mesh_runtime_store = __esm({
3595
3900
  static WAL_MAX_BYTES = 50 * 1024 * 1024;
3596
3901
  // 50 MB
3597
3902
  constructor(dbPath) {
3598
- const dir = (0, import_path4.dirname)(dbPath);
3599
- if (!(0, import_fs4.existsSync)(dir)) (0, import_fs4.mkdirSync)(dir, { recursive: true });
3903
+ const dir = (0, import_path5.dirname)(dbPath);
3904
+ if (!(0, import_fs5.existsSync)(dir)) (0, import_fs5.mkdirSync)(dir, { recursive: true });
3600
3905
  this.dbPath = dbPath;
3601
3906
  this.db = new (loadDatabaseCtor())(dbPath);
3602
3907
  this.db.pragma("journal_mode = WAL");
@@ -3851,8 +4156,8 @@ var init_mesh_runtime_store = __esm({
3851
4156
  this.walWriteCounter = 0;
3852
4157
  try {
3853
4158
  const walPath = `${this.dbPath}-wal`;
3854
- if (!(0, import_fs4.existsSync)(walPath)) return;
3855
- const size = (0, import_fs4.statSync)(walPath).size;
4159
+ if (!(0, import_fs5.existsSync)(walPath)) return;
4160
+ const size = (0, import_fs5.statSync)(walPath).size;
3856
4161
  if (size < _MeshRuntimeStore.WAL_MAX_BYTES) return;
3857
4162
  process.stderr.write(
3858
4163
  `[adhdev-mesh] WAL file ${Math.round(size / 1024 / 1024)}MB exceeds threshold; forcing checkpoint
@@ -3868,9 +4173,9 @@ var init_mesh_runtime_store = __esm({
3868
4173
  const count = this.db.prepare("SELECT COUNT(*) AS count FROM mesh_queue WHERE mesh_id = ?").get(meshId);
3869
4174
  if (count.count > 0) return;
3870
4175
  const path42 = legacyQueuePath(meshId);
3871
- if (!(0, import_fs4.existsSync)(path42)) return;
4176
+ if (!(0, import_fs5.existsSync)(path42)) return;
3872
4177
  try {
3873
- const entries = JSON.parse((0, import_fs4.readFileSync)(path42, "utf-8"));
4178
+ const entries = JSON.parse((0, import_fs5.readFileSync)(path42, "utf-8"));
3874
4179
  if (!Array.isArray(entries)) return;
3875
4180
  const insert = this.db.prepare(`
3876
4181
  INSERT OR REPLACE INTO mesh_queue (
@@ -6976,6 +7281,410 @@ var init_dist = __esm({
6976
7281
  }
6977
7282
  });
6978
7283
 
7284
+ // src/mesh/mesh-active-work.ts
7285
+ function readString6(value) {
7286
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
7287
+ }
7288
+ function summarizeMessage(message) {
7289
+ const oneLine2 = message.replace(/\s+/g, " ").trim();
7290
+ const title = oneLine2.length > 96 ? `${oneLine2.slice(0, 93)}...` : oneLine2;
7291
+ return { title: title || "(untitled task)", summary: oneLine2 };
7292
+ }
7293
+ function elapsedSince(value, now) {
7294
+ const started = value ? new Date(value).getTime() : Number.NaN;
7295
+ return Number.isFinite(started) ? Math.max(0, now - started) : 0;
7296
+ }
7297
+ function sessionStatusFromNodes(nodes, nodeId, sessionId) {
7298
+ if (!Array.isArray(nodes)) return {};
7299
+ if (!nodeId) return { staleReason: "direct task has no node id" };
7300
+ const node = nodes.find((item) => meshNodeIdMatches(item, nodeId));
7301
+ if (!node) return { staleReason: "direct task node is no longer in the live mesh" };
7302
+ if (!sessionId) return {};
7303
+ const candidates = [];
7304
+ for (const value of [
7305
+ node.sessions,
7306
+ node.activeSessions,
7307
+ node.active_sessions,
7308
+ node.activeSessionDetails,
7309
+ node.active_session_details,
7310
+ node.sessionDetails,
7311
+ node.session_details,
7312
+ node.lastProbe?.sessions,
7313
+ node.last_probe?.sessions,
7314
+ node.lastProbe?.status?.sessions,
7315
+ node.last_probe?.status?.sessions
7316
+ ]) {
7317
+ if (Array.isArray(value)) candidates.push(...value);
7318
+ }
7319
+ for (const value of [node.activeSession, node.active_session, node.currentSession, node.current_session, node.runtimeSession, node.runtime_session, node.session]) {
7320
+ if (value && typeof value === "object") candidates.push(value);
7321
+ }
7322
+ const session = candidates.find((item) => {
7323
+ if (typeof item === "string") return item === sessionId;
7324
+ const id = readString6(item?.id) || readString6(item?.sessionId) || readString6(item?.session_id) || readString6(item?.runtimeSessionId) || readString6(item?.instanceId);
7325
+ return id === sessionId;
7326
+ });
7327
+ if (!session) return { staleReason: "direct task session is not present in live session records" };
7328
+ if (typeof session === "string") return {};
7329
+ const raw = `${readString6(session.status) || ""} ${readString6(session.lifecycle) || ""} ${readString6(session.state) || ""} ${readString6(session.activeChat?.status) || ""}`.toLowerCase();
7330
+ if (raw.includes("approval")) return { status: "awaiting_approval" };
7331
+ if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return { status: "generating" };
7332
+ if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return { status: "failed" };
7333
+ if (raw.includes("idle") || raw.includes("waiting_input") || raw.includes("ready")) return { status: "idle" };
7334
+ return {};
7335
+ }
7336
+ function isDirectDispatch(entry) {
7337
+ if (entry.kind !== "task_dispatched") return false;
7338
+ const payload = entry.payload || {};
7339
+ if (payload.source === "direct") return true;
7340
+ const via = readString6(payload.via);
7341
+ return Boolean(via && DIRECT_DISPATCH_VIA.has(via) && payload.source !== "queue");
7342
+ }
7343
+ function directDispatchTaskId(entry) {
7344
+ return readString6(entry.payload?.taskId) || entry.id;
7345
+ }
7346
+ function terminalMatchesDispatch(terminal, dispatch, taskId) {
7347
+ const terminalTaskId = readString6(terminal.payload?.taskId);
7348
+ if (terminalTaskId && terminalTaskId === taskId) return true;
7349
+ if (terminalTaskId && terminalTaskId !== taskId) return false;
7350
+ if (dispatch.sessionId && terminal.sessionId === dispatch.sessionId) return true;
7351
+ return Boolean(dispatch.nodeId && terminal.nodeId === dispatch.nodeId && !dispatch.sessionId);
7352
+ }
7353
+ function statusFromTerminal(entry) {
7354
+ if (entry.kind === "task_approval_needed") return "awaiting_approval";
7355
+ if (entry.kind === "task_completed") return "idle";
7356
+ return "failed";
7357
+ }
7358
+ function buildMeshActiveWorkSummary(activeWork) {
7359
+ const statusCounts = {
7360
+ pending: 0,
7361
+ assigned: 0,
7362
+ generating: 0,
7363
+ idle: 0,
7364
+ failed: 0,
7365
+ awaiting_approval: 0
7366
+ };
7367
+ const sourceCounts = { queue: 0, direct: 0 };
7368
+ for (const item of activeWork) {
7369
+ sourceCounts[item.source] += 1;
7370
+ statusCounts[item.status] += 1;
7371
+ }
7372
+ const staleDirectCount = activeWork.filter((item) => item.source === "direct" && item.staleReason).length;
7373
+ const staleDirectUnacknowledgedCount = activeWork.filter((item) => item.source === "direct" && item.staleDispatchUnacknowledged).length;
7374
+ return {
7375
+ totalActiveCount: activeWork.length,
7376
+ queueActiveCount: sourceCounts.queue,
7377
+ directActiveCount: sourceCounts.direct,
7378
+ awaitingApprovalCount: statusCounts.awaiting_approval,
7379
+ generatingCount: statusCounts.generating,
7380
+ failedCount: statusCounts.failed,
7381
+ idleCount: statusCounts.idle,
7382
+ sourceCounts,
7383
+ statusCounts,
7384
+ staleDirectCount,
7385
+ ...staleDirectUnacknowledgedCount > 0 ? { staleDirectUnacknowledgedCount } : {},
7386
+ ...staleDirectCount > 0 ? { staleDirectNote: "Stale direct records are orphaned ledger entries whose node/session no longer exists. They are historical recovery evidence only \u2014 not active or unresolved work. The queue (source: queue) is authoritative for pending/assigned tasks." } : {}
7387
+ };
7388
+ }
7389
+ function buildMeshActiveWork(opts) {
7390
+ const now = opts.now ?? Date.now();
7391
+ const records = [];
7392
+ const staleDirectWork = [];
7393
+ const terminalDirectWork = [];
7394
+ for (const task of opts.queue || []) {
7395
+ if (task.status !== "pending" && task.status !== "assigned") continue;
7396
+ const { title, summary: summary2 } = summarizeMessage(task.message || "");
7397
+ records.push({
7398
+ taskId: task.id,
7399
+ source: "queue",
7400
+ status: task.status,
7401
+ nodeId: task.assignedNodeId || task.targetNodeId,
7402
+ sessionId: task.assignedSessionId || task.targetSessionId,
7403
+ taskTitle: title,
7404
+ taskSummary: summary2,
7405
+ message: task.message,
7406
+ taskMode: task.taskMode,
7407
+ createdAt: task.createdAt,
7408
+ updatedAt: task.updatedAt,
7409
+ dispatchedAt: task.dispatchTimestamp,
7410
+ elapsedMs: elapsedSince(task.dispatchTimestamp || task.createdAt, now)
7411
+ });
7412
+ }
7413
+ if (opts.directDispatches !== void 0) {
7414
+ const dbTaskIds = new Set(opts.directDispatches.map((d) => d.taskId));
7415
+ for (const dispatch of opts.directDispatches) {
7416
+ const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId ?? void 0, dispatch.sessionId ?? void 0);
7417
+ const dbStatus = dispatch.status;
7418
+ const isTerminal = dbStatus === "completed" || dbStatus === "failed" || dbStatus === "stale";
7419
+ const status = isTerminal ? dbStatus === "completed" ? "idle" : "failed" : live.status || (dbStatus === "acked" ? "generating" : "assigned");
7420
+ const isNoTransition = !isTerminal && !live.status;
7421
+ const isIdleUnacknowledged = status === "idle" && !isTerminal;
7422
+ const ledgerOnlyStaleReason = !isTerminal && (isIdleUnacknowledged || isNoTransition || dispatch.dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
7423
+ const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
7424
+ const { title, summary: summary2 } = summarizeMessage(dispatch.message || "");
7425
+ const record = {
7426
+ taskId: dispatch.taskId,
7427
+ source: "direct",
7428
+ status,
7429
+ nodeId: dispatch.nodeId ?? void 0,
7430
+ sessionId: dispatch.sessionId ?? void 0,
7431
+ providerType: dispatch.providerType ?? void 0,
7432
+ taskTitle: title,
7433
+ taskSummary: summary2,
7434
+ message: dispatch.message,
7435
+ taskMode: dispatch.taskMode ?? void 0,
7436
+ createdAt: dispatch.dispatchedAt,
7437
+ updatedAt: dispatch.updatedAt,
7438
+ dispatchedAt: dispatch.dispatchedAt,
7439
+ elapsedMs: elapsedSince(dispatch.dispatchedAt, now),
7440
+ terminal: isTerminal,
7441
+ terminalKind: isTerminal ? dbStatus === "completed" ? "task_completed" : "task_failed" : void 0,
7442
+ terminalAt: isTerminal ? dispatch.updatedAt : void 0,
7443
+ staleReason: live.staleReason || ledgerOnlyStaleReason,
7444
+ ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
7445
+ };
7446
+ if (isTerminal) {
7447
+ terminalDirectWork.push(record);
7448
+ if (opts.includeTerminalDirect !== true) continue;
7449
+ }
7450
+ if ((live.staleReason || ledgerOnlyStaleReason) && !isTerminal) {
7451
+ staleDirectWork.push(record);
7452
+ continue;
7453
+ }
7454
+ records.push(record);
7455
+ }
7456
+ const ledgerEntries = (opts.ledgerEntries || []).slice().sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
7457
+ const terminals = ledgerEntries.filter((entry) => TERMINAL_LEDGER_KINDS.has(entry.kind) || entry.kind === "task_approval_needed");
7458
+ for (const dispatch of ledgerEntries.filter(isDirectDispatch)) {
7459
+ const taskId = directDispatchTaskId(dispatch);
7460
+ if (dbTaskIds.has(taskId)) continue;
7461
+ const terminal = terminals.filter((entry) => new Date(entry.timestamp).getTime() >= new Date(dispatch.timestamp).getTime()).find((entry) => terminalMatchesDispatch(entry, dispatch, taskId));
7462
+ const terminalStatus = terminal ? statusFromTerminal(terminal) : void 0;
7463
+ const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
7464
+ const status = terminalStatus || live.status || "assigned";
7465
+ const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
7466
+ const dispatchedToIdleSession = dispatch.payload?.dispatchedToIdleSession === true;
7467
+ const isNoTransition = !terminalStatus && !live.status;
7468
+ const isIdleUnacknowledged = status === "idle";
7469
+ const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
7470
+ const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
7471
+ const { title, summary: summary2 } = summarizeMessage(message);
7472
+ const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
7473
+ const record = {
7474
+ taskId,
7475
+ source: "direct",
7476
+ status,
7477
+ nodeId: dispatch.nodeId,
7478
+ sessionId: dispatch.sessionId,
7479
+ providerType: dispatch.providerType || readString6(dispatch.payload?.providerType),
7480
+ taskTitle: readString6(dispatch.payload?.taskTitle) || title,
7481
+ taskSummary: readString6(dispatch.payload?.taskSummary) || summary2,
7482
+ message,
7483
+ taskMode: readString6(dispatch.payload?.taskMode),
7484
+ createdAt: dispatch.timestamp,
7485
+ updatedAt: terminal?.timestamp || dispatch.timestamp,
7486
+ dispatchedAt: dispatch.timestamp,
7487
+ elapsedMs: elapsedSince(dispatch.timestamp, now),
7488
+ terminal: terminalRow,
7489
+ terminalKind: terminal?.kind,
7490
+ terminalAt: terminal?.timestamp,
7491
+ staleReason: live.staleReason || ledgerOnlyStaleReason,
7492
+ ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
7493
+ };
7494
+ if (terminalRow) {
7495
+ terminalDirectWork.push(record);
7496
+ if (opts.includeTerminalDirect !== true) continue;
7497
+ }
7498
+ if ((live.staleReason || ledgerOnlyStaleReason) && !terminalRow) {
7499
+ staleDirectWork.push(record);
7500
+ continue;
7501
+ }
7502
+ records.push(record);
7503
+ }
7504
+ } else {
7505
+ const ledgerEntries = (opts.ledgerEntries || []).slice().sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
7506
+ const terminals = ledgerEntries.filter((entry) => TERMINAL_LEDGER_KINDS.has(entry.kind) || entry.kind === "task_approval_needed");
7507
+ for (const dispatch of ledgerEntries.filter(isDirectDispatch)) {
7508
+ const taskId = directDispatchTaskId(dispatch);
7509
+ const terminal = terminals.filter((entry) => new Date(entry.timestamp).getTime() >= new Date(dispatch.timestamp).getTime()).find((entry) => terminalMatchesDispatch(entry, dispatch, taskId));
7510
+ const terminalStatus = terminal ? statusFromTerminal(terminal) : void 0;
7511
+ const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
7512
+ const status = terminalStatus || live.status || "assigned";
7513
+ const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
7514
+ const dispatchedToIdleSession = dispatch.payload?.dispatchedToIdleSession === true;
7515
+ const isNoTransition = !terminalStatus && !live.status;
7516
+ const isIdleUnacknowledged = status === "idle";
7517
+ const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
7518
+ const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
7519
+ const { title, summary: summary2 } = summarizeMessage(message);
7520
+ const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
7521
+ const record = {
7522
+ taskId,
7523
+ source: "direct",
7524
+ status,
7525
+ nodeId: dispatch.nodeId,
7526
+ sessionId: dispatch.sessionId,
7527
+ providerType: dispatch.providerType || readString6(dispatch.payload?.providerType),
7528
+ taskTitle: readString6(dispatch.payload?.taskTitle) || title,
7529
+ taskSummary: readString6(dispatch.payload?.taskSummary) || summary2,
7530
+ message,
7531
+ taskMode: readString6(dispatch.payload?.taskMode),
7532
+ createdAt: dispatch.timestamp,
7533
+ updatedAt: terminal?.timestamp || dispatch.timestamp,
7534
+ dispatchedAt: dispatch.timestamp,
7535
+ elapsedMs: elapsedSince(dispatch.timestamp, now),
7536
+ terminal: terminalRow,
7537
+ terminalKind: terminal?.kind,
7538
+ terminalAt: terminal?.timestamp,
7539
+ staleReason: live.staleReason || ledgerOnlyStaleReason,
7540
+ ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
7541
+ };
7542
+ if (terminalRow) {
7543
+ terminalDirectWork.push(record);
7544
+ if (opts.includeTerminalDirect !== true) continue;
7545
+ }
7546
+ if ((live.staleReason || ledgerOnlyStaleReason) && !terminalRow) {
7547
+ staleDirectWork.push(record);
7548
+ continue;
7549
+ }
7550
+ records.push(record);
7551
+ }
7552
+ }
7553
+ records.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
7554
+ staleDirectWork.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
7555
+ terminalDirectWork.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
7556
+ const summary = buildMeshActiveWorkSummary(records);
7557
+ summary.staleDirectCount = staleDirectWork.length;
7558
+ const unacknowledgedCount = staleDirectWork.filter((r) => r.staleDispatchUnacknowledged).length;
7559
+ if (unacknowledgedCount > 0) {
7560
+ summary.staleDirectUnacknowledgedCount = unacknowledgedCount;
7561
+ }
7562
+ const staleDirectWorkNote = staleDirectWork.length > 0 ? unacknowledgedCount > 0 && unacknowledgedCount === staleDirectWork.length ? `${unacknowledgedCount} direct dispatch(es) were not acknowledged by the target session \u2014 the session received the agent_command but never transitioned to generating. This is a fresh dispatch failure, not historical noise. Recovery: launch a fresh session on the same node and retry the task, or use mesh_enqueue_task for queue-based assignment.` : unacknowledgedCount > 0 ? `${unacknowledgedCount} of ${staleDirectWork.length} stale direct record(s) are fresh unacknowledged dispatch failures (session still live but never transitioned to generating); the rest are orphaned historical entries whose node/session no longer exists. Fresh unacknowledged dispatches need recovery: launch a fresh session and retry. Orphaned entries are historical evidence only \u2014 not active or unresolved work.` : "These are orphaned ledger entries whose original node or session no longer exists in the live mesh. They are historical/recovery evidence only \u2014 not active or unresolved work. Do not treat staleDirectCount as a status mismatch; use the queue (source: queue) as authoritative for pending/assigned tasks." : void 0;
7563
+ if (staleDirectWorkNote) {
7564
+ summary.staleDirectNote = staleDirectWorkNote;
7565
+ }
7566
+ return { activeWork: records, staleDirectWork, staleDirectWorkNote, terminalDirectWork, summary };
7567
+ }
7568
+ function classifyStaleDirectForPrune(record, opts = {}) {
7569
+ if (record.staleDispatchUnacknowledged === true) return "preserve_unacknowledged";
7570
+ if (record.terminal === true) return opts.includeTerminal ? "prunable_terminal" : "preserve_active";
7571
+ if (record.staleReason && PRUNABLE_ORPHAN_STALE_REASONS.has(record.staleReason)) return "prunable_orphan";
7572
+ return "preserve_active";
7573
+ }
7574
+ function pruneStaleDirectDispatches(opts) {
7575
+ const now = opts.now ?? Date.now();
7576
+ const includeTerminal = opts.includeTerminal === true;
7577
+ const execute = opts.execute === true;
7578
+ const minAgeMs = Math.max(0, opts.minAgeMs ?? 0);
7579
+ const activeWorkEvidence = buildMeshActiveWork({
7580
+ meshId: opts.meshId,
7581
+ queue: opts.queue,
7582
+ ledgerEntries: opts.ledgerEntries,
7583
+ directDispatches: opts.directDispatches,
7584
+ nodes: opts.nodes,
7585
+ now,
7586
+ includeTerminalDirect: includeTerminal
7587
+ });
7588
+ const candidates = [
7589
+ ...activeWorkEvidence.staleDirectWork,
7590
+ ...includeTerminal ? activeWorkEvidence.terminalDirectWork : []
7591
+ ];
7592
+ const storeTaskIds = new Set(opts.directDispatches.map((d) => d.taskId));
7593
+ const prunable = [];
7594
+ const skippedTooYoung = [];
7595
+ const preservedUnacknowledged = [];
7596
+ const preservedLedgerOnly = [];
7597
+ const preservedNotOrphan = [];
7598
+ for (const record of candidates) {
7599
+ const classification = classifyStaleDirectForPrune(record, { includeTerminal });
7600
+ if (classification === "preserve_unacknowledged") {
7601
+ preservedUnacknowledged.push(record);
7602
+ continue;
7603
+ }
7604
+ if (classification === "preserve_active") {
7605
+ preservedNotOrphan.push(record);
7606
+ continue;
7607
+ }
7608
+ if (!storeTaskIds.has(record.taskId)) {
7609
+ preservedLedgerOnly.push(record);
7610
+ continue;
7611
+ }
7612
+ if (minAgeMs > 0) {
7613
+ const ageRef = record.dispatchedAt || record.createdAt;
7614
+ const ageMs = elapsedSince(ageRef, now);
7615
+ if (ageMs < minAgeMs) {
7616
+ skippedTooYoung.push(record);
7617
+ continue;
7618
+ }
7619
+ }
7620
+ prunable.push(record);
7621
+ }
7622
+ let prunedCount = 0;
7623
+ if (execute && prunable.length) {
7624
+ prunedCount = deleteDirectDispatchesByTaskId(opts.meshId, prunable.map((r) => r.taskId));
7625
+ appendLedgerEntry(opts.meshId, {
7626
+ kind: "direct_dispatch_pruned",
7627
+ payload: {
7628
+ source: opts.source || "prune_stale_direct",
7629
+ prunedCount,
7630
+ taskIds: prunable.map((r) => r.taskId),
7631
+ reasons: Array.from(new Set(prunable.map((r) => r.staleReason || (r.terminal ? "terminal" : "unknown"))))
7632
+ }
7633
+ });
7634
+ }
7635
+ return {
7636
+ mode: execute ? "execute" : "dry_run",
7637
+ includeTerminal,
7638
+ candidateCount: candidates.length,
7639
+ prunable,
7640
+ prunedCount,
7641
+ skippedTooYoung,
7642
+ preservedUnacknowledged,
7643
+ preservedLedgerOnly,
7644
+ preservedNotOrphan
7645
+ };
7646
+ }
7647
+ function buildCompactStaleDirectWorkSummary(staleDirectWork, opts = {}) {
7648
+ const sampleLimit = Math.max(0, Math.min(10, Math.floor(opts.sampleLimit ?? 3)));
7649
+ const reasonCounts = {};
7650
+ for (const entry of staleDirectWork) {
7651
+ const reason = entry.staleReason || "unknown";
7652
+ reasonCounts[reason] = (reasonCounts[reason] || 0) + 1;
7653
+ }
7654
+ return {
7655
+ count: staleDirectWork.length,
7656
+ sampleLimit,
7657
+ sample: staleDirectWork.slice(0, sampleLimit).map((entry) => ({
7658
+ taskId: entry.taskId,
7659
+ status: entry.status,
7660
+ nodeId: entry.nodeId,
7661
+ sessionId: entry.sessionId,
7662
+ taskTitle: entry.taskTitle,
7663
+ createdAt: entry.createdAt,
7664
+ staleReason: entry.staleReason
7665
+ })),
7666
+ reasonCounts,
7667
+ detailHint: opts.detailHint || "Stale direct records are historical recovery evidence only. Use mesh_task_history for full ledger details, or request includeStaleDirectWorkDetails when supported by the caller.",
7668
+ ...opts.note ? { note: opts.note } : {}
7669
+ };
7670
+ }
7671
+ var DIRECT_DISPATCH_VIA, TERMINAL_LEDGER_KINDS, PRUNABLE_ORPHAN_STALE_REASONS;
7672
+ var init_mesh_active_work = __esm({
7673
+ "src/mesh/mesh-active-work.ts"() {
7674
+ "use strict";
7675
+ init_mesh_ledger();
7676
+ init_mesh_work_queue();
7677
+ init_dist();
7678
+ DIRECT_DISPATCH_VIA = /* @__PURE__ */ new Set(["p2p_direct", "local_direct", "mesh_send_task"]);
7679
+ TERMINAL_LEDGER_KINDS = /* @__PURE__ */ new Set(["task_completed", "task_failed", "task_stalled"]);
7680
+ PRUNABLE_ORPHAN_STALE_REASONS = /* @__PURE__ */ new Set([
7681
+ "direct task node is no longer in the live mesh",
7682
+ "direct task session is not present in live session records",
7683
+ "direct task has no node id"
7684
+ ]);
7685
+ }
7686
+ });
7687
+
6979
7688
  // src/mesh/mesh-events-utils.ts
6980
7689
  function readNonEmptyString2(value) {
6981
7690
  return typeof value === "string" && value.trim() ? value.trim() : "";
@@ -7030,8 +7739,8 @@ function formatCompletionMetadata(event) {
7030
7739
  function buildMeshSystemMessage(args) {
7031
7740
  const metadata = formatCompletionMetadata(args.metadataEvent);
7032
7741
  if (args.event === "agent:generating_completed") {
7033
- if (args.metadataEvent.source === "long_generating_reconciliation") {
7034
- return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The long-generating monitor reconciled the terminal handoff and marked the session complete; wait for the queued completion event/status refresh before doing any manual transcript check.`;
7742
+ if (args.metadataEvent.source === "no_progress_reconciliation") {
7743
+ return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The no-progress monitor reconciled the terminal handoff and marked the session complete; wait for the queued completion event/status refresh before doing any manual transcript check.`;
7035
7744
  }
7036
7745
  const reviewNote = args.metadataEvent.reviewRecommended === true ? " Completion evidence is insufficient \u2014 verify via git status or provider_session_id before assuming the task is done. Use mesh_read_chat once if needed, but do not poll repeatedly." : " Use mesh_read_chat once to review its final progress, but do not poll repeatedly.";
7037
7746
  return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path;${reviewNote}`;
@@ -7070,7 +7779,7 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
7070
7779
  }
7071
7780
  return `[System] ${args.nodeLabel} has stopped${metadata}. Use mesh_read_chat once if you need to inspect its last output.`;
7072
7781
  }
7073
- if (args.event === "monitor:long_generating") {
7782
+ if (args.event === "monitor:no_progress") {
7074
7783
  return `[System] ${args.nodeLabel} is still reported as generating after a long interval${metadata}. Wait for pendingCoordinatorEvents or a completion/status event; if the user explicitly asks for status, make one bounded status check and then wait again.`;
7075
7784
  }
7076
7785
  if (args.event === "worktree_bootstrap_complete") {
@@ -7209,9 +7918,9 @@ function getPendingEventsPath(meshId, coordinatorDaemonId) {
7209
7918
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
7210
7919
  if (coordinatorDaemonId) {
7211
7920
  const safeDaemon = coordinatorDaemonId.replace(/[^a-zA-Z0-9_-]/g, "_");
7212
- return (0, import_path8.join)(getLedgerDir(), `${safe}-${safeDaemon}.pending-events.jsonl`);
7921
+ return (0, import_path9.join)(getLedgerDir(), `${safe}-${safeDaemon}.pending-events.jsonl`);
7213
7922
  }
7214
- return (0, import_path8.join)(getLedgerDir(), `${safe}.pending-events.jsonl`);
7923
+ return (0, import_path9.join)(getLedgerDir(), `${safe}.pending-events.jsonl`);
7215
7924
  }
7216
7925
  function readPendingMeshCoordinatorEventsFromDisk(meshId, coordinatorDaemonId) {
7217
7926
  if (!meshId) return [];
@@ -7220,9 +7929,9 @@ function readPendingMeshCoordinatorEventsFromDisk(meshId, coordinatorDaemonId) {
7220
7929
  const paths = primaryDaemonId ? [getPendingEventsPath(meshId, primaryDaemonId), getPendingEventsPath(meshId)] : [getPendingEventsPath(meshId)];
7221
7930
  const events = [];
7222
7931
  for (const path42 of paths) {
7223
- if (!(0, import_fs8.existsSync)(path42)) continue;
7932
+ if (!(0, import_fs9.existsSync)(path42)) continue;
7224
7933
  try {
7225
- const raw = (0, import_fs8.readFileSync)(path42, "utf-8");
7934
+ const raw = (0, import_fs9.readFileSync)(path42, "utf-8");
7226
7935
  const parsed = raw.split("\n").filter(Boolean).flatMap((line) => {
7227
7936
  try {
7228
7937
  return [JSON.parse(line)];
@@ -7297,11 +8006,11 @@ function reconcilePendingMeshCoordinatorEvents(meshId, events) {
7297
8006
  }
7298
8007
  function trimPendingEventsIfNeeded(path42) {
7299
8008
  try {
7300
- if (!(0, import_fs8.existsSync)(path42)) return;
7301
- if ((0, import_fs8.statSync)(path42).size <= MAX_PENDING_EVENTS_BYTES) return;
7302
- const lines = (0, import_fs8.readFileSync)(path42, "utf-8").split("\n").filter(Boolean);
8009
+ if (!(0, import_fs9.existsSync)(path42)) return;
8010
+ if ((0, import_fs9.statSync)(path42).size <= MAX_PENDING_EVENTS_BYTES) return;
8011
+ const lines = (0, import_fs9.readFileSync)(path42, "utf-8").split("\n").filter(Boolean);
7303
8012
  if (lines.length <= MAX_PENDING_EVENTS_KEEP) return;
7304
- (0, import_fs8.writeFileSync)(path42, lines.slice(-MAX_PENDING_EVENTS_KEEP).join("\n") + "\n", "utf-8");
8013
+ (0, import_fs9.writeFileSync)(path42, lines.slice(-MAX_PENDING_EVENTS_KEEP).join("\n") + "\n", "utf-8");
7305
8014
  } catch {
7306
8015
  }
7307
8016
  }
@@ -7330,7 +8039,7 @@ function queuePendingMeshCoordinatorEvent(event) {
7330
8039
  }
7331
8040
  const path42 = getPendingEventsPath(event.meshId, event.targetCoordinatorDaemonId);
7332
8041
  trimPendingEventsIfNeeded(path42);
7333
- (0, import_fs8.appendFileSync)(path42, JSON.stringify(event) + "\n", "utf-8");
8042
+ (0, import_fs9.appendFileSync)(path42, JSON.stringify(event) + "\n", "utf-8");
7334
8043
  return true;
7335
8044
  } catch (e) {
7336
8045
  LOG.warn("MeshEvents", `Failed to persist pending coordinator event: ${e?.message || e}`);
@@ -7340,20 +8049,20 @@ function queuePendingMeshCoordinatorEvent(event) {
7340
8049
  function atomicDrainFile(path42) {
7341
8050
  const tmpPath = `${path42}.draining`;
7342
8051
  try {
7343
- (0, import_fs8.renameSync)(path42, tmpPath);
8052
+ (0, import_fs9.renameSync)(path42, tmpPath);
7344
8053
  } catch {
7345
8054
  return null;
7346
8055
  }
7347
8056
  try {
7348
- const content = (0, import_fs8.readFileSync)(tmpPath, "utf-8");
8057
+ const content = (0, import_fs9.readFileSync)(tmpPath, "utf-8");
7349
8058
  try {
7350
- (0, import_fs8.unlinkSync)(tmpPath);
8059
+ (0, import_fs9.unlinkSync)(tmpPath);
7351
8060
  } catch {
7352
8061
  }
7353
8062
  return content;
7354
8063
  } catch {
7355
8064
  try {
7356
- (0, import_fs8.unlinkSync)(tmpPath);
8065
+ (0, import_fs9.unlinkSync)(tmpPath);
7357
8066
  } catch {
7358
8067
  }
7359
8068
  return null;
@@ -7362,16 +8071,16 @@ function atomicDrainFile(path42) {
7362
8071
  function selectiveDrainFile(path42, predicate) {
7363
8072
  const tmpPath = `${path42}.draining`;
7364
8073
  try {
7365
- (0, import_fs8.renameSync)(path42, tmpPath);
8074
+ (0, import_fs9.renameSync)(path42, tmpPath);
7366
8075
  } catch {
7367
8076
  return [];
7368
8077
  }
7369
8078
  let content;
7370
8079
  try {
7371
- content = (0, import_fs8.readFileSync)(tmpPath, "utf-8");
8080
+ content = (0, import_fs9.readFileSync)(tmpPath, "utf-8");
7372
8081
  } catch {
7373
8082
  try {
7374
- (0, import_fs8.unlinkSync)(tmpPath);
8083
+ (0, import_fs9.unlinkSync)(tmpPath);
7375
8084
  } catch {
7376
8085
  }
7377
8086
  return [];
@@ -7394,12 +8103,12 @@ function selectiveDrainFile(path42, predicate) {
7394
8103
  }
7395
8104
  try {
7396
8105
  if (keptLines.length > 0) {
7397
- (0, import_fs8.writeFileSync)(path42, keptLines.join("\n") + "\n", "utf-8");
8106
+ (0, import_fs9.writeFileSync)(path42, keptLines.join("\n") + "\n", "utf-8");
7398
8107
  }
7399
- (0, import_fs8.unlinkSync)(tmpPath);
8108
+ (0, import_fs9.unlinkSync)(tmpPath);
7400
8109
  } catch {
7401
8110
  try {
7402
- if ((0, import_fs8.existsSync)(tmpPath) && !(0, import_fs8.existsSync)(path42)) (0, import_fs8.renameSync)(tmpPath, path42);
8111
+ if ((0, import_fs9.existsSync)(tmpPath) && !(0, import_fs9.existsSync)(path42)) (0, import_fs9.renameSync)(tmpPath, path42);
7403
8112
  } catch {
7404
8113
  }
7405
8114
  return [];
@@ -7493,18 +8202,18 @@ function clearPendingMeshCoordinatorEvents(meshId, coordinatorDaemonId) {
7493
8202
  }
7494
8203
  const paths = coordinatorDaemonId ? [getPendingEventsPath(meshId, coordinatorDaemonId), getPendingEventsPath(meshId)] : [getPendingEventsPath(meshId)];
7495
8204
  for (const path42 of paths) {
7496
- if ((0, import_fs8.existsSync)(path42)) try {
7497
- (0, import_fs8.unlinkSync)(path42);
8205
+ if ((0, import_fs9.existsSync)(path42)) try {
8206
+ (0, import_fs9.unlinkSync)(path42);
7498
8207
  } catch {
7499
8208
  }
7500
8209
  }
7501
8210
  }
7502
- var import_fs8, import_path8, import_crypto7, REFINE_TERMINAL_EVENTS, MAX_PENDING_EVENTS_BYTES, MAX_PENDING_EVENTS_KEEP;
8211
+ var import_fs9, import_path9, import_crypto7, REFINE_TERMINAL_EVENTS, MAX_PENDING_EVENTS_BYTES, MAX_PENDING_EVENTS_KEEP;
7503
8212
  var init_mesh_events_pending = __esm({
7504
8213
  "src/mesh/mesh-events-pending.ts"() {
7505
8214
  "use strict";
7506
- import_fs8 = require("fs");
7507
- import_path8 = require("path");
8215
+ import_fs9 = require("fs");
8216
+ import_path9 = require("path");
7508
8217
  import_crypto7 = require("crypto");
7509
8218
  init_logger();
7510
8219
  init_mesh_ledger();
@@ -7860,7 +8569,7 @@ function reconcileDirectDispatchCompletionFromTranscript(args) {
7860
8569
  });
7861
8570
  return { reconciled: true, kind, workerResult, ledgerEntryId: entry.id };
7862
8571
  }
7863
- function buildLongGeneratingCompletionReconciliation(args) {
8572
+ function buildNoProgressCompletionReconciliation(args) {
7864
8573
  const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
7865
8574
  const nodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
7866
8575
  const providerType = readNonEmptyString2(args.metadataEvent.providerType);
@@ -7879,8 +8588,8 @@ function buildLongGeneratingCompletionReconciliation(args) {
7879
8588
  providerType,
7880
8589
  providerSessionId,
7881
8590
  finalSummary,
7882
- source: "long_generating_reconciliation",
7883
- reconciledFromEvent: "monitor:long_generating",
8591
+ source: "no_progress_reconciliation",
8592
+ reconciledFromEvent: "monitor:no_progress",
7884
8593
  timestamp: args.metadataEvent.timestamp ?? Date.now(),
7885
8594
  completionDiagnostic: {
7886
8595
  ...completionDiagnostic || {},
@@ -7896,7 +8605,7 @@ function buildLongGeneratingCompletionReconciliation(args) {
7896
8605
  if (!terminal) return null;
7897
8606
  return {
7898
8607
  ...args.metadataEvent,
7899
- source: "long_generating_terminal_ledger_suppression",
8608
+ source: "no_progress_terminal_ledger_suppression",
7900
8609
  terminalLedgerKind: terminal.kind,
7901
8610
  terminalLedgerAt: terminal.timestamp
7902
8611
  };
@@ -8336,7 +9045,7 @@ function resolveCommandPath(command) {
8336
9045
  if (isExplicitCommandPath(trimmed)) {
8337
9046
  const expanded = expandHome(trimmed);
8338
9047
  const candidate = path11.isAbsolute(expanded) ? expanded : path11.resolve(expanded);
8339
- return (0, import_fs9.existsSync)(candidate) ? candidate : null;
9048
+ return (0, import_fs10.existsSync)(candidate) ? candidate : null;
8340
9049
  }
8341
9050
  return null;
8342
9051
  }
@@ -8346,7 +9055,7 @@ async function resolveDetectionPath(command, whichCmd) {
8346
9055
  const whichResult = await execAsync(`${whichCmd} ${shellQuote(command)}`);
8347
9056
  if (whichResult) return whichResult.split("\n")[0];
8348
9057
  const resolved = findBinary(command);
8349
- if (path11.isAbsolute(resolved) && (0, import_fs9.existsSync)(resolved)) return resolved;
9058
+ if (path11.isAbsolute(resolved) && (0, import_fs10.existsSync)(resolved)) return resolved;
8350
9059
  return null;
8351
9060
  }
8352
9061
  function execAsync(cmd, timeoutMs = 5e3) {
@@ -8441,14 +9150,14 @@ async function detectCLI(cliId, providerLoader, options) {
8441
9150
  const all = await detectCLIs(providerLoader, options);
8442
9151
  return all.find((c) => c.id === resolvedId && c.installed) || null;
8443
9152
  }
8444
- var import_child_process, os6, path11, import_fs9;
9153
+ var import_child_process, os6, path11, import_fs10;
8445
9154
  var init_cli_detector = __esm({
8446
9155
  "src/detection/cli-detector.ts"() {
8447
9156
  "use strict";
8448
9157
  import_child_process = require("child_process");
8449
9158
  os6 = __toESM(require("os"));
8450
9159
  path11 = __toESM(require("path"));
8451
- import_fs9 = require("fs");
9160
+ import_fs10 = require("fs");
8452
9161
  init_provider_cli_shared();
8453
9162
  }
8454
9163
  });
@@ -8747,7 +9456,7 @@ function hasRecentIntentionalCleanupStop(meshId, sessionId, nodeId) {
8747
9456
  return false;
8748
9457
  }
8749
9458
  function shouldSuppressIntentionalCleanupStop(args) {
8750
- if (args.event !== "agent:stopped" && args.event !== "monitor:long_generating") return false;
9459
+ if (args.event !== "agent:stopped" && args.event !== "monitor:no_progress") return false;
8751
9460
  if (isIntentionalCleanupStopMetadata(args.metadataEvent)) return true;
8752
9461
  return hasRecentIntentionalCleanupStop(args.meshId, args.sessionId, args.nodeId);
8753
9462
  }
@@ -8963,7 +9672,7 @@ function resolveAutoFastForwardPolicy(mesh) {
8963
9672
  function sessionStateLooksActive(state) {
8964
9673
  const status = readNonEmptyString2(state?.status).toLowerCase();
8965
9674
  const chatStatus = readNonEmptyString2(state?.activeChat?.status).toLowerCase();
8966
- const active = /* @__PURE__ */ new Set(["generating", "streaming", "long_generating", "working", "starting", "waiting_approval"]);
9675
+ const active = /* @__PURE__ */ new Set(["generating", "streaming", "no_progress", "long_generating", "working", "starting", "waiting_approval"]);
8967
9676
  return active.has(status) || active.has(chatStatus);
8968
9677
  }
8969
9678
  function nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId) {
@@ -9483,7 +10192,7 @@ async function maybeAutoFastForwardIdleNode(components, args) {
9483
10192
  const node = mesh?.nodes?.find((candidate) => meshNodeIdMatches(candidate, args.nodeId));
9484
10193
  const workspace = readNonEmptyString2(node?.workspace);
9485
10194
  if (!workspace) return;
9486
- if (!(0, import_fs10.existsSync)(workspace)) return;
10195
+ if (!(0, import_fs11.existsSync)(workspace)) return;
9487
10196
  const policy = resolveAutoFastForwardPolicy(mesh);
9488
10197
  if (!policy.enabled) return;
9489
10198
  if (nodeHasActiveMeshWork(components, args.meshId, args.nodeId, args.sessionId)) return;
@@ -9577,24 +10286,24 @@ function injectMeshSystemMessage(components, args) {
9577
10286
  LOG.info("MeshEvents", `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || "(unknown session)"}`);
9578
10287
  return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
9579
10288
  }
9580
- if (args.event === "monitor:long_generating") {
9581
- const reconciledCompletion = buildLongGeneratingCompletionReconciliation({
10289
+ if (args.event === "monitor:no_progress") {
10290
+ const reconciledCompletion = buildNoProgressCompletionReconciliation({
9582
10291
  meshId: args.meshId,
9583
10292
  nodeId: args.nodeId,
9584
10293
  nodeLabel: args.nodeLabel,
9585
10294
  metadataEvent: args.metadataEvent,
9586
10295
  sourceInstanceId: args.sourceInstanceId
9587
10296
  });
9588
- if (reconciledCompletion?.source === "long_generating_reconciliation") {
9589
- LOG.info("MeshEvents", `Reconciled long-generating monitor to completion for session ${eventSessionId || "(unknown session)"}`);
10297
+ if (reconciledCompletion?.source === "no_progress_reconciliation") {
10298
+ LOG.info("MeshEvents", `Reconciled no-progress monitor to completion for session ${eventSessionId || "(unknown session)"}`);
9590
10299
  return injectMeshSystemMessage(components, {
9591
10300
  ...args,
9592
10301
  event: "agent:generating_completed",
9593
10302
  metadataEvent: reconciledCompletion
9594
10303
  });
9595
10304
  }
9596
- if (reconciledCompletion?.source === "long_generating_terminal_ledger_suppression") {
9597
- LOG.info("MeshEvents", `Suppressed long-generating monitor because terminal ledger evidence already exists for session ${eventSessionId || "(unknown session)"}`);
10305
+ if (reconciledCompletion?.source === "no_progress_terminal_ledger_suppression") {
10306
+ LOG.info("MeshEvents", `Suppressed no-progress monitor because terminal ledger evidence already exists for session ${eventSessionId || "(unknown session)"}`);
9598
10307
  return {
9599
10308
  success: true,
9600
10309
  forwarded: 0,
@@ -9636,7 +10345,7 @@ function injectMeshSystemMessage(components, args) {
9636
10345
  const terminalFinalSummary = readNonEmptyString2(terminal.payload.finalSummary);
9637
10346
  const eventProviderSessionId = readNonEmptyString2(args.metadataEvent.providerSessionId);
9638
10347
  const eventFinalSummary = readNonEmptyString2(args.metadataEvent.finalSummary);
9639
- if (terminalProviderSessionId && terminalProviderSessionId === eventProviderSessionId || terminalFinalSummary && terminalFinalSummary === eventFinalSummary || args.metadataEvent.source === "long_generating_reconciliation") {
10348
+ if (terminalProviderSessionId && terminalProviderSessionId === eventProviderSessionId || terminalFinalSummary && terminalFinalSummary === eventFinalSummary || args.metadataEvent.source === "no_progress_reconciliation") {
9640
10349
  LOG.info("MeshEvents", `Suppressed duplicate completion with existing terminal ledger evidence for mesh ${args.meshId} session ${eventSessionId}`);
9641
10350
  return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true, terminalLedgerEvidence: true };
9642
10351
  }
@@ -10075,11 +10784,11 @@ function setupMeshEventForwarding(components) {
10075
10784
  });
10076
10785
  });
10077
10786
  }
10078
- var import_fs10, REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS;
10787
+ var import_fs11, REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS;
10079
10788
  var init_mesh_events_coordinator = __esm({
10080
10789
  "src/mesh/mesh-events-coordinator.ts"() {
10081
10790
  "use strict";
10082
- import_fs10 = require("fs");
10791
+ import_fs11 = require("fs");
10083
10792
  init_config();
10084
10793
  init_mesh_config();
10085
10794
  init_cli_detector();
@@ -10115,7 +10824,7 @@ var init_mesh_events_coordinator = __esm({
10115
10824
  "agent:waiting_approval",
10116
10825
  "agent:stopped",
10117
10826
  "agent:ready",
10118
- "monitor:long_generating",
10827
+ "monitor:no_progress",
10119
10828
  "refine:accepted",
10120
10829
  "refine:completed",
10121
10830
  "refine:failed",
@@ -10126,7 +10835,7 @@ var init_mesh_events_coordinator = __esm({
10126
10835
  "agent:generating_completed": "task_completed",
10127
10836
  "agent:waiting_approval": "task_approval_needed",
10128
10837
  "agent:stopped": "task_failed",
10129
- "monitor:long_generating": "task_stalled"
10838
+ "monitor:no_progress": "task_stalled"
10130
10839
  };
10131
10840
  MESH_FORCE_INJECT_EVENTS = /* @__PURE__ */ new Set([
10132
10841
  "agent:generating_completed",
@@ -10779,6 +11488,14 @@ var init_chat_message_normalization = __esm({
10779
11488
  });
10780
11489
 
10781
11490
  // src/mesh/mesh-reconcile-loop.ts
11491
+ function resolveAutoPruneMinAgeMs() {
11492
+ const raw = readNonEmptyString2(process.env.MESH_AUTO_PRUNE_MIN_AGE_MS);
11493
+ if (raw) {
11494
+ const parsed = Number.parseInt(raw, 10);
11495
+ if (Number.isFinite(parsed) && parsed >= 60 * 6e4 && parsed <= 30 * 24 * 60 * 6e4) return parsed;
11496
+ }
11497
+ return DEFAULT_AUTO_PRUNE_MIN_AGE_MS;
11498
+ }
10782
11499
  function resolveReconcileIntervalMs() {
10783
11500
  const raw = readNonEmptyString2(process.env.MESH_RECONCILE_INTERVAL_MS);
10784
11501
  if (raw) {
@@ -10890,6 +11607,18 @@ async function runMeshReconcileTick(components) {
10890
11607
  LOG.warn("MeshReconcile", `Completion reconcile failed for mesh ${mesh.id}: ${e?.message || e}`);
10891
11608
  }
10892
11609
  }
11610
+ {
11611
+ const minAgeMs = resolveAutoPruneMinAgeMs();
11612
+ for (const mesh of listMeshes()) {
11613
+ const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
11614
+ if (!daemonHostsMesh(mesh, selfIds)) continue;
11615
+ try {
11616
+ await autoPruneStaleDirectDispatches(components, mesh, selfIds, localDaemonId, minAgeMs);
11617
+ } catch (e) {
11618
+ LOG.warn("MeshReconcile", `Auto-prune stale direct failed for mesh ${mesh.id}: ${e?.message || e}`);
11619
+ }
11620
+ }
11621
+ }
10893
11622
  const coordinators = findLiveCoordinators(components);
10894
11623
  if (coordinators.length === 0) {
10895
11624
  return;
@@ -11072,6 +11801,68 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
11072
11801
  }
11073
11802
  }
11074
11803
  }
11804
+ async function autoPruneStaleDirectDispatches(components, mesh, selfIds, localDaemonId, minAgeMs) {
11805
+ const directDispatches = getActiveDirectDispatches(mesh.id);
11806
+ if (directDispatches.length === 0) return;
11807
+ const liveNodes = await collectLiveNodesWithSessions(components, mesh, selfIds, localDaemonId);
11808
+ const result = pruneStaleDirectDispatches({
11809
+ meshId: mesh.id,
11810
+ queue: getQueue(mesh.id),
11811
+ ledgerEntries: readLedgerEntries(mesh.id, { tail: 500 }),
11812
+ directDispatches,
11813
+ nodes: liveNodes,
11814
+ execute: true,
11815
+ minAgeMs,
11816
+ source: "daemon_reconcile_auto_prune"
11817
+ });
11818
+ if (result.prunedCount > 0) {
11819
+ LOG.info("MeshReconcile", `Auto-pruned ${result.prunedCount} orphaned direct dispatch record(s) for mesh ${mesh.id}`);
11820
+ }
11821
+ }
11822
+ async function collectLiveNodesWithSessions(components, mesh, selfIds, localDaemonId) {
11823
+ const dispatchMeshCommand = components.dispatchMeshCommand;
11824
+ return Promise.all(mesh.nodes.map(async (node) => {
11825
+ const nodeDaemonId = readNonEmptyString2(node.daemonId);
11826
+ const isLocalNode = !nodeDaemonId || selfIds.includes(nodeDaemonId) || localDaemonId !== void 0 && nodeDaemonId === localDaemonId;
11827
+ let statusResult;
11828
+ try {
11829
+ if (isLocalNode) {
11830
+ statusResult = await components.commandHandler.handle("get_status_metadata", {});
11831
+ } else if (dispatchMeshCommand) {
11832
+ statusResult = await dispatchMeshCommand(nodeDaemonId, "get_status_metadata", {});
11833
+ } else {
11834
+ return node;
11835
+ }
11836
+ } catch {
11837
+ return node;
11838
+ }
11839
+ const sessions = extractStatusMetadataSessions(statusResult);
11840
+ return sessions.length > 0 ? { ...node, sessions } : node;
11841
+ }));
11842
+ }
11843
+ function extractStatusMetadataSessions(raw) {
11844
+ let cursor = raw;
11845
+ for (let depth = 0; depth < 4 && cursor && typeof cursor === "object"; depth++) {
11846
+ const record = cursor;
11847
+ const status = record.status && typeof record.status === "object" ? record.status : void 0;
11848
+ if (status && Array.isArray(status.sessions)) return status.sessions;
11849
+ if (Array.isArray(record.sessions)) return record.sessions;
11850
+ if (record.payload && typeof record.payload === "object") {
11851
+ cursor = record.payload;
11852
+ continue;
11853
+ }
11854
+ if (record.result && typeof record.result === "object") {
11855
+ cursor = record.result;
11856
+ continue;
11857
+ }
11858
+ if (record.data && typeof record.data === "object") {
11859
+ cursor = record.data;
11860
+ continue;
11861
+ }
11862
+ break;
11863
+ }
11864
+ return [];
11865
+ }
11075
11866
  function extractPendingEvents(raw) {
11076
11867
  if (Array.isArray(raw)) return raw;
11077
11868
  if (raw && typeof raw === "object") {
@@ -11109,7 +11900,7 @@ function setupMeshReconcileLoop(components) {
11109
11900
  }
11110
11901
  };
11111
11902
  }
11112
- var DEFAULT_RECONCILE_INTERVAL_MS;
11903
+ var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS;
11113
11904
  var init_mesh_reconcile_loop = __esm({
11114
11905
  "src/mesh/mesh-reconcile-loop.ts"() {
11115
11906
  "use strict";
@@ -11122,9 +11913,12 @@ var init_mesh_reconcile_loop = __esm({
11122
11913
  init_mesh_unresolved_forward_outbox();
11123
11914
  init_mesh_events_utils();
11124
11915
  init_mesh_work_queue();
11916
+ init_mesh_ledger();
11917
+ init_mesh_active_work();
11125
11918
  init_mesh_events_stale();
11126
11919
  init_chat_message_normalization();
11127
11920
  DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
11921
+ DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
11128
11922
  }
11129
11923
  });
11130
11924
 
@@ -12189,7 +12983,7 @@ function resolveWin32GlobalBin(trimmed) {
12189
12983
  if (!dir) continue;
12190
12984
  for (const ext of WIN_EXEC_EXT) {
12191
12985
  const full = path18.join(dir, trimmed + ext);
12192
- if ((0, import_fs13.existsSync)(full)) return full;
12986
+ if ((0, import_fs14.existsSync)(full)) return full;
12193
12987
  }
12194
12988
  }
12195
12989
  return null;
@@ -12198,7 +12992,7 @@ function resolveWin32Executable(command) {
12198
12992
  if (process.platform !== "win32") return command;
12199
12993
  const trimmed = (command || "").trim();
12200
12994
  if (!trimmed) return command;
12201
- if (path18.isAbsolute(trimmed) && (0, import_fs13.existsSync)(trimmed)) return trimmed;
12995
+ if (path18.isAbsolute(trimmed) && (0, import_fs14.existsSync)(trimmed)) return trimmed;
12202
12996
  try {
12203
12997
  const out = (0, import_child_process4.execFileSync)("where", [trimmed], {
12204
12998
  encoding: "utf8",
@@ -12215,12 +13009,12 @@ function resolveWin32Executable(command) {
12215
13009
  if (globalBin) return globalBin;
12216
13010
  return command;
12217
13011
  }
12218
- var import_child_process4, import_fs13, path18, DIRECT_EXEC_EXT, WIN_EXEC_EXT;
13012
+ var import_child_process4, import_fs14, path18, DIRECT_EXEC_EXT, WIN_EXEC_EXT;
12219
13013
  var init_resolve_executable = __esm({
12220
13014
  "src/cli-adapters/resolve-executable.ts"() {
12221
13015
  "use strict";
12222
13016
  import_child_process4 = require("child_process");
12223
- import_fs13 = require("fs");
13017
+ import_fs14 = require("fs");
12224
13018
  path18 = __toESM(require("path"));
12225
13019
  DIRECT_EXEC_EXT = /* @__PURE__ */ new Set([".exe", ".com"]);
12226
13020
  WIN_EXEC_EXT = [".exe", ".com", ".cmd", ".bat"];
@@ -15188,7 +15982,7 @@ ${lastSnapshot}`;
15188
15982
  };
15189
15983
  if (parsedSessionStatus === "idle" && hasFinalAssistant(parsedStatusBeforeSend)) return null;
15190
15984
  if (this.engine.currentStatus === "generating") return "current_status_generating";
15191
- if (parsedSessionStatus === "generating" || parsedSessionStatus === "long_generating") {
15985
+ if (parsedSessionStatus === "generating" || parsedSessionStatus === "no_progress" || parsedSessionStatus === "long_generating") {
15192
15986
  const parsedModal = parsedStatusBeforeSend?.activeModal ?? parsedStatusBeforeSend?.modal ?? null;
15193
15987
  const parsedHasActionableModal = Boolean(
15194
15988
  parsedModal && Array.isArray(parsedModal.buttons) && parsedModal.buttons.some((candidate) => typeof candidate === "string" && candidate.trim())
@@ -15265,7 +16059,7 @@ ${lastSnapshot}`;
15265
16059
  }
15266
16060
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.engine.currentStatus})`);
15267
16061
  const parsedSessionStatus = typeof parsedStatusBeforeSend?.status === "string" ? String(parsedStatusBeforeSend.status) : "";
15268
- if (!allowInputDuringGeneration && (parsedSessionStatus === "generating" || parsedSessionStatus === "long_generating")) {
16062
+ if (!allowInputDuringGeneration && (parsedSessionStatus === "generating" || parsedSessionStatus === "no_progress" || parsedSessionStatus === "long_generating")) {
15269
16063
  const parsedModal = parsedStatusBeforeSend?.activeModal ?? parsedStatusBeforeSend?.modal ?? null;
15270
16064
  const parsedHasActionableModal = Boolean(
15271
16065
  parsedModal && Array.isArray(parsedModal.buttons) && parsedModal.buttons.some((candidate) => typeof candidate === "string" && candidate.trim())
@@ -16775,6 +17569,8 @@ __export(index_exports, {
16775
17569
  AcpProviderInstance: () => AcpProviderInstance,
16776
17570
  AgentStreamPoller: () => AgentStreamPoller,
16777
17571
  BUILTIN_CHAT_MESSAGE_KINDS: () => BUILTIN_CHAT_MESSAGE_KINDS,
17572
+ CHANGE_IMPACT_CONFIG_LOCATIONS: () => CHANGE_IMPACT_CONFIG_LOCATIONS,
17573
+ CHANGE_IMPACT_CONFIG_SCHEMA: () => CHANGE_IMPACT_CONFIG_SCHEMA,
16778
17574
  CHAT_MESSAGE_ACTIVITY_SOURCES: () => CHAT_MESSAGE_ACTIVITY_SOURCES,
16779
17575
  CHAT_MESSAGE_AUDIENCES: () => CHAT_MESSAGE_AUDIENCES,
16780
17576
  CHAT_MESSAGE_INTERNAL_SOURCES: () => CHAT_MESSAGE_INTERNAL_SOURCES,
@@ -16965,6 +17761,7 @@ __export(index_exports, {
16965
17761
  getSessionHostSurfaceKind: () => getSessionHostSurfaceKind,
16966
17762
  getSessionRecoveryContext: () => getSessionRecoveryContext,
16967
17763
  getWorkspaceState: () => getWorkspaceState,
17764
+ globToRegExp: () => globToRegExp,
16968
17765
  handleGitCommand: () => handleGitCommand,
16969
17766
  hasCdpManager: () => hasCdpManager,
16970
17767
  hasPendingDependents: () => hasPendingDependents,
@@ -16998,6 +17795,7 @@ __export(index_exports, {
16998
17795
  listMeshMissionSummaries: () => listMeshMissionSummaries,
16999
17796
  listMeshes: () => listMeshes,
17000
17797
  listWorktrees: () => listWorktrees,
17798
+ loadChangeImpactConfig: () => loadChangeImpactConfig,
17001
17799
  loadConfig: () => loadConfig,
17002
17800
  loadMeshCoordinatorRegistry: () => loadMeshCoordinatorRegistry,
17003
17801
  loadMeshRefineConfig: () => loadMeshRefineConfig,
@@ -17038,6 +17836,7 @@ __export(index_exports, {
17038
17836
  prepareSessionChatTailUpdate: () => prepareSessionChatTailUpdate,
17039
17837
  prepareSessionModalUpdate: () => prepareSessionModalUpdate,
17040
17838
  probeCdpPort: () => probeCdpPort,
17839
+ pruneStaleDirectDispatches: () => pruneStaleDirectDispatches,
17041
17840
  queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
17042
17841
  readAntigravityCliSession: () => readSession3,
17043
17842
  readCachedInlineMeshActiveSessionDetails: () => readCachedInlineMeshActiveSessionDetails,
@@ -17091,6 +17890,7 @@ __export(index_exports, {
17091
17890
  spawnDetachedDaemonUpgradeHelper: () => spawnDetachedDaemonUpgradeHelper,
17092
17891
  startDaemonDevSupport: () => startDaemonDevSupport,
17093
17892
  startLocalIpcServer: () => startLocalIpcServer,
17893
+ suggestChangeImpactConfig: () => suggestChangeImpactConfig,
17094
17894
  suggestMeshRefineConfig: () => suggestMeshRefineConfig,
17095
17895
  summarizeGitStatus: () => summarizeGitStatus,
17096
17896
  summarizeMeshAsyncRefineJobs: () => summarizeMeshAsyncRefineJobs,
@@ -17107,6 +17907,7 @@ __export(index_exports, {
17107
17907
  updateTaskStatus: () => updateTaskStatus,
17108
17908
  upsertMeshMission: () => upsertMeshMission,
17109
17909
  upsertSavedProviderSession: () => upsertSavedProviderSession,
17910
+ validateChangeImpactConfig: () => validateChangeImpactConfig,
17110
17911
  validateCliProviderManifest: () => validateCliProviderManifest,
17111
17912
  validateFsmSpec: () => validateFsmSpec,
17112
17913
  validateMeshRefineConfig: () => validateMeshRefineConfig,
@@ -17476,6 +18277,7 @@ init_repo_mesh_types();
17476
18277
  // src/git/index.ts
17477
18278
  init_git_executor();
17478
18279
  init_git_status();
18280
+ init_change_impact_config();
17479
18281
  init_git_diff();
17480
18282
 
17481
18283
  // src/git/git-summary.ts
@@ -18769,18 +19571,18 @@ init_mesh_task_stats();
18769
19571
  init_mesh_review_inbox();
18770
19572
 
18771
19573
  // src/mesh/coordinator-registry.ts
18772
- var import_path5 = require("path");
18773
- var import_fs5 = require("fs");
19574
+ var import_path6 = require("path");
19575
+ var import_fs6 = require("fs");
18774
19576
  init_config();
18775
19577
  var _registry = /* @__PURE__ */ new Map();
18776
19578
  function getRegistryPath() {
18777
- return (0, import_path5.join)(getDaemonDataDir(), "mesh-coordinators.json");
19579
+ return (0, import_path6.join)(getDaemonDataDir(), "mesh-coordinators.json");
18778
19580
  }
18779
19581
  function loadMeshCoordinatorRegistry() {
18780
19582
  const path42 = getRegistryPath();
18781
- if (!(0, import_fs5.existsSync)(path42)) return;
19583
+ if (!(0, import_fs6.existsSync)(path42)) return;
18782
19584
  try {
18783
- const raw = JSON.parse((0, import_fs5.readFileSync)(path42, "utf-8"));
19585
+ const raw = JSON.parse((0, import_fs6.readFileSync)(path42, "utf-8"));
18784
19586
  if (!Array.isArray(raw)) return;
18785
19587
  _registry.clear();
18786
19588
  for (const entry of raw) {
@@ -18793,7 +19595,7 @@ function loadMeshCoordinatorRegistry() {
18793
19595
  }
18794
19596
  function saveRegistry() {
18795
19597
  try {
18796
- (0, import_fs5.writeFileSync)(
19598
+ (0, import_fs6.writeFileSync)(
18797
19599
  getRegistryPath(),
18798
19600
  JSON.stringify([..._registry.values()], null, 2),
18799
19601
  { encoding: "utf-8", mode: 384 }
@@ -18828,9 +19630,9 @@ function listCoordinatorsForWorkspace(workspace) {
18828
19630
  }
18829
19631
 
18830
19632
  // src/mesh/refine-config.ts
18831
- var import_fs6 = require("fs");
18832
- var import_path6 = require("path");
18833
- var yaml = __toESM(require("js-yaml"));
19633
+ var import_fs7 = require("fs");
19634
+ var import_path7 = require("path");
19635
+ var yaml2 = __toESM(require("js-yaml"));
18834
19636
  var MESH_REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
18835
19637
  var MESH_REFINE_CONFIG_LOCATIONS = [
18836
19638
  ".adhdev/refine.json",
@@ -18971,7 +19773,7 @@ function normalizeMeshCommandConfig(entry, source) {
18971
19773
  }
18972
19774
  };
18973
19775
  }
18974
- var isRecord = isMeshConfigRecord;
19776
+ var isRecord2 = isMeshConfigRecord;
18975
19777
  function validateMeshRefineConfig(config, source = "inline") {
18976
19778
  const errors = [];
18977
19779
  const bootstrapCommands = [];
@@ -18979,14 +19781,14 @@ function validateMeshRefineConfig(config, source = "inline") {
18979
19781
  const rejectedCommands = [];
18980
19782
  const deprecationWarnings = [];
18981
19783
  let bootstrapMode = "inherit";
18982
- if (!isRecord(config)) return { valid: false, errors: ["config must be an object"], bootstrapCommands, commands, rejectedCommands, bootstrapMode, deprecationWarnings };
19784
+ if (!isRecord2(config)) return { valid: false, errors: ["config must be an object"], bootstrapCommands, commands, rejectedCommands, bootstrapMode, deprecationWarnings };
18983
19785
  if (config.version !== 1) errors.push("version must be 1");
18984
19786
  if (config.allowAutoPublishSubmoduleMainCommits !== void 0 && typeof config.allowAutoPublishSubmoduleMainCommits !== "boolean") {
18985
19787
  errors.push("allowAutoPublishSubmoduleMainCommits must be a boolean when provided");
18986
19788
  }
18987
19789
  const validation = config.validation;
18988
- if (validation !== void 0 && !isRecord(validation)) errors.push("validation must be an object");
18989
- const rawBootstrapMode = isRecord(validation) ? validation.bootstrap : void 0;
19790
+ if (validation !== void 0 && !isRecord2(validation)) errors.push("validation must be an object");
19791
+ const rawBootstrapMode = isRecord2(validation) ? validation.bootstrap : void 0;
18990
19792
  if (rawBootstrapMode !== void 0) {
18991
19793
  if (rawBootstrapMode === "inherit" || rawBootstrapMode === "skip") {
18992
19794
  bootstrapMode = rawBootstrapMode;
@@ -18994,8 +19796,8 @@ function validateMeshRefineConfig(config, source = "inline") {
18994
19796
  errors.push("validation.bootstrap must be 'inherit' or 'skip' when provided");
18995
19797
  }
18996
19798
  }
18997
- const rawCommands = isRecord(validation) ? validation.commands : void 0;
18998
- const rawBootstrapCommands = isRecord(validation) ? validation.bootstrapCommands : void 0;
19799
+ const rawCommands = isRecord2(validation) ? validation.commands : void 0;
19800
+ const rawBootstrapCommands = isRecord2(validation) ? validation.bootstrapCommands : void 0;
18999
19801
  if (rawCommands !== void 0 && !Array.isArray(rawCommands)) errors.push("validation.commands must be an array");
19000
19802
  if (rawBootstrapCommands !== void 0 && !Array.isArray(rawBootstrapCommands)) errors.push("validation.bootstrapCommands must be an array");
19001
19803
  if (Array.isArray(rawBootstrapCommands) && rawBootstrapCommands.length > 0) {
@@ -19018,9 +19820,9 @@ function validateMeshRefineConfig(config, source = "inline") {
19018
19820
  if (rejectedCommands.length) errors.push("one or more validation commands are invalid");
19019
19821
  return { valid: errors.length === 0, errors, bootstrapCommands, commands, rejectedCommands, bootstrapMode, deprecationWarnings };
19020
19822
  }
19021
- function parseConfigText(path42, text) {
19823
+ function parseConfigText2(path42, text) {
19022
19824
  if (/\.json$/i.test(path42)) return JSON.parse(text);
19023
- return yaml.load(text);
19825
+ return yaml2.load(text);
19024
19826
  }
19025
19827
  function loadMeshRefineConfig(mesh, workspace) {
19026
19828
  const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
@@ -19031,10 +19833,10 @@ function loadMeshRefineConfig(mesh, workspace) {
19031
19833
  return { config: inline, source: "mesh.policy.refineConfig", sourceType: "mesh_policy" };
19032
19834
  }
19033
19835
  for (const relative5 of MESH_REFINE_CONFIG_LOCATIONS) {
19034
- const configPath = (0, import_path6.join)(workspace, relative5);
19035
- if (!(0, import_fs6.existsSync)(configPath)) continue;
19836
+ const configPath = (0, import_path7.join)(workspace, relative5);
19837
+ if (!(0, import_fs7.existsSync)(configPath)) continue;
19036
19838
  try {
19037
- const parsed = parseConfigText(configPath, (0, import_fs6.readFileSync)(configPath, "utf-8"));
19839
+ const parsed = parseConfigText2(configPath, (0, import_fs7.readFileSync)(configPath, "utf-8"));
19038
19840
  const validation = validateMeshRefineConfig(parsed, relative5);
19039
19841
  if (!validation.valid) return { source: relative5, sourceType: "invalid", path: configPath, error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
19040
19842
  return { config: parsed, source: relative5, sourceType: "repo_file", path: configPath };
@@ -19050,20 +19852,20 @@ function loadMeshRefineConfig(mesh, workspace) {
19050
19852
  }
19051
19853
  function readPackageScripts(workspace) {
19052
19854
  try {
19053
- const parsed = JSON.parse((0, import_fs6.readFileSync)((0, import_path6.join)(workspace, "package.json"), "utf-8"));
19054
- return isRecord(parsed?.scripts) ? parsed.scripts : {};
19855
+ const parsed = JSON.parse((0, import_fs7.readFileSync)((0, import_path7.join)(workspace, "package.json"), "utf-8"));
19856
+ return isRecord2(parsed?.scripts) ? parsed.scripts : {};
19055
19857
  } catch {
19056
19858
  return {};
19057
19859
  }
19058
19860
  }
19059
19861
  function collectProjectContextSuggestions(mesh) {
19060
19862
  const commands = mesh?.projectContext?.commands;
19061
- if (!isRecord(commands)) return [];
19863
+ if (!isRecord2(commands)) return [];
19062
19864
  const suggestions = [];
19063
19865
  for (const category of MESH_REFINE_VALIDATION_CATEGORIES) {
19064
19866
  const entries = Array.isArray(commands[category]) ? commands[category] : [];
19065
19867
  for (const entry of entries) {
19066
- if (isRecord(entry) && typeof entry.command === "string") suggestions.push({ command: entry.command, category });
19868
+ if (isRecord2(entry) && typeof entry.command === "string") suggestions.push({ command: entry.command, category });
19067
19869
  }
19068
19870
  }
19069
19871
  return suggestions;
@@ -19127,12 +19929,12 @@ function resolveMeshRefineValidationPlan(mesh, workspace) {
19127
19929
  }
19128
19930
 
19129
19931
  // src/mesh/worktree-bootstrap-config.ts
19130
- var import_fs7 = require("fs");
19131
- var import_path7 = require("path");
19932
+ var import_fs8 = require("fs");
19933
+ var import_path8 = require("path");
19132
19934
  var import_node_child_process3 = require("child_process");
19133
19935
  var import_node_crypto2 = require("crypto");
19134
19936
  var import_node_util3 = require("util");
19135
- var yaml2 = __toESM(require("js-yaml"));
19937
+ var yaml3 = __toESM(require("js-yaml"));
19136
19938
  var MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS = [
19137
19939
  ".adhdev/worktree_bootstrap.json",
19138
19940
  ".adhdev/worktree_bootstrap.yaml",
@@ -19177,9 +19979,9 @@ var MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA = {
19177
19979
  var DEFAULT_TIMEOUT_MS2 = 12e4;
19178
19980
  var DEFAULT_OUTPUT_LIMIT_BYTES = 128 * 1024;
19179
19981
  var OUTPUT_SUMMARY_CHARS = 2e3;
19180
- function parseConfigText2(path42, text) {
19982
+ function parseConfigText3(path42, text) {
19181
19983
  if (/\.json$/i.test(path42)) return JSON.parse(text);
19182
- return yaml2.load(text);
19984
+ return yaml3.load(text);
19183
19985
  }
19184
19986
  function truncateOutput(value) {
19185
19987
  const text = typeof value === "string" ? value : value == null ? "" : String(value);
@@ -19219,10 +20021,10 @@ function loadMeshWorktreeBootstrapConfig(mesh, workspace) {
19219
20021
  return { config: inline, source: "mesh.policy.worktreeBootstrapConfig", sourceType: "mesh_policy" };
19220
20022
  }
19221
20023
  for (const relative5 of MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS) {
19222
- const configPath = (0, import_path7.join)(workspace, relative5);
19223
- if (!(0, import_fs7.existsSync)(configPath)) continue;
20024
+ const configPath = (0, import_path8.join)(workspace, relative5);
20025
+ if (!(0, import_fs8.existsSync)(configPath)) continue;
19224
20026
  try {
19225
- const parsed = parseConfigText2(configPath, (0, import_fs7.readFileSync)(configPath, "utf-8"));
20027
+ const parsed = parseConfigText3(configPath, (0, import_fs8.readFileSync)(configPath, "utf-8"));
19226
20028
  const validation = validateMeshWorktreeBootstrapConfig(parsed, relative5);
19227
20029
  if (!validation.valid) return { source: relative5, sourceType: "invalid", path: configPath, error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
19228
20030
  return { config: parsed, source: relative5, sourceType: "repo_file", path: configPath };
@@ -19235,9 +20037,9 @@ function loadMeshWorktreeBootstrapConfig(mesh, workspace) {
19235
20037
  function computeStaleInputsDigest(workspace, staleInputs) {
19236
20038
  const digest = {};
19237
20039
  for (const relative5 of staleInputs ?? []) {
19238
- const filePath = (0, import_path7.join)(workspace, relative5);
20040
+ const filePath = (0, import_path8.join)(workspace, relative5);
19239
20041
  try {
19240
- digest[relative5] = (0, import_node_crypto2.createHash)("sha256").update((0, import_fs7.readFileSync)(filePath)).digest("hex");
20042
+ digest[relative5] = (0, import_node_crypto2.createHash)("sha256").update((0, import_fs8.readFileSync)(filePath)).digest("hex");
19241
20043
  } catch {
19242
20044
  digest[relative5] = "absent";
19243
20045
  }
@@ -19307,10 +20109,10 @@ async function runMeshWorktreeBootstrap(mesh, workspace) {
19307
20109
  staleInputs: loaded.config.staleInputs
19308
20110
  };
19309
20111
  const staleInputPaths = loaded.config.staleInputs ?? [];
19310
- const initiallyAbsent = staleInputPaths.filter((p) => !(0, import_fs7.existsSync)((0, import_path7.join)(workspace, p)));
20112
+ const initiallyAbsent = staleInputPaths.filter((p) => !(0, import_fs8.existsSync)((0, import_path8.join)(workspace, p)));
19311
20113
  for (const command of validation.commands) {
19312
20114
  if (initiallyAbsent.length > 0) {
19313
- const appearedNow = initiallyAbsent.filter((p) => (0, import_fs7.existsSync)((0, import_path7.join)(workspace, p)));
20115
+ const appearedNow = initiallyAbsent.filter((p) => (0, import_fs8.existsSync)((0, import_path8.join)(workspace, p)));
19314
20116
  if (appearedNow.length > 0) {
19315
20117
  state.status = "stale";
19316
20118
  state.completedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -19318,7 +20120,7 @@ async function runMeshWorktreeBootstrap(mesh, workspace) {
19318
20120
  return state;
19319
20121
  }
19320
20122
  }
19321
- const cwd = command.cwd ? (0, import_path7.resolve)(workspace, command.cwd) : workspace;
20123
+ const cwd = command.cwd ? (0, import_path8.resolve)(workspace, command.cwd) : workspace;
19322
20124
  const startedAt = Date.now();
19323
20125
  state.lastCommand = command.displayCommand;
19324
20126
  try {
@@ -19440,331 +20242,7 @@ function buildMeshLedgerReconciliationEvidence(meshId, replicas) {
19440
20242
 
19441
20243
  // src/index.ts
19442
20244
  init_mesh_work_queue();
19443
-
19444
- // src/mesh/mesh-active-work.ts
19445
- init_dist();
19446
- var DIRECT_DISPATCH_VIA = /* @__PURE__ */ new Set(["p2p_direct", "local_direct", "mesh_send_task"]);
19447
- var TERMINAL_LEDGER_KINDS = /* @__PURE__ */ new Set(["task_completed", "task_failed", "task_stalled"]);
19448
- function readString6(value) {
19449
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
19450
- }
19451
- function summarizeMessage(message) {
19452
- const oneLine2 = message.replace(/\s+/g, " ").trim();
19453
- const title = oneLine2.length > 96 ? `${oneLine2.slice(0, 93)}...` : oneLine2;
19454
- return { title: title || "(untitled task)", summary: oneLine2 };
19455
- }
19456
- function elapsedSince(value, now) {
19457
- const started = value ? new Date(value).getTime() : Number.NaN;
19458
- return Number.isFinite(started) ? Math.max(0, now - started) : 0;
19459
- }
19460
- function sessionStatusFromNodes(nodes, nodeId, sessionId) {
19461
- if (!Array.isArray(nodes)) return {};
19462
- if (!nodeId) return { staleReason: "direct task has no node id" };
19463
- const node = nodes.find((item) => meshNodeIdMatches(item, nodeId));
19464
- if (!node) return { staleReason: "direct task node is no longer in the live mesh" };
19465
- if (!sessionId) return {};
19466
- const candidates = [];
19467
- for (const value of [
19468
- node.sessions,
19469
- node.activeSessions,
19470
- node.active_sessions,
19471
- node.activeSessionDetails,
19472
- node.active_session_details,
19473
- node.sessionDetails,
19474
- node.session_details,
19475
- node.lastProbe?.sessions,
19476
- node.last_probe?.sessions,
19477
- node.lastProbe?.status?.sessions,
19478
- node.last_probe?.status?.sessions
19479
- ]) {
19480
- if (Array.isArray(value)) candidates.push(...value);
19481
- }
19482
- for (const value of [node.activeSession, node.active_session, node.currentSession, node.current_session, node.runtimeSession, node.runtime_session, node.session]) {
19483
- if (value && typeof value === "object") candidates.push(value);
19484
- }
19485
- const session = candidates.find((item) => {
19486
- if (typeof item === "string") return item === sessionId;
19487
- const id = readString6(item?.id) || readString6(item?.sessionId) || readString6(item?.session_id) || readString6(item?.runtimeSessionId) || readString6(item?.instanceId);
19488
- return id === sessionId;
19489
- });
19490
- if (!session) return { staleReason: "direct task session is not present in live session records" };
19491
- if (typeof session === "string") return {};
19492
- const raw = `${readString6(session.status) || ""} ${readString6(session.lifecycle) || ""} ${readString6(session.state) || ""} ${readString6(session.activeChat?.status) || ""}`.toLowerCase();
19493
- if (raw.includes("approval")) return { status: "awaiting_approval" };
19494
- if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return { status: "generating" };
19495
- if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return { status: "failed" };
19496
- if (raw.includes("idle") || raw.includes("waiting_input") || raw.includes("ready")) return { status: "idle" };
19497
- return {};
19498
- }
19499
- function isDirectDispatch(entry) {
19500
- if (entry.kind !== "task_dispatched") return false;
19501
- const payload = entry.payload || {};
19502
- if (payload.source === "direct") return true;
19503
- const via = readString6(payload.via);
19504
- return Boolean(via && DIRECT_DISPATCH_VIA.has(via) && payload.source !== "queue");
19505
- }
19506
- function directDispatchTaskId(entry) {
19507
- return readString6(entry.payload?.taskId) || entry.id;
19508
- }
19509
- function terminalMatchesDispatch(terminal, dispatch, taskId) {
19510
- const terminalTaskId = readString6(terminal.payload?.taskId);
19511
- if (terminalTaskId && terminalTaskId === taskId) return true;
19512
- if (terminalTaskId && terminalTaskId !== taskId) return false;
19513
- if (dispatch.sessionId && terminal.sessionId === dispatch.sessionId) return true;
19514
- return Boolean(dispatch.nodeId && terminal.nodeId === dispatch.nodeId && !dispatch.sessionId);
19515
- }
19516
- function statusFromTerminal(entry) {
19517
- if (entry.kind === "task_approval_needed") return "awaiting_approval";
19518
- if (entry.kind === "task_completed") return "idle";
19519
- return "failed";
19520
- }
19521
- function buildMeshActiveWorkSummary(activeWork) {
19522
- const statusCounts = {
19523
- pending: 0,
19524
- assigned: 0,
19525
- generating: 0,
19526
- idle: 0,
19527
- failed: 0,
19528
- awaiting_approval: 0
19529
- };
19530
- const sourceCounts = { queue: 0, direct: 0 };
19531
- for (const item of activeWork) {
19532
- sourceCounts[item.source] += 1;
19533
- statusCounts[item.status] += 1;
19534
- }
19535
- const staleDirectCount = activeWork.filter((item) => item.source === "direct" && item.staleReason).length;
19536
- const staleDirectUnacknowledgedCount = activeWork.filter((item) => item.source === "direct" && item.staleDispatchUnacknowledged).length;
19537
- return {
19538
- totalActiveCount: activeWork.length,
19539
- queueActiveCount: sourceCounts.queue,
19540
- directActiveCount: sourceCounts.direct,
19541
- awaitingApprovalCount: statusCounts.awaiting_approval,
19542
- generatingCount: statusCounts.generating,
19543
- failedCount: statusCounts.failed,
19544
- idleCount: statusCounts.idle,
19545
- sourceCounts,
19546
- statusCounts,
19547
- staleDirectCount,
19548
- ...staleDirectUnacknowledgedCount > 0 ? { staleDirectUnacknowledgedCount } : {},
19549
- ...staleDirectCount > 0 ? { staleDirectNote: "Stale direct records are orphaned ledger entries whose node/session no longer exists. They are historical recovery evidence only \u2014 not active or unresolved work. The queue (source: queue) is authoritative for pending/assigned tasks." } : {}
19550
- };
19551
- }
19552
- function buildMeshActiveWork(opts) {
19553
- const now = opts.now ?? Date.now();
19554
- const records = [];
19555
- const staleDirectWork = [];
19556
- const terminalDirectWork = [];
19557
- for (const task of opts.queue || []) {
19558
- if (task.status !== "pending" && task.status !== "assigned") continue;
19559
- const { title, summary: summary2 } = summarizeMessage(task.message || "");
19560
- records.push({
19561
- taskId: task.id,
19562
- source: "queue",
19563
- status: task.status,
19564
- nodeId: task.assignedNodeId || task.targetNodeId,
19565
- sessionId: task.assignedSessionId || task.targetSessionId,
19566
- taskTitle: title,
19567
- taskSummary: summary2,
19568
- message: task.message,
19569
- taskMode: task.taskMode,
19570
- createdAt: task.createdAt,
19571
- updatedAt: task.updatedAt,
19572
- dispatchedAt: task.dispatchTimestamp,
19573
- elapsedMs: elapsedSince(task.dispatchTimestamp || task.createdAt, now)
19574
- });
19575
- }
19576
- if (opts.directDispatches !== void 0) {
19577
- const dbTaskIds = new Set(opts.directDispatches.map((d) => d.taskId));
19578
- for (const dispatch of opts.directDispatches) {
19579
- const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId ?? void 0, dispatch.sessionId ?? void 0);
19580
- const dbStatus = dispatch.status;
19581
- const isTerminal = dbStatus === "completed" || dbStatus === "failed" || dbStatus === "stale";
19582
- const status = isTerminal ? dbStatus === "completed" ? "idle" : "failed" : live.status || (dbStatus === "acked" ? "generating" : "assigned");
19583
- const isNoTransition = !isTerminal && !live.status;
19584
- const isIdleUnacknowledged = status === "idle" && !isTerminal;
19585
- const ledgerOnlyStaleReason = !isTerminal && (isIdleUnacknowledged || isNoTransition || dispatch.dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
19586
- const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
19587
- const { title, summary: summary2 } = summarizeMessage(dispatch.message || "");
19588
- const record = {
19589
- taskId: dispatch.taskId,
19590
- source: "direct",
19591
- status,
19592
- nodeId: dispatch.nodeId ?? void 0,
19593
- sessionId: dispatch.sessionId ?? void 0,
19594
- providerType: dispatch.providerType ?? void 0,
19595
- taskTitle: title,
19596
- taskSummary: summary2,
19597
- message: dispatch.message,
19598
- taskMode: dispatch.taskMode ?? void 0,
19599
- createdAt: dispatch.dispatchedAt,
19600
- updatedAt: dispatch.updatedAt,
19601
- dispatchedAt: dispatch.dispatchedAt,
19602
- elapsedMs: elapsedSince(dispatch.dispatchedAt, now),
19603
- terminal: isTerminal,
19604
- terminalKind: isTerminal ? dbStatus === "completed" ? "task_completed" : "task_failed" : void 0,
19605
- terminalAt: isTerminal ? dispatch.updatedAt : void 0,
19606
- staleReason: live.staleReason || ledgerOnlyStaleReason,
19607
- ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
19608
- };
19609
- if (isTerminal) {
19610
- terminalDirectWork.push(record);
19611
- if (opts.includeTerminalDirect !== true) continue;
19612
- }
19613
- if ((live.staleReason || ledgerOnlyStaleReason) && !isTerminal) {
19614
- staleDirectWork.push(record);
19615
- continue;
19616
- }
19617
- records.push(record);
19618
- }
19619
- const ledgerEntries = (opts.ledgerEntries || []).slice().sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
19620
- const terminals = ledgerEntries.filter((entry) => TERMINAL_LEDGER_KINDS.has(entry.kind) || entry.kind === "task_approval_needed");
19621
- for (const dispatch of ledgerEntries.filter(isDirectDispatch)) {
19622
- const taskId = directDispatchTaskId(dispatch);
19623
- if (dbTaskIds.has(taskId)) continue;
19624
- const terminal = terminals.filter((entry) => new Date(entry.timestamp).getTime() >= new Date(dispatch.timestamp).getTime()).find((entry) => terminalMatchesDispatch(entry, dispatch, taskId));
19625
- const terminalStatus = terminal ? statusFromTerminal(terminal) : void 0;
19626
- const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
19627
- const status = terminalStatus || live.status || "assigned";
19628
- const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
19629
- const dispatchedToIdleSession = dispatch.payload?.dispatchedToIdleSession === true;
19630
- const isNoTransition = !terminalStatus && !live.status;
19631
- const isIdleUnacknowledged = status === "idle";
19632
- const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
19633
- const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
19634
- const { title, summary: summary2 } = summarizeMessage(message);
19635
- const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
19636
- const record = {
19637
- taskId,
19638
- source: "direct",
19639
- status,
19640
- nodeId: dispatch.nodeId,
19641
- sessionId: dispatch.sessionId,
19642
- providerType: dispatch.providerType || readString6(dispatch.payload?.providerType),
19643
- taskTitle: readString6(dispatch.payload?.taskTitle) || title,
19644
- taskSummary: readString6(dispatch.payload?.taskSummary) || summary2,
19645
- message,
19646
- taskMode: readString6(dispatch.payload?.taskMode),
19647
- createdAt: dispatch.timestamp,
19648
- updatedAt: terminal?.timestamp || dispatch.timestamp,
19649
- dispatchedAt: dispatch.timestamp,
19650
- elapsedMs: elapsedSince(dispatch.timestamp, now),
19651
- terminal: terminalRow,
19652
- terminalKind: terminal?.kind,
19653
- terminalAt: terminal?.timestamp,
19654
- staleReason: live.staleReason || ledgerOnlyStaleReason,
19655
- ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
19656
- };
19657
- if (terminalRow) {
19658
- terminalDirectWork.push(record);
19659
- if (opts.includeTerminalDirect !== true) continue;
19660
- }
19661
- if ((live.staleReason || ledgerOnlyStaleReason) && !terminalRow) {
19662
- staleDirectWork.push(record);
19663
- continue;
19664
- }
19665
- records.push(record);
19666
- }
19667
- } else {
19668
- const ledgerEntries = (opts.ledgerEntries || []).slice().sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
19669
- const terminals = ledgerEntries.filter((entry) => TERMINAL_LEDGER_KINDS.has(entry.kind) || entry.kind === "task_approval_needed");
19670
- for (const dispatch of ledgerEntries.filter(isDirectDispatch)) {
19671
- const taskId = directDispatchTaskId(dispatch);
19672
- const terminal = terminals.filter((entry) => new Date(entry.timestamp).getTime() >= new Date(dispatch.timestamp).getTime()).find((entry) => terminalMatchesDispatch(entry, dispatch, taskId));
19673
- const terminalStatus = terminal ? statusFromTerminal(terminal) : void 0;
19674
- const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
19675
- const status = terminalStatus || live.status || "assigned";
19676
- const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
19677
- const dispatchedToIdleSession = dispatch.payload?.dispatchedToIdleSession === true;
19678
- const isNoTransition = !terminalStatus && !live.status;
19679
- const isIdleUnacknowledged = status === "idle";
19680
- const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
19681
- const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
19682
- const { title, summary: summary2 } = summarizeMessage(message);
19683
- const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
19684
- const record = {
19685
- taskId,
19686
- source: "direct",
19687
- status,
19688
- nodeId: dispatch.nodeId,
19689
- sessionId: dispatch.sessionId,
19690
- providerType: dispatch.providerType || readString6(dispatch.payload?.providerType),
19691
- taskTitle: readString6(dispatch.payload?.taskTitle) || title,
19692
- taskSummary: readString6(dispatch.payload?.taskSummary) || summary2,
19693
- message,
19694
- taskMode: readString6(dispatch.payload?.taskMode),
19695
- createdAt: dispatch.timestamp,
19696
- updatedAt: terminal?.timestamp || dispatch.timestamp,
19697
- dispatchedAt: dispatch.timestamp,
19698
- elapsedMs: elapsedSince(dispatch.timestamp, now),
19699
- terminal: terminalRow,
19700
- terminalKind: terminal?.kind,
19701
- terminalAt: terminal?.timestamp,
19702
- staleReason: live.staleReason || ledgerOnlyStaleReason,
19703
- ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
19704
- };
19705
- if (terminalRow) {
19706
- terminalDirectWork.push(record);
19707
- if (opts.includeTerminalDirect !== true) continue;
19708
- }
19709
- if ((live.staleReason || ledgerOnlyStaleReason) && !terminalRow) {
19710
- staleDirectWork.push(record);
19711
- continue;
19712
- }
19713
- records.push(record);
19714
- }
19715
- }
19716
- records.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
19717
- staleDirectWork.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
19718
- terminalDirectWork.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
19719
- const summary = buildMeshActiveWorkSummary(records);
19720
- summary.staleDirectCount = staleDirectWork.length;
19721
- const unacknowledgedCount = staleDirectWork.filter((r) => r.staleDispatchUnacknowledged).length;
19722
- if (unacknowledgedCount > 0) {
19723
- summary.staleDirectUnacknowledgedCount = unacknowledgedCount;
19724
- }
19725
- const staleDirectWorkNote = staleDirectWork.length > 0 ? unacknowledgedCount > 0 && unacknowledgedCount === staleDirectWork.length ? `${unacknowledgedCount} direct dispatch(es) were not acknowledged by the target session \u2014 the session received the agent_command but never transitioned to generating. This is a fresh dispatch failure, not historical noise. Recovery: launch a fresh session on the same node and retry the task, or use mesh_enqueue_task for queue-based assignment.` : unacknowledgedCount > 0 ? `${unacknowledgedCount} of ${staleDirectWork.length} stale direct record(s) are fresh unacknowledged dispatch failures (session still live but never transitioned to generating); the rest are orphaned historical entries whose node/session no longer exists. Fresh unacknowledged dispatches need recovery: launch a fresh session and retry. Orphaned entries are historical evidence only \u2014 not active or unresolved work.` : "These are orphaned ledger entries whose original node or session no longer exists in the live mesh. They are historical/recovery evidence only \u2014 not active or unresolved work. Do not treat staleDirectCount as a status mismatch; use the queue (source: queue) as authoritative for pending/assigned tasks." : void 0;
19726
- if (staleDirectWorkNote) {
19727
- summary.staleDirectNote = staleDirectWorkNote;
19728
- }
19729
- return { activeWork: records, staleDirectWork, staleDirectWorkNote, terminalDirectWork, summary };
19730
- }
19731
- var PRUNABLE_ORPHAN_STALE_REASONS = /* @__PURE__ */ new Set([
19732
- "direct task node is no longer in the live mesh",
19733
- "direct task session is not present in live session records",
19734
- "direct task has no node id"
19735
- ]);
19736
- function classifyStaleDirectForPrune(record, opts = {}) {
19737
- if (record.staleDispatchUnacknowledged === true) return "preserve_unacknowledged";
19738
- if (record.terminal === true) return opts.includeTerminal ? "prunable_terminal" : "preserve_active";
19739
- if (record.staleReason && PRUNABLE_ORPHAN_STALE_REASONS.has(record.staleReason)) return "prunable_orphan";
19740
- return "preserve_active";
19741
- }
19742
- function buildCompactStaleDirectWorkSummary(staleDirectWork, opts = {}) {
19743
- const sampleLimit = Math.max(0, Math.min(10, Math.floor(opts.sampleLimit ?? 3)));
19744
- const reasonCounts = {};
19745
- for (const entry of staleDirectWork) {
19746
- const reason = entry.staleReason || "unknown";
19747
- reasonCounts[reason] = (reasonCounts[reason] || 0) + 1;
19748
- }
19749
- return {
19750
- count: staleDirectWork.length,
19751
- sampleLimit,
19752
- sample: staleDirectWork.slice(0, sampleLimit).map((entry) => ({
19753
- taskId: entry.taskId,
19754
- status: entry.status,
19755
- nodeId: entry.nodeId,
19756
- sessionId: entry.sessionId,
19757
- taskTitle: entry.taskTitle,
19758
- createdAt: entry.createdAt,
19759
- staleReason: entry.staleReason
19760
- })),
19761
- reasonCounts,
19762
- detailHint: opts.detailHint || "Stale direct records are historical recovery evidence only. Use mesh_task_history for full ledger details, or request includeStaleDirectWorkDetails when supported by the caller.",
19763
- ...opts.note ? { note: opts.note } : {}
19764
- };
19765
- }
19766
-
19767
- // src/index.ts
20245
+ init_mesh_active_work();
19768
20246
  init_mesh_refine_status();
19769
20247
  init_mesh_host_ownership();
19770
20248
  init_mesh_events();
@@ -19881,8 +20359,8 @@ var P2pRelayFailureError = class extends Error {
19881
20359
  };
19882
20360
 
19883
20361
  // src/config/state-store.ts
19884
- var import_fs11 = require("fs");
19885
- var import_path9 = require("path");
20362
+ var import_fs12 = require("fs");
20363
+ var import_path10 = require("path");
19886
20364
  init_config();
19887
20365
  var DEFAULT_STATE = {
19888
20366
  recentActivity: [],
@@ -19896,7 +20374,7 @@ function isPlainObject2(value) {
19896
20374
  return !!value && typeof value === "object" && !Array.isArray(value);
19897
20375
  }
19898
20376
  function getStatePath() {
19899
- return (0, import_path9.join)(getConfigDir(), "state.json");
20377
+ return (0, import_path10.join)(getConfigDir(), "state.json");
19900
20378
  }
19901
20379
  function normalizeState(raw) {
19902
20380
  const parsed = isPlainObject2(raw) ? raw : {};
@@ -19932,11 +20410,11 @@ function normalizeState(raw) {
19932
20410
  }
19933
20411
  function loadState() {
19934
20412
  const statePath = getStatePath();
19935
- if (!(0, import_fs11.existsSync)(statePath)) {
20413
+ if (!(0, import_fs12.existsSync)(statePath)) {
19936
20414
  return { ...DEFAULT_STATE };
19937
20415
  }
19938
20416
  try {
19939
- const raw = (0, import_fs11.readFileSync)(statePath, "utf-8");
20417
+ const raw = (0, import_fs12.readFileSync)(statePath, "utf-8");
19940
20418
  return normalizeState(JSON.parse(raw));
19941
20419
  } catch {
19942
20420
  return { ...DEFAULT_STATE };
@@ -19945,7 +20423,7 @@ function loadState() {
19945
20423
  function saveState(state) {
19946
20424
  const statePath = getStatePath();
19947
20425
  const normalized = normalizeState(state);
19948
- (0, import_fs11.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
20426
+ (0, import_fs12.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
19949
20427
  }
19950
20428
  function resetState() {
19951
20429
  saveState({ ...DEFAULT_STATE });
@@ -19954,7 +20432,7 @@ function resetState() {
19954
20432
  // src/detection/ide-detector.ts
19955
20433
  var import_child_process2 = require("child_process");
19956
20434
  var import_util = require("util");
19957
- var import_fs12 = require("fs");
20435
+ var import_fs13 = require("fs");
19958
20436
  var import_os2 = require("os");
19959
20437
  var path13 = __toESM(require("path"));
19960
20438
 
@@ -20035,7 +20513,7 @@ function findCliCommand(command) {
20035
20513
  if (path13.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
20036
20514
  const candidate = trimmed.startsWith("~") ? path13.join((0, import_os2.homedir)(), trimmed.slice(1)) : trimmed;
20037
20515
  const resolved = path13.isAbsolute(candidate) ? candidate : path13.resolve(candidate);
20038
- return (0, import_fs12.existsSync)(resolved) ? resolved : null;
20516
+ return (0, import_fs13.existsSync)(resolved) ? resolved : null;
20039
20517
  }
20040
20518
  const isWin = (0, import_os2.platform)() === "win32";
20041
20519
  const paths = (process.env.PATH || "").split(isWin ? ";" : ":");
@@ -20045,8 +20523,8 @@ function findCliCommand(command) {
20045
20523
  for (const ext of exes) {
20046
20524
  const fullPath = path13.join(p, trimmed + ext);
20047
20525
  try {
20048
- if ((0, import_fs12.existsSync)(fullPath)) {
20049
- const stat2 = (0, import_fs12.statSync)(fullPath);
20526
+ if ((0, import_fs13.existsSync)(fullPath)) {
20527
+ const stat2 = (0, import_fs13.statSync)(fullPath);
20050
20528
  if (stat2.isFile() && (isWin || stat2.mode & 73)) {
20051
20529
  return fullPath;
20052
20530
  }
@@ -20064,9 +20542,9 @@ function checkPathExists(paths) {
20064
20542
  if (normalized.includes("*")) {
20065
20543
  const username = home.split(/[\\/]/).pop() || "";
20066
20544
  const resolved = normalized.replace("*", username);
20067
- if ((0, import_fs12.existsSync)(resolved)) return resolved;
20545
+ if ((0, import_fs13.existsSync)(resolved)) return resolved;
20068
20546
  } else {
20069
- if ((0, import_fs12.existsSync)(normalized)) return normalized;
20547
+ if ((0, import_fs13.existsSync)(normalized)) return normalized;
20070
20548
  }
20071
20549
  }
20072
20550
  return null;
@@ -20080,7 +20558,7 @@ async function detectIDEs(providerLoader) {
20080
20558
  let resolvedCli = cliPath;
20081
20559
  if (!resolvedCli && appPath && os30 === "darwin") {
20082
20560
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
20083
- if ((0, import_fs12.existsSync)(bundledCli)) resolvedCli = bundledCli;
20561
+ if ((0, import_fs13.existsSync)(bundledCli)) resolvedCli = bundledCli;
20084
20562
  }
20085
20563
  if (!resolvedCli && appPath && os30 === "win32") {
20086
20564
  const { dirname: dirname17 } = await import("path");
@@ -20093,7 +20571,7 @@ async function detectIDEs(providerLoader) {
20093
20571
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
20094
20572
  ];
20095
20573
  for (const c of candidates) {
20096
- if ((0, import_fs12.existsSync)(c)) {
20574
+ if ((0, import_fs13.existsSync)(c)) {
20097
20575
  resolvedCli = c;
20098
20576
  break;
20099
20577
  }
@@ -21638,8 +22116,8 @@ init_contracts();
21638
22116
  // src/providers/status-monitor.ts
21639
22117
  var DEFAULT_MONITOR_CONFIG = {
21640
22118
  approvalAlert: true,
21641
- longGeneratingAlert: true,
21642
- longGeneratingThresholdSec: 180,
22119
+ noProgressAlert: true,
22120
+ noProgressThresholdSec: 180,
21643
22121
  // 3 minutes
21644
22122
  alertCooldownSec: 60
21645
22123
  // 1 minute cooldown
@@ -21648,7 +22126,7 @@ var StatusMonitor = class {
21648
22126
  config;
21649
22127
  lastAlertTime = /* @__PURE__ */ new Map();
21650
22128
  generatingStartTimes = /* @__PURE__ */ new Map();
21651
- longGeneratingAlerted = /* @__PURE__ */ new Map();
22129
+ noProgressAlerted = /* @__PURE__ */ new Map();
21652
22130
  lastProgressFingerprint = /* @__PURE__ */ new Map();
21653
22131
  lastProgressChangeAt = /* @__PURE__ */ new Map();
21654
22132
  constructor(config) {
@@ -21666,7 +22144,7 @@ var StatusMonitor = class {
21666
22144
  * Check status transition → return notification event array.
21667
22145
  * Called from each onTick() or detectStatusTransition().
21668
22146
  */
21669
- check(agentKey, status, now, progressFingerprint) {
22147
+ check(agentKey, status, now, progressFingerprint, approvalPending) {
21670
22148
  const events = [];
21671
22149
  if (this.config.approvalAlert && status === "waiting_approval") {
21672
22150
  if (this.shouldAlert(agentKey + ":approval", now)) {
@@ -21679,9 +22157,16 @@ var StatusMonitor = class {
21679
22157
  }
21680
22158
  }
21681
22159
  if (status === "generating" || status === "streaming") {
22160
+ if (approvalPending) {
22161
+ this.generatingStartTimes.set(agentKey, now);
22162
+ this.lastProgressFingerprint.set(agentKey, progressFingerprint ?? "");
22163
+ this.lastProgressChangeAt.set(agentKey, now);
22164
+ this.noProgressAlerted.set(agentKey, false);
22165
+ return events;
22166
+ }
21682
22167
  if (!this.generatingStartTimes.has(agentKey)) {
21683
22168
  this.generatingStartTimes.set(agentKey, now);
21684
- this.longGeneratingAlerted.set(agentKey, false);
22169
+ this.noProgressAlerted.set(agentKey, false);
21685
22170
  const initialFingerprint = progressFingerprint ?? "";
21686
22171
  this.lastProgressFingerprint.set(agentKey, initialFingerprint);
21687
22172
  this.lastProgressChangeAt.set(agentKey, now);
@@ -21691,17 +22176,17 @@ var StatusMonitor = class {
21691
22176
  if (previousFingerprint !== currentFingerprint) {
21692
22177
  this.lastProgressFingerprint.set(agentKey, currentFingerprint);
21693
22178
  this.lastProgressChangeAt.set(agentKey, now);
21694
- this.longGeneratingAlerted.set(agentKey, false);
22179
+ this.noProgressAlerted.set(agentKey, false);
21695
22180
  }
21696
- if (this.config.longGeneratingAlert) {
22181
+ if (this.config.noProgressAlert) {
21697
22182
  const progressChangedAt = this.lastProgressChangeAt.get(agentKey) || this.generatingStartTimes.get(agentKey);
21698
22183
  const elapsedSec = Math.round((now - progressChangedAt) / 1e3);
21699
- const alreadyAlerted = this.longGeneratingAlerted.get(agentKey) === true;
21700
- if (elapsedSec > this.config.longGeneratingThresholdSec && !alreadyAlerted) {
21701
- if (this.shouldAlert(agentKey + ":long_gen", now)) {
21702
- this.longGeneratingAlerted.set(agentKey, true);
22184
+ const alreadyAlerted = this.noProgressAlerted.get(agentKey) === true;
22185
+ if (elapsedSec > this.config.noProgressThresholdSec && !alreadyAlerted) {
22186
+ if (this.shouldAlert(agentKey + ":no_progress", now)) {
22187
+ this.noProgressAlerted.set(agentKey, true);
21703
22188
  events.push({
21704
- type: "monitor:long_generating",
22189
+ type: "monitor:no_progress",
21705
22190
  agentKey,
21706
22191
  elapsedSec,
21707
22192
  timestamp: now,
@@ -21712,7 +22197,7 @@ var StatusMonitor = class {
21712
22197
  }
21713
22198
  } else {
21714
22199
  this.generatingStartTimes.delete(agentKey);
21715
- this.longGeneratingAlerted.delete(agentKey);
22200
+ this.noProgressAlerted.delete(agentKey);
21716
22201
  this.lastProgressFingerprint.delete(agentKey);
21717
22202
  this.lastProgressChangeAt.delete(agentKey);
21718
22203
  }
@@ -21731,7 +22216,7 @@ var StatusMonitor = class {
21731
22216
  reset(agentKey) {
21732
22217
  if (agentKey) {
21733
22218
  this.generatingStartTimes.delete(agentKey);
21734
- this.longGeneratingAlerted.delete(agentKey);
22219
+ this.noProgressAlerted.delete(agentKey);
21735
22220
  this.lastProgressFingerprint.delete(agentKey);
21736
22221
  this.lastProgressChangeAt.delete(agentKey);
21737
22222
  for (const k of this.lastAlertTime.keys()) {
@@ -21739,7 +22224,7 @@ var StatusMonitor = class {
21739
22224
  }
21740
22225
  } else {
21741
22226
  this.generatingStartTimes.clear();
21742
- this.longGeneratingAlerted.clear();
22227
+ this.noProgressAlerted.clear();
21743
22228
  this.lastProgressFingerprint.clear();
21744
22229
  this.lastProgressChangeAt.clear();
21745
22230
  this.lastAlertTime.clear();
@@ -23562,8 +24047,8 @@ var ExtensionProviderInstance = class {
23562
24047
  this.settings = context.settings || {};
23563
24048
  this.monitor.updateConfig({
23564
24049
  approvalAlert: this.settings.approvalAlert !== false,
23565
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
23566
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
24050
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
24051
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
23567
24052
  });
23568
24053
  }
23569
24054
  async onTick() {
@@ -23654,8 +24139,8 @@ var ExtensionProviderInstance = class {
23654
24139
  this.settings = { ...this.settings, ...newSettings };
23655
24140
  this.monitor.updateConfig({
23656
24141
  approvalAlert: this.settings.approvalAlert !== false,
23657
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
23658
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
24142
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
24143
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
23659
24144
  });
23660
24145
  }
23661
24146
  /** Query UUID instanceId */
@@ -23715,7 +24200,8 @@ var ExtensionProviderInstance = class {
23715
24200
  phase: agentStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
23716
24201
  });
23717
24202
  const agentKey = `${this.type}:ext`;
23718
- const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
24203
+ const approvalPending = agentStatus === "waiting_approval";
24204
+ const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint, approvalPending);
23719
24205
  for (const me of monitorEvents) {
23720
24206
  this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
23721
24207
  }
@@ -23930,7 +24416,7 @@ init_contracts();
23930
24416
  var CHAT_CONTRACT_VERSION_V1 = "1.0";
23931
24417
 
23932
24418
  // src/providers/read-chat-contract.ts
23933
- var VALID_STATUSES = ["idle", "generating", "waiting_approval", "error", "panel_hidden", "starting", "streaming", "long_generating"];
24419
+ var VALID_STATUSES = ["idle", "generating", "waiting_approval", "error", "panel_hidden", "starting", "streaming", "no_progress", "long_generating"];
23934
24420
  var VALID_ROLES = ["user", "assistant", "system", "human"];
23935
24421
  var VALID_BUBBLE_STATES = ["draft", "streaming", "final", "removed"];
23936
24422
  var VALID_TURN_STATUSES = ["open", "waiting_approval", "complete", "error"];
@@ -24197,8 +24683,8 @@ var IdeProviderInstance = class {
24197
24683
  this.settings = context.settings || {};
24198
24684
  this.monitor.updateConfig({
24199
24685
  approvalAlert: this.settings.approvalAlert !== false,
24200
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
24201
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
24686
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
24687
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
24202
24688
  });
24203
24689
  }
24204
24690
  async onTick() {
@@ -24325,8 +24811,8 @@ var IdeProviderInstance = class {
24325
24811
  this.settings = { ...this.settings, ...newSettings };
24326
24812
  this.monitor.updateConfig({
24327
24813
  approvalAlert: this.settings.approvalAlert !== false,
24328
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
24329
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
24814
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
24815
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
24330
24816
  });
24331
24817
  }
24332
24818
  // ─── Extension manage ─────────────────────────────
@@ -24457,7 +24943,7 @@ var IdeProviderInstance = class {
24457
24943
  const persistedMessages = chat.messages || messages;
24458
24944
  if (persistedMessages.length > 0) {
24459
24945
  let toSave = persistedMessages;
24460
- if (chat.status === "generating" || chat.status === "long_generating") {
24946
+ if (chat.status === "generating" || chat.status === "no_progress" || chat.status === "long_generating") {
24461
24947
  const lastIdx = toSave.length - 1;
24462
24948
  if (lastIdx >= 0 && toSave[lastIdx].role === "assistant") {
24463
24949
  toSave = toSave.slice(0, lastIdx);
@@ -24527,7 +25013,8 @@ var IdeProviderInstance = class {
24527
25013
  if (rawAgentStatus === "waiting_approval" && autoApproveActive && !this.autoApproveBusy) {
24528
25014
  this.autoApproveViaScript(chatData);
24529
25015
  }
24530
- const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
25016
+ const approvalPending = rawAgentStatus === "waiting_approval";
25017
+ const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint, approvalPending);
24531
25018
  for (const me of monitorEvents) {
24532
25019
  this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
24533
25020
  }
@@ -27084,7 +27571,7 @@ function normalizeReadChatCommandStatus(status, activeModal) {
27084
27571
  }
27085
27572
  }
27086
27573
  function isGeneratingLikeStatus(status) {
27087
- return status === "generating" || status === "streaming" || status === "long_generating" || status === "starting";
27574
+ return status === "generating" || status === "streaming" || status === "no_progress" || status === "long_generating" || status === "starting";
27088
27575
  }
27089
27576
  function hasVisibleAssistantMessage(messages) {
27090
27577
  if (!Array.isArray(messages)) return false;
@@ -31117,7 +31604,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
31117
31604
  var os19 = __toESM(require("os"));
31118
31605
  var path26 = __toESM(require("path"));
31119
31606
  var crypto5 = __toESM(require("crypto"));
31120
- var import_fs14 = require("fs");
31607
+ var import_fs15 = require("fs");
31121
31608
  var import_child_process6 = require("child_process");
31122
31609
  var import_chalk = __toESM(require("chalk"));
31123
31610
  init_provider_cli_adapter();
@@ -33762,7 +34249,7 @@ function hasNonEmptyCliModalButtons(activeModal) {
33762
34249
  return Array.isArray(buttons) && buttons.some((button) => String(button || "").trim().length > 0);
33763
34250
  }
33764
34251
  function isCliGeneratingLikeStatus(status) {
33765
- return status === "generating" || status === "streaming" || status === "long_generating" || status === "starting";
34252
+ return status === "generating" || status === "streaming" || status === "no_progress" || status === "long_generating" || status === "starting";
33766
34253
  }
33767
34254
  function buildCliStructuredInputPrompt(input, options = {}) {
33768
34255
  const promptParts = [];
@@ -33964,8 +34451,8 @@ var CliProviderInstance = class _CliProviderInstance {
33964
34451
  this.adapter.updateRuntimeSettings?.(this.settings);
33965
34452
  this.monitor.updateConfig({
33966
34453
  approvalAlert: this.settings.approvalAlert !== false,
33967
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
33968
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
34454
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
34455
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
33969
34456
  });
33970
34457
  if (context.serverConn) {
33971
34458
  this.adapter.setServerConn(context.serverConn);
@@ -34112,7 +34599,7 @@ var CliProviderInstance = class _CliProviderInstance {
34112
34599
  if (parsedMessages.length > 0) {
34113
34600
  const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
34114
34601
  let messagesToSave = parsedMessages;
34115
- if (!suppressStaleParsedBusyStatus && (parsedChatStatus === "generating" || parsedChatStatus === "long_generating")) {
34602
+ if (!suppressStaleParsedBusyStatus && (parsedChatStatus === "generating" || parsedChatStatus === "no_progress" || parsedChatStatus === "long_generating")) {
34116
34603
  const lastIdx = messagesToSave.length - 1;
34117
34604
  if (lastIdx >= 0 && messagesToSave[lastIdx]?.role === "assistant") {
34118
34605
  messagesToSave = messagesToSave.slice(0, lastIdx);
@@ -34240,8 +34727,8 @@ var CliProviderInstance = class _CliProviderInstance {
34240
34727
  this.adapter.updateRuntimeSettings?.(this.settings);
34241
34728
  this.monitor.updateConfig({
34242
34729
  approvalAlert: this.settings.approvalAlert !== false,
34243
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
34244
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
34730
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
34731
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
34245
34732
  });
34246
34733
  }
34247
34734
  /**
@@ -34973,10 +35460,11 @@ var CliProviderInstance = class _CliProviderInstance {
34973
35460
  phase: newStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
34974
35461
  });
34975
35462
  const agentKey = `${this.type}:cli`;
34976
- const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
35463
+ const approvalPending = rawStatus === "waiting_approval";
35464
+ const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint, approvalPending);
34977
35465
  const monitorParsedStatus = parsedStatus;
34978
35466
  for (const me of monitorEvents) {
34979
- if (me.type === "monitor:long_generating" && this.completionHasFinalAssistantMessage(monitorParsedStatus?.messages) && !this.hasAdapterPendingResponse() && !hasNonEmptyCliModalButtons(monitorParsedStatus?.activeModal ?? monitorParsedStatus?.modal)) {
35467
+ if (me.type === "monitor:no_progress" && this.completionHasFinalAssistantMessage(monitorParsedStatus?.messages) && !this.hasAdapterPendingResponse() && !hasNonEmptyCliModalButtons(monitorParsedStatus?.activeModal ?? monitorParsedStatus?.modal)) {
34980
35468
  this.pushEvent({
34981
35469
  event: "agent:generating_completed",
34982
35470
  chatTitle,
@@ -34987,7 +35475,7 @@ var CliProviderInstance = class _CliProviderInstance {
34987
35475
  providerType: this.type,
34988
35476
  sessionId: this.instanceId,
34989
35477
  providerSessionId: this.providerSessionId || null,
34990
- reconciliationReason: "long_generating_monitor_final_summary",
35478
+ reconciliationReason: "no_progress_monitor_final_summary",
34991
35479
  finalAssistantPresent: true
34992
35480
  }
34993
35481
  });
@@ -35664,8 +36152,8 @@ var AcpProviderInstance = class {
35664
36152
  this.settings = context.settings || {};
35665
36153
  this.monitor.updateConfig({
35666
36154
  approvalAlert: this.settings.approvalAlert !== false,
35667
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
35668
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
36155
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
36156
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
35669
36157
  });
35670
36158
  await this.spawnAgent();
35671
36159
  }
@@ -35961,8 +36449,8 @@ var AcpProviderInstance = class {
35961
36449
  this.settings = { ...this.settings, ...newSettings };
35962
36450
  this.monitor.updateConfig({
35963
36451
  approvalAlert: this.settings.approvalAlert !== false,
35964
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
35965
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
36452
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
36453
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
35966
36454
  });
35967
36455
  this.log.info(`[${this.type}] Settings updated: ${Object.keys(newSettings).join(", ")}`);
35968
36456
  }
@@ -36669,7 +37157,8 @@ ${rawInput}` : rawInput;
36669
37157
  this.lastStatus = newStatus;
36670
37158
  }
36671
37159
  const agentKey = `${this.type}:acp`;
36672
- const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
37160
+ const approvalPending = newStatus === "waiting_approval";
37161
+ const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint, approvalPending);
36673
37162
  for (const me of monitorEvents) {
36674
37163
  this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
36675
37164
  }
@@ -36731,7 +37220,7 @@ function commandExists(command) {
36731
37220
  const trimmed = command.trim();
36732
37221
  if (!trimmed) return false;
36733
37222
  if (isExplicitCommand(trimmed)) {
36734
- return (0, import_fs14.existsSync)(expandExecutable(trimmed));
37223
+ return (0, import_fs15.existsSync)(expandExecutable(trimmed));
36735
37224
  }
36736
37225
  try {
36737
37226
  (0, import_child_process6.execFileSync)(process.platform === "win32" ? "where" : "which", [trimmed], {
@@ -36743,7 +37232,7 @@ function commandExists(command) {
36743
37232
  return false;
36744
37233
  }
36745
37234
  }
36746
- var BUSY_AGENT_STATUSES = /* @__PURE__ */ new Set(["generating", "running", "streaming", "starting", "busy", "waiting", "waiting_approval", "long_generating"]);
37235
+ var BUSY_AGENT_STATUSES = /* @__PURE__ */ new Set(["generating", "running", "streaming", "starting", "busy", "waiting", "waiting_approval", "no_progress", "long_generating"]);
36747
37236
  var ZERO_MESSAGE_STARTING_SEND_WAIT_MS = 2e3;
36748
37237
  function normalizeAgentStatus(value) {
36749
37238
  return typeof value === "string" ? value.trim().toLowerCase() : "";
@@ -36867,10 +37356,10 @@ function hasConfigOverride(args, key) {
36867
37356
  }
36868
37357
  function ensureEmptyDelegatedMcpConfig(workspace) {
36869
37358
  const baseDir = path26.join(os19.tmpdir(), "adhdev-delegated-agent-empty-mcp");
36870
- (0, import_fs14.mkdirSync)(baseDir, { recursive: true });
37359
+ (0, import_fs15.mkdirSync)(baseDir, { recursive: true });
36871
37360
  const workspaceHash = crypto5.createHash("sha256").update(path26.resolve(workspace || os19.tmpdir())).digest("hex").slice(0, 16);
36872
37361
  const filePath = path26.join(baseDir, `${workspaceHash}.json`);
36873
- (0, import_fs14.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
37362
+ (0, import_fs15.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
36874
37363
  return filePath;
36875
37364
  }
36876
37365
  function buildCoordinatorDelegatedCliLaunchOptions(input) {
@@ -41608,6 +42097,7 @@ function getAvailableIdeIds() {
41608
42097
  init_config();
41609
42098
  init_cli_detector();
41610
42099
  init_git_status();
42100
+ init_change_impact_config();
41611
42101
  init_dist();
41612
42102
  init_logger();
41613
42103
 
@@ -41756,7 +42246,7 @@ function getRecentCommands(count = 50) {
41756
42246
  cleanOldFiles();
41757
42247
 
41758
42248
  // src/commands/router.ts
41759
- var yaml3 = __toESM(require("js-yaml"));
42249
+ var yaml4 = __toESM(require("js-yaml"));
41760
42250
  init_logger();
41761
42251
 
41762
42252
  // src/logging/log-tail-reader.ts
@@ -42142,8 +42632,8 @@ function buildPreviewFreshness(repoRoot) {
42142
42632
  init_mesh_refine_status();
42143
42633
 
42144
42634
  // src/mesh/mesh-init.ts
42145
- var import_fs15 = require("fs");
42146
- var import_path10 = require("path");
42635
+ var import_fs16 = require("fs");
42636
+ var import_path11 = require("path");
42147
42637
  var MESH_INIT_REFINE_CONFIG_PATH = MESH_REFINE_CONFIG_LOCATIONS[0];
42148
42638
  var MESH_INIT_WORKTREE_BOOTSTRAP_CONFIG_PATH = MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS[0];
42149
42639
  var CANDIDATE_STALE_INPUTS = [
@@ -42157,22 +42647,22 @@ var CANDIDATE_STALE_INPUTS = [
42157
42647
  "requirements.txt"
42158
42648
  ];
42159
42649
  function writeConfigFile(workspace, relativePath, config) {
42160
- const target = (0, import_path10.join)(workspace, relativePath);
42161
- (0, import_fs15.mkdirSync)((0, import_path10.dirname)(target), { recursive: true });
42162
- (0, import_fs15.writeFileSync)(target, `${JSON.stringify(config, null, 2)}
42650
+ const target = (0, import_path11.join)(workspace, relativePath);
42651
+ (0, import_fs16.mkdirSync)((0, import_path11.dirname)(target), { recursive: true });
42652
+ (0, import_fs16.writeFileSync)(target, `${JSON.stringify(config, null, 2)}
42163
42653
  `, "utf-8");
42164
42654
  return target;
42165
42655
  }
42166
42656
  function suggestMeshWorktreeBootstrapConfig(workspace) {
42167
42657
  const commands = [];
42168
- const hasPackageJson = (0, import_fs15.existsSync)((0, import_path10.join)(workspace, "package.json"));
42169
- const hasNpmLock = (0, import_fs15.existsSync)((0, import_path10.join)(workspace, "package-lock.json"));
42658
+ const hasPackageJson = (0, import_fs16.existsSync)((0, import_path11.join)(workspace, "package.json"));
42659
+ const hasNpmLock = (0, import_fs16.existsSync)((0, import_path11.join)(workspace, "package-lock.json"));
42170
42660
  if (hasPackageJson) {
42171
42661
  commands.push(
42172
42662
  hasNpmLock ? { command: "npm", args: ["ci"] } : { command: "npm", args: ["install"] }
42173
42663
  );
42174
42664
  }
42175
- const staleInputs = CANDIDATE_STALE_INPUTS.filter((relative5) => (0, import_fs15.existsSync)((0, import_path10.join)(workspace, relative5)));
42665
+ const staleInputs = CANDIDATE_STALE_INPUTS.filter((relative5) => (0, import_fs16.existsSync)((0, import_path11.join)(workspace, relative5)));
42176
42666
  if (!commands.length) {
42177
42667
  return { commands, staleInputs };
42178
42668
  }
@@ -42240,7 +42730,7 @@ function runMeshInit(mesh, workspace, detected, options = {}) {
42240
42730
  }
42241
42731
  function applyConfigSuggestion(input) {
42242
42732
  const { workspace, relativePath, existing, suggestedConfig, validate, write, overwrite } = input;
42243
- const absolute = (0, import_path10.join)(workspace, relativePath);
42733
+ const absolute = (0, import_path11.join)(workspace, relativePath);
42244
42734
  if (existing !== void 0 && !overwrite) {
42245
42735
  return { path: absolute, relativePath, written: false, skippedReason: "already_exists", config: existing };
42246
42736
  }
@@ -42965,7 +43455,7 @@ async function maybeRunDaemonUpgradeHelperFromEnv() {
42965
43455
  init_mesh_work_queue();
42966
43456
  init_repo_mesh_types();
42967
43457
  var import_os3 = require("os");
42968
- var import_path11 = require("path");
43458
+ var import_path12 = require("path");
42969
43459
  var fs26 = __toESM(require("fs"));
42970
43460
  var import_node_child_process6 = require("child_process");
42971
43461
  var CHANNEL_NPM_TAG = { stable: "latest", preview: "next" };
@@ -44438,14 +44928,14 @@ function collectFastForwardGitlinkPaths(repoRoot, baseHead, branchHead) {
44438
44928
  const baseCommit = readTreeObject(repoRoot, baseHead, path42);
44439
44929
  const branchCommit = readTreeObject(repoRoot, branchHead, path42);
44440
44930
  if (!baseCommit || !branchCommit) return false;
44441
- return isSubmoduleFastForward((0, import_path11.resolve)(repoRoot, path42), baseCommit, branchCommit);
44931
+ return isSubmoduleFastForward((0, import_path12.resolve)(repoRoot, path42), baseCommit, branchCommit);
44442
44932
  });
44443
44933
  }
44444
44934
  function evaluateGitlinkTrivialFastForward(repoRoot, baseHead, branchHead) {
44445
44935
  const changedGitlinks = readChangedGitlinkPaths(repoRoot, baseHead, branchHead).map((path42) => {
44446
44936
  const baseCommit = readTreeObject(repoRoot, baseHead, path42);
44447
44937
  const branchCommit = readTreeObject(repoRoot, branchHead, path42);
44448
- const submoduleRepoPath = (0, import_path11.resolve)(repoRoot, path42);
44938
+ const submoduleRepoPath = (0, import_path12.resolve)(repoRoot, path42);
44449
44939
  const fastForward = !!baseCommit && !!branchCommit && isSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit);
44450
44940
  return { path: path42, baseCommit, branchCommit, fastForward };
44451
44941
  });
@@ -44500,7 +44990,7 @@ function buildTreeWithGitlinksEqualized(repoRoot, commitish, paths, placeholderC
44500
44990
  if (!tree) return void 0;
44501
44991
  const updates = paths.map((path42) => `160000 commit ${placeholderCommit} ${path42}`).join("\n");
44502
44992
  if (!updates) return tree;
44503
- const tmpIndex = (0, import_path11.join)(resolveGitDir(repoRoot), `adhdev-refine-eq-${commitish.slice(0, 12)}.index`);
44993
+ const tmpIndex = (0, import_path12.join)(resolveGitDir(repoRoot), `adhdev-refine-eq-${commitish.slice(0, 12)}.index`);
44504
44994
  const env = { ...process.env, GIT_INDEX_FILE: tmpIndex };
44505
44995
  try {
44506
44996
  (0, import_node_child_process6.execFileSync)("git", ["read-tree", tree], { cwd: repoRoot, env, stdio: "ignore" });
@@ -44575,7 +45065,7 @@ function synthesizeTrivialFastForwardMergeTree(repoRoot, baseHead, branchHead, g
44575
45065
  if (!contentTree) return void 0;
44576
45066
  const updates = branchGitlinks.map((entry) => `160000 commit ${entry.branchCommit} ${entry.path}`).join("\n");
44577
45067
  if (!updates) return contentTree;
44578
- const tmpIndex = (0, import_path11.join)(resolveGitDir(repoRoot), `adhdev-refine-ff-${baseHead.slice(0, 12)}-${branchHead.slice(0, 12)}.index`);
45068
+ const tmpIndex = (0, import_path12.join)(resolveGitDir(repoRoot), `adhdev-refine-ff-${baseHead.slice(0, 12)}-${branchHead.slice(0, 12)}.index`);
44579
45069
  const env = { ...process.env, GIT_INDEX_FILE: tmpIndex };
44580
45070
  try {
44581
45071
  (0, import_node_child_process6.execFileSync)("git", ["read-tree", contentTree], { cwd: repoRoot, env, stdio: "ignore" });
@@ -44713,7 +45203,7 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
44713
45203
  return match ? { commit: match[1], path: match[2] } : null;
44714
45204
  }).filter((entry) => !!entry);
44715
45205
  for (const gitlink of gitlinks) {
44716
- const submodulePath = (0, import_path11.resolve)(repoRoot, gitlink.path);
45206
+ const submodulePath = (0, import_path12.resolve)(repoRoot, gitlink.path);
44717
45207
  const entry = {
44718
45208
  path: gitlink.path,
44719
45209
  commit: gitlink.commit,
@@ -44741,7 +45231,7 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
44741
45231
  try {
44742
45232
  const imported = await importCommitFromWorktreeSubmodule(
44743
45233
  submodulePath,
44744
- (0, import_path11.resolve)(options.worktreeRoot, gitlink.path),
45234
+ (0, import_path12.resolve)(options.worktreeRoot, gitlink.path),
44745
45235
  gitlink.commit
44746
45236
  );
44747
45237
  if (imported) {
@@ -44953,19 +45443,19 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
44953
45443
  ...extras
44954
45444
  });
44955
45445
  const isPackageManagerValidation = (candidate) => {
44956
- const command = (0, import_path11.basename)(candidate.command).replace(/\.(?:cmd|exe)$/i, "");
45446
+ const command = (0, import_path12.basename)(candidate.command).replace(/\.(?:cmd|exe)$/i, "");
44957
45447
  return ["npm", "pnpm", "yarn", "bun"].includes(command) && candidate.args.some((arg) => arg === "run" || arg === "test" || arg === "exec");
44958
45448
  };
44959
45449
  const dependenciesLikelyMissing = (cwd) => {
44960
- if (!fs26.existsSync((0, import_path11.join)(cwd, "package.json"))) return false;
44961
- if (fs26.existsSync((0, import_path11.join)(cwd, "node_modules"))) return false;
44962
- return ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"].some((lock) => fs26.existsSync((0, import_path11.join)(cwd, lock)));
45450
+ if (!fs26.existsSync((0, import_path12.join)(cwd, "package.json"))) return false;
45451
+ if (fs26.existsSync((0, import_path12.join)(cwd, "node_modules"))) return false;
45452
+ return ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"].some((lock) => fs26.existsSync((0, import_path12.join)(cwd, lock)));
44963
45453
  };
44964
45454
  if (runLegacyBootstrapCommands) {
44965
45455
  summary.bootstrap = { stage: "legacy" };
44966
45456
  for (const candidate of selection.bootstrapCommands) {
44967
45457
  const startedAt = Date.now();
44968
- const cwd = candidate.cwd ? (0, import_path11.resolve)(workspace, candidate.cwd) : workspace;
45458
+ const cwd = candidate.cwd ? (0, import_path12.resolve)(workspace, candidate.cwd) : workspace;
44969
45459
  const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
44970
45460
  try {
44971
45461
  const result = await execFileAsync4(candidate.command, candidate.args, {
@@ -44993,7 +45483,7 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
44993
45483
  }
44994
45484
  for (const candidate of selection.commands) {
44995
45485
  const startedAt = Date.now();
44996
- const cwd = candidate.cwd ? (0, import_path11.resolve)(workspace, candidate.cwd) : workspace;
45486
+ const cwd = candidate.cwd ? (0, import_path12.resolve)(workspace, candidate.cwd) : workspace;
44997
45487
  const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
44998
45488
  const bootstrapProvidedDependencies = summary.bootstrap?.stage === "cached" || summary.bootstrap?.stage === "ran" || summary.bootstrap?.stage === "legacy";
44999
45489
  if (!bootstrapProvidedDependencies && isPackageManagerValidation(candidate) && dependenciesLikelyMissing(cwd)) {
@@ -45039,7 +45529,7 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
45039
45529
  return summary;
45040
45530
  }
45041
45531
  function loadYamlModule() {
45042
- return yaml3;
45532
+ return yaml4;
45043
45533
  }
45044
45534
  function getMcpServersKey(format) {
45045
45535
  return format === "hermes_config_yaml" ? "mcp_servers" : "mcpServers";
@@ -45056,13 +45546,13 @@ function serializeMeshCoordinatorMcpConfig(config, format) {
45056
45546
  }
45057
45547
  function resolveHermesUserHome() {
45058
45548
  const explicitHome = process.env.HERMES_HOME?.trim();
45059
- return explicitHome || (0, import_path11.join)((0, import_os3.homedir)(), ".hermes");
45549
+ return explicitHome || (0, import_path12.join)((0, import_os3.homedir)(), ".hermes");
45060
45550
  }
45061
45551
  function loadHermesCoordinatorBaseConfig(targetConfigPath) {
45062
45552
  const sourceHome = resolveHermesUserHome();
45063
- const sourceConfigPath = (0, import_path11.join)(sourceHome, "config.yaml");
45553
+ const sourceConfigPath = (0, import_path12.join)(sourceHome, "config.yaml");
45064
45554
  if (!fs26.existsSync(sourceConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
45065
- if ((0, import_path11.resolve)(sourceConfigPath) === (0, import_path11.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
45555
+ if ((0, import_path12.resolve)(sourceConfigPath) === (0, import_path12.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
45066
45556
  const parsed = parseMeshCoordinatorMcpConfig(fs26.readFileSync(sourceConfigPath, "utf-8"), "hermes_config_yaml");
45067
45557
  const { mcp_servers: _mcpServers, ...baseConfig } = parsed;
45068
45558
  return { config: baseConfig, sourceHome, sourceConfigPath };
@@ -45096,10 +45586,10 @@ function stripHermesCoordinatorTempModelProviderOverrides(config) {
45096
45586
  return sanitized;
45097
45587
  }
45098
45588
  function copyHermesCoordinatorCredentialFiles(sourceHome, targetHome) {
45099
- if ((0, import_path11.resolve)(sourceHome) === (0, import_path11.resolve)(targetHome)) return;
45589
+ if ((0, import_path12.resolve)(sourceHome) === (0, import_path12.resolve)(targetHome)) return;
45100
45590
  for (const fileName of [".env", "auth.json"]) {
45101
- const sourcePath = (0, import_path11.join)(sourceHome, fileName);
45102
- const targetPath = (0, import_path11.join)(targetHome, fileName);
45591
+ const sourcePath = (0, import_path12.join)(sourceHome, fileName);
45592
+ const targetPath = (0, import_path12.join)(targetHome, fileName);
45103
45593
  if (!fs26.existsSync(sourcePath)) continue;
45104
45594
  try {
45105
45595
  fs26.copyFileSync(sourcePath, targetPath);
@@ -45601,7 +46091,7 @@ var DaemonCommandRouter = class {
45601
46091
  }
45602
46092
  const { resolveWorktreePath: resolveWorktreePath2, listWorktrees: listWorktrees2, removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
45603
46093
  const normalizePath = (value) => {
45604
- const resolved = (0, import_path11.resolve)(value);
46094
+ const resolved = (0, import_path12.resolve)(value);
45605
46095
  try {
45606
46096
  return fs26.realpathSync(resolved);
45607
46097
  } catch {
@@ -48781,6 +49271,42 @@ ${hintLines.join("\n")}` : "",
48781
49271
  note: "Suggestions are heuristic scaffold only; Refinery will not execute them until saved into repo mesh/refine config."
48782
49272
  };
48783
49273
  }
49274
+ case "get_mesh_change_impact_config_schema": {
49275
+ return {
49276
+ success: true,
49277
+ schema: CHANGE_IMPACT_CONFIG_SCHEMA,
49278
+ locations: CHANGE_IMPACT_CONFIG_LOCATIONS,
49279
+ sourceOfTruth: "repo change-impact config",
49280
+ heuristicRole: "suggestions_only_not_execution_path",
49281
+ note: "Declarative config only \u2014 JSON/YAML are parsed but never executed. Defines which package/file changes require a daemon rebuild/restart vs. a web-only redeploy vs. nothing."
49282
+ };
49283
+ }
49284
+ case "validate_mesh_change_impact_config": {
49285
+ const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
49286
+ if (args?.config !== void 0) {
49287
+ const validation = validateChangeImpactConfig(args.config, "inline");
49288
+ return { success: validation.valid, source: "inline", sourceType: "mesh_policy", ...validation };
49289
+ }
49290
+ const loaded = loadChangeImpactConfig(workspace);
49291
+ if (loaded.sourceType === "repo_file") {
49292
+ const validation = validateChangeImpactConfig(loaded.config, loaded.source);
49293
+ return { success: validation.valid, ...loaded, ...validation };
49294
+ }
49295
+ return {
49296
+ success: false,
49297
+ ...loaded,
49298
+ valid: false,
49299
+ errors: [loaded.error || "repo change-impact config unavailable"]
49300
+ };
49301
+ }
49302
+ case "suggest_mesh_change_impact_config": {
49303
+ const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
49304
+ return {
49305
+ success: true,
49306
+ ...suggestChangeImpactConfig(workspace),
49307
+ note: "Suggestions are heuristic scaffold only; the draft must be reviewed and saved into repo change-impact config before it takes effect. Nothing is executed."
49308
+ };
49309
+ }
48784
49310
  case "mesh_init": {
48785
49311
  const workspace = typeof args?.workspace === "string" && args.workspace.trim() ? args.workspace.trim() : process.cwd();
48786
49312
  const mesh = args?.inlineMesh || {};
@@ -49572,7 +50098,7 @@ ${ptyResult.output.slice(-2e3)}`);
49572
50098
  };
49573
50099
  }
49574
50100
  if (cliType === "codex-cli") {
49575
- const repoMcpConfigPath = (0, import_path11.join)(workspace, ".mcp.json");
50101
+ const repoMcpConfigPath = (0, import_path12.join)(workspace, ".mcp.json");
49576
50102
  if (fs26.existsSync(repoMcpConfigPath)) {
49577
50103
  try {
49578
50104
  const repoMcpConfig = parseMeshCoordinatorMcpConfig(
@@ -49697,7 +50223,7 @@ ${ptyResult.output.slice(-2e3)}`);
49697
50223
  workspace
49698
50224
  };
49699
50225
  }
49700
- const { existsSync: existsSync45, readFileSync: readFileSync36, writeFileSync: writeFileSync23, copyFileSync: copyFileSync4, mkdirSync: mkdirSync21 } = await import("fs");
50226
+ const { existsSync: existsSync46, readFileSync: readFileSync37, writeFileSync: writeFileSync23, copyFileSync: copyFileSync4, mkdirSync: mkdirSync21 } = await import("fs");
49701
50227
  const { dirname: dirname17 } = await import("path");
49702
50228
  const mcpConfigPath = coordinatorSetup.configPath;
49703
50229
  const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
@@ -49740,14 +50266,14 @@ ${ptyResult.output.slice(-2e3)}`);
49740
50266
  if (hermesManualFallback) return returnManualFallback(message);
49741
50267
  return { success: false, code: "mesh_coordinator_config_write_failed", error: message, meshId, cliType, workspace };
49742
50268
  }
49743
- const hadExistingMcpConfig = existsSync45(mcpConfigPath);
50269
+ const hadExistingMcpConfig = existsSync46(mcpConfigPath);
49744
50270
  let existingMcpConfig = hermesBaseConfig?.config || {};
49745
50271
  if (hermesBaseConfig) {
49746
50272
  copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname17(mcpConfigPath));
49747
50273
  }
49748
50274
  if (hadExistingMcpConfig) {
49749
50275
  try {
49750
- const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync36(mcpConfigPath, "utf-8"), configFormat);
50276
+ const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync37(mcpConfigPath, "utf-8"), configFormat);
49751
50277
  const existingCoordinatorConfig = hermesManualFallback ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig) : parsedExistingMcpConfig;
49752
50278
  existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
49753
50279
  copyFileSync4(mcpConfigPath, mcpConfigPath + ".backup");
@@ -50246,7 +50772,7 @@ ${ptyResult.output.slice(-2e3)}`);
50246
50772
  const { deriveMeshReviewInboxItems: deriveMeshReviewInboxItems2 } = await Promise.resolve().then(() => (init_mesh_review_inbox(), mesh_review_inbox_exports));
50247
50773
  const { readLedgerEntries: readLedgerEntries2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
50248
50774
  const { getGitDiffSummary: getGitDiffSummary2 } = await Promise.resolve().then(() => (init_git_diff(), git_diff_exports));
50249
- const { existsSync: existsSync45 } = await import("fs");
50775
+ const { existsSync: existsSync46 } = await import("fs");
50250
50776
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
50251
50777
  const mesh = meshRecord?.mesh;
50252
50778
  if (!mesh) return { success: false, error: "Mesh not found" };
@@ -50265,7 +50791,7 @@ ${ptyResult.output.slice(-2e3)}`);
50265
50791
  const derivation = deriveMeshReviewInboxItems2({ nodes: nodeStatuses, ledgerEntries });
50266
50792
  for (const item of derivation.items) {
50267
50793
  const workspace = item.workspace;
50268
- if (!workspace || !existsSync45(workspace)) continue;
50794
+ if (!workspace || !existsSync46(workspace)) continue;
50269
50795
  const baseRef = item.defaultBranch ? `origin/${item.defaultBranch}` : "origin/main";
50270
50796
  try {
50271
50797
  const diffResult = await getGitDiffSummary2(workspace, { baseRef, maxFiles: 100 });
@@ -50439,7 +50965,7 @@ var DaemonStatusReporter = class {
50439
50965
  case "agent:waiting_approval":
50440
50966
  case "agent:generating_completed":
50441
50967
  case "agent:stopped":
50442
- case "monitor:long_generating":
50968
+ case "monitor:no_progress":
50443
50969
  return value;
50444
50970
  default:
50445
50971
  return null;
@@ -50984,7 +51510,7 @@ var ProviderStreamAdapter = class {
50984
51510
  }
50985
51511
  const validated = validateReadChatResultPayload(data, `${this.agentType} readChat`);
50986
51512
  const validatedStatus = validated.status;
50987
- const streamStatus = validatedStatus === "generating" || validatedStatus === "long_generating" ? "streaming" : validatedStatus;
51513
+ const streamStatus = validatedStatus === "generating" || validatedStatus === "no_progress" || validatedStatus === "long_generating" ? "streaming" : validatedStatus;
50988
51514
  const state = {
50989
51515
  agentType: this.agentType,
50990
51516
  agentName: this.agentName,
@@ -58903,6 +59429,8 @@ var V1_CONTRACT_VERSION = "1.0.0";
58903
59429
  AcpProviderInstance,
58904
59430
  AgentStreamPoller,
58905
59431
  BUILTIN_CHAT_MESSAGE_KINDS,
59432
+ CHANGE_IMPACT_CONFIG_LOCATIONS,
59433
+ CHANGE_IMPACT_CONFIG_SCHEMA,
58906
59434
  CHAT_MESSAGE_ACTIVITY_SOURCES,
58907
59435
  CHAT_MESSAGE_AUDIENCES,
58908
59436
  CHAT_MESSAGE_INTERNAL_SOURCES,
@@ -59093,6 +59621,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
59093
59621
  getSessionHostSurfaceKind,
59094
59622
  getSessionRecoveryContext,
59095
59623
  getWorkspaceState,
59624
+ globToRegExp,
59096
59625
  handleGitCommand,
59097
59626
  hasCdpManager,
59098
59627
  hasPendingDependents,
@@ -59126,6 +59655,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
59126
59655
  listMeshMissionSummaries,
59127
59656
  listMeshes,
59128
59657
  listWorktrees,
59658
+ loadChangeImpactConfig,
59129
59659
  loadConfig,
59130
59660
  loadMeshCoordinatorRegistry,
59131
59661
  loadMeshRefineConfig,
@@ -59166,6 +59696,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
59166
59696
  prepareSessionChatTailUpdate,
59167
59697
  prepareSessionModalUpdate,
59168
59698
  probeCdpPort,
59699
+ pruneStaleDirectDispatches,
59169
59700
  queuePendingMeshCoordinatorEvent,
59170
59701
  readAntigravityCliSession,
59171
59702
  readCachedInlineMeshActiveSessionDetails,
@@ -59219,6 +59750,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
59219
59750
  spawnDetachedDaemonUpgradeHelper,
59220
59751
  startDaemonDevSupport,
59221
59752
  startLocalIpcServer,
59753
+ suggestChangeImpactConfig,
59222
59754
  suggestMeshRefineConfig,
59223
59755
  summarizeGitStatus,
59224
59756
  summarizeMeshAsyncRefineJobs,
@@ -59235,6 +59767,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
59235
59767
  updateTaskStatus,
59236
59768
  upsertMeshMission,
59237
59769
  upsertSavedProviderSession,
59770
+ validateChangeImpactConfig,
59238
59771
  validateCliProviderManifest,
59239
59772
  validateFsmSpec,
59240
59773
  validateMeshRefineConfig,