@sumicom/quicksave 0.8.10 → 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 +14 -1
  6. package/dist/ai/cardBuilder.d.ts.map +1 -1
  7. package/dist/ai/cardBuilder.js +184 -6
  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 +53 -2
  17. package/dist/ai/codexAppServer/provider.d.ts.map +1 -1
  18. package/dist/ai/codexAppServer/provider.js +81 -7
  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 +40 -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,16 @@ 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;
62
+ /** Continue the local card id counter after persisted memory-mode history.
63
+ * Used on cold resume after daemon restart so new Codex/OpenCode cards
64
+ * append after existing cards instead of reusing `sessionId:1`, `:2`, ...
65
+ * and overwriting or reordering older history. */
66
+ seedSequenceFromCards(cards: readonly Card[]): void;
57
67
  /** Start a new turn: reset per-turn state, keep accumulated cards. */
58
68
  startNewTurn(): void;
59
69
  /** Clear all accumulated cards. Call after a turn completes and JSONL is flushed. */
@@ -68,6 +78,7 @@ export declare class StreamCardBuilder {
68
78
  * injects a user prompt into an already-running turn; the card must survive
69
79
  * refresh before the turn's normal end-of-turn persist runs. */
70
80
  persistCard(card: Card): Promise<void>;
81
+ flushCardHistoryWrites(): Promise<void>;
71
82
  private persistCardBatch;
72
83
  /** Snapshot current JSONL byte size so history reads stop before the active turn. */
73
84
  snapshotCutoff(): Promise<void>;
@@ -97,6 +108,7 @@ export declare class StreamCardBuilder {
97
108
  cancelDeferredClear(): void;
98
109
  get jsonlCutoff(): number | null;
99
110
  set jsonlCutoff(value: number | null);
111
+ private enqueueCardHistoryEntry;
100
112
  private nextId;
101
113
  private addEvent;
102
114
  private updateEvent;
@@ -167,6 +179,7 @@ export declare class StreamCardBuilder {
167
179
  stopReason?: unknown;
168
180
  }): CardEvent;
169
181
  generatedImage(prompt: string, status: GeneratedImageCard['status'], savedPath?: string): CardEvent;
182
+ artifact(ref: MarkdownArtifactRef, afterToolUseId?: string): CardEvent;
170
183
  recoverySuggested(reason: string, action: 'compact', label: string): CardEvent;
171
184
  errorMessage(text: string): CardEvent;
172
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;
@@ -60,25 +63,132 @@ function mergeCardsById(existing, incoming) {
60
63
  byId.set(card.id, card);
61
64
  for (const card of incoming)
62
65
  byId.set(card.id, card);
63
- return Array.from(byId.values());
66
+ return sortCardsChronologically(Array.from(byId.values()));
67
+ }
68
+ function sortCardsChronologically(cards) {
69
+ return cards
70
+ .map((card, index) => ({ card, index }))
71
+ .sort((a, b) => {
72
+ const at = Number.isFinite(a.card.timestamp) ? a.card.timestamp : 0;
73
+ const bt = Number.isFinite(b.card.timestamp) ? b.card.timestamp : 0;
74
+ return at - bt || a.index - b.index;
75
+ })
76
+ .map(({ card }) => card);
77
+ }
78
+ function maxCardSequenceForSession(cards, sessionId) {
79
+ const prefix = `${sessionId}:`;
80
+ let max = 0;
81
+ for (const card of cards) {
82
+ if (!card.id.startsWith(prefix))
83
+ continue;
84
+ const raw = card.id.slice(prefix.length);
85
+ if (!/^\d+$/.test(raw))
86
+ continue;
87
+ const seq = Number(raw);
88
+ if (Number.isSafeInteger(seq) && seq > max)
89
+ max = seq;
90
+ }
91
+ return max;
64
92
  }
65
93
  /**
66
94
  * Load persisted card history for a memory-mode session.
67
95
  * Returns cards in insertion order, or empty array if none exist.
68
96
  */
69
97
  export async function loadPersistedCards(sessionId) {
70
- const p = cardHistoryPath(sessionId);
98
+ const migratedCards = await migrateLegacyCardHistory(sessionId);
99
+ const p = cardHistoryLogPath(sessionId);
71
100
  if (!existsSync(p))
72
- return [];
101
+ return migratedCards ?? [];
73
102
  try {
74
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');
75
117
  const data = JSON.parse(raw);
76
- return Array.isArray(data) ? 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;
77
130
  }
78
131
  catch {
79
- return [];
132
+ return null;
80
133
  }
81
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
+ }
82
192
  // ── Direct JSONL file reading (replaces SDK getSessionMessages/listSubagents) ──
83
193
  export function encodeCwdPath(cwd) {
84
194
  return cwd.replace(/\//g, '-');
@@ -298,6 +408,8 @@ export class StreamCardBuilder {
298
408
  cards = new Map();
299
409
  /** tool_use_id → CardId, for pairing tool_result to ToolCallCard */
300
410
  toolUseIdToCardId = new Map();
411
+ /** artifactId → CardId, for de-duping generated display artifact cards. */
412
+ artifactIdToCardId = new Map();
301
413
  /** agentId (task_id) → CardId, for matching subagent updates */
302
414
  agentIdToCardId = new Map();
303
415
  /** Cards created for subagent permissions — removed after resolution. */
@@ -317,6 +429,8 @@ export class StreamCardBuilder {
317
429
  /** Token identifying an in-flight deferred clear. A later call (new turn, cancel)
318
430
  * replaces this token, causing the pending polling task to bail out. */
319
431
  _pendingClearToken = null;
432
+ persistMemoryCards = false;
433
+ cardHistoryWriteQueue = Promise.resolve();
320
434
  constructor(sessionId, cwd) {
321
435
  this.sessionId = sessionId;
322
436
  this.cwd = cwd;
@@ -324,6 +438,16 @@ export class StreamCardBuilder {
324
438
  updateSessionId(sessionId) {
325
439
  this.sessionId = sessionId;
326
440
  }
441
+ enableMemoryPersistence(enabled = true) {
442
+ this.persistMemoryCards = enabled;
443
+ }
444
+ /** Continue the local card id counter after persisted memory-mode history.
445
+ * Used on cold resume after daemon restart so new Codex/OpenCode cards
446
+ * append after existing cards instead of reusing `sessionId:1`, `:2`, ...
447
+ * and overwriting or reordering older history. */
448
+ seedSequenceFromCards(cards) {
449
+ this.seq = Math.max(this.seq, maxCardSequenceForSession(cards, this.sessionId));
450
+ }
327
451
  /** Start a new turn: reset per-turn state, keep accumulated cards. */
328
452
  startNewTurn() {
329
453
  this.currentTextCardId = null;
@@ -332,6 +456,7 @@ export class StreamCardBuilder {
332
456
  clearCards() {
333
457
  this.cards.clear();
334
458
  this.toolUseIdToCardId.clear();
459
+ this.artifactIdToCardId.clear();
335
460
  this.agentIdToCardId.clear();
336
461
  this.ephemeralCards.clear();
337
462
  this.agentToolUseStash.clear();
@@ -348,14 +473,34 @@ export class StreamCardBuilder {
348
473
  const cards = this.getCards();
349
474
  if (cards.length === 0)
350
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
+ }
351
483
  await this.persistCardBatch(cards);
352
484
  }
353
485
  /** Persist a single card immediately. Used when a memory-mode provider
354
486
  * injects a user prompt into an already-running turn; the card must survive
355
487
  * refresh before the turn's normal end-of-turn persist runs. */
356
488
  async persistCard(card) {
489
+ if (this.persistMemoryCards) {
490
+ this.enqueueCardHistoryEntry({ op: 'upsert', card: cleanPersistedCard(card) });
491
+ await this.flushCardHistoryWrites();
492
+ return;
493
+ }
357
494
  await this.persistCardBatch([card]);
358
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
+ }
359
504
  async persistCardBatch(cards) {
360
505
  if (cards.length === 0)
361
506
  return;
@@ -444,6 +589,7 @@ export class StreamCardBuilder {
444
589
  this._jsonlCutoff = lastSize >= 0 ? lastSize : null;
445
590
  this.cards.clear();
446
591
  this.toolUseIdToCardId.clear();
592
+ this.artifactIdToCardId.clear();
447
593
  this.agentIdToCardId.clear();
448
594
  this.ephemeralCards.clear();
449
595
  this.agentToolUseStash.clear();
@@ -462,11 +608,23 @@ export class StreamCardBuilder {
462
608
  set jsonlCutoff(value) {
463
609
  this._jsonlCutoff = value;
464
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
+ }
465
622
  nextId() {
466
623
  return `${this.sessionId}:${++this.seq}`;
467
624
  }
468
625
  addEvent(card, afterCardId) {
469
626
  this.cards.set(card.id, card);
627
+ this.enqueueCardHistoryEntry({ op: 'upsert', card: cleanPersistedCard(card) });
470
628
  return { type: 'add', sessionId: this.sessionId, card, afterCardId };
471
629
  }
472
630
  updateEvent(cardId, patch) {
@@ -483,6 +641,7 @@ export class StreamCardBuilder {
483
641
  else
484
642
  bag[key] = value;
485
643
  }
644
+ this.enqueueCardHistoryEntry({ op: 'patch', cardId, patch });
486
645
  }
487
646
  return { type: 'update', sessionId: this.sessionId, cardId, patch };
488
647
  }
@@ -490,11 +649,13 @@ export class StreamCardBuilder {
490
649
  const existing = this.cards.get(cardId);
491
650
  if (existing && 'text' in existing) {
492
651
  existing.text += text;
652
+ this.enqueueCardHistoryEntry({ op: 'append_text', cardId, text });
493
653
  }
494
654
  return { type: 'append_text', sessionId: this.sessionId, cardId, text };
495
655
  }
496
656
  removeEvent(cardId) {
497
657
  this.cards.delete(cardId);
658
+ this.enqueueCardHistoryEntry({ op: 'remove', cardId });
498
659
  return { type: 'remove', sessionId: this.sessionId, cardId };
499
660
  }
500
661
  // ── Public: produce CardEvents from SDK data ────────────────────────────
@@ -748,6 +909,23 @@ export class StreamCardBuilder {
748
909
  };
749
910
  return this.addEvent(card);
750
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
+ }
751
929
  recoverySuggested(reason, action, label) {
752
930
  const card = {
753
931
  type: 'recovery_suggested',