pi-daddy 0.21.1 → 0.22.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 (65) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +12 -7
  3. package/contracts/ledger/v3/fixtures/capability-decision.json +3 -4
  4. package/contracts/ledger/v3/fixtures/check-receipt.json +3 -4
  5. package/contracts/ledger/v3/fixtures/child-lifecycle.json +3 -4
  6. package/contracts/ledger/v3/fixtures/workflow-fact.json +3 -4
  7. package/contracts/ledger/v3/fixtures/workspace-lease.json +3 -4
  8. package/contracts/ledger/v3/ledger-event.schema.json +38 -13
  9. package/dist/check-runner.d.ts.map +1 -1
  10. package/dist/check-runner.js +161 -149
  11. package/dist/check-runner.js.map +1 -1
  12. package/dist/correlation.d.ts +15 -4
  13. package/dist/correlation.d.ts.map +1 -1
  14. package/dist/correlation.js +40 -2
  15. package/dist/correlation.js.map +1 -1
  16. package/dist/delegation-approval.d.ts +2 -2
  17. package/dist/delegation-approval.d.ts.map +1 -1
  18. package/dist/delegation-approval.js +2 -0
  19. package/dist/delegation-approval.js.map +1 -1
  20. package/dist/finalization.d.ts +15 -0
  21. package/dist/finalization.d.ts.map +1 -0
  22. package/dist/finalization.js +65 -0
  23. package/dist/finalization.js.map +1 -0
  24. package/dist/git-identity.d.ts.map +1 -1
  25. package/dist/git-identity.js +18 -15
  26. package/dist/git-identity.js.map +1 -1
  27. package/dist/grant-store.d.ts +13 -0
  28. package/dist/grant-store.d.ts.map +1 -1
  29. package/dist/grant-store.js +48 -25
  30. package/dist/grant-store.js.map +1 -1
  31. package/dist/init.d.ts.map +1 -1
  32. package/dist/init.js +2 -6
  33. package/dist/init.js.map +1 -1
  34. package/dist/model-preflight.d.ts +8 -0
  35. package/dist/model-preflight.d.ts.map +1 -0
  36. package/dist/model-preflight.js +20 -0
  37. package/dist/model-preflight.js.map +1 -0
  38. package/dist/refusals.d.ts +1 -1
  39. package/dist/refusals.d.ts.map +1 -1
  40. package/dist/refusals.js +2 -0
  41. package/dist/refusals.js.map +1 -1
  42. package/dist/run-herdr.d.ts.map +1 -1
  43. package/dist/run-herdr.js +6 -5
  44. package/dist/run-herdr.js.map +1 -1
  45. package/extensions/chain-plan.ts +16 -0
  46. package/extensions/correlation-shape.ts +39 -0
  47. package/extensions/delegate-chain.ts +9 -14
  48. package/extensions/delegation.ts +2 -24
  49. package/extensions/execute-child.ts +8 -1
  50. package/extensions/grant-store-refusal.ts +36 -0
  51. package/extensions/grants.ts +6 -0
  52. package/extensions/run-delegation.ts +21 -2
  53. package/extensions/session.ts +15 -5
  54. package/extensions/stored-grant-session.ts +33 -0
  55. package/package.json +3 -1
  56. package/src/check-runner.ts +56 -44
  57. package/src/correlation.ts +55 -6
  58. package/src/delegation-approval.ts +4 -2
  59. package/src/finalization.ts +79 -0
  60. package/src/git-identity.ts +20 -16
  61. package/src/grant-store.ts +55 -25
  62. package/src/init.ts +5 -5
  63. package/src/model-preflight.ts +32 -0
  64. package/src/refusals.ts +2 -0
  65. package/src/run-herdr.ts +6 -4
package/src/init.ts CHANGED
@@ -26,6 +26,7 @@ import { mkdir, open, rm, writeFile } from "node:fs/promises";
26
26
  import { join } from "node:path";
27
27
  import { agentCapability, workspaceCapability } from "./capabilities.ts";
28
28
  import { ceilingForDefinition } from "./definitions.ts";
29
+ import { runWithFinalizers } from "./finalization.ts";
29
30
  import { ALWAYS_LIVE, assertGrantIsWritable, isLiveByDefault, renderGrantEnv, type GrantEnvSkill } from "./grant-env.ts";
30
31
  import { PI_BUILTIN_TOOLS } from "./pi-tools.ts";
31
32
  import type { Capability } from "./resolve.ts";
@@ -332,11 +333,10 @@ async function createUnlessPresent(path: string, content: string, outcome: InitO
332
333
  try {
333
334
  await mkdir(join(path, ".."), { recursive: true });
334
335
  const handle = await open(path, "wx");
335
- try {
336
- await handle.writeFile(content, "utf8");
337
- } finally {
338
- await handle.close();
339
- }
336
+ await runWithFinalizers(
337
+ () => handle.writeFile(content, "utf8"),
338
+ [{ label: "new-file handle cleanup failed", run: () => handle.close() }],
339
+ );
340
340
  outcome.written.push(path);
341
341
  } catch (error) {
342
342
  if ((error as { code?: string }).code === "EEXIST") outcome.kept.push(path);
@@ -0,0 +1,32 @@
1
+ import { refusal, type StructuredRefusal } from "./refusals.ts";
2
+
3
+ export const ENV_ALLOW_UNRESOLVED_MODELS = "PI_GRANTS_ALLOW_UNRESOLVED_MODELS";
4
+
5
+ export interface ModelCatalogue {
6
+ find(provider: string, modelId: string): unknown;
7
+ }
8
+
9
+ /** Resolve an explicit provider/id against pi's own session catalogue without probing credentials or network. */
10
+ export function preflightModel(
11
+ model: string | undefined,
12
+ catalogue: ModelCatalogue,
13
+ cache: Map<string, boolean>,
14
+ allowUnresolved: boolean,
15
+ ): StructuredRefusal | undefined {
16
+ if (model === undefined || allowUnresolved) return undefined;
17
+ const slash = model.indexOf("/");
18
+ const provider = slash > 0 ? model.slice(0, slash) : "";
19
+ const id = slash > 0 ? model.slice(slash + 1) : "";
20
+ let resolved = cache.get(model);
21
+ if (resolved === undefined) {
22
+ resolved = provider.length > 0 && id.length > 0 && catalogue.find(provider, id) !== undefined;
23
+ cache.set(model, resolved);
24
+ }
25
+ if (resolved) return undefined;
26
+ return refusal(
27
+ "MODEL_UNRESOLVED",
28
+ `grants: model ${model || "(empty)"} is not in pi's session catalogue; use provider/id from /model, ` +
29
+ `or set ${ENV_ALLOW_UNRESOLVED_MODELS}=1 to let pi attempt custom resolution`,
30
+ { model: model || "(empty)" },
31
+ );
32
+ }
package/src/refusals.ts CHANGED
@@ -11,6 +11,8 @@ export const REFUSAL_CODES = [
11
11
  "DEPTH_EXCEEDED",
12
12
  "FANOUT_EXCEEDED",
13
13
  "EXECUTOR_UNAVAILABLE",
14
+ "MODEL_UNRESOLVED",
15
+ "GRANT_STORE_INVALID",
14
16
  // Execution-phase outcomes. Without these, an external controller could distinguish a policy refusal
15
17
  // from an internal error but not a lost writer lease from a user pressing stop (R-103).
16
18
  "CHILD_TIMED_OUT",
package/src/run-herdr.ts CHANGED
@@ -22,6 +22,7 @@
22
22
  */
23
23
 
24
24
  import { rm } from "node:fs/promises";
25
+ import { runWithFinalizers } from "./finalization.ts";
25
26
  import type { ChildRunResult } from "./run-child.ts";
26
27
  import { DEFAULT_MAX_OUTPUT_BYTES, DEFAULT_TIMEOUT_MS } from "./run-child.ts";
27
28
  import { MAX_OPEN_PANES, markPaneSettled, trackPane, trimOpenPanes, untrackPane } from "./pane-reaper.ts";
@@ -293,7 +294,7 @@ export async function runHerdrPane(request: HerdrRunRequest): Promise<ChildRunRe
293
294
  // early `return` from a failed start or prompt — leaves it false, so `cleanup` closes the tab.
294
295
  let settledCleanly = false;
295
296
 
296
- try {
297
+ return runWithFinalizers(async () => {
297
298
  const started = await startAgent(exec, agentName, paneId, effectiveArgs, deadline);
298
299
  if (started.error) return { ...empty, spawnError: `herdr agent start failed: ${started.error}` };
299
300
 
@@ -352,9 +353,10 @@ export async function runHerdrPane(request: HerdrRunRequest): Promise<ChildRunRe
352
353
  timedOut: false,
353
354
  aborted: false,
354
355
  };
355
- } finally {
356
- await cleanup(settledCleanly);
357
- }
356
+ }, [{
357
+ label: "herdr pane finalizer failed",
358
+ run: () => cleanup(settledCleanly),
359
+ }]);
358
360
  }
359
361
 
360
362
  /**