@adhdev/daemon-core 0.9.82-rc.197 → 0.9.82-rc.199

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 CHANGED
@@ -15005,8 +15005,8 @@ async function detectIDEs(providerLoader) {
15005
15005
  if ((0, import_fs11.existsSync)(bundledCli)) resolvedCli = bundledCli;
15006
15006
  }
15007
15007
  if (!resolvedCli && appPath && os29 === "win32") {
15008
- const { dirname: dirname11 } = await import("path");
15009
- const appDir = dirname11(appPath);
15008
+ const { dirname: dirname12 } = await import("path");
15009
+ const appDir = dirname12(appPath);
15010
15010
  const candidates = [
15011
15011
  `${appDir}\\\\bin\\\\${def.cli}.cmd`,
15012
15012
  `${appDir}\\\\bin\\\\${def.cli}`,
@@ -27489,7 +27489,15 @@ var SpecDriver = class {
27489
27489
  * explicit wake-up there's nothing to trigger the busy → idle
27490
27490
  * downshift. */
27491
27491
  busyExpiryTimer = null;
27492
+ /** Pending idle-commit timer. Armed when the evaluator first returns idle;
27493
+ * fires after idle_hold_ms if no non-idle reading has cancelled it. */
27494
+ idleHoldTimer = null;
27495
+ /** State snapshot captured when the idle hold was armed — emitted on commit. */
27496
+ pendingIdleState = null;
27492
27497
  specWatcher = null;
27498
+ /** Ring buffer of committed state transitions (max 50). */
27499
+ stateHistory = [];
27500
+ prevStateAt = 0;
27493
27501
  /** Subscribe to outbound events. Returns an unsubscribe fn. */
27494
27502
  subscribe(listener) {
27495
27503
  this.listeners.add(listener);
@@ -27540,9 +27548,40 @@ var SpecDriver = class {
27540
27548
  shutdown() {
27541
27549
  for (const t of this.delegateTimers.values()) clearTimeout(t);
27542
27550
  this.delegateTimers.clear();
27551
+ this.cancelIdleHold();
27552
+ if (this.busyExpiryTimer) {
27553
+ clearTimeout(this.busyExpiryTimer);
27554
+ this.busyExpiryTimer = null;
27555
+ }
27543
27556
  this.specWatcher?.close();
27544
27557
  this.adapter.kill();
27545
27558
  }
27559
+ cancelIdleHold() {
27560
+ if (this.idleHoldTimer) {
27561
+ clearTimeout(this.idleHoldTimer);
27562
+ this.idleHoldTimer = null;
27563
+ }
27564
+ this.pendingIdleState = null;
27565
+ }
27566
+ pushHistory(stateId, label) {
27567
+ const now = Date.now();
27568
+ const durationMs = this.prevStateAt > 0 ? now - this.prevStateAt : 0;
27569
+ this.prevStateAt = now;
27570
+ this.stateHistory.push({ stateId, label, at: now, durationMs });
27571
+ if (this.stateHistory.length > 50) this.stateHistory.shift();
27572
+ }
27573
+ getStateHistory() {
27574
+ return this.stateHistory;
27575
+ }
27576
+ getLastBusyAt() {
27577
+ return this.lastBusyAt;
27578
+ }
27579
+ hasIdleHoldPending() {
27580
+ return this.idleHoldTimer !== null;
27581
+ }
27582
+ getSpecPath() {
27583
+ return this.opts.specPath;
27584
+ }
27546
27585
  // ────────────────────────────────────────────────────────────────────
27547
27586
  // Loading & adapter wiring
27548
27587
  // ────────────────────────────────────────────────────────────────────
@@ -27566,7 +27605,10 @@ var SpecDriver = class {
27566
27605
  }
27567
27606
  armSpecWatcher() {
27568
27607
  try {
27569
- this.specWatcher = fs10.watch(this.opts.specPath, { persistent: false }, () => {
27608
+ const dir = path19.dirname(this.opts.specPath);
27609
+ const base = path19.basename(this.opts.specPath);
27610
+ this.specWatcher = fs10.watch(dir, { persistent: false }, (_event, filename) => {
27611
+ if (filename && filename !== base) return;
27570
27612
  const res = loadSpec(this.opts.specPath);
27571
27613
  if (!res.ok) {
27572
27614
  this.emit({ kind: "spec_error", errors: res.errors });
@@ -27650,7 +27692,46 @@ var SpecDriver = class {
27650
27692
  if (evState.id === "busy") {
27651
27693
  this.lastBusyAt = Date.now();
27652
27694
  this.lastBusyState = evState;
27695
+ this.cancelIdleHold();
27653
27696
  this.scheduleBusyExpiry(busyWakeMs);
27697
+ } else if (evState.id !== this.currentStateId && evState.id !== "busy") {
27698
+ if (evState.id !== (this.spec.default_state ?? "idle")) {
27699
+ this.cancelIdleHold();
27700
+ }
27701
+ }
27702
+ const idleHoldMs = this.spec.debounce?.idle_hold_ms ?? 0;
27703
+ const isIdleState = evState.id === (this.spec.default_state ?? "idle");
27704
+ if (isIdleState && idleHoldMs > 0 && this.currentStateId !== evState.id) {
27705
+ if (!this.idleHoldTimer) {
27706
+ this.pendingIdleState = evState;
27707
+ this.idleHoldTimer = setTimeout(() => {
27708
+ this.idleHoldTimer = null;
27709
+ const committed = this.pendingIdleState;
27710
+ this.pendingIdleState = null;
27711
+ if (!committed) return;
27712
+ LOG.debug("SpecDriver", `[${this.opts.specPath.split("/").slice(-3).join("/")}] idleHold committed after ${idleHoldMs}ms`);
27713
+ this.currentStateId = committed.id;
27714
+ this.currentEval = ev;
27715
+ this.pushHistory(committed.id, committed.label);
27716
+ this.emit({
27717
+ kind: "state_changed",
27718
+ state: committed,
27719
+ modal: null,
27720
+ controls: ev.controls.map((c) => ({ id: c.id, label: c.label, action_type: c.actionType }))
27721
+ });
27722
+ this.armOrCancelDelegateTimers(committed.id);
27723
+ if (this.opts.emitTrace) this.emit({ kind: "spec_trace", entries: ev.trace });
27724
+ }, idleHoldMs);
27725
+ }
27726
+ this.currentEval = ev;
27727
+ const graceMs2 = this.spec.debounce?.startup_grace_ms ?? STARTUP_GRACE_MS;
27728
+ if (!this.idleSeenOnce && Date.now() - this.startedAtMs >= graceMs2) {
27729
+ this.idleSeenOnce = true;
27730
+ const queued = this.pendingSends.splice(0);
27731
+ for (const text of queued) setTimeout(() => this.actuallySendMessage(text), 50);
27732
+ }
27733
+ if (this.pickerInProgress) this.tryAdvancePicker(screen);
27734
+ return;
27654
27735
  }
27655
27736
  const changed = forceEmit || evState.id !== this.currentStateId || !shallowSameModal(ev, this.currentEval) || !shallowSameControls(ev, this.currentEval);
27656
27737
  if (this.pickerInProgress) this.tryAdvancePicker(screen);
@@ -27666,6 +27747,7 @@ var SpecDriver = class {
27666
27747
  }
27667
27748
  if (changed) {
27668
27749
  this.currentStateId = evState.id;
27750
+ this.pushHistory(evState.id, evState.label);
27669
27751
  this.emit({
27670
27752
  kind: "state_changed",
27671
27753
  state: evState,
@@ -28099,7 +28181,11 @@ var SpecCliAdapter = class {
28099
28181
  activeInteractivePrompt: this.activeInteractivePrompt,
28100
28182
  exited: this.exited,
28101
28183
  screen,
28102
- sections
28184
+ sections,
28185
+ stateHistory: this.driver.getStateHistory(),
28186
+ idleHoldPending: this.driver.hasIdleHoldPending(),
28187
+ lastBusyAt: this.driver.getLastBusyAt(),
28188
+ specPath: this.driver.getSpecPath()
28103
28189
  };
28104
28190
  }
28105
28191
  getRuntimeMetadata() {
@@ -33638,17 +33724,17 @@ function parseTranscriptFile(filePath, sessionId, workspaceFallback) {
33638
33724
  }
33639
33725
  function readSession(sessionPath) {
33640
33726
  if (!sessionPath || !path25.isAbsolute(sessionPath)) return null;
33641
- const basename13 = path25.basename(sessionPath, ".jsonl");
33642
- if (!isSafeSessionId(basename13)) return null;
33727
+ const basename14 = path25.basename(sessionPath, ".jsonl");
33728
+ if (!isSafeSessionId(basename14)) return null;
33643
33729
  if (!fs14.existsSync(sessionPath)) return null;
33644
33730
  const sourceMtimeMs = statMtimeMs(sessionPath);
33645
- const messages = parseTranscriptFile(sessionPath, basename13);
33731
+ const messages = parseTranscriptFile(sessionPath, basename14);
33646
33732
  if (messages.length === 0) return null;
33647
33733
  const firstSystem = messages.find((m) => m.kind === "session_start");
33648
33734
  const workspace = firstSystem?.workspace || firstSystem?.content || void 0;
33649
33735
  return {
33650
33736
  messages,
33651
- providerSessionId: basename13,
33737
+ providerSessionId: basename14,
33652
33738
  source: "provider-native",
33653
33739
  sourcePath: sessionPath,
33654
33740
  sourceMtimeMs,
@@ -33850,8 +33936,8 @@ function readSession2(sessionPath) {
33850
33936
  if (!fs15.existsSync(sessionPath)) return null;
33851
33937
  const meta = readSessionMeta(sessionPath);
33852
33938
  const metaId = String(meta?.id ?? "").trim();
33853
- const basename13 = path26.basename(sessionPath, ".jsonl");
33854
- const uuidMatch = basename13.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
33939
+ const basename14 = path26.basename(sessionPath, ".jsonl");
33940
+ const uuidMatch = basename14.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
33855
33941
  const filenameUuid2 = uuidMatch ? uuidMatch[1] : "";
33856
33942
  if (metaId && filenameUuid2 && metaId !== filenameUuid2) return null;
33857
33943
  const sessionId = metaId || filenameUuid2;
@@ -41063,6 +41149,21 @@ var DaemonCommandRouter = class {
41063
41149
  } : null
41064
41150
  };
41065
41151
  }
41152
+ case "get_spec_debug": {
41153
+ const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
41154
+ if (!sessionId) return { success: false, error: "targetSessionId required" };
41155
+ const target = this.deps.sessionRegistry.get(sessionId);
41156
+ if (!target) return { success: false, error: "Session not found", sessionId };
41157
+ const adapter = this.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
41158
+ const snapshot = adapter && typeof adapter.getDebugSnapshot === "function" ? adapter.getDebugSnapshot() : null;
41159
+ return {
41160
+ success: true,
41161
+ sessionId,
41162
+ providerType: target.providerType,
41163
+ isSpecProvider: snapshot !== null,
41164
+ snapshot
41165
+ };
41166
+ }
41066
41167
  // ── User-level coordinator-prompt files (~/.adhdev/coordinator-prompts/).
41067
41168
  // These live on this daemon's filesystem and never sync to the
41068
41169
  // cloud / other daemons — they're per-machine config. The
@@ -42390,7 +42491,7 @@ ${ptyResult.output.slice(-2e3)}`);
42390
42491
  };
42391
42492
  }
42392
42493
  const { existsSync: existsSync39, readFileSync: readFileSync33, writeFileSync: writeFileSync21, copyFileSync: copyFileSync4, mkdirSync: mkdirSync19 } = await import("fs");
42393
- const { dirname: dirname11 } = await import("path");
42494
+ const { dirname: dirname12 } = await import("path");
42394
42495
  const mcpConfigPath = coordinatorSetup.configPath;
42395
42496
  const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
42396
42497
  let hermesBaseConfig = null;
@@ -42425,7 +42526,7 @@ ${ptyResult.output.slice(-2e3)}`);
42425
42526
  };
42426
42527
  }
42427
42528
  try {
42428
- mkdirSync19(dirname11(mcpConfigPath), { recursive: true });
42529
+ mkdirSync19(dirname12(mcpConfigPath), { recursive: true });
42429
42530
  } catch (error) {
42430
42531
  const message = `Could not prepare MCP config path for automatic setup: ${error?.message || error}`;
42431
42532
  LOG.error("MeshCoordinator", message);
@@ -42435,7 +42536,7 @@ ${ptyResult.output.slice(-2e3)}`);
42435
42536
  const hadExistingMcpConfig = existsSync39(mcpConfigPath);
42436
42537
  let existingMcpConfig = hermesBaseConfig?.config || {};
42437
42538
  if (hermesBaseConfig) {
42438
- copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname11(mcpConfigPath));
42539
+ copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname12(mcpConfigPath));
42439
42540
  }
42440
42541
  if (hadExistingMcpConfig) {
42441
42542
  try {
@@ -42473,7 +42574,7 @@ ${ptyResult.output.slice(-2e3)}`);
42473
42574
  const cliArgs = [];
42474
42575
  const launchEnv = {};
42475
42576
  if (configFormat === "hermes_config_yaml") {
42476
- launchEnv.HERMES_HOME = dirname11(mcpConfigPath);
42577
+ launchEnv.HERMES_HOME = dirname12(mcpConfigPath);
42477
42578
  launchEnv.HERMES_IGNORE_USER_CONFIG = "";
42478
42579
  }
42479
42580
  let autoImportContextFilePath;