@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.
- package/dist/cli-adapters/provider-cli-shared.d.ts +1 -1
- package/dist/git/change-impact-config.d.ts +159 -0
- package/dist/git/git-status.d.ts +14 -0
- package/dist/git/index.d.ts +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1140 -607
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1090 -564
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-active-work.d.ts +66 -0
- package/dist/mesh/mesh-events-stale.d.ts +1 -1
- package/dist/providers/status-monitor.d.ts +7 -7
- package/dist/shared-types.d.ts +1 -1
- package/package.json +2 -2
- package/src/agent-stream/provider-adapter.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +2 -2
- package/src/cli-adapters/provider-cli-shared.ts +1 -1
- package/src/commands/chat-commands.ts +1 -1
- package/src/commands/cli-manager.ts +1 -1
- package/src/commands/router.ts +46 -0
- package/src/git/change-impact-config.ts +354 -0
- package/src/git/git-status.ts +182 -16
- package/src/git/index.ts +16 -0
- package/src/index.ts +2 -2
- package/src/mesh/mesh-active-work.ts +154 -0
- package/src/mesh/mesh-events-coordinator.ts +13 -12
- package/src/mesh/mesh-events-stale.ts +4 -4
- package/src/mesh/mesh-events-utils.ts +3 -3
- package/src/mesh/mesh-reconcile-loop.ts +144 -1
- package/src/providers/acp-provider-instance.ts +6 -5
- package/src/providers/cli-provider-instance.ts +14 -10
- package/src/providers/extension-provider-instance.ts +7 -6
- package/src/providers/ide-provider-instance.ts +10 -6
- package/src/providers/read-chat-contract.ts +1 -1
- package/src/providers/status-monitor.d.ts +7 -7
- package/src/providers/status-monitor.ts +37 -22
- package/src/shared-types.ts +3 -0
- 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 ? "
|
|
312
|
-
const commitShort = readInjected(true ? "
|
|
313
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
314
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
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
|
|
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) =>
|
|
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
|
|
450
|
-
|
|
451
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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 (!
|
|
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
|
|
1218
|
-
import { existsSync as
|
|
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() :
|
|
1314
|
-
if (!
|
|
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 =
|
|
1321
|
-
if (!
|
|
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
|
|
1632
|
+
return join3(getConfigDir(), "config.json");
|
|
1328
1633
|
}
|
|
1329
1634
|
function migrateStateToStateFile(raw) {
|
|
1330
|
-
const statePath =
|
|
1331
|
-
if (
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
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
|
|
1539
|
-
import { join as
|
|
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
|
|
1847
|
+
return join5(getConfigDir(), "meshes.json");
|
|
1543
1848
|
}
|
|
1544
1849
|
function loadMeshConfig() {
|
|
1545
1850
|
const path42 = getMeshConfigPath();
|
|
1546
|
-
if (!
|
|
1851
|
+
if (!existsSync5(path42)) return { meshes: [] };
|
|
1547
1852
|
try {
|
|
1548
|
-
const raw = JSON.parse(
|
|
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
|
|
2252
|
-
import { join as
|
|
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 =
|
|
2262
|
-
if (!
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2590
|
+
return join7(getLedgerDir(), `${safe}.archived-counts.json`);
|
|
2286
2591
|
}
|
|
2287
2592
|
function rotateArchiveFile(meshId, archivePath) {
|
|
2288
2593
|
let index = 1;
|
|
2289
|
-
while (
|
|
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 (!
|
|
2608
|
+
if (!existsSync6(path42)) return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: "" };
|
|
2304
2609
|
try {
|
|
2305
|
-
return JSON.parse(
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
2814
|
+
if (existsSync6(filePath)) {
|
|
2510
2815
|
try {
|
|
2511
|
-
const stat2 =
|
|
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 (!
|
|
2912
|
+
if (!existsSync6(filePath)) return [];
|
|
2608
2913
|
let content;
|
|
2609
2914
|
try {
|
|
2610
|
-
content =
|
|
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 (
|
|
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
|
|
3543
|
-
import { dirname as dirname2, join as
|
|
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
|
|
3860
|
+
return join8(getLedgerDir(), `${safeMeshId(meshId)}.queue.json`);
|
|
3556
3861
|
}
|
|
3557
3862
|
function meshRuntimeStorePath() {
|
|
3558
3863
|
const dir = getLedgerDir();
|
|
3559
|
-
const nextPath =
|
|
3560
|
-
if (
|
|
3561
|
-
const legacyPath =
|
|
3562
|
-
if (!
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
3849
|
-
const 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 (!
|
|
4170
|
+
if (!existsSync7(path42)) return;
|
|
3866
4171
|
try {
|
|
3867
|
-
const entries = JSON.parse(
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
6188
|
+
if (trimmed.startsWith("~/")) return join10(os4.homedir(), trimmed.slice(2));
|
|
5884
6189
|
if (isAbsolute4(trimmed)) return trimmed;
|
|
5885
|
-
return
|
|
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 :
|
|
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 (
|
|
5966
|
-
const existing =
|
|
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 (!
|
|
5997
|
-
const existing =
|
|
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 === "
|
|
7028
|
-
return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The
|
|
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:
|
|
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
|
|
7146
|
-
import { join as
|
|
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
|
|
7918
|
+
return join14(getLedgerDir(), `${safe}-${safeDaemon}.pending-events.jsonl`);
|
|
7210
7919
|
}
|
|
7211
|
-
return
|
|
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 (!
|
|
7929
|
+
if (!existsSync13(path42)) continue;
|
|
7221
7930
|
try {
|
|
7222
|
-
const raw =
|
|
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 (!
|
|
7298
|
-
if (
|
|
7299
|
-
const lines =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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
|
|
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: "
|
|
7877
|
-
reconciledFromEvent: "monitor:
|
|
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: "
|
|
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
|
|
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
|
|
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) &&
|
|
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
|
|
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:
|
|
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 (!
|
|
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:
|
|
9578
|
-
const reconciledCompletion =
|
|
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 === "
|
|
9586
|
-
LOG.info("MeshEvents", `Reconciled
|
|
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 === "
|
|
9594
|
-
LOG.info("MeshEvents", `Suppressed
|
|
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 === "
|
|
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:
|
|
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:
|
|
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
|
|
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 (
|
|
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) &&
|
|
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
|
|
18421
|
-
import { existsSync as
|
|
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
|
|
19219
|
+
return join11(getDaemonDataDir(), "mesh-coordinators.json");
|
|
18425
19220
|
}
|
|
18426
19221
|
function loadMeshCoordinatorRegistry() {
|
|
18427
19222
|
const path42 = getRegistryPath();
|
|
18428
|
-
if (!
|
|
19223
|
+
if (!existsSync10(path42)) return;
|
|
18429
19224
|
try {
|
|
18430
|
-
const raw = JSON.parse(
|
|
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
|
|
18479
|
-
import { join as
|
|
18480
|
-
import * as
|
|
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
|
|
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 (!
|
|
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 && !
|
|
18636
|
-
const rawBootstrapMode =
|
|
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 =
|
|
18645
|
-
const rawBootstrapCommands =
|
|
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
|
|
19463
|
+
function parseConfigText2(path42, text) {
|
|
18669
19464
|
if (/\.json$/i.test(path42)) return JSON.parse(text);
|
|
18670
|
-
return
|
|
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 =
|
|
18682
|
-
if (!
|
|
19476
|
+
const configPath = join12(workspace, relative5);
|
|
19477
|
+
if (!existsSync11(configPath)) continue;
|
|
18683
19478
|
try {
|
|
18684
|
-
const parsed =
|
|
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(
|
|
18701
|
-
return
|
|
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 (!
|
|
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 (
|
|
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
|
|
18778
|
-
import { join as
|
|
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
|
|
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
|
|
19622
|
+
function parseConfigText3(path42, text) {
|
|
18828
19623
|
if (/\.json$/i.test(path42)) return JSON.parse(text);
|
|
18829
|
-
return
|
|
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 =
|
|
18870
|
-
if (!
|
|
19664
|
+
const configPath = join13(workspace, relative5);
|
|
19665
|
+
if (!existsSync12(configPath)) continue;
|
|
18871
19666
|
try {
|
|
18872
|
-
const parsed =
|
|
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 =
|
|
19680
|
+
const filePath = join13(workspace, relative5);
|
|
18886
19681
|
try {
|
|
18887
|
-
digest[relative5] = createHash3("sha256").update(
|
|
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) => !
|
|
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) =>
|
|
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
|
|
19533
|
-
import { join as
|
|
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
|
|
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 (!
|
|
20053
|
+
if (!existsSync16(statePath)) {
|
|
19583
20054
|
return { ...DEFAULT_STATE };
|
|
19584
20055
|
}
|
|
19585
20056
|
try {
|
|
19586
|
-
const raw =
|
|
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
|
|
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
|
|
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 (
|
|
19696
|
-
const stat2 =
|
|
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 (
|
|
20185
|
+
if (existsSync18(resolved)) return resolved;
|
|
19715
20186
|
} else {
|
|
19716
|
-
if (
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
21289
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
21819
|
+
this.noProgressAlerted.set(agentKey, false);
|
|
21342
21820
|
}
|
|
21343
|
-
if (this.config.
|
|
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.
|
|
21347
|
-
if (elapsedSec > this.config.
|
|
21348
|
-
if (this.shouldAlert(agentKey + ":
|
|
21349
|
-
this.
|
|
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:
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
23213
|
-
|
|
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
|
-
|
|
23305
|
-
|
|
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
|
|
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
|
-
|
|
23848
|
-
|
|
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
|
-
|
|
23976
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
33615
|
-
|
|
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
|
-
|
|
33891
|
-
|
|
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
|
|
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:
|
|
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: "
|
|
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
|
-
|
|
35320
|
-
|
|
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
|
-
|
|
35617
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
42213
|
+
if (!existsSync36(path42)) return null;
|
|
41731
42214
|
try {
|
|
41732
|
-
const parsed = JSON.parse(
|
|
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
|
|
41798
|
-
import { dirname as dirname10, join as
|
|
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 =
|
|
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 =
|
|
41821
|
-
const hasNpmLock =
|
|
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) =>
|
|
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 =
|
|
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
|
|
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:
|
|
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 =
|
|
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(
|
|
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:
|
|
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 || !
|
|
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:
|
|
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
|
|
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
|
|
58484
|
-
import { resolve as resolve23, dirname as dirname16, join as
|
|
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,
|