@deksden-com/dd-flow-cli 0.9.0-beta.93 → 0.9.0-beta.95
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 +12 -0
- package/dist/build-info.json +3 -3
- package/dist/harness-runtime/DD-ZCODE.md +18 -6
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +1 -1
- package/dist/harness-runtime/lib/dd-zcode.mjs +17 -4
- package/dist/services/hooks.js +1 -1
- package/dist/services/lifecycle-invocations.js +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @deksden-com/dd-flow-cli
|
|
2
2
|
|
|
3
|
+
## 0.9.0-beta.95
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Qualify the exact ZCode 0.16.9 / zcode-acp 0.46.7 contract @2 tuple after live public-command receipts, and reject inferred or failed native completion evidence.
|
|
8
|
+
|
|
9
|
+
## 0.9.0-beta.94
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Admit public ZCode lifecycle commands through their native receipt without exposing runtime-owned invocation IDs.
|
|
14
|
+
|
|
3
15
|
## 0.9.0-beta.93
|
|
4
16
|
|
|
5
17
|
### 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.95",
|
|
4
|
+
"cli_commit": "4c8c5e8456d684a7887c8bfdd1afc24ae874ff6c",
|
|
5
|
+
"built_at": "2026-09-22T06:50:54.211Z",
|
|
6
6
|
"built_with_canon": {
|
|
7
7
|
"version": "4.1.1",
|
|
8
8
|
"commit": "d1a6081ab15ab92ac917ff5d037121a40c709db1",
|
|
@@ -18,10 +18,18 @@ closed transport is not proof that its native descendants stopped. The adapter
|
|
|
18
18
|
must retain causal errors, missing observations and usage completeness rather
|
|
19
19
|
than turn them into successful/zero-valued results.
|
|
20
20
|
|
|
21
|
+
The bridge marks heuristic ACP completion with
|
|
22
|
+
`_meta.zcodeCompletionEvidence: "inferred"` and exhausted failed turns with
|
|
23
|
+
`"failed"`. dd-zcode rejects these before any post-turn materializing read:
|
|
24
|
+
inferred means `native_outcome_unknown` (not retryable automatically), failed
|
|
25
|
+
means `zcode_turn_not_completed`. This additive evidence does not change the
|
|
26
|
+
acceptance of already-qualified immutable older tuples. Native allocation
|
|
27
|
+
notifications are causal identity evidence, not authorization to replay a turn.
|
|
28
|
+
|
|
21
29
|
## Bridge contract
|
|
22
30
|
|
|
23
31
|
The identity check uses `--dd-harness-version` (`dd-zcode-harness@1` for existing
|
|
24
|
-
qualified builds, `dd-zcode-harness@2` for the
|
|
32
|
+
qualified builds, `dd-zcode-harness@2` for the qualified v0.46.7 bridge) and
|
|
25
33
|
`--dd-harness-commit`, alongside native and bridge versions. This is the current
|
|
26
34
|
mechanism, not a general capability negotiation protocol. `zcodeLifecycleQualification`
|
|
27
35
|
also admits exact native-binary SHA / bridge-commit tuples; a new tuple requires
|
|
@@ -33,9 +41,12 @@ The adapter consumes `zcode/session/resolve`, `read`, `subagents`, `usage`,
|
|
|
33
41
|
operations. Resolution must preserve the session's trusted workspace.
|
|
34
42
|
Residency and retained-tree reads after close must not implicitly resume it.
|
|
35
43
|
Cancellation must target owned sessions and verify resulting tree state.
|
|
36
|
-
Profile facts must come from native readback. Session updates supply tool events
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
Profile facts must come from native readback. Session updates supply tool events.
|
|
45
|
+
The adapter forwards a standalone public lifecycle command to `dd-flow zcode
|
|
46
|
+
event handle`; native Session/tool identity binds it to the one exact issued
|
|
47
|
+
assignment in that daemon scope. The command never exposes the runtime-owned
|
|
48
|
+
invocation ID. Its shell inherits `DD_FLOW_DAEMON_ID`, finds the assignment and
|
|
49
|
+
waits for the committed receipt before any lifecycle mutation can execute.
|
|
39
50
|
|
|
40
51
|
The execution-scoped daemon preserves the live native handles needed for
|
|
41
52
|
background children. Inspection remains available during productive operations;
|
|
@@ -53,8 +64,9 @@ remain supported through their existing measured projection. All adapter usage
|
|
|
53
64
|
paths use the same helper; dd-flow services still own accounting deltas.
|
|
54
65
|
|
|
55
66
|
Contract support does not admit a new binary/commit tuple automatically. The
|
|
56
|
-
v0.46.7 @2
|
|
57
|
-
|
|
67
|
+
v0.46.7 @2 tuple is admitted only for native SHA `500ae84f…f466` and bridge
|
|
68
|
+
commit `f4d47b5…2833`, after a live public-command receipt probe. Preserve that
|
|
69
|
+
fail-closed distinction when preparing later evals.
|
|
58
70
|
|
|
59
71
|
Future consumer-only retry, recovery, readiness and accounting rules belong
|
|
60
72
|
here or in the owning dd-flow service. Bridge changes are justified when native
|
|
@@ -602,7 +602,7 @@ export async function serveDaemon(stateDir) {
|
|
|
602
602
|
const paths = locations(stateDir);
|
|
603
603
|
const state = await readState(paths.dir);
|
|
604
604
|
if (!state || state.schema_id !== STATE_SCHEMA) throw new DaemonError("daemon_state_missing", "daemon state is missing or incompatible");
|
|
605
|
-
const bridge = new AcpBridge(state.config);
|
|
605
|
+
const bridge = new AcpBridge({ ...state.config, env: { ...state.config.env, DD_FLOW_DAEMON_ID: state.daemon_id } });
|
|
606
606
|
const initialized = await bridge.start();
|
|
607
607
|
const runtime = new DaemonRuntime(paths, state, bridge, initialized);
|
|
608
608
|
await removeSocket(paths.socket, paths.dir);
|
|
@@ -710,7 +710,7 @@ export async function zcodeInvocationObserver(options, rootProviderSessionId, se
|
|
|
710
710
|
const { update, native, session } = facts;
|
|
711
711
|
const command = update.rawInput?.command ?? update.rawInput?.cmd;
|
|
712
712
|
const toolName = update._meta?.claudeCode?.toolName;
|
|
713
|
-
if (!["Bash", "bash", "execute"].includes(toolName) || typeof command !== "string"
|
|
713
|
+
if (!["Bash", "bash", "execute"].includes(toolName) || typeof command !== "string") return;
|
|
714
714
|
const parent = session === rootProviderSessionId ? null : owners.get(native.parentToolCallId);
|
|
715
715
|
if (session !== rootProviderSessionId && !parent) throw Object.assign(new Error("Native child has no confirmed immediate parent tool owner"), { code: "zcode_parent_identity_missing" });
|
|
716
716
|
await send({ bin: options.ddFlowBin, home: options.ddFlowHome,
|
|
@@ -752,15 +752,19 @@ async function forwardUsageBestEffort(options, providerSessionId, usage, toolCal
|
|
|
752
752
|
}
|
|
753
753
|
|
|
754
754
|
export function zcodeLifecycleQualification(sha256, bridgeCommit, harnessContract) {
|
|
755
|
-
// Behavioural evidence: dd-eval production-invocations
|
|
755
|
+
// Behavioural evidence: dd-eval production-invocations probes on 2026-09-13
|
|
756
|
+
// and 2026-09-22. The latter exercised public commands, file-backed JSON,
|
|
757
|
+
// concurrent children and continuation on bridge contract @2.
|
|
756
758
|
// Pin both providers of native event identity, not a string in the bundle.
|
|
757
759
|
// 60af0d3 adds retainedSubagents without changing native event forwarding.
|
|
758
760
|
const qualifiedTuples = new Map([
|
|
759
761
|
["e9f1868c0fdb863537ed910ee3828b9be96b8c2fd805473f63b439e1113266b8", new Set(["e0600fe3c46e215695f257e65aa6f674ced998d8", "60af0d31e13076a313d9770f10aa70f7c94742cf"])],
|
|
760
|
-
["500ae84fa2cb8dd74c1264e5f3d3709c004c59b58899f1b0c3e205137a0ef466", new Set(["8c0a893f3c26a0b96cb138acf762db84c800298b"])],
|
|
762
|
+
["500ae84fa2cb8dd74c1264e5f3d3709c004c59b58899f1b0c3e205137a0ef466", new Set(["8c0a893f3c26a0b96cb138acf762db84c800298b", "f4d47b517073f9bdb8d4eba3d0d59ce66c6a2833"])],
|
|
761
763
|
]);
|
|
762
764
|
const qualified = qualifiedTuples.get(sha256)?.has(bridgeCommit) === true
|
|
763
|
-
&&
|
|
765
|
+
&& (bridgeCommit === "f4d47b517073f9bdb8d4eba3d0d59ce66c6a2833"
|
|
766
|
+
? harnessContract === "dd-zcode-harness@2"
|
|
767
|
+
: harnessContract === ZCODE_HARNESS_CONTRACT);
|
|
764
768
|
return { status: qualified ? "qualified" : "unqualified", mechanism: "cli-invocation@1", sha256,
|
|
765
769
|
capabilities: qualified ? ["root", "concurrent_children", "child_continuation"] : [],
|
|
766
770
|
native_nested_children: false, reason: qualified ? null : "native_event_contract_unqualified" };
|
|
@@ -851,6 +855,15 @@ export async function promptSessionWithBridge(bridge, options) {
|
|
|
851
855
|
inactivityMode: "diagnostic",
|
|
852
856
|
});
|
|
853
857
|
const stopReason = typeof turn?.stopReason === "string" ? turn.stopReason : null;
|
|
858
|
+
const completionEvidence = turn?._meta?.zcodeCompletionEvidence;
|
|
859
|
+
if (completionEvidence === "inferred" || completionEvidence === "failed") {
|
|
860
|
+
await bridge.flush();
|
|
861
|
+
const error = new Error(`ZCode completion is ${completionEvidence}, not a confirmed successful native turn`);
|
|
862
|
+
error.code = completionEvidence === "inferred" ? "native_outcome_unknown" : "zcode_turn_not_completed";
|
|
863
|
+
error.retryable = false;
|
|
864
|
+
error.details = { completion_evidence: completionEvidence, provider_session_id: identity.providerSessionId, adapter_session_id: identity.adapterSessionId };
|
|
865
|
+
throw error;
|
|
866
|
+
}
|
|
854
867
|
if (stopReason && stopReason !== "end_turn") {
|
|
855
868
|
// A close can settle the pending prompt before its terminal event. Do not
|
|
856
869
|
// collect live evidence first: ordinary inspection is allowed to recover
|
package/dist/services/hooks.js
CHANGED
|
@@ -493,7 +493,7 @@ export function handleZcodeEvent(context, input) {
|
|
|
493
493
|
return { ok: true, observed: false, reason: "non_bash_tool" };
|
|
494
494
|
const command = stringValue(rawInput.command) ?? stringValue(rawInput.cmd);
|
|
495
495
|
const parsed = command ? parseLifecycleCommand(command) : { kind: "none" };
|
|
496
|
-
if (parsed.kind === "none"
|
|
496
|
+
if (parsed.kind === "none")
|
|
497
497
|
return { ok: true, observed: false, reason: "acp_evidence_only" };
|
|
498
498
|
const controlled = objectRecord(objectRecord(notification._meta).ddZcode);
|
|
499
499
|
const providerSessionId = stringValue(native.childSessionId) ?? stringValue(controlled.rootProviderSessionId);
|
|
@@ -920,7 +920,9 @@ export function observedLifecycleInvocation(context, command, daemonId) {
|
|
|
920
920
|
const analysis = parseLifecycleCommand(command);
|
|
921
921
|
if (analysis.kind !== "standalone" || analysis.invocation.wrapped)
|
|
922
922
|
return null;
|
|
923
|
-
|
|
923
|
+
// `issued` is a rendezvous, not authority: the CLI still blocks in
|
|
924
|
+
// awaitLifecycleInvocation until the native observer commits the receipt.
|
|
925
|
+
const matches = context.db.all("SELECT * FROM lifecycle_invocations WHERE status IN ('issued','observed','executing') ORDER BY rowid DESC")
|
|
924
926
|
.filter(row => {
|
|
925
927
|
try {
|
|
926
928
|
const scope = JSON.parse(row.scope_json);
|