pi-daddy 0.41.0 → 0.43.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 +37 -0
- package/contracts/ledger-record/v1/fixtures/capability-decision.json +3 -1
- package/contracts/ledger-record/v1/fixtures/child-lifecycle.json +25 -0
- package/contracts/ledger-record/v1/fixtures/episode-cost-gate.json +14 -0
- package/contracts/ledger-record/v1/fixtures/episode-outcome.json +12 -0
- package/contracts/ledger-record/v1/fixtures/ledger-record.jsonl +6 -3
- package/contracts/ledger-record/v1/fixtures/session-config.json +14 -0
- package/contracts/ledger-record/v1/governance-event.schema.json +356 -0
- package/dist/advisors/advisor.d.ts +24 -1
- package/dist/advisors/advisor.d.ts.map +1 -1
- package/dist/advisors/advisor.js +19 -1
- package/dist/advisors/advisor.js.map +1 -1
- package/dist/advisors/decider.d.ts +11 -0
- package/dist/advisors/decider.d.ts.map +1 -1
- package/dist/advisors/decider.js.map +1 -1
- package/dist/advisors/jev.d.ts.map +1 -1
- package/dist/advisors/jev.js +32 -1
- package/dist/advisors/jev.js.map +1 -1
- package/dist/cli.d.ts +7 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +87 -1
- package/dist/cli.js.map +1 -1
- package/dist/executors/activity-session.d.ts +13 -0
- package/dist/executors/activity-session.d.ts.map +1 -1
- package/dist/executors/activity-session.js +44 -2
- package/dist/executors/activity-session.js.map +1 -1
- package/dist/governance/episode-cost-gate.d.ts +31 -0
- package/dist/governance/episode-cost-gate.d.ts.map +1 -0
- package/dist/governance/episode-cost-gate.js +112 -0
- package/dist/governance/episode-cost-gate.js.map +1 -0
- package/dist/governance/ledger-events.d.ts +108 -1
- package/dist/governance/ledger-events.d.ts.map +1 -1
- package/dist/governance/ledger-events.js +64 -0
- package/dist/governance/ledger-events.js.map +1 -1
- package/dist/governance/ledger-v3-validation.d.ts.map +1 -1
- package/dist/governance/ledger-v3-validation.js +124 -0
- package/dist/governance/ledger-v3-validation.js.map +1 -1
- package/dist/governance/ledger.d.ts +8 -2
- package/dist/governance/ledger.d.ts.map +1 -1
- package/dist/governance/ledger.js +18 -2
- package/dist/governance/ledger.js.map +1 -1
- package/dist/kernel/context-handoff.d.ts.map +1 -1
- package/dist/kernel/context-handoff.js +4 -1
- package/dist/kernel/context-handoff.js.map +1 -1
- package/dist/kernel/definitions.d.ts +5 -0
- package/dist/kernel/definitions.d.ts.map +1 -1
- package/dist/kernel/definitions.js +30 -1
- package/dist/kernel/definitions.js.map +1 -1
- package/dist/kernel/delegate-types.d.ts +4 -0
- package/dist/kernel/delegate-types.d.ts.map +1 -1
- package/dist/kernel/delegate-types.js.map +1 -1
- package/dist/kernel/delegate.d.ts.map +1 -1
- package/dist/kernel/delegate.js +20 -5
- package/dist/kernel/delegate.js.map +1 -1
- package/dist/kernel/env-names.d.ts +5 -0
- package/dist/kernel/env-names.d.ts.map +1 -1
- package/dist/kernel/env-names.js +9 -0
- package/dist/kernel/env-names.js.map +1 -1
- package/dist/kernel/grant-env.d.ts +2 -0
- package/dist/kernel/grant-env.d.ts.map +1 -1
- package/dist/kernel/grant-env.js +1 -0
- package/dist/kernel/grant-env.js.map +1 -1
- package/dist/kernel/propagation.d.ts +2 -2
- package/dist/kernel/propagation.d.ts.map +1 -1
- package/dist/kernel/propagation.js +7 -2
- package/dist/kernel/propagation.js.map +1 -1
- package/dist/products/dashboard-cli.d.ts +4 -0
- package/dist/products/dashboard-cli.d.ts.map +1 -1
- package/dist/products/dashboard-cli.js +32 -4
- package/dist/products/dashboard-cli.js.map +1 -1
- package/dist/products/dashboard-handshake.d.ts +8 -0
- package/dist/products/dashboard-handshake.d.ts.map +1 -1
- package/dist/products/dashboard-handshake.js +3 -0
- package/dist/products/dashboard-handshake.js.map +1 -1
- package/dist/products/dashboard-herdr.d.ts +4 -0
- package/dist/products/dashboard-herdr.d.ts.map +1 -1
- package/dist/products/dashboard-herdr.js +10 -2
- package/dist/products/dashboard-herdr.js.map +1 -1
- package/dist/products/dashboard-projection.d.ts.map +1 -1
- package/dist/products/dashboard-projection.js +13 -6
- package/dist/products/dashboard-projection.js.map +1 -1
- package/dist/products/dashboard-render.d.ts +10 -0
- package/dist/products/dashboard-render.d.ts.map +1 -1
- package/dist/products/dashboard-render.js +9 -0
- package/dist/products/dashboard-render.js.map +1 -1
- package/dist/products/dashboard-session-client.d.ts +17 -0
- package/dist/products/dashboard-session-client.d.ts.map +1 -0
- package/dist/products/dashboard-session-client.js +28 -0
- package/dist/products/dashboard-session-client.js.map +1 -0
- package/dist/products/episode-outcomes.d.ts +32 -0
- package/dist/products/episode-outcomes.d.ts.map +1 -0
- package/dist/products/episode-outcomes.js +280 -0
- package/dist/products/episode-outcomes.js.map +1 -0
- package/dist/products/episode-report.d.ts +59 -0
- package/dist/products/episode-report.d.ts.map +1 -0
- package/dist/products/episode-report.js +311 -0
- package/dist/products/episode-report.js.map +1 -0
- package/extensions/chain-ledger.ts +2 -0
- package/extensions/chain-plan.ts +4 -2
- package/extensions/context-shape.ts +19 -4
- package/extensions/dashboard-session-server.ts +86 -0
- package/extensions/definition-runtime.ts +93 -0
- package/extensions/delegate-chain.ts +31 -2
- package/extensions/delegation-ledger.ts +2 -0
- package/extensions/delegation.ts +24 -0
- package/extensions/effort-advice.ts +12 -0
- package/extensions/execute-child.ts +148 -9
- package/extensions/grants-command.ts +32 -2
- package/extensions/grants.ts +23 -1
- package/extensions/pruning-advice.ts +3 -1
- package/extensions/run-delegation.ts +54 -6
- package/extensions/session-model-prompt.ts +179 -0
- package/extensions/session.ts +104 -3
- package/package.json +1 -1
- package/src/advisors/advisor.ts +45 -2
- package/src/advisors/decider.ts +8 -0
- package/src/advisors/jev.ts +34 -1
- package/src/cli.ts +90 -2
- package/src/executors/activity-session.ts +56 -2
- package/src/governance/episode-cost-gate.ts +128 -0
- package/src/governance/ledger-events.ts +173 -1
- package/src/governance/ledger-v3-validation.ts +146 -0
- package/src/governance/ledger.ts +29 -1
- package/src/kernel/context-handoff.ts +5 -1
- package/src/kernel/definitions.ts +32 -1
- package/src/kernel/delegate-types.ts +4 -0
- package/src/kernel/delegate.ts +22 -5
- package/src/kernel/env-names.ts +10 -0
- package/src/kernel/grant-env.ts +3 -0
- package/src/kernel/propagation.ts +7 -0
- package/src/products/dashboard-cli.ts +32 -4
- package/src/products/dashboard-handshake.ts +5 -0
- package/src/products/dashboard-herdr.ts +15 -1
- package/src/products/dashboard-projection.ts +13 -6
- package/src/products/dashboard-render.ts +11 -0
- package/src/products/dashboard-session-client.ts +35 -0
- package/src/products/episode-outcomes.ts +317 -0
- package/src/products/episode-report.ts +372 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/** Read-only episode accounting over the governance ledger and activity timeline. */
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import { readRecords } from "../governance/record.js";
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
const TERMINAL = new Set(["completed", "failed"]);
|
|
7
|
+
const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
8
|
+
const number = (value) => (typeof value === "number" && Number.isFinite(value) ? value : 0);
|
|
9
|
+
const text = (value) => (typeof value === "string" ? value : "");
|
|
10
|
+
const joined = (values) => [...values].filter(Boolean).sort().join(",");
|
|
11
|
+
function timestamp(value, fallback) {
|
|
12
|
+
const parsed = Date.parse(typeof value === "string" ? value : fallback);
|
|
13
|
+
return Number.isFinite(parsed) ? parsed : Date.parse(fallback);
|
|
14
|
+
}
|
|
15
|
+
function episodeOf(episodes, id, at) {
|
|
16
|
+
const existing = episodes.get(id);
|
|
17
|
+
if (existing) {
|
|
18
|
+
existing.startedMs = Math.min(existing.startedMs, at);
|
|
19
|
+
existing.endedMs = Math.max(existing.endedMs, at);
|
|
20
|
+
return existing;
|
|
21
|
+
}
|
|
22
|
+
const episode = {
|
|
23
|
+
episode: id,
|
|
24
|
+
startedMs: at,
|
|
25
|
+
endedMs: at,
|
|
26
|
+
definitions: new Set(),
|
|
27
|
+
hashes: new Set(),
|
|
28
|
+
models: new Set(),
|
|
29
|
+
modelIds: new Set(),
|
|
30
|
+
modelSources: new Set(),
|
|
31
|
+
thinkingLevels: new Set(),
|
|
32
|
+
thinkingSources: new Set(),
|
|
33
|
+
inputTokens: 0,
|
|
34
|
+
outputTokens: 0,
|
|
35
|
+
cacheReadTokens: 0,
|
|
36
|
+
reasoningTokens: 0,
|
|
37
|
+
cost: 0,
|
|
38
|
+
compactions: 0,
|
|
39
|
+
turns: 0,
|
|
40
|
+
children: new Set(),
|
|
41
|
+
terminalExecutions: new Set(),
|
|
42
|
+
};
|
|
43
|
+
episodes.set(id, episode);
|
|
44
|
+
return episode;
|
|
45
|
+
}
|
|
46
|
+
function percentile(values, fraction) {
|
|
47
|
+
if (values.length === 0)
|
|
48
|
+
return 0;
|
|
49
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
50
|
+
return sorted[Math.ceil(sorted.length * fraction) - 1] ?? 0;
|
|
51
|
+
}
|
|
52
|
+
export function reportEpisodes(input) {
|
|
53
|
+
const episodes = new Map();
|
|
54
|
+
const outcomes = new Map();
|
|
55
|
+
const ledger = readRecords(input.ledgerText);
|
|
56
|
+
if (ledger.damage)
|
|
57
|
+
throw new Error(`grants ledger damaged at line ${ledger.damage.line}: ${ledger.damage.reason}`);
|
|
58
|
+
for (const record of ledger.records) {
|
|
59
|
+
if (!object(record.body))
|
|
60
|
+
continue;
|
|
61
|
+
const body = record.body;
|
|
62
|
+
const episodeId = text(body.episodeId);
|
|
63
|
+
if (!episodeId)
|
|
64
|
+
continue;
|
|
65
|
+
if (body.event === "episode_outcome") {
|
|
66
|
+
if (["positive", "negative", "unknown"].includes(text(body.label)))
|
|
67
|
+
outcomes.set(episodeId, body.label);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const episode = episodeOf(episodes, episodeId, timestamp(body.ts, record.at));
|
|
71
|
+
if (text(body.definitionHash))
|
|
72
|
+
episode.hashes.add(text(body.definitionHash).slice(0, 12));
|
|
73
|
+
if (body.event === "capability_decision") {
|
|
74
|
+
if (body.agentType !== "delegate")
|
|
75
|
+
episode.definitions.add(text(body.agentType));
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (body.event !== "child_lifecycle")
|
|
79
|
+
continue;
|
|
80
|
+
const executionId = text(body.executionId);
|
|
81
|
+
if (executionId)
|
|
82
|
+
episode.children.add(executionId);
|
|
83
|
+
if (!TERMINAL.has(text(body.state)) || episode.terminalExecutions.has(executionId))
|
|
84
|
+
continue;
|
|
85
|
+
episode.terminalExecutions.add(executionId);
|
|
86
|
+
if (object(body.resolvedModel)) {
|
|
87
|
+
const provider = text(body.resolvedModel.provider), modelId = text(body.resolvedModel.modelId);
|
|
88
|
+
if (modelId) {
|
|
89
|
+
episode.modelIds.add(modelId);
|
|
90
|
+
episode.models.add(provider ? `${provider}/${modelId}` : modelId);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (text(body.modelSource))
|
|
94
|
+
episode.modelSources.add(text(body.modelSource));
|
|
95
|
+
if (object(body.thinkingLevel)) {
|
|
96
|
+
if (text(body.thinkingLevel.level))
|
|
97
|
+
episode.thinkingLevels.add(text(body.thinkingLevel.level));
|
|
98
|
+
if (text(body.thinkingLevel.source))
|
|
99
|
+
episode.thinkingSources.add(text(body.thinkingLevel.source));
|
|
100
|
+
}
|
|
101
|
+
if (text(body.thinkingSource))
|
|
102
|
+
episode.thinkingSources.add(text(body.thinkingSource));
|
|
103
|
+
const usage = object(body.usage) ? body.usage : undefined;
|
|
104
|
+
if (object(body.tokenDetail)) {
|
|
105
|
+
episode.inputTokens += number(body.tokenDetail.inputTokens);
|
|
106
|
+
episode.outputTokens += number(body.tokenDetail.outputTokens);
|
|
107
|
+
episode.cacheReadTokens += number(body.tokenDetail.cacheReadTokens);
|
|
108
|
+
episode.reasoningTokens += number(body.tokenDetail.reasoningTokens);
|
|
109
|
+
}
|
|
110
|
+
else if (usage) {
|
|
111
|
+
episode.inputTokens += number(usage.input);
|
|
112
|
+
episode.outputTokens += number(usage.output);
|
|
113
|
+
episode.cacheReadTokens += number(usage.cacheRead);
|
|
114
|
+
episode.reasoningTokens += number(usage.reasoning);
|
|
115
|
+
}
|
|
116
|
+
if (usage && object(usage.cost))
|
|
117
|
+
episode.cost += number(usage.cost.total);
|
|
118
|
+
episode.compactions += number(body.compactionCount);
|
|
119
|
+
}
|
|
120
|
+
const activity = readRecords(input.activityText);
|
|
121
|
+
if (activity.damage)
|
|
122
|
+
throw new Error(`activity ledger damaged at line ${activity.damage.line}: ${activity.damage.reason}`);
|
|
123
|
+
for (const record of activity.records) {
|
|
124
|
+
if (record.kind !== "activity" || !object(record.body))
|
|
125
|
+
continue;
|
|
126
|
+
const event = record.body;
|
|
127
|
+
const episodeId = text(event.episodeId);
|
|
128
|
+
if (!episodeId)
|
|
129
|
+
continue;
|
|
130
|
+
const episode = episodeOf(episodes, episodeId, timestamp(event.at, record.at));
|
|
131
|
+
if (event.kind === "task_started" && !text(event.agentId))
|
|
132
|
+
episode.turns += 1;
|
|
133
|
+
}
|
|
134
|
+
const since = input.options.since ? Date.parse(input.options.since) : Number.NEGATIVE_INFINITY;
|
|
135
|
+
let rows = [...episodes.values()]
|
|
136
|
+
.map((episode) => ({
|
|
137
|
+
episode: episode.episode,
|
|
138
|
+
started: new Date(episode.startedMs).toISOString(),
|
|
139
|
+
definition: joined(episode.definitions),
|
|
140
|
+
definitionHash: joined(episode.hashes),
|
|
141
|
+
resolvedModel: joined(episode.models),
|
|
142
|
+
modelSource: joined(episode.modelSources),
|
|
143
|
+
thinkingLevel: joined(episode.thinkingLevels),
|
|
144
|
+
thinkingSource: joined(episode.thinkingSources),
|
|
145
|
+
inputTokens: episode.inputTokens,
|
|
146
|
+
outputTokens: episode.outputTokens,
|
|
147
|
+
cacheReadTokens: episode.cacheReadTokens,
|
|
148
|
+
reasoningTokens: episode.reasoningTokens,
|
|
149
|
+
cost: episode.cost,
|
|
150
|
+
compactions: episode.compactions,
|
|
151
|
+
turns: episode.turns,
|
|
152
|
+
durationMs: Math.max(0, episode.endedMs - episode.startedMs),
|
|
153
|
+
children: episode.children.size,
|
|
154
|
+
commit: input.commitByEpisode.get(episode.episode) ?? "",
|
|
155
|
+
outcome: outcomes.get(episode.episode) ?? "",
|
|
156
|
+
}))
|
|
157
|
+
.filter((row) => Date.parse(row.started) >= since)
|
|
158
|
+
.filter((row) => !input.options.definition || row.definition.split(",").includes(input.options.definition))
|
|
159
|
+
.filter((row) => {
|
|
160
|
+
if (!input.options.model)
|
|
161
|
+
return true;
|
|
162
|
+
const source = episodes.get(row.episode);
|
|
163
|
+
return source.modelIds.has(input.options.model) || source.models.has(input.options.model);
|
|
164
|
+
})
|
|
165
|
+
.sort((a, b) => Date.parse(a.started) - Date.parse(b.started) || a.episode.localeCompare(b.episode));
|
|
166
|
+
if (input.options.groupBy) {
|
|
167
|
+
const field = input.options.groupBy === "thinking"
|
|
168
|
+
? "thinkingLevel"
|
|
169
|
+
: input.options.groupBy === "model"
|
|
170
|
+
? "resolvedModel"
|
|
171
|
+
: "definition";
|
|
172
|
+
const groups = new Map();
|
|
173
|
+
for (const row of rows) {
|
|
174
|
+
const value = row[field] || "(none)";
|
|
175
|
+
const group = groups.get(value) ?? {
|
|
176
|
+
value,
|
|
177
|
+
count: 0,
|
|
178
|
+
inputTokens: 0,
|
|
179
|
+
outputTokens: 0,
|
|
180
|
+
cacheReadTokens: 0,
|
|
181
|
+
reasoningTokens: 0,
|
|
182
|
+
cost: 0,
|
|
183
|
+
};
|
|
184
|
+
group.count += 1;
|
|
185
|
+
group.inputTokens += row.inputTokens;
|
|
186
|
+
group.outputTokens += row.outputTokens;
|
|
187
|
+
group.cacheReadTokens += row.cacheReadTokens;
|
|
188
|
+
group.reasoningTokens += row.reasoningTokens;
|
|
189
|
+
group.cost += row.cost;
|
|
190
|
+
groups.set(value, group);
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
rows: [],
|
|
194
|
+
groups: [...groups.values()].sort((a, b) => a.value.localeCompare(b.value)),
|
|
195
|
+
groupBy: input.options.groupBy,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const costs = rows.map((row) => row.cost);
|
|
199
|
+
return {
|
|
200
|
+
rows,
|
|
201
|
+
totals: {
|
|
202
|
+
episodes: rows.length,
|
|
203
|
+
cost: costs.reduce((sum, value) => sum + value, 0),
|
|
204
|
+
tokens: rows.reduce((sum, row) => sum + row.inputTokens + row.outputTokens, 0),
|
|
205
|
+
p50EpisodeCost: percentile(costs, 0.5),
|
|
206
|
+
p95EpisodeCost: percentile(costs, 0.95),
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const escapeCell = (value) => String(value).replaceAll("|", "\\|").replaceAll("\n", " ");
|
|
211
|
+
const cost = (value) => value.toFixed(6).replace(/\.?0+$/, "") || "0";
|
|
212
|
+
function duration(ms) {
|
|
213
|
+
const seconds = Math.round(ms / 1000);
|
|
214
|
+
const minutes = Math.floor(seconds / 60);
|
|
215
|
+
return minutes ? `${minutes}m ${seconds % 60}s` : `${seconds}s`;
|
|
216
|
+
}
|
|
217
|
+
function table(headers, rows) {
|
|
218
|
+
return [
|
|
219
|
+
`| ${headers.join(" | ")} |`,
|
|
220
|
+
`| ${headers.map(() => "---").join(" | ")} |`,
|
|
221
|
+
...rows.map((row) => `| ${row.map(escapeCell).join(" | ")} |`),
|
|
222
|
+
].join("\n");
|
|
223
|
+
}
|
|
224
|
+
export function renderEpisodeReport(report, json) {
|
|
225
|
+
if (json)
|
|
226
|
+
return `${JSON.stringify(report, null, 2)}\n`;
|
|
227
|
+
if (report.groups && report.groupBy) {
|
|
228
|
+
const headers = [
|
|
229
|
+
report.groupBy,
|
|
230
|
+
"count",
|
|
231
|
+
"inputTokens",
|
|
232
|
+
"outputTokens",
|
|
233
|
+
"cacheReadTokens",
|
|
234
|
+
"reasoningTokens",
|
|
235
|
+
"cost",
|
|
236
|
+
];
|
|
237
|
+
return `${table(headers, report.groups.map((group) => [
|
|
238
|
+
group.value,
|
|
239
|
+
group.count,
|
|
240
|
+
group.inputTokens,
|
|
241
|
+
group.outputTokens,
|
|
242
|
+
group.cacheReadTokens,
|
|
243
|
+
group.reasoningTokens,
|
|
244
|
+
cost(group.cost),
|
|
245
|
+
]))}\n`;
|
|
246
|
+
}
|
|
247
|
+
const headers = [
|
|
248
|
+
"episode",
|
|
249
|
+
"started",
|
|
250
|
+
"definition",
|
|
251
|
+
"definitionHash",
|
|
252
|
+
"resolvedModel",
|
|
253
|
+
"modelSource",
|
|
254
|
+
"thinkingLevel",
|
|
255
|
+
"thinkingSource",
|
|
256
|
+
"inputTokens",
|
|
257
|
+
"outputTokens",
|
|
258
|
+
"cacheReadTokens",
|
|
259
|
+
"reasoningTokens",
|
|
260
|
+
"cost",
|
|
261
|
+
"compactions",
|
|
262
|
+
"turns",
|
|
263
|
+
"duration",
|
|
264
|
+
"children",
|
|
265
|
+
"commit",
|
|
266
|
+
"outcome",
|
|
267
|
+
];
|
|
268
|
+
const rows = report.rows.map((row) => [
|
|
269
|
+
row.episode,
|
|
270
|
+
row.started,
|
|
271
|
+
row.definition,
|
|
272
|
+
row.definitionHash,
|
|
273
|
+
row.resolvedModel,
|
|
274
|
+
row.modelSource,
|
|
275
|
+
row.thinkingLevel,
|
|
276
|
+
row.thinkingSource,
|
|
277
|
+
row.inputTokens,
|
|
278
|
+
row.outputTokens,
|
|
279
|
+
row.cacheReadTokens,
|
|
280
|
+
row.reasoningTokens,
|
|
281
|
+
cost(row.cost),
|
|
282
|
+
row.compactions,
|
|
283
|
+
row.turns,
|
|
284
|
+
duration(row.durationMs),
|
|
285
|
+
row.children,
|
|
286
|
+
row.commit,
|
|
287
|
+
row.outcome,
|
|
288
|
+
]);
|
|
289
|
+
const totals = report.totals;
|
|
290
|
+
const totalsTable = table(["episodes", "cost", "tokens", "p50 episode cost", "p95 episode cost"], [[totals.episodes, cost(totals.cost), totals.tokens, cost(totals.p50EpisodeCost), cost(totals.p95EpisodeCost)]]);
|
|
291
|
+
return `${table(headers, rows)}\n\n${totalsTable}\n`;
|
|
292
|
+
}
|
|
293
|
+
export function commitsFromGitLog(log) {
|
|
294
|
+
const commits = new Map();
|
|
295
|
+
for (const entry of log.split("\u001e")) {
|
|
296
|
+
const [sha, ...body] = entry.trim().split("\n");
|
|
297
|
+
if (!/^[a-f0-9]{8,40}$/i.test(sha ?? ""))
|
|
298
|
+
continue;
|
|
299
|
+
for (const match of body.join("\n").matchAll(/^Pi-Episode:\s*(\S+)\s*$/gim)) {
|
|
300
|
+
const episode = match[1].startsWith("episode:") ? match[1] : `episode:${match[1]}`;
|
|
301
|
+
if (!commits.has(episode))
|
|
302
|
+
commits.set(episode, sha.slice(0, 8));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return commits;
|
|
306
|
+
}
|
|
307
|
+
export async function commitsForRepo(cwd) {
|
|
308
|
+
const { stdout } = await execFileAsync("git", ["log", "--format=%H%n%B%x1e"], { cwd, maxBuffer: 16 * 1024 * 1024 });
|
|
309
|
+
return commitsFromGitLog(stdout);
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=episode-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"episode-report.js","sourceRoot":"","sources":["../../src/products/episode-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -41,6 +41,8 @@ export async function recordChainRefusal(input: {
|
|
|
41
41
|
humanDenied: input.approval?.humanDenied,
|
|
42
42
|
gateOutcome: input.approval?.gateOutcome,
|
|
43
43
|
definitionDigest: input.plan.definitionDigest,
|
|
44
|
+
definitionHash: input.plan.definitionHash,
|
|
45
|
+
definitionPackageVersion: input.plan.definitionPackageVersion,
|
|
44
46
|
taskDigest: input.plan.taskDigest,
|
|
45
47
|
correlation: input.plan.correlation,
|
|
46
48
|
// An approval banked before a later chain decline is a yes that led to no spawn, not the stale
|
package/extensions/chain-plan.ts
CHANGED
|
@@ -37,6 +37,7 @@ export async function planChain(
|
|
|
37
37
|
steps: ChainStep[],
|
|
38
38
|
executionIds: readonly string[],
|
|
39
39
|
preflightModel?: (model: string | undefined) => StructuredRefusal | undefined,
|
|
40
|
+
resolveModel?: (step: ChainStep) => string | undefined,
|
|
40
41
|
): Promise<ChainPlan> {
|
|
41
42
|
const requests: GateRequest[] = [];
|
|
42
43
|
const uses = new Map<number, Set<string>>();
|
|
@@ -49,12 +50,13 @@ export async function planChain(
|
|
|
49
50
|
const { stageHandoff: _stageHandoff, ...context } = await session.delegationContext();
|
|
50
51
|
|
|
51
52
|
for (const [index, step] of steps.entries()) {
|
|
53
|
+
const model = resolveModel?.(step) ?? step.model;
|
|
52
54
|
const plan = planDelegation(
|
|
53
55
|
{
|
|
54
56
|
task: step.task,
|
|
55
57
|
agent: step.agent,
|
|
56
58
|
tools: step.tools,
|
|
57
|
-
model
|
|
59
|
+
model,
|
|
58
60
|
thinking: step.thinking,
|
|
59
61
|
context: step.context,
|
|
60
62
|
correlation: step.workspace
|
|
@@ -71,7 +73,7 @@ export async function planChain(
|
|
|
71
73
|
},
|
|
72
74
|
);
|
|
73
75
|
|
|
74
|
-
const modelRefusal = preflightModel?.(
|
|
76
|
+
const modelRefusal = preflightModel?.(model);
|
|
75
77
|
if (modelRefusal) {
|
|
76
78
|
return {
|
|
77
79
|
requests: [],
|
|
@@ -7,7 +7,12 @@
|
|
|
7
7
|
* first. This shape exists to tell the model what the parameter is for.
|
|
8
8
|
*/
|
|
9
9
|
import { Type } from "typebox";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
CONTEXT_MODES,
|
|
12
|
+
DEFAULT_CONTEXT_TURNS,
|
|
13
|
+
MAX_CONTEXT_FILES,
|
|
14
|
+
MAX_CONTEXT_TURNS,
|
|
15
|
+
} from "../src/kernel/context-handoff.ts";
|
|
11
16
|
|
|
12
17
|
export function contextShape() {
|
|
13
18
|
return Type.Object({
|
|
@@ -18,13 +23,23 @@ export function contextShape() {
|
|
|
18
23
|
"What of YOUR session this sub-agent receives. none: nothing beyond its definition and the task. " +
|
|
19
24
|
"files: the contents of paths you name. pruned: recent turns of your session plus turns naming those " +
|
|
20
25
|
"files. summary: what you write in 'summary'. fork: your whole session (needs a human's approval). " +
|
|
21
|
-
"A definition's allowed-tools caps this; asking for more than it permits is refused."
|
|
26
|
+
"A definition's allowed-tools caps this; asking for more than it permits is refused. If the child has " +
|
|
27
|
+
"read tools in the same workspace, prefer none and ask it to inspect the checkout instead of embedding files.",
|
|
22
28
|
},
|
|
23
29
|
),
|
|
24
30
|
files: Type.Optional(
|
|
25
|
-
Type.Array(Type.String(
|
|
31
|
+
Type.Array(Type.String({ minLength: 1 }), {
|
|
32
|
+
maxItems: MAX_CONTEXT_FILES,
|
|
33
|
+
description: `At most ${MAX_CONTEXT_FILES} repository-relative paths, for files and pruned.`,
|
|
34
|
+
}),
|
|
26
35
|
),
|
|
27
36
|
summary: Type.Optional(Type.String({ description: "What the sub-agent needs to know, in your own words." })),
|
|
28
|
-
turns: Type.Optional(
|
|
37
|
+
turns: Type.Optional(
|
|
38
|
+
Type.Integer({
|
|
39
|
+
minimum: 1,
|
|
40
|
+
maximum: MAX_CONTEXT_TURNS,
|
|
41
|
+
description: `For pruned: how many recent turns to keep. Default ${DEFAULT_CONTEXT_TURNS}.`,
|
|
42
|
+
}),
|
|
43
|
+
),
|
|
29
44
|
});
|
|
30
45
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { chmod, rm } from "node:fs/promises";
|
|
3
|
+
import { createServer, type Server } from "node:net";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import type { GrantsSession } from "./session.ts";
|
|
7
|
+
import { saveSessionModelEdits, sessionModelRows } from "./session-model-prompt.ts";
|
|
8
|
+
|
|
9
|
+
export interface DashboardSessionEndpoint {
|
|
10
|
+
socketPath: string;
|
|
11
|
+
token: string;
|
|
12
|
+
close(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const endpoints = new WeakMap<GrantsSession, Promise<DashboardSessionEndpoint>>();
|
|
16
|
+
|
|
17
|
+
export function ensureDashboardSessionServer(session: GrantsSession): Promise<DashboardSessionEndpoint> {
|
|
18
|
+
const existing = endpoints.get(session);
|
|
19
|
+
if (existing) return existing;
|
|
20
|
+
const started = start(session);
|
|
21
|
+
endpoints.set(session, started);
|
|
22
|
+
return started;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function start(session: GrantsSession): Promise<DashboardSessionEndpoint> {
|
|
26
|
+
const socketPath = join(tmpdir(), `pi-daddy-${process.pid}-${randomBytes(8).toString("hex")}.sock`);
|
|
27
|
+
const token = randomBytes(24).toString("hex");
|
|
28
|
+
const server = createServer((socket) => {
|
|
29
|
+
let input = "";
|
|
30
|
+
socket.setEncoding("utf8");
|
|
31
|
+
socket.on("data", (chunk) => {
|
|
32
|
+
input += chunk;
|
|
33
|
+
if (input.length > 16 * 1024) socket.destroy(new Error("dashboard request too large"));
|
|
34
|
+
if (!input.includes("\n")) return;
|
|
35
|
+
const line = input.slice(0, input.indexOf("\n"));
|
|
36
|
+
void respond(session, token, line)
|
|
37
|
+
.then((value) => socket.end(`${JSON.stringify(value)}\n`))
|
|
38
|
+
.catch((error) => socket.end(`${JSON.stringify({ ok: false, error: String(error) })}\n`));
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
await new Promise<void>((resolve, reject) => {
|
|
42
|
+
server.once("error", reject);
|
|
43
|
+
server.listen(socketPath, () => resolve());
|
|
44
|
+
});
|
|
45
|
+
await chmod(socketPath, 0o600);
|
|
46
|
+
const cleanup = () => {
|
|
47
|
+
close(server, socketPath);
|
|
48
|
+
};
|
|
49
|
+
process.once("exit", cleanup);
|
|
50
|
+
server.unref();
|
|
51
|
+
return {
|
|
52
|
+
socketPath,
|
|
53
|
+
token,
|
|
54
|
+
close: async () => {
|
|
55
|
+
process.off("exit", cleanup);
|
|
56
|
+
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
57
|
+
await rm(socketPath, { force: true });
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function respond(session: GrantsSession, token: string, line: string): Promise<Record<string, unknown>> {
|
|
63
|
+
const request = JSON.parse(line) as { token?: unknown; action?: unknown; edits?: unknown };
|
|
64
|
+
if (request.token !== token) return { ok: false, error: "unauthorised dashboard request" };
|
|
65
|
+
if (request.action === "set") {
|
|
66
|
+
if (typeof request.edits !== "string") return { ok: false, error: "dashboard edits must be a string" };
|
|
67
|
+
const invalid = await saveSessionModelEdits(
|
|
68
|
+
session,
|
|
69
|
+
request.edits,
|
|
70
|
+
[...session.definitions.keys()].sort(),
|
|
71
|
+
"grants-models",
|
|
72
|
+
);
|
|
73
|
+
if (invalid) return { ok: false, error: invalid };
|
|
74
|
+
} else if (request.action !== "get") return { ok: false, error: "unknown dashboard action" };
|
|
75
|
+
return {
|
|
76
|
+
ok: true,
|
|
77
|
+
rows: sessionModelRows(session, [...session.definitions.keys()].sort()),
|
|
78
|
+
cost: session.episodeCostGate.cost,
|
|
79
|
+
ceiling: session.episodeCostGate.ceiling,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function close(server: Server, socketPath: string): void {
|
|
84
|
+
server.close();
|
|
85
|
+
void rm(socketPath, { force: true });
|
|
86
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** Per-definition child model and thinking configuration, resolved before a delegation is planned. */
|
|
2
|
+
|
|
3
|
+
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
4
|
+
export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
5
|
+
export type ModelSource = "explicit" | "session" | "definition" | "global" | "pi";
|
|
6
|
+
export type ThinkingSource = ModelSource | "advisor";
|
|
7
|
+
export interface DefinitionRuntimeChoice {
|
|
8
|
+
model?: string;
|
|
9
|
+
thinking?: ThinkingLevel;
|
|
10
|
+
}
|
|
11
|
+
export interface DefinitionRuntimeSettings {
|
|
12
|
+
defaults: DefinitionRuntimeChoice;
|
|
13
|
+
definitions: Map<string, DefinitionRuntimeChoice>;
|
|
14
|
+
}
|
|
15
|
+
export interface ResolvedDefinitionRuntime {
|
|
16
|
+
model?: string;
|
|
17
|
+
modelSource: ModelSource;
|
|
18
|
+
thinking?: ThinkingLevel;
|
|
19
|
+
thinkingSource: ThinkingSource;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const isObject = (value: unknown): value is Record<string, unknown> =>
|
|
23
|
+
value !== null && typeof value === "object" && !Array.isArray(value);
|
|
24
|
+
export const parseThinkingLevel = (value: unknown): ThinkingLevel | undefined =>
|
|
25
|
+
typeof value === "string" && (THINKING_LEVELS as readonly string[]).includes(value)
|
|
26
|
+
? (value as ThinkingLevel)
|
|
27
|
+
: undefined;
|
|
28
|
+
export const parseConfiguredModel = (value: unknown): string | undefined => {
|
|
29
|
+
if (typeof value !== "string") return undefined;
|
|
30
|
+
const colon = value.indexOf(":");
|
|
31
|
+
if (colon <= 0 || colon === value.length - 1 || /\s/.test(value)) return undefined;
|
|
32
|
+
return `${value.slice(0, colon)}/${value.slice(colon + 1)}`;
|
|
33
|
+
};
|
|
34
|
+
const choice = (value: unknown): DefinitionRuntimeChoice => {
|
|
35
|
+
if (!isObject(value)) return {};
|
|
36
|
+
return {
|
|
37
|
+
...(parseConfiguredModel(value.model) ? { model: parseConfiguredModel(value.model) } : {}),
|
|
38
|
+
...(parseThinkingLevel(value.thinking) ? { thinking: parseThinkingLevel(value.thinking) } : {}),
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function definitionRuntimeSettingsFrom(raw: unknown): DefinitionRuntimeSettings {
|
|
43
|
+
const definitions = new Map<string, DefinitionRuntimeChoice>();
|
|
44
|
+
if (isObject(raw) && Array.isArray(raw.definitions)) {
|
|
45
|
+
for (const entry of raw.definitions) {
|
|
46
|
+
if (!isObject(entry) || typeof entry.name !== "string") continue;
|
|
47
|
+
definitions.set(entry.name, choice(entry));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { defaults: isObject(raw) ? choice(raw.defaults) : {}, definitions };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function resolvedModelOf(value: string | undefined): { provider: string; modelId: string } | null {
|
|
54
|
+
if (!value) return null;
|
|
55
|
+
const slash = value.indexOf("/");
|
|
56
|
+
return slash > 0 && slash < value.length - 1
|
|
57
|
+
? { provider: value.slice(0, slash), modelId: value.slice(slash + 1) }
|
|
58
|
+
: null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function resolveDefinitionRuntime(input: {
|
|
62
|
+
definition?: string;
|
|
63
|
+
explicit: { model?: string; thinking?: string };
|
|
64
|
+
session: ReadonlyMap<string, DefinitionRuntimeChoice>;
|
|
65
|
+
settings: DefinitionRuntimeSettings;
|
|
66
|
+
piModel?: string;
|
|
67
|
+
advisorThinking?: string;
|
|
68
|
+
}): ResolvedDefinitionRuntime {
|
|
69
|
+
const session = input.definition ? input.session.get(input.definition) : undefined;
|
|
70
|
+
const definition = input.definition ? input.settings.definitions.get(input.definition) : undefined;
|
|
71
|
+
const modelCandidates: Array<[string | undefined, ModelSource]> = [
|
|
72
|
+
[input.explicit.model, "explicit"],
|
|
73
|
+
[session?.model, "session"],
|
|
74
|
+
[definition?.model, "definition"],
|
|
75
|
+
[input.settings.defaults.model, "global"],
|
|
76
|
+
[input.piModel, "pi"],
|
|
77
|
+
];
|
|
78
|
+
const thinkingCandidates: Array<[string | undefined, ThinkingSource]> = [
|
|
79
|
+
[input.explicit.thinking, "explicit"],
|
|
80
|
+
[session?.thinking, "session"],
|
|
81
|
+
[input.advisorThinking, "advisor"],
|
|
82
|
+
[definition?.thinking, "definition"],
|
|
83
|
+
[input.settings.defaults.thinking, "global"],
|
|
84
|
+
];
|
|
85
|
+
const selectedModel = modelCandidates.find(([value]) => value !== undefined) ?? [undefined, "pi" as const];
|
|
86
|
+
const selectedThinking = thinkingCandidates.find(([value]) => value !== undefined) ?? [undefined, "pi" as const];
|
|
87
|
+
return {
|
|
88
|
+
model: selectedModel[0],
|
|
89
|
+
modelSource: selectedModel[1],
|
|
90
|
+
thinking: parseThinkingLevel(selectedThinking[0]),
|
|
91
|
+
thinkingSource: selectedThinking[1],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -43,6 +43,8 @@ import { newExecutionId } from "../src/kernel/execution-id.ts";
|
|
|
43
43
|
import { planChain, type GateRequest } from "./chain-plan.ts";
|
|
44
44
|
import { contextShape } from "./context-shape.ts";
|
|
45
45
|
import { preflightModel } from "../src/kernel/model-preflight.ts";
|
|
46
|
+
import { resolveDefinitionRuntime } from "./definition-runtime.ts";
|
|
47
|
+
import { ensureSessionModelPrompt } from "./session-model-prompt.ts";
|
|
46
48
|
import { assertDelegationAuthority } from "./delegation-authority.ts";
|
|
47
49
|
|
|
48
50
|
/** One chain step is one execution occurrence, however many capability dialogs contributed to its answer. */
|
|
@@ -100,6 +102,9 @@ export function registerChainTool(pi: ExtensionAPI, session: GrantsSession): voi
|
|
|
100
102
|
maxItems: MAX_CHAIN_STEPS,
|
|
101
103
|
description: "The steps to run IN ORDER. Each one sees the previous one's output.",
|
|
102
104
|
}),
|
|
105
|
+
episodeCostCeiling: Type.Optional(
|
|
106
|
+
Type.Number({ exclusiveMinimum: 0, description: "Override the USD ceiling for this whole episode." }),
|
|
107
|
+
),
|
|
103
108
|
});
|
|
104
109
|
|
|
105
110
|
pi.registerTool({
|
|
@@ -130,12 +135,35 @@ export function registerChainTool(pi: ExtensionAPI, session: GrantsSession): voi
|
|
|
130
135
|
const split = splitBudget(session.fanoutBudget, steps.length);
|
|
131
136
|
if (!split.ok) throw new GovernanceRefusal(refusal("FANOUT_EXCEEDED", `chain refused: ${split.reason}`));
|
|
132
137
|
|
|
138
|
+
await ensureSessionModelPrompt(
|
|
139
|
+
session,
|
|
140
|
+
steps.flatMap((step) => (step.agent ? [step.agent] : [])),
|
|
141
|
+
{
|
|
142
|
+
hasUI: ctx.hasUI && typeof ctx.ui.input === "function",
|
|
143
|
+
input: (title, placeholder) => ctx.ui.input(title, placeholder),
|
|
144
|
+
notify: (message, type) => ctx.ui.notify(message, type),
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
|
|
133
148
|
// Plan every step first. A step that can never run refuses the chain HERE, before anyone is asked — see
|
|
134
149
|
// `planChain`.
|
|
135
150
|
const executionIds = steps.map(() => newExecutionId());
|
|
136
151
|
const parentExecutionId = session.ownExecutionId ?? null;
|
|
137
|
-
const
|
|
138
|
-
|
|
152
|
+
const piModel = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
|
|
153
|
+
const chainPlan = await planChain(
|
|
154
|
+
session,
|
|
155
|
+
steps,
|
|
156
|
+
executionIds,
|
|
157
|
+
(model) =>
|
|
158
|
+
preflightModel(model, ctx.modelRegistry, session.modelResolutionCache, session.allowUnresolvedModels),
|
|
159
|
+
(step) =>
|
|
160
|
+
resolveDefinitionRuntime({
|
|
161
|
+
definition: step.agent,
|
|
162
|
+
explicit: { model: step.model, thinking: step.thinking },
|
|
163
|
+
session: session.definitionRuntimeOverrides,
|
|
164
|
+
settings: session.definitionRuntimeSettings,
|
|
165
|
+
piModel,
|
|
166
|
+
}).model,
|
|
139
167
|
);
|
|
140
168
|
if (chainPlan.doomed) {
|
|
141
169
|
const message =
|
|
@@ -274,6 +302,7 @@ export function registerChainTool(pi: ExtensionAPI, session: GrantsSession): voi
|
|
|
274
302
|
*/
|
|
275
303
|
let available = [...preApproved];
|
|
276
304
|
|
|
305
|
+
if (args.episodeCostCeiling !== undefined) session.episodeCostGate.setCeiling(args.episodeCostCeiling);
|
|
277
306
|
for (const [index, step] of steps.entries()) {
|
|
278
307
|
const childId = childSpawnId(session.ownSpawnId, index);
|
|
279
308
|
const availableForStep = available.filter((approval) => {
|