@llblab/pi-actors 0.12.7 → 0.12.9
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 +1 -1
- package/CHANGELOG.md +11 -0
- package/README.md +4 -3
- package/docs/actor-messages.md +1 -1
- package/docs/async-runs.md +3 -2
- package/docs/tool-registry.md +14 -0
- package/lib/async-runs.ts +12 -1
- package/lib/observability.ts +13 -1
- package/lib/output.ts +5 -3
- package/lib/temp.ts +2 -0
- package/lib/tools.ts +1 -0
- package/package.json +1 -1
- package/scripts/async-runner.mjs +3 -3
package/AGENTS.md
CHANGED
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
- `Executable script recipes`: Recipe templates may point directly at executable helper scripts, including JavaScript `.mjs` files with shebangs; do not prefix such recipes with `node` unless the script is intentionally not executable | Trigger: Adding or editing script-backed recipes and docs | Action: Keep the script executable bit, call `{repo}/scripts/name.mjs ...` directly, and keep the standard library on one maintained wrapper per capability unless a second wrapper has a concrete platform reason
|
|
44
44
|
- `Template migration boundary`: Tool definitions use `template`, not `script` | Trigger: Loading or editing persisted config | Action: Reject `script` entries explicitly and do not silently rewrite user config outside the repo
|
|
45
45
|
- `Reserved tool names`: Built-in or core tool names must not be shadowed | Trigger: Adding or renaming registration logic | Action: Keep conflict checks before persistence and runtime registration
|
|
46
|
-
- `Async run observability`: Ambient triangles count active async work units: each running async run contributes at least one triangle, and its reported active parallel command/subagent branches contribute the visible branch count when greater than one. Event-driven terminal/outbox watchers should initiate follow-up for unhandled terminal completion/failure states,
|
|
46
|
+
- `Async run observability`: Ambient triangles count active async work units: each running async run contributes at least one triangle, and its reported active parallel command/subagent branches contribute the visible branch count when greater than one. Event-driven terminal/outbox watchers should initiate follow-up for unhandled terminal completion/failure states, failed or in-flight `command.done` branch completions, and coordinator-bound script-authored messages with bounded body previews; actor `message` is the explicit coordinator-to-run command channel paired with these upward events. Do not restore busy-polling loops, sleep-then-status smoke examples, duplicate follow-ups for final successful leaf commands, or duplicate follow-ups for `cancel`, `kill`, or control-stop actions already handled by synchronous tool results. | Trigger: Changing async run UI, notifications, actor-message routing, or smoke-test interpretation | Action: Preserve branch-aware triangles from `progress.activeSubagents`, runtime-inferred branch bubbling for packaged fanout completion, terminal notifications as event-driven behavior, and docs/examples that teach reactive run→coordinator→message loops before sleep-polling patterns.
|
|
47
47
|
- `Communication direction`: The design target is an organic universal message layer across sync tasks, async runs, branches, tools, and coordinators. Breaking changes are allowed to compress concepts, remove accidental duplication, and make duplex communication symmetric where the domain is symmetric. | Trigger: Designing APIs or recipes that communicate | Action: Prefer a concentrated actor/message protocol (`spawn`, `message`, `inspect`, addressed endpoints, typed message envelopes, mailbox accepts/emits) over exposing FIFO/outbox/status mechanics directly; use one envelope for upward, downward, lateral, parent/branch, and branch/parent messages; absorb runtime async primitives into actor API instead of preserving parallel public concepts.
|
|
48
48
|
- `Output discipline`: Tool stdout is returned with bounded context impact | Trigger: Changing execution or formatting | Action: Keep tail truncation, full-output temp files, and failure formatting intact
|
|
49
49
|
- `Extension temp directory`: Temporary runtime files belong under `~/.pi/agent/tmp/pi-actors`; session start prepares the directory and prunes stale entries | Trigger: Adding temp files, run state, logs, or artifacts | Action: Do not use system tmp for extension-owned state unless the operator explicitly overrides a path
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.12.9: Actor Runtime Hotfix
|
|
6
|
+
|
|
7
|
+
- `[Async Runs]` Protected the `runs` state root from session-start temp pruning, tightened live-run status around owned runner processes, and kept non-Linux FIFO control usable without `/proc`-only checks. Impact: long-lived actors are less likely to disappear or be misclassified during startup and stale PID reuse is reduced on Linux.
|
|
8
|
+
- `[Actor Messages]` Preserved coordinator-bound actor message `body` and `metadata` through outbox parsing and follow-up formatting, with bounded body previews. Impact: checkpoint and decision messages reach the coordinator with the useful payload instead of only the summary line.
|
|
9
|
+
- `[Observability]` Reduced generic `command.done` follow-up noise by keeping successful final leaf completions diagnostic while still bubbling failures and in-flight parallel branch completions. Impact: long sequential pipelines no longer flood the launching coordinator with low-value leaf-completion messages.
|
|
10
|
+
- `[Output]` Moved truncated full-output files under `~/.pi/agent/tmp/pi-actors/outputs`. Impact: oversized tool output now follows the extension temp-directory contract instead of using system temp.
|
|
11
|
+
|
|
12
|
+
## 0.12.8: Usage Hint Documentation
|
|
13
|
+
|
|
14
|
+
- `[Docs]` Documented runtime actor-tool argument usage hints in README and tool-registry docs, and covered missing template-value hints separately from typed value errors. Impact: users and agents can discover the self-correction behavior without reading tests.
|
|
15
|
+
|
|
5
16
|
## 0.12.7: Tool Argument Usage Hints
|
|
6
17
|
|
|
7
18
|
- `[Tools]` Added compact usage hints to runtime actor-tool argument errors when typed normalization or placeholder resolution fails. Impact: if an agent supplies a wrong enum/type value or misses a required template value after schema validation, the error now shows the expected call shape with required and optional fields.
|
package/README.md
CHANGED
|
@@ -361,6 +361,7 @@ See [`docs/recipe-library.md`](./docs/recipe-library.md) for install notes and r
|
|
|
361
361
|
- Tool args are derived from placeholders when `args` is omitted.
|
|
362
362
|
- Typed arg declarations are progressive: `file:path`, `request_timeout:int=60000`, `speed:number=1.5`, `dry_run:bool=true`, `prompts:array`, and `mode:enum(check,fix)=check` can live in `args` or inline placeholders such as `{request_timeout:int=60000}`. They generate narrower tool schemas and runtime validation while existing untyped `args` and placeholders keep working.
|
|
363
363
|
- `{arg=default}` inline defaults resolve after runtime values and stored `defaults`; `{arg??fallback}` handles empty/null fallback values; `{flag?--flag:}` ternaries map small truthy/falsy values to strings such as optional CLI flags.
|
|
364
|
+
- Runtime actor-tool argument errors include a compact usage hint when typed normalization or template value resolution fails, including example call shape plus required and optional fields.
|
|
364
365
|
- `template: [...]` sequences execute left to right; each successful step passes stdout to the next step on stdin.
|
|
365
366
|
- Object nodes may set `parallel: true`; children receive the same stdin and joined stdout flows to the next sequence step.
|
|
366
367
|
- Parallel nodes use soft-quorum semantics: failed branches are reported as degraded coverage unless failure propagation escalates to the root.
|
|
@@ -379,11 +380,11 @@ See [`docs/recipe-library.md`](./docs/recipe-library.md) for install notes and r
|
|
|
379
380
|
- `spawn`, `message`, and `inspect` are the public async coordination vocabulary. Low-level async actions map to this actor API: start belongs to `spawn`; send/control belongs to `message`; status/tail/events/list belong to `inspect`; stop/kill are runtime control messages with synchronous results.
|
|
380
381
|
- Actor management returns compact text by default; pass `verbose: true` to `inspect` when full JSON state is needed.
|
|
381
382
|
- Detached runs inject `{run_id}` and `{state_dir}` into template values for run-local artifacts or recipe-specific control endpoints.
|
|
382
|
-
- Runtime actor messages are stored in `<state_dir>/outbox.jsonl`; coordinator attention is inferred by the runtime, not exposed as recipe or message-envelope input.
|
|
383
|
-
- Native Windows should use WSL or a recipe-specific transport for FIFO-controlled recipes.
|
|
383
|
+
- Runtime actor messages are stored in `<state_dir>/outbox.jsonl`; coordinator attention is inferred by the runtime, not exposed as recipe or message-envelope input. Follow-ups preserve bounded body previews and metadata for decision messages.
|
|
384
|
+
- Native Windows should use WSL or a recipe-specific transport for FIFO-controlled recipes; Linux uses stricter `/proc` runner ownership checks for stale PID protection.
|
|
384
385
|
- Registered tools may set `template` to a recipe JSON path/name; calling them follows that recipe's `async` mode.
|
|
385
386
|
- File-backed recipes may declare `imports` and embed imported recipes with `{ "name": "alias" }` nodes, or read `{alias.defaults.key}`, `{alias.defaults.key=fallback}`, and `{alias.values.key?yes:no}` references before command-template execution.
|
|
386
|
-
- Interactive sessions show ambient async activity as stable `▷` triangles aggregated across runs started by the current agent session. Each running async run contributes at least one triangle; parallel active branches can contribute more. One `▶` wave moves over the active set; terminal `done`/`failed`/unhandled `killed`/`exited` messages are delivered as compact follow-up context only to the launching coordinator agent, while intentional `cancel`, `kill`, and `stop` actions stay silent because the action already reports synchronously.
|
|
387
|
+
- Interactive sessions show ambient async activity as stable `▷` triangles aggregated across runs started by the current agent session. Each running async run contributes at least one triangle; parallel active branches can contribute more. One `▶` wave moves over the active set; terminal `done`/`failed`/unhandled `killed`/`exited` messages are delivered as compact follow-up context only to the launching coordinator agent, while intentional `cancel`, `kill`, and `stop` actions stay silent because the action already reports synchronously. Failed commands and in-flight parallel branch completions can bubble through `command.done`; successful final leaf completions remain diagnostic to avoid sequential pipeline noise.
|
|
387
388
|
- Use `{file}` as the canonical local file path arg.
|
|
388
389
|
- Stored `script` entries are rejected with migration guidance.
|
|
389
390
|
|
package/docs/actor-messages.md
CHANGED
|
@@ -98,7 +98,7 @@ Recipes can declare their conversational surface:
|
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
`mailbox.accepts` is a contract for coordinator-to-actor messages. `mailbox.emits` is a contract for actor-to-coordinator or actor-to-actor messages. Packaged interactive and message-producing recipes declare mailbox metadata so coordinators can discover semantic message types without reading FIFO details. Message-producing recipes produce actor-message-envelope-shaped records with `to`, `from`, `type`, `summary`, `body`, optional `correlation_id`/`reply_to`, and optional `metadata` fields. Deterministic pipelines should prefer `utility-actor-message` for this wrapping so message shape is validated and guaranteed instead of delegated to a prompt; its recipe args intentionally mirror the envelope field names.
|
|
101
|
+
`mailbox.accepts` is a contract for coordinator-to-actor messages. `mailbox.emits` is a contract for actor-to-coordinator or actor-to-actor messages. Packaged interactive and message-producing recipes declare mailbox metadata so coordinators can discover semantic message types without reading FIFO details. Message-producing recipes produce actor-message-envelope-shaped records with `to`, `from`, `type`, `summary`, `body`, optional `correlation_id`/`reply_to`, and optional `metadata` fields. Coordinator follow-ups preserve bounded body previews and metadata so checkpoints do not lose their actionable payload. Deterministic pipelines should prefer `utility-actor-message` for this wrapping so message shape is validated and guaranteed instead of delegated to a prompt; its recipe args intentionally mirror the envelope field names.
|
|
102
102
|
|
|
103
103
|
## Spawn
|
|
104
104
|
|
package/docs/async-runs.md
CHANGED
|
@@ -81,7 +81,7 @@ Use ordinary files under the extension temp directory so status tools stay simpl
|
|
|
81
81
|
- `run.json`: pid, optional source metadata (`tool`, `recipe`, `recipe_file`), command-template config, cwd, coordinator owner id, values, named `artifacts`, mailbox metadata, created time, and state dir.
|
|
82
82
|
- `progress.json`: phase, active command count, completed count, failures, and updated time.
|
|
83
83
|
- `events.jsonl`: append-only lifecycle events.
|
|
84
|
-
- `outbox.jsonl`: optional script-authored JSONL events for coordinator inspection, notifications, or follow-up context.
|
|
84
|
+
- `outbox.jsonl`: optional script-authored JSONL actor-message events for coordinator inspection, notifications, or follow-up context. Coordinator follow-ups preserve bounded `body` previews plus message metadata for decision points.
|
|
85
85
|
- `stdout.log` and `stderr.log`: detached process output.
|
|
86
86
|
- `result.json`: final code, killed flag, output selector, and optional full-output path.
|
|
87
87
|
|
|
@@ -172,7 +172,7 @@ Native Windows does not support this Unix FIFO contract. Use WSL/Linux/macOS for
|
|
|
172
172
|
|
|
173
173
|
## Coordinator Notifications
|
|
174
174
|
|
|
175
|
-
The launching coordinator should not busy-poll long-running async runs. The extension watches run state directories and delivers terminal `done`/`failed`/unhandled `killed`/`exited` transitions plus script-authored `notify`/`followup` actor messages back to the owning session. This gives the top-level async task a completion signal on the happy path while still letting recipe-local messages bubble up when scripts need finer-grained notifications. Terminal follow-ups include recipe-level named `artifacts` when declared. The generic runner also emits compact `command.done` actor messages for completed leaf commands; recipe authors declare that capability in `mailbox.emits` rather than configuring a separate delivery policy.
|
|
175
|
+
The launching coordinator should not busy-poll long-running async runs. The extension watches run state directories and delivers terminal `done`/`failed`/unhandled `killed`/`exited` transitions plus script-authored `notify`/`followup` actor messages back to the owning session. This gives the top-level async task a completion signal on the happy path while still letting recipe-local messages bubble up when scripts need finer-grained notifications. Terminal follow-ups include recipe-level named `artifacts` when declared. The generic runner also emits compact `command.done` actor messages for completed leaf commands; recipe authors declare that capability in `mailbox.emits` rather than configuring a separate delivery policy. Failures and in-flight parallel branch completions can bubble as follow-ups, while successful final leaf completions stay diagnostic to avoid flooding long sequential pipelines. Branch-level `command.done` follow-ups omit artifact manifests because the top-level terminal follow-up carries them once. Intentional `runtime.cancel`, `runtime.kill`, and control messages such as `stop` stay out of follow-up context because the initiating message already returns synchronously. If a follow-up asks for direction, answer with `message` rather than starting a polling loop. Use explicit `inspect` only when a delivered follow-up requests inspection, a real decision depends on state, or a suspected stuck run needs diagnosis — never merely because a timeout elapsed.
|
|
176
176
|
|
|
177
177
|
Ambient status indicators may refresh while work is active, but coordinator attention is event-driven from state-file changes rather than a coordinator agent loop. This lets the coordinator continue other work after `spawn`; the run signals back through `events.jsonl`, `result.json`, and `outbox.jsonl`. The ambient triangle count represents active async work units: each running async run contributes at least one triangle, and a run with multiple active parallel command/subagent branches contributes the reported active branch count. If a coordinator starts one parent run with four active parallel branches, four triangles are shown; if the same coordinator starts five independent single-branch runs, five triangles are shown.
|
|
178
178
|
|
|
@@ -227,6 +227,7 @@ Rules:
|
|
|
227
227
|
- Prune stale entries on session start.
|
|
228
228
|
- Default stale age is 24 hours unless the extension has a stronger reason.
|
|
229
229
|
- Cleanup must be fail-open: cleanup races should not prevent extension startup.
|
|
230
|
+
- The `runs` state root is preserved by startup cleanup; run lifecycle cleanup must be explicit and run-aware.
|
|
230
231
|
- State that must survive restarts belongs in the agent root, not in `tmp`.
|
|
231
232
|
|
|
232
233
|
## Ambient Observability
|
package/docs/tool-registry.md
CHANGED
|
@@ -149,6 +149,20 @@ Supported compact types are `string` (implicit), `path`, `int`, `number`, `bool`
|
|
|
149
149
|
|
|
150
150
|
Defaults are applied before substitution, with resolution order runtime values → stored `defaults` → inline default → error. Missing required values are rejected before or during execution. Typed runtime values are normalized before substitution: `int` and `number` values become numeric strings, booleans become `true`/`false`, and enums must match one of the declared values.
|
|
151
151
|
|
|
152
|
+
When typed normalization or template value resolution fails at runtime, the tool error includes a compact usage hint:
|
|
153
|
+
|
|
154
|
+
```text
|
|
155
|
+
Invalid arguments for tool "check_tool": Argument mode must be one of: check, fix.
|
|
156
|
+
|
|
157
|
+
Expected call shape for check_tool:
|
|
158
|
+
check_tool({
|
|
159
|
+
"file": "<file>",
|
|
160
|
+
"mode": "check"
|
|
161
|
+
})
|
|
162
|
+
Required: file
|
|
163
|
+
Optional: mode
|
|
164
|
+
```
|
|
165
|
+
|
|
152
166
|
Template recipe tools derive public arguments from the referenced or co-located command template when the recipe is available locally. Explicit `args` is still available when the public tool surface should be narrower or defaulted differently, or when a file-backed recipe is not available during registration. Runtime values are passed as `values`; async recipe tools also accept optional `run_id` to override the generated run id.
|
|
153
167
|
|
|
154
168
|
## File Argument Naming
|
package/lib/async-runs.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
writeSync,
|
|
21
21
|
} from "node:fs";
|
|
22
22
|
import { basename, extname, join, resolve } from "node:path";
|
|
23
|
+
import { platform } from "node:os";
|
|
23
24
|
|
|
24
25
|
import type {
|
|
25
26
|
CommandTemplateFailureScope,
|
|
@@ -76,6 +77,7 @@ export interface RunOutboxEvent {
|
|
|
76
77
|
from?: string;
|
|
77
78
|
id: string;
|
|
78
79
|
level: RunOutboxLevel;
|
|
80
|
+
metadata?: Record<string, unknown>;
|
|
79
81
|
reply_to?: string;
|
|
80
82
|
run: string;
|
|
81
83
|
state_dir: string;
|
|
@@ -225,6 +227,7 @@ function isAlive(pid: number): boolean {
|
|
|
225
227
|
}
|
|
226
228
|
|
|
227
229
|
function pidMatchesRun(pid: number, cwd: string, stateDir: string): boolean {
|
|
230
|
+
if (platform() !== "linux" || !existsSync(`/proc/${pid}`)) return isAlive(pid);
|
|
228
231
|
try {
|
|
229
232
|
const procCwd = readlinkSync(`/proc/${pid}/cwd`);
|
|
230
233
|
const cmdline = readFileSync(`/proc/${pid}/cmdline`, "utf8");
|
|
@@ -405,6 +408,7 @@ function normalizeRunOutboxEvent(
|
|
|
405
408
|
...(typeof record.correlation_id === "string" ? { correlation_id: record.correlation_id } : {}),
|
|
406
409
|
...(record.data !== undefined ? { data: record.data } : {}),
|
|
407
410
|
delivery: normalizeRunOutboxDelivery(record.delivery),
|
|
411
|
+
...(record.metadata && typeof record.metadata === "object" && !Array.isArray(record.metadata) ? { metadata: record.metadata as Record<string, unknown> } : {}),
|
|
408
412
|
event,
|
|
409
413
|
...(typeof record.from === "string" ? { from: record.from } : {}),
|
|
410
414
|
id,
|
|
@@ -442,11 +446,16 @@ export function getRunStatus(runOrDir: string): Record<string, unknown> {
|
|
|
442
446
|
if (!meta) throw new Error(`Run not found: ${runOrDir}`);
|
|
443
447
|
const result = readJson(join(stateDir, "result.json"));
|
|
444
448
|
const pid = Number(meta.pid || 0);
|
|
449
|
+
const aliveOwnedRunner = Boolean(
|
|
450
|
+
pid &&
|
|
451
|
+
isAlive(pid) &&
|
|
452
|
+
(!Array.isArray(meta.argv) || pidMatchesRun(pid, String(meta.cwd ?? ""), stateDir)),
|
|
453
|
+
);
|
|
445
454
|
const status: AsyncRunStatus = result
|
|
446
455
|
? Number(result.code ?? 0) === 0
|
|
447
456
|
? "done"
|
|
448
457
|
: "failed"
|
|
449
|
-
:
|
|
458
|
+
: aliveOwnedRunner
|
|
450
459
|
? "running"
|
|
451
460
|
: (getInterruptedRunStatus(stateDir) ?? "exited");
|
|
452
461
|
const terminalHandled = readJson(join(stateDir, "terminal-handled.json"));
|
|
@@ -530,6 +539,7 @@ export function appendRunOutboxEvent(
|
|
|
530
539
|
event?: string;
|
|
531
540
|
from?: string;
|
|
532
541
|
level?: string;
|
|
542
|
+
metadata?: Record<string, unknown>;
|
|
533
543
|
reply_to?: string;
|
|
534
544
|
summary?: string;
|
|
535
545
|
to?: string;
|
|
@@ -549,6 +559,7 @@ export function appendRunOutboxEvent(
|
|
|
549
559
|
event: type,
|
|
550
560
|
from: event.from || `run:${run}`,
|
|
551
561
|
level: normalizeRunOutboxLevel(event.level),
|
|
562
|
+
...(event.metadata ? { metadata: event.metadata } : {}),
|
|
552
563
|
...(event.reply_to ? { reply_to: event.reply_to } : {}),
|
|
553
564
|
summary: event.summary || type,
|
|
554
565
|
to,
|
package/lib/observability.ts
CHANGED
|
@@ -55,11 +55,13 @@ export interface RunTransition {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export interface RunOutboxEvent {
|
|
58
|
+
body?: unknown;
|
|
58
59
|
data?: unknown;
|
|
59
60
|
delivery: RunOutboxDelivery;
|
|
60
61
|
event: string;
|
|
61
62
|
id: string;
|
|
62
63
|
level: RunOutboxLevel;
|
|
64
|
+
metadata?: Record<string, unknown>;
|
|
63
65
|
run: string;
|
|
64
66
|
stateDir: string;
|
|
65
67
|
summary: string;
|
|
@@ -351,11 +353,13 @@ function parseOutboxLine(
|
|
|
351
353
|
? raw.id.trim()
|
|
352
354
|
: `${run.run}:${index}`;
|
|
353
355
|
return {
|
|
356
|
+
...(raw.body !== undefined ? { body: raw.body } : {}),
|
|
354
357
|
...(raw.data !== undefined ? { data: raw.data } : {}),
|
|
355
358
|
delivery: normalizeOutboxDelivery(raw.delivery),
|
|
356
359
|
event,
|
|
357
360
|
id,
|
|
358
361
|
level: normalizeOutboxLevel(raw.level),
|
|
362
|
+
...(raw.metadata && typeof raw.metadata === "object" && !Array.isArray(raw.metadata) ? { metadata: raw.metadata as Record<string, unknown> } : {}),
|
|
359
363
|
run: run.run,
|
|
360
364
|
stateDir: run.stateDir,
|
|
361
365
|
summary,
|
|
@@ -465,9 +469,17 @@ function getOutboxField(event: RunOutboxEvent, key: string): unknown {
|
|
|
465
469
|
: undefined;
|
|
466
470
|
}
|
|
467
471
|
|
|
472
|
+
function formatBodyPreview(body: unknown): string {
|
|
473
|
+
if (body === undefined) return "";
|
|
474
|
+
const rendered = typeof body === "string" ? body : JSON.stringify(body);
|
|
475
|
+
const compact = rendered.replaceAll(/\s+/g, " ").trim();
|
|
476
|
+
if (!compact) return "";
|
|
477
|
+
return `\nBody: ${compact.length > 500 ? `${compact.slice(0, 500)}…` : compact}`;
|
|
478
|
+
}
|
|
479
|
+
|
|
468
480
|
export function formatRunOutboxMessage(event: RunOutboxEvent): string {
|
|
469
481
|
if (event.event === "command.done") return `Run ${event.run}: ${event.summary}`;
|
|
470
|
-
return `Run ${event.run}: ${event.summary}${formatNamedArtifacts(getOutboxField(event, "artifacts"))}${formatRunFileList(getOutboxField(event, "run_files"))}`;
|
|
482
|
+
return `Run ${event.run}: ${event.summary}${formatBodyPreview(event.body)}${formatNamedArtifacts(getOutboxField(event, "artifacts"))}${formatRunFileList(getOutboxField(event, "run_files"))}`;
|
|
471
483
|
}
|
|
472
484
|
|
|
473
485
|
export function getRunTransitionNotificationType(
|
package/lib/output.ts
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* Owns stdout/stderr failure formatting, tail truncation, and full-output temp-file persistence
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
8
|
-
import { tmpdir } from "node:os";
|
|
7
|
+
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
|
|
9
8
|
import { join } from "node:path";
|
|
10
9
|
|
|
11
10
|
import { sanitizeFilePart } from "./identity.ts";
|
|
11
|
+
import * as Paths from "./paths.ts";
|
|
12
12
|
|
|
13
13
|
export interface FormattedOutput {
|
|
14
14
|
text: string;
|
|
@@ -25,7 +25,9 @@ export function writeFullOutput(
|
|
|
25
25
|
content: string,
|
|
26
26
|
): string | undefined {
|
|
27
27
|
try {
|
|
28
|
-
const
|
|
28
|
+
const outputRoot = join(Paths.getExtensionTmpDir(), "outputs");
|
|
29
|
+
mkdirSync(outputRoot, { recursive: true });
|
|
30
|
+
const dir = mkdtempSync(join(outputRoot, "tool-output-"));
|
|
29
31
|
const filePath = join(
|
|
30
32
|
dir,
|
|
31
33
|
`${sanitizeFilePart(toolName)}-${sanitizeFilePart(stream)}.txt`,
|
package/lib/temp.ts
CHANGED
|
@@ -13,6 +13,7 @@ export async function cleanupStaleTempEntries(
|
|
|
13
13
|
tempDir: string,
|
|
14
14
|
maxAgeMs = DEFAULT_TEMP_MAX_AGE_MS,
|
|
15
15
|
now = Date.now(),
|
|
16
|
+
preservedEntries = new Set(["runs"]),
|
|
16
17
|
): Promise<number> {
|
|
17
18
|
let entries: Array<{ name: string }>;
|
|
18
19
|
let removed = 0;
|
|
@@ -22,6 +23,7 @@ export async function cleanupStaleTempEntries(
|
|
|
22
23
|
return 0;
|
|
23
24
|
}
|
|
24
25
|
for (const entry of entries) {
|
|
26
|
+
if (preservedEntries.has(entry.name)) continue;
|
|
25
27
|
const path = join(tempDir, entry.name);
|
|
26
28
|
try {
|
|
27
29
|
const info = await stat(path);
|
package/lib/tools.ts
CHANGED
|
@@ -575,6 +575,7 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
575
575
|
correlation_id: message.correlation_id,
|
|
576
576
|
event: message.type,
|
|
577
577
|
from: message.from,
|
|
578
|
+
metadata: message.metadata,
|
|
578
579
|
reply_to: message.reply_to,
|
|
579
580
|
summary: message.summary,
|
|
580
581
|
to: message.to,
|
package/package.json
CHANGED
package/scripts/async-runner.mjs
CHANGED
|
@@ -43,8 +43,8 @@ function event(name, data = {}) {
|
|
|
43
43
|
`${JSON.stringify({ event: name, ts: new Date().toISOString(), ...data })}\n`,
|
|
44
44
|
);
|
|
45
45
|
}
|
|
46
|
-
function
|
|
47
|
-
return
|
|
46
|
+
function getCommandDoneDelivery(result) {
|
|
47
|
+
return result.code !== 0 || activeSubagents > 0 ? "followup" : "log";
|
|
48
48
|
}
|
|
49
49
|
function outbox(name, summary, data = {}, delivery = "log", level = "info") {
|
|
50
50
|
appendFileSync(
|
|
@@ -102,7 +102,7 @@ async function observedExec(command, args, options) {
|
|
|
102
102
|
command,
|
|
103
103
|
killed: result.killed,
|
|
104
104
|
},
|
|
105
|
-
|
|
105
|
+
getCommandDoneDelivery(result),
|
|
106
106
|
result.code === 0 ? "info" : "error",
|
|
107
107
|
);
|
|
108
108
|
progressRunning();
|