@sna-sdk/core 0.7.1 → 0.7.2
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.
|
@@ -253,7 +253,7 @@ function createAgentRoutes(sessionManager) {
|
|
|
253
253
|
} else {
|
|
254
254
|
cursor = session.eventCounter;
|
|
255
255
|
}
|
|
256
|
-
while (queue.length > 0 && queue[0].cursor <= cursor) queue.shift();
|
|
256
|
+
while (queue.length > 0 && queue[0].cursor !== -1 && queue[0].cursor <= cursor) queue.shift();
|
|
257
257
|
while (!signal.aborted) {
|
|
258
258
|
if (queue.length === 0) {
|
|
259
259
|
await Promise.race([
|
|
@@ -267,7 +267,11 @@ function createAgentRoutes(sessionManager) {
|
|
|
267
267
|
if (queue.length > 0) {
|
|
268
268
|
while (queue.length > 0) {
|
|
269
269
|
const item = queue.shift();
|
|
270
|
-
|
|
270
|
+
if (item.cursor === -1) {
|
|
271
|
+
await stream.writeSSE({ data: JSON.stringify(item.event) });
|
|
272
|
+
} else {
|
|
273
|
+
await stream.writeSSE({ id: String(item.cursor), data: JSON.stringify(item.event) });
|
|
274
|
+
}
|
|
271
275
|
}
|
|
272
276
|
} else {
|
|
273
277
|
await stream.writeSSE({ data: "" });
|
|
@@ -108,6 +108,12 @@ declare class SessionManager {
|
|
|
108
108
|
/** Broadcast a skill event to all subscribers (called after DB insert). */
|
|
109
109
|
broadcastSkillEvent(event: Record<string, unknown>): void;
|
|
110
110
|
/** Push a synthetic event into a session's event stream (for user message broadcast). */
|
|
111
|
+
/**
|
|
112
|
+
* Push an externally-persisted event into the session.
|
|
113
|
+
* The caller is responsible for DB persistence — this method only updates
|
|
114
|
+
* the in-memory counter/buffer and notifies listeners.
|
|
115
|
+
* eventCounter increments to stay in sync with the DB row count.
|
|
116
|
+
*/
|
|
111
117
|
pushEvent(sessionId: string, event: AgentEvent): void;
|
|
112
118
|
/** Subscribe to permission request notifications. Returns unsubscribe function. */
|
|
113
119
|
onPermissionRequest(cb: (sessionId: string, request: Record<string, unknown>, createdAt: number) => void): () => void;
|
|
@@ -165,6 +171,7 @@ declare class SessionManager {
|
|
|
165
171
|
touch(id: string): void;
|
|
166
172
|
/** Persist an agent event to chat_messages. */
|
|
167
173
|
private getMessageStats;
|
|
174
|
+
/** Persist an agent event to chat_messages. Returns true if a row was inserted. */
|
|
168
175
|
private persistEvent;
|
|
169
176
|
/** Kill all sessions. Used during shutdown. */
|
|
170
177
|
killAll(): void;
|
|
@@ -143,22 +143,27 @@ class SessionManager {
|
|
|
143
143
|
}
|
|
144
144
|
this.setSessionState(sessionId, session, "waiting");
|
|
145
145
|
}
|
|
146
|
-
if (e.type !== "assistant_delta") {
|
|
147
|
-
session.eventBuffer.push(e);
|
|
148
|
-
if (session.eventBuffer.length > MAX_EVENT_BUFFER) {
|
|
149
|
-
session.eventBuffer.splice(0, session.eventBuffer.length - MAX_EVENT_BUFFER);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
session.eventCounter++;
|
|
153
146
|
if (e.type === "thinking" || e.type === "tool_use" || e.type === "assistant_delta") {
|
|
154
147
|
this.setSessionState(sessionId, session, "processing");
|
|
155
148
|
} else if (e.type === "complete" || e.type === "error" || e.type === "interrupted") {
|
|
156
149
|
this.setSessionState(sessionId, session, "waiting");
|
|
157
150
|
}
|
|
158
|
-
this.persistEvent(sessionId, e);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
151
|
+
const persisted = this.persistEvent(sessionId, e);
|
|
152
|
+
if (persisted) {
|
|
153
|
+
session.eventCounter++;
|
|
154
|
+
session.eventBuffer.push(e);
|
|
155
|
+
if (session.eventBuffer.length > MAX_EVENT_BUFFER) {
|
|
156
|
+
session.eventBuffer.splice(0, session.eventBuffer.length - MAX_EVENT_BUFFER);
|
|
157
|
+
}
|
|
158
|
+
const listeners = this.eventListeners.get(sessionId);
|
|
159
|
+
if (listeners) {
|
|
160
|
+
for (const cb of listeners) cb(session.eventCounter, e);
|
|
161
|
+
}
|
|
162
|
+
} else if (e.type === "assistant_delta") {
|
|
163
|
+
const listeners = this.eventListeners.get(sessionId);
|
|
164
|
+
if (listeners) {
|
|
165
|
+
for (const cb of listeners) cb(-1, e);
|
|
166
|
+
}
|
|
162
167
|
}
|
|
163
168
|
});
|
|
164
169
|
proc.on("exit", (code) => {
|
|
@@ -196,11 +201,17 @@ class SessionManager {
|
|
|
196
201
|
for (const cb of this.skillEventListeners) cb(event);
|
|
197
202
|
}
|
|
198
203
|
/** Push a synthetic event into a session's event stream (for user message broadcast). */
|
|
204
|
+
/**
|
|
205
|
+
* Push an externally-persisted event into the session.
|
|
206
|
+
* The caller is responsible for DB persistence — this method only updates
|
|
207
|
+
* the in-memory counter/buffer and notifies listeners.
|
|
208
|
+
* eventCounter increments to stay in sync with the DB row count.
|
|
209
|
+
*/
|
|
199
210
|
pushEvent(sessionId, event) {
|
|
200
211
|
const session = this.sessions.get(sessionId);
|
|
201
212
|
if (!session) return;
|
|
202
|
-
session.eventBuffer.push(event);
|
|
203
213
|
session.eventCounter++;
|
|
214
|
+
session.eventBuffer.push(event);
|
|
204
215
|
if (session.eventBuffer.length > MAX_EVENT_BUFFER) {
|
|
205
216
|
session.eventBuffer.splice(0, session.eventBuffer.length - MAX_EVENT_BUFFER);
|
|
206
217
|
}
|
|
@@ -425,6 +436,7 @@ class SessionManager {
|
|
|
425
436
|
return { messageCount: 0, lastMessage: null };
|
|
426
437
|
}
|
|
427
438
|
}
|
|
439
|
+
/** Persist an agent event to chat_messages. Returns true if a row was inserted. */
|
|
428
440
|
persistEvent(sessionId, e) {
|
|
429
441
|
try {
|
|
430
442
|
const db = getDb();
|
|
@@ -432,29 +444,34 @@ class SessionManager {
|
|
|
432
444
|
case "assistant":
|
|
433
445
|
if (e.message) {
|
|
434
446
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content) VALUES (?, 'assistant', ?)`).run(sessionId, e.message);
|
|
447
|
+
return true;
|
|
435
448
|
}
|
|
436
|
-
|
|
449
|
+
return false;
|
|
437
450
|
case "thinking":
|
|
438
451
|
if (e.message) {
|
|
439
452
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content) VALUES (?, 'thinking', ?)`).run(sessionId, e.message);
|
|
453
|
+
return true;
|
|
440
454
|
}
|
|
441
|
-
|
|
455
|
+
return false;
|
|
442
456
|
case "tool_use": {
|
|
443
457
|
const toolName = e.data?.toolName ?? e.message ?? "tool";
|
|
444
458
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content, meta) VALUES (?, 'tool', ?, ?)`).run(sessionId, toolName, JSON.stringify(e.data ?? {}));
|
|
445
|
-
|
|
459
|
+
return true;
|
|
446
460
|
}
|
|
447
461
|
case "tool_result":
|
|
448
462
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content, meta) VALUES (?, 'tool_result', ?, ?)`).run(sessionId, e.message ?? "", JSON.stringify(e.data ?? {}));
|
|
449
|
-
|
|
463
|
+
return true;
|
|
450
464
|
case "complete":
|
|
451
465
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content, meta) VALUES (?, 'status', '', ?)`).run(sessionId, JSON.stringify({ status: "complete", ...e.data }));
|
|
452
|
-
|
|
466
|
+
return true;
|
|
453
467
|
case "error":
|
|
454
468
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content, meta) VALUES (?, 'error', ?, ?)`).run(sessionId, e.message ?? "Error", JSON.stringify({ status: "error" }));
|
|
455
|
-
|
|
469
|
+
return true;
|
|
470
|
+
default:
|
|
471
|
+
return false;
|
|
456
472
|
}
|
|
457
473
|
} catch {
|
|
474
|
+
return false;
|
|
458
475
|
}
|
|
459
476
|
}
|
|
460
477
|
/** Kill all sessions. Used during shutdown. */
|
|
@@ -1115,7 +1115,7 @@ function createAgentRoutes(sessionManager2) {
|
|
|
1115
1115
|
} else {
|
|
1116
1116
|
cursor = session.eventCounter;
|
|
1117
1117
|
}
|
|
1118
|
-
while (queue.length > 0 && queue[0].cursor <= cursor) queue.shift();
|
|
1118
|
+
while (queue.length > 0 && queue[0].cursor !== -1 && queue[0].cursor <= cursor) queue.shift();
|
|
1119
1119
|
while (!signal.aborted) {
|
|
1120
1120
|
if (queue.length === 0) {
|
|
1121
1121
|
await Promise.race([
|
|
@@ -1129,7 +1129,11 @@ function createAgentRoutes(sessionManager2) {
|
|
|
1129
1129
|
if (queue.length > 0) {
|
|
1130
1130
|
while (queue.length > 0) {
|
|
1131
1131
|
const item = queue.shift();
|
|
1132
|
-
|
|
1132
|
+
if (item.cursor === -1) {
|
|
1133
|
+
await stream.writeSSE({ data: JSON.stringify(item.event) });
|
|
1134
|
+
} else {
|
|
1135
|
+
await stream.writeSSE({ id: String(item.cursor), data: JSON.stringify(item.event) });
|
|
1136
|
+
}
|
|
1133
1137
|
}
|
|
1134
1138
|
} else {
|
|
1135
1139
|
await stream.writeSSE({ data: "" });
|
|
@@ -1544,22 +1548,27 @@ var SessionManager = class {
|
|
|
1544
1548
|
}
|
|
1545
1549
|
this.setSessionState(sessionId, session, "waiting");
|
|
1546
1550
|
}
|
|
1547
|
-
if (e.type !== "assistant_delta") {
|
|
1548
|
-
session.eventBuffer.push(e);
|
|
1549
|
-
if (session.eventBuffer.length > MAX_EVENT_BUFFER) {
|
|
1550
|
-
session.eventBuffer.splice(0, session.eventBuffer.length - MAX_EVENT_BUFFER);
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
session.eventCounter++;
|
|
1554
1551
|
if (e.type === "thinking" || e.type === "tool_use" || e.type === "assistant_delta") {
|
|
1555
1552
|
this.setSessionState(sessionId, session, "processing");
|
|
1556
1553
|
} else if (e.type === "complete" || e.type === "error" || e.type === "interrupted") {
|
|
1557
1554
|
this.setSessionState(sessionId, session, "waiting");
|
|
1558
1555
|
}
|
|
1559
|
-
this.persistEvent(sessionId, e);
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1556
|
+
const persisted = this.persistEvent(sessionId, e);
|
|
1557
|
+
if (persisted) {
|
|
1558
|
+
session.eventCounter++;
|
|
1559
|
+
session.eventBuffer.push(e);
|
|
1560
|
+
if (session.eventBuffer.length > MAX_EVENT_BUFFER) {
|
|
1561
|
+
session.eventBuffer.splice(0, session.eventBuffer.length - MAX_EVENT_BUFFER);
|
|
1562
|
+
}
|
|
1563
|
+
const listeners = this.eventListeners.get(sessionId);
|
|
1564
|
+
if (listeners) {
|
|
1565
|
+
for (const cb of listeners) cb(session.eventCounter, e);
|
|
1566
|
+
}
|
|
1567
|
+
} else if (e.type === "assistant_delta") {
|
|
1568
|
+
const listeners = this.eventListeners.get(sessionId);
|
|
1569
|
+
if (listeners) {
|
|
1570
|
+
for (const cb of listeners) cb(-1, e);
|
|
1571
|
+
}
|
|
1563
1572
|
}
|
|
1564
1573
|
});
|
|
1565
1574
|
proc.on("exit", (code) => {
|
|
@@ -1597,11 +1606,17 @@ var SessionManager = class {
|
|
|
1597
1606
|
for (const cb of this.skillEventListeners) cb(event);
|
|
1598
1607
|
}
|
|
1599
1608
|
/** Push a synthetic event into a session's event stream (for user message broadcast). */
|
|
1609
|
+
/**
|
|
1610
|
+
* Push an externally-persisted event into the session.
|
|
1611
|
+
* The caller is responsible for DB persistence — this method only updates
|
|
1612
|
+
* the in-memory counter/buffer and notifies listeners.
|
|
1613
|
+
* eventCounter increments to stay in sync with the DB row count.
|
|
1614
|
+
*/
|
|
1600
1615
|
pushEvent(sessionId, event) {
|
|
1601
1616
|
const session = this.sessions.get(sessionId);
|
|
1602
1617
|
if (!session) return;
|
|
1603
|
-
session.eventBuffer.push(event);
|
|
1604
1618
|
session.eventCounter++;
|
|
1619
|
+
session.eventBuffer.push(event);
|
|
1605
1620
|
if (session.eventBuffer.length > MAX_EVENT_BUFFER) {
|
|
1606
1621
|
session.eventBuffer.splice(0, session.eventBuffer.length - MAX_EVENT_BUFFER);
|
|
1607
1622
|
}
|
|
@@ -1826,6 +1841,7 @@ var SessionManager = class {
|
|
|
1826
1841
|
return { messageCount: 0, lastMessage: null };
|
|
1827
1842
|
}
|
|
1828
1843
|
}
|
|
1844
|
+
/** Persist an agent event to chat_messages. Returns true if a row was inserted. */
|
|
1829
1845
|
persistEvent(sessionId, e) {
|
|
1830
1846
|
try {
|
|
1831
1847
|
const db = getDb();
|
|
@@ -1833,29 +1849,34 @@ var SessionManager = class {
|
|
|
1833
1849
|
case "assistant":
|
|
1834
1850
|
if (e.message) {
|
|
1835
1851
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content) VALUES (?, 'assistant', ?)`).run(sessionId, e.message);
|
|
1852
|
+
return true;
|
|
1836
1853
|
}
|
|
1837
|
-
|
|
1854
|
+
return false;
|
|
1838
1855
|
case "thinking":
|
|
1839
1856
|
if (e.message) {
|
|
1840
1857
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content) VALUES (?, 'thinking', ?)`).run(sessionId, e.message);
|
|
1858
|
+
return true;
|
|
1841
1859
|
}
|
|
1842
|
-
|
|
1860
|
+
return false;
|
|
1843
1861
|
case "tool_use": {
|
|
1844
1862
|
const toolName = e.data?.toolName ?? e.message ?? "tool";
|
|
1845
1863
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content, meta) VALUES (?, 'tool', ?, ?)`).run(sessionId, toolName, JSON.stringify(e.data ?? {}));
|
|
1846
|
-
|
|
1864
|
+
return true;
|
|
1847
1865
|
}
|
|
1848
1866
|
case "tool_result":
|
|
1849
1867
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content, meta) VALUES (?, 'tool_result', ?, ?)`).run(sessionId, e.message ?? "", JSON.stringify(e.data ?? {}));
|
|
1850
|
-
|
|
1868
|
+
return true;
|
|
1851
1869
|
case "complete":
|
|
1852
1870
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content, meta) VALUES (?, 'status', '', ?)`).run(sessionId, JSON.stringify({ status: "complete", ...e.data }));
|
|
1853
|
-
|
|
1871
|
+
return true;
|
|
1854
1872
|
case "error":
|
|
1855
1873
|
db.prepare(`INSERT INTO chat_messages (session_id, role, content, meta) VALUES (?, 'error', ?, ?)`).run(sessionId, e.message ?? "Error", JSON.stringify({ status: "error" }));
|
|
1856
|
-
|
|
1874
|
+
return true;
|
|
1875
|
+
default:
|
|
1876
|
+
return false;
|
|
1857
1877
|
}
|
|
1858
1878
|
} catch {
|
|
1879
|
+
return false;
|
|
1859
1880
|
}
|
|
1860
1881
|
}
|
|
1861
1882
|
/** Kill all sessions. Used during shutdown. */
|
|
@@ -2326,7 +2347,11 @@ function handleAgentSubscribe(ws, msg, sm, state) {
|
|
|
2326
2347
|
}
|
|
2327
2348
|
}
|
|
2328
2349
|
const unsub = sm.onSessionEvent(sessionId, (eventCursor, event) => {
|
|
2329
|
-
|
|
2350
|
+
if (eventCursor === -1) {
|
|
2351
|
+
send(ws, { type: "agent.event", session: sessionId, event });
|
|
2352
|
+
} else {
|
|
2353
|
+
send(ws, { type: "agent.event", session: sessionId, cursor: eventCursor, event });
|
|
2354
|
+
}
|
|
2330
2355
|
});
|
|
2331
2356
|
state.agentUnsubs.set(sessionId, unsub);
|
|
2332
2357
|
reply(ws, msg, { cursor });
|
package/dist/server/ws.js
CHANGED
|
@@ -459,7 +459,11 @@ function handleAgentSubscribe(ws, msg, sm, state) {
|
|
|
459
459
|
}
|
|
460
460
|
}
|
|
461
461
|
const unsub = sm.onSessionEvent(sessionId, (eventCursor, event) => {
|
|
462
|
-
|
|
462
|
+
if (eventCursor === -1) {
|
|
463
|
+
send(ws, { type: "agent.event", session: sessionId, event });
|
|
464
|
+
} else {
|
|
465
|
+
send(ws, { type: "agent.event", session: sessionId, cursor: eventCursor, event });
|
|
466
|
+
}
|
|
463
467
|
});
|
|
464
468
|
state.agentUnsubs.set(sessionId, unsub);
|
|
465
469
|
reply(ws, msg, { cursor });
|