@adhdev/daemon-core 0.9.82-rc.486 → 0.9.82-rc.488
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/commands/router-refine.d.ts +14 -0
- package/dist/git/git-status.d.ts +23 -0
- package/dist/index.js +232 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +232 -33
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-event-forwarding.d.ts +9 -0
- package/dist/mesh/mesh-refine-gates.d.ts +29 -0
- package/dist/mesh/mesh-work-queue.d.ts +11 -0
- package/dist/providers/cli-provider-instance.d.ts +1 -0
- package/dist/providers/provider-instance-manager.d.ts +1 -0
- package/dist/providers/provider-instance.d.ts +1 -0
- package/package.json +3 -3
- package/src/commands/cli-manager.ts +4 -0
- package/src/commands/router-refine.ts +87 -4
- package/src/git/git-status.ts +84 -29
- package/src/mesh/mesh-event-forwarding.ts +92 -0
- package/src/mesh/mesh-queue-assignment.ts +6 -0
- package/src/mesh/mesh-refine-gates.ts +113 -7
- package/src/mesh/mesh-runtime-store.ts +5 -0
- package/src/mesh/mesh-work-queue.ts +18 -0
- package/src/providers/cli-provider-instance.ts +17 -6
- package/src/providers/provider-instance-manager.ts +1 -1
- package/src/providers/provider-instance.ts +1 -1
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { getGitRepoStatus } from '../git/git-status.js';
|
|
16
|
+
import type { ChangedPackageClassification } from '../git/git-status.js';
|
|
16
17
|
import * as yaml from 'js-yaml';
|
|
17
18
|
import { loadMeshRefineConfig, resolveMeshRefineValidationPlan } from '../mesh/refine-config.js';
|
|
18
19
|
import type { MeshRefineValidationCommandPlan } from '../mesh/refine-config.js';
|
|
@@ -74,6 +75,19 @@ type MeshRefineValidationSummary = {
|
|
|
74
75
|
};
|
|
75
76
|
/** M2-2: deprecation notices from the refine config (e.g. bootstrapCommands). */
|
|
76
77
|
deprecationWarnings?: string[];
|
|
78
|
+
/**
|
|
79
|
+
* Coarse daemon-vs-web change-impact used to scope the validation command set.
|
|
80
|
+
* When `isDaemonAffecting === false`, daemon-scoped commands are recorded in
|
|
81
|
+
* `commandsRun` with `skipped: true, skipReason: 'unaffected_daemon_scope'`
|
|
82
|
+
* rather than executed; web + typecheck commands always run. Absent when no
|
|
83
|
+
* change-impact was threaded in (legacy: full command set runs).
|
|
84
|
+
*/
|
|
85
|
+
changeImpact?: {
|
|
86
|
+
isDaemonAffecting: boolean;
|
|
87
|
+
affectedPackages: string[];
|
|
88
|
+
/** displayCommands skipped because the daemon scope is unaffected. */
|
|
89
|
+
skippedDaemonCommands?: string[];
|
|
90
|
+
};
|
|
77
91
|
};
|
|
78
92
|
|
|
79
93
|
type MeshRefineStageStatus = 'passed' | 'failed' | 'skipped';
|
|
@@ -343,6 +357,13 @@ export interface RefineContext {
|
|
|
343
357
|
baseBranch: string;
|
|
344
358
|
baseHead: string;
|
|
345
359
|
branchHead: string;
|
|
360
|
+
/**
|
|
361
|
+
* Coarse daemon-vs-web change-impact for baseHead..branchHead, resolved in the
|
|
362
|
+
* resolve_refs stage and threaded into the validation gate to scope its command
|
|
363
|
+
* set. `undefined` means "could not classify" → the gate fails open and runs ALL
|
|
364
|
+
* commands (never skip on uncertainty).
|
|
365
|
+
*/
|
|
366
|
+
changeImpact?: ChangedPackageClassification;
|
|
346
367
|
validationSummary: Awaited<ReturnType<typeof runMeshRefineValidationGate>>;
|
|
347
368
|
patchEquivalence: Awaited<ReturnType<typeof runMeshRefinePatchEquivalenceGate>>;
|
|
348
369
|
submoduleReachability: Awaited<ReturnType<typeof runMeshRefineSubmoduleReachabilityGate>>;
|
|
@@ -1477,6 +1498,14 @@ export async function runMeshRefineValidationGate(
|
|
|
1477
1498
|
persistedBootstrapState?: WorktreeBootstrapState | null;
|
|
1478
1499
|
/** M2-2: called after an inherit-mode bootstrap run so the caller can persist the new state. */
|
|
1479
1500
|
onBootstrapStateChange?: (state: WorktreeBootstrapState) => void;
|
|
1501
|
+
/**
|
|
1502
|
+
* Coarse daemon-vs-web change-impact for the branch (resolve_refs computes it
|
|
1503
|
+
* over baseHead..branchHead). When provided and `isDaemonAffecting === false`,
|
|
1504
|
+
* daemon-scoped validation commands are skipped (web + typecheck still run).
|
|
1505
|
+
* When omitted or `isDaemonAffecting === true`, the full command set runs —
|
|
1506
|
+
* fail-open to full validation on any uncertainty.
|
|
1507
|
+
*/
|
|
1508
|
+
changeImpact?: ChangedPackageClassification;
|
|
1480
1509
|
},
|
|
1481
1510
|
): Promise<MeshRefineValidationSummary> {
|
|
1482
1511
|
const { execFile } = await import('node:child_process');
|
|
@@ -1574,6 +1603,63 @@ export async function runMeshRefineValidationGate(
|
|
|
1574
1603
|
return ['package-lock.json', 'npm-shrinkwrap.json', 'pnpm-lock.yaml', 'yarn.lock', 'bun.lockb', 'bun.lock']
|
|
1575
1604
|
.some(lock => fs.existsSync(pathJoin(cwd, lock)));
|
|
1576
1605
|
};
|
|
1606
|
+
// A validation command needs installed node_modules to run. Only these can hit
|
|
1607
|
+
// the missing-deps hard-block; non-package-manager commands (e.g. a plain
|
|
1608
|
+
// `node scripts/check-vendor-drift.mjs`) need no deps and must never be aborted
|
|
1609
|
+
// by a preceding command's missing-deps.
|
|
1610
|
+
const needsNodeModules = (candidate: MeshRefineValidationCommand, cwd: string): boolean =>
|
|
1611
|
+
isPackageManagerValidation(candidate) && dependenciesLikelyMissing(cwd);
|
|
1612
|
+
|
|
1613
|
+
// (a) Coarse change-impact scoping. When the branch is web-only
|
|
1614
|
+
// (changeImpact.isDaemonAffecting === false), daemon-scoped validation commands
|
|
1615
|
+
// are pointless — and often un-runnable in a web-only worktree that never
|
|
1616
|
+
// bootstrapped daemon deps. Identify daemon-scoped commands ONLY by the coarse
|
|
1617
|
+
// daemon-vs-web bucket: a command whose script/args reference a daemon package
|
|
1618
|
+
// (daemon-core / daemon-cloud) or the vendor-drift check. web-side commands
|
|
1619
|
+
// (test:web-core / test:web-cloud) and `typecheck` ALWAYS run — the daemon/web
|
|
1620
|
+
// boundary is the human-curated safe line; we deliberately do NOT do fine
|
|
1621
|
+
// per-package skipping (web-cloud consumes web-core, so it must still run).
|
|
1622
|
+
const isDaemonScopedCommand = (candidate: MeshRefineValidationCommand): boolean => {
|
|
1623
|
+
const haystack = [candidate.command, ...(candidate.args || []), candidate.displayCommand || '']
|
|
1624
|
+
.join(' ')
|
|
1625
|
+
.toLowerCase();
|
|
1626
|
+
// Never treat a typecheck or an explicit web-side command as daemon-scoped.
|
|
1627
|
+
if (candidate.category === 'typecheck') return false;
|
|
1628
|
+
if (/\btypecheck\b/.test(haystack)) return false;
|
|
1629
|
+
if (/\bweb-core\b|\bweb-cloud\b|\bweb-standalone\b|\btest:web\b/.test(haystack)) return false;
|
|
1630
|
+
// Daemon-scoped signals: a daemon package name, a daemon test script, or the
|
|
1631
|
+
// vendor-drift check (which validates the daemon vendor bundle).
|
|
1632
|
+
return /\bdaemon-core\b|\bdaemon-cloud\b|\btest:daemon\b|check-vendor-drift/.test(haystack);
|
|
1633
|
+
};
|
|
1634
|
+
|
|
1635
|
+
const scopeUnaffectedDaemon = opts?.changeImpact?.isDaemonAffecting === false;
|
|
1636
|
+
const skippedDaemonCommands: string[] = [];
|
|
1637
|
+
const commandsToRun: MeshRefineValidationCommand[] = [];
|
|
1638
|
+
for (const candidate of selection.commands) {
|
|
1639
|
+
if (scopeUnaffectedDaemon && isDaemonScopedCommand(candidate)) {
|
|
1640
|
+
skippedDaemonCommands.push(candidate.displayCommand);
|
|
1641
|
+
// Record the skip so it's visible in the summary, never silently dropped.
|
|
1642
|
+
summary.commandsRun.push({
|
|
1643
|
+
command: candidate.command,
|
|
1644
|
+
args: candidate.args,
|
|
1645
|
+
displayCommand: candidate.displayCommand,
|
|
1646
|
+
category: candidate.category,
|
|
1647
|
+
source: candidate.source,
|
|
1648
|
+
passed: true,
|
|
1649
|
+
skipped: true,
|
|
1650
|
+
skipReason: 'unaffected_daemon_scope',
|
|
1651
|
+
});
|
|
1652
|
+
continue;
|
|
1653
|
+
}
|
|
1654
|
+
commandsToRun.push(candidate);
|
|
1655
|
+
}
|
|
1656
|
+
if (opts?.changeImpact) {
|
|
1657
|
+
summary.changeImpact = {
|
|
1658
|
+
isDaemonAffecting: opts.changeImpact.isDaemonAffecting,
|
|
1659
|
+
affectedPackages: opts.changeImpact.affectedPackages,
|
|
1660
|
+
...(skippedDaemonCommands.length ? { skippedDaemonCommands } : {}),
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1577
1663
|
|
|
1578
1664
|
if (runLegacyBootstrapCommands) {
|
|
1579
1665
|
summary.bootstrap = { stage: 'legacy' };
|
|
@@ -1619,23 +1705,31 @@ export async function runMeshRefineValidationGate(
|
|
|
1619
1705
|
}
|
|
1620
1706
|
}
|
|
1621
1707
|
|
|
1622
|
-
|
|
1708
|
+
// (b) Track a genuine missing-deps block for an AFFECTED command. Instead of
|
|
1709
|
+
// aborting the whole gate at the first missing-deps hit (which also killed
|
|
1710
|
+
// trailing no-dep commands like check-vendor-drift.mjs), we mark the blocked
|
|
1711
|
+
// command and CONTINUE evaluating the rest: commands whose deps are present, or
|
|
1712
|
+
// which need no deps at all, still run. missing_dependencies only becomes the
|
|
1713
|
+
// gate failure if at least one command that truly needed deps could not run.
|
|
1714
|
+
let missingDepsBlocked = false;
|
|
1715
|
+
for (const candidate of commandsToRun) {
|
|
1623
1716
|
const startedAt = Date.now();
|
|
1624
1717
|
const cwd = candidate.cwd ? pathResolve(workspace, candidate.cwd) : workspace;
|
|
1625
1718
|
const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
|
|
1626
1719
|
const bootstrapProvidedDependencies = summary.bootstrap?.stage === 'cached' || summary.bootstrap?.stage === 'ran' || summary.bootstrap?.stage === 'legacy';
|
|
1627
|
-
if (!bootstrapProvidedDependencies &&
|
|
1720
|
+
if (!bootstrapProvidedDependencies && needsNodeModules(candidate, cwd)) {
|
|
1721
|
+
// This command genuinely needs node_modules that are absent. Mark it
|
|
1722
|
+
// blocked, but do NOT abort — a following no-dep command (or one in a
|
|
1723
|
+
// different cwd that DOES have deps) must still get its chance to run.
|
|
1628
1724
|
summary.commandsRun.push(commandRecord(candidate, cwd, startedAt, {
|
|
1629
|
-
stderr: 'Dependencies appear to be missing: package.json and a lockfile are present, but node_modules is absent. Configure validation.bootstrapCommands in repo mesh/refine config if Refinery should install/bootstrap before validation.',
|
|
1725
|
+
stderr: 'Dependencies appear to be missing: package.json and a lockfile are present, but node_modules is absent. Configure validation.bootstrapCommands (or .adhdev/worktree_bootstrap.json) in repo mesh/refine config if Refinery should install/bootstrap before validation.',
|
|
1630
1726
|
}, false, {
|
|
1631
1727
|
exitCode: null,
|
|
1632
1728
|
skipped: true,
|
|
1633
1729
|
failureKind: 'missing_dependencies',
|
|
1634
1730
|
}));
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
summary.failureCode = 'missing_dependencies';
|
|
1638
|
-
return summary;
|
|
1731
|
+
missingDepsBlocked = true;
|
|
1732
|
+
continue;
|
|
1639
1733
|
}
|
|
1640
1734
|
// See the bootstrap loop above: resolve the win32 .cmd shim to an
|
|
1641
1735
|
// absolute path before handing it to the spawn boundary.
|
|
@@ -1681,6 +1775,18 @@ export async function runMeshRefineValidationGate(
|
|
|
1681
1775
|
}
|
|
1682
1776
|
}
|
|
1683
1777
|
|
|
1778
|
+
// (b) A command that genuinely needed deps could not run. Surface it as the
|
|
1779
|
+
// gate failure now (after letting no-dep / deps-present commands run), so the
|
|
1780
|
+
// caller can classify it blocked_review and emit a self-service hint. Every
|
|
1781
|
+
// daemon-scoped command in a web-only branch was already filtered above, so a
|
|
1782
|
+
// missing-deps block here is a real affected-command block.
|
|
1783
|
+
if (missingDepsBlocked) {
|
|
1784
|
+
summary.status = 'failed';
|
|
1785
|
+
summary.failureKind = 'missing_dependencies';
|
|
1786
|
+
summary.failureCode = 'missing_dependencies';
|
|
1787
|
+
return summary;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1684
1790
|
summary.status = 'passed';
|
|
1685
1791
|
return summary;
|
|
1686
1792
|
}
|
|
@@ -1043,6 +1043,11 @@ export class MeshRuntimeStore {
|
|
|
1043
1043
|
entry.assignedSessionId = sessionId;
|
|
1044
1044
|
if (providerType) entry.assignedProviderType = providerType;
|
|
1045
1045
|
entry.dispatchTimestamp = now;
|
|
1046
|
+
// REDRIVE-DUP: bump the per-task dispatch nonce on every claim so this dispatch
|
|
1047
|
+
// carries a nonce strictly greater than any prior (reclaimed) dispatch of the same
|
|
1048
|
+
// task. The worker echoes it on agent:generating_started; the coordinator rejects a
|
|
1049
|
+
// stale-nonce ack so a reclaimed+re-dispatched task's original inject cannot execute.
|
|
1050
|
+
entry.dispatchNonce = (entry.dispatchNonce || 0) + 1;
|
|
1046
1051
|
entry.updatedAt = now;
|
|
1047
1052
|
|
|
1048
1053
|
this.db.prepare(`
|
|
@@ -644,6 +644,17 @@ export interface MeshWorkQueueEntry {
|
|
|
644
644
|
};
|
|
645
645
|
/** ISO timestamp when the task was dispatched (assigned) to a node/session. Used for precise matching on completion. */
|
|
646
646
|
dispatchTimestamp?: string;
|
|
647
|
+
/**
|
|
648
|
+
* REDRIVE-DUP: monotonic per-task dispatch nonce. Bumped on every (re)dispatch of
|
|
649
|
+
* this task (assignQueueTask) AND on every reclaim (reclaimStrandedAssignedTask), and
|
|
650
|
+
* carried to the worker in meshContext.dispatchNonce. The worker echoes it back on
|
|
651
|
+
* agent:generating_started (metadataEvent.dispatchNonce). When a delivered-not-consumed
|
|
652
|
+
* task is reclaimed and re-dispatched to a different node, the ORIGINAL inject to the
|
|
653
|
+
* first node still carries the now-stale nonce; the coordinator rejects that node's
|
|
654
|
+
* generating_started ack (and stops it) so the SAME taskId is never executed twice.
|
|
655
|
+
* Absent on legacy rows → the coordinator skips the stale-nonce guard (backward safe).
|
|
656
|
+
*/
|
|
657
|
+
dispatchNonce?: number;
|
|
647
658
|
/**
|
|
648
659
|
* (3) The ORIGINATING coordinator session that enqueued this task. Stamped onto the
|
|
649
660
|
* worker at dispatch (meshCoordinatorSessionId) so the task's completion routes back to
|
|
@@ -1386,6 +1397,13 @@ export function reclaimStrandedAssignedTask(
|
|
|
1386
1397
|
delete entry.assignedSessionId;
|
|
1387
1398
|
delete entry.assignedProviderType;
|
|
1388
1399
|
delete entry.dispatchTimestamp;
|
|
1400
|
+
// REDRIVE-DUP: bump the dispatch nonce so the ORIGINAL inject to prevNode/prevSession
|
|
1401
|
+
// (which is delivered-but-unconsumed and about to be re-dispatched elsewhere) now
|
|
1402
|
+
// carries a stale nonce. When that stranded inject finally fires and the worker emits
|
|
1403
|
+
// agent:generating_started echoing the old nonce, the coordinator's stale-nonce guard
|
|
1404
|
+
// rejects the ack and stops that worker — so the reclaimed+re-dispatched task is never
|
|
1405
|
+
// executed by the originally-assigned session (no duplicate execution).
|
|
1406
|
+
entry.dispatchNonce = (entry.dispatchNonce || 0) + 1;
|
|
1389
1407
|
entry.strandedReclaimCount = reclaims;
|
|
1390
1408
|
entry.updatedAt = now;
|
|
1391
1409
|
// The stranded assignment is being torn down (→ pending or failed); end its
|
|
@@ -825,7 +825,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
825
825
|
* completion events silently drop because the forwarder has nothing to
|
|
826
826
|
* match against.
|
|
827
827
|
*/
|
|
828
|
-
attachMeshAssignment(assignment: { meshId: string; nodeId?: string; taskId?: string; coordinatorDaemonId?: string; coordinatorSessionId?: string }): void {
|
|
828
|
+
attachMeshAssignment(assignment: { meshId: string; nodeId?: string; taskId?: string; dispatchNonce?: number; coordinatorDaemonId?: string; coordinatorSessionId?: string }): void {
|
|
829
829
|
if (!assignment?.meshId) return;
|
|
830
830
|
this.settings = {
|
|
831
831
|
...this.settings,
|
|
@@ -838,6 +838,10 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
838
838
|
// shares this daemon. See isMeshOwnedDelegateSession's post-detach gate.
|
|
839
839
|
...(assignment.nodeId ? { meshNodeId: assignment.nodeId, meshLastNodeId: assignment.nodeId } : {}),
|
|
840
840
|
...(assignment.taskId ? { meshActiveTaskId: assignment.taskId } : {}),
|
|
841
|
+
// REDRIVE-DUP: task-level dispatch nonce, echoed on generating_started so the
|
|
842
|
+
// coordinator can reject a stale (reclaimed) dispatch. Cleared with meshActiveTaskId
|
|
843
|
+
// on detach so a subsequent unrelated turn never re-echoes a prior task's nonce.
|
|
844
|
+
...(typeof assignment.dispatchNonce === 'number' ? { meshActiveDispatchNonce: assignment.dispatchNonce } : {}),
|
|
841
845
|
...(assignment.coordinatorDaemonId ? { meshCoordinatorDaemonId: assignment.coordinatorDaemonId } : {}),
|
|
842
846
|
// Session-level routing anchor: the originating coordinator session, so this
|
|
843
847
|
// worker's completion events route back to the exact session that dispatched it.
|
|
@@ -872,17 +876,18 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
872
876
|
*/
|
|
873
877
|
detachMeshAssignment(): void {
|
|
874
878
|
if (!this.settings.meshNodeFor && !this.settings.meshActiveTaskId && !this.settings.meshNodeId) return;
|
|
875
|
-
// Session-level member: keep membership, drop only the task-level
|
|
879
|
+
// Session-level member: keep membership, drop only the task-level markers.
|
|
876
880
|
if (this.settings.launchedByCoordinator === true) {
|
|
877
881
|
if (!this.settings.meshActiveTaskId) return;
|
|
878
|
-
|
|
879
|
-
|
|
882
|
+
// REDRIVE-DUP: clear the task-level dispatch nonce with the task marker.
|
|
883
|
+
const { meshActiveTaskId, meshActiveDispatchNonce, ...rest } = this.settings;
|
|
884
|
+
void meshActiveTaskId; void meshActiveDispatchNonce;
|
|
880
885
|
this.settings = rest;
|
|
881
886
|
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
882
887
|
return;
|
|
883
888
|
}
|
|
884
|
-
const { meshNodeFor, meshNodeId, meshActiveTaskId, ...rest } = this.settings;
|
|
885
|
-
void meshNodeFor; void meshActiveTaskId;
|
|
889
|
+
const { meshNodeFor, meshNodeId, meshActiveTaskId, meshActiveDispatchNonce, ...rest } = this.settings;
|
|
890
|
+
void meshNodeFor; void meshActiveTaskId; void meshActiveDispatchNonce;
|
|
886
891
|
// WTCLAIM (A): clear the active binding but PRESERVE the last bound node id
|
|
887
892
|
// (meshLastNodeId) so a later sessionless dispatch can re-adopt this idle
|
|
888
893
|
// session ONLY for the node it last served. Carry the id being cleared, or
|
|
@@ -3189,6 +3194,12 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
3189
3194
|
const resolved = this.completingTurnTaskId();
|
|
3190
3195
|
if (resolved) enrichedEvent.taskId = resolved;
|
|
3191
3196
|
}
|
|
3197
|
+
// REDRIVE-DUP: echo the dispatch nonce this session's active task was stamped with
|
|
3198
|
+
// so the coordinator's generating_started handler can reject a stale (reclaimed)
|
|
3199
|
+
// dispatch and stop this worker before it double-executes the reclaimed task.
|
|
3200
|
+
if (enrichedEvent.dispatchNonce === undefined && typeof this.settings.meshActiveDispatchNonce === 'number') {
|
|
3201
|
+
enrichedEvent.dispatchNonce = this.settings.meshActiveDispatchNonce;
|
|
3202
|
+
}
|
|
3192
3203
|
}
|
|
3193
3204
|
if (this.context?.emitProviderEvent) {
|
|
3194
3205
|
this.context.emitProviderEvent(enrichedEvent);
|
|
@@ -312,7 +312,7 @@ export class ProviderInstanceManager {
|
|
|
312
312
|
* applied, or `{ stamped: false, reason }` when it was refused — the instance
|
|
313
313
|
* was missing / has no attach method, or the DOUBLE-DISPATCH idempotence guard
|
|
314
314
|
* fired (the same task is already running on another live session here). */
|
|
315
|
-
attachMeshAssignmentToInstance(instanceId: string, assignment: { meshId: string; nodeId?: string; taskId?: string; coordinatorDaemonId?: string; coordinatorSessionId?: string }): { stamped: boolean; reason?: string } {
|
|
315
|
+
attachMeshAssignmentToInstance(instanceId: string, assignment: { meshId: string; nodeId?: string; taskId?: string; dispatchNonce?: number; coordinatorDaemonId?: string; coordinatorSessionId?: string }): { stamped: boolean; reason?: string } {
|
|
316
316
|
const inst = this.instances.get(instanceId);
|
|
317
317
|
if (!inst || typeof inst.attachMeshAssignment !== 'function') {
|
|
318
318
|
LOG.warn('MeshDispatch', `attachMeshAssignment skipped: instance ${instanceId} ${inst ? 'has no attach method' : 'not found'}`);
|
|
@@ -220,7 +220,7 @@ export interface ProviderInstance {
|
|
|
220
220
|
/** Stamp a direct-dispatch mesh task assignment so generating_completed
|
|
221
221
|
* events route back to the originating coordinator. Cleared by
|
|
222
222
|
* detachMeshAssignment when the task reaches a terminal state. */
|
|
223
|
-
attachMeshAssignment?(assignment: { meshId: string; nodeId?: string; taskId?: string; coordinatorDaemonId?: string; coordinatorSessionId?: string }): void;
|
|
223
|
+
attachMeshAssignment?(assignment: { meshId: string; nodeId?: string; taskId?: string; dispatchNonce?: number; coordinatorDaemonId?: string; coordinatorSessionId?: string }): void;
|
|
224
224
|
detachMeshAssignment?(): void;
|
|
225
225
|
|
|
226
226
|
/** Refresh static provider definition/scripts without restarting the live runtime. */
|