hoomanjs 1.14.0 → 1.16.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/package.json +1 -1
- package/src/acp/utils/tool-kind.ts +15 -0
- package/src/cli.ts +1 -0
- package/src/configure/app.tsx +75 -0
- package/src/configure/types.ts +1 -0
- package/src/core/agent/index.ts +38 -15
- package/src/core/agents/definitions.ts +46 -0
- package/src/core/agents/index.ts +15 -0
- package/src/core/agents/registry.ts +108 -0
- package/src/core/agents/runner.ts +375 -0
- package/src/core/agents/tools.ts +100 -0
- package/src/core/approvals/allowed-tools.ts +29 -4
- package/src/core/config.ts +54 -0
- package/src/core/index.ts +6 -1
- package/src/core/prompts/agents/plan.md +35 -0
- package/src/core/prompts/agents/research.md +32 -0
- package/src/core/prompts/environment.ts +62 -0
- package/src/core/prompts/harness/behaviour.md +28 -0
- package/src/core/prompts/harness/communication.md +31 -0
- package/src/core/prompts/harness/engineering.md +29 -0
- package/src/core/prompts/harness/execution.md +29 -0
- package/src/core/prompts/harness/guardrails.md +28 -0
- package/src/core/prompts/index.ts +7 -3
- package/src/core/prompts/static/daemon.md +22 -0
- package/src/core/prompts/static/environment.md +15 -0
- package/src/core/prompts/static/filesystem.md +2 -2
- package/src/core/prompts/static/ltm.md +6 -6
- package/src/core/prompts/static/shell.md +2 -2
- package/src/core/prompts/static/skills.md +1 -1
- package/src/core/prompts/static/sleep.md +20 -0
- package/src/core/prompts/static/subagents.md +28 -0
- package/src/core/prompts/static/thinking.md +2 -2
- package/src/core/prompts/static/todo.md +3 -3
- package/src/core/prompts/static/wiki.md +1 -1
- package/src/core/prompts/system.ts +53 -1
- package/src/core/tools/index.ts +1 -0
- package/src/core/tools/sleep.ts +51 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { Agent, TextBlock } from "@strands-agents/sdk";
|
|
2
|
+
import { Graph, Node, Status } from "@strands-agents/sdk/multiagent";
|
|
3
|
+
import type {
|
|
4
|
+
BaseModelConfig,
|
|
5
|
+
Model,
|
|
6
|
+
Tool,
|
|
7
|
+
ContentBlock,
|
|
8
|
+
} from "@strands-agents/sdk";
|
|
9
|
+
import type {
|
|
10
|
+
MultiAgentInput,
|
|
11
|
+
MultiAgentState,
|
|
12
|
+
MultiAgentStreamEvent,
|
|
13
|
+
NodeInputOptions,
|
|
14
|
+
NodeResultUpdate,
|
|
15
|
+
} from "@strands-agents/sdk/multiagent";
|
|
16
|
+
import type { AgentDefinition, AgentKind } from "./definitions.ts";
|
|
17
|
+
|
|
18
|
+
export type AgentJob = {
|
|
19
|
+
id: string;
|
|
20
|
+
kind: AgentKind;
|
|
21
|
+
description: string;
|
|
22
|
+
prompt: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type AgentJobResult = {
|
|
26
|
+
id: string;
|
|
27
|
+
kind: AgentKind;
|
|
28
|
+
description: string;
|
|
29
|
+
status: "completed" | "failed";
|
|
30
|
+
content: string;
|
|
31
|
+
durationMs: number;
|
|
32
|
+
error: string | null;
|
|
33
|
+
stopReason: string | null;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type RunAgentJobsResult = {
|
|
37
|
+
results: AgentJobResult[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type RunAgentJobsOptions = {
|
|
41
|
+
jobs: readonly AgentJob[];
|
|
42
|
+
definitions: readonly AgentDefinition[];
|
|
43
|
+
tools: readonly Tool[];
|
|
44
|
+
createModel: () => Model<BaseModelConfig>;
|
|
45
|
+
concurrency: number;
|
|
46
|
+
parent: string;
|
|
47
|
+
appState: {
|
|
48
|
+
userId?: string;
|
|
49
|
+
sessionId?: string;
|
|
50
|
+
};
|
|
51
|
+
cancelSignal?: AbortSignal;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
class JobNode extends Node {
|
|
55
|
+
override readonly type = "agentJobNode";
|
|
56
|
+
private readonly execute: () => Promise<AgentJobResult>;
|
|
57
|
+
private readonly cancelSignal?: AbortSignal;
|
|
58
|
+
|
|
59
|
+
constructor(
|
|
60
|
+
id: string,
|
|
61
|
+
execute: () => Promise<AgentJobResult>,
|
|
62
|
+
description: string,
|
|
63
|
+
cancelSignal?: AbortSignal,
|
|
64
|
+
) {
|
|
65
|
+
super(id, { description });
|
|
66
|
+
this.execute = execute;
|
|
67
|
+
this.cancelSignal = cancelSignal;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
override async *handle(
|
|
71
|
+
_input: MultiAgentInput,
|
|
72
|
+
_state: MultiAgentState,
|
|
73
|
+
_options?: NodeInputOptions,
|
|
74
|
+
): AsyncGenerator<MultiAgentStreamEvent, NodeResultUpdate, undefined> {
|
|
75
|
+
if (this.cancelSignal?.aborted) {
|
|
76
|
+
return {
|
|
77
|
+
status: Status.CANCELLED,
|
|
78
|
+
content: [new TextBlock("Cancelled before job execution.")],
|
|
79
|
+
error: new Error("Cancelled before job execution."),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const result = await this.execute();
|
|
83
|
+
if (result.status === "completed") {
|
|
84
|
+
const content = result.content.trim()
|
|
85
|
+
? [new TextBlock(result.content.trim())]
|
|
86
|
+
: [];
|
|
87
|
+
return {
|
|
88
|
+
status: Status.COMPLETED,
|
|
89
|
+
content,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
status:
|
|
94
|
+
result.stopReason === "cancelled" ? Status.CANCELLED : Status.FAILED,
|
|
95
|
+
content: result.error ? [new TextBlock(result.error)] : [],
|
|
96
|
+
error: result.error ? new Error(result.error) : undefined,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function buildJobPrompt(job: AgentJob): string {
|
|
102
|
+
return `Task: ${job.description}\n\nUser request:\n${job.prompt}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function selectTools(
|
|
106
|
+
definition: AgentDefinition,
|
|
107
|
+
tools: readonly Tool[],
|
|
108
|
+
): Tool[] {
|
|
109
|
+
const byName = new Map<string, Tool>();
|
|
110
|
+
for (const tool of tools) {
|
|
111
|
+
byName.set(tool.name, tool);
|
|
112
|
+
}
|
|
113
|
+
const selected: Tool[] = [];
|
|
114
|
+
for (const name of definition.tools) {
|
|
115
|
+
const tool = byName.get(name);
|
|
116
|
+
if (!tool) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Agent '${definition.id}' cannot access missing tool '${name}'.`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
selected.push(tool);
|
|
122
|
+
}
|
|
123
|
+
return selected;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function runSingleJob(
|
|
127
|
+
job: AgentJob,
|
|
128
|
+
definition: AgentDefinition,
|
|
129
|
+
options: Omit<RunAgentJobsOptions, "jobs" | "definitions" | "concurrency">,
|
|
130
|
+
): Promise<AgentJobResult> {
|
|
131
|
+
const started = Date.now();
|
|
132
|
+
if (options.cancelSignal?.aborted) {
|
|
133
|
+
return {
|
|
134
|
+
id: job.id,
|
|
135
|
+
kind: job.kind,
|
|
136
|
+
description: job.description,
|
|
137
|
+
status: "failed",
|
|
138
|
+
content: "",
|
|
139
|
+
durationMs: 0,
|
|
140
|
+
error: "Cancelled before execution.",
|
|
141
|
+
stopReason: "cancelled",
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const child = new Agent({
|
|
146
|
+
name: `${options.parent}-${definition.id}-${job.id}`,
|
|
147
|
+
systemPrompt: definition.instructionsText,
|
|
148
|
+
model: options.createModel(),
|
|
149
|
+
appState: {
|
|
150
|
+
...(options.appState.userId ? { userId: options.appState.userId } : {}),
|
|
151
|
+
...(options.appState.sessionId
|
|
152
|
+
? { sessionId: options.appState.sessionId }
|
|
153
|
+
: {}),
|
|
154
|
+
agentKind: definition.id,
|
|
155
|
+
},
|
|
156
|
+
tools: selectTools(definition, options.tools),
|
|
157
|
+
printer: false,
|
|
158
|
+
});
|
|
159
|
+
const result = await child.invoke(buildJobPrompt(job), {
|
|
160
|
+
...(options.cancelSignal ? { cancelSignal: options.cancelSignal } : {}),
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
id: job.id,
|
|
164
|
+
kind: job.kind,
|
|
165
|
+
description: job.description,
|
|
166
|
+
status: result.stopReason === "cancelled" ? "failed" : "completed",
|
|
167
|
+
content: result.toString().trim(),
|
|
168
|
+
durationMs: Date.now() - started,
|
|
169
|
+
error: result.stopReason === "cancelled" ? "Cancelled." : null,
|
|
170
|
+
stopReason: result.stopReason,
|
|
171
|
+
};
|
|
172
|
+
} catch (error) {
|
|
173
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
174
|
+
const cancelled = options.cancelSignal?.aborted ?? false;
|
|
175
|
+
return {
|
|
176
|
+
id: job.id,
|
|
177
|
+
kind: job.kind,
|
|
178
|
+
description: job.description,
|
|
179
|
+
status: "failed",
|
|
180
|
+
content: "",
|
|
181
|
+
durationMs: Date.now() - started,
|
|
182
|
+
error: message,
|
|
183
|
+
stopReason: cancelled ? "cancelled" : null,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function contentToText(content: readonly ContentBlock[]): string {
|
|
189
|
+
return content
|
|
190
|
+
.map((block) => {
|
|
191
|
+
if ("text" in block && typeof block.text === "string") {
|
|
192
|
+
return block.text;
|
|
193
|
+
}
|
|
194
|
+
return "";
|
|
195
|
+
})
|
|
196
|
+
.filter(Boolean)
|
|
197
|
+
.join("\n")
|
|
198
|
+
.trim();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function cancelledResult(job: AgentJob, message: string): AgentJobResult {
|
|
202
|
+
return {
|
|
203
|
+
id: job.id,
|
|
204
|
+
kind: job.kind,
|
|
205
|
+
description: job.description,
|
|
206
|
+
status: "failed",
|
|
207
|
+
content: "",
|
|
208
|
+
durationMs: 0,
|
|
209
|
+
error: message,
|
|
210
|
+
stopReason: "cancelled",
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export async function runAgentJobs(
|
|
215
|
+
options: RunAgentJobsOptions,
|
|
216
|
+
): Promise<RunAgentJobsResult> {
|
|
217
|
+
if (options.jobs.length === 0) {
|
|
218
|
+
return { results: [] };
|
|
219
|
+
}
|
|
220
|
+
if (options.cancelSignal?.aborted) {
|
|
221
|
+
return {
|
|
222
|
+
results: options.jobs.map((job) => ({
|
|
223
|
+
id: job.id,
|
|
224
|
+
kind: job.kind,
|
|
225
|
+
description: job.description,
|
|
226
|
+
status: "failed",
|
|
227
|
+
content: "",
|
|
228
|
+
durationMs: 0,
|
|
229
|
+
error: "Cancelled before execution.",
|
|
230
|
+
stopReason: "cancelled",
|
|
231
|
+
})),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const defsByKind = new Map<AgentKind, AgentDefinition>(
|
|
235
|
+
options.definitions.map((entry) => [entry.id, entry]),
|
|
236
|
+
);
|
|
237
|
+
const ordered = [...options.jobs];
|
|
238
|
+
const results: Array<AgentJobResult | null> = ordered.map(() => null);
|
|
239
|
+
const graphNodes: JobNode[] = [];
|
|
240
|
+
const graphSources: string[] = [];
|
|
241
|
+
const nodeToIndex = new Map<string, number>();
|
|
242
|
+
const graphNodeResults = new Map<string, AgentJobResult>();
|
|
243
|
+
|
|
244
|
+
for (const [index, job] of ordered.entries()) {
|
|
245
|
+
const definition = defsByKind.get(job.kind);
|
|
246
|
+
if (!definition) {
|
|
247
|
+
results[index] = {
|
|
248
|
+
id: job.id,
|
|
249
|
+
kind: job.kind,
|
|
250
|
+
description: job.description,
|
|
251
|
+
status: "failed",
|
|
252
|
+
content: "",
|
|
253
|
+
durationMs: 0,
|
|
254
|
+
error: `Unknown agent kind '${job.kind}'.`,
|
|
255
|
+
stopReason: null,
|
|
256
|
+
};
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const nodeId = `${job.id}__${index + 1}`;
|
|
260
|
+
nodeToIndex.set(nodeId, index);
|
|
261
|
+
graphSources.push(nodeId);
|
|
262
|
+
graphNodes.push(
|
|
263
|
+
new JobNode(
|
|
264
|
+
nodeId,
|
|
265
|
+
async () => {
|
|
266
|
+
const jobResult = await runSingleJob(job, definition, {
|
|
267
|
+
tools: options.tools,
|
|
268
|
+
createModel: options.createModel,
|
|
269
|
+
parent: options.parent,
|
|
270
|
+
appState: options.appState,
|
|
271
|
+
cancelSignal: options.cancelSignal,
|
|
272
|
+
});
|
|
273
|
+
graphNodeResults.set(nodeId, jobResult);
|
|
274
|
+
return jobResult;
|
|
275
|
+
},
|
|
276
|
+
`${job.kind} :: ${job.description}`,
|
|
277
|
+
options.cancelSignal,
|
|
278
|
+
),
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (graphNodes.length > 0) {
|
|
283
|
+
try {
|
|
284
|
+
const graph = new Graph({
|
|
285
|
+
id: "run_agents_graph",
|
|
286
|
+
nodes: graphNodes,
|
|
287
|
+
edges: [],
|
|
288
|
+
sources: graphSources,
|
|
289
|
+
maxConcurrency: Math.max(
|
|
290
|
+
1,
|
|
291
|
+
Math.min(options.concurrency, graphNodes.length),
|
|
292
|
+
),
|
|
293
|
+
});
|
|
294
|
+
const graphRun = graph.invoke("run jobs");
|
|
295
|
+
const graphResult = options.cancelSignal
|
|
296
|
+
? await Promise.race([
|
|
297
|
+
graphRun,
|
|
298
|
+
new Promise<null>((resolve) => {
|
|
299
|
+
const onAbort = () => resolve(null);
|
|
300
|
+
options.cancelSignal?.addEventListener("abort", onAbort, {
|
|
301
|
+
once: true,
|
|
302
|
+
});
|
|
303
|
+
}),
|
|
304
|
+
])
|
|
305
|
+
: await graphRun;
|
|
306
|
+
if (graphResult === null) {
|
|
307
|
+
for (const [nodeId, index] of nodeToIndex.entries()) {
|
|
308
|
+
if (results[index]) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const recorded = graphNodeResults.get(nodeId);
|
|
312
|
+
results[index] =
|
|
313
|
+
recorded ?? cancelledResult(ordered[index]!, "Cancelled.");
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
results: results.filter(
|
|
317
|
+
(entry): entry is AgentJobResult => entry !== null,
|
|
318
|
+
),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
const nodeResultsById = new Map(
|
|
322
|
+
graphResult.results.map((entry) => [entry.nodeId, entry] as const),
|
|
323
|
+
);
|
|
324
|
+
for (const [nodeId, index] of nodeToIndex.entries()) {
|
|
325
|
+
const recorded = graphNodeResults.get(nodeId);
|
|
326
|
+
if (recorded) {
|
|
327
|
+
results[index] = recorded;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const nodeResult = nodeResultsById.get(nodeId);
|
|
331
|
+
const job = ordered[index]!;
|
|
332
|
+
const cancelled = options.cancelSignal?.aborted ?? false;
|
|
333
|
+
results[index] = {
|
|
334
|
+
id: job.id,
|
|
335
|
+
kind: job.kind,
|
|
336
|
+
description: job.description,
|
|
337
|
+
status:
|
|
338
|
+
nodeResult?.status === Status.COMPLETED ? "completed" : "failed",
|
|
339
|
+
content: nodeResult ? contentToText(nodeResult.content) : "",
|
|
340
|
+
durationMs: nodeResult?.duration ?? 0,
|
|
341
|
+
error:
|
|
342
|
+
nodeResult?.error?.message ??
|
|
343
|
+
"Job did not produce a result from Graph execution.",
|
|
344
|
+
stopReason:
|
|
345
|
+
nodeResult?.status === Status.CANCELLED || cancelled
|
|
346
|
+
? "cancelled"
|
|
347
|
+
: null,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
} catch (error) {
|
|
351
|
+
const message =
|
|
352
|
+
error instanceof Error ? error.message : "Graph execution failed.";
|
|
353
|
+
for (const [nodeId, index] of nodeToIndex.entries()) {
|
|
354
|
+
if (results[index]) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const job = ordered[index]!;
|
|
358
|
+
results[index] = {
|
|
359
|
+
id: job.id,
|
|
360
|
+
kind: job.kind,
|
|
361
|
+
description: job.description,
|
|
362
|
+
status: "failed",
|
|
363
|
+
content: "",
|
|
364
|
+
durationMs: 0,
|
|
365
|
+
error: message,
|
|
366
|
+
stopReason: options.cancelSignal?.aborted ? "cancelled" : null,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
results: results.filter((entry): entry is AgentJobResult => entry !== null),
|
|
374
|
+
};
|
|
375
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
tool,
|
|
3
|
+
type JSONValue,
|
|
4
|
+
type Tool,
|
|
5
|
+
type ToolContext,
|
|
6
|
+
} from "@strands-agents/sdk";
|
|
7
|
+
import type { BaseModelConfig, Model } from "@strands-agents/sdk";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { BUILTIN_AGENT_KINDS, type AgentDefinition } from "./definitions.ts";
|
|
10
|
+
import { runAgentJobs } from "./runner.ts";
|
|
11
|
+
|
|
12
|
+
export const RUN_AGENTS_TOOL_NAME = "run_agents";
|
|
13
|
+
|
|
14
|
+
const JobSchema = z.object({
|
|
15
|
+
kind: z.enum(BUILTIN_AGENT_KINDS),
|
|
16
|
+
description: z.string().trim().min(1),
|
|
17
|
+
prompt: z.string().trim().min(1),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const RunAgentsInputSchema = z.object({
|
|
21
|
+
jobs: z.array(JobSchema).min(1),
|
|
22
|
+
maxConcurrency: z.coerce.number().int().min(1).optional(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
type RunAgentsToolOptions = {
|
|
26
|
+
parent: string;
|
|
27
|
+
definitions: readonly AgentDefinition[];
|
|
28
|
+
tools: readonly Tool[];
|
|
29
|
+
createModel: () => Model<BaseModelConfig>;
|
|
30
|
+
defaultConcurrency: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function toJsonValue(value: unknown): JSONValue {
|
|
34
|
+
return JSON.parse(JSON.stringify(value)) as JSONValue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readAppStateString(
|
|
38
|
+
context: ToolContext,
|
|
39
|
+
key: "userId" | "sessionId",
|
|
40
|
+
): string | undefined {
|
|
41
|
+
const value = context.agent.appState.get(key);
|
|
42
|
+
return typeof value === "string" && value.trim() ? value : undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function createRunAgentsTools(options: RunAgentsToolOptions) {
|
|
46
|
+
const kinds = options.definitions.map(
|
|
47
|
+
(entry) => `- ${entry.id}: ${entry.description}`,
|
|
48
|
+
);
|
|
49
|
+
const baseTools = options.tools.filter(
|
|
50
|
+
(entry) => entry.name !== RUN_AGENTS_TOOL_NAME,
|
|
51
|
+
);
|
|
52
|
+
return [
|
|
53
|
+
tool({
|
|
54
|
+
name: RUN_AGENTS_TOOL_NAME,
|
|
55
|
+
description: `Run one or more specialized child agents in parallel and return their outputs.
|
|
56
|
+
Use this for deeper research or planning work that can be split into independent jobs.
|
|
57
|
+
Available agent kinds:
|
|
58
|
+
${kinds.join("\n")}`,
|
|
59
|
+
inputSchema: RunAgentsInputSchema,
|
|
60
|
+
callback: async (input, context?: ToolContext) => {
|
|
61
|
+
if (!context) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`${RUN_AGENTS_TOOL_NAME} requires execution context.`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
const concurrency = Math.max(
|
|
67
|
+
1,
|
|
68
|
+
Math.min(
|
|
69
|
+
input.maxConcurrency ?? options.defaultConcurrency,
|
|
70
|
+
input.jobs.length,
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
const jobs = input.jobs.map((job, index) => ({
|
|
74
|
+
id: `job-${index + 1}`,
|
|
75
|
+
kind: job.kind,
|
|
76
|
+
description: job.description,
|
|
77
|
+
prompt: job.prompt,
|
|
78
|
+
}));
|
|
79
|
+
const result = await runAgentJobs({
|
|
80
|
+
jobs,
|
|
81
|
+
definitions: options.definitions,
|
|
82
|
+
tools: baseTools,
|
|
83
|
+
createModel: options.createModel,
|
|
84
|
+
concurrency,
|
|
85
|
+
parent: options.parent,
|
|
86
|
+
appState: {
|
|
87
|
+
userId: readAppStateString(context, "userId"),
|
|
88
|
+
sessionId: readAppStateString(context, "sessionId"),
|
|
89
|
+
},
|
|
90
|
+
cancelSignal: context.agent.cancelSignal,
|
|
91
|
+
});
|
|
92
|
+
return toJsonValue({
|
|
93
|
+
requestedJobs: jobs.length,
|
|
94
|
+
concurrency,
|
|
95
|
+
...result,
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
}),
|
|
99
|
+
];
|
|
100
|
+
}
|
|
@@ -10,17 +10,42 @@ type AgentLike = {
|
|
|
10
10
|
const SESSION_ALLOWED_TOOLS_KEY = "allowedTools";
|
|
11
11
|
|
|
12
12
|
export const INTERNAL_ALWAYS_ALLOWED = new Set([
|
|
13
|
+
// Strands / runtime
|
|
13
14
|
"strands_structured_output",
|
|
15
|
+
// Todos
|
|
14
16
|
"update_todos",
|
|
17
|
+
// Thinking
|
|
15
18
|
"think",
|
|
16
|
-
|
|
19
|
+
// Agent orchestration
|
|
20
|
+
"run_agents",
|
|
21
|
+
// Sleep
|
|
22
|
+
"sleep",
|
|
23
|
+
// Time
|
|
17
24
|
"convert_time",
|
|
25
|
+
"get_current_time",
|
|
26
|
+
// Wiki
|
|
27
|
+
"wiki_knowledge_graph",
|
|
18
28
|
"wiki_list_files",
|
|
19
29
|
"wiki_read_file",
|
|
20
|
-
"wiki_write_file",
|
|
21
|
-
"wiki_knowledge_graph",
|
|
22
|
-
"wiki_stats",
|
|
23
30
|
"wiki_search",
|
|
31
|
+
"wiki_stats",
|
|
32
|
+
"wiki_write_file",
|
|
33
|
+
// Long-term memory
|
|
34
|
+
"archive_memory",
|
|
35
|
+
"search_memory",
|
|
36
|
+
"store_memory",
|
|
37
|
+
"update_memory",
|
|
38
|
+
// Skills
|
|
39
|
+
"list_skills",
|
|
40
|
+
"search_skills",
|
|
41
|
+
// MCP config
|
|
42
|
+
"get_mcp_server",
|
|
43
|
+
"list_mcp_servers",
|
|
44
|
+
// Filesystem (list / search / metadata)
|
|
45
|
+
"directory_tree",
|
|
46
|
+
"get_file_info",
|
|
47
|
+
"list_directory",
|
|
48
|
+
"search_files",
|
|
24
49
|
]);
|
|
25
50
|
|
|
26
51
|
function normalizeAllowedTools(value: unknown): string[] {
|
package/src/core/config.ts
CHANGED
|
@@ -29,6 +29,14 @@ const CompactionPartialSchema = z.object({
|
|
|
29
29
|
|
|
30
30
|
const DEFAULT_COMPACTION = { ratio: 0.75, keep: 5 } as const;
|
|
31
31
|
|
|
32
|
+
const DEFAULT_PROMPTS = {
|
|
33
|
+
behaviour: true,
|
|
34
|
+
communication: true,
|
|
35
|
+
execution: true,
|
|
36
|
+
engineering: false,
|
|
37
|
+
guardrails: true,
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
32
40
|
const DEFAULT_CHROMA = {
|
|
33
41
|
url: "http://127.0.0.1:8000",
|
|
34
42
|
collection: { memory: "memory" },
|
|
@@ -71,21 +79,37 @@ const ToolTogglePartialSchema = z.object({
|
|
|
71
79
|
enabled: z.boolean().optional(),
|
|
72
80
|
});
|
|
73
81
|
|
|
82
|
+
const PromptsPartialSchema = z.object({
|
|
83
|
+
behaviour: z.boolean().optional(),
|
|
84
|
+
communication: z.boolean().optional(),
|
|
85
|
+
execution: z.boolean().optional(),
|
|
86
|
+
engineering: z.boolean().optional(),
|
|
87
|
+
guardrails: z.boolean().optional(),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const AgentsPartialSchema = z.object({
|
|
91
|
+
enabled: z.boolean().optional(),
|
|
92
|
+
concurrency: z.number().int().min(1).optional(),
|
|
93
|
+
});
|
|
94
|
+
|
|
74
95
|
const ToolsPartialSchema = z.object({
|
|
75
96
|
todo: ToolTogglePartialSchema.optional(),
|
|
76
97
|
fetch: ToolTogglePartialSchema.optional(),
|
|
77
98
|
filesystem: ToolTogglePartialSchema.optional(),
|
|
78
99
|
shell: ToolTogglePartialSchema.optional(),
|
|
100
|
+
sleep: ToolTogglePartialSchema.optional(),
|
|
79
101
|
ltm: LtmPartialSchema.optional(),
|
|
80
102
|
wiki: WikiPartialSchema.optional(),
|
|
81
103
|
mcp: ToolTogglePartialSchema.optional(),
|
|
82
104
|
skills: ToolTogglePartialSchema.optional(),
|
|
105
|
+
agents: AgentsPartialSchema.optional(),
|
|
83
106
|
});
|
|
84
107
|
|
|
85
108
|
const ConfigSchema = z
|
|
86
109
|
.object({
|
|
87
110
|
name: z.string().min(1),
|
|
88
111
|
llm: LlmSchema,
|
|
112
|
+
prompts: PromptsPartialSchema.nullish(),
|
|
89
113
|
tools: ToolsPartialSchema.nullish(),
|
|
90
114
|
compaction: CompactionPartialSchema.nullish().transform((c) => ({
|
|
91
115
|
ratio: c?.ratio ?? DEFAULT_COMPACTION.ratio,
|
|
@@ -98,6 +122,14 @@ const ConfigSchema = z
|
|
|
98
122
|
return {
|
|
99
123
|
name: input.name,
|
|
100
124
|
llm: input.llm,
|
|
125
|
+
prompts: {
|
|
126
|
+
behaviour: input.prompts?.behaviour ?? DEFAULT_PROMPTS.behaviour,
|
|
127
|
+
communication:
|
|
128
|
+
input.prompts?.communication ?? DEFAULT_PROMPTS.communication,
|
|
129
|
+
execution: input.prompts?.execution ?? DEFAULT_PROMPTS.execution,
|
|
130
|
+
engineering: input.prompts?.engineering ?? DEFAULT_PROMPTS.engineering,
|
|
131
|
+
guardrails: input.prompts?.guardrails ?? DEFAULT_PROMPTS.guardrails,
|
|
132
|
+
},
|
|
101
133
|
tools: {
|
|
102
134
|
todo: {
|
|
103
135
|
enabled: input.tools?.todo?.enabled ?? true,
|
|
@@ -111,6 +143,9 @@ const ConfigSchema = z
|
|
|
111
143
|
shell: {
|
|
112
144
|
enabled: input.tools?.shell?.enabled ?? true,
|
|
113
145
|
},
|
|
146
|
+
sleep: {
|
|
147
|
+
enabled: input.tools?.sleep?.enabled ?? true,
|
|
148
|
+
},
|
|
114
149
|
ltm: {
|
|
115
150
|
enabled: ltm?.enabled ?? false,
|
|
116
151
|
chroma: {
|
|
@@ -139,6 +174,10 @@ const ConfigSchema = z
|
|
|
139
174
|
skills: {
|
|
140
175
|
enabled: input.tools?.skills?.enabled ?? false,
|
|
141
176
|
},
|
|
177
|
+
agents: {
|
|
178
|
+
enabled: input.tools?.agents?.enabled ?? true,
|
|
179
|
+
concurrency: input.tools?.agents?.concurrency ?? 3,
|
|
180
|
+
},
|
|
142
181
|
},
|
|
143
182
|
compaction: input.compaction,
|
|
144
183
|
};
|
|
@@ -147,6 +186,7 @@ const ConfigSchema = z
|
|
|
147
186
|
export type ConfigData = z.infer<typeof ConfigSchema>;
|
|
148
187
|
export type LlmConfig = z.infer<typeof LlmSchema>;
|
|
149
188
|
export type CompactionConfig = ConfigData["compaction"];
|
|
189
|
+
export type PromptsConfig = ConfigData["prompts"];
|
|
150
190
|
export type LtmConfig = ConfigData["tools"]["ltm"];
|
|
151
191
|
export type WikiConfig = ConfigData["tools"]["wiki"];
|
|
152
192
|
export type ToolsConfig = ConfigData["tools"];
|
|
@@ -158,6 +198,7 @@ const defaultConfigData = (): ConfigData => ({
|
|
|
158
198
|
model: "gemma4:e4b",
|
|
159
199
|
params: {},
|
|
160
200
|
},
|
|
201
|
+
prompts: { ...DEFAULT_PROMPTS },
|
|
161
202
|
tools: {
|
|
162
203
|
todo: {
|
|
163
204
|
enabled: true,
|
|
@@ -171,6 +212,9 @@ const defaultConfigData = (): ConfigData => ({
|
|
|
171
212
|
shell: {
|
|
172
213
|
enabled: true,
|
|
173
214
|
},
|
|
215
|
+
sleep: {
|
|
216
|
+
enabled: true,
|
|
217
|
+
},
|
|
174
218
|
ltm: {
|
|
175
219
|
enabled: false,
|
|
176
220
|
chroma: {
|
|
@@ -191,6 +235,10 @@ const defaultConfigData = (): ConfigData => ({
|
|
|
191
235
|
skills: {
|
|
192
236
|
enabled: false,
|
|
193
237
|
},
|
|
238
|
+
agents: {
|
|
239
|
+
enabled: true,
|
|
240
|
+
concurrency: 2,
|
|
241
|
+
},
|
|
194
242
|
},
|
|
195
243
|
compaction: {
|
|
196
244
|
ratio: 0.75,
|
|
@@ -215,6 +263,10 @@ export class Config {
|
|
|
215
263
|
return this.data.llm;
|
|
216
264
|
}
|
|
217
265
|
|
|
266
|
+
get prompts(): PromptsConfig {
|
|
267
|
+
return { ...this.data.prompts };
|
|
268
|
+
}
|
|
269
|
+
|
|
218
270
|
get tools(): ToolsConfig {
|
|
219
271
|
return {
|
|
220
272
|
...this.data.tools,
|
|
@@ -222,6 +274,7 @@ export class Config {
|
|
|
222
274
|
fetch: { ...this.data.tools.fetch },
|
|
223
275
|
filesystem: { ...this.data.tools.filesystem },
|
|
224
276
|
shell: { ...this.data.tools.shell },
|
|
277
|
+
sleep: { ...this.data.tools.sleep },
|
|
225
278
|
ltm: {
|
|
226
279
|
...this.data.tools.ltm,
|
|
227
280
|
chroma: {
|
|
@@ -238,6 +291,7 @@ export class Config {
|
|
|
238
291
|
},
|
|
239
292
|
mcp: { ...this.data.tools.mcp },
|
|
240
293
|
skills: { ...this.data.tools.skills },
|
|
294
|
+
agents: { ...this.data.tools.agents },
|
|
241
295
|
};
|
|
242
296
|
}
|
|
243
297
|
|
package/src/core/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
export type BootstrapMeta = {
|
|
22
22
|
userId?: string;
|
|
23
23
|
sessionId?: string;
|
|
24
|
+
mode?: "default" | "daemon";
|
|
24
25
|
acp?: AcpMeta;
|
|
25
26
|
};
|
|
26
27
|
|
|
@@ -47,7 +48,11 @@ export async function bootstrap(
|
|
|
47
48
|
);
|
|
48
49
|
const mcp = { config: mcpConfig, manager: mcpManager };
|
|
49
50
|
const registry = createSkillsRegistry(basePath());
|
|
50
|
-
const system = await createSystemPrompt(
|
|
51
|
+
const system = await createSystemPrompt(
|
|
52
|
+
instructionsMdPath(),
|
|
53
|
+
config,
|
|
54
|
+
meta.mode ?? "default",
|
|
55
|
+
);
|
|
51
56
|
const agent = await createAgent(config, system, registry, mcp, print, {
|
|
52
57
|
userId: meta?.userId ?? meta?.sessionId,
|
|
53
58
|
sessionId: meta?.sessionId,
|