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.
- package/CHANGELOG.md +154 -80
- package/README.md +54 -25
- package/dist/approval-prompt.d.ts +3 -1
- package/dist/approval-prompt.d.ts.map +1 -1
- package/dist/approval-prompt.js +1 -1
- package/dist/approval-prompt.js.map +1 -1
- package/dist/approval-store.d.ts.map +1 -1
- package/dist/approval-store.js +4 -1
- package/dist/approval-store.js.map +1 -1
- package/dist/approval.d.ts +20 -2
- package/dist/approval.d.ts.map +1 -1
- package/dist/approval.js +26 -6
- package/dist/approval.js.map +1 -1
- package/dist/chain.d.ts +6 -1
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +1 -1
- package/dist/chain.js.map +1 -1
- package/dist/check-runner.d.ts +61 -0
- package/dist/check-runner.d.ts.map +1 -0
- package/dist/check-runner.js +237 -0
- package/dist/check-runner.js.map +1 -0
- package/dist/correlation.d.ts +90 -0
- package/dist/correlation.d.ts.map +1 -0
- package/dist/correlation.js +183 -0
- package/dist/correlation.js.map +1 -0
- package/dist/delegate-types.d.ts +140 -0
- package/dist/delegate-types.d.ts.map +1 -0
- package/dist/delegate-types.js +8 -0
- package/dist/delegate-types.js.map +1 -0
- package/dist/delegate.d.ts +4 -128
- package/dist/delegate.d.ts.map +1 -1
- package/dist/delegate.js +68 -72
- package/dist/delegate.js.map +1 -1
- package/dist/delegation-approval.d.ts +36 -0
- package/dist/delegation-approval.d.ts.map +1 -0
- package/dist/delegation-approval.js +67 -0
- package/dist/delegation-approval.js.map +1 -0
- package/dist/git-identity.d.ts +13 -0
- package/dist/git-identity.d.ts.map +1 -0
- package/dist/git-identity.js +44 -0
- package/dist/git-identity.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/lease-record.d.ts +78 -0
- package/dist/lease-record.d.ts.map +1 -0
- package/dist/lease-record.js +52 -0
- package/dist/lease-record.js.map +1 -0
- package/dist/ledger-events.d.ts +87 -0
- package/dist/ledger-events.d.ts.map +1 -0
- package/dist/ledger-events.js +45 -0
- package/dist/ledger-events.js.map +1 -0
- package/dist/ledger-report.d.ts +19 -12
- package/dist/ledger-report.d.ts.map +1 -1
- package/dist/ledger-report.js +81 -1
- package/dist/ledger-report.js.map +1 -1
- package/dist/ledger.d.ts +44 -1
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +20 -2
- package/dist/ledger.js.map +1 -1
- package/dist/refusals.d.ts +16 -0
- package/dist/refusals.d.ts.map +1 -0
- package/dist/refusals.js +51 -0
- package/dist/refusals.js.map +1 -0
- package/dist/run-child.d.ts +4 -0
- package/dist/run-child.d.ts.map +1 -1
- package/dist/run-child.js +21 -1
- package/dist/run-child.js.map +1 -1
- package/dist/run-herdr.d.ts +7 -0
- package/dist/run-herdr.d.ts.map +1 -1
- package/dist/run-herdr.js +41 -12
- package/dist/run-herdr.js.map +1 -1
- package/dist/workspace-lease.d.ts +28 -0
- package/dist/workspace-lease.d.ts.map +1 -0
- package/dist/workspace-lease.js +276 -0
- package/dist/workspace-lease.js.map +1 -0
- package/dist/workspace.d.ts +32 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +79 -0
- package/dist/workspace.js.map +1 -0
- package/extensions/approval-banking.ts +66 -0
- package/extensions/approvals.ts +95 -7
- package/extensions/chain-approval-facts.ts +51 -0
- package/extensions/chain-ledger.ts +48 -0
- package/extensions/delegate-chain.ts +115 -81
- package/extensions/delegation.ts +81 -23
- package/extensions/execute-child.ts +289 -0
- package/extensions/fanout-outcome.ts +97 -0
- package/extensions/run-delegation.ts +130 -120
- package/extensions/session.ts +4 -0
- package/extensions/workspace-runtime.ts +165 -0
- package/package.json +17 -1
- package/src/approval-prompt.ts +4 -2
- package/src/approval-store.ts +4 -1
- package/src/approval.ts +47 -14
- package/src/chain.ts +3 -1
- package/src/check-runner.ts +341 -0
- package/src/correlation.ts +260 -0
- package/src/delegate-types.ts +144 -0
- package/src/delegate.ts +80 -179
- package/src/delegation-approval.ts +99 -0
- package/src/git-identity.ts +52 -0
- package/src/index.ts +50 -0
- package/src/lease-record.ts +119 -0
- package/src/ledger-events.ts +138 -0
- package/src/ledger-report.ts +90 -2
- package/src/ledger.ts +68 -3
- package/src/refusals.ts +66 -0
- package/src/run-child.ts +23 -1
- package/src/run-herdr.ts +41 -11
- package/src/workspace-lease.ts +303 -0
- package/src/workspace.ts +135 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { GovernanceRefusal, refusal } from "./refusals.js";
|
|
3
|
+
const MAX_CORRELATION_BYTES = 32 * 1024;
|
|
4
|
+
/**
|
|
5
|
+
* Per-field bound on the STRING fields — most are an id, a digest, a label or a timestamp. It does not
|
|
6
|
+
* apply to the three sequence numbers or to `assurance_scope`, which are checked separately; an earlier
|
|
7
|
+
* version of this comment claimed it covered every declared field.
|
|
8
|
+
*/
|
|
9
|
+
const MAX_CORRELATION_FIELD_CHARS = 512;
|
|
10
|
+
/** `assurance_scope` is the one structured field, so it gets its own, larger bound. */
|
|
11
|
+
const MAX_CORRELATION_SCOPE_BYTES = 4 * 1024;
|
|
12
|
+
/**
|
|
13
|
+
* The exact field set of the pinned schema 1.0 contract. Anything else is refused by name.
|
|
14
|
+
*
|
|
15
|
+
* This is a whitelist rather than a size cap because `correlation` is a MODEL-FACING tool parameter that
|
|
16
|
+
* is copied verbatim onto every append-only ledger event. `src/ledger.ts` states the invariant — capability
|
|
17
|
+
* ids, counts and identifiers only, never prompts, tool arguments or results — and ADR-0034 repeats that the
|
|
18
|
+
* ledger must never carry task text. A 32 KB "bounded JSON object" with no key whitelist and no per-field
|
|
19
|
+
* length bound satisfied neither: `assurance_scope` was declared `Type.Any()`, undeclared keys survived the
|
|
20
|
+
* round trip, and every string was unbounded, so a model could write 32 KB of arbitrary text into the
|
|
21
|
+
* ledger through it (R-111). Refusing an unknown key is also the loud option: if upstream adds a field, the
|
|
22
|
+
* refusal names it, which is an actionable break rather than a silent secrets sink.
|
|
23
|
+
*/
|
|
24
|
+
const CORRELATION_FIELDS = new Set([
|
|
25
|
+
"schema_version", "run_id", "task_id", "workspace_id", "context_id", "phase", "assurance",
|
|
26
|
+
"assurance_effective", "policy_label", "assurance_source", "assurance_scope", "activated_at",
|
|
27
|
+
"plan_digest", "definition_digest", "task_digest", "base_sha", "head_sha", "tree_sha",
|
|
28
|
+
"event_seq", "last_change_seq", "last_authority_seq", "check_receipt_id",
|
|
29
|
+
]);
|
|
30
|
+
const CORRELATION_NUMERIC = new Set([
|
|
31
|
+
"event_seq", "last_change_seq", "last_authority_seq",
|
|
32
|
+
]);
|
|
33
|
+
function correlationRefusal(message, details) {
|
|
34
|
+
return new GovernanceRefusal(refusal("CORRELATION_INVALID", `correlation metadata: ${message}`, details));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Size refusals get their own code, because they call for a different response: "you sent too much" is
|
|
38
|
+
* retryable by truncating, "you sent a field we do not recognise" is not. `CORRELATION_TOO_LARGE` was
|
|
39
|
+
* declared in the taxonomy and constructed nowhere, so the union advertised a distinction the code did not
|
|
40
|
+
* make — and the enumeration test kept the dead member green.
|
|
41
|
+
*/
|
|
42
|
+
function correlationTooLarge(message, details) {
|
|
43
|
+
return new GovernanceRefusal(refusal("CORRELATION_TOO_LARGE", `correlation metadata: ${message}`, details));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Snapshot bounded JSON metadata so a caller cannot mutate a record after planning, and so nothing
|
|
47
|
+
* unbounded or undeclared can reach the ledger through it.
|
|
48
|
+
*
|
|
49
|
+
* Refuses with a stable code rather than a bare `Error`: this is reachable from a model-facing tool
|
|
50
|
+
* parameter on all three delegation tools, and it used to throw outside every try, producing a governed
|
|
51
|
+
* refusal with no code and no ledger line at all (R-112).
|
|
52
|
+
*/
|
|
53
|
+
export function normaliseCorrelation(input) {
|
|
54
|
+
if (input === undefined)
|
|
55
|
+
return undefined;
|
|
56
|
+
let encoded;
|
|
57
|
+
try {
|
|
58
|
+
encoded = JSON.stringify(input);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
throw correlationRefusal(`must be JSON serializable (${String(error)})`);
|
|
62
|
+
}
|
|
63
|
+
if (encoded === undefined)
|
|
64
|
+
throw correlationRefusal("must be a JSON object");
|
|
65
|
+
if (Buffer.byteLength(encoded) > MAX_CORRELATION_BYTES) {
|
|
66
|
+
throw correlationTooLarge(`exceeds ${MAX_CORRELATION_BYTES} bytes`, { limit: MAX_CORRELATION_BYTES });
|
|
67
|
+
}
|
|
68
|
+
const parsed = JSON.parse(encoded);
|
|
69
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
70
|
+
throw correlationRefusal("must be a JSON object");
|
|
71
|
+
}
|
|
72
|
+
const source = parsed;
|
|
73
|
+
const undeclared = Object.keys(source).filter((key) => !CORRELATION_FIELDS.has(key));
|
|
74
|
+
if (undeclared.length > 0) {
|
|
75
|
+
throw correlationRefusal(`carries fields outside the pinned schema 1.0 contract: ${undeclared.sort().join(", ")}`, { undeclared: undeclared.sort().join(",") });
|
|
76
|
+
}
|
|
77
|
+
const output = {};
|
|
78
|
+
for (const [key, value] of Object.entries(source)) {
|
|
79
|
+
if (value === undefined || value === null)
|
|
80
|
+
continue;
|
|
81
|
+
if (key === "assurance_scope") {
|
|
82
|
+
const size = Buffer.byteLength(JSON.stringify(value) ?? "");
|
|
83
|
+
if (size > MAX_CORRELATION_SCOPE_BYTES) {
|
|
84
|
+
throw correlationTooLarge(`assurance_scope exceeds ${MAX_CORRELATION_SCOPE_BYTES} bytes`, { limit: MAX_CORRELATION_SCOPE_BYTES, actual: size });
|
|
85
|
+
}
|
|
86
|
+
output[key] = value;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (CORRELATION_NUMERIC.has(key)) {
|
|
90
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
91
|
+
throw correlationRefusal(`${key} must be a finite number`, { field: key });
|
|
92
|
+
}
|
|
93
|
+
output[key] = value;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (typeof value !== "string")
|
|
97
|
+
throw correlationRefusal(`${key} must be a string`, { field: key });
|
|
98
|
+
if (value.length > MAX_CORRELATION_FIELD_CHARS) {
|
|
99
|
+
throw correlationTooLarge(`${key} exceeds ${MAX_CORRELATION_FIELD_CHARS} characters`, { field: key, limit: MAX_CORRELATION_FIELD_CHARS, actual: value.length });
|
|
100
|
+
}
|
|
101
|
+
output[key] = value;
|
|
102
|
+
}
|
|
103
|
+
return output;
|
|
104
|
+
}
|
|
105
|
+
export function digestTask(task) {
|
|
106
|
+
return createHash("sha256").update(task, "utf8").digest("hex");
|
|
107
|
+
}
|
|
108
|
+
export function digestCapabilities(capabilities) {
|
|
109
|
+
const canonical = [...new Set(capabilities)].sort();
|
|
110
|
+
return createHash("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Builds the binding from TRUSTED values only. It deliberately does not take a `CorrelationMetadata`.
|
|
114
|
+
*
|
|
115
|
+
* It used to read `workspace_id` and `context_id` straight out of caller-supplied correlation into what
|
|
116
|
+
* the comment above calls the trusted scope, and a bound approval could then be spent outside the
|
|
117
|
+
* workspace it named (R-110): approve a delegation carrying a real, registry-validated `workspace` spec,
|
|
118
|
+
* then re-issue the identical task with `correlation.workspace_id` set and NO `workspace` field — no
|
|
119
|
+
* registry lookup, no lease, the parent's own cwd, and every digest still matching. The guard that would
|
|
120
|
+
* have caught it can only fire when there is a spec to disagree with.
|
|
121
|
+
*
|
|
122
|
+
* `workspaceId` must therefore be the id of a workspace that was actually resolved and leased.
|
|
123
|
+
* `contextId` is a caller-declared label that nothing validates; it is included because it can only
|
|
124
|
+
* ever NARROW a binding, and a mismatch fails closed.
|
|
125
|
+
*/
|
|
126
|
+
export function buildApprovalBinding(input) {
|
|
127
|
+
const requested = [...new Set(input.requested)].sort();
|
|
128
|
+
const effective = [...new Set(input.effective)].sort();
|
|
129
|
+
return {
|
|
130
|
+
version: "1",
|
|
131
|
+
task_sha256: digestTask(input.task),
|
|
132
|
+
requested_sha256: digestCapabilities(requested),
|
|
133
|
+
effective_sha256: digestCapabilities(effective),
|
|
134
|
+
requested,
|
|
135
|
+
effective,
|
|
136
|
+
...(input.definitionSha256 ? { definition_sha256: input.definitionSha256 } : {}),
|
|
137
|
+
...(input.workspaceId ? { workspace_id: input.workspaceId } : {}),
|
|
138
|
+
...(input.contextId ? { context_id: input.contextId } : {}),
|
|
139
|
+
parent_id: input.parentId,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function approvalBindingDigest(binding) {
|
|
143
|
+
const ordered = {
|
|
144
|
+
version: binding.version,
|
|
145
|
+
task_sha256: binding.task_sha256,
|
|
146
|
+
requested_sha256: binding.requested_sha256,
|
|
147
|
+
effective_sha256: binding.effective_sha256,
|
|
148
|
+
requested: binding.requested,
|
|
149
|
+
effective: binding.effective,
|
|
150
|
+
definition_sha256: binding.definition_sha256 ?? null,
|
|
151
|
+
workspace_id: binding.workspace_id ?? null,
|
|
152
|
+
context_id: binding.context_id ?? null,
|
|
153
|
+
parent_id: binding.parent_id,
|
|
154
|
+
};
|
|
155
|
+
return createHash("sha256").update(JSON.stringify(ordered), "utf8").digest("hex");
|
|
156
|
+
}
|
|
157
|
+
export function isApprovalBinding(value) {
|
|
158
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
159
|
+
return false;
|
|
160
|
+
const binding = value;
|
|
161
|
+
return binding.version === "1" &&
|
|
162
|
+
[binding.task_sha256, binding.requested_sha256, binding.effective_sha256].every((digest) => typeof digest === "string" && /^[a-f0-9]{64}$/i.test(digest)) &&
|
|
163
|
+
Array.isArray(binding.requested) && binding.requested.every((item) => typeof item === "string") &&
|
|
164
|
+
Array.isArray(binding.effective) && binding.effective.every((item) => typeof item === "string") &&
|
|
165
|
+
typeof binding.parent_id === "string" &&
|
|
166
|
+
["definition_sha256", "workspace_id", "context_id"].every((key) => {
|
|
167
|
+
const value = binding[key];
|
|
168
|
+
return value === undefined || typeof value === "string";
|
|
169
|
+
}) &&
|
|
170
|
+
// Self-consistency. This guard's only trust boundary is a binding parsed off DISK
|
|
171
|
+
// (`approval-store.ts`), so an internally contradictory record — digests that do not match the
|
|
172
|
+
// capability arrays sitting beside them — must be unrepresentable rather than merely unlikely.
|
|
173
|
+
binding.requested_sha256 === digestCapabilities(binding.requested) &&
|
|
174
|
+
binding.effective_sha256 === digestCapabilities(binding.effective);
|
|
175
|
+
}
|
|
176
|
+
export function approvalBindingsEqual(a, b) {
|
|
177
|
+
if (a === undefined || b === undefined)
|
|
178
|
+
return a === b;
|
|
179
|
+
if (!isApprovalBinding(a) || !isApprovalBinding(b))
|
|
180
|
+
return false;
|
|
181
|
+
return approvalBindingDigest(a) === approvalBindingDigest(b);
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=correlation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correlation.js","sourceRoot":"","sources":["../src/correlation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shapes `planDelegation` consumes and produces. Split out of `./delegate.ts` only to stay under the
|
|
3
|
+
* 400-line module ceiling this project enforces mechanically; `./delegate.ts` re-exports all three, so
|
|
4
|
+
* "the delegate module" remains one import for every caller.
|
|
5
|
+
*/
|
|
6
|
+
import type { Capability, ResolveResult } from "./resolve.ts";
|
|
7
|
+
import type { DefinitionDigest, SkillDefinition } from "./definitions.ts";
|
|
8
|
+
import type { InheritableApproval } from "./approval.ts";
|
|
9
|
+
import type { Catalog } from "./catalog.ts";
|
|
10
|
+
import type { ApprovalBinding, CorrelationMetadata } from "./correlation.ts";
|
|
11
|
+
import type { StructuredRefusal } from "./refusals.ts";
|
|
12
|
+
export interface DelegationRequest {
|
|
13
|
+
task: string;
|
|
14
|
+
/**
|
|
15
|
+
* Capabilities the delegator wants the child to hold.
|
|
16
|
+
*
|
|
17
|
+
* Optional since ADR-0016: prefer `agent`, which names an operator-authored definition. This form
|
|
18
|
+
* lets the MODEL choose the capability set, which is the weaker arrangement — it is still bounded by
|
|
19
|
+
* the session grant (ADR-0008), so it cannot escalate, but nothing about it was reviewed by a human.
|
|
20
|
+
*/
|
|
21
|
+
tools?: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Name of a `SKILL.md` definition to spawn (ADR-0016).
|
|
24
|
+
*
|
|
25
|
+
* When given, the definition's `allowed-tools` is the ceiling and its body is the child's system
|
|
26
|
+
* prompt. The model chooses only *which* definition and *what* task; the capability set is the
|
|
27
|
+
* operator's, written down in a file.
|
|
28
|
+
*/
|
|
29
|
+
agent?: string;
|
|
30
|
+
model?: string;
|
|
31
|
+
provider?: string;
|
|
32
|
+
thinking?: string;
|
|
33
|
+
/** Optional external join metadata. It never participates in capability authority. */
|
|
34
|
+
correlation?: CorrelationMetadata;
|
|
35
|
+
/**
|
|
36
|
+
* Trusted binding scope, supplied by the caller that actually resolved and leased the
|
|
37
|
+
* workspace. Deliberately separate from `correlation`, whose values are a model-supplied
|
|
38
|
+
* claim: reading the workspace id out of correlation let a bound approval be spent outside
|
|
39
|
+
* the workspace it named (R-110).
|
|
40
|
+
*/
|
|
41
|
+
boundWorkspaceId?: string;
|
|
42
|
+
boundContextId?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface DelegationContext {
|
|
45
|
+
ownGrant: Capability[];
|
|
46
|
+
depth: number;
|
|
47
|
+
maxDepth: number;
|
|
48
|
+
gated: Capability[];
|
|
49
|
+
/**
|
|
50
|
+
* Approvals in force for this delegation, with subject and scope (ADR-0014).
|
|
51
|
+
*
|
|
52
|
+
* One source of truth for two different questions. The **gate check here** honours every entry,
|
|
53
|
+
* including `once` — that approval applies to *this* spawn, which is exactly what the human said yes
|
|
54
|
+
* to. What crosses to the CHILD is `inheritApprovals`, which drops `once` and keeps the subject, so
|
|
55
|
+
* the same list cannot silently authorise a subtree.
|
|
56
|
+
*/
|
|
57
|
+
approved?: InheritableApproval[];
|
|
58
|
+
ledgerPath?: string;
|
|
59
|
+
/** Path to this extension, so a child granted `tool:delegate` can delegate in turn. */
|
|
60
|
+
extensionPath?: string;
|
|
61
|
+
/** Live capability catalog. When supplied, capabilities absent from it are refused as unknown. */
|
|
62
|
+
catalog?: Catalog;
|
|
63
|
+
/**
|
|
64
|
+
* Absolute path per skill NAME, from the catalog's `source` field (R-32).
|
|
65
|
+
*
|
|
66
|
+
* Without it every granted `skill:` capability is unresolvable and the delegation is refused, which
|
|
67
|
+
* is the correct direction: a caller that cannot say where a skill lives cannot honestly grant it.
|
|
68
|
+
*/
|
|
69
|
+
skillPaths?: Record<string, string>;
|
|
70
|
+
/** Let the child load `AGENTS.md` / `CLAUDE.md`. Default false — see `planSpawn`. */
|
|
71
|
+
contextFiles?: boolean;
|
|
72
|
+
/** Known `SKILL.md` definitions by name, for `DelegationRequest.agent` (ADR-0016). */
|
|
73
|
+
definitions?: Map<string, SkillDefinition>;
|
|
74
|
+
/**
|
|
75
|
+
* Build an INTERACTIVE plan — no `--print` — for an executor that drives the child after starting it.
|
|
76
|
+
*
|
|
77
|
+
* `runHerdrPane` requires this: `--print` makes pi process the prompt and exit, so it never reaches the
|
|
78
|
+
* interactive readiness `herdr agent start` waits for and the agent is never detected. Default is the
|
|
79
|
+
* non-interactive plan, because a governed child should not sit waiting for a human by accident.
|
|
80
|
+
*/
|
|
81
|
+
interactive?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Total descendants this session may still create (`src/fanout.ts`). Split among children by the caller.
|
|
84
|
+
*
|
|
85
|
+
* Omitted means unbounded, which is the pre-fan-out behaviour and correct for a single blocking
|
|
86
|
+
* delegation — the accident that used to bound cardinality to one.
|
|
87
|
+
*/
|
|
88
|
+
fanoutBudget?: number;
|
|
89
|
+
/** This session's ledger id, so a child's `parentId` names its real parent (F8). */
|
|
90
|
+
spawnId?: string;
|
|
91
|
+
/** Ledger id assigned to THIS child, distinguishing it from its siblings (F8). */
|
|
92
|
+
childSpawnId?: string;
|
|
93
|
+
}
|
|
94
|
+
export interface Delegation {
|
|
95
|
+
ok: boolean;
|
|
96
|
+
reason?: string;
|
|
97
|
+
/** Stable machine-readable refusal accompanying the unchanged human diagnostic. */
|
|
98
|
+
refusal?: StructuredRefusal;
|
|
99
|
+
args: string[];
|
|
100
|
+
/** Per-child environment — never merged into the parent's process.env. */
|
|
101
|
+
env: Record<string, string>;
|
|
102
|
+
effective: Capability[];
|
|
103
|
+
/**
|
|
104
|
+
* The result this plan was made from. **Required** (B-I3): while it was optional the extension
|
|
105
|
+
* guarded its ledger write with `if (ledgerPath && plan.result)`, silently dropping every refusal
|
|
106
|
+
* that returned before `resolve()` ran. The type is what keeps a new early exit auditable.
|
|
107
|
+
*/
|
|
108
|
+
result: ResolveResult;
|
|
109
|
+
childDepth: number;
|
|
110
|
+
/**
|
|
111
|
+
* The capabilities this delegation asked for, whatever route named them.
|
|
112
|
+
*
|
|
113
|
+
* Carried on the plan rather than re-derived by the caller (the B-I3 lesson): with `agent`, the
|
|
114
|
+
* request names a DEFINITION and the capabilities come from its `allowed-tools`, so a ledger that
|
|
115
|
+
* read the tool parameters would record an empty request for every definition spawn.
|
|
116
|
+
*/
|
|
117
|
+
requested: Capability[];
|
|
118
|
+
/** Ledger id for this child, if the caller assigned one (F8). */
|
|
119
|
+
childId?: string;
|
|
120
|
+
/**
|
|
121
|
+
* Which operator-authored instructions this spawn used (ADR-0018).
|
|
122
|
+
*
|
|
123
|
+
* Absent for a `tools:`-style delegation, which has no definition and therefore no instructions to
|
|
124
|
+
* identify — and absent on an ADR-0017 authorisation refusal, which is decided before the file is read.
|
|
125
|
+
*/
|
|
126
|
+
definitionDigest?: DefinitionDigest;
|
|
127
|
+
/** Trusted SHA-256 of the exact model-authored task. The task text itself is never stored. */
|
|
128
|
+
taskDigest: string;
|
|
129
|
+
/** Non-authoritative external join metadata, snapshotted at planning time. */
|
|
130
|
+
correlation?: CorrelationMetadata;
|
|
131
|
+
/** Exact approval scope, present when correlation/workspace context requested task-bound approval. */
|
|
132
|
+
approvalBinding?: ApprovalBinding;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Plan a governed delegation. Pure: returns argv and env, spawns nothing.
|
|
136
|
+
*
|
|
137
|
+
* Fails closed on depth, on any requested capability the delegator does not hold, on gated capabilities
|
|
138
|
+
* without approval, and on a grant that cannot narrow (a universal capability slipping through).
|
|
139
|
+
*/
|
|
140
|
+
//# sourceMappingURL=delegate-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegate-types.d.ts","sourceRoot":"","sources":["../src/delegate-types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGvD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uFAAuF;IACvF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kGAAkG;IAClG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,qFAAqF;IACrF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,sFAAsF;IACtF,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC3C;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB;;;;OAIG;IACH,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,8FAA8F;IAC9F,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,sGAAsG;IACtG,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED;;;;;GAKG"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export {};
|
|
2
|
+
/**
|
|
3
|
+
* Plan a governed delegation. Pure: returns argv and env, spawns nothing.
|
|
4
|
+
*
|
|
5
|
+
* Fails closed on depth, on any requested capability the delegator does not hold, on gated capabilities
|
|
6
|
+
* without approval, and on a grant that cannot narrow (a universal capability slipping through).
|
|
7
|
+
*/
|
|
8
|
+
//# sourceMappingURL=delegate-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegate-types.js","sourceRoot":"","sources":["../src/delegate-types.ts"],"names":[],"mappings":";AA0IA;;;;;GAKG"}
|
package/dist/delegate.d.ts
CHANGED
|
@@ -1,133 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Governed delegation
|
|
3
|
-
*
|
|
4
|
-
* The `tool_call` interceptor can only *permit or refuse* a `pi-subagents` spawn, because that package's
|
|
5
|
-
* `Agent` tool has no `tools` parameter. When we do the spawning ourselves the grant becomes an argument,
|
|
6
|
-
* which is what "give them some tools but not others" actually requires.
|
|
7
|
-
*
|
|
8
|
-
* Two properties fall out of owning the spawn:
|
|
9
|
-
*
|
|
10
|
-
* 1. **No propagation race at all.** Each child receives its own explicit `env` object, so nothing is
|
|
11
|
-
* written to the shared `process.env`. The interceptor's constraint (only parent-level facts may be
|
|
12
|
-
* pushed, because the channel is global) does not apply here.
|
|
13
|
-
* 2. **Depth control by capability.** `tool:delegate` is itself a capability. Grant it and the child can
|
|
14
|
-
* sub-delegate; withhold it and the child is a leaf. No separate depth mechanism is required, though
|
|
15
|
-
* `maxDepth` remains as a cheap backstop.
|
|
2
|
+
* Governed delegation planning. Owning the spawn makes the grant an argument; each child receives its own
|
|
3
|
+
* environment, and `tool:delegate` determines whether it is a delegator or a leaf.
|
|
16
4
|
*/
|
|
17
|
-
import { type DefinitionDigest, type SkillDefinition } from "./definitions.ts";
|
|
18
|
-
import { type Capability, type ResolveResult } from "./resolve.ts";
|
|
19
5
|
export { DELEGATE_CAPABILITY, agentCapability, maySpawnDefinition, normaliseCapability } from "./capabilities.ts";
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
export interface DelegationRequest {
|
|
23
|
-
task: string;
|
|
24
|
-
/**
|
|
25
|
-
* Capabilities the delegator wants the child to hold.
|
|
26
|
-
*
|
|
27
|
-
* Optional since ADR-0016: prefer `agent`, which names an operator-authored definition. This form
|
|
28
|
-
* lets the MODEL choose the capability set, which is the weaker arrangement — it is still bounded by
|
|
29
|
-
* the session grant (ADR-0008), so it cannot escalate, but nothing about it was reviewed by a human.
|
|
30
|
-
*/
|
|
31
|
-
tools?: string[];
|
|
32
|
-
/**
|
|
33
|
-
* Name of a `SKILL.md` definition to spawn (ADR-0016).
|
|
34
|
-
*
|
|
35
|
-
* When given, the definition's `allowed-tools` is the ceiling and its body is the child's system
|
|
36
|
-
* prompt. The model chooses only *which* definition and *what* task; the capability set is the
|
|
37
|
-
* operator's, written down in a file.
|
|
38
|
-
*/
|
|
39
|
-
agent?: string;
|
|
40
|
-
model?: string;
|
|
41
|
-
provider?: string;
|
|
42
|
-
thinking?: string;
|
|
43
|
-
}
|
|
44
|
-
export interface DelegationContext {
|
|
45
|
-
ownGrant: Capability[];
|
|
46
|
-
depth: number;
|
|
47
|
-
maxDepth: number;
|
|
48
|
-
gated: Capability[];
|
|
49
|
-
/**
|
|
50
|
-
* Approvals in force for this delegation, with subject and scope (ADR-0014).
|
|
51
|
-
*
|
|
52
|
-
* One source of truth for two different questions. The **gate check here** honours every entry,
|
|
53
|
-
* including `once` — that approval applies to *this* spawn, which is exactly what the human said yes
|
|
54
|
-
* to. What crosses to the CHILD is `inheritApprovals`, which drops `once` and keeps the subject, so
|
|
55
|
-
* the same list cannot silently authorise a subtree.
|
|
56
|
-
*/
|
|
57
|
-
approved?: InheritableApproval[];
|
|
58
|
-
ledgerPath?: string;
|
|
59
|
-
/** Path to this extension, so a child granted `tool:delegate` can delegate in turn. */
|
|
60
|
-
extensionPath?: string;
|
|
61
|
-
/** Live capability catalog. When supplied, capabilities absent from it are refused as unknown. */
|
|
62
|
-
catalog?: Catalog;
|
|
63
|
-
/**
|
|
64
|
-
* Absolute path per skill NAME, from the catalog's `source` field (R-32).
|
|
65
|
-
*
|
|
66
|
-
* Without it every granted `skill:` capability is unresolvable and the delegation is refused, which
|
|
67
|
-
* is the correct direction: a caller that cannot say where a skill lives cannot honestly grant it.
|
|
68
|
-
*/
|
|
69
|
-
skillPaths?: Record<string, string>;
|
|
70
|
-
/** Let the child load `AGENTS.md` / `CLAUDE.md`. Default false — see `planSpawn`. */
|
|
71
|
-
contextFiles?: boolean;
|
|
72
|
-
/** Known `SKILL.md` definitions by name, for `DelegationRequest.agent` (ADR-0016). */
|
|
73
|
-
definitions?: Map<string, SkillDefinition>;
|
|
74
|
-
/**
|
|
75
|
-
* Build an INTERACTIVE plan — no `--print` — for an executor that drives the child after starting it.
|
|
76
|
-
*
|
|
77
|
-
* `runHerdrPane` requires this: `--print` makes pi process the prompt and exit, so it never reaches the
|
|
78
|
-
* interactive readiness `herdr agent start` waits for and the agent is never detected. Default is the
|
|
79
|
-
* non-interactive plan, because a governed child should not sit waiting for a human by accident.
|
|
80
|
-
*/
|
|
81
|
-
interactive?: boolean;
|
|
82
|
-
/**
|
|
83
|
-
* Total descendants this session may still create (`src/fanout.ts`). Split among children by the caller.
|
|
84
|
-
*
|
|
85
|
-
* Omitted means unbounded, which is the pre-fan-out behaviour and correct for a single blocking
|
|
86
|
-
* delegation — the accident that used to bound cardinality to one.
|
|
87
|
-
*/
|
|
88
|
-
fanoutBudget?: number;
|
|
89
|
-
/** This session's ledger id, so a child's `parentId` names its real parent (F8). */
|
|
90
|
-
spawnId?: string;
|
|
91
|
-
/** Ledger id assigned to THIS child, distinguishing it from its siblings (F8). */
|
|
92
|
-
childSpawnId?: string;
|
|
93
|
-
}
|
|
94
|
-
export interface Delegation {
|
|
95
|
-
ok: boolean;
|
|
96
|
-
reason?: string;
|
|
97
|
-
args: string[];
|
|
98
|
-
/** Per-child environment — never merged into the parent's process.env. */
|
|
99
|
-
env: Record<string, string>;
|
|
100
|
-
effective: Capability[];
|
|
101
|
-
/**
|
|
102
|
-
* The result this plan was made from. **Required** (B-I3): while it was optional the extension
|
|
103
|
-
* guarded its ledger write with `if (ledgerPath && plan.result)`, silently dropping every refusal
|
|
104
|
-
* that returned before `resolve()` ran. The type is what keeps a new early exit auditable.
|
|
105
|
-
*/
|
|
106
|
-
result: ResolveResult;
|
|
107
|
-
childDepth: number;
|
|
108
|
-
/**
|
|
109
|
-
* The capabilities this delegation asked for, whatever route named them.
|
|
110
|
-
*
|
|
111
|
-
* Carried on the plan rather than re-derived by the caller (the B-I3 lesson): with `agent`, the
|
|
112
|
-
* request names a DEFINITION and the capabilities come from its `allowed-tools`, so a ledger that
|
|
113
|
-
* read the tool parameters would record an empty request for every definition spawn.
|
|
114
|
-
*/
|
|
115
|
-
requested: Capability[];
|
|
116
|
-
/** Ledger id for this child, if the caller assigned one (F8). */
|
|
117
|
-
childId?: string;
|
|
118
|
-
/**
|
|
119
|
-
* Which operator-authored instructions this spawn used (ADR-0018).
|
|
120
|
-
*
|
|
121
|
-
* Absent for a `tools:`-style delegation, which has no definition and therefore no instructions to
|
|
122
|
-
* identify — and absent on an ADR-0017 authorisation refusal, which is decided before the file is read.
|
|
123
|
-
*/
|
|
124
|
-
definitionDigest?: DefinitionDigest;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Plan a governed delegation. Pure: returns argv and env, spawns nothing.
|
|
128
|
-
*
|
|
129
|
-
* Fails closed on depth, on any requested capability the delegator does not hold, on gated capabilities
|
|
130
|
-
* without approval, and on a grant that cannot narrow (a universal capability slipping through).
|
|
131
|
-
*/
|
|
6
|
+
import type { Delegation, DelegationContext, DelegationRequest } from "./delegate-types.ts";
|
|
7
|
+
export type { Delegation, DelegationContext, DelegationRequest } from "./delegate-types.ts";
|
|
132
8
|
export declare function planDelegation(request: DelegationRequest, ctx: DelegationContext): Delegation;
|
|
133
9
|
//# sourceMappingURL=delegate.d.ts.map
|
package/dist/delegate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAYlH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC5F,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE5F,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,EAAE,iBAAiB,GAAG,UAAU,CA+Q7F"}
|