@adhdev/daemon-core 0.9.82-rc.211 → 0.9.82-rc.213
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 +113 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +113 -8
- package/dist/index.mjs.map +1 -1
- package/dist/providers/spec/driver.d.ts +4 -0
- package/dist/providers/spec/schema.gen.d.ts +8 -0
- package/dist/providers/spec/types.d.ts +6 -0
- package/dist/repo-mesh-types.d.ts +17 -0
- package/package.json +1 -1
- package/src/commands/router.ts +25 -0
- package/src/config/mesh-config.ts +23 -1
- package/src/mesh/mesh-events-coordinator.ts +43 -0
- package/src/mesh/mesh-events-utils.ts +6 -1
- package/src/providers/cli-provider-instance.ts +4 -3
- package/src/providers/spec/driver.ts +35 -4
- package/src/providers/spec/loader.ts +1 -0
- package/src/providers/spec/schema.gen.ts +1 -0
- package/src/providers/spec/schema.json +1 -0
- package/src/providers/spec/types.ts +6 -0
- package/src/repo-mesh-types.ts +19 -0
package/dist/index.mjs
CHANGED
|
@@ -40,6 +40,7 @@ var init_repo_mesh_types = __esm({
|
|
|
40
40
|
maxParallelTasks: 2,
|
|
41
41
|
spawnedSessionVisibility: "visible",
|
|
42
42
|
sessionCleanupOnNodeRemove: "preserve",
|
|
43
|
+
autoFastForward: { enabled: true },
|
|
43
44
|
maxTaskRetries: 1
|
|
44
45
|
};
|
|
45
46
|
}
|
|
@@ -1357,7 +1358,17 @@ function normalizeRepoIdentity(remoteUrl) {
|
|
|
1357
1358
|
return identity;
|
|
1358
1359
|
}
|
|
1359
1360
|
function mergeMeshPolicy(base, patch) {
|
|
1360
|
-
const
|
|
1361
|
+
const autoFastForward = normalizeAutoFastForwardPolicy({
|
|
1362
|
+
...DEFAULT_MESH_POLICY.autoFastForward,
|
|
1363
|
+
...base?.autoFastForward && typeof base.autoFastForward === "object" ? base.autoFastForward : {},
|
|
1364
|
+
...patch?.autoFastForward && typeof patch.autoFastForward === "object" ? patch.autoFastForward : {}
|
|
1365
|
+
});
|
|
1366
|
+
const policy = {
|
|
1367
|
+
...DEFAULT_MESH_POLICY,
|
|
1368
|
+
...base || {},
|
|
1369
|
+
...patch || {},
|
|
1370
|
+
autoFastForward
|
|
1371
|
+
};
|
|
1361
1372
|
if (!["block", "warn", "checkpoint_then_continue"].includes(policy.dirtyWorkspaceBehavior)) {
|
|
1362
1373
|
policy.dirtyWorkspaceBehavior = "warn";
|
|
1363
1374
|
}
|
|
@@ -1372,6 +1383,15 @@ function mergeMeshPolicy(base, patch) {
|
|
|
1372
1383
|
}
|
|
1373
1384
|
return policy;
|
|
1374
1385
|
}
|
|
1386
|
+
function normalizeAutoFastForwardPolicy(value) {
|
|
1387
|
+
const record = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1388
|
+
const maxBehind = Number(record.maxBehind);
|
|
1389
|
+
return {
|
|
1390
|
+
enabled: record.enabled !== false,
|
|
1391
|
+
...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
|
|
1392
|
+
requireCleanSubmodules: record.requireCleanSubmodules !== false
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1375
1395
|
function listMeshes() {
|
|
1376
1396
|
return loadMeshConfig().meshes;
|
|
1377
1397
|
}
|
|
@@ -5554,12 +5574,14 @@ function formatCompletionMetadata(event) {
|
|
|
5554
5574
|
const completionDiagnostic = event.completionDiagnostic && typeof event.completionDiagnostic === "object" ? event.completionDiagnostic : null;
|
|
5555
5575
|
const diagnosticReason = completionDiagnostic ? readNonEmptyString2(completionDiagnostic.blockReason) || "present" : "";
|
|
5556
5576
|
const finalAssistantPresent = typeof completionDiagnostic?.finalAssistantPresent === "boolean" ? String(completionDiagnostic.finalAssistantPresent) : "";
|
|
5577
|
+
const evidenceLevel = readNonEmptyString2(event.evidenceLevel);
|
|
5557
5578
|
const parts = [
|
|
5558
5579
|
readNonEmptyString2(event.targetSessionId) ? `session_id=${readNonEmptyString2(event.targetSessionId)}` : "",
|
|
5559
5580
|
readNonEmptyString2(event.providerType) ? `provider=${readNonEmptyString2(event.providerType)}` : "",
|
|
5560
5581
|
readNonEmptyString2(event.providerSessionId) ? `provider_session_id=${readNonEmptyString2(event.providerSessionId)}` : "",
|
|
5561
5582
|
diagnosticReason ? `completion_diagnostic=${diagnosticReason}` : "",
|
|
5562
|
-
finalAssistantPresent ? `final_assistant=${finalAssistantPresent}` : ""
|
|
5583
|
+
finalAssistantPresent ? `final_assistant=${finalAssistantPresent}` : "",
|
|
5584
|
+
evidenceLevel && evidenceLevel !== "sufficient" ? `evidence_level=${evidenceLevel}` : ""
|
|
5563
5585
|
].filter(Boolean);
|
|
5564
5586
|
return parts.length > 0 ? ` (${parts.join("; ")})` : "";
|
|
5565
5587
|
}
|
|
@@ -5569,7 +5591,8 @@ function buildMeshSystemMessage(args) {
|
|
|
5569
5591
|
if (args.metadataEvent.source === "long_generating_reconciliation") {
|
|
5570
5592
|
return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The long-generating monitor reconciled the terminal handoff and marked the session complete; wait for the queued completion event/status refresh before doing any manual transcript check.`;
|
|
5571
5593
|
}
|
|
5572
|
-
|
|
5594
|
+
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.";
|
|
5595
|
+
return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path;${reviewNote}`;
|
|
5573
5596
|
}
|
|
5574
5597
|
if (args.event === "agent:waiting_approval") {
|
|
5575
5598
|
return `[System] ${args.nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
|
|
@@ -6716,6 +6739,34 @@ function isIdleSessionState(state) {
|
|
|
6716
6739
|
function isDirtyNode(node) {
|
|
6717
6740
|
return node?.health === "dirty" || node?.git?.dirty === true;
|
|
6718
6741
|
}
|
|
6742
|
+
function resolveAutoFastForwardPolicy(mesh) {
|
|
6743
|
+
const record = mesh?.policy?.autoFastForward && typeof mesh.policy.autoFastForward === "object" && !Array.isArray(mesh.policy.autoFastForward) ? mesh.policy.autoFastForward : {};
|
|
6744
|
+
const maxBehind = Number(record.maxBehind);
|
|
6745
|
+
return {
|
|
6746
|
+
enabled: record.enabled !== false,
|
|
6747
|
+
...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
|
|
6748
|
+
requireCleanSubmodules: record.requireCleanSubmodules !== false
|
|
6749
|
+
};
|
|
6750
|
+
}
|
|
6751
|
+
function sessionStateLooksActive(state) {
|
|
6752
|
+
const status = readNonEmptyString2(state?.status).toLowerCase();
|
|
6753
|
+
const chatStatus = readNonEmptyString2(state?.activeChat?.status).toLowerCase();
|
|
6754
|
+
const active = /* @__PURE__ */ new Set(["generating", "streaming", "long_generating", "working", "starting", "waiting_approval"]);
|
|
6755
|
+
return active.has(status) || active.has(chatStatus);
|
|
6756
|
+
}
|
|
6757
|
+
function nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId) {
|
|
6758
|
+
if (nodeHasActiveAssignment(meshId, nodeId)) return true;
|
|
6759
|
+
return components.instanceManager.getByCategory("cli").some((inst) => {
|
|
6760
|
+
const state = inst.getState();
|
|
6761
|
+
const settings = state.settings || {};
|
|
6762
|
+
if (readNonEmptyString2(settings.meshNodeFor) !== meshId) return false;
|
|
6763
|
+
const instNodeId = readNonEmptyString2(settings.meshNodeId) || readNonEmptyString2(settings.nodeId);
|
|
6764
|
+
if (instNodeId !== nodeId) return false;
|
|
6765
|
+
const sessionId = readNonEmptyString2(state.instanceId);
|
|
6766
|
+
if (currentSessionId && sessionId === currentSessionId && isIdleSessionState(state)) return false;
|
|
6767
|
+
return sessionStateLooksActive(state);
|
|
6768
|
+
});
|
|
6769
|
+
}
|
|
6719
6770
|
function isLaunchableNode(node) {
|
|
6720
6771
|
if (!node || node.status === "disabled" || node.status === "removed") return false;
|
|
6721
6772
|
const health = readNonEmptyString2(node.health).toLowerCase();
|
|
@@ -7044,6 +7095,9 @@ async function maybeAutoFastForwardIdleNode(components, args) {
|
|
|
7044
7095
|
const workspace = readNonEmptyString2(node?.workspace);
|
|
7045
7096
|
if (!workspace) return;
|
|
7046
7097
|
if (!existsSync14(workspace)) return;
|
|
7098
|
+
const policy = resolveAutoFastForwardPolicy(mesh);
|
|
7099
|
+
if (!policy.enabled) return;
|
|
7100
|
+
if (nodeHasActiveMeshWork(components, args.meshId, args.nodeId, args.sessionId)) return;
|
|
7047
7101
|
const throttleKey = `${args.meshId}:${args.nodeId}`;
|
|
7048
7102
|
const now = Date.now();
|
|
7049
7103
|
const lastAttempt = idleAutoFastForwardLastAttempt.get(throttleKey) || 0;
|
|
@@ -7062,6 +7116,12 @@ async function maybeAutoFastForwardIdleNode(components, args) {
|
|
|
7062
7116
|
trigger: "idle_auto"
|
|
7063
7117
|
});
|
|
7064
7118
|
if (!dryRun || dryRun.code !== "fast_forward_available" || dryRun.allowed !== true) return;
|
|
7119
|
+
const behind = Number(dryRun.current?.behind);
|
|
7120
|
+
if (policy.maxBehind !== void 0 && Number.isFinite(behind) && behind > policy.maxBehind) return;
|
|
7121
|
+
if (policy.requireCleanSubmodules) {
|
|
7122
|
+
const submodules = Array.isArray(dryRun.current?.submodules) ? dryRun.current.submodules : [];
|
|
7123
|
+
if (submodules.some((submodule) => submodule?.dirty || submodule?.outOfSync || submodule?.error)) return;
|
|
7124
|
+
}
|
|
7065
7125
|
await fastForwardMeshNode({
|
|
7066
7126
|
meshId: args.meshId,
|
|
7067
7127
|
nodeId: args.nodeId,
|
|
@@ -28185,6 +28245,7 @@ var SCHEMA_V3 = {
|
|
|
28185
28245
|
"busy_hold_ms": { "type": "integer", "minimum": 0 },
|
|
28186
28246
|
"idle_hold_ms": { "type": "integer", "minimum": 0 },
|
|
28187
28247
|
"startup_grace_ms": { "type": "integer", "minimum": 0 },
|
|
28248
|
+
"screen_active_hold_ms": { "type": "integer", "minimum": 0 },
|
|
28188
28249
|
"completion_marker": {
|
|
28189
28250
|
"type": "object",
|
|
28190
28251
|
"additionalProperties": false,
|
|
@@ -28527,6 +28588,7 @@ function attachDebounceAlias(spec) {
|
|
|
28527
28588
|
...t.busy_hold_ms !== void 0 ? { busy_hold_ms: t.busy_hold_ms } : {},
|
|
28528
28589
|
...t.idle_hold_ms !== void 0 ? { idle_hold_ms: t.idle_hold_ms } : {},
|
|
28529
28590
|
...t.startup_grace_ms !== void 0 ? { startup_grace_ms: t.startup_grace_ms } : {},
|
|
28591
|
+
...t.screen_active_hold_ms !== void 0 ? { screen_active_hold_ms: t.screen_active_hold_ms } : {},
|
|
28530
28592
|
...cm ? {
|
|
28531
28593
|
completion_idle_after: {
|
|
28532
28594
|
...cm.section ? { section: cm.section } : {},
|
|
@@ -28830,6 +28892,10 @@ var SpecDriver = class {
|
|
|
28830
28892
|
completionIdleKey = "";
|
|
28831
28893
|
/** Previous screen lines — passed to evaluate() for `changed` condition detection. */
|
|
28832
28894
|
prevScreenLines = [];
|
|
28895
|
+
/** Timestamp of the last PTY frame that changed the screen content.
|
|
28896
|
+
* Used by screen_active_hold_ms to suppress idle downshifts while
|
|
28897
|
+
* the terminal is still actively updating. */
|
|
28898
|
+
lastScreenChangedAt = 0;
|
|
28833
28899
|
/** Timer that re-runs evaluate() once the hold window expires. Needed
|
|
28834
28900
|
* because the PTY stops emitting once the agent finishes; without an
|
|
28835
28901
|
* explicit wake-up there's nothing to trigger the busy → idle
|
|
@@ -29031,8 +29097,12 @@ var SpecDriver = class {
|
|
|
29031
29097
|
reevaluate(forceEmit = false) {
|
|
29032
29098
|
const screen = this.adapter.snapshot();
|
|
29033
29099
|
const cursor = this.adapter.getCursorPosition();
|
|
29100
|
+
const currentLines = screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
29034
29101
|
const ev = evaluate(this.spec, screen, cursor, this.prevScreenLines.length > 0 ? this.prevScreenLines : void 0);
|
|
29035
|
-
this.prevScreenLines
|
|
29102
|
+
if (this.prevScreenLines.length > 0 && currentLines.join("\n") !== this.prevScreenLines.join("\n")) {
|
|
29103
|
+
this.lastScreenChangedAt = Date.now();
|
|
29104
|
+
}
|
|
29105
|
+
this.prevScreenLines = currentLines;
|
|
29036
29106
|
let evState = ev.state;
|
|
29037
29107
|
const busyHoldMs = this.spec.debounce?.busy_hold_ms ?? BUSY_HOLD_MS;
|
|
29038
29108
|
if (this.currentStateId === "busy" && evState.id === "idle") {
|
|
@@ -29050,12 +29120,19 @@ var SpecDriver = class {
|
|
|
29050
29120
|
}
|
|
29051
29121
|
}
|
|
29052
29122
|
const completionIdleRule = this.spec.debounce?.completion_idle_after;
|
|
29123
|
+
const screenActiveHoldMs = this.spec.debounce?.screen_active_hold_ms;
|
|
29053
29124
|
let busyWakeMs = busyHoldMs;
|
|
29054
29125
|
const postModalGraceMs = busyHoldMs;
|
|
29055
29126
|
const now = Date.now();
|
|
29056
29127
|
const recentlyInModal = isModalState(this.currentStateId) || this.lastModalAt > 0 && now - this.lastModalAt < postModalGraceMs;
|
|
29057
29128
|
const recentlyLeftModal = !recentlyInModal && this.lastModalExitAt > 0 && now - this.lastModalExitAt < postModalGraceMs;
|
|
29058
|
-
|
|
29129
|
+
const screenActiveMs = screenActiveHoldMs ?? 0;
|
|
29130
|
+
const screenStableMs = this.lastScreenChangedAt > 0 ? now - this.lastScreenChangedAt : Infinity;
|
|
29131
|
+
const screenIsActive = screenActiveMs > 0 && screenStableMs < screenActiveMs;
|
|
29132
|
+
if (screenIsActive) {
|
|
29133
|
+
busyWakeMs = Math.min(busyWakeMs, screenActiveMs - screenStableMs + 50);
|
|
29134
|
+
}
|
|
29135
|
+
if (evState.id === "busy" && completionIdleRule && !recentlyInModal && !recentlyLeftModal && !screenIsActive) {
|
|
29059
29136
|
const completionKey = matchesCompletionIdleRule(this.spec, ev, screen);
|
|
29060
29137
|
if (completionKey) {
|
|
29061
29138
|
const now2 = Date.now();
|
|
@@ -29085,8 +29162,13 @@ var SpecDriver = class {
|
|
|
29085
29162
|
this.completionIdleFirstSeenAt = 0;
|
|
29086
29163
|
}
|
|
29087
29164
|
} else if (evState.id !== "busy") {
|
|
29088
|
-
|
|
29089
|
-
|
|
29165
|
+
if (!screenIsActive) {
|
|
29166
|
+
this.completionIdleKey = "";
|
|
29167
|
+
this.completionIdleFirstSeenAt = 0;
|
|
29168
|
+
}
|
|
29169
|
+
}
|
|
29170
|
+
if (screenIsActive && this.currentStateId === "busy" && evState.id === (this.spec.default_state ?? "idle")) {
|
|
29171
|
+
evState = this.lastBusyState ?? evState;
|
|
29090
29172
|
}
|
|
29091
29173
|
if (evState.id === "busy") {
|
|
29092
29174
|
this.lastBusyAt = Date.now();
|
|
@@ -31793,7 +31875,7 @@ var CliProviderInstance = class {
|
|
|
31793
31875
|
message: typeof modal?.message === "string" ? modal.message.trim() : "",
|
|
31794
31876
|
buttons: Array.isArray(modal?.buttons) ? modal.buttons.map((button) => String(button).trim()) : []
|
|
31795
31877
|
});
|
|
31796
|
-
if (
|
|
31878
|
+
if (approvalFingerprint !== this.lastApprovalEventFingerprint) {
|
|
31797
31879
|
this.lastApprovalEventFingerprint = approvalFingerprint;
|
|
31798
31880
|
this.appendRuntimeSystemMessage(
|
|
31799
31881
|
this.formatApprovalRequestMessage(modal?.message, modal?.buttons),
|
|
@@ -39868,6 +39950,23 @@ function getGitSubmoduleDriftState(git) {
|
|
|
39868
39950
|
}
|
|
39869
39951
|
return { dirty, outOfSync };
|
|
39870
39952
|
}
|
|
39953
|
+
function isInlineMeshAutoFastForwardEligible(git) {
|
|
39954
|
+
if (!git) return false;
|
|
39955
|
+
if (readBooleanValue(git.isGitRepo) !== true) return false;
|
|
39956
|
+
if (!readStringValue(git.branch)) return false;
|
|
39957
|
+
if (!readStringValue(git.upstream)) return false;
|
|
39958
|
+
const upstreamStatus = readStringValue(git.upstreamStatus, git.upstream_status);
|
|
39959
|
+
if (upstreamStatus !== "fresh") return false;
|
|
39960
|
+
if ((readNumberValue(git.ahead) ?? 0) !== 0) return false;
|
|
39961
|
+
if ((readNumberValue(git.behind) ?? 0) <= 0) return false;
|
|
39962
|
+
const hasConflicts = readBooleanValue(git.hasConflicts) ?? (Array.isArray(git.conflictFiles) && git.conflictFiles.length > 0);
|
|
39963
|
+
if (hasConflicts) return false;
|
|
39964
|
+
if ((readNumberValue(git.stashCount, git.stash_count) ?? 0) > 0) return false;
|
|
39965
|
+
const submoduleDrift = getGitSubmoduleDriftState(git);
|
|
39966
|
+
if (submoduleDrift.dirty || submoduleDrift.outOfSync) return false;
|
|
39967
|
+
const dirty = readBooleanValue(git.dirty) ?? countGitWorktreeChanges(git) > 0;
|
|
39968
|
+
return dirty !== true && countGitWorktreeChanges(git) === 0;
|
|
39969
|
+
}
|
|
39871
39970
|
function deriveMeshNodeHealthFromGit(git) {
|
|
39872
39971
|
if (!git || readBooleanValue(git.isGitRepo) === false) return "degraded";
|
|
39873
39972
|
const branch = readStringValue(git.branch);
|
|
@@ -39997,6 +40096,12 @@ function applyInlineMeshBranchConvergence(mesh, node, status) {
|
|
|
39997
40096
|
status.isDirty = uncommittedChanges > 0;
|
|
39998
40097
|
status.uncommittedChanges = uncommittedChanges;
|
|
39999
40098
|
status.branchConvergence = buildInlineMeshBranchConvergence({ mesh, node, status });
|
|
40099
|
+
status.autoFastForwardEligible = isInlineMeshAutoFastForwardEligible(git);
|
|
40100
|
+
if (status.autoFastForwardEligible) {
|
|
40101
|
+
status.suggestedAction = "auto_fast_forward";
|
|
40102
|
+
} else {
|
|
40103
|
+
delete status.suggestedAction;
|
|
40104
|
+
}
|
|
40000
40105
|
}
|
|
40001
40106
|
function summarizeInlineMeshBranchConvergence(nodes) {
|
|
40002
40107
|
const followUps = nodes.filter((node) => readObjectRecord(node.branchConvergence).needsConvergence === true).map((node) => {
|