@adhdev/daemon-core 0.9.82-rc.400 → 0.9.82-rc.401
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-adapter-types.d.ts +2 -1
- package/dist/cli-adapters/cli-state-engine.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +3 -1
- package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/dist/git/git-worktree.d.ts +49 -1
- package/dist/git/index.d.ts +1 -1
- package/dist/index.js +190 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +190 -28
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +10 -0
- package/package.json +2 -2
- package/src/cli-adapter-types.d.ts +2 -1
- package/src/cli-adapter-types.ts +2 -2
- package/src/cli-adapters/cli-state-engine.ts +15 -0
- package/src/cli-adapters/provider-cli-adapter.ts +32 -11
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +6 -0
- package/src/commands/cli-manager.ts +16 -2
- package/src/commands/med-family/mesh-crud.ts +9 -0
- package/src/git/git-worktree.ts +185 -3
- package/src/git/index.ts +1 -0
- package/src/mesh/mesh-event-forwarding.ts +13 -2
- package/src/mesh/mesh-queue-assignment.ts +12 -0
- package/src/mesh/mesh-reconcile-loop.ts +4 -0
- package/src/mesh/mesh-runtime-store.ts +20 -2
- package/src/providers/cli-provider-instance.ts +52 -8
|
@@ -85,8 +85,9 @@ export interface CliAdapter {
|
|
|
85
85
|
spawn(): Promise<void>;
|
|
86
86
|
sendMessage(text: string, options?: {
|
|
87
87
|
force?: boolean;
|
|
88
|
+
meshTaskId?: string;
|
|
88
89
|
}): Promise<void>;
|
|
89
|
-
forceSendMessage?(text: string): Promise<void>;
|
|
90
|
+
forceSendMessage?(text: string, meshTaskId?: string): Promise<void>;
|
|
90
91
|
getStatus(options?: {
|
|
91
92
|
allowParse?: boolean;
|
|
92
93
|
}): CliAdapterStatus;
|
|
@@ -187,8 +187,9 @@ export declare class ProviderCliAdapter implements CliAdapter {
|
|
|
187
187
|
private waitForEchoAndSubmit;
|
|
188
188
|
sendMessage(text: string, options?: {
|
|
189
189
|
force?: boolean;
|
|
190
|
+
meshTaskId?: string;
|
|
190
191
|
}): Promise<void>;
|
|
191
|
-
forceSendMessage(text: string): Promise<void>;
|
|
192
|
+
forceSendMessage(text: string, meshTaskId?: string): Promise<void>;
|
|
192
193
|
private waitForForceSubmitSettle;
|
|
193
194
|
private enqueuePendingOutboundMessage;
|
|
194
195
|
private shouldQueuePendingOutboundMessage;
|
|
@@ -229,6 +230,7 @@ export declare class ProviderCliAdapter implements CliAdapter {
|
|
|
229
230
|
} | null);
|
|
230
231
|
get currentTurnScope(): TurnParseScope | null;
|
|
231
232
|
set currentTurnScope(v: TurnParseScope | null);
|
|
233
|
+
get currentTurnTaskId(): string | null;
|
|
232
234
|
get responseEpoch(): number;
|
|
233
235
|
set responseEpoch(v: number);
|
|
234
236
|
get submitRetryUsed(): boolean;
|
|
@@ -19,11 +19,54 @@ export interface WorktreeCreateOptions {
|
|
|
19
19
|
meshName: string;
|
|
20
20
|
/** Override the auto-resolved target directory */
|
|
21
21
|
targetDir?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Remote to fetch+compare the base branch against before branching.
|
|
24
|
+
* Default: 'origin'.
|
|
25
|
+
*/
|
|
26
|
+
remote?: string;
|
|
27
|
+
/**
|
|
28
|
+
* When true (default) and `baseBranch` is given, fetch the base branch from
|
|
29
|
+
* `remote` and, if the local base branch is strictly behind the remote
|
|
30
|
+
* (no divergence), branch the worktree from the remote-tracking ref instead
|
|
31
|
+
* of the stale local ref. The decision is always surfaced via `baseSync`.
|
|
32
|
+
* Set false to preserve the legacy "branch from local ref, never fetch"
|
|
33
|
+
* behavior.
|
|
34
|
+
*/
|
|
35
|
+
syncBaseFromRemote?: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* How the worktree's start point was resolved relative to the remote base.
|
|
39
|
+
* Surfaced so the coordinator can detect a stale base node before dispatching
|
|
40
|
+
* work onto a worktree built on a behind/diverged base.
|
|
41
|
+
*/
|
|
42
|
+
export interface WorktreeBaseSync {
|
|
43
|
+
/** The base branch name requested (e.g. 'main'). */
|
|
44
|
+
branch: string;
|
|
45
|
+
/** The remote compared against (e.g. 'origin'). */
|
|
46
|
+
remote: string;
|
|
47
|
+
/** The actual ref/commit-ish used as the worktree branch start point. */
|
|
48
|
+
startRef: string;
|
|
49
|
+
/** Whether `git fetch <remote> <branch>` succeeded. */
|
|
50
|
+
fetched: boolean;
|
|
51
|
+
/** Local base-branch SHA before clone, if the local ref exists. */
|
|
52
|
+
localSha?: string;
|
|
53
|
+
/** Remote-tracking base-branch SHA after fetch, if it exists. */
|
|
54
|
+
remoteSha?: string;
|
|
55
|
+
/** Commits the local base ref is behind the remote (0 when up-to-date/ahead). */
|
|
56
|
+
behindBy: number;
|
|
57
|
+
/** Commits the local base ref is ahead of the remote. */
|
|
58
|
+
aheadBy: number;
|
|
59
|
+
/** What was done with the base ref. */
|
|
60
|
+
action: 'up_to_date' | 'local_behind_used_remote' | 'local_ahead_used_local' | 'diverged_used_local' | 'no_remote_ref_used_local' | 'no_local_ref_used_remote';
|
|
61
|
+
/** Human-readable warning when the base was stale/diverged. */
|
|
62
|
+
warning?: string;
|
|
22
63
|
}
|
|
23
64
|
export interface WorktreeCreateResult {
|
|
24
65
|
success: true;
|
|
25
66
|
worktreePath: string;
|
|
26
67
|
branch: string;
|
|
68
|
+
/** Present when `baseBranch` was given and base sync resolution ran. */
|
|
69
|
+
baseSync?: WorktreeBaseSync;
|
|
27
70
|
}
|
|
28
71
|
export interface WorktreeEntry {
|
|
29
72
|
path: string;
|
|
@@ -56,7 +99,12 @@ export declare function resolveWorktreePath(repoRoot: string, meshName: string,
|
|
|
56
99
|
/**
|
|
57
100
|
* Create a new git worktree with a fresh branch.
|
|
58
101
|
*
|
|
59
|
-
* Runs: git worktree add <targetDir> -b <branch> [
|
|
102
|
+
* Runs: git worktree add <targetDir> -b <branch> [startRef]
|
|
103
|
+
*
|
|
104
|
+
* When `baseBranch` is given and `syncBaseFromRemote` is not disabled, the
|
|
105
|
+
* start point is resolved against the remote first (see
|
|
106
|
+
* resolveWorktreeBaseStartPoint) so a stale local base does not produce a stale
|
|
107
|
+
* worktree. The resolution is returned as `baseSync`.
|
|
60
108
|
*/
|
|
61
109
|
export declare function createWorktree(opts: WorktreeCreateOptions): Promise<WorktreeCreateResult>;
|
|
62
110
|
/**
|
package/dist/git/index.d.ts
CHANGED
|
@@ -17,4 +17,4 @@ export type { GitCommandResult, GitCommandServices, GitFileDiff, GitLogEntry, Gi
|
|
|
17
17
|
export { TurnSnapshotTracker } from './turn-snapshot-tracker.js';
|
|
18
18
|
export type { TurnCompletedCallback } from './turn-snapshot-tracker.js';
|
|
19
19
|
export { createWorktree, listWorktrees, parseWorktreeListOutput, removeWorktree, resolveWorktreePath, } from './git-worktree.js';
|
|
20
|
-
export type { WorktreeCreateOptions, WorktreeCreateResult, WorktreeEntry, WorktreeRemoveResult, } from './git-worktree.js';
|
|
20
|
+
export type { WorktreeBaseSync, WorktreeCreateOptions, WorktreeCreateResult, WorktreeEntry, WorktreeRemoveResult, } from './git-worktree.js';
|
package/dist/index.js
CHANGED
|
@@ -399,10 +399,10 @@ function readInjected(value) {
|
|
|
399
399
|
}
|
|
400
400
|
function getDaemonBuildInfo() {
|
|
401
401
|
if (cached) return cached;
|
|
402
|
-
const commit = readInjected(true ? "
|
|
403
|
-
const commitShort = readInjected(true ? "
|
|
404
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
405
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
402
|
+
const commit = readInjected(true ? "c31eb449e8febc28e378bd25394830a96a67594e" : void 0) ?? "unknown";
|
|
403
|
+
const commitShort = readInjected(true ? "c31eb449" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
404
|
+
const version = readInjected(true ? "0.9.82-rc.401" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
405
|
+
const builtAt = readInjected(true ? "2026-06-27T16:14:32.200Z" : void 0);
|
|
406
406
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
407
407
|
return cached;
|
|
408
408
|
}
|
|
@@ -1441,16 +1441,103 @@ function resolveWorktreePath(repoRoot, meshName, branch) {
|
|
|
1441
1441
|
const parentDir = path4.dirname(repoRoot);
|
|
1442
1442
|
return path4.join(parentDir, WORKTREE_DIR_NAME, safeMeshName, safeBranch);
|
|
1443
1443
|
}
|
|
1444
|
+
async function tryGit(cwd, args) {
|
|
1445
|
+
try {
|
|
1446
|
+
const { stdout, stderr } = await execFileAsync2("git", args, {
|
|
1447
|
+
cwd,
|
|
1448
|
+
encoding: "utf8",
|
|
1449
|
+
timeout: GIT_TIMEOUT_MS,
|
|
1450
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
1451
|
+
windowsHide: true
|
|
1452
|
+
});
|
|
1453
|
+
return { ok: true, stdout: (stdout || "").trim(), stderr: (stderr || "").trim() };
|
|
1454
|
+
} catch (error) {
|
|
1455
|
+
return {
|
|
1456
|
+
ok: false,
|
|
1457
|
+
stdout: typeof error?.stdout === "string" ? error.stdout.trim() : "",
|
|
1458
|
+
stderr: typeof error?.stderr === "string" ? error.stderr.trim() : error?.message || ""
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
async function resolveWorktreeBaseStartPoint(repoRoot, baseBranch, remote) {
|
|
1463
|
+
const fetchResult = await tryGit(repoRoot, ["fetch", remote, baseBranch]);
|
|
1464
|
+
const fetched = fetchResult.ok;
|
|
1465
|
+
const localRev = await tryGit(repoRoot, ["rev-parse", "--verify", "--quiet", `refs/heads/${baseBranch}`]);
|
|
1466
|
+
const remoteRev = await tryGit(repoRoot, ["rev-parse", "--verify", "--quiet", `refs/remotes/${remote}/${baseBranch}`]);
|
|
1467
|
+
const localSha = localRev.ok && localRev.stdout ? localRev.stdout : void 0;
|
|
1468
|
+
const remoteSha = remoteRev.ok && remoteRev.stdout ? remoteRev.stdout : void 0;
|
|
1469
|
+
const remoteRef = `${remote}/${baseBranch}`;
|
|
1470
|
+
const base = {
|
|
1471
|
+
branch: baseBranch,
|
|
1472
|
+
remote,
|
|
1473
|
+
startRef: baseBranch,
|
|
1474
|
+
fetched,
|
|
1475
|
+
localSha,
|
|
1476
|
+
remoteSha,
|
|
1477
|
+
behindBy: 0,
|
|
1478
|
+
aheadBy: 0,
|
|
1479
|
+
action: "up_to_date"
|
|
1480
|
+
};
|
|
1481
|
+
const fetchWarn = fetched ? "" : ` (warning: git fetch ${remote} ${baseBranch} failed: ${fetchResult.stderr || "unknown error"})`;
|
|
1482
|
+
if (!remoteSha) {
|
|
1483
|
+
return {
|
|
1484
|
+
...base,
|
|
1485
|
+
action: "no_remote_ref_used_local",
|
|
1486
|
+
...fetched ? {} : { warning: `Could not fetch ${remoteRef}${fetchWarn}; worktree branched from local ${baseBranch}.` }
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
if (!localSha) {
|
|
1490
|
+
return {
|
|
1491
|
+
...base,
|
|
1492
|
+
startRef: remoteRef,
|
|
1493
|
+
action: "no_local_ref_used_remote"
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
if (localSha === remoteSha) {
|
|
1497
|
+
return base;
|
|
1498
|
+
}
|
|
1499
|
+
const localIsAncestor = (await tryGit(repoRoot, ["merge-base", "--is-ancestor", localSha, remoteSha])).ok;
|
|
1500
|
+
const remoteIsAncestor = (await tryGit(repoRoot, ["merge-base", "--is-ancestor", remoteSha, localSha])).ok;
|
|
1501
|
+
const behindBy = Number((await tryGit(repoRoot, ["rev-list", "--count", `${localSha}..${remoteSha}`])).stdout) || 0;
|
|
1502
|
+
const aheadBy = Number((await tryGit(repoRoot, ["rev-list", "--count", `${remoteSha}..${localSha}`])).stdout) || 0;
|
|
1503
|
+
if (localIsAncestor && !remoteIsAncestor) {
|
|
1504
|
+
return {
|
|
1505
|
+
...base,
|
|
1506
|
+
startRef: remoteRef,
|
|
1507
|
+
behindBy,
|
|
1508
|
+
aheadBy,
|
|
1509
|
+
action: "local_behind_used_remote",
|
|
1510
|
+
warning: `Base node local ${baseBranch} was behind ${remoteRef} by ${behindBy} commit(s); worktree branched from ${remoteRef} (${remoteSha.slice(0, 8)}) instead of stale local ${localSha.slice(0, 8)}.${fetchWarn}`
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
if (remoteIsAncestor) {
|
|
1514
|
+
return { ...base, behindBy, aheadBy, action: "local_ahead_used_local" };
|
|
1515
|
+
}
|
|
1516
|
+
return {
|
|
1517
|
+
...base,
|
|
1518
|
+
behindBy,
|
|
1519
|
+
aheadBy,
|
|
1520
|
+
action: "diverged_used_local",
|
|
1521
|
+
warning: `Base node local ${baseBranch} (${localSha.slice(0, 8)}) has DIVERGED from ${remoteRef} (${remoteSha.slice(0, 8)}): behind ${behindBy}, ahead ${aheadBy}. Worktree branched from local; a rebase onto ${remoteRef} will be required before its push can fast-forward.${fetchWarn}`
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1444
1524
|
async function createWorktree(opts) {
|
|
1445
1525
|
const { repoRoot, branch, baseBranch, meshName } = opts;
|
|
1526
|
+
const remote = (opts.remote || "origin").trim() || "origin";
|
|
1446
1527
|
const targetDir = opts.targetDir || resolveWorktreePath(repoRoot, meshName, branch);
|
|
1447
1528
|
if ((0, import_node_fs2.existsSync)(targetDir)) {
|
|
1448
1529
|
throw new Error(`Worktree target directory already exists: ${targetDir}`);
|
|
1449
1530
|
}
|
|
1450
1531
|
await (0, import_promises3.mkdir)(path4.dirname(targetDir), { recursive: true });
|
|
1532
|
+
let baseSync;
|
|
1533
|
+
let startRef = baseBranch;
|
|
1534
|
+
if (baseBranch && opts.syncBaseFromRemote !== false) {
|
|
1535
|
+
baseSync = await resolveWorktreeBaseStartPoint(repoRoot, baseBranch, remote);
|
|
1536
|
+
startRef = baseSync.startRef;
|
|
1537
|
+
}
|
|
1451
1538
|
const args = ["worktree", "add", targetDir, "-b", branch];
|
|
1452
|
-
if (
|
|
1453
|
-
args.push(
|
|
1539
|
+
if (startRef) {
|
|
1540
|
+
args.push(startRef);
|
|
1454
1541
|
}
|
|
1455
1542
|
try {
|
|
1456
1543
|
await execFileAsync2("git", args, {
|
|
@@ -1473,7 +1560,8 @@ async function createWorktree(opts) {
|
|
|
1473
1560
|
return {
|
|
1474
1561
|
success: true,
|
|
1475
1562
|
worktreePath: targetDir,
|
|
1476
|
-
branch
|
|
1563
|
+
branch,
|
|
1564
|
+
...baseSync ? { baseSync } : {}
|
|
1477
1565
|
};
|
|
1478
1566
|
}
|
|
1479
1567
|
async function removeWorktree(repoRoot, worktreePath, opts = {}) {
|
|
@@ -5654,11 +5742,14 @@ var init_mesh_runtime_store = __esm({
|
|
|
5654
5742
|
}
|
|
5655
5743
|
/** A node may only execute one write task at a time (worktree isolation). */
|
|
5656
5744
|
hasActiveNodeAssignment(meshId, nodeId) {
|
|
5745
|
+
const nodeIdForms = expandDaemonIdForms(nodeId);
|
|
5746
|
+
if (nodeIdForms.length === 0) return false;
|
|
5747
|
+
const placeholders = nodeIdForms.map(() => "?").join(", ");
|
|
5657
5748
|
const row = this.db.prepare(`
|
|
5658
5749
|
SELECT 1 FROM mesh_queue
|
|
5659
|
-
WHERE mesh_id = ? AND status = 'assigned' AND assigned_node_id
|
|
5750
|
+
WHERE mesh_id = ? AND status = 'assigned' AND assigned_node_id IN (${placeholders})
|
|
5660
5751
|
LIMIT 1
|
|
5661
|
-
`).get(meshId,
|
|
5752
|
+
`).get(meshId, ...nodeIdForms);
|
|
5662
5753
|
return row !== void 0;
|
|
5663
5754
|
}
|
|
5664
5755
|
/**
|
|
@@ -11874,7 +11965,14 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
11874
11965
|
targetSessionId: sessionId,
|
|
11875
11966
|
cliType: providerType,
|
|
11876
11967
|
action: "send_chat",
|
|
11877
|
-
message: task.message
|
|
11968
|
+
message: task.message,
|
|
11969
|
+
meshContext: {
|
|
11970
|
+
meshId,
|
|
11971
|
+
nodeId,
|
|
11972
|
+
taskId: task.id,
|
|
11973
|
+
...readNonEmptyString2(loadConfig().machineId) ? { coordinatorDaemonId: readNonEmptyString2(loadConfig().machineId) } : {},
|
|
11974
|
+
...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { coordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {}
|
|
11975
|
+
}
|
|
11878
11976
|
}),
|
|
11879
11977
|
{
|
|
11880
11978
|
meshId,
|
|
@@ -19544,6 +19642,15 @@ var init_cli_state_engine = __esm({
|
|
|
19544
19642
|
currentStatus = "starting";
|
|
19545
19643
|
isWaitingForResponse = false;
|
|
19546
19644
|
currentTurnScope = null;
|
|
19645
|
+
// ARCH-REFACTOR R1 (per-turn task identity): the mesh taskId bound to the most
|
|
19646
|
+
// recently STARTED turn. Unlike currentTurnScope (nulled the moment the turn
|
|
19647
|
+
// settles, before the completion event is even built), this persists past
|
|
19648
|
+
// completion and is only overwritten when the NEXT turn starts. That window is
|
|
19649
|
+
// exactly what the completion path needs: when a turn settles to idle, this still
|
|
19650
|
+
// holds THAT turn's taskId (the next task's turn cannot have started yet — it is
|
|
19651
|
+
// queued in pendingOutbound and only flushed asynchronously after idle), so the
|
|
19652
|
+
// completion event carries the correct id instead of the racy session scalar.
|
|
19653
|
+
currentTurnTaskId = null;
|
|
19547
19654
|
activeModal = null;
|
|
19548
19655
|
// ── Approval ─────────────────────────────────────
|
|
19549
19656
|
lastApprovalResolvedAt = 0;
|
|
@@ -19653,6 +19760,7 @@ var init_cli_state_engine = __esm({
|
|
|
19653
19760
|
this.finishRetryCount = 0;
|
|
19654
19761
|
this.clearIdleFinishCandidate("send_message");
|
|
19655
19762
|
this.currentTurnScope = turnScope;
|
|
19763
|
+
this.currentTurnTaskId = typeof turnScope.taskId === "string" && turnScope.taskId.trim() ? turnScope.taskId : null;
|
|
19656
19764
|
this.responseEpoch += 1;
|
|
19657
19765
|
}
|
|
19658
19766
|
/** Called when PTY exits */
|
|
@@ -21605,15 +21713,18 @@ ${lastSnapshot}`;
|
|
|
21605
21713
|
}
|
|
21606
21714
|
async sendMessage(text, options = {}) {
|
|
21607
21715
|
if (options.force === true) {
|
|
21608
|
-
await this.forceSendMessage(text);
|
|
21716
|
+
await this.forceSendMessage(text, options.meshTaskId);
|
|
21609
21717
|
return;
|
|
21610
21718
|
}
|
|
21611
|
-
await this.sendMessageNow(text, true);
|
|
21719
|
+
await this.sendMessageNow(text, true, options.meshTaskId);
|
|
21612
21720
|
}
|
|
21613
|
-
async forceSendMessage(text) {
|
|
21721
|
+
async forceSendMessage(text, meshTaskId) {
|
|
21614
21722
|
if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
|
|
21615
21723
|
const content = String(text || "");
|
|
21616
21724
|
if (!content.trim()) return;
|
|
21725
|
+
if (typeof meshTaskId === "string" && meshTaskId.trim()) {
|
|
21726
|
+
this.engine.currentTurnTaskId = meshTaskId;
|
|
21727
|
+
}
|
|
21617
21728
|
if (this.engine.currentStatus === "waiting_approval" || this.engine.hasActionableApproval()) {
|
|
21618
21729
|
LOG.info("CLI", `[${this.cliType}] force-send held \u2014 session parked on approval modal (status=${this.engine.currentStatus})`);
|
|
21619
21730
|
return;
|
|
@@ -21626,7 +21737,7 @@ ${lastSnapshot}`;
|
|
|
21626
21737
|
async waitForForceSubmitSettle() {
|
|
21627
21738
|
await new Promise((resolve24) => setTimeout(resolve24, FORCE_SUBMIT_SETTLE_MS));
|
|
21628
21739
|
}
|
|
21629
|
-
enqueuePendingOutboundMessage(text, reason) {
|
|
21740
|
+
enqueuePendingOutboundMessage(text, reason, meshTaskId) {
|
|
21630
21741
|
const content = String(text || "");
|
|
21631
21742
|
const duplicate = this.pendingOutboundQueue.some((message2) => message2.content === content);
|
|
21632
21743
|
if (duplicate) {
|
|
@@ -21638,7 +21749,8 @@ ${lastSnapshot}`;
|
|
|
21638
21749
|
role: "user",
|
|
21639
21750
|
content,
|
|
21640
21751
|
queuedAt,
|
|
21641
|
-
source: "sendMessage"
|
|
21752
|
+
source: "sendMessage",
|
|
21753
|
+
...typeof meshTaskId === "string" && meshTaskId.trim() ? { meshTaskId } : {}
|
|
21642
21754
|
};
|
|
21643
21755
|
this.pendingOutboundQueue.push(message);
|
|
21644
21756
|
LOG.info("CLI", `[${this.cliType}] queued outbound message while busy (${reason}); queue=${this.pendingOutboundQueue.length}`);
|
|
@@ -21681,7 +21793,7 @@ ${lastSnapshot}`;
|
|
|
21681
21793
|
if (this.engine.currentStatus !== "idle" || this.engine.isWaitingForResponse || this.engine.hasActionableApproval()) break;
|
|
21682
21794
|
const next = this.pendingOutboundQueue[0];
|
|
21683
21795
|
try {
|
|
21684
|
-
await this.sendMessageNow(next.content, false);
|
|
21796
|
+
await this.sendMessageNow(next.content, false, next.meshTaskId);
|
|
21685
21797
|
this.pendingOutboundQueue.shift();
|
|
21686
21798
|
this.onStatusChange?.();
|
|
21687
21799
|
} catch (error) {
|
|
@@ -21694,7 +21806,7 @@ ${lastSnapshot}`;
|
|
|
21694
21806
|
this.pendingOutboundFlushInFlight = false;
|
|
21695
21807
|
}
|
|
21696
21808
|
}
|
|
21697
|
-
async sendMessageNow(text, allowQueue) {
|
|
21809
|
+
async sendMessageNow(text, allowQueue, meshTaskId) {
|
|
21698
21810
|
if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
|
|
21699
21811
|
const allowInputDuringGeneration = this.provider.allowInputDuringGeneration === true;
|
|
21700
21812
|
const allowInterventionPrompt = allowInputDuringGeneration && this.engine.isWaitingForResponse && !this.engine.hasActionableApproval();
|
|
@@ -21714,7 +21826,7 @@ ${lastSnapshot}`;
|
|
|
21714
21826
|
})() : null;
|
|
21715
21827
|
const queueReason = this.shouldQueuePendingOutboundMessage(parsedStatusBeforeSend);
|
|
21716
21828
|
if (allowQueue && queueReason) {
|
|
21717
|
-
this.enqueuePendingOutboundMessage(text, queueReason);
|
|
21829
|
+
this.enqueuePendingOutboundMessage(text, queueReason, meshTaskId);
|
|
21718
21830
|
return;
|
|
21719
21831
|
}
|
|
21720
21832
|
if (!allowInterventionPrompt) {
|
|
@@ -21731,7 +21843,7 @@ ${lastSnapshot}`;
|
|
|
21731
21843
|
}
|
|
21732
21844
|
if (!this.ready) {
|
|
21733
21845
|
if (allowQueue) {
|
|
21734
|
-
this.enqueuePendingOutboundMessage(text, "not_ready_pending_prompt");
|
|
21846
|
+
this.enqueuePendingOutboundMessage(text, "not_ready_pending_prompt", meshTaskId);
|
|
21735
21847
|
return;
|
|
21736
21848
|
}
|
|
21737
21849
|
throw new Error(`${this.cliName} not ready (status: ${this.engine.currentStatus})`);
|
|
@@ -21745,7 +21857,7 @@ ${lastSnapshot}`;
|
|
|
21745
21857
|
const terminalLooksIdle = this.engine.currentStatus === "idle" && this.runDetectStatus(this.recentOutputBuffer) === "idle" && !this.engine.isWaitingForResponse && !this.engine.currentTurnScope && !this.engine.hasActionableApproval() && !parsedHasActionableModal;
|
|
21746
21858
|
if (!terminalLooksIdle) {
|
|
21747
21859
|
if (allowQueue) {
|
|
21748
|
-
this.enqueuePendingOutboundMessage(text, `parsed_status_${parsedSessionStatus}
|
|
21860
|
+
this.enqueuePendingOutboundMessage(text, `parsed_status_${parsedSessionStatus}`, meshTaskId);
|
|
21749
21861
|
return;
|
|
21750
21862
|
}
|
|
21751
21863
|
throw new Error(`${this.cliName} is still processing the previous prompt`);
|
|
@@ -21755,7 +21867,7 @@ ${lastSnapshot}`;
|
|
|
21755
21867
|
const snap = this.getSnapshot();
|
|
21756
21868
|
if (!this.engine.clearStaleIdleResponseGuard("send_message_guard", snap) && !this.engine.clearParsedIdleResponseGuard("send_message_parsed_idle_guard", parsedStatusBeforeSend, snap)) {
|
|
21757
21869
|
if (allowQueue) {
|
|
21758
|
-
this.enqueuePendingOutboundMessage(text, "waiting_for_response");
|
|
21870
|
+
this.enqueuePendingOutboundMessage(text, "waiting_for_response", meshTaskId);
|
|
21759
21871
|
return;
|
|
21760
21872
|
}
|
|
21761
21873
|
throw new Error(`${this.cliName} is still processing the previous prompt`);
|
|
@@ -21766,7 +21878,11 @@ ${lastSnapshot}`;
|
|
|
21766
21878
|
prompt: text,
|
|
21767
21879
|
startedAt: Date.now(),
|
|
21768
21880
|
bufferStart: this.accumulatedBuffer.length,
|
|
21769
|
-
rawBufferStart: this.accumulatedRawBuffer.length
|
|
21881
|
+
rawBufferStart: this.accumulatedRawBuffer.length,
|
|
21882
|
+
// ARCH-REFACTOR R1: bind this turn to its mesh task. engine.onTurnStarted
|
|
21883
|
+
// copies this into currentTurnTaskId so the turn's completion event carries
|
|
21884
|
+
// the right id even if a later task overwrites the session scalar meanwhile.
|
|
21885
|
+
...typeof meshTaskId === "string" && meshTaskId.trim() ? { taskId: meshTaskId } : {}
|
|
21770
21886
|
};
|
|
21771
21887
|
LOG.info("CLI", `[${this.cliType}] sendMessage turn scope buffer=${turnScope.bufferStart} raw=${turnScope.rawBufferStart} prompt=${JSON.stringify(text).slice(0, 120)}`);
|
|
21772
21888
|
if (this.submitRetryTimer) {
|
|
@@ -22116,6 +22232,13 @@ ${lastSnapshot}`;
|
|
|
22116
22232
|
set currentTurnScope(v) {
|
|
22117
22233
|
this.engine.currentTurnScope = v;
|
|
22118
22234
|
}
|
|
22235
|
+
// ARCH-REFACTOR R1: the mesh taskId bound to the most recently started turn,
|
|
22236
|
+
// surviving past turn settle until the next turn starts. The provider instance
|
|
22237
|
+
// reads this when stamping completion events so they carry the completing turn's
|
|
22238
|
+
// task rather than the racy last-write-wins session scalar.
|
|
22239
|
+
get currentTurnTaskId() {
|
|
22240
|
+
return this.engine.currentTurnTaskId;
|
|
22241
|
+
}
|
|
22119
22242
|
get responseEpoch() {
|
|
22120
22243
|
return this.engine.responseEpoch;
|
|
22121
22244
|
}
|
|
@@ -40705,11 +40828,28 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
40705
40828
|
isMeshWorkerSession() {
|
|
40706
40829
|
return !!(this.settings.meshNodeFor || this.settings.meshActiveTaskId || this.settings.meshNodeId || this.settings.launchedByCoordinator);
|
|
40707
40830
|
}
|
|
40831
|
+
/**
|
|
40832
|
+
* ARCH-REFACTOR R1: the taskId to attribute the CURRENTLY-completing turn to.
|
|
40833
|
+
* Prefers the per-turn binding (engine.currentTurnTaskId, set when the turn was
|
|
40834
|
+
* submitted and surviving until the next turn starts) over the last-write-wins
|
|
40835
|
+
* session scalar (settings.meshActiveTaskId). The scalar is retained only as a
|
|
40836
|
+
* backward-compat alias for the "current/last assignment" and is the source of the
|
|
40837
|
+
* NOTIF-MISDELIVER / TASK-MSG-MISROUTE race: a second task attaching before this
|
|
40838
|
+
* turn completes overwrites it. Returns undefined for a non-task ad-hoc turn.
|
|
40839
|
+
*/
|
|
40840
|
+
completingTurnTaskId() {
|
|
40841
|
+
const turnTaskId = this.adapter?.currentTurnTaskId;
|
|
40842
|
+
if (typeof turnTaskId === "string" && turnTaskId.trim()) return turnTaskId;
|
|
40843
|
+
const scalar = this.settings.meshActiveTaskId;
|
|
40844
|
+
return typeof scalar === "string" && scalar.trim() ? scalar : void 0;
|
|
40845
|
+
}
|
|
40708
40846
|
// EVTTRACE correlation context for this session's completion lifecycle. taskId is
|
|
40709
40847
|
// the primary grep anchor; instanceId is the session fallback.
|
|
40710
40848
|
meshTraceCtx(event = "agent:generating_completed") {
|
|
40711
40849
|
return {
|
|
40712
|
-
|
|
40850
|
+
// ARCH-REFACTOR R1: trace the per-turn taskId (falling back to the scalar) so
|
|
40851
|
+
// EvtTrace anchors on the same id the completion event actually carries.
|
|
40852
|
+
taskId: this.completingTurnTaskId(),
|
|
40713
40853
|
sessionId: this.instanceId,
|
|
40714
40854
|
nodeId: this.settings.meshNodeId,
|
|
40715
40855
|
meshId: this.settings.meshNodeFor,
|
|
@@ -40768,6 +40908,8 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
40768
40908
|
chatTitle: pending.chatTitle,
|
|
40769
40909
|
duration: pending.duration,
|
|
40770
40910
|
timestamp: pending.timestamp,
|
|
40911
|
+
// ARCH-REFACTOR R1: attribute to the turn captured at idle-transition.
|
|
40912
|
+
...pending.taskId ? { taskId: pending.taskId } : {},
|
|
40771
40913
|
// When finalization is forced past the timeout on a `parsed_status:` block
|
|
40772
40914
|
// (the parser never confirmed a final assistant turn) we previously rode an
|
|
40773
40915
|
// empty `finalSummary` unconditionally. That empty value propagates to the
|
|
@@ -40793,6 +40935,8 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
40793
40935
|
chatTitle: pending.chatTitle,
|
|
40794
40936
|
duration: pending.duration,
|
|
40795
40937
|
timestamp: pending.timestamp,
|
|
40938
|
+
// ARCH-REFACTOR R1: attribute to the turn captured at idle-transition.
|
|
40939
|
+
...pending.taskId ? { taskId: pending.taskId } : {},
|
|
40796
40940
|
finalSummary: this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages)
|
|
40797
40941
|
});
|
|
40798
40942
|
this.completedDebouncePending = null;
|
|
@@ -41069,7 +41213,11 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
41069
41213
|
duration,
|
|
41070
41214
|
timestamp: now,
|
|
41071
41215
|
firstObservedAt: now,
|
|
41072
|
-
previousStatus: this.lastStatus
|
|
41216
|
+
previousStatus: this.lastStatus,
|
|
41217
|
+
// ARCH-REFACTOR R1: snapshot the completing turn's taskId NOW (sync),
|
|
41218
|
+
// before any follow-up task's flush can start a new turn and move
|
|
41219
|
+
// engine.currentTurnTaskId.
|
|
41220
|
+
...this.completingTurnTaskId() ? { taskId: this.completingTurnTaskId() } : {}
|
|
41073
41221
|
};
|
|
41074
41222
|
const ownsExternalHistory = !!this.adapter?.chatMessagesOwnedExternally;
|
|
41075
41223
|
const meshWorkerSession = this.isMeshWorkerSession();
|
|
@@ -41172,10 +41320,11 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
41172
41320
|
workspace: typeof event.workspace === "string" && event.workspace.trim() ? event.workspace : this.workingDir,
|
|
41173
41321
|
providerSessionId: typeof event.providerSessionId === "string" && event.providerSessionId.trim() ? event.providerSessionId : this.providerSessionId
|
|
41174
41322
|
};
|
|
41175
|
-
if (this.isMeshWorkerSession()
|
|
41323
|
+
if (this.isMeshWorkerSession()) {
|
|
41176
41324
|
const existingTaskId = typeof enrichedEvent.taskId === "string" && enrichedEvent.taskId.trim() ? enrichedEvent.taskId : void 0;
|
|
41177
41325
|
if (!existingTaskId) {
|
|
41178
|
-
|
|
41326
|
+
const resolved = this.completingTurnTaskId();
|
|
41327
|
+
if (resolved) enrichedEvent.taskId = resolved;
|
|
41179
41328
|
}
|
|
41180
41329
|
}
|
|
41181
41330
|
if (this.context?.emitProviderEvent) {
|
|
@@ -44055,11 +44204,15 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
44055
44204
|
}
|
|
44056
44205
|
const message = input.textFallback;
|
|
44057
44206
|
if (!message) throw new Error("message required for send_chat");
|
|
44207
|
+
const meshTaskId = meshContext && typeof meshContext === "object" && typeof meshContext.taskId === "string" && meshContext.taskId.trim() ? meshContext.taskId : void 0;
|
|
44058
44208
|
const forceSend = args?.force === true || args?.forceSend === true;
|
|
44059
44209
|
if (forceSend && typeof adapter.forceSendMessage === "function") {
|
|
44060
|
-
await adapter.forceSendMessage(message);
|
|
44210
|
+
if (meshTaskId) await adapter.forceSendMessage(message, meshTaskId);
|
|
44211
|
+
else await adapter.forceSendMessage(message);
|
|
44061
44212
|
} else if (forceSend) {
|
|
44062
|
-
await adapter.sendMessage(message, { force: true });
|
|
44213
|
+
await adapter.sendMessage(message, meshTaskId ? { force: true, meshTaskId } : { force: true });
|
|
44214
|
+
} else if (meshTaskId) {
|
|
44215
|
+
await adapter.sendMessage(message, { meshTaskId });
|
|
44063
44216
|
} else {
|
|
44064
44217
|
await adapter.sendMessage(message);
|
|
44065
44218
|
}
|
|
@@ -48587,6 +48740,11 @@ var meshCrudHandlers = {
|
|
|
48587
48740
|
baseBranch,
|
|
48588
48741
|
meshName: mesh.name
|
|
48589
48742
|
});
|
|
48743
|
+
if (result.baseSync?.warning) {
|
|
48744
|
+
console.warn(`[mesh] clone_mesh_node base sync (${result.baseSync.action}): ${result.baseSync.warning}`);
|
|
48745
|
+
} else if (result.baseSync && result.baseSync.action !== "up_to_date") {
|
|
48746
|
+
console.log(`[mesh] clone_mesh_node base sync: ${result.baseSync.action} (startRef=${result.baseSync.startRef})`);
|
|
48747
|
+
}
|
|
48590
48748
|
let node;
|
|
48591
48749
|
if (meshRecord.inline) {
|
|
48592
48750
|
const { randomUUID: randomUUID15 } = await import("crypto");
|
|
@@ -48777,6 +48935,8 @@ var meshCrudHandlers = {
|
|
|
48777
48935
|
node,
|
|
48778
48936
|
worktreePath: result.worktreePath,
|
|
48779
48937
|
branch: result.branch,
|
|
48938
|
+
...result.baseSync ? { baseSync: result.baseSync } : {},
|
|
48939
|
+
...result.baseSync?.warning ? { baseStaleWarning: result.baseSync.warning } : {},
|
|
48780
48940
|
worktreeBootstrap: runningBootstrapState,
|
|
48781
48941
|
worktreeSetup: {
|
|
48782
48942
|
status: "running",
|
|
@@ -48792,6 +48952,8 @@ var meshCrudHandlers = {
|
|
|
48792
48952
|
node,
|
|
48793
48953
|
worktreePath: result.worktreePath,
|
|
48794
48954
|
branch: result.branch,
|
|
48955
|
+
...result.baseSync ? { baseSync: result.baseSync } : {},
|
|
48956
|
+
...result.baseSync?.warning ? { baseStaleWarning: result.baseSync.warning } : {},
|
|
48795
48957
|
submodulesInitialized,
|
|
48796
48958
|
worktreeBootstrap: bootstrapState
|
|
48797
48959
|
};
|