@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,442 @@
1
+ # 11 — File Formats
2
+
3
+ ## agent.json
4
+
5
+ Agent configuration. Defines modes, model, tools, permissions.
6
+
7
+ > **Critical field types — common mistakes:**
8
+ > - `model` must be a **string** (e.g. `"moonshotai/kimi-k2.5"`), NOT an object like `{ "role": "main" }`
9
+ > - Each `modes.*` entry must be an **object** with at minimum `{ "enabled": boolean }`, NOT a bare boolean like `true`
10
+ > - Both of these were real bugs found during testing that caused every agent to fail validation
11
+
12
+ ```json
13
+ {
14
+ "name": "researcher",
15
+ "description": "Deep research agent that investigates topics",
16
+ "model": "anthropic/claude-3.5-sonnet",
17
+ "temperature": 0.7,
18
+ "reasoning": "medium",
19
+ "memory": { "enabled": true, "maxLines": 200 },
20
+ "skillDiscovery": true,
21
+ "modes": {
22
+ "chat": {
23
+ "enabled": true,
24
+ "tools": [],
25
+ "disallowedTools": [],
26
+ "skills": [],
27
+ "autoLoadSkills": [],
28
+ "mcpServers": [],
29
+ "permissions": {}
30
+ },
31
+ "task": {
32
+ "enabled": true,
33
+ "tools": [],
34
+ "disallowedTools": ["bash"],
35
+ "skills": [],
36
+ "autoLoadSkills": [],
37
+ "mcpServers": [],
38
+ "maxIterations": 50,
39
+ "maxDurationSeconds": 300,
40
+ "permissions": {}
41
+ },
42
+ "subagent": {
43
+ "enabled": true,
44
+ "tools": ["web_search", "read_file", "grep"],
45
+ "disallowedTools": [],
46
+ "skills": ["summarize"],
47
+ "autoLoadSkills": [],
48
+ "mcpServers": [],
49
+ "allowedAgents": [],
50
+ "disallowedAgents": [],
51
+ "maxIterations": 20,
52
+ "maxDurationSeconds": 120,
53
+ "permissions": {}
54
+ },
55
+ "daemon": {
56
+ "enabled": false,
57
+ "cron": "*/5 * * * *",
58
+ "triggers": [],
59
+ "conflictPolicy": "skip",
60
+ "alertRouting": ["log", "webhook:https://slack.com/webhook/xxx"],
61
+ "heartbeatFile": "",
62
+ "tools": [],
63
+ "disallowedTools": [],
64
+ "skills": [],
65
+ "autoLoadSkills": [],
66
+ "mcpServers": [],
67
+ "permissions": {}
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ | Field | Type | Description |
74
+ |-------|------|-------------|
75
+ | `name` | string | Agent identifier (overrides folder name) |
76
+ | `description` | string | Human-readable description |
77
+ | `model` | string | Default model for all modes |
78
+ | `temperature` | number | 0.0–1.0 |
79
+ | `reasoning` | string | Enable reasoning mode for this agent (default: "medium"). Options: "low", "medium", "high" |
80
+ | `memory.enabled` | boolean | Enable persistent memory for this agent (default: true) |
81
+ | `memory.maxLines` | number | MEMORY.md line cap before auto-export (default: 200 from settings.json) |
82
+ | `skillDiscovery` | boolean | List skill names in system prompt (default: true). When false, agent is skill-blind. |
83
+ | `modes.chat.enabled` | boolean | Whether chat mode is supported |
84
+ | `modes.task.enabled` | boolean | Whether task mode is supported |
85
+ | `modes.task.maxIterations` | number | Override global limit |
86
+ | `modes.task.maxDurationSeconds` | number | Override global limit |
87
+ | `modes.subagent.enabled` | boolean | Whether sub-agent mode is supported |
88
+ | `modes.subagent.maxIterations` | number | Limit when called as sub-agent |
89
+ | `modes.subagent.maxDurationSeconds` | number | Limit when called as sub-agent |
90
+ | `modes.subagent.allowedAgents` | string[] | Agents this sub-agent can spawn/message (empty = all) |
91
+ | `modes.subagent.disallowedAgents` | string[] | Agents this sub-agent cannot spawn/message |
92
+ | `modes.daemon.enabled` | boolean | Whether daemon mode is supported |
93
+ | `modes.daemon.cron` | string | Cron expression for scheduling |
94
+ | `modes.daemon.triggers` | string[] | System-wide event triggers: `task.completed`, `task.failed`, `agent.error`. See **Daemon Trigger Payload** below. |
95
+ | `modes.daemon.conflictPolicy` | string | skip / queue / restart |
96
+ | `modes.daemon.alertRouting` | string \| string[] | Array of routing targets: `"log"`, `"webhook:<url>"`, `"agent:<name>"`, `"ably:<channel>"`. Single string also accepted. |
97
+ | `modes.daemon.heartbeatFile` | string | Custom path to heartbeat file (default: `.veil/heartbeats/<name>.md`) |
98
+ | `modes.*.tools` | string[] | Tool whitelist (empty = all) |
99
+ | `modes.*.disallowedTools` | string[] | Tool blacklist (mutually exclusive with `tools`) |
100
+ | `modes.*.skills` | string[] | Skill whitelist (empty = all) |
101
+ | `modes.*.autoLoadSkills` | string[] | Skills always loaded in full |
102
+ | `modes.*.mcpServers` | string[] | MCP server whitelist (empty = all configured) |
103
+ | `modes.*.permissions` | object | `{ allow: [], deny: [] }` per-mode overrides |
104
+
105
+ **Minimal valid example** (guaranteed to pass schema validation):
106
+ ```json
107
+ {
108
+ "name": "hello",
109
+ "description": "A minimal reference agent",
110
+ "model": "anthropic/claude-3-haiku",
111
+ "temperature": 0.5,
112
+ "reasoning": null,
113
+ "modes": {
114
+ "chat": { "enabled": true },
115
+ "task": { "enabled": false },
116
+ "subagent": { "enabled": false },
117
+ "daemon": { "enabled": false }
118
+ }
119
+ }
120
+ ```
121
+
122
+ This example is the basis for `examples/agents/hello/agent.json` in the source tree — it is validated in CI and used as the reference for documentation.
123
+
124
+ ---
125
+
126
+ ## AGENT.md
127
+
128
+ Plain markdown. Agent instructions and system prompt content. No configuration — that's in agent.json.
129
+
130
+ ```markdown
131
+ You are a research agent. Your job is to investigate topics thoroughly
132
+ and produce detailed, well-sourced reports.
133
+
134
+ ## Rules
135
+ - Always cite your sources
136
+ - Prefer primary sources over summaries
137
+ - If you can't find reliable information, say so
138
+ ```
139
+
140
+ ---
141
+
142
+ ## SOUL.md
143
+
144
+ Optional. Plain markdown. Persona and behavioral identity. Loaded after AGENT.md.
145
+
146
+ ```markdown
147
+ You speak in a concise, professional tone.
148
+ You never apologize unnecessarily.
149
+ You prefer bullet points over long paragraphs.
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Memory Files
155
+
156
+ Persistent memory lives in `.veil/memory/`, NOT in agent folders (agent folders are static/read-only).
157
+
158
+ ```
159
+ .veil/memory/
160
+ ├── MEMORY.md ← global memory (always loaded, ~200-line cap)
161
+ ├── <topic>.md ← auto-exported global memory files
162
+ └── agents/
163
+ └── <agentName>/
164
+ ├── MEMORY.md ← agent-specific memory (always loaded, ~200-line cap)
165
+ └── <topic>.md ← auto-exported agent memory files
166
+ ```
167
+
168
+ **Main MEMORY.md** is always loaded into the system prompt (Layer 4). Capped at ~200 lines (configurable).
169
+ **Auto-export:** When MEMORY.md exceeds the line cap, the compact model moves older entries into a separate `<topic>.md` file.
170
+ **Memory tools:** `memory_write` (append), `memory_read` (read), `memory_search` (search across all memory files).
171
+ **System prompt includes:** Main MEMORY.md content + list of available memory files with descriptions.
172
+ **Pre-compaction:** Before context compaction, runtime asks model to extract key learnings → writes to MEMORY.md (model can return `NO_MEMORY` to skip).
173
+
174
+ ```markdown
175
+ # Memory
176
+
177
+ ## User Preferences
178
+ - Prefers reports in markdown format
179
+ - Works in EST timezone
180
+
181
+ ## Known Facts
182
+ - Main project is called ProjectX
183
+ - Production server is at 192.168.1.100
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Heartbeat Files
189
+
190
+ Daemon checklists live in `.veil/heartbeats/`, NOT in agent folders.
191
+
192
+ ```
193
+ .veil/heartbeats/
194
+ └── <agentName>.md ← per-agent heartbeat checklist
195
+ ```
196
+
197
+ Plain markdown. Read on each daemon tick. Agent.json `heartbeatFile` field can override the default path.
198
+
199
+ ```markdown
200
+ # Heartbeat Checklist
201
+
202
+ - Check if any tasks have been in "processing" state for more than 1 hour
203
+ - Monitor ./logs/ folder for new ERROR lines since last check
204
+ - If it's Monday morning, generate a weekly summary
205
+ ```
206
+
207
+ **Output contract:** Agent MUST output `HEARTBEAT_OK` or `HEARTBEAT_ALERT: <message>`.
208
+
209
+ ### Daemon Trigger Payload
210
+
211
+ When a daemon is triggered by a system event (not cron), it receives context via a system message:
212
+
213
+ ```
214
+ [System] Daemon triggered by event: task.completed
215
+ {
216
+ "event": "task.completed",
217
+ "taskId": "task_abc123",
218
+ "agentName": "researcher",
219
+ "status": "finished",
220
+ "output": { ... },
221
+ "timestamp": "2026-03-01T08:00:00Z"
222
+ }
223
+ ```
224
+
225
+ The daemon can then decide what action to take based on the event payload.
226
+
227
+ ---
228
+
229
+ ## SKILL.md
230
+
231
+ Markdown with YAML frontmatter. Prompt template invokable by agents.
232
+
233
+ ```markdown
234
+ ---
235
+ name: review-code
236
+ description: Review code for security and performance issues
237
+ argument-hint: [file-path]
238
+ allowed-tools: bash, read_file, edit_file
239
+ context: fork
240
+ agent: anthropic/claude-3-haiku
241
+ disable-model-invocation: false
242
+ ---
243
+
244
+ Review the following file: $1
245
+
246
+ Focus on:
247
+ 1. Security vulnerabilities
248
+ 2. Performance bottlenecks
249
+ 3. Code style issues
250
+
251
+ Additional context: $ARGUMENTS
252
+ ```
253
+
254
+ | Field | Type | Description |
255
+ |-------|------|-------------|
256
+ | `name` | string | Skill identifier |
257
+ | `description` | string | Human-readable description |
258
+ | `argument-hint` | string | Hint shown for usage |
259
+ | `allowed-tools` | string | Comma-separated tool whitelist |
260
+ | `context` | main / fork | `main` = inject into conversation. `fork` = isolated sub-agent. |
261
+ | `agent` | string | Model to use (fork mode only). Named `agent` for Claude Code heritage; functionally equivalent to `model`. |
262
+ | `disable-model-invocation` | boolean | Only user/API can trigger this skill, not the agent itself (default: false) |
263
+
264
+ ---
265
+
266
+ ## tool.json
267
+
268
+ Custom tool manifest.
269
+
270
+ ```json
271
+ {
272
+ "name": "send_slack_message",
273
+ "description": "Sends a message to a Slack channel",
274
+ "input_schema": {
275
+ "type": "object",
276
+ "properties": {
277
+ "channel": { "type": "string", "description": "Slack channel name" },
278
+ "message": { "type": "string", "description": "Message to send" }
279
+ },
280
+ "required": ["channel", "message"]
281
+ },
282
+ "timeout": 30
283
+ }
284
+ ```
285
+
286
+ ---
287
+
288
+ ## config.json (Forkable, Source Code Only)
289
+
290
+ All branding/naming values. This is a **source code file** inside the VeilCLI repository (`config/config.json`), NOT a user-facing config file. Anyone forking VeilCLI changes only this file to rebrand.
291
+
292
+ ```json
293
+ {
294
+ "productName": "VeilCli",
295
+ "productNameShort": "veil",
296
+ "domain": "veil.com",
297
+ "configDir": ".veil",
298
+ "globalConfigDir": "~/.veil",
299
+ "defaultPort": 5050,
300
+ "authHeader": "X-Veil-Secret",
301
+ "tokenPrefix": "kbt_",
302
+ "sessionIdPrefix": "sess_",
303
+ "taskIdPrefix": "task_"
304
+ }
305
+ ```
306
+
307
+ ---
308
+
309
+ ## auth.json
310
+
311
+ Local authentication. Gitignored. The `llm` section provides **default** LLM credentials. If `settings.json` also defines `models.main.base_url` / `models.main.api_key`, the settings.json values take precedence per model role.
312
+
313
+ ```json
314
+ {
315
+ "veil_token": "kbt_xxxxxxxxxxxx",
316
+ "llm": {
317
+ "base_url": "https://openrouter.ai/api/v1",
318
+ "api_key": "sk-or-xxxxxxxxxxxx"
319
+ }
320
+ }
321
+ ```
322
+
323
+ ---
324
+
325
+ ## settings.json
326
+
327
+ Full configuration. Merged across layers (global → project → local → CLI flags).
328
+
329
+ > **Location:** Project settings go in `.veil/settings.json` — NOT in the project root as `settings.json`. A common mistake is placing settings at `./settings.json` which is never read by the runtime.
330
+
331
+ > **Canonical field names:** The LLM credential fields are `base_url` and `api_key` (snake_case). Do NOT use `apiEndpoint`, `apiKey`, or other variants. These names match the fields in `auth.json` and are exported as constants from `src/settings/fields.js` to prevent inconsistency across the codebase.
332
+
333
+ ```json
334
+ {
335
+ "models": {
336
+ "main": {
337
+ "model": "anthropic/claude-3.5-sonnet",
338
+ "base_url": "",
339
+ "api_key": ""
340
+ },
341
+ "compact": {
342
+ "model": "anthropic/claude-3-haiku"
343
+ },
344
+ "title": {
345
+ "model": "anthropic/claude-3-haiku"
346
+ }
347
+ },
348
+ "permissions": {
349
+ "allow": [],
350
+ "ask": [],
351
+ "deny": []
352
+ },
353
+ "hooks": {
354
+ "PreToolUse": "",
355
+ "PostToolUse": ""
356
+ },
357
+ "mcpServers": {
358
+ "filesystem": {
359
+ "transport": "stdio",
360
+ "command": "npx",
361
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/project"]
362
+ },
363
+ "web-search": {
364
+ "transport": "http",
365
+ "url": "http://localhost:3001/mcp"
366
+ }
367
+ },
368
+ "api": {
369
+ "port": 5050,
370
+ "secret": ""
371
+ },
372
+ "compaction": {
373
+ "threshold": 0.85,
374
+ "enabled": true,
375
+ "observationMaskingTurns": 10
376
+ },
377
+ "memory": {
378
+ "enabled": true,
379
+ "maxLines": 200,
380
+ "preCompactionExtraction": true
381
+ },
382
+ "storage": {
383
+ "retention": {
384
+ "sessions": { "maxAgeDays": 90, "maxCount": 10000 },
385
+ "tasks": { "maxAgeDays": 30, "maxCount": 5000 }
386
+ }
387
+ },
388
+ "limits": {
389
+ "maxIterations": 50,
390
+ "maxDurationSeconds": 300,
391
+ "maxConcurrentTasks": 5,
392
+ "maxSubAgentDepth": 3
393
+ },
394
+ "retry": {
395
+ "maxAttempts": 3,
396
+ "delaySeconds": 5,
397
+ "onExhausted": "fail"
398
+ },
399
+ "ably": {
400
+ "enabled": false,
401
+ "instanceId": "",
402
+ "tokenEndpoint": "https://veil.com/api/v1/ably/token",
403
+ "requestTimeout": 30000,
404
+ "maxRequestAge": 60000
405
+ }
406
+ }
407
+ ```
408
+
409
+ **`onExhausted` valid values:** `"fail"` (default — task fails) or `"wait"` (task pauses in `waiting` status for human retry via `task_respond`).
410
+
411
+ Settings resolution order (last wins):
412
+ ```
413
+ 1. VeilCLI hardcoded defaults
414
+ 2. ~/.veil/settings.json
415
+ 3. ./.veil/settings.json
416
+ 4. ./.veil/settings.local.json ← same format as settings.json, partial overrides merged
417
+ 5. CLI flags
418
+ ```
419
+
420
+ `settings.local.json` uses the same schema as `settings.json`. Only the fields present are merged — omitted fields inherit from the previous layer. This file is gitignored and intended for developer-specific overrides (e.g., different API keys, ports).
421
+
422
+ **Settings field names are defined as constants in `src/settings/fields.js`** and used everywhere in the runtime code. Never hardcode field name strings (`'api_key'`, `'base_url'`) inline — always import from `fields.js`. This prevents the class of bugs where one module reads `settings.apiKey` and another writes `settings.api_key`.
423
+
424
+ ---
425
+
426
+ ## version.json
427
+
428
+ Present **only** in globally cached agents/tools from the Veil.com registry (`~/.veil/agents/<name>/`, `~/.veil/tools/<name>/`). Project-level agents/tools (`.veil/agents/`, `.veil/tools/`) do NOT have this file — they are user-owned and not registry-managed.
429
+
430
+ ```json
431
+ {
432
+ "hash": "sha256:a1b2c3d4e5f6...",
433
+ "version": "1.2.0",
434
+ "updatedAt": "2026-03-01T08:00:00Z"
435
+ }
436
+ ```
437
+
438
+ | Field | Type | Description |
439
+ |-------|------|-------------|
440
+ | `hash` | string | SHA256 hash of agent/tool contents |
441
+ | `version` | string | Version from Veil.com registry |
442
+ | `updatedAt` | string | ISO 8601 timestamp of last update |
@@ -0,0 +1,205 @@
1
+ # 12 — Folder Structure
2
+
3
+ ## Project-Level
4
+
5
+ ```
6
+ ./ ← any folder user runs veil from (clean, no Veil files at root)
7
+ └── .veil/
8
+ ├── AGENT.md ← global instructions for all agents in this project
9
+ ├── settings.json ← project settings (committed to git)
10
+ ├── settings.local.json ← local overrides (gitignored)
11
+ ├── auth.json ← local auth override (gitignored)
12
+ ├── runtime.pid ← written on server start, deleted on stop
13
+ ├── agents/ ← agent definitions (STATIC, read-only)
14
+ │ └── <agentName>/ ← $AGENT_FOLDER resolves to this path
15
+ │ ├── AGENT.md ← agent instructions
16
+ │ ├── agent.json ← agent config (modes, model, tools)
17
+ │ ├── SOUL.md ← optional persona
18
+ │ ├── skills/
19
+ │ │ └── <skillName>/
20
+ │ │ ├── SKILL.md
21
+ │ │ └── scripts/ ← scripts referenced by the skill
22
+ │ └── tools/ ← agent-level custom tools
23
+ │ └── <toolName>/
24
+ │ ├── tool.json
25
+ │ └── index.js
26
+ ├── memory/ ← persistent memory (MUTABLE)
27
+ │ ├── MEMORY.md ← global memory (always loaded, ~200-line cap)
28
+ │ ├── <topic>.md ← auto-exported global memory files
29
+ │ └── agents/
30
+ │ └── <agentName>/
31
+ │ ├── MEMORY.md ← agent-specific memory (always loaded, ~200-line cap)
32
+ │ └── <topic>.md ← auto-exported agent memory files
33
+ ├── heartbeats/ ← daemon checklists (MUTABLE)
34
+ │ └── <agentName>.md ← per-agent heartbeat checklist
35
+ ├── skills/ ← shared skills available to all agents
36
+ │ └── <skillName>/
37
+ │ └── SKILL.md
38
+ └── tools/ ← project-level custom tools
39
+ └── <toolName>/
40
+ ├── tool.json
41
+ └── index.js
42
+ ```
43
+
44
+ **Global instructions:** `.veil/AGENT.md` only. No fallback to project root. Claude Code projects must be converted via `veil import`.
45
+
46
+ > **Common mistake:** Placing agents in `./agents/` (project root) instead of `./.veil/agents/`. The runtime only scans `.veil/agents/`.
47
+
48
+ ## Global
49
+
50
+ ```
51
+ ~/.veil/
52
+ ├── auth.json ← Veil.com token + LLM API config
53
+ ├── settings.json ← global default settings
54
+ ├── data.db ← SQLite database (all runtime data)
55
+ ├── agents/ ← agents cached from Veil.com (static)
56
+ │ └── <agentName>/
57
+ │ ├── version.json ← SHA256 hash for registry version checking
58
+ │ └── ... ← same structure as project agent
59
+ ├── skills/ ← global shared skills
60
+ │ └── <skillName>/
61
+ │ └── SKILL.md
62
+ ├── tools/ ← global custom tools
63
+ │ └── <toolName>/
64
+ │ ├── tool.json
65
+ │ ├── index.js
66
+ │ └── version.json ← SHA256 hash for registry version checking
67
+ ├── backups/ ← auto-backups of data.db
68
+ │ └── data-2026-03-01.db
69
+ └── archive/ ← archived old sessions/tasks (gzipped JSONL)
70
+ └── sessions-2026-03.jsonl.gz
71
+ ```
72
+
73
+ ## Source Code (Runtime)
74
+
75
+ ```
76
+ veilcli/
77
+ ├── package.json ← bin: { "veil": "./src/cli/index.js" } ← MUST point here
78
+ ├── config/
79
+ │ └── config.json ← forkable branding/naming
80
+ ├── src/
81
+ │ ├── core/ ← Pure business logic (transport agnostic)
82
+ │ │ ├── loop.js ← Agentic loop (async generator)
83
+ │ │ ├── router.js ← Multi-model delegation (IntelligenceRouter)
84
+ │ │ ├── registry.js ← Tool registration + ajv compilation
85
+ │ │ ├── prompt.js ← System prompt assembly (7 layers)
86
+ │ │ ├── compaction.js ← Context compaction logic
87
+ │ │ ├── agent.js ← Agent loader + discovery
88
+ │ │ ├── memory.js ← Memory management (read/write/search/export)
89
+ │ │ └── queue.js ← Agent message queue
90
+ │ ├── api/ ← REST-specific transport
91
+ │ │ ├── routes.js
92
+ │ │ ├── middleware.js
93
+ │ │ └── controllers.js
94
+ │ ├── cli/
95
+ │ │ ├── index.js ← CLI entry point (bin target)
96
+ │ │ ├── parser.js
97
+ │ │ └── formatter.js
98
+ │ ├── llm/
99
+ │ │ └── client.js ← Native fetch-based LLM client (no SDK)
100
+ │ ├── settings/
101
+ │ │ └── fields.js ← Settings field name constants (single source of truth)
102
+ │ ├── utils/
103
+ │ │ ├── context.js ← CWD singleton — set once at startup, read everywhere
104
+ │ │ ├── id.js ← generateId() via crypto.randomBytes (no nanoid)
105
+ │ │ └── paths.js ← Path helpers: getProjectAgentsDir(cwd), etc.
106
+ │ ├── tools/ ← Built-in tool implementations
107
+ │ │ ├── bash.js
108
+ │ │ ├── read-file.js
109
+ │ │ ├── write-file.js
110
+ │ │ └── ...
111
+ │ ├── infrastructure/ ← External resource management
112
+ │ │ ├── database.js ← SQLite setup, migrations, queries
113
+ │ │ ├── scheduler.js ← Cron scheduling for daemons
114
+ │ │ ├── ably.js ← Ably bridge
115
+ │ │ └── mcp.js ← MCP client wrapper
116
+ │ └── system-prompts/ ← System prompt fragments
117
+ │ ├── base-core.md
118
+ │ ├── safety-rules.md
119
+ │ ├── environment.md
120
+ │ ├── task-heuristics.md
121
+ │ └── reminders/
122
+ │ ├── anti-drift.md
123
+ │ ├── stall-recovery.md
124
+ │ └── ...
125
+ ├── migrations/ ← SQLite migration files (all use IF NOT EXISTS)
126
+ │ ├── 001-initial.sql
127
+ │ └── ...
128
+ ├── examples/ ← Reference agents guaranteed to pass schema validation
129
+ │ └── agents/
130
+ │ └── hello/ ← Minimal chat-only agent (used in smoke test)
131
+ │ ├── AGENT.md
132
+ │ └── agent.json
133
+ └── test/
134
+ ├── unit/
135
+ ├── api/
136
+ └── smoke.js ← Fast startup + health + one chat call
137
+ ```
138
+
139
+ ### Critical Implementation Rules
140
+
141
+ **1. CWD singleton (`src/utils/context.js`):**
142
+ The server's working directory is set **once** at startup (in `src/cli/index.js`) and stored in a singleton. All routes, agents, and tools read from `context.getCwd()`. **Never call `process.cwd()` inside `src/core/`, `src/api/`, `src/tools/`, or `src/infrastructure/`.** Only `src/cli/index.js` may call `process.cwd()` (at launch time).
143
+
144
+ ```javascript
145
+ // src/utils/context.js
146
+ let _cwd = null;
147
+ module.exports = {
148
+ setCwd(cwd) { _cwd = cwd; },
149
+ getCwd() {
150
+ if (!_cwd) throw new Error('Context not initialized — call setCwd() at startup');
151
+ return _cwd;
152
+ }
153
+ };
154
+ // Usage in routes:
155
+ const { getCwd } = require('../utils/context');
156
+ const agentsDir = paths.getProjectAgentsDir(getCwd());
157
+ ```
158
+
159
+ This also eliminates the circular dependency that arises when routes import from `server.js` to get `cwd` and `server.js` imports routes.
160
+
161
+ **2. Path helpers (`src/utils/paths.js`):**
162
+ All path construction goes through named helpers. **Do not construct `.veil/` paths inline.**
163
+
164
+ ```javascript
165
+ // Correct — explicit, clear:
166
+ const agentsDir = paths.getProjectAgentsDir(cwd); // → cwd/.veil/agents
167
+
168
+ // Wrong — was getAgentsDir(cwd) which returned cwd/agents (missing .veil):
169
+ const agentsDir = paths.getAgentsDir(cwd); // REMOVED — ambiguous name
170
+ ```
171
+
172
+ Key functions in `paths.js`:
173
+ - `getProjectConfigDir(cwd)` → `cwd/.veil/`
174
+ - `getProjectAgentsDir(cwd)` → `cwd/.veil/agents/`
175
+ - `getProjectSettingsPath(cwd)` → `cwd/.veil/settings.json`
176
+ - `getGlobalAgentsDir()` → `~/.veil/agents/`
177
+ - `getAgentDir(cwd, agentName)` → full path for a named agent
178
+
179
+ **3. Settings field names (`src/settings/fields.js`):**
180
+ ```javascript
181
+ // src/settings/fields.js — single source of truth
182
+ module.exports = {
183
+ MODEL_BASE_URL: 'base_url',
184
+ MODEL_API_KEY: 'api_key',
185
+ MODEL_NAME: 'model',
186
+ // ... all other field names
187
+ };
188
+ // Never hardcode 'api_key' or 'base_url' as strings in any other file.
189
+ ```
190
+
191
+ **4. `package.json` bin path:**
192
+ ```json
193
+ { "bin": { "veil": "./src/cli/index.js" } }
194
+ ```
195
+ The root `index.js` does NOT exist. The CLI entry is always `src/cli/index.js`.
196
+
197
+ **5. Version reading:**
198
+ Do NOT use `require('../../package.json')` inside deeply-nested route files — the relative depth varies and causes `Cannot find module` errors. Instead, read version at startup and pass it via `context.js` or a `constants.js` file:
199
+ ```javascript
200
+ // Set once at startup in src/cli/index.js:
201
+ const { version } = require('../package.json'); // correct relative path from cli/
202
+ context.setVersion(version);
203
+ // In routes:
204
+ const version = context.getVersion();
205
+ ```