pi-daddy 0.43.1 → 0.44.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 +93 -31
- package/README.md +15 -0
- package/THIRD_PARTY_LICENSES/musl-COPYRIGHT.txt +193 -0
- package/THIRD_PARTY_LICENSES/zig-MIT.txt +21 -0
- package/THIRD_PARTY_NOTICES.md +9 -0
- package/dist/cli.d.ts +2 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +10 -5
- package/dist/cli.js.map +1 -1
- package/dist/executors/activity-session.js +116 -65
- package/dist/executors/activity-session.js.map +1 -1
- package/dist/executors/captured-execution.d.ts +13 -0
- package/dist/executors/captured-execution.d.ts.map +1 -0
- package/dist/executors/captured-execution.js +53 -0
- package/dist/executors/captured-execution.js.map +1 -0
- package/dist/executors/child-final.d.ts +31 -0
- package/dist/executors/child-final.d.ts.map +1 -0
- package/dist/executors/child-final.js +197 -0
- package/dist/executors/child-final.js.map +1 -0
- package/dist/executors/executor.d.ts +2 -1
- package/dist/executors/executor.d.ts.map +1 -1
- package/dist/executors/executor.js +12 -0
- package/dist/executors/executor.js.map +1 -1
- package/dist/executors/owned-worker.d.ts +18 -0
- package/dist/executors/owned-worker.d.ts.map +1 -0
- package/dist/executors/owned-worker.js +295 -0
- package/dist/executors/owned-worker.js.map +1 -0
- package/dist/executors/worker-artifact.d.ts +3 -0
- package/dist/executors/worker-artifact.d.ts.map +1 -0
- package/dist/executors/worker-artifact.js +3 -0
- package/dist/executors/worker-artifact.js.map +1 -0
- package/dist/governance/approval-prompt.d.ts +4 -3
- package/dist/governance/approval-prompt.d.ts.map +1 -1
- package/dist/governance/approval-prompt.js +13 -6
- package/dist/governance/approval-prompt.js.map +1 -1
- package/dist/governance/captured-worker-record.d.ts +9 -0
- package/dist/governance/captured-worker-record.d.ts.map +1 -0
- package/dist/governance/captured-worker-record.js +123 -0
- package/dist/governance/captured-worker-record.js.map +1 -0
- package/dist/governance/lease-record.d.ts +4 -0
- package/dist/governance/lease-record.d.ts.map +1 -1
- package/dist/governance/lease-record.js +19 -2
- package/dist/governance/lease-record.js.map +1 -1
- package/dist/governance/ledger-events.d.ts +5 -5
- package/dist/governance/ledger-events.d.ts.map +1 -1
- package/dist/governance/ledger-v3-validation.js +1 -1
- package/dist/governance/ledger-v3-validation.js.map +1 -1
- package/dist/governance/workspace-acceptance.d.ts +3 -1
- package/dist/governance/workspace-acceptance.d.ts.map +1 -1
- package/dist/governance/workspace-acceptance.js +21 -5
- package/dist/governance/workspace-acceptance.js.map +1 -1
- package/dist/governance/workspace-lease.d.ts.map +1 -1
- package/dist/governance/workspace-lease.js +44 -1
- package/dist/governance/workspace-lease.js.map +1 -1
- package/dist/kernel/authority-text.d.ts +3 -0
- package/dist/kernel/authority-text.d.ts.map +1 -0
- package/dist/kernel/authority-text.js +22 -0
- package/dist/kernel/authority-text.js.map +1 -0
- package/dist/kernel/bounded-read-failures.d.ts +9 -0
- package/dist/kernel/bounded-read-failures.d.ts.map +1 -0
- package/dist/kernel/bounded-read-failures.js +33 -0
- package/dist/kernel/bounded-read-failures.js.map +1 -0
- package/dist/kernel/bounded-read-sync.d.ts +24 -0
- package/dist/kernel/bounded-read-sync.d.ts.map +1 -0
- package/dist/kernel/bounded-read-sync.js +75 -0
- package/dist/kernel/bounded-read-sync.js.map +1 -0
- package/dist/kernel/bounded-read.d.ts +29 -2
- package/dist/kernel/bounded-read.d.ts.map +1 -1
- package/dist/kernel/bounded-read.js +98 -9
- package/dist/kernel/bounded-read.js.map +1 -1
- package/dist/kernel/capacity.d.ts +36 -0
- package/dist/kernel/capacity.d.ts.map +1 -0
- package/dist/kernel/capacity.js +94 -0
- package/dist/kernel/capacity.js.map +1 -0
- package/dist/kernel/captured-worker-contract.d.ts +38 -0
- package/dist/kernel/captured-worker-contract.d.ts.map +1 -0
- package/dist/kernel/captured-worker-contract.js +2 -0
- package/dist/kernel/captured-worker-contract.js.map +1 -0
- package/dist/kernel/catalog.d.ts +12 -3
- package/dist/kernel/catalog.d.ts.map +1 -1
- package/dist/kernel/catalog.js +28 -8
- package/dist/kernel/catalog.js.map +1 -1
- package/dist/kernel/chain.d.ts +1 -0
- package/dist/kernel/chain.d.ts.map +1 -1
- package/dist/kernel/chain.js.map +1 -1
- package/dist/kernel/definitions.d.ts +11 -1
- package/dist/kernel/definitions.d.ts.map +1 -1
- package/dist/kernel/definitions.js +6 -3
- package/dist/kernel/definitions.js.map +1 -1
- package/dist/kernel/fanout.d.ts +5 -25
- package/dist/kernel/fanout.d.ts.map +1 -1
- package/dist/kernel/fanout.js +15 -30
- package/dist/kernel/fanout.js.map +1 -1
- package/dist/kernel/grant-env.d.ts +0 -2
- package/dist/kernel/grant-env.d.ts.map +1 -1
- package/dist/kernel/grant-env.js +0 -1
- package/dist/kernel/grant-env.js.map +1 -1
- package/dist/kernel/model-preflight.d.ts +8 -0
- package/dist/kernel/model-preflight.d.ts.map +1 -1
- package/dist/kernel/model-preflight.js +23 -0
- package/dist/kernel/model-preflight.js.map +1 -1
- package/dist/kernel/selected-definitions.d.ts +15 -0
- package/dist/kernel/selected-definitions.d.ts.map +1 -0
- package/dist/kernel/selected-definitions.js +146 -0
- package/dist/kernel/selected-definitions.js.map +1 -0
- package/dist/kernel/skill-packages.d.ts +4 -2
- package/dist/kernel/skill-packages.d.ts.map +1 -1
- package/dist/kernel/skill-packages.js +34 -41
- package/dist/kernel/skill-packages.js.map +1 -1
- package/dist/kernel/skill-resources.d.ts.map +1 -1
- package/dist/kernel/skill-resources.js +8 -11
- package/dist/kernel/skill-resources.js.map +1 -1
- package/dist/kernel/workspace.d.ts +6 -3
- package/dist/kernel/workspace.d.ts.map +1 -1
- package/dist/kernel/workspace.js +10 -6
- package/dist/kernel/workspace.js.map +1 -1
- package/dist/products/dashboard-cli.js +1 -1
- package/dist/products/dashboard-cli.js.map +1 -1
- package/dist/products/dashboard-render.d.ts +1 -2
- package/dist/products/dashboard-render.d.ts.map +1 -1
- package/dist/products/dashboard-render.js +3 -1
- package/dist/products/dashboard-render.js.map +1 -1
- package/dist/products/dashboard-session-client.d.ts +1 -2
- package/dist/products/dashboard-session-client.d.ts.map +1 -1
- package/dist/products/dashboard-session-client.js.map +1 -1
- package/dist/products/episode-report.d.ts +12 -8
- package/dist/products/episode-report.d.ts.map +1 -1
- package/dist/products/episode-report.js +61 -12
- package/dist/products/episode-report.js.map +1 -1
- package/extensions/approvals.ts +1 -1
- package/extensions/chain-plan.ts +9 -3
- package/extensions/context-staging.ts +30 -35
- package/extensions/dashboard-session-server.ts +1 -2
- package/extensions/definition-describe.ts +44 -0
- package/extensions/definition-runtime.ts +89 -26
- package/extensions/delegate-chain.ts +273 -256
- package/extensions/delegation-activation.ts +1 -1
- package/extensions/delegation.ts +41 -53
- package/extensions/execute-child.ts +167 -134
- package/extensions/fanout-outcome.ts +1 -9
- package/extensions/grants-command.ts +2 -9
- package/extensions/grants.ts +106 -38
- package/extensions/init-command.ts +52 -12
- package/extensions/reload-environment.ts +21 -2
- package/extensions/retired-inputs.ts +7 -0
- package/extensions/run-delegation.ts +212 -265
- package/extensions/session-capacity.ts +53 -0
- package/extensions/session-environment.ts +5 -2
- package/extensions/session-model-prompt.ts +33 -32
- package/extensions/session-report.ts +28 -11
- package/extensions/session.ts +200 -157
- package/extensions/workspace-runtime.ts +2 -1
- package/native/README.md +25 -0
- package/native/linux-x64/worker +0 -0
- package/native/linux-x64/worker.sha256 +1 -0
- package/native/provenance.json +42 -0
- package/native/toolchain.json +18 -0
- package/native/worker.c +209 -0
- package/package.json +15 -7
- package/src/cli.ts +16 -6
- package/src/executors/activity-session.ts +120 -66
- package/src/executors/captured-execution.ts +61 -0
- package/src/executors/child-final.ts +190 -0
- package/src/executors/executor.ts +14 -1
- package/src/executors/owned-worker.ts +322 -0
- package/src/executors/worker-artifact.ts +2 -0
- package/src/governance/approval-prompt.ts +18 -8
- package/src/governance/captured-worker-record.ts +138 -0
- package/src/governance/lease-record.ts +21 -2
- package/src/governance/ledger-events.ts +5 -5
- package/src/governance/ledger-v3-validation.ts +1 -1
- package/src/governance/workspace-acceptance.ts +20 -4
- package/src/governance/workspace-lease.ts +59 -1
- package/src/kernel/authority-text.ts +17 -0
- package/src/kernel/bounded-read-failures.ts +42 -0
- package/src/kernel/bounded-read-sync.ts +87 -0
- package/src/kernel/bounded-read.ts +128 -11
- package/src/kernel/capacity.ts +123 -0
- package/src/kernel/captured-worker-contract.ts +29 -0
- package/src/kernel/catalog.ts +45 -16
- package/src/kernel/chain.ts +1 -0
- package/src/kernel/definitions.ts +11 -2
- package/src/kernel/fanout.ts +17 -32
- package/src/kernel/grant-env.ts +0 -3
- package/src/kernel/model-preflight.ts +29 -0
- package/src/kernel/selected-definitions.ts +156 -0
- package/src/kernel/skill-packages.ts +40 -38
- package/src/kernel/skill-resources.ts +8 -9
- package/src/kernel/workspace.ts +12 -5
- package/src/products/dashboard-cli.ts +1 -1
- package/src/products/dashboard-render.ts +6 -2
- package/src/products/dashboard-session-client.ts +1 -2
- package/src/products/episode-report.ts +80 -17
- package/dist/advisors/advisor.d.ts +0 -90
- package/dist/advisors/advisor.d.ts.map +0 -1
- package/dist/advisors/advisor.js +0 -154
- package/dist/advisors/advisor.js.map +0 -1
- package/dist/advisors/decider.d.ts +0 -88
- package/dist/advisors/decider.d.ts.map +0 -1
- package/dist/advisors/decider.js +0 -29
- package/dist/advisors/decider.js.map +0 -1
- package/dist/advisors/jev.d.ts +0 -41
- package/dist/advisors/jev.d.ts.map +0 -1
- package/dist/advisors/jev.js +0 -139
- package/dist/advisors/jev.js.map +0 -1
- package/dist/advisors/settings.d.ts +0 -49
- package/dist/advisors/settings.d.ts.map +0 -1
- package/dist/advisors/settings.js +0 -99
- package/dist/advisors/settings.js.map +0 -1
- package/dist/governance/episode-cost-gate.d.ts +0 -31
- package/dist/governance/episode-cost-gate.d.ts.map +0 -1
- package/dist/governance/episode-cost-gate.js +0 -112
- package/dist/governance/episode-cost-gate.js.map +0 -1
- package/extensions/advisor-session.ts +0 -67
- package/extensions/effort-advice.ts +0 -112
- package/extensions/pruning-advice.ts +0 -133
- package/src/advisors/advisor.ts +0 -246
- package/src/advisors/decider.ts +0 -74
- package/src/advisors/jev.ts +0 -163
- package/src/advisors/settings.ts +0 -115
- package/src/governance/episode-cost-gate.ts +0 -128
package/extensions/session.ts
CHANGED
|
@@ -14,14 +14,19 @@
|
|
|
14
14
|
* copy of `ownGrant` before observation is exactly how a stale upper bound would become an enforced one.
|
|
15
15
|
*/
|
|
16
16
|
import { randomUUID } from "node:crypto";
|
|
17
|
+
import { BoundedReadCleanupError } from "../src/kernel/bounded-read.ts";
|
|
18
|
+
import type { discoverSkillPackages } from "../src/kernel/skill-packages.ts";
|
|
19
|
+
import { collectBoundedReadFailures } from "../src/kernel/bounded-read-failures.ts";
|
|
17
20
|
import { isEpisodeId, newEpisodeId } from "../src/kernel/episode-id.ts";
|
|
18
21
|
import { parseInherited, type InheritableApproval } from "../src/kernel/approval.ts";
|
|
19
22
|
import type { ApprovalBinding } from "../src/kernel/correlation.ts";
|
|
20
23
|
import { createApprovalGateProvider } from "../src/governance/approval-prompt.ts";
|
|
21
|
-
import { makeCatalog, skillPathsFromCatalog, type Catalog } from "../src/kernel/catalog.ts";
|
|
24
|
+
import { makeCatalog, skillPathsFromCatalog, type CatalogLoaders, type Catalog } from "../src/kernel/catalog.ts";
|
|
22
25
|
import type { SkillDefinition } from "../src/kernel/definitions.ts";
|
|
23
26
|
import { DELEGATE_CAPABILITY, type DelegationContext } from "../src/kernel/delegate.ts";
|
|
24
|
-
import {
|
|
27
|
+
import { capacityForLifecycle } from "./session-capacity.ts";
|
|
28
|
+
import type { CapacityAllocator } from "../src/kernel/capacity.ts";
|
|
29
|
+
import type { StructuredRefusal } from "../src/kernel/refusals.ts";
|
|
25
30
|
import { chooseExecutor, ENV_HERDR, type ExecutorChoice } from "../src/executors/executor.ts";
|
|
26
31
|
import { WILDCARD } from "../src/kernel/pi-tools.ts";
|
|
27
32
|
import {
|
|
@@ -42,6 +47,7 @@ import {
|
|
|
42
47
|
parseList,
|
|
43
48
|
} from "../src/kernel/propagation.ts";
|
|
44
49
|
import type { Capability } from "../src/kernel/resolve.ts";
|
|
50
|
+
import { selectedDefinitions, type SelectedCommand } from "../src/kernel/selected-definitions.ts";
|
|
45
51
|
import { loadDefinitions } from "../src/kernel/definitions.ts";
|
|
46
52
|
import { buildCatalog } from "../src/kernel/catalog.ts";
|
|
47
53
|
import { ENV_WORKSPACE_REGISTRY } from "../src/kernel/workspace.ts";
|
|
@@ -50,9 +56,9 @@ import { republishable } from "./approvals.ts";
|
|
|
50
56
|
import { storedGrantSessionState } from "./stored-grant-session.ts";
|
|
51
57
|
import { nativeSessionRootFromEnv, type NativeSessionHost } from "../src/executors/native-session-target.ts";
|
|
52
58
|
import { createHandoffStager, type ParentSession } from "./context-staging.ts";
|
|
53
|
-
import { createAdvisorSession, type AdvisorSession } from "./advisor-session.ts";
|
|
54
59
|
import { join } from "node:path";
|
|
55
60
|
import { readFileSync, statSync } from "node:fs";
|
|
61
|
+
import { readBoundedTextSync } from "../src/kernel/bounded-read-sync.ts";
|
|
56
62
|
import { agentDir, projectSettingsPath } from "../src/kernel/project-paths.ts";
|
|
57
63
|
import { ENV_ALLOW_UNRESOLVED_MODELS } from "../src/kernel/model-preflight.ts";
|
|
58
64
|
import { beginExtensionLifecycle, rememberChildPublication, type ReloadLifecycle } from "./reload-environment.ts";
|
|
@@ -67,7 +73,6 @@ import {
|
|
|
67
73
|
} from "../src/kernel/workspace-pin.ts";
|
|
68
74
|
import { loadWorkspaceRegistry } from "../src/kernel/workspace.ts";
|
|
69
75
|
import { reconcileAcceptedWorkspaces } from "../src/governance/workspace-acceptance.ts";
|
|
70
|
-
import { EpisodeCostGate, episodeCostCeilingFromSettings } from "../src/governance/episode-cost-gate.ts";
|
|
71
76
|
import {
|
|
72
77
|
definitionRuntimeSettingsFrom,
|
|
73
78
|
parseConfiguredModel,
|
|
@@ -76,6 +81,13 @@ import {
|
|
|
76
81
|
type DefinitionRuntimeSettings,
|
|
77
82
|
} from "./definition-runtime.ts";
|
|
78
83
|
|
|
84
|
+
/** Trusted composition dependencies, not inherited or requester-supplied authority. */
|
|
85
|
+
export interface SessionDiscovery extends CatalogLoaders {
|
|
86
|
+
packages?: typeof discoverSkillPackages;
|
|
87
|
+
acceptanceRead?: (path: string) => Promise<string>;
|
|
88
|
+
canonicalise?: (path: string) => Promise<string>;
|
|
89
|
+
}
|
|
90
|
+
|
|
79
91
|
/**
|
|
80
92
|
* Run governed children in herdr panes instead of captured child processes.
|
|
81
93
|
*
|
|
@@ -109,13 +121,7 @@ import {
|
|
|
109
121
|
ENV_ACTIVITY_ROOT,
|
|
110
122
|
ENV_ACTIVITY_TASK,
|
|
111
123
|
} from "../src/products/activity-timeline.ts";
|
|
112
|
-
import {
|
|
113
|
-
ENV_HERDR_KEEP_PANE,
|
|
114
|
-
ENV_GOVERNANCE,
|
|
115
|
-
ENV_ADVISOR,
|
|
116
|
-
ENV_EPISODE_ID,
|
|
117
|
-
ENV_EPISODE_COST_CEILING,
|
|
118
|
-
} from "../src/kernel/env-names.ts";
|
|
124
|
+
import { ENV_HERDR_KEEP_PANE, ENV_GOVERNANCE, ENV_EPISODE_ID } from "../src/kernel/env-names.ts";
|
|
119
125
|
export { ENV_HERDR_KEEP_PANE, ENV_GOVERNANCE } from "../src/kernel/env-names.ts";
|
|
120
126
|
import { adoptLegacyEnvironment } from "../src/kernel/env-names.ts";
|
|
121
127
|
|
|
@@ -165,8 +171,10 @@ export interface GrantsSession extends NativeSessionHost {
|
|
|
165
171
|
ownExecutionId?: string;
|
|
166
172
|
/** Stable identity shared by this root session and every descendant. */
|
|
167
173
|
episodeId: string;
|
|
168
|
-
/**
|
|
174
|
+
/** Configured active descendant capacity; use capacity.available for actual reservations. */
|
|
169
175
|
fanoutBudget: number;
|
|
176
|
+
capacity: CapacityAllocator;
|
|
177
|
+
capacityRefusal?: StructuredRefusal;
|
|
170
178
|
/** Whether delegation tools are active. Reconciled against the owner-bound root at session_start. */
|
|
171
179
|
mayDelegate: boolean;
|
|
172
180
|
/** True only after session_start binds this instance to ctx.sessionManager. */
|
|
@@ -178,10 +186,8 @@ export interface GrantsSession extends NativeSessionHost {
|
|
|
178
186
|
/** Committed per-definition and global child runtime defaults. */
|
|
179
187
|
readonly definitionRuntimeSettings: DefinitionRuntimeSettings;
|
|
180
188
|
/** PD-7's future session controls write here; deliberately empty in this commit. */
|
|
189
|
+
currentThinking?: () => string;
|
|
181
190
|
readonly definitionRuntimeOverrides: Map<string, DefinitionRuntimeChoice>;
|
|
182
|
-
readonly sessionModelPrompt: "ask" | "never";
|
|
183
|
-
sessionModelPrompted: boolean;
|
|
184
|
-
sessionModelPromptInFlight?: Promise<void>;
|
|
185
191
|
/** Path to this extension, so a child granted `tool:delegate` can delegate in turn. */
|
|
186
192
|
readonly extensionPath?: string;
|
|
187
193
|
/** Hook-only observer path for leaf children; it exposes no tools. */
|
|
@@ -194,8 +200,6 @@ export interface GrantsSession extends NativeSessionHost {
|
|
|
194
200
|
* context handoff (ADR-0078): its file path for `fork`, its message turns for `pruned`.
|
|
195
201
|
*/
|
|
196
202
|
parentSession?: ParentSession;
|
|
197
|
-
/** ADR-0077: the session's advisor, off unless the environment enables one. Never consulted for authority. */
|
|
198
|
-
advisorSession: AdvisorSession;
|
|
199
203
|
/** Root identity keyed to ctx.sessionManager once session_start supplies it. */
|
|
200
204
|
reloadLifecycle: ReloadLifecycle;
|
|
201
205
|
/** Approval keys approved for this session. In memory only — this dies with the process. */
|
|
@@ -213,8 +217,6 @@ export interface GrantsSession extends NativeSessionHost {
|
|
|
213
217
|
inheritedApprovals: Map<string, string | undefined>;
|
|
214
218
|
/** ONE single-flight queue for the whole session — see `obtainApprovals` for why it lives here. */
|
|
215
219
|
readonly approvalGateFor: ReturnType<typeof createApprovalGateProvider>;
|
|
216
|
-
/** One cumulative cost threshold shared by every child in this episode. */
|
|
217
|
-
readonly episodeCostGate: EpisodeCostGate;
|
|
218
220
|
/** Set at `session_start`; `process.cwd()` until then. */
|
|
219
221
|
cwd: string;
|
|
220
222
|
/** This session's own grant. Starts as the inherited upper bound, tightened once tools are observed. */
|
|
@@ -222,6 +224,7 @@ export interface GrantsSession extends NativeSessionHost {
|
|
|
222
224
|
observed: boolean;
|
|
223
225
|
observedTools: string[] | null;
|
|
224
226
|
/** ADR-0016: `SKILL.md` definitions, keyed by name. The format this package spawns from now. */
|
|
227
|
+
ensureDefinitions?: () => Promise<void>;
|
|
225
228
|
definitions: Map<string, SkillDefinition>;
|
|
226
229
|
/**
|
|
227
230
|
* Definitions discovery dropped, and why — reported at session start (ADR-0076, rule 8).
|
|
@@ -248,6 +251,10 @@ export interface GrantsSession extends NativeSessionHost {
|
|
|
248
251
|
workspaceSkips: string[];
|
|
249
252
|
/** Which registry ids this machine has accepted, and which it has not. Reported, and drives `/grants`. */
|
|
250
253
|
workspaceAcceptance?: { accepted: string[]; firstUse: boolean; unaccepted: string[] };
|
|
254
|
+
/** Trusted discovery dependencies supplied by composition, never inherited authority. */
|
|
255
|
+
readonly discovery: SessionDiscovery;
|
|
256
|
+
/** Exact unresolved discovery failure/capability; physical recovery never rewrites the failed outcome. */
|
|
257
|
+
discoveryCleanupFailure?: BoundedReadCleanupError;
|
|
251
258
|
catalog: Catalog;
|
|
252
259
|
/**
|
|
253
260
|
* The in-flight catalog build, so `delegate` can wait for it instead of racing it.
|
|
@@ -327,7 +334,9 @@ export interface GrantsSession extends NativeSessionHost {
|
|
|
327
334
|
* Failing to establish is not an error: a machine with no registry has no workspaces to route to, and the
|
|
328
335
|
* absent pin refuses anything that tries. That is the same direction as every other failure in this mechanism.
|
|
329
336
|
*/
|
|
330
|
-
async function establishRootPin(session: GrantsSession): Promise<void> {
|
|
337
|
+
async function establishRootPin(session: GrantsSession, lifecycle: ReloadLifecycle): Promise<void> {
|
|
338
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
339
|
+
if (lifecycle.workspacePinFailure) throw lifecycle.workspacePinFailure;
|
|
331
340
|
if (session.pinSettled) return;
|
|
332
341
|
// **One assignment, at the end, on every path.** Review found the previous shape — assign at each `return`
|
|
333
342
|
// — missing two of five exits: the `catch` around an unreadable registry, which is precisely the state a
|
|
@@ -339,26 +348,34 @@ async function establishRootPin(session: GrantsSession): Promise<void> {
|
|
|
339
348
|
// settled with nothing settled — routing nowhere, which is safe, but with the LIFECYCLE unset, so the next
|
|
340
349
|
// reload would mint again. Review could construct no throw today; "currently unreachable" is exactly the
|
|
341
350
|
// property this feature has now been wrong about four times, and the ordering costs nothing.
|
|
342
|
-
|
|
351
|
+
// Charge/publish BEFORE invoking trusted loaders: reentrant startup/init/reload joins the same owner.
|
|
352
|
+
lifecycle.workspacePinAcquisition ??= Promise.resolve().then(async () => {
|
|
353
|
+
const settled = await settleWorkspacePin(session, lifecycle);
|
|
354
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
355
|
+
lifecycle.workspacePin = settled;
|
|
356
|
+
return settled;
|
|
357
|
+
});
|
|
358
|
+
const settled = await lifecycle.workspacePinAcquisition;
|
|
359
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
343
360
|
session.pinSettled = true;
|
|
344
361
|
session.workspacePin = settled;
|
|
345
|
-
session.reloadLifecycle.workspacePin = settled;
|
|
346
362
|
}
|
|
347
363
|
|
|
348
364
|
/**
|
|
349
|
-
* What this session's destination pin IS (ADR-0042).
|
|
365
|
+
* What this session's destination pin IS (ADR-0042). Ordinary unavailable registries settle an empty map;
|
|
366
|
+
* physical reader cleanup failures instead retain their exact rejected outcome on the owning lifecycle.
|
|
350
367
|
*
|
|
351
|
-
* An empty map means "settled, and you may route nowhere", which is different from never having settled
|
|
352
|
-
*
|
|
368
|
+
* An empty map means "settled, and you may route nowhere", which is different from never having settled.
|
|
369
|
+
* The distinction that matters is not empty-versus-absent but
|
|
353
370
|
* settled-versus-not, and settling happens exactly once per owner.
|
|
354
371
|
*/
|
|
355
|
-
async function settleWorkspacePin(session: GrantsSession): Promise<WorkspacePins> {
|
|
372
|
+
async function settleWorkspacePin(session: GrantsSession, lifecycle: ReloadLifecycle): Promise<WorkspacePins> {
|
|
356
373
|
// Settled by an EARLIER SESSION OBJECT for this same owner — an extension reload. Adopting rather than
|
|
357
374
|
// re-deriving is the point: a root may mint, but only once, and only from the registry as it stood before
|
|
358
375
|
// any child had a chance to rewrite it.
|
|
359
|
-
if (
|
|
376
|
+
if (lifecycle.workspacePin) return lifecycle.workspacePin;
|
|
360
377
|
|
|
361
|
-
const raw =
|
|
378
|
+
const raw = lifecycle.root[ENV_WORKSPACE_PIN];
|
|
362
379
|
const inherited = parseWorkspacePin(raw);
|
|
363
380
|
// Inherited, so it is authority and is kept exactly as it arrived — including an empty one, which says
|
|
364
381
|
// "your parent established a pin and gave you none of it".
|
|
@@ -379,51 +396,159 @@ async function settleWorkspacePin(session: GrantsSession): Promise<WorkspacePins
|
|
|
379
396
|
const registryPath = process.env[ENV_WORKSPACE_REGISTRY];
|
|
380
397
|
if (!registryPath) return new Map();
|
|
381
398
|
try {
|
|
382
|
-
|
|
399
|
+
let registry: Awaited<ReturnType<typeof loadWorkspaceRegistry>>;
|
|
400
|
+
try {
|
|
401
|
+
registry = await (session.discovery.registry ?? loadWorkspaceRegistry)(registryPath);
|
|
402
|
+
} catch (error) {
|
|
403
|
+
if (error instanceof BoundedReadCleanupError) throw error;
|
|
404
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
405
|
+
// Only registry unavailability is soft: the catalog reports it, and settling prevents later reminting.
|
|
406
|
+
return new Map();
|
|
407
|
+
}
|
|
408
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
383
409
|
// **The id SET, which the destination pin does not cover.** ADR-0042 bound what an id means; a child
|
|
384
410
|
// holding `tool:write` inherits the registry path and can append an id of its own, and the NEXT root
|
|
385
411
|
// session mints a pin for it. Measured: a child-created id reached the catalog, the pin and a real route
|
|
386
412
|
// with no operator action. So an id nobody accepted is not pinned, and therefore not routable.
|
|
387
|
-
const acceptance = await reconcileAcceptedWorkspaces(
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
413
|
+
const acceptance = await reconcileAcceptedWorkspaces(
|
|
414
|
+
registryPath,
|
|
415
|
+
Object.keys(registry.workspaces),
|
|
416
|
+
process.env,
|
|
417
|
+
session.discovery.acceptanceRead,
|
|
418
|
+
() => assertDiscoveryHealthy(session, lifecycle),
|
|
419
|
+
);
|
|
420
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
421
|
+
const skips = acceptance.unaccepted.map(
|
|
422
|
+
(id) => `${id} — it is in the registry but this machine has never accepted it`,
|
|
423
|
+
);
|
|
391
424
|
const accepted = new Set(acceptance.accepted);
|
|
392
425
|
const narrowed = {
|
|
393
426
|
workspaces: Object.fromEntries(Object.entries(registry.workspaces).filter(([id]) => accepted.has(id))),
|
|
394
427
|
};
|
|
395
|
-
|
|
396
|
-
|
|
428
|
+
const pins = await establishWorkspacePin(
|
|
429
|
+
narrowed,
|
|
430
|
+
async (path) => {
|
|
431
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
432
|
+
try {
|
|
433
|
+
return await (session.discovery.canonicalise ?? realpath)(path);
|
|
434
|
+
} finally {
|
|
435
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
(id, reason) => {
|
|
439
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
440
|
+
skips.push(`${id} — ${reason}`);
|
|
441
|
+
},
|
|
397
442
|
);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
443
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
444
|
+
session.workspaceAcceptance = acceptance;
|
|
445
|
+
for (const skip of skips) session.workspaceSkips.push(skip);
|
|
446
|
+
return pins;
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (error instanceof BoundedReadCleanupError) {
|
|
449
|
+
lifecycle.workspacePinFailure = error;
|
|
450
|
+
throw error;
|
|
451
|
+
}
|
|
452
|
+
throw error;
|
|
405
453
|
}
|
|
406
454
|
}
|
|
407
455
|
|
|
408
|
-
|
|
409
|
-
|
|
456
|
+
/** Publish a safely observed rejection for delegation, never a fulfilled prior catalog. The extension reports it. */
|
|
457
|
+
export function recordDiscoveryCleanupFailure(
|
|
458
|
+
session: GrantsSession,
|
|
459
|
+
error: BoundedReadCleanupError,
|
|
460
|
+
lifecycle = session.reloadLifecycle,
|
|
461
|
+
): BoundedReadCleanupError {
|
|
462
|
+
const failure = collectBoundedReadFailures([lifecycle.discoveryCleanupFailure, error])!;
|
|
463
|
+
lifecycle.discoveryCleanupFailure = failure;
|
|
464
|
+
if (session.reloadLifecycle === lifecycle) session.discoveryCleanupFailure = failure;
|
|
465
|
+
return failure;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export function retainDiscoveryCleanupFailure(
|
|
469
|
+
session: GrantsSession,
|
|
470
|
+
error: BoundedReadCleanupError,
|
|
471
|
+
lifecycle = session.reloadLifecycle,
|
|
472
|
+
): void {
|
|
473
|
+
const failure = recordDiscoveryCleanupFailure(session, error, lifecycle);
|
|
474
|
+
if (session.reloadLifecycle === lifecycle) {
|
|
475
|
+
session.catalogReady = Promise.reject(failure);
|
|
476
|
+
void session.catalogReady.then(undefined, () => undefined);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** Terminal owner state, checked independently of a replaceable catalog promise and after every wait. */
|
|
481
|
+
export function assertDiscoveryHealthy(session: GrantsSession, lifecycle = session.reloadLifecycle): void {
|
|
482
|
+
const failure =
|
|
483
|
+
lifecycle.discoveryCleanupFailure ??
|
|
484
|
+
lifecycle.workspacePinFailure ??
|
|
485
|
+
(session.reloadLifecycle === lifecycle ? session.discoveryCleanupFailure : undefined);
|
|
486
|
+
if (failure) throw failure;
|
|
487
|
+
if (session.reloadLifecycle !== lifecycle) throw new Error("discovery owner replaced before publication");
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
async function delegationCatalog(session: GrantsSession): Promise<Catalog> {
|
|
491
|
+
const lifecycle = session.reloadLifecycle;
|
|
492
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
493
|
+
const catalog = await session.catalogReady;
|
|
494
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
495
|
+
return catalog;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export async function loadProjectDefinitions(
|
|
499
|
+
session: GrantsSession,
|
|
500
|
+
cwd: string,
|
|
501
|
+
lifecycle = session.reloadLifecycle,
|
|
502
|
+
selected?: readonly SelectedCommand[],
|
|
503
|
+
): Promise<void> {
|
|
504
|
+
try {
|
|
505
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
506
|
+
await loadProjectDiscovery(session, cwd, lifecycle, selected);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
if (error instanceof BoundedReadCleanupError) retainDiscoveryCleanupFailure(session, error, lifecycle);
|
|
509
|
+
throw error;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
async function loadProjectDiscovery(
|
|
514
|
+
session: GrantsSession,
|
|
515
|
+
cwd: string,
|
|
516
|
+
lifecycle: ReloadLifecycle,
|
|
517
|
+
selected?: readonly SelectedCommand[],
|
|
518
|
+
): Promise<void> {
|
|
519
|
+
await establishRootPin(session, lifecycle);
|
|
410
520
|
const skips: string[] = [];
|
|
411
|
-
session
|
|
521
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
522
|
+
const snapshot = selected === undefined ? undefined : await selectedDefinitions(selected);
|
|
523
|
+
const definitions =
|
|
524
|
+
snapshot?.definitions ??
|
|
525
|
+
(await (session.discovery.definitions ?? loadDefinitions)(cwd, (_path, reason) => skips.push(reason)));
|
|
526
|
+
if (snapshot) skips.push(...snapshot.skips);
|
|
527
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
528
|
+
const ready = buildCatalog(
|
|
529
|
+
{
|
|
530
|
+
...(snapshot ? { snapshot } : {}),
|
|
531
|
+
cwd,
|
|
532
|
+
observedTools: session.observedTools,
|
|
533
|
+
// ADR-0035: `workspace:<id>` is a capability, so the registered ids belong in the catalog the same way
|
|
534
|
+
// discovered definitions do — for `/grants` to list what this session may route to and for `init` to
|
|
535
|
+
// scaffold them. Read live rather than cached at load, because the registry is an operator file.
|
|
536
|
+
registryPath: process.env[ENV_WORKSPACE_REGISTRY],
|
|
537
|
+
},
|
|
538
|
+
session.discovery,
|
|
539
|
+
);
|
|
540
|
+
session.catalogReady = ready;
|
|
541
|
+
const catalog = await ready;
|
|
542
|
+
assertDiscoveryHealthy(session, lifecycle);
|
|
543
|
+
session.definitions = definitions;
|
|
412
544
|
session.definitionSkips = skips;
|
|
413
|
-
session.
|
|
414
|
-
cwd,
|
|
415
|
-
observedTools: session.observedTools,
|
|
416
|
-
// ADR-0035: `workspace:<id>` is a capability, so the registered ids belong in the catalog the same way
|
|
417
|
-
// discovered definitions do — for `/grants` to list what this session may route to and for `init` to
|
|
418
|
-
// scaffold them. Read live rather than cached at load, because the registry is an operator file.
|
|
419
|
-
registryPath: process.env[ENV_WORKSPACE_REGISTRY],
|
|
420
|
-
});
|
|
421
|
-
session.catalog = await session.catalogReady;
|
|
545
|
+
session.catalog = catalog;
|
|
422
546
|
}
|
|
423
547
|
export function createGrantsSession(
|
|
424
548
|
extensionPath: string | undefined,
|
|
425
549
|
lifecycle?: ReloadLifecycle,
|
|
426
550
|
observerExtensionPath?: string,
|
|
551
|
+
discovery: SessionDiscovery = {},
|
|
427
552
|
): GrantsSession {
|
|
428
553
|
// ADR-0076 PR 3b: legacy PI_GRANTS_* names are adopted BEFORE the first environment read and before the
|
|
429
554
|
// reload snapshot, or an operator on the old names would get an ungoverned wildcard root (review finding).
|
|
@@ -459,34 +584,18 @@ export function createGrantsSession(
|
|
|
459
584
|
// G7 / A-S4 + B-I4: strict, three-way parsing that fails CLOSED. A malformed bound used to yield
|
|
460
585
|
// `NaN`, and every comparison against `NaN` is false, so depth limiting switched itself off.
|
|
461
586
|
const bounds = depthConfig(environment[ENV_DEPTH], environment[ENV_MAX_DEPTH]);
|
|
462
|
-
const costConfig = episodeCostConfiguration(storeCwd, environment[ENV_EPISODE_COST_CEILING]);
|
|
463
587
|
const runtimeConfig = projectDefinitionRuntimeConfiguration(storeCwd);
|
|
464
588
|
const depth = bounds.depth;
|
|
465
|
-
const maxDepth =
|
|
589
|
+
const maxDepth = runtimeConfig.malformed ? 0 : bounds.maxDepth;
|
|
466
590
|
const emptyCatalog = makeCatalog([]);
|
|
467
|
-
|
|
468
|
-
// narrow it. The block IS read — the first version passed `undefined` and every narrowing the release advertised
|
|
469
|
-
// was dead code reachable only from tests, which review measured: `enabled: false` turned nothing off.
|
|
470
|
-
//
|
|
471
|
-
// Read from `storeCwd` for `loadStoredGrantStateSync`'s reason: this factory runs before any hook, so `ctx.cwd`
|
|
472
|
-
// does not exist yet. Reading a workspace-writable file here is safe precisely because it can only narrow.
|
|
473
|
-
const advisorSession = createAdvisorSession({
|
|
474
|
-
block: projectAdvisorBlock(storeCwd, environment),
|
|
475
|
-
episodeId,
|
|
476
|
-
...(storedLedger ? { ledgerPath: storedLedger } : {}),
|
|
477
|
-
});
|
|
591
|
+
const capacityState = capacityForLifecycle(activeLifecycle, environment[ENV_FANOUT]);
|
|
478
592
|
const session: GrantsSession = {
|
|
479
|
-
advisorSession,
|
|
480
593
|
adoptedLegacyEnv,
|
|
481
594
|
governed,
|
|
482
595
|
inherited,
|
|
483
596
|
depth,
|
|
484
597
|
maxDepth,
|
|
485
|
-
malformedBounds: [
|
|
486
|
-
...bounds.malformed,
|
|
487
|
-
...(costConfig.malformed ? [costConfig.malformed] : []),
|
|
488
|
-
...(runtimeConfig.malformed ? [runtimeConfig.malformed] : []),
|
|
489
|
-
],
|
|
598
|
+
malformedBounds: [...bounds.malformed, ...(runtimeConfig.malformed ? [runtimeConfig.malformed] : [])],
|
|
490
599
|
definitionSkips: [],
|
|
491
600
|
workspacePin: undefined,
|
|
492
601
|
pinSettled: false,
|
|
@@ -511,9 +620,9 @@ export function createGrantsSession(
|
|
|
511
620
|
ownSpawnId: environment[ENV_PARENT_ID]?.trim() || `d${depth}`,
|
|
512
621
|
ownExecutionId: environment[ENV_EXECUTION_ID]?.trim() || undefined,
|
|
513
622
|
episodeId,
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
623
|
+
fanoutBudget: capacityState.allocator.total,
|
|
624
|
+
capacity: capacityState.allocator,
|
|
625
|
+
capacityRefusal: capacityState.refusal,
|
|
517
626
|
/**
|
|
518
627
|
* Review finding S-5, fixed. The comment on the tools has always claimed conditional registration; the
|
|
519
628
|
* call was unconditional, `DELEGATE_CAPABILITY` was imported and never used, and "withhold it and the
|
|
@@ -528,8 +637,6 @@ export function createGrantsSession(
|
|
|
528
637
|
modelResolutionCache: new Map<string, boolean>(),
|
|
529
638
|
definitionRuntimeSettings: runtimeConfig.settings,
|
|
530
639
|
definitionRuntimeOverrides: new Map<string, DefinitionRuntimeChoice>(),
|
|
531
|
-
sessionModelPrompt: runtimeConfig.prompt,
|
|
532
|
-
sessionModelPrompted: false,
|
|
533
640
|
extensionPath,
|
|
534
641
|
observerExtensionPath,
|
|
535
642
|
activityRootId,
|
|
@@ -538,12 +645,12 @@ export function createGrantsSession(
|
|
|
538
645
|
sessionApprovalBindings: new Map<string, ApprovalBinding>(),
|
|
539
646
|
inheritedApprovals: parseInherited(environment[ENV_APPROVED]),
|
|
540
647
|
approvalGateFor: createApprovalGateProvider(),
|
|
541
|
-
episodeCostGate: new EpisodeCostGate(costConfig.ceiling),
|
|
542
648
|
cwd: process.cwd(),
|
|
543
649
|
ownGrant: deriveOwnGrant(inherited, null),
|
|
544
650
|
observed: false,
|
|
545
651
|
observedTools: null,
|
|
546
652
|
definitions: new Map<string, SkillDefinition>(),
|
|
653
|
+
discovery: Object.freeze({ ...discovery }),
|
|
547
654
|
catalog: emptyCatalog,
|
|
548
655
|
catalogReady: Promise.resolve(emptyCatalog),
|
|
549
656
|
delegationContext: async (approved?: InheritableApproval[]) => ({
|
|
@@ -560,21 +667,17 @@ export function createGrantsSession(
|
|
|
560
667
|
// one through the same builder `publishChildEnv` uses. Read here rather than in the kernel so there is
|
|
561
668
|
// one place that knows the environment is where a session's own pin lives.
|
|
562
669
|
...(session.workspacePin ? { workspacePin: session.workspacePin } : {}),
|
|
563
|
-
//
|
|
564
|
-
// `options` is forwarded, and its absence is why the second decision point was dead in production: a
|
|
565
|
-
// one-parameter arrow is assignable to a two-parameter type, so the ids reached here and were discarded while
|
|
566
|
-
// the advisor had already been asked. Review measured it. `test/pruning-advice.test.ts` now goes through this
|
|
567
|
-
// function rather than calling the stager directly.
|
|
670
|
+
// Context is staged only after its capability survives the ceiling, grant and gate.
|
|
568
671
|
stageHandoff: (granted, options) =>
|
|
569
672
|
createHandoffStager({
|
|
570
673
|
cwd: session.cwd,
|
|
571
674
|
forkRoot: join(agentDir(), "context-forks"),
|
|
572
675
|
...(session.parentSession ? { parentSession: session.parentSession } : {}),
|
|
573
676
|
})(granted, options),
|
|
574
|
-
catalog: await session
|
|
677
|
+
catalog: await delegationCatalog(session),
|
|
575
678
|
// R-32: where each granted skill lives, so `planSpawn` can pass `--skill` for those and only those.
|
|
576
679
|
// Derived from the catalog's own `source`, so it cannot drift from what was discovered.
|
|
577
|
-
skillPaths: skillPathsFromCatalog(await session
|
|
680
|
+
skillPaths: skillPathsFromCatalog(await delegationCatalog(session)),
|
|
578
681
|
// ADR-0016: operator-authored SKILL.md definitions, so `delegate({agent})` can name one.
|
|
579
682
|
definitions: session.definitions,
|
|
580
683
|
// The herdr executor drives the child after starting it, so its plan must NOT carry `--print`.
|
|
@@ -603,7 +706,14 @@ export function createGrantsSession(
|
|
|
603
706
|
}
|
|
604
707
|
session.publishChildEnv();
|
|
605
708
|
},
|
|
606
|
-
reconcileEnvironment: (environment, lifecycle) =>
|
|
709
|
+
reconcileEnvironment: (environment, lifecycle) => {
|
|
710
|
+
if (session.reloadLifecycle !== lifecycle) {
|
|
711
|
+
session.pinSettled = false;
|
|
712
|
+
session.workspacePin = undefined;
|
|
713
|
+
session.discoveryCleanupFailure = lifecycle.discoveryCleanupFailure;
|
|
714
|
+
}
|
|
715
|
+
reconcileSessionEnvironment(session, environment, lifecycle);
|
|
716
|
+
},
|
|
607
717
|
publishChildEnv: () => {
|
|
608
718
|
const env = childEnv({
|
|
609
719
|
ownGrant: session.ownGrant,
|
|
@@ -629,66 +739,17 @@ export function createGrantsSession(
|
|
|
629
739
|
return session;
|
|
630
740
|
}
|
|
631
741
|
|
|
632
|
-
/**
|
|
633
|
-
* The `advisor` block of `.pi/pi-daddy/settings.json`, or undefined.
|
|
634
|
-
*
|
|
635
|
-
* Unreadable, absent or malformed all yield undefined: this file is the reviewable record, not authority, and the
|
|
636
|
-
* only thing it can do to an advisor is turn one off. A parse failure therefore costs nothing worth reporting.
|
|
637
|
-
*/
|
|
638
|
-
export function reconcileAdvisorSession(session: GrantsSession, environment: NodeJS.ProcessEnv): void {
|
|
639
|
-
session.advisorSession = createAdvisorSession({
|
|
640
|
-
block: projectAdvisorBlock(session.storeCwd, environment),
|
|
641
|
-
episodeId: session.episodeId,
|
|
642
|
-
...(session.ledgerPath ? { ledgerPath: session.ledgerPath } : {}),
|
|
643
|
-
env: environment,
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
function episodeCostConfiguration(cwd: string, inherited: string | undefined): { ceiling: number; malformed?: string } {
|
|
648
|
-
if (inherited !== undefined) {
|
|
649
|
-
const value = Number(inherited);
|
|
650
|
-
return Number.isFinite(value) && value > 0
|
|
651
|
-
? { ceiling: value }
|
|
652
|
-
: { ceiling: episodeCostCeilingFromSettings(undefined), malformed: ENV_EPISODE_COST_CEILING };
|
|
653
|
-
}
|
|
654
|
-
const path = projectSettingsPath(cwd);
|
|
655
|
-
try {
|
|
656
|
-
const stats = statSync(path);
|
|
657
|
-
if (!stats.isFile() || stats.size > 1024 * 1024) {
|
|
658
|
-
return { ceiling: episodeCostCeilingFromSettings(undefined), malformed: "settings.json episodeCostCeiling" };
|
|
659
|
-
}
|
|
660
|
-
const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
661
|
-
if (parsed && typeof parsed === "object" && Object.hasOwn(parsed, "episodeCostCeiling")) {
|
|
662
|
-
const value = (parsed as Record<string, unknown>).episodeCostCeiling;
|
|
663
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
664
|
-
return { ceiling: episodeCostCeilingFromSettings(undefined), malformed: "settings.json episodeCostCeiling" };
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
return { ceiling: episodeCostCeilingFromSettings(parsed) };
|
|
668
|
-
} catch (error) {
|
|
669
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
670
|
-
return { ceiling: episodeCostCeilingFromSettings(undefined) };
|
|
671
|
-
}
|
|
672
|
-
return { ceiling: episodeCostCeilingFromSettings(undefined), malformed: "settings.json episodeCostCeiling" };
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
|
|
676
742
|
function projectDefinitionRuntimeConfiguration(cwd: string): {
|
|
677
743
|
settings: DefinitionRuntimeSettings;
|
|
678
|
-
prompt: "ask" | "never";
|
|
679
744
|
malformed?: string;
|
|
680
745
|
} {
|
|
681
|
-
const empty = { settings: definitionRuntimeSettingsFrom(undefined)
|
|
746
|
+
const empty = { settings: definitionRuntimeSettingsFrom(undefined) };
|
|
682
747
|
try {
|
|
683
748
|
const path = projectSettingsPath(cwd);
|
|
684
|
-
const
|
|
685
|
-
if (!stats.isFile() || stats.size > 1024 * 1024) return { ...empty, malformed: "settings.json model defaults" };
|
|
686
|
-
const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
749
|
+
const parsed: unknown = JSON.parse(readBoundedTextSync(path, { maxBytes: 1024 * 1024 }).text);
|
|
687
750
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
688
751
|
return { ...empty, malformed: "settings.json model defaults" };
|
|
689
752
|
const root = parsed as Record<string, unknown>;
|
|
690
|
-
if (root.sessionModelPrompt !== undefined && !["ask", "never"].includes(String(root.sessionModelPrompt)))
|
|
691
|
-
return { ...empty, malformed: "settings.json sessionModelPrompt" };
|
|
692
753
|
const candidates = [root.defaults, ...(Array.isArray(root.definitions) ? root.definitions : [])];
|
|
693
754
|
for (const candidate of candidates) {
|
|
694
755
|
if (candidate === undefined) continue;
|
|
@@ -702,28 +763,10 @@ function projectDefinitionRuntimeConfiguration(cwd: string): {
|
|
|
702
763
|
}
|
|
703
764
|
return {
|
|
704
765
|
settings: definitionRuntimeSettingsFrom(root),
|
|
705
|
-
prompt: root.sessionModelPrompt === "never" ? "never" : "ask",
|
|
706
766
|
};
|
|
707
767
|
} catch (error) {
|
|
768
|
+
if (error instanceof BoundedReadCleanupError) throw error;
|
|
708
769
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") return empty;
|
|
709
770
|
return { ...empty, malformed: "settings.json model defaults" };
|
|
710
771
|
}
|
|
711
772
|
}
|
|
712
|
-
|
|
713
|
-
function projectAdvisorBlock(cwd: string, env: NodeJS.ProcessEnv = process.env): unknown {
|
|
714
|
-
// Only when an advisor could exist at all. This runs in every session including every child, before any hook, and
|
|
715
|
-
// a child can never use the result because `PI_DADDY_ADVISOR` is stripped from it.
|
|
716
|
-
if (!env[ENV_ADVISOR]?.trim()) return undefined;
|
|
717
|
-
try {
|
|
718
|
-
const path = projectSettingsPath(cwd);
|
|
719
|
-
// Bounded and type-checked first: this is the third unbounded session-start read AGENTS.md warns about, and the
|
|
720
|
-
// only one whose path a governed child holding `tool:write` can replace with a FIFO — which would hang pi
|
|
721
|
-
// before any hook exists to report it.
|
|
722
|
-
const stats = statSync(path);
|
|
723
|
-
if (!stats.isFile() || stats.size > 1024 * 1024) return undefined;
|
|
724
|
-
const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
725
|
-
return typeof parsed === "object" && parsed !== null ? (parsed as Record<string, unknown>).advisor : undefined;
|
|
726
|
-
} catch {
|
|
727
|
-
return undefined;
|
|
728
|
-
}
|
|
729
|
-
}
|
|
@@ -184,6 +184,7 @@ export async function releaseDelegationWorkspace(input: {
|
|
|
184
184
|
reason: string;
|
|
185
185
|
/** Deliberately keep the lease: a herdr writer tab would not close, so the pane may still be live. */
|
|
186
186
|
retain?: boolean;
|
|
187
|
+
onObservationFailure?: (error: unknown) => void;
|
|
187
188
|
}): Promise<LeaseReleaseOutcome | "retained" | undefined> {
|
|
188
189
|
if (!input.prepared) return undefined;
|
|
189
190
|
// A retained lease writes no `state: "released"`, so the record stays `active` and the NEXT owner reads
|
|
@@ -197,7 +198,7 @@ export async function releaseDelegationWorkspace(input: {
|
|
|
197
198
|
: await input.prepared.lease.release(input.reason);
|
|
198
199
|
if (input.ledgerPath) {
|
|
199
200
|
await appendLedgerEvent(
|
|
200
|
-
{ path: input.ledgerPath, strict:
|
|
201
|
+
{ path: input.ledgerPath, strict: !input.onObservationFailure, onFailure: input.onObservationFailure },
|
|
201
202
|
buildWorkspaceLeaseEvent({
|
|
202
203
|
...(input.episodeId ? { episodeId: input.episodeId } : {}),
|
|
203
204
|
executionId: input.executionId,
|
package/native/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Captured worker artifact
|
|
2
|
+
|
|
3
|
+
The packaged Linux x64 helper is built from `worker.c` with the official Zig 0.14.1 compiler
|
|
4
|
+
archive and static musl target pinned in `toolchain.json`. No system compiler or libc is used.
|
|
5
|
+
The distribution is published at <https://ziglang.org/download/>; the archive SHA-256 was checked
|
|
6
|
+
against <https://ziglang.org/download/index.json>. `provenance.json` records all build inputs,
|
|
7
|
+
output bytes/hash and the unmodified license files copied from that archive.
|
|
8
|
+
|
|
9
|
+
From `packages/pi-daddy`, `npm run build:worker` downloads and hash-checks the pinned archive,
|
|
10
|
+
extracts it into a fresh temporary directory, rebuilds, and writes the binary, checksum,
|
|
11
|
+
provenance, license copies and `src/executors/worker-artifact.ts`. Node 22.19+ and `tar` are required.
|
|
12
|
+
`node scripts/build-worker.ts --verify` repeats the build in a new directory and compares every
|
|
13
|
+
output byte without modifying tracked files; CI runs this on each Node matrix leg. An existing
|
|
14
|
+
archive can be supplied with `PI_DADDY_WORKER_TOOLCHAIN_ARCHIVE`; it must pass the same hash check.
|
|
15
|
+
The temporary compiler is removed after each run. The verified archive cache is under the OS
|
|
16
|
+
temporary directory; it is never part of the npm package.
|
|
17
|
+
|
|
18
|
+
The runtime verifies the opened executable against the SHA-256 constant compiled into its
|
|
19
|
+
JavaScript before executing that same held descriptor. The adjacent checksum is for independent
|
|
20
|
+
verification, never runtime authority. This binds the artifact to the reviewed package contents;
|
|
21
|
+
it does not protect a package whose JavaScript was modified by the same user.
|
|
22
|
+
|
|
23
|
+
The build checks ELF64/x86-64 and absence of dynamic/interpreter program headers. Runtime still
|
|
24
|
+
requires the qualified Linux kernel facilities: subreaper, pidfds and `/proc` child enumeration.
|
|
25
|
+
No compiler runs during package installation or execution.
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
981a45591b1de790501ef531f6b0ccc8299b1e9a2d08a7970ccaeeecb9868798
|