@wingman-ai/gateway 0.2.4 → 0.3.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.
Files changed (95) hide show
  1. package/.wingman/agents/coding/agent.md +5 -0
  2. package/.wingman/agents/coding-v2/agent.md +58 -0
  3. package/.wingman/agents/game-dev/agent.md +94 -0
  4. package/.wingman/agents/game-dev/art-generation.md +37 -0
  5. package/.wingman/agents/game-dev/asset-refinement.md +17 -0
  6. package/.wingman/agents/game-dev/planning-idea.md +17 -0
  7. package/.wingman/agents/game-dev/ui-specialist.md +17 -0
  8. package/.wingman/agents/main/agent.md +2 -0
  9. package/README.md +1 -0
  10. package/dist/agent/config/agentConfig.d.ts +4 -0
  11. package/dist/agent/config/mcpClientManager.cjs +44 -10
  12. package/dist/agent/config/mcpClientManager.d.ts +6 -2
  13. package/dist/agent/config/mcpClientManager.js +44 -10
  14. package/dist/agent/config/toolRegistry.cjs +3 -1
  15. package/dist/agent/config/toolRegistry.js +3 -1
  16. package/dist/agent/tests/mcpClientManager.test.cjs +124 -0
  17. package/dist/agent/tests/mcpClientManager.test.d.ts +1 -0
  18. package/dist/agent/tests/mcpClientManager.test.js +118 -0
  19. package/dist/agent/tools/command_execute.cjs +1 -1
  20. package/dist/agent/tools/command_execute.js +1 -1
  21. package/dist/cli/config/schema.d.ts +2 -0
  22. package/dist/cli/core/agentInvoker.cjs +55 -66
  23. package/dist/cli/core/agentInvoker.d.ts +10 -13
  24. package/dist/cli/core/agentInvoker.js +42 -62
  25. package/dist/cli/core/imagePersistence.cjs +125 -0
  26. package/dist/cli/core/imagePersistence.d.ts +24 -0
  27. package/dist/cli/core/imagePersistence.js +85 -0
  28. package/dist/cli/core/sessionManager.cjs +297 -40
  29. package/dist/cli/core/sessionManager.d.ts +9 -0
  30. package/dist/cli/core/sessionManager.js +297 -40
  31. package/dist/debug/terminalProbe.cjs +57 -0
  32. package/dist/debug/terminalProbe.d.ts +10 -0
  33. package/dist/debug/terminalProbe.js +20 -0
  34. package/dist/debug/terminalProbeAuth.cjs +140 -0
  35. package/dist/debug/terminalProbeAuth.d.ts +20 -0
  36. package/dist/debug/terminalProbeAuth.js +97 -0
  37. package/dist/gateway/http/fs.cjs +19 -0
  38. package/dist/gateway/http/fs.js +19 -0
  39. package/dist/gateway/http/sessions.cjs +25 -5
  40. package/dist/gateway/http/sessions.js +25 -5
  41. package/dist/gateway/server.cjs +112 -11
  42. package/dist/gateway/server.d.ts +2 -0
  43. package/dist/gateway/server.js +112 -11
  44. package/dist/providers/codex.cjs +230 -37
  45. package/dist/providers/codex.d.ts +2 -0
  46. package/dist/providers/codex.js +231 -38
  47. package/dist/tests/agentInvokerSummarization.test.cjs +56 -37
  48. package/dist/tests/agentInvokerSummarization.test.js +58 -39
  49. package/dist/tests/agentInvokerWorkdir.test.cjs +50 -0
  50. package/dist/tests/agentInvokerWorkdir.test.js +52 -2
  51. package/dist/tests/cli-init.test.cjs +36 -0
  52. package/dist/tests/cli-init.test.js +36 -0
  53. package/dist/tests/codex-provider.test.cjs +173 -0
  54. package/dist/tests/codex-provider.test.js +174 -1
  55. package/dist/tests/falRuntime.test.cjs +78 -0
  56. package/dist/tests/falRuntime.test.d.ts +1 -0
  57. package/dist/tests/falRuntime.test.js +72 -0
  58. package/dist/tests/falSummary.test.cjs +51 -0
  59. package/dist/tests/falSummary.test.d.ts +1 -0
  60. package/dist/tests/falSummary.test.js +45 -0
  61. package/dist/tests/gateway.test.cjs +109 -1
  62. package/dist/tests/gateway.test.js +109 -1
  63. package/dist/tests/imagePersistence.test.cjs +143 -0
  64. package/dist/tests/imagePersistence.test.d.ts +1 -0
  65. package/dist/tests/imagePersistence.test.js +137 -0
  66. package/dist/tests/sessionMessageAttachments.test.cjs +30 -0
  67. package/dist/tests/sessionMessageAttachments.test.js +30 -0
  68. package/dist/tests/sessionStateMessages.test.cjs +126 -0
  69. package/dist/tests/sessionStateMessages.test.js +126 -0
  70. package/dist/tests/sessions-api.test.cjs +117 -3
  71. package/dist/tests/sessions-api.test.js +118 -4
  72. package/dist/tests/terminalProbe.test.cjs +45 -0
  73. package/dist/tests/terminalProbe.test.d.ts +1 -0
  74. package/dist/tests/terminalProbe.test.js +39 -0
  75. package/dist/tests/terminalProbeAuth.test.cjs +85 -0
  76. package/dist/tests/terminalProbeAuth.test.d.ts +1 -0
  77. package/dist/tests/terminalProbeAuth.test.js +79 -0
  78. package/dist/tools/fal/runtime.cjs +103 -0
  79. package/dist/tools/fal/runtime.d.ts +10 -0
  80. package/dist/tools/fal/runtime.js +60 -0
  81. package/dist/tools/fal/summary.cjs +78 -0
  82. package/dist/tools/fal/summary.d.ts +22 -0
  83. package/dist/tools/fal/summary.js +41 -0
  84. package/dist/tools/mcp-fal-ai.cjs +1041 -0
  85. package/dist/tools/mcp-fal-ai.d.ts +1 -0
  86. package/dist/tools/mcp-fal-ai.js +1025 -0
  87. package/dist/types/mcp.cjs +2 -0
  88. package/dist/types/mcp.d.ts +8 -0
  89. package/dist/types/mcp.js +3 -1
  90. package/dist/webui/assets/index-0nUBsUUq.js +278 -0
  91. package/dist/webui/assets/index-kk7OrD-G.css +11 -0
  92. package/dist/webui/index.html +2 -2
  93. package/package.json +16 -13
  94. package/dist/webui/assets/index-DVWQluit.css +0 -11
  95. package/dist/webui/assets/index-Dlyzwalc.js +0 -270
@@ -34,6 +34,7 @@ __webpack_require__.d(__webpack_exports__, {
34
34
  const langgraph_checkpoint_sqlite_namespaceObject = require("@langchain/langgraph-checkpoint-sqlite");
35
35
  const external_deepagents_namespaceObject = require("deepagents");
36
36
  const external_uuid_namespaceObject = require("uuid");
37
+ const external_imagePersistence_cjs_namespaceObject = require("./imagePersistence.cjs");
37
38
  function _define_property(obj, key, value) {
38
39
  if (key in obj) Object.defineProperty(obj, key, {
39
40
  value: value,
@@ -54,9 +55,9 @@ class SessionManager {
54
55
  await this.checkpointer.setup();
55
56
  this.db = adapter.db;
56
57
  this.db.run(`
57
- CREATE TABLE IF NOT EXISTS sessions (
58
- id TEXT PRIMARY KEY,
59
- name TEXT NOT NULL,
58
+ CREATE TABLE IF NOT EXISTS sessions (
59
+ id TEXT PRIMARY KEY,
60
+ name TEXT NOT NULL,
60
61
  agent_name TEXT NOT NULL,
61
62
  created_at INTEGER NOT NULL,
62
63
  updated_at INTEGER NOT NULL,
@@ -66,11 +67,24 @@ class SessionManager {
66
67
  metadata TEXT
67
68
  );
68
69
 
69
- CREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(updated_at DESC);
70
- CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions(agent_name);
71
- CREATE INDEX IF NOT EXISTS idx_sessions_status_updated ON sessions(status, updated_at DESC);
72
- CREATE INDEX IF NOT EXISTS idx_sessions_status_agent_updated ON sessions(status, agent_name, updated_at DESC);
73
- `);
70
+ CREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(updated_at DESC);
71
+ CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions(agent_name);
72
+ CREATE INDEX IF NOT EXISTS idx_sessions_status_updated ON sessions(status, updated_at DESC);
73
+ CREATE INDEX IF NOT EXISTS idx_sessions_status_agent_updated ON sessions(status, agent_name, updated_at DESC);
74
+
75
+ CREATE TABLE IF NOT EXISTS session_pending_messages (
76
+ id TEXT PRIMARY KEY,
77
+ session_id TEXT NOT NULL,
78
+ request_id TEXT NOT NULL,
79
+ role TEXT NOT NULL,
80
+ content TEXT NOT NULL,
81
+ attachments TEXT,
82
+ created_at INTEGER NOT NULL
83
+ );
84
+
85
+ CREATE INDEX IF NOT EXISTS idx_pending_messages_session_created ON session_pending_messages(session_id, created_at ASC);
86
+ CREATE INDEX IF NOT EXISTS idx_pending_messages_session_request ON session_pending_messages(session_id, request_id);
87
+ `);
74
88
  }
75
89
  createSession(agentName, name) {
76
90
  if (!this.db) throw new Error("SessionManager not initialized");
@@ -211,6 +225,8 @@ class SessionManager {
211
225
  checkpointStmt.run(sessionId);
212
226
  const writesStmt = this.db.prepare("DELETE FROM writes WHERE thread_id = ?");
213
227
  writesStmt.run(sessionId);
228
+ const pendingStmt = this.db.prepare("DELETE FROM session_pending_messages WHERE session_id = ?");
229
+ pendingStmt.run(sessionId);
214
230
  }
215
231
  clearSessionMessages(sessionId) {
216
232
  if (!this.db || !this.checkpointer) throw new Error("SessionManager not initialized");
@@ -218,21 +234,52 @@ class SessionManager {
218
234
  checkpointStmt.run(sessionId);
219
235
  const writesStmt = this.db.prepare("DELETE FROM writes WHERE thread_id = ?");
220
236
  writesStmt.run(sessionId);
237
+ const pendingStmt = this.db.prepare("DELETE FROM session_pending_messages WHERE session_id = ?");
238
+ pendingStmt.run(sessionId);
221
239
  const sessionStmt = this.db.prepare(`
222
- UPDATE sessions
223
- SET message_count = 0, last_message_preview = NULL, updated_at = ?
224
- WHERE id = ?
225
- `);
240
+ UPDATE sessions
241
+ SET message_count = 0, last_message_preview = NULL, updated_at = ?
242
+ WHERE id = ?
243
+ `);
226
244
  sessionStmt.run(Date.now(), sessionId);
227
245
  }
246
+ persistPendingMessage(input) {
247
+ if (!this.db) throw new Error("SessionManager not initialized");
248
+ const attachments = Array.isArray(input.message.attachments) && input.message.attachments.length > 0 ? JSON.stringify(input.message.attachments) : null;
249
+ const stmt = this.db.prepare(`
250
+ INSERT INTO session_pending_messages (
251
+ id, session_id, request_id, role, content, attachments, created_at
252
+ )
253
+ VALUES (?, ?, ?, ?, ?, ?, ?)
254
+ ON CONFLICT(id) DO UPDATE SET
255
+ role = excluded.role,
256
+ content = excluded.content,
257
+ attachments = excluded.attachments,
258
+ created_at = excluded.created_at
259
+ `);
260
+ stmt.run(input.message.id, input.sessionId, input.requestId, input.message.role, input.message.content || "", attachments, input.message.createdAt);
261
+ }
262
+ clearPendingMessagesForRequest(sessionId, requestId) {
263
+ if (!this.db) throw new Error("SessionManager not initialized");
264
+ const stmt = this.db.prepare(`
265
+ DELETE FROM session_pending_messages
266
+ WHERE session_id = ? AND request_id = ?
267
+ `);
268
+ stmt.run(sessionId, requestId);
269
+ }
228
270
  getCheckpointer() {
229
271
  if (!this.checkpointer) throw new Error("SessionManager not initialized");
230
272
  return this.checkpointer;
231
273
  }
232
274
  async listMessages(sessionId) {
233
275
  if (!this.checkpointer) throw new Error("SessionManager not initialized");
276
+ const pendingMessages = this.listPendingMessages(sessionId);
234
277
  const stateMessages = await this.loadMessagesFromState(sessionId);
235
- if (null !== stateMessages) return stateMessages;
278
+ if (null !== stateMessages) {
279
+ const mergedStateMessages = mergePendingMessages(stateMessages, pendingMessages);
280
+ this.persistAssistantImageAttachments(sessionId, mergedStateMessages);
281
+ return mergedStateMessages;
282
+ }
236
283
  const rawCheckpoints = this.loadRecentCheckpoints(sessionId, 25);
237
284
  const fallbackTuple = 0 === rawCheckpoints.length ? await this.checkpointer.getTuple({
238
285
  configurable: {
@@ -245,7 +292,10 @@ class SessionManager {
245
292
  fallbackTuple.checkpoint
246
293
  ] : []
247
294
  ];
248
- if (0 === checkpoints.length) return [];
295
+ if (0 === checkpoints.length) {
296
+ this.persistAssistantImageAttachments(sessionId, pendingMessages);
297
+ return pendingMessages;
298
+ }
249
299
  let bestScore = -1;
250
300
  let bestMessages = [];
251
301
  for (const checkpoint of checkpoints){
@@ -263,8 +313,14 @@ class SessionManager {
263
313
  bestMessages = scored[0].messages;
264
314
  }
265
315
  }
266
- if (0 === bestMessages.length) return [];
267
- return filterEmptyAssistantMessages(bestMessages);
316
+ if (0 === bestMessages.length) {
317
+ this.persistAssistantImageAttachments(sessionId, pendingMessages);
318
+ return pendingMessages;
319
+ }
320
+ const filteredMessages = filterEmptyAssistantMessages(bestMessages);
321
+ const mergedMessages = mergePendingMessages(filteredMessages, pendingMessages);
322
+ this.persistAssistantImageAttachments(sessionId, mergedMessages);
323
+ return mergedMessages;
268
324
  }
269
325
  rowToSession(row) {
270
326
  return {
@@ -329,6 +385,36 @@ class SessionManager {
329
385
  return null;
330
386
  }
331
387
  }
388
+ persistAssistantImageAttachments(sessionId, messages) {
389
+ try {
390
+ (0, external_imagePersistence_cjs_namespaceObject.persistAssistantImagesToDisk)({
391
+ dbPath: this.dbPath,
392
+ sessionId,
393
+ messages: messages
394
+ });
395
+ } catch {}
396
+ }
397
+ listPendingMessages(sessionId) {
398
+ if (!this.db) return [];
399
+ const stmt = this.db.prepare(`
400
+ SELECT id, role, content, attachments, created_at
401
+ FROM session_pending_messages
402
+ WHERE session_id = ?
403
+ ORDER BY created_at ASC, rowid ASC
404
+ `);
405
+ const rows = stmt.all(sessionId);
406
+ if (!rows.length) return [];
407
+ return rows.map((row)=>{
408
+ const attachments = parseSessionAttachments(row.attachments);
409
+ return {
410
+ id: row.id,
411
+ role: row.role,
412
+ content: row.content || "",
413
+ attachments,
414
+ createdAt: row.created_at || Date.now()
415
+ };
416
+ }).filter((message)=>"user" === message.role || message.content.trim().length > 0 || (message.attachments?.length || 0) > 0);
417
+ }
332
418
  constructor(dbPath){
333
419
  _define_property(this, "checkpointer", null);
334
420
  _define_property(this, "stateReaderAgent", null);
@@ -337,6 +423,50 @@ class SessionManager {
337
423
  this.dbPath = dbPath;
338
424
  }
339
425
  }
426
+ function mergePendingMessages(persisted, pending) {
427
+ if (0 === pending.length) return persisted;
428
+ if (0 === persisted.length) return pending;
429
+ const merged = [
430
+ ...persisted
431
+ ];
432
+ for (const candidate of pending){
433
+ const duplicate = merged.some((message)=>isLikelyDuplicateMessage(message, candidate));
434
+ if (!duplicate) merged.push(candidate);
435
+ }
436
+ merged.sort((a, b)=>{
437
+ if (a.createdAt === b.createdAt) {
438
+ if (a.role === b.role) return 0;
439
+ return "user" === a.role ? -1 : 1;
440
+ }
441
+ return a.createdAt - b.createdAt;
442
+ });
443
+ return merged;
444
+ }
445
+ function isLikelyDuplicateMessage(left, right) {
446
+ if (left.id && right.id && left.id === right.id) return true;
447
+ if (left.role !== right.role) return false;
448
+ if ((left.content || "").trim() !== (right.content || "").trim()) return false;
449
+ const leftAttachments = left.attachments || [];
450
+ const rightAttachments = right.attachments || [];
451
+ if (leftAttachments.length !== rightAttachments.length) return false;
452
+ for(let index = 0; index < leftAttachments.length; index += 1)if (!isAttachmentEquivalent(leftAttachments[index], rightAttachments[index])) return false;
453
+ return Math.abs((left.createdAt || 0) - (right.createdAt || 0)) < 30000;
454
+ }
455
+ function isAttachmentEquivalent(left, right) {
456
+ if (!left || !right) return left === right;
457
+ return left.kind === right.kind && (left.dataUrl || "") === (right.dataUrl || "") && (left.name || "") === (right.name || "") && (left.mimeType || "") === (right.mimeType || "") && (left.size || 0) === (right.size || 0);
458
+ }
459
+ function parseSessionAttachments(raw) {
460
+ if (!raw) return;
461
+ try {
462
+ const parsed = JSON.parse(raw);
463
+ if (!Array.isArray(parsed)) return;
464
+ const attachments = parsed.filter((item)=>item && "object" == typeof item && "string" == typeof item.kind && "string" == typeof item.dataUrl);
465
+ return attachments.length > 0 ? attachments : void 0;
466
+ } catch {
467
+ return;
468
+ }
469
+ }
340
470
  function isMessageLike(entry) {
341
471
  if (!entry || "object" != typeof entry) return false;
342
472
  return "string" == typeof entry.role || "string" == typeof entry.type || "string" == typeof entry?.kwargs?.role || "string" == typeof entry?.additional_kwargs?.role;
@@ -348,22 +478,30 @@ function toSessionMessage(entry, index, baseTime) {
348
478
  const role = resolveMessageRole(entry);
349
479
  if ("user" !== role && "assistant" !== role) {
350
480
  if (isToolMessage(entry)) {
351
- const toolContent = extractMessageContent(entry, extractContentBlocks(entry));
481
+ const blocks = extractContentBlocks(entry);
482
+ const toolContent = extractMessageContent(entry, blocks);
352
483
  const ui = extractUiFromPayload(toolContent);
353
- if (ui?.spec) return {
354
- id: `msg-${index}`,
355
- role: "assistant",
356
- content: "",
357
- createdAt: baseTime + index,
358
- uiBlocks: [
359
- {
360
- spec: ui.spec,
361
- uiOnly: ui.uiOnly,
362
- textFallback: ui.textFallback
363
- }
364
- ],
365
- uiTextFallback: ui.textFallback
366
- };
484
+ const attachments = extractAttachments(blocks);
485
+ if (ui?.spec || attachments.length > 0) {
486
+ const content = toolContent || ui?.textFallback || "";
487
+ return {
488
+ id: `msg-${index}`,
489
+ role: "assistant",
490
+ content,
491
+ attachments: attachments.length > 0 ? attachments : void 0,
492
+ createdAt: baseTime + index,
493
+ ...ui?.spec ? {
494
+ uiBlocks: [
495
+ {
496
+ spec: ui.spec,
497
+ uiOnly: ui.uiOnly,
498
+ textFallback: ui.textFallback
499
+ }
500
+ ],
501
+ uiTextFallback: ui.textFallback
502
+ } : {}
503
+ };
504
+ }
367
505
  }
368
506
  return null;
369
507
  }
@@ -402,11 +540,59 @@ function extractContentBlocks(entry) {
402
540
  entry?.content,
403
541
  entry?.kwargs?.content,
404
542
  entry?.additional_kwargs?.content,
405
- entry?.data?.content
543
+ entry?.data?.content,
544
+ entry?.artifact,
545
+ entry?.kwargs?.artifact,
546
+ entry?.additional_kwargs?.artifact,
547
+ entry?.data?.artifact
548
+ ];
549
+ for (const candidate of candidates){
550
+ const blocks = extractContentBlocksFromValue(candidate);
551
+ if (blocks.length > 0) return blocks;
552
+ }
553
+ return [];
554
+ }
555
+ function extractContentBlocksFromValue(value, depth = 0) {
556
+ if (depth > 5 || null == value) return [];
557
+ if (Array.isArray(value)) {
558
+ const unwrapped = [];
559
+ for (const item of value){
560
+ const parsedBlocks = extractBlocksFromTextLikeItem(item, depth + 1);
561
+ if (parsedBlocks.length > 0) {
562
+ unwrapped.push(...parsedBlocks);
563
+ continue;
564
+ }
565
+ unwrapped.push(item);
566
+ }
567
+ return unwrapped;
568
+ }
569
+ if ("string" == typeof value) {
570
+ const parsed = tryParseJsonPayload(value);
571
+ return parsed ? extractContentBlocksFromValue(parsed, depth + 1) : [];
572
+ }
573
+ if ("object" != typeof value) return [];
574
+ const record = value;
575
+ const candidates = [
576
+ record.content,
577
+ record?.kwargs?.content,
578
+ record?.additional_kwargs?.content,
579
+ record?.data?.content
406
580
  ];
407
- for (const candidate of candidates)if (Array.isArray(candidate)) return candidate;
581
+ for (const candidate of candidates){
582
+ const blocks = extractContentBlocksFromValue(candidate, depth + 1);
583
+ if (blocks.length > 0) return blocks;
584
+ }
408
585
  return [];
409
586
  }
587
+ function extractBlocksFromTextLikeItem(value, depth = 0) {
588
+ if (!value || "object" != typeof value || Array.isArray(value)) return [];
589
+ const record = value;
590
+ const text = "string" == typeof record.text ? record.text : "string" == typeof record.value && isTextLikeContentType(record.type) ? record.value : "string" == typeof record.output_text ? record.output_text : "string" == typeof record.input_text ? record.input_text : null;
591
+ if (!text) return [];
592
+ const parsed = tryParseJsonPayload(text);
593
+ if (!parsed) return [];
594
+ return extractContentBlocksFromValue(parsed, depth + 1);
595
+ }
410
596
  function extractMessageContent(entry, blocks = []) {
411
597
  if (!entry || "object" != typeof entry) return "";
412
598
  const candidates = [
@@ -424,18 +610,63 @@ function extractMessageContent(entry, blocks = []) {
424
610
  }
425
611
  function extractTextContent(value, depth = 0) {
426
612
  if (depth > 5 || null == value) return "";
427
- if ("string" == typeof value) return value;
613
+ if ("string" == typeof value) {
614
+ const parsed = tryParseJsonPayload(value);
615
+ if (null !== parsed) {
616
+ const extracted = extractTextContent(parsed, depth + 1).trim();
617
+ if (extracted) return extracted;
618
+ }
619
+ return value;
620
+ }
428
621
  if (Array.isArray(value)) return value.map((entry)=>extractTextContent(entry, depth + 1)).filter((entry)=>entry.length > 0).join("");
429
622
  if ("object" != typeof value) return "";
430
623
  const record = value;
431
- if ("string" == typeof record.text) return record.text;
624
+ if ("string" == typeof record.text) {
625
+ const parsed = tryParseJsonPayload(record.text);
626
+ if (null !== parsed) {
627
+ const extracted = extractTextContent(parsed, depth + 1).trim();
628
+ if (extracted) return extracted;
629
+ }
630
+ return record.text;
631
+ }
432
632
  if (record.text && "object" == typeof record.text && "string" == typeof record.text.value) return record.text.value;
433
- if ("string" == typeof record.output_text) return record.output_text;
434
- if ("string" == typeof record.input_text) return record.input_text;
435
- if ("string" == typeof record.value && isTextLikeContentType(record.type)) return record.value;
633
+ if ("string" == typeof record.output_text) {
634
+ const parsed = tryParseJsonPayload(record.output_text);
635
+ if (null !== parsed) {
636
+ const extracted = extractTextContent(parsed, depth + 1).trim();
637
+ if (extracted) return extracted;
638
+ }
639
+ return record.output_text;
640
+ }
641
+ if ("string" == typeof record.input_text) {
642
+ const parsed = tryParseJsonPayload(record.input_text);
643
+ if (null !== parsed) {
644
+ const extracted = extractTextContent(parsed, depth + 1).trim();
645
+ if (extracted) return extracted;
646
+ }
647
+ return record.input_text;
648
+ }
649
+ if ("string" == typeof record.value && isTextLikeContentType(record.type)) {
650
+ const parsed = tryParseJsonPayload(record.value);
651
+ if (null !== parsed) {
652
+ const extracted = extractTextContent(parsed, depth + 1).trim();
653
+ if (extracted) return extracted;
654
+ }
655
+ return record.value;
656
+ }
436
657
  if ("content" in record) return extractTextContent(record.content, depth + 1);
437
658
  return "";
438
659
  }
660
+ function tryParseJsonPayload(value) {
661
+ const trimmed = value.trim();
662
+ if (!trimmed) return null;
663
+ if (!(trimmed.startsWith("{") || trimmed.startsWith("["))) return null;
664
+ try {
665
+ return JSON.parse(trimmed);
666
+ } catch {
667
+ return null;
668
+ }
669
+ }
439
670
  function isTextLikeContentType(type) {
440
671
  if ("string" != typeof type) return false;
441
672
  const normalized = type.toLowerCase();
@@ -454,10 +685,12 @@ function filterUiOnlyAssistantMessages(messages) {
454
685
  let pendingFallback = null;
455
686
  for (const entry of messages){
456
687
  if (isToolMessage(entry)) {
457
- const content = extractMessageContent(entry, extractContentBlocks(entry));
688
+ const blocks = extractContentBlocks(entry);
689
+ const content = extractMessageContent(entry, blocks);
458
690
  const ui = extractUiFromPayload(content);
691
+ const attachments = extractAttachments(blocks);
459
692
  if (ui?.uiOnly && ui?.textFallback) pendingFallback = ui.textFallback.trim();
460
- if (ui?.spec) filtered.push(entry);
693
+ if (ui?.spec || attachments.length > 0) filtered.push(entry);
461
694
  continue;
462
695
  }
463
696
  const role = resolveMessageRole(entry);
@@ -537,6 +770,30 @@ function extractImageUrl(block) {
537
770
  const data = block.source.data;
538
771
  if (mediaType && data) return `data:${mediaType};base64,${data}`;
539
772
  }
773
+ if ("image" === block.type) {
774
+ const sourceType = block.source_type || block.sourceType;
775
+ const mimeType = block.mime_type || block.mimeType || block.media_type || block.mediaType || "image/png";
776
+ if ("base64" === sourceType && "string" == typeof block.data) return `data:${mimeType};base64,${block.data}`;
777
+ if ("url" === sourceType && "string" == typeof block.url) return block.url;
778
+ if ("string" == typeof block.data) return `data:${mimeType};base64,${block.data}`;
779
+ }
780
+ if ("output_image" === block.type) {
781
+ if ("string" == typeof block.image_url) return block.image_url;
782
+ if ("string" == typeof block.image_url?.url) return block.image_url.url;
783
+ if ("string" == typeof block.url) return block.url;
784
+ }
785
+ if ("resource_link" === block.type) {
786
+ const mimeType = "string" == typeof block.mimeType ? block.mimeType.trim().toLowerCase() : "";
787
+ const uri = "string" == typeof block.uri ? block.uri.trim() : "";
788
+ if (uri && (!mimeType || mimeType.startsWith("image/"))) return uri;
789
+ }
790
+ if ("resource" === block.type && block.resource) {
791
+ const resource = block.resource;
792
+ const mimeType = "string" == typeof resource.mimeType ? resource.mimeType.trim().toLowerCase() : "";
793
+ if (!mimeType || !mimeType.startsWith("image/")) return null;
794
+ if ("string" == typeof resource.blob && resource.blob.trim()) return `data:${mimeType};base64,${resource.blob.trim()}`;
795
+ if ("string" == typeof resource.uri && resource.uri.trim()) return resource.uri.trim();
796
+ }
540
797
  return null;
541
798
  }
542
799
  function extractAudioUrl(block) {
@@ -35,6 +35,7 @@ export interface SessionAttachment {
35
35
  name?: string;
36
36
  mimeType?: string;
37
37
  size?: number;
38
+ path?: string;
38
39
  }
39
40
  /**
40
41
  * SessionManager handles session metadata and provides unified access to
@@ -90,6 +91,12 @@ export declare class SessionManager {
90
91
  * Clear session messages while preserving the session record
91
92
  */
92
93
  clearSessionMessages(sessionId: string): void;
94
+ persistPendingMessage(input: {
95
+ sessionId: string;
96
+ requestId: string;
97
+ message: SessionMessage;
98
+ }): void;
99
+ clearPendingMessagesForRequest(sessionId: string, requestId: string): void;
93
100
  /**
94
101
  * Get the checkpointer for use with DeepAgents
95
102
  */
@@ -109,6 +116,8 @@ export declare class SessionManager {
109
116
  private loadRecentCheckpoints;
110
117
  private getStateReaderAgent;
111
118
  private loadMessagesFromState;
119
+ private persistAssistantImageAttachments;
120
+ private listPendingMessages;
112
121
  }
113
122
  export declare function extractMessagesFromState(state: any): SessionMessage[] | null;
114
123
  export declare function extractAttachments(blocks: any[]): SessionAttachment[];