@deksden-com/dd-flow-cli 0.9.0-beta.143 → 0.9.0-beta.145
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 +65 -0
- package/README.md +13 -8
- package/dist/build-info.json +7 -7
- package/dist/cli/command-inputs.js +9 -3
- package/dist/cli/command-metadata.js +33 -14
- package/dist/cli/help.js +32 -1
- package/dist/cli/hook-ingress.js +27 -3
- package/dist/cli/memory-flow-admission.js +7 -3
- package/dist/cli/memory-init-invocation.js +10 -3
- package/dist/cli/memory-init.js +74 -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 +70 -32
- package/dist/cli/setup.js +2 -1
- package/dist/cli.js +12 -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/dd-agy-daemon.mjs +62 -20
- package/dist/harness-runtime/lib/dd-agy.mjs +17 -3
- package/dist/harness-runtime/lib/dd-droid.mjs +1 -1
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +70 -13
- package/dist/harness-runtime/lib/dd-grok.mjs +6 -3
- 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/entity-references.d.mts +2 -0
- package/dist/harness-runtime/lib/entity-references.mjs +9 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +13 -6
- package/dist/harness-runtime/lib/native-hook-command.mjs +4 -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 +4 -2
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +19 -6
- package/dist/harness-runtime/lib/runner-events.mjs +3 -1
- package/dist/harness-runtime/lib/setup-progress.mjs +8 -6
- 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/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/cli-operation-classifier.js +1 -1
- package/dist/services/cli-upgrade-allowlist.js +1 -1
- package/dist/services/code-checks.js +11 -4
- package/dist/services/compatibility-preflight.js +5 -2
- package/dist/services/controller-fanout.js +21 -4
- package/dist/services/controller-memory-task.js +45 -0
- package/dist/services/controller-memory-worker.js +48 -0
- package/dist/services/dashboard.js +2 -2
- package/dist/services/descriptor-run-start.js +26 -19
- package/dist/services/descriptor-stage-coordinator.js +30 -10
- package/dist/services/descriptor-stage-start.js +40 -4
- package/dist/services/engines.js +64 -14
- package/dist/services/eval-snapshots.js +104 -93
- package/dist/services/external-work-launch.js +5 -3
- package/dist/services/flow-catalog.js +8 -4
- package/dist/services/frozen-engine.js +113 -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 +2 -13
- package/dist/services/hybrid-digest.js +14 -0
- package/dist/services/installed-flow-source.js +25 -4
- package/dist/services/lifecycle-invocations.js +33 -5
- 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 +28 -2
- package/dist/services/memory-init-controller-start.js +19 -8
- package/dist/services/memory-init-git-policy.js +50 -21
- package/dist/services/memory-init-lint-capability.js +21 -3
- package/dist/services/memory-init-native-capacity.js +13 -0
- package/dist/services/memory-init-preflight.js +11 -1
- 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-layer.js +68 -4
- 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-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 +77 -23
- 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/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 +74 -9
- package/dist/services/recovery-snapshot-database.js +32 -4
- 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-controller-process.js +2 -6
- package/dist/services/run-controller-recovery.js +6 -1
- package/dist/services/run-controller.js +131 -80
- 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/runtime-scope-worker.js +46 -19
- package/dist/services/sessions.js +3 -24
- package/dist/services/setup.js +63 -13
- package/dist/services/stage-handlers.js +9 -7
- package/dist/services/stage-pause.js +108 -44
- package/dist/services/version-status.js +1 -1
- package/dist/services/vnext-fanout.js +21 -9
- package/dist/services/vnext-plan-review.js +3 -5
- package/dist/services/work-registry.js +80 -33
- 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 +8 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AppError, errorRecord } from "../shared/errors.js";
|
|
2
2
|
import { parseCommandArgs } from "../services/lifecycle-command.js";
|
|
3
3
|
import { channel } from "node:diagnostics_channel";
|
|
4
|
+
import { nativeHookNonParticipation } from "./native-hook-preparation.js";
|
|
4
5
|
/** These ingress commands consume a prepared store; they never prepare it. */
|
|
5
6
|
export function isNativeHookIngress(args) {
|
|
6
7
|
return args[2] === "handle" && ((args[1] === "event" && ["agy", "grok", "droid", "opencode", "zcode"].includes(args[0] ?? ""))
|
|
@@ -8,7 +9,8 @@ export function isNativeHookIngress(args) {
|
|
|
8
9
|
}
|
|
9
10
|
export async function dispatchNativeHook(args, stdin, env) {
|
|
10
11
|
const started = performance.now();
|
|
11
|
-
const deadline = env.DD_FLOW_HOOK_DEADLINE_AT ? Number(env.DD_FLOW_HOOK_DEADLINE_AT) :
|
|
12
|
+
const deadline = env.DD_FLOW_HOOK_DEADLINE_AT ? Number(env.DD_FLOW_HOOK_DEADLINE_AT) : Date.now() + 25_000;
|
|
13
|
+
env = { ...env, DD_FLOW_HOOK_DEADLINE_AT: String(deadline) };
|
|
12
14
|
if (deadline !== undefined && !Number.isSafeInteger(deadline))
|
|
13
15
|
throw new AppError("hook_deadline_invalid", "Native hook deadline must be an absolute millisecond timestamp", 1, { effect: "unknown" });
|
|
14
16
|
const phase = (name) => {
|
|
@@ -24,6 +26,11 @@ export async function dispatchNativeHook(args, stdin, env) {
|
|
|
24
26
|
for (const [name, values] of parsed.options)
|
|
25
27
|
if (!["project-root", "json"].includes(name) || values.length !== 1 || name === "json" && values[0] !== "__dd_flow_flag__")
|
|
26
28
|
throw new AppError("usage", `Invalid native hook option: ${name}`, 2);
|
|
29
|
+
const unrelated = nativeHookNonParticipation(args, stdin);
|
|
30
|
+
if (unrelated) {
|
|
31
|
+
phase("ack");
|
|
32
|
+
return unrelated;
|
|
33
|
+
}
|
|
27
34
|
const [hooks, { createContext }, { resolveDdFlowHome }] = await Promise.all([
|
|
28
35
|
import("../services/hooks.js"), import("../runtime/context.js"), import("../storage/paths.js")
|
|
29
36
|
]);
|
|
@@ -58,7 +65,9 @@ export async function dispatchNativeHook(args, stdin, env) {
|
|
|
58
65
|
timing.subscribe(log);
|
|
59
66
|
try {
|
|
60
67
|
phase("receipt");
|
|
61
|
-
|
|
68
|
+
const result = await handler(context, { projectRoot, stdin });
|
|
69
|
+
phase("ack");
|
|
70
|
+
return result;
|
|
62
71
|
}
|
|
63
72
|
finally {
|
|
64
73
|
timing.unsubscribe(log);
|
|
@@ -68,8 +77,9 @@ export async function dispatchNativeHook(args, stdin, env) {
|
|
|
68
77
|
export async function runNativeHookIngress(args) {
|
|
69
78
|
try {
|
|
70
79
|
const { readNativeHookInput, hookBudget } = await import("../harness-runtime/lib/native-hook-command.mjs");
|
|
71
|
-
const
|
|
72
|
-
|
|
80
|
+
const budget = hookBudget(25_000, process.env.DD_FLOW_HOOK_DEADLINE_AT ? Number(process.env.DD_FLOW_HOOK_DEADLINE_AT) : null);
|
|
81
|
+
const stdin = await readNativeHookInput(process.stdin, budget);
|
|
82
|
+
process.stdout.write(`${JSON.stringify(await dispatchNativeHook(args, stdin, { ...process.env, DD_FLOW_HOOK_DEADLINE_AT: String(budget.deadlineAt) }))}\n`);
|
|
73
83
|
return 0;
|
|
74
84
|
}
|
|
75
85
|
catch (error) {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { parseJsonObject } from "../shared/json.js";
|
|
3
|
+
import { continuationCommand, parseLifecycleCommand } from "../services/lifecycle-command.js";
|
|
4
|
+
const object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
5
|
+
const text = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
6
|
+
/** Only a validated, ordinary before-tool command can skip the stateful handler.
|
|
7
|
+
* In particular after-tool, Stop, malformed and ambiguous events remain owned
|
|
8
|
+
* by their existing handler; this gate does not invent an admission policy. */
|
|
9
|
+
export function nativeHookNonParticipation(args, stdin) {
|
|
10
|
+
const payload = parseJsonObject(stdin || "{}", "native hook stdin");
|
|
11
|
+
let command;
|
|
12
|
+
if (args[0] === "zcode" && args[1] === "event") {
|
|
13
|
+
const update = object(object(payload.params).update);
|
|
14
|
+
if (payload.method !== "session/update" || update.sessionUpdate !== "tool_call")
|
|
15
|
+
return null;
|
|
16
|
+
const tool = text(object(object(update._meta).claudeCode).toolName);
|
|
17
|
+
if (!tool || !["Bash", "bash", "execute"].includes(tool))
|
|
18
|
+
return null;
|
|
19
|
+
const input = object(update.rawInput);
|
|
20
|
+
command = text(input.command) ?? text(input.cmd);
|
|
21
|
+
}
|
|
22
|
+
else if (args[0] === "grok" || args[0] === "zcode") {
|
|
23
|
+
const event = text(payload.hook_event_name) ?? text(payload.hookEventName);
|
|
24
|
+
if (event && !["PreToolUse", "pre_tool_use"].includes(event))
|
|
25
|
+
return null;
|
|
26
|
+
const tool = text(payload.tool_name) ?? text(payload.toolName);
|
|
27
|
+
if (tool && !(args[0] === "grok" ? ["Bash", "bash", "run_terminal_command", "RunTerminalCommand"] : ["Bash", "bash", "execute"]).includes(tool))
|
|
28
|
+
return null;
|
|
29
|
+
const input = object(payload.tool_input ?? payload.toolInput);
|
|
30
|
+
command = text(input.command) ?? text(input.cmd) ?? text(payload.command);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const family = args[0];
|
|
34
|
+
if (payload.schema_id !== `dd-flow/${family}-tool-event@1` || payload.phase !== "before")
|
|
35
|
+
return null;
|
|
36
|
+
const session = object(payload.session), tool = text(payload.tool);
|
|
37
|
+
const directory = family === "agy" ? text(payload.launch_cwd) : text(session.directory);
|
|
38
|
+
const id = family === "agy" ? text(payload.conversation_id) : text(session.provider_session_id);
|
|
39
|
+
if (!directory || !path.isAbsolute(directory) || !id || !text(payload.daemon_id) || !text(payload.event_id)
|
|
40
|
+
|| family === "opencode" && !text(payload.tool_call_id) || !tool
|
|
41
|
+
|| !(family === "droid" ? ["Execute"] : ["bash", "Bash", "shell", "run_command", "run_terminal_command", "RunTerminalCommand"]).includes(tool)
|
|
42
|
+
|| family === "droid" && session.parent_provider_session_id === id)
|
|
43
|
+
return null;
|
|
44
|
+
if (family === "agy" && Array.isArray(payload.workspace_paths)
|
|
45
|
+
&& payload.workspace_paths.length && !payload.workspace_paths.some(value => typeof value === "string" && path.resolve(value) === path.resolve(directory)))
|
|
46
|
+
return null;
|
|
47
|
+
const input = object(payload.input);
|
|
48
|
+
command = text(input.command) ?? text(input.cmd) ?? text(input.CommandLine);
|
|
49
|
+
}
|
|
50
|
+
if (!command)
|
|
51
|
+
return null;
|
|
52
|
+
const parsed = parseLifecycleCommand(command);
|
|
53
|
+
return parsed.participation === "none" && !continuationCommand(command)
|
|
54
|
+
? { ok: true, observed: false, reason: args[0] === "zcode" && args[1] === "event" ? "acp_evidence_only" : "event_not_participating" } : null;
|
|
55
|
+
}
|
|
@@ -40,7 +40,8 @@ export function writeResponseFile(payload, output) {
|
|
|
40
40
|
if (!output.responseFile)
|
|
41
41
|
return payload;
|
|
42
42
|
const file = path.resolve(output.responseFile), temporary = `${file}.tmp-${randomUUID()}`;
|
|
43
|
-
const
|
|
43
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
44
|
+
const visible = record ? { ...(record.prompt_delivery ? { prompt_delivery: record.prompt_delivery } : {}), ...record } : { ok: true, result: payload };
|
|
44
45
|
let ownsTemporary = false;
|
|
45
46
|
try {
|
|
46
47
|
prepareResponseDestination(file);
|
package/dist/cli/run-cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { prepareResponseDestination, writeResponseFile } from "./response-file.js";
|
|
3
|
+
import { preparePromptDelivery, publishPromptDelivery } from "../services/prompt-delivery.js";
|
|
3
4
|
import { requiredOption, optionalOption, optionalNumber, optionalPositiveNumber } from "./argument-values.js";
|
|
4
5
|
import { resolveCommandPaths, resolveCommandProjectRoot } from "../services/command-context.js";
|
|
5
6
|
import { assertAuxiliaryCommandScope, cliManagedInvocationScope, manualRecoveryInvocationScope, assertManualRecoveryInvocationTarget } from "../services/lifecycle-invocations.js";
|
|
@@ -49,6 +50,7 @@ import { validateSchema } from "../services/schema-validation.js";
|
|
|
49
50
|
import { preflightMemoryPermissions } from "../services/memory-permissions.js";
|
|
50
51
|
import { ingestDroidUsage, ingestAgyUsage, ingestGrokUsage, ingestOpenCodeUsage, ingestZcodeUsage } from "../services/usage.js";
|
|
51
52
|
import { applyMigration, assessMigrationImpact, assertUpgradeRunStorageReady, planMigration, prepareMigrationApply, verifyMigrationReport } from "../services/migrations.js";
|
|
53
|
+
import { PreparedUpgradeStorageReadiness } from "../services/memory-upgrade-storage-readiness.js";
|
|
52
54
|
import { publishProjectSummary } from "../services/project-summary.js";
|
|
53
55
|
import { requireProjectByRoot } from "../services/projects.js";
|
|
54
56
|
import { resolveProjectRoot, resolveDdFlowHome } from "../storage/paths.js";
|
|
@@ -62,9 +64,9 @@ import { addVnextCodeRepair, prepareVnextCodeRepair, prepareVnextCodeStart } fro
|
|
|
62
64
|
import { isVnextCodeReviewRun, prepareCodeReviewDecision, prepareVnextCodeReviewRepair, prepareVnextCodeReviewStart } from "../services/vnext-code-review.js";
|
|
63
65
|
import { applyVnextMerge, getVnextMergeRequest, isVnextMergeRun, prepareVnextMergeFinish, prepareVnextMergeStart, repairVnextMerge, routeVnextMergeRequest } from "../services/vnext-merge.js";
|
|
64
66
|
import { sdlcStageHandlers } from "../services/stage-handlers.js";
|
|
65
|
-
import { startMemoryStage, finishMemoryStage } from "../services/memory-stage-dispatch.js";
|
|
67
|
+
import { startMemoryStage, finishMemoryStage, startMemoryUpgradeStage, finishMemoryUpgradeStage } from "../services/memory-stage-dispatch.js";
|
|
66
68
|
import { bindStageCoordinatorWork } from "../services/work-registry.js";
|
|
67
|
-
import { registerMemoryCompositionFromStage } from "../services/memory-compose-handler.js";
|
|
69
|
+
import { registerMemoryCompositionFromStage, publishMemoryUpgradeFromStage } from "../services/memory-compose-handler.js";
|
|
68
70
|
import { runMemoryVerificationChecks } from "../services/memory-verify-handler.js";
|
|
69
71
|
import { registerMemoryReviewFromStage, collectMemoryInitialReviewFromStage } from "../services/memory-review-handler.js";
|
|
70
72
|
import { PreparedRetainedMemoryReviewAuthority } from "../services/memory-retained-review-authority.js";
|
|
@@ -81,7 +83,7 @@ import { blockStageForRuntime, unblockStageAfterRuntimeRepair, prepareStageRunti
|
|
|
81
83
|
import { classifyCliOperation, requiresExplicitUpgradeAuthorization } from "../services/cli-operation-classifier.js";
|
|
82
84
|
import { prepareWorkerPrompt, renderWorkerPrompt } from "../services/prompts.js";
|
|
83
85
|
import { finishStage, prepareStageFinish, startStage, prepareLegacyStageStart } from "../services/stage-lifecycle.js";
|
|
84
|
-
import { addWorkBatch, prepareWorkBatch, prepareWorkDeps, prepareWorkDelete, prepareWorkSettlement, prepareWorkRetry, assertStageLifecycleOwner, assertWorkLifecycleOwner, cancelWork, deleteWork, failWork, finishWork, listWorks, mutateWorkDeps, prepareWorkResult, workResultByteLimit, retryWork, showWork, startWork } from "../services/work-registry.js";
|
|
86
|
+
import { addWorkBatch, assertGenericWorkBatchAllowed, prepareWorkBatch, prepareWorkDeps, prepareWorkDelete, prepareWorkSettlement, prepareWorkRetry, assertStageLifecycleOwner, assertWorkLifecycleOwner, cancelWork, deleteWork, failWork, finishWork, listWorks, mutateWorkDeps, prepareWorkResult, workResultByteLimit, retryWork, showWork, startWork } from "../services/work-registry.js";
|
|
85
87
|
import { launchExternalWork } from "../services/external-work-launch.js";
|
|
86
88
|
import { createEvalBootstrapSnapshot, prepareEvalBootstrapSnapshot, createEvalRunSnapshot, prepareEvalRunSnapshotCreation, prepareVnextSpecifyRun, prepareEvalRunRestore, prepareEvalBootstrapRestore, restoreEvalBootstrapSnapshot, restoreEvalRunSnapshot } from "../services/eval-snapshots.js";
|
|
87
89
|
import { forkRunFromSnapshot, prepareRunFork } from "../services/run-fork.js";
|
|
@@ -328,7 +330,7 @@ async function runCliRequest(args, io, env) {
|
|
|
328
330
|
if (!invocationId) {
|
|
329
331
|
const preparationContext = createContext({ ...env }, "read_existing", undefined, maintenanceOptions);
|
|
330
332
|
try {
|
|
331
|
-
preparedInput = await prepareCliInput(invocationArgs, preparationContext, io, scopeProjectRoot, output.responseFile);
|
|
333
|
+
preparedInput = await prepareCliInput(invocationArgs, preparationContext, io, scopeProjectRoot, output.responseFile, undefined, commandProgress(io, output));
|
|
332
334
|
}
|
|
333
335
|
finally {
|
|
334
336
|
preparationContext.db.close?.();
|
|
@@ -357,8 +359,8 @@ async function runCliRequest(args, io, env) {
|
|
|
357
359
|
throw error;
|
|
358
360
|
}
|
|
359
361
|
},
|
|
360
|
-
prepare: async () => { preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, preparedRoute); },
|
|
361
|
-
assertCurrent: scope => assertLifecycleInvocationCurrent(dispatchContext, scope, command) });
|
|
362
|
+
prepare: async () => { preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, preparedRoute, commandProgress(io, output)); },
|
|
363
|
+
assertCurrent: scope => assertLifecycleInvocationCurrent(dispatchContext, scope, command), progress: commandProgress(io, output) });
|
|
362
364
|
if (admission.replay) {
|
|
363
365
|
retainedOutcome = admission.outcome;
|
|
364
366
|
settleHook = undefined;
|
|
@@ -386,7 +388,7 @@ async function runCliRequest(args, io, env) {
|
|
|
386
388
|
throw new AppError(error.code, error.message, error.exitCode, error.details);
|
|
387
389
|
}
|
|
388
390
|
if (!retainedOutcome && !preparedInput)
|
|
389
|
-
preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile);
|
|
391
|
+
preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, undefined, commandProgress(io, output));
|
|
390
392
|
if (!retainedOutcome)
|
|
391
393
|
await preflightCliCompatibility(dispatchContext, invocationArgs, env, scopeProjectRoot);
|
|
392
394
|
if (!retainedOutcome) {
|
|
@@ -394,11 +396,10 @@ async function runCliRequest(args, io, env) {
|
|
|
394
396
|
const event = !invocationId ? nativeEvent : null;
|
|
395
397
|
settleHook = observeLifecycleCommand(dispatchContext, command, event ?? undefined);
|
|
396
398
|
}
|
|
397
|
-
const result = retainedOutcome ? retainedOutcome.result : await dispatch(dispatchArgs, dispatchContext, io, scopeProjectRoot, classification, output, preparedInput);
|
|
398
|
-
if (!retainedOutcome &&
|
|
399
|
-
&& result && typeof result === "object" && "prompt_path" in result && typeof result.prompt_path === "string") {
|
|
399
|
+
const result = preparePromptDelivery(retainedOutcome ? retainedOutcome.result : await dispatch(dispatchArgs, dispatchContext, io, scopeProjectRoot, classification, output, preparedInput));
|
|
400
|
+
if (!retainedOutcome && result && typeof result === "object" && "worker_prompt_markdown" in result && typeof result.worker_prompt_markdown === "string") {
|
|
400
401
|
try {
|
|
401
|
-
bindLifecyclePublishedPacket(dispatchContext,
|
|
402
|
+
bindLifecyclePublishedPacket(dispatchContext, result.worker_prompt_markdown);
|
|
402
403
|
}
|
|
403
404
|
catch (cause) {
|
|
404
405
|
throw new AppError("invocation_packet_binding_failed", "Stage started but its published lifecycle assignments could not be retained", 1, { effect: "committed", cause: String(cause) });
|
|
@@ -422,6 +423,7 @@ async function runCliRequest(args, io, env) {
|
|
|
422
423
|
writeProgressJsonl(io, "done", "Command finished");
|
|
423
424
|
emitHumanProgress(io, output, progress, "done");
|
|
424
425
|
const payload = attachProgress(resultWithWarnings, progress, output);
|
|
426
|
+
publishPromptDelivery(payload);
|
|
425
427
|
const visible = writeResponseFile(payload, output);
|
|
426
428
|
const maintenanceReceipt = payload;
|
|
427
429
|
const maintenanceExpiry = maintenanceReceipt?.lease_expires_at ?? maintenanceReceipt?.process?.lease_expires_at;
|
|
@@ -436,7 +438,7 @@ async function runCliRequest(args, io, env) {
|
|
|
436
438
|
const record = errorRecord(caught);
|
|
437
439
|
maintenanceDiagnostics?.failure(caught);
|
|
438
440
|
const error = isAppError(caught) ? caught : Object.assign(new AppError(record.code, record.message, record.exitCode, { effect: "unknown", ...record.details, ...(caught?.errcode !== undefined ? { sqlite_extended_code: caught.errcode } : {}) }), { ...(record.cause ? { cause: record.cause } : {}), ...(record.cleanup_error ? { cleanup_error: record.cleanup_error } : {}), ...(record.retryable !== undefined ? { retryable: record.retryable } : {}) });
|
|
439
|
-
if (!settleHook && !invocationSettlement) {
|
|
441
|
+
if (!settleHook && !invocationSettlement && error.code !== "prompt_delivery_pending") {
|
|
440
442
|
let earlyContext;
|
|
441
443
|
try {
|
|
442
444
|
const command = quote(["dd-flow", ...args]);
|
|
@@ -558,6 +560,8 @@ function databaseModeForCommand(args, classification) {
|
|
|
558
560
|
if (isRuntimeMaintenance(args))
|
|
559
561
|
return "read_existing";
|
|
560
562
|
const [family, command] = args;
|
|
563
|
+
if (family === "migration" && command === "readiness")
|
|
564
|
+
return "prepared_existing";
|
|
561
565
|
// Managed attempts are issued only from a prepared store. Opening that
|
|
562
566
|
// existing writer must not run migrations before their input is admitted.
|
|
563
567
|
if (parseArgs(args.slice(2)).options.has("invocation-id"))
|
|
@@ -635,7 +639,12 @@ function writeProgressJsonl(io, phase, message, detail) {
|
|
|
635
639
|
io.stderr.write(`${JSON.stringify({ schema_id: "dd-flow/progress@1", phase, message, at: new Date().toISOString(), ...(detail ? { detail } : {}) })}\n`);
|
|
636
640
|
}
|
|
637
641
|
function commandProgress(io, output) {
|
|
638
|
-
return (message) =>
|
|
642
|
+
return (message) => {
|
|
643
|
+
if (output.progressJsonl)
|
|
644
|
+
writeProgressJsonl(io, "running", message);
|
|
645
|
+
else if (!output.json && !output.hookJsonOnly)
|
|
646
|
+
io.stderr.write(`[running] ${message}\n`);
|
|
647
|
+
};
|
|
639
648
|
}
|
|
640
649
|
function attachProgress(result, progress, output) {
|
|
641
650
|
if (progress.length === 0 || output.hookJsonOnly)
|
|
@@ -842,6 +851,14 @@ function stringValue(value) {
|
|
|
842
851
|
function shortSha(value) {
|
|
843
852
|
return value ? value.slice(0, 12) : "unknown";
|
|
844
853
|
}
|
|
854
|
+
function compositionProposalRetry(context, parsed) {
|
|
855
|
+
return {
|
|
856
|
+
retry_command: runtimeCommand(context, ["memory", "compose", "register", "--run", requiredOption(parsed, "run"),
|
|
857
|
+
"--project-root", requiredOption(parsed, "project-root"), "--request-key", requiredOption(parsed, "request-key"),
|
|
858
|
+
"--result-file", requiredOption(parsed, "result-file"), "--json", "--progress-jsonl"]),
|
|
859
|
+
retry_instruction: "Correct only the rejected composition proposal, then execute retry_command in this same Session. No Work graph was registered by this rejected call; do not dispatch children or finish the Stage until registration succeeds."
|
|
860
|
+
};
|
|
861
|
+
}
|
|
845
862
|
/** All caller supplied file/stdin data is consumed here, before a managed
|
|
846
863
|
* invocation is allowed to claim execution or a stage claims its hook event. */
|
|
847
864
|
async function prepareCliInput(...args) {
|
|
@@ -852,7 +869,7 @@ async function prepareCliInput(...args) {
|
|
|
852
869
|
return prepareError(error);
|
|
853
870
|
}
|
|
854
871
|
}
|
|
855
|
-
async function prepareCliInputValue(args, context, io, scopeProjectRoot, responseFile = null, route = prepareCommandRoute(args)) {
|
|
872
|
+
async function prepareCliInputValue(args, context, io, scopeProjectRoot, responseFile = null, route = prepareCommandRoute(args), progress) {
|
|
856
873
|
const { family, command, parsed } = route;
|
|
857
874
|
const prepared = { route };
|
|
858
875
|
if (optionalOption(parsed, "reason-file")) {
|
|
@@ -941,6 +958,10 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
941
958
|
prepared.codexHooks = prepareCodexHooks(context, { projectRoot: requiredOption(parsed, "project-root"), target: optionalOption(parsed, "target"), profile: optionalOption(parsed, "profile"), yes: hasOption(parsed, "yes") });
|
|
942
959
|
if (route.key === "migration apply")
|
|
943
960
|
prepared.migrationApply = prepareMigrationApply(context, migrationApplyInput(parsed, classifyCliOperation(args, context.env)));
|
|
961
|
+
if (route.key === "migration readiness")
|
|
962
|
+
prepared.upgradeStorageScope = {
|
|
963
|
+
projectRoot: requiredOption(parsed, "project-root"), workspaceRoot: requiredOption(parsed, "workspace-root"), requestId: requiredOption(parsed, "request-id")
|
|
964
|
+
};
|
|
944
965
|
if (route.key === "migration verify")
|
|
945
966
|
prepared.migrationVerification = verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
946
967
|
if (responseFile)
|
|
@@ -1115,6 +1136,7 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1115
1136
|
if (route.key === "work add-batch") {
|
|
1116
1137
|
const file = requiredOption(parsed, "file");
|
|
1117
1138
|
try {
|
|
1139
|
+
assertGenericWorkBatchAllowed(context, requiredOption(parsed, "parent"));
|
|
1118
1140
|
prepared.workBatch = prepareWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file,
|
|
1119
1141
|
...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}),
|
|
1120
1142
|
...(optionalOption(parsed, "request-key") ? { requestKey: optionalOption(parsed, "request-key") } : {}) });
|
|
@@ -1210,14 +1232,15 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1210
1232
|
const bundle = readFlowRunExecutionBundle(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") });
|
|
1211
1233
|
if (bundle)
|
|
1212
1234
|
prepared.stageBindingBundle = { stage, bundle, ...(contextSha256 ? { contextSha256 } : {}) };
|
|
1213
|
-
if (bundle
|
|
1235
|
+
if (bundle && ["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id)) {
|
|
1214
1236
|
bundle.descriptor.stage(stage);
|
|
1215
1237
|
if (hasOption(parsed, "dir"))
|
|
1216
1238
|
throw new AppError("usage", "Memory stage layout belongs to its frozen descriptor", 2);
|
|
1217
1239
|
const loaded = contextFile && contextSha256 ? loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) : undefined;
|
|
1218
1240
|
if (loaded)
|
|
1219
1241
|
prepared.stageStart = { externalContext: loaded };
|
|
1220
|
-
prepared.memoryStage = { operation: "start", bytes: loaded ? Buffer.from(loaded.bytes) : Buffer.alloc(0)
|
|
1242
|
+
prepared.memoryStage = { operation: "start", bytes: loaded ? Buffer.from(loaded.bytes) : Buffer.alloc(0),
|
|
1243
|
+
flowId: bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
|
|
1221
1244
|
return prepared;
|
|
1222
1245
|
}
|
|
1223
1246
|
}
|
|
@@ -1313,7 +1336,7 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1313
1336
|
throw new AppError("usage", "work finish requires exactly one of --result-file or --result-stdin", 2);
|
|
1314
1337
|
const maxBytes = workResultByteLimit(context, requiredPosition(parsed, 0, "work-id"));
|
|
1315
1338
|
prepared.workFinish = { result: resultFile ? prepareTextFile(resultFile, "Work result", maxBytes).text : await readStdin(io.stdin, maxBytes) };
|
|
1316
|
-
prepareWorkResult(context, requiredPosition(parsed, 0, "work-id"), prepared.workFinish.result);
|
|
1339
|
+
prepareWorkResult(context, requiredPosition(parsed, 0, "work-id"), prepared.workFinish.result, progress);
|
|
1317
1340
|
}
|
|
1318
1341
|
if (family === "stage" && command === "fanout" && requiredPosition(parsed, 0, "fanout action") === "reconcile") {
|
|
1319
1342
|
prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
|
|
@@ -1323,10 +1346,18 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1323
1346
|
}
|
|
1324
1347
|
if (family === "memory" && (["compose", "corrections"].includes(command ?? "") && parsed.positional[0] === "register"
|
|
1325
1348
|
|| command === "repair" && parsed.positional[0] === "publication-reserve")) {
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1349
|
+
try {
|
|
1350
|
+
const bytes = prepareFileBytes(requiredOption(parsed, "result-file"), `Memory ${command} result`, 1024 * 1024).bytes;
|
|
1351
|
+
if (!bytes.length || bytes.length > 1024 * 1024)
|
|
1352
|
+
throw new AppError("usage", "Memory operation result exceeds its byte bound", 2, { phase: "prepare", effect: "no_effect", recoverable: true });
|
|
1353
|
+
prepared.memoryOperationBytes = bytes;
|
|
1354
|
+
}
|
|
1355
|
+
catch (error) {
|
|
1356
|
+
if (command === "compose" && isAppError(error) && error.details.phase === "prepare"
|
|
1357
|
+
&& error.details.effect === "no_effect" && error.details.recoverable === true)
|
|
1358
|
+
Object.assign(error.details, compositionProposalRetry(context, parsed));
|
|
1359
|
+
throw error;
|
|
1360
|
+
}
|
|
1330
1361
|
}
|
|
1331
1362
|
if (family !== "stage" || command !== "finish")
|
|
1332
1363
|
return prepared;
|
|
@@ -1343,9 +1374,9 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1343
1374
|
const semanticFile = optionalOption(parsed, "semantic-file") ?? optionalOption(parsed, "data");
|
|
1344
1375
|
const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
|
|
1345
1376
|
const runtime = run(projectRoot, runId);
|
|
1346
|
-
if (JSON.parse(runtime.indexJson).execution_bundle?.flow_id
|
|
1377
|
+
if (["mb-init", "mb-upgrade"].includes(JSON.parse(runtime.indexJson).execution_bundle?.flow_id)) {
|
|
1347
1378
|
const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
|
|
1348
|
-
if (!bundle || bundle.descriptor.descriptor.id
|
|
1379
|
+
if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
|
|
1349
1380
|
throw new AppError("cli_input_unprepared", "Memory RUN bundle is unavailable", 1);
|
|
1350
1381
|
bundle.descriptor.stage(stage);
|
|
1351
1382
|
if (["dir", "outcome", "decision-file", "verification-file", "retry-check", "reason", "reason-file", "request", "work"].some(option => hasOption(parsed, option)))
|
|
@@ -1355,7 +1386,7 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1355
1386
|
const bytes = resultStdin ? Buffer.from(await readStdin(io.stdin, 1024 * 1024)) : prepareFileBytes(resultFile, "Memory stage result", 1024 * 1024).bytes;
|
|
1356
1387
|
if (!bytes.length || bytes.length > 1024 * 1024)
|
|
1357
1388
|
throw new AppError("usage", "Memory stage result exceeds its byte bound", 2);
|
|
1358
|
-
prepared.memoryStage = { operation: "finish", bytes };
|
|
1389
|
+
prepared.memoryStage = { operation: "finish", bytes, flowId: bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
|
|
1359
1390
|
return prepared;
|
|
1360
1391
|
}
|
|
1361
1392
|
const terminalReplay = (name) => {
|
|
@@ -1668,6 +1699,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1668
1699
|
});
|
|
1669
1700
|
}
|
|
1670
1701
|
if (family === "work" && command === "add-batch") {
|
|
1702
|
+
assertGenericWorkBatchAllowed(context, requiredOption(parsed, "parent"));
|
|
1671
1703
|
return addWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file: requiredOption(parsed, "file"), prepared: prepared.workBatch,
|
|
1672
1704
|
...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}),
|
|
1673
1705
|
...(optionalOption(parsed, "request-key") ? { requestKey: optionalOption(parsed, "request-key") } : {}) });
|
|
@@ -1694,7 +1726,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1694
1726
|
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
1695
1727
|
const recoveryId = optionalOption(parsed, "recovery-id");
|
|
1696
1728
|
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work start");
|
|
1697
|
-
return startWork(context, requiredPosition(parsed, 0, "work-id"), { ...(hookEventId ? { hookEventId } : {}), ...(recoveryId ? { recoveryId } : {}), projectRoot });
|
|
1729
|
+
return startWork(context, requiredPosition(parsed, 0, "work-id"), { ...(hookEventId ? { hookEventId } : {}), ...(recoveryId ? { recoveryId } : {}), projectRoot, progress: commandProgress(io, output) });
|
|
1698
1730
|
}
|
|
1699
1731
|
if (family === "work" && command === "finish") {
|
|
1700
1732
|
const resultFile = optionalOption(parsed, "result-file");
|
|
@@ -1748,7 +1780,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1748
1780
|
matchKey: entityReferenceVariants(runId).map(reference => stageStartMatchKey(reference, stage, projectRoot, contextSha256)),
|
|
1749
1781
|
errorCode: "trusted_session_binding_required", operation: "Memory stage start"
|
|
1750
1782
|
}).eventKey;
|
|
1751
|
-
const work = startMemoryStage(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes });
|
|
1783
|
+
const work = (prepared.memoryStage.flowId === "mb-upgrade" ? startMemoryUpgradeStage : startMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes });
|
|
1752
1784
|
try {
|
|
1753
1785
|
const binding = bindStageCoordinatorWork(context, { workId: work.work_id, hookEventId: event, stage,
|
|
1754
1786
|
promptPath: work.prompt_path, resultPath: work.result_path, ...(contextSha256 ? { contextSha256 } : {}) });
|
|
@@ -1867,7 +1899,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1867
1899
|
// Descriptor settlement validates exact coordinator custody and claims
|
|
1868
1900
|
// the fresh hook atomically with result acceptance. Do not consume it
|
|
1869
1901
|
// through the SDLC lifecycle owner before that settlement.
|
|
1870
|
-
return controlledReply(finishMemoryStage(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes,
|
|
1902
|
+
return controlledReply((prepared.memoryStage.flowId === "mb-upgrade" ? finishMemoryUpgradeStage : finishMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes,
|
|
1871
1903
|
hookEventId: hookEventId ?? findRecentMatchingHookEvent(context, {
|
|
1872
1904
|
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
1873
1905
|
matchKey: entityReferenceVariants(runId).map(reference => stageLifecycleMatchKey("stage_finish", reference, stage, projectRoot)),
|
|
@@ -2179,6 +2211,13 @@ function dispatchEngine(context, command, parsed, classification, prepared) {
|
|
|
2179
2211
|
function dispatchMigration(context, command, parsed, classification, prepared) {
|
|
2180
2212
|
if (command === "impact")
|
|
2181
2213
|
return assessMigrationImpact(context, { projectRoot: requiredOption(parsed, "project-root"), ...(optionalOption(parsed, "target-version") ? { targetVersion: optionalOption(parsed, "target-version") } : {}), ...(optionalOption(parsed, "mode") ? { mode: optionalOption(parsed, "mode") } : {}) });
|
|
2214
|
+
if (command === "readiness") {
|
|
2215
|
+
if (!prepared.upgradeStorageScope)
|
|
2216
|
+
throw new AppError("validation", "Upgrade storage scope must be prepared before publication", 2);
|
|
2217
|
+
// Unlike the initial readonly syntax pass, this connection is admitted
|
|
2218
|
+
// prepared-existing. Physical writer checks still precede receipt writes.
|
|
2219
|
+
return { ok: true, ...PreparedUpgradeStorageReadiness.prepare(context, prepared.upgradeStorageScope).apply(context) };
|
|
2220
|
+
}
|
|
2182
2221
|
if (command === "plan" || command === "report") {
|
|
2183
2222
|
return planMigration(context, {
|
|
2184
2223
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
@@ -2425,7 +2464,7 @@ function dispatchMemory(context, command, parsed, prepared) {
|
|
|
2425
2464
|
const workspaceRoot = requiredOption(parsed, "workspace-root");
|
|
2426
2465
|
if (wave === "2") {
|
|
2427
2466
|
const bundle = readFlowRunExecutionBundle(context, work);
|
|
2428
|
-
if (!bundle || bundle.descriptor.descriptor.id
|
|
2467
|
+
if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) || bundle.execution.snapshot().scope.workspace !== workspaceRoot)
|
|
2429
2468
|
throw new AppError("memory_repair_scope_invalid", "Repair workspace does not match its frozen memory RUN", 2, { phase: "prepare", effect: "no_effect" });
|
|
2430
2469
|
}
|
|
2431
2470
|
if (subcommand === "current-view")
|
|
@@ -2455,14 +2494,13 @@ function dispatchMemory(context, command, parsed, prepared) {
|
|
|
2455
2494
|
if (error instanceof AppError && (error.code === "memory_stage_result_invalid"
|
|
2456
2495
|
|| error.code === "memory_preflight_handler_invalid" && ["unselected_preflight_source", "proposed_source_sha256_mismatch"].includes(String(error.details.reason))) && error.details.phase === "prepare"
|
|
2457
2496
|
&& error.details.effect === "no_effect" && error.details.recoverable === true) {
|
|
2458
|
-
error.details
|
|
2459
|
-
"--project-root", requiredOption(parsed, "project-root"), "--request-key", requiredOption(parsed, "request-key"),
|
|
2460
|
-
"--result-file", requiredOption(parsed, "result-file"), "--json", "--progress-jsonl"]);
|
|
2461
|
-
error.details.retry_instruction = "Correct only the rejected composition proposal, then execute retry_command in this same Session. No Work graph was registered by this rejected call; do not dispatch children or finish the Stage until registration succeeds.";
|
|
2497
|
+
Object.assign(error.details, compositionProposalRetry(context, parsed));
|
|
2462
2498
|
}
|
|
2463
2499
|
throw error;
|
|
2464
2500
|
}
|
|
2465
2501
|
}
|
|
2502
|
+
if (command === "upgrade" && subcommand === "publish")
|
|
2503
|
+
return publishMemoryUpgradeFromStage(context, scope());
|
|
2466
2504
|
if (command === "verify" && subcommand === "checks")
|
|
2467
2505
|
return runMemoryVerificationChecks(context, scope());
|
|
2468
2506
|
if (command === "review" && subcommand === "register")
|
package/dist/cli/setup.js
CHANGED
|
@@ -129,7 +129,8 @@ export async function runSetupCli(args, env, io = { stdout: process.stdout, stde
|
|
|
129
129
|
throw error;
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
-
result = { ...installed,
|
|
132
|
+
result = { ...installed, request_installation: installed.installation,
|
|
133
|
+
action: route.key === "setup update" ? "setup_update" : "setup_install", observed_cli: observedCli,
|
|
133
134
|
executor: installed.executor ? { profile_id: installed.executor.id, revision: hybridDigest(installed.executor), readiness: "unknown_requires_qualification" } : { profile_id: null, readiness: "not_configured" },
|
|
134
135
|
...readSetupStatus(home) };
|
|
135
136
|
}
|
package/dist/cli.js
CHANGED
|
@@ -49,10 +49,22 @@ if (args[0] === "init" || args[0] === "flow" && args[1] === "mb-init") {
|
|
|
49
49
|
publishSetup('imports_ready');
|
|
50
50
|
process.exit(await runMemoryInitCli(args, process.env));
|
|
51
51
|
}
|
|
52
|
+
if (args[0] === "upgrade" || args[0] === "flow" && args[1] === "mb-upgrade") {
|
|
53
|
+
const { runMemoryUpgradeCli } = await import("./cli/memory-upgrade.js");
|
|
54
|
+
process.exit(await runMemoryUpgradeCli(args, process.env));
|
|
55
|
+
}
|
|
52
56
|
if (args.slice(0, 3).join(" ") === "runtime storage admit") {
|
|
53
57
|
const { runMemoryStorageAdmissionCli } = await import("./cli/memory-storage-admission.js");
|
|
54
58
|
process.exit(runMemoryStorageAdmissionCli(args));
|
|
55
59
|
}
|
|
60
|
+
if (args.slice(0, 3).join(" ") === "runtime storage qualify") {
|
|
61
|
+
const { runMemoryStorageQualificationCli } = await import("./cli/memory-storage-qualification.js");
|
|
62
|
+
process.exit(runMemoryStorageQualificationCli(args));
|
|
63
|
+
}
|
|
64
|
+
if (args.slice(0, 3).join(" ") === "runtime storage transition") {
|
|
65
|
+
const { runMemoryStorageTransitionCli } = await import("./cli/memory-storage-transition.js");
|
|
66
|
+
process.exit(runMemoryStorageTransitionCli(args));
|
|
67
|
+
}
|
|
56
68
|
if (args.slice(0, 3).join(" ") === "runtime flow admit") {
|
|
57
69
|
const { runMemoryFlowAdmissionCli } = await import("./cli/memory-flow-admission.js");
|
|
58
70
|
process.exit(await runMemoryFlowAdmissionCli(args));
|
|
@@ -18,3 +18,11 @@ export const memoryInitHandlerIdentities = Object.freeze({
|
|
|
18
18
|
review: Object.freeze({ stage: "review", entryId: "memory-stage-entry", finishId: "memory-stage-finish" }),
|
|
19
19
|
finish: Object.freeze({ stage: "finish", entryId: "memory-stage-entry", finishId: "memory-stage-finish" })
|
|
20
20
|
});
|
|
21
|
+
export const memoryUpgradeHandlerIdentities = Object.freeze({
|
|
22
|
+
preflight: Object.freeze({ stage: "preflight", entryId: "memory-upgrade-stage-entry", finishId: "memory-upgrade-stage-finish" }),
|
|
23
|
+
"diff-analysis": Object.freeze({ stage: "diff-analysis", entryId: "memory-upgrade-stage-entry", finishId: "memory-upgrade-stage-finish" }),
|
|
24
|
+
upgrade: Object.freeze({ stage: "upgrade", entryId: "memory-upgrade-stage-entry", finishId: "memory-upgrade-stage-finish" }),
|
|
25
|
+
verify: Object.freeze({ stage: "verify", entryId: "memory-upgrade-stage-entry", finishId: "memory-upgrade-stage-finish" }),
|
|
26
|
+
review: Object.freeze({ stage: "review", entryId: "memory-upgrade-stage-entry", finishId: "memory-upgrade-stage-finish" }),
|
|
27
|
+
finish: Object.freeze({ stage: "finish", entryId: "memory-upgrade-stage-entry", finishId: "memory-upgrade-stage-finish" })
|
|
28
|
+
});
|
|
@@ -23,7 +23,7 @@ async function hook(options) {
|
|
|
23
23
|
const normalized = { schema_id: "dd-flow/agy-tool-event@1", event_id: identity.event_id, phase, daemon_id: identity.daemon_id, launch_cwd: identity.launch_cwd, conversation_id: payload.conversationId, parent_conversation_id: identity.parent_conversation_id, execution_num: payload.executionNum, step_index: payload.stepIdx, tool: payload.toolCall?.name, input: args, workspace_paths: payload.workspacePaths, transcript_path: payload.transcriptPath, model: payload.modelName, profile: identity.profile, ...(phase === "after" ? { error: payload.error ?? "" } : {}) };
|
|
24
24
|
await invokeNativeHook({ bin: options["dd-flow-bin"] ?? identity.dd_flow_bin,
|
|
25
25
|
args: ["agy", "event", "handle", "--project-root", options["project-root"] ?? identity.project_root, "--json"],
|
|
26
|
-
payload: normalized, home: options["dd-flow-home"] ?? identity.dd_flow_home, timeoutMs: budget.remaining("admission"
|
|
26
|
+
payload: normalized, home: options["dd-flow-home"] ?? identity.dd_flow_home, timeoutMs: budget.remaining("admission") });
|
|
27
27
|
}
|
|
28
28
|
return event === "PreToolUse" ? { decision: "allow" } : {};
|
|
29
29
|
} catch (error) {
|
|
@@ -31,8 +31,8 @@ async function hook(options) {
|
|
|
31
31
|
// reads only receipts matching request IDs it actually accepted this turn.
|
|
32
32
|
const correctable = event === "PreToolUse" && error.code === "lifecycle_shell_syntax_invalid" && error.details?.effect === "no_effect"
|
|
33
33
|
&& error.details?.recoverable === true && error.details?.disposition === "pre_cli_shell_refusal";
|
|
34
|
-
try { await writeJsonAtomic(path.join(options["state-dir"], "hook-failures", `${requestId}.json`), { request_id: requestId, observed_at: new Date().toISOString(), code: error.code ?? "agy_hook_rejected", message: error.message,
|
|
35
|
-
details: { ...error.details, effect: correctable ? "no_effect" : "unknown", ...(correctable ? { native_pre_cli_refusal: true } : {}) } }); }
|
|
34
|
+
try { await budget.run(() => writeJsonAtomic(path.join(options["state-dir"], "hook-failures", `${requestId}.json`), { request_id: requestId, observed_at: new Date().toISOString(), code: error.code ?? "agy_hook_rejected", message: error.message,
|
|
35
|
+
details: { ...error.details, effect: correctable ? "no_effect" : "unknown", ...(correctable ? { native_pre_cli_refusal: true } : {}) } }, () => budget.remaining("failure_receipt_commit")), "failure_receipt"); }
|
|
36
36
|
catch (storage) { process.stderr.write(`${JSON.stringify({ code: error.code, receipt_error: storage.message })}\n`); }
|
|
37
37
|
throw error;
|
|
38
38
|
}
|
|
@@ -12,12 +12,12 @@ async function hook(options) {
|
|
|
12
12
|
const budget = hookBudget();
|
|
13
13
|
const stdin = await readNativeHookInput(process.stdin, budget); const payload = JSON.parse(stdin || "{}"); const identity = await callDaemon(options["state-dir"], "hook.resolve", { sessionId: payload.sessionId ?? payload.session_id, payload }, budget.remaining("identity"));
|
|
14
14
|
const enriched = { ...payload, _meta: { ...(payload._meta ?? {}), ddGrok: identity } }; const args = ["grok", "event", "handle", "--project-root", options["project-root"], "--json"]; const command = options["dd-flow-bin"];
|
|
15
|
-
try { return await invokeNativeHook({ bin: command, args, home: options["dd-flow-home"], payload: enriched, timeoutMs: budget.remaining("admission"
|
|
15
|
+
try { return await invokeNativeHook({ bin: command, args, home: options["dd-flow-home"], payload: enriched, timeoutMs: budget.remaining("admission") }); }
|
|
16
16
|
catch (error) {
|
|
17
17
|
if (identity.nativeHookBinding) error.details = { ...error.details, native_hook_binding: identity.nativeHookBinding };
|
|
18
18
|
try {
|
|
19
|
-
await writeNativeHookFailure(options["state-dir"], identity.nativeHookBinding, error,
|
|
20
|
-
if (identity.nativeHookBinding) await callDaemon(options["state-dir"], "hook.failed", { requestId: identity.nativeHookBinding.request_id },
|
|
19
|
+
await writeNativeHookFailure(options["state-dir"], identity.nativeHookBinding, error, budget);
|
|
20
|
+
if (identity.nativeHookBinding) await callDaemon(options["state-dir"], "hook.failed", { requestId: identity.nativeHookBinding.request_id }, budget.remaining("failure_notification"));
|
|
21
21
|
} catch (storage) { process.stderr.write(`${JSON.stringify({ code: error.code, receipt_error: storage.message })}\n`); }
|
|
22
22
|
throw error;
|
|
23
23
|
}
|
|
@@ -37,14 +37,14 @@ async function hook(options) {
|
|
|
37
37
|
const identity = await callDaemon(options["state-dir"], "hook.resolve", { sessionId: payload.session_id ?? payload.sessionId, payload }, budget.remaining("identity"));
|
|
38
38
|
const command = options["dd-flow-bin"]; const args = ["zcode", "hook", "handle", "--project-root", options["project-root"], "--json"];
|
|
39
39
|
|
|
40
|
-
try { return await invokeNativeHook({ bin: command, args, home: options["dd-flow-home"], timeoutMs: budget.remaining("admission"
|
|
40
|
+
try { return await invokeNativeHook({ bin: command, args, home: options["dd-flow-home"], timeoutMs: budget.remaining("admission"), payload: { ...payload, _meta: { ...(payload._meta ?? {}), ddZcode: identity } } }); }
|
|
41
41
|
catch (error) {
|
|
42
42
|
if (identity.nativeHookBinding) error.details = { ...error.details, native_hook_binding: identity.nativeHookBinding };
|
|
43
43
|
try {
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
await writeNativeHookFailure(options["state-dir"], identity.nativeHookBinding, error,
|
|
47
|
-
if (identity.nativeHookBinding) await callDaemon(options["state-dir"], "hook.failed", { requestId: identity.nativeHookBinding.request_id },
|
|
44
|
+
// Evidence shares the original deadline; an expired admission cannot
|
|
45
|
+
// start a new independent failure-report window.
|
|
46
|
+
await writeNativeHookFailure(options["state-dir"], identity.nativeHookBinding, error, budget);
|
|
47
|
+
if (identity.nativeHookBinding) await callDaemon(options["state-dir"], "hook.failed", { requestId: identity.nativeHookBinding.request_id }, budget.remaining("failure_notification"));
|
|
48
48
|
} catch (storage) { process.stderr.write(`${JSON.stringify({ code: error.code, receipt_error: storage.message })}\n`); }
|
|
49
49
|
throw error;
|
|
50
50
|
}
|
|
@@ -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
|
+
|