@deksden-com/dd-flow-cli 0.9.0-beta.121 → 0.9.0-beta.123
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 +17 -0
- package/dist/build-info.json +5 -5
- package/dist/cli/hook-ingress.js +4 -3
- package/dist/cli/native-hook-ingress.js +34 -2
- package/dist/cli/run-cli.js +3 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +6 -3
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +68 -6
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +21 -5
- package/dist/harness-runtime/lib/dd-codex.mjs +5 -1
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +60 -8
- package/dist/harness-runtime/lib/dd-zcode.mjs +40 -16
- package/dist/harness-runtime/lib/delegation-instructions.mjs +3 -3
- package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
- package/dist/harness-runtime/lib/lifecycle-command.mjs +218 -0
- package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +3 -2
- package/dist/harness-runtime/lib/model-observations.mjs +16 -9
- package/dist/harness-runtime/lib/native-hook-command.d.mts +2 -2
- package/dist/harness-runtime/lib/native-hook-command.mjs +36 -5
- package/dist/harness-runtime/lib/tool-observations.d.mts +1 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +52 -11
- package/dist/runtime/context.js +1 -1
- package/dist/schemas/flow-run.schema.json +1 -0
- package/dist/services/codex-capacity.js +131 -12
- package/dist/services/codex-hook-delivery.js +6 -2
- package/dist/services/eval-snapshots.js +13 -8
- package/dist/services/external-work-launch.js +27 -9
- package/dist/services/hooks.js +51 -59
- package/dist/services/lifecycle-command.js +1 -383
- package/dist/services/lifecycle-contract.js +1 -30
- package/dist/services/lifecycle-invocations.js +34 -13
- package/dist/services/merge-server.js +26 -9
- package/dist/services/review-copy.js +21 -0
- package/dist/services/run-controller-adapter.js +25 -3
- package/dist/services/run-controller-recovery.js +9 -2
- package/dist/services/run-controller.js +59 -20
- package/dist/services/run-observations.js +7 -2
- package/dist/services/runs.js +60 -47
- package/dist/services/stage-report-renderer.js +44 -2
- package/dist/services/verification-matrix.js +345 -0
- package/dist/services/vnext-code-review.js +30 -11
- package/dist/services/vnext-code.js +31 -10
- package/dist/services/vnext-merge.js +30 -7
- package/dist/services/vnext-plan-review.js +28 -7
- package/dist/services/vnext-plan.js +16 -3
- package/dist/services/work-registry.js +15 -1
- package/dist/storage/database.js +43 -9
- package/package.json +4 -1
|
@@ -21,6 +21,7 @@ import { acceptStageReport, prepareStageReport } from "./stage-report-renderer.j
|
|
|
21
21
|
import { applyExternalStageContext, prepareExternalStageContext } from "./stage-context.js";
|
|
22
22
|
import { subagentCapacityKey } from "./vnext-fanout.js";
|
|
23
23
|
import { stageLifecycleInstruction } from "../harness-runtime/lib/delegation-instructions.mjs";
|
|
24
|
+
import { prepareRunVerificationMatrix, acceptVerificationMatrix, commitVerificationMatrixStart, verificationMatrixInstruction } from "./verification-matrix.js";
|
|
24
25
|
export function isVnextPlanRun(context, input) {
|
|
25
26
|
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
26
27
|
return Boolean(context.db.get("SELECT id FROM runs WHERE project_id = ? AND id = ? AND flow_kind = 'vnext_protocolize'", [project.id, input.runId]));
|
|
@@ -41,7 +42,7 @@ export function prepareVnextPlanStart(context, input) {
|
|
|
41
42
|
const { profile: codeCheckProfile } = readCodeCheckProfile(run.workspace_root);
|
|
42
43
|
if (context.db.get("SELECT 1 FROM works WHERE project_id = ? AND run_id = ? AND task = ? AND status = 'running'", [project.id, run.id, planTask]))
|
|
43
44
|
throw new AppError("invalid_work_state", "PLAN already has a running Work", 1, { run_id: run.id });
|
|
44
|
-
prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage: "plan", dir: "03-plan", status: "running", dataSchemaId: "dd-flow/protocol-plan@6" });
|
|
45
|
+
const attachment = prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage: "plan", dir: "03-plan", status: "running", dataSchemaId: "dd-flow/protocol-plan@6" });
|
|
45
46
|
const root = path.join(home, vnextStageDirectory("plan"));
|
|
46
47
|
if (input.externalContext)
|
|
47
48
|
prepareExternalStageContext({ stageRoot: root, loaded: input.externalContext });
|
|
@@ -58,12 +59,16 @@ export function prepareVnextPlanStart(context, input) {
|
|
|
58
59
|
const git = gitFacts(run.workspace_root);
|
|
59
60
|
for (const file of [...planPaths, ...mapPaths, ...["stage-prompt.md", "work-context.json", "fixture-root.prompt.md"].map(name => path.join(root, name))])
|
|
60
61
|
prepareOutputFile(file);
|
|
61
|
-
|
|
62
|
+
const matrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage: "plan", role: "input", stageAttempt: attachment.attempt });
|
|
63
|
+
return { projectRoot, project, run, home, root, rootWork, workspaceRoute, protocols, template, codeCheckProfile, planPaths, mapPaths, owned, identities, aspects, runVariables, mergeRequired, git, matrix };
|
|
62
64
|
}
|
|
63
65
|
export function startVnextPlan(context, input, prepared = prepareVnextPlanStart(context, input)) {
|
|
64
66
|
const { projectRoot, project, run, root, rootWork, workspaceRoute, protocols, template, codeCheckProfile, planPaths, mapPaths, owned, identities, aspects, runVariables, mergeRequired, git, home } = prepared;
|
|
65
67
|
ensureWorkRegistry(context);
|
|
66
68
|
assertStageStartHookEvent(context, { projectId: project.id, eventKey: input.hookEventId, runId: run.id, stage: "plan", projectRoot, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
|
|
69
|
+
if (context.db.get("SELECT 1 FROM works WHERE project_id = ? AND run_id = ? AND task = ? AND status = 'running'", [project.id, run.id, planTask]))
|
|
70
|
+
throw new AppError("invalid_work_state", "PLAN already has a running Work", 1, { run_id: run.id });
|
|
71
|
+
prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage: "plan", dir: "03-plan", status: "running", dataSchemaId: "dd-flow/protocol-plan@6" }, true);
|
|
67
72
|
fs.mkdirSync(root, { recursive: true });
|
|
68
73
|
const now = context.now();
|
|
69
74
|
// Imported stage fixtures have a synthetic running root with no prior agent.
|
|
@@ -113,9 +118,13 @@ export function startVnextPlan(context, input, prepared = prepareVnextPlanStart(
|
|
|
113
118
|
: `PLAN-REVIEW mode is ${planReviewMode}: include review groups only for genuinely applicable aspects; an empty set is allowed.`;
|
|
114
119
|
fs.writeFileSync(promptPath, `${prompt}\n<review_mode_rule>${reviewModeRule}</review_mode_rule>\n<verification_rule>Every flow check must be an autonomous executable command. Do not declare external/manual proof or a human review as a check, evidence substitute, DEF, or gate. The only user interaction supported by this flow is an explicit stage pause for a material unanswered question.</verification_rule>\n${stageLifecycleInstruction}\n`);
|
|
115
120
|
const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
|
|
121
|
+
if (prepared.matrix)
|
|
122
|
+
fs.appendFileSync(promptPath, verificationMatrixInstruction(prepared.matrix.metadata, home, "designing the source PLAN and its tests"));
|
|
116
123
|
fs.writeFileSync(path.join(root, "work-context.json"), JSON.stringify({ schema_id: "dd-flow/work-context@1", system: { run_id: run.id, work_id: planWorkId, stage: "plan" }, workspace: { project_root: projectRoot, workspace_root: run.workspace_root, stage_root: root }, artifacts: artifactMaterialization, input: { protocols, owned_obligations: Object.fromEntries(owned) } }, null, 2));
|
|
117
124
|
const binding = bindStageCoordinatorWork(context, { workId: planWorkId, hookEventId: input.hookEventId, stage: "plan", promptPath, resultPath: path.join(root, "stage-report.json"), ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
|
|
118
|
-
|
|
125
|
+
const attachmentInput = { projectRoot, runId: run.id, stage: "plan", dir: "03-plan", status: "running", dataSchemaId: "dd-flow/protocol-plan@6" };
|
|
126
|
+
const attachment = prepareFlowRunStageAttachment(context, attachmentInput);
|
|
127
|
+
commitVerificationMatrixStart(context, prepared.matrix, () => attachFlowRunStage(context, attachmentInput, attachment));
|
|
119
128
|
const workSessionId = String(binding.work_session_id);
|
|
120
129
|
const sessionId = String(binding.session_id);
|
|
121
130
|
refreshRunWorkProjection(context, project.id, run.id);
|
|
@@ -169,6 +178,9 @@ function finishVnextPlanOwned(context, input) {
|
|
|
169
178
|
plan_checksums: planFiles.map((file) => ({ path: path.relative(run.workspace_root, file).split(path.sep).join("/"), sha256: checksum(file) })),
|
|
170
179
|
aspect_map_checksums: mapFiles.map((file) => ({ path: path.relative(home, file).split(path.sep).join("/"), sha256: checksum(file) }))
|
|
171
180
|
});
|
|
181
|
+
const matrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage: "plan", role: "output" });
|
|
182
|
+
if (matrix)
|
|
183
|
+
Object.assign(report.semantic, { verification_matrix: matrix.metadata });
|
|
172
184
|
const preparedReport = prepareStageReport(report);
|
|
173
185
|
validateSchema({ schemaName: "stage-report", file: path.join(root, "stage-report.json"), data: preparedReport.normalized, projectRoot, ddFlowHome: context.ddFlowHome, runId: run.id, runRoot: home });
|
|
174
186
|
const completionInput = { projectRoot, runId: run.id, stage: "plan", status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256, git: report.mechanical.git };
|
|
@@ -179,6 +191,7 @@ function finishVnextPlanOwned(context, input) {
|
|
|
179
191
|
throw new AppError("invalid_work_state", "PLAN Work changed during finish preparation", 1);
|
|
180
192
|
context.db.run("UPDATE works SET status = 'completed', result = ?, completed_at = ?, updated_at = ? WHERE work_id = ?", ["PLAN accepted; awaiting PLAN-REVIEW", now, now, work.work_id]);
|
|
181
193
|
context.db.run("UPDATE work_sessions SET status = 'completed', result_path = ?, updated_at = ?, completed_at = ? WHERE id = ?", [path.join(root, "stage-report.json"), now, now, workSession.id]);
|
|
194
|
+
acceptVerificationMatrix(context, matrix);
|
|
182
195
|
acceptStageReport(context, { projectId: project.id, runId: run.id, stage: "plan", root, prepared: preparedReport });
|
|
183
196
|
completeFlowRunStage(context, completionInput, preparedCompletion);
|
|
184
197
|
refreshRunWorkProjection(context, project.id, run.id);
|
|
@@ -25,6 +25,7 @@ import { prepareError, prepareJsonFile } from "../cli/input-preparation.js";
|
|
|
25
25
|
import { lifecycleRetryInstruction } from "../harness-runtime/lib/delegation-instructions.mjs";
|
|
26
26
|
import { acceptedDecisionPrecedence } from "./stage-context.js";
|
|
27
27
|
import { reviewEnvironmentAllowed, reviewExecutionCopyHash, reviewSourceHash, reviewSourcePaths } from "./review-copy.js";
|
|
28
|
+
import { latestRunVerificationMatrix, verificationMatrixInstruction, verificationMatrixContract } from "./verification-matrix.js";
|
|
28
29
|
const workColumns = "work_id, project_id, run_id, parent_work_id, task, launch_policy, result_schema, payload_json, depends_on_json, status, result, created_at, started_at, updated_at, completed_at";
|
|
29
30
|
export function ensureWorkRegistry(context) { context.db.exec("SELECT 1 FROM works LIMIT 1"); context.db.exec("SELECT 1 FROM work_sessions LIMIT 1"); }
|
|
30
31
|
function ownsPhysicalWorkSession(context, projectId, owner, identity) {
|
|
@@ -986,6 +987,16 @@ function assertSharedReviewInput(context, work, run, generation = recoveryGuard(
|
|
|
986
987
|
if (value.cycle !== payload?.review_cycle)
|
|
987
988
|
throw new AppError("review_input_invalid", "CODE-REVIEW cycle differs from its Work", 1);
|
|
988
989
|
}
|
|
990
|
+
const matrix = latestRunVerificationMatrix(context, work.project_id, work.run_id, String(kind), "input");
|
|
991
|
+
const marked = JSON.parse(context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [work.project_id, work.run_id]).index_json).verification_matrix_contract === verificationMatrixContract;
|
|
992
|
+
if (marked && !matrix)
|
|
993
|
+
throw new AppError("review_input_contract_missing", "Marked reviewer Stage lacks its immutable matrix baseline", 1, { stage: kind });
|
|
994
|
+
if (matrix) {
|
|
995
|
+
add("run", matrix.metadata.json.path, matrix.metadata.json.sha256);
|
|
996
|
+
add("run", matrix.metadata.markdown.path, matrix.metadata.markdown.sha256);
|
|
997
|
+
}
|
|
998
|
+
for (const item of value.reviewer_inputs ?? [])
|
|
999
|
+
add("workspace", item.path, item.sha256);
|
|
989
1000
|
const runInputs = [...expected].map(([key, sha]) => { const [root, ...parts] = key.split(":"); return { root: root, path: parts.join(":"), sha256: sha }; }).sort((a, b) => `${a.root}:${a.path}`.localeCompare(`${b.root}:${b.path}`));
|
|
990
1001
|
for (const item of runInputs) {
|
|
991
1002
|
const target = reviewInputPath(item.root === "run" ? home : run.workspace_root, item.path);
|
|
@@ -1236,7 +1247,10 @@ export function validateReviewResolutions(workId, assigned, resolutions) {
|
|
|
1236
1247
|
}
|
|
1237
1248
|
function renderWorkerPrompt(context, work, run, dependencies, commands) {
|
|
1238
1249
|
const body = renderWorkerPromptBody(context, work, run, dependencies, commands);
|
|
1239
|
-
|
|
1250
|
+
const kind = parsePayload(work)?.kind;
|
|
1251
|
+
const matrixStage = kind === "plan-review" || kind === "code-review" ? kind : codePacket(work) ? "code" : null;
|
|
1252
|
+
const matrix = matrixStage ? latestRunVerificationMatrix(context, work.project_id, work.run_id, matrixStage, kind === "plan-review" || kind === "code-review" ? "input" : undefined) : null;
|
|
1253
|
+
return ["<accepted_decision_precedence>", `Read ${path.join(requireRunHome(run), "01-specify", "specify.json")} and later accepted RUN decisions relevant to this Work.`, acceptedDecisionPrecedence, "</accepted_decision_precedence>", "", body, verificationMatrixInstruction(matrix?.metadata ?? null, requireRunHome(run), `assigned Work ${work.work_id}`)].join("\n");
|
|
1240
1254
|
}
|
|
1241
1255
|
function workerLifecycleCommands(context, work, run) {
|
|
1242
1256
|
const resultInputPath = path.join(requireRunHome(run), "works", work.work_id, "result-input.json");
|
package/dist/storage/database.js
CHANGED
|
@@ -331,7 +331,16 @@ export function ensureWorkLaunchStorage(db) {
|
|
|
331
331
|
CREATE INDEX IF NOT EXISTS idx_work_launches_status ON work_launches(status, updated_at DESC);
|
|
332
332
|
`);
|
|
333
333
|
}
|
|
334
|
-
export function getDatabase(ddFlowHome, mode = "initialize", importPermitId) {
|
|
334
|
+
export function getDatabase(ddFlowHome, mode = "initialize", importPermitId, hookDeadlineAt) {
|
|
335
|
+
if (hookDeadlineAt !== undefined && (!Number.isSafeInteger(hookDeadlineAt) || mode !== "hook"))
|
|
336
|
+
throw new AppError("hook_deadline_invalid", "Hook deadline must be a finite absolute millisecond deadline", 1, { effect: "unknown" });
|
|
337
|
+
const deadline = hookDeadlineAt === undefined ? undefined : (phase) => {
|
|
338
|
+
const remaining = hookDeadlineAt - Date.now();
|
|
339
|
+
if (remaining <= 0)
|
|
340
|
+
throw new AppError("native_hook_timeout", "Native hook deadline expired before durable admission", 1, { phase, deadline_at: hookDeadlineAt, effect: "unknown" });
|
|
341
|
+
return Math.max(1, Math.min(4000, remaining));
|
|
342
|
+
};
|
|
343
|
+
deadline?.("store_open");
|
|
335
344
|
if (mode !== "read_existing")
|
|
336
345
|
assertImportAdmission(ddFlowHome, importPermitId);
|
|
337
346
|
const dbPath = path.join(ddFlowHome, "db.sqlite");
|
|
@@ -344,7 +353,12 @@ export function getDatabase(ddFlowHome, mode = "initialize", importPermitId) {
|
|
|
344
353
|
if (!exists || mode === "migrate")
|
|
345
354
|
ensureDir(ddFlowHome);
|
|
346
355
|
const db = mode === "read_existing" ? new DatabaseSync(dbPath, { readOnly: true }) : new DatabaseSync(dbPath);
|
|
347
|
-
|
|
356
|
+
try {
|
|
357
|
+
db.exec(`PRAGMA busy_timeout = ${deadline?.("store_open") ?? 4000}`);
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
return closeAfterOpenFailure(db, error);
|
|
361
|
+
}
|
|
348
362
|
if (mode !== "read_existing") {
|
|
349
363
|
try {
|
|
350
364
|
registerStoreWriter(db);
|
|
@@ -354,7 +368,7 @@ export function getDatabase(ddFlowHome, mode = "initialize", importPermitId) {
|
|
|
354
368
|
}
|
|
355
369
|
}
|
|
356
370
|
try {
|
|
357
|
-
configureDatabase(db, mode);
|
|
371
|
+
configureDatabase(db, mode, deadline);
|
|
358
372
|
}
|
|
359
373
|
catch (error) {
|
|
360
374
|
return closeAfterOpenFailure(db, error);
|
|
@@ -374,6 +388,7 @@ export function getDatabase(ddFlowHome, mode = "initialize", importPermitId) {
|
|
|
374
388
|
migrateSessionStorageKeys(db, dbPath);
|
|
375
389
|
}
|
|
376
390
|
else {
|
|
391
|
+
deadline?.("store_validation");
|
|
377
392
|
assertHookStoreReady(db);
|
|
378
393
|
}
|
|
379
394
|
}
|
|
@@ -384,7 +399,7 @@ export function getDatabase(ddFlowHome, mode = "initialize", importPermitId) {
|
|
|
384
399
|
if (mode === "read_existing")
|
|
385
400
|
db.exec("PRAGMA query_only = ON");
|
|
386
401
|
db.exec("PRAGMA foreign_keys = ON");
|
|
387
|
-
return databaseFacade(dbPath, mode !== "read_existing", db);
|
|
402
|
+
return databaseFacade(dbPath, mode !== "read_existing", db, undefined, deadline);
|
|
388
403
|
}
|
|
389
404
|
/**
|
|
390
405
|
* Hook ingress is a hot, synchronous path. It may write receipts, but it
|
|
@@ -554,7 +569,7 @@ function emptyReadOnlyDatabase(dbPath) {
|
|
|
554
569
|
all: () => []
|
|
555
570
|
};
|
|
556
571
|
}
|
|
557
|
-
function databaseFacade(dbPath, writable, db, close = () => db.close?.()) {
|
|
572
|
+
function databaseFacade(dbPath, writable, db, close = () => db.close?.(), deadline) {
|
|
558
573
|
let reserved = false;
|
|
559
574
|
let poisoned = false;
|
|
560
575
|
let closed = false;
|
|
@@ -567,6 +582,14 @@ function databaseFacade(dbPath, writable, db, close = () => db.close?.()) {
|
|
|
567
582
|
let manualFrames = [];
|
|
568
583
|
let readDepth = 0;
|
|
569
584
|
const writerTiming = channel("dd-flow.sqlite.writer");
|
|
585
|
+
const ingressPhase = channel("dd-flow.sqlite.ingress");
|
|
586
|
+
const bound = (phase) => {
|
|
587
|
+
const remaining = deadline?.(phase);
|
|
588
|
+
if (remaining !== undefined)
|
|
589
|
+
db.exec(`PRAGMA busy_timeout = ${remaining}`);
|
|
590
|
+
if (ingressPhase.hasSubscribers)
|
|
591
|
+
ingressPhase.publish({ phase, ...(remaining !== undefined ? { remaining_ms: remaining } : {}) });
|
|
592
|
+
};
|
|
570
593
|
const publishCommitted = () => {
|
|
571
594
|
const pending = afterCommit;
|
|
572
595
|
afterCommit = [];
|
|
@@ -584,8 +607,11 @@ function databaseFacade(dbPath, writable, db, close = () => db.close?.()) {
|
|
|
584
607
|
throw new AppError("read_snapshot_write", "Cannot reserve a writer inside a read snapshot", 1);
|
|
585
608
|
if (!writable)
|
|
586
609
|
throw new AppError("read_only_storage", "Read-only access cannot start a write transaction", 1, { path: dbPath });
|
|
610
|
+
bound("writer_reservation");
|
|
587
611
|
db.exec("BEGIN IMMEDIATE");
|
|
588
612
|
reserved = true;
|
|
613
|
+
if (ingressPhase.hasSubscribers)
|
|
614
|
+
ingressPhase.publish({ phase: "writer_reserved" });
|
|
589
615
|
};
|
|
590
616
|
const rollback = (sql, original) => {
|
|
591
617
|
try {
|
|
@@ -626,6 +652,8 @@ function databaseFacade(dbPath, writable, db, close = () => db.close?.()) {
|
|
|
626
652
|
beginWriteTransaction();
|
|
627
653
|
}
|
|
628
654
|
catch (error) {
|
|
655
|
+
if (error instanceof AppError)
|
|
656
|
+
throw error;
|
|
629
657
|
throw new AppError("storage_write_failed", "Cannot reserve SQLite writer", 1, { cause: errorDetails(error), transaction_phase: "begin", transaction_rolled_back: true });
|
|
630
658
|
}
|
|
631
659
|
let phase = "body";
|
|
@@ -636,11 +664,14 @@ function databaseFacade(dbPath, writable, db, close = () => db.close?.()) {
|
|
|
636
664
|
throw new AppError("write_transaction_async", "Write transaction callback must be synchronous", 1);
|
|
637
665
|
}
|
|
638
666
|
phase = "commit";
|
|
667
|
+
bound("commit");
|
|
639
668
|
if (nested)
|
|
640
669
|
db.exec(`RELEASE ${marker}`);
|
|
641
670
|
else
|
|
642
671
|
db.exec("COMMIT");
|
|
643
672
|
phase = "committed";
|
|
673
|
+
if (ingressPhase.hasSubscribers)
|
|
674
|
+
ingressPhase.publish({ phase: "committed" });
|
|
644
675
|
if (!nested && writerTiming.hasSubscribers)
|
|
645
676
|
writerTiming.publish({ operation: operationName, wall_time_ms: performance.now() - startedAt, path: dbPath });
|
|
646
677
|
if (!nested)
|
|
@@ -786,22 +817,25 @@ function databaseFacade(dbPath, writable, db, close = () => db.close?.()) {
|
|
|
786
817
|
executePublications([operation]);
|
|
787
818
|
} },
|
|
788
819
|
run: (sql, params = []) => { assertUsable(); if (readDepth)
|
|
789
|
-
throw new AppError("read_snapshot_write", "Cannot write inside a read snapshot", 1); return db.prepare(sql).run(...params); },
|
|
790
|
-
get: (sql, params = []) => { assertUsable(); return db.prepare(sql).get(...params); },
|
|
791
|
-
all: (sql, params = []) => { assertUsable(); return db.prepare(sql).all(...params); }
|
|
820
|
+
throw new AppError("read_snapshot_write", "Cannot write inside a read snapshot", 1); bound("write"); return db.prepare(sql).run(...params); },
|
|
821
|
+
get: (sql, params = []) => { assertUsable(); bound("lookup"); return db.prepare(sql).get(...params); },
|
|
822
|
+
all: (sql, params = []) => { assertUsable(); bound("lookup"); return db.prepare(sql).all(...params); }
|
|
792
823
|
};
|
|
793
824
|
}
|
|
794
825
|
function errorDetails(error) {
|
|
795
826
|
const value = error;
|
|
796
827
|
return { message: String(value?.message ?? error), ...(typeof value?.code === "string" ? { code: value.code } : {}), ...(typeof value?.errcode === "number" ? { sqlite_extended_code: value.errcode } : {}) };
|
|
797
828
|
}
|
|
798
|
-
function configureDatabase(db, mode) {
|
|
829
|
+
function configureDatabase(db, mode, ingressDeadline) {
|
|
799
830
|
if (mode !== "read_existing") {
|
|
800
831
|
// Concurrent first openers can collide while changing journal mode, even
|
|
801
832
|
// with busy_timeout set. Retrying this configuration does not replay writes.
|
|
802
833
|
const deadline = performance.now() + 4000, pause = new Int32Array(new SharedArrayBuffer(4));
|
|
803
834
|
for (;;) {
|
|
804
835
|
try {
|
|
836
|
+
const remaining = ingressDeadline?.("store_configuration");
|
|
837
|
+
if (remaining !== undefined)
|
|
838
|
+
db.exec(`PRAGMA busy_timeout = ${remaining}`);
|
|
805
839
|
const current = db.prepare("PRAGMA journal_mode").get();
|
|
806
840
|
if (current.journal_mode !== "wal")
|
|
807
841
|
db.exec("PRAGMA journal_mode = WAL");
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deksden-com/dd-flow-cli",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
3
|
+
"version": "0.9.0-beta.123",
|
|
4
4
|
"description": "Mechanical runtime CLI for dd-flow workflows.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@10.23.0",
|
|
6
7
|
"bin": {
|
|
7
8
|
"dd-flow": "dist/cli.js",
|
|
8
9
|
"dd-codex": "dist/harness-runtime/bin/dd-codex.mjs",
|
|
@@ -49,6 +50,8 @@
|
|
|
49
50
|
"ajv": "^8.20.0",
|
|
50
51
|
"shell-quote": "^1.10.0",
|
|
51
52
|
"smol-toml": "^1.6.1",
|
|
53
|
+
"tree-sitter": "0.25.1",
|
|
54
|
+
"tree-sitter-bash": "0.25.1",
|
|
52
55
|
"yaml": "^2.9.0"
|
|
53
56
|
},
|
|
54
57
|
"publishConfig": {
|