pi-daddy 0.17.0 → 0.18.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 (113) hide show
  1. package/CHANGELOG.md +154 -80
  2. package/README.md +54 -25
  3. package/dist/approval-prompt.d.ts +3 -1
  4. package/dist/approval-prompt.d.ts.map +1 -1
  5. package/dist/approval-prompt.js +1 -1
  6. package/dist/approval-prompt.js.map +1 -1
  7. package/dist/approval-store.d.ts.map +1 -1
  8. package/dist/approval-store.js +4 -1
  9. package/dist/approval-store.js.map +1 -1
  10. package/dist/approval.d.ts +20 -2
  11. package/dist/approval.d.ts.map +1 -1
  12. package/dist/approval.js +26 -6
  13. package/dist/approval.js.map +1 -1
  14. package/dist/chain.d.ts +6 -1
  15. package/dist/chain.d.ts.map +1 -1
  16. package/dist/chain.js +1 -1
  17. package/dist/chain.js.map +1 -1
  18. package/dist/check-runner.d.ts +61 -0
  19. package/dist/check-runner.d.ts.map +1 -0
  20. package/dist/check-runner.js +237 -0
  21. package/dist/check-runner.js.map +1 -0
  22. package/dist/correlation.d.ts +90 -0
  23. package/dist/correlation.d.ts.map +1 -0
  24. package/dist/correlation.js +183 -0
  25. package/dist/correlation.js.map +1 -0
  26. package/dist/delegate-types.d.ts +140 -0
  27. package/dist/delegate-types.d.ts.map +1 -0
  28. package/dist/delegate-types.js +8 -0
  29. package/dist/delegate-types.js.map +1 -0
  30. package/dist/delegate.d.ts +4 -128
  31. package/dist/delegate.d.ts.map +1 -1
  32. package/dist/delegate.js +68 -72
  33. package/dist/delegate.js.map +1 -1
  34. package/dist/delegation-approval.d.ts +36 -0
  35. package/dist/delegation-approval.d.ts.map +1 -0
  36. package/dist/delegation-approval.js +67 -0
  37. package/dist/delegation-approval.js.map +1 -0
  38. package/dist/git-identity.d.ts +13 -0
  39. package/dist/git-identity.d.ts.map +1 -0
  40. package/dist/git-identity.js +44 -0
  41. package/dist/git-identity.js.map +1 -0
  42. package/dist/index.d.ts +5 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +5 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/lease-record.d.ts +78 -0
  47. package/dist/lease-record.d.ts.map +1 -0
  48. package/dist/lease-record.js +52 -0
  49. package/dist/lease-record.js.map +1 -0
  50. package/dist/ledger-events.d.ts +87 -0
  51. package/dist/ledger-events.d.ts.map +1 -0
  52. package/dist/ledger-events.js +45 -0
  53. package/dist/ledger-events.js.map +1 -0
  54. package/dist/ledger-report.d.ts +19 -12
  55. package/dist/ledger-report.d.ts.map +1 -1
  56. package/dist/ledger-report.js +81 -1
  57. package/dist/ledger-report.js.map +1 -1
  58. package/dist/ledger.d.ts +44 -1
  59. package/dist/ledger.d.ts.map +1 -1
  60. package/dist/ledger.js +20 -2
  61. package/dist/ledger.js.map +1 -1
  62. package/dist/refusals.d.ts +16 -0
  63. package/dist/refusals.d.ts.map +1 -0
  64. package/dist/refusals.js +51 -0
  65. package/dist/refusals.js.map +1 -0
  66. package/dist/run-child.d.ts +4 -0
  67. package/dist/run-child.d.ts.map +1 -1
  68. package/dist/run-child.js +21 -1
  69. package/dist/run-child.js.map +1 -1
  70. package/dist/run-herdr.d.ts +7 -0
  71. package/dist/run-herdr.d.ts.map +1 -1
  72. package/dist/run-herdr.js +41 -12
  73. package/dist/run-herdr.js.map +1 -1
  74. package/dist/workspace-lease.d.ts +28 -0
  75. package/dist/workspace-lease.d.ts.map +1 -0
  76. package/dist/workspace-lease.js +276 -0
  77. package/dist/workspace-lease.js.map +1 -0
  78. package/dist/workspace.d.ts +32 -0
  79. package/dist/workspace.d.ts.map +1 -0
  80. package/dist/workspace.js +79 -0
  81. package/dist/workspace.js.map +1 -0
  82. package/extensions/approval-banking.ts +66 -0
  83. package/extensions/approvals.ts +95 -7
  84. package/extensions/chain-approval-facts.ts +51 -0
  85. package/extensions/chain-ledger.ts +48 -0
  86. package/extensions/delegate-chain.ts +115 -81
  87. package/extensions/delegation.ts +81 -23
  88. package/extensions/execute-child.ts +289 -0
  89. package/extensions/fanout-outcome.ts +97 -0
  90. package/extensions/run-delegation.ts +130 -120
  91. package/extensions/session.ts +4 -0
  92. package/extensions/workspace-runtime.ts +165 -0
  93. package/package.json +17 -1
  94. package/src/approval-prompt.ts +4 -2
  95. package/src/approval-store.ts +4 -1
  96. package/src/approval.ts +47 -14
  97. package/src/chain.ts +3 -1
  98. package/src/check-runner.ts +341 -0
  99. package/src/correlation.ts +260 -0
  100. package/src/delegate-types.ts +144 -0
  101. package/src/delegate.ts +80 -179
  102. package/src/delegation-approval.ts +99 -0
  103. package/src/git-identity.ts +52 -0
  104. package/src/index.ts +50 -0
  105. package/src/lease-record.ts +119 -0
  106. package/src/ledger-events.ts +138 -0
  107. package/src/ledger-report.ts +90 -2
  108. package/src/ledger.ts +68 -3
  109. package/src/refusals.ts +66 -0
  110. package/src/run-child.ts +23 -1
  111. package/src/run-herdr.ts +41 -11
  112. package/src/workspace-lease.ts +303 -0
  113. package/src/workspace.ts +135 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-lease.js","sourceRoot":"","sources":["../src/workspace-lease.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ export declare const ENV_WORKSPACE_REGISTRY = "PI_GRANTS_WORKSPACE_REGISTRY";
2
+ export type WorkspaceAccess = "read" | "write";
3
+ export interface WorkspaceRegistryFile {
4
+ version: 1;
5
+ workspaces: Record<string, {
6
+ path: string;
7
+ }>;
8
+ }
9
+ export interface ValidatedWorkspace {
10
+ workspaceId: string;
11
+ /** Canonical Git worktree root and the child's validated initial CWD. Not a sandbox. */
12
+ root: string;
13
+ gitCommonDir: string;
14
+ }
15
+ export declare function loadWorkspaceRegistry(path: string): Promise<WorkspaceRegistryFile>;
16
+ export declare function resolveWorkspace(registry: WorkspaceRegistryFile, workspaceId: string): Promise<ValidatedWorkspace>;
17
+ /**
18
+ * Canonicalize and validate the initial workspace against Git's registered worktree list.
19
+ * This prevents accidental misrouting. It does not constrain any path a child accesses after spawn.
20
+ */
21
+ export declare function validateRegisteredWorkspace(input: {
22
+ workspaceId: string;
23
+ registeredRoot: string;
24
+ suppliedRoot?: string;
25
+ }): Promise<ValidatedWorkspace>;
26
+ /**
27
+ * The governed-writer lease lives in `./workspace-lease.ts` and is re-exported here so that
28
+ * "the workspace module" remains one import for callers. Split only to stay under the 400-line
29
+ * module ceiling this project enforces mechanically.
30
+ */
31
+ export { ENV_WORKSPACE_LEASE_DIR, acquireWorkspaceLease, defaultWorkspaceLeaseDir, leaseAcquisitionOutcome, leaseReleaseLedgerOutcome, type LeaseReleaseOutcome, type WorkspaceLease, } from "./workspace-lease.ts";
32
+ //# sourceMappingURL=workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,sBAAsB,iCAAiC,CAAC;AAGrE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC;AAE/C,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA6BxF;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAUxH;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,CAAC,KAAK,EAAE;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAyC9B;AAED;;;;GAIG;AACH,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,KAAK,cAAc,GACpB,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,79 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { execFile } from "node:child_process";
3
+ import { once } from "node:events";
4
+ import { mkdir, readFile, realpath, rename, stat, writeFile } from "node:fs/promises";
5
+ import { homedir } from "node:os";
6
+ import { isAbsolute, join } from "node:path";
7
+ import { promisify } from "node:util";
8
+ import { GovernanceRefusal, refusal } from "./refusals.js";
9
+ const execFileAsync = promisify(execFile);
10
+ export const ENV_WORKSPACE_REGISTRY = "PI_GRANTS_WORKSPACE_REGISTRY";
11
+ export async function loadWorkspaceRegistry(path) {
12
+ let parsed;
13
+ try {
14
+ parsed = JSON.parse(await readFile(path, "utf8"));
15
+ }
16
+ catch (error) {
17
+ throw new GovernanceRefusal(refusal("WORKSPACE_NOT_REGISTERED", `workspace registry ${path} could not be read (${String(error)})`, { registry_path: path }));
18
+ }
19
+ const file = parsed;
20
+ if (file?.version !== 1 || !file.workspaces || typeof file.workspaces !== "object" || Array.isArray(file.workspaces)) {
21
+ throw new GovernanceRefusal(refusal("WORKSPACE_NOT_REGISTERED", `workspace registry ${path} must contain {version:1, workspaces:{id:{path}}}`, { registry_path: path }));
22
+ }
23
+ for (const [id, value] of Object.entries(file.workspaces)) {
24
+ if (!id || !value || typeof value.path !== "string" || !isAbsolute(value.path)) {
25
+ throw new GovernanceRefusal(refusal("WORKSPACE_NOT_REGISTERED", `workspace registry entry ${JSON.stringify(id)} must name an absolute path`, { registry_path: path, workspace_id: id }));
26
+ }
27
+ }
28
+ return { version: 1, workspaces: structuredClone(file.workspaces) };
29
+ }
30
+ export async function resolveWorkspace(registry, workspaceId) {
31
+ const registered = Object.hasOwn(registry.workspaces, workspaceId) ? registry.workspaces[workspaceId] : undefined;
32
+ if (!registered) {
33
+ throw new GovernanceRefusal(refusal("WORKSPACE_NOT_REGISTERED", `workspace ${JSON.stringify(workspaceId)} is not present in the operator-owned registry`, { workspace_id: workspaceId }));
34
+ }
35
+ return validateRegisteredWorkspace({ workspaceId, registeredRoot: registered.path });
36
+ }
37
+ /**
38
+ * Canonicalize and validate the initial workspace against Git's registered worktree list.
39
+ * This prevents accidental misrouting. It does not constrain any path a child accesses after spawn.
40
+ */
41
+ export async function validateRegisteredWorkspace(input) {
42
+ let registered;
43
+ let supplied;
44
+ try {
45
+ registered = await realpath(input.registeredRoot);
46
+ supplied = await realpath(input.suppliedRoot ?? input.registeredRoot);
47
+ if (!(await stat(registered)).isDirectory())
48
+ throw new Error("registered root is not a directory");
49
+ }
50
+ catch (error) {
51
+ throw new GovernanceRefusal(refusal("WORKSPACE_NOT_REGISTERED", `workspace ${input.workspaceId} root could not be canonicalized (${String(error)})`, { workspace_id: input.workspaceId }));
52
+ }
53
+ if (registered !== supplied) {
54
+ throw new GovernanceRefusal(refusal("WORKSPACE_NOT_REGISTERED", `workspace ${input.workspaceId} resolved to ${supplied}, not its registered worktree ${registered}`, { workspace_id: input.workspaceId, supplied_root: supplied, registered_root: registered }));
55
+ }
56
+ try {
57
+ const top = (await execFileAsync("git", ["-C", registered, "rev-parse", "--show-toplevel"], { encoding: "utf8" })).stdout.trim();
58
+ const canonicalTop = await realpath(top);
59
+ if (canonicalTop !== registered)
60
+ throw new Error(`path is inside worktree ${canonicalTop}, not its root`);
61
+ const commonRaw = (await execFileAsync("git", ["-C", registered, "rev-parse", "--path-format=absolute", "--git-common-dir"], { encoding: "utf8" })).stdout.trim();
62
+ const gitCommonDir = await realpath(commonRaw);
63
+ const list = (await execFileAsync("git", ["-C", registered, "worktree", "list", "--porcelain"], { encoding: "utf8" })).stdout;
64
+ const registeredWorktrees = await Promise.all(list.split("\n").filter((line) => line.startsWith("worktree ")).map((line) => realpath(line.slice(9))));
65
+ if (!registeredWorktrees.includes(registered))
66
+ throw new Error("Git does not list this path as a worktree");
67
+ return { workspaceId: input.workspaceId, root: registered, gitCommonDir };
68
+ }
69
+ catch (error) {
70
+ throw new GovernanceRefusal(refusal("WORKSPACE_NOT_REGISTERED", `workspace ${input.workspaceId} is not the registered Git worktree it claims to be (${String(error)})`, { workspace_id: input.workspaceId, registered_root: registered }));
71
+ }
72
+ }
73
+ /**
74
+ * The governed-writer lease lives in `./workspace-lease.ts` and is re-exported here so that
75
+ * "the workspace module" remains one import for callers. Split only to stay under the 400-line
76
+ * module ceiling this project enforces mechanically.
77
+ */
78
+ export { ENV_WORKSPACE_LEASE_DIR, acquireWorkspaceLease, defaultWorkspaceLeaseDir, leaseAcquisitionOutcome, leaseReleaseLedgerOutcome, } from "./workspace-lease.js";
79
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Taking back authority a gate banked for an operation that was then refused.
3
+ *
4
+ * Split out of `./approvals.ts` only to stay under the 400-line module ceiling this project enforces
5
+ * mechanically; `./approvals.ts` re-exports both names.
6
+ */
7
+ import { revokeApproval } from "../src/approval-store.ts";
8
+ import type { Capability } from "../src/resolve.ts";
9
+ import { snapshotOf } from "./approvals.ts";
10
+ import type { GrantsSession } from "./session.ts";
11
+
12
+ /** One piece of authority a gate banked, and where it lives, so it can be undone. */
13
+ export interface BankedApproval {
14
+ key: string;
15
+ capability: Capability;
16
+ subject: string;
17
+ /** True when a 30-day entry reached the on-disk store, which outlives the process. */
18
+ persisted: boolean;
19
+ /**
20
+ * False when this call did not CREATE the authority — it joined a dialog another delegation had already
21
+ * opened, or the key was already standing from an earlier grant.
22
+ *
23
+ * Load-bearing: unbanking revoked by `capability@subject` with no ownership check, so one refused
24
+ * delegation could destroy an approval a live sibling was running under. Two reachable shapes — a
25
+ * joined gate (one human answer shared by concurrent waiters, each recording it as its own) and a
26
+ * legacy-then-bound sequence, where a bound call re-prompts over a plain key that already exists and
27
+ * then deletes both on refusal. Only what this call actually created may be taken back.
28
+ */
29
+ owned: boolean;
30
+ }
31
+
32
+ /**
33
+ * Give back authority a gate banked for an operation that was then refused.
34
+ *
35
+ * Best-effort by necessity — the persisted store can be busy — but never silent: an approval that could
36
+ * not be revoked is exactly the one an operator needs to know is still standing (R-113).
37
+ */
38
+ export async function unbankApprovals(
39
+ session: GrantsSession,
40
+ ctx: { ui: { notify: (message: string) => void } },
41
+ banked: BankedApproval[] | undefined,
42
+ ): Promise<void> {
43
+ if (!banked || banked.length === 0) return;
44
+ const stranded: string[] = [];
45
+ for (const entry of banked) {
46
+ // Never take back authority this call did not create — see `owned`.
47
+ if (!entry.owned) continue;
48
+ session.sessionApprovals.delete(entry.key);
49
+ session.sessionApprovalBindings.delete(entry.key);
50
+ if (!entry.persisted) continue;
51
+ const outcome = await revokeApproval(session.cwd, entry.key, (subject) => snapshotOf(session, subject), new Date());
52
+ // `"absent"` is NOT proof the entry is gone. `revokeApproval` reports it when the key is missing from
53
+ // the *valid* set, and `loadApprovals` excludes entries whose ceiling or body digest no longer
54
+ // matches — those are still physically in the file and revive the moment they validate again. So an
55
+ // entry that is merely stale reads as "nothing to revoke" and is silently left standing.
56
+ if (outcome !== "revoked") stranded.push(entry.key);
57
+ }
58
+ // Children inherit from the published set, so it must shrink with the session state, not after it.
59
+ session.publishChildEnv();
60
+ if (stranded.length > 0) {
61
+ ctx.ui.notify(
62
+ `grants: this delegation was refused, but a stored approval for ${stranded.join(", ")} could not be ` +
63
+ `revoked and still stands — remove it with \`/grants revoke\` if it was not intended.`,
64
+ );
65
+ }
66
+ }
@@ -28,7 +28,12 @@ import type { createApprovalGate } from "../src/approval-prompt.ts";
28
28
  import { timeoutMsFromEnv, type PromptOutcomeKind } from "../src/approval-prompt.ts";
29
29
  import { ceilingForDefinition, digestDefinition } from "../src/definitions.ts";
30
30
  import type { Capability } from "../src/resolve.ts";
31
+ import { approvalBindingDigest, type ApprovalBinding } from "../src/correlation.ts";
32
+ import type { RefusalCode } from "../src/refusals.ts";
31
33
  import type { GrantsSession } from "./session.ts";
34
+ import type { BankedApproval } from "./approval-banking.ts";
35
+ export { unbankApprovals } from "./approval-banking.ts";
36
+ export type { BankedApproval } from "./approval-banking.ts";
32
37
 
33
38
  /**
34
39
  * What a subject looks like right now, for the confused-deputy check in the approval store.
@@ -102,6 +107,8 @@ export interface ApprovalOutcome {
102
107
  * and the behaviour that was already in place.
103
108
  */
104
109
  scopes: Record<Capability, ApprovalScope>;
110
+ /** Complete scope map for the ledger, including inherited, session, and persisted answers. */
111
+ recordedScopes: Record<Capability, ApprovalScope>;
105
112
  /**
106
113
  * Where EACH approved capability's yes came from (R-46).
107
114
  *
@@ -110,6 +117,10 @@ export interface ApprovalOutcome {
110
117
  * always computed this map; the bug was that it was thrown away.
111
118
  */
112
119
  sources: Record<Capability, ApprovalSource>;
120
+ /** Exact binding per approved capability when this is a correlated controller call. */
121
+ bindings: Record<Capability, ApprovalBinding>;
122
+ expiresAt: Record<Capability, string>;
123
+ uses: Record<Capability, { max: number; remaining: number }>;
113
124
  humanDenied: boolean;
114
125
  /**
115
126
  * Which of the five prompt outcomes ended the loop, when one did (ADR-0026's F5).
@@ -119,6 +130,18 @@ export interface ApprovalOutcome {
119
130
  * dismissal is a queue or a longer timeout, `no-ui` is an operator pre-approving, `error` is a defect.
120
131
  */
121
132
  gateOutcome?: PromptOutcomeKind;
133
+ /**
134
+ * Authority this gate LEFT BEHIND: session keys added, and 30-day entries written to disk.
135
+ *
136
+ * Recorded so a caller that refuses after the gate can take it back. `run-delegation.ts` already
137
+ * documents the rule at length — "a refused operation must not leave authority behind" — and moved the
138
+ * executor check before the gate to honour it. The adjacent path did not: an operator clicks *Always*,
139
+ * the load-bearing ledger append then fails (a lock timeout under fan-out is a documented condition),
140
+ * the delegation is refused, and a project-wide 30-day approval survives for a spawn that never
141
+ * happened (R-113).
142
+ */
143
+ banked?: BankedApproval[];
144
+ refusalCode?: RefusalCode;
122
145
  reason?: string;
123
146
  }
124
147
 
@@ -143,8 +166,9 @@ export async function storedApprovals(
143
166
  session: GrantsSession,
144
167
  gated: Capability[],
145
168
  subject: string,
169
+ expectedBinding?: ApprovalBinding,
146
170
  ): Promise<ResolveApprovalsResult> {
147
- const { valid } = await loadApprovals({
171
+ const { valid, dropped } = await loadApprovals({
148
172
  cwd: session.cwd,
149
173
  now: new Date(),
150
174
  snapshotOf: (name) => snapshotOf(session, name),
@@ -153,7 +177,10 @@ export async function storedApprovals(
153
177
  gated,
154
178
  subject,
155
179
  sessionApprovals: session.sessionApprovals,
180
+ sessionApprovalBindings: session.sessionApprovalBindings,
156
181
  persisted: valid,
182
+ expectedBinding,
183
+ expiredKeys: new Set(dropped.filter((item) => item.verdict === "expired").map((item) => item.key)),
157
184
  // ADR-0022. Verified HERE rather than at parse time because it needs `session.definitions`, which
158
185
  // arrives at `session_start` — after the session object is built.
159
186
  inherited: verifyInherited(session.inheritedApprovals, (name) => snapshotOf(session, name)),
@@ -180,11 +207,23 @@ export async function obtainApprovals(
180
207
  ctx: ApprovalUIContext | null,
181
208
  task?: string,
182
209
  signal?: AbortSignal,
210
+ expectedBinding?: ApprovalBinding,
183
211
  ): Promise<ApprovalOutcome> {
184
212
  const snapshot = (name: string) => snapshotOf(session, name);
185
- const pre = await storedApprovals(session, gatedBlocked, subject);
213
+ const pre = await storedApprovals(session, gatedBlocked, subject, expectedBinding);
214
+ const preBindings: Record<Capability, ApprovalBinding> = {};
215
+ if (expectedBinding) for (const capability of pre.approved) preBindings[capability] = expectedBinding;
186
216
  if (ctx === null || pre.needsPrompt.length === 0) {
187
- return { approved: pre.approved, sources: pre.sources, scopes: {}, humanDenied: false };
217
+ return {
218
+ approved: pre.approved,
219
+ sources: pre.sources,
220
+ scopes: {},
221
+ recordedScopes: pre.scopes,
222
+ bindings: preBindings,
223
+ expiresAt: pre.expiresAt,
224
+ uses: {},
225
+ humanDenied: false,
226
+ };
188
227
  }
189
228
 
190
229
  // The gate PROVIDER lives on the session, not here: `obtainApprovals` runs once per `tool_call` and once
@@ -203,12 +242,21 @@ export async function obtainApprovals(
203
242
  // about — so a mixed set reports exactly which yes came from where.
204
243
  const sources: Record<Capability, ApprovalSource> = { ...pre.sources };
205
244
  const scopes: Record<Capability, ApprovalScope> = {};
245
+ const recordedScopes: Record<Capability, ApprovalScope> = { ...pre.scopes };
246
+ const bindings: Record<Capability, ApprovalBinding> = { ...preBindings };
247
+ const expiresAt: Record<Capability, string> = { ...pre.expiresAt };
248
+ const uses: Record<Capability, { max: number; remaining: number }> = {};
206
249
  let humanDenied = false;
250
+ const banked: BankedApproval[] = [];
207
251
  let gateOutcome: PromptOutcomeKind | undefined;
252
+ let refusalCode: RefusalCode | undefined;
208
253
  let reason: string | undefined;
209
254
 
210
255
  for (const capability of pre.needsPrompt) {
211
- const outcome = await gate.request({ capability, subject, path, task, signal });
256
+ const outcome = await gate.request({
257
+ capability, subject, path, task, signal,
258
+ ...(expectedBinding ? { bindingKey: approvalBindingDigest(expectedBinding) } : {}),
259
+ });
212
260
  if (outcome.scope === null) {
213
261
  // Forward the gate's own discriminant rather than re-deriving it from `hasUI`: `hasUI` is true in
214
262
  // RPC mode too, so an automated client's timeout or dismissal there would misreport as "a human
@@ -219,6 +267,11 @@ export async function obtainApprovals(
219
267
  // so "was there an operator who timed out, or was there nobody?" had no answer, and the two want
220
268
  // different fixes. It was computed here all along and thrown away.
221
269
  gateOutcome = outcome.kind;
270
+ refusalCode = pre.expired.includes(capability)
271
+ ? "APPROVAL_EXPIRED"
272
+ : pre.scopeMismatched.includes(capability)
273
+ ? "APPROVAL_SCOPE_MISMATCH"
274
+ : "GATED_UNAPPROVED";
222
275
  reason = outcome.reason;
223
276
  break;
224
277
  }
@@ -230,9 +283,20 @@ export async function obtainApprovals(
230
283
  // were not.
231
284
  sources[capability] = outcome.joined ? "session" : "prompt";
232
285
  scopes[capability] = outcome.scope;
286
+ recordedScopes[capability] = outcome.scope;
287
+ if (expectedBinding) bindings[capability] = expectedBinding;
288
+ if (outcome.scope === "once") uses[capability] = { max: 1, remaining: 0 };
233
289
 
234
290
  if (outcome.scope === "session" || outcome.scope === "always") {
235
- session.sessionApprovals.add(approvalKey(capability, subject));
291
+ const key = approvalKey(capability, subject);
292
+ // Whether THIS call created the authority, decided before we add it. `joined` means a human answer
293
+ // opened by another delegation was shared with us; an existing key means it was already standing.
294
+ // Either way it is not ours to take back if we are refused — see `BankedApproval.owned`.
295
+ const owned = !outcome.joined
296
+ && !(expectedBinding ? session.sessionApprovalBindings.has(key) : session.sessionApprovals.has(key));
297
+ if (expectedBinding) session.sessionApprovalBindings.set(key, expectedBinding);
298
+ else session.sessionApprovals.add(key);
299
+ banked.push({ key, capability, subject, persisted: false, owned });
236
300
  }
237
301
  if (outcome.scope === "always") {
238
302
  const now = new Date();
@@ -242,6 +306,8 @@ export async function obtainApprovals(
242
306
  // "type-missing" or "type-changed". Writing it is not unsafe (it fails closed), it is simply a
243
307
  // dead entry that silently accumulates in the file. Skip it and say so, taking the same
244
308
  // downgrade-to-session path as a failed write below: the human's yes still stands.
309
+ const expires = expiryFor(now);
310
+ expiresAt[capability] = expires;
245
311
  const written =
246
312
  current === null
247
313
  ? false
@@ -250,12 +316,13 @@ export async function obtainApprovals(
250
316
  approvalKey(capability, subject),
251
317
  {
252
318
  approvedAt: now.toISOString(),
253
- expiresAt: expiryFor(now),
319
+ expiresAt: expires,
254
320
  cwd: session.cwd,
255
321
  grantAtApproval: current.ceiling,
256
322
  // ADR-0019: the tools AND the instructions the human actually saw. Pinning only the
257
323
  // former would let a rewritten body inherit a yes that was given about different text.
258
324
  bodyAtApproval: current.bodySha256,
325
+ ...(expectedBinding ? { binding: expectedBinding } : {}),
259
326
  // The TASK is deliberately absent (ADR-0021). It is shown in the dialog, where a human
260
327
  // needs it, and never written down, because the model assembles it from the parent's
261
328
  // context and it can carry anything the parent could see.
@@ -263,6 +330,12 @@ export async function obtainApprovals(
263
330
  snapshot,
264
331
  now,
265
332
  );
333
+ if (written) {
334
+ // Mark the on-disk half so a later refusal knows there is a file entry to take back, not just a
335
+ // session key that dies with the process.
336
+ const persistedKey = approvalKey(capability, subject);
337
+ for (const entry of banked) if (entry.key === persistedKey) entry.persisted = true;
338
+ }
266
339
  if (!written) {
267
340
  // The human already said yes; the security decision stands. Only the convenience cache
268
341
  // failed, so this downgrades scope rather than refusing the delegation (see approval-store.ts).
@@ -276,10 +349,25 @@ export async function obtainApprovals(
276
349
  // The human's yes stands; only the cache failed. Downgrade THIS capability, not the set — under
277
350
  // the old scalar this also rewrote the scope of every other capability answered in the same call.
278
351
  scopes[capability] = "session";
352
+ recordedScopes[capability] = "session";
353
+ delete expiresAt[capability];
279
354
  }
280
355
  }
281
356
  session.publishChildEnv(); // a new session approval widens what children may inherit — republish now
282
357
  }
283
358
 
284
- return { approved, sources, scopes, humanDenied, gateOutcome, reason };
359
+ return {
360
+ approved,
361
+ sources,
362
+ scopes,
363
+ recordedScopes,
364
+ bindings,
365
+ expiresAt,
366
+ uses,
367
+ humanDenied,
368
+ gateOutcome,
369
+ refusalCode,
370
+ reason,
371
+ banked,
372
+ };
285
373
  }
@@ -0,0 +1,51 @@
1
+ import type { InheritableApproval } from "../src/approval.ts";
2
+ import type { Capability } from "../src/resolve.ts";
3
+ import type { ApprovalOutcome } from "./approvals.ts";
4
+
5
+ export interface ChainApprovalAudit {
6
+ sources: Map<string, ApprovalOutcome["sources"][Capability]>;
7
+ scopes: Map<string, ApprovalOutcome["recordedScopes"][Capability]>;
8
+ expiresAt: Map<string, string>;
9
+ uses: Map<string, { max: number; remaining: number }>;
10
+ }
11
+
12
+ export function newChainApprovalAudit(): ChainApprovalAudit {
13
+ return { sources: new Map(), scopes: new Map(), expiresAt: new Map(), uses: new Map() };
14
+ }
15
+
16
+ export function rememberChainApproval(
17
+ audit: ChainApprovalAudit,
18
+ capability: Capability,
19
+ subject: string,
20
+ outcome: ApprovalOutcome,
21
+ ): void {
22
+ const key = `${capability}@${subject}`;
23
+ const source = outcome.sources[capability];
24
+ const scope = outcome.recordedScopes[capability];
25
+ if (source) audit.sources.set(key, source);
26
+ if (scope) audit.scopes.set(key, scope);
27
+ const expiresAt = outcome.expiresAt[capability];
28
+ if (expiresAt) audit.expiresAt.set(key, expiresAt);
29
+ const uses = outcome.uses[capability];
30
+ if (uses) audit.uses.set(key, uses);
31
+ }
32
+
33
+ export function chainApprovalFacts(
34
+ audit: ChainApprovalAudit,
35
+ available: InheritableApproval[],
36
+ subject: string,
37
+ ): Pick<ApprovalOutcome, "approved" | "sources" | "scopes" | "expiresAt" | "uses" | "humanDenied"> {
38
+ const entries = available.filter((approval) => approval.subject === subject);
39
+ const values = <T>(map: Map<string, T>) => Object.fromEntries(entries.flatMap((approval) => {
40
+ const value = map.get(`${approval.capability}@${approval.subject}`);
41
+ return value === undefined ? [] : [[approval.capability, value]];
42
+ }));
43
+ return {
44
+ approved: entries.map((approval) => approval.capability),
45
+ sources: values(audit.sources),
46
+ scopes: values(audit.scopes),
47
+ expiresAt: values(audit.expiresAt),
48
+ uses: values(audit.uses),
49
+ humanDenied: false,
50
+ };
51
+ }
@@ -0,0 +1,48 @@
1
+ import type { ApprovalOutcome } from "./approvals.ts";
2
+ import type { Delegation } from "../src/delegate.ts";
3
+ import { appendRecord, buildRecord } from "../src/ledger.ts";
4
+ import type { StructuredRefusal } from "../src/refusals.ts";
5
+ import { childSpawnId } from "../src/fanout.ts";
6
+ import type { GrantsSession } from "./session.ts";
7
+
8
+ /** Audit a chain preflight/gate refusal with the same trusted planner facts as a normal delegation. */
9
+ export async function recordChainRefusal(input: {
10
+ session: GrantsSession;
11
+ plan: Delegation;
12
+ stepIndex: number;
13
+ agent?: string;
14
+ reason: string;
15
+ refusal?: StructuredRefusal;
16
+ approval?: ApprovalOutcome;
17
+ }): Promise<void> {
18
+ if (!input.session.ledgerPath) return;
19
+ await appendRecord(
20
+ { path: input.session.ledgerPath, strict: true },
21
+ buildRecord({
22
+ parentId: input.session.ownSpawnId,
23
+ childId: childSpawnId(input.session.ownSpawnId, input.stepIndex),
24
+ depth: input.plan.childDepth,
25
+ agentType: input.agent ?? "delegate",
26
+ requested: input.plan.requested,
27
+ parentGrant: input.session.ownGrant,
28
+ result: input.plan.result,
29
+ blocked: true,
30
+ reason: input.reason,
31
+ approved: input.approval?.approved,
32
+ approvalSources: input.approval?.sources,
33
+ approvalScopes: input.approval?.recordedScopes,
34
+ approvalExpiresAt: input.approval?.expiresAt,
35
+ approvalUses: input.approval?.uses,
36
+ humanDenied: input.approval?.humanDenied,
37
+ gateOutcome: input.approval?.gateOutcome,
38
+ definitionDigest: input.plan.definitionDigest,
39
+ taskDigest: input.plan.taskDigest,
40
+ correlation: input.plan.correlation,
41
+ // An approval banked before a later chain decline is a yes that led to no spawn, not the stale
42
+ // GATED_UNAPPROVED refusal from the pre-approval plan.
43
+ refusal: input.refusal ?? (input.approval?.approved.length ? undefined : input.plan.refusal),
44
+ executor: input.session.executor.kind,
45
+ now: new Date(),
46
+ }),
47
+ );
48
+ }