@ash-cloud/ash-ai 0.1.15 → 0.1.17
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/dist/index.cjs +723 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +212 -7
- package/dist/index.d.ts +212 -7
- package/dist/index.js +723 -29
- package/dist/index.js.map +1 -1
- package/dist/playground/components/NormalizedMessageList.d.ts +4 -11
- package/dist/playground/components/NormalizedMessageList.d.ts.map +1 -1
- package/dist/playground/components/ToolCallCard.d.ts +20 -5
- package/dist/playground/components/ToolCallCard.d.ts.map +1 -1
- package/dist/playground/index.d.ts +19 -4
- package/dist/playground/index.d.ts.map +1 -1
- package/dist/playground/pages/ChatPage.d.ts.map +1 -1
- package/dist/playground.css +1 -1
- package/dist/playground.js +3012 -2293
- package/dist/{schema-DSLyNeoS.d.cts → schema-dr_7pxIB.d.cts} +140 -2
- package/dist/{schema-DSLyNeoS.d.ts → schema-dr_7pxIB.d.ts} +140 -2
- package/dist/schema.cjs +25 -1
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +24 -2
- package/dist/schema.js.map +1 -1
- package/package.json +4 -2
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var zod = require('zod');
|
|
4
4
|
var child_process = require('child_process');
|
|
5
|
+
var events = require('events');
|
|
5
6
|
var nanoid = require('nanoid');
|
|
6
7
|
var fs = require('fs/promises');
|
|
7
8
|
var path4 = require('path');
|
|
@@ -384,6 +385,85 @@ var init_errors = __esm({
|
|
|
384
385
|
}
|
|
385
386
|
});
|
|
386
387
|
|
|
388
|
+
// src/relay/stream-event-writer.ts
|
|
389
|
+
exports.StreamEventWriter = void 0;
|
|
390
|
+
var init_stream_event_writer = __esm({
|
|
391
|
+
"src/relay/stream-event-writer.ts"() {
|
|
392
|
+
exports.StreamEventWriter = class {
|
|
393
|
+
storage;
|
|
394
|
+
sessionId;
|
|
395
|
+
emitter;
|
|
396
|
+
flushIntervalMs;
|
|
397
|
+
maxBatchChars;
|
|
398
|
+
// Text delta batching state
|
|
399
|
+
pendingDelta = "";
|
|
400
|
+
pendingDeltaCount = 0;
|
|
401
|
+
flushTimer = null;
|
|
402
|
+
closed = false;
|
|
403
|
+
constructor(options) {
|
|
404
|
+
this.storage = options.storage;
|
|
405
|
+
this.sessionId = options.sessionId;
|
|
406
|
+
this.emitter = options.emitter;
|
|
407
|
+
this.flushIntervalMs = options.flushIntervalMs ?? 100;
|
|
408
|
+
this.maxBatchChars = options.maxBatchChars ?? 500;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Write a stream event. Text deltas are batched; all other events are written immediately.
|
|
412
|
+
*/
|
|
413
|
+
async write(event) {
|
|
414
|
+
if (this.closed) return;
|
|
415
|
+
if (this.emitter) {
|
|
416
|
+
this.emitter.emit("event", event);
|
|
417
|
+
}
|
|
418
|
+
if (event.type === "text_delta") {
|
|
419
|
+
this.pendingDelta += event.delta;
|
|
420
|
+
this.pendingDeltaCount++;
|
|
421
|
+
if (!this.flushTimer) {
|
|
422
|
+
this.flushTimer = setTimeout(() => this.flush(), this.flushIntervalMs);
|
|
423
|
+
}
|
|
424
|
+
if (this.pendingDelta.length >= this.maxBatchChars) {
|
|
425
|
+
await this.flush();
|
|
426
|
+
}
|
|
427
|
+
} else {
|
|
428
|
+
await this.flush();
|
|
429
|
+
await this.writeToStorage([{ eventType: event.type, payload: event, batchCount: 1 }]);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Flush any pending text_delta events to storage as a single batched event.
|
|
434
|
+
*/
|
|
435
|
+
async flush() {
|
|
436
|
+
if (this.flushTimer) {
|
|
437
|
+
clearTimeout(this.flushTimer);
|
|
438
|
+
this.flushTimer = null;
|
|
439
|
+
}
|
|
440
|
+
if (this.pendingDelta.length === 0) return;
|
|
441
|
+
const batchedEvent = {
|
|
442
|
+
type: "text_delta",
|
|
443
|
+
delta: this.pendingDelta
|
|
444
|
+
};
|
|
445
|
+
const batchCount = this.pendingDeltaCount;
|
|
446
|
+
this.pendingDelta = "";
|
|
447
|
+
this.pendingDeltaCount = 0;
|
|
448
|
+
await this.writeToStorage([{ eventType: "text_delta", payload: batchedEvent, batchCount }]);
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Close the writer, flushing any remaining events.
|
|
452
|
+
*/
|
|
453
|
+
async close() {
|
|
454
|
+
await this.flush();
|
|
455
|
+
this.closed = true;
|
|
456
|
+
if (this.emitter) {
|
|
457
|
+
this.emitter.emit("done");
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async writeToStorage(events) {
|
|
461
|
+
return this.storage.appendEvents(this.sessionId, events);
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
|
|
387
467
|
// src/session/manager.ts
|
|
388
468
|
exports.SessionManager = void 0;
|
|
389
469
|
var init_manager = __esm({
|
|
@@ -1788,11 +1868,10 @@ var init_sandbox_logger = __esm({
|
|
|
1788
1868
|
};
|
|
1789
1869
|
}
|
|
1790
1870
|
});
|
|
1791
|
-
|
|
1792
|
-
// src/agent/harness.ts
|
|
1793
1871
|
exports.AgentHarness = void 0;
|
|
1794
1872
|
var init_harness = __esm({
|
|
1795
1873
|
"src/agent/harness.ts"() {
|
|
1874
|
+
init_stream_event_writer();
|
|
1796
1875
|
init_manager();
|
|
1797
1876
|
init_claude_sdk();
|
|
1798
1877
|
init_backend();
|
|
@@ -1809,6 +1888,8 @@ var init_harness = __esm({
|
|
|
1809
1888
|
sessionSkillDirs = /* @__PURE__ */ new Map();
|
|
1810
1889
|
/** Tracks active executions by session ID for stop/interrupt capability */
|
|
1811
1890
|
activeExecutions = /* @__PURE__ */ new Map();
|
|
1891
|
+
/** EventEmitters for same-process SSE relay fast-path, keyed by session ID */
|
|
1892
|
+
sessionEmitters = /* @__PURE__ */ new Map();
|
|
1812
1893
|
constructor(config) {
|
|
1813
1894
|
this.name = config.name;
|
|
1814
1895
|
this.config = config;
|
|
@@ -1956,6 +2037,20 @@ var init_harness = __esm({
|
|
|
1956
2037
|
getSessionManager() {
|
|
1957
2038
|
return this.sessionManager;
|
|
1958
2039
|
}
|
|
2040
|
+
/**
|
|
2041
|
+
* Get the stream event storage (if configured)
|
|
2042
|
+
*/
|
|
2043
|
+
getStreamEventStorage() {
|
|
2044
|
+
return this.config.streamEventStorage;
|
|
2045
|
+
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Get the EventEmitter for a session's stream events.
|
|
2048
|
+
* Used by the relay endpoint for same-process fast-path.
|
|
2049
|
+
* Returns undefined if no active execution or no streamEventStorage configured.
|
|
2050
|
+
*/
|
|
2051
|
+
getSessionEmitter(sessionId) {
|
|
2052
|
+
return this.sessionEmitters.get(sessionId);
|
|
2053
|
+
}
|
|
1959
2054
|
async ensureInitialized() {
|
|
1960
2055
|
if (!this.initialized) {
|
|
1961
2056
|
await this.initialize();
|
|
@@ -1970,9 +2065,24 @@ var init_harness = __esm({
|
|
|
1970
2065
|
async *send(prompt, options = {}) {
|
|
1971
2066
|
const controller = new AbortController();
|
|
1972
2067
|
self.activeExecutions.set(session.id, controller);
|
|
2068
|
+
let writer;
|
|
2069
|
+
let emitter;
|
|
2070
|
+
if (self.config.streamEventStorage) {
|
|
2071
|
+
emitter = new events.EventEmitter();
|
|
2072
|
+
self.sessionEmitters.set(session.id, emitter);
|
|
2073
|
+
writer = new exports.StreamEventWriter({
|
|
2074
|
+
storage: self.config.streamEventStorage,
|
|
2075
|
+
sessionId: session.id,
|
|
2076
|
+
emitter
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
1973
2079
|
if (options.signal) {
|
|
1974
2080
|
options.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
1975
2081
|
}
|
|
2082
|
+
const writeEvent = writer ? (event) => {
|
|
2083
|
+
writer.write(event).catch(() => {
|
|
2084
|
+
});
|
|
2085
|
+
} : void 0;
|
|
1976
2086
|
let partialTextContent = "";
|
|
1977
2087
|
const sandboxLogs = [];
|
|
1978
2088
|
const logQueue = [];
|
|
@@ -1986,7 +2096,9 @@ var init_harness = __esm({
|
|
|
1986
2096
|
const yieldQueuedLogs = async function* () {
|
|
1987
2097
|
while (logQueue.length > 0) {
|
|
1988
2098
|
const entry = logQueue.shift();
|
|
1989
|
-
|
|
2099
|
+
const event = { type: "sandbox_log", entry };
|
|
2100
|
+
writeEvent?.(event);
|
|
2101
|
+
yield event;
|
|
1990
2102
|
}
|
|
1991
2103
|
};
|
|
1992
2104
|
logger3.info("execution", `Starting execution for session ${session.id}`);
|
|
@@ -2003,11 +2115,13 @@ var init_harness = __esm({
|
|
|
2003
2115
|
if (self.hooks.onMessage && savedUserMessage) {
|
|
2004
2116
|
await self.hooks.onMessage(savedUserMessage);
|
|
2005
2117
|
}
|
|
2006
|
-
|
|
2118
|
+
const sessionStartEvent = {
|
|
2007
2119
|
type: "session_start",
|
|
2008
2120
|
sessionId: session.id,
|
|
2009
2121
|
sdkSessionId: session.sdkSessionId ?? ""
|
|
2010
2122
|
};
|
|
2123
|
+
writeEvent?.(sessionStartEvent);
|
|
2124
|
+
yield sessionStartEvent;
|
|
2011
2125
|
yield* yieldQueuedLogs();
|
|
2012
2126
|
const assistantContent = [];
|
|
2013
2127
|
let wasAborted = false;
|
|
@@ -2028,22 +2142,26 @@ var init_harness = __esm({
|
|
|
2028
2142
|
}
|
|
2029
2143
|
if (event.type === "text_delta" && event.delta) {
|
|
2030
2144
|
partialTextContent += event.delta;
|
|
2031
|
-
|
|
2145
|
+
const textDeltaEvent = {
|
|
2032
2146
|
type: "text_delta",
|
|
2033
2147
|
delta: event.delta
|
|
2034
2148
|
};
|
|
2149
|
+
writeEvent?.(textDeltaEvent);
|
|
2150
|
+
yield textDeltaEvent;
|
|
2035
2151
|
} else if (event.type === "thinking_delta" && event.delta) {
|
|
2036
|
-
|
|
2152
|
+
const thinkingEvent = {
|
|
2037
2153
|
type: "thinking_delta",
|
|
2038
2154
|
delta: event.delta
|
|
2039
2155
|
};
|
|
2156
|
+
writeEvent?.(thinkingEvent);
|
|
2157
|
+
yield thinkingEvent;
|
|
2040
2158
|
} else if (event.type === "text") {
|
|
2041
2159
|
const textContent = {
|
|
2042
2160
|
type: "text",
|
|
2043
2161
|
text: event.text
|
|
2044
2162
|
};
|
|
2045
2163
|
assistantContent.push(textContent);
|
|
2046
|
-
|
|
2164
|
+
const messageEvent = {
|
|
2047
2165
|
type: "message",
|
|
2048
2166
|
message: {
|
|
2049
2167
|
id: "",
|
|
@@ -2053,6 +2171,8 @@ var init_harness = __esm({
|
|
|
2053
2171
|
createdAt: /* @__PURE__ */ new Date()
|
|
2054
2172
|
}
|
|
2055
2173
|
};
|
|
2174
|
+
writeEvent?.(messageEvent);
|
|
2175
|
+
yield messageEvent;
|
|
2056
2176
|
} else if (event.type === "tool_use") {
|
|
2057
2177
|
logger3.debug("execution", `Tool use: ${event.name}`, { toolId: event.id });
|
|
2058
2178
|
const toolContent = {
|
|
@@ -2062,12 +2182,14 @@ var init_harness = __esm({
|
|
|
2062
2182
|
input: event.input
|
|
2063
2183
|
};
|
|
2064
2184
|
assistantContent.push(toolContent);
|
|
2065
|
-
|
|
2185
|
+
const toolUseEvent = {
|
|
2066
2186
|
type: "tool_use",
|
|
2067
2187
|
id: event.id,
|
|
2068
2188
|
name: event.name,
|
|
2069
2189
|
input: event.input
|
|
2070
2190
|
};
|
|
2191
|
+
writeEvent?.(toolUseEvent);
|
|
2192
|
+
yield toolUseEvent;
|
|
2071
2193
|
yield* yieldQueuedLogs();
|
|
2072
2194
|
if (self.hooks.onToolUse) {
|
|
2073
2195
|
await self.hooks.onToolUse(event.name, event.input, null);
|
|
@@ -2084,12 +2206,14 @@ var init_harness = __esm({
|
|
|
2084
2206
|
isError: event.isError
|
|
2085
2207
|
};
|
|
2086
2208
|
assistantContent.push(resultContent);
|
|
2087
|
-
|
|
2209
|
+
const toolResultEvent = {
|
|
2088
2210
|
type: "tool_result",
|
|
2089
2211
|
toolUseId: event.toolUseId,
|
|
2090
2212
|
content: event.content,
|
|
2091
2213
|
isError: event.isError
|
|
2092
2214
|
};
|
|
2215
|
+
writeEvent?.(toolResultEvent);
|
|
2216
|
+
yield toolResultEvent;
|
|
2093
2217
|
}
|
|
2094
2218
|
}
|
|
2095
2219
|
if (wasAborted || controller.signal.aborted) {
|
|
@@ -2118,19 +2242,23 @@ var init_harness = __esm({
|
|
|
2118
2242
|
}
|
|
2119
2243
|
});
|
|
2120
2244
|
yield* yieldQueuedLogs();
|
|
2121
|
-
|
|
2245
|
+
const stoppedEvent = {
|
|
2122
2246
|
type: "session_stopped",
|
|
2123
2247
|
sessionId: session.id,
|
|
2124
2248
|
reason: "user_requested",
|
|
2125
2249
|
partialContent: partialTextContent || void 0
|
|
2126
2250
|
};
|
|
2251
|
+
writeEvent?.(stoppedEvent);
|
|
2252
|
+
yield stoppedEvent;
|
|
2127
2253
|
return;
|
|
2128
2254
|
}
|
|
2129
2255
|
logger3.info("execution", "Execution completed successfully");
|
|
2130
|
-
|
|
2256
|
+
const turnCompleteEvent = {
|
|
2131
2257
|
type: "turn_complete",
|
|
2132
2258
|
sessionId: session.id
|
|
2133
2259
|
};
|
|
2260
|
+
writeEvent?.(turnCompleteEvent);
|
|
2261
|
+
yield turnCompleteEvent;
|
|
2134
2262
|
if (assistantContent.length > 0) {
|
|
2135
2263
|
const savedAssistantMessages = await self.sessionManager.saveMessages(
|
|
2136
2264
|
session.id,
|
|
@@ -2154,11 +2282,13 @@ var init_harness = __esm({
|
|
|
2154
2282
|
}
|
|
2155
2283
|
});
|
|
2156
2284
|
yield* yieldQueuedLogs();
|
|
2157
|
-
|
|
2285
|
+
const sessionEndEvent = {
|
|
2158
2286
|
type: "session_end",
|
|
2159
2287
|
sessionId: session.id,
|
|
2160
2288
|
status: "completed"
|
|
2161
2289
|
};
|
|
2290
|
+
writeEvent?.(sessionEndEvent);
|
|
2291
|
+
yield sessionEndEvent;
|
|
2162
2292
|
if (self.hooks.onSessionEnd) {
|
|
2163
2293
|
const updatedSession = await self.sessionManager.getSession(session.id);
|
|
2164
2294
|
if (updatedSession) {
|
|
@@ -2189,12 +2319,14 @@ var init_harness = __esm({
|
|
|
2189
2319
|
}
|
|
2190
2320
|
});
|
|
2191
2321
|
yield* yieldQueuedLogs();
|
|
2192
|
-
|
|
2322
|
+
const catchStoppedEvent = {
|
|
2193
2323
|
type: "session_stopped",
|
|
2194
2324
|
sessionId: session.id,
|
|
2195
2325
|
reason: "user_requested",
|
|
2196
2326
|
partialContent: partialTextContent || void 0
|
|
2197
2327
|
};
|
|
2328
|
+
writeEvent?.(catchStoppedEvent);
|
|
2329
|
+
yield catchStoppedEvent;
|
|
2198
2330
|
} else {
|
|
2199
2331
|
logger3.error("execution", `Execution error: ${errorMessage}`);
|
|
2200
2332
|
await self.sessionManager.updateSession(session.id, {
|
|
@@ -2212,13 +2344,19 @@ var init_harness = __esm({
|
|
|
2212
2344
|
await self.hooks.onError(error, updatedSession);
|
|
2213
2345
|
}
|
|
2214
2346
|
}
|
|
2215
|
-
|
|
2347
|
+
const errorEvent = {
|
|
2216
2348
|
type: "error",
|
|
2217
2349
|
error: errorMessage
|
|
2218
2350
|
};
|
|
2351
|
+
writeEvent?.(errorEvent);
|
|
2352
|
+
yield errorEvent;
|
|
2219
2353
|
}
|
|
2220
2354
|
} finally {
|
|
2221
2355
|
self.activeExecutions.delete(session.id);
|
|
2356
|
+
if (writer) {
|
|
2357
|
+
await writer.close();
|
|
2358
|
+
}
|
|
2359
|
+
self.sessionEmitters.delete(session.id);
|
|
2222
2360
|
}
|
|
2223
2361
|
},
|
|
2224
2362
|
async getSession() {
|
|
@@ -2722,12 +2860,88 @@ var init_queue_memory = __esm({
|
|
|
2722
2860
|
};
|
|
2723
2861
|
}
|
|
2724
2862
|
});
|
|
2863
|
+
exports.MemoryStreamEventStorage = void 0;
|
|
2864
|
+
var init_stream_event_memory = __esm({
|
|
2865
|
+
"src/storage/stream-event-memory.ts"() {
|
|
2866
|
+
exports.MemoryStreamEventStorage = class {
|
|
2867
|
+
events = /* @__PURE__ */ new Map();
|
|
2868
|
+
nextSequence = /* @__PURE__ */ new Map();
|
|
2869
|
+
async initialize() {
|
|
2870
|
+
}
|
|
2871
|
+
async close() {
|
|
2872
|
+
this.events.clear();
|
|
2873
|
+
this.nextSequence.clear();
|
|
2874
|
+
}
|
|
2875
|
+
async appendEvents(sessionId, events) {
|
|
2876
|
+
if (!this.events.has(sessionId)) {
|
|
2877
|
+
this.events.set(sessionId, []);
|
|
2878
|
+
}
|
|
2879
|
+
const sessionEvents2 = this.events.get(sessionId);
|
|
2880
|
+
let seq = this.nextSequence.get(sessionId) ?? 0;
|
|
2881
|
+
const stored = [];
|
|
2882
|
+
for (const event of events) {
|
|
2883
|
+
seq += 1;
|
|
2884
|
+
const storedEvent = {
|
|
2885
|
+
id: nanoid.nanoid(),
|
|
2886
|
+
sessionId,
|
|
2887
|
+
sequence: seq,
|
|
2888
|
+
eventType: event.eventType,
|
|
2889
|
+
payload: event.payload,
|
|
2890
|
+
batchCount: event.batchCount ?? 1,
|
|
2891
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
2892
|
+
};
|
|
2893
|
+
sessionEvents2.push(storedEvent);
|
|
2894
|
+
stored.push(storedEvent);
|
|
2895
|
+
}
|
|
2896
|
+
this.nextSequence.set(sessionId, seq);
|
|
2897
|
+
return stored;
|
|
2898
|
+
}
|
|
2899
|
+
async readEvents(sessionId, options) {
|
|
2900
|
+
const sessionEvents2 = this.events.get(sessionId) ?? [];
|
|
2901
|
+
let result = sessionEvents2;
|
|
2902
|
+
if (options?.afterSequence !== void 0) {
|
|
2903
|
+
result = result.filter((e) => e.sequence > options.afterSequence);
|
|
2904
|
+
}
|
|
2905
|
+
if (options?.limit !== void 0) {
|
|
2906
|
+
result = result.slice(0, options.limit);
|
|
2907
|
+
}
|
|
2908
|
+
return result;
|
|
2909
|
+
}
|
|
2910
|
+
async getLatestSequence(sessionId) {
|
|
2911
|
+
return this.nextSequence.get(sessionId) ?? 0;
|
|
2912
|
+
}
|
|
2913
|
+
async deleteSessionEvents(sessionId) {
|
|
2914
|
+
this.events.delete(sessionId);
|
|
2915
|
+
this.nextSequence.delete(sessionId);
|
|
2916
|
+
}
|
|
2917
|
+
async cleanupOldEvents(maxAgeMs) {
|
|
2918
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
2919
|
+
let deleted = 0;
|
|
2920
|
+
for (const [sessionId, sessionEvents2] of this.events) {
|
|
2921
|
+
const before = sessionEvents2.length;
|
|
2922
|
+
const remaining = sessionEvents2.filter(
|
|
2923
|
+
(e) => e.createdAt.getTime() >= cutoff
|
|
2924
|
+
);
|
|
2925
|
+
deleted += before - remaining.length;
|
|
2926
|
+
if (remaining.length === 0) {
|
|
2927
|
+
this.events.delete(sessionId);
|
|
2928
|
+
this.nextSequence.delete(sessionId);
|
|
2929
|
+
} else {
|
|
2930
|
+
this.events.set(sessionId, remaining);
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
return deleted;
|
|
2934
|
+
}
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
});
|
|
2725
2938
|
|
|
2726
2939
|
// src/storage/index.ts
|
|
2727
2940
|
var init_storage = __esm({
|
|
2728
2941
|
"src/storage/index.ts"() {
|
|
2729
2942
|
init_memory();
|
|
2730
2943
|
init_queue_memory();
|
|
2944
|
+
init_stream_event_memory();
|
|
2731
2945
|
}
|
|
2732
2946
|
});
|
|
2733
2947
|
|
|
@@ -4233,6 +4447,50 @@ VERCEL_EOF`);
|
|
|
4233
4447
|
}
|
|
4234
4448
|
});
|
|
4235
4449
|
|
|
4450
|
+
// src/runtime/providers/firecracker/types.ts
|
|
4451
|
+
var init_types3 = __esm({
|
|
4452
|
+
"src/runtime/providers/firecracker/types.ts"() {
|
|
4453
|
+
}
|
|
4454
|
+
});
|
|
4455
|
+
|
|
4456
|
+
// src/runtime/providers/firecracker/image-manager.ts
|
|
4457
|
+
var init_image_manager = __esm({
|
|
4458
|
+
"src/runtime/providers/firecracker/image-manager.ts"() {
|
|
4459
|
+
}
|
|
4460
|
+
});
|
|
4461
|
+
|
|
4462
|
+
// src/runtime/providers/firecracker/network-manager.ts
|
|
4463
|
+
var init_network_manager = __esm({
|
|
4464
|
+
"src/runtime/providers/firecracker/network-manager.ts"() {
|
|
4465
|
+
}
|
|
4466
|
+
});
|
|
4467
|
+
|
|
4468
|
+
// src/runtime/providers/firecracker/ssh-executor.ts
|
|
4469
|
+
var init_ssh_executor = __esm({
|
|
4470
|
+
"src/runtime/providers/firecracker/ssh-executor.ts"() {
|
|
4471
|
+
}
|
|
4472
|
+
});
|
|
4473
|
+
|
|
4474
|
+
// src/runtime/providers/firecracker/vm-manager.ts
|
|
4475
|
+
var init_vm_manager = __esm({
|
|
4476
|
+
"src/runtime/providers/firecracker/vm-manager.ts"() {
|
|
4477
|
+
init_types3();
|
|
4478
|
+
}
|
|
4479
|
+
});
|
|
4480
|
+
|
|
4481
|
+
// src/runtime/providers/firecracker.ts
|
|
4482
|
+
var init_firecracker = __esm({
|
|
4483
|
+
"src/runtime/providers/firecracker.ts"() {
|
|
4484
|
+
init_types2();
|
|
4485
|
+
init_types3();
|
|
4486
|
+
init_image_manager();
|
|
4487
|
+
init_network_manager();
|
|
4488
|
+
init_ssh_executor();
|
|
4489
|
+
init_vm_manager();
|
|
4490
|
+
init_types3();
|
|
4491
|
+
}
|
|
4492
|
+
});
|
|
4493
|
+
|
|
4236
4494
|
// src/runtime/providers/index.ts
|
|
4237
4495
|
var init_providers = __esm({
|
|
4238
4496
|
"src/runtime/providers/index.ts"() {
|
|
@@ -4244,6 +4502,7 @@ var init_providers = __esm({
|
|
|
4244
4502
|
init_fly();
|
|
4245
4503
|
init_daytona();
|
|
4246
4504
|
init_vercel();
|
|
4505
|
+
init_firecracker();
|
|
4247
4506
|
init_local();
|
|
4248
4507
|
init_modal();
|
|
4249
4508
|
init_e2b();
|
|
@@ -4251,6 +4510,7 @@ var init_providers = __esm({
|
|
|
4251
4510
|
init_fly();
|
|
4252
4511
|
init_daytona();
|
|
4253
4512
|
init_vercel();
|
|
4513
|
+
init_firecracker();
|
|
4254
4514
|
}
|
|
4255
4515
|
});
|
|
4256
4516
|
|
|
@@ -5136,10 +5396,11 @@ async function getOrCreateSandbox(options) {
|
|
|
5136
5396
|
startupScriptRan: true,
|
|
5137
5397
|
// Assume ran for reconnected sandboxes
|
|
5138
5398
|
startupScriptHash: void 0,
|
|
5399
|
+
// Unknown — caller should not re-run based on hash mismatch alone
|
|
5139
5400
|
isNew: false,
|
|
5140
5401
|
configFileUrl: void 0,
|
|
5141
|
-
configInstalledAt:
|
|
5142
|
-
//
|
|
5402
|
+
configInstalledAt: now2
|
|
5403
|
+
// Assume config was installed — prevents unnecessary re-install on reconnection
|
|
5143
5404
|
};
|
|
5144
5405
|
} else {
|
|
5145
5406
|
console.log("[SANDBOX] Reconnected sandbox failed health check, will create new");
|
|
@@ -5525,7 +5786,8 @@ async function* executeInSandbox(prompt, apiKey, options) {
|
|
|
5525
5786
|
markSdkInstalled(sessionId);
|
|
5526
5787
|
}
|
|
5527
5788
|
const currentScriptHash = hashStartupScript(options.startupScript);
|
|
5528
|
-
const
|
|
5789
|
+
const scriptHashChanged = cachedScriptHash !== void 0 && currentScriptHash !== cachedScriptHash;
|
|
5790
|
+
const needsStartupScript = options.startupScript && (!startupScriptRan || scriptHashChanged);
|
|
5529
5791
|
if (needsStartupScript) {
|
|
5530
5792
|
console.log("[SANDBOX] Running startup script...");
|
|
5531
5793
|
const startupStartTime = Date.now();
|
|
@@ -5659,11 +5921,18 @@ const { query } = require('@anthropic-ai/claude-agent-sdk');
|
|
|
5659
5921
|
const prompt = ${JSON.stringify(prompt)};
|
|
5660
5922
|
const options = ${JSON.stringify(sdkOptions)};
|
|
5661
5923
|
|
|
5924
|
+
let queryCompleted = false;
|
|
5925
|
+
|
|
5662
5926
|
async function run() {
|
|
5663
5927
|
try {
|
|
5664
5928
|
for await (const message of query({ prompt, options })) {
|
|
5665
5929
|
console.log(JSON.stringify(message));
|
|
5666
5930
|
}
|
|
5931
|
+
queryCompleted = true;
|
|
5932
|
+
// Exit cleanly immediately after query completes.
|
|
5933
|
+
// MCP server cleanup (e.g. Playwright browser shutdown) can trigger
|
|
5934
|
+
// unhandled rejections after the query is done, causing spurious exit code 1.
|
|
5935
|
+
process.exit(0);
|
|
5667
5936
|
} catch (error) {
|
|
5668
5937
|
const errorInfo = {
|
|
5669
5938
|
type: 'error',
|
|
@@ -5679,6 +5948,11 @@ async function run() {
|
|
|
5679
5948
|
}
|
|
5680
5949
|
|
|
5681
5950
|
process.on('unhandledRejection', (reason) => {
|
|
5951
|
+
// Ignore rejections from MCP server cleanup after query has completed
|
|
5952
|
+
if (queryCompleted) {
|
|
5953
|
+
process.exit(0);
|
|
5954
|
+
return;
|
|
5955
|
+
}
|
|
5682
5956
|
const errorInfo = {
|
|
5683
5957
|
type: 'error',
|
|
5684
5958
|
error: reason instanceof Error ? reason.message : String(reason),
|
|
@@ -5689,6 +5963,11 @@ process.on('unhandledRejection', (reason) => {
|
|
|
5689
5963
|
});
|
|
5690
5964
|
|
|
5691
5965
|
process.on('uncaughtException', (error) => {
|
|
5966
|
+
// Ignore exceptions from MCP server cleanup after query has completed
|
|
5967
|
+
if (queryCompleted) {
|
|
5968
|
+
process.exit(0);
|
|
5969
|
+
return;
|
|
5970
|
+
}
|
|
5692
5971
|
const errorInfo = {
|
|
5693
5972
|
type: 'error',
|
|
5694
5973
|
error: error.message || 'Unknown error',
|
|
@@ -5927,7 +6206,18 @@ var init_sandbox_file_watcher = __esm({
|
|
|
5927
6206
|
this.watchPath = options.watchPath;
|
|
5928
6207
|
this.debounceMs = options.debounceMs ?? 300;
|
|
5929
6208
|
this.patterns = options.patterns ?? ["**/*"];
|
|
5930
|
-
this.ignored = options.ignored ?? [
|
|
6209
|
+
this.ignored = options.ignored ?? [
|
|
6210
|
+
"**/node_modules/**",
|
|
6211
|
+
"**/.git/**",
|
|
6212
|
+
"**/*.log",
|
|
6213
|
+
// Temporary files from atomic saves and editors
|
|
6214
|
+
"**/*.tmp",
|
|
6215
|
+
"**/*.tmp.*",
|
|
6216
|
+
"**/*~",
|
|
6217
|
+
"**/*.swp",
|
|
6218
|
+
"**/*.swo",
|
|
6219
|
+
"**/.DS_Store"
|
|
6220
|
+
];
|
|
5931
6221
|
this.emitInitialEvents = options.emitInitialEvents ?? false;
|
|
5932
6222
|
this.followSymlinks = options.followSymlinks ?? false;
|
|
5933
6223
|
this.usePolling = options.usePolling ?? false;
|
|
@@ -6168,7 +6458,7 @@ const fs = require('fs');
|
|
|
6168
6458
|
const path = require('path');
|
|
6169
6459
|
|
|
6170
6460
|
const watchPath = process.argv[2] || '.';
|
|
6171
|
-
const
|
|
6461
|
+
const ignoredDirs = new Set(['node_modules', '.git', '.cache', '__pycache__']);
|
|
6172
6462
|
|
|
6173
6463
|
// Track all watchers for cleanup
|
|
6174
6464
|
const watchers = new Map();
|
|
@@ -6187,7 +6477,23 @@ function emit(type, filePath) {
|
|
|
6187
6477
|
}
|
|
6188
6478
|
|
|
6189
6479
|
function shouldIgnore(name) {
|
|
6190
|
-
|
|
6480
|
+
// Ignore hidden files/dirs (starting with .)
|
|
6481
|
+
if (name.startsWith('.')) return true;
|
|
6482
|
+
|
|
6483
|
+
// Ignore known directories
|
|
6484
|
+
if (ignoredDirs.has(name)) return true;
|
|
6485
|
+
|
|
6486
|
+
// Ignore temporary files from atomic saves and editors:
|
|
6487
|
+
// - filename.tmp (ends with .tmp)
|
|
6488
|
+
// - filename.tmp.123456789 (has .tmp. in middle - atomic write pattern)
|
|
6489
|
+
// - filename~ (editor backup files)
|
|
6490
|
+
// - filename.swp / filename.swo (Vim swap files)
|
|
6491
|
+
if (name.endsWith('.tmp')) return true;
|
|
6492
|
+
if (name.includes('.tmp.')) return true;
|
|
6493
|
+
if (name.endsWith('~')) return true;
|
|
6494
|
+
if (name.endsWith('.swp') || name.endsWith('.swo')) return true;
|
|
6495
|
+
|
|
6496
|
+
return false;
|
|
6191
6497
|
}
|
|
6192
6498
|
|
|
6193
6499
|
function watchDir(dir) {
|
|
@@ -6734,15 +7040,35 @@ var init_sandbox_file_sync = __esm({
|
|
|
6734
7040
|
}
|
|
6735
7041
|
}
|
|
6736
7042
|
/**
|
|
6737
|
-
* Send a file sync event webhook
|
|
7043
|
+
* Send a file sync event webhook.
|
|
7044
|
+
* Strips raw Buffer content (previousContent/newContent) and replaces with a
|
|
7045
|
+
* pre-signed S3 download URL when the file exists in storage.
|
|
6738
7046
|
*/
|
|
6739
7047
|
async sendFileSyncWebhook(sessionId, event) {
|
|
7048
|
+
let downloadUrl;
|
|
7049
|
+
if (event.success && event.canonicalPath && !event.operation.startsWith("deleted")) {
|
|
7050
|
+
try {
|
|
7051
|
+
downloadUrl = await this.fileStore.getSignedUrl(sessionId, event.canonicalPath);
|
|
7052
|
+
} catch {
|
|
7053
|
+
}
|
|
7054
|
+
}
|
|
7055
|
+
const webhookEvent = {
|
|
7056
|
+
operation: event.operation,
|
|
7057
|
+
source: event.source,
|
|
7058
|
+
canonicalPath: event.canonicalPath,
|
|
7059
|
+
basePath: event.basePath,
|
|
7060
|
+
sandboxPath: event.sandboxPath,
|
|
7061
|
+
fileSize: event.fileSize,
|
|
7062
|
+
success: event.success,
|
|
7063
|
+
error: event.error,
|
|
7064
|
+
downloadUrl
|
|
7065
|
+
};
|
|
6740
7066
|
const payload = {
|
|
6741
7067
|
event: "file_sync",
|
|
6742
7068
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6743
7069
|
sessionId,
|
|
6744
7070
|
metadata: this.webhookConfig?.metadata,
|
|
6745
|
-
fileSyncEvent:
|
|
7071
|
+
fileSyncEvent: webhookEvent
|
|
6746
7072
|
};
|
|
6747
7073
|
await this.sendWebhook(payload, sessionId);
|
|
6748
7074
|
}
|
|
@@ -7871,7 +8197,7 @@ var init_credentials = __esm({
|
|
|
7871
8197
|
}
|
|
7872
8198
|
});
|
|
7873
8199
|
exports.gitHubSkillSourceSchema = void 0; exports.localSkillSourceSchema = void 0; exports.skillSourceSchema = void 0; exports.skillConfigSchema = void 0; exports.fileEntrySchema = void 0;
|
|
7874
|
-
var
|
|
8200
|
+
var init_types4 = __esm({
|
|
7875
8201
|
"src/skills/types.ts"() {
|
|
7876
8202
|
exports.gitHubSkillSourceSchema = zod.z.object({
|
|
7877
8203
|
type: zod.z.literal("github"),
|
|
@@ -8415,7 +8741,7 @@ var init_manager2 = __esm({
|
|
|
8415
8741
|
// src/skills/index.ts
|
|
8416
8742
|
var init_skills = __esm({
|
|
8417
8743
|
"src/skills/index.ts"() {
|
|
8418
|
-
|
|
8744
|
+
init_types4();
|
|
8419
8745
|
init_local_provider();
|
|
8420
8746
|
init_github_provider();
|
|
8421
8747
|
init_manager2();
|
|
@@ -9030,6 +9356,135 @@ var init_utils = __esm({
|
|
|
9030
9356
|
init_sandbox_logger();
|
|
9031
9357
|
}
|
|
9032
9358
|
});
|
|
9359
|
+
|
|
9360
|
+
// src/relay/stream-event-relay.ts
|
|
9361
|
+
async function* streamEventRelay(options) {
|
|
9362
|
+
const {
|
|
9363
|
+
storage,
|
|
9364
|
+
sessionId,
|
|
9365
|
+
afterSequence = 0,
|
|
9366
|
+
emitter,
|
|
9367
|
+
pollIntervalMs = 100,
|
|
9368
|
+
heartbeatIntervalMs = 15e3,
|
|
9369
|
+
signal
|
|
9370
|
+
} = options;
|
|
9371
|
+
let lastSequence = afterSequence;
|
|
9372
|
+
let done = false;
|
|
9373
|
+
const catchUpEvents = await storage.readEvents(sessionId, {
|
|
9374
|
+
afterSequence: lastSequence,
|
|
9375
|
+
limit: 1e4
|
|
9376
|
+
});
|
|
9377
|
+
for (const stored of catchUpEvents) {
|
|
9378
|
+
if (signal?.aborted || done) return;
|
|
9379
|
+
lastSequence = stored.sequence;
|
|
9380
|
+
yield stored.payload;
|
|
9381
|
+
if (TERMINAL_EVENT_TYPES.has(stored.eventType)) {
|
|
9382
|
+
done = true;
|
|
9383
|
+
return;
|
|
9384
|
+
}
|
|
9385
|
+
}
|
|
9386
|
+
if (signal?.aborted) return;
|
|
9387
|
+
if (emitter) {
|
|
9388
|
+
yield* relayFromEmitter(emitter, signal, heartbeatIntervalMs);
|
|
9389
|
+
} else {
|
|
9390
|
+
yield* relayByPolling(storage, sessionId, lastSequence, pollIntervalMs, heartbeatIntervalMs, signal);
|
|
9391
|
+
}
|
|
9392
|
+
}
|
|
9393
|
+
async function* relayFromEmitter(emitter, signal, heartbeatIntervalMs = 15e3) {
|
|
9394
|
+
const queue = [];
|
|
9395
|
+
let resolve3 = null;
|
|
9396
|
+
const onEvent = (event) => {
|
|
9397
|
+
queue.push(event);
|
|
9398
|
+
if (resolve3) {
|
|
9399
|
+
resolve3();
|
|
9400
|
+
resolve3 = null;
|
|
9401
|
+
}
|
|
9402
|
+
};
|
|
9403
|
+
const onDone = () => {
|
|
9404
|
+
queue.push("done");
|
|
9405
|
+
if (resolve3) {
|
|
9406
|
+
resolve3();
|
|
9407
|
+
resolve3 = null;
|
|
9408
|
+
}
|
|
9409
|
+
};
|
|
9410
|
+
emitter.on("event", onEvent);
|
|
9411
|
+
emitter.on("done", onDone);
|
|
9412
|
+
const heartbeatTimer = setInterval(() => {
|
|
9413
|
+
if (resolve3) {
|
|
9414
|
+
resolve3();
|
|
9415
|
+
resolve3 = null;
|
|
9416
|
+
}
|
|
9417
|
+
}, heartbeatIntervalMs);
|
|
9418
|
+
try {
|
|
9419
|
+
while (!signal?.aborted) {
|
|
9420
|
+
while (queue.length > 0) {
|
|
9421
|
+
const item = queue.shift();
|
|
9422
|
+
if (item === "done") return;
|
|
9423
|
+
yield item;
|
|
9424
|
+
if (TERMINAL_EVENT_TYPES.has(item.type)) return;
|
|
9425
|
+
}
|
|
9426
|
+
await new Promise((r) => {
|
|
9427
|
+
resolve3 = r;
|
|
9428
|
+
if (signal) {
|
|
9429
|
+
signal.addEventListener("abort", () => {
|
|
9430
|
+
resolve3 = null;
|
|
9431
|
+
r();
|
|
9432
|
+
}, { once: true });
|
|
9433
|
+
}
|
|
9434
|
+
});
|
|
9435
|
+
}
|
|
9436
|
+
} finally {
|
|
9437
|
+
clearInterval(heartbeatTimer);
|
|
9438
|
+
emitter.off("event", onEvent);
|
|
9439
|
+
emitter.off("done", onDone);
|
|
9440
|
+
}
|
|
9441
|
+
}
|
|
9442
|
+
async function* relayByPolling(storage, sessionId, startSequence, pollIntervalMs, heartbeatIntervalMs, signal) {
|
|
9443
|
+
let lastSequence = startSequence;
|
|
9444
|
+
while (!signal?.aborted) {
|
|
9445
|
+
const events = await storage.readEvents(sessionId, {
|
|
9446
|
+
afterSequence: lastSequence,
|
|
9447
|
+
limit: 100
|
|
9448
|
+
});
|
|
9449
|
+
for (const stored of events) {
|
|
9450
|
+
if (signal?.aborted) return;
|
|
9451
|
+
lastSequence = stored.sequence;
|
|
9452
|
+
yield stored.payload;
|
|
9453
|
+
if (TERMINAL_EVENT_TYPES.has(stored.eventType)) {
|
|
9454
|
+
return;
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
9457
|
+
if (events.length === 0) {
|
|
9458
|
+
await new Promise((r) => {
|
|
9459
|
+
const timer = setTimeout(r, pollIntervalMs);
|
|
9460
|
+
if (signal) {
|
|
9461
|
+
signal.addEventListener("abort", () => {
|
|
9462
|
+
clearTimeout(timer);
|
|
9463
|
+
r();
|
|
9464
|
+
}, { once: true });
|
|
9465
|
+
}
|
|
9466
|
+
});
|
|
9467
|
+
}
|
|
9468
|
+
}
|
|
9469
|
+
}
|
|
9470
|
+
var TERMINAL_EVENT_TYPES;
|
|
9471
|
+
var init_stream_event_relay = __esm({
|
|
9472
|
+
"src/relay/stream-event-relay.ts"() {
|
|
9473
|
+
TERMINAL_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
9474
|
+
"session_end",
|
|
9475
|
+
"error",
|
|
9476
|
+
"session_stopped"
|
|
9477
|
+
]);
|
|
9478
|
+
}
|
|
9479
|
+
});
|
|
9480
|
+
|
|
9481
|
+
// src/relay/index.ts
|
|
9482
|
+
var init_relay = __esm({
|
|
9483
|
+
"src/relay/index.ts"() {
|
|
9484
|
+
init_stream_event_writer();
|
|
9485
|
+
init_stream_event_relay();
|
|
9486
|
+
}
|
|
9487
|
+
});
|
|
9033
9488
|
function broadcastToSession(sessionId, event) {
|
|
9034
9489
|
const subscribers = sessionSubscribers.get(sessionId);
|
|
9035
9490
|
if (subscribers && subscribers.size > 0) {
|
|
@@ -9162,6 +9617,8 @@ function createSessionsRouter(options) {
|
|
|
9162
9617
|
agentName: session.agentName,
|
|
9163
9618
|
startedAt: /* @__PURE__ */ new Date()
|
|
9164
9619
|
});
|
|
9620
|
+
const hasStreamEventStorage = !!agent.getStreamEventStorage();
|
|
9621
|
+
let sendSequenceCounter = 0;
|
|
9165
9622
|
return streaming.streamSSE(c, async (stream) => {
|
|
9166
9623
|
try {
|
|
9167
9624
|
for await (const event of activeSession.send(data.prompt, {
|
|
@@ -9173,7 +9630,9 @@ function createSessionsRouter(options) {
|
|
|
9173
9630
|
if (controller.signal.aborted) {
|
|
9174
9631
|
break;
|
|
9175
9632
|
}
|
|
9633
|
+
sendSequenceCounter++;
|
|
9176
9634
|
await stream.writeSSE({
|
|
9635
|
+
...hasStreamEventStorage ? { id: String(sendSequenceCounter) } : {},
|
|
9177
9636
|
event: event.type,
|
|
9178
9637
|
data: JSON.stringify(event)
|
|
9179
9638
|
});
|
|
@@ -9356,11 +9815,77 @@ function createSessionsRouter(options) {
|
|
|
9356
9815
|
}
|
|
9357
9816
|
});
|
|
9358
9817
|
});
|
|
9818
|
+
router.get("/:sessionId/stream", async (c) => {
|
|
9819
|
+
const sessionId = c.req.param("sessionId");
|
|
9820
|
+
const afterParam = c.req.query("after");
|
|
9821
|
+
const afterSequence = afterParam ? parseInt(afterParam, 10) : 0;
|
|
9822
|
+
const session = await sessionManager.getSession(sessionId);
|
|
9823
|
+
if (!session) {
|
|
9824
|
+
return c.json({ error: "Session not found" }, 404);
|
|
9825
|
+
}
|
|
9826
|
+
const agent = agents2.get(session.agentName);
|
|
9827
|
+
if (!agent) {
|
|
9828
|
+
return c.json({ error: `Agent not found: ${session.agentName}` }, 404);
|
|
9829
|
+
}
|
|
9830
|
+
const streamEventStorage = agent.getStreamEventStorage();
|
|
9831
|
+
if (!streamEventStorage) {
|
|
9832
|
+
return c.json({ error: "Stream event storage not configured" }, 501);
|
|
9833
|
+
}
|
|
9834
|
+
return streaming.streamSSE(c, async (stream) => {
|
|
9835
|
+
const abortController = new AbortController();
|
|
9836
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
9837
|
+
abortController.abort();
|
|
9838
|
+
});
|
|
9839
|
+
const heartbeatInterval = setInterval(async () => {
|
|
9840
|
+
try {
|
|
9841
|
+
await stream.writeSSE({
|
|
9842
|
+
event: "heartbeat",
|
|
9843
|
+
data: JSON.stringify({ type: "heartbeat", timestamp: Date.now() })
|
|
9844
|
+
});
|
|
9845
|
+
} catch {
|
|
9846
|
+
clearInterval(heartbeatInterval);
|
|
9847
|
+
}
|
|
9848
|
+
}, 15e3);
|
|
9849
|
+
let sequenceCounter = afterSequence;
|
|
9850
|
+
try {
|
|
9851
|
+
const sessionEmitter = agent.getSessionEmitter(sessionId);
|
|
9852
|
+
const relay = streamEventRelay({
|
|
9853
|
+
storage: streamEventStorage,
|
|
9854
|
+
sessionId,
|
|
9855
|
+
afterSequence,
|
|
9856
|
+
emitter: sessionEmitter,
|
|
9857
|
+
signal: abortController.signal
|
|
9858
|
+
});
|
|
9859
|
+
for await (const event of relay) {
|
|
9860
|
+
if (abortController.signal.aborted) break;
|
|
9861
|
+
sequenceCounter++;
|
|
9862
|
+
await stream.writeSSE({
|
|
9863
|
+
id: String(sequenceCounter),
|
|
9864
|
+
event: event.type,
|
|
9865
|
+
data: JSON.stringify(event)
|
|
9866
|
+
});
|
|
9867
|
+
}
|
|
9868
|
+
} catch (error) {
|
|
9869
|
+
if (!abortController.signal.aborted) {
|
|
9870
|
+
await stream.writeSSE({
|
|
9871
|
+
event: "error",
|
|
9872
|
+
data: JSON.stringify({
|
|
9873
|
+
type: "error",
|
|
9874
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
9875
|
+
})
|
|
9876
|
+
});
|
|
9877
|
+
}
|
|
9878
|
+
} finally {
|
|
9879
|
+
clearInterval(heartbeatInterval);
|
|
9880
|
+
}
|
|
9881
|
+
});
|
|
9882
|
+
});
|
|
9359
9883
|
return router;
|
|
9360
9884
|
}
|
|
9361
9885
|
var createSessionSchema, fileAttachmentSchema, sendMessageSchema, updateEnvSchema, listSessionsSchema, activeStreams, sessionSubscribers;
|
|
9362
9886
|
var init_sessions = __esm({
|
|
9363
9887
|
"src/server/routes/sessions.ts"() {
|
|
9888
|
+
init_stream_event_relay();
|
|
9364
9889
|
createSessionSchema = zod.z.object({
|
|
9365
9890
|
agentName: zod.z.string(),
|
|
9366
9891
|
metadata: zod.z.record(zod.z.unknown()).optional(),
|
|
@@ -16081,9 +16606,11 @@ __export(schema_exports, {
|
|
|
16081
16606
|
sessionEventsRelations: () => sessionEventsRelations,
|
|
16082
16607
|
sessionStatusEnum: () => sessionStatusEnum,
|
|
16083
16608
|
sessions: () => sessions,
|
|
16084
|
-
sessionsRelations: () => sessionsRelations
|
|
16609
|
+
sessionsRelations: () => sessionsRelations,
|
|
16610
|
+
streamEvents: () => streamEvents,
|
|
16611
|
+
streamEventsRelations: () => streamEventsRelations
|
|
16085
16612
|
});
|
|
16086
|
-
var sessionStatusEnum, messageRoleEnum, agentStatusEnum, agentBackendEnum, queueItemStatusEnum, configDeploymentStatusEnum, configDeploymentTriggerEnum, eventCategoryEnum, sessions, messages, attachments, agents, queueItems, configDeployments, sessionEvents, sessionsRelations, sessionEventsRelations, messagesRelations, attachmentsRelations, configDeploymentsRelations, agentsRelations;
|
|
16613
|
+
var sessionStatusEnum, messageRoleEnum, agentStatusEnum, agentBackendEnum, queueItemStatusEnum, configDeploymentStatusEnum, configDeploymentTriggerEnum, eventCategoryEnum, sessions, messages, attachments, agents, queueItems, configDeployments, sessionEvents, streamEvents, sessionsRelations, sessionEventsRelations, messagesRelations, attachmentsRelations, configDeploymentsRelations, streamEventsRelations, agentsRelations;
|
|
16087
16614
|
var init_schema = __esm({
|
|
16088
16615
|
"src/storage-postgres/schema.ts"() {
|
|
16089
16616
|
sessionStatusEnum = pgCore.pgEnum("session_status", [
|
|
@@ -16324,6 +16851,21 @@ var init_schema = __esm({
|
|
|
16324
16851
|
pgCore.index("idx_session_events_category").on(table.sessionId, table.category)
|
|
16325
16852
|
]
|
|
16326
16853
|
);
|
|
16854
|
+
streamEvents = pgCore.pgTable(
|
|
16855
|
+
"stream_events",
|
|
16856
|
+
{
|
|
16857
|
+
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
16858
|
+
sessionId: pgCore.uuid("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
16859
|
+
sequence: pgCore.integer("sequence").notNull(),
|
|
16860
|
+
eventType: pgCore.text("event_type").notNull(),
|
|
16861
|
+
payload: pgCore.jsonb("payload").notNull().$type(),
|
|
16862
|
+
batchCount: pgCore.integer("batch_count").default(1).notNull(),
|
|
16863
|
+
createdAt: pgCore.timestamp("created_at", { withTimezone: true }).defaultNow().notNull()
|
|
16864
|
+
},
|
|
16865
|
+
(table) => [
|
|
16866
|
+
pgCore.index("idx_stream_events_session_seq").on(table.sessionId, table.sequence)
|
|
16867
|
+
]
|
|
16868
|
+
);
|
|
16327
16869
|
sessionsRelations = drizzleOrm.relations(sessions, ({ one, many }) => ({
|
|
16328
16870
|
parentSession: one(sessions, {
|
|
16329
16871
|
fields: [sessions.parentSessionId],
|
|
@@ -16334,7 +16876,8 @@ var init_schema = __esm({
|
|
|
16334
16876
|
relationName: "parentSession"
|
|
16335
16877
|
}),
|
|
16336
16878
|
messages: many(messages),
|
|
16337
|
-
events: many(sessionEvents)
|
|
16879
|
+
events: many(sessionEvents),
|
|
16880
|
+
streamEvents: many(streamEvents)
|
|
16338
16881
|
}));
|
|
16339
16882
|
sessionEventsRelations = drizzleOrm.relations(sessionEvents, ({ one }) => ({
|
|
16340
16883
|
session: one(sessions, {
|
|
@@ -16361,6 +16904,12 @@ var init_schema = __esm({
|
|
|
16361
16904
|
references: [agents.id]
|
|
16362
16905
|
})
|
|
16363
16906
|
}));
|
|
16907
|
+
streamEventsRelations = drizzleOrm.relations(streamEvents, ({ one }) => ({
|
|
16908
|
+
session: one(sessions, {
|
|
16909
|
+
fields: [streamEvents.sessionId],
|
|
16910
|
+
references: [sessions.id]
|
|
16911
|
+
})
|
|
16912
|
+
}));
|
|
16364
16913
|
agentsRelations = drizzleOrm.relations(agents, ({ many }) => ({
|
|
16365
16914
|
configDeployments: many(configDeployments)
|
|
16366
16915
|
}));
|
|
@@ -16548,6 +17097,20 @@ var init_storage2 = __esm({
|
|
|
16548
17097
|
`);
|
|
16549
17098
|
await this.db.execute(drizzleOrm.sql`
|
|
16550
17099
|
ALTER TABLE agents ADD COLUMN IF NOT EXISTS backend agent_backend DEFAULT 'claude' NOT NULL;
|
|
17100
|
+
`);
|
|
17101
|
+
await this.db.execute(drizzleOrm.sql`
|
|
17102
|
+
CREATE TABLE IF NOT EXISTS stream_events (
|
|
17103
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
17104
|
+
session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
17105
|
+
sequence INTEGER NOT NULL,
|
|
17106
|
+
event_type TEXT NOT NULL,
|
|
17107
|
+
payload JSONB NOT NULL,
|
|
17108
|
+
batch_count INTEGER NOT NULL DEFAULT 1,
|
|
17109
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
17110
|
+
);
|
|
17111
|
+
`);
|
|
17112
|
+
await this.db.execute(drizzleOrm.sql`
|
|
17113
|
+
CREATE INDEX IF NOT EXISTS idx_stream_events_session_seq ON stream_events(session_id, sequence);
|
|
16551
17114
|
`);
|
|
16552
17115
|
}
|
|
16553
17116
|
/**
|
|
@@ -16740,6 +17303,20 @@ var init_storage2 = __esm({
|
|
|
16740
17303
|
`);
|
|
16741
17304
|
await this.db.execute(drizzleOrm.sql`
|
|
16742
17305
|
CREATE INDEX IF NOT EXISTS idx_queue_items_pending ON queue_items(agent_name, session_id, status, priority, created_at);
|
|
17306
|
+
`);
|
|
17307
|
+
await this.db.execute(drizzleOrm.sql`
|
|
17308
|
+
CREATE TABLE IF NOT EXISTS stream_events (
|
|
17309
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
17310
|
+
session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
17311
|
+
sequence INTEGER NOT NULL,
|
|
17312
|
+
event_type TEXT NOT NULL,
|
|
17313
|
+
payload JSONB NOT NULL,
|
|
17314
|
+
batch_count INTEGER NOT NULL DEFAULT 1,
|
|
17315
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
17316
|
+
);
|
|
17317
|
+
`);
|
|
17318
|
+
await this.db.execute(drizzleOrm.sql`
|
|
17319
|
+
CREATE INDEX IF NOT EXISTS idx_stream_events_session_seq ON stream_events(session_id, sequence);
|
|
16743
17320
|
`);
|
|
16744
17321
|
await this.db.execute(drizzleOrm.sql`
|
|
16745
17322
|
DO $$ BEGIN
|
|
@@ -17130,6 +17707,56 @@ var init_storage2 = __esm({
|
|
|
17130
17707
|
return rows.map((row) => this.rowToAgent(row));
|
|
17131
17708
|
}
|
|
17132
17709
|
// ============================================================================
|
|
17710
|
+
// Stream Events (SSE Relay)
|
|
17711
|
+
// ============================================================================
|
|
17712
|
+
async appendEvents(sessionId, events) {
|
|
17713
|
+
if (events.length === 0) return [];
|
|
17714
|
+
const [maxSeqResult] = await this.db.select({ maxSeq: drizzleOrm.sql`COALESCE(MAX(${streamEvents.sequence}), 0)` }).from(streamEvents).where(drizzleOrm.eq(streamEvents.sessionId, sessionId));
|
|
17715
|
+
let nextSeq = (maxSeqResult?.maxSeq ?? 0) + 1;
|
|
17716
|
+
const rows = await this.db.insert(streamEvents).values(
|
|
17717
|
+
events.map((event) => ({
|
|
17718
|
+
sessionId,
|
|
17719
|
+
sequence: nextSeq++,
|
|
17720
|
+
eventType: event.eventType,
|
|
17721
|
+
payload: event.payload,
|
|
17722
|
+
batchCount: event.batchCount ?? 1
|
|
17723
|
+
}))
|
|
17724
|
+
).returning();
|
|
17725
|
+
return rows.map((row) => this.rowToStoredStreamEvent(row));
|
|
17726
|
+
}
|
|
17727
|
+
async readEvents(sessionId, options) {
|
|
17728
|
+
const conditions = [drizzleOrm.eq(streamEvents.sessionId, sessionId)];
|
|
17729
|
+
if (options?.afterSequence !== void 0) {
|
|
17730
|
+
conditions.push(drizzleOrm.sql`${streamEvents.sequence} > ${options.afterSequence}`);
|
|
17731
|
+
}
|
|
17732
|
+
const limit = options?.limit ?? 1e3;
|
|
17733
|
+
const rows = await this.db.select().from(streamEvents).where(drizzleOrm.and(...conditions)).orderBy(drizzleOrm.asc(streamEvents.sequence)).limit(limit);
|
|
17734
|
+
return rows.map((row) => this.rowToStoredStreamEvent(row));
|
|
17735
|
+
}
|
|
17736
|
+
async getLatestSequence(sessionId) {
|
|
17737
|
+
const [result] = await this.db.select({ maxSeq: drizzleOrm.sql`COALESCE(MAX(${streamEvents.sequence}), 0)` }).from(streamEvents).where(drizzleOrm.eq(streamEvents.sessionId, sessionId));
|
|
17738
|
+
return result?.maxSeq ?? 0;
|
|
17739
|
+
}
|
|
17740
|
+
async deleteSessionEvents(sessionId) {
|
|
17741
|
+
await this.db.delete(streamEvents).where(drizzleOrm.eq(streamEvents.sessionId, sessionId));
|
|
17742
|
+
}
|
|
17743
|
+
async cleanupOldEvents(maxAgeMs) {
|
|
17744
|
+
const cutoff = new Date(Date.now() - maxAgeMs);
|
|
17745
|
+
const result = await this.db.delete(streamEvents).where(drizzleOrm.sql`${streamEvents.createdAt} < ${cutoff.toISOString()}`);
|
|
17746
|
+
return result.rowCount ?? 0;
|
|
17747
|
+
}
|
|
17748
|
+
rowToStoredStreamEvent(row) {
|
|
17749
|
+
return {
|
|
17750
|
+
id: row.id,
|
|
17751
|
+
sessionId: row.sessionId,
|
|
17752
|
+
sequence: row.sequence,
|
|
17753
|
+
eventType: row.eventType,
|
|
17754
|
+
payload: row.payload,
|
|
17755
|
+
batchCount: row.batchCount,
|
|
17756
|
+
createdAt: row.createdAt
|
|
17757
|
+
};
|
|
17758
|
+
}
|
|
17759
|
+
// ============================================================================
|
|
17133
17760
|
// Database Access (for extensions)
|
|
17134
17761
|
// ============================================================================
|
|
17135
17762
|
/**
|
|
@@ -17935,7 +18562,7 @@ var init_storage3 = __esm({
|
|
|
17935
18562
|
}
|
|
17936
18563
|
if (options.filePath) {
|
|
17937
18564
|
query = query.or(
|
|
17938
|
-
`event_data->>filePath.eq.${options.filePath},and(category.eq.tool,tool_name.in.(Read,Write,Edit),event_data->input->>file_path.eq.${options.filePath})`
|
|
18565
|
+
`event_data->>filePath.eq.${options.filePath},event_data->>canonicalPath.eq.${options.filePath},and(category.eq.tool,tool_name.in.(Read,Write,Edit),event_data->input->>file_path.eq.${options.filePath})`
|
|
17939
18566
|
);
|
|
17940
18567
|
}
|
|
17941
18568
|
const ascending = options.order !== "desc";
|
|
@@ -17965,6 +18592,72 @@ var init_storage3 = __esm({
|
|
|
17965
18592
|
}
|
|
17966
18593
|
return data ? data.sequence_number + 1 : 1;
|
|
17967
18594
|
}
|
|
18595
|
+
// ============================================================================
|
|
18596
|
+
// Stream Events (SSE Relay)
|
|
18597
|
+
// ============================================================================
|
|
18598
|
+
async appendEvents(sessionId, events) {
|
|
18599
|
+
if (events.length === 0) return [];
|
|
18600
|
+
const { data: maxSeqData } = await this.client.from("stream_events").select("sequence").eq("session_id", sessionId).order("sequence", { ascending: false }).limit(1).single();
|
|
18601
|
+
let nextSeq = maxSeqData ? maxSeqData.sequence + 1 : 1;
|
|
18602
|
+
const { data, error } = await this.client.from("stream_events").insert(
|
|
18603
|
+
events.map((event) => ({
|
|
18604
|
+
session_id: sessionId,
|
|
18605
|
+
sequence: nextSeq++,
|
|
18606
|
+
event_type: event.eventType,
|
|
18607
|
+
payload: event.payload,
|
|
18608
|
+
batch_count: event.batchCount ?? 1
|
|
18609
|
+
}))
|
|
18610
|
+
).select();
|
|
18611
|
+
if (error) {
|
|
18612
|
+
throw new Error(`Failed to append stream events: ${error.message}`);
|
|
18613
|
+
}
|
|
18614
|
+
return data.map((row) => this.rowToStoredStreamEvent(row));
|
|
18615
|
+
}
|
|
18616
|
+
async readEvents(sessionId, options) {
|
|
18617
|
+
let query = this.client.from("stream_events").select("*").eq("session_id", sessionId);
|
|
18618
|
+
if (options?.afterSequence !== void 0) {
|
|
18619
|
+
query = query.gt("sequence", options.afterSequence);
|
|
18620
|
+
}
|
|
18621
|
+
const limit = options?.limit ?? 1e3;
|
|
18622
|
+
query = query.order("sequence", { ascending: true }).limit(limit);
|
|
18623
|
+
const { data, error } = await query;
|
|
18624
|
+
if (error) {
|
|
18625
|
+
throw new Error(`Failed to read stream events: ${error.message}`);
|
|
18626
|
+
}
|
|
18627
|
+
return data.map((row) => this.rowToStoredStreamEvent(row));
|
|
18628
|
+
}
|
|
18629
|
+
async getLatestSequence(sessionId) {
|
|
18630
|
+
const { data, error } = await this.client.from("stream_events").select("sequence").eq("session_id", sessionId).order("sequence", { ascending: false }).limit(1).single();
|
|
18631
|
+
if (error && error.code !== "PGRST116") {
|
|
18632
|
+
throw new Error(`Failed to get latest sequence: ${error.message}`);
|
|
18633
|
+
}
|
|
18634
|
+
return data ? data.sequence : 0;
|
|
18635
|
+
}
|
|
18636
|
+
async deleteStreamEvents(sessionId) {
|
|
18637
|
+
const { error } = await this.client.from("stream_events").delete().eq("session_id", sessionId);
|
|
18638
|
+
if (error) {
|
|
18639
|
+
throw new Error(`Failed to delete stream events: ${error.message}`);
|
|
18640
|
+
}
|
|
18641
|
+
}
|
|
18642
|
+
async cleanupOldEvents(maxAgeMs) {
|
|
18643
|
+
const cutoff = new Date(Date.now() - maxAgeMs).toISOString();
|
|
18644
|
+
const { data, error } = await this.client.from("stream_events").delete().lt("created_at", cutoff).select("id");
|
|
18645
|
+
if (error) {
|
|
18646
|
+
throw new Error(`Failed to cleanup old stream events: ${error.message}`);
|
|
18647
|
+
}
|
|
18648
|
+
return data?.length ?? 0;
|
|
18649
|
+
}
|
|
18650
|
+
rowToStoredStreamEvent(row) {
|
|
18651
|
+
return {
|
|
18652
|
+
id: row.id,
|
|
18653
|
+
sessionId: row.session_id,
|
|
18654
|
+
sequence: row.sequence,
|
|
18655
|
+
eventType: row.event_type,
|
|
18656
|
+
payload: row.payload,
|
|
18657
|
+
batchCount: row.batch_count,
|
|
18658
|
+
createdAt: new Date(row.created_at)
|
|
18659
|
+
};
|
|
18660
|
+
}
|
|
17968
18661
|
};
|
|
17969
18662
|
}
|
|
17970
18663
|
});
|
|
@@ -18797,6 +19490,7 @@ var init_src = __esm({
|
|
|
18797
19490
|
init_credentials();
|
|
18798
19491
|
init_skills();
|
|
18799
19492
|
init_utils();
|
|
19493
|
+
init_relay();
|
|
18800
19494
|
init_server();
|
|
18801
19495
|
init_server2();
|
|
18802
19496
|
init_schemas();
|
|
@@ -18973,6 +19667,7 @@ exports.shouldUseSandbox = shouldUseSandbox;
|
|
|
18973
19667
|
exports.shutdownSandboxPool = shutdownSandboxPool;
|
|
18974
19668
|
exports.sseMcpWithAuth = sseMcpWithAuth;
|
|
18975
19669
|
exports.startServer = startServer;
|
|
19670
|
+
exports.streamEventRelay = streamEventRelay;
|
|
18976
19671
|
exports.updateToolCallWithResult = updateToolCallWithResult;
|
|
18977
19672
|
exports.writeFileToSandbox = writeFileToSandbox;
|
|
18978
19673
|
//# sourceMappingURL=index.cjs.map
|