@kevin5251984/guild 0.2.12 → 0.2.14
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 +10 -8
- package/src/catalog/subagents.ts +1 -2
- package/src/chat-parts.ts +1 -6
- package/src/generate.ts +27 -55
- package/src/handlers.ts +6 -183
- package/src/harness.ts +8 -21
- package/src/image-gen.ts +1 -2
- package/src/llm.ts +7 -14
- package/src/mention.ts +12 -63
- package/src/oauth.ts +27 -131
- package/src/public/chat.css +191 -143
- package/src/public/chat.html +49 -191
- package/src/public/i18n.js +6 -18
- package/src/public/index.html +46 -1
- package/src/public/settings.html +1 -2
- package/src/public/skills-add.html +0 -8
- package/src/public/studio.html +117 -171
- package/src/public/style.css +150 -260
- package/src/public/subagents-add.html +0 -8
- package/src/router.ts +0 -11
- package/src/store.ts +0 -14
- package/src/subagent.ts +6 -203
- package/src/tools.ts +18 -117
- package/src/trajectory.ts +5 -51
- package/src/usage.ts +0 -10
- package/vendor/protocol/src/index.ts +1 -4
- package/src/public/buddy.js +0 -432
package/src/store.ts
CHANGED
|
@@ -605,7 +605,6 @@ export class GuildStore {
|
|
|
605
605
|
name?: string;
|
|
606
606
|
handle?: string;
|
|
607
607
|
oneLiner?: string;
|
|
608
|
-
portrait?: string | null;
|
|
609
608
|
skillIds?: string[];
|
|
610
609
|
soul?: { name: string; body: string };
|
|
611
610
|
agent?: { name: string; body: string };
|
|
@@ -647,9 +646,6 @@ export class GuildStore {
|
|
|
647
646
|
handle,
|
|
648
647
|
skillIds,
|
|
649
648
|
oneLiner: input.oneLiner?.trim() || bot.oneLiner,
|
|
650
|
-
portrait: Object.hasOwn(input, "portrait")
|
|
651
|
-
? normalizePortrait(input.portrait)
|
|
652
|
-
: bot.portrait,
|
|
653
649
|
model: Object.hasOwn(input, "model") ? input.model ?? null : bot.model,
|
|
654
650
|
};
|
|
655
651
|
this.writeBot(next);
|
|
@@ -1173,16 +1169,6 @@ export class GuildStore {
|
|
|
1173
1169
|
}
|
|
1174
1170
|
}
|
|
1175
1171
|
|
|
1176
|
-
function normalizePortrait(raw: string | null | undefined): string | undefined {
|
|
1177
|
-
if (raw == null) return undefined;
|
|
1178
|
-
const value = raw.trim();
|
|
1179
|
-
if (!value) return undefined;
|
|
1180
|
-
if (!/^\/generated\/[A-Za-z0-9._-]+$/.test(value)) {
|
|
1181
|
-
throw new StoreError(400, "invalid portrait");
|
|
1182
|
-
}
|
|
1183
|
-
return value;
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
1172
|
export class StoreError extends Error {
|
|
1187
1173
|
constructor(
|
|
1188
1174
|
readonly status: number,
|
package/src/subagent.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import { parseAgentFile } from "./agent-file.ts";
|
|
3
2
|
import { listHostAgents, type HostAgent } from "./host-agents.ts";
|
|
4
3
|
import type { LibraryItem } from "@guild/protocol";
|
|
5
|
-
import { parseSandbox, type Sandbox } from "./harness.ts";
|
|
6
4
|
import {
|
|
7
5
|
hostContext,
|
|
8
6
|
type SubAgentRef,
|
|
@@ -72,18 +70,6 @@ function agentKey(value: string): string {
|
|
|
72
70
|
return value.trim().replace(/^\/+/, "").toLowerCase();
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
/** Parent read_only cannot escalate via spawn. Explorer from full_access keeps run. */
|
|
76
|
-
export function childSpawnPolicy(
|
|
77
|
-
parentSandbox: ToolContext["sandbox"],
|
|
78
|
-
agentReadOnly: boolean,
|
|
79
|
-
): { sandbox: Sandbox; allowWrite: boolean } {
|
|
80
|
-
const parent = parseSandbox(parentSandbox);
|
|
81
|
-
if (parent === "read_only") {
|
|
82
|
-
return { sandbox: "read_only", allowWrite: false };
|
|
83
|
-
}
|
|
84
|
-
return { sandbox: parent, allowWrite: !agentReadOnly };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
73
|
export function resolveSubagent(
|
|
88
74
|
name: string,
|
|
89
75
|
agents: SubAgentRef[],
|
|
@@ -103,192 +89,11 @@ export function resolveSubagent(
|
|
|
103
89
|
|
|
104
90
|
const CHILD_TOOLS = `You ARE already running on the user's local computer (Guild).
|
|
105
91
|
Tools: run, read, write, list, skill, image_gen. You cannot spawn subagents.
|
|
106
|
-
Never say you cannot access this machine. Check [exit code: N] on every run
|
|
107
|
-
Independent searches: emit multiple tool calls in one round; they run in parallel.`;
|
|
92
|
+
Never say you cannot access this machine. Check [exit code: N] on every run.`;
|
|
108
93
|
|
|
109
94
|
const CHILD_TOOLS_RO = `You ARE already running on the user's local computer (Guild).
|
|
110
95
|
Tools: run, read, list, skill. You cannot write files and cannot spawn subagents.
|
|
111
|
-
Read-only. Never edit, patch, or create files. Check [exit code: N] on every run
|
|
112
|
-
Independent searches: emit multiple tool calls in one round; they run in parallel.`;
|
|
113
|
-
|
|
114
|
-
export const SPAWN_MAX_PARALLEL = 8;
|
|
115
|
-
export const SPAWN_CONCURRENCY = 4;
|
|
116
|
-
|
|
117
|
-
export type SpawnJob = {
|
|
118
|
-
prompt: string;
|
|
119
|
-
name: string;
|
|
120
|
-
description: string;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
/** Devin luna-explore / Pi scout → Guild explorer. */
|
|
124
|
-
export function spawnProfile(raw: string): string {
|
|
125
|
-
const key = raw.trim().toLowerCase();
|
|
126
|
-
if (!key) return "";
|
|
127
|
-
if (key === "luna-explore" || key === "explore" || key === "scout") {
|
|
128
|
-
return "explorer";
|
|
129
|
-
}
|
|
130
|
-
if (key === "luna-general" || key === "general") return "worker";
|
|
131
|
-
if (key === "luna-reviewer") return "reviewer";
|
|
132
|
-
return raw.trim();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function recordOf(value: unknown): Record<string, unknown> | null {
|
|
136
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
137
|
-
return value as Record<string, unknown>;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function flagTrue(value: unknown): boolean {
|
|
141
|
-
return value === true || value === "true";
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function flagFalse(value: unknown): boolean {
|
|
145
|
-
return value === false || value === "false";
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function oneJob(raw: Record<string, unknown>): SpawnJob {
|
|
149
|
-
const prompt = String(raw.prompt || raw.task || "").trim();
|
|
150
|
-
const name = spawnProfile(
|
|
151
|
-
String(raw.profile || raw.name || raw.agent || raw.subagent_type || ""),
|
|
152
|
-
);
|
|
153
|
-
const description = String(raw.title || raw.description || "").trim();
|
|
154
|
-
return { prompt, name, description };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function handlesOf(ctx: ToolContext) {
|
|
158
|
-
if (!ctx.spawnHandles) ctx.spawnHandles = new Map();
|
|
159
|
-
return ctx.spawnHandles;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function startBackground(job: SpawnJob, ctx: ToolContext) {
|
|
163
|
-
const id = randomUUID();
|
|
164
|
-
const title = job.description || job.name || "worker";
|
|
165
|
-
const profile = job.name || "worker";
|
|
166
|
-
const handle: {
|
|
167
|
-
id: string;
|
|
168
|
-
title: string;
|
|
169
|
-
profile: string;
|
|
170
|
-
done: Promise<ToolOutcome>;
|
|
171
|
-
outcome?: ToolOutcome;
|
|
172
|
-
} = {
|
|
173
|
-
id,
|
|
174
|
-
title,
|
|
175
|
-
profile,
|
|
176
|
-
done: Promise.resolve({ text: "", isError: false }),
|
|
177
|
-
};
|
|
178
|
-
handle.done = spawnSubagent({ ...job, ctx }).then((outcome) => {
|
|
179
|
-
handle.outcome = outcome;
|
|
180
|
-
return outcome;
|
|
181
|
-
});
|
|
182
|
-
handlesOf(ctx).set(id, handle);
|
|
183
|
-
return handle;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function ackBackground(
|
|
187
|
-
rows: { id: string; title: string; profile: string }[],
|
|
188
|
-
): string {
|
|
189
|
-
return rows
|
|
190
|
-
.map(
|
|
191
|
-
(row) =>
|
|
192
|
-
`agent_id: ${row.id}\ntitle: ${row.title}\nprofile: ${row.profile}\nstatus: running\nCall read_spawn with this agent_id (block=true) before the final reply.`,
|
|
193
|
-
)
|
|
194
|
-
.join("\n\n");
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** Pi subagent: single {prompt|task, name|agent} or parallel tasks[]. */
|
|
198
|
-
export function spawnJobs(args: Record<string, unknown>): SpawnJob[] {
|
|
199
|
-
if (Array.isArray(args.tasks) && args.tasks.length) {
|
|
200
|
-
return args.tasks.map((item) => oneJob(recordOf(item) || {}));
|
|
201
|
-
}
|
|
202
|
-
return [oneJob(args)];
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async function mapWithConcurrency<T, R>(
|
|
206
|
-
items: T[],
|
|
207
|
-
concurrency: number,
|
|
208
|
-
fn: (item: T, index: number) => Promise<R>,
|
|
209
|
-
): Promise<R[]> {
|
|
210
|
-
if (!items.length) return [];
|
|
211
|
-
const limit = Math.max(1, Math.min(concurrency, items.length));
|
|
212
|
-
const out: R[] = new Array(items.length);
|
|
213
|
-
let next = 0;
|
|
214
|
-
await Promise.all(
|
|
215
|
-
Array.from({ length: limit }, async () => {
|
|
216
|
-
while (true) {
|
|
217
|
-
const i = next++;
|
|
218
|
-
if (i >= items.length) return;
|
|
219
|
-
out[i] = await fn(items[i], i);
|
|
220
|
-
}
|
|
221
|
-
}),
|
|
222
|
-
);
|
|
223
|
-
return out;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export async function runSpawnJobs(
|
|
227
|
-
args: Record<string, unknown>,
|
|
228
|
-
ctx: ToolContext,
|
|
229
|
-
): Promise<ToolOutcome> {
|
|
230
|
-
const jobs = spawnJobs(args);
|
|
231
|
-
if (!jobs.length || jobs.some((job) => !job.prompt)) {
|
|
232
|
-
return { text: "spawn needs a prompt or task", isError: true };
|
|
233
|
-
}
|
|
234
|
-
if (jobs.length > SPAWN_MAX_PARALLEL) {
|
|
235
|
-
return {
|
|
236
|
-
text: `Too many parallel tasks (${jobs.length}). Max is ${SPAWN_MAX_PARALLEL}.`,
|
|
237
|
-
isError: true,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
const background = flagTrue(args.background) || flagTrue(args.is_background);
|
|
241
|
-
if (background) {
|
|
242
|
-
const started = jobs.map((job) => startBackground(job, ctx));
|
|
243
|
-
return { text: ackBackground(started), isError: false };
|
|
244
|
-
}
|
|
245
|
-
if (jobs.length === 1) {
|
|
246
|
-
return spawnSubagent({ ...jobs[0], ctx });
|
|
247
|
-
}
|
|
248
|
-
const results = await mapWithConcurrency(jobs, SPAWN_CONCURRENCY, (job) =>
|
|
249
|
-
spawnSubagent({ ...job, ctx }),
|
|
250
|
-
);
|
|
251
|
-
const failed = results.filter((row) => row.isError).length;
|
|
252
|
-
const body = results
|
|
253
|
-
.map((row, i) => {
|
|
254
|
-
const label = jobs[i].description || jobs[i].name || "worker";
|
|
255
|
-
const status = row.isError ? "failed" : "completed";
|
|
256
|
-
return `### [${label}] ${status}\n\n${row.text}`;
|
|
257
|
-
})
|
|
258
|
-
.join("\n\n---\n\n");
|
|
259
|
-
return {
|
|
260
|
-
text: `Parallel: ${results.length - failed}/${results.length} succeeded\n\n${body}`,
|
|
261
|
-
isError: failed === results.length,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export async function readSpawn(
|
|
266
|
-
args: Record<string, unknown>,
|
|
267
|
-
ctx: ToolContext,
|
|
268
|
-
): Promise<ToolOutcome> {
|
|
269
|
-
const id = String(args.agent_id || args.id || "").trim();
|
|
270
|
-
if (!id) return { text: "read_spawn needs agent_id", isError: true };
|
|
271
|
-
const handle = handlesOf(ctx).get(id);
|
|
272
|
-
if (!handle) {
|
|
273
|
-
return {
|
|
274
|
-
text: `unknown agent_id ${id}. It must come from a background spawn in this turn.`,
|
|
275
|
-
isError: true,
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
if (flagFalse(args.block) && !handle.outcome) {
|
|
279
|
-
return {
|
|
280
|
-
text: `agent_id: ${handle.id}\ntitle: ${handle.title}\nprofile: ${handle.profile}\nstatus: running`,
|
|
281
|
-
isError: false,
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
const outcome = await handle.done;
|
|
285
|
-
return {
|
|
286
|
-
text: `# ${handle.title}\nagent_id: ${handle.id}\nprofile: ${handle.profile}\nstatus: ${
|
|
287
|
-
outcome.isError ? "failed" : "completed"
|
|
288
|
-
}\n\n${outcome.text}`,
|
|
289
|
-
isError: outcome.isError,
|
|
290
|
-
};
|
|
291
|
-
}
|
|
96
|
+
Read-only. Never edit, patch, or create files. Check [exit code: N] on every run.`;
|
|
292
97
|
|
|
293
98
|
export async function spawnSubagent(input: {
|
|
294
99
|
prompt: string;
|
|
@@ -310,13 +115,12 @@ export async function spawnSubagent(input: {
|
|
|
310
115
|
? input.ctx.subagents
|
|
311
116
|
: listSpawnRefs([]);
|
|
312
117
|
const agent = resolveSubagent(input.name || "worker", agents);
|
|
313
|
-
const child = childSpawnPolicy(input.ctx.sandbox, agent.readOnly);
|
|
314
118
|
const { llmComplete } = await import("./llm.ts");
|
|
315
119
|
const label = (input.description || agent.name).trim();
|
|
316
120
|
const system = [
|
|
317
121
|
agent.instructions,
|
|
318
122
|
hostContext(),
|
|
319
|
-
|
|
123
|
+
agent.readOnly ? CHILD_TOOLS_RO : CHILD_TOOLS,
|
|
320
124
|
]
|
|
321
125
|
.filter(Boolean)
|
|
322
126
|
.join("\n\n");
|
|
@@ -326,7 +130,7 @@ export async function spawnSubagent(input: {
|
|
|
326
130
|
system,
|
|
327
131
|
messages: [{ role: "user", content: prompt }],
|
|
328
132
|
temperature: 0.3,
|
|
329
|
-
role: "
|
|
133
|
+
role: "chat",
|
|
330
134
|
tools: true,
|
|
331
135
|
skills: input.ctx.skills,
|
|
332
136
|
toolCtx: {
|
|
@@ -335,11 +139,10 @@ export async function spawnSubagent(input: {
|
|
|
335
139
|
dataDir,
|
|
336
140
|
env: input.ctx.env,
|
|
337
141
|
spawnDepth: 1,
|
|
338
|
-
allowWrite:
|
|
339
|
-
sandbox:
|
|
142
|
+
allowWrite: !agent.readOnly,
|
|
143
|
+
sandbox: input.ctx.sandbox,
|
|
340
144
|
workspace: input.ctx.workspace,
|
|
341
145
|
dispatch: input.ctx.dispatch,
|
|
342
|
-
signal: input.ctx.signal,
|
|
343
146
|
},
|
|
344
147
|
});
|
|
345
148
|
if (!result) {
|
package/src/tools.ts
CHANGED
|
@@ -75,17 +75,6 @@ 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. */
|
|
79
|
-
spawnHandles?: Map<
|
|
80
|
-
string,
|
|
81
|
-
{
|
|
82
|
-
id: string;
|
|
83
|
-
title: string;
|
|
84
|
-
profile: string;
|
|
85
|
-
done: Promise<ToolOutcome>;
|
|
86
|
-
outcome?: ToolOutcome;
|
|
87
|
-
}
|
|
88
|
-
>;
|
|
89
78
|
};
|
|
90
79
|
|
|
91
80
|
const BASE_TOOLS: Tool[] = [
|
|
@@ -189,7 +178,7 @@ export function guildTools(
|
|
|
189
178
|
name: Type.String({ description: "Skill name or slug" }),
|
|
190
179
|
}),
|
|
191
180
|
});
|
|
192
|
-
if ((ctx.spawnDepth ?? 0) < 1) {
|
|
181
|
+
if ((ctx.spawnDepth ?? 0) < 1 && sandbox !== "read_only") {
|
|
193
182
|
const agents = ctx.subagents ?? [];
|
|
194
183
|
const listed = agents
|
|
195
184
|
.slice(0, 40)
|
|
@@ -201,28 +190,14 @@ export function guildTools(
|
|
|
201
190
|
const catalog = listed ? ` Available: ${listed}.` : "";
|
|
202
191
|
tools.push({
|
|
203
192
|
name: "spawn",
|
|
204
|
-
description: `
|
|
193
|
+
description: `Spawn a subagent with a fresh context. It returns a summary, not a transcript. Use for parallel exploration, review, or a bounded implementation slice.${catalog} Subagents cannot spawn children.`,
|
|
205
194
|
parameters: Type.Object({
|
|
206
|
-
prompt: Type.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}),
|
|
210
|
-
),
|
|
211
|
-
task: Type.Optional(
|
|
212
|
-
Type.String({ description: "Alias of prompt (Devin/Pi)" }),
|
|
213
|
-
),
|
|
195
|
+
prompt: Type.String({
|
|
196
|
+
description: "Self-contained task for the subagent. Include paths, constraints, and the deliverable.",
|
|
197
|
+
}),
|
|
214
198
|
name: Type.Optional(
|
|
215
199
|
Type.String({
|
|
216
|
-
description: "Subagent name or slug. Default worker.",
|
|
217
|
-
}),
|
|
218
|
-
),
|
|
219
|
-
agent: Type.Optional(
|
|
220
|
-
Type.String({ description: "Alias of name (Pi)" }),
|
|
221
|
-
),
|
|
222
|
-
profile: Type.Optional(
|
|
223
|
-
Type.String({
|
|
224
|
-
description:
|
|
225
|
-
"Devin profile: explorer | reviewer | worker. luna-explore → explorer, luna-general → worker.",
|
|
200
|
+
description: "Subagent name or slug from the library. Default worker.",
|
|
226
201
|
}),
|
|
227
202
|
),
|
|
228
203
|
description: Type.Optional(
|
|
@@ -230,51 +205,6 @@ export function guildTools(
|
|
|
230
205
|
description: "Short 3–8 word label for the chat UI",
|
|
231
206
|
}),
|
|
232
207
|
),
|
|
233
|
-
title: Type.Optional(
|
|
234
|
-
Type.String({ description: "Alias of description (Devin title)" }),
|
|
235
|
-
),
|
|
236
|
-
background: Type.Optional(
|
|
237
|
-
Type.Boolean({
|
|
238
|
-
description:
|
|
239
|
-
"If true, return agent_id immediately and keep working. Then call read_spawn.",
|
|
240
|
-
}),
|
|
241
|
-
),
|
|
242
|
-
is_background: Type.Optional(
|
|
243
|
-
Type.Boolean({ description: "Alias of background (Devin)" }),
|
|
244
|
-
),
|
|
245
|
-
tasks: Type.Optional(
|
|
246
|
-
Type.Array(
|
|
247
|
-
Type.Object({
|
|
248
|
-
prompt: Type.Optional(Type.String()),
|
|
249
|
-
task: Type.Optional(Type.String()),
|
|
250
|
-
name: Type.Optional(Type.String()),
|
|
251
|
-
agent: Type.Optional(Type.String()),
|
|
252
|
-
profile: Type.Optional(Type.String()),
|
|
253
|
-
description: Type.Optional(Type.String()),
|
|
254
|
-
title: Type.Optional(Type.String()),
|
|
255
|
-
}),
|
|
256
|
-
{
|
|
257
|
-
description:
|
|
258
|
-
"Pi parallel: run these subagents concurrently (max 8, 4 at a time).",
|
|
259
|
-
},
|
|
260
|
-
),
|
|
261
|
-
),
|
|
262
|
-
}),
|
|
263
|
-
});
|
|
264
|
-
tools.push({
|
|
265
|
-
name: "read_spawn",
|
|
266
|
-
description:
|
|
267
|
-
"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.",
|
|
268
|
-
parameters: Type.Object({
|
|
269
|
-
agent_id: Type.Optional(
|
|
270
|
-
Type.String({ description: "Id returned by background spawn" }),
|
|
271
|
-
),
|
|
272
|
-
id: Type.Optional(Type.String({ description: "Alias of agent_id" })),
|
|
273
|
-
block: Type.Optional(
|
|
274
|
-
Type.Boolean({
|
|
275
|
-
description: "Wait for the child. Default true.",
|
|
276
|
-
}),
|
|
277
|
-
),
|
|
278
208
|
}),
|
|
279
209
|
});
|
|
280
210
|
}
|
|
@@ -292,7 +222,7 @@ export const GUILD_TOOLS: Tool[] = guildTools();
|
|
|
292
222
|
|
|
293
223
|
function openaiParameters(name: string): {
|
|
294
224
|
type: "object";
|
|
295
|
-
properties: Record<string,
|
|
225
|
+
properties: Record<string, { type: "string"; description?: string }>;
|
|
296
226
|
required: string[];
|
|
297
227
|
} {
|
|
298
228
|
if (name === "run") {
|
|
@@ -327,42 +257,14 @@ function openaiParameters(name: string): {
|
|
|
327
257
|
};
|
|
328
258
|
}
|
|
329
259
|
if (name === "spawn") {
|
|
330
|
-
|
|
260
|
+
return {
|
|
331
261
|
type: "object",
|
|
332
262
|
properties: {
|
|
333
|
-
prompt: { type: "string", description: "Self-contained task
|
|
334
|
-
task: { type: "string", description: "Alias of prompt" },
|
|
263
|
+
prompt: { type: "string", description: "Self-contained task" },
|
|
335
264
|
name: { type: "string", description: "Subagent name or slug" },
|
|
336
|
-
agent: { type: "string", description: "Alias of name" },
|
|
337
|
-
profile: { type: "string", description: "explorer | reviewer | worker" },
|
|
338
265
|
description: { type: "string", description: "Short UI label" },
|
|
339
|
-
title: { type: "string", description: "Alias of description" },
|
|
340
|
-
},
|
|
341
|
-
};
|
|
342
|
-
return {
|
|
343
|
-
type: "object",
|
|
344
|
-
properties: {
|
|
345
|
-
...job.properties,
|
|
346
|
-
background: { type: "boolean", description: "Return agent_id immediately" },
|
|
347
|
-
is_background: { type: "boolean", description: "Alias of background" },
|
|
348
|
-
tasks: {
|
|
349
|
-
type: "array",
|
|
350
|
-
description: "Pi parallel: [{name, prompt}, ...] max 8, 4 at a time",
|
|
351
|
-
items: job,
|
|
352
|
-
},
|
|
353
266
|
},
|
|
354
|
-
required: [],
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
if (name === "read_spawn") {
|
|
358
|
-
return {
|
|
359
|
-
type: "object",
|
|
360
|
-
properties: {
|
|
361
|
-
agent_id: { type: "string", description: "Id from background spawn" },
|
|
362
|
-
id: { type: "string", description: "Alias of agent_id" },
|
|
363
|
-
block: { type: "boolean", description: "Wait. Default true." },
|
|
364
|
-
},
|
|
365
|
-
required: [],
|
|
267
|
+
required: ["prompt"],
|
|
366
268
|
};
|
|
367
269
|
}
|
|
368
270
|
if (name === "image_gen") {
|
|
@@ -425,7 +327,6 @@ export const BUILTIN_TOOL_NAMES = [
|
|
|
425
327
|
"list",
|
|
426
328
|
"skill",
|
|
427
329
|
"spawn",
|
|
428
|
-
"read_spawn",
|
|
429
330
|
"image_gen",
|
|
430
331
|
"browser",
|
|
431
332
|
] as const;
|
|
@@ -483,12 +384,13 @@ export async function builtinExecute(
|
|
|
483
384
|
if (name === "list") return listDir(asString(args.path), pathBase);
|
|
484
385
|
if (name === "skill") return loadSkill(asString(args.name), ctx.skills ?? []);
|
|
485
386
|
if (name === "spawn") {
|
|
486
|
-
const {
|
|
487
|
-
return
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
387
|
+
const { spawnSubagent } = await import("./subagent.ts");
|
|
388
|
+
return spawnSubagent({
|
|
389
|
+
prompt: asString(args.prompt),
|
|
390
|
+
name: typeof args.name === "string" ? args.name : "",
|
|
391
|
+
description: typeof args.description === "string" ? args.description : "",
|
|
392
|
+
ctx,
|
|
393
|
+
});
|
|
492
394
|
}
|
|
493
395
|
if (name === "image_gen") {
|
|
494
396
|
const { generateImage } = await import("./image-gen.ts");
|
|
@@ -811,8 +713,7 @@ Never say you cannot access this machine. Never tell the user to run the command
|
|
|
811
713
|
When the question is about this computer, call tools first, then answer with evidence from the output.
|
|
812
714
|
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.
|
|
813
715
|
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.
|
|
814
|
-
|
|
815
|
-
Independent tool calls in one round also run in parallel — fire several reads/searches together.
|
|
716
|
+
To delegate a bounded slice (explore, review, implement) to a specialist with its own context, call spawn. Pass a self-contained prompt. The subagent returns a summary. Subagents cannot spawn children.
|
|
816
717
|
Check the [exit code: N] marker on every run result; investigate failures before moving on. Prefer the workdir argument over cd.
|
|
817
718
|
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.
|
|
818
719
|
Prefer small commands. macOS RAM: sysctl hw.memsize ; memory_pressure. Disk: df -h.`;
|
package/src/trajectory.ts
CHANGED
|
@@ -38,35 +38,6 @@ 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" || /^\s*spawn(\s+spawn)?\s*$/i.test(event.summary || ""));
|
|
56
|
-
if (!looksSpawn) return event as T & { kind: TrajectoryKind; summary: string };
|
|
57
|
-
const args = recordOf(payload?.args) || payload || {};
|
|
58
|
-
const agent = String(args.name || "worker").trim() || "worker";
|
|
59
|
-
const label = String(args.description || "").replace(/\s+/g, " ").trim();
|
|
60
|
-
const prompt = String(args.prompt || "").replace(/\s+/g, " ").trim();
|
|
61
|
-
return {
|
|
62
|
-
...event,
|
|
63
|
-
kind: "spawn",
|
|
64
|
-
summary: clip(
|
|
65
|
-
`${agent}${label ? " · " + label : ""}${prompt ? " — " + prompt : ""}`,
|
|
66
|
-
),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
41
|
function cap(value: unknown): unknown {
|
|
71
42
|
if (typeof value === "string") {
|
|
72
43
|
return value.length > FIELD_CAP ? value.slice(0, FIELD_CAP) : value;
|
|
@@ -169,34 +140,17 @@ export function turnTrajectoryEvents(input: {
|
|
|
169
140
|
});
|
|
170
141
|
continue;
|
|
171
142
|
}
|
|
172
|
-
if (trace.name === "spawn"
|
|
173
|
-
const agent =
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
trace.args.name ||
|
|
177
|
-
trace.args.agent ||
|
|
178
|
-
"worker",
|
|
179
|
-
).trim() || "worker";
|
|
180
|
-
const label = String(trace.args.title || trace.args.description || "")
|
|
181
|
-
.replace(/\s+/g, " ")
|
|
182
|
-
.trim();
|
|
183
|
-
const prompt = String(trace.args.prompt || trace.args.task || "")
|
|
184
|
-
.replace(/\s+/g, " ")
|
|
185
|
-
.trim();
|
|
186
|
-
const bg =
|
|
187
|
-
trace.args.background === true || trace.args.is_background === true
|
|
188
|
-
? " (bg)"
|
|
189
|
-
: "";
|
|
190
|
-
const waitId = String(trace.args.agent_id || trace.args.id || "").trim();
|
|
143
|
+
if (trace.name === "spawn") {
|
|
144
|
+
const agent = String(trace.args.name || "worker").trim() || "worker";
|
|
145
|
+
const label = String(trace.args.description || "").replace(/\s+/g, " ").trim();
|
|
146
|
+
const prompt = String(trace.args.prompt || "").replace(/\s+/g, " ").trim();
|
|
191
147
|
events.push({
|
|
192
148
|
ts,
|
|
193
149
|
turnId,
|
|
194
150
|
botId,
|
|
195
151
|
kind: "spawn",
|
|
196
152
|
summary: clip(
|
|
197
|
-
trace.
|
|
198
|
-
? `read ${waitId || agent}${trace.running ? " …" : ""}`
|
|
199
|
-
: `${agent}${bg}${label ? " · " + label : ""}${prompt ? " — " + prompt : ""}${trace.running ? " …" : ""}`,
|
|
153
|
+
`${agent}${label ? " · " + label : ""}${prompt ? " — " + prompt : ""}${trace.running ? " …" : ""}`,
|
|
200
154
|
),
|
|
201
155
|
payload: cap(trace.args),
|
|
202
156
|
result: String(cap(trace.text ?? "")),
|
package/src/usage.ts
CHANGED
|
@@ -59,19 +59,13 @@ 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 };
|
|
64
62
|
} | null): ChatUsage {
|
|
65
63
|
if (!usage) return { rounds: 1 };
|
|
66
64
|
const input = num(usage.prompt_tokens);
|
|
67
65
|
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);
|
|
71
66
|
return {
|
|
72
67
|
input,
|
|
73
68
|
output,
|
|
74
|
-
cacheRead,
|
|
75
69
|
totalTokens: num(usage.total_tokens) || sum(input, output),
|
|
76
70
|
rounds: 1,
|
|
77
71
|
};
|
|
@@ -80,8 +74,6 @@ export function fromOpenAiUsage(usage?: {
|
|
|
80
74
|
export function fromAnthropicUsage(usage?: {
|
|
81
75
|
input_tokens?: number;
|
|
82
76
|
output_tokens?: number;
|
|
83
|
-
cache_read_input_tokens?: number;
|
|
84
|
-
cache_creation_input_tokens?: number;
|
|
85
77
|
} | null): ChatUsage {
|
|
86
78
|
if (!usage) return { rounds: 1 };
|
|
87
79
|
const input = num(usage.input_tokens);
|
|
@@ -89,8 +81,6 @@ export function fromAnthropicUsage(usage?: {
|
|
|
89
81
|
return {
|
|
90
82
|
input,
|
|
91
83
|
output,
|
|
92
|
-
cacheRead: num(usage.cache_read_input_tokens),
|
|
93
|
-
cacheWrite: num(usage.cache_creation_input_tokens),
|
|
94
84
|
totalTokens: sum(input, output),
|
|
95
85
|
rounds: 1,
|
|
96
86
|
};
|
|
@@ -37,8 +37,6 @@ 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;
|
|
42
40
|
/** Chat model for this bot. Missing → guild default. */
|
|
43
41
|
model?: ModelRef | null;
|
|
44
42
|
createdAt: string;
|
|
@@ -143,8 +141,7 @@ export type AuxRole =
|
|
|
143
141
|
| "skills"
|
|
144
142
|
| "approval"
|
|
145
143
|
| "title"
|
|
146
|
-
| "generate"
|
|
147
|
-
| "spawn";
|
|
144
|
+
| "generate";
|
|
148
145
|
|
|
149
146
|
export type ModelsFile = {
|
|
150
147
|
default?: ModelRef | null;
|