@deksden-com/dd-flow-cli 0.9.0-beta.38 → 0.9.0-beta.40

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/dist/build-info.json +5 -5
  3. package/dist/cli/help.js +42 -1
  4. package/dist/cli/run-cli.js +147 -17
  5. package/dist/harness-runtime/bin/dd-agy.mjs +3 -2
  6. package/dist/harness-runtime/bin/dd-codex.mjs +4 -3
  7. package/dist/harness-runtime/bin/dd-droid.mjs +3 -2
  8. package/dist/harness-runtime/bin/dd-grok.mjs +4 -3
  9. package/dist/harness-runtime/bin/dd-opencode.mjs +4 -3
  10. package/dist/harness-runtime/bin/dd-zcode.mjs +4 -3
  11. package/dist/harness-runtime/lib/daemon-operations.mjs +48 -9
  12. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +49 -12
  13. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +57 -16
  14. package/dist/harness-runtime/lib/dd-codex.mjs +10 -2
  15. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +3 -2
  16. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +12 -6
  17. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +4 -3
  18. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +8 -5
  19. package/dist/harness-runtime/lib/managed-daemon.mjs +125 -6
  20. package/dist/harness-runtime/lib/process-json.mjs +1 -1
  21. package/dist/harness-runtime/lib/session-settlement.mjs +4 -2
  22. package/dist/schemas/run-control-receipt.schema.json +99 -0
  23. package/dist/schemas/run-control-request.schema.json +36 -0
  24. package/dist/services/cleanup.js +7 -4
  25. package/dist/services/cli-operation-classifier.js +3 -1
  26. package/dist/services/code-checks.js +33 -7
  27. package/dist/services/controller-fanout.js +105 -0
  28. package/dist/services/engines.js +2 -0
  29. package/dist/services/eval-snapshots.js +379 -36
  30. package/dist/services/execution-policy.js +122 -6
  31. package/dist/services/external-work-launch.js +33 -12
  32. package/dist/services/harness-adapter.js +49 -2
  33. package/dist/services/hooks.js +5 -5
  34. package/dist/services/managed-daemon-binding.js +38 -0
  35. package/dist/services/managed-processes.js +54 -7
  36. package/dist/services/merge-server.js +53 -30
  37. package/dist/services/native-daemon-history.js +49 -0
  38. package/dist/services/native-session-control.js +31 -0
  39. package/dist/services/protocols.js +9 -0
  40. package/dist/services/recovery-snapshot-database.js +103 -0
  41. package/dist/services/run-control-receipt.js +56 -0
  42. package/dist/services/run-control-worker.js +297 -0
  43. package/dist/services/run-control.js +752 -0
  44. package/dist/services/run-controller-adapter.js +79 -0
  45. package/dist/services/run-controller-capture.js +117 -0
  46. package/dist/services/run-controller-process.js +178 -0
  47. package/dist/services/run-controller-recovery.js +219 -0
  48. package/dist/services/run-controller-state.js +31 -0
  49. package/dist/services/run-controller.js +539 -0
  50. package/dist/services/run-recovery-runtime.js +69 -0
  51. package/dist/services/run-recovery.js +33 -9
  52. package/dist/services/runs.js +65 -13
  53. package/dist/services/runtime-budget.js +362 -0
  54. package/dist/services/runtime-scope-capture.js +50 -0
  55. package/dist/services/runtime-scope-control.js +431 -0
  56. package/dist/services/runtime-scope-resume.js +543 -0
  57. package/dist/services/runtime-scope-stop.js +95 -0
  58. package/dist/services/runtime-scope-worker.js +165 -0
  59. package/dist/services/runtime-service.js +4 -0
  60. package/dist/services/vnext-code-review.js +1 -1
  61. package/dist/services/vnext-code.js +1 -1
  62. package/dist/services/vnext-execution-profile.js +2 -35
  63. package/dist/services/vnext-fanout.js +23 -7
  64. package/dist/services/vnext-plan-review.js +12 -9
  65. package/dist/services/vnext-plan.js +1 -1
  66. package/dist/services/vnext-protocolize.js +10 -4
  67. package/dist/services/vnext-specify.js +12 -5
  68. package/dist/services/work-registry.js +26 -8
  69. package/dist/services/workspace-bootstrap.js +10 -1
  70. package/dist/storage/database.js +160 -13
  71. package/dist/storage/writer-contract.js +76 -0
  72. package/dist/storage/writer-migration.js +102 -0
  73. package/package.json +1 -1
@@ -0,0 +1,105 @@
1
+ import { AppError } from "../shared/errors.js";
2
+ import { getVnextFanoutStatus, observeVnextNativeChildren, reconcileVnextFanout, subagentCapacityKey } from "./vnext-fanout.js";
3
+ import { getFlowRunVariables } from "./runs.js";
4
+ import { dispatchVnextPlanReview } from "./vnext-plan-review.js";
5
+ import { resolveExecutionPolicy } from "./execution-policy.js";
6
+ /** The shared controller owns this former eval-only Work-graph decision. */
7
+ export async function nextControllerFanout(context, input) {
8
+ const children = input.children.filter(child => child.status !== "settled_by_root");
9
+ const observations = { harness_id: input.harness, parent_session_id: input.sessionId, children };
10
+ if (children.length) {
11
+ const observed = observeVnextNativeChildren(context, { ...input, observations });
12
+ const issues = observed.issues;
13
+ if (issues.length)
14
+ throw new AppError("fanout_reconciliation_required", "Native children have unresolved lifecycle ownership", 1, { issues });
15
+ }
16
+ let fanout = getVnextFanoutStatus(context, input).orchestration;
17
+ if (!fanout)
18
+ return { kind: "none" };
19
+ if (children.length) {
20
+ const reconciled = await reconcileVnextFanout(context, { ...input, observations });
21
+ const issues = reconciled.issues.filter(issue => issue.code !== "child_check_in_progress");
22
+ if (issues.length)
23
+ throw new AppError("fanout_reconciliation_required", "Native child outcome requires reconciliation, not replay", 1, { issues });
24
+ if (children.some(child => child.status === "running" || child.status === "unknown"))
25
+ return { kind: "wait", reason: "native_children_unsettled" };
26
+ fanout = getVnextFanoutStatus(context, input).orchestration;
27
+ }
28
+ if (fanout.state === "dispatch_required") {
29
+ dispatchVnextPlanReview(context, input);
30
+ fanout = getVnextFanoutStatus(context, input).orchestration;
31
+ if (fanout.state === "dispatch_required" || fanout.state === "capacity_required")
32
+ return { kind: "wait", reason: "qualified_capacity_required" };
33
+ }
34
+ const prompt = (text) => ({ kind: "prompt", prompt: text, fingerprint: JSON.stringify({ stage: input.stage, parent: fanout.parent_work_id, works: fanout.works, text }) });
35
+ if (fanout.state === "coordinator_required")
36
+ return prompt(`Continue the current ${input.stage} Stage using its authoritative packet. Materialize only its required Work graph with real dependencies. Do not launch children, finish the Stage, or start a successor. Return after registering the graph; the runtime will dispatch its ready Work.`);
37
+ if (fanout.works.failed || fanout.works.cancelled)
38
+ return prompt(`Some ${input.stage} child Work failed or was cancelled. Inspect its recorded receipts. Choose a legal scoped repair/retry or block the Stage with the concrete reason. Do not relaunch settled siblings or infer success from a child that omitted work finish.`);
39
+ if (fanout.works.ready.length) {
40
+ const choices = fanout.works.ready.map(work => {
41
+ const payload = context.db.get("SELECT payload_json FROM works WHERE work_id = ?", [work.work_id])?.payload_json;
42
+ const override = payload ? JSON.parse(payload).execution_override : undefined;
43
+ return { work, resolved: resolveExecutionPolicy({ ddFlowHome: context.ddFlowHome, policy: input.policy, profiles: input.profiles, stage: input.stage, ...(override ? { work: override } : {}) }) };
44
+ });
45
+ const external = choices.filter(choice => choice.resolved.delegation.mode === "external");
46
+ if (external.length) {
47
+ const maximum = Math.min(...external.map(choice => choice.resolved.delegation.max_parallel ?? 1));
48
+ return { kind: "external", works: external.slice(0, maximum).map(choice => choice.work) };
49
+ }
50
+ const variables = getFlowRunVariables(context, input);
51
+ const capacity = variables.variables[subagentCapacityKey];
52
+ if (typeof capacity !== "number" || !Number.isInteger(capacity) || capacity < 1)
53
+ return { kind: "wait", reason: "qualified_capacity_required" };
54
+ const wave = choices.slice(0, capacity).map(choice => choice.work);
55
+ return prompt([
56
+ `Launch exactly ${wave.length} direct native children for the current ${input.stage} Stage; the recorded capacity is ${capacity}.`,
57
+ "Use this harness's native child mechanism, not external roots or forks. Wait for these children to settle; do not cancel siblings because one failed. Do not finish this Stage or start a successor in this Turn.",
58
+ ...wave.map(work => {
59
+ if (!work.work_id || !work.start_command)
60
+ throw new AppError("fanout_contract_invalid", "Ready Work omits its trusted start command", 1);
61
+ return `Assign one leaf child Work ${work.work_id}. ${work.launch_policy === "fresh_agent_required" ? "Use empty, non-inherited context when supported; a new Session id alone does not prove empty context." : "Inherit context only through the harness's declared mechanism."}\nIts first technical action must be this exact standalone command:\n${work.start_command}\nUse only the Work packet returned by it. Complete its declared result and exact work finish command, then stop. Do not create children or start another Work. Missing facts go through the Work's failure/result contract; only the coordinator handles Stage HITL.`;
62
+ })
63
+ ].join("\n\n"));
64
+ }
65
+ if (fanout.works.running)
66
+ return { kind: "wait", reason: "work_receipts_pending" };
67
+ if (fanout.works.created)
68
+ throw new AppError("fanout_graph_stalled", "Created Work has no ready or running dependency path", 1);
69
+ return prompt(`All declared ${input.stage} child Work has settled (${fanout.works.completed} completed). Read the accepted result receipts and continue this Stage's semantic decision under its original packet. Execute an exact repair command returned by a rejected stage finish; a rejection alone does not create repair Work. Return after finish or after declaring a repair graph. Do not start a successor Stage.`);
70
+ }
71
+ /** Normalize provider-observed topology only; never parse assistant prose. */
72
+ export function controllerNativeChildren(receipt, rootSessionId) {
73
+ const candidates = [];
74
+ const object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
75
+ const list = (value) => Array.isArray(value) ? value : [];
76
+ const text = (value, keys) => { for (const key of keys)
77
+ if (typeof value[key] === "string" && value[key])
78
+ return value[key]; return null; };
79
+ const add = (raw, source, scopedParent = null, fallback = "unknown") => {
80
+ const value = object(raw);
81
+ const id = text(value, ["provider_session_id", "session_id", "sessionId", "childSessionId", "conversation_id", "conversationId", "id", "subagentId"]);
82
+ const parent = text(value, ["parent_provider_session_id", "parent_session_id", "parentSessionId", "parentID", "parentId"]) ?? scopedParent;
83
+ if (!id || parent !== rootSessionId)
84
+ return;
85
+ const state = String(value.status ?? value.state ?? fallback).toLowerCase();
86
+ const status = ["completed", "complete", "done", "idle", "success", "succeeded"].includes(state) ? "completed" : state === "settled_by_root" ? "settled_by_root" : ["failed", "error"].includes(state) ? "failed" : ["cancelled", "canceled"].includes(state) ? "cancelled" : ["running", "active", "pending"].includes(state) ? "running" : fallback;
87
+ candidates.push({ session_id: id, parent_session_id: parent, status, source });
88
+ };
89
+ for (const value of list(receipt.descendants))
90
+ add(value, "adapter.descendants");
91
+ for (const value of list(receipt.children))
92
+ add(value, "opencode.session.children");
93
+ const subagents = object(object(receipt.evidence).subagents ?? receipt.subagents);
94
+ for (const value of list(subagents.running))
95
+ add(value, "zcode/session/subagents", rootSessionId, "running");
96
+ for (const value of [...list(subagents.completed), ...list(object(subagents.ended).items)])
97
+ add(value, "zcode/session/subagents", rootSessionId, "completed");
98
+ const unique = new Map();
99
+ for (const child of candidates) {
100
+ const previous = unique.get(child.session_id);
101
+ if (!previous || previous.status === "unknown" || previous.status === "running" && child.status !== "running")
102
+ unique.set(child.session_id, child);
103
+ }
104
+ return [...unique.values()].sort((left, right) => left.session_id.localeCompare(right.session_id));
105
+ }
@@ -276,6 +276,8 @@ function runIdForArgs(args) {
276
276
  const [family, command, ...rest] = args;
277
277
  const parsed = parseLightArgs(rest);
278
278
  if (family === "run" && command !== "start" && command !== "list") {
279
+ if (["drive", "control", "recovery", "capacity"].includes(command ?? ""))
280
+ return option(parsed, "run");
279
281
  if (command === "flags")
280
282
  return positional(parsed, 1);
281
283
  return positional(parsed, 0);