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

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,13 @@
1
1
  # @deksden-com/dd-flow-cli
2
2
 
3
+ ## 0.9.0-beta.31
4
+
5
+ ### Patch Changes
6
+
7
+ - Treat an authoritative fully-idle Antigravity root Stop as settlement evidence
8
+ for otherwise unobserved native children, while preserving their distinct
9
+ `settled_by_root` status rather than claiming individual completion.
10
+
3
11
  ## 0.9.0-beta.30
4
12
 
5
13
  ### Patch Changes
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "cli_package": "@deksden-com/dd-flow-cli",
3
- "cli_version": "0.9.0-beta.30",
4
- "cli_commit": "88b1f0f37ffa205a7ed21b56054849d083707501",
5
- "built_at": "2026-09-07T14:11:41.176Z",
3
+ "cli_version": "0.9.0-beta.31",
4
+ "cli_commit": "f21c8317e762f978272536faff6822921dffeff5",
5
+ "built_at": "2026-09-07T16:20:23.716Z",
6
6
  "built_with_canon": {
7
7
  "version": "4.0.6",
8
8
  "commit": "c6fc50cb3b5526ea0162ee4454d1ee36f17be2da",
@@ -222,13 +222,17 @@ export class Runtime {
222
222
  if (!current && this.lastResult) { await this.journal("stale_terminal_observed", { result }); return; }
223
223
  this.lastResult = result; this.markActivity();
224
224
  const rootStop = this.sessionObservations.get(this.init?.conversation_id)?.stop;
225
- await this.persist({ active_tree: result.status === "RUNNING" || rootStop?.fullyIdle === false, last_result: result });
225
+ const pendingTerminal = current && result.status === "SUCCESS";
226
+ if (pendingTerminal) current.terminalResult = result;
227
+ if (pendingTerminal && rootStop?.fullyIdle === true) for (const [id, descendant] of this.descendants) if (descendant.status === "unknown") this.descendants.set(id, { ...descendant, status: "settled_by_root", settlement_evidence: "root_fully_idle_stop" });
228
+ const terminalReceipt = pendingTerminal ? this.receipt(result) : null;
229
+ await this.persist({ active_tree: pendingTerminal ? !terminalReceipt.settled : result.status === "RUNNING" || rootStop?.fullyIdle === false, last_result: result });
226
230
  try { this.usageIngest = await forwardUsage(this.config, result.conversation_id ?? this.init.conversation_id, result, this.toolSnapshot(), [...this.descendants.values()]); }
227
231
  catch (error) { this.usageIngest = { status: "failed", error: error.message ?? String(error) }; await this.journal("usage_ingest_failed", this.usageIngest); }
228
232
  if (!current) return;
229
233
  if (this.active !== current) return;
230
- this.active = null;
231
234
  if (result.status === "ERROR" || result.status === "CANCELLED") {
235
+ this.active = null;
232
236
  const message = String(result.error ?? "Antigravity returned ERROR"); const normalized = message.toLowerCase();
233
237
  const code = result.status === "CANCELLED" ? "agy_turn_cancelled" : /quota|insufficient.?credit|billing|usage limit/.test(normalized) ? "agy_provider_quota_exhausted"
234
238
  : /rate.?limit|too many requests|\b429\b/.test(normalized) ? "agy_provider_rate_limited"
@@ -236,17 +240,18 @@ export class Runtime {
236
240
  : "agy_provider_failed";
237
241
  return current.reject(new DaemonError(code, message, ["agy_provider_quota_exhausted", "agy_provider_rate_limited"].includes(code), { provider_result: result, provider_error_scope: current.previousError && current.previousError === result.error ? "repeated_conversation_error" : "terminal_result", usage_ingest: this.usageIngest }));
238
242
  }
239
- current.resolve(this.receipt(result));
243
+ if (terminalReceipt.settled) { this.active = null; current.resolve(terminalReceipt); }
240
244
  }
241
245
  receipt(result = this.lastResult) {
242
246
  const assistantText = typeof result?.response === "string" ? result.response : typeof result?.text === "string" ? result.text : null;
243
247
  const root = this.init?.conversation_id; const observation = this.sessionObservations.get(root);
244
- const treeSettled = [...this.descendants.values()].every(child => ["completed", "failed", "cancelled"].includes(child.status));
248
+ const treeSettled = [...this.descendants.values()].every(child => ["completed", "failed", "cancelled", "settled_by_root"].includes(child.status));
245
249
  // A terminal root result is the provider's authoritative end-of-turn
246
250
  // observation when AGY does not emit a root Stop hook. A received
247
251
  // non-idle Stop still wins, and every discovered child must be terminal.
248
252
  const rootSettled = observation?.stop?.fullyIdle !== false;
249
- return { harness: "antigravity-cli", runtime_family: "antigravity", provider_session_id: root ?? null, adapter_session_id: root ?? null, cwd: this.config.cwd, profile: this.profile, result, ...(assistantText === null ? {} : { assistant_text: assistantText }), usage: usageSnapshot(result ?? {}, this.toolSnapshot()), usage_ingest: this.usageIngest, descendants: [...this.descendants.values()], transcript_path: observation?.transcript_path ?? null, settled: Boolean(!this.active && result && ["SUCCESS", "ERROR", "CANCELLED"].includes(result.status) && treeSettled && rootSettled) };
253
+ const terminalPending = this.active?.terminalResult === result;
254
+ return { harness: "antigravity-cli", runtime_family: "antigravity", provider_session_id: root ?? null, adapter_session_id: root ?? null, cwd: this.config.cwd, profile: this.profile, result, ...(assistantText === null ? {} : { assistant_text: assistantText }), usage: usageSnapshot(result ?? {}, this.toolSnapshot()), usage_ingest: this.usageIngest, descendants: [...this.descendants.values()], transcript_path: observation?.transcript_path ?? null, settled: Boolean((!this.active || terminalPending) && result && ["SUCCESS", "ERROR", "CANCELLED"].includes(result.status) && treeSettled && rootSettled) };
250
255
  }
251
256
  async zeroUsage() { await forwardUsage(this.config, this.init.conversation_id, { usage: {} }, this.toolSnapshot(), []); }
252
257
  async prompt(text, assertDispatch = captureDispatchGuard(this)) { if (!text) throw new DaemonError("prompt_required", "prompt is required"); await this.start(); await this.draining; if (this.active) throw new DaemonError("operation_in_progress", "an Antigravity turn is already running"); if (this.lastResult && !this.receipt().settled) throw new DaemonError("tree_not_settled", "the prior Antigravity agent tree is not fully settled"); const root = this.init?.conversation_id; const prior = this.sessionObservations.get(root) ?? {}; this.turnGeneration++; this.sessionObservations.set(root, { ...prior, step_floor: prior.last_step_index ?? null, stop: null, last_hook: null }); this.lastProviderEvent = null; await this.persist({ active_tree: true }); return await new Promise((resolve, reject) => { assertDispatch(); this.active = { resolve, reject, generation: this.turnGeneration, previousError: this.lastResult?.error ?? null }; this.child.stdin.write(`${JSON.stringify({ event: "user", message: { content: text } })}\n`, error => { if (error) { this.active = null; reject(error); } }); }); }
@@ -258,10 +263,10 @@ export class Runtime {
258
263
  if (!child) throw new DaemonError("agy_child_identity_unconfirmed", "Antigravity hook has no confirmed native parent", true, { conversation_id: conversationId });
259
264
  return child;
260
265
  }
261
- async observeHook(event, payload) { const conversationId = payload.conversationId ?? this.init?.conversation_id; const child = await this.childForHook(conversationId); const prior = this.sessionObservations.get(conversationId) ?? {}; const execution = Number.isInteger(payload.executionNum) ? payload.executionNum : null; const step = Number.isInteger(payload.stepIdx) ? payload.stepIdx : null; const floor = prior.step_floor; const stale = step !== null && Number.isInteger(floor) && step <= floor; const fingerprint = createHash("sha256").update(JSON.stringify({ event, payload })).digest("hex"); if (!stale && prior.last_hook !== fingerprint) this.markActivity(); if (conversationId && !stale) this.sessionObservations.set(conversationId, { ...prior, last_hook: fingerprint, ...(payload.transcriptPath ? { transcript_path: payload.transcriptPath } : {}), ...(event === "Stop" ? { stop: { fullyIdle: payload.fullyIdle === true, terminationReason: payload.terminationReason ?? null, executionNum: payload.executionNum ?? null } } : {}) }); if (child && !stale && event === "Stop" && payload.fullyIdle === true) this.descendants.set(conversationId, { ...child, status: "completed" }); const parent = child?.parent_provider_session_id ?? null;
266
+ async observeHook(event, payload) { const conversationId = payload.conversationId ?? this.init?.conversation_id; const child = await this.childForHook(conversationId); const prior = this.sessionObservations.get(conversationId) ?? {}; const execution = Number.isInteger(payload.executionNum) ? payload.executionNum : null; const step = Number.isInteger(payload.stepIdx) ? payload.stepIdx : null; const floor = prior.step_floor; const stale = step !== null && Number.isInteger(floor) && step <= floor; const fingerprint = createHash("sha256").update(JSON.stringify({ event, payload })).digest("hex"); if (!stale && prior.last_hook !== fingerprint) this.markActivity(); if (conversationId && !stale) this.sessionObservations.set(conversationId, { ...prior, last_hook: fingerprint, ...(payload.transcriptPath ? { transcript_path: payload.transcriptPath } : {}), ...(event === "Stop" ? { stop: { fullyIdle: payload.fullyIdle === true, terminationReason: payload.terminationReason ?? null, executionNum: payload.executionNum ?? null } } : {}) }); if (child && !stale && event === "Stop" && payload.fullyIdle === true) this.descendants.set(conversationId, { ...child, status: "completed" }); const terminalPending = this.active?.terminalResult === this.lastResult; if (!child && !stale && event === "Stop" && payload.fullyIdle === true && (!this.active || terminalPending) && ["SUCCESS", "ERROR", "CANCELLED"].includes(this.lastResult?.status)) for (const [id, descendant] of this.descendants) if (descendant.status === "unknown") this.descendants.set(id, { ...descendant, status: "settled_by_root", settlement_evidence: "root_fully_idle_stop" }); const settled = terminalPending ? this.receipt(this.lastResult) : null; const completed = settled?.settled ? this.active : null; if (completed) this.active = null; const parent = child?.parent_provider_session_id ?? null;
262
267
  const observed = payload.model ? observedProfile(payload) : {};
263
268
  await observeModel({ journal: this.config.journal, harness: "antigravity-cli", sessionId: conversationId, parentSessionId: parent, requested: this.config, observed, source: "native.hook", evidence: payload.model ? "configured" : "unavailable", reason: payload.model ? null : "hook_omits_current_model" });
264
- const eventId = createHash("sha256").update(JSON.stringify({ daemon: this.config.daemonId, event, conversationId, turn_generation: this.turnGeneration, fully_idle: payload.fullyIdle ?? null, termination_reason: payload.terminationReason ?? null, execution: payload.executionNum ?? null, step: payload.stepIdx ?? null, tool: payload.toolCall?.name ?? null, args: payload.toolCall?.args ?? null })).digest("hex"); await this.persist(); await this.journal("hook", { stale, hook_event: event, event_id: eventId, conversation_id: conversationId, turn_generation: this.turnGeneration, execution_num: execution, step_index: step, termination_reason: payload.terminationReason ?? null, fully_idle: payload.fullyIdle ?? null }); return { event_id: eventId, daemon_id: this.config.daemonId, parent_conversation_id: parent, profile: observed, requested_profile: { provider: this.config.provider, model: this.config.model, reasoning: this.config.reasoning, mode: this.config.mode }, project_root: this.config.projectRoot, dd_flow_bin: this.config.ddFlowBin, dd_flow_home: this.config.ddFlowHome };
269
+ const eventId = createHash("sha256").update(JSON.stringify({ daemon: this.config.daemonId, event, conversationId, turn_generation: this.turnGeneration, fully_idle: payload.fullyIdle ?? null, termination_reason: payload.terminationReason ?? null, execution: payload.executionNum ?? null, step: payload.stepIdx ?? null, tool: payload.toolCall?.name ?? null, args: payload.toolCall?.args ?? null })).digest("hex"); await this.persist(completed ? { active_tree: false } : {}); await this.journal("hook", { stale, hook_event: event, event_id: eventId, conversation_id: conversationId, turn_generation: this.turnGeneration, execution_num: execution, step_index: step, termination_reason: payload.terminationReason ?? null, fully_idle: payload.fullyIdle ?? null }); completed?.resolve(settled); return { event_id: eventId, daemon_id: this.config.daemonId, parent_conversation_id: parent, profile: observed, requested_profile: { provider: this.config.provider, model: this.config.model, reasoning: this.config.reasoning, mode: this.config.mode }, project_root: this.config.projectRoot, dd_flow_bin: this.config.ddFlowBin, dd_flow_home: this.config.ddFlowHome };
265
270
  }
266
271
  async cancel() { cancelPendingDispatch(this); if (!this.child || this.exited) return { ...this.receipt(), settled: Boolean(this.exited) }; try { process.kill(-this.child.pid, "SIGINT"); } catch (error) { if (error.code !== "ESRCH") throw error; } const deadline = Date.now() + 5000; while (!this.exited && Date.now() < deadline) await new Promise(resolve => setTimeout(resolve, 100)); if (!this.exited) await stopProcessGroup(this.child, 5_000); await this.persist({ active_tree: false }); return { ...this.receipt(), cancelled: true, settled: Boolean(this.exited) };
267
272
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deksden-com/dd-flow-cli",
3
- "version": "0.9.0-beta.30",
3
+ "version": "0.9.0-beta.31",
4
4
  "description": "Mechanical runtime CLI for dd-flow workflows.",
5
5
  "type": "module",
6
6
  "bin": {