@llblab/pi-actors 0.41.1 → 0.42.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/AGENTS.md +11 -6
- package/BACKLOG.md +1 -65
- package/CHANGELOG.md +25 -0
- package/README.md +24 -4
- package/dist/index.js +25 -121
- package/dist/lib/async-runs.d.ts +37 -5
- package/dist/lib/async-runs.js +181 -51
- package/dist/lib/automatic-review-runtime.d.ts +18 -0
- package/dist/lib/automatic-review-runtime.js +96 -0
- package/dist/lib/command-templates.js +1 -1
- package/dist/lib/draft-consolidation-transaction.d.ts +65 -0
- package/dist/lib/draft-consolidation-transaction.js +610 -0
- package/dist/lib/draft-consolidation.d.ts +35 -0
- package/dist/lib/draft-consolidation.js +126 -0
- package/dist/lib/draft-review.d.ts +56 -0
- package/dist/lib/draft-review.js +254 -0
- package/dist/lib/draft-sleep.d.ts +65 -0
- package/dist/lib/draft-sleep.js +468 -0
- package/dist/lib/file-state.d.ts +8 -1
- package/dist/lib/file-state.js +115 -18
- package/dist/lib/inspector-actions.d.ts +16 -0
- package/dist/lib/inspector-actions.js +57 -0
- package/dist/lib/inspector-command.d.ts +7 -0
- package/dist/lib/inspector-command.js +37 -0
- package/dist/lib/inspector-overlay.d.ts +23 -3
- package/dist/lib/inspector-overlay.js +327 -70
- package/dist/lib/inspector.d.ts +9 -0
- package/dist/lib/inspector.js +49 -1
- package/dist/lib/observability.d.ts +13 -0
- package/dist/lib/observability.js +160 -6
- package/dist/lib/paths.d.ts +7 -0
- package/dist/lib/paths.js +28 -0
- package/dist/lib/recipes-discovery.js +32 -24
- package/dist/lib/recipes-usage.d.ts +18 -6
- package/dist/lib/recipes-usage.js +445 -34
- package/dist/lib/review-control.d.ts +14 -0
- package/dist/lib/review-control.js +111 -0
- package/dist/lib/review-diagnostics.d.ts +11 -0
- package/dist/lib/review-diagnostics.js +148 -0
- package/dist/lib/review-projection.d.ts +14 -0
- package/dist/lib/review-projection.js +170 -0
- package/dist/lib/run-ui-runtime.d.ts +18 -0
- package/dist/lib/run-ui-runtime.js +123 -0
- package/dist/lib/runs-artifacts.d.ts +1 -1
- package/dist/lib/runs-artifacts.js +1 -1
- package/dist/lib/runs-control.d.ts +10 -2
- package/dist/lib/runs-control.js +37 -7
- package/dist/lib/runs-identity.d.ts +1 -1
- package/dist/lib/runs-identity.js +1 -1
- package/dist/lib/runs-index.d.ts +11 -2
- package/dist/lib/runs-index.js +46 -23
- package/dist/lib/runs-mailbox.d.ts +1 -1
- package/dist/lib/runs-mailbox.js +1 -1
- package/dist/lib/runs-messages.d.ts +1 -1
- package/dist/lib/runs-messages.js +1 -1
- package/dist/lib/runs-outbox.d.ts +1 -1
- package/dist/lib/runs-outbox.js +1 -1
- package/dist/lib/runs-ownership.d.ts +1 -1
- package/dist/lib/runs-ownership.js +18 -4
- package/dist/lib/runs-parent-teardown.d.ts +51 -0
- package/dist/lib/runs-parent-teardown.js +172 -0
- package/dist/lib/runs-process.d.ts +1 -1
- package/dist/lib/runs-process.js +5 -4
- package/dist/lib/runs-retention.d.ts +1 -1
- package/dist/lib/runs-retention.js +1 -1
- package/dist/lib/runs-start.d.ts +5 -3
- package/dist/lib/runs-start.js +7 -48
- package/dist/lib/runs-status.d.ts +5 -3
- package/dist/lib/runs-status.js +7 -6
- package/dist/lib/runtime.d.ts +7 -1
- package/dist/lib/runtime.js +32 -18
- package/dist/lib/tool-review-lineage-transaction.d.ts +27 -0
- package/dist/lib/tool-review-lineage-transaction.js +597 -0
- package/dist/lib/tool-review-lineage.d.ts +24 -0
- package/dist/lib/tool-review-lineage.js +98 -0
- package/dist/lib/tool-review-scheduler.d.ts +80 -0
- package/dist/lib/tool-review-scheduler.js +494 -0
- package/dist/lib/tool-review-transaction.d.ts +50 -0
- package/dist/lib/tool-review-transaction.js +362 -0
- package/dist/lib/tool-review.d.ts +56 -0
- package/dist/lib/tool-review.js +197 -0
- package/dist/lib/tools-inspect.js +28 -4
- package/dist/lib/tools-local.js +21 -4
- package/dist/lib/tools-message.d.ts +1 -0
- package/dist/lib/tools-message.js +29 -17
- package/dist/lib/tools-spawn.js +14 -2
- package/dist/lib/tools.d.ts +1 -0
- package/dist/lib/tools.js +1 -0
- package/dist/recipes/draft-review.json +24 -0
- package/dist/recipes/tool-review.json +24 -0
- package/dist/scripts/async-runner.mjs +9 -9
- package/dist/scripts/build-dist.mjs +6 -1
- package/dist/scripts/conformance.mjs +6 -1
- package/dist/scripts/recipe-utils.mjs +3 -3
- package/dist/scripts/release-gates.mjs +165 -0
- package/dist/skills/actors/SKILL.md +9 -8
- package/dist/skills/swarm/SKILL.md +1 -1
- package/docs/actor-inspector.md +20 -11
- package/docs/async-runs.md +16 -2
- package/docs/recipe-library.md +7 -3
- package/docs/template-recipes.md +4 -11
- package/docs/tool-registry.md +13 -4
- package/index.ts +27 -142
- package/lib/async-runs.ts +283 -67
- package/lib/automatic-review-runtime.ts +135 -0
- package/lib/command-templates.ts +1 -1
- package/lib/draft-consolidation-transaction.ts +821 -0
- package/lib/draft-consolidation.ts +181 -0
- package/lib/draft-review.ts +325 -0
- package/lib/draft-sleep.ts +576 -0
- package/lib/file-state.ts +143 -19
- package/lib/inspector-actions.ts +79 -0
- package/lib/inspector-command.ts +54 -0
- package/lib/inspector-overlay.ts +377 -91
- package/lib/inspector.ts +73 -1
- package/lib/observability.ts +194 -5
- package/lib/paths.ts +43 -0
- package/lib/recipes-discovery.ts +34 -26
- package/lib/recipes-usage.ts +569 -40
- package/lib/review-control.ts +137 -0
- package/lib/review-diagnostics.ts +164 -0
- package/lib/review-projection.ts +200 -0
- package/lib/run-ui-runtime.ts +153 -0
- package/lib/runs-artifacts.ts +1 -1
- package/lib/runs-control.ts +69 -6
- package/lib/runs-identity.ts +1 -1
- package/lib/runs-index.ts +57 -21
- package/lib/runs-mailbox.ts +1 -1
- package/lib/runs-messages.ts +1 -1
- package/lib/runs-outbox.ts +1 -1
- package/lib/runs-ownership.ts +23 -4
- package/lib/runs-parent-teardown.ts +257 -0
- package/lib/runs-process.ts +5 -4
- package/lib/runs-retention.ts +1 -1
- package/lib/runs-start.ts +13 -68
- package/lib/runs-status.ts +17 -8
- package/lib/runtime.ts +34 -17
- package/lib/tool-review-lineage-transaction.ts +881 -0
- package/lib/tool-review-lineage.ts +145 -0
- package/lib/tool-review-scheduler.ts +635 -0
- package/lib/tool-review-transaction.ts +563 -0
- package/lib/tool-review.ts +270 -0
- package/lib/tools-inspect.ts +35 -4
- package/lib/tools-local.ts +29 -4
- package/lib/tools-message.ts +45 -30
- package/lib/tools-spawn.ts +22 -2
- package/lib/tools.ts +5 -0
- package/package.json +5 -3
- package/recipes/draft-review.json +24 -0
- package/recipes/tool-review.json +24 -0
- package/scripts/async-runner.mjs +9 -9
- package/scripts/build-dist.mjs +6 -1
- package/scripts/conformance.mjs +6 -1
- package/scripts/recipe-utils.mjs +3 -3
- package/scripts/release-gates.mjs +165 -0
- package/skills/actors/SKILL.md +9 -8
- package/skills/swarm/SKILL.md +1 -1
package/dist/lib/inspector.js
CHANGED
|
@@ -19,10 +19,14 @@ export function readActorInspectorRuns(stateRoot, ownerId) {
|
|
|
19
19
|
const stateDir = path.join(stateRoot, entry.name);
|
|
20
20
|
if (!matchesOwner(stateDir, ownerId))
|
|
21
21
|
return [];
|
|
22
|
+
const runMeta = readJsonFileResilient(path.join(stateDir, "run.json"), {}).value;
|
|
22
23
|
const progress = readJsonFileResilient(path.join(stateDir, "progress.json"), {}).value;
|
|
23
24
|
const result = readJsonFileResilient(path.join(stateDir, "result.json"), {}).value;
|
|
24
25
|
return [{
|
|
25
26
|
run: entry.name,
|
|
27
|
+
...(typeof runMeta.run_instance_id === "string"
|
|
28
|
+
? { runInstanceId: runMeta.run_instance_id }
|
|
29
|
+
: {}),
|
|
26
30
|
status: String(progress.phase ?? (Object.keys(result).length ? "terminal" : "unknown")),
|
|
27
31
|
...(typeof progress.updatedAt === "string"
|
|
28
32
|
? { updatedAt: progress.updatedAt }
|
|
@@ -42,6 +46,50 @@ export function readActorInspectorRuns(stateRoot, ownerId) {
|
|
|
42
46
|
return [];
|
|
43
47
|
}
|
|
44
48
|
}
|
|
49
|
+
export function readActorInspectorRecipe(stateDir) {
|
|
50
|
+
const result = readJsonFileResilient(path.join(stateDir, "run.json"), {});
|
|
51
|
+
const meta = result.value;
|
|
52
|
+
const contexts = Array.isArray(meta.recipe_context_records)
|
|
53
|
+
? meta.recipe_context_records
|
|
54
|
+
.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)))
|
|
55
|
+
: [];
|
|
56
|
+
const primary = contexts.find((entry) => entry.depth === 0) ?? contexts[0];
|
|
57
|
+
const definition = primary?.recipe && typeof primary.recipe === "object" &&
|
|
58
|
+
!Array.isArray(primary.recipe)
|
|
59
|
+
? primary.recipe
|
|
60
|
+
: undefined;
|
|
61
|
+
const redacted = (value) => SessionEvidence.redactSessionEvidenceValue(value);
|
|
62
|
+
const redactedRecord = (value) => redacted(Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)));
|
|
63
|
+
return {
|
|
64
|
+
composition: contexts.map((entry) => redactedRecord({
|
|
65
|
+
...(typeof entry.alias === "string" ? { alias: entry.alias } : {}),
|
|
66
|
+
depth: entry.depth,
|
|
67
|
+
file: entry.file,
|
|
68
|
+
import_path: entry.import_path,
|
|
69
|
+
name: entry.name,
|
|
70
|
+
})),
|
|
71
|
+
...(definition
|
|
72
|
+
? { definition: redactedRecord(definition) }
|
|
73
|
+
: {}),
|
|
74
|
+
diagnostics: result.diagnostics.map((diagnostic) => diagnostic.message),
|
|
75
|
+
identity: redactedRecord({
|
|
76
|
+
run: meta.run,
|
|
77
|
+
recipe: meta.recipe,
|
|
78
|
+
recipe_file: meta.recipe_file,
|
|
79
|
+
launch_source: meta.launch_source,
|
|
80
|
+
}),
|
|
81
|
+
launch: redactedRecord({
|
|
82
|
+
cwd: meta.cwd,
|
|
83
|
+
template: meta.template,
|
|
84
|
+
values: meta.values,
|
|
85
|
+
model_policy: meta.model_policy,
|
|
86
|
+
mailbox: meta.mailbox,
|
|
87
|
+
artifacts: meta.artifacts,
|
|
88
|
+
notification_policy: meta.notification_policy,
|
|
89
|
+
retire_when: meta.retire_when,
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
45
93
|
function ownedSessionPath(stateDir, sessionFile) {
|
|
46
94
|
if (path.isAbsolute(sessionFile))
|
|
47
95
|
return undefined;
|
|
@@ -78,7 +126,7 @@ export function readActorInspectorTurns(stateDir) {
|
|
|
78
126
|
withFileTypes: true,
|
|
79
127
|
})
|
|
80
128
|
.filter((file) => file.isFile() && file.name.endsWith(".jsonl"))
|
|
81
|
-
.map((file) => path.join("sessions", entry.name, file.name))
|
|
129
|
+
.map((file) => path.posix.join("sessions", entry.name, file.name))
|
|
82
130
|
.filter((file) => !recordedFiles.has(file));
|
|
83
131
|
return files.length > 0
|
|
84
132
|
? [{ id: entry.name, session_files: files, stage: "subagent" }]
|
|
@@ -15,12 +15,15 @@ export interface RunObservation {
|
|
|
15
15
|
failures?: number;
|
|
16
16
|
ownerId?: string;
|
|
17
17
|
artifacts?: Record<string, string>;
|
|
18
|
+
launchCorrelation?: Record<string, string>;
|
|
18
19
|
launchSource?: AsyncRuns.AsyncRunLaunchSource;
|
|
19
20
|
modelPolicy?: Record<string, unknown>;
|
|
21
|
+
notificationPolicy?: "normal" | "silent";
|
|
20
22
|
recipeFile?: string;
|
|
21
23
|
terminalHandled?: boolean;
|
|
22
24
|
retireWhen?: string;
|
|
23
25
|
run: string;
|
|
26
|
+
semanticResult?: RunTerminalSemanticResult;
|
|
24
27
|
tool?: string;
|
|
25
28
|
stateDir?: string;
|
|
26
29
|
status: RunObservedStatus;
|
|
@@ -130,12 +133,22 @@ export interface RunTransition {
|
|
|
130
133
|
run: string;
|
|
131
134
|
stateDir?: string;
|
|
132
135
|
artifacts?: Record<string, string>;
|
|
136
|
+
launchCorrelation?: Record<string, string>;
|
|
133
137
|
launchSource?: AsyncRuns.AsyncRunLaunchSource;
|
|
134
138
|
modelPolicy?: Record<string, unknown>;
|
|
135
139
|
recipeFile?: string;
|
|
136
140
|
terminalHandled?: boolean;
|
|
137
141
|
to: RunObservedStatus;
|
|
138
142
|
tool?: string;
|
|
143
|
+
semanticResult?: RunTerminalSemanticResult;
|
|
144
|
+
}
|
|
145
|
+
export interface RunTerminalSemanticResult {
|
|
146
|
+
body?: string;
|
|
147
|
+
correlationId?: string;
|
|
148
|
+
metadata: Record<string, unknown>;
|
|
149
|
+
summary: string;
|
|
150
|
+
synthesized: boolean;
|
|
151
|
+
type: string;
|
|
139
152
|
}
|
|
140
153
|
export interface RunOutboxEvent {
|
|
141
154
|
body?: unknown;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Zones: async runtime, ambient UI, diagnostics
|
|
4
4
|
* Owns ambient summaries, terminal events, and run outbox delivery for detached command-template runs
|
|
5
5
|
*/
|
|
6
|
-
import { existsSync, readdirSync, readFileSync, watch, } from "node:fs";
|
|
6
|
+
import { closeSync, existsSync, fstatSync, openSync, readdirSync, readFileSync, readSync, watch, } from "node:fs";
|
|
7
7
|
import { basename, dirname, isAbsolute, join, relative, resolve, } from "node:path";
|
|
8
8
|
import * as AsyncRuns from "./async-runs.js";
|
|
9
9
|
import * as Paths from "./paths.js";
|
|
@@ -54,6 +54,13 @@ export function deliverRunTransitionNotifications(transitions, sink, inFlight =
|
|
|
54
54
|
AsyncRuns.markRunTerminalNotificationHandled(transition.stateDir, transition.to);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (transition.stateDir) {
|
|
59
|
+
AsyncRuns.recordRunTerminalDeliveryFailure(transition.stateDir, transition.to, error);
|
|
60
|
+
}
|
|
61
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
62
|
+
sink.notify(`Actor terminal delivery failed for run:${transition.run}: ${message.replaceAll(/\s+/g, " ").slice(0, 240)}`, "error");
|
|
63
|
+
}
|
|
57
64
|
finally {
|
|
58
65
|
inFlight.delete(key);
|
|
59
66
|
}
|
|
@@ -282,6 +289,121 @@ function scanRunStateDirs(stateRoot, depth = 0, seen = new Set()) {
|
|
|
282
289
|
}
|
|
283
290
|
return result;
|
|
284
291
|
}
|
|
292
|
+
const TERMINAL_RESULT_BYTES = 8 * 1024;
|
|
293
|
+
const TERMINAL_RESULT_CHARS = 4_000;
|
|
294
|
+
function readBoundedStart(path) {
|
|
295
|
+
if (!existsSync(path))
|
|
296
|
+
return "";
|
|
297
|
+
const fd = openSync(path, "r");
|
|
298
|
+
try {
|
|
299
|
+
const size = Math.min(fstatSync(fd).size, TERMINAL_RESULT_BYTES);
|
|
300
|
+
const buffer = Buffer.alloc(size);
|
|
301
|
+
const bytes = readSync(fd, buffer, 0, size, 0);
|
|
302
|
+
const text = buffer.subarray(0, bytes).toString("utf8").trim();
|
|
303
|
+
return text.length > TERMINAL_RESULT_CHARS
|
|
304
|
+
? `${text.slice(0, TERMINAL_RESULT_CHARS - 1)}…`
|
|
305
|
+
: text;
|
|
306
|
+
}
|
|
307
|
+
finally {
|
|
308
|
+
closeSync(fd);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function semanticBodyFromStdout(stateDir) {
|
|
312
|
+
const text = readBoundedStart(join(stateDir, "stdout.log"));
|
|
313
|
+
if (!text)
|
|
314
|
+
return undefined;
|
|
315
|
+
const withoutMarker = text.replace(/^ACTOR_REVIEW_RESULT\s*(?:\r?\n)?/, "").trim();
|
|
316
|
+
return withoutMarker || undefined;
|
|
317
|
+
}
|
|
318
|
+
function terminalSemanticResult(status, stateDir, observedStatus) {
|
|
319
|
+
const correlation = status.launch_correlation &&
|
|
320
|
+
typeof status.launch_correlation === "object" &&
|
|
321
|
+
!Array.isArray(status.launch_correlation)
|
|
322
|
+
? status.launch_correlation
|
|
323
|
+
: {};
|
|
324
|
+
const correlationId = typeof correlation.correlation_id === "string"
|
|
325
|
+
? correlation.correlation_id
|
|
326
|
+
: typeof correlation.tool_call_id === "string"
|
|
327
|
+
? correlation.tool_call_id
|
|
328
|
+
: undefined;
|
|
329
|
+
const mailbox = status.mailbox &&
|
|
330
|
+
typeof status.mailbox === "object" &&
|
|
331
|
+
!Array.isArray(status.mailbox)
|
|
332
|
+
? status.mailbox
|
|
333
|
+
: {};
|
|
334
|
+
const emits = Array.isArray(mailbox.emits)
|
|
335
|
+
? mailbox.emits.filter((item) => typeof item === "string")
|
|
336
|
+
: [];
|
|
337
|
+
const outbox = readJsonlFileResilient(join(stateDir, "outbox.jsonl")).records;
|
|
338
|
+
const explicit = outbox.findLast((record) => {
|
|
339
|
+
const type = String(record.type ?? record.event ?? "");
|
|
340
|
+
return emits.includes(type) && !["command.done", "run.done", "run.failed"].includes(type);
|
|
341
|
+
});
|
|
342
|
+
if (explicit) {
|
|
343
|
+
const type = String(explicit.type ?? explicit.event);
|
|
344
|
+
return {
|
|
345
|
+
...(explicit.body === undefined
|
|
346
|
+
? {} : { body: formatSemanticBody(explicit.body) }),
|
|
347
|
+
...(typeof explicit.correlation_id === "string"
|
|
348
|
+
? { correlationId: explicit.correlation_id } : correlationId ? { correlationId } : {}),
|
|
349
|
+
metadata: {
|
|
350
|
+
...(explicit.metadata &&
|
|
351
|
+
typeof explicit.metadata === "object" &&
|
|
352
|
+
!Array.isArray(explicit.metadata)
|
|
353
|
+
? explicit.metadata : {}),
|
|
354
|
+
...(status.transport_context &&
|
|
355
|
+
typeof status.transport_context === "object" &&
|
|
356
|
+
!Array.isArray(status.transport_context)
|
|
357
|
+
? {
|
|
358
|
+
transport_context: status.transport_context,
|
|
359
|
+
} : {}),
|
|
360
|
+
run: String(status.run ?? ""),
|
|
361
|
+
status: observedStatus,
|
|
362
|
+
},
|
|
363
|
+
summary: String(explicit.summary ?? type),
|
|
364
|
+
synthesized: false,
|
|
365
|
+
type,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const reviewCompleted = observedStatus === "done" && emits.includes("review.completed");
|
|
369
|
+
const result = status.result &&
|
|
370
|
+
typeof status.result === "object" &&
|
|
371
|
+
!Array.isArray(status.result)
|
|
372
|
+
? status.result
|
|
373
|
+
: {};
|
|
374
|
+
const type = reviewCompleted
|
|
375
|
+
? "review.completed"
|
|
376
|
+
: observedStatus === "done" ? "run.done" : "run.failed";
|
|
377
|
+
const stdoutBody = observedStatus === "done"
|
|
378
|
+
? semanticBodyFromStdout(stateDir) : undefined;
|
|
379
|
+
return {
|
|
380
|
+
...(stdoutBody
|
|
381
|
+
? { body: stdoutBody }
|
|
382
|
+
: typeof result.error === "string" ? { body: result.error } : {}),
|
|
383
|
+
...(correlationId ? { correlationId } : {}),
|
|
384
|
+
metadata: {
|
|
385
|
+
...(status.transport_context &&
|
|
386
|
+
typeof status.transport_context === "object" &&
|
|
387
|
+
!Array.isArray(status.transport_context)
|
|
388
|
+
? {
|
|
389
|
+
transport_context: status.transport_context,
|
|
390
|
+
} : {}),
|
|
391
|
+
run: String(status.run ?? ""), status: observedStatus,
|
|
392
|
+
},
|
|
393
|
+
summary: reviewCompleted ? "Review completed." :
|
|
394
|
+
observedStatus === "done" ? "Run completed." : `Run ${observedStatus}.`,
|
|
395
|
+
synthesized: true,
|
|
396
|
+
type,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function formatSemanticBody(body) {
|
|
400
|
+
const rendered = typeof body === "string" ? body : JSON.stringify(body);
|
|
401
|
+
const text = typeof rendered === "string" ? rendered : String(body);
|
|
402
|
+
const compact = text.trim();
|
|
403
|
+
return compact.length > TERMINAL_RESULT_CHARS
|
|
404
|
+
? `${compact.slice(0, TERMINAL_RESULT_CHARS - 1)}…`
|
|
405
|
+
: compact;
|
|
406
|
+
}
|
|
285
407
|
function observeRun(stateDir) {
|
|
286
408
|
try {
|
|
287
409
|
const status = AsyncRuns.getRunStatus(stateDir);
|
|
@@ -289,9 +411,15 @@ function observeRun(stateDir) {
|
|
|
289
411
|
const run = typeof status.run === "string" ? status.run : undefined;
|
|
290
412
|
if (!run)
|
|
291
413
|
return undefined;
|
|
414
|
+
const observedStatus = status.status;
|
|
292
415
|
return {
|
|
293
416
|
activeSubagents: toNumber(progress.activeSubagents),
|
|
294
417
|
completed: toNumber(progress.completed),
|
|
418
|
+
...(status.launch_correlation &&
|
|
419
|
+
typeof status.launch_correlation === "object" &&
|
|
420
|
+
!Array.isArray(status.launch_correlation)
|
|
421
|
+
? { launchCorrelation: status.launch_correlation }
|
|
422
|
+
: {}),
|
|
295
423
|
failures: Array.isArray(progress.failures)
|
|
296
424
|
? progress.failures.length
|
|
297
425
|
: undefined,
|
|
@@ -311,6 +439,9 @@ function observeRun(stateDir) {
|
|
|
311
439
|
!Array.isArray(status.model_policy)
|
|
312
440
|
? { modelPolicy: status.model_policy }
|
|
313
441
|
: {}),
|
|
442
|
+
...(status.notification_policy === "silent"
|
|
443
|
+
? { notificationPolicy: "silent" }
|
|
444
|
+
: {}),
|
|
314
445
|
...(typeof status.recipe_file === "string"
|
|
315
446
|
? { recipeFile: status.recipe_file }
|
|
316
447
|
: {}),
|
|
@@ -318,9 +449,12 @@ function observeRun(stateDir) {
|
|
|
318
449
|
...(typeof status.retire_when === "string"
|
|
319
450
|
? { retireWhen: status.retire_when }
|
|
320
451
|
: {}),
|
|
452
|
+
...(TERMINAL.has(observedStatus)
|
|
453
|
+
? { semanticResult: terminalSemanticResult(status, stateDir, observedStatus) }
|
|
454
|
+
: {}),
|
|
321
455
|
run,
|
|
322
456
|
stateDir,
|
|
323
|
-
status:
|
|
457
|
+
status: observedStatus,
|
|
324
458
|
...(typeof status.tool === "string" ? { tool: status.tool } : {}),
|
|
325
459
|
updatedAt: getUpdatedAt(status),
|
|
326
460
|
};
|
|
@@ -615,15 +749,19 @@ export function detectRunTransitions(previous, summary) {
|
|
|
615
749
|
for (const run of summary.runs) {
|
|
616
750
|
const key = runObservationKey(run);
|
|
617
751
|
const old = previous.get(key);
|
|
618
|
-
if (
|
|
752
|
+
if (run.notificationPolicy !== "silent" &&
|
|
753
|
+
!run.terminalHandled &&
|
|
754
|
+
TERMINAL.has(run.status)) {
|
|
619
755
|
transitions.push({
|
|
620
756
|
from: old ?? "running",
|
|
621
757
|
run: run.run,
|
|
622
758
|
...(run.stateDir ? { stateDir: run.stateDir } : {}),
|
|
623
759
|
...(run.artifacts ? { artifacts: run.artifacts } : {}),
|
|
760
|
+
...(run.launchCorrelation ? { launchCorrelation: run.launchCorrelation } : {}),
|
|
624
761
|
...(run.launchSource ? { launchSource: run.launchSource } : {}),
|
|
625
762
|
...(run.modelPolicy ? { modelPolicy: run.modelPolicy } : {}),
|
|
626
763
|
...(run.recipeFile ? { recipeFile: run.recipeFile } : {}),
|
|
764
|
+
...(run.semanticResult ? { semanticResult: run.semanticResult } : {}),
|
|
627
765
|
...(run.terminalHandled ? { terminalHandled: true } : {}),
|
|
628
766
|
to: run.status,
|
|
629
767
|
...(run.tool ? { tool: run.tool } : {}),
|
|
@@ -707,6 +845,11 @@ export function detectRunOutboxEvents(previousLineCounts, summary, seenEventIds
|
|
|
707
845
|
const key = run.stateDir ?? run.run;
|
|
708
846
|
const records = readOutboxRecords(run);
|
|
709
847
|
const previousCount = previousLineCounts.get(key) ?? 0;
|
|
848
|
+
if (run.notificationPolicy === "silent") {
|
|
849
|
+
previousLineCounts.set(key, records.length);
|
|
850
|
+
seenEventIds.set(key, seenEventIds.get(key) ?? new Set());
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
710
853
|
const start = Math.min(previousCount, records.length);
|
|
711
854
|
const seen = seenEventIds.get(key) ?? new Set();
|
|
712
855
|
for (let index = start; index < records.length; index += 1) {
|
|
@@ -831,7 +974,8 @@ function isUserRecipeFile(file) {
|
|
|
831
974
|
return false;
|
|
832
975
|
const recipeRoot = resolve(Paths.getRecipeRoot());
|
|
833
976
|
const path = resolve(file);
|
|
834
|
-
|
|
977
|
+
const relation = relative(recipeRoot, path);
|
|
978
|
+
return relation === "" || (!relation.startsWith("..") && !isAbsolute(relation));
|
|
835
979
|
}
|
|
836
980
|
export function shouldSuggestRecipePersistence(transition) {
|
|
837
981
|
if (transition.to !== "done")
|
|
@@ -877,16 +1021,26 @@ function formatTransitionNextActions(transition) {
|
|
|
877
1021
|
].filter(Boolean);
|
|
878
1022
|
return `\nNext actions: ${actions.join(" | ")}`;
|
|
879
1023
|
}
|
|
1024
|
+
function formatTransitionSemanticResult(transition) {
|
|
1025
|
+
const result = transition.semanticResult;
|
|
1026
|
+
if (!result)
|
|
1027
|
+
return "";
|
|
1028
|
+
const correlation = result.correlationId
|
|
1029
|
+
? `\nCorrelation: ${result.correlationId}` : "";
|
|
1030
|
+
const body = result.body ? `\nResult:\n${result.body}` : "";
|
|
1031
|
+
return `\nSemantic result: ${result.type} — ${result.summary}${correlation}${body}`;
|
|
1032
|
+
}
|
|
880
1033
|
export function formatRunTransitionMessage(transition) {
|
|
881
1034
|
const artifacts = formatNamedArtifacts(transition.artifacts);
|
|
882
1035
|
const runFiles = formatRunFileList(getRunArtifacts(transition));
|
|
883
1036
|
const persistenceSuggestion = formatRecipePersistenceSuggestion(transition);
|
|
884
1037
|
const policy = formatTransitionPolicy(transition);
|
|
885
1038
|
const nextActions = formatTransitionNextActions(transition);
|
|
1039
|
+
const semanticResult = formatTransitionSemanticResult(transition);
|
|
886
1040
|
if (transition.to === "done")
|
|
887
|
-
return `Run ${transition.run} completed successfully.${policy}${artifacts}${runFiles}${nextActions}${persistenceSuggestion}`;
|
|
1041
|
+
return `Run ${transition.run} completed successfully.${semanticResult}${policy}${artifacts}${runFiles}${nextActions}${persistenceSuggestion}`;
|
|
888
1042
|
if (transition.to === "failed")
|
|
889
|
-
return `Run ${transition.run} failed.${policy}${artifacts}${runFiles}${nextActions}`;
|
|
1043
|
+
return `Run ${transition.run} failed.${semanticResult}${policy}${artifacts}${runFiles}${nextActions}`;
|
|
890
1044
|
if (transition.to === "cancelled")
|
|
891
1045
|
return `Run ${transition.run} was cancelled.${policy}${nextActions}`;
|
|
892
1046
|
if (transition.to === "killed")
|
package/dist/lib/paths.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Owns agent directory, tools config, recipe root, and actor run state root resolution
|
|
5
5
|
*/
|
|
6
6
|
export declare function getAgentDir(env?: Record<string, string | undefined>): string;
|
|
7
|
+
export declare function isAutomaticRecipeReviewEnabled(env?: Record<string, string | undefined>): boolean;
|
|
7
8
|
export interface ExtensionRuntimePaths {
|
|
8
9
|
configPath: string;
|
|
9
10
|
runStateRoot: string;
|
|
@@ -12,6 +13,12 @@ export interface ExtensionRuntimePaths {
|
|
|
12
13
|
export declare function getConfigPath(agentDir?: string): string;
|
|
13
14
|
export declare function getExtensionTmpDir(agentDir?: string, extensionName?: string): string;
|
|
14
15
|
export declare function getRunStateRoot(agentDir?: string): string;
|
|
16
|
+
export declare function getDraftSleepRoot(agentDir?: string): string;
|
|
17
|
+
export declare function getDraftSleepStatePath(agentDir?: string): string;
|
|
18
|
+
export declare function getDraftSleepBatchDir(batchId: string, agentDir?: string): string;
|
|
19
|
+
export declare function getToolReviewRoot(agentDir?: string): string;
|
|
20
|
+
export declare function getToolReviewStatePath(agentDir?: string): string;
|
|
21
|
+
export declare function getToolReviewBatchDir(reviewId: string, agentDir?: string): string;
|
|
15
22
|
export declare function getExtensionRuntimePaths(agentDir?: string): ExtensionRuntimePaths;
|
|
16
23
|
export declare const EXTENSION_RUNTIME_PATHS: ExtensionRuntimePaths;
|
|
17
24
|
export declare function getExtensionSkillsDir(extensionUrl: string): string;
|
package/dist/lib/paths.js
CHANGED
|
@@ -12,6 +12,10 @@ export function getAgentDir(env = process.env) {
|
|
|
12
12
|
? resolve(env.PI_CODING_AGENT_DIR)
|
|
13
13
|
: join(homedir(), ".pi", "agent");
|
|
14
14
|
}
|
|
15
|
+
export function isAutomaticRecipeReviewEnabled(env = process.env) {
|
|
16
|
+
const value = env.PI_ACTORS_AUTOMATIC_REVIEW?.trim().toLowerCase();
|
|
17
|
+
return value === undefined || !["0", "false", "off"].includes(value);
|
|
18
|
+
}
|
|
15
19
|
export function getConfigPath(agentDir = getAgentDir()) {
|
|
16
20
|
return join(agentDir, "tool-registry.json");
|
|
17
21
|
}
|
|
@@ -21,6 +25,30 @@ export function getExtensionTmpDir(agentDir = getAgentDir(), extensionName = "pi
|
|
|
21
25
|
export function getRunStateRoot(agentDir = getAgentDir()) {
|
|
22
26
|
return join(getExtensionTmpDir(agentDir), "runs");
|
|
23
27
|
}
|
|
28
|
+
export function getDraftSleepRoot(agentDir = getAgentDir()) {
|
|
29
|
+
return join(getExtensionTmpDir(agentDir), "draft-sleep");
|
|
30
|
+
}
|
|
31
|
+
export function getDraftSleepStatePath(agentDir = getAgentDir()) {
|
|
32
|
+
return join(getDraftSleepRoot(agentDir), "state.json");
|
|
33
|
+
}
|
|
34
|
+
export function getDraftSleepBatchDir(batchId, agentDir = getAgentDir()) {
|
|
35
|
+
if (!/^[a-f0-9-]{36}$/u.test(batchId)) {
|
|
36
|
+
throw new Error("Invalid draft sleep batch id.");
|
|
37
|
+
}
|
|
38
|
+
return join(getDraftSleepRoot(agentDir), "batches", batchId);
|
|
39
|
+
}
|
|
40
|
+
export function getToolReviewRoot(agentDir = getAgentDir()) {
|
|
41
|
+
return join(getExtensionTmpDir(agentDir), "tool-review");
|
|
42
|
+
}
|
|
43
|
+
export function getToolReviewStatePath(agentDir = getAgentDir()) {
|
|
44
|
+
return join(getToolReviewRoot(agentDir), "state.json");
|
|
45
|
+
}
|
|
46
|
+
export function getToolReviewBatchDir(reviewId, agentDir = getAgentDir()) {
|
|
47
|
+
if (!/^[a-f0-9-]{36}$/u.test(reviewId)) {
|
|
48
|
+
throw new Error("Invalid tool review id.");
|
|
49
|
+
}
|
|
50
|
+
return join(getToolReviewRoot(agentDir), "batches", reviewId);
|
|
51
|
+
}
|
|
24
52
|
export function getExtensionRuntimePaths(agentDir = getAgentDir()) {
|
|
25
53
|
return {
|
|
26
54
|
configPath: getConfigPath(agentDir),
|
|
@@ -528,30 +528,38 @@ function templatePreview(value) {
|
|
|
528
528
|
}
|
|
529
529
|
export function listDraftRecipes(root) {
|
|
530
530
|
return listRecipeFiles(root).map((path) => {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
531
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
532
|
+
const id = RecipesReferences.getRecipeIdFromPath(path);
|
|
533
|
+
const bytes = readFileSync(path);
|
|
534
|
+
const fingerprint = createHash("sha256").update(bytes).digest("hex");
|
|
535
|
+
const stat = statSync(path);
|
|
536
|
+
const config = RecipesReferences.readRawRecipeConfig(path);
|
|
537
|
+
const resolved = RecipesReferences.readResolvedRecipeConfig(path);
|
|
538
|
+
const diagnostics = getRecipeConfigDiagnostics(path, resolved);
|
|
539
|
+
const after = readFileSync(path);
|
|
540
|
+
if (createHash("sha256").update(after).digest("hex") !== fingerprint) {
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
const sourceRun = String(config?.description ?? "").match(/spawn run ([^\s]+)/)?.[1];
|
|
544
|
+
const preview = templatePreview(config?.template);
|
|
545
|
+
const riskLabels = getRecipeRiskLabels(resolved);
|
|
546
|
+
return {
|
|
547
|
+
id,
|
|
548
|
+
path,
|
|
549
|
+
sha256: fingerprint,
|
|
550
|
+
size: bytes.byteLength,
|
|
551
|
+
created_at: stat.birthtime.toISOString(),
|
|
552
|
+
modified_at: stat.mtime.toISOString(),
|
|
553
|
+
valid: Boolean(resolved),
|
|
554
|
+
diagnostics,
|
|
555
|
+
...(riskLabels.length ? { risk_labels: riskLabels } : {}),
|
|
556
|
+
...(config?.description ? { description: config.description } : {}),
|
|
557
|
+
...(sourceRun ? { source_run: sourceRun } : {}),
|
|
558
|
+
...(config?.async !== undefined ? { async: config.async } : {}),
|
|
559
|
+
...(preview ? { template_preview: preview } : {}),
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
throw new Error(`Draft changed repeatedly during inventory: ${path}`);
|
|
555
563
|
});
|
|
556
564
|
}
|
|
557
565
|
export function summarizeDiscovery(result) {
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Recipe usage metadata
|
|
3
|
-
* Zones: recipe telemetry,
|
|
4
|
-
* Owns
|
|
2
|
+
* Recipe usage and name-based lineage metadata.
|
|
3
|
+
* Zones: recipe lineage, launch telemetry, revision history, migration
|
|
4
|
+
* Owns priority-compatible recipe names and lifetime/revision counters for user recipes.
|
|
5
5
|
*/
|
|
6
6
|
export type RecipeLaunchKind = "direct" | "spawn" | "tool";
|
|
7
|
-
export declare function
|
|
8
|
-
export declare function
|
|
9
|
-
export declare
|
|
7
|
+
export declare function getRecipeUsageIndexPath(recipeRoot: string): string;
|
|
8
|
+
export declare function getRecipeUsageLedgerPath(lineageName: string, recipeRoot: string): string;
|
|
9
|
+
export declare const RECIPE_REVISION_SNAPSHOT_LIMIT = 32;
|
|
10
|
+
export declare function getRecipeRevisionSnapshotPath(lineageName: string, revision: number, recipeRoot: string): string;
|
|
11
|
+
export declare function readRecipeUsage(path: string, recipeRoot?: string): Record<string, unknown> | undefined;
|
|
12
|
+
export declare function ensureRecipeLineage(path: string, now?: Date, recipeRoot?: string): boolean;
|
|
13
|
+
export declare function isCurrentRecipeRevisionReviewed(path: string, recipeRoot?: string): boolean;
|
|
14
|
+
export interface RecipeLaunchRecordOptions {
|
|
15
|
+
onMutationLockContention?(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function recordRecipeLaunch(path: string, now?: Date, kind?: RecipeLaunchKind, recipeRoot?: string, options?: RecipeLaunchRecordOptions): boolean;
|
|
18
|
+
export declare function recordRecipeReview(path: string, reviewEpoch: string, now?: Date, recipeRoot?: string): boolean;
|
|
19
|
+
export declare function recordRecipeRollback(path: string, rollbackRevision: number, now?: Date, recipeRoot?: string): boolean;
|
|
20
|
+
export declare function retireRecipeUsage(path: string, recipeRoot?: string): boolean;
|
|
21
|
+
export declare function moveRecipeUsage(fromPath: string, toPath: string, recipeRoot?: string): boolean;
|