@deksden-com/dd-flow-cli 0.9.0-beta.145 → 0.9.0-beta.149
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 +71 -0
- package/dist/build-info.json +7 -7
- package/dist/cli/command-inputs.js +6 -2
- package/dist/cli/command-metadata.js +18 -2
- package/dist/cli/diagnostic-invocation.js +46 -0
- package/dist/cli/diagnostic.js +120 -0
- package/dist/cli/help.js +35 -0
- package/dist/cli/memory-flow-admission.js +4 -2
- package/dist/cli/memory-init.js +5 -2
- package/dist/cli/run-cli.js +16 -15
- package/dist/cli.js +6 -0
- package/dist/domain/diagnostic-aspects.js +103 -0
- package/dist/domain/diagnostic-flows.js +8 -0
- package/dist/harness-runtime/lib/agy-native-inspection.mjs +216 -0
- package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +84 -10
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +306 -34
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +27 -13
- package/dist/harness-runtime/lib/dd-codex.mjs +4 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +4 -3
- package/dist/harness-runtime/lib/dd-droid.mjs +70 -28
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +3 -3
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +66 -20
- package/dist/harness-runtime/lib/dd-opencode.mjs +30 -3
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +7 -6
- package/dist/harness-runtime/lib/driver-recovery.mjs +9 -8
- package/dist/harness-runtime/lib/managed-daemon.d.mts +1 -1
- package/dist/harness-runtime/lib/managed-daemon.mjs +11 -3
- package/dist/harness-runtime/lib/native-children.d.mts +8 -1
- package/dist/harness-runtime/lib/native-children.mjs +114 -8
- package/dist/harness-runtime/lib/native-request-proof.mjs +1 -1
- package/dist/harness-runtime/lib/observation-clock.d.mts +2 -2
- package/dist/harness-runtime/lib/observation-clock.mjs +7 -4
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +1 -1
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +1 -1
- package/dist/harness-runtime/lib/tool-observations.mjs +1 -1
- package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
- package/dist/schemas/diagnostic-work-result.schema.json +296 -0
- package/dist/schemas/execution-bundle.schema.json +7 -1
- package/dist/schemas/project-flow-pack-manifest.schema.json +1 -1
- package/dist/services/check-environment.js +21 -0
- package/dist/services/check-runner-evidence.js +267 -15
- package/dist/services/code-checks.js +23 -15
- package/dist/services/controller-execution.js +10 -4
- package/dist/services/controller-fanout.js +13 -6
- package/dist/services/controller-memory-worker.js +3 -2
- package/dist/services/dashboard.js +2 -2
- package/dist/services/descriptor-run-settlement.js +5 -3
- package/dist/services/descriptor-run-start.js +22 -3
- package/dist/services/descriptor-stage-coordinator.js +37 -4
- package/dist/services/descriptor-stage-settlement.js +2 -1
- package/dist/services/diagnostic-evidence.js +73 -0
- package/dist/services/diagnostic-report-publication.js +72 -0
- package/dist/services/diagnostic-report.js +67 -0
- package/dist/services/diagnostic-run-bundle.js +74 -0
- package/dist/services/diagnostic-snapshot.js +159 -0
- package/dist/services/diagnostic-stage-handlers.js +108 -0
- package/dist/services/diagnostic-stage-results.js +132 -0
- package/dist/services/diagnostic-start.js +84 -0
- package/dist/services/diagnostic-work-batch.js +34 -0
- package/dist/services/diagnostic-work-collection.js +97 -0
- package/dist/services/diagnostic-work-registration.js +101 -0
- package/dist/services/diagnostic-work-results.js +93 -0
- package/dist/services/eval-snapshots.js +85 -29
- package/dist/services/execution-bundles.js +19 -7
- package/dist/services/flow-catalog.js +6 -1
- package/dist/services/frozen-engine.js +37 -2
- package/dist/services/hybrid-contracts.js +2 -0
- package/dist/services/installed-flow-source.js +25 -3
- package/dist/services/lifecycle-invocations.js +147 -34
- package/dist/services/memory-init-contexts.js +16 -0
- package/dist/services/memory-init-controller-start.js +12 -4
- package/dist/services/memory-init-lint-capability.js +5 -5
- package/dist/services/memory-init-native-capacity.js +4 -4
- package/dist/services/memory-init-preflight.js +10 -16
- package/dist/services/memory-init-worktrunk-capability.js +3 -7
- package/dist/services/memory-layer.js +4 -3
- package/dist/services/memory-tool-probe.js +25 -0
- package/dist/services/memory-verification-checks.js +15 -4
- package/dist/services/native-daemon-history.js +2 -1
- package/dist/services/provider-capacity.js +43 -38
- package/dist/services/release-cache.js +8 -0
- package/dist/services/run-control.js +2 -1
- package/dist/services/run-controller-adapter.js +2 -1
- package/dist/services/run-controller-process.js +2 -1
- package/dist/services/run-controller-recovery.js +2 -1
- package/dist/services/run-controller.js +85 -15
- package/dist/services/run-fork.js +1 -1
- package/dist/services/runs.js +6 -1
- package/dist/services/runtime-scope-resume.js +12 -6
- package/dist/services/runtime-scope-stop.js +3 -2
- package/dist/services/runtime-service.js +2 -1
- package/dist/services/stage-handlers.js +16 -3
- package/dist/services/stage-pause.js +4 -1
- package/dist/services/version-status.js +1 -1
- package/dist/services/vnext-code.js +2 -2
- package/dist/services/vnext-fanout.js +31 -1
- package/dist/services/work-registry.js +37 -6
- package/dist/services/workspace-bootstrap.js +2 -1
- package/package.json +6 -3
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
} from "./dd-zcode.mjs";
|
|
31
31
|
|
|
32
32
|
const REQUEST_SCHEMA = "dd-zcode/daemon-request@1";
|
|
33
|
-
import { settledNativeTree } from "./native-children.mjs";
|
|
33
|
+
import { settledNativeTree, nativeExecutionReceipt } from "./native-children.mjs";
|
|
34
34
|
const RESPONSE_SCHEMA = "dd-zcode/daemon-response@1";
|
|
35
35
|
const STATE_SCHEMA = "dd-zcode/daemon-state@1";
|
|
36
36
|
// ACP silence starts diagnostics; it is not a provider-failure deadline.
|
|
@@ -549,7 +549,7 @@ export class DaemonRuntime {
|
|
|
549
549
|
for (const receipt of [previous, closed]) {
|
|
550
550
|
if (receipt?.close?.closed === true && receipt.settled === true
|
|
551
551
|
&& receipt.topology?.complete === true && receipt.topology.root_session_id === params.sessionId
|
|
552
|
-
&& Array.isArray(receipt.topology.child_session_ids)) return receipt;
|
|
552
|
+
&& Array.isArray(receipt.topology.child_session_ids)) return nativeExecutionReceipt(receipt, { daemonId: this.state.daemon_id, generation: this.controlGeneration ?? 0 });
|
|
553
553
|
}
|
|
554
554
|
const known = [...new Set([...topology.child_session_ids, ...(previous?.topology?.child_session_ids ?? [])])];
|
|
555
555
|
const emptyRoot = this.neverPromptedRootProof(params.sessionId);
|
|
@@ -559,7 +559,7 @@ export class DaemonRuntime {
|
|
|
559
559
|
this.state.settlement_observations ??= {};
|
|
560
560
|
this.state.settlement_observations[params.sessionId] = observation;
|
|
561
561
|
await this.persist();
|
|
562
|
-
return observation;
|
|
562
|
+
return nativeExecutionReceipt(observation, { daemonId: this.state.daemon_id, generation: this.controlGeneration ?? 0 });
|
|
563
563
|
}
|
|
564
564
|
const adapter = params.adapterSessionId ?? params.sessionId;
|
|
565
565
|
if (!this.liveAdapters.has(adapter) && this.activeProductive) {
|
|
@@ -584,8 +584,8 @@ export class DaemonRuntime {
|
|
|
584
584
|
const childIds = [...new Set([...(this.sessions.get(result.provider_session_id)?.known_child_session_ids ?? []), ...zcodeChildSessionIds(nativeChildren)])];
|
|
585
585
|
const childStates = new Map([...(nativeChildren?.ended?.items ?? nativeChildren?.completed ?? []), ...(nativeChildren?.running ?? []).map(child => ({ ...child, status: "running" }))].map(child => [child.childSessionId ?? child.sessionId, child.status]));
|
|
586
586
|
const rootSettled = !this.activeProductive && failure?.provider_session_id === result.provider_session_id && (result.evidence ?? result).read?.projection?.status === "idle";
|
|
587
|
-
return { ...result, settled, ...settledNativeTree(result.provider_session_id, childIds.map(childSessionId => ({ childSessionId, status: childStates.get(childSessionId) ?? "unknown" })), settled, rootSettled),
|
|
588
|
-
...(failure?.provider_session_id === result.provider_session_id ? { terminal_turn_id: String(failure.request_id) } : {}) };
|
|
587
|
+
return nativeExecutionReceipt({ ...result, settled, ...settledNativeTree(result.provider_session_id, childIds.map(childSessionId => ({ childSessionId, status: childStates.get(childSessionId) ?? "unknown" })), settled, rootSettled),
|
|
588
|
+
...(failure?.provider_session_id === result.provider_session_id ? { terminal_turn_id: String(failure.request_id) } : {}) }, { daemonId: this.state.daemon_id, generation: this.controlGeneration ?? 0 });
|
|
589
589
|
}
|
|
590
590
|
if (operation === "session.cancel") {
|
|
591
591
|
if (!this.sessions.has(params.sessionId)) throw new DaemonError("session_identity_mismatch", "cancellation Session is not owned by this daemon");
|
|
@@ -604,6 +604,7 @@ export class DaemonRuntime {
|
|
|
604
604
|
return result;
|
|
605
605
|
}
|
|
606
606
|
if (operation === "daemon.stop") return await completeDaemonShutdown(this.paths.dir, this.state, patch => this.persist(patch), () => this.prepareStop(params.cancelTree === true), [
|
|
607
|
+
["provider", () => this.bridge.close?.()],
|
|
607
608
|
["provider_resource", () => finishDaemonProcess(this.state.config, this.state.provider_process)],
|
|
608
609
|
["daemon_resource", () => finishDaemonProcess(this.state.config, this.state.resource_process)]
|
|
609
610
|
]);
|
|
@@ -691,7 +692,7 @@ export class DaemonRuntime {
|
|
|
691
692
|
try { if (!bridgeClosed && typeof this.bridge.close === "function") await this.bridge.close(); }
|
|
692
693
|
catch (error) { throw new DaemonError(error.code ?? "provider_process_stop_incomplete", error.message ?? String(error), false, error.details); }
|
|
693
694
|
await this.persist({ active_tree: false, shutdown_state: "stopping", active_operation: null, active_request: null, physical_settlement: { confirmed_at: new Date().toISOString(), bridge_pid: this.bridge.child?.pid ?? null } });
|
|
694
|
-
return { daemon_id: this.state.daemon_id, stopped: true, clean: true, physical_settlement: this.state.physical_settlement, _shutdown: true };
|
|
695
|
+
return { daemon_id: this.state.daemon_id, stopped: true, clean: true, settled: true, native_settled: true, physical_settlement: this.state.physical_settlement, _shutdown: true };
|
|
695
696
|
}
|
|
696
697
|
|
|
697
698
|
}
|
|
@@ -6,22 +6,23 @@ import { writeJsonAtomic } from "./runner-events.mjs";
|
|
|
6
6
|
import { reportedError } from "./operation-errors.mjs";
|
|
7
7
|
import { ObservationClock } from "./observation-clock.mjs";
|
|
8
8
|
import { runtimeProcess } from "./managed-daemon.mjs";
|
|
9
|
+
import { nativeShutdownConfirmed } from "./native-children.mjs";
|
|
9
10
|
|
|
10
|
-
function cleanShutdownConfirmed(state) {
|
|
11
|
+
async function cleanShutdownConfirmed(root, state) {
|
|
11
12
|
const proof = state.shutdown;
|
|
13
|
+
let fence;
|
|
14
|
+
try { fence = JSON.parse(await readFile(path.join(root, 'operation-fence.json'), 'utf8')); }
|
|
15
|
+
catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
12
16
|
return state.shutdown_state === "clean" && state.active_tree === false
|
|
13
|
-
&&
|
|
14
|
-
&& proof
|
|
15
|
-
&& proof.required_phases.length >= 3 && proof.required_phases.includes("tree")
|
|
16
|
-
&& proof.required_phases.includes("daemon_resource")
|
|
17
|
-
&& proof.required_phases.every(phase => typeof phase === "string" && proof.phases?.[phase] === true);
|
|
17
|
+
&& typeof state.daemon_id === 'string' && fence?.daemon_id === state.daemon_id && Number.isSafeInteger(fence.generation) && fence.generation >= 0
|
|
18
|
+
&& nativeShutdownConfirmed(proof, { daemonId: state.daemon_id, generation: fence.generation });
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/** Explicit same-Session restart retains native stores and the old daemon
|
|
21
22
|
* receipt; it does not turn a terminal directory into a new execution. */
|
|
22
23
|
export async function authorizeRetainedDaemonResume(root, previous, sessionId, config) {
|
|
23
24
|
const sessions = [...(previous.sessions ?? []), ...(previous.root_session_id ? [previous.root_session_id] : [])];
|
|
24
|
-
if (!cleanShutdownConfirmed(previous) || typeof sessionId !== "string" || !sessions.some(session => (typeof session === "string" ? session : session.provider_session_id) === sessionId)) throw Object.assign(new Error("terminal daemon restart requires a retained, settled Session identity and complete shutdown proof"), { code: "daemon_state_terminal" });
|
|
25
|
+
if (!await cleanShutdownConfirmed(root, previous) || typeof sessionId !== "string" || !sessions.some(session => (typeof session === "string" ? session : session.provider_session_id) === sessionId)) throw Object.assign(new Error("terminal daemon restart requires a retained, settled Session identity and complete shutdown proof"), { code: "daemon_state_terminal" });
|
|
25
26
|
for (const key of ["cwd", "projectRoot", "ddFlowHome", "ddFlowBin", "provider", "model", "reasoning", "mode", "variant", "agent", "bin", "noFlow"]) {
|
|
26
27
|
if ((previous.config?.[key] ?? null) !== (config[key] ?? null)) throw Object.assign(new Error(`retained daemon configuration changed: ${key}`), { code: "daemon_config_mismatch" });
|
|
27
28
|
}
|
|
@@ -53,7 +54,7 @@ export async function recoverySettlement(driversRoot) {
|
|
|
53
54
|
}
|
|
54
55
|
else if (entry.name === "daemon.json") {
|
|
55
56
|
const state = JSON.parse(await readFile(file, "utf8"));
|
|
56
|
-
if (!cleanShutdownConfirmed(state)) throw Object.assign(new Error("provider tree has no complete clean shutdown receipt"), { code: "recovery_settlement_unconfirmed" });
|
|
57
|
+
if (!await cleanShutdownConfirmed(directory, state)) throw Object.assign(new Error("provider tree has no complete clean shutdown receipt"), { code: "recovery_settlement_unconfirmed" });
|
|
57
58
|
await assertDaemonReplaceable(directory);
|
|
58
59
|
await reconcileDriverReplies(directory);
|
|
59
60
|
if (state.config?.resourceHome && state.resource_process?.id) {
|
|
@@ -10,7 +10,7 @@ export function heartbeatDaemonProcess(config: RuntimeConfig, record: ProcessBin
|
|
|
10
10
|
export function finishDaemonProcess(config: RuntimeConfig, record: ProcessBinding | null | undefined, state?: string, reason?: string): Promise<void>;
|
|
11
11
|
export function cleanupFailedDaemonStart(config: RuntimeConfig, record: ProcessBinding | null, child: ChildProcess | null, original: Error & { details?: Record<string, unknown> }, readState: () => Promise<Record<string, unknown>>, daemonId: string): Promise<void>;
|
|
12
12
|
export function completeBudgetOperation(stateDir: string, request: { id: string; operation: string; params?: { sessionId?: string } }, result: unknown, observe?: () => Promise<Record<string, unknown>>): Promise<Record<string, unknown>>;
|
|
13
|
-
export function stopProcessGroup(child: ChildProcess | null | undefined, graceMs?: number, options?: { detached?: boolean; settlementMs?: number }): Promise<void>;
|
|
13
|
+
export function stopProcessGroup(child: ChildProcess | { pid: number; started: string } | null | undefined, graceMs?: number, options?: { detached?: boolean; settlementMs?: number; started?: string }): Promise<void>;
|
|
14
14
|
export function assertStartupChild(child: Pick<ChildProcess, "pid" | "exitCode" | "signalCode">, expected: Record<string, unknown>, observed: Record<string, unknown> | null, log: string): void;
|
|
15
15
|
export function retainStartupFailure(input: { state: Record<string, unknown>; publish: (state: Record<string, unknown>) => Promise<unknown>; log: string; error: Error & { code?: string; details?: Record<string, unknown> } }): Promise<void>;
|
|
16
16
|
export function startManagedBridge(input: { config: RuntimeConfig; state: Record<string, unknown>; bridge: { child: ChildProcess | null; options: { onSpawn?: (child: ChildProcess) => Promise<void> }; start: () => Promise<unknown> }; kind: string; publish: (state: Record<string, unknown>) => Promise<unknown>; log?: string }): Promise<unknown>;
|
|
@@ -5,6 +5,7 @@ import { commandJson } from "./process-json.mjs";
|
|
|
5
5
|
import { errorRecord, reportedError, diagnosticJson } from "./operation-errors.mjs";
|
|
6
6
|
import { writeJsonAtomic } from "./runner-events.mjs";
|
|
7
7
|
import { runBoundedCommand } from "./native-hook-command.mjs";
|
|
8
|
+
import { processSnapshot } from "./process-snapshot.mjs";
|
|
8
9
|
import { createRenewalState, maintenanceRetryable, renewalConfirmed, renewalDelay, renewalFailure, renewalRemaining, RENEWAL_POLICY } from "./lease-renewal.mjs";
|
|
9
10
|
|
|
10
11
|
const leases = new Map();
|
|
@@ -598,14 +599,14 @@ export async function closeBridgeProcess(bridge, drain, stopOptions = {}) {
|
|
|
598
599
|
finally { bridge.closePending = null; }
|
|
599
600
|
}
|
|
600
601
|
|
|
601
|
-
export async function stopProcessGroup(child, graceMs = 1_000, { detached = true, settlementMs = 10_000 } = {}) {
|
|
602
|
+
export async function stopProcessGroup(child, graceMs = 1_000, { detached = true, settlementMs = 10_000, started = child?.started ?? null } = {}) {
|
|
602
603
|
if (!child?.pid) return;
|
|
603
604
|
if (processStops.has(child)) return await processStops.get(child);
|
|
604
|
-
const pending = stopOwnedChild(child, graceMs, detached, settlementMs);
|
|
605
|
+
const pending = stopOwnedChild(child, graceMs, detached, settlementMs, started);
|
|
605
606
|
processStops.set(child, pending);
|
|
606
607
|
try { return await pending; } finally { processStops.delete(child); }
|
|
607
608
|
}
|
|
608
|
-
async function stopOwnedChild(child, graceMs, detached, settlementMs) {
|
|
609
|
+
async function stopOwnedChild(child, graceMs, detached, settlementMs, started) {
|
|
609
610
|
const target = process.platform === "win32" || !detached ? child.pid : -child.pid;
|
|
610
611
|
const details = signal => ({ pid: child.pid, pgid: target < 0 ? child.pid : null, signal, platform: process.platform, exit_code: child.exitCode ?? null, signal_code: child.signalCode ?? null, observed_at: new Date().toISOString() });
|
|
611
612
|
const alive = () => { try { process.kill(target, 0); return true; } catch (error) { if (error.code === "ESRCH") return false; if (error.code === "EPERM") return true; throw error; } };
|
|
@@ -615,6 +616,13 @@ async function stopOwnedChild(child, graceMs, detached, settlementMs) {
|
|
|
615
616
|
return !alive();
|
|
616
617
|
};
|
|
617
618
|
const signal = async name => {
|
|
619
|
+
// A retained raw PID is not a ChildProcess handle. Revalidate its birth at
|
|
620
|
+
// every signal, including escalation after the TERM grace period.
|
|
621
|
+
if (started !== null) {
|
|
622
|
+
if (typeof started !== 'string' || !started) throw Object.assign(new Error('process birth identity is invalid'), { code: 'process_identity_unproven' });
|
|
623
|
+
const observed = (await processSnapshot()).find(item => item.pid === child.pid);
|
|
624
|
+
if (!observed || observed.started !== started || observed.zombie) return false;
|
|
625
|
+
}
|
|
618
626
|
try { process.kill(target, name); return true; }
|
|
619
627
|
catch (error) {
|
|
620
628
|
if (error.code === "ESRCH") return false;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
export const NATIVE_CHILD_CONTRACT_VERSION: "native-children@
|
|
1
|
+
export const NATIVE_CHILD_CONTRACT_VERSION: "native-children@2";
|
|
2
|
+
export const NATIVE_TREE_SETTLEMENT_CONTRACT: "native-tree-settlement@2";
|
|
3
|
+
export const NATIVE_SHUTDOWN_CONTRACT: "dd-flow/daemon-shutdown@2";
|
|
4
|
+
export const NATIVE_SHUTDOWN_PHASES: readonly string[];
|
|
5
|
+
export function nativeShutdownConfirmed(receipt: unknown, binding?: { daemonId?: string; generation?: number }): boolean;
|
|
6
|
+
export function nativeExecutionReceipt(receipt: Record<string, unknown>, options?: { daemonId?: string; generation?: number; revision?: number; resources?: Record<string, unknown> }): Record<string, unknown>;
|
|
7
|
+
export type NativeTreeReadiness = "ready" | "owned_children_live" | "inspection_pending" | "unproven";
|
|
8
|
+
export function nativeTreeReadiness(receipt: Record<string, unknown>, binding: { sessionId: string; operationId: string; turnId?: string; requestProof?: unknown; allowDescendants?: boolean }): NativeTreeReadiness;
|
|
2
9
|
export function settledNativeTree(root: string, children: Record<string, unknown>[], settled: boolean, rootSettled?: boolean): Record<string, unknown>;
|
|
3
10
|
export function safeNativeFailure(value: unknown): Record<string, unknown> | undefined;
|
|
4
11
|
export interface NativeChild {
|
|
@@ -1,9 +1,104 @@
|
|
|
1
|
-
export const NATIVE_CHILD_CONTRACT_VERSION = "native-children@
|
|
1
|
+
export const NATIVE_CHILD_CONTRACT_VERSION = "native-children@2";
|
|
2
|
+
export const NATIVE_TREE_SETTLEMENT_CONTRACT = "native-tree-settlement@2";
|
|
3
|
+
export const NATIVE_SHUTDOWN_CONTRACT = "dd-flow/daemon-shutdown@2";
|
|
4
|
+
export const NATIVE_SHUTDOWN_PHASES = Object.freeze(["execution", "owned_resources", "provider", "daemon_resource"]);
|
|
2
5
|
import { errorRecord } from "./operation-errors.mjs";
|
|
3
6
|
import { safeProviderError } from "./provider-overload-policy.mjs";
|
|
7
|
+
import { streamRequestKey } from "./provider-overload-policy.mjs";
|
|
8
|
+
const liveStatus = value => ["active", "running", "inprogress", "pending", "streaming", "busy"].includes(String(typeof value === "object" && value ? value.type : value).toLowerCase());
|
|
9
|
+
|
|
10
|
+
/** One current receipt only. Producers, not status ranks, establish freshness. */
|
|
11
|
+
export function nativeTreeReadiness(receipt, { sessionId, operationId, turnId, requestProof, allowDescendants = true } = {}) {
|
|
12
|
+
if (receipt?.native_settlement?.contract !== NATIVE_TREE_SETTLEMENT_CONTRACT || receipt.native_settlement.scope !== "required_execution") return "unproven";
|
|
13
|
+
if (!receipt || typeof sessionId !== "string" || !sessionId || typeof operationId !== "string" || !operationId || receipt.provider_session_id !== sessionId
|
|
14
|
+
|| turnId !== undefined && receipt.terminal_turn_id !== undefined && receipt.terminal_turn_id !== turnId) return "unproven";
|
|
15
|
+
const requestKey = requestProof?.kind === "stream_request" ? streamRequestKey(requestProof) : null;
|
|
16
|
+
if (requestProof?.kind === "stream_request" && (!requestKey || requestProof.operation_id !== operationId
|
|
17
|
+
|| streamRequestKey(receipt.terminal_metadata?.request_proof) !== requestKey)) return "unproven";
|
|
18
|
+
const pending = receipt.native_settlement;
|
|
19
|
+
if (pending?.state === "inspection_pending") {
|
|
20
|
+
const clock = pending.observation_clock;
|
|
21
|
+
return receipt.settled === false && pending.confirmed === false && pending.operation_id === operationId
|
|
22
|
+
&& pending.provider_session_id === sessionId && requestKey && pending.request_key === requestKey
|
|
23
|
+
&& clock?.policy === "operation-progress@1" && Number.isFinite(clock.started_at)
|
|
24
|
+
&& Number.isSafeInteger(clock.timeout_ms) && clock.timeout_ms > 0 && clock.timeout_ms <= 2147483647
|
|
25
|
+
&& Number.isFinite(clock.elapsed_ms) && clock.elapsed_ms >= 0 && clock.elapsed_ms < clock.timeout_ms
|
|
26
|
+
&& Number.isSafeInteger(clock.gaps) && clock.gaps >= 0
|
|
27
|
+
&& typeof clock.uncertain === "boolean"
|
|
28
|
+
&& Number.isFinite(clock.uncertainty_elapsed_ms) && clock.uncertainty_elapsed_ms >= 0
|
|
29
|
+
&& (clock.observed_at === null || Number.isFinite(clock.observed_at))
|
|
30
|
+
&& clock.observation_lost === false ? "inspection_pending" : "unproven";
|
|
31
|
+
}
|
|
32
|
+
if (receipt.native_turn_items?.pending === true) return "unproven";
|
|
33
|
+
const ids = receipt.tree_session_ids, observations = receipt.tree_observations, unsettled = receipt.unsettled_sessions;
|
|
34
|
+
if (!Array.isArray(ids) || !ids.length || ids[0] !== sessionId || ids.some(id => typeof id !== "string" || !id)
|
|
35
|
+
|| new Set(ids).size !== ids.length || !allowDescendants && ids.length !== 1
|
|
36
|
+
|| !Array.isArray(observations) || observations.length !== ids.length
|
|
37
|
+
|| !ids.every(id => observations.filter(item => item?.provider_session_id === id).length === 1)
|
|
38
|
+
|| observations.some(item => !item || typeof item !== "object" || Array.isArray(item) || typeof item.settled !== "boolean" || item.settled === true && liveStatus(item.status)) || !Array.isArray(unsettled)
|
|
39
|
+
|| new Set(unsettled).size !== unsettled.length) return "unproven";
|
|
40
|
+
// A producer's tree list cannot silently omit another positively observed
|
|
41
|
+
// owned child. Normalize the same receipt, not a second historical ledger.
|
|
42
|
+
try {
|
|
43
|
+
const children = normalizeNativeChildren(receipt, sessionId);
|
|
44
|
+
if (children.some(child => !ids.includes(child.session_id) || !ids.includes(child.parent_session_id) || child.parent_session_id === child.session_id)) return "unproven";
|
|
45
|
+
for (const child of children) {
|
|
46
|
+
let parent = child.parent_session_id;
|
|
47
|
+
const visited = new Set([child.session_id]);
|
|
48
|
+
while (parent !== sessionId) {
|
|
49
|
+
if (visited.has(parent)) return "unproven";
|
|
50
|
+
visited.add(parent); parent = children.find(item => item.session_id === parent)?.parent_session_id;
|
|
51
|
+
if (!parent) return "unproven";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} catch { return "unproven"; }
|
|
55
|
+
const live = observations.filter(item => !item.settled);
|
|
56
|
+
if (unsettled.length !== live.length || !live.every(item => unsettled.includes(item.provider_session_id))) return "unproven";
|
|
57
|
+
const settlement = receipt.settlement;
|
|
58
|
+
const budgetSettled = !settlement || settlement.state === "not_required"
|
|
59
|
+
|| settlement.state === "settled" && settlement.operation_ids?.includes(operationId) === true;
|
|
60
|
+
if (receipt.settled === true && receipt.native_settled !== false && pending?.confirmed !== false && !live.length && budgetSettled) return "ready";
|
|
61
|
+
const root = observations.find(item => item.provider_session_id === sessionId);
|
|
62
|
+
const knownLive = live.length > 0 && live.every(item => item.provider_session_id !== sessionId && liveStatus(item.status));
|
|
63
|
+
return allowDescendants && receipt.settled === false && root.settled === true && knownLive
|
|
64
|
+
&& (!settlement || settlement.state === "not_required" || settlement.state === "pending" && settlement.reason === "tree_unsettled")
|
|
65
|
+
? "owned_children_live" : "unproven";
|
|
66
|
+
}
|
|
4
67
|
|
|
5
68
|
function nativeFailure(code, message, exitCode, details) { return Object.assign(new Error(message), { code, exitCode, details }); }
|
|
6
69
|
|
|
70
|
+
/** Control settlement is not native settlement; explicit uncertainty wins. */
|
|
71
|
+
export function nativeShutdownConfirmed(receipt, { daemonId, generation } = {}) {
|
|
72
|
+
const proof = receipt?.schema_id === NATIVE_SHUTDOWN_CONTRACT ? receipt : receipt?.shutdown_proof;
|
|
73
|
+
const result = receipt?.schema_id === NATIVE_SHUTDOWN_CONTRACT ? receipt.result : receipt;
|
|
74
|
+
return Boolean(proof?.schema_id === NATIVE_SHUTDOWN_CONTRACT && typeof proof.daemon_id === "string" && proof.daemon_id
|
|
75
|
+
&& (daemonId === undefined || proof.daemon_id === daemonId) && Number.isSafeInteger(proof.generation) && proof.generation >= 0
|
|
76
|
+
&& (generation === undefined || proof.generation === generation)
|
|
77
|
+
&& Array.isArray(proof.required_phases) && new Set(proof.required_phases).size === proof.required_phases.length
|
|
78
|
+
&& NATIVE_SHUTDOWN_PHASES.every(phase => proof.required_phases.includes(phase))
|
|
79
|
+
&& proof.required_phases.every(phase => typeof phase === "string" && proof.phases?.[phase] === true)
|
|
80
|
+
&& result?.daemon_id === proof.daemon_id && result.shutdown_contract === NATIVE_SHUTDOWN_CONTRACT
|
|
81
|
+
&& result.clean === true && result.stopped === true && result.physical_stopped === true
|
|
82
|
+
&& result.native_settled === true && result.settled === true && result.native_settlement?.confirmed !== false
|
|
83
|
+
&& result.resource_retirement?.confirmed !== false);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Scope metadata does not manufacture readiness or physical cleanup. */
|
|
87
|
+
export function nativeExecutionReceipt(receipt, { daemonId, generation = 0, revision = 0, resources } = {}) {
|
|
88
|
+
if (receipt.native_settlement?.contract !== undefined && receipt.native_settlement.contract !== NATIVE_TREE_SETTLEMENT_CONTRACT
|
|
89
|
+
|| receipt.native_settlement?.scope !== undefined && receipt.native_settlement.scope !== "required_execution")
|
|
90
|
+
throw Object.assign(new Error("historical native proof requires fresh execution observation"), { code: "native_contract_mismatch" });
|
|
91
|
+
if ((resources?.entries ?? []).some(item => item.owner_daemon_id !== undefined && item.owner_daemon_id !== daemonId
|
|
92
|
+
|| item.generation !== undefined && item.generation !== generation)) throw Object.assign(new Error("native resource belongs to another owner"), { code: "native_resource_owner_mismatch" });
|
|
93
|
+
const owner = { owner_daemon_id: daemonId ?? null, generation, revision };
|
|
94
|
+
return { ...receipt,
|
|
95
|
+
native_settlement: { ...receipt.native_settlement, contract: NATIVE_TREE_SETTLEMENT_CONTRACT, scope: "required_execution", ...owner },
|
|
96
|
+
native_resources: { observed_at: new Date().toISOString(), coverage: "unsupported", ...resources,
|
|
97
|
+
schema_id: "native-resources@1", ...owner,
|
|
98
|
+
entries: (resources?.entries ?? []).map(item => ({ ...item, owner_daemon_id: owner.owner_daemon_id, generation: owner.generation })) }
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
7
102
|
/** Project a positively settled native tree, never infer it from missing activity. */
|
|
8
103
|
export function settledNativeTree(root, children, settled, rootSettled = false) {
|
|
9
104
|
if (settled !== true && rootSettled !== true || typeof root !== "string" || !root || !Array.isArray(children)) return {};
|
|
@@ -12,9 +107,9 @@ export function settledNativeTree(root, children, settled, rootSettled = false)
|
|
|
12
107
|
const observations = [{ provider_session_id: root, status: "idle", settled: true }, ...children.map((child, index) => {
|
|
13
108
|
const status = String(child.status ?? child.state ?? "unknown").toLowerCase();
|
|
14
109
|
const terminal = settled === true || ["completed", "success", "succeeded", "failed", "cancelled", "canceled", "settled_by_root"].includes(status);
|
|
15
|
-
return { provider_session_id: ids[index], status: terminal ? "idle" :
|
|
110
|
+
return { provider_session_id: ids[index], status: terminal ? "idle" : liveStatus(status) ? "running" : "unknown", settled: terminal };
|
|
16
111
|
})];
|
|
17
|
-
return { tree_session_ids: [root, ...ids], unsettled_sessions: observations.filter(item => !item.settled).map(item => item.provider_session_id), tree_observations: observations };
|
|
112
|
+
return { native_settlement: { contract: NATIVE_TREE_SETTLEMENT_CONTRACT, scope: "required_execution" }, tree_session_ids: [root, ...ids], unsettled_sessions: observations.filter(item => !item.settled).map(item => item.provider_session_id), tree_observations: observations };
|
|
18
113
|
}
|
|
19
114
|
|
|
20
115
|
/** Retain typed failure facts, never provider headers, request bodies or free-form messages. */
|
|
@@ -51,6 +146,8 @@ export function normalizeNativeChildren(receipt, rootSessionId) {
|
|
|
51
146
|
.map(value => text(object(value), ["provider_session_id", "session_id"]))
|
|
52
147
|
.filter(id => id !== null)
|
|
53
148
|
);
|
|
149
|
+
const unsettledTree = new Set(list(receipt.tree_observations).filter(value => object(value).settled === false)
|
|
150
|
+
.map(value => text(object(value), ["provider_session_id", "session_id"])).filter(Boolean));
|
|
54
151
|
const add = (raw, source, scopedParent = null, fallback = "unknown") => {
|
|
55
152
|
const value = object(raw);
|
|
56
153
|
const id = text(value, ["provider_session_id", "session_id", "sessionId", "childSessionId", "conversation_id", "conversationId", "id", "subagentId"]);
|
|
@@ -66,9 +163,12 @@ export function normalizeNativeChildren(receipt, rootSessionId) {
|
|
|
66
163
|
// Provider idle means no current activity, not a Work result. Only an
|
|
67
164
|
// explicit terminal result or a separately proven root settlement may be
|
|
68
165
|
// terminal here; unknown states stay unknown for reconciliation.
|
|
69
|
-
const status = ["completed", "complete", "done", "success", "succeeded"].includes(state) ? "completed" : state === "settled_by_root" ? "settled_by_root" : ["failed", "error"].includes(state) ? "failed" : ["cancelled", "canceled", "interrupted"].includes(state) ? "cancelled" :
|
|
166
|
+
const status = ["completed", "complete", "done", "success", "succeeded"].includes(state) ? "completed" : state === "settled_by_root" ? "settled_by_root" : ["failed", "error"].includes(state) ? "failed" : ["cancelled", "canceled", "interrupted"].includes(state) ? "cancelled" : liveStatus(state) ? "running" : fallback;
|
|
70
167
|
const rootChild = parent === rootSessionId;
|
|
71
168
|
const settled = rootChild && status === "unknown" && settledTree.has(id) ? "settled_by_root" : status;
|
|
169
|
+
if ((unsettledTree.has(id) || value.native_turn_items?.pending === true) && ["completed", "failed", "cancelled", "settled_by_root"].includes(settled)
|
|
170
|
+
|| settledTree.has(id) && (unsettledTree.has(id) || status === "running" || value.native_turn_items?.pending === true))
|
|
171
|
+
throw nativeFailure("native_child_observation_conflict", "Current child outcome contradicts tree observation", 1, { session_id: id, source });
|
|
72
172
|
candidates.push({ session_id: id, parent_session_id: parent ?? "", status: settled, source, ...(rootChild ? {} : { provenance: "parent_mismatch" }),
|
|
73
173
|
...(text(value, ["terminal_turn_id"]) ? { terminal_turn_id: text(value, ["terminal_turn_id"]) } : {}),
|
|
74
174
|
...(text(value, ["terminal_observed_at"]) ? { terminal_observed_at: text(value, ["terminal_observed_at"]) } : {}),
|
|
@@ -87,9 +187,12 @@ export function normalizeNativeChildren(receipt, rootSessionId) {
|
|
|
87
187
|
add(value, "zcode/session/subagents", rootSessionId);
|
|
88
188
|
}
|
|
89
189
|
const unique = new Map();
|
|
90
|
-
const
|
|
190
|
+
const terminal = value => ["completed", "failed", "cancelled", "settled_by_root"].includes(value);
|
|
91
191
|
for (const child of candidates) {
|
|
92
192
|
const previous = unique.get(child.session_id);
|
|
193
|
+
if (previous && (terminal(previous.status) && child.native_turn_items?.pending === true
|
|
194
|
+
|| terminal(child.status) && previous.native_turn_items?.pending === true))
|
|
195
|
+
throw nativeFailure("native_child_observation_conflict", "Current child outcome contradicts pending native items", 1, { session_id: child.session_id, observations: [previous, child] });
|
|
93
196
|
if (previous && previous.parent_session_id !== child.parent_session_id) {
|
|
94
197
|
// Keep the contradictory parent visible to the existing provenance gate,
|
|
95
198
|
// independently of input order or which source happened to say success.
|
|
@@ -97,17 +200,20 @@ export function normalizeNativeChildren(receipt, rootSessionId) {
|
|
|
97
200
|
unique.set(child.session_id, { ...foreign, provenance: "parent_mismatch" });
|
|
98
201
|
continue;
|
|
99
202
|
}
|
|
100
|
-
if (previous &&
|
|
203
|
+
if (previous && terminal(previous.status) && terminal(child.status) && previous.status !== child.status && ![previous.status, child.status].includes("settled_by_root")) throw nativeFailure("native_child_outcome_conflict", "Native child has contradictory terminal outcomes", 1, { session_id: child.session_id, observations: [previous, child] });
|
|
204
|
+
if (previous && (previous.status === "running" && terminal(child.status) || child.status === "running" && terminal(previous.status)))
|
|
205
|
+
throw nativeFailure("native_child_observation_conflict", "Current child has both active and terminal facts", 1, { session_id: child.session_id, observations: [previous, child] });
|
|
101
206
|
if (previous && previous.status === child.status && previous.terminal_turn_id && child.terminal_turn_id && previous.terminal_turn_id !== child.terminal_turn_id)
|
|
102
207
|
throw nativeFailure("native_child_turn_conflict", "Native child has contradictory terminal Turn identities", 1, { session_id: child.session_id, observations: [previous, child] });
|
|
103
208
|
if (previous && previous.status === child.status) {
|
|
104
209
|
unique.set(child.session_id, { ...previous, ...child,
|
|
105
210
|
...(child.terminal_turn_id ?? previous.terminal_turn_id ? { terminal_turn_id: child.terminal_turn_id ?? previous.terminal_turn_id } : {}),
|
|
106
211
|
...(child.provider_error ?? previous.provider_error ? { provider_error: child.provider_error ?? previous.provider_error } : {}),
|
|
107
|
-
...(child.native_turn_items ?? previous.native_turn_items ? { native_turn_items:
|
|
212
|
+
...(child.native_turn_items ?? previous.native_turn_items ? { native_turn_items: { ...previous.native_turn_items, ...child.native_turn_items,
|
|
213
|
+
...([previous.native_turn_items, child.native_turn_items].some(items => items?.pending === true) ? { pending: true } : {}) } } : {}) });
|
|
108
214
|
continue;
|
|
109
215
|
}
|
|
110
|
-
if (!previous ||
|
|
216
|
+
if (!previous || previous.status === "unknown" || previous.status === "settled_by_root" && terminal(child.status)) unique.set(child.session_id, child);
|
|
111
217
|
}
|
|
112
218
|
return [...unique.values()].sort((left, right) => left.session_id.localeCompare(right.session_id));
|
|
113
219
|
}
|
|
@@ -13,7 +13,7 @@ const fail = reason => { throw Object.assign(new Error('Native request proof doe
|
|
|
13
13
|
* the adapter, and recomputed by the adapter's shared request builder. */
|
|
14
14
|
export function nativeCommandFingerprint({ executable, args, cwd, env, operationId }) {
|
|
15
15
|
return hashJson({ executable, args, cwd, operation_id: operationId,
|
|
16
|
-
environment: Object.fromEntries(environmentKeys.map(key => [key, env[key] ?? null])) });
|
|
16
|
+
environment: Object.fromEntries([...environmentKeys, ...(env.PLAYWRIGHT_BROWSERS_PATH === undefined ? [] : ['PLAYWRIGHT_BROWSERS_PATH'])].map(key => [key, env[key] ?? null])) });
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/** Called after the adapter's own argument preparation but before its RPC or
|
|
@@ -3,12 +3,12 @@ export function validateObservationDuration(value: number, label?: string): numb
|
|
|
3
3
|
export interface ObservationState {
|
|
4
4
|
policy: string; timeout_ms: number; started_at: number; cursor?: unknown;
|
|
5
5
|
observed_at: number | null; elapsed_ms: number; uncertainty_elapsed_ms: number;
|
|
6
|
-
gaps: number; observation_lost: boolean;
|
|
6
|
+
gaps: number; uncertain?: boolean; observation_lost: boolean;
|
|
7
7
|
}
|
|
8
8
|
export class ObservationClock {
|
|
9
9
|
constructor(options: { timeoutMs: number; gapMs?: number; uncertaintyMs?: number;
|
|
10
10
|
wall?: () => number; monotonic?: () => number; onGap?: (value: Record<string, unknown>) => void;
|
|
11
|
-
saved?: ObservationState | null });
|
|
11
|
+
saved?: ObservationState | null; snapshotIsLive?: boolean });
|
|
12
12
|
observationLost: boolean;
|
|
13
13
|
sample(progress?: unknown, progressAt?: number): boolean;
|
|
14
14
|
state(): ObservationState;
|
|
@@ -9,7 +9,7 @@ export function validateObservationDuration(value, label = "observation duration
|
|
|
9
9
|
* progress or proof of provider failure. Durable snapshots retain that episode. */
|
|
10
10
|
export class ObservationClock {
|
|
11
11
|
constructor({ timeoutMs, gapMs = 60_000, uncertaintyMs = 120_000, wall = Date.now,
|
|
12
|
-
monotonic = () => performance.now(), onGap = () => {}, saved = null }) {
|
|
12
|
+
monotonic = () => performance.now(), onGap = () => {}, saved = null, snapshotIsLive = false }) {
|
|
13
13
|
validateObservationDuration(timeoutMs); validateObservationDuration(gapMs, "gap duration");
|
|
14
14
|
validateObservationDuration(uncertaintyMs, "observation recovery duration");
|
|
15
15
|
Object.assign(this, { timeoutMs, gapMs, uncertaintyMs, wall, monotonic, onGap });
|
|
@@ -21,13 +21,16 @@ export class ObservationClock {
|
|
|
21
21
|
|| !Number.isFinite(saved.started_at) || !Number.isSafeInteger(saved.gaps) || saved.gaps < 0
|
|
22
22
|
|| saved.observed_at !== null && !Number.isFinite(saved.observed_at)
|
|
23
23
|
|| typeof saved.observation_lost !== "boolean"
|
|
24
|
+
|| saved.uncertain !== undefined && typeof saved.uncertain !== "boolean"
|
|
24
25
|
|| !Number.isFinite(saved.elapsed_ms) || saved.elapsed_ms < 0
|
|
25
26
|
|| !Number.isFinite(saved.uncertainty_elapsed_ms) || saved.uncertainty_elapsed_ms < 0)
|
|
26
27
|
throw new RangeError("invalid retained observation clock");
|
|
27
28
|
this.startedWall = saved.started_at; this.elapsed = saved.elapsed_ms;
|
|
28
29
|
this.progress = saved.cursor; this.progressAt = saved.observed_at;
|
|
29
|
-
//
|
|
30
|
-
|
|
30
|
+
// Durable reconstruction has an observation gap. Transferring a freshly
|
|
31
|
+
// observed producer clock is not a second restart of that same producer.
|
|
32
|
+
this.uncertain = snapshotIsLive ? saved.uncertain ?? true : true;
|
|
33
|
+
this.gaps = saved.gaps + (snapshotIsLive ? 0 : 1);
|
|
31
34
|
this.uncertaintyElapsed = saved.uncertainty_elapsed_ms;
|
|
32
35
|
this.observationLost = saved.observation_lost || this.gaps > 1 || this.uncertaintyElapsed >= uncertaintyMs;
|
|
33
36
|
}
|
|
@@ -77,7 +80,7 @@ export class ObservationClock {
|
|
|
77
80
|
return { policy: OBSERVATION_CLOCK_CONTRACT_VERSION, timeout_ms: this.timeoutMs,
|
|
78
81
|
started_at: this.startedWall, cursor: this.progress, observed_at: this.progressAt,
|
|
79
82
|
elapsed_ms: this.elapsed, uncertainty_elapsed_ms: this.uncertaintyElapsed,
|
|
80
|
-
gaps: this.gaps, observation_lost: this.observationLost };
|
|
83
|
+
gaps: this.gaps, uncertain: this.uncertain, observation_lost: this.observationLost };
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const CAPACITY_POLICY: "provider-overload-episode@1";
|
|
2
|
-
export const MAX_OVERLOAD_RETRIES:
|
|
2
|
+
export const MAX_OVERLOAD_RETRIES: 3;
|
|
3
3
|
export const MAX_RETRY_AFTER_MS: 30000;
|
|
4
4
|
export function classifyProviderError(native: unknown): string;
|
|
5
5
|
export function safeProviderError(native: unknown): Record<string, unknown>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Pure policy shared by the controller and pinned-engine EVAL consumers. */
|
|
2
2
|
export const CAPACITY_POLICY = "provider-overload-episode@1";
|
|
3
|
-
export const MAX_OVERLOAD_RETRIES =
|
|
3
|
+
export const MAX_OVERLOAD_RETRIES = 3;
|
|
4
4
|
export const MAX_RETRY_AFTER_MS = 30_000;
|
|
5
5
|
|
|
6
6
|
function policyError(code, message, details = {}) { return Object.assign(new Error(message), { code, details }); }
|
|
@@ -156,7 +156,7 @@ export function agyToolObservation(step, rootSessionId) {
|
|
|
156
156
|
const sessionId = step.conversation_id ?? rootSessionId;
|
|
157
157
|
return { session: identity("antigravity-cli", sessionId), parent_session: sessionId !== rootSessionId ? identity("antigravity-cli", rootSessionId) : null,
|
|
158
158
|
call_id: step.step_id ?? step.stepId ?? step.id ?? step.tool_call_id ?? step.toolCallId ?? (Number.isInteger(step.step_index) ? String(step.step_index) : null),
|
|
159
|
-
name: step.tool_name ?? step.tool_info?.name, ...(step.tool_info?.error ? { status: "failed" } : nativeStatus(step.state)) };
|
|
159
|
+
name: step.tool_name ?? step.tool_info?.name, ...(step.tool_info?.error ? { status: "failed" } : nativeStatus(step.state === "ACTIVE" ? "running" : step.state)) };
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
/** Native AGY JSONL records have planner tool invocations, not matching live
|