pi-herdr-agents 0.1.2 → 0.1.3
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.
|
@@ -317,13 +317,18 @@ The full bounded `RunEnvelope` exists exactly once, inside the single terminal e
|
|
|
317
317
|
```text
|
|
318
318
|
approved
|
|
319
319
|
started
|
|
320
|
+
reader_checkout_ready | reader_checkout_retained | reader_checkout_disposed
|
|
320
321
|
agent_started
|
|
322
|
+
agent_completed
|
|
321
323
|
agent_result
|
|
324
|
+
pane_close_failed
|
|
322
325
|
workflow_log
|
|
323
326
|
completed | failed | cancelled | interrupted
|
|
324
327
|
delivery
|
|
325
328
|
```
|
|
326
329
|
|
|
330
|
+
`agent_completed` records the observed child exit, session availability, final assistant-text length, and (when present) final stop reason before the corresponding `agent_result` records the workflow-facing success or failure envelope.
|
|
331
|
+
|
|
327
332
|
`delivery` follows the terminal event and contains only its terminal-event ID/state, target parent session, attempt time, and `sent | failed` delivery status. It never duplicates the task result. Evidence readers obtain the result from the referenced terminal event. Do not add mutable snapshots, locks, leases, backups, call caching, or replay.
|
|
328
333
|
|
|
329
334
|
One process-global owner records the run ID, canonical project identity, lifecycle gate, Worker, and private child handles. `/new`, `/resume`, and `/fork` cannot start a second workflow while this owner exists; cancellation names the run ID and must match the current canonical project identity. On same-process `/reload`, the owner and Worker continue, and completion selects the latest extension API once. On full startup, scan only direct child directories under the current project's `.pi/plans/` and inspect only each journal's last valid event. A `delivery` event carries its referenced terminal state, a terminal event is already settled, and a non-terminal running event with no live process-global owner receives one `interrupted` terminal event. Do not recurse, restart, clean, or expose history UI.
|
package/package.json
CHANGED
|
@@ -85,6 +85,7 @@ import {
|
|
|
85
85
|
|
|
86
86
|
import {
|
|
87
87
|
findLastAssistantMessage,
|
|
88
|
+
inspectFinalAssistantMessage,
|
|
88
89
|
findObservedSessionRuntime,
|
|
89
90
|
getNewEntries,
|
|
90
91
|
seedSubagentSessionFile,
|
|
@@ -3033,18 +3034,43 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
3033
3034
|
if (childController.signal.aborted || watched.error === "cancelled") {
|
|
3034
3035
|
return workflowFailure("cancelled", "Workflow cancelled.");
|
|
3035
3036
|
}
|
|
3036
|
-
const
|
|
3037
|
+
const sessionExists = existsSync(sessionFile);
|
|
3038
|
+
const childEntries = sessionExists
|
|
3037
3039
|
? getNewEntries(sessionFile, 0)
|
|
3038
3040
|
: [];
|
|
3039
|
-
const
|
|
3040
|
-
|
|
3041
|
+
const finalAssistant = inspectFinalAssistantMessage(childEntries);
|
|
3042
|
+
journal.append("agent_completed", {
|
|
3043
|
+
id,
|
|
3044
|
+
role: roleName,
|
|
3045
|
+
sessionFile,
|
|
3046
|
+
sessionExists,
|
|
3047
|
+
exitCode: watched.exitCode,
|
|
3048
|
+
...(watched.errorMessage
|
|
3049
|
+
? { errorMessage: watched.errorMessage }
|
|
3050
|
+
: {}),
|
|
3051
|
+
finalAssistantContentLength: finalAssistant.contentLength,
|
|
3052
|
+
...(finalAssistant.stopReason
|
|
3053
|
+
? { finalAssistantStopReason: finalAssistant.stopReason }
|
|
3054
|
+
: {}),
|
|
3055
|
+
});
|
|
3056
|
+
if (watched.exitCode !== 0 || watched.errorMessage) {
|
|
3041
3057
|
return workflowFailure(
|
|
3042
3058
|
"child_error",
|
|
3043
3059
|
watched.errorMessage ??
|
|
3044
|
-
summary ??
|
|
3045
3060
|
`Workflow child exited with code ${watched.exitCode}`,
|
|
3046
3061
|
);
|
|
3047
3062
|
}
|
|
3063
|
+
if (!finalAssistant.text) {
|
|
3064
|
+
return workflowFailure(
|
|
3065
|
+
"empty_completion",
|
|
3066
|
+
`Workflow child completed without assistant text${
|
|
3067
|
+
finalAssistant.stopReason
|
|
3068
|
+
? ` (stopReason: ${finalAssistant.stopReason})`
|
|
3069
|
+
: ""
|
|
3070
|
+
}.`,
|
|
3071
|
+
);
|
|
3072
|
+
}
|
|
3073
|
+
const summary = finalAssistant.text;
|
|
3048
3074
|
const observed = findObservedSessionRuntime(childEntries);
|
|
3049
3075
|
const observedModel =
|
|
3050
3076
|
observed.provider && observed.modelId
|
|
@@ -136,6 +136,39 @@ export function findObservedSessionRuntime(entries: SessionEntry[]): ObservedSes
|
|
|
136
136
|
return observed;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
export interface FinalAssistantMessage {
|
|
140
|
+
text: string | null;
|
|
141
|
+
contentLength: number;
|
|
142
|
+
stopReason?: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Inspect only the final assistant message for workflow completion evidence. */
|
|
146
|
+
export function inspectFinalAssistantMessage(
|
|
147
|
+
entries: SessionEntry[],
|
|
148
|
+
): FinalAssistantMessage {
|
|
149
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
150
|
+
const entry = entries[i];
|
|
151
|
+
if (entry.type !== "message") continue;
|
|
152
|
+
const msg = entry as MessageEntry;
|
|
153
|
+
if (msg.message.role !== "assistant") continue;
|
|
154
|
+
|
|
155
|
+
const texts = msg.message.content
|
|
156
|
+
.filter(
|
|
157
|
+
(block) =>
|
|
158
|
+
block.type === "text" && typeof block.text === "string",
|
|
159
|
+
)
|
|
160
|
+
.map((block) => block.text as string);
|
|
161
|
+
const text = texts.join("\n");
|
|
162
|
+
const stopReason = (msg.message as { stopReason?: unknown }).stopReason;
|
|
163
|
+
return {
|
|
164
|
+
text: text.trim() ? text : null,
|
|
165
|
+
contentLength: text.length,
|
|
166
|
+
...(typeof stopReason === "string" ? { stopReason } : {}),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return { text: null, contentLength: 0 };
|
|
170
|
+
}
|
|
171
|
+
|
|
139
172
|
export function findLastAssistantMessage(entries: SessionEntry[]): string | null {
|
|
140
173
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
141
174
|
const entry = entries[i];
|