pi-daddy 0.45.0 → 0.46.1
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 +10 -0
- package/README.md +391 -45
- package/contracts/ledger-record/v1/governance-event.schema.json +2 -0
- package/dist/executors/captured-execution.d.ts +2 -2
- package/dist/executors/captured-execution.d.ts.map +1 -1
- package/dist/executors/captured-execution.js +2 -2
- package/dist/executors/captured-execution.js.map +1 -1
- package/dist/executors/executor.d.ts.map +1 -1
- package/dist/executors/executor.js +4 -2
- package/dist/executors/executor.js.map +1 -1
- package/dist/executors/herdr-cli.d.ts +2 -0
- package/dist/executors/herdr-cli.d.ts.map +1 -1
- package/dist/executors/herdr-cli.js +2 -2
- package/dist/executors/herdr-cli.js.map +1 -1
- package/dist/executors/herdr-launcher.d.ts +2 -0
- package/dist/executors/herdr-launcher.d.ts.map +1 -0
- package/dist/executors/herdr-launcher.js +123 -0
- package/dist/executors/herdr-launcher.js.map +1 -0
- package/dist/executors/herdr-owned.d.ts +13 -0
- package/dist/executors/herdr-owned.d.ts.map +1 -0
- package/dist/executors/herdr-owned.js +229 -0
- package/dist/executors/herdr-owned.js.map +1 -0
- package/dist/executors/herdr-qualification.d.ts +6 -0
- package/dist/executors/herdr-qualification.d.ts.map +1 -0
- package/dist/executors/herdr-qualification.js +33 -0
- package/dist/executors/herdr-qualification.js.map +1 -0
- package/dist/executors/herdr-wire.d.ts +5 -0
- package/dist/executors/herdr-wire.d.ts.map +1 -0
- package/dist/executors/herdr-wire.js +47 -0
- package/dist/executors/herdr-wire.js.map +1 -0
- package/dist/governance/approval-prompt.d.ts +4 -11
- package/dist/governance/approval-prompt.d.ts.map +1 -1
- package/dist/governance/approval-prompt.js +83 -24
- package/dist/governance/approval-prompt.js.map +1 -1
- package/dist/governance/ledger-v3-validation.js +1 -1
- package/dist/governance/ledger-v3-validation.js.map +1 -1
- package/dist/governance/ledger.d.ts +3 -4
- package/dist/governance/ledger.d.ts.map +1 -1
- package/dist/governance/ledger.js +1 -1
- package/dist/governance/ledger.js.map +1 -1
- package/dist/governance/runtime-settlement-lock.d.ts +5 -0
- package/dist/governance/runtime-settlement-lock.d.ts.map +1 -0
- package/dist/governance/runtime-settlement-lock.js +72 -0
- package/dist/governance/runtime-settlement-lock.js.map +1 -0
- package/dist/governance/runtime-settlement.d.ts +22 -0
- package/dist/governance/runtime-settlement.d.ts.map +1 -0
- package/dist/governance/runtime-settlement.js +236 -0
- package/dist/governance/runtime-settlement.js.map +1 -0
- package/extensions/activity-timeline.ts +94 -31
- package/extensions/execute-child.ts +98 -74
- package/extensions/execution-controls.ts +42 -0
- package/extensions/executor-session.ts +2 -1
- package/extensions/grants-command.ts +4 -0
- package/extensions/grants.ts +4 -0
- package/extensions/reload-environment.ts +8 -0
- package/extensions/runtime-snapshot.ts +123 -0
- package/extensions/session.ts +1 -1
- package/package.json +1 -1
- package/src/executors/captured-execution.ts +2 -1
- package/src/executors/executor.ts +4 -2
- package/src/executors/herdr-cli.ts +19 -12
- package/src/executors/herdr-launcher.ts +124 -0
- package/src/executors/herdr-owned.ts +260 -0
- package/src/executors/herdr-qualification.ts +33 -0
- package/src/executors/herdr-wire.ts +43 -0
- package/src/governance/approval-prompt.ts +86 -29
- package/src/governance/ledger-v3-validation.ts +1 -1
- package/src/governance/ledger.ts +3 -4
- package/src/governance/runtime-settlement-lock.ts +76 -0
- package/src/governance/runtime-settlement.ts +271 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/** Local runtime facts for explicitly armed resume; no task text, grants, approval or model verdicts. */
|
|
2
|
+
import { lockRuntimeSettlement } from "./runtime-settlement-lock.js";
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
+
import { mkdirSync, realpathSync, lstatSync, openSync, writeFileSync, fsyncSync, closeSync, renameSync, unlinkSync, constants, readFileSync, } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { readBoundedTextSync } from "../kernel/bounded-read-sync.js";
|
|
7
|
+
import { isCapturedWorkerIdentity, readCapturedWorkerReceipt, sameWorkerIdentity } from "./captured-worker-record.js";
|
|
8
|
+
const MAX_RECORD = 4 * 1024 * 1024;
|
|
9
|
+
export const settlementHash = (value) => createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
10
|
+
const ownerId = () => {
|
|
11
|
+
const stat = readFileSync("/proc/self/stat", "utf8");
|
|
12
|
+
return [
|
|
13
|
+
readFileSync("/proc/sys/kernel/random/boot_id", "utf8").trim(),
|
|
14
|
+
process.pid,
|
|
15
|
+
stat.slice(stat.lastIndexOf(")") + 2).split(" ")[19],
|
|
16
|
+
].join(":");
|
|
17
|
+
};
|
|
18
|
+
function ownerAlive(id) {
|
|
19
|
+
const [boot, pid, ticks] = id.split(":");
|
|
20
|
+
if (!/^\d+$/.test(pid ?? "") || !/^\d+$/.test(ticks ?? ""))
|
|
21
|
+
throw Error("invalid runtime journal owner");
|
|
22
|
+
if (readFileSync("/proc/sys/kernel/random/boot_id", "utf8").trim() !== boot)
|
|
23
|
+
return false;
|
|
24
|
+
try {
|
|
25
|
+
const text = readFileSync("/proc/" + pid + "/stat", "utf8");
|
|
26
|
+
return text.slice(text.lastIndexOf(")") + 2).split(" ")[19] === ticks;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error.code === "ENOENT")
|
|
30
|
+
return false;
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function validate(value, sessionId, cwd) {
|
|
35
|
+
const j = value;
|
|
36
|
+
if (!j ||
|
|
37
|
+
j.version !== 1 ||
|
|
38
|
+
j.sessionId !== sessionId ||
|
|
39
|
+
j.cwd !== cwd ||
|
|
40
|
+
!/^[a-f0-9-]{36}$/.test(j.ownerScope) ||
|
|
41
|
+
typeof j.ownerId !== "string" ||
|
|
42
|
+
!Array.isArray(j.executions) ||
|
|
43
|
+
j.executions.length > 10000)
|
|
44
|
+
throw Error("invalid runtime journal identity");
|
|
45
|
+
const ids = new Set();
|
|
46
|
+
for (const e of j.executions) {
|
|
47
|
+
if (!e ||
|
|
48
|
+
typeof e.id !== "string" ||
|
|
49
|
+
!e.id ||
|
|
50
|
+
e.id.length > 512 ||
|
|
51
|
+
ids.has(e.id) ||
|
|
52
|
+
!["pending", "settled", "not-started", "unknown"].includes(e.state) ||
|
|
53
|
+
(e.identity !== undefined && (!isCapturedWorkerIdentity(e.identity) || e.identity.executionId !== e.id)) ||
|
|
54
|
+
(e.receiptSha256 !== undefined && !/^[a-f0-9]{64}$/.test(e.receiptSha256)) ||
|
|
55
|
+
(e.state === "settled" && (!e.identity || !e.receiptSha256)) ||
|
|
56
|
+
(e.state === "not-started" && e.identity) ||
|
|
57
|
+
(e.problem !== undefined && typeof e.problem !== "string"))
|
|
58
|
+
throw Error("invalid runtime journal execution");
|
|
59
|
+
ids.add(e.id);
|
|
60
|
+
}
|
|
61
|
+
return j;
|
|
62
|
+
}
|
|
63
|
+
export async function openRuntimeSettlement(directory, sessionId, cwd) {
|
|
64
|
+
if (process.platform !== "linux" || process.arch !== "x64")
|
|
65
|
+
throw Error("runtime settlement requires Linux x64");
|
|
66
|
+
if (!sessionId || sessionId.length > 512 || cwd !== realpathSync(cwd))
|
|
67
|
+
throw Error("runtime session/cwd identity is unavailable");
|
|
68
|
+
const root = join(realpathSync(directory), "pi-daddy", "runtime-settlement");
|
|
69
|
+
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
70
|
+
if (realpathSync(root) !== root)
|
|
71
|
+
throw Error("runtime settlement root must not contain symlinks");
|
|
72
|
+
const info = lstatSync(root);
|
|
73
|
+
if (!info.isDirectory() || info.uid !== process.getuid() || info.mode & 0o077)
|
|
74
|
+
throw Error("runtime settlement root must be private");
|
|
75
|
+
const path = join(root, settlementHash([sessionId, cwd]) + ".json");
|
|
76
|
+
const lock = await lockRuntimeSettlement(path + ".lock");
|
|
77
|
+
try {
|
|
78
|
+
let journal;
|
|
79
|
+
try {
|
|
80
|
+
journal = validate(JSON.parse(readBoundedTextSync(path, { maxBytes: MAX_RECORD }).text), sessionId, cwd);
|
|
81
|
+
if (ownerAlive(journal.ownerId))
|
|
82
|
+
throw Error("prior runtime owner is still alive");
|
|
83
|
+
journal.ownerId = ownerId();
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (error.code !== "ENOENT")
|
|
87
|
+
throw error;
|
|
88
|
+
journal = { version: 1, sessionId, cwd, ownerScope: randomUUID(), ownerId: ownerId(), executions: [] };
|
|
89
|
+
}
|
|
90
|
+
let failed, active = new Set();
|
|
91
|
+
const save = () => {
|
|
92
|
+
let temporary, fd;
|
|
93
|
+
try {
|
|
94
|
+
if (!lock.healthy())
|
|
95
|
+
throw Error("runtime ownership lock was lost");
|
|
96
|
+
const text = JSON.stringify(journal) + "\n";
|
|
97
|
+
if (Buffer.byteLength(text) > MAX_RECORD)
|
|
98
|
+
throw Error("runtime settlement journal exceeds its bound");
|
|
99
|
+
temporary = path + "." + randomUUID();
|
|
100
|
+
fd = openSync(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
101
|
+
writeFileSync(fd, text);
|
|
102
|
+
fsyncSync(fd);
|
|
103
|
+
closeSync(fd);
|
|
104
|
+
fd = undefined;
|
|
105
|
+
renameSync(temporary, path);
|
|
106
|
+
const parent = openSync(root, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
107
|
+
try {
|
|
108
|
+
fsyncSync(parent);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
closeSync(parent);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
failed = String(error);
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
if (fd !== undefined)
|
|
120
|
+
closeSync(fd);
|
|
121
|
+
if (temporary)
|
|
122
|
+
try {
|
|
123
|
+
unlinkSync(temporary);
|
|
124
|
+
}
|
|
125
|
+
catch { }
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const entry = (id) => {
|
|
129
|
+
const e = journal.executions.find((e) => e.id === id);
|
|
130
|
+
if (!e)
|
|
131
|
+
throw Error("runtime execution was not registered");
|
|
132
|
+
return e;
|
|
133
|
+
};
|
|
134
|
+
save();
|
|
135
|
+
return {
|
|
136
|
+
ownerScope: journal.ownerScope,
|
|
137
|
+
ownerId: journal.ownerId,
|
|
138
|
+
sessionId,
|
|
139
|
+
cwd,
|
|
140
|
+
begin(id) {
|
|
141
|
+
if (failed)
|
|
142
|
+
throw Error(failed);
|
|
143
|
+
if (typeof id !== "string" ||
|
|
144
|
+
!id ||
|
|
145
|
+
id.length > 512 ||
|
|
146
|
+
journal.executions.some((e) => e.id === id) ||
|
|
147
|
+
journal.executions.length >= 10000)
|
|
148
|
+
throw Error("runtime execution id must be fresh and bounded");
|
|
149
|
+
journal.executions.push({ id, state: "pending" });
|
|
150
|
+
active.add(id);
|
|
151
|
+
save();
|
|
152
|
+
},
|
|
153
|
+
bind(identity) {
|
|
154
|
+
const e = entry(identity.executionId);
|
|
155
|
+
if (e.state !== "pending" || (e.identity && !sameWorkerIdentity(e.identity, identity)))
|
|
156
|
+
throw Error("runtime ownership changed");
|
|
157
|
+
e.identity = { ...identity };
|
|
158
|
+
save();
|
|
159
|
+
},
|
|
160
|
+
async finish(id, cleanup, problem) {
|
|
161
|
+
const e = entry(id);
|
|
162
|
+
if (problem)
|
|
163
|
+
e.problem = problem.slice(0, 2000);
|
|
164
|
+
try {
|
|
165
|
+
if (cleanup.state === "not-started" && !e.identity)
|
|
166
|
+
e.state = "not-started";
|
|
167
|
+
else if (cleanup.state === "settled" && e.identity && sameWorkerIdentity(e.identity, cleanup.identity)) {
|
|
168
|
+
const receipt = await readCapturedWorkerReceipt(e.identity);
|
|
169
|
+
if (receipt) {
|
|
170
|
+
e.state = "settled";
|
|
171
|
+
e.receiptSha256 = settlementHash(receipt);
|
|
172
|
+
}
|
|
173
|
+
else
|
|
174
|
+
e.state = "unknown";
|
|
175
|
+
}
|
|
176
|
+
else
|
|
177
|
+
e.state = "unknown";
|
|
178
|
+
save();
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
active.delete(id);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
async snapshot() {
|
|
185
|
+
if (!lock.healthy())
|
|
186
|
+
failed = "runtime ownership lock was lost";
|
|
187
|
+
let changed = false;
|
|
188
|
+
for (const e of journal.executions) {
|
|
189
|
+
if (active.has(e.id) || !e.identity)
|
|
190
|
+
continue;
|
|
191
|
+
const receipt = await readCapturedWorkerReceipt(e.identity);
|
|
192
|
+
if (!receipt || (e.receiptSha256 && e.receiptSha256 !== settlementHash(receipt))) {
|
|
193
|
+
if (e.state === "settled") {
|
|
194
|
+
e.state = "unknown";
|
|
195
|
+
changed = true;
|
|
196
|
+
}
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (e.state !== "settled") {
|
|
200
|
+
e.state = "settled";
|
|
201
|
+
e.receiptSha256 = settlementHash(receipt);
|
|
202
|
+
changed = true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (changed) {
|
|
206
|
+
try {
|
|
207
|
+
save();
|
|
208
|
+
}
|
|
209
|
+
catch { }
|
|
210
|
+
}
|
|
211
|
+
const outstanding = journal.executions.filter((e) => !["settled", "not-started"].includes(e.state) || e.problem);
|
|
212
|
+
const state = failed || outstanding.some((e) => !active.has(e.id))
|
|
213
|
+
? "unknown"
|
|
214
|
+
: active.size
|
|
215
|
+
? "busy"
|
|
216
|
+
: outstanding.length
|
|
217
|
+
? "unknown"
|
|
218
|
+
: "idle";
|
|
219
|
+
return {
|
|
220
|
+
ownerScope: journal.ownerScope,
|
|
221
|
+
ownerId: journal.ownerId,
|
|
222
|
+
evidenceDigest: settlementHash({ ownerScope: journal.ownerScope, executions: journal.executions }),
|
|
223
|
+
state,
|
|
224
|
+
settledExecutionIds: journal.executions.filter((e) => e.state === "settled").map((e) => e.id),
|
|
225
|
+
outstandingExecutionIds: outstanding.map((e) => e.id),
|
|
226
|
+
...(failed ? { reason: failed } : {}),
|
|
227
|
+
};
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
await lock.release();
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=runtime-settlement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-settlement.js","sourceRoot":"","sources":["../../src/governance/runtime-settlement.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,13 @@ interface HookContext {
|
|
|
21
21
|
cwd: string;
|
|
22
22
|
model?: { id?: string };
|
|
23
23
|
thinkingLevel?: string;
|
|
24
|
+
sessionManager?: {
|
|
25
|
+
getBranch(): Array<{
|
|
26
|
+
id: string;
|
|
27
|
+
type: string;
|
|
28
|
+
message?: { role?: string; content?: unknown; stopReason?: string };
|
|
29
|
+
}>;
|
|
30
|
+
};
|
|
24
31
|
ui: { notify(message: string, level?: "info" | "warning" | "error"): void };
|
|
25
32
|
}
|
|
26
33
|
const digest = (text: string) => createHash("sha256").update(text).digest("hex");
|
|
@@ -32,7 +39,11 @@ const digest = (text: string) => createHash("sha256").update(text).digest("hex")
|
|
|
32
39
|
export function registerActivityTimeline(pi: ExtensionAPI, session?: ActivitySessionState, lifecycleTool = true): void {
|
|
33
40
|
let recorder: ActivityTimelineRecorder | undefined,
|
|
34
41
|
pendingFinal: string | undefined,
|
|
35
|
-
|
|
42
|
+
pendingStopReason: string | undefined,
|
|
43
|
+
observedUser: string | undefined,
|
|
44
|
+
pendingStart: { event: unknown; prompt: string; priorIds: Set<string> } | undefined;
|
|
45
|
+
let turnPriorIds = new Set<string>();
|
|
46
|
+
let activeRoot = false;
|
|
36
47
|
let paths = new Map<string, string>(),
|
|
37
48
|
available = new Map<string, ActivitySkill>();
|
|
38
49
|
let reported = false;
|
|
@@ -77,6 +88,44 @@ export function registerActivityTimeline(pi: ExtensionAPI, session?: ActivitySes
|
|
|
77
88
|
await target.skill("skill_available", fact);
|
|
78
89
|
}
|
|
79
90
|
};
|
|
91
|
+
const messageText = (content: unknown): string =>
|
|
92
|
+
typeof content === "string"
|
|
93
|
+
? content
|
|
94
|
+
: Array.isArray(content)
|
|
95
|
+
? content
|
|
96
|
+
.filter((part) => part?.type === "text")
|
|
97
|
+
.map((part) => part.text ?? "")
|
|
98
|
+
.join("")
|
|
99
|
+
: "";
|
|
100
|
+
// Pi emits before_agent_start before user message_end. Read the finalized branch only after
|
|
101
|
+
// every message transformer has run; never carry a prior turn's user into the next task.
|
|
102
|
+
const startPending = async (ctx: HookContext) => {
|
|
103
|
+
const pending = pendingStart;
|
|
104
|
+
if (!pending) return;
|
|
105
|
+
const user = ctx.sessionManager
|
|
106
|
+
?.getBranch()
|
|
107
|
+
.find((entry) => !pending.priorIds.has(entry.id) && entry.type === "message" && entry.message?.role === "user");
|
|
108
|
+
const target = current(ctx);
|
|
109
|
+
const taskId = await target.start(
|
|
110
|
+
user ? messageText(user.message?.content) : (observedUser ?? pending.prompt),
|
|
111
|
+
ctx.model?.id,
|
|
112
|
+
ctx.thinkingLevel,
|
|
113
|
+
);
|
|
114
|
+
pendingStart = undefined;
|
|
115
|
+
observedUser = undefined;
|
|
116
|
+
if (session) session.activity = { rootId: target.rootId, path: target.path, ...(taskId ? { taskId } : {}) };
|
|
117
|
+
await skills(pending.event, target);
|
|
118
|
+
};
|
|
119
|
+
const resetTurn = () => {
|
|
120
|
+
activeRoot = false;
|
|
121
|
+
pendingStart = undefined;
|
|
122
|
+
observedUser = undefined;
|
|
123
|
+
pendingFinal = undefined;
|
|
124
|
+
pendingStopReason = undefined;
|
|
125
|
+
turnPriorIds = new Set();
|
|
126
|
+
paths.clear();
|
|
127
|
+
available.clear();
|
|
128
|
+
};
|
|
80
129
|
if (lifecycleTool)
|
|
81
130
|
pi.registerTool({
|
|
82
131
|
name: "activity_lifecycle",
|
|
@@ -101,6 +150,8 @@ export function registerActivityTimeline(pi: ExtensionAPI, session?: ActivitySes
|
|
|
101
150
|
},
|
|
102
151
|
});
|
|
103
152
|
pi.on("session_start", (_event, ctx: HookContext) => {
|
|
153
|
+
resetTurn();
|
|
154
|
+
recorder = undefined;
|
|
104
155
|
try {
|
|
105
156
|
recorder = current(ctx);
|
|
106
157
|
// A leaf observer attaches to the parent-owned child event; a root gets a stable session identity.
|
|
@@ -122,42 +173,45 @@ export function registerActivityTimeline(pi: ExtensionAPI, session?: ActivitySes
|
|
|
122
173
|
}
|
|
123
174
|
return undefined;
|
|
124
175
|
});
|
|
176
|
+
pi.on("session_shutdown", () => {
|
|
177
|
+
resetTurn();
|
|
178
|
+
recorder = undefined;
|
|
179
|
+
});
|
|
125
180
|
pi.on("before_agent_start", async (event, ctx: HookContext) => {
|
|
126
181
|
try {
|
|
182
|
+
resetTurn();
|
|
127
183
|
const target = current(ctx);
|
|
128
|
-
//
|
|
129
|
-
//
|
|
184
|
+
// Leaves attach to the parent-owned execution. Roots bind the current finalized user at
|
|
185
|
+
// the first context boundary, or at settlement if cancelled before the first model call.
|
|
130
186
|
if (!process.env[ENV_ACTIVITY_TASK]) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
187
|
+
activeRoot = true;
|
|
188
|
+
turnPriorIds = new Set(ctx.sessionManager?.getBranch().map((entry) => entry.id) ?? []);
|
|
189
|
+
pendingStart = { event, prompt: (event as { prompt?: string }).prompt ?? "", priorIds: turnPriorIds };
|
|
190
|
+
if (session) session.activity = { rootId: target.rootId, path: target.path };
|
|
191
|
+
} else await skills(event, target);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
report(ctx, error);
|
|
194
|
+
}
|
|
195
|
+
return undefined;
|
|
196
|
+
});
|
|
197
|
+
pi.on("context", async (_event, ctx: HookContext) => {
|
|
198
|
+
try {
|
|
199
|
+
await startPending(ctx);
|
|
141
200
|
} catch (error) {
|
|
142
201
|
report(ctx, error);
|
|
143
202
|
}
|
|
144
203
|
return undefined;
|
|
145
204
|
});
|
|
146
205
|
pi.on("message_end", (event) => {
|
|
147
|
-
const message = (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
.map((part) => part.text ?? "")
|
|
157
|
-
.join("")
|
|
158
|
-
: "";
|
|
159
|
-
if (message?.role === "user") finalizedUser = text;
|
|
160
|
-
if (message?.role === "assistant") pendingFinal = text;
|
|
206
|
+
const message = (event as { message?: { role?: string; content?: unknown; stopReason?: string } }).message;
|
|
207
|
+
// This fallback is scoped to this pending start. Steering within an active turn cannot
|
|
208
|
+
// overwrite its initial prompt or seed the next root task.
|
|
209
|
+
if (message?.role === "user" && pendingStart && observedUser === undefined)
|
|
210
|
+
observedUser = messageText(message.content);
|
|
211
|
+
if (message?.role === "assistant") {
|
|
212
|
+
pendingFinal = messageText(message.content);
|
|
213
|
+
pendingStopReason = message.stopReason;
|
|
214
|
+
}
|
|
161
215
|
return undefined;
|
|
162
216
|
});
|
|
163
217
|
pi.on("tool_execution_start", (event) => {
|
|
@@ -178,19 +232,28 @@ export function registerActivityTimeline(pi: ExtensionAPI, session?: ActivitySes
|
|
|
178
232
|
return undefined;
|
|
179
233
|
});
|
|
180
234
|
pi.on("agent_settled", async (event, ctx: HookContext) => {
|
|
235
|
+
if (!activeRoot) return undefined;
|
|
181
236
|
try {
|
|
237
|
+
await startPending(ctx);
|
|
238
|
+
const final = ctx.sessionManager
|
|
239
|
+
?.getBranch()
|
|
240
|
+
.findLast(
|
|
241
|
+
(entry) => !turnPriorIds.has(entry.id) && entry.type === "message" && entry.message?.role === "assistant",
|
|
242
|
+
);
|
|
243
|
+
const stopReason = final?.message?.stopReason ?? pendingStopReason;
|
|
182
244
|
if (recorder && !process.env[ENV_ACTIVITY_TASK])
|
|
183
245
|
await recorder.finish(
|
|
184
|
-
pendingFinal,
|
|
185
|
-
(event as {
|
|
246
|
+
final ? messageText(final.message?.content) : pendingFinal,
|
|
247
|
+
(event as { aborted?: boolean }).aborted || stopReason === "aborted"
|
|
186
248
|
? "cancelled"
|
|
187
|
-
:
|
|
249
|
+
: stopReason === "error"
|
|
188
250
|
? "failed"
|
|
189
251
|
: "completed",
|
|
190
252
|
);
|
|
191
|
-
pendingFinal = undefined;
|
|
192
253
|
} catch (error) {
|
|
193
254
|
report(ctx, error);
|
|
255
|
+
} finally {
|
|
256
|
+
resetTurn();
|
|
194
257
|
}
|
|
195
258
|
return undefined;
|
|
196
259
|
});
|