pi-daddy 0.22.0 → 0.24.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 +40 -0
- package/README.md +12 -6
- package/contracts/daily-view/v1/README.md +114 -0
- package/contracts/daily-view/v1/fixtures/view.json +525 -0
- package/contracts/daily-view/v1/fixtures/view.txt +75 -0
- package/contracts/daily-view/v1/fixtures/work.jsonl +17 -0
- package/contracts/daily-view/v1/p03/README.md +26 -0
- package/contracts/daily-view/v1/p03/fixtures/retained-executions.json +241 -0
- package/contracts/daily-view/v1/p03/projection.schema.json +194 -0
- package/contracts/daily-view/v1/p03/provenance.json +21 -0
- package/contracts/dashboard-host/v1/README.md +176 -0
- package/contracts/debrief/v1/README.md +146 -0
- package/contracts/debrief/v1/intervention/README.md +32 -0
- package/contracts/debrief/v1/intervention/fixtures/artifact-3ee11136beb864f518252e5024dae9197421fbf97b6e981d3243c782b5c44bd0.txt +1 -0
- package/contracts/debrief/v1/intervention/fixtures/artifact-58905d872dd3c54a9705f67b05fa82edbad7af6476ae8993faf1a7b9fadfb905.txt +1 -0
- package/contracts/debrief/v1/intervention/fixtures/assessment.json +29 -0
- package/contracts/debrief/v1/intervention/fixtures/blind-view.json +20 -0
- package/contracts/debrief/v1/intervention/fixtures/choice.json +7 -0
- package/contracts/debrief/v1/intervention/fixtures/manifest.json +57 -0
- package/contracts/debrief/v1/intervention/fixtures/reveal.json +39 -0
- package/contracts/debrief/v1/provenance.json +89 -0
- package/contracts/debrief/v1/work-capture/README.md +38 -0
- package/contracts/debrief/v1/work-capture/fixtures/batch.json +17 -0
- package/contracts/debrief/v1/work-capture/fixtures/cases.json +184 -0
- package/contracts/debrief/v1/work-capture/fixtures/review.json +184 -0
- package/contracts/debrief/v1/work-capture/review-request.schema.json +41 -0
- package/contracts/debrief/v1/work-capture/work-case.schema.json +190 -0
- package/contracts/debrief/v2/README.md +71 -0
- package/contracts/debrief/v2/input-pin.json +66 -0
- package/contracts/debrief/v2/intervention-upstream.md +40 -0
- package/contracts/debrief/v2/work-signals-upstream.md +22 -0
- package/contracts/dispatch-control/v1/README.md +109 -0
- package/contracts/dispatch-control/v1/request.schema.json +19 -0
- package/contracts/effect-profile/v1/README.md +124 -0
- package/contracts/execution-retention/v1/README.md +109 -0
- package/contracts/execution-retention/v2/README.md +148 -0
- package/contracts/execution-retention/v2/fixtures/interrupted-native-bytes.json +91 -0
- package/contracts/execution-retention/v2/fixtures/missing-native-session.json +91 -0
- package/contracts/execution-retention/v2/fixtures/native-file-unknown-branch.json +91 -0
- package/contracts/execution-retention/v2/fixtures/native-id-without-bytes.json +91 -0
- package/contracts/execution-retention/v2/fixtures/native-live-branch.json +90 -0
- package/contracts/execution-retention/v2/fixtures/replaced-native-session.json +92 -0
- package/contracts/execution-retention/v2/fixtures/result-82c620cdfeb8ca6b858cf653d7f5e2eedca5315d6244216d60e8b36f03223d6a.bin +1 -0
- package/contracts/execution-retention/v2/fixtures/session-2ec1f134bc739984434f7fca689e1c3d1c39fce2dcadae709e9b6faedfcc4ac7.bin +3 -0
- package/contracts/execution-retention/v2/fixtures/session-9bdb4bd16a901da31580acf056bda525225259e15ec409e0410480109011772f.bin +3 -0
- package/contracts/execution-retention/v2/manifest.schema.json +858 -0
- package/contracts/experiment/v1/README.md +164 -0
- package/contracts/factory-order/v1/README.md +151 -0
- package/contracts/factory-order/v1/adoption-pin.json +8 -0
- package/contracts/intent-control/v1/README.md +135 -0
- package/contracts/intent-control/v1/request.schema.json +1096 -0
- package/contracts/intent-control/v2/README.md +34 -0
- package/contracts/intent-control/v2/request.schema.json +1070 -0
- package/contracts/ledger/v4/README.md +241 -0
- package/contracts/ledger/v4/fixtures/layout-options.json +428 -0
- package/contracts/ledger/v4/fixtures/work-acceptance.json +57 -0
- package/contracts/ledger/v4/fixtures/work-occurrence.json +44 -0
- package/contracts/ledger/v4/fixtures/work-revision.json +26 -0
- package/contracts/ledger/v4/fixtures/work-snapshot.json +73 -0
- package/contracts/ledger/v4/ledger-event.schema.json +162 -0
- package/contracts/ordinary-control/v1/README.md +59 -0
- package/contracts/producer-ipc/v1/README.md +137 -0
- package/dist/check-runner.d.ts +5 -0
- package/dist/check-runner.d.ts.map +1 -1
- package/dist/check-runner.js +16 -2
- package/dist/check-runner.js.map +1 -1
- package/dist/cli.d.ts +3 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +49 -3
- package/dist/cli.js.map +1 -1
- package/dist/control-journal.d.ts +20 -0
- package/dist/control-journal.d.ts.map +1 -0
- package/dist/control-journal.js +73 -0
- package/dist/control-journal.js.map +1 -0
- package/dist/daily-dashboard-host.d.ts +99 -0
- package/dist/daily-dashboard-host.d.ts.map +1 -0
- package/dist/daily-dashboard-host.js +71 -0
- package/dist/daily-dashboard-host.js.map +1 -0
- package/dist/daily-view-input.d.ts +44 -0
- package/dist/daily-view-input.d.ts.map +1 -0
- package/dist/daily-view-input.js +68 -0
- package/dist/daily-view-input.js.map +1 -0
- package/dist/daily-view-render.d.ts +4 -0
- package/dist/daily-view-render.d.ts.map +1 -0
- package/dist/daily-view-render.js +41 -0
- package/dist/daily-view-render.js.map +1 -0
- package/dist/daily-view.d.ts +74 -0
- package/dist/daily-view.d.ts.map +1 -0
- package/dist/daily-view.js +170 -0
- package/dist/daily-view.js.map +1 -0
- package/dist/dashboard-cli.d.ts +26 -1
- package/dist/dashboard-cli.d.ts.map +1 -1
- package/dist/dashboard-cli.js +128 -3
- package/dist/dashboard-cli.js.map +1 -1
- package/dist/dashboard-harness.d.ts +22 -0
- package/dist/dashboard-harness.d.ts.map +1 -0
- package/dist/dashboard-harness.js +89 -0
- package/dist/dashboard-harness.js.map +1 -0
- package/dist/dashboard-herdr.d.ts.map +1 -1
- package/dist/dashboard-herdr.js +7 -2
- package/dist/dashboard-herdr.js.map +1 -1
- package/dist/dashboard-host-contract.d.ts +116 -0
- package/dist/dashboard-host-contract.d.ts.map +1 -0
- package/dist/dashboard-host-contract.js +14 -0
- package/dist/dashboard-host-contract.js.map +1 -0
- package/dist/dashboard-host-transport.d.ts +16 -0
- package/dist/dashboard-host-transport.d.ts.map +1 -0
- package/dist/dashboard-host-transport.js +124 -0
- package/dist/dashboard-host-transport.js.map +1 -0
- package/dist/dashboard-host.d.ts +203 -0
- package/dist/dashboard-host.d.ts.map +1 -0
- package/dist/dashboard-host.js +376 -0
- package/dist/dashboard-host.js.map +1 -0
- package/dist/dashboard-observation.d.ts +28 -0
- package/dist/dashboard-observation.d.ts.map +1 -0
- package/dist/dashboard-observation.js +86 -0
- package/dist/dashboard-observation.js.map +1 -0
- package/dist/debrief-contract.d.ts +65 -0
- package/dist/debrief-contract.d.ts.map +1 -0
- package/dist/debrief-contract.js +151 -0
- package/dist/debrief-contract.js.map +1 -0
- package/dist/debrief-fixture.d.ts +16 -0
- package/dist/debrief-fixture.d.ts.map +1 -0
- package/dist/debrief-fixture.js +41 -0
- package/dist/debrief-fixture.js.map +1 -0
- package/dist/debrief-host.d.ts +59 -0
- package/dist/debrief-host.d.ts.map +1 -0
- package/dist/debrief-host.js +49 -0
- package/dist/debrief-host.js.map +1 -0
- package/dist/debrief-render.d.ts +5 -0
- package/dist/debrief-render.d.ts.map +1 -0
- package/dist/debrief-render.js +65 -0
- package/dist/debrief-render.js.map +1 -0
- package/dist/debrief.d.ts +90 -0
- package/dist/debrief.d.ts.map +1 -0
- package/dist/debrief.js +263 -0
- package/dist/debrief.js.map +1 -0
- package/dist/delegate-types.d.ts +2 -0
- package/dist/delegate-types.d.ts.map +1 -1
- package/dist/delegate-types.js.map +1 -1
- package/dist/delegate.d.ts.map +1 -1
- package/dist/delegate.js +1 -0
- package/dist/delegate.js.map +1 -1
- package/dist/dispatch-control.d.ts +45 -0
- package/dist/dispatch-control.d.ts.map +1 -0
- package/dist/dispatch-control.js +76 -0
- package/dist/dispatch-control.js.map +1 -0
- package/dist/effect-profile-runtime.d.ts +40 -0
- package/dist/effect-profile-runtime.d.ts.map +1 -0
- package/dist/effect-profile-runtime.js +51 -0
- package/dist/effect-profile-runtime.js.map +1 -0
- package/dist/effect-profile.d.ts +40 -0
- package/dist/effect-profile.d.ts.map +1 -0
- package/dist/effect-profile.js +152 -0
- package/dist/effect-profile.js.map +1 -0
- package/dist/execution-retention.d.ts +90 -0
- package/dist/execution-retention.d.ts.map +1 -0
- package/dist/execution-retention.js +247 -0
- package/dist/execution-retention.js.map +1 -0
- package/dist/experiment-contract.d.ts +56 -0
- package/dist/experiment-contract.d.ts.map +1 -0
- package/dist/experiment-contract.js +80 -0
- package/dist/experiment-contract.js.map +1 -0
- package/dist/experiment-state.d.ts +22 -0
- package/dist/experiment-state.d.ts.map +1 -0
- package/dist/experiment-state.js +79 -0
- package/dist/experiment-state.js.map +1 -0
- package/dist/experiment-store.d.ts +30 -0
- package/dist/experiment-store.d.ts.map +1 -0
- package/dist/experiment-store.js +147 -0
- package/dist/experiment-store.js.map +1 -0
- package/dist/experiment.d.ts +65 -0
- package/dist/experiment.d.ts.map +1 -0
- package/dist/experiment.js +357 -0
- package/dist/experiment.js.map +1 -0
- package/dist/factory-contract.d.ts +79 -0
- package/dist/factory-contract.d.ts.map +1 -0
- package/dist/factory-contract.js +89 -0
- package/dist/factory-contract.js.map +1 -0
- package/dist/factory-migration.d.ts +20 -0
- package/dist/factory-migration.d.ts.map +1 -0
- package/dist/factory-migration.js +55 -0
- package/dist/factory-migration.js.map +1 -0
- package/dist/factory-order.d.ts +848 -0
- package/dist/factory-order.d.ts.map +1 -0
- package/dist/factory-order.js +83 -0
- package/dist/factory-order.js.map +1 -0
- package/dist/factory-registry.d.ts +121 -0
- package/dist/factory-registry.d.ts.map +1 -0
- package/dist/factory-registry.js +133 -0
- package/dist/factory-registry.js.map +1 -0
- package/dist/file-lock.d.ts +8 -2
- package/dist/file-lock.d.ts.map +1 -1
- package/dist/file-lock.js +78 -19
- package/dist/file-lock.js.map +1 -1
- package/dist/herdr-poll.d.ts +8 -0
- package/dist/herdr-poll.d.ts.map +1 -1
- package/dist/herdr-poll.js +23 -0
- package/dist/herdr-poll.js.map +1 -1
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/intent-application.d.ts +17 -0
- package/dist/intent-application.d.ts.map +1 -0
- package/dist/intent-application.js +113 -0
- package/dist/intent-application.js.map +1 -0
- package/dist/intent-control.d.ts +201 -0
- package/dist/intent-control.d.ts.map +1 -0
- package/dist/intent-control.js +110 -0
- package/dist/intent-control.js.map +1 -0
- package/dist/intent-scheduling.d.ts +9 -0
- package/dist/intent-scheduling.d.ts.map +1 -0
- package/dist/intent-scheduling.js +15 -0
- package/dist/intent-scheduling.js.map +1 -0
- package/dist/ledger-append.d.ts +15 -0
- package/dist/ledger-append.d.ts.map +1 -0
- package/dist/ledger-append.js +94 -0
- package/dist/ledger-append.js.map +1 -0
- package/dist/ledger.d.ts +11 -0
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +3 -7
- package/dist/ledger.js.map +1 -1
- package/dist/native-session-target.d.ts +9 -0
- package/dist/native-session-target.d.ts.map +1 -0
- package/dist/native-session-target.js +29 -0
- package/dist/native-session-target.js.map +1 -0
- package/dist/native-session.d.ts +59 -0
- package/dist/native-session.d.ts.map +1 -0
- package/dist/native-session.js +154 -0
- package/dist/native-session.js.map +1 -0
- package/dist/order-schedule.d.ts +37 -0
- package/dist/order-schedule.d.ts.map +1 -0
- package/dist/order-schedule.js +81 -0
- package/dist/order-schedule.js.map +1 -0
- package/dist/ordinary-children.d.ts +52 -0
- package/dist/ordinary-children.d.ts.map +1 -0
- package/dist/ordinary-children.js +93 -0
- package/dist/ordinary-children.js.map +1 -0
- package/dist/producer-ipc-contract.d.ts +29 -0
- package/dist/producer-ipc-contract.d.ts.map +1 -0
- package/dist/producer-ipc-contract.js +29 -0
- package/dist/producer-ipc-contract.js.map +1 -0
- package/dist/producer-ipc.d.ts +55 -0
- package/dist/producer-ipc.d.ts.map +1 -0
- package/dist/producer-ipc.js +143 -0
- package/dist/producer-ipc.js.map +1 -0
- package/dist/resource-budget.d.ts +334 -0
- package/dist/resource-budget.d.ts.map +1 -0
- package/dist/resource-budget.js +415 -0
- package/dist/resource-budget.js.map +1 -0
- package/dist/retention-contract.d.ts +73 -0
- package/dist/retention-contract.d.ts.map +1 -0
- package/dist/retention-contract.js +104 -0
- package/dist/retention-contract.js.map +1 -0
- package/dist/retention-json.d.ts +3 -0
- package/dist/retention-json.d.ts.map +1 -0
- package/dist/retention-json.js +32 -0
- package/dist/retention-json.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 +16 -2
- package/dist/run-child.js.map +1 -1
- package/dist/run-herdr.d.ts +6 -0
- package/dist/run-herdr.d.ts.map +1 -1
- package/dist/run-herdr.js +19 -6
- package/dist/run-herdr.js.map +1 -1
- package/dist/vendor/adoption.d.ts +129 -0
- package/dist/vendor/adoption.d.ts.map +1 -0
- package/dist/vendor/adoption.js +102 -0
- package/dist/vendor/adoption.js.map +1 -0
- package/dist/work-command.d.ts +30 -0
- package/dist/work-command.d.ts.map +1 -0
- package/dist/work-command.js +148 -0
- package/dist/work-command.js.map +1 -0
- package/dist/work-ledger-destination.d.ts +20 -0
- package/dist/work-ledger-destination.d.ts.map +1 -0
- package/dist/work-ledger-destination.js +131 -0
- package/dist/work-ledger-destination.js.map +1 -0
- package/dist/work-ledger-json.d.ts +25 -0
- package/dist/work-ledger-json.d.ts.map +1 -0
- package/dist/work-ledger-json.js +267 -0
- package/dist/work-ledger-json.js.map +1 -0
- package/dist/work-ledger-occurrences.d.ts +227 -0
- package/dist/work-ledger-occurrences.d.ts.map +1 -0
- package/dist/work-ledger-occurrences.js +115 -0
- package/dist/work-ledger-occurrences.js.map +1 -0
- package/dist/work-ledger-projection.d.ts +295 -0
- package/dist/work-ledger-projection.d.ts.map +1 -0
- package/dist/work-ledger-projection.js +237 -0
- package/dist/work-ledger-projection.js.map +1 -0
- package/dist/work-ledger-snapshot.d.ts +29 -0
- package/dist/work-ledger-snapshot.d.ts.map +1 -0
- package/dist/work-ledger-snapshot.js +267 -0
- package/dist/work-ledger-snapshot.js.map +1 -0
- package/dist/work-ledger-types.d.ts +261 -0
- package/dist/work-ledger-types.d.ts.map +1 -0
- package/dist/work-ledger-types.js +13 -0
- package/dist/work-ledger-types.js.map +1 -0
- package/dist/work-ledger-validation.d.ts +14 -0
- package/dist/work-ledger-validation.d.ts.map +1 -0
- package/dist/work-ledger-validation.js +226 -0
- package/dist/work-ledger-validation.js.map +1 -0
- package/dist/work-ledger.d.ts +43 -0
- package/dist/work-ledger.d.ts.map +1 -0
- package/dist/work-ledger.js +225 -0
- package/dist/work-ledger.js.map +1 -0
- package/extensions/daily-dashboard-session.ts +30 -0
- package/extensions/daily-work-session.ts +20 -0
- package/extensions/delegate-chain.ts +1 -0
- package/extensions/delegation-native.ts +12 -0
- package/extensions/delegation.ts +25 -11
- package/extensions/execute-child.ts +44 -5
- package/extensions/executor-session.ts +9 -0
- package/extensions/grants-command.ts +14 -15
- package/extensions/grants-connected-command.ts +19 -0
- package/extensions/grants.ts +26 -2
- package/extensions/ordinary-runtime.ts +14 -0
- package/extensions/primary-shadow.ts +11 -0
- package/extensions/run-delegation.ts +9 -7
- package/extensions/session.ts +10 -20
- package/extensions/work-runtime.ts +44 -0
- package/package.json +78 -4
- package/src/check-runner.ts +20 -2
- package/src/cli.ts +41 -4
- package/src/control-journal.ts +26 -0
- package/src/daily-dashboard-host.ts +49 -0
- package/src/daily-view-input.ts +59 -0
- package/src/daily-view-render.ts +44 -0
- package/src/daily-view.ts +152 -0
- package/src/dashboard-cli.ts +108 -4
- package/src/dashboard-harness.ts +37 -0
- package/src/dashboard-herdr.ts +7 -2
- package/src/dashboard-host-contract.ts +45 -0
- package/src/dashboard-host-transport.ts +40 -0
- package/src/dashboard-host.ts +191 -0
- package/src/dashboard-observation.ts +45 -0
- package/src/debrief-contract.ts +127 -0
- package/src/debrief-fixture.ts +29 -0
- package/src/debrief-host.ts +53 -0
- package/src/debrief-render.ts +49 -0
- package/src/debrief.ts +198 -0
- package/src/delegate-types.ts +2 -0
- package/src/delegate.ts +1 -0
- package/src/dispatch-control.ts +81 -0
- package/src/effect-profile-runtime.ts +46 -0
- package/src/effect-profile.ts +129 -0
- package/src/execution-retention.ts +230 -0
- package/src/experiment-contract.ts +66 -0
- package/src/experiment-state.ts +44 -0
- package/src/experiment-store.ts +98 -0
- package/src/experiment.ts +231 -0
- package/src/factory-contract.ts +47 -0
- package/src/factory-migration.ts +29 -0
- package/src/factory-order.ts +56 -0
- package/src/factory-registry.ts +60 -0
- package/src/file-lock.ts +47 -11
- package/src/herdr-poll.ts +18 -0
- package/src/index.ts +32 -0
- package/src/intent-application.ts +90 -0
- package/src/intent-control.ts +107 -0
- package/src/intent-scheduling.ts +14 -0
- package/src/ledger-append.ts +72 -0
- package/src/ledger.ts +3 -9
- package/src/native-session-target.ts +23 -0
- package/src/native-session.ts +139 -0
- package/src/order-schedule.ts +42 -0
- package/src/ordinary-children.ts +59 -0
- package/src/producer-ipc-contract.ts +26 -0
- package/src/producer-ipc.ts +107 -0
- package/src/resource-budget.ts +349 -0
- package/src/retention-contract.ts +84 -0
- package/src/retention-json.ts +20 -0
- package/src/run-child.ts +13 -2
- package/src/run-herdr.ts +11 -7
- package/src/vendor/adoption.ts +109 -0
- package/src/work-command.ts +179 -0
- package/src/work-ledger-destination.ts +113 -0
- package/src/work-ledger-json.ts +228 -0
- package/src/work-ledger-occurrences.ts +99 -0
- package/src/work-ledger-projection.ts +262 -0
- package/src/work-ledger-snapshot.ts +246 -0
- package/src/work-ledger-types.ts +229 -0
- package/src/work-ledger-validation.ts +174 -0
- package/src/work-ledger.ts +186 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Delegation } from "../src/delegate.ts";
|
|
2
|
+
import { retentionConfigurationDigest } from "../src/execution-retention.ts";
|
|
3
|
+
import { appendDeclaredWorkOccurrence } from "../src/work-command.ts";
|
|
4
|
+
import type { WorkOccurrencePayload } from "../src/work-ledger.ts";
|
|
5
|
+
import type { GrantsSession } from "./session.ts";
|
|
6
|
+
import type { PreparedWorkspace } from "./workspace-runtime.ts";
|
|
7
|
+
|
|
8
|
+
export async function beginDeclaredWorkAttempt(input: {
|
|
9
|
+
session: GrantsSession;
|
|
10
|
+
plan: Delegation;
|
|
11
|
+
childId: string;
|
|
12
|
+
executionId: string;
|
|
13
|
+
parentExecutionId: string | null;
|
|
14
|
+
toolCallId?: string;
|
|
15
|
+
preparedWorkspace?: PreparedWorkspace;
|
|
16
|
+
configuredTimeoutMs: number;
|
|
17
|
+
startedAt: Date;
|
|
18
|
+
}): Promise<null | { finish(state: Extract<WorkOccurrencePayload["state"], "completed" | "failed">): Promise<void> }> {
|
|
19
|
+
const state = input.session.declaredWork;
|
|
20
|
+
if (!state) return null;
|
|
21
|
+
const argument = (flag: string): string | null => {
|
|
22
|
+
const index = input.plan.args.indexOf(flag);
|
|
23
|
+
return index >= 0 ? input.plan.args[index + 1] ?? null : null;
|
|
24
|
+
};
|
|
25
|
+
const identity = {
|
|
26
|
+
executionId: input.executionId,
|
|
27
|
+
parentExecutionId: input.parentExecutionId,
|
|
28
|
+
childId: input.childId,
|
|
29
|
+
variantId: input.plan.correlation?.context_id ?? null,
|
|
30
|
+
toolCallId: input.toolCallId ?? null,
|
|
31
|
+
taskId: input.plan.correlation?.task_id ?? input.plan.taskDigest,
|
|
32
|
+
workspaceId: input.preparedWorkspace?.workspace.workspaceId ?? input.plan.correlation?.workspace_id ?? null,
|
|
33
|
+
definitionDigest: input.plan.definitionDigest?.sha256 ?? null,
|
|
34
|
+
configurationDigest: retentionConfigurationDigest({
|
|
35
|
+
args: input.plan.args,
|
|
36
|
+
effective: input.plan.effective,
|
|
37
|
+
timeoutMs: input.configuredTimeoutMs,
|
|
38
|
+
}),
|
|
39
|
+
modelId: argument("--model"),
|
|
40
|
+
effortId: argument("--thinking"),
|
|
41
|
+
};
|
|
42
|
+
await appendDeclaredWorkOccurrence(state, { ...identity, now: input.startedAt }, "starting");
|
|
43
|
+
return { finish: (terminal) => appendDeclaredWorkOccurrence(state, { ...identity, now: new Date() }, terminal) };
|
|
44
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-daddy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.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",
|
|
@@ -37,6 +37,67 @@
|
|
|
37
37
|
"types": "./dist/ledger.d.ts",
|
|
38
38
|
"default": "./dist/ledger.js"
|
|
39
39
|
},
|
|
40
|
+
"./work-ledger": {
|
|
41
|
+
"types": "./dist/work-ledger.d.ts",
|
|
42
|
+
"default": "./dist/work-ledger.js"
|
|
43
|
+
},
|
|
44
|
+
"./factory-order": {
|
|
45
|
+
"types": "./dist/factory-order.d.ts",
|
|
46
|
+
"default": "./dist/factory-order.js"
|
|
47
|
+
},
|
|
48
|
+
"./experiment": {
|
|
49
|
+
"types": "./dist/experiment.d.ts",
|
|
50
|
+
"default": "./dist/experiment.js"
|
|
51
|
+
},
|
|
52
|
+
"./dashboard-harness": {
|
|
53
|
+
"types": "./dist/dashboard-harness.d.ts",
|
|
54
|
+
"default": "./dist/dashboard-harness.js"
|
|
55
|
+
},
|
|
56
|
+
"./dashboard-host": {
|
|
57
|
+
"types": "./dist/dashboard-host.d.ts",
|
|
58
|
+
"default": "./dist/dashboard-host.js"
|
|
59
|
+
},
|
|
60
|
+
"./dashboard-host-transport": {
|
|
61
|
+
"types": "./dist/dashboard-host-transport.d.ts",
|
|
62
|
+
"default": "./dist/dashboard-host-transport.js"
|
|
63
|
+
},
|
|
64
|
+
"./debrief-host": {
|
|
65
|
+
"types": "./dist/debrief-host.d.ts",
|
|
66
|
+
"default": "./dist/debrief-host.js"
|
|
67
|
+
},
|
|
68
|
+
"./contracts/debrief/v2/*": "./contracts/debrief/v2/*",
|
|
69
|
+
"./debrief": {
|
|
70
|
+
"types": "./dist/debrief.d.ts",
|
|
71
|
+
"default": "./dist/debrief.js"
|
|
72
|
+
},
|
|
73
|
+
"./daily-view": {
|
|
74
|
+
"types": "./dist/daily-view.d.ts",
|
|
75
|
+
"default": "./dist/daily-view.js"
|
|
76
|
+
},
|
|
77
|
+
"./intent-control": {
|
|
78
|
+
"types": "./dist/intent-control.d.ts",
|
|
79
|
+
"default": "./dist/intent-control.js"
|
|
80
|
+
},
|
|
81
|
+
"./dispatch-control": {
|
|
82
|
+
"types": "./dist/dispatch-control.d.ts",
|
|
83
|
+
"default": "./dist/dispatch-control.js"
|
|
84
|
+
},
|
|
85
|
+
"./resource-budget": {
|
|
86
|
+
"types": "./dist/resource-budget.d.ts",
|
|
87
|
+
"default": "./dist/resource-budget.js"
|
|
88
|
+
},
|
|
89
|
+
"./producer-ipc": {
|
|
90
|
+
"types": "./dist/producer-ipc.d.ts",
|
|
91
|
+
"default": "./dist/producer-ipc.js"
|
|
92
|
+
},
|
|
93
|
+
"./effect-profile": {
|
|
94
|
+
"types": "./dist/effect-profile.d.ts",
|
|
95
|
+
"default": "./dist/effect-profile.js"
|
|
96
|
+
},
|
|
97
|
+
"./execution-retention": {
|
|
98
|
+
"types": "./dist/execution-retention.d.ts",
|
|
99
|
+
"default": "./dist/execution-retention.js"
|
|
100
|
+
},
|
|
40
101
|
"./spawn": {
|
|
41
102
|
"types": "./dist/spawn.d.ts",
|
|
42
103
|
"default": "./dist/spawn.js"
|
|
@@ -137,6 +198,19 @@
|
|
|
137
198
|
"types": "./dist/dashboard-render.d.ts",
|
|
138
199
|
"default": "./dist/dashboard-render.js"
|
|
139
200
|
},
|
|
201
|
+
"./contracts/factory-order/v1/*": "./contracts/factory-order/v1/*",
|
|
202
|
+
"./contracts/experiment/v1/*": "./contracts/experiment/v1/*",
|
|
203
|
+
"./contracts/producer-ipc/v1/*": "./contracts/producer-ipc/v1/*",
|
|
204
|
+
"./contracts/debrief/v1/*": "./contracts/debrief/v1/*",
|
|
205
|
+
"./contracts/intent-control/v2/request.schema.json": "./contracts/intent-control/v2/request.schema.json",
|
|
206
|
+
"./contracts/intent-control/v1/request.schema.json": "./contracts/intent-control/v1/request.schema.json",
|
|
207
|
+
"./contracts/dispatch-control/v1/request.schema.json": "./contracts/dispatch-control/v1/request.schema.json",
|
|
208
|
+
"./contracts/daily-view/v1/fixtures/*": "./contracts/daily-view/v1/fixtures/*",
|
|
209
|
+
"./contracts/daily-view/v1/p03/projection.schema.json": "./contracts/daily-view/v1/p03/projection.schema.json",
|
|
210
|
+
"./contracts/execution-retention/v2/manifest.schema.json": "./contracts/execution-retention/v2/manifest.schema.json",
|
|
211
|
+
"./contracts/execution-retention/v2/fixtures/*": "./contracts/execution-retention/v2/fixtures/*",
|
|
212
|
+
"./contracts/ledger/v4/ledger-event.schema.json": "./contracts/ledger/v4/ledger-event.schema.json",
|
|
213
|
+
"./contracts/ledger/v4/fixtures/*.json": "./contracts/ledger/v4/fixtures/*.json",
|
|
140
214
|
"./contracts/ledger/v3/ledger-event.schema.json": "./contracts/ledger/v3/ledger-event.schema.json",
|
|
141
215
|
"./contracts/ledger/v3/fixtures/*.json": "./contracts/ledger/v3/fixtures/*.json",
|
|
142
216
|
"./contracts/ledger/v2/ledger-event.schema.json": "./contracts/ledger/v2/ledger-event.schema.json",
|
|
@@ -159,13 +233,13 @@
|
|
|
159
233
|
"scripts": {
|
|
160
234
|
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
161
235
|
"typecheck": "tsc -p tsconfig.check.json",
|
|
162
|
-
"test": "node
|
|
236
|
+
"test": "node scripts/unit-tests.ts",
|
|
163
237
|
"test:integration": "node --test test-integration/*.it.ts",
|
|
164
238
|
"test:integration:ci": "node --test test-integration/approval.it.ts test-integration/governance.it.ts",
|
|
165
239
|
"contracts:generate": "node scripts/generate-ledger-v3-contract.ts",
|
|
240
|
+
"contracts:generate:v4": "node scripts/generate-ledger-v4-contract.ts contracts/ledger/v4",
|
|
166
241
|
"prepack": "npm run build",
|
|
167
|
-
"test:smoke": "node scripts/smoke-installed.mjs"
|
|
168
|
-
"test:mutation": "node scripts/mutation-audit.mjs"
|
|
242
|
+
"test:smoke": "node scripts/smoke-installed.mjs"
|
|
169
243
|
},
|
|
170
244
|
"main": "./dist/index.js",
|
|
171
245
|
"types": "./dist/index.d.ts",
|
package/src/check-runner.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { beginExecutionRetention, retentionConfigurationDigest, type RetentionStatus } from "./execution-retention.ts";
|
|
2
3
|
import { chmod, mkdtemp, readFile, realpath, rm, stat, writeFile } from "node:fs/promises";
|
|
3
4
|
import { tmpdir } from "node:os";
|
|
4
5
|
import { basename, isAbsolute, join } from "node:path";
|
|
@@ -128,11 +129,15 @@ export async function runNamedCheck(input: {
|
|
|
128
129
|
signal?: AbortSignal;
|
|
129
130
|
leaseDir?: string;
|
|
130
131
|
ledgerPath?: string;
|
|
132
|
+
/** Explicit host archive boundary; defaults to PI_GRANTS_EXECUTION_ARCHIVE, never correlation. */
|
|
133
|
+
retentionDirectory?: string;
|
|
134
|
+
toolCallId?: string;
|
|
131
135
|
}): Promise<{
|
|
132
136
|
output: string;
|
|
133
137
|
exitCode: number | null;
|
|
134
138
|
signal: NodeJS.Signals | null;
|
|
135
139
|
receipt: CheckReceipt;
|
|
140
|
+
retention: RetentionStatus;
|
|
136
141
|
}> {
|
|
137
142
|
if (input.registry?.version !== 1 || !input.registry.checks || typeof input.registry.checks !== "object") {
|
|
138
143
|
throw new GovernanceRefusal(refusal("CHECK_CONFIGURATION_INVALID", "check registry must contain {version:1, checks:{...}}"));
|
|
@@ -159,11 +164,17 @@ export async function runNamedCheck(input: {
|
|
|
159
164
|
const executionId = newExecutionId();
|
|
160
165
|
const parentExecutionId = null;
|
|
161
166
|
const correlation = normaliseCorrelation(input.correlation);
|
|
167
|
+
const retention = beginExecutionRetention({ executionId, parentExecutionId, childId: ownerId,
|
|
168
|
+
toolCallId: input.toolCallId ?? null, executor: "check", taskDigest: null, definitionDigest: null,
|
|
169
|
+
configurationDigest: retentionConfigurationDigest({ executable, argv: definition.argv, access,
|
|
170
|
+
timeoutMs: definition.timeout_ms ?? null, maxOutputBytes: definition.max_output_bytes ?? null }),
|
|
171
|
+
workspaceId: input.workspace.workspaceId }, input.retentionDirectory);
|
|
162
172
|
let lease: Awaited<ReturnType<typeof acquireWorkspaceLease>> | undefined;
|
|
163
173
|
let stagedDir: string | undefined;
|
|
164
174
|
/** Failures of best-effort RECORDS, reported alongside whatever actually happened — never instead of it. */
|
|
165
175
|
const notes: string[] = [];
|
|
166
176
|
let releaseReason = "failed";
|
|
177
|
+
let observedOutcome = { code: null as number | null, signal: null as string | null, timedOut: false, aborted: false, truncated: false };
|
|
167
178
|
return runWithFinalizers(async () => {
|
|
168
179
|
try {
|
|
169
180
|
try {
|
|
@@ -228,8 +239,11 @@ export async function runNamedCheck(input: {
|
|
|
228
239
|
command: "setpriv", args: ["--pdeathsig", "KILL", "--", stagedExecutable, ...definition.argv],
|
|
229
240
|
env: buildCheckEnvironment(definition, input.inheritedEnv), cwd: input.workspace.root,
|
|
230
241
|
signal: checkSignal, timeoutMs: definition.timeout_ms, maxOutputBytes: definition.max_output_bytes,
|
|
231
|
-
onSpawn: (pid) => lease!.attachProcess(pid),
|
|
242
|
+
onSpawn: (pid) => { lease!.attachProcess(pid); retention.native({ pid }); },
|
|
243
|
+
onObservation: (stream, bytes) => retention.capture(stream, bytes),
|
|
232
244
|
});
|
|
245
|
+
observedOutcome = { code: result.code, signal: result.signal ?? null, timedOut: result.timedOut, aborted: result.aborted, truncated: result.truncated };
|
|
246
|
+
retention.capture("result", Buffer.from(result.text), true);
|
|
233
247
|
const ended = new Date();
|
|
234
248
|
releaseReason = input.signal?.aborted ? "cancelled" : result.timedOut ? "timeout" : result.aborted ? "failed" : "completed";
|
|
235
249
|
// Every throw below re-stamps this. Without that, a check refused for CHECK_IDENTITY_MISMATCH still
|
|
@@ -285,6 +299,7 @@ export async function runNamedCheck(input: {
|
|
|
285
299
|
head_sha: before.headSha, tree_sha: before.treeSha, ...(correlation ? { correlation } : {}),
|
|
286
300
|
};
|
|
287
301
|
const receipt: CheckReceipt = { receipt_id: receiptId(body), ...body };
|
|
302
|
+
retention.capture("checkReceipt", Buffer.from(JSON.stringify(receipt) + "\n"), true);
|
|
288
303
|
if (input.ledgerPath) {
|
|
289
304
|
await appendLedgerEvent(
|
|
290
305
|
{ path: input.ledgerPath, strict: true },
|
|
@@ -295,8 +310,11 @@ export async function runNamedCheck(input: {
|
|
|
295
310
|
}),
|
|
296
311
|
);
|
|
297
312
|
}
|
|
298
|
-
|
|
313
|
+
retention.finish({ code: result.code, signal: result.signal ?? null, timedOut: result.timedOut,
|
|
314
|
+
aborted: result.aborted, truncated: result.truncated, failed: result.code !== 0 });
|
|
315
|
+
return { output: result.text, exitCode: result.code, signal: result.signal ?? null, receipt, retention: retention.status() };
|
|
299
316
|
} catch (error) {
|
|
317
|
+
retention.finish({ ...observedOutcome, aborted: observedOutcome.aborted || Boolean(input.signal?.aborted), failed: true });
|
|
300
318
|
// The run did not complete, so the release must not be recorded as if it had. Without this the
|
|
301
319
|
// `finally` wrote `released / completed` as the LAST lease event for a refused check, so the trail
|
|
302
320
|
// still read like a clean run and `verifyLedger` still counted a clean release — the R-114 fix put a
|
package/src/cli.ts
CHANGED
|
@@ -21,12 +21,15 @@ import { UnsafeGrantError } from "./grant-env.ts";
|
|
|
21
21
|
import { applyInit, countDeclaring, planInit, type InitPlan } from "./init.ts";
|
|
22
22
|
import { registeredWorkspaceIds } from "./workspace.ts";
|
|
23
23
|
import { discoverSkillPackages, skillPackageRoots, type RefusedSkill, type SkillPackage } from "./skill-packages.ts";
|
|
24
|
+
import { declareWork } from "./work-command.ts";
|
|
24
25
|
|
|
25
26
|
const USAGE = `pi-daddy — capability governance for pi sub-agents
|
|
26
27
|
|
|
27
28
|
Usage:
|
|
28
29
|
pi-daddy init [--force] [--dir <path>] scaffold .pi/skills/ and .pi/grants.env from installed
|
|
29
30
|
packages that declare skills (package.json "pi": {"skills": …})
|
|
31
|
+
pi-daddy work add --id <id> --outcome <text> [--dir <path>]
|
|
32
|
+
declare one current obligation for ordinary delegation
|
|
30
33
|
pi-daddy --help | --version
|
|
31
34
|
|
|
32
35
|
init copies each declared SKILL.md into .pi/skills/ and writes a grant naming exactly what those files
|
|
@@ -38,9 +41,11 @@ placeholder and stays unspawnable until you fill it in. Capabilities that can ch
|
|
|
38
41
|
It never rewrites .pi/grants.env — delete that file if you want it regenerated.`;
|
|
39
42
|
|
|
40
43
|
export interface ParsedArgs {
|
|
41
|
-
command: "init" | "help" | "version";
|
|
44
|
+
command: "init" | "work-add" | "help" | "version";
|
|
42
45
|
dir?: string;
|
|
43
46
|
force: boolean;
|
|
47
|
+
id?: string;
|
|
48
|
+
outcome?: string;
|
|
44
49
|
/** Non-empty means refuse: argv said something this program does not understand. */
|
|
45
50
|
errors: string[];
|
|
46
51
|
}
|
|
@@ -58,16 +63,28 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
58
63
|
if (args.includes("--help") || args.includes("-h")) return { command: "help", force: false, errors: [] };
|
|
59
64
|
if (args.includes("--version") || args.includes("-v")) return { command: "version", force: false, errors: [] };
|
|
60
65
|
|
|
61
|
-
const [command, ...
|
|
62
|
-
if (command !== "init") return { command: "help", force: false, errors: [`unknown command "${command}"`] };
|
|
66
|
+
const [command, ...tail] = args;
|
|
67
|
+
if (command !== "init" && command !== "work") return { command: "help", force: false, errors: [`unknown command "${command}"`] };
|
|
68
|
+
const work = command === "work";
|
|
69
|
+
const rest = work && tail[0] === "add" ? tail.slice(1) : tail;
|
|
70
|
+
if (work && tail[0] !== "add") return { command: "work-add", force: false, errors: ["work needs subcommand add"] };
|
|
63
71
|
|
|
64
72
|
const errors: string[] = [];
|
|
65
73
|
let dir: string | undefined;
|
|
66
74
|
let force = false;
|
|
75
|
+
let id: string | undefined, outcome: string | undefined;
|
|
67
76
|
for (let i = 0; i < rest.length; i += 1) {
|
|
68
77
|
const arg = rest[i];
|
|
69
|
-
if (arg === "--force") {
|
|
78
|
+
if (arg === "--force" && !work) {
|
|
70
79
|
force = true;
|
|
80
|
+
} else if (arg === "--id" && work) {
|
|
81
|
+
const value = rest[i + 1];
|
|
82
|
+
if (value === undefined || value.startsWith("-")) errors.push("--id needs a value");
|
|
83
|
+
else { id = value; i += 1; }
|
|
84
|
+
} else if (arg === "--outcome" && work) {
|
|
85
|
+
const value = rest[i + 1];
|
|
86
|
+
if (value === undefined || value.startsWith("-")) errors.push("--outcome needs text");
|
|
87
|
+
else { outcome = value; i += 1; }
|
|
71
88
|
} else if (arg === "--dir") {
|
|
72
89
|
const value = rest[i + 1];
|
|
73
90
|
// A flag is not a path. Without this, `--dir --force` consumed the flag as the directory AND left
|
|
@@ -87,6 +104,11 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
87
104
|
}
|
|
88
105
|
}
|
|
89
106
|
|
|
107
|
+
if (work) {
|
|
108
|
+
if (!id && !errors.some(error => error.startsWith("--id"))) errors.push("--id needs a value");
|
|
109
|
+
if (!outcome && !errors.some(error => error.startsWith("--outcome"))) errors.push("--outcome needs text");
|
|
110
|
+
return { command: "work-add", force: false, errors, ...(id ? { id } : {}), ...(outcome ? { outcome } : {}), ...(dir ? { dir } : {}) };
|
|
111
|
+
}
|
|
90
112
|
return { command: "init", dir, force, errors };
|
|
91
113
|
}
|
|
92
114
|
|
|
@@ -260,6 +282,21 @@ export async function main(argv: string[]): Promise<number> {
|
|
|
260
282
|
console.log(USAGE);
|
|
261
283
|
return 0;
|
|
262
284
|
}
|
|
285
|
+
if (parsed.command === "work-add") {
|
|
286
|
+
const declared = await declareWork({
|
|
287
|
+
cwd: resolvePath(parsed.dir ?? process.cwd()),
|
|
288
|
+
id: parsed.id!,
|
|
289
|
+
outcome: parsed.outcome!,
|
|
290
|
+
});
|
|
291
|
+
console.log(
|
|
292
|
+
`pi-daddy work: declared ${declared.id}\n` +
|
|
293
|
+
` ledger ${declared.ledgerPath}\n` +
|
|
294
|
+
` selection ${declared.statePath}\n\n` +
|
|
295
|
+
`Ordinary governed delegations from this project will record attempts after the Pi extension loads it.\n` +
|
|
296
|
+
`Run /reload in an existing Pi session, then /grants dashboard. Runtime success is not acceptance.`,
|
|
297
|
+
);
|
|
298
|
+
return 0;
|
|
299
|
+
}
|
|
263
300
|
|
|
264
301
|
// ABSOLUTE, always. `readSkill` compares a resolved entry against the package directory, and a relative
|
|
265
302
|
// `--dir` made that comparison false for every entry: every declared skill of every package was reported
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { mkdir, open, lstat } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { withFileLock } from "./file-lock.ts";
|
|
5
|
+
import { runWithFinalizers } from "./finalization.ts";
|
|
6
|
+
import { ownedDirectory, readExperimentFile } from "./experiment-store.ts";
|
|
7
|
+
import { cloneExperiment, closed, experimentHash } from "./experiment-contract.ts";
|
|
8
|
+
import { freezeWork, parseWorkJson } from "./work-ledger-json.ts";
|
|
9
|
+
export interface ControlBinding<T> {version:"control-journal-v1";directory:string;device:string;inode:string;journalDevice:string;journalInode:string;initial:T}
|
|
10
|
+
export async function createControlJournal<T>(directory:string,input:T):Promise<ControlBinding<T>>{
|
|
11
|
+
const initial=cloneExperiment(input);if(typeof directory!=="string"||directory!==resolve(directory)||directory.length>1024||directory.split("/").includes(".pi"))throw new Error("explicit control directory required");
|
|
12
|
+
await ownedDirectory(dirname(directory));await mkdir(directory,{mode:0o700});const root=await ownedDirectory(directory),f=await open(join(directory,"control.jsonl"),"wx",0o600);
|
|
13
|
+
const b=await runWithFinalizers(async()=>{const s=await f.stat({bigint:true});const b={version:"control-journal-v1" as const,directory,device:String(root.dev),inode:String(root.ino),journalDevice:String(s.dev),journalInode:String(s.ino),initial};await f.writeFile(JSON.stringify(b)+"\n");await f.sync();return b;},[{label:"control creation close failed",run:()=>f.close()}]);
|
|
14
|
+
const d=await open(directory,constants.O_RDONLY|constants.O_DIRECTORY);await runWithFinalizers(()=>d.sync(),[{label:"control directory close failed",run:()=>d.close()}]);return freezeWork(b) as unknown as ControlBinding<T>;
|
|
15
|
+
}
|
|
16
|
+
export function controlJournal<T>(input:ControlBinding<T>){
|
|
17
|
+
const b=freezeWork(cloneExperiment(input)) as unknown as ControlBinding<T>;closed(b,["version","directory","device","inode","journalDevice","journalInode","initial"]);
|
|
18
|
+
if(b.version!=="control-journal-v1"||b.directory!==resolve(b.directory)||b.directory.split("/").includes(".pi")||[b.device,b.inode,b.journalDevice,b.journalInode].some(v=>!/^\d+$/.test(v)))throw new Error("invalid control binding");
|
|
19
|
+
const path=join(b.directory,"control.jsonl");
|
|
20
|
+
const check=async()=>{const root=await ownedDirectory(b.directory),file=await lstat(path,{bigint:true});if(String(root.dev)!==b.device||String(root.ino)!==b.inode||String(file.dev)!==b.journalDevice||String(file.ino)!==b.journalInode)throw new Error("control identity changed");};
|
|
21
|
+
const read=async()=>{await check();const bytes=await readExperimentFile(path,512*1024);await check();const text=new TextDecoder("utf8",{fatal:true}).decode(bytes);if(!text.endsWith("\n"))throw new Error("torn control journal");const lines=text.slice(0,-1).split("\n");if(lines.length>257||experimentHash(parseWorkJson(lines[0]))!==experimentHash(b))throw new Error("control header/limit mismatch");let previous=experimentHash(b);const events:Record<string,unknown>[]=[];
|
|
22
|
+
for(const line of lines.slice(1)){const r=parseWorkJson(line) as Record<string,unknown>;closed(r,["sequence","previous","event"]);if(r.sequence!==events.length||r.previous!==previous||!r.event||typeof r.event!=="object"||Array.isArray(r.event))throw new Error("invalid control sequence");previous=experimentHash(r);events.push(r.event as Record<string,unknown>);}return{events,previous,size:bytes.length};};
|
|
23
|
+
return {binding:b,read,async transaction<R>(fn:(events:Record<string,unknown>[],append:(e:unknown)=>Promise<void>)=>Promise<R>){await check();return withFileLock(path,"factory policy",async()=>{const state=await read();return fn(state.events,async event=>{if(state.events.length>=256)throw new Error("control event budget exhausted");const record={sequence:state.events.length,previous:state.previous,event:cloneExperiment(event)},data=Buffer.from(JSON.stringify(record)+"\n");if(state.size+data.length>512*1024||data.length>65536)throw new Error("control byte budget exhausted");await check();const f=await open(path,constants.O_WRONLY|constants.O_APPEND|constants.O_NOFOLLOW|constants.O_NONBLOCK);
|
|
24
|
+
await runWithFinalizers(async()=>{const s=await f.stat({bigint:true});if(!s.isFile()||s.nlink!==1n||String(s.dev)!==b.journalDevice||String(s.ino)!==b.journalInode||s.size!==BigInt(state.size)||(s.mode&0o077n)!==0n)throw new Error("control journal changed under lock");await f.writeFile(data);await f.sync();},[{label:"control append close failed",run:()=>f.close()}]);await check();state.size+=data.length;state.previous=experimentHash(record);state.events.push(cloneExperiment(event) as Record<string,unknown>);
|
|
25
|
+
});},{staleRecovery:"disabled"});}};
|
|
26
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { isAbsolute, resolve, join } from "node:path";
|
|
4
|
+
import { createDashboardHost, dashboardHostDigest, dashboardHostRequestDigest, type DashboardHostAuthority, type DashboardHostConfig, type DashboardHostRequest } from "./dashboard-host.ts";
|
|
5
|
+
import type { DashboardHarness } from "./dashboard-host-contract.ts";
|
|
6
|
+
import { loadedDashboardHarnessDigest } from "./dashboard-harness.ts";
|
|
7
|
+
import { createDispatchBudget, openResourceBudget, resourceBindingDigest } from "./resource-budget.ts";
|
|
8
|
+
import { dispatchRequestDigest, type DispatchRequest } from "./dispatch-control.ts";
|
|
9
|
+
import { serveDashboardHost } from "./dashboard-host-transport.ts";
|
|
10
|
+
import type { DeclaredWorkState } from "./work-command.ts";
|
|
11
|
+
import type { OrdinaryChildren } from "./ordinary-children.ts";
|
|
12
|
+
import { dataDigest } from "./debrief-contract.ts";
|
|
13
|
+
|
|
14
|
+
export interface DailyDashboardHostInput {id:string;cwd:string;directory:string;socketPath?:string;declared:DeclaredWorkState;ordinary:OrdinaryChildren;harness:DashboardHarness;author:string}
|
|
15
|
+
const idPattern=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
16
|
+
const hash=(value:string)=>createHash("sha256").update(value).digest("hex");
|
|
17
|
+
|
|
18
|
+
/** One explicitly started, process-owned daily host. No authority, controller or prior host is recovered from disk. */
|
|
19
|
+
export async function startDailyDashboardHost(input:DailyDashboardHostInput){
|
|
20
|
+
if(!idPattern.test(input.id)||!isAbsolute(input.cwd)||resolve(input.cwd)!==input.cwd||!isAbsolute(input.directory)||resolve(input.directory)!==input.directory||!input.author.trim()||input.author.length>256)throw Error("explicit bounded daily host configuration required");
|
|
21
|
+
const harnessDigest=loadedDashboardHarnessDigest(input.harness);if(!harnessDigest)throw Error("loaded skill-harness bridge required");
|
|
22
|
+
if(input.declared.ledgerPath!==join(input.cwd,".pi","work.jsonl"))throw Error("current declared work must belong to this host project");
|
|
23
|
+
await mkdir(input.directory,{mode:0o700});const archiveRoot=join(input.directory,"archive");await mkdir(archiveRoot,{mode:0o700});
|
|
24
|
+
const reference=input.harness.retainArchiveSource(archiveRoot,{sourceId:`host-${input.id}-bootstrap`,parser:{id:"daily-host-bootstrap",version:"1"},retention:"exact",bytes:Buffer.from("explicit daily host bootstrap")});
|
|
25
|
+
const component={kind:"detector",id:"retained-work",version:"1",population:`daily:${input.id}`};
|
|
26
|
+
const trustInput={archiveRoot,component,seed:hash(`daily-host\0${input.id}`),maxUnflagged:0,cohort:[{incidentId:"bootstrap",manifestId:reference.manifestId,flagged:true,split:"calibration"}],exposure:null};
|
|
27
|
+
const trustPolicyId=input.harness.trustPolicyDigest(trustInput),trustDirectory=join(input.directory,"trust");input.harness.createTrustLifecycle(trustDirectory,trustInput,[trustPolicyId]);
|
|
28
|
+
const workRelative="work.jsonl",factsRelative="facts.jsonl",workMirror=join(input.directory,workRelative);await writeFile(workMirror,await readFile(input.declared.ledgerPath),{mode:0o600,flag:"wx"});
|
|
29
|
+
const refreshWork=async()=>{const temporary=`${workMirror}.${process.pid}.${randomUUID()}.tmp`;try{await writeFile(temporary,await readFile(input.declared.ledgerPath),{mode:0o600,flag:"wx"});await rename(temporary,workMirror);}finally{await rm(temporary,{force:true});}};
|
|
30
|
+
const facts={scopeDigest:input.declared.selectedSnapshot.snapshot.digest,version:"declared-work-v1",population:`daily:${input.id}`,expectedWaits:[],checkpoints:[],violations:[],priorAccepted:[]};await writeFile(join(input.directory,factsRelative),JSON.stringify(facts)+"\n",{mode:0o600,flag:"wx"});
|
|
31
|
+
const policy={version:"archive-policy-v2",id:`daily-${input.id}`,revision:"1",sourceRoot:input.directory,archiveRoot,maxBytes:8*1024*1024,retention:"exact",expiresAt:new Date(Date.now()+24*60*60*1000).toISOString(),sources:[{id:"work",path:workRelative,parser:{id:"pi-daddy-work-ledger",version:"4"},contentPolicy:"manifest-only"},{id:"facts",path:factsRelative,parser:{id:"pi-daddy-work-signal-facts",version:"1"},contentPolicy:"manifest-only"}]};
|
|
32
|
+
const policyPath=join(input.directory,"archive-policy.json"),policyBytes=JSON.stringify(policy);await writeFile(policyPath,policyBytes,{mode:0o600,flag:"wx"});
|
|
33
|
+
const authorityDigest=dataDigest({kind:"daily-dashboard-host",id:input.id,cwd:input.cwd}),budget=await createDispatchBudget({directory:join(input.directory,"budget"),authorityDigest,limits:{maxAttempts:6,maxInputBytes:65536,maxConcurrent:3}});
|
|
34
|
+
const config:DashboardHostConfig={version:"producer-dashboard-host-v1",trustDirectory,trustPolicyId,archiveRoot,scope:`daily:${input.id}`,author:input.author,policyPath,policySha256:hash(policyBytes),sources:[{id:"work",kind:"work"},{id:"facts",kind:"facts"}],selection:input.declared.selectedSnapshot,cases:null,blind:null,budgetDigest:resourceBindingDigest(budget),experimentDigest:null,harnessArtifactDigest:harnessDigest,ordinaryDigest:input.ordinary.bindingDigest};
|
|
35
|
+
const requestDigests=new Set<string>(),dispatchDigests=new Set<string>();let host:ReturnType<typeof createDashboardHost>,factsResultManifest:string|null=null;
|
|
36
|
+
const authority=():DashboardHostAuthority=>({hostDigests:[dashboardHostDigest(config)],requestDigests:[...requestDigests],workContext:{selectedSnapshot:config.selection,authority:null},dispatch:{authorityDigest,requestDigests:[...dispatchDigests]},experiment:null});
|
|
37
|
+
const request=(context:{hostDigest:string;selectionDigest:string;tip:string},operation:DashboardHostRequest["operation"],payload:unknown,requestId:string):DashboardHostRequest=>({version:"1.0",requestId,hostDigest:context.hostDigest,selectionDigest:context.selectionDigest,expectedTip:context.tip,operation,payload});
|
|
38
|
+
host=createDashboardHost({harness:input.harness,config,budget,ordinary:input.ordinary,authority,beforeObservation:sourceId=>sourceId==="work"?refreshWork():Promise.resolve(),humanActions:async context=>{
|
|
39
|
+
const state=await openResourceBudget(budget).controls(null).inspect(),action=state.paused?"resume-dispatch":"pause-dispatch",key=state.paused?"resume-dispatch":"pause-new-dispatch";
|
|
40
|
+
const native:DispatchRequest={version:"1.0",requestId:`${action}:${state.revision}`,bindingDigest:config.budgetDigest,expectedRevision:state.revision,action,targetExecutionId:null};dispatchDigests.add(dispatchRequestDigest(native));
|
|
41
|
+
const hostRequest=request(context,"dispatch",native,`host-${action}:${state.revision}`);requestDigests.add(dashboardHostRequestDigest(hostRequest));
|
|
42
|
+
const previous=context.observations.find(x=>x.sourceId==="work")?.checkpointId??null,refresh=request(context,"observe",{sourceId:"work",previousCheckpointId:previous,facts:context.observations.find(x=>x.sourceId==="facts")?.checkpointId?factsResultManifest:null},`refresh-work:${state.revision}:${context.tip.slice(0,12)}`);requestDigests.add(dashboardHostRequestDigest(refresh));
|
|
43
|
+
return [{key,label:state.paused?"Resume new governed dispatch":"Pause new governed dispatch; running children continue",request:hostRequest},{key:"refresh-current-work",label:"Capture current declared work and attempts",request:refresh}];
|
|
44
|
+
}});
|
|
45
|
+
const before=await host.frame(),observeFacts=request({hostDigest:host.hostDigest,selectionDigest:before.selectionDigest,tip:before.tip},"observe",{sourceId:"facts",previousCheckpointId:null,facts:null},"observe-current-facts");requestDigests.add(dashboardHostRequestDigest(observeFacts));const factsResult=await host.action(observeFacts) as {result:{sourceManifestId:string}};factsResultManifest=factsResult.result.sourceManifestId;
|
|
46
|
+
const afterFacts=await host.frame(),observeWork=request({hostDigest:host.hostDigest,selectionDigest:afterFacts.selectionDigest,tip:afterFacts.tip},"observe",{sourceId:"work",previousCheckpointId:null,facts:factsResult.result.sourceManifestId},"observe-current-work");requestDigests.add(dashboardHostRequestDigest(observeWork));await host.action(observeWork);
|
|
47
|
+
const socketPath=input.socketPath??join(input.directory,"dashboard.sock"),server=await serveDashboardHost(socketPath,host);
|
|
48
|
+
let closed=false;return Object.freeze({host,socketPath,directory:input.directory,async close(){if(closed)return;closed=true;await server.close();}});
|
|
49
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { open, lstat } from "node:fs/promises";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { Compile } from "typebox/compile";
|
|
6
|
+
import { parseRetentionJson } from "./retention-json.ts";
|
|
7
|
+
import { runWithFinalizers } from "./finalization.ts";
|
|
8
|
+
|
|
9
|
+
export const ARCHIVE_PROJECTION_VERSION = "execution-archive-projection-v1" as const;
|
|
10
|
+
export const DAILY_INPUT_LIMIT = 4 * 1024 * 1024;
|
|
11
|
+
export interface ArchiveExecution {
|
|
12
|
+
executionId: string; parentExecutionIds: (string | null)[]; retainedSessionIds: string[]; activeBranch: null;
|
|
13
|
+
toolCallIds: (string | null)[]; archiveIds: string[]; runtime: "running" | "terminal" | "conflict";
|
|
14
|
+
outcome: { code: number | null; signal: string | null; timedOut: boolean; aborted: boolean; truncated: boolean; failed: boolean } | null;
|
|
15
|
+
sourceReferences: string[]; issues: string[]; coverage: "partial"; acceptance: "not-assessed";
|
|
16
|
+
}
|
|
17
|
+
export interface ArchiveProjection { version: typeof ARCHIVE_PROJECTION_VERSION; executions: ArchiveExecution[]; acceptance: "not-assessed" }
|
|
18
|
+
const schema = createRequire(import.meta.url)("../contracts/daily-view/v1/p03/projection.schema.json");
|
|
19
|
+
const validator = Compile(schema);
|
|
20
|
+
export const byteHash = (bytes: Uint8Array | string): string => createHash("sha256").update(bytes).digest("hex");
|
|
21
|
+
export function freezeDaily<T>(value: T): T {
|
|
22
|
+
if (value && typeof value === "object") { Object.values(value).forEach(freezeDaily); Object.freeze(value); }
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
/** Exact vendored P03 shape, plus no duplicate execution identity or contradictory terminal shape.
|
|
26
|
+
* Only the producer's emitted integer-token subset is supported; no lossy fractional rounding. */
|
|
27
|
+
export function parseArchiveProjection(text: string): ArchiveProjection {
|
|
28
|
+
const value = parseRetentionJson(text, DAILY_INPUT_LIMIT);
|
|
29
|
+
for (const match of text.matchAll(/"(?:[^"\\]|\\[\s\S])*"|[^\s{}\[\],:]+/g)) {
|
|
30
|
+
if (/^-?\d/.test(match[0]) && !/^-?(0|[1-9]\d*)$/.test(match[0])) throw new TypeError("unsupported archive numeric token");
|
|
31
|
+
}
|
|
32
|
+
if (!validator.Check(value)) throw new TypeError("unsupported archive projection shape/version");
|
|
33
|
+
const projection = value as ArchiveProjection, ids = new Set<string>();
|
|
34
|
+
for (const e of projection.executions) {
|
|
35
|
+
if (ids.has(e.executionId) || (e.runtime !== "terminal" && e.outcome !== null)) throw new TypeError("ambiguous archive execution");
|
|
36
|
+
ids.add(e.executionId);
|
|
37
|
+
}
|
|
38
|
+
projection.executions.sort((a, b) => a.executionId < b.executionId ? -1 : a.executionId > b.executionId ? 1 : 0);
|
|
39
|
+
return freezeDaily(projection);
|
|
40
|
+
}
|
|
41
|
+
export type SnapshotRead = { status: "read"; bytes: Buffer; sha256: string } | { status: "missing" | "error" | "unconfigured"; reason: string };
|
|
42
|
+
/** Caller-authorized exact file only. No directories, symlink following, watching or mtime freshness claim. */
|
|
43
|
+
export async function readDailySnapshot(path: string | undefined, limit = DAILY_INPUT_LIMIT): Promise<SnapshotRead> {
|
|
44
|
+
if (!path) return { status: "unconfigured", reason: "not-configured" };
|
|
45
|
+
try {
|
|
46
|
+
const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
47
|
+
return await runWithFinalizers(async (): Promise<SnapshotRead> => {
|
|
48
|
+
const before = await file.stat({ bigint: true });
|
|
49
|
+
if (!before.isFile() || before.size > BigInt(limit)) return { status: "error", reason: "not-regular-or-over-limit" };
|
|
50
|
+
const bytes = Buffer.alloc(Number(before.size)); let offset = 0;
|
|
51
|
+
while (offset < bytes.length) { const n = (await file.read(bytes, offset, bytes.length - offset, offset)).bytesRead;
|
|
52
|
+
if (!n) return { status: "error", reason: "changed-during-read" }; offset += n; }
|
|
53
|
+
const after = await file.stat({ bigint: true }), located = await lstat(path, { bigint: true });
|
|
54
|
+
if (before.size !== after.size || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs ||
|
|
55
|
+
after.dev !== located.dev || after.ino !== located.ino) return { status: "error", reason: "changed-during-read" };
|
|
56
|
+
return { status: "read", bytes, sha256: byteHash(bytes) };
|
|
57
|
+
}, [{ label: "snapshot close failed", run: () => file.close() }]);
|
|
58
|
+
} catch (error) { return { status: (error as { code?: string }).code === "ENOENT" ? "missing" : "error", reason: "snapshot-unavailable" }; }
|
|
59
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { DailyView } from "./daily-view.ts";
|
|
2
|
+
const clean = (value: unknown) => String(value ?? "unknown").replace(/[\p{Cc}\p{Cf}]/gu, " ").replace(/\s+/g, " ").trim();
|
|
3
|
+
/** Text-only, accessible labels. No command links, control actions, model calls or terminal input. */
|
|
4
|
+
export function renderDailyView(view: DailyView, width = 100): string {
|
|
5
|
+
const lines = ["PI-DADDY — READ-ONLY DAILY VIEW", view.narrative,
|
|
6
|
+
`Scope: ${view.scope ? `${view.scope.id} revision ${view.scope.revision} digest ${view.scope.digest}` : "UNRESOLVED — select an exact P01 snapshot"}`,
|
|
7
|
+
`Acceptance authority: ${view.authority}; coverage: PARTIAL; freshness: SNAPSHOT / UNKNOWN`,
|
|
8
|
+
`Models: NOT CONSULTED; connectivity UNKNOWN. Continuity: ${view.continuity}${view.scopeChanged ? "; SCOPE CHANGED" : ""}`,
|
|
9
|
+
`Inputs: work ${view.sources.work}; archive ${view.sources.archive}`, "", "INTENDED OBLIGATIONS"];
|
|
10
|
+
if (!view.obligations.length) lines.push("EMPTY / UNRESOLVED: no scoped obligations available; not proof of no intended work.");
|
|
11
|
+
const attempts = new Map(view.attempts.map(a => [a.executionId, a]));
|
|
12
|
+
for (const o of view.obligations) {
|
|
13
|
+
lines.push(`${o.obligation.id}@${o.obligation.revision} | ${o.acceptance.toUpperCase()} | ${o.activity.toUpperCase()}`,
|
|
14
|
+
` intent ${o.intent.id}@${o.intent.revision}; policy ${o.policy.id}@${o.policy.revision}`,
|
|
15
|
+
` artifact ${o.artifactCoverage}; evidence ${o.evidenceCoverage}; P01 matched receipts: ${o.receiptIds.join(", ") || "none"}`,
|
|
16
|
+
` obstacle: ${o.obstacles.join(", ") || (o.acceptance === "accepted-under-supplied-authority" ? "none under supplied snapshot authority" : "acceptance not established")}`);
|
|
17
|
+
for (const id of o.attempts) {
|
|
18
|
+
const a = attempts.get(id)!;
|
|
19
|
+
lines.push(` attempt ${id} (logical ${a.logicalChildId ?? "unknown"}) | P01 runtime ${a.intentRuntime} | P03 ${a.archive?.runtime ?? "unavailable"}`);
|
|
20
|
+
}
|
|
21
|
+
for (const ref of o.claimReferences) lines.push(` claim source ${ref}`);
|
|
22
|
+
}
|
|
23
|
+
lines.push("", "EXECUTION SNAPSHOTS — NOT ACCEPTANCE");
|
|
24
|
+
if (!view.attempts.length) lines.push("EMPTY: no execution attempts in these snapshots.");
|
|
25
|
+
for (const a of view.attempts) {
|
|
26
|
+
const e = a.archive;
|
|
27
|
+
lines.push(`${a.executionId} | logical ${a.logicalChildId ?? "unknown"} | P01 ${a.intentRuntime} | P03 ${e?.runtime ?? "unavailable"}`);
|
|
28
|
+
if (e) {
|
|
29
|
+
lines.push(` producer-reported exit ${e.outcome?.code ?? "unknown"}; failed control/runtime ${e.outcome?.failed ?? "unknown"}; aborted ${e.outcome?.aborted ?? "unknown"}`,
|
|
30
|
+
` parents ${e.parentExecutionIds.map(clean).join(", ") || "unknown"}; calls ${e.toolCallIds.map(clean).join(", ") || "unknown"}`,
|
|
31
|
+
` reported retained sessions ${e.retainedSessionIds.join(", ") || "none"}; ACTIVE BRANCH UNKNOWN`,
|
|
32
|
+
" check receipt detail/bytes not supplied by P03 v1; never inferred from exit status");
|
|
33
|
+
}
|
|
34
|
+
lines.push(` obstacle/coverage: ${a.issues.join(", ") || "partial snapshot; freshness unknown"}`);
|
|
35
|
+
for (const r of a.sourceAvailability) lines.push(` source ${r.sha256} | ${r.state}`);
|
|
36
|
+
}
|
|
37
|
+
lines.push("", `Coverage gaps: ${view.issues.join("; ") || "partial snapshots; live freshness unavailable"}`,
|
|
38
|
+
`Work source SHA256: ${view.sources.workSha256 ?? "missing"}`, `Archive projection SHA256: ${view.sources.archiveSha256 ?? "missing"}`,
|
|
39
|
+
"P06: fixed digest profile only; arbitrary shell/model effects and aggregate CPU/memory/PID/money caps unsupported.",
|
|
40
|
+
"No steering, dispatch, acceptance, session edit or recovery action is performed by this view.");
|
|
41
|
+
const bounded = lines.slice(0, 300); if (lines.length > 300) bounded.push(`${lines.length - 300} additional lines omitted; full rows remain in the read-model.`);
|
|
42
|
+
const columns = Number.isFinite(width) ? Math.max(20, Math.min(240, Math.floor(width))) : 100;
|
|
43
|
+
return bounded.map(line => { const text = [...clean(line)]; return text.length > columns ? text.slice(0, columns - 3).join("") + "..." : text.join(""); }).join("\n");
|
|
44
|
+
}
|