@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.js
CHANGED
|
@@ -313,10 +313,10 @@ function readInjected(value) {
|
|
|
313
313
|
}
|
|
314
314
|
function getDaemonBuildInfo() {
|
|
315
315
|
if (cached) return cached;
|
|
316
|
-
const commit = readInjected(true ? "
|
|
317
|
-
const commitShort = readInjected(true ? "
|
|
318
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
319
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
316
|
+
const commit = readInjected(true ? "ae30dde113f601dc80d6396a625c1dafb3dbaddd" : void 0) ?? "unknown";
|
|
317
|
+
const commitShort = readInjected(true ? "ae30dde1" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
318
|
+
const version = readInjected(true ? "0.9.82-rc.330" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
319
|
+
const builtAt = readInjected(true ? "2026-06-19T17:20:52.196Z" : void 0);
|
|
320
320
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
321
321
|
return cached;
|
|
322
322
|
}
|
|
@@ -10264,7 +10264,11 @@ function injectMeshSystemMessage(components, args) {
|
|
|
10264
10264
|
event: args.event,
|
|
10265
10265
|
meshId: args.meshId,
|
|
10266
10266
|
nodeId: eventNodeId || void 0,
|
|
10267
|
-
...args.metadataEvent
|
|
10267
|
+
...args.metadataEvent,
|
|
10268
|
+
// Ensure a `workspace` field reaches updateMeshOwnedSession even when the
|
|
10269
|
+
// worker provider event only carried `workspaceName`. The merge spread of
|
|
10270
|
+
// metadataEvent above wins when it already has a non-empty `workspace`.
|
|
10271
|
+
workspace: readNonEmptyString2(args.metadataEvent.workspace) || readNonEmptyString2(args.metadataEvent.workspaceName) || void 0
|
|
10268
10272
|
});
|
|
10269
10273
|
} catch {
|
|
10270
10274
|
}
|
|
@@ -10623,7 +10627,7 @@ function injectMeshSystemMessage(components, args) {
|
|
|
10623
10627
|
meshId: args.meshId,
|
|
10624
10628
|
nodeLabel: args.nodeLabel,
|
|
10625
10629
|
nodeId: args.nodeId || void 0,
|
|
10626
|
-
workspace: readNonEmptyString2(args.metadataEvent.workspace),
|
|
10630
|
+
workspace: readNonEmptyString2(args.metadataEvent.workspace) || readNonEmptyString2(args.metadataEvent.workspaceName),
|
|
10627
10631
|
metadataEvent: {
|
|
10628
10632
|
...args.metadataEvent,
|
|
10629
10633
|
...recoveryContext ? { recoveryContext } : {}
|
|
@@ -17163,6 +17167,13 @@ function validateFsmSpec(raw) {
|
|
|
17163
17167
|
if (typeof t.key !== "string" || !t.key) errs.push("pre_launch_trust.key is required");
|
|
17164
17168
|
}
|
|
17165
17169
|
}
|
|
17170
|
+
if (spec.refocus_when_stalled_ms !== void 0) {
|
|
17171
|
+
if (typeof spec.refocus_when_stalled_ms !== "number" || !(spec.refocus_when_stalled_ms > 0)) {
|
|
17172
|
+
errs.push("refocus_when_stalled_ms must be a positive number");
|
|
17173
|
+
} else if (!Array.isArray(spec.send_on_spawn) || spec.send_on_spawn.length === 0) {
|
|
17174
|
+
errs.push("refocus_when_stalled_ms requires send_on_spawn (the wake sequence to re-inject)");
|
|
17175
|
+
}
|
|
17176
|
+
}
|
|
17166
17177
|
if (!Array.isArray(spec.states) || spec.states.length === 0) {
|
|
17167
17178
|
errs.push("states[] must be a non-empty array");
|
|
17168
17179
|
return errs;
|
|
@@ -31854,6 +31865,14 @@ var FsmDriver = class {
|
|
|
31854
31865
|
/** Timer that re-runs evaluate() when a time-condition would flip true
|
|
31855
31866
|
* with no PTY frame to trigger it. */
|
|
31856
31867
|
wakeTimer = null;
|
|
31868
|
+
/** Timer driving the focus-gated stall watchdog (refocus_when_stalled_ms).
|
|
31869
|
+
* Re-arms itself while the machine is generating so a re-prime can fire
|
|
31870
|
+
* even when the PTY has gone completely quiet. */
|
|
31871
|
+
stallTimer = null;
|
|
31872
|
+
/** Wall-clock time the last stall re-prime was injected. The cooldown gate:
|
|
31873
|
+
* after a re-prime we don't re-inject until the screen changes (which
|
|
31874
|
+
* resets the stall reference) or another full stall window lapses. */
|
|
31875
|
+
lastRefocusAt = 0;
|
|
31857
31876
|
currentEval = null;
|
|
31858
31877
|
stateHistory = [];
|
|
31859
31878
|
prevStateAt = 0;
|
|
@@ -31894,11 +31913,18 @@ var FsmDriver = class {
|
|
|
31894
31913
|
const seqs = this.spec.send_on_spawn;
|
|
31895
31914
|
if (!Array.isArray(seqs) || seqs.length === 0) return;
|
|
31896
31915
|
const delay2 = Math.max(0, this.spec.send_on_spawn_delay_ms ?? 250);
|
|
31897
|
-
setTimeout(() =>
|
|
31898
|
-
|
|
31899
|
-
|
|
31900
|
-
|
|
31901
|
-
|
|
31916
|
+
setTimeout(() => this.sendSpawnPrime(), delay2);
|
|
31917
|
+
}
|
|
31918
|
+
/** Write the declared `send_on_spawn` sequences to the PTY once. Used both
|
|
31919
|
+
* at spawn (scheduleSpawnPrime) and, for focus-gated TUIs, by the stall
|
|
31920
|
+
* watchdog to re-inject the focus-in wake mid-turn. No-op when the spec
|
|
31921
|
+
* declares no prime, so non-focus-gated CLIs are never poked. */
|
|
31922
|
+
sendSpawnPrime() {
|
|
31923
|
+
const seqs = this.spec.send_on_spawn;
|
|
31924
|
+
if (!Array.isArray(seqs) || seqs.length === 0) return;
|
|
31925
|
+
for (const seq of seqs) {
|
|
31926
|
+
if (typeof seq === "string" && seq.length > 0) this.adapter.send_keys(seq);
|
|
31927
|
+
}
|
|
31902
31928
|
}
|
|
31903
31929
|
dispatch(cmd) {
|
|
31904
31930
|
switch (cmd.kind) {
|
|
@@ -31959,6 +31985,10 @@ var FsmDriver = class {
|
|
|
31959
31985
|
clearTimeout(this.wakeTimer);
|
|
31960
31986
|
this.wakeTimer = null;
|
|
31961
31987
|
}
|
|
31988
|
+
if (this.stallTimer) {
|
|
31989
|
+
clearTimeout(this.stallTimer);
|
|
31990
|
+
this.stallTimer = null;
|
|
31991
|
+
}
|
|
31962
31992
|
this.specWatcher?.close();
|
|
31963
31993
|
this.adapter.kill();
|
|
31964
31994
|
}
|
|
@@ -32099,11 +32129,13 @@ var FsmDriver = class {
|
|
|
32099
32129
|
this.commitTransition(ev.fired, now, ev);
|
|
32100
32130
|
this.emitStateChanged(forceEmit);
|
|
32101
32131
|
this.scheduleWakeForState();
|
|
32132
|
+
this.scheduleStallWatchdog();
|
|
32102
32133
|
this.maybeMarkReady();
|
|
32103
32134
|
return;
|
|
32104
32135
|
}
|
|
32105
32136
|
this.emitStateChanged(forceEmit);
|
|
32106
32137
|
this.scheduleWakeForState();
|
|
32138
|
+
this.scheduleStallWatchdog();
|
|
32107
32139
|
this.maybeMarkReady();
|
|
32108
32140
|
}
|
|
32109
32141
|
commitTransition(fired, now, ev) {
|
|
@@ -32251,6 +32283,67 @@ var FsmDriver = class {
|
|
|
32251
32283
|
this.reevaluate();
|
|
32252
32284
|
}, Math.max(soonest + 30, 50));
|
|
32253
32285
|
}
|
|
32286
|
+
// ── Focus-gated stall watchdog (refocus_when_stalled_ms) ──────────────────
|
|
32287
|
+
//
|
|
32288
|
+
// A focus-event TUI (antigravity's `agy`) freezes its render loop the moment
|
|
32289
|
+
// it thinks it has lost focus mid-turn: the screen stops updating and only
|
|
32290
|
+
// repaints on the next keypress, which the daemon never sends. The output
|
|
32291
|
+
// pump is wired entirely to PTY onData (no PTY data → no reevaluate, no
|
|
32292
|
+
// on_screen_changed), so a normal time-wake that only re-reads the screen
|
|
32293
|
+
// can't help — there is nothing new to read. The fix is to re-inject the
|
|
32294
|
+
// focus-in wake (`send_on_spawn`) so the CLI flushes the held output itself.
|
|
32295
|
+
/** True when this spec opts into stall recovery (declares a positive
|
|
32296
|
+
* refocus window AND a wake sequence to re-inject). */
|
|
32297
|
+
stallRecoveryEnabled() {
|
|
32298
|
+
const ms = this.spec.refocus_when_stalled_ms;
|
|
32299
|
+
return typeof ms === "number" && ms > 0 && Array.isArray(this.spec.send_on_spawn) && this.spec.send_on_spawn.length > 0;
|
|
32300
|
+
}
|
|
32301
|
+
/** Wall-clock time the screen last changed in the current state, falling
|
|
32302
|
+
* back to state entry when it has not changed since (i.e. fully stalled
|
|
32303
|
+
* from the start of the state). */
|
|
32304
|
+
lastScreenChangeAt() {
|
|
32305
|
+
return this.regionLastChangedAt.get(-1) ?? this.stateEnteredAt;
|
|
32306
|
+
}
|
|
32307
|
+
/** Arm a timer to re-inject the focus-in prime if the screen stays frozen
|
|
32308
|
+
* through a `generating` state. Only active for opted-in focus-gated specs;
|
|
32309
|
+
* a no-op (and cleared) for every other CLI and every non-generating state.
|
|
32310
|
+
* Re-arms itself so it keeps watching while the PTY is quiet. */
|
|
32311
|
+
scheduleStallWatchdog() {
|
|
32312
|
+
if (this.stallTimer) {
|
|
32313
|
+
clearTimeout(this.stallTimer);
|
|
32314
|
+
this.stallTimer = null;
|
|
32315
|
+
}
|
|
32316
|
+
if (!this.stallRecoveryEnabled()) return;
|
|
32317
|
+
const st = stateById(this.spec, this.currentStateId);
|
|
32318
|
+
if (!st || statusForState(st) !== "generating") return;
|
|
32319
|
+
const windowMs = this.spec.refocus_when_stalled_ms;
|
|
32320
|
+
const since = Math.max(this.lastScreenChangeAt(), this.lastRefocusAt);
|
|
32321
|
+
const remaining = windowMs - (Date.now() - since);
|
|
32322
|
+
this.stallTimer = setTimeout(
|
|
32323
|
+
() => {
|
|
32324
|
+
this.stallTimer = null;
|
|
32325
|
+
this.onStallTick();
|
|
32326
|
+
},
|
|
32327
|
+
Math.max(remaining + 30, 50)
|
|
32328
|
+
);
|
|
32329
|
+
}
|
|
32330
|
+
/** Fire when the stall window elapses: if the screen is still frozen and we
|
|
32331
|
+
* are still generating, re-inject the focus-in wake once, then re-arm. */
|
|
32332
|
+
onStallTick() {
|
|
32333
|
+
if (!this.stallRecoveryEnabled()) return;
|
|
32334
|
+
const st = stateById(this.spec, this.currentStateId);
|
|
32335
|
+
if (!st || statusForState(st) !== "generating") return;
|
|
32336
|
+
const windowMs = this.spec.refocus_when_stalled_ms;
|
|
32337
|
+
const now = Date.now();
|
|
32338
|
+
const stalledFor = now - this.lastScreenChangeAt();
|
|
32339
|
+
const sinceLastRefocus = now - this.lastRefocusAt;
|
|
32340
|
+
if (stalledFor >= windowMs && sinceLastRefocus >= windowMs) {
|
|
32341
|
+
LOG.info("FsmDriver", `[${this.specTag()}] stall detected (${stalledFor}ms quiet, generating) \u2014 re-injecting focus-in`);
|
|
32342
|
+
this.sendSpawnPrime();
|
|
32343
|
+
this.lastRefocusAt = now;
|
|
32344
|
+
}
|
|
32345
|
+
this.scheduleStallWatchdog();
|
|
32346
|
+
}
|
|
32254
32347
|
maybeMarkReady() {
|
|
32255
32348
|
if (this.readySeenOnce) return;
|
|
32256
32349
|
const st = stateById(this.spec, this.currentStateId);
|
|
@@ -35497,6 +35590,12 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35497
35590
|
targetSessionId: typeof event.targetSessionId === "string" && event.targetSessionId.trim() ? event.targetSessionId : this.instanceId,
|
|
35498
35591
|
providerType: typeof event.providerType === "string" && event.providerType.trim() ? event.providerType : this.type,
|
|
35499
35592
|
workspaceName: typeof event.workspaceName === "string" && event.workspaceName.trim() ? event.workspaceName : this.workingDir,
|
|
35593
|
+
// Carry the workspace under BOTH `workspace` and `workspaceName` so the
|
|
35594
|
+
// downstream mesh forward/merge path — which reads `workspace` — can
|
|
35595
|
+
// propagate it to the coordinator snapshot. Without `workspace` the live
|
|
35596
|
+
// event path delivers an empty workspace and the dashboard falls back to
|
|
35597
|
+
// the generic "Terminal (Mesh Node)" title.
|
|
35598
|
+
workspace: typeof event.workspace === "string" && event.workspace.trim() ? event.workspace : this.workingDir,
|
|
35500
35599
|
providerSessionId: typeof event.providerSessionId === "string" && event.providerSessionId.trim() ? event.providerSessionId : this.providerSessionId
|
|
35501
35600
|
};
|
|
35502
35601
|
if (this.context?.emitProviderEvent) {
|