@adhdev/daemon-core 0.9.82-rc.216 → 0.9.82-rc.217

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.mjs CHANGED
@@ -27615,8 +27615,9 @@ function evaluateCondition(cond, sections, fullScreen, cursor, prevLines, trace,
27615
27615
  const endRow = cursor.row;
27616
27616
  const currentSlice = curLines.slice(startRow, endRow).join("\n");
27617
27617
  const prevSlice = prevLines.slice(startRow, endRow).join("\n");
27618
- const result = currentSlice !== prevSlice;
27619
- trace.push({ kind: "section", text: `state[${stateId}] changed cond cursor_above=${cond.cursor_above} rows[${startRow},${endRow}) changed=${result}` });
27618
+ const didChange = currentSlice !== prevSlice;
27619
+ const result = cond.changed ? didChange : !didChange;
27620
+ trace.push({ kind: "section", text: `state[${stateId}] changed cond cursor_above=${cond.cursor_above} rows[${startRow},${endRow}) changed=${didChange} expected=${cond.changed} result=${result}` });
27620
27621
  return result;
27621
27622
  }
27622
27623
  if (isRegexCondition(cond)) {
@@ -28339,7 +28340,8 @@ var SCHEMA_V3 = {
28339
28340
  "required": ["cursor_above", "changed"],
28340
28341
  "properties": {
28341
28342
  "cursor_above": { "type": "integer", "minimum": 1 },
28342
- "changed": { "type": "boolean", "const": true }
28343
+ "changed": { "type": "boolean" },
28344
+ "stable_ms": { "type": "integer", "minimum": 0 }
28343
28345
  }
28344
28346
  },
28345
28347
  "allCondition": {
@@ -28815,6 +28817,13 @@ init_logger();
28815
28817
  var STARTUP_GRACE_MS = 2500;
28816
28818
  var BUSY_HOLD_MS = 6e3;
28817
28819
  var SUBMIT_DELAY_FLOOR_MS = 200;
28820
+ function collectChangedConditions(when) {
28821
+ if (!when) return [];
28822
+ if ("cursor_above" in when && "changed" in when) return [when];
28823
+ if ("all" in when) return when.all.flatMap((c) => collectChangedConditions(c));
28824
+ if ("any" in when) return when.any.flatMap((c) => collectChangedConditions(c));
28825
+ return [];
28826
+ }
28818
28827
  function countNewlines(s) {
28819
28828
  let n = 0;
28820
28829
  for (let i = 0; i < s.length; i += 1) if (s.charCodeAt(i) === 10) n += 1;
@@ -28920,6 +28929,9 @@ var SpecDriver = class {
28920
28929
  * Used by screen_active_hold_ms to suppress idle downshifts while
28921
28930
  * the terminal is still actively updating. */
28922
28931
  lastScreenChangedAt = 0;
28932
+ /** Per-cursor_above region last-changed timestamps for stable_ms tracking.
28933
+ * Key: cursor_above value. Value: last time that region changed. */
28934
+ regionLastChangedAt = /* @__PURE__ */ new Map();
28923
28935
  /** Timer that re-runs evaluate() once the hold window expires. Needed
28924
28936
  * because the PTY stops emitting once the agent finishes; without an
28925
28937
  * explicit wake-up there's nothing to trigger the busy → idle
@@ -29124,18 +29136,47 @@ var SpecDriver = class {
29124
29136
  const cursor = this.adapter.getCursorPosition();
29125
29137
  const currentLines = screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
29126
29138
  const ev = evaluate(this.spec, screen, cursor, this.prevScreenLines.length > 0 ? this.prevScreenLines : void 0);
29139
+ const now = Date.now();
29127
29140
  if (this.prevScreenLines.length > 0 && currentLines.join("\n") !== this.prevScreenLines.join("\n")) {
29128
- this.lastScreenChangedAt = Date.now();
29141
+ this.lastScreenChangedAt = now;
29142
+ }
29143
+ if (cursor && this.prevScreenLines.length > 0) {
29144
+ for (const state of this.spec.states) {
29145
+ const conditions = collectChangedConditions(state.when);
29146
+ for (const cond of conditions) {
29147
+ if (cond.stable_ms == null) continue;
29148
+ const startRow = Math.max(0, cursor.row - cond.cursor_above);
29149
+ const endRow = cursor.row;
29150
+ const cur = currentLines.slice(startRow, endRow).join("\n");
29151
+ const prev = this.prevScreenLines.slice(startRow, endRow).join("\n");
29152
+ if (cur !== prev) this.regionLastChangedAt.set(cond.cursor_above, now);
29153
+ }
29154
+ }
29129
29155
  }
29130
29156
  this.prevScreenLines = currentLines;
29131
29157
  let evState = ev.state;
29132
29158
  const busyHoldMs = this.spec.debounce?.busy_hold_ms ?? BUSY_HOLD_MS;
29133
29159
  if (this.currentStateId === "busy" && evState.id === "idle") {
29134
- const ageMs = Date.now() - this.lastBusyAt;
29160
+ const ageMs = now - this.lastBusyAt;
29135
29161
  if (ageMs < busyHoldMs) {
29136
29162
  evState = this.lastBusyState ?? evState;
29137
29163
  }
29138
29164
  }
29165
+ if (evState.id === (this.spec.default_state ?? "idle") && cursor) {
29166
+ const stableConditions = collectChangedConditions(
29167
+ this.spec.states.find((s) => s.id === evState.id)?.when
29168
+ ).filter((c) => c.changed === false && c.stable_ms != null);
29169
+ for (const cond of stableConditions) {
29170
+ const lastChanged = this.regionLastChangedAt.get(cond.cursor_above) ?? 0;
29171
+ const stableMs = cond.stable_ms;
29172
+ const stableAge = lastChanged > 0 ? now - lastChanged : Infinity;
29173
+ if (stableAge < stableMs) {
29174
+ evState = this.lastBusyState ?? { id: "busy", label: "Generating", title: null };
29175
+ this.scheduleBusyExpiry(stableMs - stableAge + 50);
29176
+ break;
29177
+ }
29178
+ }
29179
+ }
29139
29180
  const idleStateId = this.spec.default_state ?? "idle";
29140
29181
  const isModalState = (id) => id !== null && id !== "busy" && id !== idleStateId;
29141
29182
  if (isModalState(this.currentStateId) && evState.id === "busy") {
@@ -29148,7 +29189,6 @@ var SpecDriver = class {
29148
29189
  const screenActiveHoldMs = this.spec.debounce?.screen_active_hold_ms;
29149
29190
  let busyWakeMs = busyHoldMs;
29150
29191
  const postModalGraceMs = busyHoldMs;
29151
- const now = Date.now();
29152
29192
  const recentlyInModal = isModalState(this.currentStateId) || this.lastModalAt > 0 && now - this.lastModalAt < postModalGraceMs;
29153
29193
  const recentlyLeftModal = !recentlyInModal && this.lastModalExitAt > 0 && now - this.lastModalExitAt < postModalGraceMs;
29154
29194
  const screenActiveMs = screenActiveHoldMs ?? 0;
@@ -29160,15 +29200,14 @@ var SpecDriver = class {
29160
29200
  if (evState.id === "busy" && completionIdleRule && !recentlyInModal && !recentlyLeftModal && !screenIsActive) {
29161
29201
  const completionKey = matchesCompletionIdleRule(this.spec, ev, screen);
29162
29202
  if (completionKey) {
29163
- const now2 = Date.now();
29164
29203
  if (completionKey !== this.completionIdleKey) {
29165
29204
  this.completionIdleKey = completionKey;
29166
- this.completionIdleFirstSeenAt = now2;
29205
+ this.completionIdleFirstSeenAt = now;
29167
29206
  LOG.debug("SpecDriver", `[${this.opts.specPath.split("/").slice(-3).join("/")}] completion_idle_after matched: key="${completionKey}"`);
29168
29207
  }
29169
29208
  const holdMs = Math.max(0, completionIdleRule.hold_ms || 0);
29170
29209
  const forceAfterMs = typeof completionIdleRule.force_after_ms === "number" ? completionIdleRule.force_after_ms : null;
29171
- const ageMs = now2 - this.completionIdleFirstSeenAt;
29210
+ const ageMs = now - this.completionIdleFirstSeenAt;
29172
29211
  if (ageMs >= holdMs) {
29173
29212
  const targetMatches = matchesCompletionIdleTargetState(this.spec, ev, screen, cursor);
29174
29213
  const forced = !targetMatches && forceAfterMs !== null && ageMs >= holdMs + forceAfterMs;