@deksden-com/dd-flow-cli 0.9.0-beta.151 → 0.9.0-beta.155
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 +23 -0
- package/dist/build-info.json +8 -8
- package/dist/cli/command-inputs.js +33 -3
- package/dist/cli/command-metadata.js +73 -2
- package/dist/cli/delivery.js +108 -0
- package/dist/cli/diagnostic-invocation.js +23 -7
- package/dist/cli/diagnostic.js +14 -3
- package/dist/cli/help.js +117 -1
- package/dist/cli/hook-ingress.js +36 -49
- package/dist/cli/interactive-invocation.js +33 -0
- package/dist/cli/interactive.js +114 -0
- package/dist/cli/memory-fix-invocation.js +52 -0
- package/dist/cli/memory-fix.js +100 -0
- package/dist/cli/memory-flow-admission.js +7 -3
- package/dist/cli/memory-init.js +5 -4
- package/dist/cli/native-hook-ingress.js +5 -1
- package/dist/cli/native-hook-preparation.js +31 -4
- package/dist/cli/run-cli.js +273 -19
- package/dist/cli.js +17 -3
- package/dist/domain/delivery-flows.js +8 -0
- package/dist/domain/distill-flow.js +20 -0
- package/dist/domain/interactive-flow.js +1 -0
- package/dist/domain/memory-fix-flow.js +2 -0
- package/dist/domain/review-fix-flow.js +2 -0
- package/dist/domain/source-snapshot-flows.js +4 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +18 -3
- package/dist/harness-runtime/bin/dd-grok.mjs +15 -5
- package/dist/harness-runtime/bin/dd-zcode.mjs +15 -7
- package/dist/harness-runtime/lib/agy-error-boundary.mjs +18 -10
- package/dist/harness-runtime/lib/agy-native-inspection.mjs +12 -3
- package/dist/harness-runtime/lib/agy-native-inventory.mjs +19 -0
- package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +13 -6
- package/dist/harness-runtime/lib/agy-transcript-stream.mjs +54 -0
- package/dist/harness-runtime/lib/daemon-observation.d.mts +1 -0
- package/dist/harness-runtime/lib/daemon-observation.mjs +12 -4
- package/dist/harness-runtime/lib/daemon-operations.mjs +20 -5
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +371 -114
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +43 -3
- package/dist/harness-runtime/lib/dd-codex.mjs +36 -4
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +25 -11
- package/dist/harness-runtime/lib/dd-droid.mjs +124 -11
- package/dist/harness-runtime/lib/dd-grok-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +137 -25
- package/dist/harness-runtime/lib/dd-grok.mjs +4 -2
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +170 -25
- package/dist/harness-runtime/lib/dd-opencode.mjs +40 -19
- package/dist/harness-runtime/lib/dd-zcode-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +115 -22
- package/dist/harness-runtime/lib/dd-zcode.mjs +75 -36
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +1 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +10 -3
- package/dist/harness-runtime/lib/delivery-command.mjs +84 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +0 -19
- package/dist/harness-runtime/lib/droid-observation.mjs +9 -5
- package/dist/harness-runtime/lib/lifecycle-contract.d.mts +1 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +2 -1
- package/dist/harness-runtime/lib/native-hook-command.d.mts +3 -2
- package/dist/harness-runtime/lib/native-hook-command.mjs +63 -8
- package/dist/harness-runtime/lib/native-streams.mjs +3 -10
- package/dist/harness-runtime/lib/opencode-plugin.mjs +36 -9
- package/dist/harness-runtime/lib/operation-errors.mjs +13 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +6 -2
- package/dist/harness-runtime/lib/process-json.mjs +4 -5
- package/dist/harness-runtime/lib/tool-observations.mjs +20 -10
- package/dist/harness-runtime/lib/zcode-native-hook-ownership.mjs +109 -0
- package/dist/schemas/code-work-result.schema.json +2 -1
- package/dist/schemas/delivery-stage-result.schema.json +286 -0
- package/dist/schemas/distill-stage-result.schema.json +667 -0
- package/dist/schemas/distill-work-result.schema.json +428 -0
- package/dist/schemas/execution-bundle.schema.json +6 -2
- package/dist/schemas/interactive-stage-result.schema.json +47 -0
- package/dist/schemas/interactive-work-result.schema.json +63 -0
- package/dist/schemas/mb-fix-stage-result.schema.json +93 -0
- package/dist/schemas/project-flow-pack-manifest.schema.json +2 -2
- package/dist/schemas/review-fix-stage-result.schema.json +210 -0
- package/dist/schemas/run-control-receipt.schema.json +1 -0
- package/dist/services/assigned-lifecycle.js +382 -0
- package/dist/services/check-runner-evidence.js +77 -10
- package/dist/services/check-runner-proof.js +69 -1
- package/dist/services/cli-operation-classifier.js +6 -1
- package/dist/services/code-checks.js +56 -23
- package/dist/services/codex-hook-delivery.js +11 -2
- package/dist/services/controller-execution.js +4 -1
- package/dist/services/controller-fanout.js +9 -4
- package/dist/services/controller-memory-worker.js +6 -4
- package/dist/services/controller-result-authority.js +122 -0
- package/dist/services/dashboard.js +4 -2
- package/dist/services/delivery-access.js +123 -0
- package/dist/services/delivery-approval.js +68 -0
- package/dist/services/delivery-artifact.js +86 -0
- package/dist/services/delivery-checks.js +178 -0
- package/dist/services/delivery-execution.js +181 -0
- package/dist/services/delivery-intents.js +212 -0
- package/dist/services/delivery-operation.js +81 -0
- package/dist/services/delivery-policy.js +91 -0
- package/dist/services/delivery-procedures.js +43 -0
- package/dist/services/delivery-request.js +99 -0
- package/dist/services/delivery-result.js +144 -0
- package/dist/services/delivery-stage-commands.js +26 -0
- package/dist/services/delivery-stage-entry.js +56 -0
- package/dist/services/delivery-stage-handlers.js +135 -0
- package/dist/services/delivery-stage-results.js +94 -0
- package/dist/services/descriptor-coordinator-session.js +3 -1
- package/dist/services/descriptor-run-start.js +24 -4
- package/dist/services/descriptor-stage-coordinator.js +78 -10
- package/dist/services/descriptor-stage-settlement.js +59 -14
- package/dist/services/diagnostic-repair-intake.js +123 -0
- package/dist/services/diagnostic-repair-source-intake.js +102 -0
- package/dist/services/diagnostic-report.js +30 -0
- package/dist/services/diagnostic-run-bundle.js +48 -9
- package/dist/services/diagnostic-snapshot.js +2 -2
- package/dist/services/diagnostic-stage-handlers.js +3 -2
- package/dist/services/diagnostic-stage-results.js +26 -0
- package/dist/services/diagnostic-start.js +56 -6
- package/dist/services/diagnostic-work-collection.js +3 -3
- package/dist/services/distill-document-publication.js +280 -0
- package/dist/services/distill-promotion.js +197 -0
- package/dist/services/distill-publication-custody.js +162 -0
- package/dist/services/distill-stage-handlers.js +249 -0
- package/dist/services/distill-stage-results.js +105 -0
- package/dist/services/distill-user-inputs.js +4 -0
- package/dist/services/distill-work-batch.js +42 -0
- package/dist/services/distill-work-collection.js +74 -0
- package/dist/services/distill-work-registration.js +93 -0
- package/dist/services/distill-work-results.js +98 -0
- package/dist/services/eval-snapshots.js +17 -7
- package/dist/services/external-work-launch.js +8 -2
- package/dist/services/flow-catalog.js +24 -0
- package/dist/services/frozen-engine.js +19 -2
- package/dist/services/generic-execution.js +4 -3
- package/dist/services/harness-adapter.js +2 -14
- package/dist/services/hook-memory-routing.js +1 -1
- package/dist/services/hooks.js +31 -6
- package/dist/services/hybrid-contracts.js +6 -1
- package/dist/services/installed-flow-source.js +37 -2
- package/dist/services/interactive-attached-session.js +215 -0
- package/dist/services/interactive-baseline-custody.js +54 -0
- package/dist/services/interactive-before-versions.js +148 -0
- package/dist/services/interactive-bundle-data.js +85 -0
- package/dist/services/interactive-checks.js +179 -0
- package/dist/services/interactive-execution.js +33 -0
- package/dist/services/interactive-finish-integration.js +236 -0
- package/dist/services/interactive-git-baseline.js +230 -0
- package/dist/services/interactive-git-dispatch.js +278 -0
- package/dist/services/interactive-input-delivery.js +82 -0
- package/dist/services/interactive-mutation-scope.js +231 -0
- package/dist/services/interactive-request.js +287 -0
- package/dist/services/interactive-run-bundle.js +109 -0
- package/dist/services/interactive-stage-entry.js +41 -0
- package/dist/services/interactive-stage-handlers.js +289 -0
- package/dist/services/interactive-stage-observation.js +136 -0
- package/dist/services/interactive-stage-results.js +111 -0
- package/dist/services/interactive-user-inputs.js +142 -0
- package/dist/services/interactive-work-batch.js +43 -0
- package/dist/services/interactive-work-collection.js +119 -0
- package/dist/services/interactive-work-completion.js +180 -0
- package/dist/services/interactive-work-context.js +249 -0
- package/dist/services/interactive-work-history.js +112 -0
- package/dist/services/interactive-work-registration.js +124 -0
- package/dist/services/interactive-work-results.js +115 -0
- package/dist/services/interactive-work-start.js +155 -0
- package/dist/services/lifecycle-invocations.js +214 -24
- package/dist/services/managed-daemon-binding.js +52 -1
- package/dist/services/memory-compose-handler.js +2 -22
- package/dist/services/memory-finish-integration.js +58 -17
- package/dist/services/memory-fix-finish-integration.js +129 -0
- package/dist/services/memory-fix-git-dispatch.js +139 -0
- package/dist/services/memory-fix-intake-custody.js +11 -0
- package/dist/services/memory-fix-intake-settlement.js +127 -0
- package/dist/services/memory-fix-publication.js +196 -0
- package/dist/services/memory-fix-repair-admission.js +116 -0
- package/dist/services/memory-fix-request.js +141 -0
- package/dist/services/memory-fix-run-bundle.js +83 -0
- package/dist/services/memory-fix-scope.js +78 -0
- package/dist/services/memory-fix-sources.js +85 -0
- package/dist/services/memory-fix-stage-entry.js +24 -0
- package/dist/services/memory-fix-stage-handlers.js +282 -0
- package/dist/services/memory-fix-stage-results.js +106 -0
- package/dist/services/memory-fix-workspace.js +127 -0
- package/dist/services/memory-flow-user-inputs.js +88 -0
- package/dist/services/memory-init-contexts.js +55 -5
- package/dist/services/memory-init-controller-start.js +20 -4
- package/dist/services/memory-layer.js +7 -3
- package/dist/services/memory-native-review-copy.js +27 -3
- package/dist/services/memory-repair-document-delta.js +1 -0
- package/dist/services/project-flow-source.js +195 -0
- package/dist/services/project-git-integration.js +92 -0
- package/dist/services/repair-intents.js +2 -4
- package/dist/services/retained-native-hook.js +245 -0
- package/dist/services/review-fix-handoff.js +82 -0
- package/dist/services/review-fix-intake-custody.js +17 -0
- package/dist/services/review-fix-stage-entry.js +43 -0
- package/dist/services/review-fix-stage-handlers.js +176 -0
- package/dist/services/review-fix-stage-results.js +86 -0
- package/dist/services/run-control-worker.js +42 -1
- package/dist/services/run-control.js +40 -5
- package/dist/services/run-controller-adapter.js +37 -3
- package/dist/services/run-controller-process.js +12 -4
- package/dist/services/run-controller-recovery.js +46 -6
- package/dist/services/run-controller-state.js +3 -2
- package/dist/services/run-controller.js +209 -43
- package/dist/services/run-recovery.js +31 -2
- package/dist/services/run-timeline.js +59 -0
- package/dist/services/runs.js +18 -31
- package/dist/services/sessions.js +2 -1
- package/dist/services/stage-handlers.js +55 -3
- package/dist/services/stage-lifecycle.js +4 -3
- package/dist/services/stage-pause.js +26 -9
- package/dist/services/stage-report-renderer.js +2 -6
- package/dist/services/technical-notes.js +31 -0
- package/dist/services/verification-matrix.js +1 -1
- package/dist/services/version-status.js +1 -1
- package/dist/services/vnext-code-review.js +74 -5
- package/dist/services/vnext-code.js +9 -32
- package/dist/services/vnext-fanout.js +53 -8
- package/dist/services/vnext-merge.js +10 -5
- package/dist/services/vnext-plan-review.js +1 -1
- package/dist/services/vnext-plan.js +1 -1
- package/dist/services/vnext-protocolize.js +1 -1
- package/dist/services/vnext-specify.js +10 -5
- package/dist/services/work-registry.js +252 -66
- package/dist/services/worktrees.js +7 -4
- package/dist/storage/database.js +2 -1
- package/dist/verification-components.json +82 -0
- package/package.json +4 -3
- package/tools/verification-jobs.mjs +342 -0
|
@@ -5,10 +5,10 @@ import { canonicalNativeOperation } from './adapter-timeouts.mjs';
|
|
|
5
5
|
import { admitNativeRequestBuffer } from "./native-streams.mjs";
|
|
6
6
|
import { prepareDaemonRequest } from "./native-request-proof.mjs";
|
|
7
7
|
import { captureDispatchGuard, cancelPendingDispatch } from "./dispatch-fence.mjs";
|
|
8
|
-
import { errorRecord, isProvenShellCorrection } from "./operation-errors.mjs";
|
|
8
|
+
import { errorRecord, isNativeHookObservationLoss, isProvenShellCorrection, reportedError } from "./operation-errors.mjs";
|
|
9
9
|
import { ToolObservations, acpToolObservation } from "./tool-observations.mjs";
|
|
10
|
-
import { acceptNativeHookRequest,
|
|
11
|
-
import { parseLifecycleCommand, continuationCommand } from "./lifecycle-command.mjs";
|
|
10
|
+
import { acceptNativeHookRequest, readNativeHookFailures, writeNativeHookFailure } from "./native-hook-command.mjs";
|
|
11
|
+
import { parseLifecycleCommand, continuationCommand, commandOption } from "./lifecycle-command.mjs";
|
|
12
12
|
import { ObservationClock } from "./observation-clock.mjs";
|
|
13
13
|
import { createHash, randomUUID } from "node:crypto";
|
|
14
14
|
import { chmod, copyFile, cp, lstat, mkdir, open, readFile, readdir, rename, unlink, writeFile } from "node:fs/promises";
|
|
@@ -43,7 +43,8 @@ const errorPayload = errorRecord;
|
|
|
43
43
|
function shellQuote(value) { return `'${String(value).replace(/'/g, "'\\''")}'`; }
|
|
44
44
|
|
|
45
45
|
function executable(command, args = []) { const resolved = command ?? process.env.DD_GROK_BIN ?? "grok"; return /\.[cm]?js$/.test(resolved) ? { command: process.execPath, args: [resolved, ...args] } : { command: resolved, args }; }
|
|
46
|
-
export function isolatedEnv(config) { return { ...process.env, HOME: config.grokHome, GROK_HOME: config.grokHome, ...(config.authPath ? { GROK_AUTH_PATH: config.authPath } : {}), ...managedFlowEnv(config)
|
|
46
|
+
export function isolatedEnv(config) { return { ...process.env, HOME: config.grokHome, GROK_HOME: config.grokHome, ...(config.authPath ? { GROK_AUTH_PATH: config.authPath } : {}), ...managedFlowEnv(config),
|
|
47
|
+
DD_GROK_STATE_DIR: !config.noFlow && config.daemonId ? config.runtime_owner?.state_dir : undefined }; }
|
|
47
48
|
function within(candidate, root) { const resolved = path.resolve(candidate); const parent = path.resolve(root); return resolved === parent || resolved.startsWith(`${parent}${path.sep}`); }
|
|
48
49
|
|
|
49
50
|
async function writeIsolatedConfig(paths) {
|
|
@@ -185,7 +186,7 @@ export async function startDaemon(options) {
|
|
|
185
186
|
export async function stopDaemon(options) { const timeoutMs = options.timeoutMs ?? 30_000; return await confirmDaemonStopped(() => callDaemon(options.stateDir, "daemon.stop", { cancelTree: options.cancelTree === true }, timeoutMs), locations(options.stateDir).socket, timeoutMs, { readState: () => readState(options.stateDir) }); }
|
|
186
187
|
|
|
187
188
|
export class Runtime {
|
|
188
|
-
constructor(paths, state, bridge, initialized) { this.paths = paths; this.state = state; this.bridge = bridge; this.initialized = initialized; this.sessions = new Map((state.sessions ?? []).map((item) => [item.provider_session_id, item])); this.descendants = new Map((state.descendants ?? []).map(child => [child.provider_session_id, child])); for (const session of this.sessions.values()) if (session.parent_provider_session_id && !this.descendants.has(session.provider_session_id)) this.descendants.set(session.provider_session_id, { provider_session_id: session.provider_session_id, parent_provider_session_id: session.parent_provider_session_id, status: "unknown", source: "legacy.daemon_state", outcome_error: { code: "native_child_outcome_unavailable", message: "Retained Grok child has no durable outcome receipt" } }); this.toolUsage = new Map(); this.loadedSessionId = null; this.active = null; this.server = null; this.ready = false; this.persisting = Promise.resolve(); }
|
|
189
|
+
constructor(paths, state, bridge, initialized) { this.paths = paths; this.state = state; this.bridge = bridge; this.initialized = initialized; this.sessions = new Map((state.sessions ?? []).map((item) => [item.provider_session_id, item])); this.descendants = new Map((state.descendants ?? []).map(child => [child.provider_session_id, child])); for (const session of this.sessions.values()) if (session.parent_provider_session_id && !this.descendants.has(session.provider_session_id)) this.descendants.set(session.provider_session_id, { provider_session_id: session.provider_session_id, parent_provider_session_id: session.parent_provider_session_id, status: "unknown", source: "legacy.daemon_state", outcome_error: { code: "native_child_outcome_unavailable", message: "Retained Grok child has no durable outcome receipt" } }); this.toolUsage = new Map(); this.promptCreators = new Map(); this.childCreators = new Map(); this.childSpawnWaiters = new Map(); this.loadedSessionId = null; this.active = null; this.server = null; this.ready = false; this.persisting = Promise.resolve(); }
|
|
189
190
|
persist(patch = {}) { Object.assign(this.state, patch, { pid: process.pid, updated_at: new Date().toISOString(), sessions: [...this.sessions.values()], descendants: [...this.descendants.values()] }); const snapshot = structuredClone(this.state); this.persisting = this.persisting.catch(() => {}).then(() => writeState(this.paths.state, snapshot)); return this.persisting; }
|
|
190
191
|
async reduceTerminal(outcome) {
|
|
191
192
|
const session = this.sessions.get(outcome.session_id), turn = session?.native_prompt_operation;
|
|
@@ -234,7 +235,7 @@ export class Runtime {
|
|
|
234
235
|
}
|
|
235
236
|
options(params) { const sessionId = params.sessionId; const session = sessionId ? this.sessions.get(sessionId) : null; const root = session?.root_provider_session_id ?? sessionId; return { ...this.state.config, ...params, cwd: params.cwd ?? session?.cwd ?? this.state.config.cwd, initialized: this.initialized, daemonId: this.state.daemon_id, rootProviderSessionId: root, toolUsage: this.toolUsage, allowBackground: true, rootSettled: id => this.settledRoot(id), liveSession: params.liveSession ?? Boolean(sessionId && sessionId === this.loadedSessionId) }; }
|
|
236
237
|
track(result, before = null) { if (!result?.provider_session_id) return; const session = this.sessions.get(result.provider_session_id); const root = result.parent_provider_session_id ? (this.sessions.get(result.parent_provider_session_id)?.root_provider_session_id ?? result.parent_provider_session_id) : (session?.root_provider_session_id ?? result.provider_session_id); this.sessions.set(result.provider_session_id, { ...session, native_root_receipt: session?.native_root_receipt ?? null, provider_session_id: result.provider_session_id, adapter_session_id: result.adapter_session_id ?? result.provider_session_id, parent_provider_session_id: result.parent_provider_session_id ?? session?.parent_provider_session_id ?? null, root_provider_session_id: root, cwd: result.cwd ?? result.target?.newCwd ?? result.info?.cwd ?? session?.cwd ?? null }); for (const child of this.running(result)) this.recordDescendant({ ...child, stale_activity: Boolean(before && before.get(child.sessionId ?? child.subagentId ?? child.provider_session_id) !== this.descendants.get(child.sessionId ?? child.subagentId ?? child.provider_session_id)) }, root, "x.ai/subagent/list_running", "running"); for (const child of result.descendants ?? []) this.recordDescendant(child, root, child.source ?? "x.ai/subagent/event", child.status ?? "unknown"); }
|
|
237
|
-
recordDescendant(value, root, source, fallback) {
|
|
238
|
+
recordDescendant(value, root, source, fallback, nativeSpawn = false) {
|
|
238
239
|
const id = value?.provider_session_id ?? value?.session_id ?? value?.sessionId ?? value?.childSessionId ?? value?.subagentId ?? value?.child_session_id ?? value?.subagent_id;
|
|
239
240
|
const parent = value?.parent_provider_session_id ?? value?.parentSessionId ?? value?.parent_session_id ?? root;
|
|
240
241
|
if (typeof id !== "string" || !id || typeof parent !== "string" || !parent) return;
|
|
@@ -273,6 +274,14 @@ export class Runtime {
|
|
|
273
274
|
return;
|
|
274
275
|
}
|
|
275
276
|
if (!newAttempt && terminal(descendant?.status) && terminal(status) && descendant.status !== status) throw new DaemonError("native_child_outcome_conflict", "Grok child has contradictory terminal outcomes", false, { session_id: id, previous: descendant.status, observed: status });
|
|
277
|
+
if (nativeSpawn && spawned && typeof attempt === "string" && attempt && source === "x.ai/subagent/event") {
|
|
278
|
+
const promptId = value.parent_prompt_id;
|
|
279
|
+
const creator = typeof promptId === "string" && promptId ? this.promptCreators.get(JSON.stringify([parent, promptId])) : null;
|
|
280
|
+
const old = this.childCreators.get(id);
|
|
281
|
+
if (old && old.attempt === attempt && (old.parent !== parent || typeof promptId === "string" && promptId && old.promptId !== promptId)) throw new DaemonError("native_child_execution_ambiguous", "Grok child attempt has conflicting native creator", false, { session_id: id });
|
|
282
|
+
if (creator) this.childCreators.set(id, { parent, promptId, attempt, creator });
|
|
283
|
+
else if (newAttempt) this.childCreators.delete(id);
|
|
284
|
+
}
|
|
276
285
|
this.sessions.set(id, {
|
|
277
286
|
...prior,
|
|
278
287
|
provider_session_id: id,
|
|
@@ -296,6 +305,7 @@ export class Runtime {
|
|
|
296
305
|
...(newAttempt && descendant?.attempt_id ? { retired_attempt_ids: [...retired, descendant.attempt_id] } : retired.length ? { retired_attempt_ids: retired } : {}),
|
|
297
306
|
...(!newAttempt && !terminal(status) && descendant?.outcome_error ? { outcome_error: descendant.outcome_error } : {})
|
|
298
307
|
});
|
|
308
|
+
if (this.childCreators.has(id)) for (const wake of this.childSpawnWaiters.get(id) ?? []) wake();
|
|
299
309
|
if (spawned && typeof value.model === "string" && value.model.trim()) {
|
|
300
310
|
return { sessionId: id, parentSessionId: parent, attemptId: attempt ?? null, parentPromptId: value.parent_prompt_id ?? null, model: value.model.trim() };
|
|
301
311
|
}
|
|
@@ -307,7 +317,7 @@ export class Runtime {
|
|
|
307
317
|
const modelFacts = [];
|
|
308
318
|
if (/subagent/i.test(kind)) {
|
|
309
319
|
for (const value of [update?.subagent, update?.subagentInfo, update?.child, update?.result, update]) {
|
|
310
|
-
const fact = this.recordDescendant(value, root, "x.ai/subagent/event", /fail|error/i.test(kind) ? "failed" : /cancel/i.test(kind) ? "cancelled" : /complete|finish|done/i.test(kind) ? "completed" : "running");
|
|
320
|
+
const fact = this.recordDescendant(value, root, "x.ai/subagent/event", /fail|error/i.test(kind) ? "failed" : /cancel/i.test(kind) ? "cancelled" : /complete|finish|done/i.test(kind) ? "completed" : "running", value === update);
|
|
311
321
|
if (fact) modelFacts.push(fact);
|
|
312
322
|
}
|
|
313
323
|
}
|
|
@@ -396,7 +406,7 @@ export class Runtime {
|
|
|
396
406
|
assertDaemonDispatch(this.paths?.dir);
|
|
397
407
|
if (this.active) throw new DaemonError("operation_busy", `${this.active} is already running`, true);
|
|
398
408
|
this.active = name;
|
|
399
|
-
const current = this.nativeHookOperation = { operationId, sessionId, generation: (this.hookGeneration = (this.hookGeneration ?? 0) + 1), requests: new Map() };
|
|
409
|
+
const current = this.nativeHookOperation = { operationId, sessionId, generation: (this.hookGeneration = (this.hookGeneration ?? 0) + 1), requests: new Map(), guard: captureDispatchGuard(this) };
|
|
400
410
|
let rejectHook;
|
|
401
411
|
const hookFailed = new Promise((_, reject) => { rejectHook = reject; });
|
|
402
412
|
void hookFailed.catch(() => {});
|
|
@@ -406,15 +416,27 @@ export class Runtime {
|
|
|
406
416
|
// and local archive export must not erase an unrelated Session's receipt.
|
|
407
417
|
if (name === "session.prompt" && this.sessions.has(sessionId)) {
|
|
408
418
|
const session = this.sessions.get(sessionId);
|
|
419
|
+
if (typeof operationId === "string" && operationId) {
|
|
420
|
+
current.nativePromptId = randomUUID();
|
|
421
|
+
// Retain only for this daemon lifetime: a late native spawn or hook
|
|
422
|
+
// cannot be safely distinguished from an expired one by a timer.
|
|
423
|
+
// After restart the in-memory correlation is deliberately unavailable.
|
|
424
|
+
this.promptCreators.set(JSON.stringify([sessionId, current.nativePromptId]), current);
|
|
425
|
+
}
|
|
409
426
|
session.native_root_receipt = null;
|
|
410
427
|
this.bridge.dispatchGeneration = current.generation;
|
|
411
|
-
session.native_prompt_operation = { daemon_id: this.state.daemon_id, connection_id: this.bridge.connectionId, operation_id: operationId, generation: current.generation
|
|
428
|
+
session.native_prompt_operation = { daemon_id: this.state.daemon_id, connection_id: this.bridge.connectionId, operation_id: operationId, generation: current.generation,
|
|
429
|
+
...(current.nativePromptId ? { native_prompt_id: current.nativePromptId } : {}) };
|
|
412
430
|
}
|
|
413
431
|
try {
|
|
414
432
|
await this.persist({ active_tree: true, active_operation: name });
|
|
415
433
|
assertDaemonDispatch(this.paths?.dir);
|
|
416
434
|
const before = new Map(this.descendants);
|
|
417
435
|
const result = await Promise.race([task(), hookFailed]);
|
|
436
|
+
await this.observeHookFailures(current);
|
|
437
|
+
if (current.error) throw current.error;
|
|
438
|
+
const hookDiagnostics = (this.bridge.lifecycleDiagnostics ?? []).filter(item => item.operation_id === current.operationId && item.generation === current.generation);
|
|
439
|
+
if (hookDiagnostics.length) result.native_hook_diagnostics = [...new Map([...(result.native_hook_diagnostics ?? []), ...hookDiagnostics].map(item => [JSON.stringify(item), item])).values()];
|
|
418
440
|
await this.drainTerminal();
|
|
419
441
|
this.track(result, before);
|
|
420
442
|
const session = this.sessions.get(result.provider_session_id);
|
|
@@ -429,41 +451,131 @@ export class Runtime {
|
|
|
429
451
|
// root-turn proof, not proof that any descendants have stopped. A lost
|
|
430
452
|
// response, bridge error, or failure before dispatch earns no receipt.
|
|
431
453
|
try { await this.drainTerminal(); } catch (storage) { error.cleanup_error ??= errorRecord(storage); }
|
|
432
|
-
try { await
|
|
454
|
+
try { await this.persist({ active_tree: true, active_operation: null }); }
|
|
433
455
|
catch (storage) { error.cleanup_error = errorRecord(storage); }
|
|
434
456
|
throw error;
|
|
435
457
|
}
|
|
436
|
-
finally { clearInterval(timer); current.requests.clear(); if (this.nativeHookOperation === current) this.nativeHookOperation = null; this.active = null; }
|
|
458
|
+
finally { clearInterval(timer); if (!current.nativePromptId) current.requests.clear(); if (this.nativeHookOperation === current) this.nativeHookOperation = null; this.active = null; }
|
|
437
459
|
}
|
|
438
460
|
async observeHookFailures(current = this.nativeHookOperation) {
|
|
439
|
-
if (!current || this.nativeHookOperation !== current
|
|
440
|
-
current.reading
|
|
441
|
-
|
|
442
|
-
if (
|
|
443
|
-
}
|
|
461
|
+
if (!current || this.nativeHookOperation !== current) return;
|
|
462
|
+
while (current.reading) {
|
|
463
|
+
await current.reading;
|
|
464
|
+
if (this.nativeHookOperation !== current) return;
|
|
465
|
+
}
|
|
466
|
+
const reading = readNativeHookFailures(this.paths.dir, current.requests, current, error => this.acceptHookFailure(current, error));
|
|
467
|
+
current.reading = reading;
|
|
468
|
+
try { await reading; } finally { if (current.reading === reading) current.reading = null; }
|
|
469
|
+
}
|
|
470
|
+
acceptHookFailure(current, error) {
|
|
471
|
+
if (this.nativeHookOperation !== current) return;
|
|
472
|
+
const unconfirmed = error.details?.native_hook_binding?.native_creator === "unconfirmed";
|
|
473
|
+
if (!unconfirmed && isProvenShellCorrection(error)) return;
|
|
474
|
+
if (unconfirmed || isNativeHookObservationLoss(error)) {
|
|
475
|
+
const diagnostic = { ...errorRecord(error), session_id: current.sessionId, operation_id: current.operationId,
|
|
476
|
+
generation: current.generation, delivery_stage: "hook_observation", completeness: "incomplete" };
|
|
477
|
+
this.bridge.lifecycleDiagnostics ??= [];
|
|
478
|
+
this.bridge.lifecycleDiagnostics.push(diagnostic);
|
|
479
|
+
try { void Promise.resolve(this.bridge.journal?.write("native_hook_observation_failure", diagnostic)).catch(() => {}); } catch { /* diagnostic is optional */ }
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
current.reject(error);
|
|
444
483
|
}
|
|
445
484
|
async acceptHook(identity, sessionId, payload) {
|
|
446
485
|
const input = payload?.tool_input ?? payload?.toolInput;
|
|
447
486
|
const command = input?.command ?? input?.cmd;
|
|
448
487
|
if ((payload?.hook_event_name ?? payload?.hookEventName) !== "PreToolUse" || typeof command !== "string" || (!["lifecycle", "auxiliary"].includes(parseLifecycleCommand(command).participation) && !continuationCommand(command))) return identity;
|
|
449
|
-
const
|
|
450
|
-
const root =
|
|
488
|
+
const active = this.nativeHookOperation, tool = payload.tool_use_id ?? payload.toolUseId ?? payload.tool_call_id;
|
|
489
|
+
const root = identity.rootProviderSessionId;
|
|
490
|
+
const source = sessionId !== root ? this.childCreators.get(sessionId) : null;
|
|
491
|
+
// An older Grok may not surface creator metadata. Preserve its ordinary
|
|
492
|
+
// stateful handler, but never turn the current prompt into guessed proof.
|
|
493
|
+
if (sessionId !== root && !source) return identity;
|
|
494
|
+
const current = sessionId !== root ? source?.creator : active;
|
|
451
495
|
const pending = [...(this.bridge.pending?.values() ?? [])].some(request => request.method === "session/prompt" && request.sessionId === root);
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
496
|
+
const nativePrompt = payload.prompt_id ?? payload.promptId;
|
|
497
|
+
if (!current || current.sessionId !== root || !tool || sessionId === root && (!pending || active !== current)
|
|
498
|
+
|| sessionId === root && typeof nativePrompt === "string" && nativePrompt && nativePrompt !== current.nativePromptId
|
|
499
|
+
|| sessionId !== root && (source.parent !== identity.parentProviderSessionId || this.promptCreators.get(JSON.stringify([source.parent, source.promptId])) !== current))
|
|
500
|
+
throw new DaemonError("native_hook_unproven", "Native hook has no exact source-bound Session operation");
|
|
501
|
+
current.guard(); assertDaemonDispatch(this.paths.dir);
|
|
502
|
+
const nativePayload = { ...payload, _meta: { ...(payload._meta ?? {}), ddGrok: { ...identity } } };
|
|
503
|
+
const binding = await acceptNativeHookRequest(this.paths.dir, { daemon_id: this.state.daemon_id, operation_id: current.operationId, turn_generation: current.generation,
|
|
504
|
+
provider_session_id: sessionId, root_provider_session_id: root, parent_provider_session_id: identity.parentProviderSessionId ?? null,
|
|
505
|
+
tool_call_id: tool, event: "PreToolUse", command_sha256: createHash("sha256").update(command).digest("hex"), native_cwd: payload.cwd ?? null,
|
|
506
|
+
native_creator: source || typeof nativePrompt === "string" && nativePrompt && nativePrompt === current.nativePromptId ? "confirmed" : "unconfirmed",
|
|
507
|
+
...(source ? { source_harness: "grok-acp", native_command: command, native_payload: nativePayload } : {}) }, current);
|
|
508
|
+
current.guard();
|
|
509
|
+
if (sessionId === root && this.nativeHookOperation !== current || sessionId !== root && this.childCreators.get(sessionId) !== source)
|
|
510
|
+
throw new DaemonError("native_hook_unproven", "Native hook operation changed during acceptance");
|
|
455
511
|
current.requests.set(binding.request_id, { current, binding });
|
|
456
|
-
|
|
512
|
+
const parsed = parseLifecycleCommand(command);
|
|
513
|
+
const fastAck = Boolean(source && parsed.kind === "standalone" && parsed.invocation.operation === "work_start" && commandOption(parsed.invocation, "invocation-id"));
|
|
514
|
+
return { ...identity, nativeHookBinding: binding, ...(fastAck ? { fastAck: true } : {}) };
|
|
515
|
+
}
|
|
516
|
+
async waitForChildCreator(sessionId, deadlineAt) {
|
|
517
|
+
const remaining = deadlineAt - Date.now();
|
|
518
|
+
if (this.childCreators.has(sessionId) || !Number.isSafeInteger(deadlineAt) || remaining <= 0 || remaining > 30_000) return;
|
|
519
|
+
// Leave most of the existing host budget to the legacy handler if this
|
|
520
|
+
// provider omits the optional spawn correlation; do not poll the provider.
|
|
521
|
+
const waitMs = Math.floor(remaining / 3);
|
|
522
|
+
if (!waitMs) return;
|
|
523
|
+
await new Promise(resolve => {
|
|
524
|
+
const waiters = this.childSpawnWaiters.get(sessionId) ?? new Set();
|
|
525
|
+
const wake = () => { clearTimeout(timer); waiters.delete(wake); if (!waiters.size) this.childSpawnWaiters.delete(sessionId); resolve(); };
|
|
526
|
+
const timer = setTimeout(wake, waitMs);
|
|
527
|
+
waiters.add(wake); this.childSpawnWaiters.set(sessionId, waiters);
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
hookOwner(binding) {
|
|
531
|
+
if (!binding?.request_id) return null;
|
|
532
|
+
const owners = new Set([this.nativeHookOperation, ...this.promptCreators.values()]);
|
|
533
|
+
for (const owner of owners) {
|
|
534
|
+
if (!owner || owner.operationId !== binding.operation_id || owner.generation !== binding.turn_generation) continue;
|
|
535
|
+
const accepted = owner.requests.get(binding.request_id);
|
|
536
|
+
if (accepted?.current === owner && JSON.stringify(accepted.binding) === JSON.stringify(binding)) return owner;
|
|
537
|
+
}
|
|
538
|
+
return null;
|
|
457
539
|
}
|
|
458
540
|
async dispatch(operation, params, operationId = null) {
|
|
459
541
|
const assertDispatch = captureDispatchGuard(this);
|
|
460
542
|
if (["session.create", "session.prompt", "session.fork", "session.archive"].includes(operation)) assertDaemonDispatch(this.paths.dir);
|
|
461
543
|
if (operation === "daemon.ready") return { ready: this.ready, daemon_id: this.state.daemon_id, pid: process.pid, socket: this.paths.socket, versions: this.state.versions, config_isolation: this.state.config_isolation, shutdown_state: this.state.shutdown_state, recovery_status: this.state.recovery_status, auth_status: this.state.auth_status, active_tree: this.state.active_tree, active_operation: this.active, sessions: [...this.sessions.values()], config: this.state.config };
|
|
462
544
|
if (operation === "daemon.status") { let observationError; if (!this.active && !this.state.shutdown?.phases?.tree) try { await this.refreshTree(); } catch (error) { observationError = errorPayload(error); } return { ...await this.dispatch("daemon.ready", {}), observation_error: observationError ?? this.state.observation_error ?? null }; }
|
|
463
|
-
if (operation === "hook.resolve") { const sessionId = String(params.sessionId ?? ""); if (!sessionId) throw new DaemonError("hook_identity_missing", "hook has no sessionId"); let session = this.sessions.get(sessionId); if (!session) { const roots = [...this.sessions.values()].filter((item) => !item.parent_provider_session_id); if (roots.length !== 1) throw new DaemonError("hook_identity_unknown", "unknown hook Session has no unique root"); const observed = await inspectSessionWithBridge(this.bridge, this.options({ sessionId, liveSession: true })); this.track(observed); session = this.sessions.get(sessionId); if (!session || session.root_provider_session_id !== roots[0].provider_session_id) throw new DaemonError("hook_identity_unknown", "hook Session is not a member of the retained native tree"); await this.persist(); } return await this.acceptHook({ daemonId: this.state.daemon_id, rootProviderSessionId: session.root_provider_session_id, parentProviderSessionId: session.parent_provider_session_id ?? null, observedProfile: this.bridge.modelProfiles?.get(sessionId) ?? {} }, sessionId, params.payload); }
|
|
464
|
-
if (operation === "hook.
|
|
545
|
+
if (operation === "hook.resolve") { const sessionId = String(params.sessionId ?? ""); if (!sessionId) throw new DaemonError("hook_identity_missing", "hook has no sessionId"); let session = this.sessions.get(sessionId); if ((!session || session.parent_provider_session_id) && !this.childCreators.has(sessionId) && this.promptCreators.size) { await this.waitForChildCreator(sessionId, params.hookDeadlineAt); session = this.sessions.get(sessionId); } if (!session) { const roots = [...this.sessions.values()].filter((item) => !item.parent_provider_session_id); if (roots.length !== 1) throw new DaemonError("hook_identity_unknown", "unknown hook Session has no unique root"); const observed = await inspectSessionWithBridge(this.bridge, this.options({ sessionId, liveSession: true })); this.track(observed); session = this.sessions.get(sessionId); if (!session || session.root_provider_session_id !== roots[0].provider_session_id) throw new DaemonError("hook_identity_unknown", "hook Session is not a member of the retained native tree"); await this.persist(); } return await this.acceptHook({ daemonId: this.state.daemon_id, rootProviderSessionId: session.root_provider_session_id, parentProviderSessionId: session.parent_provider_session_id ?? null, observedProfile: this.bridge.modelProfiles?.get(sessionId) ?? {} }, sessionId, params.payload); }
|
|
546
|
+
if (operation === "hook.verify") {
|
|
547
|
+
if (params.daemonId !== this.state.daemon_id) throw new DaemonError("native_hook_unproven", "Native hook daemon identity differs");
|
|
548
|
+
const owner = this.hookOwner(params.binding);
|
|
549
|
+
if (!owner) throw new DaemonError("native_hook_unproven", "Native hook has no matching retained operation");
|
|
550
|
+
owner.guard(); assertDaemonDispatch(this.paths.dir);
|
|
551
|
+
return { verified: true, nativeHookBinding: params.binding };
|
|
552
|
+
}
|
|
553
|
+
if (operation === "hook.failed") {
|
|
554
|
+
if (params.binding !== undefined || params.failure !== undefined) {
|
|
555
|
+
const binding = params.binding, failure = params.failure;
|
|
556
|
+
const current = this.hookOwner(binding);
|
|
557
|
+
while (current?.reading) await current.reading;
|
|
558
|
+
const accepted = current?.requests.get(binding?.request_id);
|
|
559
|
+
if (!current || this.hookOwner(binding) !== current || accepted?.current !== current || JSON.stringify(accepted.binding) !== JSON.stringify(binding)
|
|
560
|
+
|| typeof failure?.code !== "string" || typeof failure?.message !== "string")
|
|
561
|
+
throw new DaemonError("native_hook_unproven", "Hook failure has no exact current owned delivery");
|
|
562
|
+
current.guard(); assertDaemonDispatch(this.paths.dir);
|
|
563
|
+
if (this.nativeHookOperation !== current) {
|
|
564
|
+
// The creator has returned. Retain the bound failure under its
|
|
565
|
+
// original operation, without rejecting the newer productive turn.
|
|
566
|
+
await writeNativeHookFailure(this.paths.dir, binding, reportedError(failure, "Native hook failed"));
|
|
567
|
+
return { observed: true, retained: true };
|
|
568
|
+
}
|
|
569
|
+
current.requests.delete(binding.request_id);
|
|
570
|
+
const error = reportedError(failure, "Native hook failed");
|
|
571
|
+
error.details = { ...error.details, native_hook_binding: binding };
|
|
572
|
+
this.acceptHookFailure(current, error);
|
|
573
|
+
return { observed: true };
|
|
574
|
+
}
|
|
575
|
+
await this.observeHookFailures(); return { observed: true };
|
|
576
|
+
}
|
|
465
577
|
if (operation === "session.create") return await this.productive(operation, async () => { const result = await createSessionWithBridge(this.bridge, { ...this.options(params), onSessionCreated: async (session) => { this.track(session); this.loadedSessionId = session.provider_session_id; await this.persist(); } }, this.initialized); this.loadedSessionId = result.provider_session_id; return result; }, null, operationId);
|
|
466
|
-
if (operation === "session.prompt") { await this.requireSettled(); return await this.productive(operation, async () => { const result = await promptSessionWithBridge(this.bridge, { ...this.options(params), assertDispatch, operationId }); this.loadedSessionId = params.sessionId; return result; }, params.sessionId, operationId); }
|
|
578
|
+
if (operation === "session.prompt") { await this.requireSettled(); return await this.productive(operation, async () => { const result = await promptSessionWithBridge(this.bridge, { ...this.options(params), assertDispatch, operationId, nativePromptId: this.nativeHookOperation?.nativePromptId }); this.loadedSessionId = params.sessionId; return result; }, params.sessionId, operationId); }
|
|
467
579
|
if (operation === "session.fork") { await this.requireSettled(); return await this.productive(operation, async () => { const result = await forkSessionWithBridge(this.bridge, this.options(params)); this.loadedSessionId = params.sessionId; return result; }); }
|
|
468
580
|
if (operation === "session.inspect" || operation === "session.resume") { const before = new Map(this.descendants); const result = await inspectSessionWithBridge(this.bridge, this.options({ ...params, liveSession: true })); this.track(result, before); await this.persist(); const settled = !this.active && !this.treeRunning(result); const descendants = this.topology(result.provider_session_id); const terminal = this.sessions.get(result.provider_session_id)?.native_root_receipt; return nativeExecutionReceipt({ ...result, settled, descendants, ...settledNativeTree(result.provider_session_id, descendants, settled, !this.active && !this.rootRunning(result)), ...(terminal?.kind === "failed" && terminal.request_id !== undefined ? { terminal_turn_id: String(terminal.request_id) } : {}) }, { daemonId: this.state.daemon_id, generation: this.controlGeneration ?? 0 }); }
|
|
469
581
|
if (operation === "session.cancel") { if (!this.sessions.has(params.sessionId)) throw new DaemonError("session_identity_mismatch", "cancellation Session is not owned by this daemon"); cancelPendingDispatch(this); const result = await cancelSessionWithBridge(this.bridge, this.options({ ...params, liveSession: true })); await this.persist({ active_tree: Boolean(this.active) || !result.settled }); return result; }
|
|
@@ -153,12 +153,14 @@ export async function promptSessionWithBridge(bridge, options) {
|
|
|
153
153
|
const local = { ...options, rootProviderSessionId: options.rootProviderSessionId ?? sessionId }; const initial_usage_error = await forwardUsageBestEffort(local, sessionId, await sessionUsage(bridge, sessionId), sessionToolCalls(local, bridge, sessionId));
|
|
154
154
|
options.assertDispatch?.();
|
|
155
155
|
bridge.dispatchOperationId = options.operationId;
|
|
156
|
-
const
|
|
156
|
+
const diagnosticsCursor = bridge.lifecycleDiagnostics?.length ?? 0;
|
|
157
|
+
const cursor = bridge.toolCursor(); const textCursor = bridge.assistantTextCursor(sessionId); const turn = await bridge.request("session/prompt", { sessionId, prompt: [{ type: "text", text: prompt }], ...(options.nativePromptId ? { _meta: { promptId: options.nativePromptId } } : {}) }, null, { idleSessionId: sessionId, idleTimeoutMs: options.livenessTimeoutMs ?? options.timeoutMs ?? 600_000 });
|
|
157
158
|
try {
|
|
158
159
|
await bridge.flush();
|
|
159
160
|
const evidence = await inspect(bridge, sessionId, local); evidence.tool_calls = recordToolCalls(local, bridge, sessionId, cursor); const usage_ingest_error = await forwardUsageBestEffort(local, sessionId, evidence.usage, evidence.tool_calls); if (initial_usage_error) evidence.initial_usage_error = initial_usage_error; if (usage_ingest_error) evidence.usage_ingest_error = usage_ingest_error;
|
|
160
161
|
if (!options.allowBackground && (evidence.subagents?.subagents ?? []).length) { await cancelTree(bridge, sessionId); fail("background Grok Build subagents cannot outlive the one-shot dd-grok connection"); }
|
|
161
|
-
|
|
162
|
+
const diagnostics = (bridge.lifecycleDiagnostics ?? []).slice(diagnosticsCursor).filter(item => item.session_id === sessionId);
|
|
163
|
+
return receipt(options, { harness: "grok-acp", provider_session_id: sessionId, adapter_session_id: sessionId, assistant_text: bridge.assistantTextSince(sessionId, textCursor), turn, profile: profileReceipt, evidence, ...(diagnostics.length ? { native_hook_diagnostics: diagnostics } : {}) });
|
|
162
164
|
} catch (error) { throw Object.assign(new Error("Grok native Turn completed but mandatory observation failed", { cause: error }), { code: "native_outcome_observation_failed", retryable: false,
|
|
163
165
|
details: { provider_session_id: sessionId, native_outcome: { status: "completed", turn }, observation_error: errorRecord(error), settled: false, journal_locator: options.journal } }); }
|
|
164
166
|
}
|