@sumicom/quicksave 0.8.11 → 0.8.12

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 (67) hide show
  1. package/dist/ai/artifactStore.d.ts +12 -0
  2. package/dist/ai/artifactStore.d.ts.map +1 -0
  3. package/dist/ai/artifactStore.js +143 -0
  4. package/dist/ai/artifactStore.js.map +1 -0
  5. package/dist/ai/cardBuilder.d.ts +9 -1
  6. package/dist/ai/cardBuilder.d.ts.map +1 -1
  7. package/dist/ai/cardBuilder.js +151 -5
  8. package/dist/ai/cardBuilder.js.map +1 -1
  9. package/dist/ai/claudeCliProvider.d.ts +2 -1
  10. package/dist/ai/claudeCliProvider.d.ts.map +1 -1
  11. package/dist/ai/claudeCliProvider.js +34 -0
  12. package/dist/ai/claudeCliProvider.js.map +1 -1
  13. package/dist/ai/codexAppServer/cardAdapter.d.ts.map +1 -1
  14. package/dist/ai/codexAppServer/cardAdapter.js +38 -1
  15. package/dist/ai/codexAppServer/cardAdapter.js.map +1 -1
  16. package/dist/ai/codexAppServer/provider.d.ts +49 -2
  17. package/dist/ai/codexAppServer/provider.d.ts.map +1 -1
  18. package/dist/ai/codexAppServer/provider.js +77 -3
  19. package/dist/ai/codexAppServer/provider.js.map +1 -1
  20. package/dist/ai/codexAppServer/tokenAccounting.d.ts.map +1 -1
  21. package/dist/ai/codexAppServer/tokenAccounting.js +2 -0
  22. package/dist/ai/codexAppServer/tokenAccounting.js.map +1 -1
  23. package/dist/ai/codexMcpProvider.d.ts.map +1 -1
  24. package/dist/ai/codexMcpProvider.js +59 -3
  25. package/dist/ai/codexMcpProvider.js.map +1 -1
  26. package/dist/ai/codexQuota.d.ts +29 -0
  27. package/dist/ai/codexQuota.d.ts.map +1 -0
  28. package/dist/ai/codexQuota.js +196 -0
  29. package/dist/ai/codexQuota.js.map +1 -0
  30. package/dist/ai/enrichEntry.d.ts.map +1 -1
  31. package/dist/ai/enrichEntry.js +2 -1
  32. package/dist/ai/enrichEntry.js.map +1 -1
  33. package/dist/ai/provider.d.ts +10 -2
  34. package/dist/ai/provider.d.ts.map +1 -1
  35. package/dist/ai/provider.js.map +1 -1
  36. package/dist/ai/sandboxMcp.d.ts +1 -0
  37. package/dist/ai/sandboxMcp.d.ts.map +1 -1
  38. package/dist/ai/sandboxMcp.js +1 -0
  39. package/dist/ai/sandboxMcp.js.map +1 -1
  40. package/dist/ai/sandboxMcpStdio.js +62 -0
  41. package/dist/ai/sandboxMcpStdio.js.map +1 -1
  42. package/dist/ai/sessionManager.d.ts +7 -2
  43. package/dist/ai/sessionManager.d.ts.map +1 -1
  44. package/dist/ai/sessionManager.js +37 -9
  45. package/dist/ai/sessionManager.js.map +1 -1
  46. package/dist/handlers/legacyBusAdapter.d.ts.map +1 -1
  47. package/dist/handlers/legacyBusAdapter.js +3 -0
  48. package/dist/handlers/legacyBusAdapter.js.map +1 -1
  49. package/dist/handlers/messageHandler.d.ts +10 -2
  50. package/dist/handlers/messageHandler.d.ts.map +1 -1
  51. package/dist/handlers/messageHandler.js +48 -3
  52. package/dist/handlers/messageHandler.js.map +1 -1
  53. package/dist/index.js +2 -2
  54. package/dist/service/contextUsage.d.ts +9 -0
  55. package/dist/service/contextUsage.d.ts.map +1 -0
  56. package/dist/service/contextUsage.js +36 -0
  57. package/dist/service/contextUsage.js.map +1 -0
  58. package/dist/service/ipcClient.js +1 -1
  59. package/dist/service/run.d.ts.map +1 -1
  60. package/dist/service/run.js +26 -3
  61. package/dist/service/run.js.map +1 -1
  62. package/dist/service/singleton.d.ts +1 -0
  63. package/dist/service/singleton.d.ts.map +1 -1
  64. package/dist/service/singleton.js +3 -0
  65. package/dist/service/singleton.js.map +1 -1
  66. package/dist/service/types.js +1 -1
  67. package/package.json +3 -3
@@ -0,0 +1,12 @@
1
+ import type { Artifact, ArtifactMetadata, MarkdownArtifactRef } from '@sumicom/quicksave-shared';
2
+ export interface PublishMarkdownArtifactArgs {
3
+ sessionId: string;
4
+ cwd: string;
5
+ sourcePath: string;
6
+ title?: string;
7
+ }
8
+ export declare function publishMarkdownArtifact(args: PublishMarkdownArtifactArgs): Promise<MarkdownArtifactRef>;
9
+ export declare function loadArtifactBySession(sessionId: string, artifactId: string): Promise<Artifact | null>;
10
+ export declare function listSessionArtifacts(cwd: string, sessionId: string): Promise<ArtifactMetadata[]>;
11
+ export declare function removeSessionArtifacts(cwd: string, sessionId: string): Promise<void>;
12
+ //# sourceMappingURL=artifactStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifactStore.d.ts","sourceRoot":"","sources":["../../src/ai/artifactStore.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAuCjG,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuC7G;AAED,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4B3G;AAED,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiBtG;AAED,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM1F"}
@@ -0,0 +1,143 @@
1
+ // SPDX-FileCopyrightText: 2026 King Young Technology
2
+ // SPDX-License-Identifier: MIT
3
+ // ============================================================================
4
+ // Persistent artifact store on the agent.
5
+ //
6
+ // Generated reports live under:
7
+ //
8
+ // <state>/artifacts/<encoded-project-path>/<sessionId>/<artifactId>/
9
+ // artifact.md
10
+ // meta.json
11
+ //
12
+ // Tool results and cards carry only ArtifactMetadata. The PWA fetches content
13
+ // on demand through `artifact:fetch`, keeping large reports out of model
14
+ // context and out of card snapshots.
15
+ // ============================================================================
16
+ import { copyFile, mkdir, readFile, readdir, realpath, rm, stat, writeFile } from 'fs/promises';
17
+ import { basename, extname, isAbsolute, join, resolve, sep } from 'path';
18
+ import { randomUUID } from 'crypto';
19
+ import { getArtifactsDir } from '../service/singleton.js';
20
+ const ARTIFACT_FILENAME = 'artifact.md';
21
+ const META_FILENAME = 'meta.json';
22
+ const MARKDOWN_ARTIFACT_MAX_BYTES = 10 * 1024 * 1024;
23
+ const MARKDOWN_ARTIFACT_EXTENSIONS = ['.md', '.markdown'];
24
+ function encodeProjectPath(cwd) {
25
+ return cwd.replace(/[\\/]/g, '-').replace(/:/g, '');
26
+ }
27
+ function dirFor(cwd, sessionId) {
28
+ return join(getArtifactsDir(), encodeProjectPath(cwd), sessionId);
29
+ }
30
+ function artifactDirFor(cwd, sessionId, artifactId) {
31
+ return join(dirFor(cwd, sessionId), artifactId);
32
+ }
33
+ function storedMarkdownPath(cwd, sessionId, artifactId) {
34
+ return join(artifactDirFor(cwd, sessionId, artifactId), ARTIFACT_FILENAME);
35
+ }
36
+ function metaPathFor(cwd, sessionId, artifactId) {
37
+ return join(artifactDirFor(cwd, sessionId, artifactId), META_FILENAME);
38
+ }
39
+ function isPathInside(child, parent) {
40
+ return child === parent || child.startsWith(parent.endsWith(sep) ? parent : parent + sep);
41
+ }
42
+ function assertMarkdownPath(filePath) {
43
+ const ext = extname(filePath).toLowerCase();
44
+ if (!MARKDOWN_ARTIFACT_EXTENSIONS.includes(ext)) {
45
+ throw new Error('Only .md and .markdown artifacts are supported');
46
+ }
47
+ }
48
+ export async function publishMarkdownArtifact(args) {
49
+ const cwdReal = await realpath(args.cwd);
50
+ const requestedSource = isAbsolute(args.sourcePath)
51
+ ? args.sourcePath
52
+ : resolve(cwdReal, args.sourcePath);
53
+ assertMarkdownPath(requestedSource);
54
+ const sourceReal = await realpath(requestedSource);
55
+ if (!isPathInside(sourceReal, cwdReal)) {
56
+ throw new Error('Artifact source must be inside the project directory');
57
+ }
58
+ const st = await stat(sourceReal);
59
+ if (!st.isFile()) {
60
+ throw new Error('Artifact source must be a regular file');
61
+ }
62
+ if (st.size > MARKDOWN_ARTIFACT_MAX_BYTES) {
63
+ throw new Error(`Markdown artifact exceeds ${MARKDOWN_ARTIFACT_MAX_BYTES} byte limit`);
64
+ }
65
+ const artifactId = randomUUID();
66
+ const artifactDir = artifactDirFor(cwdReal, args.sessionId, artifactId);
67
+ await mkdir(artifactDir, { recursive: true });
68
+ await copyFile(sourceReal, storedMarkdownPath(cwdReal, args.sessionId, artifactId));
69
+ const meta = {
70
+ refKind: 'artifact',
71
+ artifactId,
72
+ sessionId: args.sessionId,
73
+ cwd: cwdReal,
74
+ kind: 'markdown',
75
+ title: args.title?.trim() || basename(sourceReal),
76
+ mimeType: 'text/markdown',
77
+ size: st.size,
78
+ createdAt: Date.now(),
79
+ sourcePath: sourceReal,
80
+ };
81
+ await writeFile(metaPathFor(cwdReal, args.sessionId, artifactId), JSON.stringify(meta, null, 2));
82
+ return meta;
83
+ }
84
+ export async function loadArtifactBySession(sessionId, artifactId) {
85
+ const base = getArtifactsDir();
86
+ let projectDirs;
87
+ try {
88
+ projectDirs = await readdir(base);
89
+ }
90
+ catch {
91
+ return null;
92
+ }
93
+ for (const projectDir of projectDirs) {
94
+ const artifactDir = join(base, projectDir, sessionId, artifactId);
95
+ try {
96
+ const [metaRaw, bytes] = await Promise.all([
97
+ readFile(join(artifactDir, META_FILENAME), 'utf8'),
98
+ readFile(join(artifactDir, ARTIFACT_FILENAME)),
99
+ ]);
100
+ const meta = JSON.parse(metaRaw);
101
+ if (meta.sessionId !== sessionId || meta.artifactId !== artifactId)
102
+ continue;
103
+ return {
104
+ ...meta,
105
+ contentBase64: bytes.toString('base64'),
106
+ };
107
+ }
108
+ catch {
109
+ // Keep scanning: sessions can share ids across different project dirs in
110
+ // malformed or hand-edited state, and missing files are local-only.
111
+ }
112
+ }
113
+ return null;
114
+ }
115
+ export async function listSessionArtifacts(cwd, sessionId) {
116
+ let artifactIds;
117
+ try {
118
+ artifactIds = await readdir(dirFor(cwd, sessionId));
119
+ }
120
+ catch {
121
+ return [];
122
+ }
123
+ const metas = [];
124
+ for (const artifactId of artifactIds) {
125
+ try {
126
+ const raw = await readFile(metaPathFor(cwd, sessionId, artifactId), 'utf8');
127
+ metas.push(JSON.parse(raw));
128
+ }
129
+ catch {
130
+ // skip unreadable metas
131
+ }
132
+ }
133
+ return metas.sort((a, b) => a.createdAt - b.createdAt);
134
+ }
135
+ export async function removeSessionArtifacts(cwd, sessionId) {
136
+ try {
137
+ await rm(dirFor(cwd, sessionId), { recursive: true, force: true });
138
+ }
139
+ catch {
140
+ // best effort
141
+ }
142
+ }
143
+ //# sourceMappingURL=artifactStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifactStore.js","sourceRoot":"","sources":["../../src/ai/artifactStore.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Attachment, Card, CardId, CardEvent, CardHistoryResponse, PendingInputAttachment, GeneratedImageCard, SystemCard } from '@sumicom/quicksave-shared';
1
+ import type { Attachment, Card, CardId, CardEvent, CardHistoryResponse, PendingInputAttachment, GeneratedImageCard, SystemCard, MarkdownArtifactRef } from '@sumicom/quicksave-shared';
2
2
  /**
3
3
  * Load persisted card history for a memory-mode session.
4
4
  * Returns cards in insertion order, or empty array if none exist.
@@ -33,6 +33,8 @@ export declare class StreamCardBuilder {
33
33
  private cards;
34
34
  /** tool_use_id → CardId, for pairing tool_result to ToolCallCard */
35
35
  private toolUseIdToCardId;
36
+ /** artifactId → CardId, for de-duping generated display artifact cards. */
37
+ private artifactIdToCardId;
36
38
  /** agentId (task_id) → CardId, for matching subagent updates */
37
39
  private agentIdToCardId;
38
40
  /** Cards created for subagent permissions — removed after resolution. */
@@ -52,8 +54,11 @@ export declare class StreamCardBuilder {
52
54
  /** Token identifying an in-flight deferred clear. A later call (new turn, cancel)
53
55
  * replaces this token, causing the pending polling task to bail out. */
54
56
  private _pendingClearToken;
57
+ private persistMemoryCards;
58
+ private cardHistoryWriteQueue;
55
59
  constructor(sessionId: string, cwd: string);
56
60
  updateSessionId(sessionId: string): void;
61
+ enableMemoryPersistence(enabled?: boolean): void;
57
62
  /** Continue the local card id counter after persisted memory-mode history.
58
63
  * Used on cold resume after daemon restart so new Codex/OpenCode cards
59
64
  * append after existing cards instead of reusing `sessionId:1`, `:2`, ...
@@ -73,6 +78,7 @@ export declare class StreamCardBuilder {
73
78
  * injects a user prompt into an already-running turn; the card must survive
74
79
  * refresh before the turn's normal end-of-turn persist runs. */
75
80
  persistCard(card: Card): Promise<void>;
81
+ flushCardHistoryWrites(): Promise<void>;
76
82
  private persistCardBatch;
77
83
  /** Snapshot current JSONL byte size so history reads stop before the active turn. */
78
84
  snapshotCutoff(): Promise<void>;
@@ -102,6 +108,7 @@ export declare class StreamCardBuilder {
102
108
  cancelDeferredClear(): void;
103
109
  get jsonlCutoff(): number | null;
104
110
  set jsonlCutoff(value: number | null);
111
+ private enqueueCardHistoryEntry;
105
112
  private nextId;
106
113
  private addEvent;
107
114
  private updateEvent;
@@ -172,6 +179,7 @@ export declare class StreamCardBuilder {
172
179
  stopReason?: unknown;
173
180
  }): CardEvent;
174
181
  generatedImage(prompt: string, status: GeneratedImageCard['status'], savedPath?: string): CardEvent;
182
+ artifact(ref: MarkdownArtifactRef, afterToolUseId?: string): CardEvent;
175
183
  recoverySuggested(reason: string, action: 'compact', label: string): CardEvent;
176
184
  errorMessage(text: string): CardEvent;
177
185
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cardBuilder.d.ts","sourceRoot":"","sources":["../../src/ai/cardBuilder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cardBuilder.d.ts","sourceRoot":"","sources":["../../src/ai/cardBuilder.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { readFile, readdir, writeFile, mkdir, stat, open } from 'fs/promises';
1
+ import { appendFile, readFile, readdir, writeFile, mkdir, stat, open, rename } from 'fs/promises';
2
2
  import { join } from 'path';
3
3
  import { createReadStream, existsSync } from 'fs';
4
4
  import { createInterface } from 'readline';
@@ -46,6 +46,9 @@ const TOOL_RESULT_TRUNCATE_LENGTH = 500;
46
46
  function cardHistoryPath(sessionId) {
47
47
  return join(getCardHistoryDir(), `${sessionId}.json`);
48
48
  }
49
+ function cardHistoryLogPath(sessionId) {
50
+ return join(getCardHistoryDir(), `${sessionId}.jsonl`);
51
+ }
49
52
  function cleanPersistedCard(card) {
50
53
  const { pendingInput, ...rest } = card;
51
54
  void pendingInput;
@@ -92,18 +95,100 @@ function maxCardSequenceForSession(cards, sessionId) {
92
95
  * Returns cards in insertion order, or empty array if none exist.
93
96
  */
94
97
  export async function loadPersistedCards(sessionId) {
95
- const p = cardHistoryPath(sessionId);
98
+ const migratedCards = await migrateLegacyCardHistory(sessionId);
99
+ const p = cardHistoryLogPath(sessionId);
96
100
  if (!existsSync(p))
97
- return [];
101
+ return migratedCards ?? [];
98
102
  try {
99
103
  const raw = await readFile(p, 'utf-8');
104
+ const replayed = replayCardHistoryLog(raw);
105
+ return migratedCards ? mergeCardsById(migratedCards, replayed) : replayed;
106
+ }
107
+ catch {
108
+ return migratedCards ?? [];
109
+ }
110
+ }
111
+ async function migrateLegacyCardHistory(sessionId) {
112
+ const legacyPath = cardHistoryPath(sessionId);
113
+ if (!existsSync(legacyPath))
114
+ return null;
115
+ try {
116
+ const raw = await readFile(legacyPath, 'utf-8');
100
117
  const data = JSON.parse(raw);
101
- return Array.isArray(data) ? sortCardsChronologically(data) : [];
118
+ if (!Array.isArray(data) || data.length === 0)
119
+ return null;
120
+ const cards = sortCardsChronologically(data).map(cleanPersistedCard);
121
+ try {
122
+ await mkdir(getCardHistoryDir(), { recursive: true });
123
+ await appendFile(cardHistoryLogPath(sessionId), JSON.stringify({ op: 'seed', cards }) + '\n');
124
+ await rename(legacyPath, `${legacyPath}.migrated-${Date.now()}`);
125
+ }
126
+ catch {
127
+ // Keep the legacy file as fallback; the next read can retry migration.
128
+ }
129
+ return cards;
102
130
  }
103
131
  catch {
104
- return [];
132
+ return null;
105
133
  }
106
134
  }
135
+ function replayCardHistoryLog(raw) {
136
+ const cardsById = new Map();
137
+ for (const line of raw.split('\n')) {
138
+ if (!line.trim())
139
+ continue;
140
+ let entry;
141
+ try {
142
+ entry = JSON.parse(line);
143
+ }
144
+ catch {
145
+ continue;
146
+ }
147
+ switch (entry.op) {
148
+ case 'seed':
149
+ for (const card of entry.cards ?? []) {
150
+ if (!card?.id || cardsById.has(card.id))
151
+ continue;
152
+ cardsById.set(card.id, cleanPersistedCard(card));
153
+ }
154
+ break;
155
+ case 'upsert':
156
+ if (entry.card?.id)
157
+ cardsById.set(entry.card.id, cleanPersistedCard(entry.card));
158
+ break;
159
+ case 'patch': {
160
+ const existing = cardsById.get(entry.cardId);
161
+ if (!existing)
162
+ break;
163
+ const bag = existing;
164
+ for (const [key, value] of Object.entries(entry.patch ?? {})) {
165
+ if (value === null)
166
+ delete bag[key];
167
+ else
168
+ bag[key] = value;
169
+ }
170
+ cardsById.set(entry.cardId, cleanPersistedCard(existing));
171
+ break;
172
+ }
173
+ case 'append_text': {
174
+ const existing = cardsById.get(entry.cardId);
175
+ if (existing && 'text' in existing) {
176
+ existing.text += entry.text;
177
+ cardsById.set(entry.cardId, cleanPersistedCard(existing));
178
+ }
179
+ break;
180
+ }
181
+ case 'remove':
182
+ cardsById.delete(entry.cardId);
183
+ break;
184
+ }
185
+ }
186
+ return sortCardsChronologically(Array.from(cardsById.values()));
187
+ }
188
+ async function appendCardHistoryEntry(sessionId, entry) {
189
+ await mkdir(getCardHistoryDir(), { recursive: true });
190
+ await appendFile(cardHistoryLogPath(sessionId), JSON.stringify(entry) + '\n');
191
+ }
107
192
  // ── Direct JSONL file reading (replaces SDK getSessionMessages/listSubagents) ──
108
193
  export function encodeCwdPath(cwd) {
109
194
  return cwd.replace(/\//g, '-');
@@ -323,6 +408,8 @@ export class StreamCardBuilder {
323
408
  cards = new Map();
324
409
  /** tool_use_id → CardId, for pairing tool_result to ToolCallCard */
325
410
  toolUseIdToCardId = new Map();
411
+ /** artifactId → CardId, for de-duping generated display artifact cards. */
412
+ artifactIdToCardId = new Map();
326
413
  /** agentId (task_id) → CardId, for matching subagent updates */
327
414
  agentIdToCardId = new Map();
328
415
  /** Cards created for subagent permissions — removed after resolution. */
@@ -342,6 +429,8 @@ export class StreamCardBuilder {
342
429
  /** Token identifying an in-flight deferred clear. A later call (new turn, cancel)
343
430
  * replaces this token, causing the pending polling task to bail out. */
344
431
  _pendingClearToken = null;
432
+ persistMemoryCards = false;
433
+ cardHistoryWriteQueue = Promise.resolve();
345
434
  constructor(sessionId, cwd) {
346
435
  this.sessionId = sessionId;
347
436
  this.cwd = cwd;
@@ -349,6 +438,9 @@ export class StreamCardBuilder {
349
438
  updateSessionId(sessionId) {
350
439
  this.sessionId = sessionId;
351
440
  }
441
+ enableMemoryPersistence(enabled = true) {
442
+ this.persistMemoryCards = enabled;
443
+ }
352
444
  /** Continue the local card id counter after persisted memory-mode history.
353
445
  * Used on cold resume after daemon restart so new Codex/OpenCode cards
354
446
  * append after existing cards instead of reusing `sessionId:1`, `:2`, ...
@@ -364,6 +456,7 @@ export class StreamCardBuilder {
364
456
  clearCards() {
365
457
  this.cards.clear();
366
458
  this.toolUseIdToCardId.clear();
459
+ this.artifactIdToCardId.clear();
367
460
  this.agentIdToCardId.clear();
368
461
  this.ephemeralCards.clear();
369
462
  this.agentToolUseStash.clear();
@@ -380,14 +473,34 @@ export class StreamCardBuilder {
380
473
  const cards = this.getCards();
381
474
  if (cards.length === 0)
382
475
  return;
476
+ if (this.persistMemoryCards) {
477
+ for (const card of cards) {
478
+ this.enqueueCardHistoryEntry({ op: 'upsert', card: cleanPersistedCard(card) });
479
+ }
480
+ await this.flushCardHistoryWrites();
481
+ return;
482
+ }
383
483
  await this.persistCardBatch(cards);
384
484
  }
385
485
  /** Persist a single card immediately. Used when a memory-mode provider
386
486
  * injects a user prompt into an already-running turn; the card must survive
387
487
  * refresh before the turn's normal end-of-turn persist runs. */
388
488
  async persistCard(card) {
489
+ if (this.persistMemoryCards) {
490
+ this.enqueueCardHistoryEntry({ op: 'upsert', card: cleanPersistedCard(card) });
491
+ await this.flushCardHistoryWrites();
492
+ return;
493
+ }
389
494
  await this.persistCardBatch([card]);
390
495
  }
496
+ async flushCardHistoryWrites() {
497
+ try {
498
+ await this.cardHistoryWriteQueue;
499
+ }
500
+ catch {
501
+ // Individual write failures are intentionally swallowed in the queue.
502
+ }
503
+ }
391
504
  async persistCardBatch(cards) {
392
505
  if (cards.length === 0)
393
506
  return;
@@ -476,6 +589,7 @@ export class StreamCardBuilder {
476
589
  this._jsonlCutoff = lastSize >= 0 ? lastSize : null;
477
590
  this.cards.clear();
478
591
  this.toolUseIdToCardId.clear();
592
+ this.artifactIdToCardId.clear();
479
593
  this.agentIdToCardId.clear();
480
594
  this.ephemeralCards.clear();
481
595
  this.agentToolUseStash.clear();
@@ -494,11 +608,23 @@ export class StreamCardBuilder {
494
608
  set jsonlCutoff(value) {
495
609
  this._jsonlCutoff = value;
496
610
  }
611
+ enqueueCardHistoryEntry(entry) {
612
+ if (!this.persistMemoryCards || this.sessionId === 'pending')
613
+ return;
614
+ const sessionId = this.sessionId;
615
+ this.cardHistoryWriteQueue = this.cardHistoryWriteQueue
616
+ .catch(() => undefined)
617
+ .then(() => appendCardHistoryEntry(sessionId, entry))
618
+ .catch((err) => {
619
+ console.warn(`[card-history] append failed for session=${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
620
+ });
621
+ }
497
622
  nextId() {
498
623
  return `${this.sessionId}:${++this.seq}`;
499
624
  }
500
625
  addEvent(card, afterCardId) {
501
626
  this.cards.set(card.id, card);
627
+ this.enqueueCardHistoryEntry({ op: 'upsert', card: cleanPersistedCard(card) });
502
628
  return { type: 'add', sessionId: this.sessionId, card, afterCardId };
503
629
  }
504
630
  updateEvent(cardId, patch) {
@@ -515,6 +641,7 @@ export class StreamCardBuilder {
515
641
  else
516
642
  bag[key] = value;
517
643
  }
644
+ this.enqueueCardHistoryEntry({ op: 'patch', cardId, patch });
518
645
  }
519
646
  return { type: 'update', sessionId: this.sessionId, cardId, patch };
520
647
  }
@@ -522,11 +649,13 @@ export class StreamCardBuilder {
522
649
  const existing = this.cards.get(cardId);
523
650
  if (existing && 'text' in existing) {
524
651
  existing.text += text;
652
+ this.enqueueCardHistoryEntry({ op: 'append_text', cardId, text });
525
653
  }
526
654
  return { type: 'append_text', sessionId: this.sessionId, cardId, text };
527
655
  }
528
656
  removeEvent(cardId) {
529
657
  this.cards.delete(cardId);
658
+ this.enqueueCardHistoryEntry({ op: 'remove', cardId });
530
659
  return { type: 'remove', sessionId: this.sessionId, cardId };
531
660
  }
532
661
  // ── Public: produce CardEvents from SDK data ────────────────────────────
@@ -780,6 +909,23 @@ export class StreamCardBuilder {
780
909
  };
781
910
  return this.addEvent(card);
782
911
  }
912
+ artifact(ref, afterToolUseId) {
913
+ this.currentTextCardId = null;
914
+ const existingCardId = this.artifactIdToCardId.get(ref.artifactId);
915
+ if (existingCardId) {
916
+ return this.updateEvent(existingCardId, { artifact: ref });
917
+ }
918
+ const id = `${this.sessionId}:artifact:${ref.artifactId}`;
919
+ this.artifactIdToCardId.set(ref.artifactId, id);
920
+ const card = {
921
+ type: 'artifact',
922
+ id,
923
+ timestamp: Date.now(),
924
+ artifact: ref,
925
+ };
926
+ const afterCardId = afterToolUseId ? this.toolUseIdToCardId.get(afterToolUseId) : undefined;
927
+ return this.addEvent(card, afterCardId);
928
+ }
783
929
  recoverySuggested(reason, action, label) {
784
930
  const card = {
785
931
  type: 'recovery_suggested',