@jait/gateway 0.1.550 → 0.1.553

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/routes/chat.d.ts.map +1 -1
  2. package/dist/routes/chat.js +66 -21
  3. package/dist/routes/chat.js.map +1 -1
  4. package/dist/routes/threads.d.ts.map +1 -1
  5. package/dist/routes/threads.js +16 -0
  6. package/dist/routes/threads.js.map +1 -1
  7. package/dist/services/thread-router.d.ts.map +1 -1
  8. package/dist/services/thread-router.js +5 -3
  9. package/dist/services/thread-router.js.map +1 -1
  10. package/dist/tools/agent-loop.d.ts.map +1 -1
  11. package/dist/tools/agent-loop.js +12 -0
  12. package/dist/tools/agent-loop.js.map +1 -1
  13. package/package.json +2 -2
  14. package/web-dist/assets/{_basePickBy-Dp2NyPAx.js → _basePickBy-DvI7dW8V.js} +1 -1
  15. package/web-dist/assets/{_baseUniq-Cv3a4hfc.js → _baseUniq-1ihWHc9y.js} +1 -1
  16. package/web-dist/assets/{arc-CPixogAk.js → arc-kvIsHe11.js} +1 -1
  17. package/web-dist/assets/{architectureDiagram-2XIMDMQ5-DSvD2ZZi.js → architectureDiagram-2XIMDMQ5-CSCYhmPn.js} +1 -1
  18. package/web-dist/assets/{blockDiagram-WCTKOSBZ-CSzjczoh.js → blockDiagram-WCTKOSBZ-R_Y3-6lR.js} +1 -1
  19. package/web-dist/assets/{c4Diagram-IC4MRINW-HtEHTMAh.js → c4Diagram-IC4MRINW-556ThiPx.js} +1 -1
  20. package/web-dist/assets/channel-B_F9voP2.js +1 -0
  21. package/web-dist/assets/{chunk-4BX2VUAB-Bf-f74ZD.js → chunk-4BX2VUAB-BvXfLkWT.js} +1 -1
  22. package/web-dist/assets/{chunk-55IACEB6-DjmtjKpi.js → chunk-55IACEB6-DieOGDSZ.js} +1 -1
  23. package/web-dist/assets/{chunk-FMBD7UC4-CfSdN_3Q.js → chunk-FMBD7UC4-CL7tnOm_.js} +1 -1
  24. package/web-dist/assets/{chunk-JSJVCQXG-g7BDCVpe.js → chunk-JSJVCQXG-CFpbZu21.js} +1 -1
  25. package/web-dist/assets/{chunk-KX2RTZJC-D8T-LUIR.js → chunk-KX2RTZJC-BZeqeuDy.js} +1 -1
  26. package/web-dist/assets/{chunk-NQ4KR5QH-E1sqN5pn.js → chunk-NQ4KR5QH-BviS_Rvb.js} +1 -1
  27. package/web-dist/assets/{chunk-QZHKN3VN-Bx5hIiz4.js → chunk-QZHKN3VN-BoO08RGh.js} +1 -1
  28. package/web-dist/assets/{chunk-WL4C6EOR-ByXOkMiV.js → chunk-WL4C6EOR-BkGVNP8A.js} +1 -1
  29. package/web-dist/assets/classDiagram-VBA2DB6C-DSXp3uOM.js +1 -0
  30. package/web-dist/assets/classDiagram-v2-RAHNMMFH-DSXp3uOM.js +1 -0
  31. package/web-dist/assets/clone-BndpjwTg.js +1 -0
  32. package/web-dist/assets/{cose-bilkent-S5V4N54A-DQJ1rWoS.js → cose-bilkent-S5V4N54A-aV1nXScq.js} +1 -1
  33. package/web-dist/assets/{dagre-KLK3FWXG-CHh0QVw_.js → dagre-KLK3FWXG-u2jAD-hh.js} +1 -1
  34. package/web-dist/assets/{diagram-E7M64L7V-DM1x9aKy.js → diagram-E7M64L7V-DbEZsnzR.js} +1 -1
  35. package/web-dist/assets/{diagram-IFDJBPK2-C0gnOtfS.js → diagram-IFDJBPK2-p34ED3DN.js} +1 -1
  36. package/web-dist/assets/{diagram-P4PSJMXO-DOICDmQd.js → diagram-P4PSJMXO-DKLijNZg.js} +1 -1
  37. package/web-dist/assets/{erDiagram-INFDFZHY-eMzR0jJ2.js → erDiagram-INFDFZHY-Cwq9W4G8.js} +1 -1
  38. package/web-dist/assets/{flowDiagram-PKNHOUZH-C7H26CoC.js → flowDiagram-PKNHOUZH-BbuWTEov.js} +1 -1
  39. package/web-dist/assets/{ganttDiagram-A5KZAMGK-C5wozmPC.js → ganttDiagram-A5KZAMGK-CCX00m3B.js} +1 -1
  40. package/web-dist/assets/{gitGraphDiagram-K3NZZRJ6-DlLV2W4D.js → gitGraphDiagram-K3NZZRJ6-DJiy0306.js} +1 -1
  41. package/web-dist/assets/{graph-Jatw0BgY.js → graph-DZb4ZtmZ.js} +1 -1
  42. package/web-dist/assets/{index-DqA2TMw6.js → index-CU-7VgCj.js} +1 -1
  43. package/web-dist/assets/{index-ByIT5B0X.js → index-DwLI2BcF.js} +379 -379
  44. package/web-dist/assets/{index-DEu1E_j7.js → index-Is4s4-P0.js} +1 -1
  45. package/web-dist/assets/{infoDiagram-LFFYTUFH-DSFBtmB-.js → infoDiagram-LFFYTUFH-BCHKrdng.js} +1 -1
  46. package/web-dist/assets/{ishikawaDiagram-PHBUUO56-B46mTWHh.js → ishikawaDiagram-PHBUUO56-ceGcqRw4.js} +1 -1
  47. package/web-dist/assets/{journeyDiagram-4ABVD52K-4ASDg60N.js → journeyDiagram-4ABVD52K-DILZrTC2.js} +1 -1
  48. package/web-dist/assets/{kanban-definition-K7BYSVSG-BYlV4Vtg.js → kanban-definition-K7BYSVSG-Ce_c4wVl.js} +1 -1
  49. package/web-dist/assets/{layout-DZD4LK4k.js → layout-BhCmU-BF.js} +1 -1
  50. package/web-dist/assets/{linear-Cr_-2rAF.js → linear-Du9uBf61.js} +1 -1
  51. package/web-dist/assets/{mindmap-definition-YRQLILUH-B0RQyALT.js → mindmap-definition-YRQLILUH-B8irtriU.js} +1 -1
  52. package/web-dist/assets/{pieDiagram-SKSYHLDU-DlqEQkBe.js → pieDiagram-SKSYHLDU-DRYarfMw.js} +1 -1
  53. package/web-dist/assets/{quadrantDiagram-337W2JSQ-TwiaxjYw.js → quadrantDiagram-337W2JSQ-CWa9hYgm.js} +1 -1
  54. package/web-dist/assets/{requirementDiagram-Z7DCOOCP-LCG5OTze.js → requirementDiagram-Z7DCOOCP-Dr0Dh1G9.js} +1 -1
  55. package/web-dist/assets/{sankeyDiagram-WA2Y5GQK-BQqU31_e.js → sankeyDiagram-WA2Y5GQK-CSIlj_5q.js} +1 -1
  56. package/web-dist/assets/{sequenceDiagram-2WXFIKYE-DIE9LH9s.js → sequenceDiagram-2WXFIKYE-cvPrF5kh.js} +1 -1
  57. package/web-dist/assets/{stateDiagram-RAJIS63D-CkVALlUV.js → stateDiagram-RAJIS63D-CcMtQOjg.js} +1 -1
  58. package/web-dist/assets/stateDiagram-v2-FVOUBMTO-6OPyDgAh.js +1 -0
  59. package/web-dist/assets/{timeline-definition-YZTLITO2-UTbg0ItL.js → timeline-definition-YZTLITO2-C6w5hhZ1.js} +1 -1
  60. package/web-dist/assets/{treemap-KZPCXAKY-B3ZI07yL.js → treemap-KZPCXAKY-CcM8WExl.js} +1 -1
  61. package/web-dist/assets/{vennDiagram-LZ73GAT5-Cpy6iU9G.js → vennDiagram-LZ73GAT5-CGlE-r2b.js} +1 -1
  62. package/web-dist/assets/{xychartDiagram-JWTSCODW--XzABSvu.js → xychartDiagram-JWTSCODW-DUFote5V.js} +1 -1
  63. package/web-dist/index.html +1 -1
  64. package/web-dist/assets/channel-C8Ypz7L8.js +0 -1
  65. package/web-dist/assets/classDiagram-VBA2DB6C-BgcBXLb4.js +0 -1
  66. package/web-dist/assets/classDiagram-v2-RAHNMMFH-BgcBXLb4.js +0 -1
  67. package/web-dist/assets/clone-D91NotDC.js +0 -1
  68. package/web-dist/assets/stateDiagram-v2-FVOUBMTO-CDJgTjE4.js +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/routes/chat.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAA4B,MAAM,uBAAuB,CAAC;AACnF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAe,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAgCjE,OAAO,KAAK,EAAS,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAgGvF,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAmBrE;AAyED,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAE3D;AAscD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAyCtF;AAmiBD,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,oBAAoB,CAAC,EAAE,0BAA0B,CAAC;IAClD,YAAY,CAAC,EAAE,CACb,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,KACtD,OAAO,CAAC,UAAU,CAAC,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,SAAS,EACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,aAAa,EACjC,iBAAiB,CAAC,EAAE,cAAc,QA6nEnC"}
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/routes/chat.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAA4B,MAAM,uBAAuB,CAAC;AACnF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAe,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAgCjE,OAAO,KAAK,EAAS,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAqHvF,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAmBrE;AA8FD,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAE3D;AAydD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAyCtF;AAmiBD,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,oBAAoB,CAAC,EAAE,0BAA0B,CAAC;IAClD,YAAY,CAAC,EAAE,CACb,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,KACtD,OAAO,CAAC,UAAU,CAAC,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,SAAS,EACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,aAAa,EACjC,iBAAiB,CAAC,EAAE,cAAc,QAmnEnC"}
@@ -32,6 +32,24 @@ function formatExternalProviderFirstTurn(systemPrompt, userContent, recentContex
32
32
  parts.push("User request:", userContent);
33
33
  return parts.join("\n");
34
34
  }
35
+ const SSE_HEADERS = {
36
+ "Content-Type": "text/event-stream",
37
+ "Cache-Control": "no-cache, no-transform",
38
+ Connection: "keep-alive",
39
+ "X-Accel-Buffering": "no",
40
+ "Content-Encoding": "identity",
41
+ };
42
+ function writeSseHead(reply, headers = {}) {
43
+ reply.raw.writeHead(200, {
44
+ ...SSE_HEADERS,
45
+ ...headers,
46
+ });
47
+ reply.raw.flushHeaders?.();
48
+ }
49
+ function writeSseChunk(reply, chunk) {
50
+ reply.raw.write(chunk);
51
+ reply.raw.flush?.();
52
+ }
35
53
  function normalizeExternalContextContent(content) {
36
54
  if (typeof content === "string")
37
55
  return content;
@@ -131,6 +149,25 @@ function truncateForMemoryBlock(value, max = 220) {
131
149
  function shouldIncludeContactMemories(content) {
132
150
  return /\b(based on what you know|remember|preference|preferences|prefer|my usual|about me|what do you know)\b/i.test(content);
133
151
  }
152
+ function memoryRelevanceTokens(content) {
153
+ return new Set(content
154
+ .toLowerCase()
155
+ .split(/[^a-z0-9_äöüß]+/i)
156
+ .map((token) => token.trim())
157
+ .filter((token) => token.length >= 4)
158
+ .filter((token) => !["this", "that", "with", "from", "have", "your", "what", "when", "where", "which", "there", "their", "about", "should", "would", "could", "please", "help"].includes(token)));
159
+ }
160
+ function hasLexicalMemoryOverlap(query, content) {
161
+ const queryTokens = memoryRelevanceTokens(query);
162
+ if (queryTokens.size === 0)
163
+ return false;
164
+ const contentTokens = memoryRelevanceTokens(content);
165
+ for (const token of queryTokens) {
166
+ if (contentTokens.has(token))
167
+ return true;
168
+ }
169
+ return false;
170
+ }
134
171
  function memorySourceLabel(entry) {
135
172
  if (entry.source.type === "pre_compaction") {
136
173
  return "memory compact";
@@ -170,12 +207,15 @@ async function retrieveRelevantMemoryContext(memoryService, content) {
170
207
  return null;
171
208
  const projectMemories = await memoryService.search(query, 25, "project");
172
209
  const byId = new Map();
210
+ const includeBroadMemory = shouldIncludeContactMemories(query);
173
211
  for (const entry of projectMemories) {
174
212
  if (!isSourceVisible(entry))
175
213
  continue;
214
+ if (!includeBroadMemory && !hasLexicalMemoryOverlap(query, entry.content))
215
+ continue;
176
216
  byId.set(entry.id, entry);
177
217
  }
178
- if (shouldIncludeContactMemories(query)) {
218
+ if (includeBroadMemory) {
179
219
  const contactMemories = await memoryService.search(query, Math.max(0, 25 - byId.size), "contact");
180
220
  for (const entry of contactMemories) {
181
221
  if (!isSourceVisible(entry))
@@ -342,6 +382,14 @@ function parseUserDisplaySegments(raw) {
342
382
  });
343
383
  continue;
344
384
  }
385
+ if (record.type === "skill" && typeof record.id === "string") {
386
+ segments.push({
387
+ type: "skill",
388
+ id: record.id,
389
+ name: typeof record.name === "string" ? record.name : record.id,
390
+ });
391
+ continue;
392
+ }
345
393
  if (record.type === "image"
346
394
  && typeof record.data === "string"
347
395
  && typeof record.mimeType === "string"
@@ -405,15 +453,22 @@ function parseQueuedChatMessages(raw) {
405
453
  if (!Array.isArray(raw))
406
454
  return [];
407
455
  const queue = [];
456
+ const seenIds = new Set();
408
457
  for (const entry of raw) {
409
458
  if (!entry || typeof entry !== "object")
410
459
  continue;
411
460
  const record = entry;
412
461
  if (typeof record.content !== "string" || !record.content.trim())
413
462
  continue;
463
+ const id = typeof record.id === "string" ? record.id : undefined;
464
+ if (id) {
465
+ if (seenIds.has(id))
466
+ continue;
467
+ seenIds.add(id);
468
+ }
414
469
  const displaySegments = parseUserDisplaySegments(record.displaySegments);
415
470
  queue.push({
416
- id: typeof record.id === "string" ? record.id : undefined,
471
+ id,
417
472
  content: record.content,
418
473
  queuedAt: typeof record.queuedAt === "number" ? record.queuedAt : undefined,
419
474
  mode: isValidChatMode(record.mode) ? record.mode : undefined,
@@ -1414,14 +1469,8 @@ export function registerChatRoutes(app, config, depsOrDb, sessionServiceArg) {
1414
1469
  }
1415
1470
  // Set SSE headers (include CORS — reply.raw bypasses @fastify/cors)
1416
1471
  const reqOrigin = request.headers.origin ?? "*";
1417
- reply.raw.writeHead(200, {
1418
- "Content-Type": "text/event-stream",
1419
- "Cache-Control": "no-cache",
1420
- Connection: "keep-alive",
1421
- // Prevent reverse proxies (nginx etc.) from buffering the SSE stream,
1422
- // which would otherwise swallow chunks and let the connection time out.
1423
- "X-Accel-Buffering": "no",
1424
- "Access-Control-Allow-Origin": reqOrigin,
1472
+ writeSseHead(reply, {
1473
+ "Access-Control-Allow-Origin": String(reqOrigin),
1425
1474
  "Access-Control-Allow-Credentials": "true",
1426
1475
  });
1427
1476
  // Build model endpoint for prompt resolution
@@ -1543,7 +1592,7 @@ export function registerChatRoutes(app, config, depsOrDb, sessionServiceArg) {
1543
1592
  const safeWrite = (data) => {
1544
1593
  if (!clientDisconnected) {
1545
1594
  try {
1546
- reply.raw.write(data);
1595
+ writeSseChunk(reply, data);
1547
1596
  }
1548
1597
  catch {
1549
1598
  clientDisconnected = true;
@@ -2639,12 +2688,8 @@ export function registerChatRoutes(app, config, depsOrDb, sessionServiceArg) {
2639
2688
  const query = request.query;
2640
2689
  const limit = parseMessageLimit(query?.limit);
2641
2690
  const reqOrigin = request.headers.origin ?? "*";
2642
- reply.raw.writeHead(200, {
2643
- "Content-Type": "text/event-stream",
2644
- "Cache-Control": "no-cache",
2645
- Connection: "keep-alive",
2646
- "X-Accel-Buffering": "no",
2647
- "Access-Control-Allow-Origin": reqOrigin,
2691
+ writeSseHead(reply, {
2692
+ "Access-Control-Allow-Origin": String(reqOrigin),
2648
2693
  "Access-Control-Allow-Credentials": "true",
2649
2694
  });
2650
2695
  const isStreaming = activeStreams.has(sessionId);
@@ -2674,7 +2719,7 @@ export function registerChatRoutes(app, config, depsOrDb, sessionServiceArg) {
2674
2719
  total = windowed.total;
2675
2720
  hasMore = windowed.hasMore;
2676
2721
  }
2677
- reply.raw.write(`data: ${JSON.stringify({
2722
+ writeSseChunk(reply, `data: ${JSON.stringify({
2678
2723
  type: "snapshot",
2679
2724
  messages: snapshotMessages,
2680
2725
  streaming: isStreaming,
@@ -2685,7 +2730,7 @@ export function registerChatRoutes(app, config, depsOrDb, sessionServiceArg) {
2685
2730
  })}\n\n`);
2686
2731
  if (!isStreaming) {
2687
2732
  // Not streaming — send done immediately
2688
- reply.raw.write(`data: ${JSON.stringify({ type: "done", session_id: sessionId, prompt_count: history.filter(m => m.role === "user").length, remaining_prompts: null })}\n\n`);
2733
+ writeSseChunk(reply, `data: ${JSON.stringify({ type: "done", session_id: sessionId, prompt_count: history.filter(m => m.role === "user").length, remaining_prompts: null })}\n\n`);
2689
2734
  reply.raw.end();
2690
2735
  return;
2691
2736
  }
@@ -2715,7 +2760,7 @@ export function registerChatRoutes(app, config, depsOrDb, sessionServiceArg) {
2715
2760
  if (closed)
2716
2761
  return;
2717
2762
  try {
2718
- reply.raw.write(`: keepalive\n\n`);
2763
+ writeSseChunk(reply, `: keepalive\n\n`);
2719
2764
  }
2720
2765
  catch {
2721
2766
  closeStream();
@@ -2725,7 +2770,7 @@ export function registerChatRoutes(app, config, depsOrDb, sessionServiceArg) {
2725
2770
  if (closed)
2726
2771
  return;
2727
2772
  try {
2728
- reply.raw.write(`data: ${JSON.stringify(event)}\n\n`);
2773
+ writeSseChunk(reply, `data: ${JSON.stringify(event)}\n\n`);
2729
2774
  if (event.type === "done" || event.type === "error") {
2730
2775
  closeStream();
2731
2776
  }