@llblab/pi-actors 0.12.10 → 0.12.13
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/BACKLOG.md +1 -1
- package/CHANGELOG.md +14 -0
- package/lib/execution.ts +15 -1
- package/lib/tools.ts +3 -0
- package/package.json +1 -1
- package/scripts/async-runner.mjs +23 -5
- package/scripts/recipe-utils.mjs +10 -2
package/BACKLOG.md
CHANGED
|
@@ -6,7 +6,7 @@ Continue progressive component/pipeline expansion in small validated slices; rea
|
|
|
6
6
|
|
|
7
7
|
- Plan organic universal communication primitives.
|
|
8
8
|
- Priority: High.
|
|
9
|
-
- Status: The actor-like model is empirically useful: async runs can emit follow-up messages upward, coordinators can send run-local commands downward, multiple parallel runs can progress independently, and recipes no longer need sleep-poll coordination. The design is captured in `docs/actor-messages.md`: `spawn`, `message`, and `inspect` as concentrated verbs; addressed actors; one symmetric message envelope; mailbox `accepts`/`emits`; and adapter mappings from runtime async actions. Initial implementation landed pure actor address/message normalization plus public `spawn`, `message`, and `inspect` tools for `run:<id>` actors; `spawn` accepts state/artifact metadata, `message` routes `run:<id>` → `coordinator` and `run:<id>` → `session:<id>` envelopes into the runtime attention path, `message` can invoke `tool:<name>` actors, `inspect target=tool:<name>` exposes registered tool actor contracts, `inspect target=coordinator` exposes current-session run inventory, all packaged async recipes declare mailbox metadata, `inspect view=mailbox` exposes recipe mailbox contracts from run metadata, recipe-authored messages now use envelope-aligned `type` fields with deterministic validated wrapping available through `utility-actor-message`,
|
|
9
|
+
- Status: The actor-like model is empirically useful: async runs can emit follow-up messages upward, coordinators can send run-local commands downward, multiple parallel runs can progress independently, and recipes no longer need sleep-poll coordination. The design is captured in `docs/actor-messages.md`: `spawn`, `message`, and `inspect` as concentrated verbs; addressed actors; one symmetric message envelope; mailbox `accepts`/`emits`; and adapter mappings from runtime async actions. Initial implementation landed pure actor address/message normalization plus public `spawn`, `message`, and `inspect` tools for `run:<id>` actors; `spawn` accepts state/artifact metadata, `message` routes `run:<id>` → `coordinator` and `run:<id>` → `session:<id>` envelopes into the runtime attention path, `message` can invoke `tool:<name>` actors, `inspect target=tool:<name>` exposes registered tool actor contracts, `inspect target=coordinator` exposes current-session run inventory, all packaged async recipes declare mailbox metadata, `inspect view=mailbox` exposes recipe mailbox contracts from run metadata, recipe-authored messages now use envelope-aligned `type` fields with deterministic validated wrapping available through `utility-actor-message`, run termination now accepts actor-native `control.stop`/`control.cancel`/`control.kill` aliases while preserving legacy `runtime.*` compatibility, async command events preserve full argv details while keeping coordinator summaries bounded, and async-run operations recommendations now emit structured `message`/`inspect` objects instead of shell-like suggestion strings. Remaining work is to absorb remaining runtime async action surfaces into the actor vocabulary instead of preserving a parallel public API.
|
|
10
10
|
- Scope: Design and implement a small semantic layer around addressed messages and actors while preserving low-level primitives as adapters where useful. Candidate top-level concepts are `spawn` for creating an actor/run from a recipe or template, `message` for sending typed messages to any address, and `inspect` for intentional observation/debugging. Candidate addresses include `run:<id>`, `branch:<run>/<branch>`, `coordinator`, `session:<id>`, `tool:<name>`, and future chat/session endpoints. Candidate message fields include `to`, `from`, `type`, `summary`, `body`, `reply_to`, `correlation_id`, and `metadata`.
|
|
11
11
|
- Contract direction: Unify “send down” and “messages up” as one message model. `to: run:<id>` routes to a run mailbox, `to: coordinator` routes to the coordinator attention path, and branch/tool/session addresses can be layered over the same semantic envelope. Recipes should declare mailbox capability (`accepts`, `emits`) without exposing FIFO/outbox mechanics or delivery policy as their public interface.
|
|
12
12
|
- Design gates: Breaking changes are allowed in this phase, so compress concepts instead of preserving accidental surfaces. Consolidate duplicated lifecycle/message/event APIs into a concentrated protocol with the fewest durable nouns and verbs that still explain the system. Duplex communication should be symmetric where the domain is symmetric: the same message envelope should represent run→coordinator, coordinator→run, run→run, branch→parent, and parent→branch traffic, with routing/transport hidden below it. Keep command templates as the portable synchronous execution graph; keep recipe files as semantic definitions; avoid leaking transports into public args; make polling an explicit diagnostic operation, not an example path; replace runtime action names with the actor API rather than preserving parallel concepts.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.13: Structured Run Operations Recommendations
|
|
4
|
+
|
|
5
|
+
- `[Recipe Utilities]` Changed `utility-run-ops-snapshot` recommendations from shell-like suggestion strings to structured `message` and `inspect` call objects. Impact: async-run operations reports now preserve the actor API shape directly and avoid reintroducing command-string parsing into coordinator handoffs.
|
|
6
|
+
|
|
7
|
+
## 0.12.12: Async Command Summary Hygiene
|
|
8
|
+
|
|
9
|
+
- `[Observability]` Kept async `command.done` summaries bounded while preserving full argv-shaped command details in event payloads. Impact: long prompted fanouts keep diagnostic fidelity without flooding coordinator follow-ups with huge command lines.
|
|
10
|
+
|
|
11
|
+
## 0.12.11: Recipe Import Diagnostics Hotfix
|
|
12
|
+
|
|
13
|
+
- `[Template Recipes]` Added regression coverage proving imported recipe nodes execute correctly under a repeated parallel parent (`imports` + `repeat` + object `template`). Impact: the suspected composition blocker is now guarded as supported behavior instead of relying on manual smoke interpretation.
|
|
14
|
+
- `[Observability]` Expanded command details in foreground execution results and async `command.start`/`command.done` events from executable-only labels to full argv-shaped launch strings. Impact: failed fanout branches no longer appear as misleading `pi && pi && ...` summaries when the real command was `pi -p --model ...` with a long prompt.
|
|
15
|
+
- `[Spawn]` Allowed the public `spawn` schema to accept inline object command-template configs, not only strings and arrays. Impact: agents can launch object-form templates with `parallel`, `repeat`, `failure`, and nested `template` directly through `spawn` as documented.
|
|
16
|
+
|
|
3
17
|
## 0.12.10: Actor Ownership and Recipe Operations
|
|
4
18
|
|
|
5
19
|
- `[Actor Messages]` Added actor-native `control.stop`, `control.cancel`, and `control.kill` handling for run termination while retaining `runtime.cancel` and `runtime.kill` as compatibility aliases. Impact: public examples can use the same control-message vocabulary declared by recipe mailboxes instead of preserving runtime action names.
|
package/lib/execution.ts
CHANGED
|
@@ -98,6 +98,20 @@ function createTemplateConfig(
|
|
|
98
98
|
return { args: cfg.args, defaults: cfg.defaults, template: cfg.template };
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
function quoteCommandDetailPart(value: string): string {
|
|
102
|
+
if (value === "") return "''";
|
|
103
|
+
if (/^[A-Za-z0-9_/:=.,@%+\-]+$/.test(value)) return value;
|
|
104
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function formatInvocationDetail(
|
|
108
|
+
invocation: CommandTemplates.CommandTemplateInvocation,
|
|
109
|
+
): string {
|
|
110
|
+
return [invocation.command, ...invocation.args]
|
|
111
|
+
.map(quoteCommandDetailPart)
|
|
112
|
+
.join(" ");
|
|
113
|
+
}
|
|
114
|
+
|
|
101
115
|
function formatCommandDetail(commands: string[]): string {
|
|
102
116
|
return commands.length === 1 ? commands[0] : commands.join(" && ");
|
|
103
117
|
}
|
|
@@ -494,7 +508,7 @@ async function executeTemplateConfig(
|
|
|
494
508
|
});
|
|
495
509
|
return {
|
|
496
510
|
branches: [],
|
|
497
|
-
commands: [invocation
|
|
511
|
+
commands: [formatInvocationDetail(invocation)],
|
|
498
512
|
failures: [],
|
|
499
513
|
result,
|
|
500
514
|
};
|
package/lib/tools.ts
CHANGED
|
@@ -344,6 +344,9 @@ export function createSpawnToolDefinition<
|
|
|
344
344
|
template: unionSchema([
|
|
345
345
|
stringSchema("Inline command template string"),
|
|
346
346
|
arraySchema("Inline command-template sequence or parallel tree"),
|
|
347
|
+
looseObjectSchema(
|
|
348
|
+
"Inline command-template object with flags such as parallel, repeat, retry, failure, and nested template.",
|
|
349
|
+
),
|
|
347
350
|
]),
|
|
348
351
|
values: looseObjectSchema("Runtime placeholder values passed to the actor."),
|
|
349
352
|
verbose: booleanSchema("Return full JSON instead of compact text."),
|
package/package.json
CHANGED
package/scripts/async-runner.mjs
CHANGED
|
@@ -43,6 +43,19 @@ function event(name, data = {}) {
|
|
|
43
43
|
`${JSON.stringify({ event: name, ts: new Date().toISOString(), ...data })}\n`,
|
|
44
44
|
);
|
|
45
45
|
}
|
|
46
|
+
function quoteCommandDetailPart(value) {
|
|
47
|
+
if (value === "") return "''";
|
|
48
|
+
if (/^[A-Za-z0-9_/:=.,@%+\-]+$/.test(value)) return value;
|
|
49
|
+
return `'${String(value).replaceAll("'", "'\\''")}'`;
|
|
50
|
+
}
|
|
51
|
+
function formatCommandDetail(command, args) {
|
|
52
|
+
return [command, ...args].map(quoteCommandDetailPart).join(" ");
|
|
53
|
+
}
|
|
54
|
+
function summarizeCommandDetail(commandDetail) {
|
|
55
|
+
return commandDetail.length > 160
|
|
56
|
+
? `${commandDetail.slice(0, 157)}...`
|
|
57
|
+
: commandDetail;
|
|
58
|
+
}
|
|
46
59
|
function getCommandDoneDelivery(result) {
|
|
47
60
|
return result.code !== 0 || activeSubagents > 0 ? "followup" : "log";
|
|
48
61
|
}
|
|
@@ -76,30 +89,35 @@ function progressRunning() {
|
|
|
76
89
|
});
|
|
77
90
|
}
|
|
78
91
|
async function observedExec(command, args, options) {
|
|
92
|
+
const commandDetail = formatCommandDetail(command, args);
|
|
79
93
|
activeSubagents += 1;
|
|
80
|
-
event("command.start", { activeSubagents, command });
|
|
94
|
+
event("command.start", { activeSubagents, command: commandDetail });
|
|
81
95
|
progressRunning();
|
|
82
96
|
const result = await execCommandTemplate(command, args, options);
|
|
83
97
|
activeSubagents = Math.max(0, activeSubagents - 1);
|
|
84
98
|
completedSubagents += 1;
|
|
85
99
|
if (result.code !== 0) {
|
|
86
|
-
subagentFailures.push({
|
|
100
|
+
subagentFailures.push({
|
|
101
|
+
code: result.code,
|
|
102
|
+
command: commandDetail,
|
|
103
|
+
killed: result.killed,
|
|
104
|
+
});
|
|
87
105
|
}
|
|
88
106
|
event("command.done", {
|
|
89
107
|
activeSubagents,
|
|
90
108
|
code: result.code,
|
|
91
|
-
command,
|
|
109
|
+
command: commandDetail,
|
|
92
110
|
killed: result.killed,
|
|
93
111
|
});
|
|
94
112
|
outbox(
|
|
95
113
|
"command.done",
|
|
96
|
-
`Command ${
|
|
114
|
+
`Command ${summarizeCommandDetail(commandDetail)} completed with code ${result.code}`,
|
|
97
115
|
{
|
|
98
116
|
activeSubagents,
|
|
99
117
|
...(meta.artifacts ? { artifacts: meta.artifacts } : {}),
|
|
100
118
|
run_files: [stdoutPath, stderrPath, resultPath, eventsPath, outboxPath],
|
|
101
119
|
code: result.code,
|
|
102
|
-
command,
|
|
120
|
+
command: commandDetail,
|
|
103
121
|
killed: result.killed,
|
|
104
122
|
},
|
|
105
123
|
getCommandDoneDelivery(result),
|
package/scripts/recipe-utils.mjs
CHANGED
|
@@ -135,10 +135,18 @@ function runOpsSnapshot(rootValue, eventFileValue, linesValue = "80", staleMinut
|
|
|
135
135
|
const updatedMs = Date.parse(run.updated || "");
|
|
136
136
|
const stale = Number.isFinite(updatedMs) && Number.isFinite(staleMs) && now - updatedMs > staleMs;
|
|
137
137
|
if (run.status === "running" && stale) {
|
|
138
|
-
return [{
|
|
138
|
+
return [{
|
|
139
|
+
run: run.run,
|
|
140
|
+
reason: "running-stale",
|
|
141
|
+
suggested_message: { to: `run:${run.run}`, type: "control.stop", body: "stop" },
|
|
142
|
+
}];
|
|
139
143
|
}
|
|
140
144
|
if (["failed", "exited", "killed"].includes(run.status)) {
|
|
141
|
-
return [{
|
|
145
|
+
return [{
|
|
146
|
+
run: run.run,
|
|
147
|
+
reason: `terminal-${run.status}`,
|
|
148
|
+
suggested_inspect: { target: `run:${run.run}`, view: "tail" },
|
|
149
|
+
}];
|
|
142
150
|
}
|
|
143
151
|
return [];
|
|
144
152
|
});
|