@deksden-com/dd-flow-cli 0.9.0-beta.21 → 0.9.0-beta.24
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 +25 -0
- package/README.md +9 -0
- package/dist/build-info.json +3 -3
- package/dist/cli/help.js +1 -0
- package/dist/cli/run-cli.js +3 -0
- package/dist/cli.js +0 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +34 -0
- package/dist/harness-runtime/bin/dd-codex.mjs +22 -0
- package/dist/harness-runtime/bin/dd-droid.mjs +30 -0
- package/dist/harness-runtime/bin/dd-grok.mjs +28 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +18 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +62 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +78 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +259 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +59 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +93 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +387 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +123 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +435 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +282 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +168 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +157 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +80 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +419 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +598 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +129 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +119 -0
- package/dist/harness-runtime/lib/model-observations.mjs +77 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +33 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +19 -0
- package/dist/harness-runtime/lib/process-json.mjs +61 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/runner-events.mjs +200 -0
- package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
- package/dist/harness-runtime/lib/session-settlement.mjs +28 -0
- package/dist/services/code-checks.js +25 -11
- package/dist/services/execution-policy.js +61 -0
- package/dist/services/external-work-launch.js +76 -0
- package/dist/services/harness-adapter.js +31 -14
- package/dist/services/hooks.js +43 -20
- package/dist/services/runs.js +2 -1
- package/dist/services/stage-context.js +2 -2
- package/dist/services/vnext-execution-profile.js +38 -3
- package/dist/services/work-registry.js +8 -1
- package/dist/storage/database.js +21 -0
- package/package.json +8 -2
package/dist/services/hooks.js
CHANGED
|
@@ -324,10 +324,12 @@ export function handleCodexHook(context, input) {
|
|
|
324
324
|
// Codex retains the root session_id for a thread-spawned child, while the
|
|
325
325
|
// payload transcript belongs to the current agent_id. Keep lineage separate
|
|
326
326
|
// from ownership: otherwise a child overwrites the root transcript binding.
|
|
327
|
-
const
|
|
328
|
-
const binding = bindingOwnerId ? upsertSessionBindingFromPayload(context, project, bindingOwnerId, payload) : undefined;
|
|
327
|
+
const eventKey = hookEventKey(payload, eventName, toolName, command);
|
|
329
328
|
const storageId = providerSessionId ? storageSessionId(nativeSessionIdentity("codex-desktop", providerSessionId)) : null;
|
|
330
329
|
const parentSessionId = parentProviderSessionId ? storageSessionId(nativeSessionIdentity("codex-desktop", parentProviderSessionId)) : null;
|
|
330
|
+
assertHookEventReplay(context, { projectId: project.id, eventKey, harness: "codex-desktop", providerSessionId: providerSessionId ?? null, parentSessionId, daemonId: stringValue(context.env.DD_FLOW_DAEMON_ID) ?? null, sessionId: storageId, turnId: turnId ?? null, eventName, toolName: toolName ?? null, matchKey, cwd: stringValue(payload.cwd) ?? null });
|
|
331
|
+
const bindingOwnerId = providerSessionId ?? rootProviderSessionId;
|
|
332
|
+
const binding = bindingOwnerId ? upsertSessionBindingFromPayload(context, project, bindingOwnerId, payload) : undefined;
|
|
331
333
|
const observedSession = flowPayload
|
|
332
334
|
? bindObservedFlowSession(context, project, {
|
|
333
335
|
...flowPayload,
|
|
@@ -338,7 +340,6 @@ export function handleCodexHook(context, input) {
|
|
|
338
340
|
: undefined;
|
|
339
341
|
const effectiveSessionId = observedSession?.session_id ?? storageId;
|
|
340
342
|
const protocolId = observedSession?.protocol_id ?? binding?.protocol_id ?? null;
|
|
341
|
-
const eventKey = hookEventKey(payload, eventName, toolName, command);
|
|
342
343
|
const inserted = recordHookEvent(context, {
|
|
343
344
|
projectId: project.id,
|
|
344
345
|
protocolId,
|
|
@@ -458,6 +459,10 @@ export function handleZcodeEvent(context, input) {
|
|
|
458
459
|
const sessionId = storageSessionId(nativeSessionIdentity("zcode-acp", providerSessionId));
|
|
459
460
|
const parentSessionId = childProviderSessionId ? storageSessionId(nativeSessionIdentity("zcode-acp", rootProviderSessionId)) : null;
|
|
460
461
|
const agentId = stringValue(runtime.agentId);
|
|
462
|
+
const eventKey = stringValue(update.toolCallId) ?? crypto.createHash("sha256").update(JSON.stringify({
|
|
463
|
+
harness: "zcode-acp", providerSessionId, command
|
|
464
|
+
})).digest("hex");
|
|
465
|
+
assertHookEventReplay(context, { projectId: project.id, eventKey, harness: "zcode-acp", providerSessionId, parentSessionId, daemonId: daemonId ?? null, sessionId, turnId: null, eventName: "PreToolUse", toolName: toolName ?? "Bash", matchKey, cwd: expectedRoot });
|
|
461
466
|
const observedSession = flowPayload ? bindObservedFlowSession(context, project, {
|
|
462
467
|
...flowPayload,
|
|
463
468
|
harness: "zcode-acp",
|
|
@@ -465,9 +470,6 @@ export function handleZcodeEvent(context, input) {
|
|
|
465
470
|
agent_id: agentId ?? null,
|
|
466
471
|
parent_session_id: parentSessionId
|
|
467
472
|
}, sessionId) : undefined;
|
|
468
|
-
const eventKey = stringValue(update.toolCallId) ?? crypto.createHash("sha256").update(JSON.stringify({
|
|
469
|
-
harness: "zcode-acp", providerSessionId, command
|
|
470
|
-
})).digest("hex");
|
|
471
473
|
const payload = {
|
|
472
474
|
session_id: providerSessionId,
|
|
473
475
|
agent_id: agentId,
|
|
@@ -564,12 +566,13 @@ export function handleGrokEvent(context, input) {
|
|
|
564
566
|
const parentSessionId = isChild ? storageSessionId(nativeSessionIdentity("grok-acp", stringValue(ddGrok.parentProviderSessionId) ?? rootProviderSessionId)) : null;
|
|
565
567
|
const daemonId = stringValue(ddGrok.daemonId);
|
|
566
568
|
const agentId = stringValue(hook.agent_id) ?? stringValue(hook.agentId);
|
|
567
|
-
const observedSession = flowPayload ? bindObservedFlowSession(context, project, {
|
|
568
|
-
...flowPayload, harness: "grok-acp", provider_session_id: providerSessionId, agent_id: agentId ?? null, parent_session_id: parentSessionId
|
|
569
|
-
}, sessionId) : undefined;
|
|
570
569
|
const eventKey = stringValue(hook.event_id) ?? stringValue(hook.eventId) ?? crypto.createHash("sha256").update(JSON.stringify({
|
|
571
570
|
harness: "grok-acp", providerSessionId, command, turn: hook.turn_id ?? hook.turnId ?? null
|
|
572
571
|
})).digest("hex");
|
|
572
|
+
assertHookEventReplay(context, { projectId: project.id, eventKey, harness: "grok-acp", providerSessionId, parentSessionId, daemonId: daemonId ?? null, sessionId, turnId: stringValue(hook.turn_id) ?? stringValue(hook.turnId) ?? null, eventName: "PreToolUse", toolName: toolName ?? "Bash", matchKey, cwd: expectedRoot });
|
|
573
|
+
const observedSession = flowPayload ? bindObservedFlowSession(context, project, {
|
|
574
|
+
...flowPayload, harness: "grok-acp", provider_session_id: providerSessionId, agent_id: agentId ?? null, parent_session_id: parentSessionId
|
|
575
|
+
}, sessionId) : undefined;
|
|
573
576
|
const payload = {
|
|
574
577
|
...hook, session_id: providerSessionId, agent_id: agentId, cwd: expectedRoot, tool_name: toolName ?? "Bash", tool_input: rawInput,
|
|
575
578
|
command, provider: stringValue(observedProfile.provider), model: stringValue(observedProfile.model), reasoning: stringValue(observedProfile.reasoning), mode: stringValue(observedProfile.mode), agent_type: stringValue(hook.subagent_type) ?? stringValue(hook.subagentType)
|
|
@@ -668,6 +671,7 @@ function handleControlledToolEvent(context, input, event, harness) {
|
|
|
668
671
|
}
|
|
669
672
|
}
|
|
670
673
|
const agentId = stringValue(session.agent_id);
|
|
674
|
+
assertHookEventReplay(context, { projectId: project.id, eventKey: baseKey, harness, providerSessionId, parentSessionId, daemonId, sessionId, turnId: stringValue(event.message_id) ?? null, eventName: "PreToolUse", toolName, matchKey, cwd: expectedRoot });
|
|
671
675
|
const observedSession = flowPayload ? bindObservedFlowSession(context, project, {
|
|
672
676
|
...flowPayload, harness, provider_session_id: providerSessionId, agent_id: agentId ?? null,
|
|
673
677
|
parent_session_id: parentSessionId, provider: stringValue(profile.provider) ?? null, model: stringValue(profile.model) ?? null,
|
|
@@ -914,21 +918,24 @@ export function claimRecentMatchingHookEvent(context, input) {
|
|
|
914
918
|
/** Finds the trusted event that a stage- or Work-specific claimant will atomically claim. */
|
|
915
919
|
export function findRecentMatchingHookEvent(context, input) {
|
|
916
920
|
const freshAfter = new Date(Date.parse(context.now()) - 60_000).toISOString();
|
|
917
|
-
const find = () => context.db.
|
|
921
|
+
const find = () => context.db.all(`SELECT id, event_key FROM hook_events
|
|
918
922
|
WHERE project_id = ? AND match_key = ? AND status = 'observed' AND created_at >= ?
|
|
919
|
-
ORDER BY id DESC LIMIT
|
|
920
|
-
let
|
|
923
|
+
ORDER BY id DESC LIMIT 2`, [input.projectId, input.matchKey, freshAfter]);
|
|
924
|
+
let events = find();
|
|
921
925
|
// Some harnesses publish nested-agent tool events concurrently with command
|
|
922
926
|
// execution. Give the trusted event a short bounded window to reach SQLite.
|
|
923
927
|
const deadline = Date.now() + 250;
|
|
924
|
-
while (
|
|
928
|
+
while (events.length === 0 && Date.now() < deadline) {
|
|
925
929
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
|
|
926
|
-
|
|
930
|
+
events = find();
|
|
927
931
|
}
|
|
928
|
-
if (!
|
|
932
|
+
if (events.length === 0 || !events[0]?.event_key) {
|
|
929
933
|
throw new AppError(input.errorCode, `${input.operation} requires one matching harness lifecycle event. Stay in this Session and retry the exact standalone lifecycle command; do not create a new Session.`, 1);
|
|
930
934
|
}
|
|
931
|
-
|
|
935
|
+
if (events.length > 1) {
|
|
936
|
+
throw new AppError("ambiguous_lifecycle_receipt", `${input.operation} has more than one fresh matching lifecycle event; provide its exact hook event ID`, 1, { match_key: input.matchKey, event_keys: events.map((event) => event.event_key) });
|
|
937
|
+
}
|
|
938
|
+
return { id: events[0].id, eventKey: events[0].event_key };
|
|
932
939
|
}
|
|
933
940
|
/** Fallback for Desktop paths that execute the original command after a hook rewrite. */
|
|
934
941
|
export function claimBootstrapHookEvent(context, input) {
|
|
@@ -1405,12 +1412,11 @@ function sessionBinding(context, projectId, sessionId) {
|
|
|
1405
1412
|
return context.db.get("SELECT * FROM codex_session_bindings WHERE project_id = ? AND session_id = ?", [projectId, sessionId]);
|
|
1406
1413
|
}
|
|
1407
1414
|
function recordHookEvent(context, input) {
|
|
1408
|
-
|
|
1409
|
-
if (existing)
|
|
1415
|
+
if (assertHookEventReplay(context, input))
|
|
1410
1416
|
return false;
|
|
1411
1417
|
context.db.run(`INSERT INTO hook_events
|
|
1412
|
-
|
|
1413
|
-
|
|
1418
|
+
(project_id, protocol_id, harness, provider_session_id, parent_session_id, daemon_id, session_id, agent_id, turn_id, transcript_path, provider, model, reasoning, mode, agent_type, cwd, event_key, match_key, event_name, tool_name, status, sanitized_summary, created_at)
|
|
1419
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
1414
1420
|
input.projectId,
|
|
1415
1421
|
input.protocolId,
|
|
1416
1422
|
input.harness,
|
|
@@ -1437,6 +1443,23 @@ function recordHookEvent(context, input) {
|
|
|
1437
1443
|
]);
|
|
1438
1444
|
return true;
|
|
1439
1445
|
}
|
|
1446
|
+
/** A duplicate delivery is harmless only when all durable identity facts match. */
|
|
1447
|
+
function assertHookEventReplay(context, input) {
|
|
1448
|
+
const existing = context.db.get(`SELECT harness, provider_session_id, parent_session_id, daemon_id, session_id, turn_id, event_name, tool_name, match_key, cwd
|
|
1449
|
+
FROM hook_events WHERE project_id = ? AND event_key = ?`, [input.projectId, input.eventKey]);
|
|
1450
|
+
if (existing) {
|
|
1451
|
+
const observed = {
|
|
1452
|
+
harness: input.harness, provider_session_id: input.providerSessionId, parent_session_id: input.parentSessionId,
|
|
1453
|
+
daemon_id: input.daemonId ?? null, session_id: input.sessionId, turn_id: input.turnId,
|
|
1454
|
+
event_name: input.eventName, tool_name: input.toolName, match_key: input.matchKey, cwd: input.cwd
|
|
1455
|
+
};
|
|
1456
|
+
if (Object.entries(observed).some(([key, value]) => existing[key] !== value)) {
|
|
1457
|
+
throw new AppError("hook_event_replay_mismatch", "Lifecycle event ID was already observed with different immutable identity", 1, { event_key: input.eventKey });
|
|
1458
|
+
}
|
|
1459
|
+
return true;
|
|
1460
|
+
}
|
|
1461
|
+
return false;
|
|
1462
|
+
}
|
|
1440
1463
|
function projectForHook(context, explicitRoot, cwd) {
|
|
1441
1464
|
if (explicitRoot)
|
|
1442
1465
|
return requireProjectByRoot(context, resolveProjectRoot(explicitRoot));
|
package/dist/services/runs.js
CHANGED
|
@@ -189,7 +189,8 @@ function snapshotVnextExecutionProfile(projectRoot) {
|
|
|
189
189
|
merge_delivery: profile.merge_delivery,
|
|
190
190
|
merge_cleanup: profile.merge_cleanup,
|
|
191
191
|
stop_target: profile.stop_target,
|
|
192
|
-
code_bootstrap: profile.code_bootstrap
|
|
192
|
+
code_bootstrap: profile.code_bootstrap,
|
|
193
|
+
...(profile.execution ? { execution: profile.execution } : {})
|
|
193
194
|
}
|
|
194
195
|
};
|
|
195
196
|
}
|
|
@@ -76,8 +76,8 @@ export function renderExternalStageContext(input) {
|
|
|
76
76
|
].join("\n");
|
|
77
77
|
}
|
|
78
78
|
function validateContext(value, expectedStage) {
|
|
79
|
-
if (value.schema_id !== "dd-eval/stage-context@1")
|
|
80
|
-
throw new AppError("validation", "Stage context must use dd-
|
|
79
|
+
if (value.schema_id !== "dd-flow/stage-context@1" && value.schema_id !== "dd-eval/stage-context@1")
|
|
80
|
+
throw new AppError("validation", "Stage context must use dd-flow/stage-context@1", 2);
|
|
81
81
|
if (value.stage !== expectedStage)
|
|
82
82
|
throw new AppError("validation", "Stage context does not match --stage", 2, { expected_stage: expectedStage, context_stage: value.stage });
|
|
83
83
|
if (!value.objective?.trim())
|
|
@@ -13,7 +13,7 @@ export function loadVnextExecutionProfile(projectRoot) {
|
|
|
13
13
|
throw new AppError("execution_profile_missing", "vNext flow requires .memory-bank/dd-flow/project-execution.json", 1, { file, cause: String(error) });
|
|
14
14
|
}
|
|
15
15
|
const profile = value;
|
|
16
|
-
if (profile.schema_id !== "dd-flow/project-execution@2"
|
|
16
|
+
if ((profile.schema_id !== "dd-flow/project-execution@2" && profile.schema_id !== "dd-flow/project-execution@3")
|
|
17
17
|
|| (profile.stage_session_mode !== undefined && profile.stage_session_mode !== "same_session" && profile.stage_session_mode !== "new_session")
|
|
18
18
|
|| !["auto", "off", "standard", "deep"].includes(String(profile.plan_review_mode))
|
|
19
19
|
|| (profile.code_review_mode !== undefined && !["auto", "off", "standard", "deep"].includes(String(profile.code_review_mode)))
|
|
@@ -22,9 +22,44 @@ export function loadVnextExecutionProfile(projectRoot) {
|
|
|
22
22
|
|| !["retain", "delete_after_success"].includes(String(profile.merge_cleanup?.source))
|
|
23
23
|
|| !["code_completed", "code_review_completed", "merge_completed"].includes(String(profile.stop_target))
|
|
24
24
|
|| !profile.code_bootstrap || typeof profile.code_bootstrap.command !== "string" || !profile.code_bootstrap.command.trim()
|
|
25
|
-
|| typeof profile.code_bootstrap.policy_ref !== "string" || !profile.code_bootstrap.policy_ref.trim()
|
|
26
|
-
|
|
25
|
+
|| typeof profile.code_bootstrap.policy_ref !== "string" || !profile.code_bootstrap.policy_ref.trim()
|
|
26
|
+
|| !validRouting(profile.execution, profile.schema_id === "dd-flow/project-execution@3")) {
|
|
27
|
+
throw new AppError("execution_profile_invalid", "Project execution profile does not match its supported dd-flow schema", 1, { file });
|
|
27
28
|
}
|
|
28
29
|
return { ...profile, stage_session_mode: profile.stage_session_mode ?? "same_session", code_review_mode: profile.code_review_mode ?? "off" };
|
|
29
30
|
}
|
|
30
31
|
export function executionProfilePath(projectRoot) { return path.join(projectRoot, relativePath); }
|
|
32
|
+
function validRouting(value, required) {
|
|
33
|
+
if (value === undefined)
|
|
34
|
+
return !required;
|
|
35
|
+
if (!object(value) || !profileId(value.agent_profile_id) || !override(value, true))
|
|
36
|
+
return false;
|
|
37
|
+
if (value.stage_overrides !== undefined) {
|
|
38
|
+
if (!object(value.stage_overrides))
|
|
39
|
+
return false;
|
|
40
|
+
for (const [stage, item] of Object.entries(value.stage_overrides))
|
|
41
|
+
if (!stage.trim() || !object(item) || !override(item, false))
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
function override(value, root) {
|
|
47
|
+
const allowed = root ? new Set(["agent_profile_id", "delegation", "stage_overrides"]) : new Set(["agent_profile_id", "delegation"]);
|
|
48
|
+
if (Object.keys(value).some(key => !allowed.has(key)))
|
|
49
|
+
return false;
|
|
50
|
+
if (!root && value.agent_profile_id === undefined && value.delegation === undefined)
|
|
51
|
+
return false;
|
|
52
|
+
if (value.agent_profile_id !== undefined && !profileId(value.agent_profile_id))
|
|
53
|
+
return false;
|
|
54
|
+
if (value.delegation === undefined)
|
|
55
|
+
return true;
|
|
56
|
+
if (!object(value.delegation) || (value.delegation.mode !== "native" && value.delegation.mode !== "external"))
|
|
57
|
+
return false;
|
|
58
|
+
if (value.delegation.agent_profile_id !== undefined && !profileId(value.delegation.agent_profile_id))
|
|
59
|
+
return false;
|
|
60
|
+
if (value.delegation.max_parallel !== undefined && (!Number.isInteger(value.delegation.max_parallel) || Number(value.delegation.max_parallel) < 1))
|
|
61
|
+
return false;
|
|
62
|
+
return value.delegation.mode !== "native" || value.delegation.max_parallel === undefined;
|
|
63
|
+
}
|
|
64
|
+
function profileId(value) { return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value); }
|
|
65
|
+
function object(value) { return Boolean(value) && typeof value === "object" && !Array.isArray(value); }
|
|
@@ -245,7 +245,14 @@ export function startWork(context, id, input) {
|
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
const identity = claimWorkStartHookEvent(context, { projectId: work.project_id, eventKey: hookEventId, workId: work.work_id, projectRoot: run.project_root, ...(recoveryId ? { recoveryId } : {}) });
|
|
248
|
-
|
|
248
|
+
let started;
|
|
249
|
+
try {
|
|
250
|
+
started = startBoundWork(context, work, run, identity, hookEventId);
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
context.db.run("UPDATE hook_events SET status = 'observed', claimed_at = NULL WHERE project_id = ? AND event_key = ? AND status = 'claimed'", [work.project_id, hookEventId]);
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
249
256
|
appendFlowRunTimelineEvent(context, work.project_id, work.run_id, { type: "work_started", work_id: work.work_id, session_id: identity.sessionId, agent_id: identity.agentId });
|
|
250
257
|
return started;
|
|
251
258
|
}
|
package/dist/storage/database.js
CHANGED
|
@@ -116,6 +116,27 @@ export function ensureVnextWorkStorage(db) {
|
|
|
116
116
|
CREATE INDEX IF NOT EXISTS idx_check_receipts_run ON check_receipts(project_id, run_id, started_at);
|
|
117
117
|
`);
|
|
118
118
|
}
|
|
119
|
+
/** Durable intent for a provider-root external Work. It is deliberately
|
|
120
|
+
* separate from `work_sessions`: only a trusted hook may bind that Session. */
|
|
121
|
+
export function ensureWorkLaunchStorage(db) {
|
|
122
|
+
db.exec(`
|
|
123
|
+
CREATE TABLE IF NOT EXISTS work_launches (
|
|
124
|
+
operation_id TEXT PRIMARY KEY,
|
|
125
|
+
work_id TEXT NOT NULL UNIQUE,
|
|
126
|
+
profile_json TEXT NOT NULL,
|
|
127
|
+
profile_hash TEXT NOT NULL,
|
|
128
|
+
status TEXT NOT NULL,
|
|
129
|
+
state_dir TEXT NOT NULL,
|
|
130
|
+
journal_path TEXT NOT NULL,
|
|
131
|
+
provider_session_id TEXT,
|
|
132
|
+
adapter_receipt_json TEXT,
|
|
133
|
+
created_at TEXT NOT NULL,
|
|
134
|
+
updated_at TEXT NOT NULL,
|
|
135
|
+
FOREIGN KEY(work_id) REFERENCES works(work_id)
|
|
136
|
+
);
|
|
137
|
+
CREATE INDEX IF NOT EXISTS idx_work_launches_status ON work_launches(status, updated_at DESC);
|
|
138
|
+
`);
|
|
139
|
+
}
|
|
119
140
|
export function getDatabase(ddFlowHome, mode = "initialize") {
|
|
120
141
|
const dbPath = path.join(ddFlowHome, "db.sqlite");
|
|
121
142
|
const exists = fs.existsSync(dbPath);
|
package/package.json
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deksden-com/dd-flow-cli",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
3
|
+
"version": "0.9.0-beta.24",
|
|
4
4
|
"description": "Mechanical runtime CLI for dd-flow workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"dd-flow": "
|
|
7
|
+
"dd-flow": "dist/cli.js",
|
|
8
|
+
"dd-codex": "dist/harness-runtime/bin/dd-codex.mjs",
|
|
9
|
+
"dd-zcode": "dist/harness-runtime/bin/dd-zcode.mjs",
|
|
10
|
+
"dd-grok": "dist/harness-runtime/bin/dd-grok.mjs",
|
|
11
|
+
"dd-opencode": "dist/harness-runtime/bin/dd-opencode.mjs",
|
|
12
|
+
"dd-agy": "dist/harness-runtime/bin/dd-agy.mjs",
|
|
13
|
+
"dd-droid": "dist/harness-runtime/bin/dd-droid.mjs"
|
|
8
14
|
},
|
|
9
15
|
"files": [
|
|
10
16
|
"dist",
|