@adhdev/daemon-core 0.9.82-rc.292 → 0.9.82-rc.293
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +227 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +229 -43
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-missions.d.ts +25 -1
- package/dist/mesh/mesh-runtime-store.d.ts +15 -0
- package/dist/mesh/mesh-unresolved-forward-outbox.d.ts +30 -0
- package/package.json +2 -2
- package/src/commands/router.ts +15 -3
- package/src/git/git-status.ts +46 -11
- package/src/mesh/coordinator-prompt.ts +2 -2
- package/src/mesh/mesh-events-coordinator.ts +51 -6
- package/src/mesh/mesh-missions.ts +41 -3
- package/src/mesh/mesh-reconcile-loop.ts +55 -0
- package/src/mesh/mesh-runtime-store.ts +30 -0
- package/src/mesh/mesh-unresolved-forward-outbox.ts +185 -0
- package/src/providers/cli-provider-instance.ts +21 -16
- package/src/providers/extension-provider-instance.ts +5 -1
- package/src/providers/ide-provider-instance.ts +6 -1
package/dist/index.mjs
CHANGED
|
@@ -270,10 +270,10 @@ function readInjected(value) {
|
|
|
270
270
|
}
|
|
271
271
|
function getDaemonBuildInfo() {
|
|
272
272
|
if (cached) return cached;
|
|
273
|
-
const commit = readInjected(true ? "
|
|
274
|
-
const commitShort = readInjected(true ? "
|
|
275
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
276
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
273
|
+
const commit = readInjected(true ? "88f97abcc85d83fe5d5313b229d32a7d76b1208a" : void 0) ?? "unknown";
|
|
274
|
+
const commitShort = readInjected(true ? "88f97abc" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
275
|
+
const version = readInjected(true ? "0.9.82-rc.293" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
276
|
+
const builtAt = readInjected(true ? "2026-06-16T11:13:51.625Z" : void 0);
|
|
277
277
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
278
278
|
return cached;
|
|
279
279
|
}
|
|
@@ -344,6 +344,15 @@ async function getGitRepoStatus(workspace, options = {}) {
|
|
|
344
344
|
);
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
|
+
function isNonRuntimeRootFile(file) {
|
|
348
|
+
const base = file.slice(file.lastIndexOf("/") + 1);
|
|
349
|
+
if (/^\.(?:verify|marker|converge|ff-verify|patch-equiv|live-verify)\b/i.test(base)) return true;
|
|
350
|
+
if (/(?:^|\/)docs\//i.test(file)) return true;
|
|
351
|
+
if (/^(?:README|CHANGELOG|LICENSE|NOTICE|AUTHORS|CONTRIBUTING|CODEOWNERS)(?:\.[A-Za-z0-9]+)?$/i.test(base)) {
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
347
356
|
async function classifyDaemonBuildChange(repoPath, buildCommit, options) {
|
|
348
357
|
try {
|
|
349
358
|
const diff = await runGit(repoPath, ["diff", "--name-only", `${buildCommit}..HEAD`], options);
|
|
@@ -352,18 +361,18 @@ async function classifyDaemonBuildChange(repoPath, buildCommit, options) {
|
|
|
352
361
|
return { isDaemonAffecting: true, affectedPackages: [] };
|
|
353
362
|
}
|
|
354
363
|
const pkgs = /* @__PURE__ */ new Set();
|
|
355
|
-
let
|
|
364
|
+
let sawRuntimeAmbiguousNonPackage = false;
|
|
356
365
|
for (const file of files) {
|
|
357
366
|
const match = file.match(/(?:^|\/)packages\/([^/]+)\//);
|
|
358
367
|
if (!match) {
|
|
359
|
-
|
|
368
|
+
if (!isNonRuntimeRootFile(file)) sawRuntimeAmbiguousNonPackage = true;
|
|
360
369
|
continue;
|
|
361
370
|
}
|
|
362
371
|
pkgs.add(match[1]);
|
|
363
372
|
}
|
|
364
373
|
const affectedPackages = [...pkgs].sort();
|
|
365
|
-
const
|
|
366
|
-
return { isDaemonAffecting: !
|
|
374
|
+
const allBenign = !sawRuntimeAmbiguousNonPackage && affectedPackages.every((p) => WEB_ONLY_PACKAGES.has(p) && !DAEMON_RUNTIME_PACKAGES.has(p));
|
|
375
|
+
return { isDaemonAffecting: !allBenign, affectedPackages };
|
|
367
376
|
} catch {
|
|
368
377
|
return { isDaemonAffecting: true, affectedPackages: [] };
|
|
369
378
|
}
|
|
@@ -390,7 +399,8 @@ async function detectDaemonBuildBehind(repo, submodules, options) {
|
|
|
390
399
|
options
|
|
391
400
|
);
|
|
392
401
|
const scopeLabel = scope === "root" ? "workspace" : scope;
|
|
393
|
-
const
|
|
402
|
+
const benignDetail = affectedPackages.length > 0 ? `only web packages changed (${affectedPackages.join(", ")})` : "only non-runtime files changed (markers/docs)";
|
|
403
|
+
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.`;
|
|
394
404
|
return {
|
|
395
405
|
buildCommit: build.commit,
|
|
396
406
|
buildCommitShort: build.commitShort,
|
|
@@ -1995,7 +2005,7 @@ function buildRulesSection(coordinatorCliType) {
|
|
|
1995
2005
|
- **Reuse idle sessions.** For follow-up, retry, commit/push, or cleanup on the same issue, send only the delta to the existing idle session. Start fresh only for independent work, provider mismatch, transcript contamination, or required worktree isolation.
|
|
1996
2006
|
- **Respect explicit provider requests.** Map: Hermes \u2192 \`hermes-cli\`, Claude/Claude Code \u2192 \`claude-cli\`, Codex \u2192 \`codex-cli\`, Gemini \u2192 \`gemini-cli\`, Antigravity \u2192 \`antigravity-cli\`. Never substitute the coordinator's own runtime.
|
|
1997
2007
|
- **Verify via git, not source.** Use \`mesh_git_status\` to confirm side effects. Treat agent summaries as self-reports, not verification.
|
|
1998
|
-
- **Limit parallelism.** Start with 1\u20132 tasks; scale only on success. Never duplicate a session because \`mesh_read_chat\` shows no final message while tool/terminal activity is ongoing.
|
|
2008
|
+
- **Limit parallelism.** Start with 1\u20132 tasks; scale only on success. Never duplicate a session because \`mesh_read_chat\` shows no final message while tool/terminal activity is ongoing. This caps *concurrent* load \u2014 it does not mean serialize independent work: when a new, independent request arrives and there is headroom under \`maxParallelTasks\`, dispatch it right away rather than waiting for an in-flight task or a user nudge (read-only diagnosis especially, since it has no merge cost).
|
|
1999
2009
|
- **Check history first.** Call \`mesh_task_history\` at session start to avoid duplicate work and inform recovery. On failure, read task history before retrying.
|
|
2000
2010
|
- **Converge branches.** After worktree tasks: refine/fast-forward, or classify as \`pushed_feature_branch_needs_merge\` / \`blocked_review\` / \`cleanup_candidate\` / \`not_mergeable\`. Clean up with \`mesh_remove_node\`.
|
|
2001
2011
|
- **Refinery is config-driven.** \`mesh_refine_node\` must run validation from \`.adhdev/refine.{json,yaml,yml}\` or \`repo-mesh.refine.*\`. Heuristics are scaffolding only.
|
|
@@ -2046,7 +2056,7 @@ Before doing any coordinator work, confirm that the actual callable tool list in
|
|
|
2046
2056
|
c. **Targeted Tasks**: Use \`mesh_send_task\` only when you need to bypass the queue and force a specific node to execute a task immediately.
|
|
2047
2057
|
d. For the first dispatch of a new task, provide a **complete, self-contained** instruction that includes all context the agent needs (file paths, line numbers, what to change, why). Do not send partial instructions expecting future follow-up.
|
|
2048
2058
|
e. For a continuation of the same issue in an existing session, send a concise **delta instruction**: current verified state, the exact failed/blocked step, the newly approved action, and final reporting requirements. Do not resend the full original task or open a new chat solely to continue the same work; that wastes coordinator and worker context.
|
|
2049
|
-
4. **Monitor** \u2014 Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly. Do **not** repeatedly call \`mesh_status\` or \`mesh_view_queue\` just to wait for assigned/generating work. After dispatching a direct or queued task, send one progress update with the task/session handle, then stop. Wait for \`pendingCoordinatorEvents\` or another completion/approval/status signal, an explicit user status request, or a real timeout/stall signal before reading status/chat/queue again. Use at most one compact \`mesh_read_chat\` check after a terminal signal. Handle approvals via \`mesh_approve\`.
|
|
2059
|
+
4. **Monitor** \u2014 Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly. Do **not** repeatedly call \`mesh_status\` or \`mesh_view_queue\` just to wait for assigned/generating work. After dispatching a direct or queued task, send one progress update with the task/session handle, then stop. Wait for \`pendingCoordinatorEvents\` or another completion/approval/status signal, an explicit user status request, or a real timeout/stall signal before reading status/chat/queue again. Use at most one compact \`mesh_read_chat\` check after a terminal signal. Handle approvals via \`mesh_approve\`. **Proactively parallelize new work.** When the user reports a new bug or asks for new work, start it immediately if it is independent of in-flight tasks and there is headroom under \`maxParallelTasks\` \u2014 do not wait for a current task to finish or for the user to prompt you to parallelize. Read-only diagnosis (\`live_debug_readonly\`) has no isolation or merge cost, so dispatch it in parallel right away. The no-polling / concurrency-limit rules constrain *re-checking or duplicating already-dispatched work*; they are **not** a reason to defer starting a new, independent task.
|
|
2050
2060
|
5. **Verify** \u2014 When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
|
|
2051
2061
|
6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
|
|
2052
2062
|
7. **Converge branches** \u2014 Before marking any task complete, classify every touched node/branch into exactly one final state: \`merged_to_main\`, \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\`. Use \`mesh_status\` branchConvergenceSummary. For obvious clean branch catch-up (ahead 0, behind > 0, upstream fresh, no dirty/stash/submodule issues), use \`mesh_fast_forward_node\` dry-run first and execute only when explicitly safe/approved; this avoids consuming an agent session. Use \`mesh_refine_node\` for clean worktree branches when safe. Before/refine merging root commits that contain submodule gitlink changes, require each submodule commit to be reachable from the configured submodule remote main branch, not merely present on a feature ref or local checkout. If \`mesh_refine_node\` returns \`submodule_reachability_failed\` or publish-required evidence, keep the public convergence bucket as \`blocked_review\`; unless \`allowAutoPublishSubmoduleMainCommits\` is explicitly enabled and Refinery reports successful non-force publish plus post-publish verification, ask the user for explicit approval to push/publish the unreachable submodule commit(s) to submodule main, then rerun \`mesh_refine_node\`. Do not merge the root branch until the submodule commit(s) are reachable from submodule origin/main. A task that remains on a non-main branch is not fully complete unless the final report names the follow-up state and next step.
|
|
@@ -4389,6 +4399,34 @@ var init_mesh_runtime_store = __esm({
|
|
|
4389
4399
|
).get(meshId);
|
|
4390
4400
|
return row?.cnt ?? 0;
|
|
4391
4401
|
}
|
|
4402
|
+
/**
|
|
4403
|
+
* Mark specific pending-event rows drained by id (ack). Used by the
|
|
4404
|
+
* unresolved-delegate durable-forward outbox: an event is peeked (not drained)
|
|
4405
|
+
* while its push to the coordinator is unconfirmed, then marked drained ONLY
|
|
4406
|
+
* after the push is acked. A failed push leaves the row undrained so the next
|
|
4407
|
+
* reconcile tick retries it. Returns the number of rows newly marked drained.
|
|
4408
|
+
*/
|
|
4409
|
+
markPendingEventsDrainedById(ids) {
|
|
4410
|
+
const idList = ids.filter((id) => typeof id === "string" && id.length > 0);
|
|
4411
|
+
if (idList.length === 0) return 0;
|
|
4412
|
+
const now = Date.now();
|
|
4413
|
+
return this.db.prepare(
|
|
4414
|
+
`UPDATE mesh_pending_events SET drained = 1, drained_at = ? WHERE drained = 0 AND id IN (${idList.map(() => "?").join(",")})`
|
|
4415
|
+
).run(now, ...idList).changes;
|
|
4416
|
+
}
|
|
4417
|
+
/**
|
|
4418
|
+
* Hard-delete pending-event rows by id (including the dedup fingerprint history).
|
|
4419
|
+
* Used to expire an unresolved-delegate outbox entry that has exhausted its retry
|
|
4420
|
+
* budget — fully removing it frees the fingerprint so a genuinely new completion
|
|
4421
|
+
* for the same task could be re-queued later. Returns the number of rows deleted.
|
|
4422
|
+
*/
|
|
4423
|
+
deletePendingEventsById(ids) {
|
|
4424
|
+
const idList = ids.filter((id) => typeof id === "string" && id.length > 0);
|
|
4425
|
+
if (idList.length === 0) return 0;
|
|
4426
|
+
return this.db.prepare(
|
|
4427
|
+
`DELETE FROM mesh_pending_events WHERE id IN (${idList.map(() => "?").join(",")})`
|
|
4428
|
+
).run(...idList).changes;
|
|
4429
|
+
}
|
|
4392
4430
|
};
|
|
4393
4431
|
}
|
|
4394
4432
|
});
|
|
@@ -4396,6 +4434,7 @@ var init_mesh_runtime_store = __esm({
|
|
|
4396
4434
|
// src/mesh/mesh-missions.ts
|
|
4397
4435
|
var mesh_missions_exports = {};
|
|
4398
4436
|
__export(mesh_missions_exports, {
|
|
4437
|
+
GOAL_PREVIEW_MAX: () => GOAL_PREVIEW_MAX,
|
|
4399
4438
|
MESH_MISSION_STATUSES: () => MESH_MISSION_STATUSES,
|
|
4400
4439
|
buildMissionPromptSection: () => buildMissionPromptSection,
|
|
4401
4440
|
getActiveMeshMissionSummaries: () => getActiveMeshMissionSummaries,
|
|
@@ -4468,12 +4507,23 @@ function summarizeMeshMission(meshId, mission) {
|
|
|
4468
4507
|
function getActiveMeshMissionSummaries(meshId) {
|
|
4469
4508
|
return getMeshMissions(meshId, ["active"]).map((mission) => summarizeMeshMission(meshId, mission));
|
|
4470
4509
|
}
|
|
4510
|
+
function slimMissionSummary(summary) {
|
|
4511
|
+
const goal = typeof summary.goal === "string" ? summary.goal : "";
|
|
4512
|
+
const goalTruncated = goal.length > GOAL_PREVIEW_MAX;
|
|
4513
|
+
const { goal: _omitGoal, ...rest } = summary;
|
|
4514
|
+
return {
|
|
4515
|
+
...rest,
|
|
4516
|
+
goalPreview: goalTruncated ? goal.slice(0, GOAL_PREVIEW_MAX) : goal,
|
|
4517
|
+
goalTruncated
|
|
4518
|
+
};
|
|
4519
|
+
}
|
|
4471
4520
|
function getMeshStatusMissionSummaries(meshId, options) {
|
|
4472
4521
|
const historyLimit = Math.max(0, options?.historyLimit ?? 10);
|
|
4473
4522
|
const all = getMeshMissions(meshId);
|
|
4474
4523
|
const live = all.filter((m) => m.status === "active" || m.status === "paused");
|
|
4475
4524
|
const history = all.filter((m) => m.status === "completed" || m.status === "abandoned").sort((a, b) => (b.updatedAt || "").localeCompare(a.updatedAt || "")).slice(0, historyLimit);
|
|
4476
|
-
|
|
4525
|
+
const full = [...live, ...history].map((mission) => summarizeMeshMission(meshId, mission));
|
|
4526
|
+
return options?.verbose ? full : full.map(slimMissionSummary);
|
|
4477
4527
|
}
|
|
4478
4528
|
function buildMissionPromptSection(meshId) {
|
|
4479
4529
|
const summaries = getActiveMeshMissionSummaries(meshId);
|
|
@@ -4493,13 +4543,14 @@ function buildMissionPromptSection(meshId) {
|
|
|
4493
4543
|
);
|
|
4494
4544
|
return lines.join("\n");
|
|
4495
4545
|
}
|
|
4496
|
-
var MESH_MISSION_STATUSES;
|
|
4546
|
+
var MESH_MISSION_STATUSES, GOAL_PREVIEW_MAX;
|
|
4497
4547
|
var init_mesh_missions = __esm({
|
|
4498
4548
|
"src/mesh/mesh-missions.ts"() {
|
|
4499
4549
|
"use strict";
|
|
4500
4550
|
init_mesh_runtime_store();
|
|
4501
4551
|
init_mesh_work_queue();
|
|
4502
4552
|
MESH_MISSION_STATUSES = ["active", "paused", "completed", "abandoned"];
|
|
4553
|
+
GOAL_PREVIEW_MAX = 120;
|
|
4503
4554
|
}
|
|
4504
4555
|
});
|
|
4505
4556
|
|
|
@@ -7597,6 +7648,111 @@ var init_mesh_routing = __esm({
|
|
|
7597
7648
|
}
|
|
7598
7649
|
});
|
|
7599
7650
|
|
|
7651
|
+
// src/mesh/mesh-unresolved-forward-outbox.ts
|
|
7652
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
7653
|
+
function getStore() {
|
|
7654
|
+
try {
|
|
7655
|
+
return MeshRuntimeStore.getInstance();
|
|
7656
|
+
} catch {
|
|
7657
|
+
return void 0;
|
|
7658
|
+
}
|
|
7659
|
+
}
|
|
7660
|
+
function enqueueUnresolvedDelegateForward(coordinatorDaemonId, eventName, forwardPayload) {
|
|
7661
|
+
const target = readNonEmptyString2(coordinatorDaemonId);
|
|
7662
|
+
const event = readNonEmptyString2(eventName);
|
|
7663
|
+
if (!target || !event) return false;
|
|
7664
|
+
const store = getStore();
|
|
7665
|
+
if (!store) return false;
|
|
7666
|
+
const queuedAt = Date.now();
|
|
7667
|
+
const fingerprintSource = {
|
|
7668
|
+
event,
|
|
7669
|
+
meshId: UNRESOLVED_FORWARD_OUTBOX_MESH_ID,
|
|
7670
|
+
nodeLabel: readNonEmptyString2(forwardPayload.nodeId) || readNonEmptyString2(forwardPayload.workspace) || "unresolved-delegate",
|
|
7671
|
+
nodeId: readNonEmptyString2(forwardPayload.nodeId) || void 0,
|
|
7672
|
+
workspace: readNonEmptyString2(forwardPayload.workspace) || void 0,
|
|
7673
|
+
metadataEvent: forwardPayload,
|
|
7674
|
+
queuedAt,
|
|
7675
|
+
targetCoordinatorDaemonId: target
|
|
7676
|
+
};
|
|
7677
|
+
const fingerprint = `${target}::${buildPendingEventFingerprint(fingerprintSource)}`;
|
|
7678
|
+
try {
|
|
7679
|
+
const inserted = store.insertPendingEvent({
|
|
7680
|
+
id: randomUUID9(),
|
|
7681
|
+
meshId: UNRESOLVED_FORWARD_OUTBOX_MESH_ID,
|
|
7682
|
+
coordinatorDaemonId: target,
|
|
7683
|
+
event,
|
|
7684
|
+
// Store the flat forward payload + the queue timestamp so the retry tick can
|
|
7685
|
+
// rebuild the push args and apply age-based expiry without a schema change.
|
|
7686
|
+
payload: { forwardPayload, coordinatorDaemonId: target, queuedAt },
|
|
7687
|
+
fingerprint,
|
|
7688
|
+
queuedAt
|
|
7689
|
+
});
|
|
7690
|
+
if (inserted) {
|
|
7691
|
+
LOG.info("MeshEvents", `Durably queued unresolved-delegate ${event} for coordinator ${target} (outbox)`);
|
|
7692
|
+
}
|
|
7693
|
+
return true;
|
|
7694
|
+
} catch (e) {
|
|
7695
|
+
LOG.warn("MeshEvents", `Failed to persist unresolved-delegate forward to outbox: ${e?.message || e}`);
|
|
7696
|
+
return false;
|
|
7697
|
+
}
|
|
7698
|
+
}
|
|
7699
|
+
function peekUnresolvedDelegateForwards() {
|
|
7700
|
+
const store = getStore();
|
|
7701
|
+
if (!store) return [];
|
|
7702
|
+
let rows;
|
|
7703
|
+
try {
|
|
7704
|
+
rows = store.peekPendingEvents(UNRESOLVED_FORWARD_OUTBOX_MESH_ID);
|
|
7705
|
+
} catch {
|
|
7706
|
+
return [];
|
|
7707
|
+
}
|
|
7708
|
+
const out = [];
|
|
7709
|
+
for (const row of rows) {
|
|
7710
|
+
const stored = row.payload && typeof row.payload === "object" ? row.payload : {};
|
|
7711
|
+
const coordinatorDaemonId = readNonEmptyString2(stored.coordinatorDaemonId);
|
|
7712
|
+
const forwardPayload = stored.forwardPayload && typeof stored.forwardPayload === "object" ? stored.forwardPayload : void 0;
|
|
7713
|
+
if (!coordinatorDaemonId || !forwardPayload) continue;
|
|
7714
|
+
const queuedAt = typeof stored.queuedAt === "number" ? stored.queuedAt : 0;
|
|
7715
|
+
out.push({ id: row.id, coordinatorDaemonId, payload: forwardPayload, queuedAt });
|
|
7716
|
+
}
|
|
7717
|
+
return out;
|
|
7718
|
+
}
|
|
7719
|
+
function ackUnresolvedDelegateForward(id) {
|
|
7720
|
+
const store = getStore();
|
|
7721
|
+
if (!store) return;
|
|
7722
|
+
try {
|
|
7723
|
+
store.markPendingEventsDrainedById([id]);
|
|
7724
|
+
} catch {
|
|
7725
|
+
}
|
|
7726
|
+
}
|
|
7727
|
+
function expireStaleUnresolvedDelegateForwards(nowMs = Date.now()) {
|
|
7728
|
+
const entries = peekUnresolvedDelegateForwards();
|
|
7729
|
+
const staleIds = entries.filter((e) => e.queuedAt > 0 && nowMs - e.queuedAt >= UNRESOLVED_FORWARD_MAX_AGE_MS).map((e) => e.id);
|
|
7730
|
+
if (staleIds.length === 0) return 0;
|
|
7731
|
+
const store = getStore();
|
|
7732
|
+
if (!store) return 0;
|
|
7733
|
+
try {
|
|
7734
|
+
const removed = store.deletePendingEventsById(staleIds);
|
|
7735
|
+
if (removed > 0) {
|
|
7736
|
+
LOG.warn("MeshEvents", `Expired ${removed} unresolved-delegate forward(s) after ${Math.round(UNRESOLVED_FORWARD_MAX_AGE_MS / 6e4)}m of failed retries \u2014 coordinator unreachable, completion dropped`);
|
|
7737
|
+
}
|
|
7738
|
+
return removed;
|
|
7739
|
+
} catch {
|
|
7740
|
+
return 0;
|
|
7741
|
+
}
|
|
7742
|
+
}
|
|
7743
|
+
var UNRESOLVED_FORWARD_OUTBOX_MESH_ID, UNRESOLVED_FORWARD_MAX_AGE_MS;
|
|
7744
|
+
var init_mesh_unresolved_forward_outbox = __esm({
|
|
7745
|
+
"src/mesh/mesh-unresolved-forward-outbox.ts"() {
|
|
7746
|
+
"use strict";
|
|
7747
|
+
init_logger();
|
|
7748
|
+
init_mesh_runtime_store();
|
|
7749
|
+
init_mesh_events_pending();
|
|
7750
|
+
init_mesh_events_utils();
|
|
7751
|
+
UNRESOLVED_FORWARD_OUTBOX_MESH_ID = "__unresolved_forward_outbox__";
|
|
7752
|
+
UNRESOLVED_FORWARD_MAX_AGE_MS = 30 * 60 * 1e3;
|
|
7753
|
+
}
|
|
7754
|
+
});
|
|
7755
|
+
|
|
7600
7756
|
// src/mesh/mesh-events-coordinator.ts
|
|
7601
7757
|
import { existsSync as existsSync14 } from "fs";
|
|
7602
7758
|
function resolveCoordinatorDrainDaemonIds(components) {
|
|
@@ -8732,12 +8888,25 @@ function forwardUnresolvedDelegateEvent(components, routing, event) {
|
|
|
8732
8888
|
nodeId: readNonEmptyString2(routing.nodeId) || readNonEmptyString2(event.meshNodeId) || void 0,
|
|
8733
8889
|
workspace: readNonEmptyString2(routing.workspace) || readNonEmptyString2(event.workspace) || void 0
|
|
8734
8890
|
};
|
|
8735
|
-
|
|
8736
|
-
|
|
8891
|
+
const persisted = enqueueUnresolvedDelegateForward(coordinatorDaemonId, eventName, payload);
|
|
8892
|
+
Promise.resolve(components.dispatchMeshCommand(coordinatorDaemonId, "mesh_forward_event", payload)).then((result) => {
|
|
8893
|
+
if (result && result.success === false) {
|
|
8894
|
+
LOG.warn("MeshEvents", `Immediate forward of ${eventName} to coordinator ${coordinatorDaemonId} rejected (${readNonEmptyString2(result.error) || "no reason"}) \u2014 left queued for retry`);
|
|
8895
|
+
return;
|
|
8896
|
+
}
|
|
8897
|
+
if (persisted) ackUnresolvedDelegateForwardByFingerprint(coordinatorDaemonId, eventName, payload);
|
|
8898
|
+
}).catch((e) => {
|
|
8899
|
+
LOG.warn("MeshEvents", `Immediate forward of ${eventName} to coordinator ${coordinatorDaemonId} failed: ${e?.message || e} \u2014 left queued for retry`);
|
|
8737
8900
|
});
|
|
8738
|
-
LOG.info("MeshEvents", `
|
|
8901
|
+
LOG.info("MeshEvents", `Durably forwarded ${eventName} for unresolved-mesh worker at ${routing.workspace || "(no workspace)"} to coordinator daemon ${coordinatorDaemonId}`);
|
|
8739
8902
|
return true;
|
|
8740
8903
|
}
|
|
8904
|
+
function ackUnresolvedDelegateForwardByFingerprint(coordinatorDaemonId, eventName, payload) {
|
|
8905
|
+
const match = peekUnresolvedDelegateForwards().find(
|
|
8906
|
+
(entry) => entry.coordinatorDaemonId === coordinatorDaemonId && readNonEmptyString2(entry.payload.event) === eventName && readNonEmptyString2(entry.payload.targetSessionId || entry.payload.sessionId || entry.payload.instanceId) === readNonEmptyString2(payload.targetSessionId || payload.sessionId || payload.instanceId) && readNonEmptyString2(entry.payload.workspace) === readNonEmptyString2(payload.workspace)
|
|
8907
|
+
);
|
|
8908
|
+
if (match) ackUnresolvedDelegateForward(match.id);
|
|
8909
|
+
}
|
|
8741
8910
|
function setupMeshEventForwarding(components) {
|
|
8742
8911
|
components.instanceManager.onEvent((event) => {
|
|
8743
8912
|
if (event.event === "agent:ready" || event.event === "agent:generating_completed") {
|
|
@@ -8818,6 +8987,7 @@ var init_mesh_events_coordinator = __esm({
|
|
|
8818
8987
|
init_mesh_runtime_store();
|
|
8819
8988
|
init_mesh_events_pending();
|
|
8820
8989
|
init_mesh_routing();
|
|
8990
|
+
init_mesh_unresolved_forward_outbox();
|
|
8821
8991
|
init_repo_mesh_types();
|
|
8822
8992
|
init_dist();
|
|
8823
8993
|
init_mesh_events_stale();
|
|
@@ -8933,6 +9103,13 @@ async function runMeshReconcileTick(components) {
|
|
|
8933
9103
|
return void 0;
|
|
8934
9104
|
}
|
|
8935
9105
|
})();
|
|
9106
|
+
if (dispatchMeshCommand) {
|
|
9107
|
+
try {
|
|
9108
|
+
await retryUnresolvedDelegateForwards(components);
|
|
9109
|
+
} catch (e) {
|
|
9110
|
+
LOG.warn("MeshReconcile", `Unresolved-delegate forward retry failed: ${e?.message || e}`);
|
|
9111
|
+
}
|
|
9112
|
+
}
|
|
8936
9113
|
if (dispatchMeshCommand) {
|
|
8937
9114
|
for (const mesh of listMeshes()) {
|
|
8938
9115
|
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
@@ -8986,6 +9163,28 @@ async function runMeshReconcileTick(components) {
|
|
|
8986
9163
|
}
|
|
8987
9164
|
}
|
|
8988
9165
|
}
|
|
9166
|
+
async function retryUnresolvedDelegateForwards(components) {
|
|
9167
|
+
const dispatchMeshCommand = components.dispatchMeshCommand;
|
|
9168
|
+
if (!dispatchMeshCommand) return;
|
|
9169
|
+
expireStaleUnresolvedDelegateForwards();
|
|
9170
|
+
const entries = peekUnresolvedDelegateForwards();
|
|
9171
|
+
if (entries.length === 0) return;
|
|
9172
|
+
for (const entry of entries) {
|
|
9173
|
+
let result;
|
|
9174
|
+
try {
|
|
9175
|
+
result = await dispatchMeshCommand(entry.coordinatorDaemonId, "mesh_forward_event", entry.payload);
|
|
9176
|
+
} catch (e) {
|
|
9177
|
+
LOG.warn("MeshReconcile", `Retry forward to coordinator ${entry.coordinatorDaemonId} failed: ${e?.message || e} \u2014 left queued`);
|
|
9178
|
+
continue;
|
|
9179
|
+
}
|
|
9180
|
+
if (result && result.success === false) {
|
|
9181
|
+
LOG.warn("MeshReconcile", `Retry forward to coordinator ${entry.coordinatorDaemonId} rejected (${readNonEmptyString2(result.error) || "no reason"}) \u2014 left queued`);
|
|
9182
|
+
continue;
|
|
9183
|
+
}
|
|
9184
|
+
ackUnresolvedDelegateForward(entry.id);
|
|
9185
|
+
LOG.info("MeshReconcile", `Retried+delivered unresolved-delegate ${readNonEmptyString2(entry.payload.event)} to coordinator ${entry.coordinatorDaemonId}`);
|
|
9186
|
+
}
|
|
9187
|
+
}
|
|
8989
9188
|
async function pullRemoteNodeQueues(components, mesh, localDaemonId, candidateDaemonIds) {
|
|
8990
9189
|
const dispatchMeshCommand = components.dispatchMeshCommand;
|
|
8991
9190
|
if (!dispatchMeshCommand) return;
|
|
@@ -9062,6 +9261,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
9062
9261
|
init_mesh_events_pending();
|
|
9063
9262
|
init_mesh_runtime_store();
|
|
9064
9263
|
init_mesh_events_coordinator();
|
|
9264
|
+
init_mesh_unresolved_forward_outbox();
|
|
9065
9265
|
init_mesh_events_utils();
|
|
9066
9266
|
DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
|
|
9067
9267
|
}
|
|
@@ -21999,7 +22199,7 @@ var ExtensionProviderInstance = class {
|
|
|
21999
22199
|
this.runtimeMessages = [];
|
|
22000
22200
|
}
|
|
22001
22201
|
updateSettings(newSettings) {
|
|
22002
|
-
this.settings = { ...newSettings };
|
|
22202
|
+
this.settings = { ...this.settings, ...newSettings };
|
|
22003
22203
|
this.monitor.updateConfig({
|
|
22004
22204
|
approvalAlert: this.settings.approvalAlert !== false,
|
|
22005
22205
|
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
@@ -22666,7 +22866,7 @@ var IdeProviderInstance = class {
|
|
|
22666
22866
|
this.extensions.clear();
|
|
22667
22867
|
}
|
|
22668
22868
|
updateSettings(newSettings) {
|
|
22669
|
-
this.settings = { ...newSettings };
|
|
22869
|
+
this.settings = { ...this.settings, ...newSettings };
|
|
22670
22870
|
this.monitor.updateConfig({
|
|
22671
22871
|
approvalAlert: this.settings.approvalAlert !== false,
|
|
22672
22872
|
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
@@ -24145,7 +24345,7 @@ function resolveLegacyProviderScript(fn, scriptName, params) {
|
|
|
24145
24345
|
import * as fs6 from "fs";
|
|
24146
24346
|
import * as os8 from "os";
|
|
24147
24347
|
import * as path13 from "path";
|
|
24148
|
-
import { randomUUID as
|
|
24348
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
24149
24349
|
init_logger();
|
|
24150
24350
|
|
|
24151
24351
|
// src/logging/debug-trace.ts
|
|
@@ -25686,7 +25886,7 @@ function safeBundleIdSegment(value, fallback) {
|
|
|
25686
25886
|
function createChatDebugBundleId(targetSessionId) {
|
|
25687
25887
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.]/g, "").replace("T", "T").replace("Z", "Z");
|
|
25688
25888
|
const sessionSegment = safeBundleIdSegment(targetSessionId, "unknown-session");
|
|
25689
|
-
return `chat-debug-${timestamp}-${sessionSegment}-${
|
|
25889
|
+
return `chat-debug-${timestamp}-${sessionSegment}-${randomUUID11().slice(0, 8)}`;
|
|
25690
25890
|
}
|
|
25691
25891
|
function buildChatDebugBundleSummary(bundle) {
|
|
25692
25892
|
const target = bundle.target && typeof bundle.target === "object" ? bundle.target : {};
|
|
@@ -32214,22 +32414,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
32214
32414
|
};
|
|
32215
32415
|
}
|
|
32216
32416
|
updateSettings(newSettings) {
|
|
32217
|
-
|
|
32218
|
-
for (const key of [
|
|
32219
|
-
"meshNodeFor",
|
|
32220
|
-
"meshNodeId",
|
|
32221
|
-
"meshActiveTaskId",
|
|
32222
|
-
"meshCoordinatorFor",
|
|
32223
|
-
"meshCoordinatorDaemonId",
|
|
32224
|
-
"meshCoordinatorNodeId",
|
|
32225
|
-
"spawnedSessionVisibility",
|
|
32226
|
-
"launchedByCoordinator"
|
|
32227
|
-
]) {
|
|
32228
|
-
if (this.settings[key] !== void 0 && newSettings[key] === void 0) {
|
|
32229
|
-
runtimeMeshSettings[key] = this.settings[key];
|
|
32230
|
-
}
|
|
32231
|
-
}
|
|
32232
|
-
this.settings = { ...newSettings, ...runtimeMeshSettings };
|
|
32417
|
+
this.settings = { ...this.settings, ...newSettings };
|
|
32233
32418
|
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
32234
32419
|
this.monitor.updateConfig({
|
|
32235
32420
|
approvalAlert: this.settings.approvalAlert !== false,
|
|
@@ -46292,9 +46477,9 @@ ${hintLines.join("\n")}` : "",
|
|
|
46292
46477
|
});
|
|
46293
46478
|
let node;
|
|
46294
46479
|
if (meshRecord.inline) {
|
|
46295
|
-
const { randomUUID:
|
|
46480
|
+
const { randomUUID: randomUUID15 } = await import("crypto");
|
|
46296
46481
|
node = {
|
|
46297
|
-
id: `node_${
|
|
46482
|
+
id: `node_${randomUUID15().replace(/-/g, "")}`,
|
|
46298
46483
|
workspace: result.worktreePath,
|
|
46299
46484
|
repoRoot: result.worktreePath,
|
|
46300
46485
|
daemonId: sourceNode.daemonId,
|
|
@@ -47068,10 +47253,11 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
47068
47253
|
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
47069
47254
|
const meshHost = resolveMeshHostStatus(mesh);
|
|
47070
47255
|
const refreshRequested = args?.refresh === true || args?.forceRefresh === true;
|
|
47256
|
+
const verboseMissions = args?.verbose === true || args?.compact === false;
|
|
47071
47257
|
const peekScope = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : this.deps.statusInstanceId || void 0;
|
|
47072
47258
|
const pendingCoordinatorEventCount = getPendingMeshCoordinatorEvents(meshId, peekScope).length;
|
|
47073
47259
|
const hadAggregateCache = this.aggregateMeshStatusCache.has(meshId);
|
|
47074
|
-
if (!refreshRequested && pendingCoordinatorEventCount === 0) {
|
|
47260
|
+
if (!refreshRequested && !verboseMissions && pendingCoordinatorEventCount === 0) {
|
|
47075
47261
|
const cachedStatus = this.getCachedAggregateMeshStatus(meshId, mesh, { requireDirectPeerTruth: args?.requireDirectPeerTruth === true });
|
|
47076
47262
|
if (cachedStatus) {
|
|
47077
47263
|
logRepoMeshStatusDebug("return_cached", {
|
|
@@ -47374,7 +47560,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
47374
47560
|
liveSessionRecords: liveMeshSessions
|
|
47375
47561
|
});
|
|
47376
47562
|
const { getMeshStatusMissionSummaries: getMeshStatusMissionSummaries2 } = await Promise.resolve().then(() => (init_mesh_missions(), mesh_missions_exports));
|
|
47377
|
-
const missions = getMeshStatusMissionSummaries2(meshId);
|
|
47563
|
+
const missions = getMeshStatusMissionSummaries2(meshId, { verbose: verboseMissions });
|
|
47378
47564
|
const statusResult = {
|
|
47379
47565
|
success: true,
|
|
47380
47566
|
meshId: mesh.id,
|
|
@@ -47428,7 +47614,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
47428
47614
|
}))
|
|
47429
47615
|
};
|
|
47430
47616
|
const { pendingCoordinatorEvents: _pendingCoordinatorEvents, unroutableDeliveries: _unroutableDeliveries, ...cacheableStatusResult } = statusResult;
|
|
47431
|
-
const rememberedStatus = this.rememberAggregateMeshStatus(meshId, cacheableStatusResult, refreshReason);
|
|
47617
|
+
const rememberedStatus = verboseMissions ? cacheableStatusResult : this.rememberAggregateMeshStatus(meshId, cacheableStatusResult, refreshReason);
|
|
47432
47618
|
const returnedStatus = {
|
|
47433
47619
|
...rememberedStatus,
|
|
47434
47620
|
...pendingCoordinatorEvents.length > 0 ? { pendingCoordinatorEvents } : {},
|
|
@@ -54984,7 +55170,7 @@ var SessionHostPtyTransportFactory = class {
|
|
|
54984
55170
|
};
|
|
54985
55171
|
|
|
54986
55172
|
// src/cli-adapters/raw-terminal-io.ts
|
|
54987
|
-
import { randomUUID as
|
|
55173
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
54988
55174
|
import {
|
|
54989
55175
|
SessionHostClient as SessionHostClient2
|
|
54990
55176
|
} from "@adhdev/session-host-core";
|
|
@@ -55084,7 +55270,7 @@ var RawTerminalAttachment = class _RawTerminalAttachment {
|
|
|
55084
55270
|
const sessionId = String(options.sessionId || "").trim();
|
|
55085
55271
|
if (!sessionId) throw new Error("sessionId is required");
|
|
55086
55272
|
const mode = options.mode || "read";
|
|
55087
|
-
const clientId = options.clientId || `raw-terminal-${process.pid}-${
|
|
55273
|
+
const clientId = options.clientId || `raw-terminal-${process.pid}-${randomUUID14().slice(0, 8)}`;
|
|
55088
55274
|
const client = options.client || new SessionHostClient2({ endpoint: options.endpoint });
|
|
55089
55275
|
await client.connect();
|
|
55090
55276
|
const attachResponse = await client.request({
|