@adhdev/daemon-core 1.0.28-rc.5 → 1.0.28-rc.6
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/config/mesh-config.d.ts +2 -0
- package/dist/git/git-commands.d.ts +1 -0
- package/dist/index.js +7150 -7079
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7153 -7082
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-completion-synthesis.d.ts +29 -0
- package/dist/mesh/mesh-node-identity.d.ts +2 -0
- package/dist/mesh/mesh-refine-gates.d.ts +32 -0
- package/dist/mesh/mesh-runtime-store.d.ts +1 -0
- package/dist/mesh/mesh-work-queue.d.ts +15 -0
- package/dist/mesh/node-facts.d.ts +17 -0
- package/dist/providers/cli-provider-instance.d.ts +30 -57
- package/dist/providers/transcript-evidence.d.ts +63 -0
- package/dist/repo-mesh-types.d.ts +23 -0
- package/package.json +5 -3
- package/src/cli-adapters/cli-state-engine.ts +4 -3
- package/src/commands/high-family/mesh-status.ts +5 -0
- package/src/commands/router-refine.ts +30 -0
- package/src/config/mesh-config.ts +7 -0
- package/src/git/git-commands.ts +11 -1
- package/src/mesh/mesh-completion-synthesis.ts +76 -50
- package/src/mesh/mesh-node-identity.ts +37 -3
- package/src/mesh/mesh-queue-assignment.ts +34 -0
- package/src/mesh/mesh-reconcile-loop.ts +62 -71
- package/src/mesh/mesh-refine-gates.ts +58 -0
- package/src/mesh/mesh-runtime-store.ts +9 -1
- package/src/mesh/mesh-work-queue.ts +20 -1
- package/src/mesh/node-facts.ts +54 -0
- package/src/providers/cli-provider-instance.ts +72 -152
- package/src/providers/transcript-evidence.ts +105 -0
- package/src/repo-mesh-types.ts +23 -0
|
@@ -11,6 +11,35 @@ export interface AssignedTaskTerminalEvidence {
|
|
|
11
11
|
nodeId?: string;
|
|
12
12
|
sessionId: string;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Purpose-tagged coordinator-side evidence query — the remote half of the
|
|
16
|
+
* transcript-authority choke point (P1). Existing callers keep the original
|
|
17
|
+
* poll* shells below; new consumers (the reconcile loop's early-arm / redrive
|
|
18
|
+
* sites in P3) should enter here so the purpose vocabulary — not another
|
|
19
|
+
* ad-hoc gate — expresses what is being asked of the worker transcript.
|
|
20
|
+
*/
|
|
21
|
+
export type AssignedTaskEvidencePurpose = 'turn-progress' | 'terminal-evidence';
|
|
22
|
+
export interface AssignedTaskCompletionEvidence {
|
|
23
|
+
purpose: AssignedTaskEvidencePurpose;
|
|
24
|
+
/** "Did the worker START this task's turn" — post-dispatch agent bubble. */
|
|
25
|
+
inTurnProgress: boolean;
|
|
26
|
+
/** "Did the worker FINISH this task's turn" — populated for 'terminal-evidence'. */
|
|
27
|
+
terminal: AssignedTaskTerminalEvidence | null;
|
|
28
|
+
}
|
|
29
|
+
export declare function resolveAssignedTaskCompletionEvidence(components: DaemonComponents, mesh: {
|
|
30
|
+
id: string;
|
|
31
|
+
nodes?: Array<{
|
|
32
|
+
id: string;
|
|
33
|
+
daemonId?: string;
|
|
34
|
+
workspace?: string;
|
|
35
|
+
}>;
|
|
36
|
+
}, row: {
|
|
37
|
+
id: string;
|
|
38
|
+
assignedSessionId?: string;
|
|
39
|
+
assignedNodeId?: string;
|
|
40
|
+
assignedProviderType?: string;
|
|
41
|
+
dispatchTimestamp?: string;
|
|
42
|
+
}, purpose: AssignedTaskEvidencePurpose): Promise<AssignedTaskCompletionEvidence>;
|
|
14
43
|
export declare function pollAssignedTaskInTurnProgress(components: DaemonComponents, mesh: {
|
|
15
44
|
id: string;
|
|
16
45
|
nodes?: Array<{
|
|
@@ -53,6 +53,7 @@ export declare function recordInlineMeshDirectGitTruth(node: any, git: Record<st
|
|
|
53
53
|
reporterProviderVersions: Record<string, string> | null;
|
|
54
54
|
reporterDaemonBuildVersion: string | null;
|
|
55
55
|
reportedMemberState: MeshReportedMemberState | null;
|
|
56
|
+
nodeFacts: import('@adhdev/mesh-shared').MeshNodeFacts | null;
|
|
56
57
|
};
|
|
57
58
|
/**
|
|
58
59
|
* Persist the live self-reported platform/arch onto the local meshes.json node
|
|
@@ -72,6 +73,7 @@ export declare function persistNodeReporterPlatform(meshSource: 'inline_cache' |
|
|
|
72
73
|
reporterProviderVersions?: Record<string, string> | null;
|
|
73
74
|
reporterDaemonBuildVersion?: string | null;
|
|
74
75
|
reportedMemberState?: MeshReportedMemberState | null;
|
|
76
|
+
nodeFacts?: import('@adhdev/mesh-shared').MeshNodeFacts | null;
|
|
75
77
|
}): void;
|
|
76
78
|
export declare function inlineMeshCarriesTransientNodeTruth(inlineMesh: any): boolean;
|
|
77
79
|
export declare function readInlineMeshNodeId(node: any): string;
|
|
@@ -504,6 +504,38 @@ type GitlinkTrivialFastForwardEvaluation = {
|
|
|
504
504
|
* gate.
|
|
505
505
|
*/
|
|
506
506
|
export declare function collectFastForwardGitlinkPaths(repoRoot: string, baseHead: string, branchHead: string): string[];
|
|
507
|
+
/**
|
|
508
|
+
* Collect gitlink resolutions for the *trivial fast-forward* case, so the
|
|
509
|
+
* gitlink-aware root rebase ({@link rootRebaseResolvingGitlinks}) can drive a
|
|
510
|
+
* behind>0 rebase whose changed submodule pointer would otherwise make a plain
|
|
511
|
+
* `git rebase baseHead` abort on the gitlink.
|
|
512
|
+
*
|
|
513
|
+
* When base has advanced the SAME submodule as the branch, git's recursive merge
|
|
514
|
+
* refuses to auto-merge the gitlink even when the two commits are in a strict
|
|
515
|
+
* ancestor/descendant relationship (a real fast-forward). That is fine for the
|
|
516
|
+
* patch-equivalence gate (it synthesizes the merged tree), but the sync_base
|
|
517
|
+
* *rebase* still runs `git rebase baseHead`, which stops on the same gitlink
|
|
518
|
+
* conflict and aborts → the branch is wrongly blocked. This helper produces the
|
|
519
|
+
* per-path resolution the root rebase needs so those paths take the gitlink-aware
|
|
520
|
+
* path instead of the plain rebase.
|
|
521
|
+
*
|
|
522
|
+
* Direction rule (kept consistent with the diverged path, which always resolves to
|
|
523
|
+
* the linear descendant): pick whichever of base/branch commit is the DESCENDANT
|
|
524
|
+
* of the other and resolve the gitlink to it — the more-advanced commit wins.
|
|
525
|
+
* - base ancestor-of branch → branch is more advanced → resolve to branch-side.
|
|
526
|
+
* - branch ancestor-of base → base is more advanced → resolve to base-side.
|
|
527
|
+
* - neither ancestor (diverged) or ambiguous → excluded (left to the diverged
|
|
528
|
+
* converge path / patch-equivalence gate).
|
|
529
|
+
*
|
|
530
|
+
* The base-side submodule commit is committed in the base workspace and may be
|
|
531
|
+
* missing from the worktree's submodule object store; a best-effort local fetch
|
|
532
|
+
* (identical to {@link convergeDivergedSubmoduleGitlinks}) brings it in so the
|
|
533
|
+
* ancestry checks and the root rebase's `checkout --detach` can see it.
|
|
534
|
+
*/
|
|
535
|
+
export declare function collectTrivialFastForwardGitlinkResolutions(worktreeRoot: string, baseRepoRoot: string, baseHead: string, branchHead: string): Array<{
|
|
536
|
+
path: string;
|
|
537
|
+
rebasedCommit: string;
|
|
538
|
+
}>;
|
|
507
539
|
export type SubmoduleGitlinkConvergeResult = {
|
|
508
540
|
/** True when at least one diverged gitlink submodule was rebased onto its base-side commit. */
|
|
509
541
|
converged: boolean;
|
|
@@ -112,6 +112,7 @@ export declare class MeshRuntimeStore {
|
|
|
112
112
|
providerType?: string;
|
|
113
113
|
providerMaxParallel?: number;
|
|
114
114
|
nodeIsWorktree?: boolean;
|
|
115
|
+
assignedTranscriptProfile?: MeshWorkQueueEntry['assignedTranscriptProfile'];
|
|
115
116
|
}): MeshWorkQueueEntry | null;
|
|
116
117
|
getQueueStatsByStatus(meshId: string): {
|
|
117
118
|
status: string;
|
|
@@ -151,6 +151,20 @@ export interface MeshWorkQueueEntry {
|
|
|
151
151
|
* by counting active assignments grouped by node + provider.
|
|
152
152
|
*/
|
|
153
153
|
assignedProviderType?: string;
|
|
154
|
+
/**
|
|
155
|
+
* Transcript-authority class of the claiming session's provider, stamped at
|
|
156
|
+
* claim time (P1 of the transcript-authority unification — root repo
|
|
157
|
+
* docs/design/2026-07-25-transcript-authority-unification.md). Lets the
|
|
158
|
+
* COORDINATOR side classify a remote worker (early-arm / redrive gates)
|
|
159
|
+
* without resolving the provider module locally — the structural fix for
|
|
160
|
+
* the "remote class unknowable → reprobe-only" blind spot. Absent on rows
|
|
161
|
+
* claimed by older daemons; consumers must fall back to local resolution.
|
|
162
|
+
*/
|
|
163
|
+
assignedTranscriptProfile?: {
|
|
164
|
+
class: 'native-source' | 'pure-pty' | 'daemon-owned';
|
|
165
|
+
timing: 'hold' | 'floor' | 'immediate';
|
|
166
|
+
emitsPtyTurnEvents: boolean;
|
|
167
|
+
};
|
|
154
168
|
/** Human/operator reason for terminal cancellation. */
|
|
155
169
|
cancelReason?: string;
|
|
156
170
|
cancelledAt?: string;
|
|
@@ -331,6 +345,7 @@ export declare function claimNextTask(meshId: string, nodeId: string, sessionId:
|
|
|
331
345
|
providerType?: string;
|
|
332
346
|
providerMaxParallel?: number;
|
|
333
347
|
nodeIsWorktree?: boolean;
|
|
348
|
+
assignedTranscriptProfile?: MeshWorkQueueEntry['assignedTranscriptProfile'];
|
|
334
349
|
}): MeshWorkQueueEntry | null;
|
|
335
350
|
export type DependencyFailurePolicy = 'block' | 'cancel';
|
|
336
351
|
/**
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* buildLocalNodeFacts — THE single producer of this daemon's MeshNodeFacts
|
|
3
|
+
* bundle (design: root repo docs/design/2026-07-25-deploy-lag-visibility.md).
|
|
4
|
+
*
|
|
5
|
+
* De-mirroring core: the reporter envelope (git-commands handleGitCommand)
|
|
6
|
+
* and the self/worktree node stamp (mesh-node-identity, whose local probe
|
|
7
|
+
* bypasses the envelope) BOTH call this one function, so a fact added here
|
|
8
|
+
* reaches self and remote nodes identically — the field-by-field dual-path
|
|
9
|
+
* plumbing that produced the mirror-defect class (version chip self-miss,
|
|
10
|
+
* slot-cap chip remote-miss, dead stale-build badge) cannot recur for bundle
|
|
11
|
+
* fields.
|
|
12
|
+
*/
|
|
13
|
+
import type { MeshNodeFacts } from '@adhdev/mesh-shared';
|
|
14
|
+
export declare function buildLocalNodeFacts(deps?: {
|
|
15
|
+
providerVersions?: Record<string, string> | null;
|
|
16
|
+
machineNickname?: string | null;
|
|
17
|
+
}): MeshNodeFacts;
|
|
@@ -620,60 +620,33 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
620
620
|
*/
|
|
621
621
|
flushMeshCompletionBeforeCleanup(): boolean;
|
|
622
622
|
/**
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
*
|
|
631
|
-
*
|
|
632
|
-
*
|
|
633
|
-
*
|
|
634
|
-
*
|
|
635
|
-
*
|
|
636
|
-
*
|
|
637
|
-
*
|
|
638
|
-
*
|
|
639
|
-
*
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
*
|
|
645
|
-
*
|
|
646
|
-
* stall
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
* KIMI-NATIVE-SOURCE-COMPLETION-EMIT (Fix 3): stall-path completion reconcile for a
|
|
651
|
-
* NATIVE-SOURCE provider (transcriptAuthority=provider + nativeHistory — e.g. kimi,
|
|
652
|
-
* whose authoritative transcript is ~/.kimi-code/.../wire.jsonl). Like the pure-PTY
|
|
653
|
-
* class, a native-source worker that submits a prompt while already idle can collapse
|
|
654
|
-
* idle→idle so agent:generating_completed never emits; its PTY then goes screen-quiet
|
|
655
|
-
* the instant the answer renders, and the status-agnostic no-progress watchdog
|
|
656
|
-
* (checkMeshWorkerStall) misreads that finished-but-static idle as a wedge and would
|
|
657
|
-
* false-fire monitor:no_progress. sampleNativeTranscriptProgress only re-arms while the
|
|
658
|
-
* transcript is STILL ADVANCING; once the turn is done the transcript is static and the
|
|
659
|
-
* sample stops rescuing — so the stall path needs this decisive completion check for the
|
|
660
|
-
* finished-and-quiet case. tryReconcilePurePtyCompletionForStall does NOT cover this
|
|
661
|
-
* class (isPurePtyTranscriptProvider is false for a native-source provider), so it runs
|
|
662
|
-
* as a fallback here.
|
|
663
|
-
*
|
|
664
|
-
* Mutually exclusive with the pure-PTY path by construction: a provider is either
|
|
665
|
-
* native-source (this method) or pure-PTY (that method), never both. Called from
|
|
666
|
-
* checkMeshWorkerStall only after tryReconcilePurePtyCompletionForStall returned false.
|
|
667
|
-
*
|
|
668
|
-
* Evidence bar is identical to the pure-PTY / pre-cleanup paths: idle with nothing
|
|
669
|
-
* pending, the injected task's turn genuinely started, and an in-turn final assistant
|
|
670
|
-
* summary — here resolved from the native transcript by completionFinalSummary (its
|
|
671
|
-
* native-source branch reads readExternalCompletionMessages). No summary ⇒ no proof of
|
|
672
|
-
* completion ⇒ return false and let the real stall fire so a genuinely-wedged worker is
|
|
673
|
-
* still surfaced. Idempotent: a real/late emit writes the terminal ledger and the
|
|
674
|
-
* coordinator's reconcile makes any duplicate a no-op.
|
|
675
|
-
*/
|
|
676
|
-
private tryReconcileNativeSourceCompletionForStall;
|
|
623
|
+
* TRANSCRIPT-COMPLETION-STALL-RESCUE (P2 of the transcript-authority
|
|
624
|
+
* unification — root repo docs/design/2026-07-25-transcript-authority-
|
|
625
|
+
* unification.md): stall-path completion reconcile for every class whose
|
|
626
|
+
* turns can finish without a generating_completed emit (idle→idle collapse)
|
|
627
|
+
* and then sit PTY-quiet at a static idle prompt.
|
|
628
|
+
*
|
|
629
|
+
* Historically this was TWO class-enumerated copies — KIMI-PURE-PTY-
|
|
630
|
+
* COMPLETION-EMIT (Fix 3) gated on isPurePtyTranscriptProvider, and
|
|
631
|
+
* KIMI-NATIVE-SOURCE-COMPLETION-EMIT gated on isNativeSourceCanonicalHistory
|
|
632
|
+
* — whose bodies were identical because completionFinalSummary already picks
|
|
633
|
+
* the class-appropriate evidence source (authoritative native transcript for
|
|
634
|
+
* a native-source provider, the PTY parse otherwise). The enumeration itself
|
|
635
|
+
* was the recurring defect: each new class had to be remembered at this site.
|
|
636
|
+
* The profile collapses it: any class except daemon-owned is eligible, and
|
|
637
|
+
* the evidence bar — not the class — decides.
|
|
638
|
+
*
|
|
639
|
+
* Called from checkMeshWorkerStall just before the fire. Returns true when
|
|
640
|
+
* the session is a finished turn — idle, nothing pending, the injected
|
|
641
|
+
* task's turn genuinely started, and an in-turn final assistant summary —
|
|
642
|
+
* in which case it emits the missing generating_completed (idempotent: a
|
|
643
|
+
* real/late emit writes the terminal ledger and the coordinator's reconcile
|
|
644
|
+
* makes any duplicate a no-op) and the caller SUPPRESSES the stall. Returns
|
|
645
|
+
* false for a daemon-owned provider and for a genuinely mid-turn / wedged
|
|
646
|
+
* worker with no in-turn final assistant, so a real stall still fires
|
|
647
|
+
* unchanged. Evidence bar is identical to flushMeshCompletionBeforeCleanup.
|
|
648
|
+
*/
|
|
649
|
+
private tryReconcileTranscriptCompletionForStall;
|
|
677
650
|
/**
|
|
678
651
|
* AUTOAPPROVE-FLAP-RECUR (Fix A+B): how long a busy blip / modal scroll-out may
|
|
679
652
|
* persist before the in-progress settle gate is torn down. For a delegated
|
|
@@ -769,9 +742,9 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
769
742
|
*/
|
|
770
743
|
private emitGeneratingCompleted;
|
|
771
744
|
/**
|
|
772
|
-
* COMPLETION-WEAK-REARM (fix1): the double-emit guard shared by the
|
|
773
|
-
* re-emit paths (flushMeshCompletionBeforeCleanup,
|
|
774
|
-
*
|
|
745
|
+
* COMPLETION-WEAK-REARM (fix1): the double-emit guard shared by the transcript
|
|
746
|
+
* re-emit paths (flushMeshCompletionBeforeCleanup,
|
|
747
|
+
* tryReconcileTranscriptCompletionForStall). Returns true when a re-emit for `taskId`
|
|
775
748
|
* must be SUPPRESSED because this turn's completion already fired with strong evidence.
|
|
776
749
|
*
|
|
777
750
|
* The defect this replaces: the old guard short-circuited on ANY prior emit for the
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript authority profile — the single place that classifies a provider
|
|
3
|
+
* for completion/stall/redrive decisions.
|
|
4
|
+
*
|
|
5
|
+
* Historically five predicates coexisted (transcriptAuthority, nativeHistory
|
|
6
|
+
* presence, adapter chatMessagesOwnedExternally, isPurePtyTranscriptProvider,
|
|
7
|
+
* and the two completion-timing flags) and every judgment site combined a
|
|
8
|
+
* different subset — each recurring "pure-PTY gate excludes native-source"
|
|
9
|
+
* defect (kimi reconcile, antigravity tail/guard, codex floor, STARTED
|
|
10
|
+
* redrive) was an instance of a site enumerating only some classes. This
|
|
11
|
+
* module makes the enumeration itself the shared artifact: sites consume a
|
|
12
|
+
* profile, never the raw predicates.
|
|
13
|
+
*
|
|
14
|
+
* Design: docs/design/2026-07-25-transcript-authority-unification.md (root
|
|
15
|
+
* repo). This file is Phase P0 (layer A — pure, synchronous classification).
|
|
16
|
+
* Layer B (the evidence-query choke point) lands in later phases.
|
|
17
|
+
*
|
|
18
|
+
* Rule for NEW code: call resolveTranscriptAuthorityProfile() — do not call
|
|
19
|
+
* isPurePtyTranscriptProvider / isNativeSourceCanonicalHistory or read the
|
|
20
|
+
* timing flags directly (annotate `// authority-ok: <why>` where a raw read
|
|
21
|
+
* is genuinely about something other than completion classification).
|
|
22
|
+
*/
|
|
23
|
+
import type { ProviderCanonicalHistoryConfig } from './contracts.js';
|
|
24
|
+
/** Where the authoritative transcript for completion evidence lives. */
|
|
25
|
+
export type TranscriptClass = 'native-source' | 'pure-pty' | 'daemon-owned';
|
|
26
|
+
/**
|
|
27
|
+
* When a completion may be emitted relative to the PTY idle verdict:
|
|
28
|
+
* - 'hold' — idle holds for the native transcript to land (antigravity).
|
|
29
|
+
* - 'floor' — idle without a final assistant holds under the CANON-C
|
|
30
|
+
* min-elapsed floor (codex / kimi / cursor / opencode).
|
|
31
|
+
* - 'immediate' — emit immediately; a write-lag native source upgrades the
|
|
32
|
+
* weak emit on reconcile (claude), daemon-owned emits plainly.
|
|
33
|
+
*/
|
|
34
|
+
export type CompletionTiming = 'hold' | 'floor' | 'immediate';
|
|
35
|
+
export interface TranscriptAuthorityProfile {
|
|
36
|
+
class: TranscriptClass;
|
|
37
|
+
timing: CompletionTiming;
|
|
38
|
+
/** transcriptAuthority === 'provider' — provider parser output is canonical. */
|
|
39
|
+
providerOwnsTranscript: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* False ⇒ this class may run whole turns without PTY generating_started /
|
|
42
|
+
* generating_completed events, so the ABSENCE of a turn-start event must
|
|
43
|
+
* never be read as "dispatch not consumed" (the STARTED-redrive rule,
|
|
44
|
+
* generalized). True only where PTY turn events are reliable.
|
|
45
|
+
*/
|
|
46
|
+
emitsPtyTurnEvents: boolean;
|
|
47
|
+
/** The provider's native history config when the class is native-source. */
|
|
48
|
+
nativeHistory?: ProviderCanonicalHistoryConfig;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Structural input — matches how judgment sites actually hold providers
|
|
52
|
+
* (CliProviderModule / ProviderModule / nullable this.provider). Null or
|
|
53
|
+
* undefined resolves to the conservative daemon-owned/immediate default,
|
|
54
|
+
* matching the established null-provider ⇒ not-pure-PTY contract.
|
|
55
|
+
*/
|
|
56
|
+
export interface TranscriptAuthorityInput {
|
|
57
|
+
transcriptAuthority?: 'provider' | 'daemon';
|
|
58
|
+
nativeHistory?: ProviderCanonicalHistoryConfig;
|
|
59
|
+
tui?: Record<string, unknown>;
|
|
60
|
+
requiresFinalAssistantBeforeIdle?: boolean;
|
|
61
|
+
holdCompletionForTranscript?: boolean;
|
|
62
|
+
}
|
|
63
|
+
export declare function resolveTranscriptAuthorityProfile(provider: TranscriptAuthorityInput | null | undefined): TranscriptAuthorityProfile;
|
|
@@ -793,6 +793,14 @@ export interface LocalMeshNodeEntry {
|
|
|
793
793
|
* first envelope carrying it is ingested.
|
|
794
794
|
*/
|
|
795
795
|
reportedMemberState?: MeshReportedMemberState;
|
|
796
|
+
/**
|
|
797
|
+
* Versioned per-machine runtime facts bundle (MeshNodeFacts — deploy-lag
|
|
798
|
+
* visibility design §a). Remote nodes: ingested wholesale from the
|
|
799
|
+
* git_status envelope's reporterNodeFacts. Self/worktree nodes: built by
|
|
800
|
+
* the SAME producer (buildLocalNodeFacts). Relayed opaquely — surfaces
|
|
801
|
+
* must pass the object through, never rebuild it field-by-field.
|
|
802
|
+
*/
|
|
803
|
+
nodeFacts?: import('@adhdev/mesh-shared').MeshNodeFacts;
|
|
796
804
|
/**
|
|
797
805
|
* The operator-set machine nickname (config.machineNickname) of the daemon
|
|
798
806
|
* that owns this node's workspace. The local coordinator stamps its own
|
|
@@ -904,6 +912,14 @@ export interface RepoMeshStatus {
|
|
|
904
912
|
* an optional `stats` operational rollup (durations / retries).
|
|
905
913
|
*/
|
|
906
914
|
missions?: (MeshMissionSummary | MeshMissionSlimSummary)[];
|
|
915
|
+
/**
|
|
916
|
+
* Preview-deploy freshness rollup (deploy-lag visibility): stamped by the
|
|
917
|
+
* dashboard mesh_status when the repo has a preview pipeline. Shape is
|
|
918
|
+
* daemon-defined (mesh/preview-freshness.ts) — `currentMainCommit` is the
|
|
919
|
+
* global deploy-lag anchor the dashboard compares nodeFacts.daemonBuild
|
|
920
|
+
* against. Omitted for repos without the pipeline and by older daemons.
|
|
921
|
+
*/
|
|
922
|
+
previewFreshness?: Record<string, unknown>;
|
|
907
923
|
/**
|
|
908
924
|
* MAGI cross-verification activity, reconstructed from the mesh ledger
|
|
909
925
|
* (magi_dispatched / magi_synthesis entries) and folded in so the dashboard's
|
|
@@ -1070,6 +1086,13 @@ export interface RepoMeshNodeStatus {
|
|
|
1070
1086
|
* (scope/isDaemonAffecting flags). Omitted when the build is current.
|
|
1071
1087
|
*/
|
|
1072
1088
|
staleDaemonBuild?: Record<string, unknown>;
|
|
1089
|
+
/**
|
|
1090
|
+
* Versioned per-machine runtime facts bundle (MeshNodeFacts). Opaque
|
|
1091
|
+
* pass-through from the node record — see the node-level field doc.
|
|
1092
|
+
* Carries the daemon build COMMIT (not just the version string), which is
|
|
1093
|
+
* the deploy-lag anchor the dashboard renders.
|
|
1094
|
+
*/
|
|
1095
|
+
nodeFacts?: import('@adhdev/mesh-shared').MeshNodeFacts;
|
|
1073
1096
|
error?: string;
|
|
1074
1097
|
}
|
|
1075
1098
|
export type RepoMeshQueueTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed' | 'cancelled';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "1.0.28-rc.
|
|
3
|
+
"version": "1.0.28-rc.6",
|
|
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",
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
"dev": "tsup --watch --no-clean",
|
|
31
31
|
"lint": "eslint \"src/mesh/**/*.ts\"",
|
|
32
32
|
"test": "vitest run",
|
|
33
|
+
"test:fast": "vitest run --config vitest.fast.config.mts",
|
|
34
|
+
"test:git": "vitest run --config vitest.git.config.mts",
|
|
33
35
|
"test:watch": "vitest",
|
|
34
36
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
35
37
|
},
|
|
@@ -47,8 +49,8 @@
|
|
|
47
49
|
"author": "vilmire",
|
|
48
50
|
"license": "AGPL-3.0-or-later",
|
|
49
51
|
"dependencies": {
|
|
50
|
-
"@adhdev/mesh-shared": "1.0.28-rc.
|
|
51
|
-
"@adhdev/session-host-core": "1.0.28-rc.
|
|
52
|
+
"@adhdev/mesh-shared": "1.0.28-rc.6",
|
|
53
|
+
"@adhdev/session-host-core": "1.0.28-rc.6",
|
|
52
54
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
53
55
|
"ajv": "^8.20.0",
|
|
54
56
|
"ajv-formats": "^3.0.1",
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
buildCliScreenSnapshot,
|
|
17
17
|
compactPromptText,
|
|
18
|
-
isPurePtyTranscriptProvider,
|
|
19
18
|
normalizePromptText,
|
|
20
19
|
promptLikelyVisible,
|
|
21
20
|
type CliChatMessage,
|
|
@@ -24,6 +23,7 @@ import {
|
|
|
24
23
|
type CliTraceEntry,
|
|
25
24
|
type ParsedSession,
|
|
26
25
|
} from './provider-cli-shared.js';
|
|
26
|
+
import { resolveTranscriptAuthorityProfile } from '../providers/transcript-evidence.js';
|
|
27
27
|
import type { CliScriptRunner } from './cli-script-runner.js';
|
|
28
28
|
|
|
29
29
|
// ─── Types ─────────────────────────────────────────────────────────────────
|
|
@@ -375,8 +375,9 @@ export class CliStateEngine {
|
|
|
375
375
|
// existing PTY-parsed final-assistant idle gate produce a real
|
|
376
376
|
// generating→idle completion edge. Same idle safety as above: applyIdle
|
|
377
377
|
// / finishResponse still own the actual idle transition.
|
|
378
|
-
const
|
|
379
|
-
|
|
378
|
+
const promoteProfile = resolveTranscriptAuthorityProfile(this.provider);
|
|
379
|
+
const promoteOnTurnStart = promoteProfile.providerOwnsTranscript
|
|
380
|
+
|| promoteProfile.class === 'pure-pty';
|
|
380
381
|
if (promoteOnTurnStart && this.currentStatus !== 'waiting_approval') {
|
|
381
382
|
this.setStatus('generating', 'turn_started');
|
|
382
383
|
this.callbacks.onStatusChange();
|
|
@@ -357,6 +357,11 @@ export const meshStatusHandlers: Record<string, HighFamilyHandler> = {
|
|
|
357
357
|
...(typeof node.reportedDaemonBuildVersion === 'string' && node.reportedDaemonBuildVersion
|
|
358
358
|
? { daemonBuildVersion: node.reportedDaemonBuildVersion }
|
|
359
359
|
: {}),
|
|
360
|
+
// Opaque pass-through of the versioned facts bundle —
|
|
361
|
+
// never rebuild it field-by-field (deploy-lag design §a).
|
|
362
|
+
...(node.nodeFacts && typeof node.nodeFacts === 'object'
|
|
363
|
+
? { nodeFacts: node.nodeFacts }
|
|
364
|
+
: {}),
|
|
360
365
|
providerPriority,
|
|
361
366
|
// ORCHESTRATION_NODE_SLOTS.md: surface the node's capability
|
|
362
367
|
// slots so the dashboard slot editor can read them. Only
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
RefineExecFileAsync,
|
|
37
37
|
RefineStageOutcome,
|
|
38
38
|
classifyPatchEquivalenceFailure,
|
|
39
|
+
collectTrivialFastForwardGitlinkResolutions,
|
|
39
40
|
convergeDivergedSubmoduleGitlinks,
|
|
40
41
|
recordMeshRefineStage,
|
|
41
42
|
resolveRefineryAutoPublishSubmoduleMainCommits,
|
|
@@ -626,6 +627,35 @@ export async function refineSyncBaseStage(self: DaemonCommandRouter, ctx: Refine
|
|
|
626
627
|
}
|
|
627
628
|
} catch { /* fail-open: on gate error, fall through to the rebase */ }
|
|
628
629
|
|
|
630
|
+
// TRIVIAL-FF GITLINK: the diverged path above only fills gitlinkResolutions
|
|
631
|
+
// when base and branch advanced the SAME submodule to NON-ff (sibling) commits.
|
|
632
|
+
// When the changed gitlink is instead a strict fast-forward (base advanced the
|
|
633
|
+
// submodule to an ancestor/descendant of the branch-side commit — the common
|
|
634
|
+
// case when a sibling branch already merged its oss bump), the pre-rebase gate
|
|
635
|
+
// reports NO submodule_conflict, so gitlinkResolutions stays empty and the plain
|
|
636
|
+
// `git rebase baseHead` below runs. That plain rebase still hits the same gitlink
|
|
637
|
+
// and aborts ("Recursive merging with submodules currently only supports trivial
|
|
638
|
+
// cases"), wrongly blocking the branch. So when behind>0 and any changed gitlink
|
|
639
|
+
// remains (and the diverged path did not already resolve them), collect the
|
|
640
|
+
// trivial-ff resolutions and take the gitlink-aware root rebase too. Direction is
|
|
641
|
+
// the same as the diverged rule: resolve to the more-advanced (descendant) commit.
|
|
642
|
+
if (gitlinkResolutions.length === 0) {
|
|
643
|
+
try {
|
|
644
|
+
const ffResolutions = collectTrivialFastForwardGitlinkResolutions(
|
|
645
|
+
node.workspace, repoRoot, baseHead, branchHead,
|
|
646
|
+
);
|
|
647
|
+
if (ffResolutions.length > 0) {
|
|
648
|
+
gitlinkResolutions = ffResolutions;
|
|
649
|
+
recordMeshRefineStage(refineStages, 'submodule_gitlink_converge', 'passed', syncStarted, {
|
|
650
|
+
reason: 'submodule_trivial_ff_gitlink_aware_rebase',
|
|
651
|
+
gitlinks: ffResolutions.map(r => ({ path: r.path, rebasedCommit: r.rebasedCommit })),
|
|
652
|
+
});
|
|
653
|
+
LOG.info('Mesh', `[Refinery] Trivial fast-forward submodule gitlink(s) for node ${node.id} — using gitlink-aware rebase: `
|
|
654
|
+
+ ffResolutions.map(r => `${r.path}→${r.rebasedCommit.slice(0, 12)}`).join(', '));
|
|
655
|
+
}
|
|
656
|
+
} catch { /* fail-open: on collection error, fall through to the plain rebase */ }
|
|
657
|
+
}
|
|
658
|
+
|
|
629
659
|
// behind>0: strictly-behind OR diverged. Rebase the branch onto the pinned
|
|
630
660
|
// baseHead. A conflict aborts and terminates blocked_review (retryable=false —
|
|
631
661
|
// a real content conflict needs human resolution, not a base-movement retry).
|
|
@@ -651,6 +651,8 @@ export function updateNode(
|
|
|
651
651
|
* Slots are NOT carried — they are coordinator-owned config
|
|
652
652
|
* (REMOTE-NODE-SLOTS-COORDINATOR-LOCAL fix). */
|
|
653
653
|
reportedMemberState?: MeshReportedMemberState;
|
|
654
|
+
/** Versioned runtime-facts bundle — whole-object replace, opaque. */
|
|
655
|
+
nodeFacts?: import('@adhdev/mesh-shared').MeshNodeFacts;
|
|
654
656
|
},
|
|
655
657
|
): LocalMeshNodeEntry | undefined {
|
|
656
658
|
const config = loadMeshConfig();
|
|
@@ -676,6 +678,11 @@ export function updateNode(
|
|
|
676
678
|
// fields). normalizeReportedMemberState upstream guarantees a clean shape.
|
|
677
679
|
node.reportedMemberState = opts.reportedMemberState;
|
|
678
680
|
}
|
|
681
|
+
if (opts.nodeFacts) {
|
|
682
|
+
// Versioned runtime-facts bundle — whole-object replace, OPAQUE (unknown
|
|
683
|
+
// future fields persist untouched; deploy-lag design §a).
|
|
684
|
+
node.nodeFacts = opts.nodeFacts;
|
|
685
|
+
}
|
|
679
686
|
if (opts.policy) node.policy = { ...node.policy, ...opts.policy };
|
|
680
687
|
if (Object.prototype.hasOwnProperty.call(opts, 'capabilities')) {
|
|
681
688
|
// Explicit replace: normalize (trim/dedup/drop-empties); an empty result
|
package/src/git/git-commands.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { getGitDiffSummary, getGitFileDiff } from './git-diff.js';
|
|
|
4
4
|
import { GitCommandError, isPathInside, resolveGitRepository, runGit } from './git-executor.js';
|
|
5
5
|
import { createGitSnapshotStore } from './git-snapshot-store.js';
|
|
6
6
|
import { getGitRepoStatus } from './git-status.js';
|
|
7
|
+
import { buildLocalNodeFacts } from '../mesh/node-facts.js';
|
|
7
8
|
import { loadConfig } from '../config/config.js';
|
|
8
9
|
import type {
|
|
9
10
|
GitCommandName,
|
|
@@ -133,7 +134,7 @@ type GitCommandSuccess =
|
|
|
133
134
|
// (REMOTE-NODE-SLOTS-COORDINATOR-LOCAL fix). The legacy flat
|
|
134
135
|
// reporterProviderVersions/reporterDaemonBuildVersion fields are ALSO emitted for
|
|
135
136
|
// back-compat during a mixed-version-mesh rollout.
|
|
136
|
-
| { success: true; status: GitRepoStatus; reporterPlatform?: string; reporterArch?: string; reporterMachineNickname?: string; reporterProviderVersions?: Record<string, string>; reporterDaemonBuildVersion?: string; reporterMemberState?: { providerVersions?: Record<string, string>; daemonBuildVersion?: string; lastReportedAt?: number } }
|
|
137
|
+
| { success: true; status: GitRepoStatus; reporterPlatform?: string; reporterArch?: string; reporterMachineNickname?: string; reporterProviderVersions?: Record<string, string>; reporterDaemonBuildVersion?: string; reporterMemberState?: { providerVersions?: Record<string, string>; daemonBuildVersion?: string; lastReportedAt?: number }; reporterNodeFacts?: import('@adhdev/mesh-shared').MeshNodeFacts }
|
|
137
138
|
| { success: true; diffSummary: GitDiffSummary }
|
|
138
139
|
| { success: true; diff: GitFileDiff }
|
|
139
140
|
| { success: true; snapshot: GitSnapshot }
|
|
@@ -382,6 +383,14 @@ export async function handleGitCommand(
|
|
|
382
383
|
lastReportedAt: Date.now(),
|
|
383
384
|
}
|
|
384
385
|
: undefined;
|
|
386
|
+
// Versioned facts bundle (deploy-lag visibility design §a): built by the
|
|
387
|
+
// SAME buildLocalNodeFacts the self-node stamp uses, relayed opaquely —
|
|
388
|
+
// never rebuild it field-by-field downstream. Legacy flat fields and
|
|
389
|
+
// reporterMemberState ride alongside for mixed-version meshes.
|
|
390
|
+
const reporterNodeFacts = buildLocalNodeFacts({
|
|
391
|
+
providerVersions: reporterProviderVersions ?? null,
|
|
392
|
+
machineNickname: reporterMachineNickname ?? null,
|
|
393
|
+
});
|
|
385
394
|
return {
|
|
386
395
|
success: true,
|
|
387
396
|
status,
|
|
@@ -391,6 +400,7 @@ export async function handleGitCommand(
|
|
|
391
400
|
...(reporterProviderVersions ? { reporterProviderVersions } : {}),
|
|
392
401
|
...(reporterDaemonBuildVersion ? { reporterDaemonBuildVersion } : {}),
|
|
393
402
|
...(reporterMemberState ? { reporterMemberState } : {}),
|
|
403
|
+
reporterNodeFacts,
|
|
394
404
|
};
|
|
395
405
|
}
|
|
396
406
|
|