@llblab/pi-actors 0.29.2 → 0.30.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 +16 -1
- package/BACKLOG.md +89 -206
- package/CHANGELOG.md +9 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +71 -315
- package/dist/lib/actor-inspector-tui.d.ts +35 -0
- package/dist/lib/actor-inspector-tui.js +159 -1
- package/dist/lib/observability.d.ts +34 -0
- package/dist/lib/observability.js +103 -1
- package/dist/lib/paths.d.ts +9 -0
- package/dist/lib/paths.js +15 -0
- package/dist/lib/pi.d.ts +19 -0
- package/dist/lib/pi.js +21 -0
- package/dist/lib/runtime.d.ts +5 -0
- package/dist/lib/runtime.js +48 -0
- package/dist/lib/tools.d.ts +9 -0
- package/dist/lib/tools.js +58 -8
- package/dist/skills/actors/SKILL.md +10 -116
- package/dist/skills/swarm/SKILL.md +20 -1
- package/docs/README.md +1 -0
- package/docs/actors-deep-reference.md +66 -0
- package/index.ts +99 -379
- package/lib/actor-inspector-tui.ts +221 -1
- package/lib/observability.ts +194 -34
- package/lib/paths.ts +27 -0
- package/lib/pi.ts +47 -0
- package/lib/runtime.ts +55 -0
- package/lib/tools.ts +119 -24
- package/package.json +1 -1
- package/skills/actors/SKILL.md +10 -116
- package/skills/swarm/SKILL.md +20 -1
|
@@ -7,7 +7,165 @@ import * as path from "node:path";
|
|
|
7
7
|
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
8
8
|
import * as Limits from "./limits.js";
|
|
9
9
|
import * as Paths from "./paths.js";
|
|
10
|
-
import { readJsonFileResilient, readJsonlFileResilient } from "./state-readers.js";
|
|
10
|
+
import { readJsonFileResilient, readJsonlFileResilient, } from "./state-readers.js";
|
|
11
|
+
export const DEFAULT_ACTOR_INSPECTOR_ROWS = 12;
|
|
12
|
+
export const ACTOR_INSPECTOR_COMMAND_DESCRIPTIONS = {
|
|
13
|
+
filter: "Filter actor inspector rows: all, room, direct, broadcast, unread, branch <name>, mention <text>",
|
|
14
|
+
inspect: "Inspect actor message by visible number",
|
|
15
|
+
toggle: "Toggle actor inspector widget; optional row count",
|
|
16
|
+
};
|
|
17
|
+
export function createActorInspectorControllerState() {
|
|
18
|
+
return {
|
|
19
|
+
readKeys: new Set(),
|
|
20
|
+
roomLimitPerRun: DEFAULT_ACTOR_INSPECTOR_ROWS,
|
|
21
|
+
rows: DEFAULT_ACTOR_INSPECTOR_ROWS,
|
|
22
|
+
unreadOnly: false,
|
|
23
|
+
visible: false,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function getActorInspectorPreviewOptions(state, ownerId) {
|
|
27
|
+
return {
|
|
28
|
+
channels: state.channels,
|
|
29
|
+
currentRunOnly: true,
|
|
30
|
+
branch: state.branch,
|
|
31
|
+
mention: state.mention,
|
|
32
|
+
ownerId,
|
|
33
|
+
readKeys: state.readKeys,
|
|
34
|
+
roomLimitPerRun: state.roomLimitPerRun,
|
|
35
|
+
unreadOnly: state.unreadOnly,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function handleActorInspectorToggle(state, args) {
|
|
39
|
+
const raw = Array.isArray(args) ? args[0] : String(args ?? "");
|
|
40
|
+
if (String(raw).trim()) {
|
|
41
|
+
const rows = Number.parseInt(String(raw), 10);
|
|
42
|
+
if (!Number.isFinite(rows) || rows <= 0) {
|
|
43
|
+
return {
|
|
44
|
+
notify: "Usage: /actors-inspector-toggle [rows] where rows > 0",
|
|
45
|
+
type: "warning",
|
|
46
|
+
update: false,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
state.rows = rows;
|
|
50
|
+
state.roomLimitPerRun = rows;
|
|
51
|
+
state.selectedSequence = undefined;
|
|
52
|
+
state.visible = true;
|
|
53
|
+
return {
|
|
54
|
+
notify: `Actor inspector rows ${rows}`,
|
|
55
|
+
type: "info",
|
|
56
|
+
update: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (state.selectedSequence !== undefined) {
|
|
60
|
+
state.selectedSequence = undefined;
|
|
61
|
+
state.visible = true;
|
|
62
|
+
return { notify: "Actor inspector table", type: "info", update: true };
|
|
63
|
+
}
|
|
64
|
+
if (state.visible)
|
|
65
|
+
state.visible = false;
|
|
66
|
+
else {
|
|
67
|
+
state.rows = DEFAULT_ACTOR_INSPECTOR_ROWS;
|
|
68
|
+
state.roomLimitPerRun = DEFAULT_ACTOR_INSPECTOR_ROWS;
|
|
69
|
+
state.visible = true;
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
notify: `Actor inspector ${state.visible ? "shown" : "hidden"}`,
|
|
73
|
+
type: "info",
|
|
74
|
+
update: true,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function handleActorInspectorFilter(state, args) {
|
|
78
|
+
const parts = Array.isArray(args)
|
|
79
|
+
? args.map(String)
|
|
80
|
+
: String(args ?? "").split(/\s+/);
|
|
81
|
+
const mode = (parts[0] ?? "").trim().toLowerCase();
|
|
82
|
+
if (!mode || mode === "all" || mode === "clear") {
|
|
83
|
+
state.channels = undefined;
|
|
84
|
+
state.mention = undefined;
|
|
85
|
+
state.branch = undefined;
|
|
86
|
+
state.unreadOnly = false;
|
|
87
|
+
}
|
|
88
|
+
else if (mode === "room" || mode === "direct" || mode === "broadcast") {
|
|
89
|
+
state.channels = [mode];
|
|
90
|
+
state.mention = undefined;
|
|
91
|
+
}
|
|
92
|
+
else if (mode === "unread") {
|
|
93
|
+
state.unreadOnly = true;
|
|
94
|
+
}
|
|
95
|
+
else if (mode === "branch" || mode === "current-branch") {
|
|
96
|
+
const branch = parts.slice(1).join(" ").trim();
|
|
97
|
+
if (!branch) {
|
|
98
|
+
return {
|
|
99
|
+
notify: `Usage: /actors-inspector-filter ${mode} <branch-name>`,
|
|
100
|
+
type: "warning",
|
|
101
|
+
update: false,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
state.branch = branch;
|
|
105
|
+
}
|
|
106
|
+
else if (mode === "mention") {
|
|
107
|
+
const mention = parts.slice(1).join(" ").trim();
|
|
108
|
+
if (!mention) {
|
|
109
|
+
return {
|
|
110
|
+
notify: "Usage: /actors-inspector-filter mention <text>",
|
|
111
|
+
type: "warning",
|
|
112
|
+
update: false,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
state.channels = undefined;
|
|
116
|
+
state.mention = mention;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
return {
|
|
120
|
+
notify: "Usage: /actors-inspector-filter all|room|direct|broadcast|unread|branch <name>|mention <text>",
|
|
121
|
+
type: "warning",
|
|
122
|
+
update: false,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
state.selectedSequence = undefined;
|
|
126
|
+
state.visible = true;
|
|
127
|
+
return {
|
|
128
|
+
notify: `Actor inspector filter ${mode || "all"}`,
|
|
129
|
+
type: "info",
|
|
130
|
+
update: true,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export function handleActorInspectorInspect(state, args, previews) {
|
|
134
|
+
const raw = Array.isArray(args) ? args[0] : String(args ?? "");
|
|
135
|
+
return selectActorInspectorSequence(state, Number.parseInt(String(raw), 10), previews);
|
|
136
|
+
}
|
|
137
|
+
export function selectActorInspectorSequence(state, sequence, previews) {
|
|
138
|
+
if (!Number.isFinite(sequence) || sequence <= 0) {
|
|
139
|
+
return {
|
|
140
|
+
notify: "Usage: /actors-inspect <number>",
|
|
141
|
+
type: "warning",
|
|
142
|
+
update: false,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const preview = previews.find((item) => item.sequence === sequence);
|
|
146
|
+
if (preview)
|
|
147
|
+
state.readKeys.add(inspectorPreviewReadKey(preview));
|
|
148
|
+
state.selectedSequence = sequence;
|
|
149
|
+
state.visible = true;
|
|
150
|
+
return {
|
|
151
|
+
notify: `Actor inspect item ${sequence}`,
|
|
152
|
+
type: "info",
|
|
153
|
+
update: true,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
export function renderActorInspectorPanel(input) {
|
|
157
|
+
const previews = readActorInspectorPreviews(input.stateRoot, input.state.rows, getActorInspectorPreviewOptions(input.state, input.ownerId));
|
|
158
|
+
const rows = (input.state.selectedSequence !== undefined
|
|
159
|
+
? renderInspectorItemView(previews, input.width, input.style, {
|
|
160
|
+
sequence: input.state.selectedSequence,
|
|
161
|
+
})
|
|
162
|
+
: renderInspectorWidget(previews, input.width, input.style)) ?? [];
|
|
163
|
+
const run = previews[0]?.run;
|
|
164
|
+
const roster = input.state.selectedSequence === undefined && run
|
|
165
|
+
? renderInspectorRosterPanel(readActorInspectorRoster(input.stateRoot, run), input.width, input.style)
|
|
166
|
+
: undefined;
|
|
167
|
+
return roster ? [...roster, ...rows] : rows;
|
|
168
|
+
}
|
|
11
169
|
function asRecord(value) {
|
|
12
170
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
13
171
|
? value
|
|
@@ -35,6 +35,32 @@ export interface RunSummary {
|
|
|
35
35
|
runs: RunObservation[];
|
|
36
36
|
total: number;
|
|
37
37
|
}
|
|
38
|
+
export interface RunUiObservationState {
|
|
39
|
+
eventLines: Map<string, number>;
|
|
40
|
+
frame: number;
|
|
41
|
+
observed: Map<string, RunObservedStatus>;
|
|
42
|
+
outboxEventIds: Map<string, Set<string>>;
|
|
43
|
+
}
|
|
44
|
+
export interface RunUiSnapshot {
|
|
45
|
+
outboxEvents: RunOutboxEvent[];
|
|
46
|
+
status: string | undefined;
|
|
47
|
+
summary: RunSummary;
|
|
48
|
+
transitions: RunTransition[];
|
|
49
|
+
}
|
|
50
|
+
export interface RunUiNotificationSink {
|
|
51
|
+
notify(message: string, level: "info" | "warning" | "error"): void;
|
|
52
|
+
sendFollowUp(message: {
|
|
53
|
+
customType: string;
|
|
54
|
+
content: string;
|
|
55
|
+
display: true;
|
|
56
|
+
details: unknown;
|
|
57
|
+
}): void;
|
|
58
|
+
}
|
|
59
|
+
export declare function createRunUiObservationState(): RunUiObservationState;
|
|
60
|
+
export declare function readRunUiSnapshot(state: RunUiObservationState, ownerId: string): RunUiSnapshot;
|
|
61
|
+
export declare function pruneRunUiObservationState(state: RunUiObservationState, snapshot: Pick<RunUiSnapshot, "summary" | "transitions">): void;
|
|
62
|
+
export declare function deliverRunTransitionNotifications(transitions: RunTransition[], sink: RunUiNotificationSink): void;
|
|
63
|
+
export declare function deliverRunOutboxNotifications(events: RunOutboxEvent[], sink: RunUiNotificationSink): void;
|
|
38
64
|
export interface RunRetirementCandidate {
|
|
39
65
|
activeSubagents: number;
|
|
40
66
|
childRuns: number;
|
|
@@ -49,6 +75,14 @@ export interface RunRetirementExecution {
|
|
|
49
75
|
run: string;
|
|
50
76
|
stateDir: string;
|
|
51
77
|
}
|
|
78
|
+
export interface RunStateWatcher {
|
|
79
|
+
close(): void;
|
|
80
|
+
refresh(): void;
|
|
81
|
+
}
|
|
82
|
+
export declare function createRunStateWatcher(input: {
|
|
83
|
+
stateRoot?: string;
|
|
84
|
+
onChange: () => void;
|
|
85
|
+
}): RunStateWatcher;
|
|
52
86
|
export interface RunRetirementExecutorOptions {
|
|
53
87
|
attempted?: Set<string>;
|
|
54
88
|
cancelRun: (candidate: RunRetirementCandidate) => Record<string, unknown>;
|
|
@@ -3,11 +3,113 @@
|
|
|
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 } from "node:fs";
|
|
6
|
+
import { existsSync, readdirSync, readFileSync, 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";
|
|
10
10
|
import { readJsonlFileResilient } from "./state-readers.js";
|
|
11
|
+
export function createRunUiObservationState() {
|
|
12
|
+
return {
|
|
13
|
+
eventLines: new Map(),
|
|
14
|
+
frame: 0,
|
|
15
|
+
observed: new Map(),
|
|
16
|
+
outboxEventIds: new Map(),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function readRunUiSnapshot(state, ownerId) {
|
|
20
|
+
const summary = summarizeRuns(undefined, ownerId);
|
|
21
|
+
const status = renderRunStatus(summary, state.frame++);
|
|
22
|
+
return {
|
|
23
|
+
outboxEvents: detectRunOutboxEvents(state.eventLines, summary, state.outboxEventIds),
|
|
24
|
+
status,
|
|
25
|
+
summary,
|
|
26
|
+
transitions: detectRunTransitions(state.observed, summary),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function pruneRunUiObservationState(state, snapshot) {
|
|
30
|
+
pruneRunObservationState(state.observed, state.eventLines, snapshot.summary, snapshot.transitions.map((transition) => transition.stateDir ?? transition.run), state.outboxEventIds);
|
|
31
|
+
}
|
|
32
|
+
export function deliverRunTransitionNotifications(transitions, sink) {
|
|
33
|
+
for (const transition of transitions) {
|
|
34
|
+
if (!shouldNotifyRunTransition(transition))
|
|
35
|
+
continue;
|
|
36
|
+
const text = formatRunTransitionMessage(transition);
|
|
37
|
+
sink.notify(text, getRunTransitionNotificationType(transition));
|
|
38
|
+
if (!shouldSendRunTransitionFollowUp(transition))
|
|
39
|
+
continue;
|
|
40
|
+
sink.sendFollowUp({
|
|
41
|
+
customType: "pi-actors-run",
|
|
42
|
+
content: text,
|
|
43
|
+
display: true,
|
|
44
|
+
details: transition,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function deliverRunOutboxNotifications(events, sink) {
|
|
49
|
+
for (const event of events) {
|
|
50
|
+
if (!shouldNotifyRunOutboxEvent(event))
|
|
51
|
+
continue;
|
|
52
|
+
const text = formatRunOutboxMessage(event);
|
|
53
|
+
sink.notify(text, getRunOutboxNotificationType(event));
|
|
54
|
+
if (!shouldSendRunOutboxFollowUp(event))
|
|
55
|
+
continue;
|
|
56
|
+
sink.sendFollowUp({
|
|
57
|
+
customType: "pi-actors-run-message",
|
|
58
|
+
content: text,
|
|
59
|
+
display: true,
|
|
60
|
+
details: event,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function createRunStateWatcher(input) {
|
|
65
|
+
const stateRoot = input.stateRoot ?? Paths.getRunStateRoot();
|
|
66
|
+
let stateRootWatcher;
|
|
67
|
+
const runDirWatchers = new Map();
|
|
68
|
+
const close = () => {
|
|
69
|
+
stateRootWatcher?.close();
|
|
70
|
+
stateRootWatcher = undefined;
|
|
71
|
+
for (const watcher of runDirWatchers.values())
|
|
72
|
+
watcher.close();
|
|
73
|
+
runDirWatchers.clear();
|
|
74
|
+
};
|
|
75
|
+
const watchRunDir = (stateDir) => {
|
|
76
|
+
if (runDirWatchers.has(stateDir) || !existsSync(stateDir))
|
|
77
|
+
return;
|
|
78
|
+
try {
|
|
79
|
+
const watcher = watch(stateDir, input.onChange);
|
|
80
|
+
watcher.on("error", () => {
|
|
81
|
+
watcher.close();
|
|
82
|
+
runDirWatchers.delete(stateDir);
|
|
83
|
+
});
|
|
84
|
+
runDirWatchers.set(stateDir, watcher);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Watching is best-effort; explicit inspect remains available.
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
function refresh() {
|
|
91
|
+
if (!existsSync(stateRoot))
|
|
92
|
+
return;
|
|
93
|
+
if (!stateRootWatcher) {
|
|
94
|
+
try {
|
|
95
|
+
stateRootWatcher = watch(stateRoot, input.onChange);
|
|
96
|
+
stateRootWatcher.on("error", () => {
|
|
97
|
+
stateRootWatcher?.close();
|
|
98
|
+
stateRootWatcher = undefined;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Watching is best-effort; explicit inspect remains available.
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
for (const entry of readdirSync(stateRoot, { withFileTypes: true })) {
|
|
106
|
+
if (!entry.isDirectory())
|
|
107
|
+
continue;
|
|
108
|
+
watchRunDir(`${stateRoot}/${entry.name}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { close, refresh };
|
|
112
|
+
}
|
|
11
113
|
const TERMINAL = new Set([
|
|
12
114
|
"done",
|
|
13
115
|
"failed",
|
package/dist/lib/paths.d.ts
CHANGED
|
@@ -4,9 +4,18 @@
|
|
|
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 interface ExtensionRuntimePaths {
|
|
8
|
+
configPath: string;
|
|
9
|
+
runStateRoot: string;
|
|
10
|
+
tempDir: string;
|
|
11
|
+
}
|
|
7
12
|
export declare function getConfigPath(agentDir?: string): string;
|
|
8
13
|
export declare function getExtensionTmpDir(agentDir?: string, extensionName?: string): string;
|
|
9
14
|
export declare function getRunStateRoot(agentDir?: string): string;
|
|
15
|
+
export declare function getExtensionRuntimePaths(agentDir?: string): ExtensionRuntimePaths;
|
|
16
|
+
export declare const EXTENSION_RUNTIME_PATHS: ExtensionRuntimePaths;
|
|
17
|
+
export declare function getExtensionSkillsDir(extensionUrl: string): string;
|
|
18
|
+
export declare function getExistingExtensionSkillPaths(extensionUrl: string): string[];
|
|
10
19
|
export declare function getRecipeRoot(agentDir?: string): string;
|
|
11
20
|
export declare function getRecipeCandidateRoot(agentDir?: string): string;
|
|
12
21
|
export declare function getPackagedRecipeRoot(): string;
|
package/dist/lib/paths.js
CHANGED
|
@@ -21,6 +21,21 @@ export function getExtensionTmpDir(agentDir = getAgentDir(), extensionName = "pi
|
|
|
21
21
|
export function getRunStateRoot(agentDir = getAgentDir()) {
|
|
22
22
|
return join(getExtensionTmpDir(agentDir), "runs");
|
|
23
23
|
}
|
|
24
|
+
export function getExtensionRuntimePaths(agentDir = getAgentDir()) {
|
|
25
|
+
return {
|
|
26
|
+
configPath: getConfigPath(agentDir),
|
|
27
|
+
runStateRoot: getRunStateRoot(agentDir),
|
|
28
|
+
tempDir: getExtensionTmpDir(agentDir),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export const EXTENSION_RUNTIME_PATHS = getExtensionRuntimePaths();
|
|
32
|
+
export function getExtensionSkillsDir(extensionUrl) {
|
|
33
|
+
return join(dirname(fileURLToPath(extensionUrl)), "skills");
|
|
34
|
+
}
|
|
35
|
+
export function getExistingExtensionSkillPaths(extensionUrl) {
|
|
36
|
+
const skillsDir = getExtensionSkillsDir(extensionUrl);
|
|
37
|
+
return existsSync(skillsDir) ? [skillsDir] : [];
|
|
38
|
+
}
|
|
24
39
|
export function getRecipeRoot(agentDir = getAgentDir()) {
|
|
25
40
|
return join(agentDir, "recipes");
|
|
26
41
|
}
|
package/dist/lib/pi.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi SDK adapter boundary.
|
|
3
|
+
* Zones: pi agent sdk boundary, extension host adapters
|
|
4
|
+
* Owns direct pi SDK imports and exposes narrow pi-actors-facing helpers/types for the composition root.
|
|
5
|
+
*/
|
|
6
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
export type { ExtensionAPI, ExtensionContext };
|
|
8
|
+
export interface PiNotificationSink {
|
|
9
|
+
notify(message: string, level: "info" | "warning" | "error"): void;
|
|
10
|
+
sendFollowUp(message: {
|
|
11
|
+
customType: string;
|
|
12
|
+
content: string;
|
|
13
|
+
display: true;
|
|
14
|
+
details: unknown;
|
|
15
|
+
}): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function getSessionId(ctx: ExtensionContext): string;
|
|
18
|
+
export declare function createNotificationSink(pi: ExtensionAPI, ctx: ExtensionContext): PiNotificationSink;
|
|
19
|
+
export declare function registerToolDefinitions(pi: ExtensionAPI, definitions: Iterable<unknown>): void;
|
package/dist/lib/pi.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi SDK adapter boundary.
|
|
3
|
+
* Zones: pi agent sdk boundary, extension host adapters
|
|
4
|
+
* Owns direct pi SDK imports and exposes narrow pi-actors-facing helpers/types for the composition root.
|
|
5
|
+
*/
|
|
6
|
+
export function getSessionId(ctx) {
|
|
7
|
+
return ctx.sessionManager.getSessionId();
|
|
8
|
+
}
|
|
9
|
+
export function createNotificationSink(pi, ctx) {
|
|
10
|
+
return {
|
|
11
|
+
notify: (message, level) => ctx.ui.notify(message, level),
|
|
12
|
+
sendFollowUp: (message) => pi.sendMessage(message, {
|
|
13
|
+
deliverAs: "followUp",
|
|
14
|
+
triggerTurn: true,
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function registerToolDefinitions(pi, definitions) {
|
|
19
|
+
for (const definition of definitions)
|
|
20
|
+
pi.registerTool(definition);
|
|
21
|
+
}
|
package/dist/lib/runtime.d.ts
CHANGED
|
@@ -33,4 +33,9 @@ export interface ToolRegistryRuntime {
|
|
|
33
33
|
notify(ctx: RuntimeContext, message: string, type: "info" | "warning" | "error"): void;
|
|
34
34
|
registerRuntimeTool(cfg: Config.RegisteredTool): void;
|
|
35
35
|
}
|
|
36
|
+
export interface RecipeToolReloadWatcher {
|
|
37
|
+
close(): void;
|
|
38
|
+
watch(ctx: RuntimeContext): void;
|
|
39
|
+
}
|
|
36
40
|
export declare function createAutoToolsRuntime(deps: ToolRegistryRuntimeDeps): ToolRegistryRuntime;
|
|
41
|
+
export declare function createRecipeToolReloadWatcher(runtime: Pick<ToolRegistryRuntime, "loadTools">): RecipeToolReloadWatcher;
|
package/dist/lib/runtime.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Zones: runtime coordination, registry loading, pi tools
|
|
4
4
|
* Owns persisted tool loading, conflict detection, runtime registration, and warning notification
|
|
5
5
|
*/
|
|
6
|
+
import { existsSync, watch } from "node:fs";
|
|
6
7
|
import * as Paths from "./paths.js";
|
|
7
8
|
import * as RecipeDiscovery from "./recipe-discovery.js";
|
|
8
9
|
import * as Tools from "./tools.js";
|
|
@@ -123,3 +124,50 @@ export function createAutoToolsRuntime(deps) {
|
|
|
123
124
|
registerRuntimeTool,
|
|
124
125
|
};
|
|
125
126
|
}
|
|
127
|
+
export function createRecipeToolReloadWatcher(runtime) {
|
|
128
|
+
let reloadTimeout;
|
|
129
|
+
let rootWatcher;
|
|
130
|
+
let failureNotified = false;
|
|
131
|
+
const close = () => {
|
|
132
|
+
rootWatcher?.close();
|
|
133
|
+
rootWatcher = undefined;
|
|
134
|
+
if (reloadTimeout)
|
|
135
|
+
clearTimeout(reloadTimeout);
|
|
136
|
+
reloadTimeout = undefined;
|
|
137
|
+
};
|
|
138
|
+
const notifyFailure = (ctx) => {
|
|
139
|
+
if (failureNotified)
|
|
140
|
+
return;
|
|
141
|
+
failureNotified = true;
|
|
142
|
+
ctx.ui.notify("Recipe live reload watcher failed; restart the session or use register_tool again to refresh recipe tools.", "warning");
|
|
143
|
+
};
|
|
144
|
+
const scheduleReload = (ctx) => {
|
|
145
|
+
failureNotified = false;
|
|
146
|
+
if (reloadTimeout)
|
|
147
|
+
clearTimeout(reloadTimeout);
|
|
148
|
+
reloadTimeout = setTimeout(() => {
|
|
149
|
+
runtime.loadTools(ctx);
|
|
150
|
+
ctx.ui.notify("Recipe tools refreshed from ~/.pi/agent/recipes", "info");
|
|
151
|
+
}, 150);
|
|
152
|
+
reloadTimeout.unref?.();
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
close,
|
|
156
|
+
watch(ctx) {
|
|
157
|
+
const recipeRoot = Paths.getRecipeRoot();
|
|
158
|
+
if (rootWatcher || !existsSync(recipeRoot))
|
|
159
|
+
return;
|
|
160
|
+
try {
|
|
161
|
+
rootWatcher = watch(recipeRoot, () => scheduleReload(ctx));
|
|
162
|
+
rootWatcher.on("error", () => {
|
|
163
|
+
rootWatcher?.close();
|
|
164
|
+
rootWatcher = undefined;
|
|
165
|
+
notifyFailure(ctx);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
notifyFailure(ctx);
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
package/dist/lib/tools.d.ts
CHANGED
|
@@ -8,6 +8,14 @@ import * as Execution from "./execution.ts";
|
|
|
8
8
|
import * as Registry from "./registry.ts";
|
|
9
9
|
export type RegisterToolInput = Registry.RegisterToolInput;
|
|
10
10
|
export type RegisterToolRuntimeDeps<TContext> = Registry.RegisterToolRuntimeDeps<TContext>;
|
|
11
|
+
export interface CoreActorToolDefinitionDeps<TContext extends AsyncRunToolContext> {
|
|
12
|
+
configPath: string;
|
|
13
|
+
getActiveTools: () => string[];
|
|
14
|
+
getRuntimeTool: (name: string) => unknown;
|
|
15
|
+
registryRuntime: Pick<RegisterToolRuntimeDeps<TContext>, "getExternalToolConflict" | "getTools" | "notify" | "registerRuntimeTool">;
|
|
16
|
+
setActiveTools: (toolNames: string[]) => void;
|
|
17
|
+
}
|
|
18
|
+
export declare const RESERVED_TOOL_NAMES: Set<string>;
|
|
11
19
|
type JsonSchema = Record<string, unknown>;
|
|
12
20
|
export declare function createRegisterToolDefinition<TContext>(deps: RegisterToolRuntimeDeps<TContext>): {
|
|
13
21
|
name: string;
|
|
@@ -35,5 +43,6 @@ export interface ActorMessageToolDeps<TContext = unknown> {
|
|
|
35
43
|
getTool?: (name: string) => any | undefined;
|
|
36
44
|
}
|
|
37
45
|
export declare function createActorMessageToolDefinition<TContext = unknown>(deps?: ActorMessageToolDeps<TContext>): any;
|
|
46
|
+
export declare function createCoreActorToolDefinitions<TContext extends AsyncRunToolContext>(deps: CoreActorToolDefinitionDeps<TContext>): any[];
|
|
38
47
|
export declare function createRuntimeToolDefinition(cfg: RegisteredTool, exec: Execution.RegisteredToolExec): any;
|
|
39
48
|
export {};
|
package/dist/lib/tools.js
CHANGED
|
@@ -18,6 +18,19 @@ import * as RecipeReferences from "./recipe-references.js";
|
|
|
18
18
|
import * as RecipeUsage from "./recipe-usage.js";
|
|
19
19
|
import * as Registry from "./registry.js";
|
|
20
20
|
import * as Schema from "./schema.js";
|
|
21
|
+
export const RESERVED_TOOL_NAMES = new Set([
|
|
22
|
+
"read",
|
|
23
|
+
"write",
|
|
24
|
+
"edit",
|
|
25
|
+
"bash",
|
|
26
|
+
"find",
|
|
27
|
+
"grep",
|
|
28
|
+
"ls",
|
|
29
|
+
"register_tool",
|
|
30
|
+
"message",
|
|
31
|
+
"spawn",
|
|
32
|
+
"inspect",
|
|
33
|
+
]);
|
|
21
34
|
function stringSchema(description) {
|
|
22
35
|
return { description, type: "string" };
|
|
23
36
|
}
|
|
@@ -359,9 +372,15 @@ function summarizeOtherSessions(currentSession, allRuns) {
|
|
|
359
372
|
}
|
|
360
373
|
function compactSessionRuns(session, runs, summary = {}) {
|
|
361
374
|
const suffix = [
|
|
362
|
-
summary.other_sessions !== undefined
|
|
363
|
-
|
|
364
|
-
|
|
375
|
+
summary.other_sessions !== undefined
|
|
376
|
+
? `other_sessions=${String(summary.other_sessions)}`
|
|
377
|
+
: "",
|
|
378
|
+
summary.other_runs !== undefined
|
|
379
|
+
? `other_runs=${String(summary.other_runs)}`
|
|
380
|
+
: "",
|
|
381
|
+
]
|
|
382
|
+
.filter(Boolean)
|
|
383
|
+
.join(" ");
|
|
365
384
|
if (runs.length === 0)
|
|
366
385
|
return `\nsession=${session} runs=0${suffix ? ` ${suffix}` : ""}`;
|
|
367
386
|
return `\nsession=${session} runs=${runs.length}${suffix ? ` ${suffix}` : ""}\n${runs
|
|
@@ -578,7 +597,9 @@ function formatToolActorFailure(tool, message, params, error) {
|
|
|
578
597
|
});
|
|
579
598
|
}
|
|
580
599
|
function shadowedRecipeLaunchDiagnostic(recipe) {
|
|
581
|
-
if (typeof recipe !== "string" ||
|
|
600
|
+
if (typeof recipe !== "string" ||
|
|
601
|
+
recipe.includes("/") ||
|
|
602
|
+
recipe.includes("~"))
|
|
582
603
|
return undefined;
|
|
583
604
|
const discovery = RecipeDiscovery.discoverRecipeSources([
|
|
584
605
|
{ root: Paths.getRecipeRoot(), defaultTool: true, mutableUsage: true },
|
|
@@ -604,7 +625,9 @@ function writeSpawnCandidateRecipe(input, meta) {
|
|
|
604
625
|
if (process.env.NODE_TEST_CONTEXT &&
|
|
605
626
|
process.env.PI_ACTORS_ENABLE_SPAWN_CANDIDATES_IN_TEST !== "1")
|
|
606
627
|
return undefined;
|
|
607
|
-
if (input.template === undefined ||
|
|
628
|
+
if (input.template === undefined ||
|
|
629
|
+
input.file !== undefined ||
|
|
630
|
+
input.recipe !== undefined)
|
|
608
631
|
return undefined;
|
|
609
632
|
const root = Paths.getRecipeCandidateRoot();
|
|
610
633
|
mkdirSync(root, { recursive: true });
|
|
@@ -656,12 +679,15 @@ async function routeBranchEnvelope(stateDir, runId, recipient, message, _options
|
|
|
656
679
|
return await AsyncRuns.sendRunMessage(runId, JSON.stringify(branchMessage));
|
|
657
680
|
}
|
|
658
681
|
catch (error) {
|
|
659
|
-
const record = error && typeof error === "object"
|
|
682
|
+
const record = error && typeof error === "object"
|
|
683
|
+
? error
|
|
684
|
+
: {};
|
|
660
685
|
if (record.queued === true) {
|
|
661
686
|
return {
|
|
662
687
|
control_path: record.control_path,
|
|
663
688
|
control_type: record.control_type,
|
|
664
|
-
delivery_error: record.delivery_error ??
|
|
689
|
+
delivery_error: record.delivery_error ??
|
|
690
|
+
(error instanceof Error ? error.message : String(error)),
|
|
665
691
|
inbox_id: record.inbox_id,
|
|
666
692
|
queued: true,
|
|
667
693
|
run: runId,
|
|
@@ -805,7 +831,10 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
805
831
|
const target = String(input.target ?? "");
|
|
806
832
|
const view = String(input.view ?? "");
|
|
807
833
|
if (target === "recipes" || target === "recipe-registry") {
|
|
808
|
-
if (view !== "status" &&
|
|
834
|
+
if (view !== "status" &&
|
|
835
|
+
view !== "summary" &&
|
|
836
|
+
view !== "doctor" &&
|
|
837
|
+
view !== "imports") {
|
|
809
838
|
throw new Error("inspect recipes supports view=status, view=summary, view=doctor, or view=imports.");
|
|
810
839
|
}
|
|
811
840
|
const discovered = RecipeDiscovery.discoverRecipeSources([
|
|
@@ -1257,6 +1286,27 @@ export function createActorMessageToolDefinition(deps = {}) {
|
|
|
1257
1286
|
},
|
|
1258
1287
|
};
|
|
1259
1288
|
}
|
|
1289
|
+
export function createCoreActorToolDefinitions(deps) {
|
|
1290
|
+
return [
|
|
1291
|
+
createRegisterToolDefinition({
|
|
1292
|
+
configPath: deps.configPath,
|
|
1293
|
+
getActiveTools: deps.getActiveTools,
|
|
1294
|
+
getExternalToolConflict: deps.registryRuntime.getExternalToolConflict,
|
|
1295
|
+
getTools: deps.registryRuntime.getTools,
|
|
1296
|
+
notify: deps.registryRuntime.notify,
|
|
1297
|
+
registerRuntimeTool: deps.registryRuntime.registerRuntimeTool,
|
|
1298
|
+
reservedToolNames: RESERVED_TOOL_NAMES,
|
|
1299
|
+
setActiveTools: deps.setActiveTools,
|
|
1300
|
+
}),
|
|
1301
|
+
createSpawnToolDefinition(),
|
|
1302
|
+
createActorMessageToolDefinition({
|
|
1303
|
+
getTool: (name) => deps.getRuntimeTool(name),
|
|
1304
|
+
}),
|
|
1305
|
+
createInspectToolDefinition({
|
|
1306
|
+
getTool: (name) => deps.getRuntimeTool(name),
|
|
1307
|
+
}),
|
|
1308
|
+
];
|
|
1309
|
+
}
|
|
1260
1310
|
export function createRuntimeToolDefinition(cfg, exec) {
|
|
1261
1311
|
const paramSchema = {};
|
|
1262
1312
|
const required = [];
|