@evoclock/pi-agentic-driver 1.1.2 → 2.0.0

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.
Files changed (114) hide show
  1. package/.agentic-driver/loop-until-done.defaults.json +5 -0
  2. package/README.md +302 -41
  3. package/config/merge-policy.v1.example.json +4 -0
  4. package/config/relay-authority.v1.example.json +7 -0
  5. package/config/treehouse.v1.json +4 -0
  6. package/docs/durable-driver-api.md +23 -0
  7. package/docs/durable-persistence.md +32 -0
  8. package/docs/merge-policy.md +45 -0
  9. package/docs/relay-authority.md +9 -0
  10. package/extensions/aidr.ts +3 -0
  11. package/extensions/harvest-lessons.ts +8 -0
  12. package/extensions/herdr-dispatch.ts +32 -3
  13. package/extensions/loop-until-done.ts +7 -0
  14. package/extensions/recovery-evidence.ts +3 -3
  15. package/extensions/treehouse-recycle.ts +47 -0
  16. package/janus/README.md +2 -4
  17. package/janus/janus/config.ts +2 -2
  18. package/janus/janus/providers.ts +2 -4
  19. package/janus/tests/providers.test.ts +2 -2
  20. package/{extensions → lib}/task-store-adapter.ts +1 -5
  21. package/package.json +158 -4
  22. package/scripts/enforcement/approval_ledger_pi.js +135 -0
  23. package/scripts/enforcement/attended_authority_guard.js +113 -0
  24. package/scripts/enforcement/board_claim_bridge_pi.js +10 -10
  25. package/scripts/enforcement/durable_assignment_pi.js +488 -0
  26. package/scripts/enforcement/durable_driver_pi.d.ts +51 -0
  27. package/scripts/enforcement/durable_driver_pi.js +254 -0
  28. package/scripts/enforcement/herdr_async_dispatch_pi.js +86 -20
  29. package/scripts/enforcement/herdr_async_seam_pi.js +90 -3
  30. package/scripts/enforcement/herdr_communication_pi.js +126 -12
  31. package/scripts/enforcement/herdr_lifecycle_pi.js +730 -81
  32. package/scripts/enforcement/linux_microvm_cutover_pi.js +3 -3
  33. package/scripts/enforcement/linux_microvm_remote_fixture.sh +0 -6
  34. package/scripts/enforcement/loop_until_done_guide.md +13 -0
  35. package/scripts/enforcement/loop_until_done_pi.js +214 -0
  36. package/scripts/enforcement/merge_policy.js +181 -0
  37. package/scripts/enforcement/outward_effect_pi.js +156 -0
  38. package/scripts/enforcement/recovery_evidence_review_pi.js +31 -3
  39. package/scripts/enforcement/storage_owner_lock_pi.js +66 -0
  40. package/scripts/enforcement/task_board_core_pi.js +13 -100
  41. package/scripts/enforcement/task_board_hash_pi.js +86 -0
  42. package/scripts/enforcement/treehouse_recycle_pi.js +145 -0
  43. package/scripts/harvest_lessons.js +219 -0
  44. package/scripts/relay/record_approval.mjs +10 -0
  45. package/scripts/relay/send_request.mjs +26 -0
  46. package/skills/architect-pi/SKILL.md +44 -0
  47. package/skills/arena-pi/SKILL.md +46 -0
  48. package/skills/automate-me-pi/SKILL.md +37 -0
  49. package/skills/benchmark-checklist-pi/SKILL.md +35 -0
  50. package/skills/blast-radius-pi/SKILL.md +24 -0
  51. package/skills/bro-pi/SKILL.md +17 -0
  52. package/skills/correct-pi/SKILL.md +36 -0
  53. package/skills/create-verification-skill-pi/LICENSE +21 -0
  54. package/skills/create-verification-skill-pi/SKILL.md +24 -0
  55. package/skills/decision-to-spec/SKILL.md +5 -5
  56. package/skills/figure-it-out-pi/SKILL.md +37 -0
  57. package/skills/harvest-lessons/SKILL.md +56 -0
  58. package/skills/harvest-lessons/harvest.example.json +8 -0
  59. package/skills/how/LICENSE +24 -0
  60. package/skills/how/WORKFLOW_BOUNDARIES.md +19 -0
  61. package/skills/how-pi/SKILL.md +33 -0
  62. package/skills/in-your-own-words-pi/SKILL.md +15 -0
  63. package/skills/interrogate-pi/SKILL.md +35 -0
  64. package/skills/ketchup-pi/SKILL.md +21 -0
  65. package/skills/maintain-verification-skill-pi/SKILL.md +37 -0
  66. package/skills/make-bot-ui-pi/SKILL.md +33 -0
  67. package/skills/no-comments-pi/SKILL.md +33 -0
  68. package/skills/poteto-help-pi/SKILL.md +50 -0
  69. package/skills/poteto-mode-pi/LICENSE +21 -0
  70. package/skills/poteto-mode-pi/SKILL.md +41 -0
  71. package/skills/poteto-mode-pi/playbooks.md +95 -0
  72. package/skills/principle-attack-the-premise-pi/SKILL.md +17 -0
  73. package/skills/principle-boundary-discipline-pi/SKILL.md +19 -0
  74. package/skills/principle-build-the-lever-pi/SKILL.md +19 -0
  75. package/skills/principle-encode-lessons-in-structure-pi/SKILL.md +17 -0
  76. package/skills/principle-exhaust-the-design-space-pi/SKILL.md +17 -0
  77. package/skills/principle-experience-first-pi/SKILL.md +17 -0
  78. package/skills/principle-explain-the-number-pi/SKILL.md +21 -0
  79. package/skills/principle-fix-root-causes-pi/SKILL.md +21 -0
  80. package/skills/principle-foundational-thinking-pi/SKILL.md +21 -0
  81. package/skills/principle-guard-the-context-window-pi/SKILL.md +19 -0
  82. package/skills/principle-laziness-protocol-pi/SKILL.md +19 -0
  83. package/skills/principle-make-operations-idempotent-pi/SKILL.md +21 -0
  84. package/skills/principle-migrate-callers-then-delete-legacy-apis-pi/SKILL.md +19 -0
  85. package/skills/principle-minimize-reader-load-pi/SKILL.md +19 -0
  86. package/skills/principle-model-the-domain-pi/SKILL.md +19 -0
  87. package/skills/principle-never-block-on-the-human-pi/SKILL.md +19 -0
  88. package/skills/principle-outcome-oriented-execution-pi/SKILL.md +19 -0
  89. package/skills/principle-prove-it-works-pi/SKILL.md +19 -0
  90. package/skills/principle-redesign-from-first-principles-pi/SKILL.md +19 -0
  91. package/skills/principle-separate-before-serializing-shared-state-pi/SKILL.md +19 -0
  92. package/skills/principle-sequence-verifiable-units-pi/SKILL.md +19 -0
  93. package/skills/principle-subtract-before-you-add-pi/SKILL.md +19 -0
  94. package/skills/principle-test-behavior-not-implementation-pi/SKILL.md +21 -0
  95. package/skills/principle-type-system-discipline-pi/SKILL.md +19 -0
  96. package/skills/public-pr-wording/SKILL.md +69 -0
  97. package/skills/public-pr-wording/pr_wording_config.example.json +22 -0
  98. package/skills/public-pr-wording/pr_wording_lint.py +249 -0
  99. package/skills/public-pr-wording/pr_wording_rules.json +60 -0
  100. package/skills/pulse/SKILL.md +6 -0
  101. package/skills/recall-pi/SKILL.md +29 -0
  102. package/skills/reflect-pi/SKILL.md +27 -0
  103. package/skills/setup-pstack-pi/SKILL.md +23 -0
  104. package/skills/setup-pstack-pi/assets/pstack-model-roles.v1.example.json +7 -0
  105. package/skills/sharpen-pi/SKILL.md +19 -0
  106. package/skills/show-me-your-work-pi/SKILL.md +27 -0
  107. package/skills/swarm-pi/SKILL.md +27 -0
  108. package/skills/tasks/SKILL.md +8 -2
  109. package/skills/tdd-pi/SKILL.md +21 -0
  110. package/skills/teach-pi/SKILL.md +21 -0
  111. package/skills/technical-writing-pi/SKILL.md +27 -0
  112. package/skills/typescript-best-practices-pi/SKILL.md +21 -0
  113. package/skills/unslop-pi/SKILL.md +27 -0
  114. package/skills/why-pi/SKILL.md +37 -0
@@ -0,0 +1,488 @@
1
+ // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+ // Packaged document store for dispatch and an isolated development-only assignment fixture.
4
+ import { createHash } from "node:crypto";
5
+ import { mkdirSync, rmdirSync, realpathSync, statSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { claimSeamLock, releaseSeamLock } from "./storage_owner_lock_pi.js";
8
+ export { claimSeamLock, releaseSeamLock } from "./storage_owner_lock_pi.js";
9
+ const terminal = (record) => ({ status: "terminal", outcome: { status: "completed", result: { disposition: record.disposition } } });
10
+ const stopped = (record) => ["held", "cancelled", "reported"].includes(record.disposition);
11
+
12
+ function bind(input) {
13
+ const fields = ["requestId", "assignmentId", "envelopeRef", "artifact", "role", "brief"];
14
+ if (!input || fields.some((key) => typeof input[key] !== "string" || !input[key] || input[key].length > 4000)) {
15
+ throw new Error("invalid fixture assignment");
16
+ }
17
+ if (!Array.isArray(input.scope) || !input.scope.length || input.scope.some((p) => typeof p !== "string" || !p || p.length > 4000)) {
18
+ throw new Error("invalid fixture scope");
19
+ }
20
+ // A fixed field order, not caller object order. This is identity, NOT admission authority.
21
+ const assignment = Object.fromEntries(fields.map((key) => [key, input[key]]));
22
+ assignment.scope = [...new Set(input.scope)].sort();
23
+ return { assignment, digest: createHash("sha256").update(JSON.stringify(assignment)).digest("hex") };
24
+ }
25
+
26
+ /** Opens only a private fixture directory. Existing lock (even stale) refuses;
27
+ * after abrupt process death an operator must reconcile that exact lock separately.
28
+ * Fake transport is injected test code, not an OS sandbox or trusted admission API.
29
+ */
30
+ export async function openDurableAssignmentFixture({ directory, transport, boundary = async () => {}, registerTask = true }) {
31
+ if (transport?.schema !== "fixture.fake-transport.v1" || typeof transport.send !== "function" || typeof transport.observe !== "function") {
32
+ throw new Error("fake fixture transport required");
33
+ }
34
+ if (!registerTask) throw new Error("assignment task definition required");
35
+ const [{ Harness, createRegistry, defineDoc, defineTask, defineExtension }, { openNodeSqliteStorage },
36
+ { createModels }, { BACKGROUND_CONTEXT: context }, { default: durablePackage }] = await Promise.all([
37
+ import("@earendil-works/pi-durable"),
38
+ import("@earendil-works/pi-durable/storage/sqlite/node"),
39
+ import("@earendil-works/pi-ai/models"),
40
+ import("@earendil-works/chord/context"),
41
+ import("@earendil-works/pi-durable/package.json", { with: { type: "json" } }),
42
+ ]);
43
+ if (durablePackage.version !== "1.0.4") throw new Error("fixture requires pi-durable 1.0.4");
44
+ // Owner supplies the already-created canonical private directory; no fallback.
45
+ if (realpathSync(directory) !== directory) throw new Error("canonical fixture directory required");
46
+ const info = statSync(directory);
47
+ if (!info.isDirectory() || info.uid !== process.getuid() || (info.mode & 0o077) !== 0) throw new Error("private same-owner fixture directory required");
48
+ const lock = join(directory, "owner.lock");
49
+ mkdirSync(lock, { mode: 0o700 }); // Atomic exclusive process ownership; no optimistic stale recovery.
50
+ let harness;
51
+ try {
52
+ const Records = defineDoc({ kind: "fixture.assignments", version: 1, scope: "conversation", history: "latest", fork: "initial", initial: () => ({ records: {} }) });
53
+ const recordFor = async (tx, conversationId, key) => (await tx.doc(Records, conversationId)).records[key];
54
+ const finish = async (task, runtime, ctx, disposition, reason) => {
55
+ await runtime.commit(async (tx) => {
56
+ const record = await recordFor(tx, runtime.conversationId, task.input.key);
57
+ if (!stopped(record)) Object.assign(record, { disposition, reason });
58
+ return terminal(record);
59
+ }, ctx);
60
+ };
61
+ const Assignment = defineTask({
62
+ name: "fixture.external-assignment", version: 1, initial: () => ({ phase: "admitted" }),
63
+ phases: {
64
+ admitted: async (task, runtime, ctx) => {
65
+ let send = false;
66
+ await runtime.commit(async (tx) => {
67
+ const record = await recordFor(tx, runtime.conversationId, task.input.key);
68
+ if (stopped(record)) return terminal(record);
69
+ record.disposition = "dispatch-intent";
70
+ send = true;
71
+ return { status: "running", checkpoint: { phase: "intent" } };
72
+ }, ctx);
73
+ if (!send) return;
74
+ await boundary("after-intent", task.input);
75
+ const latest = (await runtime.snapshot(Records, runtime.conversationId, ctx)).records[task.input.key];
76
+ if (stopped(latest)) {
77
+ await finish(task, runtime, ctx, latest.disposition, latest.reason ?? "");
78
+ return;
79
+ }
80
+ runtime.signal.throwIfAborted();
81
+ // Transport is never invoked inside a commit. Only this invocation sends;
82
+ // a new invocation at the persisted intent phase can only hold unknown.
83
+ let ack;
84
+ try {
85
+ ack = await transport.send(task.input.assignment, runtime.signal);
86
+ } catch {
87
+ await finish(task, runtime, ctx, "held", "delivery-unknown");
88
+ return;
89
+ }
90
+ await boundary("after-send", task.input);
91
+ runtime.signal.throwIfAborted();
92
+ if (typeof ack?.deliveryId !== "string" || !ack.deliveryId || ack.deliveryId.length > 512) {
93
+ await finish(task, runtime, ctx, "held", "delivery-unknown");
94
+ return;
95
+ }
96
+ await runtime.commit(async (tx) => {
97
+ const record = await recordFor(tx, runtime.conversationId, task.input.key);
98
+ // Cancellation/hold does not erase an ack received while send was in flight.
99
+ record.deliveryId = ack.deliveryId;
100
+ if (stopped(record)) return terminal(record);
101
+ record.disposition = "acknowledged";
102
+ return { status: "running", checkpoint: { phase: "observe", tick: 0 } };
103
+ }, ctx);
104
+ await boundary("after-ack", task.input);
105
+ },
106
+ intent: async (task, runtime, ctx) => finish(task, runtime, ctx, "held", "delivery-unknown"),
107
+ observe: async (task, runtime, ctx) => {
108
+ const snapshot = await runtime.snapshot(Records, runtime.conversationId, ctx);
109
+ const record = snapshot.records[task.input.key];
110
+ if (stopped(record)) { await finish(task, runtime, ctx, record.disposition, record.reason ?? ""); return; }
111
+ await runtime.sleep(runtime.now() + 25, ctx);
112
+ let report;
113
+ try { report = await transport.observe(record.deliveryId, runtime.signal); }
114
+ catch { await finish(task, runtime, ctx, "held", "observation-failed"); return; }
115
+ await runtime.commit(async (tx) => {
116
+ const current = await recordFor(tx, runtime.conversationId, task.input.key);
117
+ if (stopped(current)) return terminal(current);
118
+ const exact = report?.deliveryId === current.deliveryId && report?.assignmentId === task.input.assignment.assignmentId &&
119
+ report?.artifact === task.input.assignment.artifact && report?.role === task.input.assignment.role &&
120
+ typeof report?.text === "string" && report.text.length > 0 && report.text.length <= 16384;
121
+ if (exact) {
122
+ current.disposition = "reported";
123
+ current.report = { deliveryId: report.deliveryId, assignmentId: report.assignmentId, artifact: report.artifact, role: report.role, text: report.text };
124
+ return terminal(current);
125
+ }
126
+ return { status: "running", checkpoint: { phase: "observe", tick: task.state.checkpoint.tick + 1 } };
127
+ }, ctx);
128
+ },
129
+ },
130
+ abort: async (task, runtime, ctx) => finish(task, runtime, ctx, "cancelled", "task-aborted"),
131
+ });
132
+ const registry = createRegistry();
133
+ registry.install(defineExtension({ name: "fixture.assignments", tasks: [Assignment] }));
134
+ // No providers, environment, CodingTools or user-input submissions.
135
+ harness = await Harness.open(await openNodeSqliteStorage(join(directory, "state.sqlite")), { models: createModels(), registry }, context);
136
+ const root = await harness.root(context);
137
+ let closed = false;
138
+ return {
139
+ async admit(input) {
140
+ const { assignment, digest } = bind(input);
141
+ const key = createHash("sha256").update(assignment.requestId).digest("hex");
142
+ const receipt = await root.commit(async (tx) => {
143
+ const records = (await tx.doc(Records, root.id)).records;
144
+ if (records[key]) {
145
+ if (records[key].digest !== digest) throw new Error("request identity conflict");
146
+ return { taskId: records[key].taskId, key, digest, nonAuthorizing: true };
147
+ }
148
+ const taskId = await tx.createTask(Assignment, { key, assignment }, { ownership: { kind: "conversation" } });
149
+ records[key] = { taskId, digest, disposition: "admitted" };
150
+ return { taskId, key, digest, nonAuthorizing: true };
151
+ }, context);
152
+ // Scheduling is explicit and separate from admission/receipt, so submit never waits for transport.
153
+ await boundary("after-admit", receipt);
154
+ return receipt;
155
+ },
156
+ async status(receipt) {
157
+ const records = await harness.snapshot(Records, root.id, context);
158
+ const record = records?.records[receipt.key];
159
+ if (!record || record.taskId !== receipt.taskId || record.digest !== receipt.digest) throw new Error("unknown fixture receipt");
160
+ return structuredClone(record);
161
+ },
162
+ async control(receipt, action) {
163
+ if (!["hold", "cancel"].includes(action)) throw new Error("unknown fixture control");
164
+ await root.commit(async (tx) => {
165
+ const record = await recordFor(tx, root.id, receipt.key);
166
+ if (!record || record.taskId !== receipt.taskId || record.digest !== receipt.digest) throw new Error("unknown fixture receipt");
167
+ if (!stopped(record)) Object.assign(record, { disposition: action === "cancel" ? "cancelled" : "held", reason: "fixture-control" });
168
+ }, context);
169
+ // No stop/send/close effect against any external worker or newer assignment.
170
+ },
171
+ resume() { harness.resume(); },
172
+ async close() {
173
+ if (closed) return;
174
+ await harness.close(context);
175
+ closed = true;
176
+ rmdirSync(lock);
177
+ },
178
+ };
179
+ } catch (error) {
180
+ if (harness) await harness.close(context);
181
+ rmdirSync(lock);
182
+ throw error;
183
+ }
184
+ }
185
+
186
+ // Bounded persistence for Herdr effects. Durable tasks own journey checkpoints;
187
+ // this store keeps identities and attributed reports, never prompts or pane history.
188
+ // Cross-process ownership remains a host lock, outside pi-durable's scheduler.
189
+ export async function openDurableSeamStore({ directory, orphanWaitMs = 30_000 }) {
190
+ if (!Number.isSafeInteger(orphanWaitMs) || orphanWaitMs < 1 || orphanWaitMs > 60_000) {
191
+ throw new Error("invalid orphan wait bound");
192
+ }
193
+ const [{ Harness, createRegistry, defineDoc, defineTask, defineExtension }, { openNodeSqliteStorage },
194
+ { BACKGROUND_CONTEXT: context }, { default: durablePackage }] = await Promise.all([
195
+ import("@earendil-works/pi-durable"),
196
+ import("@earendil-works/pi-durable/storage/sqlite/node"),
197
+ import("@earendil-works/chord/context"),
198
+ import("@earendil-works/pi-durable/package.json", { with: { type: "json" } }),
199
+ ]);
200
+ if (durablePackage.version !== "1.0.4") throw new Error("dispatch store requires pi-durable 1.0.4");
201
+ if (realpathSync(directory) !== directory) throw new Error("canonical seam directory required");
202
+ const info = statSync(directory);
203
+ if (!info.isDirectory() || info.uid !== process.getuid() || (info.mode & 0o077) !== 0) {
204
+ throw new Error("private same-owner seam directory required");
205
+ }
206
+ const lock = claimSeamLock(directory);
207
+ let harness;
208
+ try {
209
+ const Records = defineDoc({ kind: "driver.seam-records", version: 1, scope: "conversation",
210
+ history: "latest", fork: "initial", initial: () => ({ submission: {}, delivery: {}, journey: {} }) });
211
+ const OutwardRecords = defineDoc({ kind: "driver.outward-records", version: 1, scope: "conversation",
212
+ history: "latest", fork: "initial", initial: () => ({ outward: {} }) });
213
+ const active = new Map();
214
+ const attached = async (task, runtime, ctx) => {
215
+ if (!active.has(task.input.id)) await runtime.sleep(runtime.now() + orphanWaitMs, ctx);
216
+ const call = active.get(task.input.id);
217
+ if (!call) await runtime.commit(() => terminal("blocked"), ctx);
218
+ return call;
219
+ };
220
+ const terminal = (state, report) => ({ status: "terminal", outcome: { status: "completed",
221
+ result: { state, ...(report ? { report } : {}) } } });
222
+ const checkpoint = (phase, report, tick = 0) => ({ status: "running", checkpoint: {
223
+ phase, ...(report ? { report } : {}), tick,
224
+ } });
225
+ const Journey = defineTask({
226
+ name: "driver.herdr-iteration", version: 1, initial: () => ({ phase: "admitted" }),
227
+ phases: {
228
+ admitted: async (task, runtime, ctx) => {
229
+ const call = await attached(task, runtime, ctx);
230
+ if (!call) return;
231
+ await call.boundary?.("before-send");
232
+ await runtime.commit(() => checkpoint("in-flight"), ctx);
233
+ let exchange;
234
+ try { exchange = await call.send(runtime.signal); }
235
+ catch { await runtime.commit(() => terminal("blocked"), ctx); return; }
236
+ await call.boundary?.("after-exchange");
237
+ if (exchange?.ok !== true || exchange.promptSent !== true || typeof exchange.report !== "string" ||
238
+ !exchange.report || Buffer.byteLength(exchange.report, "utf8") > 32768) {
239
+ await runtime.commit(() => terminal("blocked"), ctx);
240
+ return;
241
+ }
242
+ await runtime.commit(() => checkpoint("assignment-sent"), ctx);
243
+ await call.boundary?.("after-send");
244
+ await runtime.commit(() => checkpoint("result-received", exchange.report), ctx);
245
+ await call.boundary?.("after-result");
246
+ await runtime.commit(() => terminal("verified", exchange.report), ctx);
247
+ },
248
+ "in-flight": async (task, runtime, ctx) => {
249
+ const call = await attached(task, runtime, ctx);
250
+ if (!call) return;
251
+ let observation;
252
+ try { observation = await call.observe(runtime.signal); } catch { /* Unknown is not non-delivery. */ }
253
+ if (!observation?.present) { await runtime.commit(() => terminal("blocked"), ctx); return; }
254
+ await runtime.commit(() => checkpoint("assignment-sent"), ctx);
255
+ if (typeof observation.report === "string" && observation.report &&
256
+ Buffer.byteLength(observation.report, "utf8") <= 32768) {
257
+ await runtime.commit(() => checkpoint("result-received", observation.report), ctx);
258
+ }
259
+ },
260
+ "assignment-sent": async (task, runtime, ctx) => {
261
+ const call = await attached(task, runtime, ctx);
262
+ if (!call) return;
263
+ let observation;
264
+ try { observation = await call.observe(runtime.signal); } catch { /* Preserve uncertainty. */ }
265
+ if (!observation?.present) { await runtime.commit(() => terminal("blocked"), ctx); return; }
266
+ if (typeof observation.report === "string" && observation.report &&
267
+ Buffer.byteLength(observation.report, "utf8") <= 32768) {
268
+ await runtime.commit(() => checkpoint("result-received", observation.report), ctx);
269
+ return;
270
+ }
271
+ if ((task.state.checkpoint.tick ?? 0) >= 120) {
272
+ await runtime.commit(() => terminal("blocked"), ctx);
273
+ return;
274
+ }
275
+ await runtime.sleep(runtime.now() + 1000, ctx);
276
+ await runtime.commit(() => checkpoint("assignment-sent", undefined, (task.state.checkpoint.tick ?? 0) + 1), ctx);
277
+ },
278
+ "result-received": async (task, runtime, ctx) => {
279
+ await runtime.commit(() => terminal("verified", task.state.checkpoint.report), ctx);
280
+ },
281
+ },
282
+ abort: async (_task, runtime, ctx) => runtime.commit(() => terminal("blocked"), ctx),
283
+ });
284
+ const Outward = defineTask({
285
+ name: "driver.outward-effect", version: 1, initial: () => ({ phase: "admitted" }),
286
+ phases: {
287
+ admitted: async (task, runtime, ctx) => {
288
+ const call = await attached(task, runtime, ctx);
289
+ if (!call) return;
290
+ await call.boundary?.("before-effect");
291
+ await runtime.commit(() => checkpoint("in-flight"), ctx);
292
+ try { await call.apply(runtime.signal); }
293
+ catch { /* An error does not prove that the remote effect failed. */ }
294
+ await call.boundary?.("after-effect");
295
+ let proof;
296
+ try { proof = await call.observe(runtime.signal); } catch { /* Hold on uncertainty. */ }
297
+ await runtime.commit(() => terminal(proof?.verified === true ? "verified" : "blocked",
298
+ proof?.verified === true ? proof.reference : undefined), ctx);
299
+ },
300
+ "in-flight": async (task, runtime, ctx) => {
301
+ const call = await attached(task, runtime, ctx);
302
+ if (!call) return;
303
+ let proof;
304
+ try { proof = await call.observe(runtime.signal); } catch { /* Never replay the effect. */ }
305
+ await runtime.commit(() => terminal(proof?.verified === true ? "verified" : "blocked",
306
+ proof?.verified === true ? proof.reference : undefined), ctx);
307
+ },
308
+ },
309
+ abort: async (_task, runtime, ctx) => runtime.commit(() => terminal("blocked"), ctx),
310
+ });
311
+ // Satisfy the pinned Models interface without importing a provider registry.
312
+ // Every model/auth operation fails closed; this store supports documents only.
313
+ const models = Object.freeze(Object.fromEntries([
314
+ "getProviders", "getProvider", "getModels", "getModel", "getModelsOfType", "getModelOfType", "getAllModels",
315
+ "refresh", "checkAuth", "getAvailable", "getAvailableOfType", "getAllAvailable", "getAuth", "login", "logout",
316
+ "stream", "complete", "streamSimple", "completeSimple", "streamDeferred", "fetchDeferred", "cancelDeferred",
317
+ "generateImages", "classify",
318
+ ].map((name) => [name, () => { throw new Error("model operations are disabled for the document store"); }])));
319
+ const registry = createRegistry();
320
+ registry.install(defineExtension({ name: "driver.herdr-iteration", tasks: [Journey, Outward] }));
321
+ harness = await Harness.open(await openNodeSqliteStorage(join(directory, "state.sqlite")),
322
+ { models, registry }, context);
323
+ const root = await harness.root(context);
324
+ let closed = false;
325
+ const identity = ["id", "digest", "role", "repository", "createdAt"];
326
+ const requiredIdentity = {
327
+ submission: identity,
328
+ delivery: identity,
329
+ journey: identity,
330
+ outward: ["id", "digest", "repository", "createdAt"],
331
+ };
332
+ const fields = {
333
+ submission: new Set([...identity, "phase", "acceptedAt", "deliveryId"]),
334
+ delivery: new Set([...identity, "state", "acceptedAt", "code", "report", "answeredAt", "proof"]),
335
+ journey: new Set([...identity, "journeyId", "iteration", "taskId", "cardId"]),
336
+ outward: new Set([...requiredIdentity.outward, "kind", "journeyId"]),
337
+ };
338
+ function check(kind, record, partial = false) {
339
+ if (closed) throw new Error("seam store closed");
340
+ const allowed = fields[kind];
341
+ if (!allowed || !record || Object.getPrototypeOf(record) !== Object.prototype) throw new Error("invalid seam record");
342
+ if (Object.keys(record).some((key) => !allowed.has(key))) throw new Error("unsupported seam field; snapshots and prompts are forbidden");
343
+ for (const [key, value] of Object.entries(record)) {
344
+ const limit = key === "report" ? 16384 : 4096;
345
+ if (key === "createdAt") {
346
+ if (!Number.isSafeInteger(value) || value < 0) throw new Error("invalid seam timestamp");
347
+ } else if (typeof value !== "string" || !value || Buffer.byteLength(value, "utf8") > limit) {
348
+ throw new Error("invalid bounded seam field");
349
+ }
350
+ }
351
+ if (!partial && requiredIdentity[kind].some((key) => record[key] === undefined)) {
352
+ throw new Error("missing seam identity");
353
+ }
354
+ if (record.digest !== undefined && !/^[a-f0-9]{64}$/.test(record.digest)) throw new Error("invalid seam digest");
355
+ if (record.report !== undefined && kind === "delivery" && (record.state !== "answered" || record.proof !== "snapshot-backed-v1")) {
356
+ throw new Error("attributed report proof required");
357
+ }
358
+ }
359
+ function checkHandle(kind, id) {
360
+ if (closed) throw new Error("seam store closed");
361
+ if (!fields[kind] || typeof id !== "string" || !/^[a-z0-9-]{1,128}$/.test(id)) throw new Error("invalid seam handle");
362
+ }
363
+ return {
364
+ schema: "agentic-driver.offline-seam-store.v1",
365
+ async get(kind, id) {
366
+ if (kind === "journey") throw new Error("journey state belongs to the task scheduler");
367
+ checkHandle(kind, id);
368
+ const snapshot = await harness.snapshot(Records, root.id, context);
369
+ return structuredClone(snapshot?.[kind]?.[id]);
370
+ },
371
+ async reserve(kind, record) {
372
+ if (kind === "journey") throw new Error("journey admission belongs to the task scheduler");
373
+ check(kind, record);
374
+ checkHandle(kind, record.id);
375
+ return root.commit(async (tx) => {
376
+ const records = (await tx.doc(Records, root.id))[kind];
377
+ const existing = records[record.id];
378
+ if (existing) {
379
+ if (identity.some((key) => key !== "createdAt" && existing[key] !== record[key])) throw new Error("seam identity conflict");
380
+ // Durable commit documents are draft proxies; permitted fields are
381
+ // primitives, so materialize the record before returning it.
382
+ return { created: false, record: { ...existing } };
383
+ }
384
+ // Never evict a send tombstone to make room: eviction could enable replay.
385
+ if (Object.keys(records).length >= 64) throw new Error("seam store capacity reached; owner reconciliation required");
386
+ records[record.id] = structuredClone(record);
387
+ return { created: true, record: structuredClone(record) };
388
+ }, context);
389
+ },
390
+ async listJourneys() {
391
+ if (closed) throw new Error("seam store closed");
392
+ const snapshot = await harness.snapshot(Records, root.id, context);
393
+ return Promise.all(Object.values(snapshot?.journey ?? {}).map(async ({ taskRef, ...entry }) => {
394
+ const task = await harness.getTask(taskRef, context);
395
+ const settled = task?.state.status === "terminal" ? task.state.outcome?.result : undefined;
396
+ return { ...structuredClone(entry), state: settled?.state ?? task?.state.checkpoint?.phase ?? "blocked",
397
+ ...(settled?.report ? { report: settled.report } : {}) };
398
+ }));
399
+ },
400
+ async runJourneyStep(record, handlers) {
401
+ check("journey", record);
402
+ checkHandle("journey", record.id);
403
+ if (active.has(record.id)) throw new Error("journey is already running");
404
+ active.set(record.id, handlers);
405
+ try {
406
+ const taskRef = await root.commit(async (tx) => {
407
+ const index = (await tx.doc(Records, root.id)).journey;
408
+ if (index[record.id]) {
409
+ if (index[record.id].digest !== record.digest || index[record.id].repository !== record.repository ||
410
+ index[record.id].role !== record.role || index[record.id].taskId !== record.taskId) {
411
+ throw new Error("journey identity conflict");
412
+ }
413
+ return index[record.id].taskRef;
414
+ }
415
+ if (Object.keys(index).length >= 64) throw new Error("journey capacity reached; reconcile before admitting more");
416
+ const taskRef = await tx.createTask(Journey, { id: record.id }, { ownership: { kind: "conversation" } });
417
+ index[record.id] = { ...structuredClone(record), taskRef };
418
+ return taskRef;
419
+ }, context);
420
+ harness.resume();
421
+ const task = await harness.waitForTask(taskRef, context);
422
+ if (task.state.status !== "terminal" || task.state.outcome.status !== "completed") {
423
+ throw new Error("durable journey task did not settle safely");
424
+ }
425
+ return structuredClone(task.state.outcome.result);
426
+ } finally {
427
+ active.delete(record.id);
428
+ }
429
+ },
430
+ async runOutwardEffect(record, handlers) {
431
+ check("outward", record);
432
+ checkHandle("outward", record.id);
433
+ if (typeof handlers?.apply !== "function" || typeof handlers?.observe !== "function") throw new Error("outward handlers required");
434
+ if (active.has(record.id)) throw new Error("outward effect already running");
435
+ active.set(record.id, handlers);
436
+ try {
437
+ const taskRef = await root.commit(async (tx) => {
438
+ const index = (await tx.doc(OutwardRecords, root.id)).outward;
439
+ const previous = index[record.id];
440
+ if (previous) {
441
+ if (Object.keys(record).some((key) => key !== "createdAt" && previous[key] !== record[key])) throw new Error("outward identity conflict");
442
+ return previous.taskRef;
443
+ }
444
+ if (Object.keys(index).length >= 64) throw new Error("outward capacity reached; reconcile before more effects");
445
+ const ref = await tx.createTask(Outward, { id: record.id }, { ownership: { kind: "conversation" } });
446
+ index[record.id] = { ...structuredClone(record), taskRef: ref };
447
+ return ref;
448
+ }, context);
449
+ harness.resume();
450
+ const task = await harness.waitForTask(taskRef, context);
451
+ if (task.state.status !== "terminal" || task.state.outcome.status !== "completed") throw new Error("outward task did not settle");
452
+ return structuredClone(task.state.outcome.result);
453
+ } finally { active.delete(record.id); }
454
+ },
455
+ async update(kind, id, digest, patch) {
456
+ if (kind === "journey") throw new Error("journey changes belong to the task scheduler");
457
+ checkHandle(kind, id);
458
+ check(kind, patch, true);
459
+ if (identity.some((key) => Object.hasOwn(patch, key))) throw new Error("immutable seam identity");
460
+ return root.commit(async (tx) => {
461
+ const records = (await tx.doc(Records, root.id))[kind];
462
+ const current = records[id];
463
+ if (!current || current.digest !== digest) throw new Error("unknown seam identity");
464
+ const next = { ...current, ...patch };
465
+ // A hold observed by this transaction cannot be overwritten by a
466
+ // racing ack. Keep its ack metadata, but never resume/attribute it.
467
+ if (current.phase === "held") next.phase = "held";
468
+ if (current.state === "held") next.state = "held";
469
+ check(kind, next);
470
+ // Once proven and committed, a report cannot be replaced by later text.
471
+ if (current.state === "answered" && JSON.stringify(next) !== JSON.stringify(current)) throw new Error("answered seam record is immutable");
472
+ records[id] = next;
473
+ return structuredClone(next);
474
+ }, context);
475
+ },
476
+ async close() {
477
+ if (closed) return;
478
+ await harness.close(context);
479
+ closed = true;
480
+ releaseSeamLock(lock);
481
+ },
482
+ };
483
+ } catch (error) {
484
+ if (harness) await harness.close(context);
485
+ releaseSeamLock(lock);
486
+ throw error;
487
+ }
488
+ }
@@ -0,0 +1,51 @@
1
+ // SPDX-FileCopyrightText: 2026 evoclock
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+ export declare const DURABLE_DRIVER_PROTOCOL: "agentic-driver.run.v1";
4
+
5
+ export type DurableStepKind = "internal" | "external";
6
+ export type DurableRunState = "active" | "held" | "finished" | "cancelled";
7
+ export type DurableStepState = "pending" | "ready" | "intent" | "held" | "finished";
8
+ export interface DurableStep {
9
+ id: string;
10
+ kind: DurableStepKind;
11
+ state: DurableStepState;
12
+ requestKey: string;
13
+ correlationId?: string;
14
+ /** Registration-only ID retained on runs written before correlation IDs replaced tasks. */
15
+ taskId?: string;
16
+ intentOwner?: string;
17
+ receipt?: { requestKey: string; verified: true; reference: string };
18
+ releases?: Array<{ requestKey: string; reference: string }>;
19
+ }
20
+ export interface DurableRun {
21
+ schema?: typeof DURABLE_DRIVER_PROTOCOL;
22
+ runId: string;
23
+ digest: string;
24
+ state: DurableRunState;
25
+ steps: DurableStep[];
26
+ }
27
+ export interface DurableDriver {
28
+ schema: typeof DURABLE_DRIVER_PROTOCOL;
29
+ start(plan: { runId: string; steps: Array<{ id: string; kind: DurableStepKind }> }): Promise<{
30
+ runId: string; digest: string; stepIds: string[];
31
+ }>;
32
+ status(runId: string): Promise<DurableRun | null>;
33
+ /** One intent for the entire host-owned worker/review/verification invocation. */
34
+ invokeWorker<Result>(input: {
35
+ requestId: string; goal: string; signal: AbortSignal;
36
+ transport: { invoke(input: { requestId: string; goal: string; requestKey: string; signal: AbortSignal }): Promise<{
37
+ result: Result; verified: true; reference: string;
38
+ }> };
39
+ }): Promise<{ runId: string; state: "finished"; result: Result } |
40
+ { runId: string; state: "finished" | "held"; step: DurableStep } |
41
+ { runId: string; state: "cancelled" }>;
42
+ resume(runId: string): Promise<DurableRun>;
43
+ report(runId: string, report: { stepId: string; requestKey: string; receipt?: {
44
+ requestKey: string; verified: true; reference: string;
45
+ } }): Promise<DurableRun>;
46
+ reconcile(runId: string, evidence: { stepId: string; requestKey: string; verified: boolean; reference?: string }): Promise<DurableRun>;
47
+ release(runId: string, evidence: { stepId: string; requestKey: string; verifiedNotSent: true; reference: string }): Promise<DurableRun>;
48
+ cancel(runId: string): Promise<DurableRun>;
49
+ close(): Promise<void>;
50
+ }
51
+ export declare function openDurableDriver(options: { directory: string }): Promise<DurableDriver>;