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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/src/acp/utils/tool-kind.ts +15 -0
  3. package/src/cli.ts +1 -0
  4. package/src/configure/app.tsx +75 -0
  5. package/src/configure/types.ts +1 -0
  6. package/src/core/agent/index.ts +38 -15
  7. package/src/core/agents/definitions.ts +46 -0
  8. package/src/core/agents/index.ts +15 -0
  9. package/src/core/agents/registry.ts +108 -0
  10. package/src/core/agents/runner.ts +375 -0
  11. package/src/core/agents/tools.ts +100 -0
  12. package/src/core/approvals/allowed-tools.ts +29 -4
  13. package/src/core/config.ts +54 -0
  14. package/src/core/index.ts +6 -1
  15. package/src/core/prompts/agents/plan.md +35 -0
  16. package/src/core/prompts/agents/research.md +32 -0
  17. package/src/core/prompts/environment.ts +62 -0
  18. package/src/core/prompts/harness/behaviour.md +28 -0
  19. package/src/core/prompts/harness/communication.md +31 -0
  20. package/src/core/prompts/harness/engineering.md +29 -0
  21. package/src/core/prompts/harness/execution.md +29 -0
  22. package/src/core/prompts/harness/guardrails.md +28 -0
  23. package/src/core/prompts/index.ts +7 -3
  24. package/src/core/prompts/static/daemon.md +22 -0
  25. package/src/core/prompts/static/environment.md +15 -0
  26. package/src/core/prompts/static/filesystem.md +2 -2
  27. package/src/core/prompts/static/ltm.md +6 -6
  28. package/src/core/prompts/static/shell.md +2 -2
  29. package/src/core/prompts/static/skills.md +1 -1
  30. package/src/core/prompts/static/sleep.md +20 -0
  31. package/src/core/prompts/static/subagents.md +28 -0
  32. package/src/core/prompts/static/thinking.md +2 -2
  33. package/src/core/prompts/static/todo.md +3 -3
  34. package/src/core/prompts/static/wiki.md +1 -1
  35. package/src/core/prompts/system.ts +53 -1
  36. package/src/core/tools/index.ts +1 -0
  37. 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
- "get_current_time",
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[] {
@@ -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(instructionsMdPath(), config);
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,