pi-daddy 0.44.4 → 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.
- package/CHANGELOG.md +6 -0
- package/README.md +48 -0
- package/dist/kernel/definition-sources.d.ts +13 -0
- package/dist/kernel/definition-sources.d.ts.map +1 -0
- package/dist/kernel/definition-sources.js +2 -0
- package/dist/kernel/definition-sources.js.map +1 -0
- package/dist/kernel/definitions.d.ts +2 -0
- package/dist/kernel/definitions.d.ts.map +1 -1
- package/dist/kernel/definitions.js.map +1 -1
- package/dist/kernel/env-names.d.ts +2 -0
- package/dist/kernel/env-names.d.ts.map +1 -1
- package/dist/kernel/env-names.js +3 -0
- package/dist/kernel/env-names.js.map +1 -1
- package/dist/kernel/propagation.d.ts +1 -1
- package/dist/kernel/propagation.d.ts.map +1 -1
- package/dist/kernel/propagation.js +2 -0
- package/dist/kernel/propagation.js.map +1 -1
- package/dist/kernel/selected-definitions.d.ts +1 -1
- package/dist/kernel/selected-definitions.d.ts.map +1 -1
- package/dist/kernel/selected-definitions.js +19 -10
- package/dist/kernel/selected-definitions.js.map +1 -1
- package/dist/products/public-evidence.d.ts +62 -0
- package/dist/products/public-evidence.d.ts.map +1 -0
- package/dist/products/public-evidence.js +116 -0
- package/dist/products/public-evidence.js.map +1 -0
- package/extensions/definition-describe.ts +4 -1
- package/extensions/delegate-chain.ts +21 -15
- package/extensions/delegation.ts +57 -27
- package/extensions/execution-evidence.ts +10 -1
- package/extensions/public-evidence.ts +90 -0
- package/extensions/run-delegation.ts +20 -4
- package/extensions/session.ts +10 -1
- package/package.json +1 -1
- package/src/kernel/definition-sources.ts +12 -0
- package/src/kernel/definitions.ts +2 -0
- package/src/kernel/env-names.ts +3 -0
- package/src/kernel/propagation.ts +2 -0
- package/src/kernel/selected-definitions.ts +31 -10
- 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":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EACL,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,SAAS,EACT,aAAa,EACb,SAAS,EACT,YAAY,EACZ,QAAQ,GACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEtD,MAAM,CAAC,MAAM,sBAAsB,GAAG,6BAA6B,CAAC;AACpE,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAC1D,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AA8C5C,MAAM,UAAU,yBAAyB,CAAC,SAA6B;IACrE,OAAO,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,KAAK;QACnD,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;AAC1D,CAAC;AACD,MAAM,MAAM,GAAG,CAAC,KAAiB,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACvF,MAAM,IAAI,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AACnF,0GAA0G;AAC1G,MAAM,UAAU,qBAAqB,CAAC,KAA0B,EAAE,KAA0B;IAC1F,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;IAClC,IACE,OAAO,CAAC,QAAQ,KAAK,OAAO;QAC5B,CAAC,UAAU,CAAC,SAAS,CAAC;QACtB,OAAO,CAAC,SAAS,CAAC,KAAK,SAAS;QAChC,YAAY,CAAC,SAAS,CAAC,KAAK,SAAS;QAErC,MAAM,KAAK,CAAC,6EAA6E,CAAC,CAAC;IAC7F,MAAM,MAAM,GAAG,QAAQ,CACrB,SAAS,EACT,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,WAAW,GAAG,SAAS,CAAC,UAAU,GAAG,SAAS,CAAC,UAAU,CACzF,CAAC;IACF,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,GAAG,KAAK,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC;YACrF,MAAM,KAAK,CAAC,yEAAyE,CAAC,CAAC;QACzF,MAAM,SAAS,GAAG,UAAU,EAAE,EAC5B,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,iBAAiB,MAAM,EAAE,CAAC;QAC3C,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACtD,MAAM,SAAS,GAAG,QAAQ,CACxB,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,EACzB,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,WAAW,GAAG,SAAS,CAAC,UAAU,CAClE,CAAC;QACF,IAAI,QAA2B,CAAC;QAChC,IAAI,CAAC;YACH,IAAI,KAAK,GAAG,CAAC,EACX,KAAK,GAAG,CAAC,CAAC;YACZ,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,OAAe,EAAqB,EAAE;gBAC/D,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;gBAC5B,KAAK,IAAI,CAAC,CAAC;gBACX,IAAI,KAAK,GAAG,yBAAyB,IAAI,KAAK,GAAG,yBAAyB;oBACxE,MAAM,KAAK,CAAC,qFAAqF,CAAC,CAAC;gBACrG,MAAM,EAAE,GAAG,QAAQ,CACjB,IAAI,CAAC,iBAAiB,SAAS,EAAE,EAAE,IAAI,CAAC,EACxC,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,UAAU,GAAG,SAAS,CAAC,UAAU,EACvG,KAAK,CACN,CAAC;gBACF,IAAI,CAAC;oBACH,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE;wBAAE,MAAM,KAAK,CAAC,gDAAgD,CAAC,CAAC;oBAC3F,aAAa,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;oBAC3B,SAAS,CAAC,EAAE,CAAC,CAAC;gBAChB,CAAC;wBAAS,CAAC;oBACT,SAAS,CAAC,EAAE,CAAC,CAAC;gBAChB,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;YAC1F,CAAC,CAAC;YACF,MAAM,QAAQ,GAAG,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC5D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC1C,IAAI,CAAC,OAAO,CAAC,KAAK;oBAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;gBACvG,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;gBAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBACxC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAC,MAAM;oBAAE,MAAM,KAAK,CAAC,2DAA2D,CAAC,CAAC;gBAChH,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;oBAC7E,MAAM,KAAK,CAAC,gEAAgE,CAAC,CAAC;gBAChF,OAAO;oBACL,OAAO,EAAE,OAAO,CAAC,OAAO;oBACxB,WAAW,EAAE,OAAO,CAAC,WAAW;oBAChC,KAAK,EAAE,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,WAAW,OAAO,CAAC,OAAO,YAAY,EAAE,KAAK,CAAC,EAAE;iBACpF,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE;gBAC9D,IAAI,CAAC,UAAU,CAAC,QAAQ;oBAAE,MAAM,KAAK,CAAC,4CAA4C,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;gBACtG,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;oBACzE,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,IAAI,EAAE,GAAG,CAAC,cAAc,KAAK,WAAW,QAAQ,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;iBAC9F,CAAC,CAAC,CAAC;gBACJ,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,GAAG,UAAU,CAAC;gBACxD,OAAO;oBACL,GAAG,QAAQ;oBACX,SAAS;oBACT,IAAI,EAAE,GAAG,CAAC,cAAc,KAAK,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;iBACzF,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,QAAQ,GAAG,GAAG,CACZ,eAAe,EACf,IAAI,CAAC;gBACH,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,CAAC;gBACV,SAAS;gBACT,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,KAAK,EAAE,UAAU;gBACjB,QAAQ;gBACR,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,eAAe,EAAE,KAAK,CAAC,eAAe;gBACtC,MAAM;gBACN,WAAW;aACZ,CAAC,CACH,CAAC;YACF,SAAS,CAAC,SAAS,CAAC,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,SAAS,CAAC,CAAC;QACvB,CAAC;QACD,SAAS,CAAC,MAAM,CAAC,CAAC;QAClB,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;QACpC,IAAI,YAAY,CAAC,SAAS,CAAC,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG;YAC/F,MAAM,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC3E,OAAO,QAAQ,CAAC;IAClB,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,MAAM,CAAC,CAAC;IACpB,CAAC;AACH,CAAC"}
|
|
@@ -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
|
-
|
|
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,4 @@
|
|
|
1
|
+
import { publicEvidenceCall } from "./public-evidence.ts";
|
|
1
2
|
import { executionEvidenceContent } from "./execution-evidence.ts";
|
|
2
3
|
import { assertDefinitionIdentity } from "./definition-describe.ts";
|
|
3
4
|
/**
|
|
@@ -142,6 +143,7 @@ export function registerChainTool(pi: ExtensionAPI, session: GrantsSession): voi
|
|
|
142
143
|
// Plan every step first. A step that can never run refuses the chain HERE, before anyone is asked — see
|
|
143
144
|
// `planChain`.
|
|
144
145
|
const executionIds = steps.map(() => newExecutionId());
|
|
146
|
+
const capture = publicEvidenceCall(session.publicEvidence, _toolCallId, "delegate_chain", steps, executionIds);
|
|
145
147
|
await reconcileDelegationCapacity(session);
|
|
146
148
|
const firstReservation = reserveDelegationCapacity(session, executionIds[0]);
|
|
147
149
|
try {
|
|
@@ -326,6 +328,7 @@ export function registerChainTool(pi: ExtensionAPI, session: GrantsSession): voi
|
|
|
326
328
|
{
|
|
327
329
|
...(index === 0 ? { capacityReservation: firstReservation } : {}),
|
|
328
330
|
resolvedRuntime: runtimeChoices.get(step),
|
|
331
|
+
onDefinition: (definition) => capture.selected(index, definition),
|
|
329
332
|
preApproved: availableForStep,
|
|
330
333
|
toolCallId: _toolCallId,
|
|
331
334
|
// Only approvals actually offered to this step are attributed or consumed here.
|
|
@@ -407,22 +410,25 @@ export function registerChainTool(pi: ExtensionAPI, session: GrantsSession): voi
|
|
|
407
410
|
if (outcomes[0].refusal) throw new GovernanceRefusal({ ...outcomes[0].refusal, message });
|
|
408
411
|
throw new Error(message);
|
|
409
412
|
}
|
|
410
|
-
return
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
+
},
|
|
424
429
|
},
|
|
425
|
-
|
|
430
|
+
outcomes,
|
|
431
|
+
);
|
|
426
432
|
} finally {
|
|
427
433
|
firstReservation.finalize({ state: "not-started", reason: "chain ended before its first execution" });
|
|
428
434
|
}
|
package/extensions/delegation.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { publicEvidenceCall } from "./public-evidence.ts";
|
|
1
2
|
import { executionEvidenceContent } from "./execution-evidence.ts";
|
|
2
3
|
import { reconcileDelegationCapacity, reserveDelegationCapacity } from "./session-capacity.ts";
|
|
3
4
|
import { registerDefinitionDescribe } from "./definition-describe.ts";
|
|
@@ -253,6 +254,14 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
|
|
|
253
254
|
// ADR-0032: one child, same block. `_onUpdate` was discarded here, so a delegation showed the bare word
|
|
254
255
|
// `delegate` for up to DEFAULT_TIMEOUT_MS — sixty minutes by default.
|
|
255
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
|
+
);
|
|
256
265
|
const outcome = await runOneDelegation(
|
|
257
266
|
session,
|
|
258
267
|
{
|
|
@@ -266,12 +275,16 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
|
|
|
266
275
|
correlation: params.correlation,
|
|
267
276
|
workspace: params.workspace,
|
|
268
277
|
},
|
|
269
|
-
|
|
278
|
+
occurrence,
|
|
270
279
|
// Reserve one actual child plus the currently available disjoint subtree allowance.
|
|
271
280
|
undefined,
|
|
272
281
|
ctx,
|
|
273
282
|
signal,
|
|
274
|
-
{
|
|
283
|
+
{
|
|
284
|
+
onProgress: progress.sink(0),
|
|
285
|
+
toolCallId: _toolCallId,
|
|
286
|
+
onDefinition: (definition) => capture.selected(0, definition),
|
|
287
|
+
},
|
|
275
288
|
);
|
|
276
289
|
progress.settle([outcome]);
|
|
277
290
|
|
|
@@ -284,19 +297,22 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
|
|
|
284
297
|
}
|
|
285
298
|
// Pi 1.0.4 preserves returned isError. Keep every outcome dimension and useful output together.
|
|
286
299
|
const failed = !outcome.ok || outcome.control === "failed";
|
|
287
|
-
return
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
+
);
|
|
300
316
|
},
|
|
301
317
|
});
|
|
302
318
|
|
|
@@ -356,11 +372,19 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
|
|
|
356
372
|
// (R-116).
|
|
357
373
|
const infrastructureErrors: unknown[] = [],
|
|
358
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
|
+
);
|
|
359
382
|
const pending = children.map(async (child, index): Promise<DelegationOutcome> => {
|
|
360
383
|
try {
|
|
361
384
|
return await runOneDelegation(session, child, occurrences[index], split.perChild, ctx, signal, {
|
|
362
385
|
// Reserve each batch share synchronously here, before another dispatch can interleave.
|
|
363
386
|
capacityReservation: reserveDelegationCapacity(session, occurrences[index].executionId, split.perChild),
|
|
387
|
+
onDefinition: (definition) => capture.selected(index, definition),
|
|
364
388
|
onProgress: progress.sink(index),
|
|
365
389
|
toolCallId: _toolCallId,
|
|
366
390
|
});
|
|
@@ -385,19 +409,25 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
|
|
|
385
409
|
if (failed.length === children.length && outcomes.every((o) => !o.work && !o.final && !o.cleanup))
|
|
386
410
|
throw totalFanoutFailure(failed, `fan-out failed: every child was refused or failed.\n\n${report}`);
|
|
387
411
|
|
|
388
|
-
return
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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
|
+
},
|
|
399
428
|
},
|
|
400
|
-
|
|
429
|
+
outcomes,
|
|
430
|
+
);
|
|
401
431
|
},
|
|
402
432
|
});
|
|
403
433
|
|
|
@@ -83,7 +83,7 @@ export function executionEvidenceContent(
|
|
|
83
83
|
outcomes: readonly DelegationOutcome[],
|
|
84
84
|
requested: number,
|
|
85
85
|
) {
|
|
86
|
-
const evidence =
|
|
86
|
+
const evidence = executionEvidence(tool, outcomes, requested);
|
|
87
87
|
return {
|
|
88
88
|
type: "text" as const,
|
|
89
89
|
text:
|
|
@@ -92,3 +92,12 @@ export function executionEvidenceContent(
|
|
|
92
92
|
"\n```",
|
|
93
93
|
};
|
|
94
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
|
|
116
|
-
|
|
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 =
|
|
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
|
|
package/extensions/session.ts
CHANGED
|
@@ -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 =
|
|
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.
|
|
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;
|
package/src/kernel/env-names.ts
CHANGED
|
@@ -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
|
//
|
|
@@ -5,6 +5,7 @@ import { lstat, realpath } from "node:fs/promises";
|
|
|
5
5
|
import { BoundedReadCleanupError, readBoundedBytes } from "./bounded-read.ts";
|
|
6
6
|
import { ceilingForDefinition, parseSkillDefinition, type SkillDefinition } from "./definitions.ts";
|
|
7
7
|
import { resolve } from "./resolve.ts";
|
|
8
|
+
import type { DefinitionSource } from "./definition-sources.ts";
|
|
8
9
|
import type { CatalogEntry } from "./catalog.ts";
|
|
9
10
|
export interface SelectedCommand {
|
|
10
11
|
source?: string;
|
|
@@ -16,13 +17,20 @@ const hash = (value: string) => createHash("sha256").update(value, "utf8").diges
|
|
|
16
17
|
const object = (x: unknown): x is Record<string, unknown> => !!x && typeof x === "object" && !Array.isArray(x);
|
|
17
18
|
const exact = (x: Record<string, unknown>, keys: string[]) =>
|
|
18
19
|
Object.keys(x).sort().join(",") === [...keys].sort().join(",");
|
|
19
|
-
async function read(path: string): Promise<string> {
|
|
20
|
+
async function read(path: string, kind: DefinitionSource["kind"], sources?: DefinitionSource[]): Promise<string> {
|
|
20
21
|
if (!(await lstat(path)).isFile()) throw Error(`nonregular selected resource: ${path}`);
|
|
21
22
|
const result = await readBoundedBytes(path, { maxBytes: 1024 * 1024, timeoutMs: 3000 });
|
|
22
23
|
if (!result.ok) throw Error(result.detail);
|
|
23
|
-
|
|
24
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(result.bytes);
|
|
25
|
+
sources?.push(Object.freeze({ kind, path, base64: result.bytes.toString("base64") }));
|
|
26
|
+
return text;
|
|
24
27
|
}
|
|
25
|
-
async function principalBinding(
|
|
28
|
+
async function principalBinding(
|
|
29
|
+
path: string,
|
|
30
|
+
inline: SkillDefinition,
|
|
31
|
+
skillText: string,
|
|
32
|
+
sources?: DefinitionSource[],
|
|
33
|
+
): Promise<SkillDefinition> {
|
|
26
34
|
const phase = inline.name;
|
|
27
35
|
const marked = inline.metadata?.["principal-package"] === "principal-pi-skills";
|
|
28
36
|
if (!phases.includes(phase) || path !== join(dirname(dirname(path)), phase, "SKILL.md")) {
|
|
@@ -32,7 +40,7 @@ async function principalBinding(path: string, inline: SkillDefinition, skillText
|
|
|
32
40
|
const root = dirname(dirname(path));
|
|
33
41
|
let packageText: string;
|
|
34
42
|
try {
|
|
35
|
-
packageText = await read(join(root, "package.json"));
|
|
43
|
+
packageText = await read(join(root, "package.json"), "package", sources);
|
|
36
44
|
} catch (error) {
|
|
37
45
|
if ((error as NodeJS.ErrnoException).code === "ENOENT" && !marked) return inline;
|
|
38
46
|
throw error;
|
|
@@ -42,7 +50,7 @@ async function principalBinding(path: string, inline: SkillDefinition, skillText
|
|
|
42
50
|
if (marked) throw Error("marked Principal skill has missing or wrong package identity");
|
|
43
51
|
return inline;
|
|
44
52
|
}
|
|
45
|
-
const manifest: unknown = JSON.parse(await read(join(root, "principal-agents.json")));
|
|
53
|
+
const manifest: unknown = JSON.parse(await read(join(root, "principal-agents.json"), "binding-manifest", sources));
|
|
46
54
|
if (
|
|
47
55
|
!object(manifest) ||
|
|
48
56
|
!exact(manifest, ["version", "package", "bindings"]) ||
|
|
@@ -73,7 +81,7 @@ async function principalBinding(path: string, inline: SkillDefinition, skillText
|
|
|
73
81
|
(await realpath(path)) !== join(await realpath(root), row.skill)
|
|
74
82
|
)
|
|
75
83
|
throw Error("Principal binding path escaped its selected package");
|
|
76
|
-
const agentText = await read(agentPath);
|
|
84
|
+
const agentText = await read(agentPath, "delegated-agent", sources);
|
|
77
85
|
if (hash(skillText) !== row.skillSha256 || hash(agentText) !== row.agentSha256)
|
|
78
86
|
throw Error("Principal binding hash mismatch");
|
|
79
87
|
const agent = parseSkillDefinition(agentPath, agentText);
|
|
@@ -106,7 +114,10 @@ async function principalBinding(path: string, inline: SkillDefinition, skillText
|
|
|
106
114
|
binding: Object.freeze({ package: "principal-pi-skills", phase }),
|
|
107
115
|
};
|
|
108
116
|
}
|
|
109
|
-
export async function selectedDefinitions(
|
|
117
|
+
export async function selectedDefinitions(
|
|
118
|
+
commands: readonly SelectedCommand[],
|
|
119
|
+
captureSources = false,
|
|
120
|
+
): Promise<{
|
|
110
121
|
definitions: Map<string, SkillDefinition>;
|
|
111
122
|
skills: CatalogEntry[];
|
|
112
123
|
skips: string[];
|
|
@@ -127,10 +138,11 @@ export async function selectedDefinitions(commands: readonly SelectedCommand[]):
|
|
|
127
138
|
try {
|
|
128
139
|
if (!command.sourceInfo?.path) throw Error("selected resource lacks a public source path");
|
|
129
140
|
const path = resolvePath(command.sourceInfo.path);
|
|
130
|
-
const
|
|
141
|
+
const sources: DefinitionSource[] | undefined = captureSources ? [] : undefined;
|
|
142
|
+
const text = await read(path, "selected-skill", sources);
|
|
131
143
|
const parsed = parseSkillDefinition(path, text);
|
|
132
144
|
if (!parsed || parsed.name !== name) throw Error("selected name/path does not match definition");
|
|
133
|
-
const bound = await principalBinding(path, parsed, text);
|
|
145
|
+
const bound = await principalBinding(path, parsed, text, sources);
|
|
134
146
|
const definitionId = hash(
|
|
135
147
|
JSON.stringify({
|
|
136
148
|
name,
|
|
@@ -145,7 +157,16 @@ export async function selectedDefinitions(commands: readonly SelectedCommand[]):
|
|
|
145
157
|
}),
|
|
146
158
|
);
|
|
147
159
|
if (bound.metadata) Object.freeze(bound.metadata);
|
|
148
|
-
definitions.set(
|
|
160
|
+
definitions.set(
|
|
161
|
+
name,
|
|
162
|
+
Object.freeze({
|
|
163
|
+
...bound,
|
|
164
|
+
definitionId,
|
|
165
|
+
...(sources
|
|
166
|
+
? { sourceSnapshot: Object.freeze({ resources: Object.freeze(sources), body: bound.body }) }
|
|
167
|
+
: {}),
|
|
168
|
+
}),
|
|
169
|
+
);
|
|
149
170
|
skills.push({ capability: `skill:${name}`, kind: "skill", source: path });
|
|
150
171
|
} catch (error) {
|
|
151
172
|
if (error instanceof BoundedReadCleanupError) throw error;
|