@deksden-com/dd-flow-cli 0.9.0-beta.141 → 0.9.0-beta.143
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 +17 -0
- package/dist/build-info.json +7 -7
- package/dist/cli/command-inputs.js +1 -1
- package/dist/cli/command-option-descriptions.js +1 -0
- package/dist/cli/help.js +1 -0
- package/dist/cli/run-cli.js +9 -3
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +8 -3
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +29 -8
- package/dist/harness-runtime/lib/dd-codex.mjs +184 -51
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +26 -7
- package/dist/schemas/code-review-decision.schema.json +9 -0
- package/dist/schemas/flow-run.schema.json +1 -0
- package/dist/schemas/plan-review-decision.schema.json +9 -0
- package/dist/services/accepted-intent.js +127 -0
- package/dist/services/check-runner-evidence.js +2 -0
- package/dist/services/code-checks.js +255 -65
- package/dist/services/repair-intents.js +1 -1
- package/dist/services/run-control.js +36 -1
- package/dist/services/runtime-budget.js +16 -0
- package/dist/services/stage-pause.js +30 -0
- package/dist/services/verification-matrix.js +20 -2
- package/dist/services/vnext-code-review.js +14 -2
- package/dist/services/vnext-plan-review.js +6 -1
- package/dist/services/vnext-protocolize.js +2 -0
- package/dist/services/vnext-specify.js +4 -0
- package/dist/services/work-registry.js +4 -2
- package/dist/storage/database.js +4 -0
- package/package.json +1 -1
- package/tools/probe-codex-history.mjs +37 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @deksden-com/dd-flow-cli
|
|
2
2
|
|
|
3
|
+
## 0.9.0-beta.143
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 60daf29: Keep an early AGY provider rejection as the readiness failure rather than a stream-drain cleanup failure. Preserve strict journal and physical cleanup errors.
|
|
8
|
+
- d86445c: Observe accepted Codex Turns while persisted history is unavailable, preserving exact native outcomes, cancellation identity and conservative tree settlement. Keep RPC errors intact in compact journals.
|
|
9
|
+
|
|
10
|
+
Treat successful metadata-only history responses as incomplete in cancellation and final result admission; require exact current Turn item evidence before bypassing retained-output/profile safeguards. Reject incomplete cancellation records and terminal settlement without a valid Turn identity. Prefer native completed output over stale snapshots of the same Turn.
|
|
11
|
+
|
|
12
|
+
## 0.9.0-beta.142
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 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.
|
|
17
|
+
- Retry no-effect physical confirmation on the same held check, bootstrap or service process before dispatch, without replaying commands or extending exhausted authority.
|
|
18
|
+
- Backfilled from published artifact commit `19ca01d6`: Keep native Session observation stable, use bounded shared Codex read phases, scope check evidence to declared inputs, retain accepted intake/HITL review sources, and reject stale review coverage without productive replay. These changes were already shipped; beta.143 does not count them as new work.
|
|
19
|
+
|
|
3
20
|
## 0.9.0-beta.141
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/dist/build-info.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
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-10-
|
|
3
|
+
"cli_version": "0.9.0-beta.143",
|
|
4
|
+
"cli_commit": "2294710b9c9b678193798301d5a81048506d672d",
|
|
5
|
+
"built_at": "2026-10-20T02:52:47.825Z",
|
|
6
6
|
"built_with_canon": {
|
|
7
7
|
"version": "4.1.2",
|
|
8
|
-
"commit": "
|
|
8
|
+
"commit": "a1cfe8b06329c691c314064b6ea6a3da6628cceb",
|
|
9
9
|
"flow_contract": "dd-flow-canonical-2026-08",
|
|
10
|
-
"repo_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-canon-
|
|
11
|
-
"memorybank_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-canon-
|
|
12
|
-
"flow_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-canon-
|
|
10
|
+
"repo_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-canon-plan092",
|
|
11
|
+
"memorybank_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-canon-plan092/.memory-bank",
|
|
12
|
+
"flow_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-canon-plan092/.memory-bank/dd-flow",
|
|
13
13
|
"layout": "dot_memory_bank"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -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
|
package/dist/cli/run-cli.js
CHANGED
|
@@ -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 =>
|
|
2782
|
-
throw new AppError("usage", "
|
|
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))
|
|
@@ -294,7 +294,7 @@ export class Runtime {
|
|
|
294
294
|
let allowProviderFinalization;
|
|
295
295
|
const providerRegistered = new Promise(resolve => { allowProviderFinalization = resolve; });
|
|
296
296
|
const observationFailure = error => { this.drainingError ??= error; readyReject(error); this.active?.reject(error); };
|
|
297
|
-
this.nativeDrain = readNativeLines(this.child.stdout, line => { this.buffer = `${line}\n`; return this.draining = this.drain(readyResolve); }, observationFailure);
|
|
297
|
+
this.nativeDrain = readNativeLines(this.child.stdout, line => { this.buffer = `${line}\n`; return this.draining = this.drain(readyResolve, readyReject); }, observationFailure);
|
|
298
298
|
this.diagnostics = streamNativeDiagnostics(this.child.stderr, chunk => this.journal("stderr", { text: chunk }), observationFailure);
|
|
299
299
|
this.child.on("error", error => { readyReject(error); if (this.active) { this.active.reject(error); this.active = null; } });
|
|
300
300
|
// A dead leader is not yet a finalized tree: stop must also join the
|
|
@@ -329,7 +329,7 @@ export class Runtime {
|
|
|
329
329
|
try { return await Promise.race([this.readyPromise, new Promise((_, reject) => { readyTimer = observedTimeout(() => reject(new DaemonError("agy_init_timeout", "Antigravity init timed out", true)), SETUP_STARTUP_MS, { progress: () => this.lastActivityAt }); })]); }
|
|
330
330
|
finally { clearTimeout(readyTimer); }
|
|
331
331
|
}
|
|
332
|
-
async drain(readyResolve) { let index; while ((index = this.buffer.indexOf("\n")) >= 0) { const line = this.buffer.slice(0, index).trim(); this.buffer = this.buffer.slice(index + 1); if (!line) continue; let event; try { event = JSON.parse(line); } catch { await this.journal("invalid_json", { line: line.slice(0, 1000) }); continue; }
|
|
332
|
+
async drain(readyResolve, readyReject) { let index; while ((index = this.buffer.indexOf("\n")) >= 0) { const line = this.buffer.slice(0, index).trim(); this.buffer = this.buffer.slice(index + 1); if (!line) continue; let event; try { event = JSON.parse(line); } catch { await this.journal("invalid_json", { line: line.slice(0, 1000) }); continue; }
|
|
333
333
|
// Record an early provider rejection before journalling: a short-lived
|
|
334
334
|
// child can close while the asynchronous write is still pending.
|
|
335
335
|
if (event.event === "result" && ["ERROR", "CANCELLED"].includes(event.result?.status)) {
|
|
@@ -344,7 +344,12 @@ export class Runtime {
|
|
|
344
344
|
}
|
|
345
345
|
this.observeProviderActivity(event);
|
|
346
346
|
if (event.event === "result" && this.init && !this.startupError) { await this.finishTurn(event.result ?? {}, event.terminal_metadata, event); continue; }
|
|
347
|
-
await this.journal("provider_event", { event }); if (event.event === "init") { this.init = { conversation_id: event.conversation_id, ...(event.init ?? {}) }; if (!this.init.conversation_id) throw new DaemonError("agy_init_missing", "Antigravity init has no conversation ID"); const requested = { provider: this.config.provider, model: this.config.model, reasoning: this.config.reasoning, mode: this.config.mode, permission_mode: "always-proceed" }; this.profile = assertProfile(requested, observedProfile(this.init, requested)); await observeModel({ journal: this.config.journal, harness: "antigravity-cli", sessionId: this.init.conversation_id, requested, observed: this.profile.observed, source: "native.init" }); await this.persist({ shutdown_state: "running", active_tree: false }); readyResolve(this.init); } else if (event.event === "step_update") this.observeStep(event.step_update ?? {}); else if (event.event === "result") {
|
|
347
|
+
await this.journal("provider_event", { event }); if (event.event === "init") { this.init = { conversation_id: event.conversation_id, ...(event.init ?? {}) }; if (!this.init.conversation_id) throw new DaemonError("agy_init_missing", "Antigravity init has no conversation ID"); const requested = { provider: this.config.provider, model: this.config.model, reasoning: this.config.reasoning, mode: this.config.mode, permission_mode: "always-proceed" }; this.profile = assertProfile(requested, observedProfile(this.init, requested)); await observeModel({ journal: this.config.journal, harness: "antigravity-cli", sessionId: this.init.conversation_id, requested, observed: this.profile.observed, source: "native.init" }); await this.persist({ shutdown_state: "running", active_tree: false }); readyResolve(this.init); } else if (event.event === "step_update") this.observeStep(event.step_update ?? {}); else if (event.event === "result") {
|
|
348
|
+
// A successfully read/journalled provider refusal rejects readiness, not
|
|
349
|
+
// the stream drain that physical cleanup must still join.
|
|
350
|
+
if (this.startupError) { readyReject(this.startupError); continue; }
|
|
351
|
+
await this.finishTurn(event.result ?? {});
|
|
352
|
+
} } }
|
|
348
353
|
observeStep(step) {
|
|
349
354
|
const sessionId = step.conversation_id ?? this.init?.conversation_id, observation = this.sessionObservations.get(sessionId) ?? {};
|
|
350
355
|
const fresh = Number.isInteger(step.step_index) && step.step_index > (observation.last_step_index ?? -1) && step.step_index > (observation.step_floor ?? -1);
|
|
@@ -14,7 +14,7 @@ import path from "node:path";
|
|
|
14
14
|
import { spawn } from "node:child_process";
|
|
15
15
|
import { waitForSettlement } from "./session-settlement.mjs";
|
|
16
16
|
import { errorRecord } from "./operation-errors.mjs";
|
|
17
|
-
import { CODEX_REQUEST_TIMEOUT_MS, LATE_RESPONSE_GRACE_MS, CodexBridge, cancelSessionWithBridge, createSessionWithBridge, inspectSessionWithBridge, promptSessionWithBridge, startSessionWithBridge } from "./dd-codex.mjs";
|
|
17
|
+
import { CODEX_REQUEST_TIMEOUT_MS, LATE_RESPONSE_GRACE_MS, CodexBridge, codexReadPhase, readCodexHistory, retainedCodexTurn, cancelSessionWithBridge, createSessionWithBridge, inspectSessionWithBridge, promptSessionWithBridge, startSessionWithBridge } from "./dd-codex.mjs";
|
|
18
18
|
import { assertDaemonOwnership, assertStartupChild, prepareRuntimeOwner, cleanupFailedDaemonStart, confirmDaemonProcess, confirmDaemonStopped, finishDaemonProcess, scheduleDaemonProcessHeartbeat, registerDaemonProcess, startManagedBridge, stopProcessGroup } from "./managed-daemon.mjs";
|
|
19
19
|
|
|
20
20
|
export function codexBridgeEnv(config, daemonId) { return { ...config.env, ...managedFlowEnv(config, daemonId) }; }
|
|
@@ -28,10 +28,11 @@ async function readState(file) { try { return JSON.parse(await readFile(file, "u
|
|
|
28
28
|
async function writeState(file, value) { const temp = `${file}.${process.pid}.tmp`; await writeFile(temp, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 }); await rename(temp, file); }
|
|
29
29
|
async function removeSocket(socket) { try { const info = await lstat(socket); if (!info.isSocket()) throw Object.assign(new Error("refusing to remove non-socket daemon path"), { code: "unsafe_socket" }); await unlink(socket); } catch (error) { if (error.code !== "ENOENT") throw error; } }
|
|
30
30
|
const errorPayload = errorRecord;
|
|
31
|
-
const terminalTurn = turn => ["completed", "interrupted", "failed", "cancelled"].includes(turn
|
|
32
|
-
export function settledCodexSession(status, latestTurn, active) {
|
|
31
|
+
const terminalTurn = turn => typeof turn?.id === "string" && turn.id.trim().length > 0 && ["completed", "interrupted", "failed", "cancelled"].includes(turn.status);
|
|
32
|
+
export function settledCodexSession(status, latestTurn, active, historyAvailable = true) {
|
|
33
|
+
if (latestTurn && !terminalTurn(latestTurn)) return false;
|
|
33
34
|
if (status === "idle") return true;
|
|
34
|
-
if (status === "notLoaded") return
|
|
35
|
+
if (status === "notLoaded") return terminalTurn(latestTurn) || historyAvailable && !latestTurn;
|
|
35
36
|
// `systemError` alone is not evidence that the provider stopped. A current
|
|
36
37
|
// terminal Turn plus no daemon operation is the narrow settled case.
|
|
37
38
|
return status === "systemError" && !active && terminalTurn(latestTurn);
|
|
@@ -49,26 +50,46 @@ async function observeCodexSessions(bridge, options, sessions, active, remember
|
|
|
49
50
|
const observed = await inspectSessionWithBridge(bridge, { ...options, sessionId, turnId: sessionId === options.sessionId ? options.turnId : undefined });
|
|
50
51
|
for (const child of observed.descendants ?? []) { sessions.add(child.provider_session_id); remember(child.provider_session_id); }
|
|
51
52
|
const status = observed.status?.type ?? observed.status;
|
|
52
|
-
const
|
|
53
|
-
const
|
|
53
|
+
const currentId = bridge.currentTurns?.get(sessionId) ?? (sessionId === options.sessionId ? options.turnId : undefined);
|
|
54
|
+
const retained = retainedCodexTurn(bridge, sessionId, currentId);
|
|
55
|
+
const compactTurn = observed.thread?.turns?.findLast(turn => !currentId || turn?.id === currentId);
|
|
56
|
+
const stored = ["notLoaded", "systemError"].includes(status) || currentId && !terminalTurn(retained)
|
|
57
|
+
? await readCodexHistory(bridge, "thread/turns/list", { threadId: sessionId, limit: 20, sortDirection: "desc", itemsView: "summary" }, options.readPhase) : null;
|
|
58
|
+
const hydrated = stored && !stored.available
|
|
59
|
+
? await readCodexHistory(bridge, "thread/read", { threadId: sessionId, includeTurns: true }, options.readPhase) : null;
|
|
60
|
+
const hydratedAvailable = hydrated?.available && Array.isArray(hydrated.result.thread.turns);
|
|
61
|
+
const hydratedTurn = hydratedAvailable ? hydrated.result.thread.turns.findLast(turn => !currentId || turn?.id === currentId) : null;
|
|
62
|
+
const malformedHistory = [stored?.result?.data, hydrated?.result?.thread.turns, observed.thread?.turns].some(turns =>
|
|
63
|
+
turns?.some(turn => typeof turn?.id !== "string" || !turn.id.trim() || !["inProgress", "completed", "interrupted", "failed", "cancelled"].includes(turn.status)));
|
|
64
|
+
const latest = retained && terminalTurn(retained) ? retained
|
|
65
|
+
: malformedHistory ? { status: "unknown" }
|
|
66
|
+
: stored?.result?.data.find(turn => !currentId || turn?.id === currentId) ?? hydratedTurn ?? compactTurn ?? retained
|
|
67
|
+
?? (currentId ? { id: currentId, status: "unknown" } : null);
|
|
68
|
+
const observedStatus = hydrated?.result?.thread.status ?? observed.status;
|
|
69
|
+
const settled = settledCodexSession(observedStatus?.type ?? observedStatus, latest, active(), (stored?.available ?? true) || hydratedAvailable);
|
|
54
70
|
if (!settled) unsettled.push(sessionId);
|
|
55
|
-
observations.push({ ...observed, settled });
|
|
71
|
+
observations.push({ ...observed, status: observedStatus, settled });
|
|
56
72
|
}
|
|
57
73
|
return { sessions: unsettled.reverse(), active: Boolean(active()), observations };
|
|
58
74
|
}
|
|
59
75
|
|
|
60
76
|
export async function inspectCodexTree(bridge, options, { remember = () => {}, active = () => null } = {}) {
|
|
77
|
+
options = { ...options, readPhase: options.readPhase ?? codexReadPhase(bridge, options) };
|
|
61
78
|
const sessions = new Set([options.sessionId]);
|
|
62
79
|
const observed = await observeCodexSessions(bridge, options, sessions, active, remember);
|
|
63
80
|
return { ...observed.observations[0], settled: !observed.active && !observed.sessions.length, unsettled_sessions: observed.sessions, tree_session_ids: [...sessions], tree_observations: observed.observations.map(item => ({ provider_session_id: item.provider_session_id, status: item.status, settled: item.settled })) };
|
|
64
81
|
}
|
|
65
82
|
|
|
66
83
|
export async function cancelCodexTree(bridge, options, { remember = () => {}, active = () => null, timeoutMs = 5_000 } = {}) {
|
|
84
|
+
// Zero requests one best-effort cancellation/observation pass, not an
|
|
85
|
+
// invalid zero-length RPC window or permission to skip owned children.
|
|
86
|
+
const readBudget = timeoutMs === 0 ? CODEX_REQUEST_TIMEOUT_MS : timeoutMs;
|
|
87
|
+
options = { ...options, readPhase: options.readPhase ?? codexReadPhase(bridge, { ...options, timeoutMs: readBudget, livenessTimeoutMs: readBudget }) };
|
|
67
88
|
const sessions = new Set([options.sessionId]);
|
|
68
89
|
const settlement = await waitForSettlement({
|
|
69
90
|
timeoutMs, knownSessions: () => [...sessions],
|
|
70
91
|
cancel: async sessionId => {
|
|
71
|
-
try { await cancelSessionWithBridge(bridge, { ...options, sessionId, turnId:
|
|
92
|
+
try { await cancelSessionWithBridge(bridge, { ...options, sessionId, turnId: sessionId === options.sessionId ? options.turnId : undefined }); }
|
|
72
93
|
catch (error) { if (error.code !== "turn_not_active") throw error; }
|
|
73
94
|
},
|
|
74
95
|
observe: () => observeCodexSessions(bridge, options, sessions, active, remember)
|