@deksden-com/dd-flow-cli 0.9.0-beta.144 → 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 +130 -0
- package/README.md +13 -8
- package/dist/build-info.json +7 -7
- package/dist/cli/command-inputs.js +13 -3
- package/dist/cli/command-metadata.js +51 -16
- package/dist/cli/diagnostic-invocation.js +46 -0
- package/dist/cli/diagnostic.js +120 -0
- package/dist/cli/help.js +67 -1
- package/dist/cli/hook-ingress.js +27 -3
- package/dist/cli/memory-flow-admission.js +9 -3
- package/dist/cli/memory-init-invocation.js +10 -3
- package/dist/cli/memory-init.js +77 -68
- package/dist/cli/memory-storage-admission.js +3 -2
- package/dist/cli/memory-storage-qualification.js +105 -0
- package/dist/cli/memory-storage-transition.js +123 -0
- package/dist/cli/memory-upgrade.js +189 -0
- package/dist/cli/native-hook-ingress.js +14 -4
- package/dist/cli/native-hook-preparation.js +55 -0
- package/dist/cli/response-file.js +2 -1
- package/dist/cli/run-cli.js +79 -40
- package/dist/cli/setup.js +2 -1
- package/dist/cli.js +18 -0
- package/dist/domain/diagnostic-aspects.js +103 -0
- package/dist/domain/diagnostic-flows.js +8 -0
- package/dist/domain/stage-handler-identities.js +8 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +3 -3
- package/dist/harness-runtime/bin/dd-grok.mjs +3 -3
- package/dist/harness-runtime/bin/dd-zcode.mjs +5 -5
- package/dist/harness-runtime/lib/agy-error-boundary.mjs +58 -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 +366 -52
- package/dist/harness-runtime/lib/dd-agy.mjs +17 -3
- 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 +71 -29
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +73 -16
- package/dist/harness-runtime/lib/dd-grok.mjs +6 -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/dd-zcode.mjs +44 -14
- package/dist/harness-runtime/lib/delegation-instructions.mjs +1 -1
- package/dist/harness-runtime/lib/driver-recovery.mjs +9 -8
- package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
- package/dist/harness-runtime/lib/entity-references.mjs +9 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +1 -1
- package/dist/harness-runtime/lib/managed-daemon.mjs +24 -9
- 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-hook-command.mjs +4 -4
- 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/opencode-plugin.mjs +3 -3
- package/dist/harness-runtime/lib/operation-errors.mjs +1 -1
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +5 -3
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +20 -7
- package/dist/harness-runtime/lib/runner-events.mjs +3 -1
- package/dist/harness-runtime/lib/setup-progress.mjs +8 -6
- 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/mb-upgrade-stage-result.schema.json +863 -0
- package/dist/schemas/memory-repair-work-result.schema.json +9 -2
- package/dist/schemas/project-flow-pack-manifest.schema.json +1 -1
- package/dist/schemas/release-impact.schema.json +3 -3
- package/dist/schemas/stage-start-response.schema.json +4 -0
- package/dist/services/accepted-intent.js +13 -1
- package/dist/services/canon.js +43 -7
- package/dist/services/check-environment.js +21 -0
- package/dist/services/check-runner-evidence.js +267 -15
- package/dist/services/cli-operation-classifier.js +1 -1
- package/dist/services/cli-upgrade-allowlist.js +1 -1
- package/dist/services/code-checks.js +34 -19
- package/dist/services/compatibility-preflight.js +5 -2
- package/dist/services/controller-execution.js +10 -4
- package/dist/services/controller-fanout.js +34 -10
- package/dist/services/controller-memory-task.js +45 -0
- package/dist/services/controller-memory-worker.js +49 -0
- package/dist/services/dashboard.js +2 -2
- package/dist/services/descriptor-run-settlement.js +5 -3
- package/dist/services/descriptor-run-start.js +46 -20
- package/dist/services/descriptor-stage-coordinator.js +66 -13
- package/dist/services/descriptor-stage-settlement.js +2 -1
- package/dist/services/descriptor-stage-start.js +40 -4
- 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/engines.js +64 -14
- package/dist/services/eval-snapshots.js +186 -119
- package/dist/services/execution-bundles.js +19 -7
- package/dist/services/external-work-launch.js +5 -3
- package/dist/services/flow-catalog.js +13 -4
- package/dist/services/frozen-engine.js +148 -11
- package/dist/services/hook-memory-routing.js +188 -0
- package/dist/services/hook-recovery-guard.js +14 -0
- package/dist/services/hook-session-observation.js +24 -0
- package/dist/services/hooks.js +27 -8
- package/dist/services/hybrid-contracts.js +4 -13
- package/dist/services/hybrid-digest.js +14 -0
- package/dist/services/installed-flow-source.js +47 -4
- package/dist/services/lifecycle-invocations.js +179 -38
- package/dist/services/memory-accepted-composition.js +5 -4
- package/dist/services/memory-completed-correction-wave.js +23 -15
- package/dist/services/memory-compose-handler.js +163 -21
- package/dist/services/memory-composition-acceptance.js +186 -10
- package/dist/services/memory-composition-graph.js +42 -11
- package/dist/services/memory-composition-registration.js +23 -8
- package/dist/services/memory-composition-work-custody.js +14 -9
- package/dist/services/memory-composition-work-results.js +15 -3
- package/dist/services/memory-corrected-review-batch.js +10 -3
- package/dist/services/memory-corrected-review-findings.js +4 -2
- package/dist/services/memory-corrected-review-inputs.js +10 -5
- package/dist/services/memory-corrected-review-receipt.js +17 -9
- package/dist/services/memory-corrected-review-registration.js +3 -2
- package/dist/services/memory-correction-check-receipt.js +20 -4
- package/dist/services/memory-custody-snapshot.js +36 -0
- package/dist/services/memory-finish-evidence.js +44 -22
- package/dist/services/memory-finish-handler.js +37 -23
- package/dist/services/memory-finish-integration.js +218 -48
- package/dist/services/memory-finish-readiness.js +66 -19
- package/dist/services/memory-git-integration.js +87 -18
- package/dist/services/memory-historical-corrected-review.js +21 -15
- package/dist/services/memory-historical-original-review.js +22 -9
- package/dist/services/memory-historical-repair-registration.js +14 -8
- package/dist/services/memory-historical-second-review.js +30 -11
- package/dist/services/memory-init-contexts.js +44 -2
- package/dist/services/memory-init-controller-start.js +27 -8
- package/dist/services/memory-init-git-policy.js +50 -21
- package/dist/services/memory-init-lint-capability.js +24 -6
- package/dist/services/memory-init-native-capacity.js +17 -4
- package/dist/services/memory-init-preflight.js +21 -17
- package/dist/services/memory-init-request-candidate.js +73 -17
- package/dist/services/memory-init-request-input.js +23 -7
- package/dist/services/memory-init-run-bundle.js +49 -12
- package/dist/services/memory-init-storage.js +17 -7
- package/dist/services/memory-init-worktrunk-capability.js +3 -7
- package/dist/services/memory-layer.js +71 -6
- package/dist/services/memory-lint-config.js +111 -2
- package/dist/services/memory-native-review-copy.js +5 -67
- package/dist/services/memory-normal-flow-entry.js +12 -0
- package/dist/services/memory-ordinary-defaults.js +22 -5
- package/dist/services/memory-preflight-handler.js +15 -0
- package/dist/services/memory-repair-current-view.js +1 -1
- package/dist/services/memory-repair-document-delta.js +70 -33
- package/dist/services/memory-repair-history.js +12 -7
- package/dist/services/memory-repair-physical-binding.js +1 -119
- package/dist/services/memory-repair-publication-plan.js +5 -3
- package/dist/services/memory-repair-publication-source.js +1 -1
- package/dist/services/memory-repair-publication.js +3 -2
- package/dist/services/memory-repair-registration.js +2 -1
- package/dist/services/memory-repair-work-baseline.js +4 -2
- package/dist/services/memory-repair-work-completion.js +32 -12
- package/dist/services/memory-repair-work-results.js +21 -2
- package/dist/services/memory-repair-workspace.js +11 -2
- package/dist/services/memory-repair-writer-copy.js +2 -1
- package/dist/services/memory-result-binding.js +78 -12
- package/dist/services/memory-retained-review-authority.js +17 -8
- package/dist/services/memory-retained-second-baseline.js +1 -1
- package/dist/services/memory-retained-second-repairs.js +12 -6
- package/dist/services/memory-review-correction-plan.js +47 -12
- package/dist/services/memory-review-handler.js +17 -17
- package/dist/services/memory-review-inputs.js +55 -9
- package/dist/services/memory-review-limits.js +10 -0
- package/dist/services/memory-review-receipt.js +2 -2
- package/dist/services/memory-review-registration.js +13 -4
- package/dist/services/memory-review-work-custody.js +3 -1
- package/dist/services/memory-review-work-input.js +26 -7
- package/dist/services/memory-review-work-results.js +26 -4
- package/dist/services/memory-second-corrected-review-inputs.js +5 -3
- package/dist/services/memory-second-correction-plan.js +2 -1
- package/dist/services/memory-second-repair-launch.js +4 -4
- package/dist/services/memory-second-repair-source.js +1 -1
- package/dist/services/memory-stage-dispatch.js +38 -1
- package/dist/services/memory-stage-results.js +113 -22
- package/dist/services/memory-tool-probe.js +25 -0
- package/dist/services/memory-upgrade-analysis-handler.js +243 -0
- package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
- package/dist/services/memory-upgrade-evidence.js +100 -0
- package/dist/services/memory-upgrade-inventory.js +442 -0
- package/dist/services/memory-upgrade-publication-custody.js +73 -0
- package/dist/services/memory-upgrade-publication.js +413 -0
- package/dist/services/memory-upgrade-selection.js +430 -0
- package/dist/services/memory-upgrade-start.js +60 -0
- package/dist/services/memory-upgrade-storage-readiness.js +340 -0
- package/dist/services/memory-verification-acceptance.js +38 -17
- package/dist/services/memory-verification-checks.js +92 -27
- package/dist/services/memory-verification-receipt.js +6 -5
- package/dist/services/memory-verify-handler.js +15 -13
- package/dist/services/memory-work-result-json.js +12 -0
- package/dist/services/memory-work-result-preparation.js +5 -4
- package/dist/services/merge-server.js +4 -2
- package/dist/services/migrations.js +127 -98
- package/dist/services/native-daemon-history.js +2 -1
- package/dist/services/physical-startup.js +45 -0
- package/dist/services/project-registry.js +97 -0
- package/dist/services/project-summary.js +1 -1
- package/dist/services/projects.js +3 -93
- package/dist/services/prompt-delivery.js +71 -0
- package/dist/services/provider-capacity.js +112 -46
- package/dist/services/recovery-snapshot-database.js +32 -4
- package/dist/services/release-cache.js +8 -0
- package/dist/services/repair-intents.js +1 -12
- package/dist/services/repair-receipt.js +13 -0
- package/dist/services/run-control-worker.js +35 -18
- package/dist/services/run-control.js +2 -1
- package/dist/services/run-controller-adapter.js +2 -1
- package/dist/services/run-controller-process.js +4 -7
- package/dist/services/run-controller-recovery.js +8 -2
- package/dist/services/run-controller.js +215 -94
- package/dist/services/run-fork.js +1 -1
- package/dist/services/run-recovery-guard.js +30 -0
- package/dist/services/run-recovery.js +11 -44
- package/dist/services/run-start-custody.js +17 -5
- 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-scope-worker.js +46 -19
- package/dist/services/runtime-service.js +2 -1
- package/dist/services/sessions.js +3 -24
- package/dist/services/setup.js +63 -13
- package/dist/services/stage-handlers.js +22 -7
- package/dist/services/stage-pause.js +111 -44
- package/dist/services/version-status.js +1 -1
- package/dist/services/vnext-code.js +2 -2
- package/dist/services/vnext-fanout.js +51 -9
- package/dist/services/vnext-plan-review.js +3 -5
- package/dist/services/work-registry.js +117 -39
- package/dist/services/workspace-bootstrap.js +2 -1
- package/dist/services/workspace-files.js +1 -1
- package/dist/shared/entity-references.js +1 -8
- package/dist/storage/data-preservation.js +65 -0
- package/dist/storage/database.js +53 -19
- package/dist/storage/import-guard.js +84 -27
- package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
- package/dist/storage/paired-writer-migration.js +424 -0
- package/dist/storage/writer-contract.js +11 -4
- package/dist/storage/writer-migration.js +313 -52
- package/dist/storage/writer-restore.js +217 -0
- package/package.json +13 -6
|
@@ -3,11 +3,14 @@ import { SETUP_STARTUP_MS, startupObserver, observationDetails } from './setup-p
|
|
|
3
3
|
import { callDaemonObserved } from './daemon-observation.mjs';
|
|
4
4
|
import { operationProgressReporter } from './operation-progress.mjs';
|
|
5
5
|
import { prepareDaemonRequest } from "./native-request-proof.mjs";
|
|
6
|
+
import { inspectAgyNative, stopAgyNativeTree, ownedAgyEndpoint, readAgyNativeQualification, AGY_NATIVE_INSPECTION_CONTRACT } from './agy-native-inspection.mjs';
|
|
7
|
+
import { processSnapshot } from './process-snapshot.mjs';
|
|
8
|
+
import { streamRequestKey } from './provider-overload-policy.mjs';
|
|
6
9
|
import { captureDispatchGuard, cancelPendingDispatch } from "./dispatch-fence.mjs";
|
|
7
10
|
import { errorRecord, isProvenShellCorrection } from "./operation-errors.mjs";
|
|
8
11
|
import { ToolObservations, agyToolObservation, agyTranscriptObservations } from "./tool-observations.mjs";
|
|
9
12
|
import { constants } from "node:fs";
|
|
10
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
11
14
|
import { createHash, randomUUID } from "node:crypto";
|
|
12
15
|
import { appendFile, chmod, lstat, mkdir, open, readFile, realpath, rename, unlink, writeFile } from "node:fs/promises";
|
|
13
16
|
import { spawn } from "node:child_process";
|
|
@@ -19,14 +22,48 @@ import { agyWorkspaceHooks } from "./agy-hooks.mjs";
|
|
|
19
22
|
import { durableDaemonDispatch, completeDaemonShutdown, assertDaemonDispatch, assertDaemonRestartable, trackDaemonConnections, retireDaemonEndpoint, retireFailedDaemon } from "./daemon-operations.mjs";
|
|
20
23
|
import path from "node:path";
|
|
21
24
|
import { observeModel } from "./model-observations.mjs";
|
|
22
|
-
import { AGY_VERSION_TIMEOUT_MS, AgyError, agyTerminalFailure, assertProfile, executable, observedProfile, runAgy, usageSnapshot } from "./dd-agy.mjs";
|
|
23
|
-
import {
|
|
25
|
+
import { AGY_VERSION_TIMEOUT_MS, AgyError, agyTerminalFailure, agyModelArgs, assertProfile, executable, observedProfile, runAgy, usageSnapshot } from "./dd-agy.mjs";
|
|
26
|
+
import { recoveredAgyErrorBoundary } from "./agy-error-boundary.mjs";
|
|
27
|
+
import { settledNativeTree, nativeExecutionReceipt } from "./native-children.mjs";
|
|
24
28
|
import { retainStartupFailure, prepareRuntimeOwner, cleanupFailedStart, cleanupFailedDaemonStart, confirmDaemonProcess, confirmDaemonStopped, finishDaemonProcess, scheduleDaemonProcessHeartbeat, registerDaemonProcess, stopProcessGroup } from "./managed-daemon.mjs";
|
|
25
29
|
import { readNativeLines, streamNativeDiagnostics, nativeObservationError, admitNativeRequestBuffer } from "./native-streams.mjs";
|
|
26
30
|
|
|
27
31
|
const REQUEST_SCHEMA = "dd-agy/daemon-request@1", RESPONSE_SCHEMA = "dd-agy/daemon-response@1", STATE_SCHEMA = "dd-agy/daemon-state@1";
|
|
28
32
|
class DaemonError extends AgyError {}
|
|
29
|
-
|
|
33
|
+
// Only unambiguously correlated native tool results can introduce children or
|
|
34
|
+
// classify services. This transcript format has no tool-call IDs: parallel
|
|
35
|
+
// calls therefore retain unknown resources, never FIFO-derived authority.
|
|
36
|
+
export function agyTranscriptFacts(text, source, sessionId) {
|
|
37
|
+
const children = [], resources = [], calls = [];
|
|
38
|
+
if (!source?.locator || !source?.sha256 || !sessionId) return { children, resources };
|
|
39
|
+
for (const line of text.trim().split('\n')) {
|
|
40
|
+
let event;
|
|
41
|
+
try { event = JSON.parse(line); } catch { return { children: [], resources: [] }; }
|
|
42
|
+
if (event.source !== 'MODEL') continue;
|
|
43
|
+
if (event.type === 'PLANNER_RESPONSE') { calls.length = 0; if (Array.isArray(event.tool_calls) && event.tool_calls.length === 1) calls.push(event.tool_calls[0]); continue; }
|
|
44
|
+
if (event.type !== 'GENERIC') continue;
|
|
45
|
+
const call = calls.shift(), content = typeof event.content === 'string' ? event.content : '';
|
|
46
|
+
const native_source = { ...source, step_index: event.step_index };
|
|
47
|
+
if (call?.name === 'invoke_subagent' && event.status === 'DONE') {
|
|
48
|
+
const match = content.match(/^Created At: [^\n]+\nCompleted At: [^\n]+\nCreated the following subagent:\n(\{[\s\S]*?\})\nThe subagent will send you a message/);
|
|
49
|
+
if (!match) continue;
|
|
50
|
+
try {
|
|
51
|
+
const child = JSON.parse(match[1]);
|
|
52
|
+
if (/^[\w-]+$/.test(child.conversationId) && typeof child.logAbsoluteUri === 'string') children.push({ provider_session_id: child.conversationId, parent_provider_session_id: sessionId, log_uri: child.logAbsoluteUri, workspace_uris: child.workspaceUris ?? [], status: 'unknown', native_source });
|
|
53
|
+
} catch { /* No partial/guessed native edge. */ }
|
|
54
|
+
}
|
|
55
|
+
if (event.status !== 'RUNNING') continue;
|
|
56
|
+
const task = content.match(/^Created At: [^\n]+\nTool is running as a background task with task id: ([\w-]+\/task-\d+)\n/);
|
|
57
|
+
if (!task || !task[1].startsWith(`${sessionId}/`)) continue;
|
|
58
|
+
let daemon = call?.name === 'run_command' ? call.args?.IsDaemon : null;
|
|
59
|
+
if (typeof daemon === 'string') { try { daemon = JSON.parse(daemon); } catch { daemon = null; } }
|
|
60
|
+
resources.push({ resource_id: task[1], provider_session_id: sessionId, classification: daemon === true ? 'support_service' : daemon === false ? 'required_work' : 'unknown', state: 'unknown', native_source,
|
|
61
|
+
...(typeof daemon === 'boolean' ? { native_classification: { tool: 'run_command', IsDaemon: daemon } } : {}) });
|
|
62
|
+
}
|
|
63
|
+
return { children, resources };
|
|
64
|
+
}
|
|
65
|
+
export const agyTranscriptResources = (text, source, sessionId) => agyTranscriptFacts(text, source, sessionId).resources;
|
|
66
|
+
export async function captureAgyTranscript(runtimeRoot, child, rootSessionId, ownedSessionIds = [rootSessionId], inspect = null) {
|
|
30
67
|
const session = { harness_id: "antigravity-cli", session_id: child.provider_session_id };
|
|
31
68
|
let handle;
|
|
32
69
|
try {
|
|
@@ -43,6 +80,7 @@ export async function captureAgyTranscript(runtimeRoot, child, rootSessionId, ow
|
|
|
43
80
|
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
44
81
|
const bytes = buffer.subarray(0, bytesRead), prefix = bytes.subarray(0, bytes.lastIndexOf(10) + 1);
|
|
45
82
|
const source = { locator: child.log_uri, byte_start: 0, byte_end: prefix.length, sha256: createHash("sha256").update(prefix).digest("hex") };
|
|
83
|
+
if (prefix.length === bytesRead && stat.size <= maximum) inspect?.(prefix.toString("utf8"), source);
|
|
46
84
|
const events = agyTranscriptObservations(prefix.toString("utf8"), child.provider_session_id, child.parent_provider_session_id ?? rootSessionId, source);
|
|
47
85
|
if (prefix.length !== bytesRead || stat.size > maximum) events.push({ session, reason: stat.size > maximum ? "agy_transcript_size_limit" : "agy_transcript_partial_tail" });
|
|
48
86
|
return events;
|
|
@@ -171,7 +209,7 @@ export async function callDaemon(stateDir, operation, params = {}, timeoutMs = 3
|
|
|
171
209
|
return await callDaemonObserved({ stateDir, socket: locations(stateDir).socket, request, responseSchema: RESPONSE_SCHEMA, harness: 'agy', timeoutMs });
|
|
172
210
|
}
|
|
173
211
|
|
|
174
|
-
function sameConfig(state, config) { return ["cwd","journal","bin","provider","model","reasoning","mode","projectRoot","ddFlowBin","ddFlowHome","resourceHome","noFlow","livenessTimeoutMs"].every(key => (state.config?.[key] ?? null) === (config[key] ?? null)); }
|
|
212
|
+
function sameConfig(state, config) { return ["cwd","journal","bin","provider","model","reasoning","mode","projectRoot","ddFlowBin","ddFlowHome","resourceHome","noFlow","livenessTimeoutMs","nativeQualificationPath"].every(key => (state.config?.[key] ?? null) === (config[key] ?? null)); }
|
|
175
213
|
// Called only after authorizeRetainedDaemonResume verifies the old daemon,
|
|
176
214
|
// provider processes and operation ledger. Native outcomes remain historical;
|
|
177
215
|
// their old liveness flags cannot describe the replacement process tree.
|
|
@@ -181,6 +219,8 @@ export function retainedAgyState(previous, sessionId) {
|
|
|
181
219
|
return {
|
|
182
220
|
...previous,
|
|
183
221
|
unclaimed_activity: false,
|
|
222
|
+
native_resources_entries: [],
|
|
223
|
+
native_topology_error: null,
|
|
184
224
|
retained_tree_settlement: { daemon_id: previous.daemon_id, session_id: sessionId, turn_generation: previous.turn_generation ?? 0, evidence: "verified_clean_daemon_shutdown" },
|
|
185
225
|
session_observations: (previous.session_observations ?? []).map(([id, observation]) => [id, id === sessionId ? { ...observation, retained_stop: observation.stop ?? null, stop: null } : observation]),
|
|
186
226
|
descendants: (previous.descendants ?? []).map(child => child.tree_settled === true ? child : { ...child, tree_settled: true, settlement_evidence: "prior_clean_daemon_shutdown", settlement_daemon_id: previous.daemon_id })
|
|
@@ -190,6 +230,7 @@ export async function startDaemon(options) {
|
|
|
190
230
|
const paths = locations(options.stateDir); await mkdir(paths.dir, { recursive: true, mode: 0o700 }); await chmod(paths.dir, 0o700);
|
|
191
231
|
const previous = await readState(paths.dir), noFlow = options.noFlow === true, cwd = await realpath(absolute(options.cwd, "--cwd")), config = { cwd, journal: options.journal ? absolute(options.journal, "--journal") : paths.journal, bin: options.bin ?? process.env.DD_AGY_BIN ?? "agy", provider: String(options.provider ?? "google"), model: String(options.model ?? "gemini-3.1-pro-high"), reasoning: String(options.reasoning ?? "high"), mode: String(options.mode ?? "accept-edits"), projectRoot: noFlow ? cwd : await realpath(absolute(options.projectRoot, "--project-root")), ddFlowBin: noFlow ? null : absolute(options.ddFlowBin, "--dd-flow-bin"), ddFlowHome: noFlow ? null : absolute(options.ddFlowHome, "--dd-flow-home"), resourceHome: noFlow ? null : (process.env.DD_FLOW_RESOURCE_HOME ? absolute(process.env.DD_FLOW_RESOURCE_HOME, "DD_FLOW_RESOURCE_HOME") : null), env: noFlow ? {} : { DD_FLOW_HOME: absolute(options.ddFlowHome, "--dd-flow-home") }, entryPath: absolute(options.entryPath, "dd-agy entry"), daemonId: randomUUID(), resumeSessionId: options.sessionId ?? null, geminiDir: paths.gemini, appDataDir: "runtime", temporary: paths.temporary, noFlow };
|
|
192
232
|
config.livenessTimeoutMs = options.livenessTimeoutMs ?? 1_800_000;
|
|
233
|
+
config.nativeQualificationPath = process.env.DD_AGY_NATIVE_QUALIFICATION ? absolute(process.env.DD_AGY_NATIVE_QUALIFICATION, 'DD_AGY_NATIVE_QUALIFICATION') : null;
|
|
193
234
|
validateObservationDuration(config.livenessTimeoutMs);
|
|
194
235
|
await prepareRuntimeOwner(config, paths.dir);
|
|
195
236
|
assertDaemonRestartable(previous);
|
|
@@ -201,7 +242,7 @@ export async function startDaemon(options) {
|
|
|
201
242
|
const version = (await runAgy(config.bin, ["--version"], { cwd: config.cwd, env: sanitizedEnv(config), timeoutMs: AGY_VERSION_TIMEOUT_MS })).stdout.trim();
|
|
202
243
|
const resourceProcess = await registerDaemonProcess(config, { kind: "agy-daemon", owner: `agy:${path.basename(paths.dir)}`, operation: `agy-daemon:${path.basename(paths.dir)}`, stdout: paths.log, stderr: paths.log });
|
|
203
244
|
const retained = retainedAgyState(previous, config.resumeSessionId);
|
|
204
|
-
await writeJson(paths.state, { schema_id: STATE_SCHEMA, daemon_id: config.daemonId, shutdown_state: "starting", active_tree: false, unclaimed_activity: retained.unclaimed_activity ?? false, retained_tree_settlement: retained.retained_tree_settlement ?? null, versions: { agy: version }, config, sessions: [], session_observations: retained.session_observations ?? [], descendants: retained.descendants ?? [], ...(Array.isArray(retained.tool_observations) ? { tool_observations: retained.tool_observations } : {}), tool_observation_gaps: retained.tool_observation_gaps ?? [], tool_calls: retained.tool_calls ?? [], completed_tool_steps: retained.completed_tool_steps ?? [], tool_identity_complete: retained.tool_identity_complete !== false, turn_generation: retained.turn_generation ?? 0, last_result: retained.last_result ?? null, resource_process: resourceProcess });
|
|
245
|
+
await writeJson(paths.state, { schema_id: STATE_SCHEMA, daemon_id: config.daemonId, shutdown_state: "starting", active_tree: false, unclaimed_activity: retained.unclaimed_activity ?? false, retained_tree_settlement: retained.retained_tree_settlement ?? null, versions: { agy: version }, config, sessions: [], session_observations: retained.session_observations ?? [], descendants: retained.descendants ?? [], native_resources_entries: retained.native_resources_entries ?? [], native_topology_error: retained.native_topology_error ?? null, ...(Array.isArray(retained.tool_observations) ? { tool_observations: retained.tool_observations } : {}), tool_observation_gaps: retained.tool_observation_gaps ?? [], tool_calls: retained.tool_calls ?? [], completed_tool_steps: retained.completed_tool_steps ?? [], tool_identity_complete: retained.tool_identity_complete !== false, turn_generation: retained.turn_generation ?? 0, last_result: retained.last_result ?? null, resource_process: resourceProcess });
|
|
205
246
|
const log = await open(paths.log, "a", 0o600), child = spawn(process.execPath, [config.entryPath, "daemon", "serve", "--state-dir", paths.dir], { cwd: config.cwd, env: sanitizedEnv(config), detached: true, stdio: ["ignore", log.fd, log.fd] }); child.unref(); await log.close();
|
|
206
247
|
try {
|
|
207
248
|
await confirmDaemonProcess(config, resourceProcess, child);
|
|
@@ -222,10 +263,17 @@ export async function startDaemon(options) {
|
|
|
222
263
|
export const stopDaemon = async 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) }); };
|
|
223
264
|
|
|
224
265
|
export class Runtime {
|
|
225
|
-
constructor(paths, state) { this.paths = paths; this.state = state; this.config = state.config; this.child = null; this.providerProcess = state.provider_process ?? null; this.providerFinalization = Promise.resolve(); this.buffer = ""; this.init = null; this.readyPromise = null; this.active = null; this.exited = false; this.toolObservations = new ToolObservations(); for (const reason of state.tool_observation_gaps ?? []) this.toolObservations.gaps.add(reason); for (const event of state.tool_observations ?? []) this.toolObservations.add(event); if (state.tool_calls?.length && !state.tool_observations) this.toolObservations.gaps.add("legacy_tools_not_attributed"); this.turnGeneration = state.turn_generation ?? 0; this.descendants = new Map((state.descendants ?? []).map(child => [child.provider_session_id, child])); this.lastResult = state.last_result ?? null; this.sessionObservations = new Map(state.session_observations ?? []); this.startupError = null; this.persisting = Promise.resolve(); this.draining = Promise.resolve(); this.lastActivityAt = state.last_activity_at ?? null; this.usageIngest = null; this.lastProviderEvent = null;
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
266
|
+
constructor(paths, state) { this.paths = paths; this.state = state; this.config = state.config; this.child = null; this.providerProcess = state.provider_process ?? null; this.providerFinalization = Promise.resolve(); this.buffer = ""; this.init = null; this.readyPromise = null; this.active = null; this.exited = false; this.toolObservations = new ToolObservations(); for (const reason of state.tool_observation_gaps ?? []) this.toolObservations.gaps.add(reason); for (const event of state.tool_observations ?? []) this.toolObservations.add(event); if (state.tool_calls?.length && !state.tool_observations) this.toolObservations.gaps.add("legacy_tools_not_attributed"); this.turnGeneration = state.turn_generation ?? 0; this.descendants = new Map((state.descendants ?? []).map(child => [child.provider_session_id, child])); this.lastResult = state.last_result ?? null; this.sessionObservations = new Map(state.session_observations ?? []); this.startupError = null; this.persisting = Promise.resolve(); this.draining = Promise.resolve(); this.lastActivityAt = state.last_activity_at ?? null; this.usageIngest = null; this.lastProviderEvent = null;
|
|
267
|
+
const saved = state.native_observation_episode;
|
|
268
|
+
if (saved?.clock) {
|
|
269
|
+
this.observationEpisode = { operationId: saved.operation_id, generation: saved.turn_generation, connectionId: saved.connection_id, terminalMetadata: state.last_terminal_metadata,
|
|
270
|
+
clock: new ObservationClock({ timeoutMs: saved.clock.timeout_ms, saved: saved.clock }) };
|
|
271
|
+
this.connectionId = saved.connection_id;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
persist(patch = {}) { Object.assign(this.state, patch, { native_observation_episode: this.episodeSnapshot(), pid: process.pid, updated_at: new Date().toISOString(), last_activity_at: this.lastActivityAt, session_observations: [...this.sessionObservations], tool_observations: [...this.toolObservations.calls.values(), ...this.toolObservations.sources.values()], tool_observation_gaps: [...this.toolObservations.gaps], turn_generation: this.turnGeneration, descendants: [...this.descendants.values()], sessions: this.init ? [{ provider_session_id: this.init.conversation_id, root_provider_session_id: this.init.conversation_id, cwd: this.config.cwd }] : [] }); const snapshot = structuredClone(this.state); const queued = performance.now(); this.persisting = this.persisting.catch(() => {}).then(async () => { const started = performance.now(); await writeJson(this.paths.state, snapshot); this.persistTiming = { queue_ms: Math.round(started - queued), write_ms: Math.round(performance.now() - started) }; }); return this.persisting; }
|
|
275
|
+
async journal(kind, payload = {}, reservedPosition = null) { const position = reservedPosition ?? (this.journalPosition = (this.journalPosition ?? 0) + 1); const line = `${JSON.stringify({ schema_id: "dd-agy/daemon-event@1", event_id: randomUUID(), position, observed_at: new Date().toISOString(), kind, ...payload })}\n`; for (const journal of new Set([this.paths.journal, this.config.journal].filter(Boolean))) { await mkdir(path.dirname(journal), { recursive: true, mode: 0o700 }); await appendFile(journal, line, { mode: 0o600 }); } return position; }
|
|
276
|
+
args(sessionId) { return ["--input-format", "stream-json", "--output-format", "stream-json", ...agyModelArgs(this.config.model, this.config.reasoning), "--mode", this.config.mode, "--dangerously-skip-permissions", "--print-timeout", "0", ...(this.nativeLogFile ? ['--log-file', this.nativeLogFile] : []), ...(this.nativeCsrfToken ? [`--csrf_token=${this.nativeCsrfToken}`] : []), `--gemini_dir=${this.paths.gemini}`, "--app_data_dir=runtime", ...(sessionId ? ["--conversation", sessionId] : ["--new-project"])]; }
|
|
229
277
|
requireSessionIdentity(requestedId) {
|
|
230
278
|
const observedId = this.init?.conversation_id ?? null;
|
|
231
279
|
if (!requestedId || requestedId === observedId) return observedId;
|
|
@@ -233,7 +281,7 @@ export class Runtime {
|
|
|
233
281
|
harness: "antigravity-cli", expected_session_id: requestedId, observed_session_id: observedId
|
|
234
282
|
});
|
|
235
283
|
}
|
|
236
|
-
markActivity() { const current = this.active; this.lastActivityAt = new Date().toISOString(); this.progress = { cursor: (this.progress?.cursor ?? 0) + 1, observed_at: Date.now() }; void current?.publishProgress?.('native_progress', { session_id: this.init?.conversation_id, observed_at: this.lastActivityAt }).catch(error => { if(this.active === current) current.reject(error); }); }
|
|
284
|
+
markActivity() { const current = this.active ?? this.observationEpisode; this.nativeRevision = (this.nativeRevision ?? 0) + 1; this.state.native_execution_revision = this.nativeRevision; this.nativeProof = null; this.nativeAbort?.abort(); this.lastActivityAt = new Date().toISOString(); this.progress = { cursor: (this.progress?.cursor ?? 0) + 1, observed_at: Date.now() }; current?.clock?.sample(this.progress); void current?.publishProgress?.('native_progress', { session_id: this.init?.conversation_id, observed_at: this.lastActivityAt }).catch(error => { if(this.active === current) current.reject(error); }); }
|
|
237
285
|
markTerminalActivity(result) {
|
|
238
286
|
const current = this.active;
|
|
239
287
|
if (!current) return;
|
|
@@ -243,11 +291,12 @@ export class Runtime {
|
|
|
243
291
|
current.terminalProgress.add(key); this.markActivity();
|
|
244
292
|
}
|
|
245
293
|
observeProviderActivity(event) {
|
|
246
|
-
|
|
294
|
+
const owner = this.active ?? this.observationEpisode;
|
|
295
|
+
if (!owner || owner.connectionId !== this.connectionId || owner.generation !== this.turnGeneration) return;
|
|
247
296
|
const root = this.init?.conversation_id, step = event.step_update;
|
|
248
297
|
const id = step?.conversation_id ?? event.result?.conversation_id ?? root;
|
|
249
298
|
let parent = id; const seen = new Set();
|
|
250
|
-
while (parent && parent !== root && !seen.has(parent)) { seen.add(parent); const child = this.descendants.get(parent); if (child?.activity_generation !==
|
|
299
|
+
while (parent && parent !== root && !seen.has(parent)) { seen.add(parent); const child = this.descendants.get(parent); if (child?.activity_generation !== owner.generation) return; parent = child.parent_provider_session_id; }
|
|
251
300
|
if (!root || parent !== root) return;
|
|
252
301
|
if (event.event === "result") { if (["SUCCESS", "ERROR", "CANCELLED"].includes(event.result?.status)) this.markTerminalActivity(event.result); return; }
|
|
253
302
|
if (event.event !== "step_update") return;
|
|
@@ -265,7 +314,7 @@ export class Runtime {
|
|
|
265
314
|
// Native step snapshots can alternate/replay; scope retained dedup to the
|
|
266
315
|
// current step. Too many unique snapshots becomes uncertain, never a grant.
|
|
267
316
|
if (snapshots.size >= 1_024) return;
|
|
268
|
-
snapshots.add(fingerprint); this.progressSteps.set(id, { index, seen: snapshots }); this.markActivity();
|
|
317
|
+
snapshots.add(fingerprint); this.progressSteps.set(id, { index, seen: snapshots }); this.invalidateSettlement(id); this.markActivity();
|
|
269
318
|
}
|
|
270
319
|
async start(sessionId = null) {
|
|
271
320
|
if (this.drainingError) throw this.drainingError;
|
|
@@ -278,6 +327,11 @@ export class Runtime {
|
|
|
278
327
|
await this.providerFinalization;
|
|
279
328
|
if (this.drainingError) throw this.drainingError;
|
|
280
329
|
assertDaemonDispatch(this.paths?.dir);
|
|
330
|
+
this.nativeAbort?.abort(); this.nativeProof = null;
|
|
331
|
+
this.nativeLogFile = path.join(this.paths.dir, `native-${randomUUID()}.log`);
|
|
332
|
+
this.nativeCsrfToken = randomUUID();
|
|
333
|
+
this.admissionClock = null;
|
|
334
|
+
this.nativeQualification = await readAgyNativeQualification(this.paths.dir, this.config.bin, this.config.nativeQualificationPath);
|
|
281
335
|
const target = executable(this.config.bin, this.args(sessionId)); this.exited = false; this.buffer = ""; this.init = null;
|
|
282
336
|
let readyResolve, readyReject; this.readyPromise = new Promise((resolve, reject) => { readyResolve = resolve; readyReject = reject; });
|
|
283
337
|
// The provider can reject before durable registration finishes. Keep a
|
|
@@ -288,9 +342,10 @@ export class Runtime {
|
|
|
288
342
|
const providerProcess = this.providerProcess;
|
|
289
343
|
try { await this.persist({ provider_process: providerProcess }); }
|
|
290
344
|
catch (error) { await cleanupFailedStart(this.config, providerProcess, null, error); throw error; }
|
|
291
|
-
try { assertDaemonDispatch(this.paths?.dir); this.child = spawn(target.command, target.args, { cwd: this.config.cwd, env: sanitizedEnv(this.config), detached: true, stdio: ["pipe", "pipe", "pipe"] }); }
|
|
345
|
+
try { assertDaemonDispatch(this.paths?.dir); this.connectionId = randomUUID(); this.child = spawn(target.command, target.args, { cwd: this.config.cwd, env: sanitizedEnv(this.config), detached: true, stdio: ["pipe", "pipe", "pipe"] }); }
|
|
292
346
|
catch (error) { await cleanupFailedStart(this.config, providerProcess, null, error); throw error; }
|
|
293
347
|
const providerChild = this.child;
|
|
348
|
+
this.nativeLaunch = { logFile: this.nativeLogFile, providerPid: providerChild.pid, providerBirth: (await processSnapshot()).find(item => item.pid === providerChild.pid)?.started, connectionId: this.connectionId };
|
|
294
349
|
let allowProviderFinalization;
|
|
295
350
|
const providerRegistered = new Promise(resolve => { allowProviderFinalization = resolve; });
|
|
296
351
|
const observationFailure = error => { this.drainingError ??= error; readyReject(error); this.active?.reject(error); };
|
|
@@ -334,10 +389,7 @@ export class Runtime {
|
|
|
334
389
|
// child can close while the asynchronous write is still pending.
|
|
335
390
|
if (event.event === "result" && ["ERROR", "CANCELLED"].includes(event.result?.status)) {
|
|
336
391
|
this.lastTerminalFailure ??= this.state.last_terminal_failure;
|
|
337
|
-
|
|
338
|
-
&& this.lastTerminalFailure.details.provider_session_id === (event.result.conversation_id || this.init?.conversation_id || null)
|
|
339
|
-
&& (this.active?.previousError === event.result.error || this.lastResult?.status === "ERROR" && this.lastResult.error === event.result.error));
|
|
340
|
-
event.terminal_metadata = repeated ? this.lastTerminalFailure.details : { observed_at: new Date().toISOString(), provider_session_id: event.result.conversation_id || this.init?.conversation_id || null,
|
|
392
|
+
event.terminal_metadata = { observed_at: new Date().toISOString(), provider_session_id: event.result.conversation_id || this.init?.conversation_id || null,
|
|
341
393
|
operation_id: this.active?.operationId ?? null, turn_generation: this.active?.generation ?? null, journal_locator: this.paths.journal, native_source: "agy.result" };
|
|
342
394
|
if (!this.init) this.startupError ??= agyTerminalFailure(event.result, event.terminal_metadata);
|
|
343
395
|
this.receivedTerminalMetadata = event.terminal_metadata;
|
|
@@ -357,25 +409,53 @@ export class Runtime {
|
|
|
357
409
|
// Tree liveness is broader than current-Turn model progress: fresh work
|
|
358
410
|
// from a retained child invalidates settlement without adopting this Turn.
|
|
359
411
|
if (fresh && (sessionId === this.init?.conversation_id || this.descendants.has(sessionId))) this.childActivity(sessionId);
|
|
360
|
-
if (Number.isInteger(step.step_index)) this.sessionObservations.set(sessionId, { ...observation, last_step_index: Math.max(observation.last_step_index ?? -1, step.step_index) });
|
|
412
|
+
if (Number.isInteger(step.step_index)) this.sessionObservations.set(sessionId, { ...this.sessionObservations.get(sessionId), ...(step.step_index > (observation.last_step_index ?? -1) ? { last_execution_num: null } : {}), last_step_index: Math.max(observation.last_step_index ?? -1, step.step_index) });
|
|
361
413
|
this.toolObservations.add(agyToolObservation(step, this.init?.conversation_id));
|
|
362
414
|
for (const child of step.subagent_info?.subagents ?? []) if (child.conversation_id) {
|
|
363
415
|
const prior = this.descendants.get(child.conversation_id);
|
|
416
|
+
if (child.conversation_id === this.init?.conversation_id || prior && prior.parent_provider_session_id !== sessionId) {
|
|
417
|
+
this.state.native_topology_error = 'agy_native_ancestry_contradiction'; this.invalidateSettlement(sessionId); continue;
|
|
418
|
+
}
|
|
364
419
|
this.descendants.set(child.conversation_id, { ...prior, provider_session_id: child.conversation_id, parent_provider_session_id: sessionId ?? null, role: child.role ?? null, subagent_type: child.type_name ?? null, log_uri: child.log_uri ?? null, workspace_uris: child.workspace_uris ?? [], status: prior?.status ?? "unknown",
|
|
420
|
+
...(bound && Number.isInteger(step.step_index) ? { native_source: { kind: 'agy.step_update.subagent_info', parent_provider_session_id: sessionId, step_index: step.step_index } } : {}),
|
|
365
421
|
...(fresh && bound && this.active ? { activity_generation: this.active.generation } : {}) });
|
|
366
422
|
}
|
|
367
423
|
}
|
|
368
424
|
toolSnapshot() { const summary = this.toolObservations.summary(); const failures_by_name = {}; for (const event of this.toolObservations.calls.values()) if (event.status === "failed") failures_by_name[event.name] = (failures_by_name[event.name] ?? 0) + 1; return { ...summary, by_name: summary.by_tool, failures_by_name }; }
|
|
369
425
|
async captureChildTools() {
|
|
370
426
|
this.capturedToolFacts ??= new Map();
|
|
427
|
+
this.nativeResources ??= new Map(this.state.native_resources_entries?.map(item => [item.resource_id, item]) ?? []);
|
|
371
428
|
const root = this.init?.conversation_id ?? this.state.sessions?.[0]?.provider_session_id ?? this.lastResult?.conversation_id;
|
|
372
|
-
|
|
429
|
+
const queue = root && this.paths.runtime ? [{ provider_session_id: root, parent_provider_session_id: root, log_uri: pathToFileURL(path.join(this.paths.runtime, 'brain', root, '.system_generated', 'logs', 'transcript.jsonl')).href }, ...this.descendants.values()] : [...this.descendants.values()];
|
|
430
|
+
const visited = new Set();
|
|
431
|
+
for (let index = 0; index < queue.length && index < 256; index++) {
|
|
432
|
+
const child = queue[index];
|
|
433
|
+
if (visited.has(child.provider_session_id)) continue;
|
|
434
|
+
visited.add(child.provider_session_id);
|
|
373
435
|
const ancestors = new Set(); let parent = child.parent_provider_session_id;
|
|
374
436
|
while (parent && parent !== root && this.descendants.has(parent) && !ancestors.has(parent)) {
|
|
375
437
|
ancestors.add(parent); parent = this.descendants.get(parent).parent_provider_session_id;
|
|
376
438
|
}
|
|
377
439
|
const events = parent === root && root
|
|
378
|
-
? await captureAgyTranscript(this.paths.runtime, child, root, [root, ...ancestors])
|
|
440
|
+
? await captureAgyTranscript(this.paths.runtime, child, root, [root, ...ancestors], (text, source) => {
|
|
441
|
+
const facts = agyTranscriptFacts(text, source, child.provider_session_id);
|
|
442
|
+
for (const resource of facts.resources) this.nativeResources.set(resource.resource_id, resource);
|
|
443
|
+
for (const discovered of facts.children) {
|
|
444
|
+
const expected = path.join(this.paths.runtime, 'brain', discovered.provider_session_id, '.system_generated', 'logs', 'transcript.jsonl');
|
|
445
|
+
const sourceBrain = path.resolve(path.dirname(fileURLToPath(source.locator)), '../../..');
|
|
446
|
+
const sibling = path.join(sourceBrain, discovered.provider_session_id, '.system_generated', 'logs', 'transcript.jsonl');
|
|
447
|
+
let locator;
|
|
448
|
+
try { locator = fileURLToPath(discovered.log_uri); } catch { locator = null; }
|
|
449
|
+
if (locator !== expected && locator !== sibling) { this.state.native_topology_error = 'agy_native_locator_unbound'; this.invalidateSettlement(root); continue; }
|
|
450
|
+
const prior = this.descendants.get(discovered.provider_session_id);
|
|
451
|
+
if (discovered.provider_session_id === root || prior && (prior.parent_provider_session_id !== discovered.parent_provider_session_id || prior.log_uri && prior.log_uri !== discovered.log_uri)) {
|
|
452
|
+
this.state.native_topology_error = 'agy_native_ancestry_contradiction'; this.invalidateSettlement(root); continue;
|
|
453
|
+
}
|
|
454
|
+
const value = { ...discovered, ...prior, native_source: discovered.native_source };
|
|
455
|
+
this.descendants.set(value.provider_session_id, value);
|
|
456
|
+
queue.push(value);
|
|
457
|
+
}
|
|
458
|
+
})
|
|
379
459
|
: [{ session: { harness_id: "antigravity-cli", session_id: child.provider_session_id }, reason: "agy_transcript_ancestry_unproven" }];
|
|
380
460
|
for (const event of events) {
|
|
381
461
|
this.toolObservations.add(event);
|
|
@@ -385,6 +465,8 @@ export class Runtime {
|
|
|
385
465
|
this.capturedToolFacts.set(key, encoded);
|
|
386
466
|
}
|
|
387
467
|
}
|
|
468
|
+
if (queue.length > 256 && visited.size < new Set(queue.map(item => item.provider_session_id)).size) { this.state.native_topology_error = 'agy_native_inventory_limit'; this.invalidateSettlement(root); }
|
|
469
|
+
this.state.native_resources_entries = [...this.nativeResources.values()];
|
|
388
470
|
}
|
|
389
471
|
async finishTurn(result, retainedMetadata = null, providerEvent = null) {
|
|
390
472
|
if (result.status === "RUNNING") { await this.persist({ active_tree: true }); return; }
|
|
@@ -399,26 +481,39 @@ export class Runtime {
|
|
|
399
481
|
return;
|
|
400
482
|
}
|
|
401
483
|
const current = this.active;
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
484
|
+
if (current?.dispatched === false) {
|
|
485
|
+
const error = new DaemonError("operation_observation_lost", "Unexpected native terminal before this stream request was dispatched", false, { operation_id: current.operationId, turn_generation: current.generation, native_outcome: "unknown" });
|
|
486
|
+
this.active = null; current.reject(error);
|
|
487
|
+
await this.journal("unexpected_terminal_before_dispatch", { result, operation_id: current.operationId });
|
|
488
|
+
await this.persist({ unclaimed_activity: true, active_tree: true });
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const terminalMetadata = { received_at: new Date().toISOString(), ...(retainedMetadata ?? this.receivedTerminalMetadata ?? { observed_at: new Date().toISOString(), provider_session_id: result.conversation_id || this.init?.conversation_id || null, operation_id: current?.operationId ?? null,
|
|
492
|
+
turn_generation: current?.generation ?? null, journal_locator: this.paths.journal, native_source: "agy.result" }) };
|
|
407
493
|
this.receivedTerminalMetadata = null;
|
|
408
494
|
// The ordered stream returns one result per input. Model usage counters
|
|
409
495
|
// are not request IDs: native command rejections can report zero turns.
|
|
410
496
|
// prompt() drains earlier events before accepting another input.
|
|
411
|
-
if (!current && this.lastResult) {
|
|
497
|
+
if (!current && this.lastResult) {
|
|
498
|
+
await this.journal("stale_terminal_observed", { result });
|
|
499
|
+
if (!isDeepStrictEqual(result, this.lastResult)) await this.persist({ unclaimed_activity: true, active_tree: true });
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
412
502
|
this.lastResult = result; this.markTerminalActivity(result);
|
|
413
503
|
const rootStop = this.sessionObservations.get(this.init?.conversation_id)?.stop;
|
|
414
504
|
const pendingTerminal = current && result.status === "SUCCESS";
|
|
415
505
|
if (current) { current.terminalResult = result; current.terminalMetadata = terminalMetadata; current.terminalPersisted = false; }
|
|
416
|
-
if (pendingTerminal && rootStop?.fullyIdle === true) for (const [id, descendant] of this.descendants) if (descendant.status === "unknown") this.descendants.set(id, { ...descendant, status: "settled_by_root", settlement_evidence: "root_fully_idle_stop" });
|
|
506
|
+
if (pendingTerminal && rootStop?.source_bound === true && rootStop.fullyIdle === true) for (const [id, descendant] of this.descendants) if (descendant.status === "unknown") this.descendants.set(id, { ...descendant, status: "settled_by_root", settlement_evidence: "root_fully_idle_stop" });
|
|
417
507
|
this.state.terminal_observation_pending = true;
|
|
418
508
|
try {
|
|
419
509
|
await this.persist({ active_tree: true, last_result: result, last_terminal_metadata: terminalMetadata });
|
|
420
510
|
if (current) current.terminalPersisted = true;
|
|
421
|
-
|
|
511
|
+
const terminalPosition = await this.journal("provider_event", { event: providerEvent ?? { event: "result", result } });
|
|
512
|
+
terminalMetadata.stream_terminal_position = terminalPosition;
|
|
513
|
+
if (current?.outboundPosition && current.dispatchJournalSettled && !current.dispatchObservationError && this.config.daemonId && current.connectionId && current.operationId) terminalMetadata.request_proof = {
|
|
514
|
+
kind: "stream_request", daemon_instance: this.config.daemonId, connection_id: current.connectionId, operation_id: current.operationId,
|
|
515
|
+
turn_generation: current.generation, session_id: this.init.conversation_id, outbound_position: current.outboundPosition, terminal_position: terminalPosition
|
|
516
|
+
};
|
|
422
517
|
await this.captureChildTools();
|
|
423
518
|
} catch (error) { return this.failTerminalObservation(current, result, terminalMetadata, error); }
|
|
424
519
|
try { this.usageIngest = await forwardUsage(this.config, result.conversation_id ?? this.init.conversation_id, result, this.toolSnapshot(), [...this.descendants.values()]); }
|
|
@@ -426,12 +521,23 @@ export class Runtime {
|
|
|
426
521
|
try { await this.persist({ terminal_observation_pending: false }); }
|
|
427
522
|
catch (error) { this.state.terminal_observation_pending = true; return this.failTerminalObservation(current, result, terminalMetadata, error); }
|
|
428
523
|
this.state.terminal_observation_pending = false;
|
|
524
|
+
this.queueNativeInspection();
|
|
429
525
|
if (!current) return;
|
|
430
526
|
if (this.active !== current) return;
|
|
431
527
|
if (result.status === "ERROR" || result.status === "CANCELLED") {
|
|
528
|
+
await this.observeHookFailures();
|
|
529
|
+
if (this.active !== current) return;
|
|
432
530
|
this.active = null;
|
|
433
531
|
const settled = this.receipt().settled;
|
|
434
|
-
|
|
532
|
+
if (result.status === "ERROR" && settled && rootStop?.fullyIdle === true && !this.state.last_hook_rejection && current.operationId && this.paths.runtime) {
|
|
533
|
+
const root = this.init.conversation_id, transcript = path.join(this.paths.runtime, "brain", root, ".system_generated", "logs", "transcript.jsonl");
|
|
534
|
+
await captureAgyTranscript(this.paths.runtime, { provider_session_id: root, parent_provider_session_id: root, log_uri: new URL(`file://${transcript}`).href }, root, [root], (text, source) => {
|
|
535
|
+
const boundary = recoveredAgyErrorBoundary(text, result.error, current.stepFloor);
|
|
536
|
+
const hook = current.nativeHookEvents?.get(boundary?.boundary_step);
|
|
537
|
+
if (boundary && hook) terminalMetadata.recovered_error_boundary = { ...boundary, receipt: { ...boundary.receipt, native_hook_event_key: hook, daemon_instance: this.config.daemonId }, source };
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
this.lastTerminalFailure = agyTerminalFailure(result, { ...terminalMetadata, native_turn_items: { observed: settled, possible_effects: true, pending: !settled }, provider_error_scope: "terminal_result", usage_ingest: this.usageIngest });
|
|
435
541
|
try { await this.persist({ active_tree: !settled, last_terminal_failure: errorRecord(this.lastTerminalFailure) }); }
|
|
436
542
|
catch (error) { return this.failTerminalObservation(current, result, terminalMetadata, error); }
|
|
437
543
|
return current.reject(this.lastTerminalFailure);
|
|
@@ -444,6 +550,7 @@ export class Runtime {
|
|
|
444
550
|
const current = this.active;
|
|
445
551
|
if (current?.observationError) throw current.observationError;
|
|
446
552
|
if (current?.terminalResult?.status !== "SUCCESS" || current.terminalResult !== this.lastResult) return;
|
|
553
|
+
this.queueNativeInspection();
|
|
447
554
|
await this.persist({ active_tree: !this.receipt().settled });
|
|
448
555
|
// Hooks can invalidate the tree while persistence is pending. Never
|
|
449
556
|
// publish a proof captured before that asynchronous boundary.
|
|
@@ -464,26 +571,127 @@ export class Runtime {
|
|
|
464
571
|
if (current) { current.observationError = error; current.reject(error); }
|
|
465
572
|
throw error;
|
|
466
573
|
}
|
|
574
|
+
queueNativeInspection() {
|
|
575
|
+
const owner = this.active ?? this.observationEpisode, proof = owner?.terminalMetadata?.request_proof;
|
|
576
|
+
if (!owner?.clock || !proof || this.state.terminal_observation_pending || this.nativeFlight || this.nativeProof || this.exited) return;
|
|
577
|
+
if (proof.connection_id !== this.connectionId || proof.daemon_instance !== this.config.daemonId || proof.turn_generation !== this.turnGeneration || proof.operation_id !== owner.operationId || proof.session_id !== this.init?.conversation_id) { this.nativeInspection = { state: 'unproven', reason: 'native_owner_binding_changed' }; return; }
|
|
578
|
+
if (owner.clock.sample(this.progress)) { this.nativeInspection = { state: 'unproven', reason: 'observation_budget_exhausted' }; return; }
|
|
579
|
+
// The observer's raw wire result is never authority before P3 qualification.
|
|
580
|
+
if (!this.nativeObserver && !this.nativeQualification?.qualified) { this.nativeInspection = { state: 'unproven', reason: 'native_inspection_not_qualified' }; return; }
|
|
581
|
+
const revision = this.nativeRevision ?? 0, connection = this.connectionId;
|
|
582
|
+
const controller = this.nativeAbort = new AbortController();
|
|
583
|
+
let recording = false;
|
|
584
|
+
this.nativeInspection = { state: 'inspection_pending', reason: 'native_idle_barrier_pending' };
|
|
585
|
+
this.nativeFlight = Promise.resolve().then(async () => {
|
|
586
|
+
await owner.dispatchJournal;
|
|
587
|
+
const clock = owner.clock.state(), remainingMs = Math.max(0, clock.timeout_ms - clock.elapsed_ms);
|
|
588
|
+
if (!remainingMs || owner.clock.sample(this.progress)) throw new Error('observation_budget_exhausted');
|
|
589
|
+
const endpoint = this.nativeObserver ? null : await ownedAgyEndpoint(this.nativeLaunch);
|
|
590
|
+
const observed = await (this.nativeObserver ?? inspectAgyNative)({ endpoint, sessionId: proof.session_id, cwd: this.config.cwd, remainingMs, signal: controller.signal, rootRelation: this.nativeQualification?.rootRelation, csrfToken: this.nativeCsrfToken });
|
|
591
|
+
if (!this.nativeObserver && JSON.stringify(await ownedAgyEndpoint(this.nativeLaunch)) !== JSON.stringify(endpoint)) throw new Error('native_endpoint_changed');
|
|
592
|
+
const current = () => !controller.signal.aborted && this.connectionId === connection && (this.nativeRevision ?? 0) === revision && this.turnGeneration === owner.generation && this.observationEpisode === owner && !this.exited && !owner.clock.sample(this.progress);
|
|
593
|
+
if (!current()) return;
|
|
594
|
+
if (!observed.idle) { this.nativeInspection = { state: 'inspection_pending', reason: observed.reason ?? 'native_wait_pending' }; return; }
|
|
595
|
+
const candidate = { daemon_instance: this.config.daemonId, connection_id: connection, operation_id: owner.operationId, turn_generation: owner.generation, request_proof: proof, revision, method: observed.method, source: 'native_idle_barrier' };
|
|
596
|
+
recording = true;
|
|
597
|
+
await this.journal('native_tree_settlement', { proof: candidate });
|
|
598
|
+
if (!current()) return;
|
|
599
|
+
// Persist conservatively before publishing. A crash may require another
|
|
600
|
+
// read-only observation; no concurrent reader gets an uncommitted grant.
|
|
601
|
+
await this.persist({ active_tree: true });
|
|
602
|
+
if (!current()) return;
|
|
603
|
+
this.nativeProof = candidate;
|
|
604
|
+
this.state.native_execution_revision = revision;
|
|
605
|
+
this.state.unclaimed_activity = false;
|
|
606
|
+
this.projectNativeIdleChildren();
|
|
607
|
+
this.nativeInspection = { state: 'ready', reason: 'native_wait_fully_idle' };
|
|
608
|
+
this.state.active_tree = !this.receipt().settled;
|
|
609
|
+
await this.completePendingTurn();
|
|
610
|
+
}).catch(error => {
|
|
611
|
+
if (!controller.signal.aborted) {
|
|
612
|
+
this.nativeProof = null;
|
|
613
|
+
const transient = !recording && ['grpc_4', 'grpc_14'].includes(error.details?.reason) && !owner.clock.sample(this.progress);
|
|
614
|
+
this.nativeInspection = { state: transient ? 'inspection_pending' : 'unproven', reason: error.details?.reason ?? error.message ?? 'native_inspection_failed' };
|
|
615
|
+
if (recording && this.active === owner && owner.terminalResult) {
|
|
616
|
+
try { this.failTerminalObservation(owner, owner.terminalResult, owner.terminalMetadata, error); } catch { /* original prompt owns the typed failure */ }
|
|
617
|
+
}
|
|
618
|
+
if (recording) this.state.terminal_observation_pending = true;
|
|
619
|
+
}
|
|
620
|
+
})
|
|
621
|
+
.finally(() => { this.nativeFlight = null; });
|
|
622
|
+
}
|
|
623
|
+
projectNativeIdleChildren() {
|
|
624
|
+
for (const [id, child] of this.descendants) this.descendants.set(id, { ...child, ...(!['completed', 'failed', 'cancelled'].includes(child.status) ? { previous_native_status: child.status, status: 'settled_by_root' } : {}), ...(child.native_turn_items ? { native_turn_items: { ...child.native_turn_items, observed: true, pending: false } } : {}), tree_settled: true, settlement_evidence: 'native_idle_barrier' });
|
|
625
|
+
}
|
|
626
|
+
async reconcileRetainedAdmission() {
|
|
627
|
+
const retained = this.state.retained_tree_settlement;
|
|
628
|
+
const fresh = !this.lastResult;
|
|
629
|
+
if (this.active || this.receipt().settled || !fresh && (retained?.evidence !== 'verified_clean_daemon_shutdown' || retained.session_id !== this.init?.conversation_id)) return;
|
|
630
|
+
if (!this.nativeObserver && !this.nativeQualification?.qualified) return;
|
|
631
|
+
if (this.admissionFlight) return await this.admissionFlight;
|
|
632
|
+
const clock = this.admissionClock ??= new ObservationClock({ timeoutMs: SETUP_STARTUP_MS });
|
|
633
|
+
if (!clock || clock.sample()) throw new DaemonError('agy_native_inspection_unavailable', 'Retained AGY native admission budget exhausted');
|
|
634
|
+
const connection = this.connectionId, revision = this.nativeRevision ?? 0, generation = this.turnGeneration;
|
|
635
|
+
const controller = this.nativeAbort = new AbortController();
|
|
636
|
+
this.admissionFlight = (async () => {
|
|
637
|
+
const endpoint = this.nativeObserver ? null : await ownedAgyEndpoint(this.nativeLaunch);
|
|
638
|
+
const observed = await (this.nativeObserver ?? inspectAgyNative)({ endpoint, sessionId: this.init.conversation_id, cwd: this.config.cwd, remainingMs: Math.max(1, clock.timeoutMs - clock.elapsed), signal: controller.signal, rootRelation: this.nativeQualification?.rootRelation, csrfToken: this.nativeCsrfToken });
|
|
639
|
+
if (!this.nativeObserver && JSON.stringify(await ownedAgyEndpoint(this.nativeLaunch)) !== JSON.stringify(endpoint)) throw new DaemonError('agy_native_inspection_unavailable', 'AGY admission endpoint changed');
|
|
640
|
+
const current = () => !controller.signal.aborted && !this.exited && !this.active && !clock.sample() && this.connectionId === connection && this.turnGeneration === generation && (this.nativeRevision ?? 0) === revision;
|
|
641
|
+
if (!observed.idle || !current()) throw new DaemonError('agy_native_inspection_unavailable', 'AGY fresh native admission is unproven');
|
|
642
|
+
// Admission is not the old stream request: no invented operation/Turn or
|
|
643
|
+
// request proof. The old failed result remains historical diagnostics.
|
|
644
|
+
const candidate = { daemon_instance: this.config.daemonId, connection_id: connection, turn_generation: generation, revision, method: observed.method, source: 'native_idle_admission' };
|
|
645
|
+
await this.journal('native_idle_admission', { proof: candidate });
|
|
646
|
+
if (!current()) throw new DaemonError('agy_native_inspection_unavailable', 'AGY native admission changed during observation persistence');
|
|
647
|
+
await this.persist({ active_tree: true });
|
|
648
|
+
if (!current()) throw new DaemonError('agy_native_inspection_unavailable', 'AGY native admission changed during observation persistence');
|
|
649
|
+
this.nativeProof = candidate;
|
|
650
|
+
this.state.native_execution_revision = revision;
|
|
651
|
+
this.state.unclaimed_activity = false; this.projectNativeIdleChildren();
|
|
652
|
+
this.state.active_tree = !this.receipt().settled;
|
|
653
|
+
})().catch(error => { this.nativeProof = null; this.nativeInspection = { state: 'unproven', reason: error.details?.reason ?? error.code ?? 'native_admission_unproven' }; throw error; }).finally(() => { this.admissionFlight = null; });
|
|
654
|
+
return await this.admissionFlight;
|
|
655
|
+
}
|
|
656
|
+
episodeSnapshot() {
|
|
657
|
+
const owner = this.observationEpisode;
|
|
658
|
+
return owner?.clock ? { operation_id: owner.operationId, turn_generation: owner.generation, connection_id: owner.connectionId, clock: owner.clock.state() } : null;
|
|
659
|
+
}
|
|
467
660
|
receipt(result = this.lastResult) {
|
|
468
661
|
if (this.state.last_terminal_failure && !this.lastTerminalFailure) this.lastTerminalFailure = this.state.last_terminal_failure;
|
|
469
|
-
const
|
|
662
|
+
const metadata = this.active?.terminalResult === result ? this.active.terminalMetadata : this.state.last_result === result ? this.state.last_terminal_metadata : this.lastTerminalFailure && this.lastTerminalFailure.details?.provider_result?.error === result?.error
|
|
470
663
|
&& this.lastTerminalFailure.details.provider_session_id === (result?.conversation_id || this.init?.conversation_id || null) ? this.lastTerminalFailure.details : null;
|
|
664
|
+
const terminalMetadata = metadata?.request_proof && (metadata.request_proof.connection_id !== this.connectionId || metadata.request_proof.daemon_instance !== this.config.daemonId)
|
|
665
|
+
? { ...metadata, request_proof: undefined } : metadata;
|
|
471
666
|
const assistantText = typeof result?.response === "string" ? result.response : typeof result?.text === "string" ? result.text : null;
|
|
472
667
|
const root = this.init?.conversation_id; const observation = this.sessionObservations.get(root);
|
|
473
668
|
const treeSettled = [...this.descendants.values()].every(child => child.tree_settled === true || ["completed", "failed", "cancelled", "settled_by_root"].includes(child.status));
|
|
474
|
-
//
|
|
475
|
-
//
|
|
476
|
-
|
|
477
|
-
const rootSettled =
|
|
669
|
+
// SUCCESS can coexist with background tasks. Only a current native idle
|
|
670
|
+
// proof (or verified prior clean retirement before new activity) closes it.
|
|
671
|
+
const retained = this.state.retained_tree_settlement;
|
|
672
|
+
const rootSettled = this.nativeProof?.turn_generation === this.turnGeneration && this.nativeProof.connection_id === this.connectionId && this.nativeProof.revision === (this.nativeRevision ?? 0)
|
|
673
|
+
|| observation?.stop?.source_bound === true && observation.stop?.fullyIdle === true && observation.stop.generation === this.turnGeneration
|
|
674
|
+
|| retained?.evidence === "verified_clean_daemon_shutdown" && retained.session_id === root && retained.turn_generation === this.turnGeneration && !this.child;
|
|
478
675
|
const terminalPending = this.active?.terminalResult === result;
|
|
479
|
-
|
|
480
|
-
|
|
676
|
+
// A positively inspected owned scope before its first Turn has no model
|
|
677
|
+
// result to manufacture. Native admission alone proves this empty execution.
|
|
678
|
+
const emptyExecution = !result && this.turnGeneration === 0 && !this.active && !this.descendants.size && this.nativeProof?.source === 'native_idle_admission';
|
|
679
|
+
const executionTerminal = emptyExecution || result && ["SUCCESS", "ERROR", "CANCELLED"].includes(result.status);
|
|
680
|
+
const settled = Boolean(!this.state.native_topology_error && !this.state.terminal_observation_pending && !this.active?.observationError && !this.state.unclaimed_activity && (!this.active || terminalPending) && executionTerminal && treeSettled && rootSettled);
|
|
681
|
+
const clock = this.observationEpisode?.clock;
|
|
682
|
+
if (clock) clock.sample(this.progress);
|
|
683
|
+
const exhausted = clock && (clock.observationLost || clock.elapsed >= clock.timeoutMs);
|
|
684
|
+
const nativeSettlement = { confirmed: Boolean(settled), state: settled ? 'ready' : exhausted ? 'unproven' : this.nativeInspection?.state ?? 'unproven', reason: settled ? 'current_root_idle_proven' : exhausted ? 'observation_budget_exhausted' : this.nativeInspection?.reason ?? 'current_root_idle_proof_missing', capability: AGY_NATIVE_INSPECTION_CONTRACT, operation_id: terminalMetadata?.request_proof?.operation_id, request_key: streamRequestKey(terminalMetadata?.request_proof), provider_session_id: root, daemon_instance: this.config.daemonId, connection_epoch: this.connectionId, generation: this.turnGeneration, ...(this.nativeProof ? { source: this.nativeProof.source, method: this.nativeProof.method, proof_sha256: createHash('sha256').update(JSON.stringify(this.nativeProof)).digest('hex') } : {}), ...(clock ? { observation_clock: clock.state() } : {}) };
|
|
685
|
+
return nativeExecutionReceipt({ harness: "antigravity-cli", runtime_family: "antigravity", provider_session_id: root ?? null, adapter_session_id: root ?? null, cwd: this.config.cwd, profile: this.profile, result, ...(terminalMetadata ? { terminal_metadata: terminalMetadata, ...(terminalMetadata.turn_id ? { terminal_turn_id: terminalMetadata.turn_id } : {}) } : {}), ...(assistantText === null ? {} : { assistant_text: assistantText }), usage: usageSnapshot(result ?? {}, this.toolSnapshot()), usage_ingest: this.usageIngest, descendants: [...this.descendants.values()], ...settledNativeTree(root, [...this.descendants.values()], settled, !this.state.unclaimed_activity && (!this.active || terminalPending) && rootSettled && ["SUCCESS", "ERROR", "CANCELLED"].includes(result?.status)), native_turn_items: { observed: Boolean(terminalMetadata?.request_proof), possible_effects: true, pending: !settled, settled, ...(terminalMetadata?.request_proof ? { request_proof: terminalMetadata.request_proof } : {}) }, transcript_path: observation?.transcript_path ?? null, settled, native_settled: settled, physical_stopped: this.exited === true, native_settlement: nativeSettlement, }, { daemonId: this.config.daemonId, generation: this.turnGeneration, revision: this.nativeRevision ?? 0, resources: { coverage: "partial", entries: [...(this.nativeResources?.values() ?? this.state.native_resources_entries ?? [])] } });
|
|
481
686
|
}
|
|
482
687
|
async zeroUsage() { await forwardUsage(this.config, this.init.conversation_id, { usage: {} }, this.toolSnapshot(), []); }
|
|
483
688
|
async prompt(text, assertDispatch = captureDispatchGuard(this), operationId = null, livenessTimeoutMs = 1_800_000) {
|
|
484
689
|
validateObservationDuration(livenessTimeoutMs);
|
|
485
690
|
if (!text) throw new DaemonError("prompt_required", "prompt is required");
|
|
486
691
|
await this.start(); await this.draining;
|
|
692
|
+
if (!this.nativeObserver && !this.nativeQualification?.qualified && this.config.bin && this.paths.dir) this.nativeQualification = await readAgyNativeQualification(this.paths.dir, this.config.bin, this.config.nativeQualificationPath);
|
|
693
|
+
if (!this.nativeObserver && !this.nativeQualification?.qualified) throw new DaemonError('agy_native_inspection_unqualified', 'AGY whole-tree inspection requires a matching native qualification receipt before productive dispatch', false, { capability: AGY_NATIVE_INSPECTION_CONTRACT });
|
|
694
|
+
await this.reconcileRetainedAdmission();
|
|
487
695
|
if (this.active || this.promptObservation) throw new DaemonError("operation_in_progress", "an Antigravity turn or its mandatory dispatch observation is already running");
|
|
488
696
|
if (this.state.last_hook_rejection && !this.receipt().settled) throw new DaemonError("tree_not_settled", "Rejected hook does not prove that the prior Antigravity tree stopped", false, { primary_error: this.state.last_hook_rejection });
|
|
489
697
|
if (this.state.unclaimed_activity || (this.lastResult && !this.receipt().settled)) throw new DaemonError("tree_not_settled", "the prior Antigravity agent tree is not fully settled");
|
|
@@ -491,7 +699,11 @@ export class Runtime {
|
|
|
491
699
|
const reply = new Promise((resolve, reject) => { resolveTurn = resolve; rejectTurn = reject; });
|
|
492
700
|
// Cancellation can reject while admission persistence is still pending.
|
|
493
701
|
void reply.catch(() => {});
|
|
494
|
-
|
|
702
|
+
this.connectionId ??= randomUUID();
|
|
703
|
+
const current = { resolve: resolveTurn, reject: rejectTurn, generation: ++this.turnGeneration, operationId, connectionId: this.connectionId, dispatched: false };
|
|
704
|
+
this.nativeAbort?.abort(); this.nativeProof = null; this.nativeInspection = null;
|
|
705
|
+
current.clock = new ObservationClock({ timeoutMs: livenessTimeoutMs, onGap: gap => { void this.journal('observation_gap', { gap, operation_id: operationId }).catch(() => {}); } });
|
|
706
|
+
this.observationEpisode = current;
|
|
495
707
|
let resolveDispatchJournal;
|
|
496
708
|
const dispatchJournal = current.dispatchJournal = new Promise(resolve => { resolveDispatchJournal = resolve; });
|
|
497
709
|
this.promptObservation = current;
|
|
@@ -504,6 +716,7 @@ export class Runtime {
|
|
|
504
716
|
this.active = current;
|
|
505
717
|
this.hookRequests = new Map();
|
|
506
718
|
const root = this.init?.conversation_id, prior = this.sessionObservations.get(root) ?? {};
|
|
719
|
+
current.stepFloor = prior.last_step_index ?? -1;
|
|
507
720
|
this.sessionObservations.set(root, { ...prior, step_floor: prior.last_step_index ?? null, stop: null, last_hook: null });
|
|
508
721
|
this.lastProviderEvent = null; this.progressSteps = new Map(); this.progressHooks = new Set(); this.progress = undefined;
|
|
509
722
|
try {
|
|
@@ -511,10 +724,11 @@ export class Runtime {
|
|
|
511
724
|
assertDispatch();
|
|
512
725
|
assertDaemonDispatch(this.paths?.dir);
|
|
513
726
|
if (this.active !== current) throw new DaemonError("operation_cancelled", "prompt reservation was superseded");
|
|
727
|
+
current.outboundPosition = this.journalPosition = (this.journalPosition ?? 0) + 1;
|
|
514
728
|
current.dispatched = true;
|
|
515
729
|
this.child.stdin.write(`${JSON.stringify({ event: "user", message: { content: text } })}\n`, error => {
|
|
516
730
|
if (error) { this.drainingError ??= nativeObservationError(error); current.dispatchObservationError = this.drainingError; current.reject(this.drainingError); finishDispatchJournal(); }
|
|
517
|
-
else void Promise.resolve().then(() => this.journal("native_dispatch", { operation_id: operationId, provider_session_id: this.init?.conversation_id,
|
|
731
|
+
else void Promise.resolve().then(() => this.journal("native_dispatch", { operation_id: operationId, provider_session_id: this.init?.conversation_id, turn_generation: current.generation, connection_id: current.connectionId }, current.outboundPosition)).then(finishDispatchJournal, cause => {
|
|
518
732
|
this.drainingError ??= nativeObservationError(cause); current.dispatchObservationError = this.drainingError;
|
|
519
733
|
current.reject(this.drainingError); finishDispatchJournal();
|
|
520
734
|
});
|
|
@@ -524,9 +738,10 @@ export class Runtime {
|
|
|
524
738
|
else if (this.active === current) this.active = null;
|
|
525
739
|
current.reject(error); finishDispatchJournal();
|
|
526
740
|
}
|
|
527
|
-
const clock =
|
|
741
|
+
const clock = current.clock;
|
|
528
742
|
let checking = false;
|
|
529
743
|
const timer = setInterval(() => {
|
|
744
|
+
if (this.active === current && current.terminalResult && this.nativeInspection?.state === 'inspection_pending' && !this.nativeFlight) this.queueNativeInspection();
|
|
530
745
|
if (this.active !== current || checking || !clock.sample(this.progress)) return;
|
|
531
746
|
checking = true; const cursor = this.progress?.cursor;
|
|
532
747
|
void this.completePendingTurn().then(() => {
|
|
@@ -552,24 +767,67 @@ export class Runtime {
|
|
|
552
767
|
});
|
|
553
768
|
}
|
|
554
769
|
return result;
|
|
770
|
+
} catch (error) {
|
|
771
|
+
await dispatchJournal;
|
|
772
|
+
if (error.code === "native_outcome_observation_failed") throw error;
|
|
773
|
+
if (current.dispatchObservationError) {
|
|
774
|
+
if (!current.terminalResult) throw current.dispatchObservationError;
|
|
775
|
+
const cause = current.dispatchObservationError;
|
|
776
|
+
const failed = current.terminalResult.status === 'SUCCESS'
|
|
777
|
+
? new DaemonError('native_outcome_observation_failed', 'Antigravity completed natively but its dispatch journal could not be saved', false, { native_outcome: this.receipt(current.terminalResult), settled: false, observation_error: errorRecord(cause) })
|
|
778
|
+
: agyTerminalFailure(current.terminalResult, { ...current.terminalMetadata, native_outcome: current.terminalResult, settled: false, native_turn_items: { observed: false, possible_effects: true, pending: true }, observation_error: errorRecord(cause) });
|
|
779
|
+
failed.cause = cause.cause ?? cause;
|
|
780
|
+
current.observationError = failed; current.dispatchObservationError = failed;
|
|
781
|
+
this.drainingError = failed; this.state.terminal_observation_pending = true;
|
|
782
|
+
if (current.terminalResult.status !== 'SUCCESS') this.lastTerminalFailure = failed;
|
|
783
|
+
throw failed;
|
|
784
|
+
}
|
|
785
|
+
const metadata = current.terminalMetadata;
|
|
786
|
+
if (error === this.lastTerminalFailure && metadata && !metadata.request_proof && current.outboundPosition && metadata.stream_terminal_position && this.config.daemonId && current.operationId) {
|
|
787
|
+
metadata.request_proof = { kind: "stream_request", daemon_instance: this.config.daemonId, connection_id: current.connectionId, operation_id: current.operationId,
|
|
788
|
+
turn_generation: current.generation, session_id: this.init.conversation_id, outbound_position: current.outboundPosition, terminal_position: metadata.stream_terminal_position };
|
|
789
|
+
const proven = agyTerminalFailure(current.terminalResult, { ...error.details, ...metadata });
|
|
790
|
+
this.lastTerminalFailure = proven;
|
|
791
|
+
await this.persist({ last_terminal_metadata: metadata, last_terminal_failure: errorRecord(proven) });
|
|
792
|
+
throw proven;
|
|
793
|
+
}
|
|
794
|
+
throw error;
|
|
555
795
|
} finally {
|
|
556
796
|
clearInterval(timer); current.observerReturned = true;
|
|
557
797
|
if (current.dispatchJournalSettled && this.promptObservation === current) this.promptObservation = null;
|
|
558
798
|
}
|
|
559
799
|
}
|
|
560
800
|
childActivity(sessionId) {
|
|
801
|
+
this.invalidateSettlement(sessionId);
|
|
561
802
|
if (sessionId === this.init?.conversation_id && !this.active) this.state.unclaimed_activity = true;
|
|
562
803
|
const child = this.descendants.get(sessionId);
|
|
563
804
|
if (!child) return;
|
|
564
805
|
this.descendants.set(sessionId, { ...child, previous_terminal_status: child.status === "unknown" ? child.previous_terminal_status : child.status, status: "unknown", tree_settled: false, settlement_evidence: null, settlement_daemon_id: null });
|
|
565
806
|
}
|
|
807
|
+
invalidateSettlement(sessionId) {
|
|
808
|
+
this.nativeProof = null; this.nativeRevision = (this.nativeRevision ?? 0) + 1; this.nativeAbort?.abort();
|
|
809
|
+
this.state.native_execution_revision = this.nativeRevision;
|
|
810
|
+
if (this.init) this.state.active_tree = true;
|
|
811
|
+
this.state.retained_tree_settlement = null;
|
|
812
|
+
for (const id of new Set([this.init?.conversation_id, sessionId])) {
|
|
813
|
+
const observation = this.sessionObservations.get(id);
|
|
814
|
+
if (observation?.stop) this.sessionObservations.set(id, { ...observation, stop: null });
|
|
815
|
+
}
|
|
816
|
+
}
|
|
566
817
|
async childForHook(conversationId) {
|
|
567
818
|
if (conversationId === this.init?.conversation_id) return null;
|
|
819
|
+
await this.captureChildTools();
|
|
568
820
|
const deadline = Date.now() + 1_000;
|
|
569
|
-
while (!this.descendants.has(conversationId) && Date.now() < deadline) await new Promise(resolve => setTimeout(resolve,
|
|
821
|
+
while (!this.descendants.has(conversationId) && Date.now() < deadline) { await new Promise(resolve => setTimeout(resolve, 25)); await this.captureChildTools(); }
|
|
570
822
|
const child = this.descendants.get(conversationId);
|
|
571
823
|
if (!child) throw new DaemonError("agy_child_identity_unconfirmed", "Antigravity hook has no confirmed native parent", true, { conversation_id: conversationId });
|
|
572
|
-
|
|
824
|
+
const seen = new Set([conversationId]); let current = child;
|
|
825
|
+
while (current.parent_provider_session_id !== this.init?.conversation_id) {
|
|
826
|
+
const parent = this.descendants.get(current.parent_provider_session_id);
|
|
827
|
+
if (this.state.native_topology_error || !current.native_source || !parent || seen.has(parent.provider_session_id)) throw new DaemonError("agy_child_parent_unqualified", "Antigravity worker has no source-bound ancestry to the controlled Session", false, { conversation_id: conversationId, parent_conversation_id: current.parent_provider_session_id });
|
|
828
|
+
seen.add(parent.provider_session_id); current = parent;
|
|
829
|
+
}
|
|
830
|
+
if (this.state.native_topology_error) throw new DaemonError("agy_child_parent_unqualified", "Antigravity native ancestry is contradictory");
|
|
573
831
|
return child;
|
|
574
832
|
}
|
|
575
833
|
async rejectHook(error, event, payload, current = this.active) {
|
|
@@ -603,10 +861,45 @@ export class Runtime {
|
|
|
603
861
|
await this.rejectHook(Object.assign(new Error(failure.message ?? "Native hook failed"), { code: failure.code, details: failure.details }), binding.event, binding.payload, binding.current);
|
|
604
862
|
}
|
|
605
863
|
}
|
|
606
|
-
async observeHook(event, payload, budget = null) {
|
|
864
|
+
async observeHook(event, payload, budget = null) {
|
|
865
|
+
const conversationId = payload.conversationId ?? this.init?.conversation_id;
|
|
866
|
+
const child = await this.childForHook(conversationId); budget?.remaining("identity");
|
|
867
|
+
const prior = this.sessionObservations.get(conversationId) ?? {};
|
|
868
|
+
const execution = Number.isInteger(payload.executionNum) ? payload.executionNum : null;
|
|
869
|
+
const step = Number.isInteger(payload.stepIdx) ? payload.stepIdx : null;
|
|
870
|
+
const stale = step !== null && (Number.isInteger(prior.step_floor) && step <= prior.step_floor
|
|
871
|
+
|| Number.isInteger(prior.last_step_index) && step < prior.last_step_index
|
|
872
|
+
|| step === prior.last_step_index && execution !== null && Number.isInteger(prior.last_execution_num) && execution < prior.last_execution_num);
|
|
873
|
+
if (stale) { await this.journal("stale_hook", { hook_event: event, conversation_id: conversationId, step_index: step }); return { stale: true }; }
|
|
874
|
+
const fingerprint = createHash("sha256").update(JSON.stringify({ event, payload })).digest("hex");
|
|
875
|
+
if (event === "Stop" && prior.last_stop_hook === fingerprint && prior.last_stop_generation === this.turnGeneration) return { stale: true };
|
|
876
|
+
this.progressHooks ??= new Set();
|
|
877
|
+
const scoped = this.active && (!child || child.activity_generation === this.active.generation);
|
|
878
|
+
const sourceBound = Boolean(scoped && step !== null && step > (this.active.stepFloor ?? -1)
|
|
879
|
+
&& this.progressSteps?.get(conversationId)?.index === step);
|
|
880
|
+
const progressKey = JSON.stringify([conversationId, step, execution, event, payload.toolCall ?? null, payload.fullyIdle ?? null]);
|
|
881
|
+
if (scoped && step !== null && (["PreToolUse", "PostToolUse"].includes(event) || event === "Stop" && payload.fullyIdle === true) && !this.progressHooks.has(progressKey) && this.progressHooks.size < 1_024) { this.progressHooks.add(progressKey); this.markActivity(); }
|
|
882
|
+
if (conversationId) this.sessionObservations.set(conversationId, { ...prior,
|
|
883
|
+
...(step !== null ? { last_step_index: step, last_execution_num: execution ?? (step === prior.last_step_index ? prior.last_execution_num : null) } : {}),
|
|
884
|
+
last_hook: fingerprint, ...(event === "PreToolUse" ? { last_pre_hook: fingerprint } : {}),
|
|
885
|
+
...(payload.transcriptPath ? { transcript_path: payload.transcriptPath } : {}),
|
|
886
|
+
...(event === "Stop" ? { last_stop_hook: fingerprint, last_stop_generation: this.turnGeneration, stop: { fullyIdle: payload.fullyIdle === true, source_bound: sourceBound, terminationReason: payload.terminationReason ?? null, executionNum: execution } } : {}) });
|
|
887
|
+
if (event === "PreToolUse" && prior.last_pre_hook !== fingerprint) this.childActivity(conversationId);
|
|
888
|
+
if (child && event === "Stop" && sourceBound && payload.fullyIdle === true) this.descendants.set(conversationId, { ...this.descendants.get(conversationId), tree_settled: true, settlement_evidence: "child_fully_idle_stop" });
|
|
889
|
+
const terminalPending = this.active?.terminalResult === this.lastResult;
|
|
890
|
+
if (!child && event === "Stop" && sourceBound && payload.fullyIdle === true && (!this.active || terminalPending) && ["SUCCESS", "ERROR", "CANCELLED"].includes(this.lastResult?.status)) for (const [id, descendant] of this.descendants) if (descendant.status === "unknown") this.descendants.set(id, { ...descendant, status: "settled_by_root", settlement_evidence: "root_fully_idle_stop" });
|
|
891
|
+
const parent = child?.parent_provider_session_id ?? null;
|
|
892
|
+
if (event === "Stop" && !stale) {
|
|
893
|
+
const observation = this.sessionObservations.get(conversationId);
|
|
894
|
+
this.sessionObservations.set(conversationId, { ...observation, stop: { ...observation.stop, generation: this.turnGeneration } });
|
|
895
|
+
}
|
|
607
896
|
const observed = payload.model ? observedProfile(payload) : {};
|
|
608
897
|
await observeModel({ journal: this.config.journal, harness: "antigravity-cli", sessionId: conversationId, parentSessionId: parent, requested: this.config, observed, source: { channel: "native.hook", scope: "configured", ...(!payload.model ? { non_asserting: true } : {}) }, evidence: payload.model ? "configured" : "unavailable", reason: payload.model ? null : "hook_omits_current_model" });
|
|
609
|
-
const eventId = createHash("sha256").update(JSON.stringify({ daemon: this.config.daemonId, event, conversationId, fully_idle: payload.fullyIdle ?? null, termination_reason: payload.terminationReason ?? null, execution: payload.executionNum ?? null, step: payload.stepIdx ?? null, tool: payload.toolCall?.name ?? null, args: payload.toolCall?.args ?? null })).digest("hex");
|
|
898
|
+
const eventId = createHash("sha256").update(JSON.stringify({ daemon: this.config.daemonId, event, conversationId, fully_idle: payload.fullyIdle ?? null, termination_reason: payload.terminationReason ?? null, execution: payload.executionNum ?? null, step: payload.stepIdx ?? null, tool: payload.toolCall?.name ?? null, args: payload.toolCall?.args ?? null })).digest("hex");
|
|
899
|
+
await this.persist(); await this.journal("hook", { stale, hook_event: event, event_id: eventId, conversation_id: conversationId, turn_generation: this.turnGeneration, execution_num: execution, step_index: step, termination_reason: payload.terminationReason ?? null, fully_idle: payload.fullyIdle ?? null, persistence_timing: this.persistTiming ?? null });
|
|
900
|
+
if (!stale && !child && event === "PreToolUse" && Number.isInteger(step) && this.active) { this.active.nativeHookEvents ??= new Map(); this.active.nativeHookEvents.set(step, `${eventId}:before`); }
|
|
901
|
+
this.queueNativeInspection();
|
|
902
|
+
await this.completePendingTurn(); return { event_id: eventId, daemon_id: this.config.daemonId, launch_cwd: this.config.cwd, parent_conversation_id: parent, profile: observed, requested_profile: { provider: this.config.provider, model: this.config.model, reasoning: this.config.reasoning, mode: this.config.mode }, project_root: this.config.projectRoot, dd_flow_bin: this.config.ddFlowBin, dd_flow_home: this.config.ddFlowHome };
|
|
610
903
|
}
|
|
611
904
|
async finalizeProviderForControl() {
|
|
612
905
|
const pending = this.providerFinalization;
|
|
@@ -633,19 +926,40 @@ export class Runtime {
|
|
|
633
926
|
}
|
|
634
927
|
async prepareStop(cancelTree) {
|
|
635
928
|
const current = this.active, priorResult = this.lastResult;
|
|
636
|
-
|
|
637
|
-
if (cancelTree) await this.cancel();
|
|
929
|
+
if (!cancelTree && !current && !priorResult) await this.reconcileRetainedAdmission();
|
|
930
|
+
if (cancelTree && (this.active || !this.receipt().settled)) await this.cancel();
|
|
638
931
|
else if (this.active || this.state.active_tree || this.state.unclaimed_activity || (this.lastResult && !this.receipt().settled)) throw new DaemonError("tree_not_settled", "Antigravity root or child tree is still running");
|
|
639
932
|
// A physical kill is settlement, not a native clean terminal receipt.
|
|
640
933
|
// In particular, the previous Turn's SUCCESS cannot certify a cancelled
|
|
641
934
|
// in-flight Turn that exited without its own result.
|
|
642
935
|
const currentResult = !current || current.terminalResult === this.lastResult || this.lastResult !== priorResult;
|
|
643
|
-
|
|
644
|
-
|
|
936
|
+
if (currentResult && this.receipt().settled) await this.retireNativeServices();
|
|
937
|
+
const clean = currentResult && this.receipt().settled;
|
|
938
|
+
return { ...this.receipt(), clean, settled: Boolean(clean), native_settled: Boolean(clean), ...(clean ? {} : { native_settlement: { confirmed: false, reason: "native_terminal_unconfirmed" } }) };
|
|
939
|
+
}
|
|
940
|
+
async retireNativeServices() {
|
|
941
|
+
const services = [...(this.nativeResources?.values() ?? [])].filter(item => item.classification === 'support_service' && item.state !== 'stopped');
|
|
942
|
+
// Unknown classification/unsupported binaries keep the existing cleanup
|
|
943
|
+
// blocker. A fixture PID printed to stdout is never a production mapping.
|
|
944
|
+
if (!services.length || !this.nativeQualification?.qualified) return;
|
|
945
|
+
const revision = this.nativeRevision ?? 0;
|
|
946
|
+
const endpoint = this.nativeStopper ? null : await ownedAgyEndpoint(this.nativeLaunch);
|
|
947
|
+
const input = { endpoint, sessionId: this.init.conversation_id, cwd: this.config.cwd,
|
|
948
|
+
csrfToken: this.nativeCsrfToken, remainingMs: 10_000, rootRelation: this.nativeQualification.rootRelation };
|
|
949
|
+
const stopped = await (this.nativeStopper ?? stopAgyNativeTree)(input, [...this.descendants.keys()]);
|
|
950
|
+
const observed = await (this.nativeObserver ?? inspectAgyNative)(input);
|
|
951
|
+
if (!this.nativeStopper && !isDeepStrictEqual(await ownedAgyEndpoint(this.nativeLaunch), endpoint)
|
|
952
|
+
|| !observed.idle || revision !== (this.nativeRevision ?? 0) || !this.receipt().settled)
|
|
953
|
+
throw new DaemonError('native_resources_unproven', 'AGY native service retirement lost its owner/execution fence');
|
|
954
|
+
// This is native handle retirement, not physical cleanup. Shared shutdown
|
|
955
|
+
// still re-observes the retained local PID/birth inventory after close.
|
|
956
|
+
for (const service of services) this.nativeResources.set(service.resource_id, { ...service, state: 'stopped',
|
|
957
|
+
native_retirement: { method: stopped.method, stopped_session_ids: stopped.stopped_session_ids, revision } });
|
|
958
|
+
await this.persist({ native_resources_entries: [...this.nativeResources.values()] });
|
|
645
959
|
}
|
|
646
|
-
async cancel() {
|
|
960
|
+
async cancel() { if (!this.active && this.receipt().settled) return { ...this.receipt(), cancelled: true }; this.nativeAbort?.abort(); this.nativeProof = null; this.nativeRevision = (this.nativeRevision ?? 0) + 1; this.state.native_execution_revision = this.nativeRevision; cancelPendingDispatch(this); if (!this.child) return this.receipt(); if (!this.exited) { try { process.kill(-this.child.pid, "SIGINT"); } catch (error) { if (error.code !== "ESRCH") throw error; } const deadline = Date.now() + 5000; while (!this.exited && Date.now() < deadline) await new Promise(resolve => setTimeout(resolve, 100)); if (!this.exited) await stopProcessGroup(this.child, 5_000); } await this.finalizeProviderForControl(); await this.persist({ active_tree: false }); return { ...this.receipt(), cancelled: true };
|
|
647
961
|
}
|
|
648
|
-
async close(cancelTree) { if (cancelTree) cancelPendingDispatch(this); if (!cancelTree && !this.exited && (this.active || this.state.active_tree || this.state.unclaimed_activity || (this.lastResult && !this.receipt().settled))) throw new DaemonError("tree_not_settled", "Antigravity root or child tree is still running"); if (cancelTree) await this.cancel(); else if (this.child && !this.exited) this.child.stdin.end(); const deadline = Date.now() + 5000; while (!this.exited && Date.now() < deadline) await new Promise(resolve => setTimeout(resolve, 100)); if (!this.exited) await this.cancel(); await this.finalizeProviderForControl(); }
|
|
962
|
+
async close(cancelTree) { if (cancelTree) cancelPendingDispatch(this); if (!cancelTree && !this.exited && (this.active || this.state.active_tree || this.state.unclaimed_activity || (this.lastResult && !this.receipt().settled))) throw new DaemonError("tree_not_settled", "Antigravity root or child tree is still running"); if (cancelTree) await this.cancel(); else if (this.child && !this.exited) this.child.stdin.end(); const deadline = Date.now() + 5000; while (!this.exited && Date.now() < deadline) await new Promise(resolve => setTimeout(resolve, 100)); if (!this.exited) { if (!this.active && this.receipt().settled) await stopProcessGroup(this.child, 5_000); else await this.cancel(); } await this.finalizeProviderForControl(); }
|
|
649
963
|
async abortProvider(reason) {
|
|
650
964
|
const child = this.child, providerProcess = this.providerProcess;
|
|
651
965
|
await stopProcessGroup(child);
|
|
@@ -700,7 +1014,7 @@ async function dispatch(runtime, request) {
|
|
|
700
1014
|
if (request.operation === "daemon.status") { await runtime.observeHookFailures(); return { daemon_id: runtime.config.daemonId, shutdown_state: runtime.state.shutdown_state, active_tree: runtime.state.active_tree, active_operation: runtime.active?.operationId ?? null, last_hook_rejection: runtime.state.last_hook_rejection ?? null, last_activity_at: runtime.lastActivityAt, provider_ready: Boolean(runtime.init && !runtime.exited), pid: process.pid, provider_pid: runtime.child?.pid ?? null, versions: runtime.state.versions, config: runtime.config, sessions: runtime.state.sessions ?? [], receipt: runtime.receipt() }; }
|
|
701
1015
|
if (request.operation === "session.create") { if (params.sessionId) throw new DaemonError("invalid_create", "create does not accept a Session ID"); if (params.prompt) throw new DaemonError("invalid_create", "create does not execute a prompt; use session.prompt"); await runtime.start(); await runtime.zeroUsage(); return runtime.receipt(); }
|
|
702
1016
|
if (request.operation === "session.prompt") { const assertDispatch = captureDispatchGuard(runtime); await runtime.start(params.sessionId ?? null); runtime.requireSessionIdentity(params.sessionId); return await runtime.prompt(params.prompt, assertDispatch, request.id, params.livenessTimeoutMs ?? runtime.config.livenessTimeoutMs ?? 1_800_000); }
|
|
703
|
-
if (request.operation === "session.inspect") { runtime.requireSessionIdentity(params.sessionId); await runtime.captureChildTools(); return runtime.receipt(); }
|
|
1017
|
+
if (request.operation === "session.inspect") { runtime.requireSessionIdentity(params.sessionId); await runtime.captureChildTools(); void runtime.reconcileRetainedAdmission().catch(error => { runtime.nativeInspection = { state: 'unproven', reason: error.details?.reason ?? error.code ?? 'native_admission_unproven' }; }); runtime.queueNativeInspection(); await runtime.persist({ native_observation_episode: runtime.episodeSnapshot() }); return runtime.receipt(); }
|
|
704
1018
|
if (request.operation === "session.resume") { await runtime.start(params.sessionId ?? runtime.init?.conversation_id ?? null); runtime.requireSessionIdentity(params.sessionId); return runtime.receipt(); }
|
|
705
1019
|
if (request.operation === "session.cancel") { runtime.requireSessionIdentity(params.sessionId); return await runtime.cancel(); }
|
|
706
1020
|
if (request.operation === "hook.observe") {
|