@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.mjs CHANGED
@@ -308,10 +308,10 @@ function readInjected(value) {
308
308
  }
309
309
  function getDaemonBuildInfo() {
310
310
  if (cached) return cached;
311
- const commit = readInjected(true ? "993e3922de8abe554822450e2b4c26c2c9f5f64a" : void 0) ?? "unknown";
312
- const commitShort = readInjected(true ? "993e3922" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
313
- const version = readInjected(true ? "0.9.82-rc.327" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
314
- const builtAt = readInjected(true ? "2026-06-19T12:31:25.870Z" : void 0);
311
+ const commit = readInjected(true ? "9277ba79593a0feae0e17de0204856825a199ebe" : void 0) ?? "unknown";
312
+ const commitShort = readInjected(true ? "9277ba79" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
313
+ const version = readInjected(true ? "0.9.82-rc.329" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
314
+ const builtAt = readInjected(true ? "2026-06-19T15:56:44.269Z" : void 0);
315
315
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
316
316
  return cached;
317
317
  }
@@ -322,6 +322,247 @@ var init_build_info = __esm({
322
322
  }
323
323
  });
324
324
 
325
+ // src/git/change-impact-config.ts
326
+ import { existsSync, readdirSync, readFileSync, statSync } from "fs";
327
+ import { join } from "path";
328
+ import * as yaml from "js-yaml";
329
+ function isRecord(value) {
330
+ return !!value && typeof value === "object" && !Array.isArray(value);
331
+ }
332
+ function isStringArray(value) {
333
+ return Array.isArray(value) && value.every((v) => typeof v === "string" && v.length > 0);
334
+ }
335
+ function validateTarget(value, key, errors) {
336
+ if (!isRecord(value)) {
337
+ errors.push(`impactTargets.${key} must be an object`);
338
+ return void 0;
339
+ }
340
+ const { recommendedCommand } = value;
341
+ if (typeof recommendedCommand !== "string" || !recommendedCommand.length) {
342
+ errors.push(`impactTargets.${key}.recommendedCommand must be a non-empty string`);
343
+ return void 0;
344
+ }
345
+ for (const k of Object.keys(value)) {
346
+ if (k !== "recommendedCommand") errors.push(`impactTargets.${key}.${k} is not a recognized field (only recommendedCommand)`);
347
+ }
348
+ return { recommendedCommand };
349
+ }
350
+ function validateChangeImpactConfig(raw, source = "inline") {
351
+ const errors = [];
352
+ if (!isRecord(raw)) {
353
+ return { valid: false, errors: [`${source}: config must be an object`] };
354
+ }
355
+ const config = {};
356
+ if (raw.daemonRuntimePackages !== void 0) {
357
+ if (isStringArray(raw.daemonRuntimePackages)) config.daemonRuntimePackages = [...raw.daemonRuntimePackages];
358
+ else errors.push("daemonRuntimePackages must be an array of non-empty strings");
359
+ }
360
+ if (raw.webOnlyPackages !== void 0) {
361
+ if (isStringArray(raw.webOnlyPackages)) config.webOnlyPackages = [...raw.webOnlyPackages];
362
+ else errors.push("webOnlyPackages must be an array of non-empty strings");
363
+ }
364
+ if (raw.nonRuntimeRootFilePatterns !== void 0) {
365
+ if (isStringArray(raw.nonRuntimeRootFilePatterns)) config.nonRuntimeRootFilePatterns = [...raw.nonRuntimeRootFilePatterns];
366
+ else errors.push("nonRuntimeRootFilePatterns must be an array of non-empty strings");
367
+ }
368
+ if (raw.impactTargets !== void 0) {
369
+ if (!isRecord(raw.impactTargets)) {
370
+ errors.push("impactTargets must be an object");
371
+ } else {
372
+ const targets = {};
373
+ for (const key of Object.keys(raw.impactTargets)) {
374
+ if (key !== "daemon" && key !== "web" && key !== "none") {
375
+ errors.push(`impactTargets.${key} is not a recognized impact kind (daemon|web|none)`);
376
+ continue;
377
+ }
378
+ const target = validateTarget(raw.impactTargets[key], key, errors);
379
+ if (target) targets[key] = target;
380
+ }
381
+ if (Object.keys(targets).length) config.impactTargets = targets;
382
+ }
383
+ }
384
+ for (const key of Object.keys(raw)) {
385
+ if (!["daemonRuntimePackages", "webOnlyPackages", "nonRuntimeRootFilePatterns", "impactTargets"].includes(key)) {
386
+ errors.push(`unknown config key '${key}'`);
387
+ }
388
+ }
389
+ return { valid: errors.length === 0, errors, config: errors.length === 0 ? config : void 0 };
390
+ }
391
+ function parseConfigText(path42, text) {
392
+ if (/\.json$/i.test(path42)) return JSON.parse(text);
393
+ return yaml.load(text);
394
+ }
395
+ function loadChangeImpactConfig(repoRoot) {
396
+ for (const relative5 of CHANGE_IMPACT_CONFIG_LOCATIONS) {
397
+ const configPath = join(repoRoot, relative5);
398
+ if (!existsSync(configPath)) continue;
399
+ try {
400
+ const text = readFileSync(configPath, "utf-8");
401
+ let mtimeMs = 0;
402
+ try {
403
+ mtimeMs = statSync(configPath).mtimeMs;
404
+ } catch {
405
+ mtimeMs = text.length;
406
+ }
407
+ const parsed = parseConfigText(configPath, text);
408
+ const validation = validateChangeImpactConfig(parsed, relative5);
409
+ if (!validation.valid) {
410
+ return {
411
+ source: relative5,
412
+ sourceType: "invalid",
413
+ path: configPath,
414
+ error: validation.errors.join("; "),
415
+ sourceKey: `invalid:${configPath}:${mtimeMs}`
416
+ };
417
+ }
418
+ return {
419
+ config: validation.config,
420
+ source: relative5,
421
+ sourceType: "repo_file",
422
+ path: configPath,
423
+ sourceKey: `file:${configPath}:${mtimeMs}`
424
+ };
425
+ } catch (error) {
426
+ return {
427
+ source: relative5,
428
+ sourceType: "invalid",
429
+ path: configPath,
430
+ error: error?.message || String(error),
431
+ sourceKey: `error:${configPath}`
432
+ };
433
+ }
434
+ }
435
+ return {
436
+ source: "unavailable",
437
+ sourceType: "unavailable",
438
+ error: `No change-impact config found. Checked: ${CHANGE_IMPACT_CONFIG_LOCATIONS.join(", ")}`,
439
+ sourceKey: "unavailable"
440
+ };
441
+ }
442
+ function globToRegExp(pattern) {
443
+ let out = "";
444
+ for (let i = 0; i < pattern.length; i++) {
445
+ const ch = pattern[i];
446
+ if (ch === "*") {
447
+ if (pattern[i + 1] === "*") {
448
+ out += ".*";
449
+ i++;
450
+ if (pattern[i + 1] === "/") i++;
451
+ } else {
452
+ out += "[^/]*";
453
+ }
454
+ } else if (ch === "?") {
455
+ out += "[^/]";
456
+ } else if (".+^${}()|[]\\".includes(ch)) {
457
+ out += "\\" + ch;
458
+ } else {
459
+ out += ch;
460
+ }
461
+ }
462
+ return new RegExp(`^${out}$`);
463
+ }
464
+ function listPackageDirs(packagesRoot) {
465
+ try {
466
+ return readdirSync(packagesRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
467
+ } catch {
468
+ return [];
469
+ }
470
+ }
471
+ function suggestChangeImpactConfig(repoRoot) {
472
+ const notes = [];
473
+ const daemon = /* @__PURE__ */ new Set();
474
+ const web = /* @__PURE__ */ new Set();
475
+ const unclassified = [];
476
+ const roots = ["packages", join("oss", "packages")];
477
+ for (const rel of roots) {
478
+ const packagesRoot = join(repoRoot, rel);
479
+ if (!existsSync(packagesRoot)) continue;
480
+ for (const name of listPackageDirs(packagesRoot)) {
481
+ if (/(^web[-.]|[-.]web$)/i.test(name) || /dashboard|frontend|ui$/i.test(name)) {
482
+ web.add(name);
483
+ } else {
484
+ daemon.add(name);
485
+ }
486
+ }
487
+ }
488
+ const daemonRuntimePackages = [...daemon].sort();
489
+ const webOnlyPackages = [...web].sort();
490
+ if (!daemonRuntimePackages.length && !webOnlyPackages.length) {
491
+ notes.push("No packages/ or oss/packages/ directories found \u2014 defaulting to an empty draft you should fill in by hand.");
492
+ } else {
493
+ if (daemonRuntimePackages.length) notes.push(`Classified ${daemonRuntimePackages.length} package(s) as daemon-runtime (change \u2192 rebuild/redeploy + restart).`);
494
+ if (webOnlyPackages.length) notes.push(`Classified ${webOnlyPackages.length} web-* package(s) as web-only (change \u2192 web redeploy, no daemon restart).`);
495
+ notes.push("Heuristic only: confirm each package actually matches its bucket before saving.");
496
+ }
497
+ const nonRuntimeRootFilePatterns = [
498
+ "*.md",
499
+ "docs/**",
500
+ "LICENSE",
501
+ "LICENSE.*",
502
+ ".gitignore"
503
+ ];
504
+ notes.push("nonRuntimeRootFilePatterns lists root files that demonstrably cannot change daemon runtime behavior; extend with your repo markers.");
505
+ const suggestedConfig = {
506
+ ...daemonRuntimePackages.length ? { daemonRuntimePackages } : {},
507
+ ...webOnlyPackages.length ? { webOnlyPackages } : {},
508
+ nonRuntimeRootFilePatterns,
509
+ impactTargets: {
510
+ daemon: { recommendedCommand: "rebuild + redeploy the daemon, then restart it" },
511
+ web: { recommendedCommand: "redeploy the web app (no daemon restart required)" },
512
+ none: { recommendedCommand: "no action required" }
513
+ }
514
+ };
515
+ return {
516
+ suggestedConfig,
517
+ notes,
518
+ discoveredPackages: { daemon: daemonRuntimePackages, web: webOnlyPackages, unclassified }
519
+ };
520
+ }
521
+ var CHANGE_IMPACT_CONFIG_LOCATIONS, CHANGE_IMPACT_CONFIG_SCHEMA;
522
+ var init_change_impact_config = __esm({
523
+ "src/git/change-impact-config.ts"() {
524
+ "use strict";
525
+ CHANGE_IMPACT_CONFIG_LOCATIONS = [
526
+ ".adhdev/change-impact.json",
527
+ ".adhdev/change-impact.yaml",
528
+ ".adhdev/change-impact.yml",
529
+ ".adhdev/repo-mesh-change-impact.json",
530
+ ".adhdev/repo-mesh-change-impact.yaml",
531
+ ".adhdev/repo-mesh-change-impact.yml"
532
+ ];
533
+ CHANGE_IMPACT_CONFIG_SCHEMA = {
534
+ $schema: "https://json-schema.org/draft/2020-12/schema",
535
+ title: "ADHDev Change Impact Config",
536
+ type: "object",
537
+ additionalProperties: false,
538
+ properties: {
539
+ daemonRuntimePackages: { type: "array", items: { type: "string", minLength: 1 } },
540
+ webOnlyPackages: { type: "array", items: { type: "string", minLength: 1 } },
541
+ nonRuntimeRootFilePatterns: { type: "array", items: { type: "string", minLength: 1 } },
542
+ impactTargets: {
543
+ type: "object",
544
+ additionalProperties: false,
545
+ properties: {
546
+ daemon: { $ref: "#/$defs/target" },
547
+ web: { $ref: "#/$defs/target" },
548
+ none: { $ref: "#/$defs/target" }
549
+ }
550
+ }
551
+ },
552
+ $defs: {
553
+ target: {
554
+ type: "object",
555
+ additionalProperties: false,
556
+ required: ["recommendedCommand"],
557
+ properties: {
558
+ recommendedCommand: { type: "string", minLength: 1 }
559
+ }
560
+ }
561
+ }
562
+ };
563
+ }
564
+ });
565
+
325
566
  // src/git/git-status.ts
326
567
  function isTransientGitFailure(error) {
327
568
  return error.reason === "timeout" || error.reason === "git_command_failed";
@@ -397,16 +638,34 @@ async function collectGitRepoStatus(repo, includeSubmodules, lastCheckedAt, opti
397
638
  ...daemonBuildBehind ? { daemonBuildBehind } : {}
398
639
  };
399
640
  }
400
- function isNonRuntimeRootFile(file) {
641
+ function resolveChangeImpactPolicy(config) {
642
+ const daemonRuntimePackages = new Set(
643
+ config?.daemonRuntimePackages && config.daemonRuntimePackages.length ? config.daemonRuntimePackages : DEFAULT_DAEMON_RUNTIME_PACKAGES
644
+ );
645
+ const webOnlyPackages = new Set(
646
+ config?.webOnlyPackages && config.webOnlyPackages.length ? config.webOnlyPackages : DEFAULT_WEB_ONLY_PACKAGES
647
+ );
648
+ const nonRuntimeRootFilePatterns = (config?.nonRuntimeRootFilePatterns || []).map(globToRegExp);
649
+ const impactTargets = {
650
+ daemon: config?.impactTargets?.daemon ?? DEFAULT_IMPACT_TARGETS.daemon,
651
+ web: config?.impactTargets?.web ?? DEFAULT_IMPACT_TARGETS.web,
652
+ none: config?.impactTargets?.none ?? DEFAULT_IMPACT_TARGETS.none
653
+ };
654
+ return { daemonRuntimePackages, webOnlyPackages, nonRuntimeRootFilePatterns, impactTargets };
655
+ }
656
+ function isNonRuntimeRootFile(file, policy) {
401
657
  const base = file.slice(file.lastIndexOf("/") + 1);
402
658
  if (/^\.(?:verify|marker|converge|ff-verify|patch-equiv|live-verify)\b/i.test(base)) return true;
403
659
  if (/(?:^|\/)docs\//i.test(file)) return true;
404
660
  if (/^(?:README|CHANGELOG|LICENSE|NOTICE|AUTHORS|CONTRIBUTING|CODEOWNERS)(?:\.[A-Za-z0-9]+)?$/i.test(base)) {
405
661
  return true;
406
662
  }
663
+ for (const re of policy.nonRuntimeRootFilePatterns) {
664
+ if (re.test(file)) return true;
665
+ }
407
666
  return false;
408
667
  }
409
- async function classifyDaemonBuildChange(repoPath, buildCommit, options) {
668
+ async function classifyDaemonBuildChange(repoPath, buildCommit, options, policy) {
410
669
  try {
411
670
  const diff = await runGit(repoPath, ["diff", "--name-only", `${buildCommit}..HEAD`], options);
412
671
  const files = diff.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
@@ -418,21 +677,47 @@ async function classifyDaemonBuildChange(repoPath, buildCommit, options) {
418
677
  for (const file of files) {
419
678
  const match = file.match(/(?:^|\/)packages\/([^/]+)\//);
420
679
  if (!match) {
421
- if (!isNonRuntimeRootFile(file)) sawRuntimeAmbiguousNonPackage = true;
680
+ if (!isNonRuntimeRootFile(file, policy)) sawRuntimeAmbiguousNonPackage = true;
422
681
  continue;
423
682
  }
424
683
  pkgs.add(match[1]);
425
684
  }
426
685
  const affectedPackages = [...pkgs].sort();
427
- const allBenign = !sawRuntimeAmbiguousNonPackage && affectedPackages.every((p) => WEB_ONLY_PACKAGES.has(p) && !DAEMON_RUNTIME_PACKAGES.has(p));
686
+ const allBenign = !sawRuntimeAmbiguousNonPackage && affectedPackages.every((p) => policy.webOnlyPackages.has(p) && !policy.daemonRuntimePackages.has(p));
428
687
  return { isDaemonAffecting: !allBenign, affectedPackages };
429
688
  } catch {
430
689
  return { isDaemonAffecting: true, affectedPackages: [] };
431
690
  }
432
691
  }
692
+ function resolveChangeImpactConfigForRepo(repoRoot, options) {
693
+ if (options.changeImpactConfig === null) {
694
+ return { config: null, sourceKey: "forced-default" };
695
+ }
696
+ if (options.changeImpactConfig !== void 0) {
697
+ let key = "injected";
698
+ try {
699
+ key = `injected:${JSON.stringify(options.changeImpactConfig)}`;
700
+ } catch {
701
+ }
702
+ return { config: options.changeImpactConfig, sourceKey: key };
703
+ }
704
+ if (!repoRoot) {
705
+ return { config: null, sourceKey: "no-repo-root" };
706
+ }
707
+ const loaded = loadChangeImpactConfig(repoRoot);
708
+ const cached2 = changeImpactConfigCache.get(repoRoot);
709
+ if (cached2 && cached2.sourceKey === loaded.sourceKey) {
710
+ return { config: cached2.config, sourceKey: loaded.sourceKey };
711
+ }
712
+ const config = loaded.sourceType === "repo_file" ? loaded.config ?? null : null;
713
+ changeImpactConfigCache.set(repoRoot, { sourceKey: loaded.sourceKey, config });
714
+ return { config, sourceKey: loaded.sourceKey };
715
+ }
433
716
  async function detectDaemonBuildBehind(repo, submodules, options) {
434
717
  const build = options.daemonBuildInfo ?? getDaemonBuildInfo();
435
718
  if (!build.commit || build.commit === "unknown") return void 0;
719
+ const { config, sourceKey: configKey } = resolveChangeImpactConfigForRepo(repo.repoRoot, options);
720
+ const policy = resolveChangeImpactPolicy(config);
436
721
  const scopes = [
437
722
  { scope: "root", repoPath: repo.repoRoot || repo.workspace }
438
723
  ];
@@ -446,11 +731,15 @@ async function detectDaemonBuildBehind(repo, submodules, options) {
446
731
  const head = headResult.stdout.trim();
447
732
  if (!head || head === build.commit) continue;
448
733
  await runGit(repoPath, ["merge-base", "--is-ancestor", build.commit, "HEAD"], options);
449
- const { isDaemonAffecting, affectedPackages } = await classifyDaemonBuildChange(
450
- repoPath,
451
- build.commit,
452
- options
453
- );
734
+ const evalKey = `${repoPath}\0${build.commit}\0${head}\0${configKey}`;
735
+ let evaluated = changeImpactEvalCache.get(evalKey);
736
+ if (!evaluated) {
737
+ evaluated = await classifyDaemonBuildChange(repoPath, build.commit, options, policy);
738
+ changeImpactEvalCache.set(evalKey, evaluated);
739
+ }
740
+ const { isDaemonAffecting, affectedPackages } = evaluated;
741
+ const kind = isDaemonAffecting ? "daemon" : affectedPackages.length > 0 ? "web" : "none";
742
+ const target = policy.impactTargets[kind];
454
743
  const scopeLabel = scope === "root" ? "workspace" : scope;
455
744
  const benignDetail = affectedPackages.length > 0 ? `only web packages changed (${affectedPackages.join(", ")})` : "only non-runtime files changed (markers/docs)";
456
745
  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.`;
@@ -461,6 +750,8 @@ async function detectDaemonBuildBehind(repo, submodules, options) {
461
750
  scope,
462
751
  isDaemonAffecting,
463
752
  ...affectedPackages && affectedPackages.length > 0 ? { affectedPackages } : {},
753
+ recommendedAction: kind,
754
+ recommendedCommand: target.recommendedCommand,
464
755
  warning
465
756
  };
466
757
  } catch {
@@ -724,14 +1015,17 @@ async function enrichSubmoduleWorktreeStatus(repo, submodule, options) {
724
1015
  submodule.error = formatGitError(error);
725
1016
  }
726
1017
  }
727
- var lastKnownGoodStatus, DAEMON_RUNTIME_PACKAGES, WEB_ONLY_PACKAGES;
1018
+ var lastKnownGoodStatus, changeImpactEvalCache, changeImpactConfigCache, DEFAULT_DAEMON_RUNTIME_PACKAGES, DEFAULT_WEB_ONLY_PACKAGES, DEFAULT_IMPACT_TARGETS;
728
1019
  var init_git_status = __esm({
729
1020
  "src/git/git-status.ts"() {
730
1021
  "use strict";
731
1022
  init_git_executor();
732
1023
  init_build_info();
1024
+ init_change_impact_config();
733
1025
  lastKnownGoodStatus = /* @__PURE__ */ new Map();
734
- DAEMON_RUNTIME_PACKAGES = /* @__PURE__ */ new Set([
1026
+ changeImpactEvalCache = /* @__PURE__ */ new Map();
1027
+ changeImpactConfigCache = /* @__PURE__ */ new Map();
1028
+ DEFAULT_DAEMON_RUNTIME_PACKAGES = [
735
1029
  "daemon-core",
736
1030
  "daemon-standalone",
737
1031
  "session-host-core",
@@ -741,13 +1035,24 @@ var init_git_status = __esm({
741
1035
  "terminal-mux-cli",
742
1036
  "ghostty-vt-node",
743
1037
  "mcp-server"
744
- ]);
745
- WEB_ONLY_PACKAGES = /* @__PURE__ */ new Set([
1038
+ ];
1039
+ DEFAULT_WEB_ONLY_PACKAGES = [
746
1040
  "web-core",
747
1041
  "web-standalone",
748
1042
  "web-devconsole",
749
1043
  "terminal-render-web"
750
- ]);
1044
+ ];
1045
+ DEFAULT_IMPACT_TARGETS = {
1046
+ daemon: {
1047
+ recommendedCommand: "Redeploy + restart the daemon (a local dist rebuild alone does not update a cloud daemon)."
1048
+ },
1049
+ web: {
1050
+ recommendedCommand: "Redeploy the web app (no daemon restart required)."
1051
+ },
1052
+ none: {
1053
+ recommendedCommand: "No action required."
1054
+ }
1055
+ };
751
1056
  }
752
1057
  });
753
1058
 
@@ -1035,7 +1340,7 @@ __export(git_worktree_exports, {
1035
1340
  });
1036
1341
  import * as path4 from "path";
1037
1342
  import { mkdir } from "fs/promises";
1038
- import { existsSync } from "fs";
1343
+ import { existsSync as existsSync2 } from "fs";
1039
1344
  import { execFile as execFile2 } from "child_process";
1040
1345
  import { promisify as promisify2 } from "util";
1041
1346
  function resolveWorktreePath(repoRoot, meshName, branch) {
@@ -1047,7 +1352,7 @@ function resolveWorktreePath(repoRoot, meshName, branch) {
1047
1352
  async function createWorktree(opts) {
1048
1353
  const { repoRoot, branch, baseBranch, meshName } = opts;
1049
1354
  const targetDir = opts.targetDir || resolveWorktreePath(repoRoot, meshName, branch);
1050
- if (existsSync(targetDir)) {
1355
+ if (existsSync2(targetDir)) {
1051
1356
  throw new Error(`Worktree target directory already exists: ${targetDir}`);
1052
1357
  }
1053
1358
  await mkdir(path4.dirname(targetDir), { recursive: true });
@@ -1066,7 +1371,7 @@ async function createWorktree(opts) {
1066
1371
  } catch (error) {
1067
1372
  const stderr = typeof error.stderr === "string" ? error.stderr : "";
1068
1373
  if (/already exists/i.test(stderr)) {
1069
- if (existsSync(targetDir)) {
1374
+ if (existsSync2(targetDir)) {
1070
1375
  throw new Error(`Worktree target directory was created concurrently: ${targetDir}`);
1071
1376
  }
1072
1377
  throw new Error(`Branch '${branch}' already exists or is checked out in another worktree`);
@@ -1080,7 +1385,7 @@ async function createWorktree(opts) {
1080
1385
  };
1081
1386
  }
1082
1387
  async function removeWorktree(repoRoot, worktreePath, opts = {}) {
1083
- if (!existsSync(worktreePath)) {
1388
+ if (!existsSync2(worktreePath)) {
1084
1389
  await pruneWorktrees(repoRoot);
1085
1390
  return { success: true, removedPath: worktreePath };
1086
1391
  }
@@ -1214,8 +1519,8 @@ __export(config_exports, {
1214
1519
  updateConfig: () => updateConfig
1215
1520
  });
1216
1521
  import { homedir } from "os";
1217
- import { join as join2 } from "path";
1218
- import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync, chmodSync } from "fs";
1522
+ import { join as join3 } from "path";
1523
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync, chmodSync } from "fs";
1219
1524
  import { randomUUID } from "crypto";
1220
1525
  function resolveProviderSourceMode(providerSourceMode, legacyDisableUpstream) {
1221
1526
  if (providerSourceMode === "normal" || providerSourceMode === "no-upstream") {
@@ -1310,25 +1615,25 @@ function ensureMachineId(config) {
1310
1615
  }
1311
1616
  function getConfigDir() {
1312
1617
  const override = process.env.ADHDEV_CONFIG_DIR;
1313
- const dir = override && override.trim() ? override.trim() : join2(homedir(), ".adhdev");
1314
- if (!existsSync2(dir)) {
1618
+ const dir = override && override.trim() ? override.trim() : join3(homedir(), ".adhdev");
1619
+ if (!existsSync3(dir)) {
1315
1620
  mkdirSync(dir, { recursive: true });
1316
1621
  }
1317
1622
  return dir;
1318
1623
  }
1319
1624
  function getDaemonDataDir() {
1320
- const dir = join2(getConfigDir(), "daemon");
1321
- if (!existsSync2(dir)) {
1625
+ const dir = join3(getConfigDir(), "daemon");
1626
+ if (!existsSync3(dir)) {
1322
1627
  mkdirSync(dir, { recursive: true });
1323
1628
  }
1324
1629
  return dir;
1325
1630
  }
1326
1631
  function getConfigPath() {
1327
- return join2(getConfigDir(), "config.json");
1632
+ return join3(getConfigDir(), "config.json");
1328
1633
  }
1329
1634
  function migrateStateToStateFile(raw) {
1330
- const statePath = join2(getConfigDir(), "state.json");
1331
- if (existsSync2(statePath)) return;
1635
+ const statePath = join3(getConfigDir(), "state.json");
1636
+ if (existsSync3(statePath)) return;
1332
1637
  const recentActivity = Array.isArray(raw.recentActivity) ? raw.recentActivity : [];
1333
1638
  const savedProviderSessions = Array.isArray(raw.savedProviderSessions) ? raw.savedProviderSessions : [];
1334
1639
  const legacySessionReads = isPlainObject(raw.recentSessionReads) ? raw.recentSessionReads : {};
@@ -1352,7 +1657,7 @@ function migrateStateToStateFile(raw) {
1352
1657
  }
1353
1658
  function loadConfig() {
1354
1659
  const configPath = getConfigPath();
1355
- if (!existsSync2(configPath)) {
1660
+ if (!existsSync3(configPath)) {
1356
1661
  const initialized = ensureMachineId({ ...DEFAULT_CONFIG });
1357
1662
  try {
1358
1663
  saveConfig(initialized.config);
@@ -1361,7 +1666,7 @@ function loadConfig() {
1361
1666
  return initialized.config;
1362
1667
  }
1363
1668
  try {
1364
- const raw = readFileSync(configPath, "utf-8");
1669
+ const raw = readFileSync2(configPath, "utf-8");
1365
1670
  const parsed = JSON.parse(raw);
1366
1671
  migrateStateToStateFile(parsed);
1367
1672
  const normalizedInput = normalizeConfig(parsed);
@@ -1383,7 +1688,7 @@ function saveConfig(config) {
1383
1688
  const configPath = getConfigPath();
1384
1689
  const dir = getConfigDir();
1385
1690
  const normalized = normalizeConfig(config);
1386
- if (!existsSync2(dir)) {
1691
+ if (!existsSync3(dir)) {
1387
1692
  mkdirSync(dir, { recursive: true, mode: 448 });
1388
1693
  }
1389
1694
  writeFileSync(configPath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
@@ -1535,17 +1840,17 @@ __export(mesh_config_exports, {
1535
1840
  updateMesh: () => updateMesh,
1536
1841
  updateNode: () => updateNode
1537
1842
  });
1538
- import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1539
- import { join as join4 } from "path";
1843
+ import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1844
+ import { join as join5 } from "path";
1540
1845
  import { createHash, randomBytes, randomUUID as randomUUID3 } from "crypto";
1541
1846
  function getMeshConfigPath() {
1542
- return join4(getConfigDir(), "meshes.json");
1847
+ return join5(getConfigDir(), "meshes.json");
1543
1848
  }
1544
1849
  function loadMeshConfig() {
1545
1850
  const path42 = getMeshConfigPath();
1546
- if (!existsSync4(path42)) return { meshes: [] };
1851
+ if (!existsSync5(path42)) return { meshes: [] };
1547
1852
  try {
1548
- const raw = JSON.parse(readFileSync2(path42, "utf-8"));
1853
+ const raw = JSON.parse(readFileSync3(path42, "utf-8"));
1549
1854
  if (!raw || !Array.isArray(raw.meshes)) return { meshes: [] };
1550
1855
  const config = raw;
1551
1856
  const migrated = migrateLoadedMeshConfig(config);
@@ -2248,8 +2553,8 @@ __export(mesh_ledger_exports, {
2248
2553
  readLedgerSlice: () => readLedgerSlice,
2249
2554
  readLedgerSliceFromStore: () => readLedgerSliceFromStore
2250
2555
  });
2251
- import { appendFileSync, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, statSync as statSync2, renameSync, writeFileSync as writeFileSync3 } from "fs";
2252
- import { join as join6 } from "path";
2556
+ import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5, statSync as statSync3, renameSync, writeFileSync as writeFileSync3 } from "fs";
2557
+ import { join as join7 } from "path";
2253
2558
  import { randomUUID as randomUUID4 } from "crypto";
2254
2559
  import { EventEmitter } from "events";
2255
2560
  function isIntentionalCleanupStopEntry(entry) {
@@ -2258,35 +2563,35 @@ function isIntentionalCleanupStopEntry(entry) {
2258
2563
  return payload.intentional === true && (payload.reason === "operator_cleanup" || payload.intentionalStopReason === "operator_cleanup" || payload.source === "mesh_cleanup_sessions" || payload.source === "mesh_remove_node");
2259
2564
  }
2260
2565
  function getLedgerDir() {
2261
- const dir = join6(getConfigDir(), LEDGER_DIR_NAME);
2262
- if (!existsSync5(dir)) {
2566
+ const dir = join7(getConfigDir(), LEDGER_DIR_NAME);
2567
+ if (!existsSync6(dir)) {
2263
2568
  mkdirSync3(dir, { recursive: true, mode: 448 });
2264
2569
  }
2265
2570
  return dir;
2266
2571
  }
2267
2572
  function getLedgerPath(meshId) {
2268
2573
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
2269
- return join6(getLedgerDir(), `${safe}.jsonl`);
2574
+ return join7(getLedgerDir(), `${safe}.jsonl`);
2270
2575
  }
2271
2576
  function getRotatedPath(meshId, index) {
2272
2577
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
2273
- return join6(getLedgerDir(), `${safe}.${index}.jsonl`);
2578
+ return join7(getLedgerDir(), `${safe}.${index}.jsonl`);
2274
2579
  }
2275
2580
  function getArchivePath(meshId) {
2276
2581
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
2277
- return join6(getLedgerDir(), `${safe}.archive.jsonl`);
2582
+ return join7(getLedgerDir(), `${safe}.archive.jsonl`);
2278
2583
  }
2279
2584
  function getRotatedArchivePath(meshId, index) {
2280
2585
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
2281
- return join6(getLedgerDir(), `${safe}.archive.${index}.jsonl`);
2586
+ return join7(getLedgerDir(), `${safe}.archive.${index}.jsonl`);
2282
2587
  }
2283
2588
  function getArchivedCountsPath(meshId) {
2284
2589
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
2285
- return join6(getLedgerDir(), `${safe}.archived-counts.json`);
2590
+ return join7(getLedgerDir(), `${safe}.archived-counts.json`);
2286
2591
  }
2287
2592
  function rotateArchiveFile(meshId, archivePath) {
2288
2593
  let index = 1;
2289
- while (existsSync5(getRotatedArchivePath(meshId, index))) {
2594
+ while (existsSync6(getRotatedArchivePath(meshId, index))) {
2290
2595
  index++;
2291
2596
  if (index > 5) break;
2292
2597
  }
@@ -2300,9 +2605,9 @@ function rotateArchiveFile(meshId, archivePath) {
2300
2605
  }
2301
2606
  function readArchivedCounts(meshId) {
2302
2607
  const path42 = getArchivedCountsPath(meshId);
2303
- if (!existsSync5(path42)) return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: "" };
2608
+ if (!existsSync6(path42)) return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: "" };
2304
2609
  try {
2305
- return JSON.parse(readFileSync4(path42, "utf-8"));
2610
+ return JSON.parse(readFileSync5(path42, "utf-8"));
2306
2611
  } catch {
2307
2612
  return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: "" };
2308
2613
  }
@@ -2341,7 +2646,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
2341
2646
  }
2342
2647
  function compactLedger(meshId) {
2343
2648
  const filePath = getLedgerPath(meshId);
2344
- if (!existsSync5(filePath)) return { archivedCount: 0, retainedCount: 0 };
2649
+ if (!existsSync6(filePath)) return { archivedCount: 0, retainedCount: 0 };
2345
2650
  const cutoff = Date.now() - ARCHIVE_TERMINAL_OLDER_THAN_MS;
2346
2651
  const entries = readLedgerEntries(meshId);
2347
2652
  const keep = [];
@@ -2356,7 +2661,7 @@ function compactLedger(meshId) {
2356
2661
  if (archive.length === 0) return { archivedCount: 0, retainedCount: keep.length };
2357
2662
  const archivePath = getArchivePath(meshId);
2358
2663
  try {
2359
- if (existsSync5(archivePath) && statSync2(archivePath).size > 50 * 1024 * 1024) {
2664
+ if (existsSync6(archivePath) && statSync3(archivePath).size > 50 * 1024 * 1024) {
2360
2665
  rotateArchiveFile(meshId, archivePath);
2361
2666
  }
2362
2667
  const archiveLines = archive.map((e) => JSON.stringify(e)).join("\n") + "\n";
@@ -2506,9 +2811,9 @@ function appendLedgerEntry(meshId, partial) {
2506
2811
  ...partial
2507
2812
  };
2508
2813
  const filePath = getLedgerPath(meshId);
2509
- if (existsSync5(filePath)) {
2814
+ if (existsSync6(filePath)) {
2510
2815
  try {
2511
- const stat2 = statSync2(filePath);
2816
+ const stat2 = statSync3(filePath);
2512
2817
  if (stat2.size >= MAX_FILE_SIZE_BYTES) {
2513
2818
  rotateLedgerFile(meshId, filePath);
2514
2819
  } else if (stat2.size >= COMPACT_THRESHOLD_BYTES) {
@@ -2604,10 +2909,10 @@ function appendRemoteLedgerEntries(meshId, entries) {
2604
2909
  }
2605
2910
  function readLedgerFile(meshId) {
2606
2911
  const filePath = getLedgerPath(meshId);
2607
- if (!existsSync5(filePath)) return [];
2912
+ if (!existsSync6(filePath)) return [];
2608
2913
  let content;
2609
2914
  try {
2610
- content = readFileSync4(filePath, "utf-8");
2915
+ content = readFileSync5(filePath, "utf-8");
2611
2916
  } catch {
2612
2917
  return [];
2613
2918
  }
@@ -2868,7 +3173,7 @@ function getSessionRecoveryContext(meshId, opts) {
2868
3173
  }
2869
3174
  function rotateLedgerFile(meshId, currentPath) {
2870
3175
  let index = 1;
2871
- while (existsSync5(getRotatedPath(meshId, index))) {
3176
+ while (existsSync6(getRotatedPath(meshId, index))) {
2872
3177
  index++;
2873
3178
  if (index > 10) break;
2874
3179
  }
@@ -3539,8 +3844,8 @@ var init_mesh_work_queue = __esm({
3539
3844
  });
3540
3845
 
3541
3846
  // src/mesh/mesh-runtime-store.ts
3542
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync2, statSync as statSync3 } from "fs";
3543
- import { dirname as dirname2, join as join7 } from "path";
3847
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6, renameSync as renameSync2, statSync as statSync4 } from "fs";
3848
+ import { dirname as dirname2, join as join8 } from "path";
3544
3849
  import { createRequire } from "module";
3545
3850
  function loadDatabaseCtor() {
3546
3851
  if (DatabaseCtor) return DatabaseCtor;
@@ -3552,19 +3857,19 @@ function safeMeshId(meshId) {
3552
3857
  return meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
3553
3858
  }
3554
3859
  function legacyQueuePath(meshId) {
3555
- return join7(getLedgerDir(), `${safeMeshId(meshId)}.queue.json`);
3860
+ return join8(getLedgerDir(), `${safeMeshId(meshId)}.queue.json`);
3556
3861
  }
3557
3862
  function meshRuntimeStorePath() {
3558
3863
  const dir = getLedgerDir();
3559
- const nextPath = join7(dir, "mesh-runtime.db");
3560
- if (existsSync6(nextPath)) return nextPath;
3561
- const legacyPath = join7(dir, "beads.db");
3562
- if (!existsSync6(legacyPath)) return nextPath;
3864
+ const nextPath = join8(dir, "mesh-runtime.db");
3865
+ if (existsSync7(nextPath)) return nextPath;
3866
+ const legacyPath = join8(dir, "beads.db");
3867
+ if (!existsSync7(legacyPath)) return nextPath;
3563
3868
  try {
3564
3869
  renameSync2(legacyPath, nextPath);
3565
3870
  for (const suffix of ["-wal", "-shm"]) {
3566
3871
  const legacyCompanion = `${legacyPath}${suffix}`;
3567
- if (existsSync6(legacyCompanion)) {
3872
+ if (existsSync7(legacyCompanion)) {
3568
3873
  renameSync2(legacyCompanion, `${nextPath}${suffix}`);
3569
3874
  }
3570
3875
  }
@@ -3590,7 +3895,7 @@ var init_mesh_runtime_store = __esm({
3590
3895
  // 50 MB
3591
3896
  constructor(dbPath) {
3592
3897
  const dir = dirname2(dbPath);
3593
- if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
3898
+ if (!existsSync7(dir)) mkdirSync4(dir, { recursive: true });
3594
3899
  this.dbPath = dbPath;
3595
3900
  this.db = new (loadDatabaseCtor())(dbPath);
3596
3901
  this.db.pragma("journal_mode = WAL");
@@ -3845,8 +4150,8 @@ var init_mesh_runtime_store = __esm({
3845
4150
  this.walWriteCounter = 0;
3846
4151
  try {
3847
4152
  const walPath = `${this.dbPath}-wal`;
3848
- if (!existsSync6(walPath)) return;
3849
- const size = statSync3(walPath).size;
4153
+ if (!existsSync7(walPath)) return;
4154
+ const size = statSync4(walPath).size;
3850
4155
  if (size < _MeshRuntimeStore.WAL_MAX_BYTES) return;
3851
4156
  process.stderr.write(
3852
4157
  `[adhdev-mesh] WAL file ${Math.round(size / 1024 / 1024)}MB exceeds threshold; forcing checkpoint
@@ -3862,9 +4167,9 @@ var init_mesh_runtime_store = __esm({
3862
4167
  const count = this.db.prepare("SELECT COUNT(*) AS count FROM mesh_queue WHERE mesh_id = ?").get(meshId);
3863
4168
  if (count.count > 0) return;
3864
4169
  const path42 = legacyQueuePath(meshId);
3865
- if (!existsSync6(path42)) return;
4170
+ if (!existsSync7(path42)) return;
3866
4171
  try {
3867
- const entries = JSON.parse(readFileSync5(path42, "utf-8"));
4172
+ const entries = JSON.parse(readFileSync6(path42, "utf-8"));
3868
4173
  if (!Array.isArray(entries)) return;
3869
4174
  const insert = this.db.prepare(`
3870
4175
  INSERT OR REPLACE INTO mesh_queue (
@@ -5705,10 +6010,10 @@ __export(mesh_coordinator_exports, {
5705
6010
  stripCoordinatorWrapperFile: () => stripCoordinatorWrapperFile
5706
6011
  });
5707
6012
  import { createHash as createHash2 } from "crypto";
5708
- import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
6013
+ import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
5709
6014
  import * as os4 from "os";
5710
6015
  import { DEFAULT_SESSION_HOST_COLS, DEFAULT_SESSION_HOST_ROWS } from "@adhdev/session-host-core";
5711
- import { basename as basename2, isAbsolute as isAbsolute4, join as join9, resolve as resolve7 } from "path";
6016
+ import { basename as basename2, isAbsolute as isAbsolute4, join as join10, resolve as resolve7 } from "path";
5712
6017
  function isHermesProvider(provider, cliType) {
5713
6018
  const type = cliType?.trim() || provider?.type?.trim() || "";
5714
6019
  return type === HERMES_CLI_TYPE;
@@ -5728,7 +6033,7 @@ function resolveHermesMeshCoordinatorSetup(options) {
5728
6033
  reason: "Could not resolve the ADHDev MCP server entrypoint and a Node runtime with WebSocket support for daemon IPC mode"
5729
6034
  };
5730
6035
  }
5731
- const configPath = join9(resolveHermesCoordinatorHome(options.meshId, options.workspace), "config.yaml");
6036
+ const configPath = join10(resolveHermesCoordinatorHome(options.meshId, options.workspace), "config.yaml");
5732
6037
  if (!configPath.trim()) {
5733
6038
  return createHermesManualMeshCoordinatorSetup(options.meshId, options.workspace);
5734
6039
  }
@@ -5875,14 +6180,14 @@ function resolveHermesCoordinatorHome(meshId, workspace) {
5875
6180
  const key = `${meshId || "mesh"}
5876
6181
  ${resolve7(workspace || os4.tmpdir())}`;
5877
6182
  const hash = createHash2("sha256").update(key).digest("hex").slice(0, 16);
5878
- return join9(os4.tmpdir(), `adhdev-hermes-mesh-coordinator-${hash}`);
6183
+ return join10(os4.tmpdir(), `adhdev-hermes-mesh-coordinator-${hash}`);
5879
6184
  }
5880
6185
  function resolveMcpConfigPath(configPath, workspace) {
5881
6186
  const trimmed = configPath.trim();
5882
6187
  if (trimmed === "~") return os4.homedir();
5883
- if (trimmed.startsWith("~/")) return join9(os4.homedir(), trimmed.slice(2));
6188
+ if (trimmed.startsWith("~/")) return join10(os4.homedir(), trimmed.slice(2));
5884
6189
  if (isAbsolute4(trimmed)) return trimmed;
5885
- return join9(workspace, trimmed);
6190
+ return join10(workspace, trimmed);
5886
6191
  }
5887
6192
  function resolveAdhdevMcpServerLaunch(options) {
5888
6193
  const directEntryPath = resolveAdhdevMcpEntryPath(options.adhdevMcpEntryPath);
@@ -5953,7 +6258,7 @@ function applyInjectionRule(systemPrompt, injection, ctx) {
5953
6258
  }
5954
6259
  case "context_file": {
5955
6260
  if (!injection.path) return {};
5956
- const target = isAbsolute4(injection.path) ? injection.path : join9(ctx.workspace, injection.path);
6261
+ const target = isAbsolute4(injection.path) ? injection.path : join10(ctx.workspace, injection.path);
5957
6262
  const wrapper = injection.wrapper && injection.wrapper.includes("{prompt}") ? injection.wrapper : "{prompt}";
5958
6263
  const managedNote = "> _Managed by adhdev mesh coordinator \u2014 do not hand-edit this block. Changes inside the sentinels are overwritten on next coordinator launch._";
5959
6264
  const promptWithNote = `${managedNote}
@@ -5962,8 +6267,8 @@ ${systemPrompt}`;
5962
6267
  const rendered = wrapper.replace(/\{prompt\}/g, promptWithNote);
5963
6268
  const sentinel = wrapper.split("{prompt}")[0].trim();
5964
6269
  try {
5965
- if (existsSync8(target)) {
5966
- const existing = readFileSync6(target, "utf-8");
6270
+ if (existsSync9(target)) {
6271
+ const existing = readFileSync7(target, "utf-8");
5967
6272
  if (sentinel && existing.includes(sentinel)) {
5968
6273
  const closing = wrapper.split("{prompt}")[1]?.trim();
5969
6274
  const safeOpen = sentinel.replace(/[.+^${}()|[\]\\]/g, "\\$&");
@@ -5993,8 +6298,8 @@ function stripCoordinatorWrapperFile(filePath) {
5993
6298
  const OPEN = "<!-- adhdev-mesh-coordinator-prompt -->";
5994
6299
  const CLOSE = "<!-- /adhdev-mesh-coordinator-prompt -->";
5995
6300
  try {
5996
- if (!existsSync8(filePath)) return;
5997
- const existing = readFileSync6(filePath, "utf-8");
6301
+ if (!existsSync9(filePath)) return;
6302
+ const existing = readFileSync7(filePath, "utf-8");
5998
6303
  const openIdx = existing.indexOf(OPEN);
5999
6304
  if (openIdx < 0) return;
6000
6305
  const closeIdx = existing.indexOf(CLOSE, openIdx);
@@ -6970,6 +7275,410 @@ var init_dist = __esm({
6970
7275
  }
6971
7276
  });
6972
7277
 
7278
+ // src/mesh/mesh-active-work.ts
7279
+ function readString6(value) {
7280
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
7281
+ }
7282
+ function summarizeMessage(message) {
7283
+ const oneLine2 = message.replace(/\s+/g, " ").trim();
7284
+ const title = oneLine2.length > 96 ? `${oneLine2.slice(0, 93)}...` : oneLine2;
7285
+ return { title: title || "(untitled task)", summary: oneLine2 };
7286
+ }
7287
+ function elapsedSince(value, now) {
7288
+ const started = value ? new Date(value).getTime() : Number.NaN;
7289
+ return Number.isFinite(started) ? Math.max(0, now - started) : 0;
7290
+ }
7291
+ function sessionStatusFromNodes(nodes, nodeId, sessionId) {
7292
+ if (!Array.isArray(nodes)) return {};
7293
+ if (!nodeId) return { staleReason: "direct task has no node id" };
7294
+ const node = nodes.find((item) => meshNodeIdMatches(item, nodeId));
7295
+ if (!node) return { staleReason: "direct task node is no longer in the live mesh" };
7296
+ if (!sessionId) return {};
7297
+ const candidates = [];
7298
+ for (const value of [
7299
+ node.sessions,
7300
+ node.activeSessions,
7301
+ node.active_sessions,
7302
+ node.activeSessionDetails,
7303
+ node.active_session_details,
7304
+ node.sessionDetails,
7305
+ node.session_details,
7306
+ node.lastProbe?.sessions,
7307
+ node.last_probe?.sessions,
7308
+ node.lastProbe?.status?.sessions,
7309
+ node.last_probe?.status?.sessions
7310
+ ]) {
7311
+ if (Array.isArray(value)) candidates.push(...value);
7312
+ }
7313
+ for (const value of [node.activeSession, node.active_session, node.currentSession, node.current_session, node.runtimeSession, node.runtime_session, node.session]) {
7314
+ if (value && typeof value === "object") candidates.push(value);
7315
+ }
7316
+ const session = candidates.find((item) => {
7317
+ if (typeof item === "string") return item === sessionId;
7318
+ const id = readString6(item?.id) || readString6(item?.sessionId) || readString6(item?.session_id) || readString6(item?.runtimeSessionId) || readString6(item?.instanceId);
7319
+ return id === sessionId;
7320
+ });
7321
+ if (!session) return { staleReason: "direct task session is not present in live session records" };
7322
+ if (typeof session === "string") return {};
7323
+ const raw = `${readString6(session.status) || ""} ${readString6(session.lifecycle) || ""} ${readString6(session.state) || ""} ${readString6(session.activeChat?.status) || ""}`.toLowerCase();
7324
+ if (raw.includes("approval")) return { status: "awaiting_approval" };
7325
+ if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return { status: "generating" };
7326
+ if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return { status: "failed" };
7327
+ if (raw.includes("idle") || raw.includes("waiting_input") || raw.includes("ready")) return { status: "idle" };
7328
+ return {};
7329
+ }
7330
+ function isDirectDispatch(entry) {
7331
+ if (entry.kind !== "task_dispatched") return false;
7332
+ const payload = entry.payload || {};
7333
+ if (payload.source === "direct") return true;
7334
+ const via = readString6(payload.via);
7335
+ return Boolean(via && DIRECT_DISPATCH_VIA.has(via) && payload.source !== "queue");
7336
+ }
7337
+ function directDispatchTaskId(entry) {
7338
+ return readString6(entry.payload?.taskId) || entry.id;
7339
+ }
7340
+ function terminalMatchesDispatch(terminal, dispatch, taskId) {
7341
+ const terminalTaskId = readString6(terminal.payload?.taskId);
7342
+ if (terminalTaskId && terminalTaskId === taskId) return true;
7343
+ if (terminalTaskId && terminalTaskId !== taskId) return false;
7344
+ if (dispatch.sessionId && terminal.sessionId === dispatch.sessionId) return true;
7345
+ return Boolean(dispatch.nodeId && terminal.nodeId === dispatch.nodeId && !dispatch.sessionId);
7346
+ }
7347
+ function statusFromTerminal(entry) {
7348
+ if (entry.kind === "task_approval_needed") return "awaiting_approval";
7349
+ if (entry.kind === "task_completed") return "idle";
7350
+ return "failed";
7351
+ }
7352
+ function buildMeshActiveWorkSummary(activeWork) {
7353
+ const statusCounts = {
7354
+ pending: 0,
7355
+ assigned: 0,
7356
+ generating: 0,
7357
+ idle: 0,
7358
+ failed: 0,
7359
+ awaiting_approval: 0
7360
+ };
7361
+ const sourceCounts = { queue: 0, direct: 0 };
7362
+ for (const item of activeWork) {
7363
+ sourceCounts[item.source] += 1;
7364
+ statusCounts[item.status] += 1;
7365
+ }
7366
+ const staleDirectCount = activeWork.filter((item) => item.source === "direct" && item.staleReason).length;
7367
+ const staleDirectUnacknowledgedCount = activeWork.filter((item) => item.source === "direct" && item.staleDispatchUnacknowledged).length;
7368
+ return {
7369
+ totalActiveCount: activeWork.length,
7370
+ queueActiveCount: sourceCounts.queue,
7371
+ directActiveCount: sourceCounts.direct,
7372
+ awaitingApprovalCount: statusCounts.awaiting_approval,
7373
+ generatingCount: statusCounts.generating,
7374
+ failedCount: statusCounts.failed,
7375
+ idleCount: statusCounts.idle,
7376
+ sourceCounts,
7377
+ statusCounts,
7378
+ staleDirectCount,
7379
+ ...staleDirectUnacknowledgedCount > 0 ? { staleDirectUnacknowledgedCount } : {},
7380
+ ...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." } : {}
7381
+ };
7382
+ }
7383
+ function buildMeshActiveWork(opts) {
7384
+ const now = opts.now ?? Date.now();
7385
+ const records = [];
7386
+ const staleDirectWork = [];
7387
+ const terminalDirectWork = [];
7388
+ for (const task of opts.queue || []) {
7389
+ if (task.status !== "pending" && task.status !== "assigned") continue;
7390
+ const { title, summary: summary2 } = summarizeMessage(task.message || "");
7391
+ records.push({
7392
+ taskId: task.id,
7393
+ source: "queue",
7394
+ status: task.status,
7395
+ nodeId: task.assignedNodeId || task.targetNodeId,
7396
+ sessionId: task.assignedSessionId || task.targetSessionId,
7397
+ taskTitle: title,
7398
+ taskSummary: summary2,
7399
+ message: task.message,
7400
+ taskMode: task.taskMode,
7401
+ createdAt: task.createdAt,
7402
+ updatedAt: task.updatedAt,
7403
+ dispatchedAt: task.dispatchTimestamp,
7404
+ elapsedMs: elapsedSince(task.dispatchTimestamp || task.createdAt, now)
7405
+ });
7406
+ }
7407
+ if (opts.directDispatches !== void 0) {
7408
+ const dbTaskIds = new Set(opts.directDispatches.map((d) => d.taskId));
7409
+ for (const dispatch of opts.directDispatches) {
7410
+ const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId ?? void 0, dispatch.sessionId ?? void 0);
7411
+ const dbStatus = dispatch.status;
7412
+ const isTerminal = dbStatus === "completed" || dbStatus === "failed" || dbStatus === "stale";
7413
+ const status = isTerminal ? dbStatus === "completed" ? "idle" : "failed" : live.status || (dbStatus === "acked" ? "generating" : "assigned");
7414
+ const isNoTransition = !isTerminal && !live.status;
7415
+ const isIdleUnacknowledged = status === "idle" && !isTerminal;
7416
+ const ledgerOnlyStaleReason = !isTerminal && (isIdleUnacknowledged || isNoTransition || dispatch.dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
7417
+ const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
7418
+ const { title, summary: summary2 } = summarizeMessage(dispatch.message || "");
7419
+ const record = {
7420
+ taskId: dispatch.taskId,
7421
+ source: "direct",
7422
+ status,
7423
+ nodeId: dispatch.nodeId ?? void 0,
7424
+ sessionId: dispatch.sessionId ?? void 0,
7425
+ providerType: dispatch.providerType ?? void 0,
7426
+ taskTitle: title,
7427
+ taskSummary: summary2,
7428
+ message: dispatch.message,
7429
+ taskMode: dispatch.taskMode ?? void 0,
7430
+ createdAt: dispatch.dispatchedAt,
7431
+ updatedAt: dispatch.updatedAt,
7432
+ dispatchedAt: dispatch.dispatchedAt,
7433
+ elapsedMs: elapsedSince(dispatch.dispatchedAt, now),
7434
+ terminal: isTerminal,
7435
+ terminalKind: isTerminal ? dbStatus === "completed" ? "task_completed" : "task_failed" : void 0,
7436
+ terminalAt: isTerminal ? dispatch.updatedAt : void 0,
7437
+ staleReason: live.staleReason || ledgerOnlyStaleReason,
7438
+ ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
7439
+ };
7440
+ if (isTerminal) {
7441
+ terminalDirectWork.push(record);
7442
+ if (opts.includeTerminalDirect !== true) continue;
7443
+ }
7444
+ if ((live.staleReason || ledgerOnlyStaleReason) && !isTerminal) {
7445
+ staleDirectWork.push(record);
7446
+ continue;
7447
+ }
7448
+ records.push(record);
7449
+ }
7450
+ const ledgerEntries = (opts.ledgerEntries || []).slice().sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
7451
+ const terminals = ledgerEntries.filter((entry) => TERMINAL_LEDGER_KINDS.has(entry.kind) || entry.kind === "task_approval_needed");
7452
+ for (const dispatch of ledgerEntries.filter(isDirectDispatch)) {
7453
+ const taskId = directDispatchTaskId(dispatch);
7454
+ if (dbTaskIds.has(taskId)) continue;
7455
+ const terminal = terminals.filter((entry) => new Date(entry.timestamp).getTime() >= new Date(dispatch.timestamp).getTime()).find((entry) => terminalMatchesDispatch(entry, dispatch, taskId));
7456
+ const terminalStatus = terminal ? statusFromTerminal(terminal) : void 0;
7457
+ const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
7458
+ const status = terminalStatus || live.status || "assigned";
7459
+ const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
7460
+ const dispatchedToIdleSession = dispatch.payload?.dispatchedToIdleSession === true;
7461
+ const isNoTransition = !terminalStatus && !live.status;
7462
+ const isIdleUnacknowledged = status === "idle";
7463
+ const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
7464
+ const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
7465
+ const { title, summary: summary2 } = summarizeMessage(message);
7466
+ const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
7467
+ const record = {
7468
+ taskId,
7469
+ source: "direct",
7470
+ status,
7471
+ nodeId: dispatch.nodeId,
7472
+ sessionId: dispatch.sessionId,
7473
+ providerType: dispatch.providerType || readString6(dispatch.payload?.providerType),
7474
+ taskTitle: readString6(dispatch.payload?.taskTitle) || title,
7475
+ taskSummary: readString6(dispatch.payload?.taskSummary) || summary2,
7476
+ message,
7477
+ taskMode: readString6(dispatch.payload?.taskMode),
7478
+ createdAt: dispatch.timestamp,
7479
+ updatedAt: terminal?.timestamp || dispatch.timestamp,
7480
+ dispatchedAt: dispatch.timestamp,
7481
+ elapsedMs: elapsedSince(dispatch.timestamp, now),
7482
+ terminal: terminalRow,
7483
+ terminalKind: terminal?.kind,
7484
+ terminalAt: terminal?.timestamp,
7485
+ staleReason: live.staleReason || ledgerOnlyStaleReason,
7486
+ ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
7487
+ };
7488
+ if (terminalRow) {
7489
+ terminalDirectWork.push(record);
7490
+ if (opts.includeTerminalDirect !== true) continue;
7491
+ }
7492
+ if ((live.staleReason || ledgerOnlyStaleReason) && !terminalRow) {
7493
+ staleDirectWork.push(record);
7494
+ continue;
7495
+ }
7496
+ records.push(record);
7497
+ }
7498
+ } else {
7499
+ const ledgerEntries = (opts.ledgerEntries || []).slice().sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
7500
+ const terminals = ledgerEntries.filter((entry) => TERMINAL_LEDGER_KINDS.has(entry.kind) || entry.kind === "task_approval_needed");
7501
+ for (const dispatch of ledgerEntries.filter(isDirectDispatch)) {
7502
+ const taskId = directDispatchTaskId(dispatch);
7503
+ const terminal = terminals.filter((entry) => new Date(entry.timestamp).getTime() >= new Date(dispatch.timestamp).getTime()).find((entry) => terminalMatchesDispatch(entry, dispatch, taskId));
7504
+ const terminalStatus = terminal ? statusFromTerminal(terminal) : void 0;
7505
+ const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
7506
+ const status = terminalStatus || live.status || "assigned";
7507
+ const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
7508
+ const dispatchedToIdleSession = dispatch.payload?.dispatchedToIdleSession === true;
7509
+ const isNoTransition = !terminalStatus && !live.status;
7510
+ const isIdleUnacknowledged = status === "idle";
7511
+ const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
7512
+ const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
7513
+ const { title, summary: summary2 } = summarizeMessage(message);
7514
+ const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
7515
+ const record = {
7516
+ taskId,
7517
+ source: "direct",
7518
+ status,
7519
+ nodeId: dispatch.nodeId,
7520
+ sessionId: dispatch.sessionId,
7521
+ providerType: dispatch.providerType || readString6(dispatch.payload?.providerType),
7522
+ taskTitle: readString6(dispatch.payload?.taskTitle) || title,
7523
+ taskSummary: readString6(dispatch.payload?.taskSummary) || summary2,
7524
+ message,
7525
+ taskMode: readString6(dispatch.payload?.taskMode),
7526
+ createdAt: dispatch.timestamp,
7527
+ updatedAt: terminal?.timestamp || dispatch.timestamp,
7528
+ dispatchedAt: dispatch.timestamp,
7529
+ elapsedMs: elapsedSince(dispatch.timestamp, now),
7530
+ terminal: terminalRow,
7531
+ terminalKind: terminal?.kind,
7532
+ terminalAt: terminal?.timestamp,
7533
+ staleReason: live.staleReason || ledgerOnlyStaleReason,
7534
+ ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
7535
+ };
7536
+ if (terminalRow) {
7537
+ terminalDirectWork.push(record);
7538
+ if (opts.includeTerminalDirect !== true) continue;
7539
+ }
7540
+ if ((live.staleReason || ledgerOnlyStaleReason) && !terminalRow) {
7541
+ staleDirectWork.push(record);
7542
+ continue;
7543
+ }
7544
+ records.push(record);
7545
+ }
7546
+ }
7547
+ records.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
7548
+ staleDirectWork.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
7549
+ terminalDirectWork.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
7550
+ const summary = buildMeshActiveWorkSummary(records);
7551
+ summary.staleDirectCount = staleDirectWork.length;
7552
+ const unacknowledgedCount = staleDirectWork.filter((r) => r.staleDispatchUnacknowledged).length;
7553
+ if (unacknowledgedCount > 0) {
7554
+ summary.staleDirectUnacknowledgedCount = unacknowledgedCount;
7555
+ }
7556
+ 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;
7557
+ if (staleDirectWorkNote) {
7558
+ summary.staleDirectNote = staleDirectWorkNote;
7559
+ }
7560
+ return { activeWork: records, staleDirectWork, staleDirectWorkNote, terminalDirectWork, summary };
7561
+ }
7562
+ function classifyStaleDirectForPrune(record, opts = {}) {
7563
+ if (record.staleDispatchUnacknowledged === true) return "preserve_unacknowledged";
7564
+ if (record.terminal === true) return opts.includeTerminal ? "prunable_terminal" : "preserve_active";
7565
+ if (record.staleReason && PRUNABLE_ORPHAN_STALE_REASONS.has(record.staleReason)) return "prunable_orphan";
7566
+ return "preserve_active";
7567
+ }
7568
+ function pruneStaleDirectDispatches(opts) {
7569
+ const now = opts.now ?? Date.now();
7570
+ const includeTerminal = opts.includeTerminal === true;
7571
+ const execute = opts.execute === true;
7572
+ const minAgeMs = Math.max(0, opts.minAgeMs ?? 0);
7573
+ const activeWorkEvidence = buildMeshActiveWork({
7574
+ meshId: opts.meshId,
7575
+ queue: opts.queue,
7576
+ ledgerEntries: opts.ledgerEntries,
7577
+ directDispatches: opts.directDispatches,
7578
+ nodes: opts.nodes,
7579
+ now,
7580
+ includeTerminalDirect: includeTerminal
7581
+ });
7582
+ const candidates = [
7583
+ ...activeWorkEvidence.staleDirectWork,
7584
+ ...includeTerminal ? activeWorkEvidence.terminalDirectWork : []
7585
+ ];
7586
+ const storeTaskIds = new Set(opts.directDispatches.map((d) => d.taskId));
7587
+ const prunable = [];
7588
+ const skippedTooYoung = [];
7589
+ const preservedUnacknowledged = [];
7590
+ const preservedLedgerOnly = [];
7591
+ const preservedNotOrphan = [];
7592
+ for (const record of candidates) {
7593
+ const classification = classifyStaleDirectForPrune(record, { includeTerminal });
7594
+ if (classification === "preserve_unacknowledged") {
7595
+ preservedUnacknowledged.push(record);
7596
+ continue;
7597
+ }
7598
+ if (classification === "preserve_active") {
7599
+ preservedNotOrphan.push(record);
7600
+ continue;
7601
+ }
7602
+ if (!storeTaskIds.has(record.taskId)) {
7603
+ preservedLedgerOnly.push(record);
7604
+ continue;
7605
+ }
7606
+ if (minAgeMs > 0) {
7607
+ const ageRef = record.dispatchedAt || record.createdAt;
7608
+ const ageMs = elapsedSince(ageRef, now);
7609
+ if (ageMs < minAgeMs) {
7610
+ skippedTooYoung.push(record);
7611
+ continue;
7612
+ }
7613
+ }
7614
+ prunable.push(record);
7615
+ }
7616
+ let prunedCount = 0;
7617
+ if (execute && prunable.length) {
7618
+ prunedCount = deleteDirectDispatchesByTaskId(opts.meshId, prunable.map((r) => r.taskId));
7619
+ appendLedgerEntry(opts.meshId, {
7620
+ kind: "direct_dispatch_pruned",
7621
+ payload: {
7622
+ source: opts.source || "prune_stale_direct",
7623
+ prunedCount,
7624
+ taskIds: prunable.map((r) => r.taskId),
7625
+ reasons: Array.from(new Set(prunable.map((r) => r.staleReason || (r.terminal ? "terminal" : "unknown"))))
7626
+ }
7627
+ });
7628
+ }
7629
+ return {
7630
+ mode: execute ? "execute" : "dry_run",
7631
+ includeTerminal,
7632
+ candidateCount: candidates.length,
7633
+ prunable,
7634
+ prunedCount,
7635
+ skippedTooYoung,
7636
+ preservedUnacknowledged,
7637
+ preservedLedgerOnly,
7638
+ preservedNotOrphan
7639
+ };
7640
+ }
7641
+ function buildCompactStaleDirectWorkSummary(staleDirectWork, opts = {}) {
7642
+ const sampleLimit = Math.max(0, Math.min(10, Math.floor(opts.sampleLimit ?? 3)));
7643
+ const reasonCounts = {};
7644
+ for (const entry of staleDirectWork) {
7645
+ const reason = entry.staleReason || "unknown";
7646
+ reasonCounts[reason] = (reasonCounts[reason] || 0) + 1;
7647
+ }
7648
+ return {
7649
+ count: staleDirectWork.length,
7650
+ sampleLimit,
7651
+ sample: staleDirectWork.slice(0, sampleLimit).map((entry) => ({
7652
+ taskId: entry.taskId,
7653
+ status: entry.status,
7654
+ nodeId: entry.nodeId,
7655
+ sessionId: entry.sessionId,
7656
+ taskTitle: entry.taskTitle,
7657
+ createdAt: entry.createdAt,
7658
+ staleReason: entry.staleReason
7659
+ })),
7660
+ reasonCounts,
7661
+ 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.",
7662
+ ...opts.note ? { note: opts.note } : {}
7663
+ };
7664
+ }
7665
+ var DIRECT_DISPATCH_VIA, TERMINAL_LEDGER_KINDS, PRUNABLE_ORPHAN_STALE_REASONS;
7666
+ var init_mesh_active_work = __esm({
7667
+ "src/mesh/mesh-active-work.ts"() {
7668
+ "use strict";
7669
+ init_mesh_ledger();
7670
+ init_mesh_work_queue();
7671
+ init_dist();
7672
+ DIRECT_DISPATCH_VIA = /* @__PURE__ */ new Set(["p2p_direct", "local_direct", "mesh_send_task"]);
7673
+ TERMINAL_LEDGER_KINDS = /* @__PURE__ */ new Set(["task_completed", "task_failed", "task_stalled"]);
7674
+ PRUNABLE_ORPHAN_STALE_REASONS = /* @__PURE__ */ new Set([
7675
+ "direct task node is no longer in the live mesh",
7676
+ "direct task session is not present in live session records",
7677
+ "direct task has no node id"
7678
+ ]);
7679
+ }
7680
+ });
7681
+
6973
7682
  // src/mesh/mesh-events-utils.ts
6974
7683
  function readNonEmptyString2(value) {
6975
7684
  return typeof value === "string" && value.trim() ? value.trim() : "";
@@ -7024,8 +7733,8 @@ function formatCompletionMetadata(event) {
7024
7733
  function buildMeshSystemMessage(args) {
7025
7734
  const metadata = formatCompletionMetadata(args.metadataEvent);
7026
7735
  if (args.event === "agent:generating_completed") {
7027
- if (args.metadataEvent.source === "long_generating_reconciliation") {
7028
- 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.`;
7736
+ if (args.metadataEvent.source === "no_progress_reconciliation") {
7737
+ 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.`;
7029
7738
  }
7030
7739
  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.";
7031
7740
  return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path;${reviewNote}`;
@@ -7064,7 +7773,7 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
7064
7773
  }
7065
7774
  return `[System] ${args.nodeLabel} has stopped${metadata}. Use mesh_read_chat once if you need to inspect its last output.`;
7066
7775
  }
7067
- if (args.event === "monitor:long_generating") {
7776
+ if (args.event === "monitor:no_progress") {
7068
7777
  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.`;
7069
7778
  }
7070
7779
  if (args.event === "worktree_bootstrap_complete") {
@@ -7142,8 +7851,8 @@ var init_mesh_events_utils = __esm({
7142
7851
  });
7143
7852
 
7144
7853
  // src/mesh/mesh-events-pending.ts
7145
- import { appendFileSync as appendFileSync2, existsSync as existsSync12, readFileSync as readFileSync10, renameSync as renameSync4, statSync as statSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "fs";
7146
- import { join as join13 } from "path";
7854
+ import { appendFileSync as appendFileSync2, existsSync as existsSync13, readFileSync as readFileSync11, renameSync as renameSync4, statSync as statSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "fs";
7855
+ import { join as join14 } from "path";
7147
7856
  import { randomUUID as randomUUID7 } from "crypto";
7148
7857
  function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
7149
7858
  const raw = Array.isArray(coordinatorDaemonId) ? coordinatorDaemonId : coordinatorDaemonId != null ? [coordinatorDaemonId] : [];
@@ -7206,9 +7915,9 @@ function getPendingEventsPath(meshId, coordinatorDaemonId) {
7206
7915
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
7207
7916
  if (coordinatorDaemonId) {
7208
7917
  const safeDaemon = coordinatorDaemonId.replace(/[^a-zA-Z0-9_-]/g, "_");
7209
- return join13(getLedgerDir(), `${safe}-${safeDaemon}.pending-events.jsonl`);
7918
+ return join14(getLedgerDir(), `${safe}-${safeDaemon}.pending-events.jsonl`);
7210
7919
  }
7211
- return join13(getLedgerDir(), `${safe}.pending-events.jsonl`);
7920
+ return join14(getLedgerDir(), `${safe}.pending-events.jsonl`);
7212
7921
  }
7213
7922
  function readPendingMeshCoordinatorEventsFromDisk(meshId, coordinatorDaemonId) {
7214
7923
  if (!meshId) return [];
@@ -7217,9 +7926,9 @@ function readPendingMeshCoordinatorEventsFromDisk(meshId, coordinatorDaemonId) {
7217
7926
  const paths = primaryDaemonId ? [getPendingEventsPath(meshId, primaryDaemonId), getPendingEventsPath(meshId)] : [getPendingEventsPath(meshId)];
7218
7927
  const events = [];
7219
7928
  for (const path42 of paths) {
7220
- if (!existsSync12(path42)) continue;
7929
+ if (!existsSync13(path42)) continue;
7221
7930
  try {
7222
- const raw = readFileSync10(path42, "utf-8");
7931
+ const raw = readFileSync11(path42, "utf-8");
7223
7932
  const parsed = raw.split("\n").filter(Boolean).flatMap((line) => {
7224
7933
  try {
7225
7934
  return [JSON.parse(line)];
@@ -7294,9 +8003,9 @@ function reconcilePendingMeshCoordinatorEvents(meshId, events) {
7294
8003
  }
7295
8004
  function trimPendingEventsIfNeeded(path42) {
7296
8005
  try {
7297
- if (!existsSync12(path42)) return;
7298
- if (statSync5(path42).size <= MAX_PENDING_EVENTS_BYTES) return;
7299
- const lines = readFileSync10(path42, "utf-8").split("\n").filter(Boolean);
8006
+ if (!existsSync13(path42)) return;
8007
+ if (statSync6(path42).size <= MAX_PENDING_EVENTS_BYTES) return;
8008
+ const lines = readFileSync11(path42, "utf-8").split("\n").filter(Boolean);
7300
8009
  if (lines.length <= MAX_PENDING_EVENTS_KEEP) return;
7301
8010
  writeFileSync6(path42, lines.slice(-MAX_PENDING_EVENTS_KEEP).join("\n") + "\n", "utf-8");
7302
8011
  } catch {
@@ -7342,7 +8051,7 @@ function atomicDrainFile(path42) {
7342
8051
  return null;
7343
8052
  }
7344
8053
  try {
7345
- const content = readFileSync10(tmpPath, "utf-8");
8054
+ const content = readFileSync11(tmpPath, "utf-8");
7346
8055
  try {
7347
8056
  unlinkSync2(tmpPath);
7348
8057
  } catch {
@@ -7365,7 +8074,7 @@ function selectiveDrainFile(path42, predicate) {
7365
8074
  }
7366
8075
  let content;
7367
8076
  try {
7368
- content = readFileSync10(tmpPath, "utf-8");
8077
+ content = readFileSync11(tmpPath, "utf-8");
7369
8078
  } catch {
7370
8079
  try {
7371
8080
  unlinkSync2(tmpPath);
@@ -7396,7 +8105,7 @@ function selectiveDrainFile(path42, predicate) {
7396
8105
  unlinkSync2(tmpPath);
7397
8106
  } catch {
7398
8107
  try {
7399
- if (existsSync12(tmpPath) && !existsSync12(path42)) renameSync4(tmpPath, path42);
8108
+ if (existsSync13(tmpPath) && !existsSync13(path42)) renameSync4(tmpPath, path42);
7400
8109
  } catch {
7401
8110
  }
7402
8111
  return [];
@@ -7490,7 +8199,7 @@ function clearPendingMeshCoordinatorEvents(meshId, coordinatorDaemonId) {
7490
8199
  }
7491
8200
  const paths = coordinatorDaemonId ? [getPendingEventsPath(meshId, coordinatorDaemonId), getPendingEventsPath(meshId)] : [getPendingEventsPath(meshId)];
7492
8201
  for (const path42 of paths) {
7493
- if (existsSync12(path42)) try {
8202
+ if (existsSync13(path42)) try {
7494
8203
  unlinkSync2(path42);
7495
8204
  } catch {
7496
8205
  }
@@ -7854,7 +8563,7 @@ function reconcileDirectDispatchCompletionFromTranscript(args) {
7854
8563
  });
7855
8564
  return { reconciled: true, kind, workerResult, ledgerEntryId: entry.id };
7856
8565
  }
7857
- function buildLongGeneratingCompletionReconciliation(args) {
8566
+ function buildNoProgressCompletionReconciliation(args) {
7858
8567
  const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
7859
8568
  const nodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
7860
8569
  const providerType = readNonEmptyString2(args.metadataEvent.providerType);
@@ -7873,8 +8582,8 @@ function buildLongGeneratingCompletionReconciliation(args) {
7873
8582
  providerType,
7874
8583
  providerSessionId,
7875
8584
  finalSummary,
7876
- source: "long_generating_reconciliation",
7877
- reconciledFromEvent: "monitor:long_generating",
8585
+ source: "no_progress_reconciliation",
8586
+ reconciledFromEvent: "monitor:no_progress",
7878
8587
  timestamp: args.metadataEvent.timestamp ?? Date.now(),
7879
8588
  completionDiagnostic: {
7880
8589
  ...completionDiagnostic || {},
@@ -7890,7 +8599,7 @@ function buildLongGeneratingCompletionReconciliation(args) {
7890
8599
  if (!terminal) return null;
7891
8600
  return {
7892
8601
  ...args.metadataEvent,
7893
- source: "long_generating_terminal_ledger_suppression",
8602
+ source: "no_progress_terminal_ledger_suppression",
7894
8603
  terminalLedgerKind: terminal.kind,
7895
8604
  terminalLedgerAt: terminal.timestamp
7896
8605
  };
@@ -8313,7 +9022,7 @@ var init_provider_cli_shared = __esm({
8313
9022
  import { exec } from "child_process";
8314
9023
  import * as os6 from "os";
8315
9024
  import * as path11 from "path";
8316
- import { existsSync as existsSync13 } from "fs";
9025
+ import { existsSync as existsSync14 } from "fs";
8317
9026
  function parseVersion(raw) {
8318
9027
  const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
8319
9028
  return match ? match[1] : raw.split("\n")[0].slice(0, 100);
@@ -8337,7 +9046,7 @@ function resolveCommandPath(command) {
8337
9046
  if (isExplicitCommandPath(trimmed)) {
8338
9047
  const expanded = expandHome(trimmed);
8339
9048
  const candidate = path11.isAbsolute(expanded) ? expanded : path11.resolve(expanded);
8340
- return existsSync13(candidate) ? candidate : null;
9049
+ return existsSync14(candidate) ? candidate : null;
8341
9050
  }
8342
9051
  return null;
8343
9052
  }
@@ -8347,7 +9056,7 @@ async function resolveDetectionPath(command, whichCmd) {
8347
9056
  const whichResult = await execAsync(`${whichCmd} ${shellQuote(command)}`);
8348
9057
  if (whichResult) return whichResult.split("\n")[0];
8349
9058
  const resolved = findBinary(command);
8350
- if (path11.isAbsolute(resolved) && existsSync13(resolved)) return resolved;
9059
+ if (path11.isAbsolute(resolved) && existsSync14(resolved)) return resolved;
8351
9060
  return null;
8352
9061
  }
8353
9062
  function execAsync(cmd, timeoutMs = 5e3) {
@@ -8692,7 +9401,7 @@ var init_mesh_unresolved_forward_outbox = __esm({
8692
9401
  });
8693
9402
 
8694
9403
  // src/mesh/mesh-events-coordinator.ts
8695
- import { existsSync as existsSync14 } from "fs";
9404
+ import { existsSync as existsSync15 } from "fs";
8696
9405
  function resolveCoordinatorDrainDaemonIds(components) {
8697
9406
  const ids = /* @__PURE__ */ new Set();
8698
9407
  const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
@@ -8744,7 +9453,7 @@ function hasRecentIntentionalCleanupStop(meshId, sessionId, nodeId) {
8744
9453
  return false;
8745
9454
  }
8746
9455
  function shouldSuppressIntentionalCleanupStop(args) {
8747
- if (args.event !== "agent:stopped" && args.event !== "monitor:long_generating") return false;
9456
+ if (args.event !== "agent:stopped" && args.event !== "monitor:no_progress") return false;
8748
9457
  if (isIntentionalCleanupStopMetadata(args.metadataEvent)) return true;
8749
9458
  return hasRecentIntentionalCleanupStop(args.meshId, args.sessionId, args.nodeId);
8750
9459
  }
@@ -8960,7 +9669,7 @@ function resolveAutoFastForwardPolicy(mesh) {
8960
9669
  function sessionStateLooksActive(state) {
8961
9670
  const status = readNonEmptyString2(state?.status).toLowerCase();
8962
9671
  const chatStatus = readNonEmptyString2(state?.activeChat?.status).toLowerCase();
8963
- const active = /* @__PURE__ */ new Set(["generating", "streaming", "long_generating", "working", "starting", "waiting_approval"]);
9672
+ const active = /* @__PURE__ */ new Set(["generating", "streaming", "no_progress", "long_generating", "working", "starting", "waiting_approval"]);
8964
9673
  return active.has(status) || active.has(chatStatus);
8965
9674
  }
8966
9675
  function nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId) {
@@ -9480,7 +10189,7 @@ async function maybeAutoFastForwardIdleNode(components, args) {
9480
10189
  const node = mesh?.nodes?.find((candidate) => meshNodeIdMatches(candidate, args.nodeId));
9481
10190
  const workspace = readNonEmptyString2(node?.workspace);
9482
10191
  if (!workspace) return;
9483
- if (!existsSync14(workspace)) return;
10192
+ if (!existsSync15(workspace)) return;
9484
10193
  const policy = resolveAutoFastForwardPolicy(mesh);
9485
10194
  if (!policy.enabled) return;
9486
10195
  if (nodeHasActiveMeshWork(components, args.meshId, args.nodeId, args.sessionId)) return;
@@ -9574,24 +10283,24 @@ function injectMeshSystemMessage(components, args) {
9574
10283
  LOG.info("MeshEvents", `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || "(unknown session)"}`);
9575
10284
  return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
9576
10285
  }
9577
- if (args.event === "monitor:long_generating") {
9578
- const reconciledCompletion = buildLongGeneratingCompletionReconciliation({
10286
+ if (args.event === "monitor:no_progress") {
10287
+ const reconciledCompletion = buildNoProgressCompletionReconciliation({
9579
10288
  meshId: args.meshId,
9580
10289
  nodeId: args.nodeId,
9581
10290
  nodeLabel: args.nodeLabel,
9582
10291
  metadataEvent: args.metadataEvent,
9583
10292
  sourceInstanceId: args.sourceInstanceId
9584
10293
  });
9585
- if (reconciledCompletion?.source === "long_generating_reconciliation") {
9586
- LOG.info("MeshEvents", `Reconciled long-generating monitor to completion for session ${eventSessionId || "(unknown session)"}`);
10294
+ if (reconciledCompletion?.source === "no_progress_reconciliation") {
10295
+ LOG.info("MeshEvents", `Reconciled no-progress monitor to completion for session ${eventSessionId || "(unknown session)"}`);
9587
10296
  return injectMeshSystemMessage(components, {
9588
10297
  ...args,
9589
10298
  event: "agent:generating_completed",
9590
10299
  metadataEvent: reconciledCompletion
9591
10300
  });
9592
10301
  }
9593
- if (reconciledCompletion?.source === "long_generating_terminal_ledger_suppression") {
9594
- LOG.info("MeshEvents", `Suppressed long-generating monitor because terminal ledger evidence already exists for session ${eventSessionId || "(unknown session)"}`);
10302
+ if (reconciledCompletion?.source === "no_progress_terminal_ledger_suppression") {
10303
+ LOG.info("MeshEvents", `Suppressed no-progress monitor because terminal ledger evidence already exists for session ${eventSessionId || "(unknown session)"}`);
9595
10304
  return {
9596
10305
  success: true,
9597
10306
  forwarded: 0,
@@ -9633,7 +10342,7 @@ function injectMeshSystemMessage(components, args) {
9633
10342
  const terminalFinalSummary = readNonEmptyString2(terminal.payload.finalSummary);
9634
10343
  const eventProviderSessionId = readNonEmptyString2(args.metadataEvent.providerSessionId);
9635
10344
  const eventFinalSummary = readNonEmptyString2(args.metadataEvent.finalSummary);
9636
- if (terminalProviderSessionId && terminalProviderSessionId === eventProviderSessionId || terminalFinalSummary && terminalFinalSummary === eventFinalSummary || args.metadataEvent.source === "long_generating_reconciliation") {
10345
+ if (terminalProviderSessionId && terminalProviderSessionId === eventProviderSessionId || terminalFinalSummary && terminalFinalSummary === eventFinalSummary || args.metadataEvent.source === "no_progress_reconciliation") {
9637
10346
  LOG.info("MeshEvents", `Suppressed duplicate completion with existing terminal ledger evidence for mesh ${args.meshId} session ${eventSessionId}`);
9638
10347
  return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true, terminalLedgerEvidence: true };
9639
10348
  }
@@ -10111,7 +10820,7 @@ var init_mesh_events_coordinator = __esm({
10111
10820
  "agent:waiting_approval",
10112
10821
  "agent:stopped",
10113
10822
  "agent:ready",
10114
- "monitor:long_generating",
10823
+ "monitor:no_progress",
10115
10824
  "refine:accepted",
10116
10825
  "refine:completed",
10117
10826
  "refine:failed",
@@ -10122,7 +10831,7 @@ var init_mesh_events_coordinator = __esm({
10122
10831
  "agent:generating_completed": "task_completed",
10123
10832
  "agent:waiting_approval": "task_approval_needed",
10124
10833
  "agent:stopped": "task_failed",
10125
- "monitor:long_generating": "task_stalled"
10834
+ "monitor:no_progress": "task_stalled"
10126
10835
  };
10127
10836
  MESH_FORCE_INJECT_EVENTS = /* @__PURE__ */ new Set([
10128
10837
  "agent:generating_completed",
@@ -10775,6 +11484,14 @@ var init_chat_message_normalization = __esm({
10775
11484
  });
10776
11485
 
10777
11486
  // src/mesh/mesh-reconcile-loop.ts
11487
+ function resolveAutoPruneMinAgeMs() {
11488
+ const raw = readNonEmptyString2(process.env.MESH_AUTO_PRUNE_MIN_AGE_MS);
11489
+ if (raw) {
11490
+ const parsed = Number.parseInt(raw, 10);
11491
+ if (Number.isFinite(parsed) && parsed >= 60 * 6e4 && parsed <= 30 * 24 * 60 * 6e4) return parsed;
11492
+ }
11493
+ return DEFAULT_AUTO_PRUNE_MIN_AGE_MS;
11494
+ }
10778
11495
  function resolveReconcileIntervalMs() {
10779
11496
  const raw = readNonEmptyString2(process.env.MESH_RECONCILE_INTERVAL_MS);
10780
11497
  if (raw) {
@@ -10886,6 +11603,18 @@ async function runMeshReconcileTick(components) {
10886
11603
  LOG.warn("MeshReconcile", `Completion reconcile failed for mesh ${mesh.id}: ${e?.message || e}`);
10887
11604
  }
10888
11605
  }
11606
+ {
11607
+ const minAgeMs = resolveAutoPruneMinAgeMs();
11608
+ for (const mesh of listMeshes()) {
11609
+ const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
11610
+ if (!daemonHostsMesh(mesh, selfIds)) continue;
11611
+ try {
11612
+ await autoPruneStaleDirectDispatches(components, mesh, selfIds, localDaemonId, minAgeMs);
11613
+ } catch (e) {
11614
+ LOG.warn("MeshReconcile", `Auto-prune stale direct failed for mesh ${mesh.id}: ${e?.message || e}`);
11615
+ }
11616
+ }
11617
+ }
10889
11618
  const coordinators = findLiveCoordinators(components);
10890
11619
  if (coordinators.length === 0) {
10891
11620
  return;
@@ -11068,6 +11797,68 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
11068
11797
  }
11069
11798
  }
11070
11799
  }
11800
+ async function autoPruneStaleDirectDispatches(components, mesh, selfIds, localDaemonId, minAgeMs) {
11801
+ const directDispatches = getActiveDirectDispatches(mesh.id);
11802
+ if (directDispatches.length === 0) return;
11803
+ const liveNodes = await collectLiveNodesWithSessions(components, mesh, selfIds, localDaemonId);
11804
+ const result = pruneStaleDirectDispatches({
11805
+ meshId: mesh.id,
11806
+ queue: getQueue(mesh.id),
11807
+ ledgerEntries: readLedgerEntries(mesh.id, { tail: 500 }),
11808
+ directDispatches,
11809
+ nodes: liveNodes,
11810
+ execute: true,
11811
+ minAgeMs,
11812
+ source: "daemon_reconcile_auto_prune"
11813
+ });
11814
+ if (result.prunedCount > 0) {
11815
+ LOG.info("MeshReconcile", `Auto-pruned ${result.prunedCount} orphaned direct dispatch record(s) for mesh ${mesh.id}`);
11816
+ }
11817
+ }
11818
+ async function collectLiveNodesWithSessions(components, mesh, selfIds, localDaemonId) {
11819
+ const dispatchMeshCommand = components.dispatchMeshCommand;
11820
+ return Promise.all(mesh.nodes.map(async (node) => {
11821
+ const nodeDaemonId = readNonEmptyString2(node.daemonId);
11822
+ const isLocalNode = !nodeDaemonId || selfIds.includes(nodeDaemonId) || localDaemonId !== void 0 && nodeDaemonId === localDaemonId;
11823
+ let statusResult;
11824
+ try {
11825
+ if (isLocalNode) {
11826
+ statusResult = await components.commandHandler.handle("get_status_metadata", {});
11827
+ } else if (dispatchMeshCommand) {
11828
+ statusResult = await dispatchMeshCommand(nodeDaemonId, "get_status_metadata", {});
11829
+ } else {
11830
+ return node;
11831
+ }
11832
+ } catch {
11833
+ return node;
11834
+ }
11835
+ const sessions = extractStatusMetadataSessions(statusResult);
11836
+ return sessions.length > 0 ? { ...node, sessions } : node;
11837
+ }));
11838
+ }
11839
+ function extractStatusMetadataSessions(raw) {
11840
+ let cursor = raw;
11841
+ for (let depth = 0; depth < 4 && cursor && typeof cursor === "object"; depth++) {
11842
+ const record = cursor;
11843
+ const status = record.status && typeof record.status === "object" ? record.status : void 0;
11844
+ if (status && Array.isArray(status.sessions)) return status.sessions;
11845
+ if (Array.isArray(record.sessions)) return record.sessions;
11846
+ if (record.payload && typeof record.payload === "object") {
11847
+ cursor = record.payload;
11848
+ continue;
11849
+ }
11850
+ if (record.result && typeof record.result === "object") {
11851
+ cursor = record.result;
11852
+ continue;
11853
+ }
11854
+ if (record.data && typeof record.data === "object") {
11855
+ cursor = record.data;
11856
+ continue;
11857
+ }
11858
+ break;
11859
+ }
11860
+ return [];
11861
+ }
11071
11862
  function extractPendingEvents(raw) {
11072
11863
  if (Array.isArray(raw)) return raw;
11073
11864
  if (raw && typeof raw === "object") {
@@ -11105,7 +11896,7 @@ function setupMeshReconcileLoop(components) {
11105
11896
  }
11106
11897
  };
11107
11898
  }
11108
- var DEFAULT_RECONCILE_INTERVAL_MS;
11899
+ var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS;
11109
11900
  var init_mesh_reconcile_loop = __esm({
11110
11901
  "src/mesh/mesh-reconcile-loop.ts"() {
11111
11902
  "use strict";
@@ -11118,9 +11909,12 @@ var init_mesh_reconcile_loop = __esm({
11118
11909
  init_mesh_unresolved_forward_outbox();
11119
11910
  init_mesh_events_utils();
11120
11911
  init_mesh_work_queue();
11912
+ init_mesh_ledger();
11913
+ init_mesh_active_work();
11121
11914
  init_mesh_events_stale();
11122
11915
  init_chat_message_normalization();
11123
11916
  DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
11917
+ DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
11124
11918
  }
11125
11919
  });
11126
11920
 
@@ -12172,7 +12966,7 @@ var init_terminal_screen = __esm({
12172
12966
 
12173
12967
  // src/cli-adapters/resolve-executable.ts
12174
12968
  import { execFileSync } from "child_process";
12175
- import { existsSync as existsSync21 } from "fs";
12969
+ import { existsSync as existsSync22 } from "fs";
12176
12970
  import * as path18 from "path";
12177
12971
  function resolveWin32GlobalBin(trimmed) {
12178
12972
  if (path18.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\")) {
@@ -12188,7 +12982,7 @@ function resolveWin32GlobalBin(trimmed) {
12188
12982
  if (!dir) continue;
12189
12983
  for (const ext of WIN_EXEC_EXT) {
12190
12984
  const full = path18.join(dir, trimmed + ext);
12191
- if (existsSync21(full)) return full;
12985
+ if (existsSync22(full)) return full;
12192
12986
  }
12193
12987
  }
12194
12988
  return null;
@@ -12197,7 +12991,7 @@ function resolveWin32Executable(command) {
12197
12991
  if (process.platform !== "win32") return command;
12198
12992
  const trimmed = (command || "").trim();
12199
12993
  if (!trimmed) return command;
12200
- if (path18.isAbsolute(trimmed) && existsSync21(trimmed)) return trimmed;
12994
+ if (path18.isAbsolute(trimmed) && existsSync22(trimmed)) return trimmed;
12201
12995
  try {
12202
12996
  const out = execFileSync("where", [trimmed], {
12203
12997
  encoding: "utf8",
@@ -15183,7 +15977,7 @@ ${lastSnapshot}`;
15183
15977
  };
15184
15978
  if (parsedSessionStatus === "idle" && hasFinalAssistant(parsedStatusBeforeSend)) return null;
15185
15979
  if (this.engine.currentStatus === "generating") return "current_status_generating";
15186
- if (parsedSessionStatus === "generating" || parsedSessionStatus === "long_generating") {
15980
+ if (parsedSessionStatus === "generating" || parsedSessionStatus === "no_progress" || parsedSessionStatus === "long_generating") {
15187
15981
  const parsedModal = parsedStatusBeforeSend?.activeModal ?? parsedStatusBeforeSend?.modal ?? null;
15188
15982
  const parsedHasActionableModal = Boolean(
15189
15983
  parsedModal && Array.isArray(parsedModal.buttons) && parsedModal.buttons.some((candidate) => typeof candidate === "string" && candidate.trim())
@@ -15260,7 +16054,7 @@ ${lastSnapshot}`;
15260
16054
  }
15261
16055
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.engine.currentStatus})`);
15262
16056
  const parsedSessionStatus = typeof parsedStatusBeforeSend?.status === "string" ? String(parsedStatusBeforeSend.status) : "";
15263
- if (!allowInputDuringGeneration && (parsedSessionStatus === "generating" || parsedSessionStatus === "long_generating")) {
16057
+ if (!allowInputDuringGeneration && (parsedSessionStatus === "generating" || parsedSessionStatus === "no_progress" || parsedSessionStatus === "long_generating")) {
15264
16058
  const parsedModal = parsedStatusBeforeSend?.activeModal ?? parsedStatusBeforeSend?.modal ?? null;
15265
16059
  const parsedHasActionableModal = Boolean(
15266
16060
  parsedModal && Array.isArray(parsedModal.buttons) && parsedModal.buttons.some((candidate) => typeof candidate === "string" && candidate.trim())
@@ -17123,6 +17917,7 @@ init_repo_mesh_types();
17123
17917
  // src/git/index.ts
17124
17918
  init_git_executor();
17125
17919
  init_git_status();
17920
+ init_change_impact_config();
17126
17921
  init_git_diff();
17127
17922
 
17128
17923
  // src/git/git-summary.ts
@@ -18417,17 +19212,17 @@ init_mesh_review_inbox();
18417
19212
 
18418
19213
  // src/mesh/coordinator-registry.ts
18419
19214
  init_config();
18420
- import { join as join10 } from "path";
18421
- import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
19215
+ import { join as join11 } from "path";
19216
+ import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
18422
19217
  var _registry = /* @__PURE__ */ new Map();
18423
19218
  function getRegistryPath() {
18424
- return join10(getDaemonDataDir(), "mesh-coordinators.json");
19219
+ return join11(getDaemonDataDir(), "mesh-coordinators.json");
18425
19220
  }
18426
19221
  function loadMeshCoordinatorRegistry() {
18427
19222
  const path42 = getRegistryPath();
18428
- if (!existsSync9(path42)) return;
19223
+ if (!existsSync10(path42)) return;
18429
19224
  try {
18430
- const raw = JSON.parse(readFileSync7(path42, "utf-8"));
19225
+ const raw = JSON.parse(readFileSync8(path42, "utf-8"));
18431
19226
  if (!Array.isArray(raw)) return;
18432
19227
  _registry.clear();
18433
19228
  for (const entry of raw) {
@@ -18475,9 +19270,9 @@ function listCoordinatorsForWorkspace(workspace) {
18475
19270
  }
18476
19271
 
18477
19272
  // src/mesh/refine-config.ts
18478
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
18479
- import { join as join11 } from "path";
18480
- import * as yaml from "js-yaml";
19273
+ import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
19274
+ import { join as join12 } from "path";
19275
+ import * as yaml2 from "js-yaml";
18481
19276
  var MESH_REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
18482
19277
  var MESH_REFINE_CONFIG_LOCATIONS = [
18483
19278
  ".adhdev/refine.json",
@@ -18618,7 +19413,7 @@ function normalizeMeshCommandConfig(entry, source) {
18618
19413
  }
18619
19414
  };
18620
19415
  }
18621
- var isRecord = isMeshConfigRecord;
19416
+ var isRecord2 = isMeshConfigRecord;
18622
19417
  function validateMeshRefineConfig(config, source = "inline") {
18623
19418
  const errors = [];
18624
19419
  const bootstrapCommands = [];
@@ -18626,14 +19421,14 @@ function validateMeshRefineConfig(config, source = "inline") {
18626
19421
  const rejectedCommands = [];
18627
19422
  const deprecationWarnings = [];
18628
19423
  let bootstrapMode = "inherit";
18629
- if (!isRecord(config)) return { valid: false, errors: ["config must be an object"], bootstrapCommands, commands, rejectedCommands, bootstrapMode, deprecationWarnings };
19424
+ if (!isRecord2(config)) return { valid: false, errors: ["config must be an object"], bootstrapCommands, commands, rejectedCommands, bootstrapMode, deprecationWarnings };
18630
19425
  if (config.version !== 1) errors.push("version must be 1");
18631
19426
  if (config.allowAutoPublishSubmoduleMainCommits !== void 0 && typeof config.allowAutoPublishSubmoduleMainCommits !== "boolean") {
18632
19427
  errors.push("allowAutoPublishSubmoduleMainCommits must be a boolean when provided");
18633
19428
  }
18634
19429
  const validation = config.validation;
18635
- if (validation !== void 0 && !isRecord(validation)) errors.push("validation must be an object");
18636
- const rawBootstrapMode = isRecord(validation) ? validation.bootstrap : void 0;
19430
+ if (validation !== void 0 && !isRecord2(validation)) errors.push("validation must be an object");
19431
+ const rawBootstrapMode = isRecord2(validation) ? validation.bootstrap : void 0;
18637
19432
  if (rawBootstrapMode !== void 0) {
18638
19433
  if (rawBootstrapMode === "inherit" || rawBootstrapMode === "skip") {
18639
19434
  bootstrapMode = rawBootstrapMode;
@@ -18641,8 +19436,8 @@ function validateMeshRefineConfig(config, source = "inline") {
18641
19436
  errors.push("validation.bootstrap must be 'inherit' or 'skip' when provided");
18642
19437
  }
18643
19438
  }
18644
- const rawCommands = isRecord(validation) ? validation.commands : void 0;
18645
- const rawBootstrapCommands = isRecord(validation) ? validation.bootstrapCommands : void 0;
19439
+ const rawCommands = isRecord2(validation) ? validation.commands : void 0;
19440
+ const rawBootstrapCommands = isRecord2(validation) ? validation.bootstrapCommands : void 0;
18646
19441
  if (rawCommands !== void 0 && !Array.isArray(rawCommands)) errors.push("validation.commands must be an array");
18647
19442
  if (rawBootstrapCommands !== void 0 && !Array.isArray(rawBootstrapCommands)) errors.push("validation.bootstrapCommands must be an array");
18648
19443
  if (Array.isArray(rawBootstrapCommands) && rawBootstrapCommands.length > 0) {
@@ -18665,9 +19460,9 @@ function validateMeshRefineConfig(config, source = "inline") {
18665
19460
  if (rejectedCommands.length) errors.push("one or more validation commands are invalid");
18666
19461
  return { valid: errors.length === 0, errors, bootstrapCommands, commands, rejectedCommands, bootstrapMode, deprecationWarnings };
18667
19462
  }
18668
- function parseConfigText(path42, text) {
19463
+ function parseConfigText2(path42, text) {
18669
19464
  if (/\.json$/i.test(path42)) return JSON.parse(text);
18670
- return yaml.load(text);
19465
+ return yaml2.load(text);
18671
19466
  }
18672
19467
  function loadMeshRefineConfig(mesh, workspace) {
18673
19468
  const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
@@ -18678,10 +19473,10 @@ function loadMeshRefineConfig(mesh, workspace) {
18678
19473
  return { config: inline, source: "mesh.policy.refineConfig", sourceType: "mesh_policy" };
18679
19474
  }
18680
19475
  for (const relative5 of MESH_REFINE_CONFIG_LOCATIONS) {
18681
- const configPath = join11(workspace, relative5);
18682
- if (!existsSync10(configPath)) continue;
19476
+ const configPath = join12(workspace, relative5);
19477
+ if (!existsSync11(configPath)) continue;
18683
19478
  try {
18684
- const parsed = parseConfigText(configPath, readFileSync8(configPath, "utf-8"));
19479
+ const parsed = parseConfigText2(configPath, readFileSync9(configPath, "utf-8"));
18685
19480
  const validation = validateMeshRefineConfig(parsed, relative5);
18686
19481
  if (!validation.valid) return { source: relative5, sourceType: "invalid", path: configPath, error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
18687
19482
  return { config: parsed, source: relative5, sourceType: "repo_file", path: configPath };
@@ -18697,20 +19492,20 @@ function loadMeshRefineConfig(mesh, workspace) {
18697
19492
  }
18698
19493
  function readPackageScripts(workspace) {
18699
19494
  try {
18700
- const parsed = JSON.parse(readFileSync8(join11(workspace, "package.json"), "utf-8"));
18701
- return isRecord(parsed?.scripts) ? parsed.scripts : {};
19495
+ const parsed = JSON.parse(readFileSync9(join12(workspace, "package.json"), "utf-8"));
19496
+ return isRecord2(parsed?.scripts) ? parsed.scripts : {};
18702
19497
  } catch {
18703
19498
  return {};
18704
19499
  }
18705
19500
  }
18706
19501
  function collectProjectContextSuggestions(mesh) {
18707
19502
  const commands = mesh?.projectContext?.commands;
18708
- if (!isRecord(commands)) return [];
19503
+ if (!isRecord2(commands)) return [];
18709
19504
  const suggestions = [];
18710
19505
  for (const category of MESH_REFINE_VALIDATION_CATEGORIES) {
18711
19506
  const entries = Array.isArray(commands[category]) ? commands[category] : [];
18712
19507
  for (const entry of entries) {
18713
- if (isRecord(entry) && typeof entry.command === "string") suggestions.push({ command: entry.command, category });
19508
+ if (isRecord2(entry) && typeof entry.command === "string") suggestions.push({ command: entry.command, category });
18714
19509
  }
18715
19510
  }
18716
19511
  return suggestions;
@@ -18774,12 +19569,12 @@ function resolveMeshRefineValidationPlan(mesh, workspace) {
18774
19569
  }
18775
19570
 
18776
19571
  // src/mesh/worktree-bootstrap-config.ts
18777
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
18778
- import { join as join12, resolve as pathResolve } from "path";
19572
+ import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
19573
+ import { join as join13, resolve as pathResolve } from "path";
18779
19574
  import { execFile as execFile3 } from "child_process";
18780
19575
  import { createHash as createHash3 } from "crypto";
18781
19576
  import { promisify as promisify3 } from "util";
18782
- import * as yaml2 from "js-yaml";
19577
+ import * as yaml3 from "js-yaml";
18783
19578
  var MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS = [
18784
19579
  ".adhdev/worktree_bootstrap.json",
18785
19580
  ".adhdev/worktree_bootstrap.yaml",
@@ -18824,9 +19619,9 @@ var MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA = {
18824
19619
  var DEFAULT_TIMEOUT_MS2 = 12e4;
18825
19620
  var DEFAULT_OUTPUT_LIMIT_BYTES = 128 * 1024;
18826
19621
  var OUTPUT_SUMMARY_CHARS = 2e3;
18827
- function parseConfigText2(path42, text) {
19622
+ function parseConfigText3(path42, text) {
18828
19623
  if (/\.json$/i.test(path42)) return JSON.parse(text);
18829
- return yaml2.load(text);
19624
+ return yaml3.load(text);
18830
19625
  }
18831
19626
  function truncateOutput(value) {
18832
19627
  const text = typeof value === "string" ? value : value == null ? "" : String(value);
@@ -18866,10 +19661,10 @@ function loadMeshWorktreeBootstrapConfig(mesh, workspace) {
18866
19661
  return { config: inline, source: "mesh.policy.worktreeBootstrapConfig", sourceType: "mesh_policy" };
18867
19662
  }
18868
19663
  for (const relative5 of MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS) {
18869
- const configPath = join12(workspace, relative5);
18870
- if (!existsSync11(configPath)) continue;
19664
+ const configPath = join13(workspace, relative5);
19665
+ if (!existsSync12(configPath)) continue;
18871
19666
  try {
18872
- const parsed = parseConfigText2(configPath, readFileSync9(configPath, "utf-8"));
19667
+ const parsed = parseConfigText3(configPath, readFileSync10(configPath, "utf-8"));
18873
19668
  const validation = validateMeshWorktreeBootstrapConfig(parsed, relative5);
18874
19669
  if (!validation.valid) return { source: relative5, sourceType: "invalid", path: configPath, error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
18875
19670
  return { config: parsed, source: relative5, sourceType: "repo_file", path: configPath };
@@ -18882,9 +19677,9 @@ function loadMeshWorktreeBootstrapConfig(mesh, workspace) {
18882
19677
  function computeStaleInputsDigest(workspace, staleInputs) {
18883
19678
  const digest = {};
18884
19679
  for (const relative5 of staleInputs ?? []) {
18885
- const filePath = join12(workspace, relative5);
19680
+ const filePath = join13(workspace, relative5);
18886
19681
  try {
18887
- digest[relative5] = createHash3("sha256").update(readFileSync9(filePath)).digest("hex");
19682
+ digest[relative5] = createHash3("sha256").update(readFileSync10(filePath)).digest("hex");
18888
19683
  } catch {
18889
19684
  digest[relative5] = "absent";
18890
19685
  }
@@ -18954,10 +19749,10 @@ async function runMeshWorktreeBootstrap(mesh, workspace) {
18954
19749
  staleInputs: loaded.config.staleInputs
18955
19750
  };
18956
19751
  const staleInputPaths = loaded.config.staleInputs ?? [];
18957
- const initiallyAbsent = staleInputPaths.filter((p) => !existsSync11(join12(workspace, p)));
19752
+ const initiallyAbsent = staleInputPaths.filter((p) => !existsSync12(join13(workspace, p)));
18958
19753
  for (const command of validation.commands) {
18959
19754
  if (initiallyAbsent.length > 0) {
18960
- const appearedNow = initiallyAbsent.filter((p) => existsSync11(join12(workspace, p)));
19755
+ const appearedNow = initiallyAbsent.filter((p) => existsSync12(join13(workspace, p)));
18961
19756
  if (appearedNow.length > 0) {
18962
19757
  state.status = "stale";
18963
19758
  state.completedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -19087,331 +19882,7 @@ function buildMeshLedgerReconciliationEvidence(meshId, replicas) {
19087
19882
 
19088
19883
  // src/index.ts
19089
19884
  init_mesh_work_queue();
19090
-
19091
- // src/mesh/mesh-active-work.ts
19092
- init_dist();
19093
- var DIRECT_DISPATCH_VIA = /* @__PURE__ */ new Set(["p2p_direct", "local_direct", "mesh_send_task"]);
19094
- var TERMINAL_LEDGER_KINDS = /* @__PURE__ */ new Set(["task_completed", "task_failed", "task_stalled"]);
19095
- function readString6(value) {
19096
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
19097
- }
19098
- function summarizeMessage(message) {
19099
- const oneLine2 = message.replace(/\s+/g, " ").trim();
19100
- const title = oneLine2.length > 96 ? `${oneLine2.slice(0, 93)}...` : oneLine2;
19101
- return { title: title || "(untitled task)", summary: oneLine2 };
19102
- }
19103
- function elapsedSince(value, now) {
19104
- const started = value ? new Date(value).getTime() : Number.NaN;
19105
- return Number.isFinite(started) ? Math.max(0, now - started) : 0;
19106
- }
19107
- function sessionStatusFromNodes(nodes, nodeId, sessionId) {
19108
- if (!Array.isArray(nodes)) return {};
19109
- if (!nodeId) return { staleReason: "direct task has no node id" };
19110
- const node = nodes.find((item) => meshNodeIdMatches(item, nodeId));
19111
- if (!node) return { staleReason: "direct task node is no longer in the live mesh" };
19112
- if (!sessionId) return {};
19113
- const candidates = [];
19114
- for (const value of [
19115
- node.sessions,
19116
- node.activeSessions,
19117
- node.active_sessions,
19118
- node.activeSessionDetails,
19119
- node.active_session_details,
19120
- node.sessionDetails,
19121
- node.session_details,
19122
- node.lastProbe?.sessions,
19123
- node.last_probe?.sessions,
19124
- node.lastProbe?.status?.sessions,
19125
- node.last_probe?.status?.sessions
19126
- ]) {
19127
- if (Array.isArray(value)) candidates.push(...value);
19128
- }
19129
- for (const value of [node.activeSession, node.active_session, node.currentSession, node.current_session, node.runtimeSession, node.runtime_session, node.session]) {
19130
- if (value && typeof value === "object") candidates.push(value);
19131
- }
19132
- const session = candidates.find((item) => {
19133
- if (typeof item === "string") return item === sessionId;
19134
- const id = readString6(item?.id) || readString6(item?.sessionId) || readString6(item?.session_id) || readString6(item?.runtimeSessionId) || readString6(item?.instanceId);
19135
- return id === sessionId;
19136
- });
19137
- if (!session) return { staleReason: "direct task session is not present in live session records" };
19138
- if (typeof session === "string") return {};
19139
- const raw = `${readString6(session.status) || ""} ${readString6(session.lifecycle) || ""} ${readString6(session.state) || ""} ${readString6(session.activeChat?.status) || ""}`.toLowerCase();
19140
- if (raw.includes("approval")) return { status: "awaiting_approval" };
19141
- if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return { status: "generating" };
19142
- if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return { status: "failed" };
19143
- if (raw.includes("idle") || raw.includes("waiting_input") || raw.includes("ready")) return { status: "idle" };
19144
- return {};
19145
- }
19146
- function isDirectDispatch(entry) {
19147
- if (entry.kind !== "task_dispatched") return false;
19148
- const payload = entry.payload || {};
19149
- if (payload.source === "direct") return true;
19150
- const via = readString6(payload.via);
19151
- return Boolean(via && DIRECT_DISPATCH_VIA.has(via) && payload.source !== "queue");
19152
- }
19153
- function directDispatchTaskId(entry) {
19154
- return readString6(entry.payload?.taskId) || entry.id;
19155
- }
19156
- function terminalMatchesDispatch(terminal, dispatch, taskId) {
19157
- const terminalTaskId = readString6(terminal.payload?.taskId);
19158
- if (terminalTaskId && terminalTaskId === taskId) return true;
19159
- if (terminalTaskId && terminalTaskId !== taskId) return false;
19160
- if (dispatch.sessionId && terminal.sessionId === dispatch.sessionId) return true;
19161
- return Boolean(dispatch.nodeId && terminal.nodeId === dispatch.nodeId && !dispatch.sessionId);
19162
- }
19163
- function statusFromTerminal(entry) {
19164
- if (entry.kind === "task_approval_needed") return "awaiting_approval";
19165
- if (entry.kind === "task_completed") return "idle";
19166
- return "failed";
19167
- }
19168
- function buildMeshActiveWorkSummary(activeWork) {
19169
- const statusCounts = {
19170
- pending: 0,
19171
- assigned: 0,
19172
- generating: 0,
19173
- idle: 0,
19174
- failed: 0,
19175
- awaiting_approval: 0
19176
- };
19177
- const sourceCounts = { queue: 0, direct: 0 };
19178
- for (const item of activeWork) {
19179
- sourceCounts[item.source] += 1;
19180
- statusCounts[item.status] += 1;
19181
- }
19182
- const staleDirectCount = activeWork.filter((item) => item.source === "direct" && item.staleReason).length;
19183
- const staleDirectUnacknowledgedCount = activeWork.filter((item) => item.source === "direct" && item.staleDispatchUnacknowledged).length;
19184
- return {
19185
- totalActiveCount: activeWork.length,
19186
- queueActiveCount: sourceCounts.queue,
19187
- directActiveCount: sourceCounts.direct,
19188
- awaitingApprovalCount: statusCounts.awaiting_approval,
19189
- generatingCount: statusCounts.generating,
19190
- failedCount: statusCounts.failed,
19191
- idleCount: statusCounts.idle,
19192
- sourceCounts,
19193
- statusCounts,
19194
- staleDirectCount,
19195
- ...staleDirectUnacknowledgedCount > 0 ? { staleDirectUnacknowledgedCount } : {},
19196
- ...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." } : {}
19197
- };
19198
- }
19199
- function buildMeshActiveWork(opts) {
19200
- const now = opts.now ?? Date.now();
19201
- const records = [];
19202
- const staleDirectWork = [];
19203
- const terminalDirectWork = [];
19204
- for (const task of opts.queue || []) {
19205
- if (task.status !== "pending" && task.status !== "assigned") continue;
19206
- const { title, summary: summary2 } = summarizeMessage(task.message || "");
19207
- records.push({
19208
- taskId: task.id,
19209
- source: "queue",
19210
- status: task.status,
19211
- nodeId: task.assignedNodeId || task.targetNodeId,
19212
- sessionId: task.assignedSessionId || task.targetSessionId,
19213
- taskTitle: title,
19214
- taskSummary: summary2,
19215
- message: task.message,
19216
- taskMode: task.taskMode,
19217
- createdAt: task.createdAt,
19218
- updatedAt: task.updatedAt,
19219
- dispatchedAt: task.dispatchTimestamp,
19220
- elapsedMs: elapsedSince(task.dispatchTimestamp || task.createdAt, now)
19221
- });
19222
- }
19223
- if (opts.directDispatches !== void 0) {
19224
- const dbTaskIds = new Set(opts.directDispatches.map((d) => d.taskId));
19225
- for (const dispatch of opts.directDispatches) {
19226
- const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId ?? void 0, dispatch.sessionId ?? void 0);
19227
- const dbStatus = dispatch.status;
19228
- const isTerminal = dbStatus === "completed" || dbStatus === "failed" || dbStatus === "stale";
19229
- const status = isTerminal ? dbStatus === "completed" ? "idle" : "failed" : live.status || (dbStatus === "acked" ? "generating" : "assigned");
19230
- const isNoTransition = !isTerminal && !live.status;
19231
- const isIdleUnacknowledged = status === "idle" && !isTerminal;
19232
- const ledgerOnlyStaleReason = !isTerminal && (isIdleUnacknowledged || isNoTransition || dispatch.dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
19233
- const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
19234
- const { title, summary: summary2 } = summarizeMessage(dispatch.message || "");
19235
- const record = {
19236
- taskId: dispatch.taskId,
19237
- source: "direct",
19238
- status,
19239
- nodeId: dispatch.nodeId ?? void 0,
19240
- sessionId: dispatch.sessionId ?? void 0,
19241
- providerType: dispatch.providerType ?? void 0,
19242
- taskTitle: title,
19243
- taskSummary: summary2,
19244
- message: dispatch.message,
19245
- taskMode: dispatch.taskMode ?? void 0,
19246
- createdAt: dispatch.dispatchedAt,
19247
- updatedAt: dispatch.updatedAt,
19248
- dispatchedAt: dispatch.dispatchedAt,
19249
- elapsedMs: elapsedSince(dispatch.dispatchedAt, now),
19250
- terminal: isTerminal,
19251
- terminalKind: isTerminal ? dbStatus === "completed" ? "task_completed" : "task_failed" : void 0,
19252
- terminalAt: isTerminal ? dispatch.updatedAt : void 0,
19253
- staleReason: live.staleReason || ledgerOnlyStaleReason,
19254
- ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
19255
- };
19256
- if (isTerminal) {
19257
- terminalDirectWork.push(record);
19258
- if (opts.includeTerminalDirect !== true) continue;
19259
- }
19260
- if ((live.staleReason || ledgerOnlyStaleReason) && !isTerminal) {
19261
- staleDirectWork.push(record);
19262
- continue;
19263
- }
19264
- records.push(record);
19265
- }
19266
- const ledgerEntries = (opts.ledgerEntries || []).slice().sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
19267
- const terminals = ledgerEntries.filter((entry) => TERMINAL_LEDGER_KINDS.has(entry.kind) || entry.kind === "task_approval_needed");
19268
- for (const dispatch of ledgerEntries.filter(isDirectDispatch)) {
19269
- const taskId = directDispatchTaskId(dispatch);
19270
- if (dbTaskIds.has(taskId)) continue;
19271
- const terminal = terminals.filter((entry) => new Date(entry.timestamp).getTime() >= new Date(dispatch.timestamp).getTime()).find((entry) => terminalMatchesDispatch(entry, dispatch, taskId));
19272
- const terminalStatus = terminal ? statusFromTerminal(terminal) : void 0;
19273
- const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
19274
- const status = terminalStatus || live.status || "assigned";
19275
- const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
19276
- const dispatchedToIdleSession = dispatch.payload?.dispatchedToIdleSession === true;
19277
- const isNoTransition = !terminalStatus && !live.status;
19278
- const isIdleUnacknowledged = status === "idle";
19279
- const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
19280
- const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
19281
- const { title, summary: summary2 } = summarizeMessage(message);
19282
- const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
19283
- const record = {
19284
- taskId,
19285
- source: "direct",
19286
- status,
19287
- nodeId: dispatch.nodeId,
19288
- sessionId: dispatch.sessionId,
19289
- providerType: dispatch.providerType || readString6(dispatch.payload?.providerType),
19290
- taskTitle: readString6(dispatch.payload?.taskTitle) || title,
19291
- taskSummary: readString6(dispatch.payload?.taskSummary) || summary2,
19292
- message,
19293
- taskMode: readString6(dispatch.payload?.taskMode),
19294
- createdAt: dispatch.timestamp,
19295
- updatedAt: terminal?.timestamp || dispatch.timestamp,
19296
- dispatchedAt: dispatch.timestamp,
19297
- elapsedMs: elapsedSince(dispatch.timestamp, now),
19298
- terminal: terminalRow,
19299
- terminalKind: terminal?.kind,
19300
- terminalAt: terminal?.timestamp,
19301
- staleReason: live.staleReason || ledgerOnlyStaleReason,
19302
- ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
19303
- };
19304
- if (terminalRow) {
19305
- terminalDirectWork.push(record);
19306
- if (opts.includeTerminalDirect !== true) continue;
19307
- }
19308
- if ((live.staleReason || ledgerOnlyStaleReason) && !terminalRow) {
19309
- staleDirectWork.push(record);
19310
- continue;
19311
- }
19312
- records.push(record);
19313
- }
19314
- } else {
19315
- const ledgerEntries = (opts.ledgerEntries || []).slice().sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
19316
- const terminals = ledgerEntries.filter((entry) => TERMINAL_LEDGER_KINDS.has(entry.kind) || entry.kind === "task_approval_needed");
19317
- for (const dispatch of ledgerEntries.filter(isDirectDispatch)) {
19318
- const taskId = directDispatchTaskId(dispatch);
19319
- const terminal = terminals.filter((entry) => new Date(entry.timestamp).getTime() >= new Date(dispatch.timestamp).getTime()).find((entry) => terminalMatchesDispatch(entry, dispatch, taskId));
19320
- const terminalStatus = terminal ? statusFromTerminal(terminal) : void 0;
19321
- const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
19322
- const status = terminalStatus || live.status || "assigned";
19323
- const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
19324
- const dispatchedToIdleSession = dispatch.payload?.dispatchedToIdleSession === true;
19325
- const isNoTransition = !terminalStatus && !live.status;
19326
- const isIdleUnacknowledged = status === "idle";
19327
- const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition" : void 0;
19328
- const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
19329
- const { title, summary: summary2 } = summarizeMessage(message);
19330
- const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
19331
- const record = {
19332
- taskId,
19333
- source: "direct",
19334
- status,
19335
- nodeId: dispatch.nodeId,
19336
- sessionId: dispatch.sessionId,
19337
- providerType: dispatch.providerType || readString6(dispatch.payload?.providerType),
19338
- taskTitle: readString6(dispatch.payload?.taskTitle) || title,
19339
- taskSummary: readString6(dispatch.payload?.taskSummary) || summary2,
19340
- message,
19341
- taskMode: readString6(dispatch.payload?.taskMode),
19342
- createdAt: dispatch.timestamp,
19343
- updatedAt: terminal?.timestamp || dispatch.timestamp,
19344
- dispatchedAt: dispatch.timestamp,
19345
- elapsedMs: elapsedSince(dispatch.timestamp, now),
19346
- terminal: terminalRow,
19347
- terminalKind: terminal?.kind,
19348
- terminalAt: terminal?.timestamp,
19349
- staleReason: live.staleReason || ledgerOnlyStaleReason,
19350
- ...isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}
19351
- };
19352
- if (terminalRow) {
19353
- terminalDirectWork.push(record);
19354
- if (opts.includeTerminalDirect !== true) continue;
19355
- }
19356
- if ((live.staleReason || ledgerOnlyStaleReason) && !terminalRow) {
19357
- staleDirectWork.push(record);
19358
- continue;
19359
- }
19360
- records.push(record);
19361
- }
19362
- }
19363
- records.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
19364
- staleDirectWork.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
19365
- terminalDirectWork.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
19366
- const summary = buildMeshActiveWorkSummary(records);
19367
- summary.staleDirectCount = staleDirectWork.length;
19368
- const unacknowledgedCount = staleDirectWork.filter((r) => r.staleDispatchUnacknowledged).length;
19369
- if (unacknowledgedCount > 0) {
19370
- summary.staleDirectUnacknowledgedCount = unacknowledgedCount;
19371
- }
19372
- 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;
19373
- if (staleDirectWorkNote) {
19374
- summary.staleDirectNote = staleDirectWorkNote;
19375
- }
19376
- return { activeWork: records, staleDirectWork, staleDirectWorkNote, terminalDirectWork, summary };
19377
- }
19378
- var PRUNABLE_ORPHAN_STALE_REASONS = /* @__PURE__ */ new Set([
19379
- "direct task node is no longer in the live mesh",
19380
- "direct task session is not present in live session records",
19381
- "direct task has no node id"
19382
- ]);
19383
- function classifyStaleDirectForPrune(record, opts = {}) {
19384
- if (record.staleDispatchUnacknowledged === true) return "preserve_unacknowledged";
19385
- if (record.terminal === true) return opts.includeTerminal ? "prunable_terminal" : "preserve_active";
19386
- if (record.staleReason && PRUNABLE_ORPHAN_STALE_REASONS.has(record.staleReason)) return "prunable_orphan";
19387
- return "preserve_active";
19388
- }
19389
- function buildCompactStaleDirectWorkSummary(staleDirectWork, opts = {}) {
19390
- const sampleLimit = Math.max(0, Math.min(10, Math.floor(opts.sampleLimit ?? 3)));
19391
- const reasonCounts = {};
19392
- for (const entry of staleDirectWork) {
19393
- const reason = entry.staleReason || "unknown";
19394
- reasonCounts[reason] = (reasonCounts[reason] || 0) + 1;
19395
- }
19396
- return {
19397
- count: staleDirectWork.length,
19398
- sampleLimit,
19399
- sample: staleDirectWork.slice(0, sampleLimit).map((entry) => ({
19400
- taskId: entry.taskId,
19401
- status: entry.status,
19402
- nodeId: entry.nodeId,
19403
- sessionId: entry.sessionId,
19404
- taskTitle: entry.taskTitle,
19405
- createdAt: entry.createdAt,
19406
- staleReason: entry.staleReason
19407
- })),
19408
- reasonCounts,
19409
- 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.",
19410
- ...opts.note ? { note: opts.note } : {}
19411
- };
19412
- }
19413
-
19414
- // src/index.ts
19885
+ init_mesh_active_work();
19415
19886
  init_mesh_refine_status();
19416
19887
  init_mesh_host_ownership();
19417
19888
  init_mesh_events();
@@ -19529,8 +20000,8 @@ var P2pRelayFailureError = class extends Error {
19529
20000
 
19530
20001
  // src/config/state-store.ts
19531
20002
  init_config();
19532
- import { existsSync as existsSync15, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
19533
- import { join as join16 } from "path";
20003
+ import { existsSync as existsSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
20004
+ import { join as join17 } from "path";
19534
20005
  var DEFAULT_STATE = {
19535
20006
  recentActivity: [],
19536
20007
  savedProviderSessions: [],
@@ -19543,7 +20014,7 @@ function isPlainObject2(value) {
19543
20014
  return !!value && typeof value === "object" && !Array.isArray(value);
19544
20015
  }
19545
20016
  function getStatePath() {
19546
- return join16(getConfigDir(), "state.json");
20017
+ return join17(getConfigDir(), "state.json");
19547
20018
  }
19548
20019
  function normalizeState(raw) {
19549
20020
  const parsed = isPlainObject2(raw) ? raw : {};
@@ -19579,11 +20050,11 @@ function normalizeState(raw) {
19579
20050
  }
19580
20051
  function loadState() {
19581
20052
  const statePath = getStatePath();
19582
- if (!existsSync15(statePath)) {
20053
+ if (!existsSync16(statePath)) {
19583
20054
  return { ...DEFAULT_STATE };
19584
20055
  }
19585
20056
  try {
19586
- const raw = readFileSync11(statePath, "utf-8");
20057
+ const raw = readFileSync12(statePath, "utf-8");
19587
20058
  return normalizeState(JSON.parse(raw));
19588
20059
  } catch {
19589
20060
  return { ...DEFAULT_STATE };
@@ -19601,7 +20072,7 @@ function resetState() {
19601
20072
  // src/detection/ide-detector.ts
19602
20073
  import { exec as exec2 } from "child_process";
19603
20074
  import { promisify as promisify4 } from "util";
19604
- import { existsSync as existsSync17, statSync as statSync7 } from "fs";
20075
+ import { existsSync as existsSync18, statSync as statSync8 } from "fs";
19605
20076
  import { platform as platform3, homedir as homedir8 } from "os";
19606
20077
  import * as path13 from "path";
19607
20078
 
@@ -19682,7 +20153,7 @@ function findCliCommand(command) {
19682
20153
  if (path13.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
19683
20154
  const candidate = trimmed.startsWith("~") ? path13.join(homedir8(), trimmed.slice(1)) : trimmed;
19684
20155
  const resolved = path13.isAbsolute(candidate) ? candidate : path13.resolve(candidate);
19685
- return existsSync17(resolved) ? resolved : null;
20156
+ return existsSync18(resolved) ? resolved : null;
19686
20157
  }
19687
20158
  const isWin = platform3() === "win32";
19688
20159
  const paths = (process.env.PATH || "").split(isWin ? ";" : ":");
@@ -19692,8 +20163,8 @@ function findCliCommand(command) {
19692
20163
  for (const ext of exes) {
19693
20164
  const fullPath = path13.join(p, trimmed + ext);
19694
20165
  try {
19695
- if (existsSync17(fullPath)) {
19696
- const stat2 = statSync7(fullPath);
20166
+ if (existsSync18(fullPath)) {
20167
+ const stat2 = statSync8(fullPath);
19697
20168
  if (stat2.isFile() && (isWin || stat2.mode & 73)) {
19698
20169
  return fullPath;
19699
20170
  }
@@ -19711,9 +20182,9 @@ function checkPathExists(paths) {
19711
20182
  if (normalized.includes("*")) {
19712
20183
  const username = home.split(/[\\/]/).pop() || "";
19713
20184
  const resolved = normalized.replace("*", username);
19714
- if (existsSync17(resolved)) return resolved;
20185
+ if (existsSync18(resolved)) return resolved;
19715
20186
  } else {
19716
- if (existsSync17(normalized)) return normalized;
20187
+ if (existsSync18(normalized)) return normalized;
19717
20188
  }
19718
20189
  }
19719
20190
  return null;
@@ -19727,7 +20198,7 @@ async function detectIDEs(providerLoader) {
19727
20198
  let resolvedCli = cliPath;
19728
20199
  if (!resolvedCli && appPath && os30 === "darwin") {
19729
20200
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
19730
- if (existsSync17(bundledCli)) resolvedCli = bundledCli;
20201
+ if (existsSync18(bundledCli)) resolvedCli = bundledCli;
19731
20202
  }
19732
20203
  if (!resolvedCli && appPath && os30 === "win32") {
19733
20204
  const { dirname: dirname17 } = await import("path");
@@ -19740,7 +20211,7 @@ async function detectIDEs(providerLoader) {
19740
20211
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
19741
20212
  ];
19742
20213
  for (const c of candidates) {
19743
- if (existsSync17(c)) {
20214
+ if (existsSync18(c)) {
19744
20215
  resolvedCli = c;
19745
20216
  break;
19746
20217
  }
@@ -21285,8 +21756,8 @@ init_contracts();
21285
21756
  // src/providers/status-monitor.ts
21286
21757
  var DEFAULT_MONITOR_CONFIG = {
21287
21758
  approvalAlert: true,
21288
- longGeneratingAlert: true,
21289
- longGeneratingThresholdSec: 180,
21759
+ noProgressAlert: true,
21760
+ noProgressThresholdSec: 180,
21290
21761
  // 3 minutes
21291
21762
  alertCooldownSec: 60
21292
21763
  // 1 minute cooldown
@@ -21295,7 +21766,7 @@ var StatusMonitor = class {
21295
21766
  config;
21296
21767
  lastAlertTime = /* @__PURE__ */ new Map();
21297
21768
  generatingStartTimes = /* @__PURE__ */ new Map();
21298
- longGeneratingAlerted = /* @__PURE__ */ new Map();
21769
+ noProgressAlerted = /* @__PURE__ */ new Map();
21299
21770
  lastProgressFingerprint = /* @__PURE__ */ new Map();
21300
21771
  lastProgressChangeAt = /* @__PURE__ */ new Map();
21301
21772
  constructor(config) {
@@ -21313,7 +21784,7 @@ var StatusMonitor = class {
21313
21784
  * Check status transition → return notification event array.
21314
21785
  * Called from each onTick() or detectStatusTransition().
21315
21786
  */
21316
- check(agentKey, status, now, progressFingerprint) {
21787
+ check(agentKey, status, now, progressFingerprint, approvalPending) {
21317
21788
  const events = [];
21318
21789
  if (this.config.approvalAlert && status === "waiting_approval") {
21319
21790
  if (this.shouldAlert(agentKey + ":approval", now)) {
@@ -21326,9 +21797,16 @@ var StatusMonitor = class {
21326
21797
  }
21327
21798
  }
21328
21799
  if (status === "generating" || status === "streaming") {
21800
+ if (approvalPending) {
21801
+ this.generatingStartTimes.set(agentKey, now);
21802
+ this.lastProgressFingerprint.set(agentKey, progressFingerprint ?? "");
21803
+ this.lastProgressChangeAt.set(agentKey, now);
21804
+ this.noProgressAlerted.set(agentKey, false);
21805
+ return events;
21806
+ }
21329
21807
  if (!this.generatingStartTimes.has(agentKey)) {
21330
21808
  this.generatingStartTimes.set(agentKey, now);
21331
- this.longGeneratingAlerted.set(agentKey, false);
21809
+ this.noProgressAlerted.set(agentKey, false);
21332
21810
  const initialFingerprint = progressFingerprint ?? "";
21333
21811
  this.lastProgressFingerprint.set(agentKey, initialFingerprint);
21334
21812
  this.lastProgressChangeAt.set(agentKey, now);
@@ -21338,17 +21816,17 @@ var StatusMonitor = class {
21338
21816
  if (previousFingerprint !== currentFingerprint) {
21339
21817
  this.lastProgressFingerprint.set(agentKey, currentFingerprint);
21340
21818
  this.lastProgressChangeAt.set(agentKey, now);
21341
- this.longGeneratingAlerted.set(agentKey, false);
21819
+ this.noProgressAlerted.set(agentKey, false);
21342
21820
  }
21343
- if (this.config.longGeneratingAlert) {
21821
+ if (this.config.noProgressAlert) {
21344
21822
  const progressChangedAt = this.lastProgressChangeAt.get(agentKey) || this.generatingStartTimes.get(agentKey);
21345
21823
  const elapsedSec = Math.round((now - progressChangedAt) / 1e3);
21346
- const alreadyAlerted = this.longGeneratingAlerted.get(agentKey) === true;
21347
- if (elapsedSec > this.config.longGeneratingThresholdSec && !alreadyAlerted) {
21348
- if (this.shouldAlert(agentKey + ":long_gen", now)) {
21349
- this.longGeneratingAlerted.set(agentKey, true);
21824
+ const alreadyAlerted = this.noProgressAlerted.get(agentKey) === true;
21825
+ if (elapsedSec > this.config.noProgressThresholdSec && !alreadyAlerted) {
21826
+ if (this.shouldAlert(agentKey + ":no_progress", now)) {
21827
+ this.noProgressAlerted.set(agentKey, true);
21350
21828
  events.push({
21351
- type: "monitor:long_generating",
21829
+ type: "monitor:no_progress",
21352
21830
  agentKey,
21353
21831
  elapsedSec,
21354
21832
  timestamp: now,
@@ -21359,7 +21837,7 @@ var StatusMonitor = class {
21359
21837
  }
21360
21838
  } else {
21361
21839
  this.generatingStartTimes.delete(agentKey);
21362
- this.longGeneratingAlerted.delete(agentKey);
21840
+ this.noProgressAlerted.delete(agentKey);
21363
21841
  this.lastProgressFingerprint.delete(agentKey);
21364
21842
  this.lastProgressChangeAt.delete(agentKey);
21365
21843
  }
@@ -21378,7 +21856,7 @@ var StatusMonitor = class {
21378
21856
  reset(agentKey) {
21379
21857
  if (agentKey) {
21380
21858
  this.generatingStartTimes.delete(agentKey);
21381
- this.longGeneratingAlerted.delete(agentKey);
21859
+ this.noProgressAlerted.delete(agentKey);
21382
21860
  this.lastProgressFingerprint.delete(agentKey);
21383
21861
  this.lastProgressChangeAt.delete(agentKey);
21384
21862
  for (const k of this.lastAlertTime.keys()) {
@@ -21386,7 +21864,7 @@ var StatusMonitor = class {
21386
21864
  }
21387
21865
  } else {
21388
21866
  this.generatingStartTimes.clear();
21389
- this.longGeneratingAlerted.clear();
21867
+ this.noProgressAlerted.clear();
21390
21868
  this.lastProgressFingerprint.clear();
21391
21869
  this.lastProgressChangeAt.clear();
21392
21870
  this.lastAlertTime.clear();
@@ -23209,8 +23687,8 @@ var ExtensionProviderInstance = class {
23209
23687
  this.settings = context.settings || {};
23210
23688
  this.monitor.updateConfig({
23211
23689
  approvalAlert: this.settings.approvalAlert !== false,
23212
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
23213
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
23690
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
23691
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
23214
23692
  });
23215
23693
  }
23216
23694
  async onTick() {
@@ -23301,8 +23779,8 @@ var ExtensionProviderInstance = class {
23301
23779
  this.settings = { ...this.settings, ...newSettings };
23302
23780
  this.monitor.updateConfig({
23303
23781
  approvalAlert: this.settings.approvalAlert !== false,
23304
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
23305
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
23782
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
23783
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
23306
23784
  });
23307
23785
  }
23308
23786
  /** Query UUID instanceId */
@@ -23362,7 +23840,8 @@ var ExtensionProviderInstance = class {
23362
23840
  phase: agentStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
23363
23841
  });
23364
23842
  const agentKey = `${this.type}:ext`;
23365
- const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
23843
+ const approvalPending = agentStatus === "waiting_approval";
23844
+ const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint, approvalPending);
23366
23845
  for (const me of monitorEvents) {
23367
23846
  this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
23368
23847
  }
@@ -23577,7 +24056,7 @@ init_contracts();
23577
24056
  var CHAT_CONTRACT_VERSION_V1 = "1.0";
23578
24057
 
23579
24058
  // src/providers/read-chat-contract.ts
23580
- var VALID_STATUSES = ["idle", "generating", "waiting_approval", "error", "panel_hidden", "starting", "streaming", "long_generating"];
24059
+ var VALID_STATUSES = ["idle", "generating", "waiting_approval", "error", "panel_hidden", "starting", "streaming", "no_progress", "long_generating"];
23581
24060
  var VALID_ROLES = ["user", "assistant", "system", "human"];
23582
24061
  var VALID_BUBBLE_STATES = ["draft", "streaming", "final", "removed"];
23583
24062
  var VALID_TURN_STATUSES = ["open", "waiting_approval", "complete", "error"];
@@ -23844,8 +24323,8 @@ var IdeProviderInstance = class {
23844
24323
  this.settings = context.settings || {};
23845
24324
  this.monitor.updateConfig({
23846
24325
  approvalAlert: this.settings.approvalAlert !== false,
23847
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
23848
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
24326
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
24327
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
23849
24328
  });
23850
24329
  }
23851
24330
  async onTick() {
@@ -23972,8 +24451,8 @@ var IdeProviderInstance = class {
23972
24451
  this.settings = { ...this.settings, ...newSettings };
23973
24452
  this.monitor.updateConfig({
23974
24453
  approvalAlert: this.settings.approvalAlert !== false,
23975
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
23976
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
24454
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
24455
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
23977
24456
  });
23978
24457
  }
23979
24458
  // ─── Extension manage ─────────────────────────────
@@ -24104,7 +24583,7 @@ var IdeProviderInstance = class {
24104
24583
  const persistedMessages = chat.messages || messages;
24105
24584
  if (persistedMessages.length > 0) {
24106
24585
  let toSave = persistedMessages;
24107
- if (chat.status === "generating" || chat.status === "long_generating") {
24586
+ if (chat.status === "generating" || chat.status === "no_progress" || chat.status === "long_generating") {
24108
24587
  const lastIdx = toSave.length - 1;
24109
24588
  if (lastIdx >= 0 && toSave[lastIdx].role === "assistant") {
24110
24589
  toSave = toSave.slice(0, lastIdx);
@@ -24174,7 +24653,8 @@ var IdeProviderInstance = class {
24174
24653
  if (rawAgentStatus === "waiting_approval" && autoApproveActive && !this.autoApproveBusy) {
24175
24654
  this.autoApproveViaScript(chatData);
24176
24655
  }
24177
- const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
24656
+ const approvalPending = rawAgentStatus === "waiting_approval";
24657
+ const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint, approvalPending);
24178
24658
  for (const me of monitorEvents) {
24179
24659
  this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
24180
24660
  }
@@ -26731,7 +27211,7 @@ function normalizeReadChatCommandStatus(status, activeModal) {
26731
27211
  }
26732
27212
  }
26733
27213
  function isGeneratingLikeStatus(status) {
26734
- return status === "generating" || status === "streaming" || status === "long_generating" || status === "starting";
27214
+ return status === "generating" || status === "streaming" || status === "no_progress" || status === "long_generating" || status === "starting";
26735
27215
  }
26736
27216
  function hasVisibleAssistantMessage(messages) {
26737
27217
  if (!Array.isArray(messages)) return false;
@@ -30767,7 +31247,7 @@ init_config();
30767
31247
  import * as os19 from "os";
30768
31248
  import * as path26 from "path";
30769
31249
  import * as crypto5 from "crypto";
30770
- import { existsSync as existsSync26, mkdirSync as mkdirSync12, writeFileSync as writeFileSync15 } from "fs";
31250
+ import { existsSync as existsSync27, mkdirSync as mkdirSync12, writeFileSync as writeFileSync15 } from "fs";
30771
31251
  import { execFileSync as execFileSync2 } from "child_process";
30772
31252
  import chalk from "chalk";
30773
31253
 
@@ -33409,7 +33889,7 @@ function hasNonEmptyCliModalButtons(activeModal) {
33409
33889
  return Array.isArray(buttons) && buttons.some((button) => String(button || "").trim().length > 0);
33410
33890
  }
33411
33891
  function isCliGeneratingLikeStatus(status) {
33412
- return status === "generating" || status === "streaming" || status === "long_generating" || status === "starting";
33892
+ return status === "generating" || status === "streaming" || status === "no_progress" || status === "long_generating" || status === "starting";
33413
33893
  }
33414
33894
  function buildCliStructuredInputPrompt(input, options = {}) {
33415
33895
  const promptParts = [];
@@ -33611,8 +34091,8 @@ var CliProviderInstance = class _CliProviderInstance {
33611
34091
  this.adapter.updateRuntimeSettings?.(this.settings);
33612
34092
  this.monitor.updateConfig({
33613
34093
  approvalAlert: this.settings.approvalAlert !== false,
33614
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
33615
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
34094
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
34095
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
33616
34096
  });
33617
34097
  if (context.serverConn) {
33618
34098
  this.adapter.setServerConn(context.serverConn);
@@ -33759,7 +34239,7 @@ var CliProviderInstance = class _CliProviderInstance {
33759
34239
  if (parsedMessages.length > 0) {
33760
34240
  const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
33761
34241
  let messagesToSave = parsedMessages;
33762
- if (!suppressStaleParsedBusyStatus && (parsedChatStatus === "generating" || parsedChatStatus === "long_generating")) {
34242
+ if (!suppressStaleParsedBusyStatus && (parsedChatStatus === "generating" || parsedChatStatus === "no_progress" || parsedChatStatus === "long_generating")) {
33763
34243
  const lastIdx = messagesToSave.length - 1;
33764
34244
  if (lastIdx >= 0 && messagesToSave[lastIdx]?.role === "assistant") {
33765
34245
  messagesToSave = messagesToSave.slice(0, lastIdx);
@@ -33887,8 +34367,8 @@ var CliProviderInstance = class _CliProviderInstance {
33887
34367
  this.adapter.updateRuntimeSettings?.(this.settings);
33888
34368
  this.monitor.updateConfig({
33889
34369
  approvalAlert: this.settings.approvalAlert !== false,
33890
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
33891
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
34370
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
34371
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
33892
34372
  });
33893
34373
  }
33894
34374
  /**
@@ -34620,10 +35100,11 @@ var CliProviderInstance = class _CliProviderInstance {
34620
35100
  phase: newStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
34621
35101
  });
34622
35102
  const agentKey = `${this.type}:cli`;
34623
- const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
35103
+ const approvalPending = rawStatus === "waiting_approval";
35104
+ const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint, approvalPending);
34624
35105
  const monitorParsedStatus = parsedStatus;
34625
35106
  for (const me of monitorEvents) {
34626
- if (me.type === "monitor:long_generating" && this.completionHasFinalAssistantMessage(monitorParsedStatus?.messages) && !this.hasAdapterPendingResponse() && !hasNonEmptyCliModalButtons(monitorParsedStatus?.activeModal ?? monitorParsedStatus?.modal)) {
35107
+ if (me.type === "monitor:no_progress" && this.completionHasFinalAssistantMessage(monitorParsedStatus?.messages) && !this.hasAdapterPendingResponse() && !hasNonEmptyCliModalButtons(monitorParsedStatus?.activeModal ?? monitorParsedStatus?.modal)) {
34627
35108
  this.pushEvent({
34628
35109
  event: "agent:generating_completed",
34629
35110
  chatTitle,
@@ -34634,7 +35115,7 @@ var CliProviderInstance = class _CliProviderInstance {
34634
35115
  providerType: this.type,
34635
35116
  sessionId: this.instanceId,
34636
35117
  providerSessionId: this.providerSessionId || null,
34637
- reconciliationReason: "long_generating_monitor_final_summary",
35118
+ reconciliationReason: "no_progress_monitor_final_summary",
34638
35119
  finalAssistantPresent: true
34639
35120
  }
34640
35121
  });
@@ -35316,8 +35797,8 @@ var AcpProviderInstance = class {
35316
35797
  this.settings = context.settings || {};
35317
35798
  this.monitor.updateConfig({
35318
35799
  approvalAlert: this.settings.approvalAlert !== false,
35319
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
35320
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
35800
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
35801
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
35321
35802
  });
35322
35803
  await this.spawnAgent();
35323
35804
  }
@@ -35613,8 +36094,8 @@ var AcpProviderInstance = class {
35613
36094
  this.settings = { ...this.settings, ...newSettings };
35614
36095
  this.monitor.updateConfig({
35615
36096
  approvalAlert: this.settings.approvalAlert !== false,
35616
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
35617
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
36097
+ noProgressAlert: (this.settings.noProgressAlert ?? this.settings.longGeneratingAlert) !== false,
36098
+ noProgressThresholdSec: this.settings.noProgressThresholdSec ?? this.settings.longGeneratingThresholdSec ?? 180
35618
36099
  });
35619
36100
  this.log.info(`[${this.type}] Settings updated: ${Object.keys(newSettings).join(", ")}`);
35620
36101
  }
@@ -36321,7 +36802,8 @@ ${rawInput}` : rawInput;
36321
36802
  this.lastStatus = newStatus;
36322
36803
  }
36323
36804
  const agentKey = `${this.type}:acp`;
36324
- const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
36805
+ const approvalPending = newStatus === "waiting_approval";
36806
+ const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint, approvalPending);
36325
36807
  for (const me of monitorEvents) {
36326
36808
  this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
36327
36809
  }
@@ -36383,7 +36865,7 @@ function commandExists(command) {
36383
36865
  const trimmed = command.trim();
36384
36866
  if (!trimmed) return false;
36385
36867
  if (isExplicitCommand(trimmed)) {
36386
- return existsSync26(expandExecutable(trimmed));
36868
+ return existsSync27(expandExecutable(trimmed));
36387
36869
  }
36388
36870
  try {
36389
36871
  execFileSync2(process.platform === "win32" ? "where" : "which", [trimmed], {
@@ -36395,7 +36877,7 @@ function commandExists(command) {
36395
36877
  return false;
36396
36878
  }
36397
36879
  }
36398
- var BUSY_AGENT_STATUSES = /* @__PURE__ */ new Set(["generating", "running", "streaming", "starting", "busy", "waiting", "waiting_approval", "long_generating"]);
36880
+ var BUSY_AGENT_STATUSES = /* @__PURE__ */ new Set(["generating", "running", "streaming", "starting", "busy", "waiting", "waiting_approval", "no_progress", "long_generating"]);
36399
36881
  var ZERO_MESSAGE_STARTING_SEND_WAIT_MS = 2e3;
36400
36882
  function normalizeAgentStatus(value) {
36401
36883
  return typeof value === "string" ? value.trim().toLowerCase() : "";
@@ -41260,6 +41742,7 @@ function getAvailableIdeIds() {
41260
41742
  init_config();
41261
41743
  init_cli_detector();
41262
41744
  init_git_status();
41745
+ init_change_impact_config();
41263
41746
  init_dist();
41264
41747
  init_logger();
41265
41748
 
@@ -41409,7 +41892,7 @@ cleanOldFiles();
41409
41892
 
41410
41893
  // src/commands/router.ts
41411
41894
  init_logger();
41412
- import * as yaml3 from "js-yaml";
41895
+ import * as yaml4 from "js-yaml";
41413
41896
 
41414
41897
  // src/logging/log-tail-reader.ts
41415
41898
  init_logger();
@@ -41710,7 +42193,7 @@ function orderMeshRefineBatchNodes(changeAreas) {
41710
42193
 
41711
42194
  // src/mesh/preview-freshness.ts
41712
42195
  import { execFileSync as execFileSync4 } from "child_process";
41713
- import { existsSync as existsSync35, readFileSync as readFileSync26 } from "fs";
42196
+ import { existsSync as existsSync36, readFileSync as readFileSync27 } from "fs";
41714
42197
  import { resolve as resolve19 } from "path";
41715
42198
  var PREVIEW_DEPLOY_RECORD = ".adhdev/preview-deploy.json";
41716
42199
  function runGit2(repoRoot, args) {
@@ -41727,9 +42210,9 @@ function runGit2(repoRoot, args) {
41727
42210
  }
41728
42211
  function readRecord6(repoRoot) {
41729
42212
  const path42 = resolve19(repoRoot, PREVIEW_DEPLOY_RECORD);
41730
- if (!existsSync35(path42)) return null;
42213
+ if (!existsSync36(path42)) return null;
41731
42214
  try {
41732
- const parsed = JSON.parse(readFileSync26(path42, "utf8"));
42215
+ const parsed = JSON.parse(readFileSync27(path42, "utf8"));
41733
42216
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
41734
42217
  } catch {
41735
42218
  return null;
@@ -41794,8 +42277,8 @@ function buildPreviewFreshness(repoRoot) {
41794
42277
  init_mesh_refine_status();
41795
42278
 
41796
42279
  // src/mesh/mesh-init.ts
41797
- import { existsSync as existsSync36, mkdirSync as mkdirSync15, writeFileSync as writeFileSync17 } from "fs";
41798
- import { dirname as dirname10, join as join39 } from "path";
42280
+ import { existsSync as existsSync37, mkdirSync as mkdirSync15, writeFileSync as writeFileSync17 } from "fs";
42281
+ import { dirname as dirname10, join as join40 } from "path";
41799
42282
  var MESH_INIT_REFINE_CONFIG_PATH = MESH_REFINE_CONFIG_LOCATIONS[0];
41800
42283
  var MESH_INIT_WORKTREE_BOOTSTRAP_CONFIG_PATH = MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS[0];
41801
42284
  var CANDIDATE_STALE_INPUTS = [
@@ -41809,7 +42292,7 @@ var CANDIDATE_STALE_INPUTS = [
41809
42292
  "requirements.txt"
41810
42293
  ];
41811
42294
  function writeConfigFile(workspace, relativePath, config) {
41812
- const target = join39(workspace, relativePath);
42295
+ const target = join40(workspace, relativePath);
41813
42296
  mkdirSync15(dirname10(target), { recursive: true });
41814
42297
  writeFileSync17(target, `${JSON.stringify(config, null, 2)}
41815
42298
  `, "utf-8");
@@ -41817,14 +42300,14 @@ function writeConfigFile(workspace, relativePath, config) {
41817
42300
  }
41818
42301
  function suggestMeshWorktreeBootstrapConfig(workspace) {
41819
42302
  const commands = [];
41820
- const hasPackageJson = existsSync36(join39(workspace, "package.json"));
41821
- const hasNpmLock = existsSync36(join39(workspace, "package-lock.json"));
42303
+ const hasPackageJson = existsSync37(join40(workspace, "package.json"));
42304
+ const hasNpmLock = existsSync37(join40(workspace, "package-lock.json"));
41822
42305
  if (hasPackageJson) {
41823
42306
  commands.push(
41824
42307
  hasNpmLock ? { command: "npm", args: ["ci"] } : { command: "npm", args: ["install"] }
41825
42308
  );
41826
42309
  }
41827
- const staleInputs = CANDIDATE_STALE_INPUTS.filter((relative5) => existsSync36(join39(workspace, relative5)));
42310
+ const staleInputs = CANDIDATE_STALE_INPUTS.filter((relative5) => existsSync37(join40(workspace, relative5)));
41828
42311
  if (!commands.length) {
41829
42312
  return { commands, staleInputs };
41830
42313
  }
@@ -41892,7 +42375,7 @@ function runMeshInit(mesh, workspace, detected, options = {}) {
41892
42375
  }
41893
42376
  function applyConfigSuggestion(input) {
41894
42377
  const { workspace, relativePath, existing, suggestedConfig, validate, write, overwrite } = input;
41895
- const absolute = join39(workspace, relativePath);
42378
+ const absolute = join40(workspace, relativePath);
41896
42379
  if (existing !== void 0 && !overwrite) {
41897
42380
  return { path: absolute, relativePath, written: false, skippedReason: "already_exists", config: existing };
41898
42381
  }
@@ -44691,7 +45174,7 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
44691
45174
  return summary;
44692
45175
  }
44693
45176
  function loadYamlModule() {
44694
- return yaml3;
45177
+ return yaml4;
44695
45178
  }
44696
45179
  function getMcpServersKey(format) {
44697
45180
  return format === "hermes_config_yaml" ? "mcp_servers" : "mcpServers";
@@ -48433,6 +48916,42 @@ ${hintLines.join("\n")}` : "",
48433
48916
  note: "Suggestions are heuristic scaffold only; Refinery will not execute them until saved into repo mesh/refine config."
48434
48917
  };
48435
48918
  }
48919
+ case "get_mesh_change_impact_config_schema": {
48920
+ return {
48921
+ success: true,
48922
+ schema: CHANGE_IMPACT_CONFIG_SCHEMA,
48923
+ locations: CHANGE_IMPACT_CONFIG_LOCATIONS,
48924
+ sourceOfTruth: "repo change-impact config",
48925
+ heuristicRole: "suggestions_only_not_execution_path",
48926
+ 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."
48927
+ };
48928
+ }
48929
+ case "validate_mesh_change_impact_config": {
48930
+ const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
48931
+ if (args?.config !== void 0) {
48932
+ const validation = validateChangeImpactConfig(args.config, "inline");
48933
+ return { success: validation.valid, source: "inline", sourceType: "mesh_policy", ...validation };
48934
+ }
48935
+ const loaded = loadChangeImpactConfig(workspace);
48936
+ if (loaded.sourceType === "repo_file") {
48937
+ const validation = validateChangeImpactConfig(loaded.config, loaded.source);
48938
+ return { success: validation.valid, ...loaded, ...validation };
48939
+ }
48940
+ return {
48941
+ success: false,
48942
+ ...loaded,
48943
+ valid: false,
48944
+ errors: [loaded.error || "repo change-impact config unavailable"]
48945
+ };
48946
+ }
48947
+ case "suggest_mesh_change_impact_config": {
48948
+ const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
48949
+ return {
48950
+ success: true,
48951
+ ...suggestChangeImpactConfig(workspace),
48952
+ 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."
48953
+ };
48954
+ }
48436
48955
  case "mesh_init": {
48437
48956
  const workspace = typeof args?.workspace === "string" && args.workspace.trim() ? args.workspace.trim() : process.cwd();
48438
48957
  const mesh = args?.inlineMesh || {};
@@ -49349,7 +49868,7 @@ ${ptyResult.output.slice(-2e3)}`);
49349
49868
  workspace
49350
49869
  };
49351
49870
  }
49352
- const { existsSync: existsSync45, readFileSync: readFileSync36, writeFileSync: writeFileSync23, copyFileSync: copyFileSync4, mkdirSync: mkdirSync21 } = await import("fs");
49871
+ const { existsSync: existsSync46, readFileSync: readFileSync37, writeFileSync: writeFileSync23, copyFileSync: copyFileSync4, mkdirSync: mkdirSync21 } = await import("fs");
49353
49872
  const { dirname: dirname17 } = await import("path");
49354
49873
  const mcpConfigPath = coordinatorSetup.configPath;
49355
49874
  const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
@@ -49392,14 +49911,14 @@ ${ptyResult.output.slice(-2e3)}`);
49392
49911
  if (hermesManualFallback) return returnManualFallback(message);
49393
49912
  return { success: false, code: "mesh_coordinator_config_write_failed", error: message, meshId, cliType, workspace };
49394
49913
  }
49395
- const hadExistingMcpConfig = existsSync45(mcpConfigPath);
49914
+ const hadExistingMcpConfig = existsSync46(mcpConfigPath);
49396
49915
  let existingMcpConfig = hermesBaseConfig?.config || {};
49397
49916
  if (hermesBaseConfig) {
49398
49917
  copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname17(mcpConfigPath));
49399
49918
  }
49400
49919
  if (hadExistingMcpConfig) {
49401
49920
  try {
49402
- const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync36(mcpConfigPath, "utf-8"), configFormat);
49921
+ const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync37(mcpConfigPath, "utf-8"), configFormat);
49403
49922
  const existingCoordinatorConfig = hermesManualFallback ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig) : parsedExistingMcpConfig;
49404
49923
  existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
49405
49924
  copyFileSync4(mcpConfigPath, mcpConfigPath + ".backup");
@@ -49898,7 +50417,7 @@ ${ptyResult.output.slice(-2e3)}`);
49898
50417
  const { deriveMeshReviewInboxItems: deriveMeshReviewInboxItems2 } = await Promise.resolve().then(() => (init_mesh_review_inbox(), mesh_review_inbox_exports));
49899
50418
  const { readLedgerEntries: readLedgerEntries2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
49900
50419
  const { getGitDiffSummary: getGitDiffSummary2 } = await Promise.resolve().then(() => (init_git_diff(), git_diff_exports));
49901
- const { existsSync: existsSync45 } = await import("fs");
50420
+ const { existsSync: existsSync46 } = await import("fs");
49902
50421
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
49903
50422
  const mesh = meshRecord?.mesh;
49904
50423
  if (!mesh) return { success: false, error: "Mesh not found" };
@@ -49917,7 +50436,7 @@ ${ptyResult.output.slice(-2e3)}`);
49917
50436
  const derivation = deriveMeshReviewInboxItems2({ nodes: nodeStatuses, ledgerEntries });
49918
50437
  for (const item of derivation.items) {
49919
50438
  const workspace = item.workspace;
49920
- if (!workspace || !existsSync45(workspace)) continue;
50439
+ if (!workspace || !existsSync46(workspace)) continue;
49921
50440
  const baseRef = item.defaultBranch ? `origin/${item.defaultBranch}` : "origin/main";
49922
50441
  try {
49923
50442
  const diffResult = await getGitDiffSummary2(workspace, { baseRef, maxFiles: 100 });
@@ -50091,7 +50610,7 @@ var DaemonStatusReporter = class {
50091
50610
  case "agent:waiting_approval":
50092
50611
  case "agent:generating_completed":
50093
50612
  case "agent:stopped":
50094
- case "monitor:long_generating":
50613
+ case "monitor:no_progress":
50095
50614
  return value;
50096
50615
  default:
50097
50616
  return null;
@@ -50636,7 +51155,7 @@ var ProviderStreamAdapter = class {
50636
51155
  }
50637
51156
  const validated = validateReadChatResultPayload(data, `${this.agentType} readChat`);
50638
51157
  const validatedStatus = validated.status;
50639
- const streamStatus = validatedStatus === "generating" || validatedStatus === "long_generating" ? "streaming" : validatedStatus;
51158
+ const streamStatus = validatedStatus === "generating" || validatedStatus === "no_progress" || validatedStatus === "long_generating" ? "streaming" : validatedStatus;
50640
51159
  const state = {
50641
51160
  agentType: this.agentType,
50642
51161
  agentName: this.agentName,
@@ -58476,12 +58995,12 @@ init_parse_session();
58476
58995
 
58477
58996
  // src/providers/sdk/v1/fixture-tooling/replay.ts
58478
58997
  init_provider_cli_shared();
58479
- import { readFileSync as readFileSync34 } from "fs";
58998
+ import { readFileSync as readFileSync35 } from "fs";
58480
58999
  import { dirname as dirname15, resolve as resolve22 } from "path";
58481
59000
 
58482
59001
  // src/providers/sdk/v1/validators/taint.ts
58483
- import { readFileSync as readFileSync35, existsSync as existsSync44 } from "fs";
58484
- import { resolve as resolve23, dirname as dirname16, join as join46 } from "path";
59002
+ import { readFileSync as readFileSync36, existsSync as existsSync45 } from "fs";
59003
+ import { resolve as resolve23, dirname as dirname16, join as join47 } from "path";
58485
59004
 
58486
59005
  // src/providers/sdk/v1/validators/index.ts
58487
59006
  init_manifest();
@@ -58561,6 +59080,8 @@ export {
58561
59080
  AcpProviderInstance,
58562
59081
  AgentStreamPoller,
58563
59082
  BUILTIN_CHAT_MESSAGE_KINDS,
59083
+ CHANGE_IMPACT_CONFIG_LOCATIONS,
59084
+ CHANGE_IMPACT_CONFIG_SCHEMA,
58564
59085
  CHAT_MESSAGE_ACTIVITY_SOURCES,
58565
59086
  CHAT_MESSAGE_AUDIENCES,
58566
59087
  CHAT_MESSAGE_INTERNAL_SOURCES,
@@ -58751,6 +59272,7 @@ export {
58751
59272
  getSessionHostSurfaceKind,
58752
59273
  getSessionRecoveryContext,
58753
59274
  getWorkspaceState,
59275
+ globToRegExp,
58754
59276
  handleGitCommand,
58755
59277
  hasCdpManager,
58756
59278
  hasPendingDependents,
@@ -58784,6 +59306,7 @@ export {
58784
59306
  listMeshMissionSummaries,
58785
59307
  listMeshes,
58786
59308
  listWorktrees,
59309
+ loadChangeImpactConfig,
58787
59310
  loadConfig,
58788
59311
  loadMeshCoordinatorRegistry,
58789
59312
  loadMeshRefineConfig,
@@ -58824,6 +59347,7 @@ export {
58824
59347
  prepareSessionChatTailUpdate,
58825
59348
  prepareSessionModalUpdate,
58826
59349
  probeCdpPort,
59350
+ pruneStaleDirectDispatches,
58827
59351
  queuePendingMeshCoordinatorEvent,
58828
59352
  readSession3 as readAntigravityCliSession,
58829
59353
  readCachedInlineMeshActiveSessionDetails,
@@ -58877,6 +59401,7 @@ export {
58877
59401
  spawnDetachedDaemonUpgradeHelper,
58878
59402
  startDaemonDevSupport,
58879
59403
  startLocalIpcServer,
59404
+ suggestChangeImpactConfig,
58880
59405
  suggestMeshRefineConfig,
58881
59406
  summarizeGitStatus,
58882
59407
  summarizeMeshAsyncRefineJobs,
@@ -58893,6 +59418,7 @@ export {
58893
59418
  updateTaskStatus,
58894
59419
  upsertMeshMission,
58895
59420
  upsertSavedProviderSession,
59421
+ validateChangeImpactConfig,
58896
59422
  validateCliProviderManifest,
58897
59423
  validateFsmSpec,
58898
59424
  validateMeshRefineConfig,