@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
@@ -15,10 +15,36 @@
15
15
  // - No bypasses, no model-supplied authority, no silent escalation. The
16
16
  // model cannot mark a call safe; classification is structural only.
17
17
 
18
+ import { mkdirSync, writeFileSync } from "node:fs";
19
+ import { dirname } from "node:path";
18
20
  import { isNativeTuiContext } from "./native_tui_context.js";
21
+ import {
22
+ detectMerge, evaluateMerge, gatherMergeEvidence, loadMergePolicy,
23
+ reviewRecord, reviewRecordPath, sessionIdOf, MERGE_POLICY_SCHEMA,
24
+ } from "./merge_policy.js";
19
25
 
20
26
  export const GUARD_SCHEMA = "pi-agentic-driver.attended-authority.v1";
21
27
 
28
+ // This records a decision's stated provenance, not the identity of its author.
29
+ // Pi's user-message type alone cannot distinguish typed owner input from a
30
+ // pi.sendUserMessage relay. Never use this record to bypass tool-call guards.
31
+ export function recordDecisionEvidence(input) {
32
+ const kind = input?.kind;
33
+ const quote = typeof input?.quote === "string" ? input.quote : "";
34
+ const where = typeof input?.where === "string" ? input.where.trim() : "";
35
+ const when = typeof input?.when === "string" ? input.when.trim() : "";
36
+ const relayer = typeof input?.relayer === "string" ? input.relayer.trim() : "";
37
+ const ledgerId = typeof input?.ledgerId === "string" ? input.ledgerId.trim() : "";
38
+ const sourced = quote.trim() && where && when && (kind === "direct" || (kind === "relay" && relayer));
39
+ return {
40
+ recordable: Boolean(sourced),
41
+ kind: kind === "direct" || kind === "relay" ? kind : "unknown",
42
+ quote, where, when, relayer: kind === "relay" ? relayer : "", ledgerId,
43
+ identityVerified: false,
44
+ irreversibleAuthority: false,
45
+ };
46
+ }
47
+
22
48
  // Tool names that can mutate the workspace.
23
49
  const MUTATING_TOOLS = new Set(["bash", "write", "edit"]);
24
50
 
@@ -168,6 +194,16 @@ export function confirmationBody(toolName, input, kind) {
168
194
  ].join("\n");
169
195
  }
170
196
 
197
+ // Structured so a coordinator relaying a headless bot result can pass it up
198
+ // as an owner decision rather than a silent refusal.
199
+ export function needsOwner(tier, missing, repo) {
200
+ return { status: "needs-owner", tier, missing, relayablePhrase: `Approve ${missing.join(", ")} for ${repo}`, nextStep: "Relay the owner's scoped words with their source and record the approval." };
201
+ }
202
+
203
+ export function mergeBlockReason(policy, source, why) {
204
+ return `${MERGE_POLICY_SCHEMA}: needs-owner-approval: pull-request merge refused (policy ${policy}, ${source}): ${why}`;
205
+ }
206
+
171
207
  export function denialReason(toolName, kind, context) {
172
208
  if (!isNativeTuiContext(context)) {
173
209
  return `${GUARD_SCHEMA}: destructive ${toolName} operation (${kind}) refused fail-closed: no native confirmation surface in this headless context`;
@@ -185,6 +221,31 @@ export async function guardToolCall(event, ctx, options = {}) {
185
221
  const toolName = String(event?.toolName ?? "").toLowerCase();
186
222
  if (!MUTATING_TOOLS.has(toolName)) return undefined;
187
223
  const input = event?.input && typeof event.input === "object" ? event.input : {};
224
+ const approvalPath = /(?:^|[\s"'\\/])approvals[\\/]/;
225
+ if ((toolName === "write" || toolName === "edit") && approvalPath.test(String(input.path ?? input.file_path ?? ""))) {
226
+ return { block: true, reason: "Approval ledger writes are reserved for the trusted relay CLI.", ...needsOwner(3, ["ledger edit"], "profile") };
227
+ }
228
+ if (toolName === "bash" && (approvalPath.test(String(input.command ?? "")) || /\brecord_approval\.mjs\b/.test(String(input.command ?? "")))) {
229
+ return { block: true, reason: "Direct approval-ledger access through the agent shell is blocked; use agentic_relay_check_request for read-only checks.", ...needsOwner(3, ["direct ledger access"], "profile") };
230
+ }
231
+
232
+ // Pull-request merges follow the owner's merge policy, never a prompt. The
233
+ // same answer applies in a headless bot session and in direct Pi use.
234
+ if (toolName === "bash") {
235
+ const detection = detectMerge(input.command);
236
+ if (detection.merge) {
237
+ const { policy, source } = (options.loadMergePolicy ?? loadMergePolicy)();
238
+ const gathered = policy === "agents-after-checks-and-review" && detection.ref
239
+ ? (options.gatherMergeEvidence ?? gatherMergeEvidence)(detection.ref)
240
+ : undefined;
241
+ const evidence = gathered && !gathered.error
242
+ ? { ...gathered, mergerSession: sessionIdOf(options.context ?? ctx) }
243
+ : gathered;
244
+ const decision = evaluateMerge(policy, detection, evidence);
245
+ if (decision.allow) return undefined;
246
+ return { block: true, reason: mergeBlockReason(policy, source, decision.reason) };
247
+ }
248
+ }
188
249
 
189
250
  const bash = toolName === "bash"
190
251
  ? classifyBashCommand(input.command)
@@ -217,6 +278,58 @@ export function registerAttendedAuthorityGuard(pi, options = {}) {
217
278
  if (!pi || REGISTERED.has(pi)) return undefined;
218
279
  REGISTERED.add(pi);
219
280
  pi.on?.("tool_call", (event, ctx) => guardToolCall(event, ctx, options));
281
+ pi.registerTool?.({
282
+ name: "agentic_decision_evidence",
283
+ label: "Decision evidence",
284
+ description: "Record the stated source of a decision for a private assignment. This does not verify identity or authorize actions.",
285
+ parameters: {
286
+ type: "object",
287
+ properties: {
288
+ kind: { type: "string", enum: ["direct", "relay"] },
289
+ quote: { type: "string" },
290
+ where: { type: "string" },
291
+ when: { type: "string" },
292
+ relayer: { type: "string" },
293
+ ledgerId: { type: "string" },
294
+ },
295
+ required: ["kind", "quote", "where", "when"],
296
+ additionalProperties: false,
297
+ },
298
+ async execute(_toolCallId, params) {
299
+ const record = recordDecisionEvidence(params);
300
+ return {
301
+ content: [{ type: "text", text: record.recordable
302
+ ? "Decision source recorded as unverified evidence; no authority created."
303
+ : "Decision source incomplete; quote, where, when and (for relays) relayer are required." }],
304
+ details: { ...record, authorityCreated: false },
305
+ };
306
+ },
307
+ });
308
+ pi.registerTool?.({
309
+ name: "agentic_merge_review_record",
310
+ label: "Merge review record",
311
+ description: "Record independent Standards and Spec verdicts for an exact PR head. Only read when the owner has set the merge policy to agents-after-checks-and-review.",
312
+ parameters: {
313
+ type: "object",
314
+ properties: {
315
+ headSha: { type: "string" },
316
+ standards: { type: "string", enum: ["PASS", "FAIL"] },
317
+ spec: { type: "string", enum: ["PASS", "FAIL"] },
318
+ standardsReviewer: { type: "string", description: "Identity or session id of the Standards reviewer." },
319
+ specReviewer: { type: "string", description: "Identity or session id of the Spec reviewer." },
320
+ },
321
+ required: ["headSha", "standards", "spec", "standardsReviewer", "specReviewer"],
322
+ additionalProperties: false,
323
+ },
324
+ async execute(_toolCallId, params, _signal, _update, context) {
325
+ // The recording session comes from the host, never from the model.
326
+ const result = reviewRecord({ ...params, recordedBySession: sessionIdOf(context) });
327
+ if (!result.ok) return { content: [{ type: "text", text: result.reason }], details: { recorded: false } };
328
+ const path = reviewRecordPath(result.record.headSha);
329
+ (options.writeReviewRecord ?? ((file, text) => { mkdirSync(dirname(file), { recursive: true }); writeFileSync(file, text); }))(path, `${JSON.stringify(result.record, null, 2)}\n`);
330
+ return { content: [{ type: "text", text: "Review verdicts recorded for this head. They do not merge anything." }], details: { recorded: true, ...result.record } };
331
+ },
332
+ });
220
333
  return { registered: true, schema: GUARD_SCHEMA };
221
334
  }
222
335
 
@@ -1,10 +1,10 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
2
  // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
- // Board-claim bridge for Vogelkop (vogelkop phase #47, step 3).
4
+ // Board-claim bridge for external board clients.
5
5
  //
6
- // Vogelkop reads its TASKS.md, selects the next eligible governed card, and
7
- // emits a versioned claim request (vogelkop.board-claim-request.v1). This
6
+ // A client reads its TASKS.md, selects the next eligible governed card, and
7
+ // emits a versioned claim request. This
8
8
  // module services that request by DELEGATING to the existing board authority
9
9
  // implementation — claimCard in task_board_core_pi.js — which already owns
10
10
  // the writer-lock CAS, the claims-file HMAC, expiry/single-attempt, and
@@ -41,8 +41,8 @@ import {
41
41
  import { readFileSync, existsSync } from "node:fs";
42
42
 
43
43
  export const BOARD_CLAIM_BRIDGE_SCHEMA = "agentic-driver.board-claim-bridge.v1";
44
- export const BOARD_CLAIM_RECEIPT_SCHEMA = "vogelkop.board-claim-receipt.v1";
45
- export const REQUEST_SCHEMA = "vogelkop.board-claim-request.v1";
44
+ export const BOARD_CLAIM_RECEIPT_SCHEMA = "agentic-driver.board-claim-receipt.v2";
45
+ export const REQUEST_SCHEMA = "agentic-driver.board-claim-request.v2";
46
46
 
47
47
  const ROLE_NAME_RE = /^[A-Za-z][A-Za-z0-9._-]{0,63}$/;
48
48
  const ROUTE_SOURCE_KINDS = new Set(["explicit-board-route", "jev-fallback"]);
@@ -98,16 +98,16 @@ export function verifyRequestedCard({ boardPath, request }) {
98
98
  */
99
99
  export function bridgeRouteFor({ request, repository }) {
100
100
  return {
101
- seatId: `vogelkop-${request.route.seatAlias}`,
101
+ seatId: `board-${request.route.seatAlias}`,
102
102
  accountId: null,
103
- provider: "vogelkop-router",
103
+ provider: "board-router",
104
104
  model: request.route.seatAlias,
105
105
  role: request.route.seat,
106
106
  effort: "default",
107
107
  containmentTier: "testudo",
108
108
  phase: "implement",
109
109
  routeDecisionDigest: sha256Hex(canonicalJsonString({
110
- schema: "vogelkop.board-claim-route.v1",
110
+ schema: "agentic-driver.board-claim-route.v2",
111
111
  cardId: request.cardId,
112
112
  seatAlias: request.route.seatAlias,
113
113
  seat: request.route.seat,
@@ -125,9 +125,9 @@ export function bridgeRouteFor({ request, repository }) {
125
125
  * observation carrying the card binding, the claim/envelope identity, the
126
126
  * route source and model, the moat result, and the branch/base/scope.
127
127
  */
128
- export function claimFromVogelkopRequest({ boardPath, request, policy = null, now = null, backendBaseUrlFor = null, moatPolicy = null }) {
128
+ export function claimFromBoardRequest({ boardPath, request, policy = null, now = null, backendBaseUrlFor = null, moatPolicy = null }) {
129
129
  if (request?.schema !== REQUEST_SCHEMA) {
130
- return fail("request-schema-invalid", `unknown request schema "${String(request?.schema)}"`);
130
+ return fail("request-schema-invalid", `unsupported request schema "${String(request?.schema)}"; expected ${REQUEST_SCHEMA}`);
131
131
  }
132
132
  if (request.boardPathBasename !== "TASKS.md") {
133
133
  return fail("request-board-invalid", "the request must name the canonical TASKS.md board");