@newsails/veil-cli 1.0.1

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 (199) hide show
  1. package/.veil/agents/analyst/AGENT.md +21 -0
  2. package/.veil/agents/analyst/agent.json +23 -0
  3. package/.veil/agents/assistant/AGENT.md +15 -0
  4. package/.veil/agents/assistant/agent.json +19 -0
  5. package/.veil/agents/coder/AGENT.md +18 -0
  6. package/.veil/agents/coder/agent.json +19 -0
  7. package/.veil/agents/hello/AGENT.md +5 -0
  8. package/.veil/agents/hello/agent.json +13 -0
  9. package/.veil/agents/writer/AGENT.md +12 -0
  10. package/.veil/agents/writer/agent.json +17 -0
  11. package/.veil/memory/MEMORY.md +343 -0
  12. package/.veil/memory/agents/analyst/MEMORY.md +55 -0
  13. package/.veil/memory/agents/hello/MEMORY.md +12 -0
  14. package/.veil/runtime.pid +1 -0
  15. package/.veil/settings.json +10 -0
  16. package/.veil-studio/studio.db +0 -0
  17. package/.veil-studio/studio.db-shm +0 -0
  18. package/.veil-studio/studio.db-wal +0 -0
  19. package/PLAN/01-vision.md +26 -0
  20. package/PLAN/02-tech-stack.md +94 -0
  21. package/PLAN/03-agents.md +232 -0
  22. package/PLAN/04-runtime.md +171 -0
  23. package/PLAN/05-tools.md +211 -0
  24. package/PLAN/06-communication.md +243 -0
  25. package/PLAN/07-storage.md +218 -0
  26. package/PLAN/08-api-cli.md +153 -0
  27. package/PLAN/09-permissions.md +108 -0
  28. package/PLAN/10-ably.md +105 -0
  29. package/PLAN/11-file-formats.md +442 -0
  30. package/PLAN/12-folder-structure.md +205 -0
  31. package/PLAN/13-operations.md +212 -0
  32. package/PLAN/README.md +23 -0
  33. package/README.md +128 -0
  34. package/REPORT.md +174 -0
  35. package/TODO.md +45 -0
  36. package/ai-tests/FRONTEND_PROMPT.md +220 -0
  37. package/ai-tests/Research & Planning.md +814 -0
  38. package/ai-tests/prompt-001-basic-api.md +230 -0
  39. package/ai-tests/prompt-002-basic-flows.md +230 -0
  40. package/ai-tests/prompt-003-agent-behaviors.md +220 -0
  41. package/api/middleware.js +60 -0
  42. package/api/routes/agents.js +193 -0
  43. package/api/routes/chat.js +93 -0
  44. package/api/routes/completions.js +122 -0
  45. package/api/routes/daemons.js +80 -0
  46. package/api/routes/memory.js +169 -0
  47. package/api/routes/models.js +40 -0
  48. package/api/routes/remote-methods.js +74 -0
  49. package/api/routes/sessions.js +208 -0
  50. package/api/routes/settings.js +108 -0
  51. package/api/routes/system.js +50 -0
  52. package/api/routes/tasks.js +270 -0
  53. package/api/server.js +120 -0
  54. package/cli/formatter.js +70 -0
  55. package/cli/index.js +443 -0
  56. package/cli/parser.js +113 -0
  57. package/config/config.json +10 -0
  58. package/config/models.json +6826 -0
  59. package/core/agent.js +329 -0
  60. package/core/cancel.js +38 -0
  61. package/core/compaction.js +176 -0
  62. package/core/events.js +13 -0
  63. package/core/loop.js +564 -0
  64. package/core/memory.js +51 -0
  65. package/core/prompt.js +185 -0
  66. package/core/queue.js +96 -0
  67. package/core/registry.js +291 -0
  68. package/core/remote-methods.js +124 -0
  69. package/core/router.js +386 -0
  70. package/core/running-sessions.js +18 -0
  71. package/docs/api/01-system.md +84 -0
  72. package/docs/api/02-agents.md +374 -0
  73. package/docs/api/03-chat.md +269 -0
  74. package/docs/api/04-tasks.md +470 -0
  75. package/docs/api/05-sessions.md +444 -0
  76. package/docs/api/06-daemons.md +142 -0
  77. package/docs/api/07-memory.md +186 -0
  78. package/docs/api/08-settings.md +133 -0
  79. package/docs/api/09-models.md +119 -0
  80. package/docs/api/09-websocket.md +350 -0
  81. package/docs/api/10-completions.md +134 -0
  82. package/docs/api/README.md +116 -0
  83. package/docs/guide/01-quickstart.md +220 -0
  84. package/docs/guide/02-folder-structure.md +185 -0
  85. package/docs/guide/03-configuration.md +252 -0
  86. package/docs/guide/04-agents.md +267 -0
  87. package/docs/guide/05-cli.md +290 -0
  88. package/docs/guide/06-tools.md +643 -0
  89. package/docs/guide/07-permissions.md +236 -0
  90. package/docs/guide/08-memory.md +139 -0
  91. package/docs/guide/09-multi-agent.md +271 -0
  92. package/docs/guide/10-daemons.md +226 -0
  93. package/docs/guide/README.md +53 -0
  94. package/docs/index.html +623 -0
  95. package/examples/README.md +151 -0
  96. package/examples/agents/assistant/AGENT.md +31 -0
  97. package/examples/agents/assistant/SOUL.md +9 -0
  98. package/examples/agents/assistant/agent.json +74 -0
  99. package/examples/agents/hello/AGENT.md +15 -0
  100. package/examples/agents/hello/agent.json +14 -0
  101. package/examples/agents/monitor/AGENT.md +51 -0
  102. package/examples/agents/monitor/agent.json +33 -0
  103. package/examples/agents/monitor/heartbeats/monitor.md +24 -0
  104. package/examples/agents/orchestrator/AGENT.md +70 -0
  105. package/examples/agents/orchestrator/agent.json +30 -0
  106. package/examples/agents/researcher/AGENT.md +52 -0
  107. package/examples/agents/researcher/agent.json +49 -0
  108. package/examples/agents/researcher/skills/web-research.md +28 -0
  109. package/examples/skills/code-review.md +72 -0
  110. package/examples/skills/summarise.md +59 -0
  111. package/examples/skills/web-research.md +42 -0
  112. package/examples/tools/word-count/index.js +27 -0
  113. package/examples/tools/word-count/tool.json +18 -0
  114. package/infrastructure/database.js +563 -0
  115. package/infrastructure/scheduler.js +122 -0
  116. package/llm/client.js +206 -0
  117. package/migrations/001-initial.sql +121 -0
  118. package/migrations/002-debuggability.sql +13 -0
  119. package/migrations/003-drop-orphaned-columns.sql +72 -0
  120. package/migrations/004-session-message-token-fields.sql +78 -0
  121. package/migrations/005-session-thinking.sql +5 -0
  122. package/package.json +30 -0
  123. package/schemas/agent.json +143 -0
  124. package/schemas/settings.json +111 -0
  125. package/scripts/fetch-models.js +93 -0
  126. package/session-debug-scenario.md +248 -0
  127. package/settings/fields.js +52 -0
  128. package/system-prompts/base-core.md +7 -0
  129. package/system-prompts/environment.md +13 -0
  130. package/system-prompts/reminders/anti-drift.md +6 -0
  131. package/system-prompts/reminders/stall-recovery.md +10 -0
  132. package/system-prompts/safety-rules.md +25 -0
  133. package/system-prompts/task-heuristics.md +27 -0
  134. package/test/client.js +71 -0
  135. package/test/integration/01-health.test.js +25 -0
  136. package/test/integration/02-agents.test.js +80 -0
  137. package/test/integration/03-chat-hello.test.js +48 -0
  138. package/test/integration/04-chat-multiturn.test.js +61 -0
  139. package/test/integration/05-chat-writer.test.js +48 -0
  140. package/test/integration/06-task-basic.test.js +68 -0
  141. package/test/integration/07-task-tools.test.js +74 -0
  142. package/test/integration/08-task-code-analysis.test.js +69 -0
  143. package/test/integration/09-memory-analyst.test.js +63 -0
  144. package/test/integration/10-task-advanced.test.js +85 -0
  145. package/test/integration/11-sessions-advanced.test.js +84 -0
  146. package/test/integration/12-assistant-chat-tools.test.js +75 -0
  147. package/test/integration/13-edge-cases.test.js +99 -0
  148. package/test/integration/14-cancel.test.js +62 -0
  149. package/test/integration/15-debug.test.js +106 -0
  150. package/test/integration/16-memory-api.test.js +83 -0
  151. package/test/integration/17-settings-api.test.js +41 -0
  152. package/test/integration/18-tool-search-activation.test.js +119 -0
  153. package/test/results/.gitkeep +0 -0
  154. package/test/runner.js +206 -0
  155. package/test/smoke.js +216 -0
  156. package/tools/agent_message.js +85 -0
  157. package/tools/agent_send.js +80 -0
  158. package/tools/agent_spawn.js +44 -0
  159. package/tools/bash.js +49 -0
  160. package/tools/edit_file.js +41 -0
  161. package/tools/glob.js +64 -0
  162. package/tools/grep.js +82 -0
  163. package/tools/list_dir.js +63 -0
  164. package/tools/log_write.js +31 -0
  165. package/tools/memory_read.js +38 -0
  166. package/tools/memory_search.js +65 -0
  167. package/tools/memory_write.js +42 -0
  168. package/tools/read_file.js +48 -0
  169. package/tools/sleep.js +22 -0
  170. package/tools/task_create.js +41 -0
  171. package/tools/task_respond.js +37 -0
  172. package/tools/task_spawn.js +64 -0
  173. package/tools/task_status.js +39 -0
  174. package/tools/task_subscribe.js +37 -0
  175. package/tools/todo_read.js +26 -0
  176. package/tools/todo_write.js +38 -0
  177. package/tools/tool_activate.js +24 -0
  178. package/tools/tool_search.js +24 -0
  179. package/tools/web_fetch.js +50 -0
  180. package/tools/web_search.js +52 -0
  181. package/tools/write_file.js +28 -0
  182. package/ui/api.js +190 -0
  183. package/ui/app.js +281 -0
  184. package/ui/index.html +382 -0
  185. package/ui/views/agents.js +377 -0
  186. package/ui/views/chat.js +610 -0
  187. package/ui/views/connection.js +96 -0
  188. package/ui/views/daemons.js +129 -0
  189. package/ui/views/feed.js +194 -0
  190. package/ui/views/memory.js +263 -0
  191. package/ui/views/models.js +146 -0
  192. package/ui/views/sessions.js +314 -0
  193. package/ui/views/settings.js +142 -0
  194. package/ui/views/tasks.js +415 -0
  195. package/utils/context.js +49 -0
  196. package/utils/id.js +16 -0
  197. package/utils/models.js +88 -0
  198. package/utils/paths.js +213 -0
  199. package/utils/settings.js +172 -0
@@ -0,0 +1,236 @@
1
+ # Permissions
2
+
3
+ VeilCLI uses a layered allow/deny permission system to control which tools an agent can use in each mode.
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ When an agent tries to call a tool, the runtime checks a permission chain from most-specific to least-specific:
10
+
11
+ ```
12
+ agent.modes.<mode>.permissions (most specific — wins)
13
+
14
+ agent.modes.<mode>.tools / disallowedTools
15
+
16
+ settings.json permissions (project-wide default)
17
+
18
+ built-in defaults (deny everything not listed)
19
+ ```
20
+
21
+ The **first matching rule wins**.
22
+
23
+ ---
24
+
25
+ ## Permission Fields
26
+
27
+ ### `allow`
28
+
29
+ An array of tool names that are explicitly permitted. Use `["*"]` as a wildcard to allow all tools.
30
+
31
+ ```json
32
+ "permissions": {
33
+ "allow": ["read_file", "list_dir", "bash"]
34
+ }
35
+ ```
36
+
37
+ ### `deny`
38
+
39
+ An array of tool names that are explicitly blocked. **`deny` always wins over `allow`** — if a tool appears in both, it is denied.
40
+
41
+ ```json
42
+ "permissions": {
43
+ "allow": ["*"],
44
+ "deny": ["bash", "write_file"]
45
+ }
46
+ ```
47
+
48
+ ### `ask`
49
+
50
+ *(Settings-level only — not available in agent.json)*
51
+
52
+ Tools that require interactive human approval before executing. When a tool in the `ask` list is called, the task pauses (`status: "waiting"`) and the caller must use `POST /tasks/:id/respond` to approve and continue.
53
+
54
+ ```json
55
+ "permissions": {
56
+ "allow": ["*"],
57
+ "ask": ["write_file", "edit_file", "bash"]
58
+ }
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Where to Set Permissions
64
+
65
+ ### 1. Global default — `settings.json`
66
+
67
+ Applies to all agents and all modes unless overridden:
68
+
69
+ ```json
70
+ {
71
+ "permissions": {
72
+ "allow": ["read_file", "list_dir", "grep", "glob", "web_search"],
73
+ "deny": [],
74
+ "ask": ["bash", "write_file"]
75
+ }
76
+ }
77
+ ```
78
+
79
+ ### 2. Per-mode in `agent.json`
80
+
81
+ Overrides the global default for a specific mode of a specific agent:
82
+
83
+ ```json
84
+ {
85
+ "modes": {
86
+ "task": {
87
+ "enabled": true,
88
+ "permissions": {
89
+ "allow": ["read_file", "list_dir", "bash", "write_file"],
90
+ "deny": []
91
+ }
92
+ }
93
+ }
94
+ }
95
+ ```
96
+
97
+ ### 3. Tool whitelists and blacklists in `agent.json`
98
+
99
+ `tools` and `disallowedTools` provide an alternative syntax:
100
+
101
+ ```json
102
+ {
103
+ "modes": {
104
+ "task": {
105
+ "enabled": true,
106
+ "tools": ["read_file", "list_dir"],
107
+ "disallowedTools": ["bash"]
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ `tools` = controls which tools are shown to the LLM (tool list in the API call)
114
+ `disallowedTools` = hides tools from the LLM
115
+ `permissions.allow`/`deny` = controls which tools are allowed to *execute* at runtime (checked at call time, supports glob patterns like `"bash(rm *)"`)
116
+
117
+ You can use `tools`/`disallowedTools`, `permissions`, or both together.
118
+
119
+ ---
120
+
121
+ ## Common Patterns
122
+
123
+ ### Allow everything (development / trusted environment)
124
+
125
+ ```json
126
+ {
127
+ "permissions": {
128
+ "allow": ["*"],
129
+ "deny": [],
130
+ "ask": []
131
+ }
132
+ }
133
+ ```
134
+
135
+ ### Read-only agent (safe for untrusted tasks)
136
+
137
+ ```json
138
+ {
139
+ "modes": {
140
+ "task": {
141
+ "enabled": true,
142
+ "permissions": {
143
+ "allow": ["read_file", "list_dir", "grep", "glob", "web_search", "web_fetch"],
144
+ "deny": []
145
+ }
146
+ }
147
+ }
148
+ }
149
+ ```
150
+
151
+ ### Full access except destructive tools
152
+
153
+ ```json
154
+ {
155
+ "permissions": {
156
+ "allow": ["*"],
157
+ "deny": ["bash", "write_file", "edit_file"]
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### Require approval for write operations
163
+
164
+ ```json
165
+ {
166
+ "permissions": {
167
+ "allow": ["*"],
168
+ "deny": [],
169
+ "ask": ["write_file", "edit_file", "bash"]
170
+ }
171
+ }
172
+ ```
173
+
174
+ ### Orchestrator agent (multi-agent tools only)
175
+
176
+ ```json
177
+ {
178
+ "modes": {
179
+ "task": {
180
+ "enabled": true,
181
+ "permissions": {
182
+ "allow": ["agent_spawn", "task_create", "task_status", "task_subscribe", "log_write"],
183
+ "deny": []
184
+ }
185
+ }
186
+ }
187
+ }
188
+ ```
189
+
190
+ ---
191
+
192
+ ## Permission Denied Behaviour
193
+
194
+ When a tool is denied, the agent receives a tool result of:
195
+
196
+ ```
197
+ Permission denied for tool "bash"
198
+ ```
199
+
200
+ The agent loop continues — the LLM can choose to try another approach.
201
+
202
+ ---
203
+
204
+ ## Agent Restrictions
205
+
206
+ In addition to tool permissions, you can restrict which sub-agents an agent is allowed to spawn:
207
+
208
+ ```json
209
+ {
210
+ "modes": {
211
+ "task": {
212
+ "allowedAgents": ["coder", "writer"],
213
+ "disallowedAgents": ["admin"]
214
+ }
215
+ }
216
+ }
217
+ ```
218
+
219
+ `allowedAgents` — whitelist: only these agents can be spawned
220
+ `disallowedAgents` — blacklist: these agents are blocked
221
+
222
+ If both are empty, all agents are allowed (subject to the target agent's own `subagent` mode config).
223
+
224
+ ---
225
+
226
+ ## Quick Reference
227
+
228
+ | Field | Location | Description |
229
+ |-------|----------|-------------|
230
+ | `permissions.allow` | settings.json or agent.json modes | Tools allowed to execute at runtime (or `["*"]`). Supports glob patterns. |
231
+ | `permissions.deny` | settings.json or agent.json modes | Tools blocked from executing at runtime. Wins over allow. Supports glob patterns. |
232
+ | `permissions.ask` | **settings.json only** | Tools requiring human approval before executing |
233
+ | `tools` | agent.json modes | Tools shown to the LLM (whitelist — LLM cannot call tools not in this list) |
234
+ | `disallowedTools` | agent.json modes | Tools hidden from the LLM (blacklist) |
235
+ | `allowedAgents` | agent.json modes | Sub-agents this agent may spawn |
236
+ | `disallowedAgents` | agent.json modes | Sub-agents blocked from spawning |
@@ -0,0 +1,139 @@
1
+ # Memory & Context Compaction
2
+
3
+ VeilCLI provides two complementary systems for managing long-running agent knowledge: **persistent memory files** for cross-session recall, and **context compaction** for keeping LLM context windows healthy during long tasks.
4
+
5
+ ---
6
+
7
+ ## Persistent Memory
8
+
9
+ Memory files are plain Markdown files that persist across sessions and tasks. They are injected into the agent's system prompt at the start of each conversation.
10
+
11
+ ### Memory scopes
12
+
13
+ | Scope | File path | Who writes it | Who reads it |
14
+ |-------|-----------|--------------|--------------|
15
+ | `agent` | `.veil/memory/agents/<name>/MEMORY.md` | The agent itself | Only that agent |
16
+ | `global` | `.veil/memory/MEMORY.md` | Any agent | All agents with memory enabled |
17
+
18
+ ### Writing memory
19
+
20
+ Agents use the `memory_write` tool:
21
+
22
+ ```
23
+ "Remember that the project uses port 5051"
24
+ → memory_write({ content: "Project uses port 5051.", scope: "agent" })
25
+ ```
26
+
27
+ Each entry is appended with a date comment:
28
+ ```markdown
29
+ <!-- 2025-03-02 -->
30
+ Project uses port 5051.
31
+ ```
32
+
33
+ ### Reading memory
34
+
35
+ - **Automatic injection**: If `memory.enabled` is `true` in the agent config (or settings), the memory file is read and included in the system prompt at session start.
36
+ - **Manual read**: Use `memory_read` to explicitly read memory mid-task.
37
+ - **Search**: Use `memory_search` to find relevant entries by keyword.
38
+
39
+ ### Configuring memory
40
+
41
+ In `agent.json`:
42
+ ```json
43
+ {
44
+ "memory": {
45
+ "enabled": true,
46
+ "maxLines": 300
47
+ }
48
+ }
49
+ ```
50
+
51
+ In `settings.json` (applies globally to all agents):
52
+ ```json
53
+ {
54
+ "memory": {
55
+ "enabled": true,
56
+ "maxLines": 500
57
+ }
58
+ }
59
+ ```
60
+
61
+ `maxLines` limits how much of the memory file is injected. Oldest entries are truncated first. The full file is preserved on disk; only the injection is capped.
62
+
63
+ ### Memory best practices
64
+
65
+ - Write focused, factual entries — avoid vague notes like "talked about stuff"
66
+ - Use global memory for facts shared across agents (e.g. project conventions, API endpoints)
67
+ - Use agent memory for agent-specific state (e.g. "already processed file X")
68
+ - Periodically use `memory_search` before writing to avoid duplicates
69
+
70
+ ---
71
+
72
+ ## Context Compaction
73
+
74
+ LLM context windows are finite. During long tasks with many tool calls, the conversation history can exceed the model's context limit. VeilCLI manages this automatically through **context compaction**.
75
+
76
+ ### How it works
77
+
78
+ 1. **Observation masking**: Tool result messages older than `observationMaskingTurns` turns are replaced with `[observation masked]`. The tool call itself remains, but the full output is hidden. This is the first and cheapest form of compression.
79
+
80
+ 2. **Full compaction**: If the estimated token count exceeds `threshold × model_context_limit`, the runtime:
81
+ - Sends the current conversation to an LLM (using the `compact` model role if configured, otherwise `main`) with a special prompt asking for a structured summary
82
+ - Replaces the full message history with the summary plus the most recent turns
83
+ - Resumes the task with the compressed context
84
+
85
+ ### Configuration
86
+
87
+ In `settings.json`:
88
+ ```json
89
+ {
90
+ "compaction": {
91
+ "threshold": 0.8,
92
+ "observationMaskingTurns": 10
93
+ }
94
+ }
95
+ ```
96
+
97
+ | Field | Default | Description |
98
+ |-------|---------|-------------|
99
+ | `threshold` | `0.8` | Fraction of context window that triggers full compaction (0.1–1.0) |
100
+ | `observationMaskingTurns` | `10` | Keep tool results visible for N most recent turns; mask older ones |
101
+
102
+ ### Using a dedicated compact model
103
+
104
+ For cost efficiency, use a cheaper/faster model for compaction:
105
+
106
+ ```json
107
+ {
108
+ "models": {
109
+ "main": {
110
+ "base_url": "https://openrouter.ai/api/v1",
111
+ "api_key": "sk-or-v1-...",
112
+ "model": "anthropic/claude-3.5-sonnet"
113
+ },
114
+ "compact": {
115
+ "base_url": "https://openrouter.ai/api/v1",
116
+ "api_key": "sk-or-v1-...",
117
+ "model": "google/gemini-flash-1.5-8b"
118
+ }
119
+ }
120
+ }
121
+ ```
122
+
123
+ ### Memory extraction during compaction
124
+
125
+ Before compacting, VeilCLI extracts important facts from the conversation and writes them to the agent's memory file (if memory is enabled). This means the agent "remembers" key decisions even after the context is compressed.
126
+
127
+ ---
128
+
129
+ ## Memory vs Compaction
130
+
131
+ | Feature | Memory | Compaction |
132
+ |---------|--------|------------|
133
+ | **Scope** | Cross-session | Within a single session/task |
134
+ | **Trigger** | Agent explicitly calls `memory_write` | Automatic when context is full |
135
+ | **Storage** | Markdown files on disk | Replaced messages in the DB |
136
+ | **Purpose** | Long-term recall across runs | Keep long tasks running smoothly |
137
+ | **Controllable by agent** | Yes | No (automatic) |
138
+
139
+ Use **memory** to remember things between runs. Rely on **compaction** to handle long-running tasks gracefully without manual intervention.
@@ -0,0 +1,271 @@
1
+ # Multi-Agent Orchestration
2
+
3
+ VeilCLI supports multiple agents working together. An **orchestrator** agent delegates work to **worker** agents using spawn, messaging, and subscription tools.
4
+
5
+ ---
6
+
7
+ ## Core Tools
8
+
9
+ | Tool | Mode | Description |
10
+ |------|------|-------------|
11
+ | `agent_spawn` | Sync | Start a **chat session** with an agent — returns `{ sessionId, response }` |
12
+ | `task_spawn` | Sync or async | Spawn a **task** for an agent with an isolated context |
13
+ | `task_create` | Always async | Create a task for any agent (uses task mode) |
14
+ | `task_status` | — | Poll a task's current status and output |
15
+ | `task_subscribe` | — | Durable subscription to task completion |
16
+ | `agent_message` | Sync | Send a message to a **specific session**, wait for reply |
17
+ | `agent_send` | Fire-and-forget | Send a message to a **specific session** without waiting |
18
+
19
+ > **Key distinction:** `task_spawn` isolates the sub-agent in its own context (task mode). `agent_spawn` opens a conversational session you can continue with `agent_message`. Use `task_spawn` for heavy delegated work; use `agent_spawn` + `agent_message` for interactive back-and-forth.
20
+
21
+ ---
22
+
23
+ ## Pattern 1: Simple Delegation (Sync)
24
+
25
+ The simplest pattern — orchestrator spawns a task worker and waits for the result:
26
+
27
+ ```
28
+ Orchestrator
29
+ └► task_spawn(agent="coder", instruction="Analyse src/index.js", wait=true)
30
+ └► Coder runs in task mode, returns summary
31
+ ◄─── Result returned inline
32
+ ```
33
+
34
+ **Agent config** (`orchestrator/agent.json`):
35
+ ```json
36
+ {
37
+ "name": "orchestrator",
38
+ "modes": {
39
+ "task": {
40
+ "enabled": true,
41
+ "permissions": {
42
+ "allow": ["task_spawn", "task_status", "log_write"]
43
+ }
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ **How it works**: `task_spawn` with `wait: true` (default) blocks until the sub-agent finishes. The sub-agent runs in `subagent` mode — isolated from the orchestrator's context.
50
+
51
+ ---
52
+
53
+ ## Pattern 2: Parallel Fan-Out (Async)
54
+
55
+ Spawn multiple task workers simultaneously, then collect results:
56
+
57
+ ```
58
+ Orchestrator
59
+ ├► task_spawn(agent="researcher", instruction="Find X", wait=false) → taskId_1
60
+ ├► task_spawn(agent="researcher", instruction="Find Y", wait=false) → taskId_2
61
+ └► task_spawn(agent="researcher", instruction="Find Z", wait=false) → taskId_3
62
+
63
+ ├─ poll task_status(taskId_1) until finished
64
+ ├─ poll task_status(taskId_2) until finished
65
+ └─ poll task_status(taskId_3) until finished
66
+
67
+ └► Synthesise all results
68
+ ```
69
+
70
+ **Orchestrator AGENT.md instruction**:
71
+ ```markdown
72
+ When given a research topic with multiple subtopics:
73
+ 1. Use task_spawn with wait=false to spawn one researcher per subtopic
74
+ 2. Save all taskIds
75
+ 3. Poll each taskId with task_status until all are finished
76
+ 4. Synthesise all outputs into a final report
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Pattern 3: Durable Subscriptions
82
+
83
+ For long-running async tasks, use `task_subscribe` to get notified on completion even if the orchestrator's own session is interrupted:
84
+
85
+ ```
86
+ Orchestrator
87
+ └► task_create(agent="analyst", input="Long analysis job") → taskId
88
+ └► task_subscribe(taskId) ← stored in DB, survives restart
89
+
90
+ [... time passes, orchestrator may pause ...]
91
+
92
+ ◄─── Notification injected when analyst finishes
93
+ ```
94
+
95
+ `task_subscribe` writes to the `task_subscriptions` SQLite table. When the target task reaches a terminal state (`finished`/`failed`), the runtime injects a notification into the subscriber's message queue — even if the subscriber session had been paused.
96
+
97
+ **When to use**: Prefer `task_subscribe` over polling loops for very long tasks or when you need to save iterations.
98
+
99
+ ---
100
+
101
+ ## Pattern 4: Deep Research Pipeline
102
+
103
+ The full deep research pattern combining fan-out + subscriptions + synthesis:
104
+
105
+ ```
106
+ Research Orchestrator
107
+ ├─ Decompose topic into N sub-questions
108
+ ├─ Spawn N researchers async (wait=false) via task_spawn
109
+ ├─ Subscribe to all N tasks
110
+ ├─ Sleep or continue other work
111
+ ├─ Collect results as notifications arrive
112
+ └─ Call synthesis agent with all results
113
+ ```
114
+
115
+ **Orchestrator task flow** (pseudocode for AGENT.md):
116
+ ```markdown
117
+ 1. Decompose the research question into 3-5 subtopics
118
+ 2. For each subtopic:
119
+ a. Use task_spawn(wait=false) to spawn a "researcher" agent
120
+ b. Note the returned taskId
121
+ c. Use task_subscribe(taskId) for durable tracking
122
+ 3. Poll task_status for each taskId until all are finished or failed
123
+ 4. Collect all outputs
124
+ 5. Use task_spawn to call a "writer" agent with all collected research
125
+ 6. Return the final synthesised report
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Pattern 5: Hierarchical Delegation
131
+
132
+ Multi-level hierarchies — orchestrators can spawn sub-orchestrators:
133
+
134
+ ```
135
+ Top-level Orchestrator
136
+ ├─► Project Manager (sub-orchestrator)
137
+ │ ├─► Coder agent
138
+ │ └─► Reviewer agent
139
+ └─► Quality Checker
140
+ └─► Tester agent
141
+ ```
142
+
143
+ The `maxSubAgentDepth` setting (default: 5) limits how deep nesting can go, preventing infinite recursion.
144
+
145
+ ---
146
+
147
+ ## Pattern 6: Session-Based Messaging
148
+
149
+ For back-and-forth exchanges with an agent in the same conversation:
150
+
151
+ ```
152
+ Orchestrator
153
+ └► agent_spawn(agent="advisor", message="What should I prioritise?") → { sessionId, response }
154
+ └► agent_message(agent="advisor", sessionId=sessionId, message="What about task B?")
155
+ ◄─ "Task B should come second because..."
156
+ └► agent_send(agent="advisor", sessionId=sessionId, message="Thanks, moving on.")
157
+ ```
158
+
159
+ **`agent_spawn`** opens the session and gets the first response. Always required to obtain a `sessionId`.
160
+
161
+ **`agent_message`** is synchronous — it routes the message to the exact session and waits for a reply.
162
+ - If the session is idle → triggers a new turn directly (same as a user message)
163
+ - If the session is mid-processing → injects at the next tool-call checkpoint
164
+ - Automatically falls back if the message is missed at the last iteration
165
+
166
+ **`agent_send`** is fire-and-forget — useful for notifications or status updates without blocking the orchestrator.
167
+
168
+ Both tools require a `sessionId` (from `agent_spawn`) and validate that the `agent` name matches the session. Unknown agents or mismatched sessions return an immediate error instead of silently timing out.
169
+
170
+ ---
171
+
172
+ ## Sub-agent Configuration
173
+
174
+ Target agents need `subagent` mode enabled to be spawnable via `task_spawn`. If `subagent` mode is not configured, the runtime falls back to `task` mode.
175
+
176
+ For `agent_spawn` (chat session), agents need `chat` mode enabled.
177
+
178
+ ```json
179
+ {
180
+ "name": "researcher",
181
+ "modes": {
182
+ "chat": {
183
+ "enabled": true,
184
+ "maxIterations": 20
185
+ },
186
+ "subagent": {
187
+ "enabled": true,
188
+ "maxIterations": 15,
189
+ "maxDurationSeconds": 90,
190
+ "permissions": {
191
+ "allow": ["web_search", "web_fetch", "read_file", "memory_write"]
192
+ }
193
+ }
194
+ }
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Agent Restrictions
201
+
202
+ Orchestrators can whitelist which sub-agents they're allowed to spawn:
203
+
204
+ ```json
205
+ {
206
+ "modes": {
207
+ "task": {
208
+ "allowedAgents": ["researcher", "writer", "coder"],
209
+ "disallowedAgents": ["admin"]
210
+ }
211
+ }
212
+ }
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Depth Limit
218
+
219
+ Configure the maximum nesting depth in `settings.json`:
220
+
221
+ ```json
222
+ {
223
+ "maxSubAgentDepth": 3
224
+ }
225
+ ```
226
+
227
+ When an agent tries to spawn beyond this depth, the spawn is rejected with an error.
228
+
229
+ ---
230
+
231
+ ## Practical Example: Code Review Pipeline
232
+
233
+ **Agents**: `coordinator`, `coder`, `reviewer`
234
+
235
+ **Coordinator AGENT.md**:
236
+ ```markdown
237
+ You are a code review coordinator.
238
+
239
+ When given a directory to review:
240
+ 1. Use list_dir to find all .js files
241
+ 2. For each file, use task_spawn with wait=false to spawn a "coder" agent
242
+ 3. Subscribe to all tasks with task_subscribe
243
+ 4. Once all analyses are done, use task_spawn to call a "reviewer" agent with the combined analysis
244
+ 5. Return the final review report
245
+ ```
246
+
247
+ **Coder `agent.json`**:
248
+ ```json
249
+ {
250
+ "name": "coder",
251
+ "modes": {
252
+ "subagent": {
253
+ "enabled": true,
254
+ "permissions": { "allow": ["read_file", "grep", "glob"] }
255
+ }
256
+ }
257
+ }
258
+ ```
259
+
260
+ **Reviewer `agent.json`**:
261
+ ```json
262
+ {
263
+ "name": "reviewer",
264
+ "modes": {
265
+ "subagent": {
266
+ "enabled": true,
267
+ "permissions": { "allow": ["read_file", "memory_write"] }
268
+ }
269
+ }
270
+ }
271
+ ```