pi-daddy 0.44.3 → 0.45.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 (39) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +48 -0
  3. package/dist/kernel/definition-sources.d.ts +13 -0
  4. package/dist/kernel/definition-sources.d.ts.map +1 -0
  5. package/dist/kernel/definition-sources.js +2 -0
  6. package/dist/kernel/definition-sources.js.map +1 -0
  7. package/dist/kernel/definitions.d.ts +2 -0
  8. package/dist/kernel/definitions.d.ts.map +1 -1
  9. package/dist/kernel/definitions.js.map +1 -1
  10. package/dist/kernel/env-names.d.ts +2 -0
  11. package/dist/kernel/env-names.d.ts.map +1 -1
  12. package/dist/kernel/env-names.js +3 -0
  13. package/dist/kernel/env-names.js.map +1 -1
  14. package/dist/kernel/propagation.d.ts +1 -1
  15. package/dist/kernel/propagation.d.ts.map +1 -1
  16. package/dist/kernel/propagation.js +2 -0
  17. package/dist/kernel/propagation.js.map +1 -1
  18. package/dist/kernel/selected-definitions.d.ts +1 -1
  19. package/dist/kernel/selected-definitions.d.ts.map +1 -1
  20. package/dist/kernel/selected-definitions.js +19 -10
  21. package/dist/kernel/selected-definitions.js.map +1 -1
  22. package/dist/products/public-evidence.d.ts +62 -0
  23. package/dist/products/public-evidence.d.ts.map +1 -0
  24. package/dist/products/public-evidence.js +116 -0
  25. package/dist/products/public-evidence.js.map +1 -0
  26. package/extensions/definition-describe.ts +4 -1
  27. package/extensions/delegate-chain.ts +22 -12
  28. package/extensions/delegation.ts +58 -26
  29. package/extensions/execution-evidence.ts +103 -0
  30. package/extensions/public-evidence.ts +90 -0
  31. package/extensions/run-delegation.ts +20 -4
  32. package/extensions/session.ts +10 -1
  33. package/package.json +1 -1
  34. package/src/kernel/definition-sources.ts +12 -0
  35. package/src/kernel/definitions.ts +2 -0
  36. package/src/kernel/env-names.ts +3 -0
  37. package/src/kernel/propagation.ts +2 -0
  38. package/src/kernel/selected-definitions.ts +31 -10
  39. package/src/products/public-evidence.ts +181 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-evidence.js","sourceRoot":"","sources":["../../src/products/public-evidence.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  /** Read-only handshake with the exact immutable definition dispatched by this extension instance. */
2
+ import { publicEvidenceCall } from "./public-evidence.ts";
2
3
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
4
  import { Type } from "typebox";
4
5
  import { digestDefinition } from "../src/kernel/definitions.ts";
@@ -38,7 +39,9 @@ export function registerDefinitionDescribe(pi: ExtensionAPI, session: GrantsSess
38
39
  binding: definition.binding ?? null,
39
40
  definitionId: definition.definitionId,
40
41
  };
41
- return { content: [{ type: "text" as const, text: JSON.stringify(details) }], details };
42
+ const capture = publicEvidenceCall(session.publicEvidence, _id, "delegate_describe", [{ agent: params.agent }]);
43
+ capture.selected(0, definition);
44
+ return capture.finish({ content: [{ type: "text" as const, text: JSON.stringify(details) }], details });
42
45
  },
43
46
  });
44
47
  }
@@ -1,3 +1,5 @@
1
+ import { publicEvidenceCall } from "./public-evidence.ts";
2
+ import { executionEvidenceContent } from "./execution-evidence.ts";
1
3
  import { assertDefinitionIdentity } from "./definition-describe.ts";
2
4
  /**
3
5
  * `delegate_chain` — a governed sequential pipeline, planned and gated as one unit (ADR-0033).
@@ -141,6 +143,7 @@ export function registerChainTool(pi: ExtensionAPI, session: GrantsSession): voi
141
143
  // Plan every step first. A step that can never run refuses the chain HERE, before anyone is asked — see
142
144
  // `planChain`.
143
145
  const executionIds = steps.map(() => newExecutionId());
146
+ const capture = publicEvidenceCall(session.publicEvidence, _toolCallId, "delegate_chain", steps, executionIds);
144
147
  await reconcileDelegationCapacity(session);
145
148
  const firstReservation = reserveDelegationCapacity(session, executionIds[0]);
146
149
  try {
@@ -325,6 +328,7 @@ export function registerChainTool(pi: ExtensionAPI, session: GrantsSession): voi
325
328
  {
326
329
  ...(index === 0 ? { capacityReservation: firstReservation } : {}),
327
330
  resolvedRuntime: runtimeChoices.get(step),
331
+ onDefinition: (definition) => capture.selected(index, definition),
328
332
  preApproved: availableForStep,
329
333
  toolCallId: _toolCallId,
330
334
  // Only approvals actually offered to this step are attributed or consumed here.
@@ -406,19 +410,25 @@ export function registerChainTool(pi: ExtensionAPI, session: GrantsSession): voi
406
410
  if (outcomes[0].refusal) throw new GovernanceRefusal({ ...outcomes[0].refusal, message });
407
411
  throw new Error(message);
408
412
  }
409
- return {
410
- isError: aborted || outcomes.some((o) => o.control === "failed"),
411
- content: [{ type: "text", text: `${report}${tail}` }],
412
- details: {
413
- outcomes,
414
- steps: steps.length,
415
- completed: outcomes.filter((o) => o.ok).length,
416
- aborted,
417
- budgetPerStep: firstReservation.childAllowance,
418
- ...(blockedReason ? { blockedReason } : {}),
419
- refusals: outcomes.map((outcome) => outcome.refusal ?? null),
413
+ return capture.finish(
414
+ {
415
+ isError: aborted || outcomes.some((o) => o.control === "failed"),
416
+ content: [
417
+ { type: "text", text: `${report}${tail}` },
418
+ executionEvidenceContent("delegate_chain", outcomes, steps.length),
419
+ ],
420
+ details: {
421
+ outcomes,
422
+ steps: steps.length,
423
+ completed: outcomes.filter((o) => o.ok).length,
424
+ aborted,
425
+ budgetPerStep: firstReservation.childAllowance,
426
+ ...(blockedReason ? { blockedReason } : {}),
427
+ refusals: outcomes.map((outcome) => outcome.refusal ?? null),
428
+ },
420
429
  },
421
- };
430
+ outcomes,
431
+ );
422
432
  } finally {
423
433
  firstReservation.finalize({ state: "not-started", reason: "chain ended before its first execution" });
424
434
  }
@@ -1,3 +1,5 @@
1
+ import { publicEvidenceCall } from "./public-evidence.ts";
2
+ import { executionEvidenceContent } from "./execution-evidence.ts";
1
3
  import { reconcileDelegationCapacity, reserveDelegationCapacity } from "./session-capacity.ts";
2
4
  import { registerDefinitionDescribe } from "./definition-describe.ts";
3
5
  /**
@@ -252,6 +254,14 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
252
254
  // ADR-0032: one child, same block. `_onUpdate` was discarded here, so a delegation showed the bare word
253
255
  // `delegate` for up to DEFAULT_TIMEOUT_MS — sixty minutes by default.
254
256
  const progress = progressReporter(session, [params.agent ?? "delegate"], onUpdate as never);
257
+ const occurrence = newDelegationOccurrence(session, 0);
258
+ const capture = publicEvidenceCall(
259
+ session.publicEvidence,
260
+ _toolCallId,
261
+ "delegate",
262
+ [params],
263
+ [occurrence.executionId],
264
+ );
255
265
  const outcome = await runOneDelegation(
256
266
  session,
257
267
  {
@@ -265,12 +275,16 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
265
275
  correlation: params.correlation,
266
276
  workspace: params.workspace,
267
277
  },
268
- newDelegationOccurrence(session, 0),
278
+ occurrence,
269
279
  // Reserve one actual child plus the currently available disjoint subtree allowance.
270
280
  undefined,
271
281
  ctx,
272
282
  signal,
273
- { onProgress: progress.sink(0), toolCallId: _toolCallId },
283
+ {
284
+ onProgress: progress.sink(0),
285
+ toolCallId: _toolCallId,
286
+ onDefinition: (definition) => capture.selected(0, definition),
287
+ },
274
288
  );
275
289
  progress.settle([outcome]);
276
290
 
@@ -283,18 +297,22 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
283
297
  }
284
298
  // Pi 1.0.4 preserves returned isError. Keep every outcome dimension and useful output together.
285
299
  const failed = !outcome.ok || outcome.control === "failed";
286
- return {
287
- isError: failed,
288
- content: [
289
- {
290
- type: "text",
291
- text: failed
292
- ? `delegation failed: ${outcome.reason ?? "required control failed"}${outcome.text ? `\n\n${outcome.text}` : ""}`
293
- : outcome.text || "(no output)",
294
- },
295
- ],
296
- details: outcome,
297
- };
300
+ return capture.finish(
301
+ {
302
+ isError: failed,
303
+ content: [
304
+ {
305
+ type: "text",
306
+ text: failed
307
+ ? `delegation failed: ${outcome.reason ?? "required control failed"}${outcome.text ? `\n\n${outcome.text}` : ""}`
308
+ : outcome.text || "(no output)",
309
+ },
310
+ executionEvidenceContent("delegate", [outcome], 1),
311
+ ],
312
+ details: outcome,
313
+ },
314
+ [outcome],
315
+ );
298
316
  },
299
317
  });
300
318
 
@@ -354,11 +372,19 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
354
372
  // (R-116).
355
373
  const infrastructureErrors: unknown[] = [],
356
374
  occurrences = children.map((_, index) => newDelegationOccurrence(session, index));
375
+ const capture = publicEvidenceCall(
376
+ session.publicEvidence,
377
+ _toolCallId,
378
+ "delegate_all",
379
+ children,
380
+ occurrences.map((row) => row.executionId),
381
+ );
357
382
  const pending = children.map(async (child, index): Promise<DelegationOutcome> => {
358
383
  try {
359
384
  return await runOneDelegation(session, child, occurrences[index], split.perChild, ctx, signal, {
360
385
  // Reserve each batch share synchronously here, before another dispatch can interleave.
361
386
  capacityReservation: reserveDelegationCapacity(session, occurrences[index].executionId, split.perChild),
387
+ onDefinition: (definition) => capture.selected(index, definition),
362
388
  onProgress: progress.sink(index),
363
389
  toolCallId: _toolCallId,
364
390
  });
@@ -383,19 +409,25 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
383
409
  if (failed.length === children.length && outcomes.every((o) => !o.work && !o.final && !o.cleanup))
384
410
  throw totalFanoutFailure(failed, `fan-out failed: every child was refused or failed.\n\n${report}`);
385
411
 
386
- return {
387
- isError: outcomes.some((o) => !o.ok || o.control === "failed"),
388
- content: [{ type: "text", text: report }],
389
- details: {
390
- outcomes,
391
- children: outcomes.length,
392
- failed: failed.length,
393
- budgetPerChild: split.perChild,
394
- granted: outcomes.map((o) => o.granted),
395
- refusals: outcomes.map((o) => o.refusal ?? null),
396
- retention: outcomes.map((o) => o.retention ?? null),
412
+ return capture.finish(
413
+ {
414
+ isError: outcomes.some((o) => !o.ok || o.control === "failed"),
415
+ content: [
416
+ { type: "text", text: report },
417
+ executionEvidenceContent("delegate_all", outcomes, children.length),
418
+ ],
419
+ details: {
420
+ outcomes,
421
+ children: outcomes.length,
422
+ failed: failed.length,
423
+ budgetPerChild: split.perChild,
424
+ granted: outcomes.map((o) => o.granted),
425
+ refusals: outcomes.map((o) => o.refusal ?? null),
426
+ retention: outcomes.map((o) => o.retention ?? null),
427
+ },
397
428
  },
398
- };
429
+ outcomes,
430
+ );
399
431
  },
400
432
  });
401
433
 
@@ -0,0 +1,103 @@
1
+ /** Model-visible projection of runtime facts. UI-only details never reach provider tool output.
2
+ * Keep authored final text and raw result objects unchanged. This is process settlement evidence,
3
+ * not task acceptance or a claim that a disposable workspace was removed.
4
+ */
5
+ import type { DelegationOutcome } from "./execute-child.ts";
6
+ import type { CapturedWorkerIdentity } from "../src/kernel/captured-worker-contract.ts";
7
+
8
+ function identity(value: CapturedWorkerIdentity) {
9
+ return {
10
+ revision: value.revision,
11
+ executionId: value.executionId,
12
+ nonce: value.nonce,
13
+ root: value.root,
14
+ rootDevice: value.rootDevice,
15
+ rootInode: value.rootInode,
16
+ bootId: value.bootId,
17
+ pidNamespace: value.pidNamespace,
18
+ helperPid: value.helperPid,
19
+ helperStartTicks: value.helperStartTicks,
20
+ helperSha256: value.helperSha256,
21
+ workerPid: value.workerPid,
22
+ ownershipPath: value.ownershipPath,
23
+ receiptPath: value.receiptPath,
24
+ };
25
+ }
26
+ function cleanup(value: DelegationOutcome["cleanup"]) {
27
+ if (!value) return null;
28
+ if (value.state === "not-started") return { state: value.state, reason: value.reason };
29
+ if (value.state === "unknown")
30
+ return {
31
+ state: value.state,
32
+ ...(value.identity ? { identity: identity(value.identity) } : {}),
33
+ reason: value.reason,
34
+ };
35
+ return {
36
+ state: value.state,
37
+ identity: identity(value.identity),
38
+ receipt: {
39
+ state: value.receipt.state,
40
+ identity: identity(value.receipt.identity),
41
+ workerCode: value.receipt.workerCode,
42
+ workerSignal: value.receipt.workerSignal,
43
+ reason: value.receipt.reason,
44
+ reapedAll: value.receipt.reapedAll,
45
+ },
46
+ };
47
+ }
48
+ function outcomeEvidence(outcome: DelegationOutcome, index: number) {
49
+ const final = outcome.final;
50
+ return {
51
+ ordinal: index + 1,
52
+ ok: outcome.ok,
53
+ work: outcome.work ?? null,
54
+ control: outcome.control ?? null,
55
+ reason: outcome.reason ?? null,
56
+ exitCode: outcome.exitCode,
57
+ timedOut: outcome.timedOut ?? null,
58
+ aborted: outcome.aborted ?? null,
59
+ truncated: outcome.truncated ?? null,
60
+ spawnFailed: outcome.spawnFailed ?? null,
61
+ final: !final
62
+ ? null
63
+ : final.state === "unavailable"
64
+ ? { state: final.state, reason: final.reason }
65
+ : {
66
+ state: final.state,
67
+ sessionId: final.sessionId,
68
+ messageId: final.messageId,
69
+ leafId: final.leafId,
70
+ sha256: final.sha256,
71
+ },
72
+ cleanup: cleanup(outcome.cleanup),
73
+ observation: outcome.observation
74
+ ? { state: outcome.observation.state, reasons: [...outcome.observation.reasons] }
75
+ : null,
76
+ retention: outcome.retention ? { status: outcome.retention.status } : null,
77
+ };
78
+ }
79
+
80
+ /** Absent dimensions remain null, never inferred as success. Only actual returned outcomes appear. */
81
+ export function executionEvidenceContent(
82
+ tool: "delegate" | "delegate_all" | "delegate_chain",
83
+ outcomes: readonly DelegationOutcome[],
84
+ requested: number,
85
+ ) {
86
+ const evidence = executionEvidence(tool, outcomes, requested);
87
+ return {
88
+ type: "text" as const,
89
+ text:
90
+ "Runtime execution evidence (process settlement; not workspace cleanup or task acceptance):\n```json\n" +
91
+ JSON.stringify(evidence) +
92
+ "\n```",
93
+ };
94
+ }
95
+
96
+ /** Shared allowlisted projection for the model-visible text and exact local capture. */
97
+ export function executionEvidence(
98
+ tool: "delegate" | "delegate_all" | "delegate_chain",
99
+ outcomes: readonly DelegationOutcome[],
100
+ requested: number,
101
+ ) {
102
+ return { version: 1, tool, requested, outcomes: outcomes.map(outcomeEvidence) };
103
+ }
@@ -0,0 +1,90 @@
1
+ /** Composition-only bridge. Snapshot definitions when dispatch actually selects them, never reread paths. */
2
+ import {
3
+ capturePublicEvidence,
4
+ type PublicEvidenceOwner,
5
+ type PublicEvidenceRequest,
6
+ type PublicDefinitionEvidence,
7
+ } from "../src/products/public-evidence.ts";
8
+ import { digestDefinition, type SkillDefinition } from "../src/kernel/definitions.ts";
9
+ import { executionEvidence } from "./execution-evidence.ts";
10
+ import type { DelegationOutcome } from "./execute-child.ts";
11
+ type Tool = "delegate_describe" | "delegate" | "delegate_all" | "delegate_chain";
12
+ interface Result {
13
+ isError?: boolean;
14
+ content: { type: "text"; text: string }[];
15
+ details?: unknown;
16
+ }
17
+ export function publicEvidenceCall(
18
+ owner: PublicEvidenceOwner | undefined,
19
+ toolCallId: string,
20
+ tool: Tool,
21
+ specs: readonly { agent?: string; definitionId?: string }[],
22
+ executionIds: readonly string[] = [],
23
+ ) {
24
+ const requested: PublicEvidenceRequest[] = specs.map((spec, index) => ({
25
+ ordinal: index + 1,
26
+ agent: spec.agent ?? null,
27
+ requestedDefinitionId: spec.definitionId ?? null,
28
+ definitionId: null,
29
+ executionId: executionIds[index] ?? null,
30
+ }));
31
+ const definitions = new Map<number, PublicDefinitionEvidence>();
32
+ return {
33
+ selected(index: number, definition: SkillDefinition | undefined) {
34
+ if (!owner) return;
35
+ definitions.delete(index);
36
+ if (requested[index]) requested[index].definitionId = null;
37
+ if (!definition) return;
38
+ const request = requested[index];
39
+ if (!request || !definition.definitionId) return;
40
+ request.definitionId = definition.definitionId;
41
+ definitions.set(index, {
42
+ agent: definition.name,
43
+ definitionId: definition.definitionId,
44
+ sourceHash: definition.sourceHash ?? null,
45
+ bodySha256: digestDefinition(definition).sha256,
46
+ binding: definition.binding ?? null,
47
+ snapshot: definition.sourceSnapshot,
48
+ });
49
+ },
50
+ finish<T extends Result>(result: T, outcomes: readonly DelegationOutcome[] = []): T {
51
+ if (!owner) return result;
52
+ let status: unknown;
53
+ try {
54
+ const ref = capturePublicEvidence(owner, {
55
+ toolCallId,
56
+ tool,
57
+ response: { isError: result.isError ?? false, content: result.content },
58
+ requested,
59
+ runtimeEvidence: tool === "delegate_describe" ? null : executionEvidence(tool, outcomes, specs.length),
60
+ definitions: [...definitions.values()],
61
+ finals: outcomes.map((outcome, index) => ({
62
+ ordinal: index + 1,
63
+ executionId: requested[index]?.executionId ?? null,
64
+ final:
65
+ outcome.final?.state === "complete"
66
+ ? {
67
+ state: "complete",
68
+ text: outcome.final.text,
69
+ sessionId: outcome.final.sessionId,
70
+ messageId: outcome.final.messageId,
71
+ leafId: outcome.final.leafId,
72
+ sha256: outcome.final.sha256,
73
+ }
74
+ : null,
75
+ })),
76
+ });
77
+ status = { status: "captured", ref: { path: ref.path, sha256: ref.sha256 } };
78
+ } catch (error) {
79
+ status = {
80
+ status: "failed",
81
+ reason: error instanceof Error ? error.message : "public evidence capture failed",
82
+ };
83
+ }
84
+ return {
85
+ ...result,
86
+ content: [...result.content, { type: "text", text: "Public evidence capture: " + JSON.stringify(status) }],
87
+ };
88
+ },
89
+ };
90
+ }
@@ -73,6 +73,8 @@ interface DelegationToolContext extends ApprovalUIContext {
73
73
  /** A planned delegation, plus whatever approvals contributed to it. */
74
74
  export interface GatedPlan {
75
75
  plan: ReturnType<typeof planDelegation>;
76
+ /** Opt-in observation of the exact context used by the final planner invocation. */
77
+ definition?: import("../src/kernel/definitions.ts").SkillDefinition;
76
78
  /** Absent when the gate was never reached — i.e. the plan succeeded or failed for another reason. */
77
79
  approval?: ApprovalOutcome;
78
80
  }
@@ -112,8 +114,17 @@ export async function planWithApprovals(
112
114
  // the re-plan — and two spellings of one argument is the defect R-28 was.
113
115
  const approvalSubject = request.agent ?? DELEGATE_SUBJECT;
114
116
 
115
- let plan = planDelegation(request, { ...(await session.delegationContext(preApproved)), ...extra });
116
- if (plan.ok || !shouldSeekApproval(plan.result)) return { plan };
117
+ let definition: GatedPlan["definition"];
118
+ const observedPlan = (context: Parameters<typeof planDelegation>[1]) => {
119
+ const next = planDelegation(request, context);
120
+ definition =
121
+ session.publicEvidence && next.definitionDigest && request.agent
122
+ ? context.definitions?.get(request.agent)
123
+ : undefined;
124
+ return next;
125
+ };
126
+ let plan = observedPlan({ ...(await session.delegationContext(preApproved)), ...extra });
127
+ if (plan.ok || !shouldSeekApproval(plan.result)) return { plan, ...(definition ? { definition } : {}) };
117
128
 
118
129
  let approval: ApprovalOutcome | undefined;
119
130
  try {
@@ -133,7 +144,7 @@ export async function planWithApprovals(
133
144
  );
134
145
  const outcome = approval;
135
146
  if (outcome.approved.length > 0) {
136
- plan = planDelegation(request, {
147
+ plan = observedPlan({
137
148
  // The scope is the REAL one: a `once` approval still authorises this spawn, and
138
149
  // `inheritApprovals` then keeps it from reaching the child. See ADR-0014. R-29 is what makes
139
150
  // this safe under fan-out: a `once` is consumed by exactly one concurrent caller.
@@ -170,7 +181,7 @@ export async function planWithApprovals(
170
181
  plan = { ...plan, reason: message, refusal: structuredRefusal("APPROVAL_FLOW_FAILED", message) };
171
182
  }
172
183
 
173
- return { plan, approval };
184
+ return { plan, approval, ...(definition ? { definition } : {}) };
174
185
  }
175
186
 
176
187
  /**
@@ -211,6 +222,7 @@ export async function runOneDelegation(
211
222
  /** Trusted reservation made before a chain's upfront gate. */
212
223
  capacityReservation?: CapacityReservation;
213
224
  /** Exact pair selected during the chain preflight; never reselect after its approval. */
225
+ onDefinition?: (definition: import("../src/kernel/definitions.ts").SkillDefinition | undefined) => void;
214
226
  resolvedRuntime?: import("./definition-runtime.ts").ResolvedDefinitionRuntime;
215
227
  /** Actual public execute argument, never read from model-authored correlation or output. */
216
228
  toolCallId?: string;
@@ -266,6 +278,7 @@ export async function runOneDelegation(
266
278
  try {
267
279
  await session.ensureDefinitions?.();
268
280
  assertDefinitionIdentity(session, spec);
281
+
269
282
  const { toolCallId, onProgress, preApproved, taskFrom, taskFromExecutionId, approvalFacts } = options;
270
283
  // pi resolves a BARE model id to an unauthenticated provider and the child dies at startup — the id
271
284
  // alone is not enough, it must be qualified with its provider (`Model<Api>` carries both).
@@ -347,6 +360,7 @@ export async function runOneDelegation(
347
360
  // no child process.
348
361
  const preview = await planWithApprovals(session, request, planContext, null, signal, preApproved);
349
362
  plan = preview.plan;
363
+ options.onDefinition?.(preview.definition);
350
364
  if (plan.ok || shouldSeekApproval(plan.result)) {
351
365
  try {
352
366
  preparedWorkspace = await prepareDelegationWorkspace({
@@ -363,6 +377,7 @@ export async function runOneDelegation(
363
377
  request.correlation = preparedWorkspace.correlation;
364
378
  const gated = await planWithApprovals(session, request, planContext, ctx, signal, preApproved);
365
379
  plan = gated.plan;
380
+ options.onDefinition?.(gated.definition);
366
381
  approvalOutcome = gated.approval;
367
382
  } catch (error) {
368
383
  const value =
@@ -382,6 +397,7 @@ export async function runOneDelegation(
382
397
  preApproved,
383
398
  );
384
399
  plan = gated.plan;
400
+ options.onDefinition?.(gated.definition);
385
401
  approvalOutcome = gated.approval;
386
402
  }
387
403
 
@@ -13,6 +13,8 @@
13
13
  * module reads them **through this object**, live, rather than capturing a copy at load time; capturing a
14
14
  * copy of `ownGrant` before observation is exactly how a stale upper bound would become an enforced one.
15
15
  */
16
+ import { createPublicEvidenceOwner, type PublicEvidenceOwner } from "../src/products/public-evidence.ts";
17
+ import { ENV_PUBLIC_EVIDENCE_DIR } from "../src/kernel/env-names.ts";
16
18
  import { randomUUID } from "node:crypto";
17
19
  import { BoundedReadCleanupError } from "../src/kernel/bounded-read.ts";
18
20
  import type { discoverSkillPackages } from "../src/kernel/skill-packages.ts";
@@ -139,6 +141,8 @@ export function activityChildEnv(activity: { rootId: string; path: string; taskI
139
141
  }
140
142
  /** Keep each child's pane after it finishes, for inspection. Off by default: fan-out would flood it. */
141
143
  export interface GrantsSession extends NativeSessionHost {
144
+ /** Reconciled from the actual owner root on reload; captured call owners stay immutable. */
145
+ publicEvidence?: PublicEvidenceOwner;
142
146
  /** Legacy PI_GRANTS_* names adopted at construction (ADR-0076 PR 3b); the session-start warning names them. */
143
147
  readonly adoptedLegacyEnv: readonly string[];
144
148
  /** False only for the explicit PI_DADDY_GOVERNANCE opt-out; otherwise roots are observed-bound. */
@@ -519,7 +523,8 @@ async function loadProjectDiscovery(
519
523
  await establishRootPin(session, lifecycle);
520
524
  const skips: string[] = [];
521
525
  assertDiscoveryHealthy(session, lifecycle);
522
- const snapshot = selected === undefined ? undefined : await selectedDefinitions(selected);
526
+ const snapshot =
527
+ selected === undefined ? undefined : await selectedDefinitions(selected, session.publicEvidence !== undefined);
523
528
  const definitions =
524
529
  snapshot?.definitions ??
525
530
  (await (session.discovery.definitions ?? loadDefinitions)(cwd, (_path, reason) => skips.push(reason)));
@@ -632,6 +637,7 @@ export function createGrantsSession(
632
637
  */
633
638
  mayDelegate: !governed || inherited.includes(DELEGATE_CAPABILITY) || inherited.includes(WILDCARD),
634
639
  ownerBound: false,
640
+ publicEvidence: createPublicEvidenceOwner(environment[ENV_PUBLIC_EVIDENCE_DIR]),
635
641
  allowUnresolvedModels: environment[ENV_ALLOW_UNRESOLVED_MODELS] === "1",
636
642
  nativeSessionRoot: nativeSessionRootFromEnv(process.env),
637
643
  modelResolutionCache: new Map<string, boolean>(),
@@ -707,6 +713,9 @@ export function createGrantsSession(
707
713
  session.publishChildEnv();
708
714
  },
709
715
  reconcileEnvironment: (environment, lifecycle) => {
716
+ const evidenceDirectory = environment[ENV_PUBLIC_EVIDENCE_DIR];
717
+ if (session.reloadLifecycle !== lifecycle || session.publicEvidence?.directory !== evidenceDirectory)
718
+ session.publicEvidence = createPublicEvidenceOwner(evidenceDirectory);
710
719
  if (session.reloadLifecycle !== lifecycle) {
711
720
  session.pinSettled = false;
712
721
  session.workspacePin = undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-daddy",
3
- "version": "0.44.3",
3
+ "version": "0.45.0",
4
4
  "description": "Capability governance for pi sub-agents: spawn Agent Skills (SKILL.md) definitions whose allowed-tools becomes a grant that can only narrow going down a delegation tree, enforced by pi's own --tools allowlist, with an append-only ledger.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -0,0 +1,12 @@
1
+ /** Opt-in immutable copies of bytes already admitted by selected-resource discovery. Never authority. */
2
+ export interface DefinitionSource {
3
+ readonly kind: "selected-skill" | "package" | "binding-manifest" | "delegated-agent";
4
+ readonly path: string;
5
+ /** Raw admitted bytes, including any BOM. Runtime sourceHash separately identifies decoded text. */
6
+ readonly base64: string;
7
+ }
8
+ export interface DefinitionSourceSnapshot {
9
+ readonly resources: readonly DefinitionSource[];
10
+ /** Exact body dispatched to the child; some generic definitions trim their source body. */
11
+ readonly body: string;
12
+ }
@@ -59,6 +59,8 @@ export function digestDefinition(definition: SkillDefinition): DefinitionDigest
59
59
  }
60
60
 
61
61
  export interface SkillDefinition {
62
+ /** Present only for explicit local public-evidence capture; excluded from authority and identity. */
63
+ sourceSnapshot?: import("./definition-sources.ts").DefinitionSourceSnapshot;
62
64
  /** Immutable selected-resource identity, independent of the model-authored name. */
63
65
  definitionId?: string;
64
66
  selectedSkillHash?: string;
@@ -45,6 +45,8 @@ export const ENV_WORKSPACE_REGISTRY = "PI_DADDY_WORKSPACE_REGISTRY";
45
45
  export const ENV_WORKSPACE_LEASE_DIR = "PI_DADDY_WORKSPACE_LEASE_DIR";
46
46
  /** ADR-0042: what each authorised workspace id MEANT when the grant was established. Authority, not metadata. */
47
47
  export const ENV_WORKSPACE_PIN = "PI_DADDY_WORKSPACE_PIN";
48
+ /** Explicit operator-only local public-response capture; never inherited by a captured child. */
49
+ export const ENV_PUBLIC_EVIDENCE_DIR = "PI_DADDY_PUBLIC_EVIDENCE_DIR";
48
50
  export const ENV_EXECUTION_ARCHIVE = "PI_DADDY_EXECUTION_ARCHIVE";
49
51
  export const ENV_NATIVE_SESSION_ROOT = "PI_DADDY_NATIVE_SESSION_ROOT";
50
52
  export const ENV_RETAIN_NATIVE_SESSIONS = "PI_DADDY_RETAIN_NATIVE_SESSIONS";
@@ -100,6 +102,7 @@ export const GOVERNANCE_ENV_KEYS: readonly string[] = Object.freeze([
100
102
  ENV_WORKSPACE_REGISTRY,
101
103
  ENV_WORKSPACE_LEASE_DIR,
102
104
  ENV_WORKSPACE_PIN,
105
+ ENV_PUBLIC_EVIDENCE_DIR,
103
106
  ENV_EXECUTION_ARCHIVE,
104
107
  ENV_NATIVE_SESSION_ROOT,
105
108
  ENV_RETAIN_NATIVE_SESSIONS,
@@ -25,6 +25,7 @@
25
25
  * interceptor and pi handed the child more than the parent held, the intersection clamps it back.
26
26
  */
27
27
 
28
+ import { ENV_PUBLIC_EVIDENCE_DIR } from "./env-names.ts";
28
29
  import type { Capability } from "./resolve.ts";
29
30
  import { attenuateWorkspacePin, formatWorkspacePin, type WorkspacePins } from "./workspace-pin.ts";
30
31
  import { WILDCARD } from "./pi-tools.ts";
@@ -86,6 +87,7 @@ export {
86
87
  * to give it.
87
88
  */
88
89
  export const GRANT_ENV_KEYS = [
90
+ ENV_PUBLIC_EVIDENCE_DIR,
89
91
  // Not governance state, but the same rule applies for a stronger reason: neither a credential the parent holds
90
92
  // nor the switch that points it at a third party is something a child inherits by being spawned (ADR-0077).
91
93
  //