@deksden-com/dd-flow-cli 0.9.0-beta.62 → 0.9.0-beta.63
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.63
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Accept the qualified retained-subagent ZCode bridge and settle a control only
|
|
8
|
+
when a durable startup rejection proves that it created no native resources.
|
|
9
|
+
|
|
3
10
|
## 0.9.0-beta.62
|
|
4
11
|
|
|
5
12
|
### Patch Changes
|
package/dist/build-info.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"cli_package": "@deksden-com/dd-flow-cli",
|
|
3
|
-
"cli_version": "0.9.0-beta.
|
|
4
|
-
"cli_commit": "
|
|
5
|
-
"built_at": "2026-09-
|
|
3
|
+
"cli_version": "0.9.0-beta.63",
|
|
4
|
+
"cli_commit": "4975ba184bd847fd34db995cd3d9e1feb3db9f9f",
|
|
5
|
+
"built_at": "2026-09-14T10:36:19.793Z",
|
|
6
6
|
"built_with_canon": {
|
|
7
7
|
"version": "4.1.1",
|
|
8
8
|
"commit": "97f811d33c212ae3497020178b1ed825c7c3ebac",
|
|
@@ -154,7 +154,7 @@ export async function startDaemon(options) {
|
|
|
154
154
|
await removeSocket(paths.socket, paths.dir);
|
|
155
155
|
const versions = await doctor({ bin: config.bin, zcodePath: config.zcodePath });
|
|
156
156
|
if (hasFlowContext && versions.compatible !== true) {
|
|
157
|
-
throw new DaemonError("zcode_lifecycle_unqualified", "ZCode native event identity is not qualified for managed CLI admission; inspect lifecycle_qualification before starting E2E.", false, { observed_runtime: versions.observed_runtime, lifecycle_qualification: versions.lifecycle_qualification });
|
|
157
|
+
throw new DaemonError("zcode_lifecycle_unqualified", "ZCode native event identity is not qualified for managed CLI admission; inspect lifecycle_qualification before starting E2E.", false, { startup_resources: "not_created", observed_runtime: versions.observed_runtime, lifecycle_qualification: versions.lifecycle_qualification });
|
|
158
158
|
}
|
|
159
159
|
const resourceProcess = await registerDaemonProcess(config, {
|
|
160
160
|
kind: "zcode-daemon", owner: `zcode:${path.basename(paths.dir)}`, operation: `zcode-daemon:${path.basename(paths.dir)}`,
|
|
@@ -681,8 +681,9 @@ async function forwardUsage(options, providerSessionId, usage, toolCalls) {
|
|
|
681
681
|
export function zcodeLifecycleQualification(sha256, bridgeCommit, harnessContract) {
|
|
682
682
|
// Behavioural evidence: dd-eval production-invocations probe, 2026-09-13.
|
|
683
683
|
// Pin both providers of native event identity, not a string in the bundle.
|
|
684
|
+
// 60af0d3 adds retainedSubagents without changing native event forwarding.
|
|
684
685
|
const qualified = sha256 === "e9f1868c0fdb863537ed910ee3828b9be96b8c2fd805473f63b439e1113266b8"
|
|
685
|
-
&&
|
|
686
|
+
&& ["e0600fe3c46e215695f257e65aa6f674ced998d8", "60af0d31e13076a313d9770f10aa70f7c94742cf"].includes(bridgeCommit) && harnessContract === ZCODE_HARNESS_CONTRACT;
|
|
686
687
|
return { status: qualified ? "qualified" : "unqualified", mechanism: "cli-invocation@1", sha256,
|
|
687
688
|
capabilities: qualified ? ["root", "concurrent_children", "child_continuation"] : [],
|
|
688
689
|
native_nested_children: false, reason: qualified ? null : "native_event_contract_unqualified" };
|
|
@@ -413,10 +413,29 @@ async function reconcileOwnedSessions(context, input, interrupt, boundary) {
|
|
|
413
413
|
}
|
|
414
414
|
unresolved.push({ check_id: check.id, status: "pending", reason });
|
|
415
415
|
}
|
|
416
|
+
const uncreatedDirectories = new Map();
|
|
416
417
|
const sessionNodes = Promise.all([...targets.values()].map(async ({ session, executable, harness }) => {
|
|
417
418
|
const env = { ...context.env, DD_FLOW_HOME: context.ddFlowHome };
|
|
418
419
|
delete env.DD_FLOW_CONTROLLER_TOKEN;
|
|
419
420
|
delete env.DD_FLOW_CONTROL_TOKEN;
|
|
421
|
+
// An explicit pre-registration rejection proves there is no Session to
|
|
422
|
+
// cancel. A missing id or an arbitrary startup failure does not.
|
|
423
|
+
if (!session.id && !session.create_operation_id && !daemonInventories.has(session.state_dir) && !unsafeDaemons.has(canonicalPath(session.state_dir, false)) && !fs.existsSync(path.join(session.state_dir, "daemon.json"))) {
|
|
424
|
+
const operations = context.db.all("SELECT o.operation_id, o.input_json FROM run_controller_operations o JOIN run_controllers c ON c.controller_id = o.controller_id WHERE c.project_id = ? AND c.run_id = ? AND json_extract(o.input_json, '$.state_dir') = ?", [project.id, input.runId, session.state_dir]);
|
|
425
|
+
if (operations.length === 1)
|
|
426
|
+
try {
|
|
427
|
+
const operation = operations[0];
|
|
428
|
+
const saved = JSON.parse(operation.input_json);
|
|
429
|
+
const journal = retainedDaemonOperations(session.state_dir);
|
|
430
|
+
const receipt = journal.length === 1 ? journal[0] : null;
|
|
431
|
+
const error = receipt?.error;
|
|
432
|
+
if (saved.executable === executable && saved.owner === session.owner.owner_id && saved.args?.[0] === "daemon" && saved.args?.[1] === "start" && receipt?.operation_id === operation.operation_id && receipt.operation === "daemon.start" && receipt.predecessor_daemon_id === null && receipt.state === "failed" && error?.details?.startup_resources === "not_created" && !registeredProcesses.some(({ record }) => JSON.parse(record.metadata_json).state_dir === session.state_dir)) {
|
|
433
|
+
uncreatedDirectories.set(session.state_dir, JSON.stringify(journal));
|
|
434
|
+
return { state_dir: session.state_dir, status: "completed", receipt: { source: "startup_resources_not_created", operation_id: operation.operation_id, error } };
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
catch { /* Incomplete evidence retains the normal unknown outcome. */ }
|
|
438
|
+
}
|
|
420
439
|
if (!session.id && session.create_operation_id) {
|
|
421
440
|
try {
|
|
422
441
|
assertCurrent();
|
|
@@ -622,7 +641,7 @@ async function reconcileOwnedSessions(context, input, interrupt, boundary) {
|
|
|
622
641
|
};
|
|
623
642
|
bindings = readBindings();
|
|
624
643
|
for (const target of targets.values())
|
|
625
|
-
if (!daemonInventories.has(target.session.state_dir))
|
|
644
|
+
if (!daemonInventories.has(target.session.state_dir) && !uncreatedDirectories.has(target.session.state_dir))
|
|
626
645
|
unresolved.push({ state_dir: target.session.state_dir, status: "pending", reason: "runtime_inventory_unverified" });
|
|
627
646
|
for (const node of nodes)
|
|
628
647
|
if ("harness" in node && "session_id" in node && typeof node.session_id === "string" && bindings.some(binding => foreign(binding) && underRoot(binding, node.session_id, node.harness ?? null)))
|
|
@@ -647,6 +666,9 @@ async function reconcileOwnedSessions(context, input, interrupt, boundary) {
|
|
|
647
666
|
const processVersion = (items) => JSON.stringify(items.map(({ home, record }) => [home, record.id, record.kind, record.owner_id, record.project_id, record.run_id, record.work_id, record.check_id, record.operation_id, record.pid, record.pid_started_at, record.lease_token, record.state, record.metadata_json]).sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right))));
|
|
648
667
|
verifyInventory = () => {
|
|
649
668
|
assertCurrent();
|
|
669
|
+
for (const [directory, journal] of uncreatedDirectories)
|
|
670
|
+
if (fs.existsSync(path.join(directory, "daemon.json")) || JSON.stringify(retainedDaemonOperations(directory)) !== journal)
|
|
671
|
+
throw new AppError("owned_inventory_changed", "Rejected startup acquired new runtime evidence", 1);
|
|
650
672
|
const currentProcesses = [...resourceHomes].flatMap(home => managedProcessStatus({ ...context, env: { ...context.env, DD_FLOW_RESOURCE_HOME: home } }).filter(record => record.project_id === project.id && record.run_id === input.runId && [null, context.ddFlowHome].includes(managedProcessRuntimeHome(record, context))).map(record => ({ home, record })));
|
|
651
673
|
if (initialDatabaseVersion !== databaseVersion() || processVersion(registeredProcesses) !== processVersion(currentProcesses) || [...nativeVersions].some(([directory, version]) => version !== nativeInventoryVersion(directory)) || [...importVersions].some(([file, version]) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex") !== version))
|
|
652
674
|
throw new AppError("owned_inventory_changed", "Owned writers changed after their settlement observation", 1);
|