@kevin5251984/guild 0.2.17 → 0.2.19
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/package.json +1 -1
- package/src/catalog/subagents.ts +2 -1
- package/src/chat-parts.ts +6 -1
- package/src/compact.ts +27 -0
- package/src/db.ts +199 -64
- package/src/generate.ts +58 -27
- package/src/handlers.ts +341 -54
- package/src/harness.ts +21 -8
- package/src/image-gen.ts +2 -1
- package/src/llm.ts +400 -58
- package/src/memory.ts +69 -0
- package/src/mention.ts +119 -12
- package/src/oauth.ts +202 -25
- package/src/opencode-free.ts +247 -0
- package/src/public/buddy.js +432 -0
- package/src/public/chat.css +174 -72
- package/src/public/chat.html +212 -43
- package/src/public/i18n.js +41 -6
- package/src/public/index.html +1 -46
- package/src/public/mobile.css +491 -0
- package/src/public/mobile.html +784 -0
- package/src/public/settings.html +188 -27
- package/src/public/skills-add.html +8 -0
- package/src/public/studio.html +171 -117
- package/src/public/style.css +220 -26
- package/src/public/subagents-add.html +8 -0
- package/src/router.ts +76 -5
- package/src/store.ts +161 -5
- package/src/subagent.ts +233 -6
- package/src/tools.ts +124 -18
- package/src/trajectory.ts +72 -9
- package/src/usage.ts +10 -0
- package/vendor/protocol/src/index.ts +14 -1
package/src/tools.ts
CHANGED
|
@@ -75,8 +75,25 @@ export type ToolContext = {
|
|
|
75
75
|
args: Record<string, unknown>,
|
|
76
76
|
ctx: ToolContext,
|
|
77
77
|
) => Promise<ToolOutcome>;
|
|
78
|
+
/** Devin-style background spawn handles for this turn. Same Map across dispatch clones. */
|
|
79
|
+
spawnHandles?: Map<string, SpawnHandle>;
|
|
78
80
|
};
|
|
79
81
|
|
|
82
|
+
export type SpawnHandle = {
|
|
83
|
+
id: string;
|
|
84
|
+
title: string;
|
|
85
|
+
profile: string;
|
|
86
|
+
done: Promise<ToolOutcome>;
|
|
87
|
+
outcome?: ToolOutcome;
|
|
88
|
+
abort?: AbortController;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** Pin the turn's handle Map before dispatch spreads a rest clone. */
|
|
92
|
+
export function attachSpawnHandles(ctx: ToolContext): Map<string, SpawnHandle> {
|
|
93
|
+
if (!ctx.spawnHandles) ctx.spawnHandles = new Map();
|
|
94
|
+
return ctx.spawnHandles;
|
|
95
|
+
}
|
|
96
|
+
|
|
80
97
|
const BASE_TOOLS: Tool[] = [
|
|
81
98
|
{
|
|
82
99
|
name: "run",
|
|
@@ -178,7 +195,7 @@ export function guildTools(
|
|
|
178
195
|
name: Type.String({ description: "Skill name or slug" }),
|
|
179
196
|
}),
|
|
180
197
|
});
|
|
181
|
-
if ((ctx.spawnDepth ?? 0) < 1
|
|
198
|
+
if ((ctx.spawnDepth ?? 0) < 1) {
|
|
182
199
|
const agents = ctx.subagents ?? [];
|
|
183
200
|
const listed = agents
|
|
184
201
|
.slice(0, 40)
|
|
@@ -190,14 +207,28 @@ export function guildTools(
|
|
|
190
207
|
const catalog = listed ? ` Available: ${listed}.` : "";
|
|
191
208
|
tools.push({
|
|
192
209
|
name: "spawn",
|
|
193
|
-
description: `
|
|
210
|
+
description: `Delegate to a specialist (Devin run_subagent / Pi subagent / Codex spawn_agent). Fresh context; returns a summary, not a transcript. You stay coordinator. Single: title + task + profile (aliases: description/prompt, name/agent). Profiles: explorer (read-only survey), reviewer (read-only critique), worker (bounded patch). luna-explore maps to explorer, luna-general to worker. Independent surveys: background=true (is_background), then read_spawn with the agent_id before the final reply. Parallel: several spawn calls this round, or tasks: [{title, task, profile}, ...] (max 8, 4 at a time). Do not spawn for one known file or a one-line change.${catalog} A read_only parent still spawns; the child stays read_only. Subagents cannot spawn children.`,
|
|
194
211
|
parameters: Type.Object({
|
|
195
|
-
prompt: Type.
|
|
196
|
-
|
|
197
|
-
|
|
212
|
+
prompt: Type.Optional(
|
|
213
|
+
Type.String({
|
|
214
|
+
description: "Self-contained task. Same as task.",
|
|
215
|
+
}),
|
|
216
|
+
),
|
|
217
|
+
task: Type.Optional(
|
|
218
|
+
Type.String({ description: "Alias of prompt (Devin/Pi)" }),
|
|
219
|
+
),
|
|
198
220
|
name: Type.Optional(
|
|
199
221
|
Type.String({
|
|
200
|
-
description: "Subagent name or slug
|
|
222
|
+
description: "Subagent name or slug. Default worker.",
|
|
223
|
+
}),
|
|
224
|
+
),
|
|
225
|
+
agent: Type.Optional(
|
|
226
|
+
Type.String({ description: "Alias of name (Pi)" }),
|
|
227
|
+
),
|
|
228
|
+
profile: Type.Optional(
|
|
229
|
+
Type.String({
|
|
230
|
+
description:
|
|
231
|
+
"Devin profile: explorer | reviewer | worker. luna-explore → explorer, luna-general → worker.",
|
|
201
232
|
}),
|
|
202
233
|
),
|
|
203
234
|
description: Type.Optional(
|
|
@@ -205,6 +236,51 @@ export function guildTools(
|
|
|
205
236
|
description: "Short 3–8 word label for the chat UI",
|
|
206
237
|
}),
|
|
207
238
|
),
|
|
239
|
+
title: Type.Optional(
|
|
240
|
+
Type.String({ description: "Alias of description (Devin title)" }),
|
|
241
|
+
),
|
|
242
|
+
background: Type.Optional(
|
|
243
|
+
Type.Boolean({
|
|
244
|
+
description:
|
|
245
|
+
"If true, return agent_id immediately and keep working. Then call read_spawn.",
|
|
246
|
+
}),
|
|
247
|
+
),
|
|
248
|
+
is_background: Type.Optional(
|
|
249
|
+
Type.Boolean({ description: "Alias of background (Devin)" }),
|
|
250
|
+
),
|
|
251
|
+
tasks: Type.Optional(
|
|
252
|
+
Type.Array(
|
|
253
|
+
Type.Object({
|
|
254
|
+
prompt: Type.Optional(Type.String()),
|
|
255
|
+
task: Type.Optional(Type.String()),
|
|
256
|
+
name: Type.Optional(Type.String()),
|
|
257
|
+
agent: Type.Optional(Type.String()),
|
|
258
|
+
profile: Type.Optional(Type.String()),
|
|
259
|
+
description: Type.Optional(Type.String()),
|
|
260
|
+
title: Type.Optional(Type.String()),
|
|
261
|
+
}),
|
|
262
|
+
{
|
|
263
|
+
description:
|
|
264
|
+
"Pi parallel: run these subagents concurrently (max 8, 4 at a time).",
|
|
265
|
+
},
|
|
266
|
+
),
|
|
267
|
+
),
|
|
268
|
+
}),
|
|
269
|
+
});
|
|
270
|
+
tools.push({
|
|
271
|
+
name: "read_spawn",
|
|
272
|
+
description:
|
|
273
|
+
"Read a background spawn started with background=true (Devin read_subagent). Pass agent_id from spawn. block=true (default) waits; block=false returns running or the summary.",
|
|
274
|
+
parameters: Type.Object({
|
|
275
|
+
agent_id: Type.Optional(
|
|
276
|
+
Type.String({ description: "Id returned by background spawn" }),
|
|
277
|
+
),
|
|
278
|
+
id: Type.Optional(Type.String({ description: "Alias of agent_id" })),
|
|
279
|
+
block: Type.Optional(
|
|
280
|
+
Type.Boolean({
|
|
281
|
+
description: "Wait for the child. Default true.",
|
|
282
|
+
}),
|
|
283
|
+
),
|
|
208
284
|
}),
|
|
209
285
|
});
|
|
210
286
|
}
|
|
@@ -222,7 +298,7 @@ export const GUILD_TOOLS: Tool[] = guildTools();
|
|
|
222
298
|
|
|
223
299
|
function openaiParameters(name: string): {
|
|
224
300
|
type: "object";
|
|
225
|
-
properties: Record<string,
|
|
301
|
+
properties: Record<string, unknown>;
|
|
226
302
|
required: string[];
|
|
227
303
|
} {
|
|
228
304
|
if (name === "run") {
|
|
@@ -257,14 +333,42 @@ function openaiParameters(name: string): {
|
|
|
257
333
|
};
|
|
258
334
|
}
|
|
259
335
|
if (name === "spawn") {
|
|
260
|
-
|
|
336
|
+
const job = {
|
|
261
337
|
type: "object",
|
|
262
338
|
properties: {
|
|
263
|
-
prompt: { type: "string", description: "Self-contained task" },
|
|
339
|
+
prompt: { type: "string", description: "Self-contained task. Same as task." },
|
|
340
|
+
task: { type: "string", description: "Alias of prompt" },
|
|
264
341
|
name: { type: "string", description: "Subagent name or slug" },
|
|
342
|
+
agent: { type: "string", description: "Alias of name" },
|
|
343
|
+
profile: { type: "string", description: "explorer | reviewer | worker" },
|
|
265
344
|
description: { type: "string", description: "Short UI label" },
|
|
345
|
+
title: { type: "string", description: "Alias of description" },
|
|
266
346
|
},
|
|
267
|
-
|
|
347
|
+
};
|
|
348
|
+
return {
|
|
349
|
+
type: "object",
|
|
350
|
+
properties: {
|
|
351
|
+
...job.properties,
|
|
352
|
+
background: { type: "boolean", description: "Return agent_id immediately" },
|
|
353
|
+
is_background: { type: "boolean", description: "Alias of background" },
|
|
354
|
+
tasks: {
|
|
355
|
+
type: "array",
|
|
356
|
+
description: "Pi parallel: [{name, prompt}, ...] max 8, 4 at a time",
|
|
357
|
+
items: job,
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
required: [],
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
if (name === "read_spawn") {
|
|
364
|
+
return {
|
|
365
|
+
type: "object",
|
|
366
|
+
properties: {
|
|
367
|
+
agent_id: { type: "string", description: "Id from background spawn" },
|
|
368
|
+
id: { type: "string", description: "Alias of agent_id" },
|
|
369
|
+
block: { type: "boolean", description: "Wait. Default true." },
|
|
370
|
+
},
|
|
371
|
+
required: [],
|
|
268
372
|
};
|
|
269
373
|
}
|
|
270
374
|
if (name === "image_gen") {
|
|
@@ -327,6 +431,7 @@ export const BUILTIN_TOOL_NAMES = [
|
|
|
327
431
|
"list",
|
|
328
432
|
"skill",
|
|
329
433
|
"spawn",
|
|
434
|
+
"read_spawn",
|
|
330
435
|
"image_gen",
|
|
331
436
|
"browser",
|
|
332
437
|
] as const;
|
|
@@ -339,6 +444,7 @@ export async function executeTool(
|
|
|
339
444
|
try {
|
|
340
445
|
const refused = gateTool(name, args, ctx);
|
|
341
446
|
if (refused) return refused;
|
|
447
|
+
attachSpawnHandles(ctx);
|
|
342
448
|
if (ctx.dispatch) {
|
|
343
449
|
const { dispatch, ...rest } = ctx;
|
|
344
450
|
return await dispatch(name, args, rest);
|
|
@@ -384,13 +490,12 @@ export async function builtinExecute(
|
|
|
384
490
|
if (name === "list") return listDir(asString(args.path), pathBase);
|
|
385
491
|
if (name === "skill") return loadSkill(asString(args.name), ctx.skills ?? []);
|
|
386
492
|
if (name === "spawn") {
|
|
387
|
-
const {
|
|
388
|
-
return
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
});
|
|
493
|
+
const { runSpawnJobs } = await import("./subagent.ts");
|
|
494
|
+
return runSpawnJobs(args, ctx);
|
|
495
|
+
}
|
|
496
|
+
if (name === "read_spawn") {
|
|
497
|
+
const { readSpawn } = await import("./subagent.ts");
|
|
498
|
+
return readSpawn(args, ctx);
|
|
394
499
|
}
|
|
395
500
|
if (name === "image_gen") {
|
|
396
501
|
const { generateImage } = await import("./image-gen.ts");
|
|
@@ -713,7 +818,8 @@ Never say you cannot access this machine. Never tell the user to run the command
|
|
|
713
818
|
When the question is about this computer, call tools first, then answer with evidence from the output.
|
|
714
819
|
To generate an image, call image_gen with a prompt. Do not search the disk or load skills looking for Imagine. After it returns, include the markdown image in your reply.
|
|
715
820
|
To use a real website in a browser, call browser with action=open and a url, then snapshot/click/type using refs like @e1. Default is a Hermes-shaped snapshot of the user's last_used Chrome profile (never the live profile). Set GUILD_BROWSER_REAL_PROFILE=0 for a throwaway empty profile.
|
|
716
|
-
|
|
821
|
+
You stay coordinator. Spawn is the specialist, not a last resort (Devin run_subagent / Pi subagent / Codex spawn_agent). Call spawn for a survey (explorer / luna-explore), a critique (reviewer), or a bounded patch (worker / luna-general) instead of stuffing that work into this turn with list/read/run. Do not spawn for one known file or a one-line change. Independent surveys: spawn with background=true, keep working, then read_spawn {agent_id, block:true} before the final reply. Or several spawn calls / tasks: [{title, task, profile}] this round. Task must be self-contained (child has a fresh context). Do not let a child commit, push, or decide architecture. A read_only seat can still spawn; the child stays read_only. Subagents cannot spawn children.
|
|
822
|
+
Independent tool calls in one round also run in parallel — fire several reads/searches together.
|
|
717
823
|
Check the [exit code: N] marker on every run result; investigate failures before moving on. Prefer the workdir argument over cd.
|
|
718
824
|
To follow a staffed skill, call skill with its exact name (or slug) before applying it. Relative paths in a skill resolve against that skill's base directory.
|
|
719
825
|
Prefer small commands. macOS RAM: sysctl hw.memsize ; memory_pressure. Disk: df -h.`;
|
package/src/trajectory.ts
CHANGED
|
@@ -38,6 +38,49 @@ export function clip(text: string, n = CLIP): string {
|
|
|
38
38
|
return one.slice(0, n - 1) + "…";
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
function recordOf(value: unknown): Record<string, unknown> | null {
|
|
42
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
43
|
+
return value as Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Old rows logged spawn as TOOL "spawn spawn". Present them as spawn. */
|
|
47
|
+
export function promoteSpawnEvent<T extends { kind: string; summary: string; payload?: unknown }>(
|
|
48
|
+
event: T,
|
|
49
|
+
): T & { kind: TrajectoryKind; summary: string } {
|
|
50
|
+
if (event.kind === "spawn") return event as T & { kind: TrajectoryKind; summary: string };
|
|
51
|
+
const payload = recordOf(event.payload);
|
|
52
|
+
const toolName = String(payload?.name || "");
|
|
53
|
+
const looksSpawn =
|
|
54
|
+
event.kind === "tool" &&
|
|
55
|
+
(toolName === "spawn" ||
|
|
56
|
+
toolName === "read_spawn" ||
|
|
57
|
+
/^\s*spawn(\s+spawn)?\s*$/i.test(event.summary || ""));
|
|
58
|
+
if (!looksSpawn) return event as T & { kind: TrajectoryKind; summary: string };
|
|
59
|
+
const args = recordOf(payload?.args) || payload || {};
|
|
60
|
+
if (toolName === "read_spawn") {
|
|
61
|
+
const waitId = String(args.agent_id || args.id || "").trim();
|
|
62
|
+
return {
|
|
63
|
+
...event,
|
|
64
|
+
kind: "spawn",
|
|
65
|
+
summary: clip(`read ${waitId}`),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const agent = String(args.name || args.profile || "worker").trim() || "worker";
|
|
69
|
+
const label = String(args.title || args.description || "")
|
|
70
|
+
.replace(/\s+/g, " ")
|
|
71
|
+
.trim();
|
|
72
|
+
const prompt = String(args.prompt || args.task || "")
|
|
73
|
+
.replace(/\s+/g, " ")
|
|
74
|
+
.trim();
|
|
75
|
+
return {
|
|
76
|
+
...event,
|
|
77
|
+
kind: "spawn",
|
|
78
|
+
summary: clip(
|
|
79
|
+
`${agent}${label ? " · " + label : ""}${prompt ? " — " + prompt : ""}`,
|
|
80
|
+
),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
41
84
|
function cap(value: unknown): unknown {
|
|
42
85
|
if (typeof value === "string") {
|
|
43
86
|
return value.length > FIELD_CAP ? value.slice(0, FIELD_CAP) : value;
|
|
@@ -140,17 +183,34 @@ export function turnTrajectoryEvents(input: {
|
|
|
140
183
|
});
|
|
141
184
|
continue;
|
|
142
185
|
}
|
|
143
|
-
if (trace.name === "spawn") {
|
|
144
|
-
const agent =
|
|
145
|
-
|
|
146
|
-
|
|
186
|
+
if (trace.name === "spawn" || trace.name === "read_spawn") {
|
|
187
|
+
const agent =
|
|
188
|
+
String(
|
|
189
|
+
trace.args.profile ||
|
|
190
|
+
trace.args.name ||
|
|
191
|
+
trace.args.agent ||
|
|
192
|
+
"worker",
|
|
193
|
+
).trim() || "worker";
|
|
194
|
+
const label = String(trace.args.title || trace.args.description || "")
|
|
195
|
+
.replace(/\s+/g, " ")
|
|
196
|
+
.trim();
|
|
197
|
+
const prompt = String(trace.args.prompt || trace.args.task || "")
|
|
198
|
+
.replace(/\s+/g, " ")
|
|
199
|
+
.trim();
|
|
200
|
+
const bg =
|
|
201
|
+
trace.args.background === true || trace.args.is_background === true
|
|
202
|
+
? " (bg)"
|
|
203
|
+
: "";
|
|
204
|
+
const waitId = String(trace.args.agent_id || trace.args.id || "").trim();
|
|
147
205
|
events.push({
|
|
148
206
|
ts,
|
|
149
207
|
turnId,
|
|
150
208
|
botId,
|
|
151
209
|
kind: "spawn",
|
|
152
210
|
summary: clip(
|
|
153
|
-
|
|
211
|
+
trace.name === "read_spawn"
|
|
212
|
+
? `read ${waitId || agent}${trace.running ? " …" : ""}`
|
|
213
|
+
: `${agent}${bg}${label ? " · " + label : ""}${prompt ? " — " + prompt : ""}${trace.running ? " …" : ""}`,
|
|
154
214
|
),
|
|
155
215
|
payload: cap(trace.args),
|
|
156
216
|
result: String(cap(trace.text ?? "")),
|
|
@@ -261,16 +321,19 @@ export function synthesizeTrajectory(messages: ChatMessage[]): TrajectoryEvent[]
|
|
|
261
321
|
continue;
|
|
262
322
|
}
|
|
263
323
|
if (part.type === "tool") {
|
|
324
|
+
const spawnish = part.name === "spawn" || part.name === "read_spawn";
|
|
264
325
|
events.push({
|
|
265
326
|
seq: seq++,
|
|
266
327
|
ts,
|
|
267
328
|
turnId,
|
|
268
329
|
botId: msg.author,
|
|
269
|
-
kind:
|
|
330
|
+
kind: spawnish ? "spawn" : "tool",
|
|
270
331
|
summary: clip(
|
|
271
|
-
part.name === "
|
|
272
|
-
? part.detail ||
|
|
273
|
-
:
|
|
332
|
+
part.name === "read_spawn"
|
|
333
|
+
? `read ${part.detail || ""}`
|
|
334
|
+
: part.name === "spawn"
|
|
335
|
+
? part.detail || part.label || "spawn"
|
|
336
|
+
: `${part.name} ${part.detail}`,
|
|
274
337
|
),
|
|
275
338
|
payload: { name: part.name, detail: part.detail },
|
|
276
339
|
result: part.output,
|
package/src/usage.ts
CHANGED
|
@@ -59,13 +59,19 @@ export function fromOpenAiUsage(usage?: {
|
|
|
59
59
|
prompt_tokens?: number;
|
|
60
60
|
completion_tokens?: number;
|
|
61
61
|
total_tokens?: number;
|
|
62
|
+
prompt_tokens_details?: { cached_tokens?: number };
|
|
63
|
+
input_tokens_details?: { cached_tokens?: number };
|
|
62
64
|
} | null): ChatUsage {
|
|
63
65
|
if (!usage) return { rounds: 1 };
|
|
64
66
|
const input = num(usage.prompt_tokens);
|
|
65
67
|
const output = num(usage.completion_tokens);
|
|
68
|
+
const cacheRead =
|
|
69
|
+
num(usage.prompt_tokens_details?.cached_tokens) ??
|
|
70
|
+
num(usage.input_tokens_details?.cached_tokens);
|
|
66
71
|
return {
|
|
67
72
|
input,
|
|
68
73
|
output,
|
|
74
|
+
cacheRead,
|
|
69
75
|
totalTokens: num(usage.total_tokens) || sum(input, output),
|
|
70
76
|
rounds: 1,
|
|
71
77
|
};
|
|
@@ -74,6 +80,8 @@ export function fromOpenAiUsage(usage?: {
|
|
|
74
80
|
export function fromAnthropicUsage(usage?: {
|
|
75
81
|
input_tokens?: number;
|
|
76
82
|
output_tokens?: number;
|
|
83
|
+
cache_read_input_tokens?: number;
|
|
84
|
+
cache_creation_input_tokens?: number;
|
|
77
85
|
} | null): ChatUsage {
|
|
78
86
|
if (!usage) return { rounds: 1 };
|
|
79
87
|
const input = num(usage.input_tokens);
|
|
@@ -81,6 +89,8 @@ export function fromAnthropicUsage(usage?: {
|
|
|
81
89
|
return {
|
|
82
90
|
input,
|
|
83
91
|
output,
|
|
92
|
+
cacheRead: num(usage.cache_read_input_tokens),
|
|
93
|
+
cacheWrite: num(usage.cache_creation_input_tokens),
|
|
84
94
|
totalTokens: sum(input, output),
|
|
85
95
|
rounds: 1,
|
|
86
96
|
};
|
|
@@ -37,6 +37,8 @@ export type Bot = {
|
|
|
37
37
|
skillIds: string[];
|
|
38
38
|
defaultPositionId: string;
|
|
39
39
|
oneLiner?: string;
|
|
40
|
+
/** Public /generated/… sprite. Missing → procedural tavern buddy. */
|
|
41
|
+
portrait?: string;
|
|
40
42
|
/** Chat model for this bot. Missing → guild default. */
|
|
41
43
|
model?: ModelRef | null;
|
|
42
44
|
createdAt: string;
|
|
@@ -53,6 +55,10 @@ export type Room = {
|
|
|
53
55
|
name: string;
|
|
54
56
|
memberIds: string[];
|
|
55
57
|
createdAt: string;
|
|
58
|
+
/** Parent channel id when this is a branched side quest. */
|
|
59
|
+
parentId?: string;
|
|
60
|
+
/** Message id in the parent room that opened this branch. */
|
|
61
|
+
branchFromId?: string;
|
|
56
62
|
};
|
|
57
63
|
|
|
58
64
|
export type ChatPart =
|
|
@@ -114,6 +120,12 @@ export type ChatMessage = {
|
|
|
114
120
|
steer?: boolean;
|
|
115
121
|
/** Live bot this steer was aimed at. */
|
|
116
122
|
steerBotId?: string;
|
|
123
|
+
/**
|
|
124
|
+
* Bot ids this message dispatches.
|
|
125
|
+
* User @mentions and bot handoffs. Missing on older rows (parse the body).
|
|
126
|
+
* Empty array means nobody — do not fall back to parsing.
|
|
127
|
+
*/
|
|
128
|
+
mentions?: string[];
|
|
117
129
|
};
|
|
118
130
|
|
|
119
131
|
export type LlmApi =
|
|
@@ -141,7 +153,8 @@ export type AuxRole =
|
|
|
141
153
|
| "skills"
|
|
142
154
|
| "approval"
|
|
143
155
|
| "title"
|
|
144
|
-
| "generate"
|
|
156
|
+
| "generate"
|
|
157
|
+
| "spawn";
|
|
145
158
|
|
|
146
159
|
export type ModelsFile = {
|
|
147
160
|
default?: ModelRef | null;
|