@llblab/pi-actors 0.32.0 → 0.34.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 +23 -11
- package/BACKLOG.md +2 -30
- package/CHANGELOG.md +30 -1
- package/dist/index.js +10 -10
- package/dist/lib/async-runs.d.ts +23 -75
- package/dist/lib/async-runs.js +49 -801
- package/dist/lib/command-templates.d.ts +3 -3
- package/dist/lib/command-templates.js +3 -3
- package/dist/lib/config.d.ts +2 -2
- package/dist/lib/config.js +5 -5
- package/dist/lib/{output.d.ts → execution-output.d.ts} +3 -3
- package/dist/lib/{output.js → execution-output.js} +3 -3
- package/dist/lib/execution.js +1 -1
- package/dist/lib/{actor-inspector-tui.d.ts → inspector.d.ts} +3 -2
- package/dist/lib/{actor-inspector-tui.js → inspector.js} +3 -2
- package/dist/lib/mailbox-loop.d.ts +3 -3
- package/dist/lib/mailbox-loop.js +3 -3
- package/dist/lib/{actor-messages.d.ts → messages.d.ts} +3 -3
- package/dist/lib/{actor-messages.js → messages.js} +3 -3
- package/dist/lib/paths.d.ts +1 -1
- package/dist/lib/paths.js +3 -3
- package/dist/lib/{actor-recipe-context.d.ts → recipes-context.d.ts} +3 -2
- package/dist/lib/{actor-recipe-context.js → recipes-context.js} +2 -1
- package/dist/lib/{recipe-discovery.d.ts → recipes-discovery.d.ts} +9 -9
- package/dist/lib/{recipe-discovery.js → recipes-discovery.js} +17 -17
- package/dist/lib/registry.d.ts +1 -1
- package/dist/lib/registry.js +22 -22
- package/dist/lib/{actor-rooms.d.ts → rooms.d.ts} +4 -4
- package/dist/lib/{actor-rooms.js → rooms.js} +3 -3
- package/dist/lib/runs-artifacts.d.ts +21 -0
- package/dist/lib/runs-artifacts.js +68 -0
- package/dist/lib/runs-control.d.ts +13 -0
- package/dist/lib/runs-control.js +59 -0
- package/dist/lib/runs-identity.d.ts +5 -0
- package/dist/lib/runs-identity.js +11 -0
- package/dist/lib/runs-index.d.ts +18 -0
- package/dist/lib/runs-index.js +119 -0
- package/dist/lib/runs-mailbox.d.ts +25 -0
- package/dist/lib/runs-mailbox.js +146 -0
- package/dist/lib/runs-messages.d.ts +15 -0
- package/dist/lib/runs-messages.js +179 -0
- package/dist/lib/runs-outbox.d.ts +41 -0
- package/dist/lib/runs-outbox.js +87 -0
- package/dist/lib/runs-process.d.ts +7 -0
- package/dist/lib/runs-process.js +36 -0
- package/dist/lib/runs-retention.d.ts +8 -0
- package/dist/lib/runs-retention.js +56 -0
- package/dist/lib/runs-start.d.ts +9 -0
- package/dist/lib/runs-start.js +67 -0
- package/dist/lib/runs-status.d.ts +8 -0
- package/dist/lib/runs-status.js +60 -0
- package/dist/lib/runtime.d.ts +2 -2
- package/dist/lib/runtime.js +5 -5
- package/dist/lib/schema.d.ts +9 -0
- package/dist/lib/schema.js +40 -0
- package/dist/lib/tools-access.d.ts +19 -0
- package/dist/lib/tools-access.js +44 -0
- package/dist/lib/tools-inspect.d.ts +11 -0
- package/dist/lib/tools-inspect.js +534 -0
- package/dist/lib/tools-local.d.ts +14 -0
- package/dist/lib/tools-local.js +156 -0
- package/dist/lib/tools-mailbox.d.ts +8 -0
- package/dist/lib/tools-mailbox.js +48 -0
- package/dist/lib/tools-message.d.ts +9 -0
- package/dist/lib/tools-message.js +346 -0
- package/dist/lib/tools-register.d.ts +18 -0
- package/dist/lib/tools-register.js +40 -0
- package/dist/lib/tools-response.d.ts +21 -0
- package/dist/lib/tools-response.js +219 -0
- package/dist/lib/tools-spawn.d.ts +12 -0
- package/dist/lib/tools-spawn.js +159 -0
- package/dist/lib/tools.d.ts +13 -39
- package/dist/lib/tools.js +11 -1497
- package/dist/recipes/utility-coordinator-lock-snapshot.json +1 -1
- package/dist/scripts/actor-worker.mjs +186 -7
- package/dist/scripts/async-runner.mjs +171 -8
- package/dist/scripts/build-dist.mjs +1 -1
- package/dist/scripts/conformance.mjs +34 -18
- package/dist/scripts/coordinator.mjs +733 -11
- package/dist/scripts/locker.mjs +345 -16
- package/dist/scripts/recipe-utils.mjs +478 -14
- package/dist/scripts/validate-recipe.mjs +110 -10
- package/dist/skills/actors/SKILL.md +2 -2
- package/dist/skills/swarm/SKILL.md +2 -2
- package/docs/recipe-library.md +1 -1
- package/docs/template-recipes.md +1 -1
- package/docs/tool-registry.md +3 -3
- package/index.ts +12 -19
- package/lib/async-runs.ts +124 -1016
- package/lib/command-templates.ts +3 -3
- package/lib/config.ts +7 -7
- package/lib/{output.ts → execution-output.ts} +3 -3
- package/lib/execution.ts +5 -1
- package/lib/{actor-inspector-tui.ts → inspector.ts} +4 -3
- package/lib/mailbox-loop.ts +3 -3
- package/lib/{actor-messages.ts → messages.ts} +3 -3
- package/lib/paths.ts +3 -3
- package/lib/{actor-recipe-context.ts → recipes-context.ts} +3 -2
- package/lib/{recipe-discovery.ts → recipes-discovery.ts} +30 -30
- package/lib/{recipe-usage.ts → recipes-usage.ts} +2 -2
- package/lib/registry.ts +37 -24
- package/lib/{actor-rooms.ts → rooms.ts} +4 -4
- package/lib/runs-artifacts.ts +96 -0
- package/lib/runs-control.ts +85 -0
- package/lib/runs-identity.ts +14 -0
- package/lib/runs-index.ts +156 -0
- package/lib/runs-mailbox.ts +208 -0
- package/lib/runs-messages.ts +252 -0
- package/lib/runs-outbox.ts +144 -0
- package/lib/runs-process.ts +51 -0
- package/lib/runs-retention.ts +77 -0
- package/lib/runs-start.ts +98 -0
- package/lib/runs-status.ts +78 -0
- package/lib/runtime.ts +6 -6
- package/lib/schema.ts +55 -0
- package/lib/tools-access.ts +68 -0
- package/lib/tools-inspect.ts +735 -0
- package/lib/tools-local.ts +234 -0
- package/lib/tools-mailbox.ts +56 -0
- package/lib/tools-message.ts +483 -0
- package/lib/tools-register.ts +64 -0
- package/lib/tools-response.ts +267 -0
- package/lib/tools-spawn.ts +240 -0
- package/lib/tools.ts +26 -2051
- package/package.json +1 -1
- package/recipes/utility-coordinator-lock-snapshot.json +1 -1
- package/scripts/actor-worker.mjs +186 -7
- package/scripts/async-runner.mjs +171 -8
- package/scripts/build-dist.mjs +1 -1
- package/scripts/conformance.mjs +34 -18
- package/scripts/coordinator.mjs +733 -11
- package/scripts/locker.mjs +345 -16
- package/scripts/recipe-utils.mjs +478 -14
- package/scripts/validate-recipe.mjs +110 -10
- package/skills/actors/SKILL.md +2 -2
- package/skills/swarm/SKILL.md +2 -2
- package/dist/lib/actor-worker.d.ts +0 -16
- package/dist/lib/actor-worker.js +0 -134
- package/dist/lib/async-runner.d.ts +0 -5
- package/dist/lib/async-runner.js +0 -134
- package/dist/lib/conformance.d.ts +0 -12
- package/dist/lib/conformance.js +0 -28
- package/dist/lib/coordinator.d.ts +0 -5
- package/dist/lib/coordinator.js +0 -660
- package/dist/lib/locker.d.ts +0 -5
- package/dist/lib/locker.js +0 -310
- package/dist/lib/recipe-utils.d.ts +0 -5
- package/dist/lib/recipe-utils.js +0 -385
- package/dist/lib/validate-recipe.d.ts +0 -6
- package/dist/lib/validate-recipe.js +0 -104
- package/lib/actor-worker.ts +0 -174
- package/lib/async-runner.ts +0 -173
- package/lib/conformance.ts +0 -46
- package/lib/coordinator.ts +0 -742
- package/lib/locker.ts +0 -340
- package/lib/recipe-utils.ts +0 -486
- package/lib/validate-recipe.ts +0 -110
- /package/dist/lib/{recipe-references.d.ts → recipes-references.d.ts} +0 -0
- /package/dist/lib/{recipe-references.js → recipes-references.js} +0 -0
- /package/dist/lib/{recipe-usage.d.ts → recipes-usage.d.ts} +0 -0
- /package/dist/lib/{recipe-usage.js → recipes-usage.js} +0 -0
- /package/lib/{recipe-references.ts → recipes-references.ts} +0 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public message tool behavior
|
|
3
|
+
* Zones: addressed actor messages, run controls, branch/room delivery, tool actor invocation
|
|
4
|
+
* Owns the public message execution path and compact delivery responses
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as AsyncRuns from "./async-runs.ts";
|
|
8
|
+
import * as Messages from "./messages.ts";
|
|
9
|
+
import * as Rooms from "./rooms.ts";
|
|
10
|
+
import * as Schema from "./schema.ts";
|
|
11
|
+
import * as ToolsAccess from "./tools-access.ts";
|
|
12
|
+
import * as ToolsMailbox from "./tools-mailbox.ts";
|
|
13
|
+
import * as ToolsResponse from "./tools-response.ts";
|
|
14
|
+
|
|
15
|
+
function asRecord(value: unknown): Record<string, unknown> {
|
|
16
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
17
|
+
? (value as Record<string, unknown>)
|
|
18
|
+
: {};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function maybeJsonText(
|
|
22
|
+
value: unknown,
|
|
23
|
+
verbose: boolean | undefined,
|
|
24
|
+
compact: string,
|
|
25
|
+
): string {
|
|
26
|
+
return verbose ? ToolsResponse.jsonText(value) : compact;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function messageBodyToRunLine(message: Messages.ActorMessage): string {
|
|
30
|
+
if (message.type !== "run.message") return JSON.stringify(message);
|
|
31
|
+
if (typeof message.body === "string") return message.body;
|
|
32
|
+
if (message.body === undefined) return message.type;
|
|
33
|
+
return JSON.stringify(message.body);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function messageBodyToToolParams(
|
|
37
|
+
message: Messages.ActorMessage,
|
|
38
|
+
): Record<string, unknown> {
|
|
39
|
+
if (
|
|
40
|
+
message.body &&
|
|
41
|
+
typeof message.body === "object" &&
|
|
42
|
+
!Array.isArray(message.body)
|
|
43
|
+
) {
|
|
44
|
+
return message.body as Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
if (message.body === undefined) return {};
|
|
47
|
+
return { input: message.body };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function formatToolActorFailure(
|
|
51
|
+
tool: string,
|
|
52
|
+
message: Messages.ActorMessage,
|
|
53
|
+
params: Record<string, unknown>,
|
|
54
|
+
error: unknown,
|
|
55
|
+
): Error {
|
|
56
|
+
const original = error instanceof Error ? error.message : String(error);
|
|
57
|
+
const paramsPreview = ToolsResponse.compactPreview(params, 240) ?? "{}";
|
|
58
|
+
return Object.assign(
|
|
59
|
+
new Error(
|
|
60
|
+
`tool actor ${tool} failed for message type ${message.type}: ${original}; params=${paramsPreview}`,
|
|
61
|
+
),
|
|
62
|
+
{
|
|
63
|
+
message_type: message.type,
|
|
64
|
+
original_error: original,
|
|
65
|
+
params_preview: paramsPreview,
|
|
66
|
+
tool,
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function assertMessageSenderBelongsToRun(
|
|
72
|
+
message: Messages.ActorMessage,
|
|
73
|
+
run: string,
|
|
74
|
+
routeLabel: string,
|
|
75
|
+
): void {
|
|
76
|
+
if (!message.from) {
|
|
77
|
+
throw new Error(`message to ${message.to} requires from=<actor address>.`);
|
|
78
|
+
}
|
|
79
|
+
const sender = Messages.parseActorAddress(message.from);
|
|
80
|
+
if (
|
|
81
|
+
(sender.kind !== "run" && sender.kind !== "branch") ||
|
|
82
|
+
sender.value !== run
|
|
83
|
+
) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`message to ${routeLabel} requires from=run:${run} or branch:${run}/<branch>; got ${message.from}.`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function routeBranchEnvelope(
|
|
91
|
+
stateDir: string,
|
|
92
|
+
runId: string,
|
|
93
|
+
recipient: string,
|
|
94
|
+
message: Messages.ActorMessage,
|
|
95
|
+
_options: { source: "direct" | "room-multicast" },
|
|
96
|
+
): Promise<Record<string, unknown>> {
|
|
97
|
+
const branchMessage = { ...message, to: recipient };
|
|
98
|
+
Rooms.appendBranchInboxMessage(stateDir, runId, recipient, branchMessage);
|
|
99
|
+
try {
|
|
100
|
+
return await AsyncRuns.sendRunMessage(runId, JSON.stringify(branchMessage));
|
|
101
|
+
} catch (error) {
|
|
102
|
+
const record =
|
|
103
|
+
error && typeof error === "object"
|
|
104
|
+
? (error as Record<string, unknown>)
|
|
105
|
+
: {};
|
|
106
|
+
if (record.queued === true) {
|
|
107
|
+
return {
|
|
108
|
+
control_path: record.control_path,
|
|
109
|
+
control_type: record.control_type,
|
|
110
|
+
delivery_error:
|
|
111
|
+
record.delivery_error ??
|
|
112
|
+
(error instanceof Error ? error.message : String(error)),
|
|
113
|
+
inbox_id: record.inbox_id,
|
|
114
|
+
queued: true,
|
|
115
|
+
run: runId,
|
|
116
|
+
sent: false,
|
|
117
|
+
state_dir: stateDir,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function getRoomMulticastRecipients(
|
|
125
|
+
message: Messages.ActorMessage,
|
|
126
|
+
run: string,
|
|
127
|
+
): string[] {
|
|
128
|
+
const raw = message.metadata?.recipients;
|
|
129
|
+
if (raw === undefined) return [];
|
|
130
|
+
if (!Array.isArray(raw)) {
|
|
131
|
+
throw new Error("room multicast metadata.recipients must be an array.");
|
|
132
|
+
}
|
|
133
|
+
return raw.map((recipient) => {
|
|
134
|
+
if (typeof recipient !== "string") {
|
|
135
|
+
throw new Error("room multicast recipients must be actor addresses.");
|
|
136
|
+
}
|
|
137
|
+
const parsed = Messages.parseActorAddress(recipient);
|
|
138
|
+
if (parsed.kind !== "branch" || parsed.value !== run) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`room multicast recipient must be branch:${run}/<branch>; got ${recipient}.`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
return Messages.formatActorAddress(parsed);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function actorMessageNextActions(
|
|
148
|
+
message: Messages.ActorMessage,
|
|
149
|
+
result: Record<string, unknown>,
|
|
150
|
+
): string[] {
|
|
151
|
+
const actions: string[] = [];
|
|
152
|
+
const address = Messages.parseActorAddress(message.to);
|
|
153
|
+
if (result.delivery_error || result.sent === false) {
|
|
154
|
+
if (address.kind === "run" && address.value) {
|
|
155
|
+
actions.push(`inspect target=run:${address.value} view=status`);
|
|
156
|
+
actions.push(`inspect target=run:${address.value} view=mailbox`);
|
|
157
|
+
} else if (address.kind === "branch" && address.value) {
|
|
158
|
+
actions.push(
|
|
159
|
+
`inspect target=branch:${address.value}/${address.branch ?? "main"} view=mailbox`,
|
|
160
|
+
);
|
|
161
|
+
actions.push(`inspect target=run:${address.value} view=status`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (result.queued === true) {
|
|
165
|
+
if (address.kind === "branch" && address.value) {
|
|
166
|
+
actions.push(
|
|
167
|
+
`inspect target=branch:${address.value}/${address.branch ?? "main"} view=mailbox`,
|
|
168
|
+
);
|
|
169
|
+
} else if (address.kind === "run" && address.value) {
|
|
170
|
+
actions.push(`inspect target=run:${address.value} view=mailbox`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return [...new Set(actions)].slice(0, 3);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function compactActorMessageResult(
|
|
177
|
+
message: Messages.ActorMessage,
|
|
178
|
+
result: Record<string, unknown>,
|
|
179
|
+
): string {
|
|
180
|
+
const tokens = [
|
|
181
|
+
`to=${message.to}`,
|
|
182
|
+
`type=${message.type}`,
|
|
183
|
+
`message=${result.sent === true || result.stopped === true ? "sent" : "not_sent"}`,
|
|
184
|
+
];
|
|
185
|
+
if (result.bytes !== undefined) tokens.push(`bytes=${String(result.bytes)}`);
|
|
186
|
+
if (result.queued === true) tokens.push("queued=true");
|
|
187
|
+
if (result.control) tokens.push(`control=${String(result.control)}`);
|
|
188
|
+
if (result.outbox) tokens.push(`messages=${String(result.outbox)}`);
|
|
189
|
+
if (result.message_count !== undefined)
|
|
190
|
+
tokens.push(`messages=${String(result.message_count)}`);
|
|
191
|
+
if (result.roster_count !== undefined)
|
|
192
|
+
tokens.push(`roster=${String(result.roster_count)}`);
|
|
193
|
+
if (result.room) tokens.push(`room=${String(result.room)}`);
|
|
194
|
+
if (result.tool) tokens.push(`tool=${String(result.tool)}`);
|
|
195
|
+
if (result.stopped === true) tokens.push("stopped=true");
|
|
196
|
+
if (result.signal) tokens.push(`signal=${String(result.signal)}`);
|
|
197
|
+
if (result.invoked === true) tokens.push("invoked=true");
|
|
198
|
+
if (result.delivery_error) {
|
|
199
|
+
tokens.push(
|
|
200
|
+
`delivery_error=${ToolsResponse.compactPreview(result.delivery_error, 96)}`,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const nextActions = Array.isArray(result.next_actions)
|
|
204
|
+
? (result.next_actions as string[])
|
|
205
|
+
: actorMessageNextActions(message, result);
|
|
206
|
+
if (nextActions.length > 0)
|
|
207
|
+
tokens.push(
|
|
208
|
+
`next=${nextActions.map((action) => action.replaceAll(/\s+/g, "_")).join("|")}`,
|
|
209
|
+
);
|
|
210
|
+
return `\n${tokens.join(" ")}`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export interface ActorMessageToolDeps<TContext = unknown> {
|
|
214
|
+
getTool?: (name: string) => any | undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function createActorMessageToolDefinition<TContext = unknown>(
|
|
218
|
+
deps: ActorMessageToolDeps<TContext> = {},
|
|
219
|
+
): any {
|
|
220
|
+
return {
|
|
221
|
+
name: "message",
|
|
222
|
+
label: "Message",
|
|
223
|
+
description:
|
|
224
|
+
"Send one typed addressed message to steer an existing actor instead of restarting it. Core routes are run:<id> and tool:<name>; advanced routes include branch:<run>/<branch>, room:<run> group timelines, coordinator, and session:<id>.",
|
|
225
|
+
parameters: Schema.objectSchema(
|
|
226
|
+
{
|
|
227
|
+
body: Schema.unionSchema([
|
|
228
|
+
Schema.stringSchema(
|
|
229
|
+
"Message body. For run:<id>, this is the run-local command line.",
|
|
230
|
+
),
|
|
231
|
+
Schema.looseObjectSchema("Structured JSON message body."),
|
|
232
|
+
Schema.arraySchema("Structured JSON message body array."),
|
|
233
|
+
]),
|
|
234
|
+
correlation_id: Schema.stringSchema(
|
|
235
|
+
"Optional correlation id for workflow/task linkage.",
|
|
236
|
+
),
|
|
237
|
+
from: Schema.stringSchema(
|
|
238
|
+
"Optional sender address, such as coordinator or run:<id>.",
|
|
239
|
+
),
|
|
240
|
+
metadata: Schema.looseObjectSchema(
|
|
241
|
+
"Optional structured metadata for routing or domain hints.",
|
|
242
|
+
),
|
|
243
|
+
reply_to: Schema.stringSchema(
|
|
244
|
+
"Optional message id this message replies to.",
|
|
245
|
+
),
|
|
246
|
+
summary: Schema.stringSchema("Optional short human-facing summary."),
|
|
247
|
+
to: Schema.stringSchema(
|
|
248
|
+
"Destination actor address, e.g. run:<id> or tool:<name>; advanced: branch:<run>/<branch>, room:<run>, coordinator, session:<id>.",
|
|
249
|
+
),
|
|
250
|
+
type: Schema.stringSchema(
|
|
251
|
+
"Semantic message type, e.g. control.approve or checkpoint.needs_scope.",
|
|
252
|
+
),
|
|
253
|
+
verbose: Schema.booleanSchema(
|
|
254
|
+
"Return full JSON instead of compact text.",
|
|
255
|
+
),
|
|
256
|
+
},
|
|
257
|
+
["to", "type"],
|
|
258
|
+
),
|
|
259
|
+
async execute(
|
|
260
|
+
_toolCallId: string,
|
|
261
|
+
params: unknown,
|
|
262
|
+
_signal: AbortSignal | undefined,
|
|
263
|
+
_onUpdate: unknown,
|
|
264
|
+
ctx: TContext,
|
|
265
|
+
) {
|
|
266
|
+
const input = asRecord(params);
|
|
267
|
+
const message = Messages.normalizeActorMessage(input);
|
|
268
|
+
const address = Messages.parseActorAddress(message.to);
|
|
269
|
+
let result: Record<string, unknown>;
|
|
270
|
+
if (address.kind === "run" && address.value) {
|
|
271
|
+
const status = ToolsAccess.assertRunAccessibleToContext(
|
|
272
|
+
address.value,
|
|
273
|
+
ctx,
|
|
274
|
+
);
|
|
275
|
+
const normalizedMailbox = ToolsMailbox.normalizeMailboxContracts(
|
|
276
|
+
asRecord(status.mailbox),
|
|
277
|
+
);
|
|
278
|
+
const acceptedTypes = new Set(
|
|
279
|
+
ToolsMailbox.mailboxTypes(normalizedMailbox.accepts),
|
|
280
|
+
);
|
|
281
|
+
const advisoryWarnings =
|
|
282
|
+
acceptedTypes.size > 0 && !acceptedTypes.has(message.type)
|
|
283
|
+
? [
|
|
284
|
+
`Message type ${message.type} is not declared in mailbox.accepts for run:${address.value}.`,
|
|
285
|
+
]
|
|
286
|
+
: [];
|
|
287
|
+
if (message.type === "control.kill") {
|
|
288
|
+
result = AsyncRuns.killRun(address.value);
|
|
289
|
+
} else if (message.type === "control.archive") {
|
|
290
|
+
result = AsyncRuns.archiveRun(address.value);
|
|
291
|
+
} else if (message.type === "control.prune") {
|
|
292
|
+
const body = asRecord(message.body);
|
|
293
|
+
result = AsyncRuns.pruneRun(address.value, {
|
|
294
|
+
preserveArtifacts:
|
|
295
|
+
body.preserve_artifacts === true ||
|
|
296
|
+
body.preserveArtifacts === true,
|
|
297
|
+
});
|
|
298
|
+
} else {
|
|
299
|
+
result = await AsyncRuns.sendRunMessage(
|
|
300
|
+
address.value,
|
|
301
|
+
messageBodyToRunLine(message),
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
if (advisoryWarnings.length > 0)
|
|
305
|
+
result = { ...result, warnings: advisoryWarnings };
|
|
306
|
+
} else if (address.kind === "branch" && address.value) {
|
|
307
|
+
const runId = address.value;
|
|
308
|
+
if (message.from)
|
|
309
|
+
assertMessageSenderBelongsToRun(
|
|
310
|
+
message,
|
|
311
|
+
runId,
|
|
312
|
+
`branch:${runId}/<branch>`,
|
|
313
|
+
);
|
|
314
|
+
const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
|
|
315
|
+
const stateDir = String(status.state_dir ?? "");
|
|
316
|
+
if (stateDir && address.branch) {
|
|
317
|
+
const ensureBranchMember = (actorAddress: string) => {
|
|
318
|
+
Rooms.ensureRoomMember(
|
|
319
|
+
stateDir,
|
|
320
|
+
runId,
|
|
321
|
+
"main",
|
|
322
|
+
actorAddress,
|
|
323
|
+
{
|
|
324
|
+
parent: `run:${runId}`,
|
|
325
|
+
role: "branch",
|
|
326
|
+
status: "present",
|
|
327
|
+
},
|
|
328
|
+
"Branch joined default room",
|
|
329
|
+
);
|
|
330
|
+
Rooms.writeBranchCommunicationSnapshot(
|
|
331
|
+
stateDir,
|
|
332
|
+
runId,
|
|
333
|
+
actorAddress,
|
|
334
|
+
);
|
|
335
|
+
};
|
|
336
|
+
ensureBranchMember(message.to);
|
|
337
|
+
if (message.from) {
|
|
338
|
+
const sender = Messages.parseActorAddress(message.from);
|
|
339
|
+
if (sender.kind === "branch" && sender.value === runId) {
|
|
340
|
+
ensureBranchMember(message.from);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
Rooms.writeCommunicationSnapshot(stateDir, runId);
|
|
344
|
+
}
|
|
345
|
+
result = await routeBranchEnvelope(
|
|
346
|
+
stateDir,
|
|
347
|
+
runId,
|
|
348
|
+
message.to,
|
|
349
|
+
message,
|
|
350
|
+
{
|
|
351
|
+
source: "direct",
|
|
352
|
+
},
|
|
353
|
+
);
|
|
354
|
+
} else if (address.kind === "room" && address.value && address.room) {
|
|
355
|
+
const runId = address.value;
|
|
356
|
+
assertMessageSenderBelongsToRun(message, runId, `room:${runId}`);
|
|
357
|
+
const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
|
|
358
|
+
const stateDir = String(status.state_dir ?? "");
|
|
359
|
+
if (!stateDir)
|
|
360
|
+
throw new Error(`${message.to} has no run state directory.`);
|
|
361
|
+
const recipients = getRoomMulticastRecipients(message, runId);
|
|
362
|
+
const roomResult = Rooms.appendRoomMessage(
|
|
363
|
+
stateDir,
|
|
364
|
+
address.room,
|
|
365
|
+
message,
|
|
366
|
+
);
|
|
367
|
+
await Promise.all(
|
|
368
|
+
recipients.map((recipient) =>
|
|
369
|
+
routeBranchEnvelope(stateDir, runId, recipient, message, {
|
|
370
|
+
source: "room-multicast",
|
|
371
|
+
}),
|
|
372
|
+
),
|
|
373
|
+
);
|
|
374
|
+
result = {
|
|
375
|
+
...roomResult,
|
|
376
|
+
...(recipients.length > 0
|
|
377
|
+
? { multicast: recipients, multicast_count: recipients.length }
|
|
378
|
+
: {}),
|
|
379
|
+
};
|
|
380
|
+
} else if (address.kind === "tool" && address.value) {
|
|
381
|
+
const tool = deps.getTool?.(address.value);
|
|
382
|
+
if (!tool || typeof tool.execute !== "function") {
|
|
383
|
+
throw new Error(
|
|
384
|
+
`tool actor not found or not executable: ${address.value}`,
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
const toolParams = messageBodyToToolParams(message);
|
|
388
|
+
let toolResult: unknown;
|
|
389
|
+
try {
|
|
390
|
+
toolResult = await tool.execute(
|
|
391
|
+
`message:${message.type}`,
|
|
392
|
+
toolParams,
|
|
393
|
+
_signal,
|
|
394
|
+
_onUpdate,
|
|
395
|
+
ctx,
|
|
396
|
+
);
|
|
397
|
+
} catch (error) {
|
|
398
|
+
throw formatToolActorFailure(
|
|
399
|
+
address.value,
|
|
400
|
+
message,
|
|
401
|
+
toolParams,
|
|
402
|
+
error,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
result = {
|
|
406
|
+
invoked: true,
|
|
407
|
+
sent: true,
|
|
408
|
+
tool: address.value,
|
|
409
|
+
tool_result: toolResult,
|
|
410
|
+
};
|
|
411
|
+
} else if (address.kind === "coordinator" || address.kind === "session") {
|
|
412
|
+
if (!message.from) {
|
|
413
|
+
throw new Error(`message to ${address.kind} requires from=run:<id>.`);
|
|
414
|
+
}
|
|
415
|
+
const sender = Messages.parseActorAddress(message.from);
|
|
416
|
+
if (sender.kind !== "run" || !sender.value) {
|
|
417
|
+
throw new Error(
|
|
418
|
+
`message to ${address.kind} currently requires from=run:<id>.`,
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
const senderStatus = ToolsAccess.assertRunAccessibleToContext(
|
|
422
|
+
sender.value,
|
|
423
|
+
ctx,
|
|
424
|
+
);
|
|
425
|
+
if (address.kind === "session") {
|
|
426
|
+
if (!senderStatus.ownerId) {
|
|
427
|
+
throw ToolsAccess.sessionMismatchError({
|
|
428
|
+
currentSession: undefined,
|
|
429
|
+
expectedSession: address.value,
|
|
430
|
+
run: sender.value,
|
|
431
|
+
target: `session:${address.value}`,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
if (senderStatus.ownerId !== address.value) {
|
|
435
|
+
throw ToolsAccess.sessionMismatchError({
|
|
436
|
+
currentSession: String(senderStatus.ownerId),
|
|
437
|
+
expectedSession: address.value,
|
|
438
|
+
run: sender.value,
|
|
439
|
+
target: `session:${address.value}`,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
result = AsyncRuns.appendRunOutboxEvent(sender.value, {
|
|
444
|
+
body: message.body,
|
|
445
|
+
correlation_id: message.correlation_id,
|
|
446
|
+
delivery:
|
|
447
|
+
message.metadata?.requires_response === true ||
|
|
448
|
+
address.kind === "session"
|
|
449
|
+
? "followup"
|
|
450
|
+
: undefined,
|
|
451
|
+
event: message.type,
|
|
452
|
+
from: message.from,
|
|
453
|
+
metadata: message.metadata,
|
|
454
|
+
reply_to: message.reply_to,
|
|
455
|
+
summary: message.summary,
|
|
456
|
+
to: message.to,
|
|
457
|
+
type: message.type,
|
|
458
|
+
});
|
|
459
|
+
} else {
|
|
460
|
+
throw new Error(
|
|
461
|
+
`message currently supports run:<id>, branch:<run>/<branch>, room:<run>, tool:<name>, coordinator, and session:<id> destinations; unsupported destination: ${message.to}`,
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
const nextActions = actorMessageNextActions(message, result);
|
|
465
|
+
const resultWithNext = nextActions.length
|
|
466
|
+
? { ...result, next_actions: nextActions }
|
|
467
|
+
: result;
|
|
468
|
+
return {
|
|
469
|
+
content: [
|
|
470
|
+
{
|
|
471
|
+
type: "text" as const,
|
|
472
|
+
text: maybeJsonText(
|
|
473
|
+
{ message, result: resultWithNext },
|
|
474
|
+
input.verbose === true,
|
|
475
|
+
compactActorMessageResult(message, resultWithNext),
|
|
476
|
+
),
|
|
477
|
+
},
|
|
478
|
+
],
|
|
479
|
+
details: { message, result: resultWithNext },
|
|
480
|
+
};
|
|
481
|
+
},
|
|
482
|
+
};
|
|
483
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public register_tool behavior
|
|
3
|
+
* Zones: runtime tool registration, tool registry mutation, register_tool schema
|
|
4
|
+
* Owns the public register_tool definition that persists local agent capabilities
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as Prompts from "./prompts.ts";
|
|
8
|
+
import * as Registry from "./registry.ts";
|
|
9
|
+
import * as Schema from "./schema.ts";
|
|
10
|
+
|
|
11
|
+
export type RegisterToolInput = Registry.RegisterToolInput;
|
|
12
|
+
export type RegisterToolRuntimeDeps<TContext> =
|
|
13
|
+
Registry.RegisterToolRuntimeDeps<TContext>;
|
|
14
|
+
|
|
15
|
+
const stringSchema = Schema.stringSchema;
|
|
16
|
+
const booleanSchema = Schema.booleanSchema;
|
|
17
|
+
const nullSchema = Schema.nullSchema;
|
|
18
|
+
const arraySchema = Schema.arraySchema;
|
|
19
|
+
const unionSchema = Schema.unionSchema;
|
|
20
|
+
const objectSchema = Schema.objectSchema;
|
|
21
|
+
const looseObjectSchema = Schema.looseObjectSchema;
|
|
22
|
+
|
|
23
|
+
export function createRegisterToolDefinition<TContext>(
|
|
24
|
+
deps: RegisterToolRuntimeDeps<TContext>,
|
|
25
|
+
) {
|
|
26
|
+
return {
|
|
27
|
+
name: "register_tool",
|
|
28
|
+
label: "Register Tool",
|
|
29
|
+
description: Prompts.REGISTER_TOOL_DESCRIPTION,
|
|
30
|
+
promptSnippet: Prompts.REGISTER_TOOL_PROMPT_SNIPPET,
|
|
31
|
+
promptGuidelines: Prompts.REGISTER_TOOL_GUIDELINES,
|
|
32
|
+
parameters: objectSchema(
|
|
33
|
+
{
|
|
34
|
+
args: stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.args),
|
|
35
|
+
async: booleanSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.async),
|
|
36
|
+
description: stringSchema(
|
|
37
|
+
Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.description,
|
|
38
|
+
),
|
|
39
|
+
name: stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.name),
|
|
40
|
+
state_dir: stringSchema(
|
|
41
|
+
Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.state_dir,
|
|
42
|
+
),
|
|
43
|
+
template: unionSchema([
|
|
44
|
+
stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.template),
|
|
45
|
+
looseObjectSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.template),
|
|
46
|
+
arraySchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.templateArray),
|
|
47
|
+
nullSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.templateNull),
|
|
48
|
+
]),
|
|
49
|
+
update: booleanSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.update),
|
|
50
|
+
values: looseObjectSchema(
|
|
51
|
+
Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.values,
|
|
52
|
+
),
|
|
53
|
+
},
|
|
54
|
+
[],
|
|
55
|
+
),
|
|
56
|
+
execute: async (
|
|
57
|
+
_toolCallId: string,
|
|
58
|
+
params: unknown,
|
|
59
|
+
_signal: AbortSignal | undefined,
|
|
60
|
+
_onUpdate: unknown,
|
|
61
|
+
ctx: TContext,
|
|
62
|
+
) => Registry.executeRegisterTool(params, ctx, deps),
|
|
63
|
+
};
|
|
64
|
+
}
|