@newsails/veil-studio 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +181 -0
- package/bin/veil-studio.js +142 -0
- package/nuxt-app/.output/public/200.html +13 -0
- package/nuxt-app/.output/public/404.html +13 -0
- package/nuxt-app/.output/public/_nuxt/builds/latest.json +1 -0
- package/nuxt-app/.output/public/_nuxt/builds/meta/6b28df26-54af-4fad-a1f0-38808960d9fe.json +1 -0
- package/nuxt-app/.output/public/_nuxt/entry.BrrOeBSX.js +120 -0
- package/nuxt-app/.output/public/_nuxt/entry.CYnp7zY5.css +1 -0
- package/nuxt-app/.output/public/_nuxt/error-404.BbdzCaXe.js +1 -0
- package/nuxt-app/.output/public/_nuxt/error-404.JekaaCis.css +1 -0
- package/nuxt-app/.output/public/_nuxt/error-500.CNP9nqm1.css +1 -0
- package/nuxt-app/.output/public/_nuxt/error-500.DbOlBIIY.js +1 -0
- package/nuxt-app/.output/public/_nuxt/index.BEoXSIOu.css +1 -0
- package/nuxt-app/.output/public/_nuxt/index.CNms2yAq.js +1 -0
- package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.B7mPwVP_.ttf +0 -0
- package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.CSr8KVlo.eot +0 -0
- package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.Dp5v-WZN.woff2 +0 -0
- package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.PXm3-2wK.woff +0 -0
- package/nuxt-app/.output/public/_nuxt/vue.-sixQ7xP.BlWffD__.js +1 -0
- package/nuxt-app/.output/public/index.html +13 -0
- package/package.json +37 -0
- package/server/index.js +184 -0
- package/server/routes/files.js +80 -0
- package/server/socket.js +507 -0
- package/server/utils/board-state.js +357 -0
- package/server/utils/config.js +51 -0
- package/server/utils/db.js +484 -0
- package/server/utils/element-instances.js +104 -0
- package/server/utils/element-registry.js +127 -0
- package/server/utils/elements/agent-instance.js +62 -0
- package/server/utils/elements/agent.js +93 -0
- package/server/utils/elements/annotation.js +30 -0
- package/server/utils/elements/approval-gate.js +49 -0
- package/server/utils/elements/assumption.js +32 -0
- package/server/utils/elements/blocker.js +36 -0
- package/server/utils/elements/chat-room.js +47 -0
- package/server/utils/elements/chat-view.js +33 -0
- package/server/utils/elements/code-block.js +39 -0
- package/server/utils/elements/collapsible-group.js +37 -0
- package/server/utils/elements/comparison-table.js +38 -0
- package/server/utils/elements/constraint.js +32 -0
- package/server/utils/elements/decision.js +39 -0
- package/server/utils/elements/diff-patch.js +38 -0
- package/server/utils/elements/divider.js +30 -0
- package/server/utils/elements/document-draft.js +35 -0
- package/server/utils/elements/fact-claim.js +36 -0
- package/server/utils/elements/feedback-request.js +43 -0
- package/server/utils/elements/file-reference.js +31 -0
- package/server/utils/elements/filter.js +48 -0
- package/server/utils/elements/generator.js +51 -0
- package/server/utils/elements/goal.js +36 -0
- package/server/utils/elements/html.js +35 -0
- package/server/utils/elements/idea.js +32 -0
- package/server/utils/elements/image-local.js +21 -0
- package/server/utils/elements/image.js +34 -0
- package/server/utils/elements/json-object.js +47 -0
- package/server/utils/elements/label-tag.js +30 -0
- package/server/utils/elements/markdown.js +37 -0
- package/server/utils/elements/merger.js +36 -0
- package/server/utils/elements/message.js +40 -0
- package/server/utils/elements/milestone.js +44 -0
- package/server/utils/elements/notification.js +34 -0
- package/server/utils/elements/outline.js +35 -0
- package/server/utils/elements/primitive.js +36 -0
- package/server/utils/elements/pro-con-list.js +39 -0
- package/server/utils/elements/processor.js +54 -0
- package/server/utils/elements/project.js +40 -0
- package/server/utils/elements/question.js +42 -0
- package/server/utils/elements/queue.js +85 -0
- package/server/utils/elements/research-note.js +41 -0
- package/server/utils/elements/section.js +35 -0
- package/server/utils/elements/source-collection.js +45 -0
- package/server/utils/elements/splitter.js +42 -0
- package/server/utils/elements/status-update.js +38 -0
- package/server/utils/elements/task.js +72 -0
- package/server/utils/elements/template.js +46 -0
- package/server/utils/elements/test-case.js +42 -0
- package/server/utils/elements/text.js +29 -0
- package/server/utils/elements/todo-list.js +57 -0
- package/server/utils/elements/url-card.js +37 -0
- package/server/utils/elements/web-search-query.js +46 -0
- package/server/utils/elements/web-snapshot.js +37 -0
- package/server/utils/file-utils.js +88 -0
- package/server/utils/session-watcher.js +108 -0
- package/server/utils/socket-io.js +14 -0
- package/server/utils/veil-client.js +185 -0
- package/server/utils/veil-ws.js +207 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const WebSocket = require('ws')
|
|
4
|
+
const { VEIL_WS_URL, VEIL_SECRET } = require('./config.js')
|
|
5
|
+
const { getIO } = require('./socket-io.js')
|
|
6
|
+
const boardState = require('./board-state.js')
|
|
7
|
+
const { initSessionStates } = require('./session-watcher.js')
|
|
8
|
+
|
|
9
|
+
let _activeStreams = null
|
|
10
|
+
let _setElementStreamingFn = null
|
|
11
|
+
|
|
12
|
+
// Sessions that received iteration.start but not yet session.stream done
|
|
13
|
+
// Used to detect sse=false sessions (agent_message tool) and emit chat:done fallback
|
|
14
|
+
const _typingSessions = new Set()
|
|
15
|
+
|
|
16
|
+
function setActiveStreamsRef(ref) {
|
|
17
|
+
_activeStreams = ref
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function setElementStreamingRef(fn) {
|
|
21
|
+
_setElementStreamingFn = fn
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let _ws = null
|
|
25
|
+
let _connected = false
|
|
26
|
+
let _retryDelay = 2000
|
|
27
|
+
const MAX_RETRY_DELAY = 60000
|
|
28
|
+
|
|
29
|
+
function isVeilConnected() {
|
|
30
|
+
return _connected
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function connectVeilWS() {
|
|
34
|
+
const wsUrl = `${VEIL_WS_URL}/ws`
|
|
35
|
+
const headers = {}
|
|
36
|
+
if (VEIL_SECRET) headers['X-Veil-Secret'] = VEIL_SECRET
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
_ws = new WebSocket(wsUrl, { headers })
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.warn(`[veil-ws] Could not connect to ${wsUrl}: ${err.message}`)
|
|
42
|
+
_scheduleReconnect()
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
_ws.on('open', () => {
|
|
47
|
+
_connected = true
|
|
48
|
+
_retryDelay = 2000
|
|
49
|
+
console.log(`[veil-ws] Connected to ${wsUrl}`)
|
|
50
|
+
try {
|
|
51
|
+
getIO().emit('veil:status', { connected: true })
|
|
52
|
+
} catch (_) {}
|
|
53
|
+
initSessionStates().catch(e => console.warn('[veil-ws] initSessionStates:', e.message))
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
_ws.on('message', (data) => {
|
|
57
|
+
try {
|
|
58
|
+
const event = JSON.parse(data.toString())
|
|
59
|
+
try { getIO().emit('veil:event', event) } catch (_) {}
|
|
60
|
+
|
|
61
|
+
// Update element state from task/session events
|
|
62
|
+
_handleVeilEvent(event)
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.error('[veil-ws] Failed to parse message:', err.message)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
_ws.on('close', () => {
|
|
69
|
+
_connected = false
|
|
70
|
+
console.log('[veil-ws] Disconnected. Reconnecting...')
|
|
71
|
+
try { getIO().emit('veil:status', { connected: false }) } catch (_) {}
|
|
72
|
+
_scheduleReconnect()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
_ws.on('error', (err) => {
|
|
76
|
+
console.error('[veil-ws] Error:', err.message)
|
|
77
|
+
// close event will fire next
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function _scheduleReconnect() {
|
|
82
|
+
setTimeout(() => {
|
|
83
|
+
connectVeilWS()
|
|
84
|
+
_retryDelay = Math.min(_retryDelay * 2, MAX_RETRY_DELAY)
|
|
85
|
+
}, _retryDelay)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function _isExternalSession(sessionId) {
|
|
89
|
+
return !_activeStreams || !_activeStreams.has(sessionId)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function _handleVeilEvent(event) {
|
|
93
|
+
if (!event || !event.type) return
|
|
94
|
+
|
|
95
|
+
const io = getIO()
|
|
96
|
+
|
|
97
|
+
// ── session.stream — mirrors SSE stream for every session in real-time ──────
|
|
98
|
+
if (event.type === 'session.stream' && event.sessionId) {
|
|
99
|
+
if (!_isExternalSession(event.sessionId)) return // Studio's chat:send owns this
|
|
100
|
+
|
|
101
|
+
const { sessionId, eventType, data } = event
|
|
102
|
+
|
|
103
|
+
if (eventType === 'error') {
|
|
104
|
+
try { io.to('board').emit('chat:error', { sessionId, error: data?.error ?? 'Unknown error' }) } catch (_) {}
|
|
105
|
+
} else if (eventType === 'done') {
|
|
106
|
+
_typingSessions.delete(sessionId)
|
|
107
|
+
try {
|
|
108
|
+
io.to('board').emit('chat:stream', { sessionId, eventType: 'done', data })
|
|
109
|
+
io.to('board').emit('chat:done', { sessionId })
|
|
110
|
+
} catch (_) {}
|
|
111
|
+
// Clear element streaming indicator
|
|
112
|
+
if (_setElementStreamingFn) _setElementStreamingFn(sessionId, false)
|
|
113
|
+
} else {
|
|
114
|
+
// inference.chunk, inference.tool, message (assistant/tool)
|
|
115
|
+
try { io.to('board').emit('chat:stream', { sessionId, eventType, data }) } catch (_) {}
|
|
116
|
+
}
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ── chat.user_message — user message before AI starts ───────────────────────
|
|
121
|
+
if (event.type === 'chat.user_message' && event.sessionId) {
|
|
122
|
+
if (!_isExternalSession(event.sessionId)) return
|
|
123
|
+
try {
|
|
124
|
+
io.to('board').emit('chat:user-message', {
|
|
125
|
+
sessionId: event.sessionId,
|
|
126
|
+
agentName: event.agentName || '',
|
|
127
|
+
message: event.event?.content ?? '',
|
|
128
|
+
})
|
|
129
|
+
} catch (_) {}
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ── chat.event — iteration start → typing indicator + element streaming ─────
|
|
134
|
+
if (event.type === 'chat.event' && event.sessionId) {
|
|
135
|
+
if (event.event?.type === 'iteration.start' && _isExternalSession(event.sessionId)) {
|
|
136
|
+
_typingSessions.add(event.sessionId)
|
|
137
|
+
try { io.to('board').emit('chat:typing', { sessionId: event.sessionId }) } catch (_) {}
|
|
138
|
+
if (_setElementStreamingFn) _setElementStreamingFn(event.sessionId, true)
|
|
139
|
+
}
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ── chat.inference_tool — tool indicator for non-SSE sessions ───────────────
|
|
144
|
+
// Equivalent of session.stream eventType inference.tool for HTTP/agent_message paths
|
|
145
|
+
if (event.type === 'chat.inference_tool' && event.sessionId) {
|
|
146
|
+
if (_isExternalSession(event.sessionId)) {
|
|
147
|
+
try {
|
|
148
|
+
io.to('board').emit('chat:stream', {
|
|
149
|
+
sessionId: event.sessionId,
|
|
150
|
+
eventType: 'inference.tool',
|
|
151
|
+
data: { name: event.event?.name ?? '' },
|
|
152
|
+
})
|
|
153
|
+
} catch (_) {}
|
|
154
|
+
}
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ── chat.message — sse=false final message (agent_message tool invocation) ────
|
|
159
|
+
// Emitted instead of session.stream events when the session was called with sse=false
|
|
160
|
+
if (event.type === 'chat.message' && event.sessionId) {
|
|
161
|
+
if (_isExternalSession(event.sessionId)) {
|
|
162
|
+
try {
|
|
163
|
+
io.to('board').emit('chat:stream', {
|
|
164
|
+
sessionId: event.sessionId,
|
|
165
|
+
eventType: 'message',
|
|
166
|
+
data: event.event,
|
|
167
|
+
})
|
|
168
|
+
} catch (_) {}
|
|
169
|
+
}
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── chat.response — fallback: stop streaming if session.stream done never fired ─
|
|
174
|
+
if (event.type === 'chat.response' && event.sessionId) {
|
|
175
|
+
const sessionId = event.sessionId
|
|
176
|
+
if (_isExternalSession(sessionId) && _typingSessions.has(sessionId)) {
|
|
177
|
+
_typingSessions.delete(sessionId)
|
|
178
|
+
try {
|
|
179
|
+
io.to('board').emit('chat:done', { sessionId })
|
|
180
|
+
} catch (_) {}
|
|
181
|
+
if (_setElementStreamingFn) _setElementStreamingFn(sessionId, false)
|
|
182
|
+
}
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── task.status — update task element state on board ────────────────────────
|
|
187
|
+
if (event.type === 'task.status' && event.taskId && event.event) {
|
|
188
|
+
const status = event.event.status
|
|
189
|
+
if (!status) return
|
|
190
|
+
const stateMap = {
|
|
191
|
+
running: 'running',
|
|
192
|
+
finished: 'completed',
|
|
193
|
+
failed: 'error',
|
|
194
|
+
cancelled: 'default',
|
|
195
|
+
}
|
|
196
|
+
const elementState = stateMap[status]
|
|
197
|
+
if (!elementState) return
|
|
198
|
+
|
|
199
|
+
for (const el of boardState.getElements()) {
|
|
200
|
+
if (el.type === 'task' && el.data && el.data.taskId === event.taskId) {
|
|
201
|
+
boardState.updateElementState(el.id, elementState)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports = { connectVeilWS, isVeilConnected, setActiveStreamsRef, setElementStreamingRef }
|