@adhdev/daemon-core 0.9.82-rc.453 → 0.9.82-rc.454
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.d.ts +1 -1
- package/dist/index.js +19 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +18 -10
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-work-queue.d.ts +19 -1
- package/package.json +3 -3
- package/src/index.ts +1 -1
- package/src/mesh/mesh-queue-assignment.ts +15 -1
- package/src/mesh/mesh-runtime-store.ts +6 -6
- package/src/mesh/mesh-work-queue.ts +28 -2
|
@@ -324,10 +324,28 @@ export declare function updateSessionTaskStatus(meshId: string, sessionId: strin
|
|
|
324
324
|
* Used by the completion event path to decide whether to wake the queue.
|
|
325
325
|
*/
|
|
326
326
|
export declare function hasPendingDependents(meshId: string, taskId: string): boolean;
|
|
327
|
+
/**
|
|
328
|
+
* M1: THE single dependency-gate predicate. A task is claimable from a
|
|
329
|
+
* dependency standpoint iff it carries no system block (`blockedReason`) AND
|
|
330
|
+
* every id in `dependsOn` has reached 'completed'.
|
|
331
|
+
*
|
|
332
|
+
* DEPENDSON-GATE-SYMMETRY: every scheduler surface that decides whether a
|
|
333
|
+
* pending task may run MUST route through this one predicate — the queue claim
|
|
334
|
+
* (claimNextQueueTask), the auto-launch candidate filter
|
|
335
|
+
* (maybeAutoLaunchOneQueueSession), and the cloud eager P2P push
|
|
336
|
+
* (enqueue-and-push). If any surface computes dependency readiness on its own,
|
|
337
|
+
* the gate goes asymmetric and a task blocked from the pull path can still be
|
|
338
|
+
* eager-pushed straight to an idle session, silently bypassing its
|
|
339
|
+
* prerequisites. The semantics here (all deps completed && !blocked) are the
|
|
340
|
+
* invariant — do not fork them.
|
|
341
|
+
*/
|
|
342
|
+
export declare function taskDependenciesSatisfied(entry: Pick<MeshWorkQueueEntry, 'dependsOn' | 'blockedReason'>, statusById: Map<string, MeshTaskStatus | string>): boolean;
|
|
327
343
|
/**
|
|
328
344
|
* M1-4: view-time dependency state for a task — unmet dependency ids and
|
|
329
345
|
* whether the task is currently claimable from a dependency standpoint.
|
|
330
|
-
* Not stored (truth stays in task statuses).
|
|
346
|
+
* Not stored (truth stays in task statuses). The `dependenciesSatisfied` field
|
|
347
|
+
* is derived from {@link taskDependenciesSatisfied} so the view and the
|
|
348
|
+
* scheduler gates can never disagree.
|
|
331
349
|
*/
|
|
332
350
|
export declare function describeTaskDependencyState(entry: Pick<MeshWorkQueueEntry, 'dependsOn' | 'blockedReason'>, statusById: Map<string, MeshTaskStatus | string>): {
|
|
333
351
|
waitingOn: string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.454",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"author": "vilmire",
|
|
47
47
|
"license": "AGPL-3.0-or-later",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@adhdev/mesh-shared": "0.9.82-rc.
|
|
50
|
-
"@adhdev/session-host-core": "0.9.82-rc.
|
|
49
|
+
"@adhdev/mesh-shared": "0.9.82-rc.454",
|
|
50
|
+
"@adhdev/session-host-core": "0.9.82-rc.454",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
|
53
53
|
"ajv-formats": "^3.0.1",
|
package/src/index.ts
CHANGED
|
@@ -270,7 +270,7 @@ export { buildMeshLedgerReconciliationEvidence, buildMeshLedgerReplicaEvidence }
|
|
|
270
270
|
export type { AnyLedgerSlice, MeshLedgerReconciliationEvidence, MeshLedgerReplicaEvidence, MeshLedgerReplicaStatus } from './mesh/mesh-ledger-reconciliation.js';
|
|
271
271
|
|
|
272
272
|
// ── Mesh Work Queue (GUPP) ──
|
|
273
|
-
export { enqueueTask, recordDirectDispatchTask, getQueue, claimNextTask, updateTaskStatus, updateSessionTaskStatus, cancelTask, requeueTask, getMeshQueueStats, getMeshQueueRevision, normalizeMeshTaskMode, validateMeshTaskModeRequest, isTaskReadonly, buildMeshNodeCapabilityTags, nodeSatisfiesRequiredTags, normalizeMeshCapabilityTags, resolveConvergeRequiredTags, insertDirectDispatch, getActiveDirectDispatches, updateDirectDispatchStatus, cleanupTerminalDirectDispatches, markStaleDirectDispatches, deleteDirectDispatchesByTaskId, recordMeshToolCall, assertNoDependencyCycle, hasPendingDependents, describeTaskDependencyState } from './mesh/mesh-work-queue.js';
|
|
273
|
+
export { enqueueTask, recordDirectDispatchTask, getQueue, claimNextTask, updateTaskStatus, updateSessionTaskStatus, cancelTask, requeueTask, getMeshQueueStats, getMeshQueueRevision, normalizeMeshTaskMode, validateMeshTaskModeRequest, isTaskReadonly, buildMeshNodeCapabilityTags, nodeSatisfiesRequiredTags, normalizeMeshCapabilityTags, resolveConvergeRequiredTags, insertDirectDispatch, getActiveDirectDispatches, updateDirectDispatchStatus, cleanupTerminalDirectDispatches, markStaleDirectDispatches, deleteDirectDispatchesByTaskId, recordMeshToolCall, assertNoDependencyCycle, hasPendingDependents, describeTaskDependencyState, taskDependenciesSatisfied } from './mesh/mesh-work-queue.js';
|
|
274
274
|
export type { MeshWorkQueueEntry, MeshTaskStatus, MeshTaskMode, MeshWorkQueueStats, MeshQueueMutationOptions, MeshTaskModeValidationResult, DirectDispatchRecord, MeshToolCallRateResult } from './mesh/mesh-work-queue.js';
|
|
275
275
|
export { buildCompactStaleDirectWorkSummary, buildMeshActiveWork, buildMeshActiveWorkSummary, classifyStaleDirectForPrune, pruneStaleDirectDispatches, PRUNABLE_ORPHAN_STALE_REASONS } from './mesh/mesh-active-work.js';
|
|
276
276
|
export type { StaleDirectPruneClassification, StaleDirectPruneResult, PruneStaleDirectDispatchesOptions } from './mesh/mesh-active-work.js';
|
|
@@ -6,7 +6,7 @@ import { getMesh } from '../config/mesh-config.js';
|
|
|
6
6
|
import { detectCLI } from '../detection/cli-detector.js';
|
|
7
7
|
import { LOG } from '../logging/logger.js';
|
|
8
8
|
import { appendLedgerEntry } from './mesh-ledger.js';
|
|
9
|
-
import { buildMeshNodeCapabilityTags, nodeSatisfiesRequiredTags, claimNextTask, updateTaskStatus, getQueue, recordTaskAutoLaunch, getActiveDirectDispatches, isTaskReadonly } from './mesh-work-queue.js';
|
|
9
|
+
import { buildMeshNodeCapabilityTags, nodeSatisfiesRequiredTags, claimNextTask, updateTaskStatus, getQueue, recordTaskAutoLaunch, getActiveDirectDispatches, isTaskReadonly, taskDependenciesSatisfied } from './mesh-work-queue.js';
|
|
10
10
|
import type { MeshWorkQueueEntry } from './mesh-work-queue.js';
|
|
11
11
|
import { fastForwardMeshNode } from './mesh-fast-forward.js';
|
|
12
12
|
import { createSessionDelivery, updateSessionDeliveryStatus } from './mesh-delivery-policy.js';
|
|
@@ -1287,6 +1287,10 @@ function readMeshNodeId(node: any): string {
|
|
|
1287
1287
|
|
|
1288
1288
|
async function maybeAutoLaunchOneQueueSession(components: DaemonComponents, meshId: string, mesh: any): Promise<boolean> {
|
|
1289
1289
|
const queue = getQueue(meshId);
|
|
1290
|
+
// DEPENDSON-GATE-SYMMETRY: status index over the FULL queue (incl. completed)
|
|
1291
|
+
// so the dependency gate below sees the terminal state of every referenced
|
|
1292
|
+
// dependency, not just the still-active rows.
|
|
1293
|
+
const statusById = new Map(queue.map(task => [task.id, task.status] as const));
|
|
1290
1294
|
const pending = queue.filter(task => task.status === 'pending');
|
|
1291
1295
|
if (!pending.length) return false;
|
|
1292
1296
|
|
|
@@ -1300,6 +1304,16 @@ async function maybeAutoLaunchOneQueueSession(components: DaemonComponents, mesh
|
|
|
1300
1304
|
// higher safety cap (default 2× the write cap).
|
|
1301
1305
|
const maxReadonlyParallelTasks = resolveMaxReadonlyParallelTasks(maxParallelTasks);
|
|
1302
1306
|
for (const task of pending) {
|
|
1307
|
+
// DEPENDSON-GATE-SYMMETRY: never spawn a session for a task whose
|
|
1308
|
+
// dependsOn set is not all-completed (or that carries a system block). The
|
|
1309
|
+
// launched session would idle→claim and be refused by the SAME predicate in
|
|
1310
|
+
// claimNextQueueTask, producing orphan-session / re-launch churn. Skip it so
|
|
1311
|
+
// a later tick — after the dependency completes — launches it. Tasks with no
|
|
1312
|
+
// dependsOn pass through unchanged (predicate is true).
|
|
1313
|
+
if (!taskDependenciesSatisfied(task, statusById)) {
|
|
1314
|
+
markAutoLaunch(meshId, task.id, { status: 'skipped', reason: 'dependencies_unsatisfied' });
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1303
1317
|
const isReadonly = isTaskReadonly(task);
|
|
1304
1318
|
if (isReadonly) {
|
|
1305
1319
|
if (activeReadonlyAssignedCount(meshId) >= maxReadonlyParallelTasks) {
|
|
@@ -3,7 +3,7 @@ import { dirname, join } from 'path';
|
|
|
3
3
|
import { LOG } from '../logging/logger.js';
|
|
4
4
|
import { loadBetterSqlite3 } from '../system/load-better-sqlite3.js';
|
|
5
5
|
import { getLedgerDir } from './mesh-ledger.js';
|
|
6
|
-
import { nodeSatisfiesRequiredTags, isTaskReadonly } from './mesh-work-queue.js';
|
|
6
|
+
import { nodeSatisfiesRequiredTags, isTaskReadonly, taskDependenciesSatisfied } from './mesh-work-queue.js';
|
|
7
7
|
import { meshNodeIdMatches, daemonIdsEquivalent, expandDaemonIdForms, sessionIdsEquivalent } from '@adhdev/mesh-shared';
|
|
8
8
|
import type { MeshTaskStatus, MeshWorkQueueEntry } from './mesh-work-queue.js';
|
|
9
9
|
import type BetterSqlite3 from 'better-sqlite3';
|
|
@@ -756,11 +756,11 @@ export class MeshRuntimeStore {
|
|
|
756
756
|
).all(meshId, ...depIds) as Array<{ id: string; status: string }>;
|
|
757
757
|
for (const r of depRows) depStatus.set(r.id, r.status);
|
|
758
758
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
759
|
+
// DEPENDSON-GATE-SYMMETRY: the claim gate shares the single
|
|
760
|
+
// taskDependenciesSatisfied predicate with the auto-launch filter and
|
|
761
|
+
// the cloud eager P2P push, so a task blocked here is blocked there too.
|
|
762
|
+
const dependenciesSatisfied = (candidate: MeshWorkQueueEntry): boolean =>
|
|
763
|
+
taskDependenciesSatisfied(candidate, depStatus);
|
|
764
764
|
|
|
765
765
|
// Per-candidate node-conflict gate: write tasks require an idle node; read-only
|
|
766
766
|
// tasks bypass the node-busy check so N read-only diagnoses can run on one node
|
|
@@ -1276,10 +1276,36 @@ export function hasPendingDependents(meshId: string, taskId: string): boolean {
|
|
|
1276
1276
|
.some(entry => Array.isArray(entry.dependsOn) && entry.dependsOn.includes(taskId));
|
|
1277
1277
|
}
|
|
1278
1278
|
|
|
1279
|
+
/**
|
|
1280
|
+
* M1: THE single dependency-gate predicate. A task is claimable from a
|
|
1281
|
+
* dependency standpoint iff it carries no system block (`blockedReason`) AND
|
|
1282
|
+
* every id in `dependsOn` has reached 'completed'.
|
|
1283
|
+
*
|
|
1284
|
+
* DEPENDSON-GATE-SYMMETRY: every scheduler surface that decides whether a
|
|
1285
|
+
* pending task may run MUST route through this one predicate — the queue claim
|
|
1286
|
+
* (claimNextQueueTask), the auto-launch candidate filter
|
|
1287
|
+
* (maybeAutoLaunchOneQueueSession), and the cloud eager P2P push
|
|
1288
|
+
* (enqueue-and-push). If any surface computes dependency readiness on its own,
|
|
1289
|
+
* the gate goes asymmetric and a task blocked from the pull path can still be
|
|
1290
|
+
* eager-pushed straight to an idle session, silently bypassing its
|
|
1291
|
+
* prerequisites. The semantics here (all deps completed && !blocked) are the
|
|
1292
|
+
* invariant — do not fork them.
|
|
1293
|
+
*/
|
|
1294
|
+
export function taskDependenciesSatisfied(
|
|
1295
|
+
entry: Pick<MeshWorkQueueEntry, 'dependsOn' | 'blockedReason'>,
|
|
1296
|
+
statusById: Map<string, MeshTaskStatus | string>,
|
|
1297
|
+
): boolean {
|
|
1298
|
+
if (entry.blockedReason) return false;
|
|
1299
|
+
const deps = Array.isArray(entry.dependsOn) ? entry.dependsOn : [];
|
|
1300
|
+
return deps.every(depId => statusById.get(depId) === 'completed');
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1279
1303
|
/**
|
|
1280
1304
|
* M1-4: view-time dependency state for a task — unmet dependency ids and
|
|
1281
1305
|
* whether the task is currently claimable from a dependency standpoint.
|
|
1282
|
-
* Not stored (truth stays in task statuses).
|
|
1306
|
+
* Not stored (truth stays in task statuses). The `dependenciesSatisfied` field
|
|
1307
|
+
* is derived from {@link taskDependenciesSatisfied} so the view and the
|
|
1308
|
+
* scheduler gates can never disagree.
|
|
1283
1309
|
*/
|
|
1284
1310
|
export function describeTaskDependencyState(
|
|
1285
1311
|
entry: Pick<MeshWorkQueueEntry, 'dependsOn' | 'blockedReason'>,
|
|
@@ -1289,7 +1315,7 @@ export function describeTaskDependencyState(
|
|
|
1289
1315
|
const waitingOn = deps.filter(depId => statusById.get(depId) !== 'completed');
|
|
1290
1316
|
return {
|
|
1291
1317
|
waitingOn,
|
|
1292
|
-
dependenciesSatisfied:
|
|
1318
|
+
dependenciesSatisfied: taskDependenciesSatisfied(entry, statusById),
|
|
1293
1319
|
};
|
|
1294
1320
|
}
|
|
1295
1321
|
|