@deksden-com/dd-flow-cli 0.9.0-beta.141 → 0.9.0-beta.142

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,12 @@
1
1
  # @deksden-com/dd-flow-cli
2
2
 
3
+ ## 0.9.0-beta.142
4
+
5
+ ### Patch Changes
6
+
7
+ - Wait for the owned OpenCode v2 listener through typed SDK transport startup failures and expose bounded read-only scope inventory batches for validated retirement-proof reuse.
8
+ - Retry no-effect physical confirmation on the same held check, bootstrap or service process before dispatch, without replaying commands or extending exhausted authority.
9
+
3
10
  ## 0.9.0-beta.141
4
11
 
5
12
  ### Patch Changes
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "cli_package": "@deksden-com/dd-flow-cli",
3
- "cli_version": "0.9.0-beta.141",
4
- "cli_commit": "c370c6e9beaefba2216bbf5bbadac02e4827768f",
5
- "built_at": "2026-10-17T22:37:48.634Z",
3
+ "cli_version": "0.9.0-beta.142",
4
+ "cli_commit": "a5b068e5cc52809ac0ea0687e40a49f9379bb198",
5
+ "built_at": "2026-10-18T10:20:25.955Z",
6
6
  "built_with_canon": {
7
7
  "version": "4.1.2",
8
8
  "commit": "1ddedf9d3f086e046f8e7b194c739ec386e7c8b9",
@@ -156,7 +156,7 @@ export const commandInputs = {
156
156
  "run control pause": route(0, `${control} request-id`, "grace-ms wait-ms escalate", "force"),
157
157
  "run control stop": route(0, `${control} request-id`, "grace-ms wait-ms escalate", "force"),
158
158
  "runtime scope fence": route(0, "scope-id request-id"),
159
- "runtime scope status": route(0, "scope-id"),
159
+ "runtime scope status": route(0, "", "scope-id scope-ids-json"),
160
160
  "runtime scope stop": route(0, "scope-id request-id"),
161
161
  "runtime scope control": route(0, "scope-id request-id mode", "manifest-json", "", "", { mode: ["pause", "stop"] }),
162
162
  "runtime scope serve": route(0, "scope-id generation"),
@@ -143,6 +143,7 @@ export const runtimeOptionDescriptions = {
143
143
  schema: "Registered JSON schema name against which the supplied file is validated.",
144
144
  "schema-dir": "Explicit schema directory for validation; not a workflow handler registry or permission to execute code.",
145
145
  "scope-id": "Exact retained runtime/EVAL scope identity; operations must not affect neighboring scopes.",
146
+ "scope-ids-json": "Read-only JSON array of 1..16 unique retained scope IDs; returns ordered fresh inventories without fencing or stopping them.",
146
147
  "semantic-file": "Stage semantic result artifact; applicable schema and finish checks are selected independently.",
147
148
  session: "Physical Session selector for source-backed usage aggregation.",
148
149
  "session-id": "Exact retained physical Session identity for registration, status, stop or usage synchronization.",
package/dist/cli/help.js CHANGED
@@ -169,6 +169,7 @@ never registers a project, selects a model automatically or creates a runtime DB
169
169
  ["runtime scope", `Usage:
170
170
  dd-flow runtime scope fence --scope-id EVAL_ID --request-id ID --json
171
171
  dd-flow runtime scope status --scope-id EVAL_ID --json
172
+ dd-flow runtime scope status --scope-ids-json '["EVAL_ID","QUAL_ID"]' --json
172
173
  dd-flow runtime scope stop --scope-id EVAL_ID --request-id ID --json
173
174
  dd-flow runtime scope control --scope-id EVAL_ID --request-id ID --mode pause|stop [--manifest-json JSON] --json
174
175
  dd-flow runtime scope resume --scope-id EVAL_ID --request-id ID --generation N --capture-key SHA256 --json
@@ -92,7 +92,7 @@ import { prepareCommandRoute } from "./command-inputs.js";
92
92
  import { parseLifecycleCommand } from "../services/lifecycle-command.js";
93
93
  import { dispatchRuntimeProcess, prepareRuntimeProcess } from "./runtime-process.js";
94
94
  import { reconcileFlowRunHealth } from "../services/run-health.js";
95
- import { fenceRuntimeBudget, runtimeBudgetStatus, prepareRuntimeScopeControl, prepareRuntimeScopeFence } from "../services/runtime-budget.js";
95
+ import { fenceRuntimeBudget, runtimeBudgetStatus, prepareRuntimeScopeStatus, prepareRuntimeScopeControl, prepareRuntimeScopeFence } from "../services/runtime-budget.js";
96
96
  import { stopRuntimeScope } from "../services/runtime-scope-stop.js";
97
97
  import { launchRuntimeScopeControl, launchRuntimeScopeResume, prepareRuntimeScopeReconciliation, prepareRuntimeScopeServing, reconcileRuntimeScopeControl, serveRuntimeScopeControl } from "../services/runtime-scope-worker.js";
98
98
  import { prepareRuntimeScopeResumeRequest } from "../services/runtime-scope-resume.js";
@@ -1017,6 +1017,8 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
1017
1017
  return prepareError(error, { parameter: "from/output" });
1018
1018
  }
1019
1019
  }
1020
+ if (route.key === "runtime scope status")
1021
+ prepareRuntimeScopeStatus(optionalOption(parsed, "scope-id"), optionalOption(parsed, "scope-ids-json"));
1020
1022
  if (["runtime scope fence", "runtime scope stop"].includes(route.key))
1021
1023
  prepareRuntimeScopeFence(requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"));
1022
1024
  if (route.key === "runtime scope resume")
@@ -2778,8 +2780,12 @@ async function dispatchRuntime(context, command, parsed, progress, prepared = {}
2778
2780
  return { ok: true, ...await stopRuntimeScope(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id")) };
2779
2781
  }
2780
2782
  if (parsed.positional.length === 1 && parsed.positional[0] === "status") {
2781
- if ([...parsed.options.keys()].some(key => key !== "scope-id") || [...parsed.options.values()].some(values => values.length !== 1))
2782
- throw new AppError("usage", "Usage: dd-flow runtime scope status --scope-id EVAL_ID", 2);
2783
+ if ([...parsed.options.keys()].some(key => !["scope-id", "scope-ids-json"].includes(key)) || parsed.options.size !== 1 || [...parsed.options.values()].some(values => values.length !== 1))
2784
+ throw new AppError("usage", "Use exactly one of --scope-id or --scope-ids-json", 2);
2785
+ const ids = prepareRuntimeScopeStatus(optionalOption(parsed, "scope-id"), optionalOption(parsed, "scope-ids-json"));
2786
+ if (parsed.options.has("scope-ids-json")) {
2787
+ return { ok: true, scopes: ids.map(id => runtimeBudgetStatus(context, id)) };
2788
+ }
2783
2789
  return { ok: true, ...runtimeBudgetStatus(context, requiredOption(parsed, "scope-id")) };
2784
2790
  }
2785
2791
  if (parsed.positional.length !== 1 || parsed.positional[0] !== "fence" || [...parsed.options.keys()].some(key => !["scope-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
@@ -499,7 +499,9 @@ export async function awaitOpenCodeStartup(client, state, child, { timeoutMs = S
499
499
  if (["loading", "activating", "registered"].includes(plugin?.state?.status)) await mark(`required_plugin_${plugin.state.status}`);
500
500
  } catch (error) {
501
501
  const transient = error.code === "opencode_unavailable" && error.retryable === true
502
- && (error.details?.native_type === "ServiceUnavailableError" || phase === "server" && ["TypeError", "ConnectionError", "Error"].includes(error.details?.native_type));
502
+ // Safe pre-identity reads use the normalized observation category,
503
+ // not a version-specific SDK error wrapper. No productive RPC here.
504
+ && (phase === "server" || error.details?.native_type === "ServiceUnavailableError");
503
505
  if (!transient) throw error;
504
506
  }
505
507
  const pause = Math.min([250, 500, 1000, 2000][Math.min(attempt++, 3)], remaining());
@@ -1245,6 +1245,7 @@ export function runCheck(command, cwd, environment, stdout, stderr, heartbeat, o
1245
1245
  let failure;
1246
1246
  let stopping = false;
1247
1247
  let closed = false;
1248
+ let admitted = false;
1248
1249
  let progress;
1249
1250
  let cleanup;
1250
1251
  let cleanupUnconfirmed = false;
@@ -1280,7 +1281,7 @@ export function runCheck(command, cwd, environment, stdout, stderr, heartbeat, o
1280
1281
  });
1281
1282
  };
1282
1283
  const observationTimer = setInterval(() => {
1283
- if (stopping || closed || observation.phase?.() === "serving")
1284
+ if (stopping || closed || !admitted || observation.phase?.() === "serving")
1284
1285
  return;
1285
1286
  if (runnerClock && runnerProgress) {
1286
1287
  try {
@@ -1331,22 +1332,6 @@ export function runCheck(command, cwd, environment, stdout, stderr, heartbeat, o
1331
1332
  error = "check process did not provide a PID";
1332
1333
  return;
1333
1334
  }
1334
- try {
1335
- onSpawn(child.pid);
1336
- }
1337
- catch (cause) {
1338
- stop(cause);
1339
- return;
1340
- }
1341
- if (observation.leaseExpiresAt) {
1342
- const expiry = observation.leaseExpiresAt();
1343
- if (!expiry || !renewalConfirmed(renewal, performance.now(), expiry)) {
1344
- stop("check confirmation has no current authority");
1345
- return;
1346
- }
1347
- }
1348
- child.stdin.end("admitted\n");
1349
- clock.sample(outputCursor);
1350
1335
  const renew = () => {
1351
1336
  if (stopping || closed)
1352
1337
  return;
@@ -1394,7 +1379,46 @@ export function runCheck(command, cwd, environment, stdout, stderr, heartbeat, o
1394
1379
  stop(cause);
1395
1380
  }
1396
1381
  };
1397
- progress = setInterval(renew, 15_000);
1382
+ // The shell is held on stdin: a no-effect identity read may be retried,
1383
+ // but never the command or an admission callback with unknown effects.
1384
+ const confirmation = createRenewalState();
1385
+ confirmation.uncertainSince = performance.now();
1386
+ let confirmationError;
1387
+ const admit = () => {
1388
+ renewalTimer = undefined;
1389
+ if (stopping || closed)
1390
+ return;
1391
+ if (confirmationError && !renewalRemaining(confirmation)) {
1392
+ stop(confirmationError);
1393
+ return;
1394
+ }
1395
+ try {
1396
+ onSpawn(child.pid);
1397
+ }
1398
+ catch (cause) {
1399
+ if (cause instanceof AppError && cause.code === "process_ownership_unknown"
1400
+ && cause.details.reason === "physical_identity_unavailable" && maintenanceRetryable(cause)
1401
+ && renewalFailure(confirmation)) {
1402
+ confirmationError = cause;
1403
+ renewalTimer = setTimeout(admit, Math.min(renewalDelay(confirmation), renewalRemaining(confirmation)));
1404
+ return;
1405
+ }
1406
+ stop(cause);
1407
+ return;
1408
+ }
1409
+ if (observation.leaseExpiresAt) {
1410
+ const expiry = observation.leaseExpiresAt();
1411
+ if (!expiry || !renewalConfirmed(renewal, performance.now(), expiry)) {
1412
+ stop("check confirmation has no current authority");
1413
+ return;
1414
+ }
1415
+ }
1416
+ admitted = true;
1417
+ child.stdin.end("admitted\n");
1418
+ clock.sample(++outputCursor);
1419
+ progress = setInterval(renew, 15_000);
1420
+ };
1421
+ admit();
1398
1422
  });
1399
1423
  }
1400
1424
  function checkShellScript() {
@@ -199,6 +199,22 @@ export function admitRuntimeCheck(context, id, leaseToken) {
199
199
  }
200
200
  }
201
201
  /** Scope selection uses retained registration, never a current profile or PID search. */
202
+ export function prepareRuntimeScopeStatus(scopeId, scopeIdsJson) {
203
+ if (Boolean(scopeId) === Boolean(scopeIdsJson))
204
+ throw new AppError("usage", "Use exactly one of --scope-id or --scope-ids-json", 2);
205
+ let ids;
206
+ try {
207
+ ids = scopeId ? [scopeId] : JSON.parse(scopeIdsJson);
208
+ }
209
+ catch {
210
+ throw new AppError("usage", "Scope inventory requires JSON IDs", 2);
211
+ }
212
+ if (!Array.isArray(ids) || !ids.length || ids.length > 16 || ids.some(id => typeof id !== "string") || new Set(ids).size !== ids.length)
213
+ throw new AppError("usage", "Scope inventory requires 1..16 unique scope IDs", 2);
214
+ for (const id of ids)
215
+ parseRuntimeBudget(JSON.stringify({ schema_id: "dd-flow/runtime-budget@1", scope_id: id, per_harness: {} }));
216
+ return ids;
217
+ }
202
218
  export function runtimeBudgetStatus(context, scopeId) {
203
219
  parseRuntimeBudget(JSON.stringify({ schema_id: "dd-flow/runtime-budget@1", scope_id: scopeId, per_harness: {} }));
204
220
  return withResourceReadSnapshot(resourceHome(context), db => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deksden-com/dd-flow-cli",
3
- "version": "0.9.0-beta.141",
3
+ "version": "0.9.0-beta.142",
4
4
  "description": "Mechanical runtime CLI for dd-flow workflows.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.23.0",