@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.
- package/.veil/agents/analyst/AGENT.md +21 -0
- package/.veil/agents/analyst/agent.json +23 -0
- package/.veil/agents/assistant/AGENT.md +15 -0
- package/.veil/agents/assistant/agent.json +19 -0
- package/.veil/agents/coder/AGENT.md +18 -0
- package/.veil/agents/coder/agent.json +19 -0
- package/.veil/agents/hello/AGENT.md +5 -0
- package/.veil/agents/hello/agent.json +13 -0
- package/.veil/agents/writer/AGENT.md +12 -0
- package/.veil/agents/writer/agent.json +17 -0
- package/.veil/memory/MEMORY.md +343 -0
- package/.veil/memory/agents/analyst/MEMORY.md +55 -0
- package/.veil/memory/agents/hello/MEMORY.md +12 -0
- package/.veil/runtime.pid +1 -0
- package/.veil/settings.json +10 -0
- package/.veil-studio/studio.db +0 -0
- package/.veil-studio/studio.db-shm +0 -0
- package/.veil-studio/studio.db-wal +0 -0
- package/PLAN/01-vision.md +26 -0
- package/PLAN/02-tech-stack.md +94 -0
- package/PLAN/03-agents.md +232 -0
- package/PLAN/04-runtime.md +171 -0
- package/PLAN/05-tools.md +211 -0
- package/PLAN/06-communication.md +243 -0
- package/PLAN/07-storage.md +218 -0
- package/PLAN/08-api-cli.md +153 -0
- package/PLAN/09-permissions.md +108 -0
- package/PLAN/10-ably.md +105 -0
- package/PLAN/11-file-formats.md +442 -0
- package/PLAN/12-folder-structure.md +205 -0
- package/PLAN/13-operations.md +212 -0
- package/PLAN/README.md +23 -0
- package/README.md +128 -0
- package/REPORT.md +174 -0
- package/TODO.md +45 -0
- package/ai-tests/FRONTEND_PROMPT.md +220 -0
- package/ai-tests/Research & Planning.md +814 -0
- package/ai-tests/prompt-001-basic-api.md +230 -0
- package/ai-tests/prompt-002-basic-flows.md +230 -0
- package/ai-tests/prompt-003-agent-behaviors.md +220 -0
- package/api/middleware.js +60 -0
- package/api/routes/agents.js +193 -0
- package/api/routes/chat.js +93 -0
- package/api/routes/completions.js +122 -0
- package/api/routes/daemons.js +80 -0
- package/api/routes/memory.js +169 -0
- package/api/routes/models.js +40 -0
- package/api/routes/remote-methods.js +74 -0
- package/api/routes/sessions.js +208 -0
- package/api/routes/settings.js +108 -0
- package/api/routes/system.js +50 -0
- package/api/routes/tasks.js +270 -0
- package/api/server.js +120 -0
- package/cli/formatter.js +70 -0
- package/cli/index.js +443 -0
- package/cli/parser.js +113 -0
- package/config/config.json +10 -0
- package/config/models.json +6826 -0
- package/core/agent.js +329 -0
- package/core/cancel.js +38 -0
- package/core/compaction.js +176 -0
- package/core/events.js +13 -0
- package/core/loop.js +564 -0
- package/core/memory.js +51 -0
- package/core/prompt.js +185 -0
- package/core/queue.js +96 -0
- package/core/registry.js +291 -0
- package/core/remote-methods.js +124 -0
- package/core/router.js +386 -0
- package/core/running-sessions.js +18 -0
- package/docs/api/01-system.md +84 -0
- package/docs/api/02-agents.md +374 -0
- package/docs/api/03-chat.md +269 -0
- package/docs/api/04-tasks.md +470 -0
- package/docs/api/05-sessions.md +444 -0
- package/docs/api/06-daemons.md +142 -0
- package/docs/api/07-memory.md +186 -0
- package/docs/api/08-settings.md +133 -0
- package/docs/api/09-models.md +119 -0
- package/docs/api/09-websocket.md +350 -0
- package/docs/api/10-completions.md +134 -0
- package/docs/api/README.md +116 -0
- package/docs/guide/01-quickstart.md +220 -0
- package/docs/guide/02-folder-structure.md +185 -0
- package/docs/guide/03-configuration.md +252 -0
- package/docs/guide/04-agents.md +267 -0
- package/docs/guide/05-cli.md +290 -0
- package/docs/guide/06-tools.md +643 -0
- package/docs/guide/07-permissions.md +236 -0
- package/docs/guide/08-memory.md +139 -0
- package/docs/guide/09-multi-agent.md +271 -0
- package/docs/guide/10-daemons.md +226 -0
- package/docs/guide/README.md +53 -0
- package/docs/index.html +623 -0
- package/examples/README.md +151 -0
- package/examples/agents/assistant/AGENT.md +31 -0
- package/examples/agents/assistant/SOUL.md +9 -0
- package/examples/agents/assistant/agent.json +74 -0
- package/examples/agents/hello/AGENT.md +15 -0
- package/examples/agents/hello/agent.json +14 -0
- package/examples/agents/monitor/AGENT.md +51 -0
- package/examples/agents/monitor/agent.json +33 -0
- package/examples/agents/monitor/heartbeats/monitor.md +24 -0
- package/examples/agents/orchestrator/AGENT.md +70 -0
- package/examples/agents/orchestrator/agent.json +30 -0
- package/examples/agents/researcher/AGENT.md +52 -0
- package/examples/agents/researcher/agent.json +49 -0
- package/examples/agents/researcher/skills/web-research.md +28 -0
- package/examples/skills/code-review.md +72 -0
- package/examples/skills/summarise.md +59 -0
- package/examples/skills/web-research.md +42 -0
- package/examples/tools/word-count/index.js +27 -0
- package/examples/tools/word-count/tool.json +18 -0
- package/infrastructure/database.js +563 -0
- package/infrastructure/scheduler.js +122 -0
- package/llm/client.js +206 -0
- package/migrations/001-initial.sql +121 -0
- package/migrations/002-debuggability.sql +13 -0
- package/migrations/003-drop-orphaned-columns.sql +72 -0
- package/migrations/004-session-message-token-fields.sql +78 -0
- package/migrations/005-session-thinking.sql +5 -0
- package/package.json +30 -0
- package/schemas/agent.json +143 -0
- package/schemas/settings.json +111 -0
- package/scripts/fetch-models.js +93 -0
- package/session-debug-scenario.md +248 -0
- package/settings/fields.js +52 -0
- package/system-prompts/base-core.md +7 -0
- package/system-prompts/environment.md +13 -0
- package/system-prompts/reminders/anti-drift.md +6 -0
- package/system-prompts/reminders/stall-recovery.md +10 -0
- package/system-prompts/safety-rules.md +25 -0
- package/system-prompts/task-heuristics.md +27 -0
- package/test/client.js +71 -0
- package/test/integration/01-health.test.js +25 -0
- package/test/integration/02-agents.test.js +80 -0
- package/test/integration/03-chat-hello.test.js +48 -0
- package/test/integration/04-chat-multiturn.test.js +61 -0
- package/test/integration/05-chat-writer.test.js +48 -0
- package/test/integration/06-task-basic.test.js +68 -0
- package/test/integration/07-task-tools.test.js +74 -0
- package/test/integration/08-task-code-analysis.test.js +69 -0
- package/test/integration/09-memory-analyst.test.js +63 -0
- package/test/integration/10-task-advanced.test.js +85 -0
- package/test/integration/11-sessions-advanced.test.js +84 -0
- package/test/integration/12-assistant-chat-tools.test.js +75 -0
- package/test/integration/13-edge-cases.test.js +99 -0
- package/test/integration/14-cancel.test.js +62 -0
- package/test/integration/15-debug.test.js +106 -0
- package/test/integration/16-memory-api.test.js +83 -0
- package/test/integration/17-settings-api.test.js +41 -0
- package/test/integration/18-tool-search-activation.test.js +119 -0
- package/test/results/.gitkeep +0 -0
- package/test/runner.js +206 -0
- package/test/smoke.js +216 -0
- package/tools/agent_message.js +85 -0
- package/tools/agent_send.js +80 -0
- package/tools/agent_spawn.js +44 -0
- package/tools/bash.js +49 -0
- package/tools/edit_file.js +41 -0
- package/tools/glob.js +64 -0
- package/tools/grep.js +82 -0
- package/tools/list_dir.js +63 -0
- package/tools/log_write.js +31 -0
- package/tools/memory_read.js +38 -0
- package/tools/memory_search.js +65 -0
- package/tools/memory_write.js +42 -0
- package/tools/read_file.js +48 -0
- package/tools/sleep.js +22 -0
- package/tools/task_create.js +41 -0
- package/tools/task_respond.js +37 -0
- package/tools/task_spawn.js +64 -0
- package/tools/task_status.js +39 -0
- package/tools/task_subscribe.js +37 -0
- package/tools/todo_read.js +26 -0
- package/tools/todo_write.js +38 -0
- package/tools/tool_activate.js +24 -0
- package/tools/tool_search.js +24 -0
- package/tools/web_fetch.js +50 -0
- package/tools/web_search.js +52 -0
- package/tools/write_file.js +28 -0
- package/ui/api.js +190 -0
- package/ui/app.js +281 -0
- package/ui/index.html +382 -0
- package/ui/views/agents.js +377 -0
- package/ui/views/chat.js +610 -0
- package/ui/views/connection.js +96 -0
- package/ui/views/daemons.js +129 -0
- package/ui/views/feed.js +194 -0
- package/ui/views/memory.js +263 -0
- package/ui/views/models.js +146 -0
- package/ui/views/sessions.js +314 -0
- package/ui/views/settings.js +142 -0
- package/ui/views/tasks.js +415 -0
- package/utils/context.js +49 -0
- package/utils/id.js +16 -0
- package/utils/models.js +88 -0
- package/utils/paths.js +213 -0
- 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)
|