@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,350 @@
1
+ # WebSocket Event Stream (`/ws`)
2
+
3
+ Connect once — receive every runtime event from every agent, session, and task in real time.
4
+
5
+ **Trigger-path parity**: Message content and tool activity are emitted to WS regardless of how a session was triggered:
6
+ - **SSE-triggered sessions** → `session.stream` events (mirrors the SSE stream exactly, including live token chunks)
7
+ - **All other triggers** (HTTP JSON, `agent_message`, `agent_spawn`) → `chat.message` and `chat.inference_tool` events
8
+
9
+ The only WS-exclusive gap vs SSE is `inference.chunk` (live streaming tokens), which is SSE-only by design. All other content — assistant messages, tool results, tool indicators — reaches WS in all cases.
10
+
11
+ ---
12
+
13
+ ## Connecting
14
+
15
+ ```js
16
+ const ws = new WebSocket('ws://localhost:5050/ws');
17
+
18
+ ws.onopen = () => console.log('Connected');
19
+ ws.onmessage = (msg) => {
20
+ const ev = JSON.parse(msg.data);
21
+ // ev.type identifies the event category
22
+ };
23
+ ```
24
+
25
+ On connection the server sends an initial handshake:
26
+ ```json
27
+ { "type": "connected", "timestamp": 1709550000000 }
28
+ ```
29
+
30
+ **Authentication** — include the secret as a query parameter or header (same as HTTP endpoints):
31
+ ```js
32
+ // Query param
33
+ new WebSocket('ws://localhost:5050/ws?secret=my-secret');
34
+
35
+ // Header (Node.js / server-side clients)
36
+ new WebSocket('ws://localhost:5050/ws', [], {
37
+ headers: { 'X-Veil-Secret': 'my-secret' }
38
+ });
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Event Envelope
44
+
45
+ Every event follows this structure:
46
+
47
+ ```json
48
+ {
49
+ "type": "<event_type>",
50
+ "sessionId": "sess_abc123",
51
+ "taskId": "task_xyz789",
52
+ "agentName": "assistant",
53
+ "event": { ... },
54
+ "eventType": "<sub_type>"
55
+ }
56
+ ```
57
+
58
+ | Field | Present when | Description |
59
+ |-------|-------------|-------------|
60
+ | `type` | Always | Top-level event category |
61
+ | `sessionId` | Chat/session events | Identifies the session |
62
+ | `taskId` | Task events | Identifies the task |
63
+ | `agentName` | Always (except `connected`) | Agent that generated the event |
64
+ | `event` | All except `session.stream` | Event-specific payload |
65
+ | `eventType` | `session.stream` only | SSE event sub-type (see below) |
66
+ | `data` | `session.stream` only | SSE event payload |
67
+
68
+ `sessionId` and `taskId` are `undefined` where not applicable — filter on the client.
69
+
70
+ ---
71
+
72
+ ## Event Reference
73
+
74
+ ### Session Lifecycle
75
+
76
+ | `type` | When | `event` fields |
77
+ |--------|------|----------------|
78
+ | `session.created` | A new chat session is started | `{ mode: 'chat', timestamp }` |
79
+ | `session.closed` | Session soft-closed (`DELETE /sessions/:id`) | `{ timestamp }` |
80
+ | `session.deleted` | Session hard-deleted (`DELETE /sessions/:id?hard=true`) | `{ timestamp }` |
81
+
82
+ ```json
83
+ { "type": "session.created", "sessionId": "sess_abc123", "agentName": "assistant",
84
+ "event": { "mode": "chat", "timestamp": 1709550000000 } }
85
+
86
+ { "type": "session.closed", "sessionId": "sess_abc123", "agentName": "assistant",
87
+ "event": { "timestamp": 1709550999000 } }
88
+ ```
89
+
90
+ ---
91
+
92
+ ### Chat — Streaming (session.stream)
93
+
94
+ `session.stream` mirrors every event that the SSE endpoint (`POST /agents/:name/chat` with `sse: true`) sends to a directly connected SSE client. **Only emitted when the session was triggered via SSE.** For sessions triggered by other means, see `chat.message` and `chat.inference_tool` below.
95
+
96
+ The sub-type is in `eventType`; the payload is in `data`.
97
+
98
+ | `eventType` | When | `data` fields |
99
+ |-------------|------|---------------|
100
+ | `inference.chunk` | Each streaming text token from the LLM | `{ content: string }` |
101
+ | `inference.tool` | LLM is about to call a tool (before execution) | `{ name: string }` |
102
+ | `message` | A message was persisted (assistant reply, tool result) | Full message object (see below) |
103
+ | `done` | Chat turn completed | `{ session, agentName, model, iterations, durationMs, tokenUsage, toolCalls }` |
104
+ | `error` | Run-level error during the turn | `{ error: string, code: string }` |
105
+
106
+ ```json
107
+ { "type": "session.stream", "sessionId": "sess_abc123", "agentName": "assistant",
108
+ "eventType": "inference.chunk", "data": { "content": "Hello" } }
109
+
110
+ { "type": "session.stream", "sessionId": "sess_abc123", "agentName": "assistant",
111
+ "eventType": "inference.tool", "data": { "name": "bash" } }
112
+
113
+ { "type": "session.stream", "sessionId": "sess_abc123", "agentName": "assistant",
114
+ "eventType": "done",
115
+ "data": { "agentName": "assistant", "model": "anthropic/claude-opus-4-5",
116
+ "iterations": 2, "durationMs": 3412,
117
+ "tokenUsage": { "input": 820, "output": 310, "cache": 0, "cost": 0.0045 },
118
+ "toolCalls": [{ "name": "bash", "durationMs": 124, "success": true }],
119
+ "session": { "id": "sess_abc123", "status": "open", ... } } }
120
+ ```
121
+
122
+ **`message` object fields:**
123
+
124
+ | Field | Type | Description |
125
+ |-------|------|-------------|
126
+ | `id` | string | Message ID |
127
+ | `session_id` | string | Session this message belongs to |
128
+ | `role` | string | `"assistant"` or `"tool"` |
129
+ | `content` | string | Message content |
130
+ | `tool_calls` | array | Tool calls made (assistant messages) |
131
+ | `tool_call_id` | string | Which tool call this is a result for (tool messages) |
132
+ | `finishReason` | string | LLM stop reason (`"end_turn"`, `"tool_use"`, etc.) |
133
+ | `iteration` | number | Loop iteration this message was generated in |
134
+ | `tokenUsage` | object | `{ input, output, cache, cost }` for this message |
135
+
136
+ ---
137
+
138
+ ### Chat — Messages (non-SSE sessions)
139
+
140
+ When a session is triggered via HTTP JSON, `agent_message`, `agent_spawn`, or any path that does not use SSE streaming, message content is delivered via these events instead of `session.stream`.
141
+
142
+ | `type` | When | `event` fields |
143
+ |--------|------|----------------|
144
+ | `chat.message` | An assistant or tool-result message was persisted | Same fields as `session.stream` → `message` (see message object table above) |
145
+ | `chat.inference_tool` | LLM is about to call a tool (before execution) | `{ name: string, timestamp }` |
146
+
147
+ ```json
148
+ { "type": "chat.message", "sessionId": "sess_abc123", "agentName": "assistant",
149
+ "event": { "id": "msg_001", "session_id": "sess_abc123", "role": "assistant",
150
+ "content": "Here is the analysis...", "tool_calls": null,
151
+ "finishReason": "end_turn", "iteration": 1,
152
+ "tokenUsage": { "input": 820, "output": 310, "cache": 0, "cost": 0.0045 },
153
+ "timestamp": 1709550003000 } }
154
+
155
+ { "type": "chat.inference_tool", "sessionId": "sess_abc123", "agentName": "assistant",
156
+ "event": { "name": "bash", "timestamp": 1709550002500 } }
157
+ ```
158
+
159
+ > **Rule of thumb**: filter on both `session.stream` (eventType `message`) and `chat.message` if you want to catch all persisted messages regardless of trigger path.
160
+
161
+ ---
162
+
163
+ ### Chat — User Message
164
+
165
+ Emitted when a user message is received, before the AI starts processing. Use this to render the user's turn in a UI.
166
+
167
+ | `type` | When | `event` fields |
168
+ |--------|------|----------------|
169
+ | `chat.user_message` | User message added to a session | `{ content: string, timestamp }` |
170
+
171
+ ```json
172
+ { "type": "chat.user_message", "sessionId": "sess_abc123", "agentName": "assistant",
173
+ "event": { "content": "Summarise this file please", "timestamp": 1709550001000 } }
174
+ ```
175
+
176
+ ---
177
+
178
+ ### Chat — Internal Loop Events
179
+
180
+ Lower-level events emitted during each loop iteration. Useful for debugging or building detailed progress UIs.
181
+
182
+ | `type` | When | `event` fields |
183
+ |--------|------|----------------|
184
+ | `chat.event` | Iteration start, LLM error | `{ type: 'iteration.start'\|'llm.error', iteration?, error? }` |
185
+ | `chat.tool` | Tool starts or ends inside a chat turn | `{ type: 'tool.start'\|'tool.end', toolName, toolInput?, durationMs?, success?, outputPreview? }` |
186
+ | `chat.response` | Chat turn fully complete (summary) | `{ content: string (≤500 chars), toolCount, timestamp }` |
187
+
188
+ > These events fire for **all** trigger paths (SSE, HTTP JSON, `agent_message`, `agent_spawn`, etc.).
189
+
190
+ ```json
191
+ { "type": "chat.event", "sessionId": "sess_abc123", "agentName": "assistant",
192
+ "event": { "type": "iteration.start", "iteration": 1, "tokensSoFar": 0, "timestamp": 1709550002000 } }
193
+
194
+ { "type": "chat.tool", "sessionId": "sess_abc123", "agentName": "assistant",
195
+ "event": { "type": "tool.end", "toolName": "read_file", "durationMs": 14, "success": true,
196
+ "outputPreview": "# README\n...", "timestamp": 1709550003000 } }
197
+
198
+ { "type": "chat.response", "sessionId": "sess_abc123", "agentName": "assistant",
199
+ "event": { "content": "The file contains a README with...", "toolCount": 1, "timestamp": 1709550004000 } }
200
+ ```
201
+
202
+ ---
203
+
204
+ ### Task Events
205
+
206
+ Emitted for background tasks (created via `POST /tasks`, `task_create`, or `task_spawn`).
207
+
208
+ | `type` | When | `event` fields |
209
+ |--------|------|----------------|
210
+ | `task.status` | Task transitions state | `{ status: 'processing'\|'finished'\|'failed'\|'canceled', sessionId?, output?, error?, reason? }` |
211
+ | `task.event` | Iteration, tool calls, LLM errors inside the task | `{ type, toolName?, toolInput?, durationMs?, success?, outputPreview?, iteration?, error? }` |
212
+
213
+ **Status values:** `pending` → `processing` → `finished` / `failed` / `canceled`
214
+
215
+ ```json
216
+ { "type": "task.status", "taskId": "task_xyz789", "agentName": "researcher",
217
+ "event": { "status": "processing", "sessionId": "sess_def456", "timestamp": 1709550010000 } }
218
+
219
+ { "type": "task.event", "taskId": "task_xyz789", "agentName": "researcher",
220
+ "event": { "type": "tool.start", "toolName": "bash",
221
+ "toolInput": { "command": "grep -r 'TODO' ." }, "timestamp": 1709550011000 } }
222
+
223
+ { "type": "task.status", "taskId": "task_xyz789", "agentName": "researcher",
224
+ "event": { "status": "finished", "output": "Found 12 TODOs across 4 files.", "timestamp": 1709550045000 } }
225
+ ```
226
+
227
+ ---
228
+
229
+ ### Daemon Events
230
+
231
+ Emitted on every scheduled daemon tick.
232
+
233
+ | `type` | When | `event` fields |
234
+ |--------|------|----------------|
235
+ | `daemon.tick` | Tick starts, finishes, or errors | `{ status: 'running'\|'done'\|'error', sessionId?, error? }` |
236
+
237
+ ```json
238
+ { "type": "daemon.tick", "agentName": "monitor",
239
+ "event": { "status": "running", "sessionId": "sess_ghi789", "timestamp": 1709550100000 } }
240
+
241
+ { "type": "daemon.tick", "agentName": "monitor",
242
+ "event": { "status": "done", "sessionId": "sess_ghi789", "timestamp": 1709550108000 } }
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Building a Full Conversation UI on WS Alone
248
+
249
+ A WS-only client can render a complete, real-time conversation UI for any number of sessions without any SSE connections or polling:
250
+
251
+ ```js
252
+ const ws = new WebSocket('ws://localhost:5050/ws?secret=my-secret');
253
+
254
+ // Keyed by sessionId
255
+ const sessions = {};
256
+
257
+ ws.onmessage = (msg) => {
258
+ const ev = JSON.parse(msg.data);
259
+ const { type, sessionId, agentName, event, eventType, data } = ev;
260
+
261
+ switch (type) {
262
+
263
+ // ── Session lifecycle ──────────────────────────────────────────────────
264
+ case 'session.created':
265
+ sessions[sessionId] = { agentName, messages: [], streaming: false };
266
+ renderSessionTab(sessionId, agentName);
267
+ break;
268
+
269
+ case 'session.closed':
270
+ case 'session.deleted':
271
+ markSessionClosed(sessionId);
272
+ break;
273
+
274
+ // ── User message (render immediately, before AI responds) ────────────
275
+ case 'chat.user_message':
276
+ appendMessage(sessionId, { role: 'user', content: event.content });
277
+ break;
278
+
279
+ // ── Streaming (SSE-triggered sessions) ─────────────────────────────
280
+ case 'session.stream':
281
+ if (eventType === 'inference.chunk') {
282
+ appendStreamingChunk(sessionId, data.content); // live token-by-token
283
+ } else if (eventType === 'inference.tool') {
284
+ showToolIndicator(sessionId, data.name); // "Using bash…"
285
+ } else if (eventType === 'message' && data.role === 'assistant') {
286
+ finalizeAssistantMessage(sessionId, data); // full message persisted
287
+ } else if (eventType === 'done') {
288
+ hideLoader(sessionId);
289
+ updateUsage(sessionId, data.tokenUsage);
290
+ } else if (eventType === 'error') {
291
+ showError(sessionId, data.error);
292
+ }
293
+ break;
294
+
295
+ // ── Messages (non-SSE sessions: HTTP JSON, agent_message, agent_spawn)
296
+ case 'chat.message':
297
+ if (event.role === 'assistant') finalizeAssistantMessage(sessionId, event);
298
+ break;
299
+
300
+ case 'chat.inference_tool':
301
+ showToolIndicator(sessionId, event.name); // "Using bash…"
302
+ break;
303
+
304
+ // ── Loading indicator (AI started thinking) ──────────────────────────
305
+ case 'chat.event':
306
+ if (event.type === 'iteration.start') showLoader(sessionId);
307
+ break;
308
+
309
+ // ── Tasks ──────────────────────────────────────────────────────────────
310
+ case 'task.status':
311
+ updateTaskStatus(ev.taskId, event.status, event.output);
312
+ break;
313
+ }
314
+ };
315
+ ```
316
+
317
+ ---
318
+
319
+ ## Filtering Events
320
+
321
+ The WebSocket receives events for **all** agents and sessions. Filter by any combination of `sessionId`, `taskId`, or `agentName`:
322
+
323
+ ```js
324
+ // Watch only one session
325
+ if (ev.sessionId !== mySessionId) return;
326
+
327
+ // Watch all sessions for one agent
328
+ if (ev.agentName !== 'researcher') return;
329
+
330
+ // Watch only task completions
331
+ if (ev.type !== 'task.status' || ev.event?.status !== 'finished') return;
332
+ ```
333
+
334
+ ---
335
+
336
+ ## Reconnection
337
+
338
+ The WebSocket is stateless — the server stores no subscription state. On reconnect:
339
+
340
+ 1. Fetch missed messages: `GET /sessions/:id/messages`
341
+ 2. Fetch missed task events: `GET /tasks/:id/events`
342
+ 3. Reconnect to WS and resume live updates
343
+
344
+ ```js
345
+ async function reconnect() {
346
+ const { messages } = await api.get(`/sessions/${sessionId}/messages`);
347
+ renderHistory(messages);
348
+ connectWS(); // then subscribe to live updates
349
+ }
350
+ ```
@@ -0,0 +1,134 @@
1
+ # Completions
2
+
3
+ A standalone chat completion endpoint for independent LLM calls. No agent config, no session, no tool-execution loop — just a direct call to the configured LLM provider.
4
+
5
+ Intended for apps built on Veil that need raw AI completions without setting up an agent workflow.
6
+
7
+ ---
8
+
9
+ ## POST /completions
10
+
11
+ **Request body**
12
+
13
+ | Field | Type | Required | Description |
14
+ |-------|------|----------|-------------|
15
+ | `messages` | array | ✅ | OpenAI-compatible messages array |
16
+ | `model` | string | — | Model ID to use. Overrides the default model from settings |
17
+ | `temperature` | number | — | Sampling temperature |
18
+ | `max_tokens` | number | — | Maximum output tokens |
19
+ | `tools` | array | — | OpenAI-compatible tool definitions |
20
+ | `reasoning` | string | — | Reasoning effort level (e.g. `"high"`) |
21
+ | `thinking` | object | — | Extended thinking config (e.g. `{ "type": "enabled", "budget_tokens": 10000 }`) |
22
+ | `sse` | boolean | — | Stream the response via SSE (default: `false`) |
23
+
24
+ The `base_url` and `api_key` are always taken from the server's configured main model — they cannot be overridden per-request.
25
+
26
+ **Standard JSON response**
27
+
28
+ ```json
29
+ {
30
+ "message": {
31
+ "role": "assistant",
32
+ "content": "The capital of France is Paris.",
33
+ "tool_calls": []
34
+ },
35
+ "usage": {
36
+ "input": 18,
37
+ "output": 9,
38
+ "cache": 0
39
+ },
40
+ "cost": 0.0000000432,
41
+ "finish_reason": "stop"
42
+ }
43
+ ```
44
+
45
+ **Response fields**
46
+
47
+ | Field | Type | Description |
48
+ |-------|------|-------------|
49
+ | `message.content` | string\|null | Assistant reply text |
50
+ | `message.tool_calls` | array | Tool calls requested by the model (may be empty) |
51
+ | `usage.input` | integer | Prompt tokens consumed |
52
+ | `usage.output` | integer | Completion tokens generated |
53
+ | `usage.cache` | integer | Cached prompt tokens (subset of `input`) |
54
+ | `cost` | number | Estimated USD cost. Uses the value returned by the API if present, otherwise calculated from `config/models.json` pricing |
55
+ | `finish_reason` | string | Why the model stopped (`stop`, `tool_calls`, `length`, etc.) |
56
+
57
+ **Example**
58
+
59
+ ```bash
60
+ curl -X POST http://localhost:5050/completions \
61
+ -H 'Content-Type: application/json' \
62
+ -d '{
63
+ "messages": [
64
+ { "role": "user", "content": "What is the capital of France?" }
65
+ ]
66
+ }'
67
+ ```
68
+
69
+ With an explicit model override:
70
+
71
+ ```bash
72
+ curl -X POST http://localhost:5050/completions \
73
+ -H 'Content-Type: application/json' \
74
+ -d '{
75
+ "model": "google/gemini-2.5-flash",
76
+ "messages": [
77
+ { "role": "system", "content": "You are a concise assistant." },
78
+ { "role": "user", "content": "Summarise the water cycle in one sentence." }
79
+ ],
80
+ "temperature": 0.3,
81
+ "max_tokens": 120
82
+ }'
83
+ ```
84
+
85
+ ---
86
+
87
+ ## SSE streaming
88
+
89
+ Set `"sse": true` in the request body to receive a streamed response.
90
+
91
+ The connection emits three event types:
92
+
93
+ | Event | Payload | Description |
94
+ |-------|---------|-------------|
95
+ | `chunk` | `{ "content": "..." }` | Incremental text delta |
96
+ | `done` | Full response object (same as JSON mode) | Final message, usage, and cost |
97
+ | `error` | `{ "error": "...", "code": "..." }` | Emitted if the LLM call fails |
98
+
99
+ **Example**
100
+
101
+ ```bash
102
+ curl -X POST http://localhost:5050/completions \
103
+ -H 'Content-Type: application/json' \
104
+ -d '{
105
+ "messages": [{ "role": "user", "content": "Tell me a short story." }],
106
+ "sse": true
107
+ }'
108
+ ```
109
+
110
+ **Sample stream output**
111
+
112
+ ```
113
+ event: chunk
114
+ data: {"content":"Once"}
115
+
116
+ event: chunk
117
+ data: {"content":" upon"}
118
+
119
+ event: chunk
120
+ data: {"content":" a time..."}
121
+
122
+ event: done
123
+ data: {"message":{"role":"assistant","content":"Once upon a time...","tool_calls":[]},"usage":{"input":12,"output":5,"cache":0},"cost":0.0000000075,"finish_reason":"stop"}
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Error responses
129
+
130
+ | Code | Condition |
131
+ |------|-----------|
132
+ | `400 VALIDATION_ERROR` | `messages` is missing or not an array |
133
+ | `400 VALIDATION_ERROR` | No model specified and no default model configured in settings |
134
+ | `500 INTERNAL_ERROR` | LLM provider returned an error |
@@ -0,0 +1,116 @@
1
+ # VeilCLI — REST API Reference
2
+
3
+ The VeilCLI server exposes a local HTTP REST API. All requests use JSON. The default port is **5050**.
4
+
5
+ ---
6
+
7
+ ## Base URL
8
+
9
+ ```
10
+ http://localhost:5050
11
+ ```
12
+
13
+ Set a custom port in `.veil/settings.json` (`"port": 5051`) or via `veil start --port 5051`.
14
+
15
+ ---
16
+
17
+ ## Authentication
18
+
19
+ Authentication is **optional**. If you set `secret` in `settings.json`, every request must include the header:
20
+
21
+ ```
22
+ X-Veil-Secret: <your-secret>
23
+ ```
24
+
25
+ If no secret is configured, the API is open (localhost only by default).
26
+
27
+ ---
28
+
29
+ ## Request Format
30
+
31
+ All request bodies must be JSON with `Content-Type: application/json`.
32
+
33
+ ---
34
+
35
+ ## Response Format
36
+
37
+ All responses are JSON. Successful responses vary by endpoint. Error responses always use:
38
+
39
+ ```json
40
+ {
41
+ "error": {
42
+ "code": "ERROR_CODE",
43
+ "message": "Human-readable description"
44
+ }
45
+ }
46
+ ```
47
+
48
+ ### Error Codes
49
+
50
+ | HTTP Status | Code | Meaning |
51
+ |-------------|------|---------|
52
+ | 400 | `VALIDATION_ERROR` | Missing or invalid request body field |
53
+ | 400 | `MODE_NOT_SUPPORTED` | Agent does not support the requested mode |
54
+ | 400 | `SESSION_CLOSED` | Session has been closed |
55
+ | 400 | `TASK_NOT_WAITING` | Task is not in `waiting` status |
56
+ | 400 | `TASK_ALREADY_TERMINAL` | Task is already finished/failed/canceled |
57
+ | 401 | `UNAUTHORIZED` | Invalid or missing `X-Veil-Secret` |
58
+ | 404 | `AGENT_NOT_FOUND` | No agent with that name |
59
+ | 404 | `TASK_NOT_FOUND` | No task with that ID |
60
+ | 404 | `SESSION_NOT_FOUND` | No session with that ID |
61
+ | 404 | `NOT_FOUND` | Resource (e.g. memory file) not found |
62
+ | 500 | `INTERNAL_ERROR` | Unexpected server error |
63
+
64
+ ---
65
+
66
+ ## Endpoints Overview
67
+
68
+ | Method | Path | Description |
69
+ |--------|------|-------------|
70
+ | WS | `/ws` | Real-time event stream |
71
+ | GET | `/health` | Liveness check |
72
+ | GET | `/status` | Full server status |
73
+ | POST | `/shutdown` | Graceful shutdown |
74
+ | GET | `/agents` | List all agents |
75
+ | GET | `/agents/:name` | Get agent details |
76
+ | GET | `/agents/:name/sessions` | Sessions scoped to agent |
77
+ | GET | `/agents/:name/tasks` | Tasks scoped to agent |
78
+ | GET | `/agents/:name/skills` | Skills and custom tools for agent |
79
+ | GET | `/agents/:name/memory` | List agent memory files |
80
+ | GET | `/agents/:name/memory/:file` | Read an agent memory file |
81
+ | PUT | `/agents/:name/memory/:file` | Write an agent memory file |
82
+ | DELETE | `/agents/:name/memory/:file` | Delete an agent memory file |
83
+ | POST | `/agents/:name/chat` | Send a chat message |
84
+ | POST | `/agents/:name/task` | Create and start a task (`tokenBudget` supported) |
85
+ | GET | `/tasks` | List tasks (filter by agent, status, priority) |
86
+ | GET | `/tasks/:id` | Get task details (includes `eventCount`, structured `error`) |
87
+ | GET | `/tasks/:id/events?since=&limit=` | Get task events (paginated) |
88
+ | GET | `/tasks/:id/context` | Inspect the LLM context snapshot |
89
+ | POST | `/tasks/:id/respond` | Respond to a waiting task |
90
+ | POST | `/tasks/:id/cancel` | Cancel a task (cooperative cancel for processing tasks) |
91
+ | GET | `/sessions` | List sessions |
92
+ | GET | `/sessions/:id` | Get session details |
93
+ | GET | `/sessions/:id/messages` | Get session messages |
94
+ | GET | `/sessions/:id/context` | Inspect session message history |
95
+ | POST | `/sessions/:id/reset` | Clear messages, re-inject system prompt |
96
+ | DELETE | `/sessions/:id?hard=true` | Close session (`hard=true` = hard delete) |
97
+ | GET | `/daemons` | List running daemons |
98
+ | POST | `/agents/:name/daemon/start` | Start a daemon agent |
99
+ | POST | `/agents/:name/daemon/stop` | Stop a daemon agent |
100
+ | POST | `/agents/:name/daemon/trigger` | Manually trigger a daemon tick |
101
+ | GET | `/settings` | Get current settings (api_keys redacted) |
102
+ | PUT | `/settings` | Update settings with live reload |
103
+
104
+ ---
105
+
106
+ ## Detailed Reference
107
+
108
+ - [System endpoints](01-system.md)
109
+ - [Agents](02-agents.md) — includes agent-scoped sessions, tasks, skills
110
+ - [Chat](03-chat.md)
111
+ - [Tasks](04-tasks.md) — includes context, token budget, structured errors
112
+ - [Sessions](05-sessions.md) — includes context, reset, hard delete
113
+ - [Daemons](06-daemons.md)
114
+ - [Memory](07-memory.md)
115
+ - [Settings](08-settings.md)
116
+ - [WebSocket](09-websocket.md)