@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.js
CHANGED
|
@@ -45,6 +45,7 @@ var init_repo_mesh_types = __esm({
|
|
|
45
45
|
maxParallelTasks: 2,
|
|
46
46
|
spawnedSessionVisibility: "visible",
|
|
47
47
|
sessionCleanupOnNodeRemove: "preserve",
|
|
48
|
+
autoFastForward: { enabled: true },
|
|
48
49
|
maxTaskRetries: 1
|
|
49
50
|
};
|
|
50
51
|
}
|
|
@@ -1359,7 +1360,17 @@ function normalizeRepoIdentity(remoteUrl) {
|
|
|
1359
1360
|
return identity;
|
|
1360
1361
|
}
|
|
1361
1362
|
function mergeMeshPolicy(base, patch) {
|
|
1362
|
-
const
|
|
1363
|
+
const autoFastForward = normalizeAutoFastForwardPolicy({
|
|
1364
|
+
...DEFAULT_MESH_POLICY.autoFastForward,
|
|
1365
|
+
...base?.autoFastForward && typeof base.autoFastForward === "object" ? base.autoFastForward : {},
|
|
1366
|
+
...patch?.autoFastForward && typeof patch.autoFastForward === "object" ? patch.autoFastForward : {}
|
|
1367
|
+
});
|
|
1368
|
+
const policy = {
|
|
1369
|
+
...DEFAULT_MESH_POLICY,
|
|
1370
|
+
...base || {},
|
|
1371
|
+
...patch || {},
|
|
1372
|
+
autoFastForward
|
|
1373
|
+
};
|
|
1363
1374
|
if (!["block", "warn", "checkpoint_then_continue"].includes(policy.dirtyWorkspaceBehavior)) {
|
|
1364
1375
|
policy.dirtyWorkspaceBehavior = "warn";
|
|
1365
1376
|
}
|
|
@@ -1374,6 +1385,15 @@ function mergeMeshPolicy(base, patch) {
|
|
|
1374
1385
|
}
|
|
1375
1386
|
return policy;
|
|
1376
1387
|
}
|
|
1388
|
+
function normalizeAutoFastForwardPolicy(value) {
|
|
1389
|
+
const record = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1390
|
+
const maxBehind = Number(record.maxBehind);
|
|
1391
|
+
return {
|
|
1392
|
+
enabled: record.enabled !== false,
|
|
1393
|
+
...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
|
|
1394
|
+
requireCleanSubmodules: record.requireCleanSubmodules !== false
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1377
1397
|
function listMeshes() {
|
|
1378
1398
|
return loadMeshConfig().meshes;
|
|
1379
1399
|
}
|
|
@@ -5560,12 +5580,14 @@ function formatCompletionMetadata(event) {
|
|
|
5560
5580
|
const completionDiagnostic = event.completionDiagnostic && typeof event.completionDiagnostic === "object" ? event.completionDiagnostic : null;
|
|
5561
5581
|
const diagnosticReason = completionDiagnostic ? readNonEmptyString2(completionDiagnostic.blockReason) || "present" : "";
|
|
5562
5582
|
const finalAssistantPresent = typeof completionDiagnostic?.finalAssistantPresent === "boolean" ? String(completionDiagnostic.finalAssistantPresent) : "";
|
|
5583
|
+
const evidenceLevel = readNonEmptyString2(event.evidenceLevel);
|
|
5563
5584
|
const parts = [
|
|
5564
5585
|
readNonEmptyString2(event.targetSessionId) ? `session_id=${readNonEmptyString2(event.targetSessionId)}` : "",
|
|
5565
5586
|
readNonEmptyString2(event.providerType) ? `provider=${readNonEmptyString2(event.providerType)}` : "",
|
|
5566
5587
|
readNonEmptyString2(event.providerSessionId) ? `provider_session_id=${readNonEmptyString2(event.providerSessionId)}` : "",
|
|
5567
5588
|
diagnosticReason ? `completion_diagnostic=${diagnosticReason}` : "",
|
|
5568
|
-
finalAssistantPresent ? `final_assistant=${finalAssistantPresent}` : ""
|
|
5589
|
+
finalAssistantPresent ? `final_assistant=${finalAssistantPresent}` : "",
|
|
5590
|
+
evidenceLevel && evidenceLevel !== "sufficient" ? `evidence_level=${evidenceLevel}` : ""
|
|
5569
5591
|
].filter(Boolean);
|
|
5570
5592
|
return parts.length > 0 ? ` (${parts.join("; ")})` : "";
|
|
5571
5593
|
}
|
|
@@ -5575,7 +5597,8 @@ function buildMeshSystemMessage(args) {
|
|
|
5575
5597
|
if (args.metadataEvent.source === "long_generating_reconciliation") {
|
|
5576
5598
|
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.`;
|
|
5577
5599
|
}
|
|
5578
|
-
|
|
5600
|
+
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.";
|
|
5601
|
+
return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path;${reviewNote}`;
|
|
5579
5602
|
}
|
|
5580
5603
|
if (args.event === "agent:waiting_approval") {
|
|
5581
5604
|
return `[System] ${args.nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
|
|
@@ -6722,6 +6745,34 @@ function isIdleSessionState(state) {
|
|
|
6722
6745
|
function isDirtyNode(node) {
|
|
6723
6746
|
return node?.health === "dirty" || node?.git?.dirty === true;
|
|
6724
6747
|
}
|
|
6748
|
+
function resolveAutoFastForwardPolicy(mesh) {
|
|
6749
|
+
const record = mesh?.policy?.autoFastForward && typeof mesh.policy.autoFastForward === "object" && !Array.isArray(mesh.policy.autoFastForward) ? mesh.policy.autoFastForward : {};
|
|
6750
|
+
const maxBehind = Number(record.maxBehind);
|
|
6751
|
+
return {
|
|
6752
|
+
enabled: record.enabled !== false,
|
|
6753
|
+
...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
|
|
6754
|
+
requireCleanSubmodules: record.requireCleanSubmodules !== false
|
|
6755
|
+
};
|
|
6756
|
+
}
|
|
6757
|
+
function sessionStateLooksActive(state) {
|
|
6758
|
+
const status = readNonEmptyString2(state?.status).toLowerCase();
|
|
6759
|
+
const chatStatus = readNonEmptyString2(state?.activeChat?.status).toLowerCase();
|
|
6760
|
+
const active = /* @__PURE__ */ new Set(["generating", "streaming", "long_generating", "working", "starting", "waiting_approval"]);
|
|
6761
|
+
return active.has(status) || active.has(chatStatus);
|
|
6762
|
+
}
|
|
6763
|
+
function nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId) {
|
|
6764
|
+
if (nodeHasActiveAssignment(meshId, nodeId)) return true;
|
|
6765
|
+
return components.instanceManager.getByCategory("cli").some((inst) => {
|
|
6766
|
+
const state = inst.getState();
|
|
6767
|
+
const settings = state.settings || {};
|
|
6768
|
+
if (readNonEmptyString2(settings.meshNodeFor) !== meshId) return false;
|
|
6769
|
+
const instNodeId = readNonEmptyString2(settings.meshNodeId) || readNonEmptyString2(settings.nodeId);
|
|
6770
|
+
if (instNodeId !== nodeId) return false;
|
|
6771
|
+
const sessionId = readNonEmptyString2(state.instanceId);
|
|
6772
|
+
if (currentSessionId && sessionId === currentSessionId && isIdleSessionState(state)) return false;
|
|
6773
|
+
return sessionStateLooksActive(state);
|
|
6774
|
+
});
|
|
6775
|
+
}
|
|
6725
6776
|
function isLaunchableNode(node) {
|
|
6726
6777
|
if (!node || node.status === "disabled" || node.status === "removed") return false;
|
|
6727
6778
|
const health = readNonEmptyString2(node.health).toLowerCase();
|
|
@@ -7050,6 +7101,9 @@ async function maybeAutoFastForwardIdleNode(components, args) {
|
|
|
7050
7101
|
const workspace = readNonEmptyString2(node?.workspace);
|
|
7051
7102
|
if (!workspace) return;
|
|
7052
7103
|
if (!(0, import_fs10.existsSync)(workspace)) return;
|
|
7104
|
+
const policy = resolveAutoFastForwardPolicy(mesh);
|
|
7105
|
+
if (!policy.enabled) return;
|
|
7106
|
+
if (nodeHasActiveMeshWork(components, args.meshId, args.nodeId, args.sessionId)) return;
|
|
7053
7107
|
const throttleKey = `${args.meshId}:${args.nodeId}`;
|
|
7054
7108
|
const now = Date.now();
|
|
7055
7109
|
const lastAttempt = idleAutoFastForwardLastAttempt.get(throttleKey) || 0;
|
|
@@ -7068,6 +7122,12 @@ async function maybeAutoFastForwardIdleNode(components, args) {
|
|
|
7068
7122
|
trigger: "idle_auto"
|
|
7069
7123
|
});
|
|
7070
7124
|
if (!dryRun || dryRun.code !== "fast_forward_available" || dryRun.allowed !== true) return;
|
|
7125
|
+
const behind = Number(dryRun.current?.behind);
|
|
7126
|
+
if (policy.maxBehind !== void 0 && Number.isFinite(behind) && behind > policy.maxBehind) return;
|
|
7127
|
+
if (policy.requireCleanSubmodules) {
|
|
7128
|
+
const submodules = Array.isArray(dryRun.current?.submodules) ? dryRun.current.submodules : [];
|
|
7129
|
+
if (submodules.some((submodule) => submodule?.dirty || submodule?.outOfSync || submodule?.error)) return;
|
|
7130
|
+
}
|
|
7071
7131
|
await fastForwardMeshNode({
|
|
7072
7132
|
meshId: args.meshId,
|
|
7073
7133
|
nodeId: args.nodeId,
|
|
@@ -28518,6 +28578,7 @@ var SCHEMA_V3 = {
|
|
|
28518
28578
|
"busy_hold_ms": { "type": "integer", "minimum": 0 },
|
|
28519
28579
|
"idle_hold_ms": { "type": "integer", "minimum": 0 },
|
|
28520
28580
|
"startup_grace_ms": { "type": "integer", "minimum": 0 },
|
|
28581
|
+
"screen_active_hold_ms": { "type": "integer", "minimum": 0 },
|
|
28521
28582
|
"completion_marker": {
|
|
28522
28583
|
"type": "object",
|
|
28523
28584
|
"additionalProperties": false,
|
|
@@ -28860,6 +28921,7 @@ function attachDebounceAlias(spec) {
|
|
|
28860
28921
|
...t.busy_hold_ms !== void 0 ? { busy_hold_ms: t.busy_hold_ms } : {},
|
|
28861
28922
|
...t.idle_hold_ms !== void 0 ? { idle_hold_ms: t.idle_hold_ms } : {},
|
|
28862
28923
|
...t.startup_grace_ms !== void 0 ? { startup_grace_ms: t.startup_grace_ms } : {},
|
|
28924
|
+
...t.screen_active_hold_ms !== void 0 ? { screen_active_hold_ms: t.screen_active_hold_ms } : {},
|
|
28863
28925
|
...cm ? {
|
|
28864
28926
|
completion_idle_after: {
|
|
28865
28927
|
...cm.section ? { section: cm.section } : {},
|
|
@@ -29163,6 +29225,10 @@ var SpecDriver = class {
|
|
|
29163
29225
|
completionIdleKey = "";
|
|
29164
29226
|
/** Previous screen lines — passed to evaluate() for `changed` condition detection. */
|
|
29165
29227
|
prevScreenLines = [];
|
|
29228
|
+
/** Timestamp of the last PTY frame that changed the screen content.
|
|
29229
|
+
* Used by screen_active_hold_ms to suppress idle downshifts while
|
|
29230
|
+
* the terminal is still actively updating. */
|
|
29231
|
+
lastScreenChangedAt = 0;
|
|
29166
29232
|
/** Timer that re-runs evaluate() once the hold window expires. Needed
|
|
29167
29233
|
* because the PTY stops emitting once the agent finishes; without an
|
|
29168
29234
|
* explicit wake-up there's nothing to trigger the busy → idle
|
|
@@ -29364,8 +29430,12 @@ var SpecDriver = class {
|
|
|
29364
29430
|
reevaluate(forceEmit = false) {
|
|
29365
29431
|
const screen = this.adapter.snapshot();
|
|
29366
29432
|
const cursor = this.adapter.getCursorPosition();
|
|
29433
|
+
const currentLines = screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
29367
29434
|
const ev = evaluate(this.spec, screen, cursor, this.prevScreenLines.length > 0 ? this.prevScreenLines : void 0);
|
|
29368
|
-
this.prevScreenLines
|
|
29435
|
+
if (this.prevScreenLines.length > 0 && currentLines.join("\n") !== this.prevScreenLines.join("\n")) {
|
|
29436
|
+
this.lastScreenChangedAt = Date.now();
|
|
29437
|
+
}
|
|
29438
|
+
this.prevScreenLines = currentLines;
|
|
29369
29439
|
let evState = ev.state;
|
|
29370
29440
|
const busyHoldMs = this.spec.debounce?.busy_hold_ms ?? BUSY_HOLD_MS;
|
|
29371
29441
|
if (this.currentStateId === "busy" && evState.id === "idle") {
|
|
@@ -29383,12 +29453,19 @@ var SpecDriver = class {
|
|
|
29383
29453
|
}
|
|
29384
29454
|
}
|
|
29385
29455
|
const completionIdleRule = this.spec.debounce?.completion_idle_after;
|
|
29456
|
+
const screenActiveHoldMs = this.spec.debounce?.screen_active_hold_ms;
|
|
29386
29457
|
let busyWakeMs = busyHoldMs;
|
|
29387
29458
|
const postModalGraceMs = busyHoldMs;
|
|
29388
29459
|
const now = Date.now();
|
|
29389
29460
|
const recentlyInModal = isModalState(this.currentStateId) || this.lastModalAt > 0 && now - this.lastModalAt < postModalGraceMs;
|
|
29390
29461
|
const recentlyLeftModal = !recentlyInModal && this.lastModalExitAt > 0 && now - this.lastModalExitAt < postModalGraceMs;
|
|
29391
|
-
|
|
29462
|
+
const screenActiveMs = screenActiveHoldMs ?? 0;
|
|
29463
|
+
const screenStableMs = this.lastScreenChangedAt > 0 ? now - this.lastScreenChangedAt : Infinity;
|
|
29464
|
+
const screenIsActive = screenActiveMs > 0 && screenStableMs < screenActiveMs;
|
|
29465
|
+
if (screenIsActive) {
|
|
29466
|
+
busyWakeMs = Math.min(busyWakeMs, screenActiveMs - screenStableMs + 50);
|
|
29467
|
+
}
|
|
29468
|
+
if (evState.id === "busy" && completionIdleRule && !recentlyInModal && !recentlyLeftModal && !screenIsActive) {
|
|
29392
29469
|
const completionKey = matchesCompletionIdleRule(this.spec, ev, screen);
|
|
29393
29470
|
if (completionKey) {
|
|
29394
29471
|
const now2 = Date.now();
|
|
@@ -29418,8 +29495,13 @@ var SpecDriver = class {
|
|
|
29418
29495
|
this.completionIdleFirstSeenAt = 0;
|
|
29419
29496
|
}
|
|
29420
29497
|
} else if (evState.id !== "busy") {
|
|
29421
|
-
|
|
29422
|
-
|
|
29498
|
+
if (!screenIsActive) {
|
|
29499
|
+
this.completionIdleKey = "";
|
|
29500
|
+
this.completionIdleFirstSeenAt = 0;
|
|
29501
|
+
}
|
|
29502
|
+
}
|
|
29503
|
+
if (screenIsActive && this.currentStateId === "busy" && evState.id === (this.spec.default_state ?? "idle")) {
|
|
29504
|
+
evState = this.lastBusyState ?? evState;
|
|
29423
29505
|
}
|
|
29424
29506
|
if (evState.id === "busy") {
|
|
29425
29507
|
this.lastBusyAt = Date.now();
|
|
@@ -32126,7 +32208,7 @@ var CliProviderInstance = class {
|
|
|
32126
32208
|
message: typeof modal?.message === "string" ? modal.message.trim() : "",
|
|
32127
32209
|
buttons: Array.isArray(modal?.buttons) ? modal.buttons.map((button) => String(button).trim()) : []
|
|
32128
32210
|
});
|
|
32129
|
-
if (
|
|
32211
|
+
if (approvalFingerprint !== this.lastApprovalEventFingerprint) {
|
|
32130
32212
|
this.lastApprovalEventFingerprint = approvalFingerprint;
|
|
32131
32213
|
this.appendRuntimeSystemMessage(
|
|
32132
32214
|
this.formatApprovalRequestMessage(modal?.message, modal?.buttons),
|
|
@@ -40196,6 +40278,23 @@ function getGitSubmoduleDriftState(git) {
|
|
|
40196
40278
|
}
|
|
40197
40279
|
return { dirty, outOfSync };
|
|
40198
40280
|
}
|
|
40281
|
+
function isInlineMeshAutoFastForwardEligible(git) {
|
|
40282
|
+
if (!git) return false;
|
|
40283
|
+
if (readBooleanValue(git.isGitRepo) !== true) return false;
|
|
40284
|
+
if (!readStringValue(git.branch)) return false;
|
|
40285
|
+
if (!readStringValue(git.upstream)) return false;
|
|
40286
|
+
const upstreamStatus = readStringValue(git.upstreamStatus, git.upstream_status);
|
|
40287
|
+
if (upstreamStatus !== "fresh") return false;
|
|
40288
|
+
if ((readNumberValue(git.ahead) ?? 0) !== 0) return false;
|
|
40289
|
+
if ((readNumberValue(git.behind) ?? 0) <= 0) return false;
|
|
40290
|
+
const hasConflicts = readBooleanValue(git.hasConflicts) ?? (Array.isArray(git.conflictFiles) && git.conflictFiles.length > 0);
|
|
40291
|
+
if (hasConflicts) return false;
|
|
40292
|
+
if ((readNumberValue(git.stashCount, git.stash_count) ?? 0) > 0) return false;
|
|
40293
|
+
const submoduleDrift = getGitSubmoduleDriftState(git);
|
|
40294
|
+
if (submoduleDrift.dirty || submoduleDrift.outOfSync) return false;
|
|
40295
|
+
const dirty = readBooleanValue(git.dirty) ?? countGitWorktreeChanges(git) > 0;
|
|
40296
|
+
return dirty !== true && countGitWorktreeChanges(git) === 0;
|
|
40297
|
+
}
|
|
40199
40298
|
function deriveMeshNodeHealthFromGit(git) {
|
|
40200
40299
|
if (!git || readBooleanValue(git.isGitRepo) === false) return "degraded";
|
|
40201
40300
|
const branch = readStringValue(git.branch);
|
|
@@ -40325,6 +40424,12 @@ function applyInlineMeshBranchConvergence(mesh, node, status) {
|
|
|
40325
40424
|
status.isDirty = uncommittedChanges > 0;
|
|
40326
40425
|
status.uncommittedChanges = uncommittedChanges;
|
|
40327
40426
|
status.branchConvergence = buildInlineMeshBranchConvergence({ mesh, node, status });
|
|
40427
|
+
status.autoFastForwardEligible = isInlineMeshAutoFastForwardEligible(git);
|
|
40428
|
+
if (status.autoFastForwardEligible) {
|
|
40429
|
+
status.suggestedAction = "auto_fast_forward";
|
|
40430
|
+
} else {
|
|
40431
|
+
delete status.suggestedAction;
|
|
40432
|
+
}
|
|
40328
40433
|
}
|
|
40329
40434
|
function summarizeInlineMeshBranchConvergence(nodes) {
|
|
40330
40435
|
const followUps = nodes.filter((node) => readObjectRecord(node.branchConvergence).needsConvergence === true).map((node) => {
|