@llblab/pi-actors 0.14.2 → 0.15.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/AGENTS.md +5 -1
- package/BACKLOG.md +18 -32
- package/CHANGELOG.md +25 -0
- package/README.md +24 -20
- package/docs/actor-messages.md +1 -1
- package/docs/async-runs.md +4 -4
- package/docs/command-templates.md +11 -11
- package/docs/recipe-library.md +7 -3
- package/docs/task-first-recipes.md +44 -43
- package/docs/template-recipes.md +7 -2
- package/docs/tool-registry.md +7 -5
- package/lib/actor-messages.ts +20 -7
- package/lib/async-runs.ts +25 -12
- package/lib/command-templates.ts +6 -1
- package/lib/config.ts +2 -2
- package/lib/execution.ts +9 -5
- package/lib/observability.ts +20 -10
- package/lib/prompts.ts +13 -20
- package/lib/tools.ts +196 -64
- package/package.json +17 -9
- package/recipes/coordinator-locker.json +46 -0
- package/recipes/music-player.json +16 -2
- package/recipes/pipeline-architect-coordinator.json +11 -3
- package/recipes/pipeline-artifact-bundle.json +12 -3
- package/recipes/pipeline-artifact-report.json +9 -3
- package/recipes/pipeline-artifact-write.json +9 -3
- package/recipes/pipeline-async-run-ops.json +18 -9
- package/recipes/pipeline-checkpoint-continuation.json +14 -3
- package/recipes/pipeline-development-tasking.json +12 -3
- package/recipes/pipeline-docs-maintenance.json +12 -3
- package/recipes/pipeline-media-library.json +12 -3
- package/recipes/pipeline-quorum-review.json +12 -9
- package/recipes/pipeline-release-readiness.json +27 -9
- package/recipes/pipeline-release-summary.json +89 -0
- package/recipes/pipeline-repo-health.json +12 -3
- package/recipes/pipeline-research-synthesis.json +11 -3
- package/recipes/pipeline-review-readiness.json +12 -6
- package/recipes/subagent-artifact.json +9 -3
- package/recipes/subagent-checkpoint.json +10 -3
- package/recipes/subagent-conflict-report.json +11 -3
- package/recipes/subagent-contradiction-map.json +11 -3
- package/recipes/subagent-critic.json +11 -3
- package/recipes/subagent-evidence-map.json +11 -3
- package/recipes/subagent-followup.json +10 -3
- package/recipes/subagent-judge.json +11 -3
- package/recipes/subagent-merge.json +11 -3
- package/recipes/subagent-message.json +8 -3
- package/recipes/subagent-normalize.json +11 -3
- package/recipes/subagent-plan.json +11 -3
- package/recipes/subagent-prompt.json +10 -3
- package/recipes/subagent-quorum.json +10 -7
- package/recipes/subagent-review-coordinator.json +14 -6
- package/recipes/subagent-review.json +11 -3
- package/recipes/subagent-task-card.json +11 -3
- package/recipes/subagent-tools.json +10 -3
- package/recipes/subagent-verify.json +11 -3
- package/recipes/subagents-prompts.json +10 -3
- package/recipes/utility-coordinator-lock-snapshot.json +14 -0
- package/recipes/utility-run-ops-snapshot.json +3 -3
- package/recipes/utility-skill-summary.json +14 -0
- package/scripts/coordinator-locker.mjs +272 -0
- package/scripts/music-player.mjs +2 -1
- package/scripts/recipe-utils.mjs +239 -81
- package/scripts/validate-recipe.mjs +28 -10
- package/skills/actors/SKILL.md +283 -0
- package/skills/swarm/SKILL.md +451 -0
- package/skills/swarm/references/development-swarm.md +596 -0
package/docs/tool-registry.md
CHANGED
|
@@ -33,7 +33,7 @@ register_tool name=transcribe_groq \
|
|
|
33
33
|
```text
|
|
34
34
|
register_tool name=call_subagent \
|
|
35
35
|
description="Run pi as a non-interactive sub-agent" \
|
|
36
|
-
template="pi -p --model {model
|
|
36
|
+
template="pi -p --model {model} --no-tools {prompt}" args="prompt:string,model:string"
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Use `update=true` to overwrite an existing tool. Omit `template` and co-located recipe fields during update to keep the previous execution binding.
|
|
@@ -53,7 +53,7 @@ For reusable actor workflows, register a small tool whose `template` points to a
|
|
|
53
53
|
register_tool name=docs_review \
|
|
54
54
|
description="Start an async docs review actor" \
|
|
55
55
|
template="docs-review.json" \
|
|
56
|
-
args="scope:path,model:string
|
|
56
|
+
args="scope:path,model:string"
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
This stores the recipe path in the registry as `template`. If `~/.pi/agent/recipes/docs-review.json` contains `async: true`, calling the tool starts a detached actor run and returns metadata immediately. If `async` is omitted or false, the same recipe runs foreground and returns normal tool output.
|
|
@@ -66,7 +66,8 @@ When co-location is clearer than a separate file, the registry entry may include
|
|
|
66
66
|
"description": "Start an async docs review",
|
|
67
67
|
"name": "review-docs",
|
|
68
68
|
"async": true,
|
|
69
|
-
"
|
|
69
|
+
"args": ["scope:path", "model:string"],
|
|
70
|
+
"template": "pi -p --model {model} --tools read,bash \"Review {scope}\""
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
```
|
|
@@ -91,11 +92,12 @@ Tool names come from the top-level registry keys. Tool entries define `template`
|
|
|
91
92
|
},
|
|
92
93
|
"call_subagent": {
|
|
93
94
|
"description": "Run pi as a non-interactive sub-agent",
|
|
94
|
-
"
|
|
95
|
+
"args": ["prompt:string", "model:string"],
|
|
96
|
+
"template": "pi -p --model {model} --no-tools {prompt}"
|
|
95
97
|
},
|
|
96
98
|
"docs_review": {
|
|
97
99
|
"description": "Start an async docs review actor",
|
|
98
|
-
"args": ["scope:path", "model:string
|
|
100
|
+
"args": ["scope:path", "model:string"],
|
|
99
101
|
"template": "docs-review.json"
|
|
100
102
|
}
|
|
101
103
|
}
|
package/lib/actor-messages.ts
CHANGED
|
@@ -44,13 +44,15 @@ export function parseActorAddress(address: string): ActorAddress {
|
|
|
44
44
|
const value = address.trim();
|
|
45
45
|
if (value === "coordinator") return { kind: "coordinator" };
|
|
46
46
|
const separator = value.indexOf(":");
|
|
47
|
-
if (separator < 0)
|
|
47
|
+
if (separator < 0)
|
|
48
|
+
throw new Error(`Actor address must include kind: ${address}`);
|
|
48
49
|
const kind = value.slice(0, separator) as ActorAddressKind;
|
|
49
50
|
const rest = value.slice(separator + 1);
|
|
50
51
|
switch (kind) {
|
|
51
52
|
case "branch": {
|
|
52
53
|
const [run, branch, ...extra] = rest.split("/");
|
|
53
|
-
if (extra.length > 0)
|
|
54
|
+
if (extra.length > 0)
|
|
55
|
+
throw new Error(`Branch address has too many parts: ${address}`);
|
|
54
56
|
return {
|
|
55
57
|
kind,
|
|
56
58
|
value: assertToken(run || "", "branch run"),
|
|
@@ -74,14 +76,19 @@ export function formatActorAddress(address: ActorAddress): string {
|
|
|
74
76
|
return `${address.kind}:${assertToken(address.value || "", `${address.kind} address`)}`;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
function normalizeOptionalString(
|
|
79
|
+
function normalizeOptionalString(
|
|
80
|
+
value: unknown,
|
|
81
|
+
label: string,
|
|
82
|
+
): string | undefined {
|
|
78
83
|
if (value === undefined || value === null) return undefined;
|
|
79
84
|
if (typeof value !== "string") throw new Error(`${label} must be a string`);
|
|
80
85
|
const normalized = value.trim();
|
|
81
86
|
return normalized || undefined;
|
|
82
87
|
}
|
|
83
88
|
|
|
84
|
-
function normalizeMetadata(
|
|
89
|
+
function normalizeMetadata(
|
|
90
|
+
value: unknown,
|
|
91
|
+
): Record<string, unknown> | undefined {
|
|
85
92
|
if (value === undefined || value === null) return undefined;
|
|
86
93
|
if (typeof value !== "object" || Array.isArray(value)) {
|
|
87
94
|
throw new Error("message metadata must be an object");
|
|
@@ -113,8 +120,14 @@ export function normalizeActorMessage(input: unknown): ActorMessage {
|
|
|
113
120
|
? { correlation_id: String(record.correlation_id) }
|
|
114
121
|
: {}),
|
|
115
122
|
...(from ? { from: formatActorAddress(parseActorAddress(from)) } : {}),
|
|
116
|
-
...(record.metadata !== undefined
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
...(record.metadata !== undefined
|
|
124
|
+
? { metadata: normalizeMetadata(record.metadata) }
|
|
125
|
+
: {}),
|
|
126
|
+
...(record.reply_to !== undefined
|
|
127
|
+
? { reply_to: String(record.reply_to) }
|
|
128
|
+
: {}),
|
|
129
|
+
...(record.summary !== undefined
|
|
130
|
+
? { summary: String(record.summary) }
|
|
131
|
+
: {}),
|
|
119
132
|
};
|
|
120
133
|
}
|
package/lib/async-runs.ts
CHANGED
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
writeFileSync,
|
|
20
20
|
writeSync,
|
|
21
21
|
} from "node:fs";
|
|
22
|
-
import { basename, extname, join, resolve } from "node:path";
|
|
23
22
|
import { platform } from "node:os";
|
|
23
|
+
import { basename, extname, join, resolve } from "node:path";
|
|
24
24
|
|
|
25
25
|
import type {
|
|
26
26
|
CommandTemplateFailureScope,
|
|
@@ -28,8 +28,8 @@ import type {
|
|
|
28
28
|
} from "./command-templates.ts";
|
|
29
29
|
import { substituteCommandTemplateToken } from "./command-templates.ts";
|
|
30
30
|
import { writeJsonAtomic } from "./file-state.ts";
|
|
31
|
-
import * as RecipeReferences from "./recipe-references.ts";
|
|
32
31
|
import * as Paths from "./paths.ts";
|
|
32
|
+
import * as RecipeReferences from "./recipe-references.ts";
|
|
33
33
|
|
|
34
34
|
export interface AsyncRunStartParams {
|
|
35
35
|
async?: boolean;
|
|
@@ -139,8 +139,7 @@ function resolveArtifactPaths(
|
|
|
139
139
|
function resolveRunTemplate(params: AsyncRunStartParams): {
|
|
140
140
|
template: CommandTemplateValue;
|
|
141
141
|
} {
|
|
142
|
-
if (!params.template)
|
|
143
|
-
throw new Error("spawn requires file or template.");
|
|
142
|
+
if (!params.template) throw new Error("spawn requires file or template.");
|
|
144
143
|
const envelope: Record<string, unknown> = {};
|
|
145
144
|
for (const key of [
|
|
146
145
|
"args",
|
|
@@ -227,7 +226,8 @@ function isAlive(pid: number): boolean {
|
|
|
227
226
|
}
|
|
228
227
|
|
|
229
228
|
function pidMatchesRun(pid: number, cwd: string, stateDir: string): boolean {
|
|
230
|
-
if (platform() !== "linux" || !existsSync(`/proc/${pid}`))
|
|
229
|
+
if (platform() !== "linux" || !existsSync(`/proc/${pid}`))
|
|
230
|
+
return isAlive(pid);
|
|
231
231
|
try {
|
|
232
232
|
const procCwd = readlinkSync(`/proc/${pid}/cwd`);
|
|
233
233
|
const cmdline = readFileSync(`/proc/${pid}/cmdline`, "utf8");
|
|
@@ -405,15 +405,23 @@ function normalizeRunOutboxEvent(
|
|
|
405
405
|
: `${run}:${index}`;
|
|
406
406
|
return {
|
|
407
407
|
...(record.body !== undefined ? { body: record.body } : {}),
|
|
408
|
-
...(typeof record.correlation_id === "string"
|
|
408
|
+
...(typeof record.correlation_id === "string"
|
|
409
|
+
? { correlation_id: record.correlation_id }
|
|
410
|
+
: {}),
|
|
409
411
|
...(record.data !== undefined ? { data: record.data } : {}),
|
|
410
412
|
delivery: normalizeRunOutboxDelivery(record.delivery),
|
|
411
|
-
...(record.metadata &&
|
|
413
|
+
...(record.metadata &&
|
|
414
|
+
typeof record.metadata === "object" &&
|
|
415
|
+
!Array.isArray(record.metadata)
|
|
416
|
+
? { metadata: record.metadata as Record<string, unknown> }
|
|
417
|
+
: {}),
|
|
412
418
|
event,
|
|
413
419
|
...(typeof record.from === "string" ? { from: record.from } : {}),
|
|
414
420
|
id,
|
|
415
421
|
level: normalizeRunOutboxLevel(record.level),
|
|
416
|
-
...(typeof record.reply_to === "string"
|
|
422
|
+
...(typeof record.reply_to === "string"
|
|
423
|
+
? { reply_to: record.reply_to }
|
|
424
|
+
: {}),
|
|
417
425
|
run,
|
|
418
426
|
state_dir: stateDir,
|
|
419
427
|
summary,
|
|
@@ -448,8 +456,9 @@ export function getRunStatus(runOrDir: string): Record<string, unknown> {
|
|
|
448
456
|
const pid = Number(meta.pid || 0);
|
|
449
457
|
const aliveOwnedRunner = Boolean(
|
|
450
458
|
pid &&
|
|
451
|
-
|
|
452
|
-
|
|
459
|
+
isAlive(pid) &&
|
|
460
|
+
(!Array.isArray(meta.argv) ||
|
|
461
|
+
pidMatchesRun(pid, String(meta.cwd ?? ""), stateDir)),
|
|
453
462
|
);
|
|
454
463
|
const status: AsyncRunStatus = result
|
|
455
464
|
? Number(result.code ?? 0) === 0
|
|
@@ -555,7 +564,9 @@ export function appendRunOutboxEvent(
|
|
|
555
564
|
...(event.body !== undefined ? { body: event.body } : {}),
|
|
556
565
|
...(event.correlation_id ? { correlation_id: event.correlation_id } : {}),
|
|
557
566
|
...(event.data !== undefined ? { data: event.data } : {}),
|
|
558
|
-
delivery: normalizeRunOutboxDelivery(
|
|
567
|
+
delivery: normalizeRunOutboxDelivery(
|
|
568
|
+
event.delivery ?? (to === "coordinator" ? "followup" : "log"),
|
|
569
|
+
),
|
|
559
570
|
event: type,
|
|
560
571
|
from: event.from || `run:${run}`,
|
|
561
572
|
level: normalizeRunOutboxLevel(event.level),
|
|
@@ -608,7 +619,9 @@ export function sendRunMessage(
|
|
|
608
619
|
fd = openSync(controlPath, constants.O_WRONLY | constants.O_NONBLOCK);
|
|
609
620
|
const bytes = writeSync(fd, payload);
|
|
610
621
|
const trimmedMessage = message.trim().toLowerCase();
|
|
611
|
-
const terminalMessage = ["stop", "cancel", "quit", "exit"].includes(
|
|
622
|
+
const terminalMessage = ["stop", "cancel", "quit", "exit"].includes(
|
|
623
|
+
trimmedMessage,
|
|
624
|
+
);
|
|
612
625
|
writeFileSync(
|
|
613
626
|
join(stateDir, "events.jsonl"),
|
|
614
627
|
`${JSON.stringify({ bytes, event: "run.message", terminal: terminalMessage || undefined, ts: new Date().toISOString() })}\n`,
|
package/lib/command-templates.ts
CHANGED
|
@@ -541,7 +541,12 @@ function shouldResolveEmbeddedCommandTemplateToken(
|
|
|
541
541
|
function isFalsyCommandTemplateValue(value: unknown): boolean {
|
|
542
542
|
if (value === undefined || value === null || value === false) return true;
|
|
543
543
|
const normalized = String(value).trim().toLowerCase();
|
|
544
|
-
return
|
|
544
|
+
return (
|
|
545
|
+
normalized === "" ||
|
|
546
|
+
normalized === "0" ||
|
|
547
|
+
normalized === "false" ||
|
|
548
|
+
normalized === "no"
|
|
549
|
+
);
|
|
545
550
|
}
|
|
546
551
|
|
|
547
552
|
function resolveCommandTemplateCondition(
|
package/lib/config.ts
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
8
|
|
|
9
|
+
import type { CommandTemplateValue } from "./command-templates.ts";
|
|
10
|
+
import * as CommandTemplates from "./command-templates.ts";
|
|
9
11
|
import { writeJsonAtomic } from "./file-state.ts";
|
|
10
12
|
import { normalizeToolName } from "./identity.ts";
|
|
11
|
-
import * as CommandTemplates from "./command-templates.ts";
|
|
12
13
|
import * as RecipeReferences from "./recipe-references.ts";
|
|
13
14
|
import * as Schema from "./schema.ts";
|
|
14
|
-
import type { CommandTemplateValue } from "./command-templates.ts";
|
|
15
15
|
|
|
16
16
|
export interface RegisteredTool {
|
|
17
17
|
name: string;
|
package/lib/execution.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Owns command-template invocation execution and pi tool-result payload formatting
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import * as CommandTemplates from "./command-templates.ts";
|
|
7
8
|
import type { RegisteredTool } from "./config.ts";
|
|
8
9
|
import { formatFailureOutput, formatOutput, formatToolText } from "./output.ts";
|
|
9
|
-
import * as CommandTemplates from "./command-templates.ts";
|
|
10
10
|
import * as Schema from "./schema.ts";
|
|
11
11
|
|
|
12
12
|
export interface ToolExecOptions {
|
|
@@ -268,9 +268,10 @@ function resolveNumericControlField(
|
|
|
268
268
|
label: string,
|
|
269
269
|
): number | undefined {
|
|
270
270
|
if (value === undefined) return undefined;
|
|
271
|
-
const resolved =
|
|
272
|
-
|
|
273
|
-
|
|
271
|
+
const resolved =
|
|
272
|
+
typeof value === "string"
|
|
273
|
+
? CommandTemplates.substituteCommandTemplateToken(value, values, label)
|
|
274
|
+
: value;
|
|
274
275
|
if (resolved === "") return undefined;
|
|
275
276
|
const numeric = Number(resolved);
|
|
276
277
|
if (!Number.isFinite(numeric) || numeric < 0)
|
|
@@ -407,7 +408,10 @@ async function executeTemplateConfig(
|
|
|
407
408
|
const controlValues = { ...(context.defaults ?? {}), ...params };
|
|
408
409
|
await applyDelay(normalized.delay, controlValues, signal);
|
|
409
410
|
if (
|
|
410
|
-
!CommandTemplates.shouldRunCommandTemplateNode(
|
|
411
|
+
!CommandTemplates.shouldRunCommandTemplateNode(
|
|
412
|
+
normalized.when,
|
|
413
|
+
controlValues,
|
|
414
|
+
)
|
|
411
415
|
) {
|
|
412
416
|
return {
|
|
413
417
|
branches: [],
|
package/lib/observability.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
8
|
-
import { basename, dirname,
|
|
8
|
+
import { basename, dirname, join, relative } from "node:path";
|
|
9
9
|
|
|
10
10
|
import * as AsyncRuns from "./async-runs.ts";
|
|
11
11
|
import * as Paths from "./paths.ts";
|
|
@@ -114,7 +114,9 @@ function observeRun(stateDir: string): RunObservation | undefined {
|
|
|
114
114
|
...(typeof status.ownerId === "string"
|
|
115
115
|
? { ownerId: status.ownerId }
|
|
116
116
|
: {}),
|
|
117
|
-
...(status.artifacts &&
|
|
117
|
+
...(status.artifacts &&
|
|
118
|
+
typeof status.artifacts === "object" &&
|
|
119
|
+
!Array.isArray(status.artifacts)
|
|
118
120
|
? { artifacts: status.artifacts as Record<string, string> }
|
|
119
121
|
: {}),
|
|
120
122
|
...(status.terminal_handled ? { terminalHandled: true } : {}),
|
|
@@ -359,7 +361,11 @@ function parseOutboxLine(
|
|
|
359
361
|
event,
|
|
360
362
|
id,
|
|
361
363
|
level: normalizeOutboxLevel(raw.level),
|
|
362
|
-
...(raw.metadata &&
|
|
364
|
+
...(raw.metadata &&
|
|
365
|
+
typeof raw.metadata === "object" &&
|
|
366
|
+
!Array.isArray(raw.metadata)
|
|
367
|
+
? { metadata: raw.metadata as Record<string, unknown> }
|
|
368
|
+
: {}),
|
|
363
369
|
run: run.run,
|
|
364
370
|
stateDir: run.stateDir,
|
|
365
371
|
summary,
|
|
@@ -413,7 +419,8 @@ export function shouldSendRunOutboxFollowUp(event: RunOutboxEvent): boolean {
|
|
|
413
419
|
|
|
414
420
|
function commonDirectory(paths: string[]): string | undefined {
|
|
415
421
|
if (paths.length === 0) return undefined;
|
|
416
|
-
const split = (path: string): string[] =>
|
|
422
|
+
const split = (path: string): string[] =>
|
|
423
|
+
dirname(path).split("/").filter(Boolean);
|
|
417
424
|
const first = split(paths[0]);
|
|
418
425
|
let length = first.length;
|
|
419
426
|
for (const path of paths.slice(1)) {
|
|
@@ -440,7 +447,9 @@ function formatPathGroup(label: string, paths: string[]): string {
|
|
|
440
447
|
const unique = [...new Set(paths.filter(Boolean))].slice(0, 8);
|
|
441
448
|
if (unique.length === 0) return "";
|
|
442
449
|
const base = commonDirectory(unique);
|
|
443
|
-
const names = unique
|
|
450
|
+
const names = unique
|
|
451
|
+
.map((path) => `\`${relativeName(base, path)}\``)
|
|
452
|
+
.join(", ");
|
|
444
453
|
return `\n${label}:\n- Base: ${base ? `\`${base}\`` : "current run"}\n- Files: ${names}`;
|
|
445
454
|
}
|
|
446
455
|
|
|
@@ -464,7 +473,9 @@ function formatNamedArtifacts(artifacts: unknown): string {
|
|
|
464
473
|
}
|
|
465
474
|
|
|
466
475
|
function getOutboxField(event: RunOutboxEvent, key: string): unknown {
|
|
467
|
-
return event.data &&
|
|
476
|
+
return event.data &&
|
|
477
|
+
typeof event.data === "object" &&
|
|
478
|
+
!Array.isArray(event.data)
|
|
468
479
|
? (event.data as Record<string, unknown>)[key]
|
|
469
480
|
: undefined;
|
|
470
481
|
}
|
|
@@ -478,7 +489,8 @@ function formatBodyPreview(body: unknown): string {
|
|
|
478
489
|
}
|
|
479
490
|
|
|
480
491
|
export function formatRunOutboxMessage(event: RunOutboxEvent): string {
|
|
481
|
-
if (event.event === "command.done")
|
|
492
|
+
if (event.event === "command.done")
|
|
493
|
+
return `Run ${event.run}: ${event.summary}`;
|
|
482
494
|
return `Run ${event.run}: ${event.summary}${formatBodyPreview(event.body)}${formatNamedArtifacts(getOutboxField(event, "artifacts"))}${formatRunFileList(getOutboxField(event, "run_files"))}`;
|
|
483
495
|
}
|
|
484
496
|
|
|
@@ -491,9 +503,7 @@ export function getRunTransitionNotificationType(
|
|
|
491
503
|
return "error";
|
|
492
504
|
}
|
|
493
505
|
|
|
494
|
-
export function shouldNotifyRunTransition(
|
|
495
|
-
transition: RunTransition,
|
|
496
|
-
): boolean {
|
|
506
|
+
export function shouldNotifyRunTransition(transition: RunTransition): boolean {
|
|
497
507
|
if (transition.terminalHandled) return false;
|
|
498
508
|
return (
|
|
499
509
|
transition.to === "done" ||
|
package/lib/prompts.ts
CHANGED
|
@@ -20,25 +20,18 @@ export const REGISTER_TOOL_GUIDELINES = [
|
|
|
20
20
|
];
|
|
21
21
|
|
|
22
22
|
export const ONBOARDING_SYSTEM_PROMPT = `pi-actors quick model:
|
|
23
|
-
- Local-first
|
|
24
|
-
-
|
|
25
|
-
- Command templates stay sync: string leaf, array sequence, object flags, parallel
|
|
26
|
-
-
|
|
27
|
-
- Recipes live in ~/.pi/agent/recipes/*.json and
|
|
28
|
-
- Recipe imports are local variables
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
- register_tool makes compact persistent buttons; args may be typed or derived from placeholders.
|
|
36
|
-
- For single calls or short pipelines, use foreground templates/tools.
|
|
37
|
-
- For subagents, swarms, background music, or long fanout, prefer async recipes/runs.
|
|
38
|
-
- Long async fanout = parent async recipe wrapping template(parallel: true) and imports; packaged fanout recipes bubble branch completion follow-ups by default.
|
|
39
|
-
- If asked to explore pi-actors, read README.md, docs/README.md, docs/template-recipes.md, docs/async-runs.md, and recipes/.
|
|
40
|
-
- Ambient triangles show active async commands/subagents for the launching coordinator.
|
|
41
|
-
- After async run finish, inspect status/tail/messages before final artifacts.`;
|
|
23
|
+
- Local-first actor memory: persist trusted local capabilities instead of rebuilding shell recipes.
|
|
24
|
+
- Layers: task -> command template -> recipe/tool -> spawn -> run:<id>; tool:<name> wraps registered capabilities.
|
|
25
|
+
- Command templates stay sync: string leaf, array sequence, object node; flags include args/defaults, parallel, when, timeout, delay, retry, failure, recover, repeat, output.
|
|
26
|
+
- Placeholders support typed/default args plus {value??fallback} and {flag?yes:no}.
|
|
27
|
+
- Recipes live in ~/.pi/agent/recipes/*.json, own template directly, and may declare metadata/defaults/imports/mailbox/artifacts.
|
|
28
|
+
- Recipe imports are local variables; imported recipes are definitions, not nested async runs; parent async:true creates one run.
|
|
29
|
+
- Use spawn/message/inspect for actor-level start/send/observe; avoid runtime/FIFO/outbox vocabulary in public guidance.
|
|
30
|
+
- Run state lives under ~/.pi/agent/tmp/pi-actors/runs; inspect status/tail/messages/mailbox/files/artifacts intentionally and avoid busy-polling.
|
|
31
|
+
- Register_tool makes persistent tools from command templates, recipe names/paths, or co-located recipes; args may be typed or placeholder-derived.
|
|
32
|
+
- Foreground tools/templates fit short work; async recipes/runs fit subagents, services, fanout, media, and long pipelines.
|
|
33
|
+
- Long fanout = parent async recipe wrapping template(parallel:true) and imports; packaged fanout recipes bubble branch completion messages.
|
|
34
|
+
- For deeper pi-actors guidance, inspect installed extension sources/docs/recipes; README and docs are not automatically in context.`;
|
|
42
35
|
|
|
43
36
|
export const REGISTER_TOOL_PARAM_DESCRIPTIONS = {
|
|
44
37
|
name: "Tool name in snake_case (e.g., 'transcribe')",
|
|
@@ -53,7 +46,7 @@ export const REGISTER_TOOL_PARAM_DESCRIPTIONS = {
|
|
|
53
46
|
templateArray:
|
|
54
47
|
"Sequential command-template composition array. Leaves may be strings or objects with template/defaults/timeout/retry/failure/recover.",
|
|
55
48
|
templateNull: "Delete the tool when template is null.",
|
|
56
|
-
args: "Optional comma-separated placeholder declarations. Usually omit because args are derived from template placeholders. Interactive shorthand defaults are accepted and normalized. Example: file,lang,
|
|
49
|
+
args: "Optional comma-separated placeholder declarations. Usually omit because args are derived from template placeholders. Interactive shorthand defaults are accepted and normalized. Example: file,lang,mode=fast",
|
|
57
50
|
update: "Set to true to overwrite an existing tool registration.",
|
|
58
51
|
values:
|
|
59
52
|
"Optional default runtime placeholder values for a co-located template recipe.",
|