@adhdev/daemon-core 1.0.28-rc.10 → 1.0.28-rc.11
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/cli-adapter-types.d.ts +7 -0
- package/dist/commands/upgrade-helper.d.ts +14 -0
- package/dist/index.js +519 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +519 -27
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +16 -0
- package/dist/providers/spec/cli-adapter.d.ts +7 -0
- package/dist/providers/spec/fsm-driver.d.ts +35 -0
- package/dist/providers/spec/fsm-evaluator.d.ts +28 -2
- package/dist/providers/spec/fsm-types.d.ts +19 -1
- package/dist/providers/spec/signal-envelope.d.ts +59 -0
- package/dist/providers/transcript-signal-source.d.ts +59 -0
- package/package.json +3 -3
- package/src/cli-adapter-types.ts +7 -0
- package/src/commands/low-family/daemon-lifecycle.ts +41 -0
- package/src/commands/med-family/mesh-restart.ts +233 -30
- package/src/commands/upgrade-helper.ts +34 -3
- package/src/providers/cli-provider-instance.ts +46 -0
- package/src/providers/spec/cli-adapter.ts +10 -0
- package/src/providers/spec/fsm-driver.ts +62 -1
- package/src/providers/spec/fsm-evaluator.ts +110 -5
- package/src/providers/spec/fsm-loader.ts +7 -0
- package/src/providers/spec/fsm-types.ts +20 -0
- package/src/providers/spec/signal-envelope.ts +136 -0
- package/src/providers/transcript-signal-source.ts +171 -0
|
@@ -413,6 +413,10 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
413
413
|
private completedDebounceTimer;
|
|
414
414
|
private completedDebouncePending;
|
|
415
415
|
private lastExternalCompletionProbe;
|
|
416
|
+
/** TX-FSM Stage 0 (shadow): lazily-created transcript signal normalizer.
|
|
417
|
+
* Fed ONLY by transcript reads this instance already performs — it adds
|
|
418
|
+
* zero I/O and its output never feeds back into any verdict. */
|
|
419
|
+
private transcriptSignalSource;
|
|
416
420
|
/**
|
|
417
421
|
* The final assistant summary of the last completed turn, cached at
|
|
418
422
|
* completion-emit time. For a native-source provider (antigravity) whose
|
|
@@ -448,6 +452,18 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
448
452
|
*/
|
|
449
453
|
private spawnedEnvOverrides;
|
|
450
454
|
private readExternalCompletionMessages;
|
|
455
|
+
/**
|
|
456
|
+
* TX-FSM Stage 0 (shadow): normalize the transcript read that JUST
|
|
457
|
+
* happened into a SignalSnapshot and inject it into the FSM driver as a
|
|
458
|
+
* pure observation (daemon → SpecCliAdapter → FsmDriver). Fed ONLY by
|
|
459
|
+
* reads this method's caller already performs — it adds zero I/O, so the
|
|
460
|
+
* getState() zero-native-read invariant and the stall-path read cadence
|
|
461
|
+
* are untouched. The snapshot is shadow data: nothing on this path feeds
|
|
462
|
+
* back into completion/stall/redrive verdicts. Fail-open end to end — a
|
|
463
|
+
* missing adapter hook (non-spec providers), an unresolved transcript, or
|
|
464
|
+
* any throw degrades to "no observation", never to a wedge.
|
|
465
|
+
*/
|
|
466
|
+
private publishTranscriptSignalObservation;
|
|
451
467
|
/**
|
|
452
468
|
* The content of the LAST visible assistant bubble in a message list, or ''
|
|
453
469
|
* when the tail is not an assistant reply. Skips trailing system/tool/activity
|
|
@@ -223,6 +223,13 @@ export declare class SpecCliAdapter implements CliAdapter {
|
|
|
223
223
|
getRuntimeMetadata(): unknown;
|
|
224
224
|
updateRuntimeMeta(meta?: Record<string, unknown>): void;
|
|
225
225
|
refreshProviderDefinition(): void;
|
|
226
|
+
/**
|
|
227
|
+
* TX-FSM Stage 0 (shadow): forward the daemon's normalized signal
|
|
228
|
+
* observation into the FSM driver. Observation-only — failures here must
|
|
229
|
+
* never break the adapter, and the driver treats the envelope as a pure
|
|
230
|
+
* injected value (no reads cross the engine boundary).
|
|
231
|
+
*/
|
|
232
|
+
setSignalObservation(snapshot: import('./signal-envelope.js').SignalSnapshot | null): void;
|
|
226
233
|
private handleEvent;
|
|
227
234
|
private detectInteractivePromptFromPtyChunk;
|
|
228
235
|
private readCurrentScreenSections;
|
|
@@ -2,6 +2,7 @@ import { type SpecPtyEvent } from './adapter.js';
|
|
|
2
2
|
import type { PtyTransportFactory } from '../../cli-adapters/pty-transport.js';
|
|
3
3
|
import { type TraceEntry } from './evaluator.js';
|
|
4
4
|
import { type TransitionEval } from './fsm-evaluator.js';
|
|
5
|
+
import type { SignalSnapshot } from './signal-envelope.js';
|
|
5
6
|
import { chunkPreservingSurrogates as chunkPreservingSurrogatesShared } from '../../cli-adapters/pty-write-chunking.js';
|
|
6
7
|
export type DashboardEvent = {
|
|
7
8
|
kind: 'pty_data';
|
|
@@ -159,6 +160,14 @@ export interface ISpecDriver {
|
|
|
159
160
|
getFsmDebug?(): unknown;
|
|
160
161
|
getFsmSnapshotHistory?(): ReadonlyArray<FsmSnapshotEntry>;
|
|
161
162
|
getEventTimeline?(limit?: number): ReadonlyArray<SpecPtyEvent>;
|
|
163
|
+
/**
|
|
164
|
+
* TX-FSM Stage 0 (shadow): inject the daemon-normalized signal observation.
|
|
165
|
+
* Observation ONLY — the driver receives the envelope, never a reader; all
|
|
166
|
+
* file discovery / session pinning / parsing stays daemon-side so the FSM
|
|
167
|
+
* engine remains a generic PTY engine. The snapshot feeds the shadow
|
|
168
|
+
* verdict of `signal` conditions exclusively; it cannot gate a transition.
|
|
169
|
+
*/
|
|
170
|
+
setSignalObservation?(snapshot: SignalSnapshot | null): void;
|
|
162
171
|
}
|
|
163
172
|
export interface SpecDriverOpts {
|
|
164
173
|
specPath: string;
|
|
@@ -244,6 +253,15 @@ export declare class FsmDriver implements ISpecDriver {
|
|
|
244
253
|
* transition — the rich pre-transition table that lastFsmEval only keeps
|
|
245
254
|
* for the single most recent evaluation. Separate from stateHistory. */
|
|
246
255
|
private fsmSnapshotHistory;
|
|
256
|
+
/** TX-FSM Stage 0 (shadow): the latest daemon-injected signal observation.
|
|
257
|
+
* Read by evalFsmNow for the shadow verdict of `signal` conditions ONLY —
|
|
258
|
+
* the Stage-0 pass-through in the evaluator means it can never alter
|
|
259
|
+
* which transition fires. */
|
|
260
|
+
private signalObservation;
|
|
261
|
+
/** Per-transition last-logged shadow divergence (`${from}→${to}` → whether
|
|
262
|
+
* the shadow verdict currently disagrees with the real one), so the
|
|
263
|
+
* shadow log emits on FLIP only, not every frame. */
|
|
264
|
+
private shadowDivergenceLast;
|
|
247
265
|
constructor(opts: SpecDriverOpts);
|
|
248
266
|
subscribe(listener: (ev: DashboardEvent) => void): () => void;
|
|
249
267
|
start(): void;
|
|
@@ -259,6 +277,14 @@ export declare class FsmDriver implements ISpecDriver {
|
|
|
259
277
|
* the session registry. Not a DashboardCommand — this is a control-plane
|
|
260
278
|
* update, not user input. */
|
|
261
279
|
updateMeta(meta: Record<string, unknown>, replace?: boolean): void;
|
|
280
|
+
/**
|
|
281
|
+
* TX-FSM Stage 0 (shadow): receive the daemon-normalized signal
|
|
282
|
+
* observation. The driver stores it verbatim — it does NOT poll, parse,
|
|
283
|
+
* or read anything itself (generic-PTY-engine boundary). The observation
|
|
284
|
+
* only feeds the shadow verdict of `signal` conditions; the evaluator's
|
|
285
|
+
* Stage-0 pass-through guarantees it cannot change a transition.
|
|
286
|
+
*/
|
|
287
|
+
setSignalObservation(snapshot: SignalSnapshot | null): void;
|
|
262
288
|
snapshot(): string;
|
|
263
289
|
getCursorPosition(): {
|
|
264
290
|
row: number;
|
|
@@ -319,6 +345,15 @@ export declare class FsmDriver implements ISpecDriver {
|
|
|
319
345
|
private emitInitialState;
|
|
320
346
|
private buildClock;
|
|
321
347
|
private evalFsmNow;
|
|
348
|
+
/**
|
|
349
|
+
* TX-FSM Stage 0 (shadow): compare each signal-guarded transition's
|
|
350
|
+
* counterfactual verdict against the real (PTY-only) one and log on FLIP.
|
|
351
|
+
* This is the "만약 적용했다면" half of the shadow log — the Stage 1-3
|
|
352
|
+
* evidence for whether signal gating would have changed any verdict, and
|
|
353
|
+
* in which direction. Read-only: runs after the evaluation is complete
|
|
354
|
+
* and never feeds back into it.
|
|
355
|
+
*/
|
|
356
|
+
private logShadowDivergence;
|
|
322
357
|
private reevaluate;
|
|
323
358
|
private commitTransition;
|
|
324
359
|
/** Snapshot the full FSM evaluation that produced a transition into the
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SectionDef } from './types.js';
|
|
2
2
|
import { type ResolvedSection, type TraceEntry } from './evaluator.js';
|
|
3
3
|
import { type CliSpecV4, type FsmCondition } from './fsm-types.js';
|
|
4
|
+
import { type SignalSnapshot } from './signal-envelope.js';
|
|
4
5
|
export interface FsmClock {
|
|
5
6
|
/** Wall-clock now (ms). Passed in so the evaluator stays pure. */
|
|
6
7
|
now: number;
|
|
@@ -14,7 +15,7 @@ export interface FsmClock {
|
|
|
14
15
|
}
|
|
15
16
|
/** Per-condition evaluation detail — the debugging payload. */
|
|
16
17
|
export interface CondResult {
|
|
17
|
-
kind: 'regex' | 'changed' | 'elapsed' | 'stable' | 'all' | 'any' | 'not';
|
|
18
|
+
kind: 'regex' | 'changed' | 'elapsed' | 'stable' | 'signal' | 'all' | 'any' | 'not';
|
|
18
19
|
result: boolean;
|
|
19
20
|
detail: string;
|
|
20
21
|
/** Remaining ms until a time-based condition would flip to true. 0 if
|
|
@@ -24,6 +25,17 @@ export interface CondResult {
|
|
|
24
25
|
* snapshot shows WHAT text the rule fired on — not just which regex. Never
|
|
25
26
|
* read by the FSM; purely for the Spec Debug Snapshot. */
|
|
26
27
|
matchedText?: string;
|
|
28
|
+
/** TX-FSM Stage 0 (shadow): present only on `signal` leaves. `result` above
|
|
29
|
+
* is ALWAYS true for a signal leaf (Stage-0 pass-through — the leaf never
|
|
30
|
+
* gates a transition); this payload records what the leaf WOULD have
|
|
31
|
+
* decided against the injected snapshot. shadowResult null = the signal
|
|
32
|
+
* was unavailable/unknown (fail-open). */
|
|
33
|
+
signal?: {
|
|
34
|
+
name: string;
|
|
35
|
+
available: boolean;
|
|
36
|
+
value: boolean | null;
|
|
37
|
+
shadowResult: boolean | null;
|
|
38
|
+
};
|
|
27
39
|
children?: CondResult[];
|
|
28
40
|
}
|
|
29
41
|
/** One evaluated transition with its full reasoning. */
|
|
@@ -42,6 +54,20 @@ export interface TransitionEval {
|
|
|
42
54
|
/** Overall: would this transition fire? */
|
|
43
55
|
fires: boolean;
|
|
44
56
|
priority: number;
|
|
57
|
+
/**
|
|
58
|
+
* TX-FSM Stage 0 (shadow): present only when the guard contains at least
|
|
59
|
+
* one `signal` leaf. Records the verdict the transition WOULD have
|
|
60
|
+
* produced if signal leaves gated (condResult/fires), so the shadow log
|
|
61
|
+
* can compare it against the real PTY-only outcome. `unknown` = at least
|
|
62
|
+
* one signal leaf had no observation (fail-open); the shadow verdict is
|
|
63
|
+
* then informational, not a counterfactual proof. NEVER consumed by the
|
|
64
|
+
* driver for any transition decision — log/debug surface only.
|
|
65
|
+
*/
|
|
66
|
+
shadow?: {
|
|
67
|
+
condResult: boolean;
|
|
68
|
+
fires: boolean;
|
|
69
|
+
unknown: boolean;
|
|
70
|
+
};
|
|
45
71
|
}
|
|
46
72
|
export interface FsmEvaluation {
|
|
47
73
|
/** Sections resolved from the screen (shared with v3 shape). */
|
|
@@ -79,7 +105,7 @@ export declare function stableRegionKey(cond: {
|
|
|
79
105
|
export declare function evaluateFsm(spec: CliSpecV4, currentStateId: string, screenText: string, cursor: {
|
|
80
106
|
row: number;
|
|
81
107
|
col: number;
|
|
82
|
-
} | undefined, prevLines: string[] | undefined, clock: FsmClock): FsmEvaluation;
|
|
108
|
+
} | undefined, prevLines: string[] | undefined, clock: FsmClock, signalSnapshot?: SignalSnapshot | null): FsmEvaluation;
|
|
83
109
|
/**
|
|
84
110
|
* Evaluate a single condition against a screen + section map — for the spec
|
|
85
111
|
* editor's live preview ("does this regex match the current screen right
|
|
@@ -49,7 +49,25 @@ export interface FsmAnyCondition {
|
|
|
49
49
|
export interface FsmNotCondition {
|
|
50
50
|
not: FsmCondition;
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
/**
|
|
53
|
+
* TX-FSM Stage 0 (shadow) — a daemon-signal leaf. References a NORMALIZED
|
|
54
|
+
* signal name from the SignalSnapshot envelope (never a provider name), so a
|
|
55
|
+
* spec can combine transcript evidence with PTY conditions via all/any/not:
|
|
56
|
+
*
|
|
57
|
+
* { all: [ { matches: "…" }, { signal: "final_assistant_present" } ] }
|
|
58
|
+
*
|
|
59
|
+
* STAGE-0 SEMANTICS: evaluated for the shadow log only. The leaf NEVER gates
|
|
60
|
+
* a transition — the evaluator treats it as pass-through for the real verdict
|
|
61
|
+
* and records what it WOULD have decided (TransitionEval.shadow). A missing
|
|
62
|
+
* or unavailable signal fails open. Promotion to a real gate is Stage 1+.
|
|
63
|
+
*/
|
|
64
|
+
export interface FsmSignalCondition {
|
|
65
|
+
/** Normalized signal name (see SIGNAL_NAMES in signal-envelope.ts). */
|
|
66
|
+
signal: string;
|
|
67
|
+
/** Expected value; default true. */
|
|
68
|
+
equals?: boolean;
|
|
69
|
+
}
|
|
70
|
+
export type FsmCondition = RegexCondition | ChangedCondition | ElapsedCondition | StableCondition | FsmSignalCondition | FsmAllCondition | FsmAnyCondition | FsmNotCondition;
|
|
53
71
|
export interface FsmState {
|
|
54
72
|
id: string;
|
|
55
73
|
label: string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { TranscriptClass, CompletionTiming } from '../transcript-evidence.js';
|
|
2
|
+
export declare const SIGNAL_SNAPSHOT_KIND: "adhdev:fsm/signal-snapshot@0";
|
|
3
|
+
/** The normalized signal vocabulary. Stage 0 keeps this deliberately small —
|
|
4
|
+
* every name must be derivable from the FLATTENED native-transcript pipeline
|
|
5
|
+
* (which drops tool_use/tool_result correlation ids — that raw-JSONL read is
|
|
6
|
+
* Stage 4 territory and must NOT be added here). */
|
|
7
|
+
export type SignalName = 'final_assistant_present' | 'in_turn_progress' | 'transcript_growing';
|
|
8
|
+
export declare const SIGNAL_NAMES: readonly SignalName[];
|
|
9
|
+
export type SignalUnavailableReason = 'no_native_source' | 'unresolved' | 'error';
|
|
10
|
+
/** The normalized, provider-agnostic observation the daemon hands the FSM. */
|
|
11
|
+
export interface SignalSnapshot {
|
|
12
|
+
kind: typeof SIGNAL_SNAPSHOT_KIND;
|
|
13
|
+
/** Wall-clock time the underlying transcript read happened (ms). */
|
|
14
|
+
sampledAt: number;
|
|
15
|
+
/** False ⇒ every signal is null and conditions fail open. */
|
|
16
|
+
available: boolean;
|
|
17
|
+
unavailableReason?: SignalUnavailableReason;
|
|
18
|
+
/** The authority profile class/timing this snapshot was produced under
|
|
19
|
+
* (via resolveTranscriptAuthorityProfile — never raw predicates). */
|
|
20
|
+
profile?: {
|
|
21
|
+
class: TranscriptClass;
|
|
22
|
+
timing: CompletionTiming;
|
|
23
|
+
};
|
|
24
|
+
/** Normalized signals; null = unknown (fail-open). */
|
|
25
|
+
signals: Record<SignalName, boolean | null>;
|
|
26
|
+
/** Raw sampling context for the shadow log / Stage 1-3 analysis. */
|
|
27
|
+
detail: {
|
|
28
|
+
msgCount: number;
|
|
29
|
+
sourceMtimeMs: number;
|
|
30
|
+
/** now - sourceMtimeMs; null when the mtime is unknown (0). */
|
|
31
|
+
ageMs: number | null;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** The fail-open snapshot: no usable observation, every signal null. */
|
|
35
|
+
export declare function unavailableSignalSnapshot(now: number, reason: SignalUnavailableReason, profile?: {
|
|
36
|
+
class: TranscriptClass;
|
|
37
|
+
timing: CompletionTiming;
|
|
38
|
+
}): SignalSnapshot;
|
|
39
|
+
/** The spec's `signal` leaf: `{ signal: 'transcript_growing', equals?: boolean }`. */
|
|
40
|
+
export interface SignalLeaf {
|
|
41
|
+
signal: string;
|
|
42
|
+
equals?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface SignalLeafEval {
|
|
45
|
+
/** The normalized signal value read from the snapshot; null = unknown. */
|
|
46
|
+
value: boolean | null;
|
|
47
|
+
/** What the leaf WOULD evaluate to if signals gated transitions; null =
|
|
48
|
+
* unknown (no snapshot / signal missing → fail-open). */
|
|
49
|
+
shadowResult: boolean | null;
|
|
50
|
+
/** Human-readable shadow-log fragment. */
|
|
51
|
+
detail: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Evaluate one `signal` leaf against the injected snapshot. This computes the
|
|
55
|
+
* SHADOW verdict only — the caller (fsm-evaluator) owns the Stage-0
|
|
56
|
+
* pass-through rule that keeps the real result true regardless of what this
|
|
57
|
+
* returns.
|
|
58
|
+
*/
|
|
59
|
+
export declare function evaluateSignalLeaf(cond: SignalLeaf, snapshot: SignalSnapshot | null | undefined): SignalLeafEval;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { TranscriptAuthorityProfile } from './transcript-evidence.js';
|
|
2
|
+
import { type SignalSnapshot } from './spec/signal-envelope.js';
|
|
3
|
+
/** One daemon transcript read, normalized for the source. */
|
|
4
|
+
export interface TranscriptSignalSample {
|
|
5
|
+
/** Parsed native-transcript messages, or null when the read failed to
|
|
6
|
+
* resolve a source this time. */
|
|
7
|
+
messages: unknown[] | null;
|
|
8
|
+
/** The probe metadata the read refreshed (sourcePath/mtime/msgCount), or
|
|
9
|
+
* null when no source is bound. A null sourceMtimeMs is treated as "no
|
|
10
|
+
* freshness evidence" (same contract as the growth-hold path). */
|
|
11
|
+
probe: {
|
|
12
|
+
msgCount: number;
|
|
13
|
+
sourceMtimeMs: number | null;
|
|
14
|
+
sourcePath?: string | null;
|
|
15
|
+
} | null;
|
|
16
|
+
/** True when the read threw — logged distinctly from a clean miss. */
|
|
17
|
+
error?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface TranscriptSignalSourceOpts {
|
|
20
|
+
/** Log tag (provider type / session label) — never used for branching. */
|
|
21
|
+
label: string;
|
|
22
|
+
/** The P0 choke-point profile for this provider. */
|
|
23
|
+
profile: TranscriptAuthorityProfile;
|
|
24
|
+
/** Turn-boundary clock (adapter.currentTurnStartedAt) for turn-scoped
|
|
25
|
+
* final-assistant detection; may yield undefined pre-turn. */
|
|
26
|
+
turnStartedAt?: () => number | undefined;
|
|
27
|
+
/** In-turn final-assistant predicate over the flattened messages — the
|
|
28
|
+
* instance passes its existing completionHasFinalAssistantMessage so the
|
|
29
|
+
* signal reuses the exact completion machinery instead of duplicating it. */
|
|
30
|
+
finalAssistantPresent: (messages: unknown[], turnStartedAt?: number) => boolean;
|
|
31
|
+
/** Freshness window (ms) for transcript_growing. The caller passes the
|
|
32
|
+
* completion pipeline's MISSING_ASSISTANT_TRANSCRIPT_GROWTH_QUIET_MS so
|
|
33
|
+
* the shadow signal is comparable to the existing growth-hold judgment. */
|
|
34
|
+
growthQuietMs: number;
|
|
35
|
+
}
|
|
36
|
+
export declare class TranscriptSignalSource {
|
|
37
|
+
private readonly opts;
|
|
38
|
+
/** msgCount seen at the previous update — drives in_turn_progress. */
|
|
39
|
+
private prevMsgCount;
|
|
40
|
+
/** Fingerprint of the last LOGGED snapshot, so the shadow log fires on
|
|
41
|
+
* change only (a quiet session must not spam one line per read). */
|
|
42
|
+
private lastLoggedFingerprint;
|
|
43
|
+
constructor(opts: TranscriptSignalSourceOpts);
|
|
44
|
+
/**
|
|
45
|
+
* Normalize one daemon read into a SignalSnapshot and emit the Stage-0
|
|
46
|
+
* shadow log when the observation changed. Pure w.r.t. the outside world:
|
|
47
|
+
* the only side effect is the (change-gated) log line. Returns the
|
|
48
|
+
* snapshot the caller should inject into the FSM driver.
|
|
49
|
+
*/
|
|
50
|
+
update(sample: TranscriptSignalSample, now?: number): SignalSnapshot;
|
|
51
|
+
/** Pure normalization — separated from update() so tests can assert the
|
|
52
|
+
* envelope without touching the log path. */
|
|
53
|
+
buildSnapshot(sample: TranscriptSignalSample, now: number): SignalSnapshot;
|
|
54
|
+
/** Stage-0 shadow log: emit one line when the normalized observation
|
|
55
|
+
* CHANGES. This log is the Stage 1-3 judgment input ("which signals were
|
|
56
|
+
* actually observable, and when"), so it carries the full signal set —
|
|
57
|
+
* but only on change, so a steady-state session stays quiet. */
|
|
58
|
+
private logOnChange;
|
|
59
|
+
}
|
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.11",
|
|
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",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"author": "vilmire",
|
|
50
50
|
"license": "AGPL-3.0-or-later",
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@adhdev/mesh-shared": "1.0.28-rc.
|
|
53
|
-
"@adhdev/session-host-core": "1.0.28-rc.
|
|
52
|
+
"@adhdev/mesh-shared": "1.0.28-rc.11",
|
|
53
|
+
"@adhdev/session-host-core": "1.0.28-rc.11",
|
|
54
54
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
55
55
|
"ajv": "^8.20.0",
|
|
56
56
|
"ajv-formats": "^3.0.1",
|
package/src/cli-adapter-types.ts
CHANGED
|
@@ -173,6 +173,13 @@ export interface CliAdapter {
|
|
|
173
173
|
// back to resolveModal + the resolution-cooldown check).
|
|
174
174
|
resolveModalMatched?(buttonIndex: number): boolean;
|
|
175
175
|
isApprovalRecentlyResolved?(): boolean;
|
|
176
|
+
/**
|
|
177
|
+
* TX-FSM Stage 0 (shadow): receive the daemon-normalized transcript signal
|
|
178
|
+
* observation (SignalSnapshot envelope, providers/spec/signal-envelope.ts).
|
|
179
|
+
* Optional — only the spec-driven FSM adapter implements it; callers must
|
|
180
|
+
* typeof-guard. Shadow-only: the observation can never gate a transition.
|
|
181
|
+
*/
|
|
182
|
+
setSignalObservation?(snapshot: unknown): void;
|
|
176
183
|
// Raw PTY I/O (for terminal view)
|
|
177
184
|
setOnPtyData?(callback: (data: string) => void): void;
|
|
178
185
|
writeRaw?(data: string): void;
|
|
@@ -112,6 +112,47 @@ export const daemonLifecycleHandlers: Record<string, LowFamilyHandler> = {
|
|
|
112
112
|
}
|
|
113
113
|
},
|
|
114
114
|
|
|
115
|
+
// Restart-only path (mesh restart_daemon_node mode="restart"): re-spawn the
|
|
116
|
+
// daemon WITHOUT the npm reinstall daemon_upgrade performs. Used to reset
|
|
117
|
+
// daemon state (memory leaks, zombie sessions, wedged internals) when the
|
|
118
|
+
// version is already correct — downtime drops to the detached re-spawn.
|
|
119
|
+
// killSessionHost is an explicit opt-in hard refresh (see upgrade-helper).
|
|
120
|
+
daemon_restart: async (ctx: LowFamilyContext, args: any) => {
|
|
121
|
+
LOG.info('Restart', 'Restart-only requested (no package reinstall)');
|
|
122
|
+
try {
|
|
123
|
+
const isStandalone = ctx.deps.packageName === '@adhdev/daemon-standalone'
|
|
124
|
+
|| process.argv[1]?.includes('daemon-standalone');
|
|
125
|
+
const pkgName = isStandalone ? '@adhdev/daemon-standalone' : 'adhdev';
|
|
126
|
+
const killSessionHost = args?.killSessionHost === true;
|
|
127
|
+
if (killSessionHost) {
|
|
128
|
+
LOG.warn('Restart', 'killSessionHost requested — session-host will be stopped and ALL hosted sessions destroyed');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
spawnDetachedDaemonUpgradeHelper({
|
|
132
|
+
packageName: pkgName,
|
|
133
|
+
targetVersion: '',
|
|
134
|
+
parentPid: process.pid,
|
|
135
|
+
restartArgv: process.argv.slice(1),
|
|
136
|
+
cwd: process.cwd(),
|
|
137
|
+
sessionHostAppName: process.env.ADHDEV_SESSION_HOST_NAME || 'adhdev',
|
|
138
|
+
skipInstall: true,
|
|
139
|
+
killSessionHost,
|
|
140
|
+
});
|
|
141
|
+
LOG.info('Restart', 'Scheduled detached restart-only helper');
|
|
142
|
+
|
|
143
|
+
// Exit after the command response has been sent so the helper can re-spawn cleanly.
|
|
144
|
+
setTimeout(() => {
|
|
145
|
+
LOG.info('Restart', 'Exiting daemon so detached helper can re-spawn it...');
|
|
146
|
+
process.exit(0);
|
|
147
|
+
}, 3000);
|
|
148
|
+
|
|
149
|
+
return { success: true, restarted: true, restarting: true, mode: 'restart', killSessionHost };
|
|
150
|
+
} catch (e: any) {
|
|
151
|
+
LOG.error('Restart', `Failed: ${e.message}`);
|
|
152
|
+
return { success: false, error: e.message };
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
|
|
115
156
|
set_machine_nickname: async (_ctx: LowFamilyContext, args: any) => {
|
|
116
157
|
const nickname = args?.nickname;
|
|
117
158
|
updateConfig({ machineNickname: nickname || null });
|