@adhdev/daemon-core 0.9.82-rc.329 → 0.9.82-rc.330
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +110 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +110 -11
- package/dist/index.mjs.map +1 -1
- package/dist/providers/spec/fsm-driver.d.ts +28 -0
- package/dist/providers/spec/fsm-types.d.ts +16 -0
- package/package.json +2 -2
- package/src/mesh/mesh-events-coordinator.ts +8 -1
- package/src/providers/cli-provider-instance.ts +8 -0
- package/src/providers/spec/fsm-driver.ts +89 -5
- package/src/providers/spec/fsm-loader.ts +8 -0
- package/src/providers/spec/fsm-types.ts +16 -0
package/dist/index.mjs
CHANGED
|
@@ -308,10 +308,10 @@ function readInjected(value) {
|
|
|
308
308
|
}
|
|
309
309
|
function getDaemonBuildInfo() {
|
|
310
310
|
if (cached) return cached;
|
|
311
|
-
const commit = readInjected(true ? "
|
|
312
|
-
const commitShort = readInjected(true ? "
|
|
313
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
314
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
311
|
+
const commit = readInjected(true ? "ae30dde113f601dc80d6396a625c1dafb3dbaddd" : void 0) ?? "unknown";
|
|
312
|
+
const commitShort = readInjected(true ? "ae30dde1" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
313
|
+
const version = readInjected(true ? "0.9.82-rc.330" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
314
|
+
const builtAt = readInjected(true ? "2026-06-19T17:20:52.196Z" : void 0);
|
|
315
315
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
316
316
|
return cached;
|
|
317
317
|
}
|
|
@@ -10261,7 +10261,11 @@ function injectMeshSystemMessage(components, args) {
|
|
|
10261
10261
|
event: args.event,
|
|
10262
10262
|
meshId: args.meshId,
|
|
10263
10263
|
nodeId: eventNodeId || void 0,
|
|
10264
|
-
...args.metadataEvent
|
|
10264
|
+
...args.metadataEvent,
|
|
10265
|
+
// Ensure a `workspace` field reaches updateMeshOwnedSession even when the
|
|
10266
|
+
// worker provider event only carried `workspaceName`. The merge spread of
|
|
10267
|
+
// metadataEvent above wins when it already has a non-empty `workspace`.
|
|
10268
|
+
workspace: readNonEmptyString2(args.metadataEvent.workspace) || readNonEmptyString2(args.metadataEvent.workspaceName) || void 0
|
|
10265
10269
|
});
|
|
10266
10270
|
} catch {
|
|
10267
10271
|
}
|
|
@@ -10620,7 +10624,7 @@ function injectMeshSystemMessage(components, args) {
|
|
|
10620
10624
|
meshId: args.meshId,
|
|
10621
10625
|
nodeLabel: args.nodeLabel,
|
|
10622
10626
|
nodeId: args.nodeId || void 0,
|
|
10623
|
-
workspace: readNonEmptyString2(args.metadataEvent.workspace),
|
|
10627
|
+
workspace: readNonEmptyString2(args.metadataEvent.workspace) || readNonEmptyString2(args.metadataEvent.workspaceName),
|
|
10624
10628
|
metadataEvent: {
|
|
10625
10629
|
...args.metadataEvent,
|
|
10626
10630
|
...recoveryContext ? { recoveryContext } : {}
|
|
@@ -17159,6 +17163,13 @@ function validateFsmSpec(raw) {
|
|
|
17159
17163
|
if (typeof t.key !== "string" || !t.key) errs.push("pre_launch_trust.key is required");
|
|
17160
17164
|
}
|
|
17161
17165
|
}
|
|
17166
|
+
if (spec.refocus_when_stalled_ms !== void 0) {
|
|
17167
|
+
if (typeof spec.refocus_when_stalled_ms !== "number" || !(spec.refocus_when_stalled_ms > 0)) {
|
|
17168
|
+
errs.push("refocus_when_stalled_ms must be a positive number");
|
|
17169
|
+
} else if (!Array.isArray(spec.send_on_spawn) || spec.send_on_spawn.length === 0) {
|
|
17170
|
+
errs.push("refocus_when_stalled_ms requires send_on_spawn (the wake sequence to re-inject)");
|
|
17171
|
+
}
|
|
17172
|
+
}
|
|
17162
17173
|
if (!Array.isArray(spec.states) || spec.states.length === 0) {
|
|
17163
17174
|
errs.push("states[] must be a non-empty array");
|
|
17164
17175
|
return errs;
|
|
@@ -31494,6 +31505,14 @@ var FsmDriver = class {
|
|
|
31494
31505
|
/** Timer that re-runs evaluate() when a time-condition would flip true
|
|
31495
31506
|
* with no PTY frame to trigger it. */
|
|
31496
31507
|
wakeTimer = null;
|
|
31508
|
+
/** Timer driving the focus-gated stall watchdog (refocus_when_stalled_ms).
|
|
31509
|
+
* Re-arms itself while the machine is generating so a re-prime can fire
|
|
31510
|
+
* even when the PTY has gone completely quiet. */
|
|
31511
|
+
stallTimer = null;
|
|
31512
|
+
/** Wall-clock time the last stall re-prime was injected. The cooldown gate:
|
|
31513
|
+
* after a re-prime we don't re-inject until the screen changes (which
|
|
31514
|
+
* resets the stall reference) or another full stall window lapses. */
|
|
31515
|
+
lastRefocusAt = 0;
|
|
31497
31516
|
currentEval = null;
|
|
31498
31517
|
stateHistory = [];
|
|
31499
31518
|
prevStateAt = 0;
|
|
@@ -31534,11 +31553,18 @@ var FsmDriver = class {
|
|
|
31534
31553
|
const seqs = this.spec.send_on_spawn;
|
|
31535
31554
|
if (!Array.isArray(seqs) || seqs.length === 0) return;
|
|
31536
31555
|
const delay2 = Math.max(0, this.spec.send_on_spawn_delay_ms ?? 250);
|
|
31537
|
-
setTimeout(() =>
|
|
31538
|
-
|
|
31539
|
-
|
|
31540
|
-
|
|
31541
|
-
|
|
31556
|
+
setTimeout(() => this.sendSpawnPrime(), delay2);
|
|
31557
|
+
}
|
|
31558
|
+
/** Write the declared `send_on_spawn` sequences to the PTY once. Used both
|
|
31559
|
+
* at spawn (scheduleSpawnPrime) and, for focus-gated TUIs, by the stall
|
|
31560
|
+
* watchdog to re-inject the focus-in wake mid-turn. No-op when the spec
|
|
31561
|
+
* declares no prime, so non-focus-gated CLIs are never poked. */
|
|
31562
|
+
sendSpawnPrime() {
|
|
31563
|
+
const seqs = this.spec.send_on_spawn;
|
|
31564
|
+
if (!Array.isArray(seqs) || seqs.length === 0) return;
|
|
31565
|
+
for (const seq of seqs) {
|
|
31566
|
+
if (typeof seq === "string" && seq.length > 0) this.adapter.send_keys(seq);
|
|
31567
|
+
}
|
|
31542
31568
|
}
|
|
31543
31569
|
dispatch(cmd) {
|
|
31544
31570
|
switch (cmd.kind) {
|
|
@@ -31599,6 +31625,10 @@ var FsmDriver = class {
|
|
|
31599
31625
|
clearTimeout(this.wakeTimer);
|
|
31600
31626
|
this.wakeTimer = null;
|
|
31601
31627
|
}
|
|
31628
|
+
if (this.stallTimer) {
|
|
31629
|
+
clearTimeout(this.stallTimer);
|
|
31630
|
+
this.stallTimer = null;
|
|
31631
|
+
}
|
|
31602
31632
|
this.specWatcher?.close();
|
|
31603
31633
|
this.adapter.kill();
|
|
31604
31634
|
}
|
|
@@ -31739,11 +31769,13 @@ var FsmDriver = class {
|
|
|
31739
31769
|
this.commitTransition(ev.fired, now, ev);
|
|
31740
31770
|
this.emitStateChanged(forceEmit);
|
|
31741
31771
|
this.scheduleWakeForState();
|
|
31772
|
+
this.scheduleStallWatchdog();
|
|
31742
31773
|
this.maybeMarkReady();
|
|
31743
31774
|
return;
|
|
31744
31775
|
}
|
|
31745
31776
|
this.emitStateChanged(forceEmit);
|
|
31746
31777
|
this.scheduleWakeForState();
|
|
31778
|
+
this.scheduleStallWatchdog();
|
|
31747
31779
|
this.maybeMarkReady();
|
|
31748
31780
|
}
|
|
31749
31781
|
commitTransition(fired, now, ev) {
|
|
@@ -31891,6 +31923,67 @@ var FsmDriver = class {
|
|
|
31891
31923
|
this.reevaluate();
|
|
31892
31924
|
}, Math.max(soonest + 30, 50));
|
|
31893
31925
|
}
|
|
31926
|
+
// ── Focus-gated stall watchdog (refocus_when_stalled_ms) ──────────────────
|
|
31927
|
+
//
|
|
31928
|
+
// A focus-event TUI (antigravity's `agy`) freezes its render loop the moment
|
|
31929
|
+
// it thinks it has lost focus mid-turn: the screen stops updating and only
|
|
31930
|
+
// repaints on the next keypress, which the daemon never sends. The output
|
|
31931
|
+
// pump is wired entirely to PTY onData (no PTY data → no reevaluate, no
|
|
31932
|
+
// on_screen_changed), so a normal time-wake that only re-reads the screen
|
|
31933
|
+
// can't help — there is nothing new to read. The fix is to re-inject the
|
|
31934
|
+
// focus-in wake (`send_on_spawn`) so the CLI flushes the held output itself.
|
|
31935
|
+
/** True when this spec opts into stall recovery (declares a positive
|
|
31936
|
+
* refocus window AND a wake sequence to re-inject). */
|
|
31937
|
+
stallRecoveryEnabled() {
|
|
31938
|
+
const ms = this.spec.refocus_when_stalled_ms;
|
|
31939
|
+
return typeof ms === "number" && ms > 0 && Array.isArray(this.spec.send_on_spawn) && this.spec.send_on_spawn.length > 0;
|
|
31940
|
+
}
|
|
31941
|
+
/** Wall-clock time the screen last changed in the current state, falling
|
|
31942
|
+
* back to state entry when it has not changed since (i.e. fully stalled
|
|
31943
|
+
* from the start of the state). */
|
|
31944
|
+
lastScreenChangeAt() {
|
|
31945
|
+
return this.regionLastChangedAt.get(-1) ?? this.stateEnteredAt;
|
|
31946
|
+
}
|
|
31947
|
+
/** Arm a timer to re-inject the focus-in prime if the screen stays frozen
|
|
31948
|
+
* through a `generating` state. Only active for opted-in focus-gated specs;
|
|
31949
|
+
* a no-op (and cleared) for every other CLI and every non-generating state.
|
|
31950
|
+
* Re-arms itself so it keeps watching while the PTY is quiet. */
|
|
31951
|
+
scheduleStallWatchdog() {
|
|
31952
|
+
if (this.stallTimer) {
|
|
31953
|
+
clearTimeout(this.stallTimer);
|
|
31954
|
+
this.stallTimer = null;
|
|
31955
|
+
}
|
|
31956
|
+
if (!this.stallRecoveryEnabled()) return;
|
|
31957
|
+
const st = stateById(this.spec, this.currentStateId);
|
|
31958
|
+
if (!st || statusForState(st) !== "generating") return;
|
|
31959
|
+
const windowMs = this.spec.refocus_when_stalled_ms;
|
|
31960
|
+
const since = Math.max(this.lastScreenChangeAt(), this.lastRefocusAt);
|
|
31961
|
+
const remaining = windowMs - (Date.now() - since);
|
|
31962
|
+
this.stallTimer = setTimeout(
|
|
31963
|
+
() => {
|
|
31964
|
+
this.stallTimer = null;
|
|
31965
|
+
this.onStallTick();
|
|
31966
|
+
},
|
|
31967
|
+
Math.max(remaining + 30, 50)
|
|
31968
|
+
);
|
|
31969
|
+
}
|
|
31970
|
+
/** Fire when the stall window elapses: if the screen is still frozen and we
|
|
31971
|
+
* are still generating, re-inject the focus-in wake once, then re-arm. */
|
|
31972
|
+
onStallTick() {
|
|
31973
|
+
if (!this.stallRecoveryEnabled()) return;
|
|
31974
|
+
const st = stateById(this.spec, this.currentStateId);
|
|
31975
|
+
if (!st || statusForState(st) !== "generating") return;
|
|
31976
|
+
const windowMs = this.spec.refocus_when_stalled_ms;
|
|
31977
|
+
const now = Date.now();
|
|
31978
|
+
const stalledFor = now - this.lastScreenChangeAt();
|
|
31979
|
+
const sinceLastRefocus = now - this.lastRefocusAt;
|
|
31980
|
+
if (stalledFor >= windowMs && sinceLastRefocus >= windowMs) {
|
|
31981
|
+
LOG.info("FsmDriver", `[${this.specTag()}] stall detected (${stalledFor}ms quiet, generating) \u2014 re-injecting focus-in`);
|
|
31982
|
+
this.sendSpawnPrime();
|
|
31983
|
+
this.lastRefocusAt = now;
|
|
31984
|
+
}
|
|
31985
|
+
this.scheduleStallWatchdog();
|
|
31986
|
+
}
|
|
31894
31987
|
maybeMarkReady() {
|
|
31895
31988
|
if (this.readySeenOnce) return;
|
|
31896
31989
|
const st = stateById(this.spec, this.currentStateId);
|
|
@@ -35137,6 +35230,12 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35137
35230
|
targetSessionId: typeof event.targetSessionId === "string" && event.targetSessionId.trim() ? event.targetSessionId : this.instanceId,
|
|
35138
35231
|
providerType: typeof event.providerType === "string" && event.providerType.trim() ? event.providerType : this.type,
|
|
35139
35232
|
workspaceName: typeof event.workspaceName === "string" && event.workspaceName.trim() ? event.workspaceName : this.workingDir,
|
|
35233
|
+
// Carry the workspace under BOTH `workspace` and `workspaceName` so the
|
|
35234
|
+
// downstream mesh forward/merge path — which reads `workspace` — can
|
|
35235
|
+
// propagate it to the coordinator snapshot. Without `workspace` the live
|
|
35236
|
+
// event path delivers an empty workspace and the dashboard falls back to
|
|
35237
|
+
// the generic "Terminal (Mesh Node)" title.
|
|
35238
|
+
workspace: typeof event.workspace === "string" && event.workspace.trim() ? event.workspace : this.workingDir,
|
|
35140
35239
|
providerSessionId: typeof event.providerSessionId === "string" && event.providerSessionId.trim() ? event.providerSessionId : this.providerSessionId
|
|
35141
35240
|
};
|
|
35142
35241
|
if (this.context?.emitProviderEvent) {
|