@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
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { parseLifecycleCommand, commandOption, commandPosition } from "./lifecycle-command.mjs";
|
|
2
|
+
import { entityReferenceVariants } from "./entity-references.mjs";
|
|
3
|
+
|
|
4
|
+
/** Ordered native transcript evidence, never assistant text as a success signal. */
|
|
5
|
+
export function recoveredAgyErrorBoundary(text, error, floor = -1) {
|
|
6
|
+
let rows;
|
|
7
|
+
try { rows = text.trim().split("\n").filter(Boolean).map(JSON.parse); } catch { return null; }
|
|
8
|
+
let previous = -1, lastError = null, boundary = null, invocation = null;
|
|
9
|
+
for (const row of rows) {
|
|
10
|
+
if (!Number.isInteger(row.step_index) || row.step_index <= previous) return null;
|
|
11
|
+
previous = row.step_index;
|
|
12
|
+
if (row.step_index <= (floor ?? -1)) continue;
|
|
13
|
+
if (row.type === "USER_INPUT") { lastError = null; boundary = null; invocation = null; continue; }
|
|
14
|
+
if (row.type === "ERROR_MESSAGE") { lastError = row; boundary = null; invocation = null; continue; }
|
|
15
|
+
if (Array.isArray(row.tool_calls)) {
|
|
16
|
+
invocation = null;
|
|
17
|
+
if (row.tool_calls.length === 1 && row.tool_calls[0]?.name === "run_command") {
|
|
18
|
+
let command = row.tool_calls[0]?.args?.CommandLine;
|
|
19
|
+
if (typeof command === "string") {
|
|
20
|
+
try { const unquoted = JSON.parse(command); if (typeof unquoted === "string") command = unquoted; } catch { /* ordinary shell bytes */ }
|
|
21
|
+
const parsed = parseLifecycleCommand(command);
|
|
22
|
+
if ((parsed.kind === "standalone" || parsed.kind === "compound" && parsed.supported === true && parsed.invocations?.length === 1) && ["stage_finish", "work_finish", "stage_pause", "recovery_accept"].includes(parsed.invocation.operation)) invocation = parsed.invocation;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (!lastError || row.type !== "GENERIC" || row.status !== "DONE" || typeof row.content !== "string" || !row.content.includes("Command exited with code 0.")) continue;
|
|
27
|
+
const output = row.content.split("Command output:\n")[1]?.replaceAll("\r", "");
|
|
28
|
+
if (!output) continue;
|
|
29
|
+
for (const match of output.matchAll(/^\s*\{/gm)) {
|
|
30
|
+
let receipt;
|
|
31
|
+
const candidate = output.slice(match.index).trim();
|
|
32
|
+
try { receipt = JSON.parse(candidate); } catch {
|
|
33
|
+
// AGY's transcript renderer can unescape control characters inside
|
|
34
|
+
// pause.user_message. Parse only the intact machine header, never the
|
|
35
|
+
// rendered question; the controller must still verify the DB receipt.
|
|
36
|
+
const end = candidate.indexOf(',\n "user_message":');
|
|
37
|
+
if (end < 0) continue;
|
|
38
|
+
try { receipt = JSON.parse(`${candidate.slice(0, end)}}}`); } catch { continue; }
|
|
39
|
+
}
|
|
40
|
+
if (receipt.ok === true && invocation && invocation.operation !== "stage_pause") {
|
|
41
|
+
if (invocation.operation === "recovery_accept" && receipt.accepted === true && typeof receipt.recovery_id === "string") boundary = { receipt: { ok: true, outcome: "completed", lifecycle_operation: "recovery_accept", recovery_id: receipt.recovery_id }, error_step: lastError.step_index, boundary_step: row.step_index };
|
|
42
|
+
const stage = receipt.stage ?? commandOption(invocation, "stage");
|
|
43
|
+
const runId = receipt.run_id;
|
|
44
|
+
const workId = receipt.work_id ?? receipt.work?.work_id ?? commandOption(invocation, "work") ?? (invocation.operation === "work_finish" ? commandPosition(invocation, 0) : null);
|
|
45
|
+
if (invocation.operation === "work_finish" && receipt.status === "completed" && typeof workId === "string") boundary = { receipt: { ok: true, outcome: "completed", work_id: workId, lifecycle_operation: "work_finish" }, error_step: lastError.step_index, boundary_step: row.step_index };
|
|
46
|
+
if (typeof stage === "string" && typeof runId === "string" && (invocation.operation === "work_finish" || entityReferenceVariants(runId).includes(commandPosition(invocation, 0)))) {
|
|
47
|
+
boundary = { receipt: { ok: true, outcome: "completed", run_id: runId, stage, ...(workId ? { work_id: workId } : {}), lifecycle_operation: invocation.operation }, error_step: lastError.step_index, boundary_step: row.step_index };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (invocation?.operation === "stage_pause" && commandOption(invocation, "stage") === receipt.stage && receipt.ok === true && receipt.outcome === "paused"
|
|
51
|
+
&& [receipt.run_id, receipt.stage, receipt.work_id].every(value => typeof value === "string" && value)
|
|
52
|
+
&& (receipt.outcome !== "paused" || typeof receipt.pause?.id === "string")) boundary = { receipt: { ok: true, outcome: receipt.outcome, run_id: receipt.run_id, stage: receipt.stage, work_id: receipt.work_id,
|
|
53
|
+
lifecycle_operation: "stage_pause", pause: { id: receipt.pause.id } }, error_step: lastError.step_index, boundary_step: row.step_index };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return boundary && lastError?.error === error ? boundary : null;
|
|
57
|
+
}
|
|
58
|
+
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import * as grpc from '@grpc/grpc-js';
|
|
2
|
+
import { create, toBinary, fromBinary } from '@bufbuild/protobuf';
|
|
3
|
+
import { WaitForConversationFullyIdleRequestSchema, WaitForConversationFullyIdleResponseSchema, GetConversationMetadataRequestSchema, GetConversationMetadataResponseSchema, ForceStopCascadeTreeRequestSchema, ForceStopCascadeTreeResponseSchema, StreamAgentStateUpdatesRequestSchema, StreamAgentStateUpdatesResponseSchema } from './agy-native_pb.js';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { readFile, lstat, realpath, open } from 'node:fs/promises';
|
|
6
|
+
import { createReadStream, constants } from 'node:fs';
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import { execFile } from 'node:child_process';
|
|
9
|
+
import { promisify } from 'node:util';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { processSnapshot } from './process-snapshot.mjs';
|
|
13
|
+
|
|
14
|
+
export const AGY_NATIVE_INSPECTION_CONTRACT = 'agy-native-inspection@2';
|
|
15
|
+
export const AGY_NATIVE_LIVE_CASES = ['root_child_running', 'child_background_running', 'grandchild_running', 'completion_delivery', 'fully_idle', 'timer_delivery', 'daemon_excluded_from_required_execution', 'owned_daemon_retirement'];
|
|
16
|
+
export const AGY_NATIVE_SAFETY_CASES = ['executor_error', 'timeout', 'late_activity_cancel_restart'];
|
|
17
|
+
export const AGY_NATIVE_QUALIFICATION_CASES = [...AGY_NATIVE_LIVE_CASES, ...AGY_NATIVE_SAFETY_CASES];
|
|
18
|
+
export const AGY_NATIVE_EVIDENCE_CONTRACT = 'agy-native-inspection-evidence@2';
|
|
19
|
+
/** Operator-retained native observations, not a bag of manually asserted PASSes.
|
|
20
|
+
* This validates evidence structure/meaning, not its authenticity: the operator
|
|
21
|
+
* still owns capture and review, as with every local qualification receipt. */
|
|
22
|
+
export function validateAgyNativeEvidence(evidence, identity) {
|
|
23
|
+
const checks = Object.fromEntries(AGY_NATIVE_QUALIFICATION_CASES.map(name => [name, false]));
|
|
24
|
+
const failed = new Set();
|
|
25
|
+
const fencedChanges = new Set();
|
|
26
|
+
const liveServices = new Map(), retiredServices = [];
|
|
27
|
+
if (evidence?.schema_id !== AGY_NATIVE_EVIDENCE_CONTRACT || evidence.binary_sha256 !== identity.sha256 || evidence.client_contract_sha256 !== identity.client_contract_sha256 || !['self', 'empty_self'].includes(evidence.root_relation) || !Array.isArray(evidence.observations)) return { qualified: false, checks };
|
|
28
|
+
for (const item of evidence.observations) {
|
|
29
|
+
if (!item || !AGY_NATIVE_QUALIFICATION_CASES.includes(item.case)) continue;
|
|
30
|
+
if (AGY_NATIVE_LIVE_CASES.includes(item.case) && item.evidence_kind !== 'live' || AGY_NATIVE_SAFETY_CASES.includes(item.case) && !['live', 'deterministic'].includes(item.evidence_kind)) { failed.add(item.case); continue; }
|
|
31
|
+
if (item.source !== 'agy.native' || typeof item.session_id !== 'string' || !item.session_id || typeof item.locator !== 'string' || !item.locator || !Number.isFinite(Date.parse(item.observed_at))) { failed.add(item.case); continue; }
|
|
32
|
+
const before = item.before, after = item.after, wait = item.wait;
|
|
33
|
+
// Observation itself must not create a user/model turn; background model
|
|
34
|
+
// work may continue, hence the explicit inspection-attributed counters.
|
|
35
|
+
if (!before || !after || !['user_turns', 'model_requests'].every(key => Number.isSafeInteger(before[key]) && before[key] >= 0 && after[key] === before[key]) || !wait || !Number.isInteger(wait.grpc_code) || wait.grpc_code < 0 || wait.grpc_code > 16 || typeof item.idle_granted !== 'boolean') { failed.add(item.case); continue; }
|
|
36
|
+
const idle = wait.grpc_code === 0 && wait.timed_out === false;
|
|
37
|
+
const busy = wait.grpc_code === 4 || wait.grpc_code === 0 && wait.timed_out === true;
|
|
38
|
+
const noGrant = item.idle_granted === false;
|
|
39
|
+
const work = Array.isArray(item.work) ? item.work : [];
|
|
40
|
+
const has = (kind, status) => work.some(record => record?.kind === kind && record.status === status && typeof record.id === 'string' && record.id && record.root_session_id === item.session_id);
|
|
41
|
+
const prior = checks[item.case]; checks[item.case] = false;
|
|
42
|
+
if (item.case === 'root_child_running' && item.root_status === 'terminal' && has('child', 'running') && busy && noGrant) checks[item.case] = true;
|
|
43
|
+
if (item.case === 'child_background_running' && has('child', 'terminal') && has('background', 'running') && busy && noGrant) checks[item.case] = true;
|
|
44
|
+
if (item.case === 'grandchild_running' && item.root_status === 'terminal' && item.inventory_complete === true && has('child', 'terminal') && has('grandchild', 'running') && work.every(record => record?.kind === 'grandchild' || record?.status === 'terminal') && busy && noGrant) checks[item.case] = true;
|
|
45
|
+
if (item.case === 'completion_delivery' && has('completion', 'pending') && busy && noGrant) checks[item.case] = true;
|
|
46
|
+
if (item.case === 'timer_delivery' && has('timer', 'terminal') && has('completion', 'terminal') && item.inventory_complete === true && work.every(record => record?.status === 'terminal' && record.root_session_id === item.session_id) && item.root_status === 'terminal' && idle && item.idle_granted === true) checks[item.case] = true;
|
|
47
|
+
if (item.case === 'fully_idle' && item.root_status === 'terminal' && item.inventory_complete === true && work.every(record => record?.status === 'terminal' && typeof record.id === 'string' && record.id && record.root_session_id === item.session_id) && idle && item.idle_granted === true) checks[item.case] = true;
|
|
48
|
+
if (item.case === 'executor_error' && typeof item.executor_error === 'string' && item.executor_error && noGrant && (busy || wait.grpc_code !== 0)) checks[item.case] = true;
|
|
49
|
+
if (item.case === 'timeout' && busy && noGrant) checks[item.case] = true;
|
|
50
|
+
if (item.case === 'late_activity_cancel_restart' && ['activity', 'cancel', 'restart'].includes(item.fence?.change) && typeof item.fence.before === 'string' && item.fence.before && typeof item.fence.after === 'string' && item.fence.after && item.fence.after !== item.fence.before && idle && noGrant) { checks[item.case] = true; fencedChanges.add(item.fence.change); }
|
|
51
|
+
const service = work.find(record => record?.kind === 'persistent' && record.native_tool === 'run_command' && record.native_is_daemon === true && record.root_session_id === item.session_id && typeof record.id === 'string' && record.id && Number.isSafeInteger(record.process?.pid) && record.process.pid > 0 && typeof record.process.birth === 'string' && record.process.birth && record.process.owned === true);
|
|
52
|
+
const serviceKey = service && JSON.stringify([item.session_id, service.id, service.process.pid, service.process.birth]);
|
|
53
|
+
if (item.case === 'daemon_excluded_from_required_execution' && service?.status === 'running' && service.process.live === true && item.root_status === 'terminal' && item.native_result_status === 'SUCCESS' && item.inventory_complete === true && work.every(record => record === service || record?.status === 'terminal' && typeof record.id === 'string' && record.id && record.root_session_id === item.session_id) && idle && item.idle_granted === true) { checks[item.case] = true; liveServices.set(serviceKey, Date.parse(item.observed_at)); }
|
|
54
|
+
if (item.case === 'owned_daemon_retirement' && service?.status === 'terminal' && service.process.live === false && item.cleanup?.coverage === 'complete' && item.cleanup.provider_stopped === true && typeof item.cleanup.snapshot_locator === 'string' && item.cleanup.snapshot_locator && noGrant) { checks[item.case] = true; retiredServices.push([serviceKey, Date.parse(item.observed_at)]); }
|
|
55
|
+
if (!checks[item.case]) failed.add(item.case);
|
|
56
|
+
checks[item.case] ||= prior;
|
|
57
|
+
}
|
|
58
|
+
checks.late_activity_cancel_restart &&= fencedChanges.size === 3;
|
|
59
|
+
checks.owned_daemon_retirement &&= liveServices.size > 0 && [...liveServices].every(([key, started]) => retiredServices.some(([retiredKey, at]) => retiredKey === key && at >= started));
|
|
60
|
+
for (const name of failed) checks[name] = false;
|
|
61
|
+
return { qualified: Object.values(checks).every(Boolean), checks };
|
|
62
|
+
}
|
|
63
|
+
const run = promisify(execFile);
|
|
64
|
+
const invalid = reason => Object.assign(new Error(`AGY native inspection unavailable: ${reason}`), { code: 'agy_native_inspection_unavailable', details: { reason, capability: AGY_NATIVE_INSPECTION_CONTRACT } });
|
|
65
|
+
const codec = schema => ({ serialize: value => Buffer.from(toBinary(schema, create(schema, value))), deserialize: bytes => fromBinary(schema, bytes) });
|
|
66
|
+
const method = (name, request, response) => ({ path: `/exa.language_server_pb.LanguageServerService/${name}`, requestStream: false, responseStream: false, requestSerialize: codec(request).serialize, requestDeserialize: codec(request).deserialize, responseSerialize: codec(response).serialize, responseDeserialize: codec(response).deserialize });
|
|
67
|
+
export const agyNativeService = {
|
|
68
|
+
wait: method('WaitForConversationFullyIdle', WaitForConversationFullyIdleRequestSchema, WaitForConversationFullyIdleResponseSchema),
|
|
69
|
+
metadata: method('GetConversationMetadata', GetConversationMetadataRequestSchema, GetConversationMetadataResponseSchema),
|
|
70
|
+
stop: method('ForceStopCascadeTree', ForceStopCascadeTreeRequestSchema, ForceStopCascadeTreeResponseSchema),
|
|
71
|
+
state: { ...method('StreamAgentStateUpdates', StreamAgentStateUpdatesRequestSchema, StreamAgentStateUpdatesResponseSchema), responseStream: true }
|
|
72
|
+
};
|
|
73
|
+
const Client = grpc.makeGenericClientConstructor(agyNativeService, 'AgyReadOnlyInspection');
|
|
74
|
+
|
|
75
|
+
async function readRegular(file, maximum, prefix = false) {
|
|
76
|
+
if (!path.isAbsolute(file)) throw invalid('native_file_path_invalid');
|
|
77
|
+
const before = await lstat(file);
|
|
78
|
+
if (!before.isFile() || before.isSymbolicLink() || !prefix && before.size > maximum) throw invalid('native_file_rejected');
|
|
79
|
+
const handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
80
|
+
try {
|
|
81
|
+
const stat = await handle.stat();
|
|
82
|
+
if (!stat.isFile() || stat.dev !== before.dev || stat.ino !== before.ino || !prefix && stat.size > maximum) throw invalid('native_file_changed');
|
|
83
|
+
const bytes = Buffer.alloc(prefix ? Math.min(stat.size, maximum) : maximum + 1);
|
|
84
|
+
const { bytesRead } = await handle.read(bytes, 0, bytes.length, 0);
|
|
85
|
+
const after = await lstat(file);
|
|
86
|
+
if (!after.isFile() || after.dev !== stat.dev || after.ino !== stat.ino || !prefix && bytesRead > maximum) throw invalid('native_file_changed');
|
|
87
|
+
return { bytes: bytes.subarray(0, bytesRead), stat };
|
|
88
|
+
} finally { await handle.close(); }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function agyBinaryIdentity(bin) {
|
|
92
|
+
if (typeof bin !== 'string' || !bin) throw invalid('native_binary_missing');
|
|
93
|
+
const candidate = path.isAbsolute(bin) ? bin : (await Promise.all((process.env.PATH ?? '').split(path.delimiter).map(async dir => {
|
|
94
|
+
const file = path.join(dir, bin); try { return (await lstat(file)).isFile() || (await lstat(file)).isSymbolicLink() ? file : null; } catch { return null; }
|
|
95
|
+
}))).find(Boolean);
|
|
96
|
+
if (!candidate) throw invalid('native_binary_missing');
|
|
97
|
+
const file = await realpath(candidate), hash = createHash('sha256');
|
|
98
|
+
for await (const chunk of createReadStream(file)) hash.update(chunk);
|
|
99
|
+
return { file, sha256: hash.digest('hex') };
|
|
100
|
+
}
|
|
101
|
+
export async function agyInspectionPolicyIdentity() {
|
|
102
|
+
const manifest = JSON.parse(await readFile(new URL('../../../package.json', import.meta.url), 'utf8'));
|
|
103
|
+
const versions = [manifest.dependencies?.['@grpc/grpc-js'], manifest.dependencies?.['@bufbuild/protobuf']];
|
|
104
|
+
if (versions[0] !== '1.14.6' || versions[1] !== '2.16.0') throw invalid('native_protocol_dependency_unqualified');
|
|
105
|
+
return createHash('sha256').update(AGY_NATIVE_INSPECTION_CONTRACT).update(JSON.stringify(versions)).update(await readFile(new URL('./agy-native_pb.js', import.meta.url))).update(await readFile(new URL('./agy-native-inspection.mjs', import.meta.url))).digest('hex');
|
|
106
|
+
}
|
|
107
|
+
export async function readAgyNativeQualification(stateDir, bin, receiptFile = null) {
|
|
108
|
+
const binary = await agyBinaryIdentity(bin);
|
|
109
|
+
const clientContract = await agyInspectionPolicyIdentity();
|
|
110
|
+
let receipt;
|
|
111
|
+
const file = receiptFile ?? path.join(stateDir, 'native-inspection-qualification.json');
|
|
112
|
+
if (!path.isAbsolute(file)) throw invalid('qualification_path_invalid');
|
|
113
|
+
try {
|
|
114
|
+
receipt = JSON.parse((await readRegular(file, 64 * 1024)).bytes.toString('utf8'));
|
|
115
|
+
}
|
|
116
|
+
catch (error) { if (error.code !== 'ENOENT') throw invalid('qualification_receipt_invalid'); }
|
|
117
|
+
let evidenceValid = false;
|
|
118
|
+
if (typeof receipt?.evidence_path === 'string' && path.isAbsolute(receipt.evidence_path)) {
|
|
119
|
+
try {
|
|
120
|
+
const evidence = (await readRegular(receipt.evidence_path, 1024 * 1024)).bytes;
|
|
121
|
+
const parsed = JSON.parse(evidence.toString('utf8'));
|
|
122
|
+
evidenceValid = createHash('sha256').update(evidence).digest('hex') === receipt.evidence_sha256
|
|
123
|
+
&& parsed.root_relation === receipt.root_relation
|
|
124
|
+
&& validateAgyNativeEvidence(parsed, { ...binary, client_contract_sha256: clientContract }).qualified;
|
|
125
|
+
} catch { /* No retained evidence means no qualification grant. */ }
|
|
126
|
+
}
|
|
127
|
+
const qualified = evidenceValid && receipt?.schema_id === AGY_NATIVE_INSPECTION_CONTRACT && receipt.scope === 'required_execution' && receipt.binary_sha256 === binary.sha256
|
|
128
|
+
&& receipt.client_contract_sha256 === clientContract
|
|
129
|
+
&& ['self', 'empty_self'].includes(receipt.root_relation)
|
|
130
|
+
&& AGY_NATIVE_QUALIFICATION_CASES.every(name => receipt.checks?.[name] === true)
|
|
131
|
+
&& typeof receipt.evidence_sha256 === 'string' && /^[a-f0-9]{64}$/.test(receipt.evidence_sha256);
|
|
132
|
+
return { ...binary, client_contract_sha256: clientContract, qualified, rootRelation: qualified ? receipt.root_relation : null };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Only the log for this physical launch may nominate an endpoint. Its live
|
|
136
|
+
* listener must be this provider or an OS descendant, with retained PID birth. */
|
|
137
|
+
export async function ownedAgyEndpoint({ logFile, providerPid, providerBirth, snapshot = processSnapshot, listeners = async port => {
|
|
138
|
+
const { stdout } = await run('lsof', ['-nP', `-iTCP:${port}`, '-sTCP:LISTEN', '-Fpn'], { timeout: 5_000, maxBuffer: 64 * 1024 });
|
|
139
|
+
let pid; return stdout.trim().split('\n').flatMap(line => line.startsWith('p') ? (pid = Number(line.slice(1)), []) : line.startsWith('n') ? [{ pid, address: line.slice(1) }] : []);
|
|
140
|
+
} }) {
|
|
141
|
+
if (!path.isAbsolute(logFile ?? '') || !Number.isSafeInteger(providerPid) || !providerBirth) throw invalid('provider_binding_missing');
|
|
142
|
+
// Startup prefix only: later log growth is not an expiry or model progress.
|
|
143
|
+
const { bytes, stat } = await readRegular(logFile, 1024 * 1024, true), text = bytes.toString('utf8');
|
|
144
|
+
const ports = [...new Set([...text.matchAll(/Language server listening on random port at (\d+) for HTTP(?:\r?$)/gm)].map(match => Number(match[1])))];
|
|
145
|
+
if (ports.length !== 1 || ports[0] < 1 || ports[0] > 65535) throw invalid('native_endpoint_missing_or_ambiguous');
|
|
146
|
+
const records = await snapshot(), provider = records.find(record => record.pid === providerPid);
|
|
147
|
+
if (!provider || provider.started !== providerBirth || provider.zombie) throw invalid('provider_birth_changed');
|
|
148
|
+
const allowed = new Set([providerPid]); let changed;
|
|
149
|
+
do { changed = false; for (const record of records) if (allowed.has(record.ppid) && !allowed.has(record.pid) && !record.zombie) { allowed.add(record.pid); changed = true; } } while (changed);
|
|
150
|
+
const owners = await listeners(ports[0]);
|
|
151
|
+
if (!owners.length || owners.some(owner => !allowed.has(owner.pid) || !/^(?:127\.0\.0\.1|\[::1\]|localhost):\d+$/.test(owner.address))) throw invalid('native_endpoint_foreign_or_nonloopback');
|
|
152
|
+
const births = owners.map(owner => ({ pid: owner.pid, started: records.find(record => record.pid === owner.pid)?.started }));
|
|
153
|
+
if (births.some(owner => !owner.started)) throw invalid('listener_birth_missing');
|
|
154
|
+
return { target: `127.0.0.1:${ports[0]}`, provider_pid: providerPid, provider_birth: providerBirth, listeners: births, log_identity: `${stat.dev}:${stat.ino}` };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** No model request: metadata read followed by the native idle barrier. The
|
|
158
|
+
* caller owns freshness fencing and qualification; a raw reply is not a grant. */
|
|
159
|
+
async function agyNativeRequest({ endpoint, sessionId, cwd, remainingMs, signal, rootRelation = null, onMetadata = null, csrfToken, rootSessionId = sessionId, parentSessionId = '' }, action = 'wait', ownedSessions = []) {
|
|
160
|
+
if (!/^127\.0\.0\.1:\d+$/.test(endpoint?.target ?? '') || Number(endpoint.target.split(':')[1]) < 1 || Number(endpoint.target.split(':')[1]) > 65535) throw invalid('native_endpoint_nonloopback');
|
|
161
|
+
if (!Number.isFinite(remainingMs) || remainingMs < 1) throw invalid('observation_budget_exhausted');
|
|
162
|
+
const deadline = Date.now() + remainingMs;
|
|
163
|
+
const client = new Client(endpoint.target, grpc.credentials.createInsecure(), { 'grpc.max_receive_message_length': 1024 * 1024, 'grpc.max_send_message_length': 64 * 1024, 'grpc.enable_retries': 0 });
|
|
164
|
+
let active;
|
|
165
|
+
const headers = new grpc.Metadata();
|
|
166
|
+
if (typeof csrfToken === 'string' && csrfToken) headers.set('x-codeium-csrf-token', csrfToken);
|
|
167
|
+
const abort = () => active?.cancel();
|
|
168
|
+
const unary = (name, request) => new Promise((resolve, reject) => {
|
|
169
|
+
if (signal?.aborted) return reject(invalid('inspection_cancelled'));
|
|
170
|
+
active = client[name](request, headers, { deadline }, (error, value) => error ? reject(invalid(`grpc_${error.code ?? 'unknown'}`)) : value ? resolve(value) : reject(invalid('grpc_message_missing')));
|
|
171
|
+
});
|
|
172
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
173
|
+
try {
|
|
174
|
+
const metadata = await unary('metadata', { conversationId: sessionId });
|
|
175
|
+
onMetadata?.(metadata);
|
|
176
|
+
const relation = metadata.metadata;
|
|
177
|
+
const rootMatches = relation?.rootConversationId === rootSessionId || rootRelation === 'empty_self' && rootSessionId === sessionId && relation?.rootConversationId === '';
|
|
178
|
+
const workspaces = await Promise.all((relation?.workspaceUris ?? []).map(async uri => { try { return await realpath(fileURLToPath(uri)); } catch { return null; } }));
|
|
179
|
+
if (!relation || relation.parentConversationId !== (action === 'state' ? parentSessionId : '') || action !== 'state' && rootSessionId !== sessionId || !rootMatches || !metadata.trajectoryId || !workspaces.includes(await realpath(cwd))) throw invalid('native_metadata_binding_unproven');
|
|
180
|
+
if (action === 'state') {
|
|
181
|
+
return await new Promise((resolve, reject) => {
|
|
182
|
+
if (signal?.aborted) return reject(invalid('inspection_cancelled'));
|
|
183
|
+
active = client.state({ conversationId: sessionId, subscriberId: randomUUID() }, headers, { deadline });
|
|
184
|
+
let done = false;
|
|
185
|
+
active.on('data', value => {
|
|
186
|
+
if (done) return;
|
|
187
|
+
const update = value.update;
|
|
188
|
+
// Every subscription begins with a native full snapshot. Never fold
|
|
189
|
+
// deltas from a previous subscription into a new incarnation.
|
|
190
|
+
if (!update || update.conversationId !== sessionId || update.trajectoryId !== metadata.trajectoryId) return;
|
|
191
|
+
const snapshot = JSON.parse(JSON.stringify(update, (key, value) => key.startsWith('$') ? undefined : value));
|
|
192
|
+
done = true; resolve({ ...snapshot, observed_at: new Date().toISOString(), method: agyNativeService.state.path }); active.cancel();
|
|
193
|
+
});
|
|
194
|
+
active.on('error', error => { if (!done) { done = true; reject(invalid(`grpc_${error.code ?? 'unknown'}`)); } });
|
|
195
|
+
active.on('end', () => { if (!done) { done = true; reject(invalid('native_state_snapshot_missing')); } });
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (action === 'stop') {
|
|
199
|
+
const reply = await unary('stop', { conversationId: sessionId });
|
|
200
|
+
const stopped = reply.stoppedConversationIds, owned = new Set([sessionId, ...ownedSessions]);
|
|
201
|
+
if (!Array.isArray(stopped) || !stopped.length || new Set(stopped).size !== stopped.length
|
|
202
|
+
|| ![...owned].every(id => stopped.includes(id)) || stopped.some(id => !owned.has(id))) {
|
|
203
|
+
const error = invalid('native_stopped_tree_binding_unproven'); error.details.stopped_session_ids = stopped ?? null; throw error;
|
|
204
|
+
}
|
|
205
|
+
return { stopped_session_ids: stopped, trajectory_id: metadata.trajectoryId, method: agyNativeService.stop.path };
|
|
206
|
+
}
|
|
207
|
+
const reply = await unary('wait', { conversationId: sessionId, inactivityTimeoutSeconds: Math.max(1, Math.min(2147483647, Math.ceil((deadline - Date.now()) / 1000))), returnOnExecutorError: false });
|
|
208
|
+
return { idle: reply.timedOut === false, reason: reply.timedOut ? 'native_wait_timed_out' : 'native_wait_fully_idle', method: agyNativeService.wait.path, trajectory_id: metadata.trajectoryId };
|
|
209
|
+
} finally { signal?.removeEventListener('abort', abort); client.close(); }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export const inspectAgyNative = input => agyNativeRequest(input);
|
|
213
|
+
/** Read-only per-node native state; no execution/readiness grant. */
|
|
214
|
+
export const inspectAgyNativeState = input => agyNativeRequest(input, 'state');
|
|
215
|
+
/** Scoped control, not an idle/physical grant. Caller must verify retirement. */
|
|
216
|
+
export const stopAgyNativeTree = (input, ownedSessions = []) => agyNativeRequest(input, 'stop', ownedSessions);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// @generated by protoc-gen-es v2.16.0 with parameter "target=js"
|
|
2
|
+
// @generated from file agy-native.proto (package exa.language_server_pb, syntax proto3)
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
|
|
5
|
+
import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Describes the file agy-native.proto.
|
|
9
|
+
*/
|
|
10
|
+
export const file_agy_native = /*@__PURE__*/
|
|
11
|
+
fileDesc("ChBhZ3ktbmF0aXZlLnByb3RvEhZleGEubGFuZ3VhZ2Vfc2VydmVyX3BiIoQBCiNXYWl0Rm9yQ29udmVyc2F0aW9uRnVsbHlJZGxlUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSIgoaaW5hY3Rpdml0eV90aW1lb3V0X3NlY29uZHMYAiABKAUSIAoYcmV0dXJuX29uX2V4ZWN1dG9yX2Vycm9yGAQgASgIIjkKJFdhaXRGb3JDb252ZXJzYXRpb25GdWxseUlkbGVSZXNwb25zZRIRCgl0aW1lZF9vdXQYASABKAgiOQoeR2V0Q29udmVyc2F0aW9uTWV0YWRhdGFSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCSJsChRDb252ZXJzYXRpb25NZXRhZGF0YRIeChZwYXJlbnRfY29udmVyc2F0aW9uX2lkGAUgASgJEhwKFHJvb3RfY29udmVyc2F0aW9uX2lkGAYgASgJEhYKDndvcmtzcGFjZV91cmlzGAcgAygJIosBCh9HZXRDb252ZXJzYXRpb25NZXRhZGF0YVJlc3BvbnNlEj4KCG1ldGFkYXRhGAEgASgLMiwuZXhhLmxhbmd1YWdlX3NlcnZlcl9wYi5Db252ZXJzYXRpb25NZXRhZGF0YRIRCgludW1fc3RlcHMYAiABKA0SFQoNdHJhamVjdG9yeV9pZBgDIAEoCSI2ChtGb3JjZVN0b3BDYXNjYWRlVHJlZVJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJIkAKHEZvcmNlU3RvcENhc2NhZGVUcmVlUmVzcG9uc2USIAoYc3RvcHBlZF9jb252ZXJzYXRpb25faWRzGAEgAygJIlAKHlN0cmVhbUFnZW50U3RhdGVVcGRhdGVzUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSFQoNc3Vic2NyaWJlcl9pZBgCIAEoCSJOCgxBZ2VudE1lc3NhZ2USCgoCaWQYASABKAkSEQoJcmVjaXBpZW50GAIgASgJEg4KBnNlbmRlchgDIAEoCRIPCgdjb250ZW50GAQgASgJIokBChpQZW5kaW5nQWdlbnRNZXNzYWdlc1VwZGF0ZRIPCgdpbmRpY2VzGAEgAygNEkQKFnBlbmRpbmdfYWdlbnRfbWVzc2FnZXMYAiADKAsyJC5leGEubGFuZ3VhZ2Vfc2VydmVyX3BiLkFnZW50TWVzc2FnZRIUCgx0b3RhbF9sZW5ndGgYAyABKA0iagoMVGFza1NuYXBzaG90Eg8KB3Rhc2tfaWQYASABKAkSEQoJdG9vbF9uYW1lGAIgASgJEhIKCnN0ZXBfaW5kZXgYBiABKA0SDwoHbG9nX3VyaRgHIAEoCRIRCglpc19kYWVtb24YCCABKAgiTQoOQmFja2dyb3VuZFRhc2sSOwoNdGFza19zbmFwc2hvdBgBIAEoCzIkLmV4YS5sYW5ndWFnZV9zZXJ2ZXJfcGIuVGFza1NuYXBzaG90IoABChVCYWNrZ3JvdW5kVGFza3NVcGRhdGUSDwoHaW5kaWNlcxgBIAMoDRJAChBiYWNrZ3JvdW5kX3Rhc2tzGAIgAygLMiYuZXhhLmxhbmd1YWdlX3NlcnZlcl9wYi5CYWNrZ3JvdW5kVGFzaxIUCgx0b3RhbF9sZW5ndGgYAyABKA0i5wIKEEFnZW50U3RhdGVVcGRhdGUSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEhUKDXRyYWplY3RvcnlfaWQYAiABKAkSDgoGc3RhdHVzGAMgASgFEhkKEWV4ZWN1dGFibGVfc3RhdHVzGAQgASgFEhwKFGV4ZWN1dG9yX2xvb3Bfc3RhdHVzGAUgASgFEhsKE2hhc19hY3RpdmVfY2hpbGRyZW4YDiABKAgSTgoXYmFja2dyb3VuZF90YXNrc191cGRhdGUYESABKAsyLS5leGEubGFuZ3VhZ2Vfc2VydmVyX3BiLkJhY2tncm91bmRUYXNrc1VwZGF0ZRISCgpmdWxseV9pZGxlGBIgASgIElkKHXBlbmRpbmdfYWdlbnRfbWVzc2FnZXNfdXBkYXRlGBQgASgLMjIuZXhhLmxhbmd1YWdlX3NlcnZlcl9wYi5QZW5kaW5nQWdlbnRNZXNzYWdlc1VwZGF0ZSJbCh9TdHJlYW1BZ2VudFN0YXRlVXBkYXRlc1Jlc3BvbnNlEjgKBnVwZGF0ZRgBIAEoCzIoLmV4YS5sYW5ndWFnZV9zZXJ2ZXJfcGIuQWdlbnRTdGF0ZVVwZGF0ZWIGcHJvdG8z");
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Describes the message exa.language_server_pb.WaitForConversationFullyIdleRequest.
|
|
15
|
+
* Use `create(WaitForConversationFullyIdleRequestSchema)` to create a new message.
|
|
16
|
+
*/
|
|
17
|
+
export const WaitForConversationFullyIdleRequestSchema = /*@__PURE__*/
|
|
18
|
+
messageDesc(file_agy_native, 0);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Describes the message exa.language_server_pb.WaitForConversationFullyIdleResponse.
|
|
22
|
+
* Use `create(WaitForConversationFullyIdleResponseSchema)` to create a new message.
|
|
23
|
+
*/
|
|
24
|
+
export const WaitForConversationFullyIdleResponseSchema = /*@__PURE__*/
|
|
25
|
+
messageDesc(file_agy_native, 1);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Describes the message exa.language_server_pb.GetConversationMetadataRequest.
|
|
29
|
+
* Use `create(GetConversationMetadataRequestSchema)` to create a new message.
|
|
30
|
+
*/
|
|
31
|
+
export const GetConversationMetadataRequestSchema = /*@__PURE__*/
|
|
32
|
+
messageDesc(file_agy_native, 2);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Describes the message exa.language_server_pb.ConversationMetadata.
|
|
36
|
+
* Use `create(ConversationMetadataSchema)` to create a new message.
|
|
37
|
+
*/
|
|
38
|
+
export const ConversationMetadataSchema = /*@__PURE__*/
|
|
39
|
+
messageDesc(file_agy_native, 3);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Describes the message exa.language_server_pb.GetConversationMetadataResponse.
|
|
43
|
+
* Use `create(GetConversationMetadataResponseSchema)` to create a new message.
|
|
44
|
+
*/
|
|
45
|
+
export const GetConversationMetadataResponseSchema = /*@__PURE__*/
|
|
46
|
+
messageDesc(file_agy_native, 4);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Describes the message exa.language_server_pb.ForceStopCascadeTreeRequest.
|
|
50
|
+
* Use `create(ForceStopCascadeTreeRequestSchema)` to create a new message.
|
|
51
|
+
*/
|
|
52
|
+
export const ForceStopCascadeTreeRequestSchema = /*@__PURE__*/
|
|
53
|
+
messageDesc(file_agy_native, 5);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Describes the message exa.language_server_pb.ForceStopCascadeTreeResponse.
|
|
57
|
+
* Use `create(ForceStopCascadeTreeResponseSchema)` to create a new message.
|
|
58
|
+
*/
|
|
59
|
+
export const ForceStopCascadeTreeResponseSchema = /*@__PURE__*/
|
|
60
|
+
messageDesc(file_agy_native, 6);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Describes the message exa.language_server_pb.StreamAgentStateUpdatesRequest.
|
|
64
|
+
* Use `create(StreamAgentStateUpdatesRequestSchema)` to create a new message.
|
|
65
|
+
*/
|
|
66
|
+
export const StreamAgentStateUpdatesRequestSchema = /*@__PURE__*/
|
|
67
|
+
messageDesc(file_agy_native, 7);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Describes the message exa.language_server_pb.AgentMessage.
|
|
71
|
+
* Use `create(AgentMessageSchema)` to create a new message.
|
|
72
|
+
*/
|
|
73
|
+
export const AgentMessageSchema = /*@__PURE__*/
|
|
74
|
+
messageDesc(file_agy_native, 8);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Describes the message exa.language_server_pb.PendingAgentMessagesUpdate.
|
|
78
|
+
* Use `create(PendingAgentMessagesUpdateSchema)` to create a new message.
|
|
79
|
+
*/
|
|
80
|
+
export const PendingAgentMessagesUpdateSchema = /*@__PURE__*/
|
|
81
|
+
messageDesc(file_agy_native, 9);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Describes the message exa.language_server_pb.TaskSnapshot.
|
|
85
|
+
* Use `create(TaskSnapshotSchema)` to create a new message.
|
|
86
|
+
*/
|
|
87
|
+
export const TaskSnapshotSchema = /*@__PURE__*/
|
|
88
|
+
messageDesc(file_agy_native, 10);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Describes the message exa.language_server_pb.BackgroundTask.
|
|
92
|
+
* Use `create(BackgroundTaskSchema)` to create a new message.
|
|
93
|
+
*/
|
|
94
|
+
export const BackgroundTaskSchema = /*@__PURE__*/
|
|
95
|
+
messageDesc(file_agy_native, 11);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Describes the message exa.language_server_pb.BackgroundTasksUpdate.
|
|
99
|
+
* Use `create(BackgroundTasksUpdateSchema)` to create a new message.
|
|
100
|
+
*/
|
|
101
|
+
export const BackgroundTasksUpdateSchema = /*@__PURE__*/
|
|
102
|
+
messageDesc(file_agy_native, 12);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Describes the message exa.language_server_pb.AgentStateUpdate.
|
|
106
|
+
* Use `create(AgentStateUpdateSchema)` to create a new message.
|
|
107
|
+
*/
|
|
108
|
+
export const AgentStateUpdateSchema = /*@__PURE__*/
|
|
109
|
+
messageDesc(file_agy_native, 13);
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Describes the message exa.language_server_pb.StreamAgentStateUpdatesResponse.
|
|
113
|
+
* Use `create(StreamAgentStateUpdatesResponseSchema)` to create a new message.
|
|
114
|
+
*/
|
|
115
|
+
export const StreamAgentStateUpdatesResponseSchema = /*@__PURE__*/
|
|
116
|
+
messageDesc(file_agy_native, 14);
|
|
117
|
+
|
|
@@ -5,7 +5,9 @@ import { randomUUID } from "node:crypto";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { hashJson, writeJsonAtomic } from "./runner-events.mjs";
|
|
7
7
|
import { errorRecord, isObservationLoss, isProvenShellCorrection, reportedError } from "./operation-errors.mjs";
|
|
8
|
-
import { assertDaemonOwnership, budgetObservationScope, releaseBudgetObservation, completeBudgetOperation } from "./managed-daemon.mjs";
|
|
8
|
+
import { assertDaemonOwnership, budgetObservationScope, releaseBudgetObservation, completeBudgetOperation, stopProcessGroup } from "./managed-daemon.mjs";
|
|
9
|
+
import { processSnapshot } from "./process-snapshot.mjs";
|
|
10
|
+
import { NATIVE_SHUTDOWN_CONTRACT, NATIVE_SHUTDOWN_PHASES } from "./native-children.mjs";
|
|
9
11
|
import { readModelObservations, modelAttribution } from "./model-observations.mjs";
|
|
10
12
|
import { publishOperationProgress, publishOperationWaiting, withOperationProgress } from './operation-progress.mjs';
|
|
11
13
|
import { prepareDaemonRequest } from "./native-request-proof.mjs";
|
|
@@ -16,6 +18,20 @@ const dispatchGenerations = new Map();
|
|
|
16
18
|
const shutdowns = new Map();
|
|
17
19
|
const settlements = new Map();
|
|
18
20
|
|
|
21
|
+
// Configuration locations only: credentials and arbitrary process env are not
|
|
22
|
+
// replay inputs. The browser cache is additive for historical five-key launches.
|
|
23
|
+
const nativeLaunchEnvironmentKeys = ["CODEX_HOME", "DD_FLOW_CONFIG_HOME", "DD_FLOW_HOME", "DD_FLOW_BIN", "DD_FLOW_RESOURCE_HOME"];
|
|
24
|
+
export function validateNativeLaunchEnvironment(value) {
|
|
25
|
+
const cache = value?.PLAYWRIGHT_BROWSERS_PATH;
|
|
26
|
+
if (!value || typeof value !== "object" || Array.isArray(value)
|
|
27
|
+
|| Object.keys(value).some(key => ![...nativeLaunchEnvironmentKeys, "PLAYWRIGHT_BROWSERS_PATH"].includes(key))
|
|
28
|
+
|| nativeLaunchEnvironmentKeys.some(key => value[key] !== null && typeof value[key] !== "string")
|
|
29
|
+
|| Object.hasOwn(value, "PLAYWRIGHT_BROWSERS_PATH") && cache !== null
|
|
30
|
+
&& (typeof cache !== "string" || !cache || cache.length > 4096 || cache.includes("\0") || !path.isAbsolute(cache)))
|
|
31
|
+
throw Object.assign(new Error("Native launch requires explicit configuration locations and an absolute browser cache"), { code: "runtime_scope_native_launch_required" });
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
|
|
19
35
|
// Progress is not outcome authority. Once native effect is retained, a failed
|
|
20
36
|
// diagnostic write must keep its identity and prohibit redispatch.
|
|
21
37
|
async function publishRetainedProgress(root, id, phase, result, metadata) {
|
|
@@ -41,28 +57,84 @@ export function commitDaemonShutdown(root) {
|
|
|
41
57
|
fence.committed = true; shutdowns.set(root, fence);
|
|
42
58
|
}
|
|
43
59
|
|
|
60
|
+
function ownedSnapshot(snapshot, rootPid) {
|
|
61
|
+
const owned = new Set([rootPid]); let size;
|
|
62
|
+
do { size = owned.size; for (const item of snapshot) if (owned.has(item.ppid)) owned.add(item.pid); } while (size !== owned.size);
|
|
63
|
+
return snapshot.filter(item => item.pid !== rootPid && owned.has(item.pid) && !item.zombie);
|
|
64
|
+
}
|
|
65
|
+
const mergeProcesses = (...snapshots) => [...new Map(snapshots.flat().map(item => [`${item.pid}:${item.started}`, item])).values()];
|
|
66
|
+
|
|
44
67
|
/** The same incarnation may retry remaining cleanup, never a native Turn. */
|
|
45
68
|
export async function completeDaemonShutdown(root, state, persist, prepare, phases) {
|
|
46
69
|
const daemonId = state.daemon_id ?? state.config?.daemonId;
|
|
47
70
|
let shutdown = state.shutdown;
|
|
48
|
-
if (
|
|
49
|
-
shutdown = {
|
|
71
|
+
if (shutdown && shutdown.schema_id !== NATIVE_SHUTDOWN_CONTRACT) throw Object.assign(new Error("shutdown contract mismatch; retain historical owner evidence"), { code: "native_contract_mismatch" });
|
|
72
|
+
if (!shutdown || shutdown.daemon_id !== daemonId) shutdown = { schema_id: NATIVE_SHUTDOWN_CONTRACT, daemon_id: daemonId, generation: dispatchGenerations.get(root) ?? 0, phases: {} };
|
|
73
|
+
shutdown = { ...shutdown, required_phases: [...new Set([...NATIVE_SHUTDOWN_PHASES, ...phases.map(([phase]) => phase)])] };
|
|
50
74
|
try {
|
|
51
|
-
if (!shutdown.phases.
|
|
75
|
+
if (!shutdown.phases.execution) {
|
|
76
|
+
// Some native adapters close their provider inside prepare(). Observe
|
|
77
|
+
// ownership first, before that close can reparent a support process.
|
|
78
|
+
const before = ownedSnapshot(await processSnapshot(), state.pid);
|
|
79
|
+
shutdown = { ...shutdown, generation: dispatchGenerations.get(root) ?? shutdown.generation,
|
|
80
|
+
owned_processes: mergeProcesses(shutdown.owned_processes ?? [], before), inventory_complete: state.pid === process.pid };
|
|
81
|
+
await persist({ shutdown });
|
|
52
82
|
const result = await prepare();
|
|
53
|
-
shutdown = { ...shutdown, phases: { tree: true }, tree_observed_at: new Date().toISOString(), result: result ?? { clean: true } };
|
|
54
83
|
commitDaemonShutdown(root);
|
|
55
|
-
|
|
84
|
+
// Compute ancestry within each snapshot, not across recycled parent PIDs.
|
|
85
|
+
const resources = mergeProcesses(shutdown.owned_processes, ownedSnapshot(await processSnapshot(), state.pid));
|
|
86
|
+
shutdown = { ...shutdown, phases: { execution: true }, owned_processes: resources,
|
|
87
|
+
inventory_complete: state.pid === process.pid, tree_observed_at: new Date().toISOString(), result: result ?? {} };
|
|
88
|
+
await persist({ shutdown, shutdown_state: "stopping" });
|
|
56
89
|
}
|
|
57
90
|
commitDaemonShutdown(root);
|
|
58
91
|
for (const [phase, action] of phases) {
|
|
92
|
+
if (phase === "daemon_resource" && !shutdown.phases.owned_resources) {
|
|
93
|
+
if (!shutdown.inventory_complete) throw Object.assign(new Error("owned resource coverage is unknown"), { code: "native_resources_unproven" });
|
|
94
|
+
for (const retained of shutdown.owned_processes ?? []) {
|
|
95
|
+
const current = (await processSnapshot()).find(item => item.pid === retained.pid);
|
|
96
|
+
if (!current || current.started !== retained.started || current.zombie) continue;
|
|
97
|
+
await stopProcessGroup({ pid: retained.pid, started: retained.started }, 1000, { detached: false });
|
|
98
|
+
}
|
|
99
|
+
const remaining = await processSnapshot();
|
|
100
|
+
if ((shutdown.owned_processes ?? []).some(retained => remaining.some(item => item.pid === retained.pid && item.started === retained.started && !item.zombie))) throw Object.assign(new Error("owned resources remain live"), { code: "native_resources_unproven" });
|
|
101
|
+
const nativeResources = shutdown.result?.native_resources;
|
|
102
|
+
const resourceGap = nativeResources?.entries?.some(resource => resource.state !== "stopped"
|
|
103
|
+
&& !(resource.classification === "required_work" && shutdown.result?.native_settled === true && shutdown.result?.settled === true)
|
|
104
|
+
&& !(shutdown.owned_processes ?? []).some(item => resource.pid === item.pid && resource.started === item.started));
|
|
105
|
+
shutdown = { ...shutdown, phases: { ...shutdown.phases, owned_resources: !resourceGap },
|
|
106
|
+
resource_retirement: { confirmed: !resourceGap, reason: resourceGap ? "native_resource_identity_unproven" : "owned_processes_retired" } };
|
|
107
|
+
await persist({ shutdown });
|
|
108
|
+
}
|
|
59
109
|
if (shutdown.phases[phase]) continue;
|
|
60
110
|
await action();
|
|
61
111
|
shutdown = { ...shutdown, phases: { ...shutdown.phases, [phase]: true } };
|
|
62
112
|
await persist({ shutdown });
|
|
63
113
|
}
|
|
64
|
-
const
|
|
65
|
-
|
|
114
|
+
const publishedRevision = state.native_execution_revision;
|
|
115
|
+
const sameRevision = publishedRevision === undefined || publishedRevision === shutdown.result?.native_settlement?.revision;
|
|
116
|
+
const nativeSettled = sameRevision && state.active_tree === false && !state.unclaimed_activity && shutdown.result?.settled === true && shutdown.result?.native_settled === true && shutdown.result?.native_settlement?.confirmed !== false;
|
|
117
|
+
const physicalStopped = shutdown.required_phases.every(phase => shutdown.phases[phase] === true);
|
|
118
|
+
// Cleanup retries/joining controls advance the control fence, not the old
|
|
119
|
+
// native Turn. Publish the current control generation with retained phases.
|
|
120
|
+
shutdown = { ...shutdown, generation: dispatchGenerations.get(root) ?? shutdown.generation };
|
|
121
|
+
const { result: ignored, ...proof } = shutdown; void ignored;
|
|
122
|
+
let result = { ...shutdown.result, daemon_id: daemonId, stopped: true, physical_stopped: physicalStopped,
|
|
123
|
+
settled: nativeSettled, native_settled: nativeSettled, clean: shutdown.result?.clean !== false && nativeSettled && physicalStopped,
|
|
124
|
+
resource_retirement: shutdown.resource_retirement,
|
|
125
|
+
shutdown_contract: NATIVE_SHUTDOWN_CONTRACT, shutdown_proof: proof, _shutdown: true };
|
|
126
|
+
shutdown = { ...shutdown, result };
|
|
127
|
+
// prepare() owns execution quiescence. A stale publication must not clear
|
|
128
|
+
// activity that arrives while its durable write is in flight.
|
|
129
|
+
await persist({ shutdown, shutdown_state: result.clean === true ? "clean" : "unclean", stopped_at: new Date().toISOString(), cleanup_error: null });
|
|
130
|
+
if (nativeSettled && (state.native_execution_revision !== publishedRevision || state.active_tree !== false || state.unclaimed_activity)) {
|
|
131
|
+
shutdown = { ...shutdown, phases: { ...shutdown.phases, execution: false } };
|
|
132
|
+
result = { ...result, clean: false, settled: false, native_settled: false,
|
|
133
|
+
native_settlement: { ...result.native_settlement, confirmed: false, reason: "late_activity_during_shutdown_publication" },
|
|
134
|
+
shutdown_proof: { ...proof, phases: shutdown.phases } };
|
|
135
|
+
shutdown = { ...shutdown, result };
|
|
136
|
+
await persist({ shutdown, shutdown_state: "unclean" });
|
|
137
|
+
}
|
|
66
138
|
return result;
|
|
67
139
|
} catch (error) {
|
|
68
140
|
if (error.code === "tree_not_settled" && !shutdowns.get(root)?.committed) throw error;
|
|
@@ -255,7 +327,9 @@ export async function durableDaemonDispatch(root, request, action, observe) {
|
|
|
255
327
|
const control = ["daemon.stop", "session.cancel"].includes(request.operation);
|
|
256
328
|
if (!productive.has(request.operation) && !control) return await action();
|
|
257
329
|
const generation = dispatchGenerations.get(root) ?? 0;
|
|
258
|
-
|
|
330
|
+
// Once retirement is committed there is no productive dispatch to cancel.
|
|
331
|
+
// Retrying/joining cleanup must not stale its own durable generation proof.
|
|
332
|
+
if (control && !(request.operation === "daemon.stop" && shutdowns.get(root)?.committed)) { dispatchGenerations.set(root, generation + 1); const state = await read(path.join(root,'daemon.json')); await writeJsonAtomic(path.join(root,'operation-fence.json'), { generation: generation + 1, daemon_id: state?.daemon_id ?? state?.config?.daemonId ?? null }); }
|
|
259
333
|
const assertCurrent = () => { assertDaemonDispatch(root); if (generation !== (dispatchGenerations.get(root) ?? 0)) throw Object.assign(new Error("control cancelled a queued productive dispatch"), { code: "operation_cancelled" }); };
|
|
260
334
|
const stop = request.operation === "daemon.stop" ? (shutdowns.get(root) ?? { pending: 0, committed: false }) : null;
|
|
261
335
|
if (stop) { stop.pending++; shutdowns.set(root, stop); }
|
|
@@ -301,7 +375,7 @@ export async function durableDaemonStart(options, action) {
|
|
|
301
375
|
// Keep only configuration locations, never inherited credentials. The owned
|
|
302
376
|
// scope can reuse this exact adapter launch after retiring the old daemon.
|
|
303
377
|
const launch = { schema_id: "dd-flow/native-launch@1", executable: options.entryPath ?? null, args: process.argv.slice(2), cwd: process.cwd(), runtime_owner: process.env.DD_FLOW_RUNTIME_OWNER ?? null,
|
|
304
|
-
env: Object.fromEntries([
|
|
378
|
+
env: validateNativeLaunchEnvironment(Object.fromEntries([...nativeLaunchEnvironmentKeys, ...(process.env.PLAYWRIGHT_BROWSERS_PATH === undefined ? [] : ["PLAYWRIGHT_BROWSERS_PATH"])].map(key => [key, process.env[key] ?? null]))) };
|
|
305
379
|
const request = prepareDaemonRequest({ id: process.env.DD_EVAL_OPERATION_ID ?? randomUUID(), operation: "daemon.start", params: { ...options, runtimeOwner: process.env.DD_FLOW_RUNTIME_OWNER ?? null, resourceHome: process.env.DD_FLOW_RESOURCE_HOME ?? null, launch } });
|
|
306
380
|
return await journalDispatch(root, request, () => action(options));
|
|
307
381
|
}
|