pi-daddy 0.26.1 → 0.27.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 +26 -0
- package/PRODUCT-GUIDE.md +125 -0
- package/README.md +16 -1
- package/REQUIREMENTS.md +41 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +35 -7
- package/dist/cli.js.map +1 -1
- package/dist/daily-dashboard-host.d.ts +14 -1
- package/dist/daily-dashboard-host.d.ts.map +1 -1
- package/dist/daily-dashboard-host.js +40 -17
- package/dist/daily-dashboard-host.js.map +1 -1
- package/dist/daily-panel.d.ts +36 -0
- package/dist/daily-panel.d.ts.map +1 -0
- package/dist/daily-panel.js +84 -0
- package/dist/daily-panel.js.map +1 -0
- package/dist/daily-view-render.d.ts +2 -1
- package/dist/daily-view-render.d.ts.map +1 -1
- package/dist/daily-view-render.js +2 -1
- package/dist/daily-view-render.js.map +1 -1
- package/dist/daily-view.d.ts +3 -0
- package/dist/daily-view.d.ts.map +1 -1
- package/dist/daily-view.js +6 -0
- package/dist/daily-view.js.map +1 -1
- package/dist/dashboard-cli.d.ts +2 -0
- package/dist/dashboard-cli.d.ts.map +1 -1
- package/dist/dashboard-cli.js +37 -15
- package/dist/dashboard-cli.js.map +1 -1
- package/dist/dashboard-host-contract.d.ts +2 -0
- package/dist/dashboard-host-contract.d.ts.map +1 -1
- package/dist/dashboard-host-contract.js.map +1 -1
- package/dist/dashboard-host-transport.d.ts.map +1 -1
- package/dist/dashboard-host-transport.js +6 -3
- package/dist/dashboard-host-transport.js.map +1 -1
- package/dist/dashboard-host.d.ts +30 -2
- package/dist/dashboard-host.d.ts.map +1 -1
- package/dist/dashboard-host.js +73 -11
- package/dist/dashboard-host.js.map +1 -1
- package/dist/dashboard-menu.d.ts +16 -0
- package/dist/dashboard-menu.d.ts.map +1 -0
- package/dist/dashboard-menu.js +33 -0
- package/dist/dashboard-menu.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/learning-connection.d.ts +127 -0
- package/dist/learning-connection.d.ts.map +1 -0
- package/dist/learning-connection.js +54 -0
- package/dist/learning-connection.js.map +1 -0
- package/dist/model-preflight.d.ts +5 -0
- package/dist/model-preflight.d.ts.map +1 -1
- package/dist/model-preflight.js +9 -0
- package/dist/model-preflight.js.map +1 -1
- package/dist/product-files.d.ts +6 -0
- package/dist/product-files.d.ts.map +1 -0
- package/dist/product-files.js +55 -0
- package/dist/product-files.js.map +1 -0
- package/dist/work-command.d.ts.map +1 -1
- package/dist/work-command.js +14 -6
- package/dist/work-command.js.map +1 -1
- package/dist/work-policy-registry.d.ts +133 -0
- package/dist/work-policy-registry.d.ts.map +1 -0
- package/dist/work-policy-registry.js +128 -0
- package/dist/work-policy-registry.js.map +1 -0
- package/dist/work-run.d.ts +56 -0
- package/dist/work-run.d.ts.map +1 -0
- package/dist/work-run.js +102 -0
- package/dist/work-run.js.map +1 -0
- package/dist/work-setup.d.ts +40 -0
- package/dist/work-setup.d.ts.map +1 -0
- package/dist/work-setup.js +174 -0
- package/dist/work-setup.js.map +1 -0
- package/extensions/daily-dashboard-session.ts +16 -7
- package/extensions/execute-child.ts +2 -2
- package/extensions/grants-command.ts +2 -2
- package/extensions/grants.ts +6 -2
- package/extensions/learning-outcome-session.ts +38 -0
- package/extensions/learning-session.ts +59 -0
- package/extensions/ordinary-runtime.ts +2 -2
- package/extensions/run-delegation.ts +2 -2
- package/extensions/work-policy-session.ts +139 -0
- package/extensions/work-run-session.ts +75 -0
- package/extensions/work-runtime.ts +2 -2
- package/extensions/work-session.ts +132 -0
- package/package.json +9 -1
- package/src/cli.ts +26 -7
- package/src/daily-dashboard-host.ts +32 -15
- package/src/daily-panel.ts +80 -0
- package/src/daily-view-render.ts +2 -1
- package/src/daily-view.ts +8 -0
- package/src/dashboard-cli.ts +29 -17
- package/src/dashboard-host-contract.ts +2 -0
- package/src/dashboard-host-transport.ts +7 -4
- package/src/dashboard-host.ts +48 -13
- package/src/dashboard-menu.ts +28 -0
- package/src/index.ts +7 -1
- package/src/learning-connection.ts +70 -0
- package/src/model-preflight.ts +9 -0
- package/src/product-files.ts +34 -0
- package/src/work-command.ts +11 -6
- package/src/work-policy-registry.ts +81 -0
- package/src/work-run.ts +79 -0
- package/src/work-setup.ts +141 -0
package/dist/work-run.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { randomUUID, createHash } from "node:crypto";
|
|
2
|
+
import { writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { setTimeout as wait } from "node:timers/promises";
|
|
5
|
+
import { createControlJournal, controlJournal } from "./control-journal.js";
|
|
6
|
+
import { composeStepTask, HANDOFF_MAX_BYTES } from "./chain.js";
|
|
7
|
+
import { splitBudget } from "./fanout.js";
|
|
8
|
+
import { workSetup } from "./work-setup.js";
|
|
9
|
+
/** Finite DAG scheduler only. Execution, grants, cancellation, deadlines and observation remain original-owner operations. */
|
|
10
|
+
export async function runWorkSetup(input) {
|
|
11
|
+
const setup = workSetup(input.setup), split = splitBudget(input.budget, setup.tasks.length);
|
|
12
|
+
if (!split.ok)
|
|
13
|
+
throw Error(split.reason);
|
|
14
|
+
if (!/^[a-f0-9]{64}$/.test(input.selectionDigest))
|
|
15
|
+
throw Error("exact selected work digest required");
|
|
16
|
+
const order = input.priorities ?? setup.tasks.map(t => t.id);
|
|
17
|
+
if (order.length !== setup.tasks.length || new Set(order).size !== order.length || order.some(id => !setup.tasks.some(t => t.id === id)))
|
|
18
|
+
throw Error("priority must name each task exactly once");
|
|
19
|
+
const binding = await createControlJournal(input.directory, { version: "work-run-v1", runId: input.orderId ?? randomUUID(), selectionDigest: input.selectionDigest, setup, policyPin: input.policyPin ?? null });
|
|
20
|
+
const journal = controlJournal(binding), outcomes = new Map();
|
|
21
|
+
const tasks = order.map(id => ({ id, state: "waiting", executionId: null, resultPath: null, resultDigest: null, reason: null }));
|
|
22
|
+
const running = new Map();
|
|
23
|
+
let persistenceFailure;
|
|
24
|
+
const publish = () => { try {
|
|
25
|
+
input.onUpdate?.(tasks.map(t => ({ ...t })));
|
|
26
|
+
}
|
|
27
|
+
catch { /* display is not execution */ } };
|
|
28
|
+
const record = (task) => journal.transaction(async (_events, append) => append({ type: "task", ...task }));
|
|
29
|
+
while (tasks.some(t => t.state === "waiting") || running.size) {
|
|
30
|
+
for (const row of tasks.filter(t => t.state === "waiting")) {
|
|
31
|
+
const spec = setup.tasks.find(t => t.id === row.id);
|
|
32
|
+
if (input.signal?.aborted || persistenceFailure || spec.dependencies.some(d => ["failed", "blocked"].includes(tasks.find(t => t.id === d).state))) {
|
|
33
|
+
row.state = "blocked";
|
|
34
|
+
row.reason = input.signal?.aborted ? "Cancelled before admission" : persistenceFailure ? "Result/control persistence failed; no further admission" : "A dependency did not finish successfully";
|
|
35
|
+
try {
|
|
36
|
+
await record(row);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
persistenceFailure ??= error;
|
|
40
|
+
}
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (input.admissionOpen?.() === false || running.size >= setup.maxParallel || !spec.dependencies.every(d => tasks.find(t => t.id === d).state === "finished"))
|
|
44
|
+
continue;
|
|
45
|
+
row.state = "running";
|
|
46
|
+
const pending = (async () => {
|
|
47
|
+
try {
|
|
48
|
+
const previous = spec.dependencies.map(d => `${d}\n${outcomes.get(d).text}`).join("\n\n");
|
|
49
|
+
if (Buffer.byteLength(previous) > HANDOFF_MAX_BYTES)
|
|
50
|
+
throw Error("Complete predecessor output exceeds the 32 KiB handoff bound; dependent work was not launched");
|
|
51
|
+
await record(row);
|
|
52
|
+
const outcome = await input.execute(spec, composeStepTask(spec.outcome, spec.dependencies.length ? previous : undefined), split.perChild, async (executionId) => { row.executionId = executionId; await record(row); publish(); });
|
|
53
|
+
outcomes.set(spec.id, outcome);
|
|
54
|
+
if (Buffer.byteLength(outcome.text) > 1024 * 1024)
|
|
55
|
+
throw Error("original child result exceeds retention bound");
|
|
56
|
+
const bytes = Buffer.from(outcome.text), file = `${spec.id}.txt`;
|
|
57
|
+
await writeFile(join(binding.directory, file), bytes, { mode: 0o600, flag: "wx" });
|
|
58
|
+
row.resultPath = join(binding.directory, file);
|
|
59
|
+
row.resultDigest = createHash("sha256").update(bytes).digest("hex");
|
|
60
|
+
row.state = outcome.ok && !outcome.control && !outcome.truncated ? "finished" : "failed";
|
|
61
|
+
row.reason = row.state === "finished" ? null : outcome.reason ?? (outcome.control ? "Observation/control failed" : outcome.truncated ? "Output truncated" : "Attempt failed");
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
row.state = "failed";
|
|
65
|
+
row.reason = String(error);
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
await record(row);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
persistenceFailure ??= error;
|
|
72
|
+
row.state = "failed";
|
|
73
|
+
row.reason = `Control acknowledgement unknown: ${String(error)}`;
|
|
74
|
+
}
|
|
75
|
+
publish();
|
|
76
|
+
})().finally(() => running.delete(row.id));
|
|
77
|
+
running.set(row.id, pending);
|
|
78
|
+
publish();
|
|
79
|
+
}
|
|
80
|
+
publish();
|
|
81
|
+
if (running.size)
|
|
82
|
+
await Promise.race(running.values());
|
|
83
|
+
else if (tasks.some(t => t.state === "waiting"))
|
|
84
|
+
await wait(100, undefined, { signal: input.signal }).catch(error => { if (!input.signal?.aborted)
|
|
85
|
+
throw error; });
|
|
86
|
+
}
|
|
87
|
+
// No detached promises, restart relaunch, refund or retry. Original children have all settled here.
|
|
88
|
+
if (persistenceFailure)
|
|
89
|
+
throw Error(`Work results may exist but control persistence failed: ${String(persistenceFailure)}`);
|
|
90
|
+
return { binding, tasks, acceptance: "not-assessed" };
|
|
91
|
+
}
|
|
92
|
+
export async function inspectWorkRun(binding) {
|
|
93
|
+
workSetup(binding.initial.setup);
|
|
94
|
+
if (binding.initial.version !== "work-run-v1")
|
|
95
|
+
throw Error("unsupported work run");
|
|
96
|
+
const rows = new Map();
|
|
97
|
+
for (const event of (await controlJournal(binding).read()).events)
|
|
98
|
+
if (event.type === "task")
|
|
99
|
+
rows.set(String(event.id), event);
|
|
100
|
+
return { binding, tasks: binding.initial.setup.tasks.map(t => rows.get(t.id) ?? { id: t.id, state: "waiting", executionId: null, resultPath: null, resultDigest: null, reason: "No launch acknowledgement; do not infer or retry" }), acceptance: "not-assessed" };
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=work-run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-run.js","sourceRoot":"","sources":["../src/work-run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type RevisionRef, type WorkRevisionEvent, type WorkSnapshotEvent, type WorkFrozen } from "./work-ledger.ts";
|
|
2
|
+
import { type DeclaredWorkState } from "./work-command.ts";
|
|
3
|
+
import { type IntentSelection } from "./intent-control.ts";
|
|
4
|
+
import type { WorkPresentation } from "./daily-panel.ts";
|
|
5
|
+
export declare const WORK_EFFORTS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
6
|
+
export interface WorkTaskSetup {
|
|
7
|
+
id: string;
|
|
8
|
+
outcome: string;
|
|
9
|
+
agent: string | null;
|
|
10
|
+
model: string;
|
|
11
|
+
thinking: typeof WORK_EFFORTS[number];
|
|
12
|
+
dependencies: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface WorkSetup {
|
|
15
|
+
version: "work-setup-v1";
|
|
16
|
+
id: string;
|
|
17
|
+
outcome: string;
|
|
18
|
+
maxParallel: number;
|
|
19
|
+
tasks: WorkTaskSetup[];
|
|
20
|
+
}
|
|
21
|
+
export interface RecordedWorkSetup {
|
|
22
|
+
version: "recorded-work-setup-v1";
|
|
23
|
+
setup: WorkSetup;
|
|
24
|
+
state: DeclaredWorkState;
|
|
25
|
+
tasks: {
|
|
26
|
+
id: string;
|
|
27
|
+
obligation: RevisionRef;
|
|
28
|
+
}[];
|
|
29
|
+
events: WorkFrozen<WorkRevisionEvent | WorkSnapshotEvent>[];
|
|
30
|
+
}
|
|
31
|
+
export declare function workSetup(input: WorkSetup): WorkSetup;
|
|
32
|
+
/** Author first, append through P01, then select separately at an explicit owner boundary. */
|
|
33
|
+
export declare function recordWorkSetup(cwd: string, value: WorkSetup, mode?: "new" | "revise" | "alternative", expected?: DeclaredWorkState | null): Promise<RecordedWorkSetup>;
|
|
34
|
+
export declare function loadWorkSetup(state: DeclaredWorkState): Promise<RecordedWorkSetup | null>;
|
|
35
|
+
/** Caller must own a quiescent session, with no running daily host. No active order is rebound. */
|
|
36
|
+
export declare function selectRecordedWork(recorded: RecordedWorkSetup, expected: IntentSelection | null): Promise<DeclaredWorkState>;
|
|
37
|
+
export declare function listWorkSetups(cwd: string): Promise<RecordedWorkSetup[]>;
|
|
38
|
+
export declare function workSetupForSelection(cwd: string, selection: IntentSelection): Promise<RecordedWorkSetup | null>;
|
|
39
|
+
export declare function workPresentation(state: DeclaredWorkState): Promise<WorkPresentation | null>;
|
|
40
|
+
//# sourceMappingURL=work-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-setup.d.ts","sourceRoot":"","sources":["../src/work-setup.ts"],"names":[],"mappings":"AAGA,OAAO,EAA6E,KAAK,WAAW,EAAqB,KAAK,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnN,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,eAAO,MAAM,YAAY,sEAAuE,CAAC;AACjG,MAAM,WAAW,aAAa;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE;AAClK,MAAM,WAAW,SAAS;IAAG,OAAO,EAAE,eAAe,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAA;CAAE;AACjI,MAAM,WAAW,iBAAiB;IAAG,OAAO,EAAE,wBAAwB,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,WAAW,CAAA;KAAE,EAAE,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC,iBAAiB,GAAG,iBAAiB,CAAC,EAAE,CAAA;CAAE;AAMnO,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAiBrD;AAGD,8FAA8F;AAC9F,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,GAAE,KAAK,GAAG,QAAQ,GAAG,aAAqB,EAAE,QAAQ,GAAE,iBAAiB,GAAG,IAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA8C1L;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAc/F;AACD,mGAAmG;AACnG,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,CASlI;AACD,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAW9E;AACD,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAKtH;AACD,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAOjG"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { appendWorkLedgerEventOnce, buildWorkRevisionEvent, buildWorkSnapshotEvent } from "./work-ledger.js";
|
|
5
|
+
import { resolveWorkSnapshotText } from "./work-ledger-snapshot.js";
|
|
6
|
+
import { loadDeclaredWork } from "./work-command.js";
|
|
7
|
+
import { withFileLock } from "./file-lock.js";
|
|
8
|
+
import { intentKey } from "./intent-control.js";
|
|
9
|
+
import { privateDirectory, projectProductDirectory, readProductJson, writeProductJson } from "./product-files.js";
|
|
10
|
+
export const WORK_EFFORTS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
11
|
+
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
12
|
+
const keys = (value, expected) => { if (!value || typeof value !== "object" || Object.keys(value).sort().join() !== expected.sort().join())
|
|
13
|
+
throw Error("unsupported work setup fields"); };
|
|
14
|
+
const id = (value) => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$/.test(value);
|
|
15
|
+
const text = (value) => typeof value === "string" && value.trim().length > 0 && Buffer.byteLength(value) <= 8192;
|
|
16
|
+
const ref = (r) => ({ kind: r.kind, id: r.id, revision: r.revision, digest: r.digest });
|
|
17
|
+
export function workSetup(input) {
|
|
18
|
+
const s = JSON.parse(JSON.stringify(input));
|
|
19
|
+
keys(s, ["version", "id", "outcome", "maxParallel", "tasks"]);
|
|
20
|
+
if (s.version !== "work-setup-v1" || !id(s.id) || !text(s.outcome) || !Number.isInteger(s.maxParallel) || s.maxParallel < 1 || s.maxParallel > 8 || !Array.isArray(s.tasks) || !s.tasks.length || s.tasks.length > 8)
|
|
21
|
+
throw Error("work setup requires 1–8 tasks and parallel limit 1–8");
|
|
22
|
+
const seen = new Set();
|
|
23
|
+
for (const task of s.tasks) {
|
|
24
|
+
keys(task, ["id", "outcome", "agent", "model", "thinking", "dependencies"]);
|
|
25
|
+
if (!id(task.id) || seen.has(task.id) || !text(task.outcome) || !(task.agent === null || typeof task.agent === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(task.agent)) || typeof task.model !== "string" || !/^[A-Za-z0-9._-]+\/[A-Za-z0-9._:/@+-]+$/.test(task.model) || task.model.length > 128 || !WORK_EFFORTS.includes(task.thinking) || !Array.isArray(task.dependencies) || new Set(task.dependencies).size !== task.dependencies.length || task.dependencies.some(d => !id(d) || d === task.id))
|
|
26
|
+
throw Error("invalid task, model/effort or dependency; no profile fallback");
|
|
27
|
+
seen.add(task.id);
|
|
28
|
+
}
|
|
29
|
+
const done = new Set();
|
|
30
|
+
while (done.size < s.tasks.length) {
|
|
31
|
+
const eligible = s.tasks.filter(t => !done.has(t.id) && t.dependencies.every(d => done.has(d)));
|
|
32
|
+
if (!eligible.length)
|
|
33
|
+
throw Error("dependencies contain a cycle or missing task");
|
|
34
|
+
eligible.forEach(t => done.add(t.id));
|
|
35
|
+
}
|
|
36
|
+
return s;
|
|
37
|
+
}
|
|
38
|
+
const setupPath = (state) => join(dirname(state.statePath), "work-setups", `${state.selectedSnapshot.snapshot.digest}.json`);
|
|
39
|
+
/** Author first, append through P01, then select separately at an explicit owner boundary. */
|
|
40
|
+
export async function recordWorkSetup(cwd, value, mode = "new", expected = null) {
|
|
41
|
+
const setup = workSetup(value), root = join(resolve(cwd), ".pi"), statePath = join(root, "work-current.json");
|
|
42
|
+
await projectProductDirectory(root);
|
|
43
|
+
await privateDirectory(join(root, "work-setups"));
|
|
44
|
+
return withFileLock(statePath, "work setup", async () => {
|
|
45
|
+
const current = await loadDeclaredWork(statePath);
|
|
46
|
+
if (intentKey(current?.selectedSnapshot ?? null) !== intentKey(expected?.selectedSnapshot ?? null))
|
|
47
|
+
throw Error("work selection changed; reopen setup");
|
|
48
|
+
if (mode !== "new" && !current)
|
|
49
|
+
throw Error("select existing work before recording a revision");
|
|
50
|
+
const original = mode !== "new" ? await loadWorkSetup(current) : null;
|
|
51
|
+
if (mode !== "new" && (!original || original.setup.tasks.length !== setup.tasks.length || original.setup.tasks.some(t => !setup.tasks.some(n => n.id === t.id && intentKey([...n.dependencies].sort()) === intentKey([...t.dependencies].sort())))))
|
|
52
|
+
throw Error("topology changes require a new work setup; no live entity expansion");
|
|
53
|
+
const revisions = current && mode !== "new" ? resolveWorkSnapshotText(await readFile(current.ledgerPath, "utf8"), current.selectedSnapshot) : null;
|
|
54
|
+
const token = hash(intentKey({ setup, mode, from: mode === "new" ? null : current?.selectedSnapshot })).slice(0, 24), prefix = mode === "new" ? `work:${setup.id}:${token.slice(0, 8)}` : current.scope.id.replace(/:scope$/, "");
|
|
55
|
+
// Preparation is durable before the first append, so an interrupted exact retry retains timestamps/identities.
|
|
56
|
+
const preparedPath = join(root, "work-setups", `pending-${token}.json`);
|
|
57
|
+
const prepared = await readProductJson(preparedPath);
|
|
58
|
+
if (prepared && (Object.keys(prepared).join() !== "now" || !Number.isFinite(Date.parse(prepared.now))))
|
|
59
|
+
throw Error("invalid retained setup preparation");
|
|
60
|
+
const now = new Date(prepared?.now ?? Date.now());
|
|
61
|
+
if (!prepared)
|
|
62
|
+
await writeProductJson(preparedPath, { now: now.toISOString() });
|
|
63
|
+
const events = [];
|
|
64
|
+
const build = (kind, entityId, content, parent, dependencies = [], policy = null) => {
|
|
65
|
+
const prior = revisions && [revisions.scope, ...revisions.revisions].find(r => r.kind === kind && r.id === entityId);
|
|
66
|
+
const event = buildWorkRevisionEvent({ eventId: `${prefix}:${token}:${kind}:${events.length}`, now, revision: { kind, id: entityId, revision: prior ? prior.revision + 1 : 1, predecessor: prior ? ref(prior) : null, scopeId: `${prefix}:scope`, contentDigest: hash(intentKey(content)), parent, dependencies, ownerId: prior?.ownerId ?? "local-operator", permittedEffects: prior ? [...prior.permittedEffects] : [], policy } });
|
|
67
|
+
events.push(event);
|
|
68
|
+
return ref(event.payload.revision);
|
|
69
|
+
};
|
|
70
|
+
const scope = mode === "alternative" ? current.scope : build("scope", `${prefix}:scope`, setup, null);
|
|
71
|
+
const policy = mode === "new" ? build("policy", `${prefix}:policy`, { acceptance: "independent-review-required" }, null) : current.policy;
|
|
72
|
+
const intent = build("goal", mode === "new" ? `${prefix}:goal` : current.intent.id, setup, scope);
|
|
73
|
+
const tasks = [];
|
|
74
|
+
while (tasks.length < setup.tasks.length) {
|
|
75
|
+
for (const task of setup.tasks) {
|
|
76
|
+
if (tasks.some(t => t.id === task.id) || !task.dependencies.every(d => tasks.some(t => t.id === d)))
|
|
77
|
+
continue;
|
|
78
|
+
const entityId = original?.tasks.find(t => t.id === task.id)?.obligation.id ?? `${prefix}:${task.id}`;
|
|
79
|
+
const obligation = build("obligation", entityId, task, intent, task.dependencies.map(d => tasks.find(t => t.id === d).obligation), policy);
|
|
80
|
+
tasks.push({ id: task.id, obligation });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const ordered = setup.tasks.map(t => tasks.find(x => x.id === t.id));
|
|
84
|
+
const snapshot = buildWorkSnapshotEvent({ eventId: `${prefix}:${token}:snapshot`, now, snapshot: { snapshotId: `${prefix}:${token}:selected`, scope, revisions: [policy, intent, ...ordered.map(t => t.obligation)], bindings: ordered.map(t => ({ intent, obligation: t.obligation, artifact: null, policy })) } });
|
|
85
|
+
events.push(snapshot);
|
|
86
|
+
const state = { version: "pi-daddy-declared-work-v1", id: setup.id, outcomeDigest: hash(setup.outcome.trim()), ledgerPath: join(root, "work.jsonl"), statePath, grantLedgerPath: null, selectedSnapshot: { snapshot: { id: snapshot.payload.snapshot.snapshotId, digest: snapshot.payload.snapshot.digest }, event: { eventId: snapshot.eventId, digest: snapshot.digest } }, scope, intent, obligation: ordered[0].obligation, policy };
|
|
87
|
+
const result = { version: "recorded-work-setup-v1", setup, state, tasks: ordered, events };
|
|
88
|
+
const path = setupPath(state), previous = await readProductJson(path);
|
|
89
|
+
if (previous && intentKey(previous) !== intentKey(result))
|
|
90
|
+
throw Error("recorded setup identity conflict");
|
|
91
|
+
if (!previous)
|
|
92
|
+
await writeProductJson(path, result);
|
|
93
|
+
for (const event of events)
|
|
94
|
+
await appendWorkLedgerEventOnce({ path: state.ledgerPath, grantLedgerPath: null }, event);
|
|
95
|
+
await loadWorkSetup(state);
|
|
96
|
+
return result;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export async function loadWorkSetup(state) {
|
|
100
|
+
const value = await readProductJson(setupPath(state));
|
|
101
|
+
if (!value)
|
|
102
|
+
return null;
|
|
103
|
+
keys(value, ["version", "setup", "state", "tasks", "events"]);
|
|
104
|
+
const setup = workSetup(value.setup);
|
|
105
|
+
if (value.version !== "recorded-work-setup-v1" || intentKey(value.state.selectedSnapshot) !== intentKey(state.selectedSnapshot) || value.state.ledgerPath !== state.ledgerPath || value.state.statePath !== state.statePath || hash(setup.outcome.trim()) !== value.state.outcomeDigest || !Array.isArray(value.tasks) || value.tasks.length !== setup.tasks.length)
|
|
106
|
+
throw Error("work setup selection mismatch");
|
|
107
|
+
const resolved = resolveWorkSnapshotText(await readFile(state.ledgerPath, "utf8"), state.selectedSnapshot);
|
|
108
|
+
if (resolved.scopeState !== "valid" || !resolved.snapshot || hash(intentKey(setup)) !== resolved.revisions.find(r => r.digest === value.state.intent.digest)?.contentDigest)
|
|
109
|
+
throw Error("recorded work labels are stale or mismatched");
|
|
110
|
+
if (intentKey(resolved.snapshot.scope) !== intentKey(value.state.scope) || !resolved.snapshot.bindings.some(b => intentKey(b.intent) === intentKey(value.state.intent) && intentKey(b.policy) === intentKey(value.state.policy) && intentKey(b.obligation) === intentKey(state.obligation)))
|
|
111
|
+
throw Error("work state is not an exact selected binding");
|
|
112
|
+
for (const task of setup.tasks) {
|
|
113
|
+
const binding = value.tasks.find(t => t.id === task.id), revision = resolved.revisions.find(r => r.digest === binding?.obligation.digest);
|
|
114
|
+
if (!binding || !revision || revision.contentDigest !== hash(intentKey(task)) || !resolved.snapshot.bindings.some(b => intentKey(b.obligation) === intentKey(binding.obligation)))
|
|
115
|
+
throw Error("task configuration is not bound to selected work");
|
|
116
|
+
}
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
/** Caller must own a quiescent session, with no running daily host. No active order is rebound. */
|
|
120
|
+
export async function selectRecordedWork(recorded, expected) {
|
|
121
|
+
const state = recorded.state;
|
|
122
|
+
return withFileLock(state.statePath, "select work", async () => {
|
|
123
|
+
const current = await loadDeclaredWork(state.statePath);
|
|
124
|
+
if (intentKey(current?.selectedSnapshot ?? null) !== intentKey(expected))
|
|
125
|
+
throw Error("selection changed; no work selected");
|
|
126
|
+
if (!await loadWorkSetup(state))
|
|
127
|
+
throw Error("recorded setup missing");
|
|
128
|
+
const { statePath: _path, ...stored } = state;
|
|
129
|
+
await writeProductJson(state.statePath, stored, true);
|
|
130
|
+
return state;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export async function listWorkSetups(cwd) {
|
|
134
|
+
const root = join(resolve(cwd), ".pi", "work-setups");
|
|
135
|
+
let files;
|
|
136
|
+
try {
|
|
137
|
+
files = await readdir(root);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
if (error.code === "ENOENT")
|
|
141
|
+
return [];
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
if (files.length > 256)
|
|
145
|
+
throw Error("work setup history bound exceeded");
|
|
146
|
+
const results = [];
|
|
147
|
+
for (const file of files.filter(f => /^[a-f0-9]{64}\.json$/.test(f)).sort()) {
|
|
148
|
+
const value = await readProductJson(join(root, file));
|
|
149
|
+
if (value?.state?.statePath !== join(resolve(cwd), ".pi", "work-current.json") || file !== `${value.state.selectedSnapshot.snapshot.digest}.json`)
|
|
150
|
+
throw Error("invalid work setup history");
|
|
151
|
+
results.push((await loadWorkSetup(value.state)));
|
|
152
|
+
}
|
|
153
|
+
return results;
|
|
154
|
+
}
|
|
155
|
+
export async function workSetupForSelection(cwd, selection) {
|
|
156
|
+
const path = join(resolve(cwd), ".pi", "work-setups", `${selection.snapshot.digest}.json`), value = await readProductJson(path);
|
|
157
|
+
if (!value)
|
|
158
|
+
return null;
|
|
159
|
+
if (value.state?.statePath !== join(resolve(cwd), ".pi", "work-current.json") || intentKey(value.state.selectedSnapshot) !== intentKey(selection))
|
|
160
|
+
throw Error("recorded selection labels mismatch");
|
|
161
|
+
return loadWorkSetup(value.state);
|
|
162
|
+
}
|
|
163
|
+
export async function workPresentation(state) {
|
|
164
|
+
const setup = await loadWorkSetup(state);
|
|
165
|
+
if (setup)
|
|
166
|
+
return { outcome: setup.setup.outcome, obligations: setup.tasks.map(t => { const spec = setup.setup.tasks.find(s => s.id === t.id); return { digest: t.obligation.digest, outcome: spec.outcome, agent: spec.agent ?? "No-tool agent", dependencies: spec.dependencies }; }) };
|
|
167
|
+
const value = await readProductJson(join(dirname(state.statePath), "work-outcomes", `${state.outcomeDigest}.json`));
|
|
168
|
+
if (!value)
|
|
169
|
+
return null;
|
|
170
|
+
if (Object.keys(value).join() !== "outcome" || typeof value.outcome !== "string" || hash(value.outcome.trim()) !== state.outcomeDigest)
|
|
171
|
+
throw Error("declared outcome digest mismatch");
|
|
172
|
+
return { outcome: value.outcome, obligations: [{ digest: state.obligation.digest, outcome: value.outcome }] };
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=work-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-setup.js","sourceRoot":"","sources":["../src/work-setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,8 @@ import { ENV_DASHBOARD_HOST_SOCKET } from "../src/dashboard-host-transport.ts";
|
|
|
7
7
|
import { startDailyDashboardHost } from "../src/daily-dashboard-host.ts";
|
|
8
8
|
import type { DeclaredWorkState } from "../src/work-command.ts";
|
|
9
9
|
import type { OrdinaryChildren } from "../src/ordinary-children.ts";
|
|
10
|
+
import { learningHarness, loadLearningConnection } from "../src/learning-connection.ts";
|
|
11
|
+
import { readProductJson } from "../src/product-files.ts";
|
|
10
12
|
|
|
11
13
|
const BRIDGE=Symbol.for("skill-harness.dashboard-host.v1"),idPattern=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
12
14
|
export function dailyDashboardPaths(cwd:string,id:string,home=homedir(),uid=process.getuid?.()??0){const key=createHash("sha256").update(`${cwd}\0${id}`).digest("hex");return {directory:join(home,".local","state","pi-daddy","hosts",key.slice(0,16)),socketDirectory:join("/tmp",`pi-daddy-${uid}`),socketPath:join("/tmp",`pi-daddy-${uid}`,`host-${key.slice(0,20)}.sock`)};}
|
|
@@ -18,22 +20,29 @@ export async function preparePrivateHostRoot(root:string):Promise<void>{
|
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export function createDailyDashboardSession(input:{ordinary:()=>OrdinaryChildren;declared:()=>DeclaredWorkState|undefined;rebind:(state:DeclaredWorkState)=>void;cwd:()=>string;env:Record<string,string|undefined>;author:string;home?:string}){
|
|
21
|
-
let current:null|Awaited<ReturnType<typeof startDailyDashboardHost>>=null,publishedSocket:string|undefined,closing:null|{present:true;closing:true;evidenceDigest:string;expiresAt:number}=null;
|
|
23
|
+
let ended=false,current:null|Awaited<ReturnType<typeof startDailyDashboardHost>>=null,publishedSocket:string|undefined,closing:null|{present:true;closing:true;evidenceDigest:string;expiresAt:number}=null;
|
|
22
24
|
return Object.freeze({
|
|
25
|
+
get running(){return current!==null;},
|
|
26
|
+
async frame(){if(!current)throw Error("Start /grants host first");return current.host.frame();},
|
|
27
|
+
async choose(key:string,binding?:{tip:string;requestDigest:string}){if(!current)throw Error("Start /grants host first");if(binding)return current.host.humanAction(key,binding);const frame=await current.host.frame(),action=frame.actions.find(a=>a.key===key);if(!action)throw Error("Action unavailable at the current boundary");return current.host.humanAction(key,{tip:frame.tip,requestDigest:action.requestDigest});},
|
|
23
28
|
async run(target:string){
|
|
29
|
+
if(ended)throw Error("Original Pi session has ended; no host replacement or control recovery");
|
|
30
|
+
if(!target)target=`daily-${randomUUID().slice(0,8)}`;
|
|
24
31
|
if(target==="stop"){
|
|
25
32
|
if(!current)return "daily host is not running";await current.close();current=null;closing=null;if(publishedSocket&&input.env[ENV_DASHBOARD_HOST_SOCKET]===publishedSocket)delete input.env[ENV_DASHBOARD_HOST_SOCKET];publishedSocket=undefined;return "daily host stopped; no child was cancelled";
|
|
26
33
|
}
|
|
27
|
-
if(target==="closing"){if(!current)throw Error("daily host is not running");const expiresAt=Date.now()+120_000;closing={present:true,closing:true,evidenceDigest:createHash("sha256").update(`${input.cwd()}\0${expiresAt}\0${randomUUID()}`).digest("hex"),expiresAt};return "explicit closing presence recorded for two minutes; pause new dispatch and finish/cancel active attempts, then choose
|
|
34
|
+
if(target==="closing"){if(!current)throw Error("daily host is not running");const expiresAt=Date.now()+120_000;closing={present:true,closing:true,evidenceDigest:createHash("sha256").update(`${input.cwd()}\0${expiresAt}\0${randomUUID()}`).digest("hex"),expiresAt};return "explicit closing presence recorded for two minutes; pause new dispatch and finish/cancel active attempts, then choose Prepare retained case cards in the dashboard. No card is counted as delivered until visibly acknowledged.";}
|
|
28
35
|
if(!idPattern.test(target))throw Error("usage — /grants host <fresh-id> | closing | stop");if(current)throw Error("daily host already running in this session; use closing or stop");
|
|
29
|
-
const declared=input.declared();if(!declared)throw Error("declare current work first with
|
|
36
|
+
const declared=input.declared();if(!declared)throw Error("declare current work first with /grants work new");
|
|
30
37
|
const home=input.home??homedir(),paths=dailyDashboardPaths(input.cwd(),target,home),root=join(home,".local","state","pi-daddy","hosts");await preparePrivateHostRoot(root);await mkdir(paths.socketDirectory,{recursive:true,mode:0o700});
|
|
31
38
|
try{await lstat(paths.directory);throw Error(`host ID ${target} already has preserved state; choose a fresh host ID (the existing state was not opened, deleted or reused)`);}catch(error){if((error as NodeJS.ErrnoException).code!=="ENOENT")throw error;}
|
|
32
|
-
const bridge=(globalThis as Record<PropertyKey,unknown>)[BRIDGE];if(!bridge)throw Error("loaded skill-harness extension bridge unavailable; load the
|
|
33
|
-
|
|
39
|
+
const bridge=(globalThis as Record<PropertyKey,unknown>)[BRIDGE];if(!bridge)throw Error("loaded skill-harness extension bridge unavailable; load the skill-harness extension in this Pi session");
|
|
40
|
+
const harness=adoptDashboardHarnessBridge(bridge),savedLearning=await readProductJson(join(input.cwd(),".pi","learning-workspace.json")),learning=savedLearning?await loadLearningConnection(input.cwd(),declared,learningHarness(harness),input.author):null;
|
|
41
|
+
try{current=await startDailyDashboardHost({id:target,cwd:input.cwd(),directory:paths.directory,socketPath:paths.socketPath,declared,ordinary:input.ordinary(),harness,author:input.author,...(learning?{learning:learning.connection}:{}),presence:()=>closing,onDeclaredWorkChanged:input.rebind});}catch(error){if((error as NodeJS.ErrnoException).code==="EEXIST")throw Error(`host ID ${target} could not start because state already exists; choose a fresh host ID. Existing state was preserved.`);throw error;}
|
|
34
42
|
publishedSocket=current.socketPath;input.env[ENV_DASHBOARD_HOST_SOCKET]=publishedSocket;
|
|
35
|
-
return `daily host started at ${publishedSocket}; current declared work/facts were captured. Run /grants dashboard
|
|
43
|
+
return `daily host started at ${publishedSocket}; current declared work/facts were captured. Run /grants dashboard for numbered actions or /grants work for work setup. Runtime remains unaccepted.`;
|
|
36
44
|
},
|
|
37
|
-
|
|
45
|
+
/** Called only when the original Pi session ends; no hold release or effect reconciliation. */
|
|
46
|
+
async close(){ended=true;if(current)await current.endSession();if(publishedSocket&&input.env[ENV_DASHBOARD_HOST_SOCKET]===publishedSocket)delete input.env[ENV_DASHBOARD_HOST_SOCKET];current=null;closing=null;publishedSocket=undefined;}
|
|
38
47
|
});
|
|
39
48
|
}
|
|
@@ -94,7 +94,7 @@ async function executeChildBody(input: {
|
|
|
94
94
|
childId: string;
|
|
95
95
|
executionId: string;
|
|
96
96
|
parentExecutionId: string | null;
|
|
97
|
-
toolCallId?: string;
|
|
97
|
+
toolCallId?: string; declaredWork?: GrantsSession["declaredWork"];
|
|
98
98
|
cwd: string;
|
|
99
99
|
preparedWorkspace?: PreparedWorkspace;
|
|
100
100
|
signal?: AbortSignal;
|
|
@@ -128,7 +128,7 @@ async function executeChildBody(input: {
|
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
const workAttempt = await beginDeclaredWorkAttempt({
|
|
131
|
-
session, plan, childId, executionId, parentExecutionId, toolCallId: input.toolCallId,
|
|
131
|
+
session, plan, childId, executionId, parentExecutionId, toolCallId: input.toolCallId, declaredWork: input.declaredWork,
|
|
132
132
|
preparedWorkspace, configuredTimeoutMs, startedAt,
|
|
133
133
|
});
|
|
134
134
|
|
|
@@ -76,12 +76,12 @@ export interface GrantsCommandContext {
|
|
|
76
76
|
const PREVIEW_LIMIT = 12;
|
|
77
77
|
|
|
78
78
|
/** The verbs `/grants` answers to. Anything else is refused rather than silently treated as no verb. */
|
|
79
|
-
const KNOWN_SUBCOMMANDS: readonly string[] = ["init", "host", "variants", "dashboard", "ledger", "approvals", "revoke"];
|
|
79
|
+
const KNOWN_SUBCOMMANDS: readonly string[] = ["work", "learning", "init", "host", "variants", "dashboard", "ledger", "approvals", "revoke"];
|
|
80
80
|
|
|
81
81
|
export const grantsCommand = {
|
|
82
82
|
description:
|
|
83
83
|
"Show this session's capability grant, delegation depth, and known agent-type ceilings; " +
|
|
84
|
-
"/grants
|
|
84
|
+
"/grants work (setup/run/results) | /grants learning | /grants host [fresh-id]|stop | /grants dashboard | /grants approvals | /grants ledger",
|
|
85
85
|
handler: async (args: string, ctx: any) => {
|
|
86
86
|
// Everything this command may see, named in one place. Previously these were whatever happened to be in
|
|
87
87
|
// the enclosing closure — which is how a diagnostic came to disagree with the enforcer (R-28).
|
package/extensions/grants.ts
CHANGED
|
@@ -48,6 +48,8 @@ import { defaultDashboardPaths, offerDashboardHandshake, openDashboardCommand }
|
|
|
48
48
|
import { associateOrdinaryHost, ordinaryChildrenFor } from "../src/ordinary-children.ts";
|
|
49
49
|
import { loadDeclaredWork } from "../src/work-command.ts";
|
|
50
50
|
import { createDailyDashboardSession } from "./daily-dashboard-session.ts";
|
|
51
|
+
import { createWorkSession } from "./work-session.ts";
|
|
52
|
+
import { createLearningSession } from "./learning-session.ts";
|
|
51
53
|
import { replacePublishedDailyWork, type PublishedDailyWork } from "./daily-work-session.ts";
|
|
52
54
|
export default function (pi: ExtensionAPI) {
|
|
53
55
|
// The path pi loads as the extension, so a child granted `tool:delegate` can be started with `-e <this>`.
|
|
@@ -70,6 +72,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
70
72
|
// dispatch surface; afterwards this callback refreshes their model-facing spawnable-definition text.
|
|
71
73
|
const delegation = { refreshSpawnable: () => {} };
|
|
72
74
|
const publishedDailyWork: PublishedDailyWork = {};
|
|
75
|
+
const workSession=createWorkSession(session,dailyHost,()=>replacePublishedDailyWork(process.env,publishedDailyWork,session.declaredWork));
|
|
76
|
+
const learningSession=createLearningSession(dailyHost);
|
|
73
77
|
|
|
74
78
|
pi.on("session_start", async (_event, ctx) => {
|
|
75
79
|
// Real SDK contexts always supply SessionManager. The fallback keeps lightweight wiring fixtures
|
|
@@ -253,7 +257,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
253
257
|
* installed here for the reason R-62 gives: it would turn pi's "interrupt this turn" into "exit pi".
|
|
254
258
|
*/
|
|
255
259
|
pi.on("session_shutdown", async () => {
|
|
256
|
-
await dailyHost.close().catch(() => undefined);
|
|
260
|
+
await workSession.close(); await dailyHost.close().catch(() => undefined);
|
|
257
261
|
});
|
|
258
262
|
|
|
259
263
|
pi.on("agent_settled", async () => {
|
|
@@ -349,7 +353,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
349
353
|
...grantsCommand,
|
|
350
354
|
// Built per invocation and spelled out field by field, rather than passing the session whole: what a
|
|
351
355
|
// read-only diagnostic may see is a decision, and `GrantsCommandContext` is where it is recorded.
|
|
352
|
-
handler: (args, ctx) =>
|
|
356
|
+
handler: (args, ctx) => /^learning(?:\s|$)/.test(args.trim()) ? learningSession.run(args.trim().slice(8).trim(),ctx).catch(error=>{ctx.ui.notify(String(error),"error");}) : /^work(?:\s|$)/.test(args.trim()) ? workSession.run(args.trim().slice(4).trim(),ctx).catch(error=>{ctx.ui.notify(String(error),"error");}) :
|
|
353
357
|
grantsCommand.handler(args, {
|
|
354
358
|
...ctx,
|
|
355
359
|
grants: {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { inspectWorkRun, type WorkRunInitial } from "../src/work-run.ts";
|
|
5
|
+
import type { ControlBinding } from "../src/control-journal.ts";
|
|
6
|
+
import { readProductJson } from "../src/product-files.ts";
|
|
7
|
+
import { readDailySnapshot } from "../src/daily-view-input.ts";
|
|
8
|
+
import { experimentHash } from "../src/experiment-contract.ts";
|
|
9
|
+
import type { LearningHarness, LearningWorkspace } from "../src/learning-connection.ts";
|
|
10
|
+
import type { DeclaredWorkState } from "../src/work-command.ts";
|
|
11
|
+
import type { ProductionObservation } from "../src/vendor/adoption.ts";
|
|
12
|
+
|
|
13
|
+
/** Link actual later-run bytes/pin; only an explicit independent human supplies outcome labels. */
|
|
14
|
+
export async function linkLatestWorkOutcome(ctx: ExtensionCommandContext, state: DeclaredWorkState, workspace: LearningWorkspace, harness: LearningHarness) {
|
|
15
|
+
const binding = await readProductJson(join(ctx.cwd, ".pi", "work-last-run.json")) as ControlBinding<WorkRunInitial> | null;
|
|
16
|
+
if (!binding) throw Error("No retained later bounded order is available");
|
|
17
|
+
const run = await inspectWorkRun(binding), pin = binding.initial.policyPin;
|
|
18
|
+
if (!pin?.adoptionId) throw Error("This run was not pinned to an adoption. It cannot establish an adopted outcome.");
|
|
19
|
+
const comparisons = workspace.inspect(Date.now()).comparisons.filter(c => c.state !== "deferred" && workspace.preparedAdoption(c.name)?.id === pin.adoptionId);
|
|
20
|
+
if(!comparisons.length)throw Error("No ready learning comparison matches this run's actual adoption pin. Reconnect original evidence before linking outcomes.");
|
|
21
|
+
const labels = comparisons.map((c, i) => `${i + 1}. ${c.title}`), picked = await ctx.ui.select("Observed adoption", labels), comparison = comparisons[labels.indexOf(picked ?? "")]; if (!comparison) return;
|
|
22
|
+
const rows = run.tasks.filter(t => t.resultPath && t.resultDigest), tasks = rows.map((t, i) => `${i + 1}. ${t.id}: ${t.state}`), selected = await ctx.ui.select("Actual retained output", tasks), row = rows[tasks.indexOf(selected ?? "")]; if (!row) return;
|
|
23
|
+
if (row.resultPath !== join(binding.directory, `${row.id}.txt`)) throw Error("retained result path changed");
|
|
24
|
+
const source = await readDailySnapshot(row.resultPath, 1024 * 1024);
|
|
25
|
+
if (source.status !== "read" || source.sha256 !== row.resultDigest) throw Error("retained output missing or mismatched");
|
|
26
|
+
await ctx.ui.editor("Complete retained output (controls escaped; editor edits discarded)", new TextDecoder("utf8", { fatal: true }).decode(source.bytes).replace(/[\p{Cc}\p{Cf}]/gu, c => c === "\n" || c === "\t" ? c : `\\u${c.charCodeAt(0).toString(16).padStart(4,"0")}`));
|
|
27
|
+
const outcomes = ["Unknown / not independently judged", "Independently confirmed success", "Independently confirmed defect"], outcome = await ctx.ui.select("Observed quality — runtime success is not a label", outcomes); if (!outcome) return;
|
|
28
|
+
const note = await ctx.ui.editor("Independent observation note / evidence (do not infer acceptance)", ""); if (!note?.trim()) return;
|
|
29
|
+
if (!await ctx.ui.confirm("Retain this scoped observation?", "This records an explicit human observation of these exact later-work bytes. Acceptance remains absent. It does not calibrate a detector, certify held-out improvement or trigger rollback.")) return;
|
|
30
|
+
const root = workspace.configuration().archiveRoot;
|
|
31
|
+
const artifact = harness.retainArchiveSource(root, { sourceId: `later-output-${source.sha256.slice(0,24)}`, parser: { id: "producer-work-output", version: "1" }, retention: "exact", bytes: source.bytes }).manifestId;
|
|
32
|
+
const evidence = harness.retainArchiveSource(root, { sourceId: `later-observation-${randomUUID()}`, parser: { id: "producer-work-observation", version: "1" }, retention: "exact", bytes: Buffer.from(JSON.stringify({ run: binding.initial, task: row, note, author: workspace.configuration().author })) }).manifestId;
|
|
33
|
+
const input: ProductionObservation = { id: `work-observation-${experimentHash({run:binding.initial.runId,task:row.id,note,outcome}).slice(0,24)}`, adoptionId: pin.adoptionId, candidateDigest: pin.candidateDigest, scopeDigest: pin.scopeDigest, originalRequirementDigest: binding.initial.selectionDigest, currentRequirementDigest: state.selectedSnapshot.snapshot.digest, outcome: outcomes.indexOf(outcome) === 1 ? "success" : outcomes.indexOf(outcome) === 2 ? "confirmed-defect" : "unknown", acceptanceDigest: null, acceptedArtifactDigest: null, observedArtifactDigest: source.sha256, evidence: [artifact, evidence] };
|
|
34
|
+
const preview = workspace.previewObservation(comparison.name, input);
|
|
35
|
+
if (!await ctx.ui.confirm("Confirm classified outcome", JSON.stringify(preview.classification))) return;
|
|
36
|
+
const retained = workspace.observe(comparison.name, input, [preview.digest]);
|
|
37
|
+
ctx.ui.notify(`Observed outcome retained (${retained.manifestId.slice(0,12)}). No acceptance, calibration or automatic rollback inferred.`, "info");
|
|
38
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { mkdir } from "node:fs/promises";
|
|
5
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { adoptDashboardHarnessBridge } from "../src/dashboard-harness.ts";
|
|
7
|
+
import { learningHarness, bindLearningConnection, loadLearningConnection, learningScopeDigest, type LearningUI } from "../src/learning-connection.ts";
|
|
8
|
+
import { loadDeclaredWork } from "../src/work-command.ts";
|
|
9
|
+
import { privateDirectory } from "../src/product-files.ts";
|
|
10
|
+
import { workPolicyMenu } from "./work-policy-session.ts";
|
|
11
|
+
import { linkLatestWorkOutcome } from "./learning-outcome-session.ts";
|
|
12
|
+
import type { createDailyDashboardSession } from "./daily-dashboard-session.ts";
|
|
13
|
+
|
|
14
|
+
/** Normal human-only entrypoint: delegates canonical learning decisions to the loaded harness wizard. */
|
|
15
|
+
export function createLearningSession(host: ReturnType<typeof createDailyDashboardSession>) {
|
|
16
|
+
let busy = false;
|
|
17
|
+
return { async run(_verb: string, ctx: ExtensionCommandContext) {
|
|
18
|
+
if (busy) throw Error("A learning dialog is already open");
|
|
19
|
+
if (!ctx.hasUI) throw Error("Open /grants learning in interactive Pi");
|
|
20
|
+
busy = true;
|
|
21
|
+
try {
|
|
22
|
+
const declared = await loadDeclaredWork(join(ctx.cwd, ".pi", "work-current.json"));
|
|
23
|
+
if (!declared) throw Error("Declare work with /grants work first so learning has an exact scope");
|
|
24
|
+
const bridge = (globalThis as Record<PropertyKey, unknown>)[Symbol.for("skill-harness.dashboard-host.v1")];
|
|
25
|
+
const h = learningHarness(adoptDashboardHarnessBridge(bridge));
|
|
26
|
+
let connected: Awaited<ReturnType<typeof loadLearningConnection>> = null;
|
|
27
|
+
try { connected = await loadLearningConnection(ctx.cwd, declared, h, "local-operator"); }
|
|
28
|
+
catch (error) { ctx.ui.notify(String(error), "warning"); }
|
|
29
|
+
const menu = ["Open cases, comparisons, trust and outcomes", "Create learning workspace for selected work", "Connect existing learning workspace", "Adoption / rollback for next orders", "Retain current work for learning", "Link observed result from latest adopted order"];
|
|
30
|
+
const choice = await ctx.ui.select(connected ? "Learning — deliberate review (not earned automatic exposure)" : "Learning not connected", connected ? menu : menu.slice(1, 3));
|
|
31
|
+
if (!choice) return;
|
|
32
|
+
if ([menu[1], menu[2]].includes(choice)) {
|
|
33
|
+
if (host.running && !await ctx.ui.confirm("Reconnect learning after stopping the current host?", "Old host evidence is preserved. No child is cancelled. Start /grants host again after configuration.")) return;
|
|
34
|
+
if (host.running) await host.run("stop");
|
|
35
|
+
let directory: string;
|
|
36
|
+
if (choice === menu[1]) {
|
|
37
|
+
if (!await ctx.ui.confirm("Enable scoped learning retention?", "Case/comparison/trust decisions and explicitly selected output artifacts will be retained privately. No model is called; no quality, calibration or authority is invented.")) return;
|
|
38
|
+
const root = join(homedir(), ".local", "state", "pi-daddy", "learning"); await mkdir(root, { recursive: true, mode: 0o700 }); await privateDirectory(root);
|
|
39
|
+
const instance = join(root, randomUUID()); await privateDirectory(instance);
|
|
40
|
+
directory = join(instance, "workspace");
|
|
41
|
+
h.createLearningWorkspace(directory, { archiveRoot: join(instance, "archive"), scopeDigest: learningScopeDigest(declared), population: `work:${learningScopeDigest(declared).slice(0, 32)}`, author: "local-operator" });
|
|
42
|
+
} else { const path = await ctx.ui.input("Existing learning workspace directory"); if (!path) return; directory = path.trim(); }
|
|
43
|
+
await bindLearningConnection(ctx.cwd, declared, directory, h, "local-operator");
|
|
44
|
+
connected = await loadLearningConnection(ctx.cwd, declared, h, "local-operator");
|
|
45
|
+
ctx.ui.notify("Learning connected to exact work scope. Start /grants host to show it in Herdr.", "info");
|
|
46
|
+
}
|
|
47
|
+
if (!connected) throw Error("Connect learning first");
|
|
48
|
+
if (choice === menu[5]) { await linkLatestWorkOutcome(ctx, declared, connected.workspace, h); return; }
|
|
49
|
+
if (choice === menu[3]) { await workPolicyMenu(ctx, declared, connected.workspace, h); return; }
|
|
50
|
+
if (choice === menu[4]) {
|
|
51
|
+
if (!host.running) throw Error("Start /grants host first; it captures and binds the current work case batch through the existing archive policy");
|
|
52
|
+
await host.choose("refresh-current-work"); ctx.ui.notify("Current work retained. Open Learning for real case readiness; this is not a defect label.", "info"); return;
|
|
53
|
+
}
|
|
54
|
+
const ui: LearningUI = { select: (title, choices) => ctx.ui.select(title, choices), input: (title, initial) => ctx.ui.input(title, initial), editor: (title, text) => ctx.ui.editor(title, text), confirm: (title, detail) => ctx.ui.confirm(title, detail), notify: (text, level) => ctx.ui.notify(text, level ?? "info") };
|
|
55
|
+
await h.runLearningWizard({ directory: connected.connection.directory, cwd: ctx.cwd, ui });
|
|
56
|
+
if (host.running) ctx.ui.notify("Learning saved. If you changed trust configuration, stop/start /grants host explicitly to bind it; attention is not reset.", "info");
|
|
57
|
+
} finally { busy = false; }
|
|
58
|
+
} };
|
|
59
|
+
}
|