@deksden-com/dd-flow-cli 0.9.0-beta.29 → 0.9.0-beta.30

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @deksden-com/dd-flow-cli
2
2
 
3
+ ## 0.9.0-beta.30
4
+
5
+ ### Patch Changes
6
+
7
+ - Bound Antigravity prompt liveness from the most recent native activity and preserve clean recovery settlement after an expired prompt.
8
+
3
9
  ## 0.9.0-beta.29
4
10
 
5
11
  ### Patch Changes
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "cli_package": "@deksden-com/dd-flow-cli",
3
- "cli_version": "0.9.0-beta.29",
4
- "cli_commit": "8b0e5fe4a8991172c6d6d5f0963a0adf5bc2228d",
5
- "built_at": "2026-09-07T13:07:57.099Z",
3
+ "cli_version": "0.9.0-beta.30",
4
+ "cli_commit": "88b1f0f37ffa205a7ed21b56054849d083707501",
5
+ "built_at": "2026-09-07T14:11:41.176Z",
6
6
  "built_with_canon": {
7
7
  "version": "4.0.6",
8
8
  "commit": "c6fc50cb3b5526ea0162ee4454d1ee36f17be2da",
@@ -3,7 +3,7 @@ import { createHash, randomUUID } from "node:crypto";
3
3
  import { appendFile, chmod, lstat, mkdir, open, readFile, realpath, rename, unlink, writeFile } from "node:fs/promises";
4
4
  import { spawn } from "node:child_process";
5
5
  import net from "node:net";
6
- import { observedTimeout } from "./observation-clock.mjs";
6
+ import { ObservationClock } from "./observation-clock.mjs";
7
7
  import { durableDaemonDispatch } from "./daemon-operations.mjs";
8
8
  import path from "node:path";
9
9
  import { observeModel } from "./model-observations.mjs";
@@ -74,25 +74,33 @@ async function prepare(paths, config) {
74
74
  export async function callDaemon(stateDir, operation, params = {}, timeoutMs = 30_000) {
75
75
  const paths = locations(stateDir), request = { schema_id: REQUEST_SCHEMA, id: process.env.DD_EVAL_OPERATION_ID ?? randomUUID(), operation, params };
76
76
  return await new Promise((resolve, reject) => {
77
- const client = net.createConnection(paths.socket); let buffer = "", settled = false, timer, lastActivity = Date.now();
78
- const finish = (error, result) => { if (settled) return; settled = true; if (timer) clearTimeout(timer); client.destroy(); error ? reject(error) : resolve(result); };
79
- const arm = () => { timer = observedTimeout(async () => {
80
- // Prompt RPCs return only at the end. Native events update this durable
81
- // timestamp, so a moving timestamp proves productive provider activity.
82
- if (operation !== "session.prompt") return finish(new DaemonError("daemon_timeout", `${operation} timed out`, true));
83
- let status = null;
77
+ const client = net.createConnection(paths.socket); let buffer = "", settled = false, timer, checking = false, lastActivity = Date.now();
78
+ const clock = new ObservationClock({ timeoutMs, onGap: gap => { void appendFile(paths.journal, `${JSON.stringify({ kind: "observation_gap", payload: { ...gap, operation_id: request.id } })}\n`, { mode: 0o600 }).catch(() => {}); } });
79
+ const finish = (error, result) => { if (settled) return; settled = true; if (timer) clearInterval(timer); client.destroy(); error ? reject(error) : resolve(result); };
80
+ const poll = Math.min(5_000, Math.max(25, Math.floor(timeoutMs / 20)));
81
+ timer = setInterval(async () => {
82
+ if (settled || checking) return;
83
+ checking = true;
84
84
  try {
85
- status = await callDaemon(stateDir, "daemon.status", {}, Math.min(timeoutMs, 5_000));
86
- const observed = Date.parse(status.last_activity_at ?? "");
87
- if (status.active_tree && Number.isFinite(observed) && observed > lastActivity) { lastActivity = observed; return arm(); }
88
- } catch { /* The original RPC retains the authoritative failure. */ }
89
- finish(new DaemonError(status?.active_tree ? "subject_liveness_timeout" : "operation_observation_lost", `${operation} has no recent productive provider activity`, true, {
90
- operation_id: request.id,
91
- last_activity_at: status?.last_activity_at ?? null,
92
- active_tree: status?.active_tree === true
93
- }));
94
- }, timeoutMs, { onGap: gap => { void appendFile(paths.journal, `${JSON.stringify({ kind: "observation_gap", payload: { ...gap, operation_id: request.id } })}\n`, { mode: 0o600 }).catch(() => {}); } }); };
95
- arm(); client.setEncoding("utf8"); client.on("connect", () => client.write(`${JSON.stringify(request)}\n`)); client.on("data", chunk => { buffer += chunk; const index = buffer.indexOf("\n"); if (index < 0) return; try { const response = JSON.parse(buffer.slice(0, index)); response.ok ? finish(null, response.result) : finish(new DaemonError(response.error.code, response.error.message, response.error.retryable, response.error.details)); } catch (error) { finish(new DaemonError("daemon_protocol_mismatch", error.message)); } }); client.on("error", error => finish(new DaemonError("daemon_not_running", error.message, true))); client.on("end", () => finish(new DaemonError("daemon_connection_closed", `${operation} connection closed before a response`, true))); client.on("close", () => finish(new DaemonError("daemon_connection_closed", `${operation} connection closed before a response`, true)));
85
+ if (operation !== "session.prompt") {
86
+ if (clock.sample()) finish(new DaemonError("daemon_timeout", `${operation} timed out`, true));
87
+ return;
88
+ }
89
+ let status = null;
90
+ try { status = await callDaemon(stateDir, "daemon.status", {}, Math.min(timeoutMs, 5_000)); }
91
+ catch { /* The original RPC retains the authoritative failure. */ }
92
+ const observed = Date.parse(status?.last_activity_at ?? "");
93
+ const progressed = status?.active_tree === true && Number.isFinite(observed) && observed > lastActivity;
94
+ if (progressed) lastActivity = observed;
95
+ if (!clock.sample(progressed ? lastActivity : undefined)) return;
96
+ finish(new DaemonError(status?.active_tree ? "subject_liveness_timeout" : "operation_observation_lost", `${operation} has no recent productive provider activity`, true, {
97
+ operation_id: request.id,
98
+ last_activity_at: status?.last_activity_at ?? null,
99
+ active_tree: status?.active_tree === true
100
+ }));
101
+ } finally { checking = false; }
102
+ }, poll);
103
+ client.setEncoding("utf8"); client.on("connect", () => client.write(`${JSON.stringify(request)}\n`)); client.on("data", chunk => { buffer += chunk; const index = buffer.indexOf("\n"); if (index < 0) return; try { const response = JSON.parse(buffer.slice(0, index)); response.ok ? finish(null, response.result) : finish(new DaemonError(response.error.code, response.error.message, response.error.retryable, response.error.details)); } catch (error) { finish(new DaemonError("daemon_protocol_mismatch", error.message)); } }); client.on("error", error => finish(new DaemonError("daemon_not_running", error.message, true))); client.on("end", () => finish(new DaemonError("daemon_connection_closed", `${operation} connection closed before a response`, true))); client.on("close", () => finish(new DaemonError("daemon_connection_closed", `${operation} connection closed before a response`, true)));
96
104
  });
97
105
  }
98
106
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deksden-com/dd-flow-cli",
3
- "version": "0.9.0-beta.29",
3
+ "version": "0.9.0-beta.30",
4
4
  "description": "Mechanical runtime CLI for dd-flow workflows.",
5
5
  "type": "module",
6
6
  "bin": {