@witqq/agent-sdk 0.7.0 → 0.9.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 (154) hide show
  1. package/dist/{types-CqvUAYxt.d.ts → agent-C6H2CgJA.d.cts} +139 -102
  2. package/dist/{types-CqvUAYxt.d.cts → agent-F7oB6eKp.d.ts} +139 -102
  3. package/dist/auth/index.cjs +72 -1
  4. package/dist/auth/index.cjs.map +1 -1
  5. package/dist/auth/index.d.cts +21 -154
  6. package/dist/auth/index.d.ts +21 -154
  7. package/dist/auth/index.js +72 -1
  8. package/dist/auth/index.js.map +1 -1
  9. package/dist/backends/claude.cjs +480 -261
  10. package/dist/backends/claude.cjs.map +1 -1
  11. package/dist/backends/claude.d.cts +3 -1
  12. package/dist/backends/claude.d.ts +3 -1
  13. package/dist/backends/claude.js +480 -261
  14. package/dist/backends/claude.js.map +1 -1
  15. package/dist/backends/copilot.cjs +337 -112
  16. package/dist/backends/copilot.cjs.map +1 -1
  17. package/dist/backends/copilot.d.cts +12 -4
  18. package/dist/backends/copilot.d.ts +12 -4
  19. package/dist/backends/copilot.js +337 -112
  20. package/dist/backends/copilot.js.map +1 -1
  21. package/dist/backends/mock-llm.cjs +719 -0
  22. package/dist/backends/mock-llm.cjs.map +1 -0
  23. package/dist/backends/mock-llm.d.cts +37 -0
  24. package/dist/backends/mock-llm.d.ts +37 -0
  25. package/dist/backends/mock-llm.js +717 -0
  26. package/dist/backends/mock-llm.js.map +1 -0
  27. package/dist/backends/vercel-ai.cjs +301 -61
  28. package/dist/backends/vercel-ai.cjs.map +1 -1
  29. package/dist/backends/vercel-ai.d.cts +3 -1
  30. package/dist/backends/vercel-ai.d.ts +3 -1
  31. package/dist/backends/vercel-ai.js +301 -61
  32. package/dist/backends/vercel-ai.js.map +1 -1
  33. package/dist/backends-Cno0gZjy.d.cts +114 -0
  34. package/dist/backends-Cno0gZjy.d.ts +114 -0
  35. package/dist/chat/accumulator.cjs +1 -1
  36. package/dist/chat/accumulator.cjs.map +1 -1
  37. package/dist/chat/accumulator.d.cts +5 -2
  38. package/dist/chat/accumulator.d.ts +5 -2
  39. package/dist/chat/accumulator.js +1 -1
  40. package/dist/chat/accumulator.js.map +1 -1
  41. package/dist/chat/backends.cjs +1084 -821
  42. package/dist/chat/backends.cjs.map +1 -1
  43. package/dist/chat/backends.d.cts +10 -6
  44. package/dist/chat/backends.d.ts +10 -6
  45. package/dist/chat/backends.js +1082 -800
  46. package/dist/chat/backends.js.map +1 -1
  47. package/dist/chat/context.cjs +50 -0
  48. package/dist/chat/context.cjs.map +1 -1
  49. package/dist/chat/context.d.cts +27 -3
  50. package/dist/chat/context.d.ts +27 -3
  51. package/dist/chat/context.js +50 -0
  52. package/dist/chat/context.js.map +1 -1
  53. package/dist/chat/core.cjs +60 -27
  54. package/dist/chat/core.cjs.map +1 -1
  55. package/dist/chat/core.d.cts +41 -382
  56. package/dist/chat/core.d.ts +41 -382
  57. package/dist/chat/core.js +58 -28
  58. package/dist/chat/core.js.map +1 -1
  59. package/dist/chat/errors.cjs +48 -26
  60. package/dist/chat/errors.cjs.map +1 -1
  61. package/dist/chat/errors.d.cts +6 -31
  62. package/dist/chat/errors.d.ts +6 -31
  63. package/dist/chat/errors.js +48 -25
  64. package/dist/chat/errors.js.map +1 -1
  65. package/dist/chat/events.cjs.map +1 -1
  66. package/dist/chat/events.d.cts +6 -2
  67. package/dist/chat/events.d.ts +6 -2
  68. package/dist/chat/events.js.map +1 -1
  69. package/dist/chat/index.cjs +1612 -1125
  70. package/dist/chat/index.cjs.map +1 -1
  71. package/dist/chat/index.d.cts +35 -10
  72. package/dist/chat/index.d.ts +35 -10
  73. package/dist/chat/index.js +1600 -1097
  74. package/dist/chat/index.js.map +1 -1
  75. package/dist/chat/react/theme.css +2517 -0
  76. package/dist/chat/react.cjs +2212 -1158
  77. package/dist/chat/react.cjs.map +1 -1
  78. package/dist/chat/react.d.cts +665 -122
  79. package/dist/chat/react.d.ts +665 -122
  80. package/dist/chat/react.js +2191 -1156
  81. package/dist/chat/react.js.map +1 -1
  82. package/dist/chat/runtime.cjs +405 -186
  83. package/dist/chat/runtime.cjs.map +1 -1
  84. package/dist/chat/runtime.d.cts +92 -28
  85. package/dist/chat/runtime.d.ts +92 -28
  86. package/dist/chat/runtime.js +405 -186
  87. package/dist/chat/runtime.js.map +1 -1
  88. package/dist/chat/server.cjs +2247 -212
  89. package/dist/chat/server.cjs.map +1 -1
  90. package/dist/chat/server.d.cts +451 -90
  91. package/dist/chat/server.d.ts +451 -90
  92. package/dist/chat/server.js +2234 -213
  93. package/dist/chat/server.js.map +1 -1
  94. package/dist/chat/sessions.cjs +64 -66
  95. package/dist/chat/sessions.cjs.map +1 -1
  96. package/dist/chat/sessions.d.cts +37 -118
  97. package/dist/chat/sessions.d.ts +37 -118
  98. package/dist/chat/sessions.js +65 -67
  99. package/dist/chat/sessions.js.map +1 -1
  100. package/dist/chat/sqlite.cjs +536 -0
  101. package/dist/chat/sqlite.cjs.map +1 -0
  102. package/dist/chat/sqlite.d.cts +164 -0
  103. package/dist/chat/sqlite.d.ts +164 -0
  104. package/dist/chat/sqlite.js +527 -0
  105. package/dist/chat/sqlite.js.map +1 -0
  106. package/dist/chat/state.cjs +14 -1
  107. package/dist/chat/state.cjs.map +1 -1
  108. package/dist/chat/state.d.cts +5 -2
  109. package/dist/chat/state.d.ts +5 -2
  110. package/dist/chat/state.js +14 -1
  111. package/dist/chat/state.js.map +1 -1
  112. package/dist/chat/storage.cjs +58 -33
  113. package/dist/chat/storage.cjs.map +1 -1
  114. package/dist/chat/storage.d.cts +18 -8
  115. package/dist/chat/storage.d.ts +18 -8
  116. package/dist/chat/storage.js +59 -34
  117. package/dist/chat/storage.js.map +1 -1
  118. package/dist/errors-C-so0M4t.d.cts +33 -0
  119. package/dist/errors-C-so0M4t.d.ts +33 -0
  120. package/dist/errors-CmVvczxZ.d.cts +28 -0
  121. package/dist/errors-CmVvczxZ.d.ts +28 -0
  122. package/dist/{in-process-transport-C2oPTYs6.d.ts → in-process-transport-7EIit9Xk.d.ts} +72 -33
  123. package/dist/{in-process-transport-DG-w5G6k.d.cts → in-process-transport-Ct9YcX8I.d.cts} +72 -33
  124. package/dist/index.cjs +354 -60
  125. package/dist/index.cjs.map +1 -1
  126. package/dist/index.d.cts +294 -123
  127. package/dist/index.d.ts +294 -123
  128. package/dist/index.js +347 -60
  129. package/dist/index.js.map +1 -1
  130. package/dist/provider-types-PTSlRPNB.d.cts +39 -0
  131. package/dist/provider-types-PTSlRPNB.d.ts +39 -0
  132. package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
  133. package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
  134. package/dist/testing.cjs +1107 -0
  135. package/dist/testing.cjs.map +1 -0
  136. package/dist/testing.d.cts +144 -0
  137. package/dist/testing.d.ts +144 -0
  138. package/dist/testing.js +1101 -0
  139. package/dist/testing.js.map +1 -0
  140. package/dist/token-store-CSUBgYwn.d.ts +48 -0
  141. package/dist/token-store-CuC4hB9Z.d.cts +48 -0
  142. package/dist/{transport-DX1Nhm4N.d.cts → transport-DLWCN18G.d.cts} +5 -4
  143. package/dist/{transport-D1OaUgRk.d.ts → transport-DsuS-GeM.d.ts} +5 -4
  144. package/dist/{types-CGF7AEX1.d.cts → types-4vbcmPTp.d.cts} +4 -2
  145. package/dist/{types-Bh5AhqD-.d.ts → types-BxggH0Yh.d.ts} +4 -2
  146. package/dist/types-DgtI1hzh.d.ts +364 -0
  147. package/dist/types-DkSXALKg.d.cts +364 -0
  148. package/package.json +41 -5
  149. package/LICENSE +0 -21
  150. package/README.md +0 -948
  151. package/dist/errors-BDLbNu9w.d.cts +0 -13
  152. package/dist/errors-BDLbNu9w.d.ts +0 -13
  153. package/dist/types-DLZzlJxt.d.ts +0 -39
  154. package/dist/types-tE0CXwBl.d.cts +0 -39
@@ -1,7 +1,8 @@
1
- import { existsSync, readdirSync, unlinkSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
1
+ import { existsSync, mkdirSync } from 'fs';
2
+ import { readdir, unlink, mkdir, access, readFile, writeFile } from 'fs/promises';
2
3
  import { join } from 'path';
3
4
 
4
- // src/chat/core.ts
5
+ // src/chat/types.ts
5
6
  function createChatId() {
6
7
  return crypto.randomUUID();
7
8
  }
@@ -10,9 +11,18 @@ function createChatId() {
10
11
  var AgentSDKError = class extends Error {
11
12
  /** @internal Marker for cross-bundle identity checks */
12
13
  _agentSDKError = true;
14
+ /** Machine-readable error code. Prefer values from the ErrorCode enum. */
15
+ code;
16
+ /** Whether this error is safe to retry */
17
+ retryable;
18
+ /** HTTP status code hint for error classification */
19
+ httpStatus;
13
20
  constructor(message, options) {
14
21
  super(message, options);
15
22
  this.name = "AgentSDKError";
23
+ this.code = options?.code;
24
+ this.retryable = options?.retryable ?? false;
25
+ this.httpStatus = options?.httpStatus;
16
26
  }
17
27
  /** Check if an error is an AgentSDKError (works across bundled copies) */
18
28
  static is(error) {
@@ -22,7 +32,7 @@ var AgentSDKError = class extends Error {
22
32
 
23
33
  // src/chat/storage.ts
24
34
  var StorageError = class extends AgentSDKError {
25
- /** Machine-readable error code */
35
+ /** Machine-readable error code from the unified ErrorCode enum */
26
36
  code;
27
37
  constructor(message, code) {
28
38
  super(message);
@@ -59,7 +69,7 @@ var InMemoryStorage = class {
59
69
  if (this.data.has(key)) {
60
70
  throw new StorageError(
61
71
  `Item with key "${key}" already exists`,
62
- "DUPLICATE_KEY"
72
+ "STORAGE_DUPLICATE_KEY" /* STORAGE_DUPLICATE_KEY */
63
73
  );
64
74
  }
65
75
  this.data.set(key, structuredClone(item));
@@ -69,7 +79,7 @@ var InMemoryStorage = class {
69
79
  if (!this.data.has(key)) {
70
80
  throw new StorageError(
71
81
  `Item with key "${key}" not found`,
72
- "NOT_FOUND"
82
+ "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
73
83
  );
74
84
  }
75
85
  this.data.set(key, structuredClone(item));
@@ -79,7 +89,7 @@ var InMemoryStorage = class {
79
89
  if (!this.data.has(key)) {
80
90
  throw new StorageError(
81
91
  `Item with key "${key}" not found`,
82
- "NOT_FOUND"
92
+ "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
83
93
  );
84
94
  }
85
95
  this.data.delete(key);
@@ -103,25 +113,25 @@ var FileStorage = class {
103
113
  constructor(options) {
104
114
  this.directory = options.directory;
105
115
  this.extension = options.extension ?? ".json";
106
- this.ensureDirectory();
116
+ this.ensureDirectorySync();
107
117
  }
108
118
  /** @inheritdoc */
109
119
  async get(key) {
110
120
  const filePath = this.keyToPath(key);
111
- if (!existsSync(filePath)) {
121
+ if (!await this.fileExists(filePath)) {
112
122
  return null;
113
123
  }
114
- return this.readFile(filePath);
124
+ return this.readJsonFile(filePath);
115
125
  }
116
126
  /** @inheritdoc */
117
127
  async list(options) {
118
- this.ensureDirectory();
119
- const files = readdirSync(this.directory).filter(
128
+ await this.ensureDirectoryAsync();
129
+ const files = (await readdir(this.directory)).filter(
120
130
  (f) => f.endsWith(this.extension)
121
131
  );
122
132
  let items = [];
123
133
  for (const file of files) {
124
- const item = this.readFile(join(this.directory, file));
134
+ const item = await this.readJsonFile(join(this.directory, file));
125
135
  items.push(item);
126
136
  }
127
137
  if (options?.filter) {
@@ -141,55 +151,55 @@ var FileStorage = class {
141
151
  /** @inheritdoc */
142
152
  async create(key, item) {
143
153
  const filePath = this.keyToPath(key);
144
- if (existsSync(filePath)) {
154
+ if (await this.fileExists(filePath)) {
145
155
  throw new StorageError(
146
156
  `Item with key "${key}" already exists`,
147
- "DUPLICATE_KEY"
157
+ "STORAGE_DUPLICATE_KEY" /* STORAGE_DUPLICATE_KEY */
148
158
  );
149
159
  }
150
- this.writeFile(filePath, item);
160
+ await this.writeJsonFile(filePath, item);
151
161
  }
152
162
  /** @inheritdoc */
153
163
  async update(key, item) {
154
164
  const filePath = this.keyToPath(key);
155
- if (!existsSync(filePath)) {
165
+ if (!await this.fileExists(filePath)) {
156
166
  throw new StorageError(
157
167
  `Item with key "${key}" not found`,
158
- "NOT_FOUND"
168
+ "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
159
169
  );
160
170
  }
161
- this.writeFile(filePath, item);
171
+ await this.writeJsonFile(filePath, item);
162
172
  }
163
173
  /** @inheritdoc */
164
174
  async delete(key) {
165
175
  const filePath = this.keyToPath(key);
166
- if (!existsSync(filePath)) {
176
+ if (!await this.fileExists(filePath)) {
167
177
  throw new StorageError(
168
178
  `Item with key "${key}" not found`,
169
- "NOT_FOUND"
179
+ "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
170
180
  );
171
181
  }
172
- unlinkSync(filePath);
182
+ await unlink(filePath);
173
183
  }
174
184
  /** @inheritdoc */
175
185
  async has(key) {
176
- return existsSync(this.keyToPath(key));
186
+ return this.fileExists(this.keyToPath(key));
177
187
  }
178
188
  /** @inheritdoc */
179
189
  async count() {
180
- this.ensureDirectory();
181
- return readdirSync(this.directory).filter(
190
+ await this.ensureDirectoryAsync();
191
+ return (await readdir(this.directory)).filter(
182
192
  (f) => f.endsWith(this.extension)
183
193
  ).length;
184
194
  }
185
195
  /** @inheritdoc */
186
196
  async clear() {
187
- this.ensureDirectory();
188
- const files = readdirSync(this.directory).filter(
197
+ await this.ensureDirectoryAsync();
198
+ const files = (await readdir(this.directory)).filter(
189
199
  (f) => f.endsWith(this.extension)
190
200
  );
191
201
  for (const file of files) {
192
- unlinkSync(join(this.directory, file));
202
+ await unlink(join(this.directory, file));
193
203
  }
194
204
  }
195
205
  keyToPath(key) {
@@ -199,36 +209,51 @@ var FileStorage = class {
199
209
  );
200
210
  return join(this.directory, `${safeKey}${this.extension}`);
201
211
  }
202
- ensureDirectory() {
212
+ /** Sync directory init — used only in constructor (one-time). */
213
+ ensureDirectorySync() {
203
214
  if (!existsSync(this.directory)) {
204
215
  mkdirSync(this.directory, { recursive: true });
205
216
  }
206
217
  }
207
- readFile(filePath) {
218
+ /** Async directory init — used in operations. */
219
+ async ensureDirectoryAsync() {
220
+ if (!await this.fileExists(this.directory)) {
221
+ await mkdir(this.directory, { recursive: true });
222
+ }
223
+ }
224
+ async fileExists(filePath) {
208
225
  try {
209
- const content = readFileSync(filePath, "utf-8");
226
+ await access(filePath);
227
+ return true;
228
+ } catch {
229
+ return false;
230
+ }
231
+ }
232
+ async readJsonFile(filePath) {
233
+ try {
234
+ const content = await readFile(filePath, "utf-8");
210
235
  return JSON.parse(content);
211
236
  } catch (error) {
212
237
  if (error instanceof SyntaxError) {
213
238
  throw new StorageError(
214
239
  `Failed to parse file: ${filePath}`,
215
- "SERIALIZATION_ERROR"
240
+ "STORAGE_SERIALIZATION_ERROR" /* STORAGE_SERIALIZATION_ERROR */
216
241
  );
217
242
  }
218
243
  throw new StorageError(
219
244
  `Failed to read file: ${filePath}`,
220
- "IO_ERROR"
245
+ "STORAGE_IO_ERROR" /* STORAGE_IO_ERROR */
221
246
  );
222
247
  }
223
248
  }
224
- writeFile(filePath, item) {
249
+ async writeJsonFile(filePath, item) {
225
250
  try {
226
251
  const content = JSON.stringify(item, null, 2);
227
- writeFileSync(filePath, content, "utf-8");
252
+ await writeFile(filePath, content, "utf-8");
228
253
  } catch {
229
254
  throw new StorageError(
230
255
  `Failed to write file: ${filePath}`,
231
- "IO_ERROR"
256
+ "STORAGE_IO_ERROR" /* STORAGE_IO_ERROR */
232
257
  );
233
258
  }
234
259
  }
@@ -273,7 +298,7 @@ var BaseSessionStore = class {
273
298
  async updateTitle(id, title) {
274
299
  const session = await this.adapter.get(id);
275
300
  if (!session) {
276
- throw new StorageError(`Session "${id}" not found`, "NOT_FOUND");
301
+ throw new StorageError(`Session "${id}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
277
302
  }
278
303
  session.title = title;
279
304
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -282,7 +307,7 @@ var BaseSessionStore = class {
282
307
  async updateConfig(id, config) {
283
308
  const session = await this.adapter.get(id);
284
309
  if (!session) {
285
- throw new StorageError(`Session "${id}" not found`, "NOT_FOUND");
310
+ throw new StorageError(`Session "${id}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
286
311
  }
287
312
  session.config = { ...session.config, ...config };
288
313
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -294,7 +319,7 @@ var BaseSessionStore = class {
294
319
  async appendMessage(sessionId, message) {
295
320
  const session = await this.adapter.get(sessionId);
296
321
  if (!session) {
297
- throw new StorageError(`Session "${sessionId}" not found`, "NOT_FOUND");
322
+ throw new StorageError(`Session "${sessionId}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
298
323
  }
299
324
  session.messages.push(structuredClone(message));
300
325
  session.metadata.messageCount = session.messages.length;
@@ -305,7 +330,7 @@ var BaseSessionStore = class {
305
330
  if (messages.length === 0) return;
306
331
  const session = await this.adapter.get(sessionId);
307
332
  if (!session) {
308
- throw new StorageError(`Session "${sessionId}" not found`, "NOT_FOUND");
333
+ throw new StorageError(`Session "${sessionId}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
309
334
  }
310
335
  for (const msg of messages) {
311
336
  session.messages.push(structuredClone(msg));
@@ -317,7 +342,7 @@ var BaseSessionStore = class {
317
342
  async loadMessages(sessionId, options) {
318
343
  const session = await this.adapter.get(sessionId);
319
344
  if (!session) {
320
- throw new StorageError(`Session "${sessionId}" not found`, "NOT_FOUND");
345
+ throw new StorageError(`Session "${sessionId}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
321
346
  }
322
347
  const total = session.messages.length;
323
348
  const offset = options?.offset ?? 0;
@@ -329,24 +354,6 @@ var BaseSessionStore = class {
329
354
  hasMore: offset + limit < total
330
355
  };
331
356
  }
332
- async archiveSession(id) {
333
- const session = await this.adapter.get(id);
334
- if (!session) {
335
- throw new StorageError(`Session "${id}" not found`, "NOT_FOUND");
336
- }
337
- session.status = "archived";
338
- session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
339
- await this.adapter.update(id, session);
340
- }
341
- async unarchiveSession(id) {
342
- const session = await this.adapter.get(id);
343
- if (!session) {
344
- throw new StorageError(`Session "${id}" not found`, "NOT_FOUND");
345
- }
346
- session.status = "active";
347
- session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
348
- await this.adapter.update(id, session);
349
- }
350
357
  async searchSessions(options) {
351
358
  const query = options.query.toLowerCase();
352
359
  const limit = options.limit ?? 20;
@@ -368,15 +375,6 @@ var BaseSessionStore = class {
368
375
  async clear() {
369
376
  return this.adapter.clear();
370
377
  }
371
- // ── Deprecated Aliases ──────────────────────────────────────
372
- /** @deprecated Use `appendMessage()` instead */
373
- async addMessage(sessionId, message) {
374
- return this.appendMessage(sessionId, message);
375
- }
376
- /** @deprecated Use `loadMessages()` instead */
377
- async getMessages(sessionId, options) {
378
- return this.loadMessages(sessionId, options);
379
- }
380
378
  };
381
379
  var InMemorySessionStore = class extends BaseSessionStore {
382
380
  constructor() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/chat/core.ts","../../src/errors.ts","../../src/chat/storage.ts","../../src/chat/sessions.ts"],"names":[],"mappings":"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","file":"sessions.js","sourcesContent":["/**\n * @witqq/agent-sdk/chat/core\n *\n * Foundational chat types and utilities: ChatMessage, ChatSession, ChatEvent,\n * IChatProvider, type guards, and AgentEvent↔ChatEvent bridge functions.\n */\n\nimport type {\n AgentEvent,\n Message,\n ToolCall,\n ToolResult,\n ToolDefinition,\n UsageData,\n ModelInfo,\n JSONValue,\n} from \"../types.js\";\n\n// ─── Unique ID ─────────────────────────────────────────────────\n\n/** Branded type for unique identifiers */\nexport type ChatId = string & { readonly __brand: \"ChatId\" };\n\n/**\n * Generate a new unique ChatId (crypto.randomUUID-based)\n * @returns Branded ChatId string\n */\nexport function createChatId(): ChatId {\n return crypto.randomUUID() as ChatId;\n}\n\n/** UUID v4 pattern for ChatId validation */\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n/**\n * Cast a string to ChatId with UUID format validation.\n * Use this instead of manual `as ChatId` type assertions.\n *\n * @param value - String to validate and cast\n * @returns Branded ChatId\n * @throws {TypeError} If value is not a valid UUID v4 format\n *\n * @example\n * ```ts\n * const id = toChatId(\"550e8400-e29b-41d4-a716-446655440000\");\n * ```\n */\nexport function toChatId(value: string): ChatId {\n if (!UUID_RE.test(value)) {\n throw new TypeError(`Invalid ChatId: \"${value}\" is not a valid UUID`);\n }\n return value as ChatId;\n}\n\n/**\n * Accepts either a plain string or branded ChatId for API convenience.\n * Use this in public API signatures so consumers don't need `as ChatId` casts.\n */\nexport type ChatIdLike = string | ChatId;\n\n// ─── Status Types ──────────────────────────────────────────────\n\n/** Lifecycle status of a message part (text, reasoning, etc.) */\nexport type PartStatus = \"pending\" | \"streaming\" | \"complete\" | \"error\";\n/** Lifecycle status of a tool call within a message */\nexport type ToolCallStatus = \"pending\" | \"running\" | \"requires_approval\" | \"complete\" | \"error\" | \"denied\";\n/** Lifecycle status of an entire message */\nexport type MessageStatus = \"pending\" | \"streaming\" | \"complete\" | \"error\" | \"cancelled\";\n/** Lifecycle status of a chat session */\nexport type SessionStatus = \"active\" | \"archived\";\n/** Lifecycle status of the chat runtime */\nexport type RuntimeStatus = \"idle\" | \"streaming\" | \"error\" | \"disposed\";\n\n// ─── Message Parts (union) ─────────────────────────────────────\n\n/** Plain text content part */\nexport interface TextPart { type: \"text\"; text: string; status: PartStatus; }\n/** Model reasoning/thinking content part */\nexport interface ReasoningPart { type: \"reasoning\"; text: string; status: PartStatus; }\n/** Tool invocation part with call ID, arguments, optional result */\nexport interface ToolCallPart { type: \"tool_call\"; toolCallId: string; name: string; args: unknown; result?: unknown; status: ToolCallStatus; error?: string; }\n/** Source reference part (URL citation) */\nexport interface SourcePart { type: \"source\"; url: string; title?: string; status: PartStatus; }\n/** File attachment part (base64-encoded data) */\nexport interface FilePart { type: \"file\"; name: string; mimeType: string; data: string; status: PartStatus; }\n/** Union of all message part types */\nexport type MessagePart = TextPart | ReasoningPart | ToolCallPart | SourcePart | FilePart;\n\n// ─── Chat Message ──────────────────────────────────────────────\n\n/** Role of message author */\nexport type ChatRole = \"user\" | \"assistant\" | \"system\";\n\n/** Metadata attached to messages — useful preset for the TMetadata generic */\nexport interface ChatMessageMetadata {\n model?: string;\n backend?: string;\n usage?: UsageData;\n isSummary?: boolean;\n isArchived?: boolean;\n estimatedTokens?: number;\n custom?: Record<string, unknown>;\n}\n\n/** Message status */\nexport type ChatMessageStatus = MessageStatus;\n\n/** A single chat message — the fundamental unit of conversation */\nexport interface ChatMessage<TMetadata = unknown> {\n id: ChatId;\n role: ChatRole;\n parts: MessagePart[];\n metadata?: TMetadata;\n createdAt: string;\n updatedAt?: string;\n status: MessageStatus;\n}\n\n// ─── Convenience Getters ───────────────────────────────────────\n\n/**\n * Join all TextPart texts in a message\n * @param message - The chat message to extract text from\n * @returns Concatenated text content\n */\nexport function getMessageText(message: ChatMessage): string {\n return message.parts\n .filter((p): p is TextPart => p.type === \"text\")\n .map((p) => p.text)\n .join(\"\");\n}\n\n/**\n * Filter all ToolCallParts from a message\n * @param message - The chat message to extract tool calls from\n * @returns Array of ToolCallPart\n */\nexport function getMessageToolCalls(message: ChatMessage): ToolCallPart[] {\n return message.parts.filter((p): p is ToolCallPart => p.type === \"tool_call\");\n}\n\n/**\n * Join all ReasoningPart texts in a message\n * @param message - The chat message to extract reasoning from\n * @returns Concatenated reasoning content\n */\nexport function getMessageReasoning(message: ChatMessage): string {\n return message.parts\n .filter((p): p is ReasoningPart => p.type === \"reasoning\")\n .map((p) => p.text)\n .join(\"\");\n}\n\n// ─── Supporting Types ──────────────────────────────────────────\n\n/** Options for sending a message */\nexport interface SendOpts { sessionId?: string; model?: string; signal?: AbortSignal; metadata?: Record<string, unknown>; }\n/** Options for creating a new session */\nexport interface CreateSessionOpts { id?: string; title?: string; model?: string; metadata?: Record<string, unknown>; }\n/** Options for listing sessions with pagination */\nexport interface ListOpts { limit?: number; offset?: number; status?: SessionStatus; }\n/** Options for backend execution (model, tokens, tools) */\nexport interface BackendOpts { model: string; signal?: AbortSignal; systemPrompt?: string; temperature?: number; maxTokens?: number; tools?: Record<string, unknown>; providerOptions?: Record<string, unknown>; }\n/** Context passed to tool execute functions */\nexport interface ToolContext { sessionId: string; userId?: string; signal: AbortSignal; }\n/** Configuration for creating a chat runtime */\nexport interface ChatRuntimeConfig { backend: string; model?: string; apiKey?: string; baseUrl?: string; context?: { maxTokens?: number; reserveTokens?: number; strategy?: \"sliding\" | \"summarize\" | \"truncate\"; }; retry?: { maxRetries?: number; initialDelay?: number; backoffFactor?: number; }; providerOptions?: Record<string, unknown>; }\n\n// ─── Chat Session ──────────────────────────────────────────────\n\n/** Session configuration snapshot */\nexport interface ChatSessionConfig {\n model: string;\n backend: string;\n systemPrompt?: string;\n temperature?: number;\n maxTokens?: number;\n}\n\n/** Session metadata */\nexport interface ChatSessionMetadata<TCustom extends Record<string, unknown> = Record<string, unknown>> {\n messageCount: number;\n totalTokens: number;\n tags?: string[];\n custom?: TCustom;\n}\n\n/** Chat session — a conversation with ordered messages */\nexport interface ChatSession<TCustom extends Record<string, unknown> = Record<string, unknown>> {\n id: ChatId;\n title?: string;\n messages: ChatMessage[];\n config: ChatSessionConfig;\n metadata: ChatSessionMetadata<TCustom>;\n status: SessionStatus;\n createdAt: string;\n updatedAt: string;\n backendSessionId?: string;\n /** Subscribe to session changes (for React useSyncExternalStore) */\n subscribe?(callback: () => void): () => void;\n /** Get immutable snapshot of session state (for React useSyncExternalStore) */\n getSnapshot?(): ChatSession<TCustom>;\n /** Last message in the session */\n readonly lastMessage?: ChatMessage;\n}\n\n/** Lightweight session info for listing (without full message array) */\nexport interface SessionInfo {\n id: ChatId;\n title?: string;\n status: SessionStatus;\n messageCount: number;\n lastMessage?: ChatMessage;\n createdAt: string;\n updatedAt: string;\n}\n\n// ─── Chat Events ───────────────────────────────────────────────\n\n/** Events emitted during chat operation */\nexport type ChatEvent =\n | { type: \"message:start\"; messageId: ChatId; role: ChatRole }\n | { type: \"message:delta\"; messageId: ChatId; text: string }\n | { type: \"message:complete\"; messageId: ChatId; message: ChatMessage }\n | {\n type: \"tool:start\";\n messageId: ChatId;\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n }\n | {\n type: \"tool:complete\";\n messageId: ChatId;\n toolCallId: string;\n toolName: string;\n result: unknown;\n isError?: boolean;\n }\n | { type: \"thinking:start\"; messageId: ChatId }\n | { type: \"thinking:delta\"; messageId: ChatId; text: string }\n | { type: \"thinking:end\"; messageId: ChatId }\n | {\n type: \"permission:request\";\n messageId: ChatId;\n toolName: string;\n toolArgs: Record<string, unknown>;\n }\n | {\n type: \"permission:response\";\n messageId: ChatId;\n toolName: string;\n allowed: boolean;\n }\n | {\n type: \"usage\";\n promptTokens: number;\n completionTokens: number;\n model?: string;\n }\n | { type: \"session:created\"; sessionId: ChatId }\n | { type: \"session:updated\"; sessionId: ChatId }\n | {\n type: \"error\";\n error: string;\n recoverable: boolean;\n messageId?: ChatId;\n }\n | { type: \"typing:start\" }\n | { type: \"typing:end\" }\n | { type: \"heartbeat\" }\n | { type: \"done\"; finalOutput?: string };\n\n/** All possible ChatEvent type strings */\nexport type ChatEventType = ChatEvent[\"type\"];\n\n// ─── Chat Middleware ───────────────────────────────────────────\n\n/** Context passed to ChatMiddleware hooks */\nexport interface ChatMiddlewareContext {\n sessionId: ChatId;\n signal: AbortSignal;\n}\n\n/** Runtime-level middleware for the send/receive lifecycle.\n * Different from EventMiddleware which operates at the event bus level. */\nexport interface ChatMiddleware {\n /** Transform message before sending to backend */\n onBeforeSend?(message: ChatMessage, context: ChatMiddlewareContext): ChatMessage | Promise<ChatMessage>;\n /** Transform/intercept stream events */\n onEvent?(event: ChatEvent, context: ChatMiddlewareContext): ChatEvent | null | Promise<ChatEvent | null>;\n /** Transform completed message after receiving from backend */\n onAfterReceive?(message: ChatMessage, context: ChatMiddlewareContext): ChatMessage | Promise<ChatMessage>;\n /** Intercept errors — return null to suppress, return error to propagate */\n onError?(error: Error, context: ChatMiddlewareContext): Error | null | Promise<Error | null>;\n}\n\n// ─── Chat Provider Abstraction ─────────────────────────────────\n\n/** Options for sending a message to a provider */\nexport interface SendMessageOptions {\n signal?: AbortSignal;\n model?: string;\n context?: Record<string, unknown>;\n /** Additional tools to include in this request */\n tools?: ToolDefinition[];\n}\n\n/** Abstract chat provider — wraps an IAgentService for chat use */\nexport interface IChatProvider {\n readonly name: string;\n sendMessage(\n session: ChatSession,\n message: string,\n options?: SendMessageOptions,\n ): Promise<ChatMessage>;\n streamMessage(\n session: ChatSession,\n message: string,\n options?: SendMessageOptions,\n ): AsyncIterable<ChatEvent>;\n listModels(): Promise<ModelInfo[]>;\n validate(): Promise<{ valid: boolean; errors: string[] }>;\n dispose(): Promise<void>;\n}\n\n// ─── Type Guards ───────────────────────────────────────────────\n\n/**\n * Check if a value is a ChatMessage\n * @param value - Value to check\n * @returns True if value has ChatMessage shape\n */\nexport function isChatMessage(value: unknown): value is ChatMessage {\n if (typeof value !== \"object\" || value === null) return false;\n const obj = value as Record<string, unknown>;\n return (\n typeof obj.id === \"string\" &&\n typeof obj.role === \"string\" &&\n (obj.role === \"user\" ||\n obj.role === \"assistant\" ||\n obj.role === \"system\") &&\n Array.isArray(obj.parts) &&\n typeof obj.createdAt === \"string\" &&\n typeof obj.status === \"string\"\n );\n}\n\n/**\n * Check if a value is a ChatSession\n * @param value - Value to check\n * @returns True if value has ChatSession shape\n */\nexport function isChatSession(value: unknown): value is ChatSession {\n if (typeof value !== \"object\" || value === null) return false;\n const obj = value as Record<string, unknown>;\n return (\n typeof obj.id === \"string\" &&\n Array.isArray(obj.messages) &&\n typeof obj.config === \"object\" &&\n obj.config !== null &&\n typeof obj.createdAt === \"string\" &&\n typeof obj.updatedAt === \"string\" &&\n typeof obj.status === \"string\"\n );\n}\n\n/**\n * Check if a value is a MessagePart\n * @param value - Value to check\n * @returns True if value has MessagePart shape\n */\nexport function isMessagePart(value: unknown): value is MessagePart {\n if (typeof value !== \"object\" || value === null) return false;\n const obj = value as Record<string, unknown>;\n return (\n typeof obj.type === \"string\" &&\n (obj.type === \"text\" ||\n obj.type === \"reasoning\" ||\n obj.type === \"tool_call\" ||\n obj.type === \"source\" ||\n obj.type === \"file\")\n );\n}\n\n/**\n * Check if a value is a TextPart\n * @param value - Value to check\n * @returns True if value is a TextPart\n */\nexport function isTextPart(value: unknown): value is TextPart {\n if (typeof value !== \"object\" || value === null) return false;\n const obj = value as Record<string, unknown>;\n return obj.type === \"text\" && typeof obj.text === \"string\";\n}\n\n/**\n * Check if a value is a ToolCallPart\n * @param value - Value to check\n * @returns True if value is a ToolCallPart\n */\nexport function isToolCallPart(value: unknown): value is ToolCallPart {\n if (typeof value !== \"object\" || value === null) return false;\n const obj = value as Record<string, unknown>;\n return obj.type === \"tool_call\" && typeof obj.toolCallId === \"string\" && typeof obj.name === \"string\";\n}\n\n/**\n * Check if a value is a ReasoningPart\n * @param value - Value to check\n * @returns True if value is a ReasoningPart\n */\nexport function isReasoningPart(value: unknown): value is ReasoningPart {\n if (typeof value !== \"object\" || value === null) return false;\n const obj = value as Record<string, unknown>;\n return obj.type === \"reasoning\" && typeof obj.text === \"string\";\n}\n\n/**\n * Check if a value is a SourcePart\n * @param value - Value to check\n * @returns True if value is a SourcePart\n */\nexport function isSourcePart(value: unknown): value is SourcePart {\n if (typeof value !== \"object\" || value === null) return false;\n const obj = value as Record<string, unknown>;\n return obj.type === \"source\" && typeof obj.url === \"string\";\n}\n\n/**\n * Check if a value is a FilePart\n * @param value - Value to check\n * @returns True if value is a FilePart\n */\nexport function isFilePart(value: unknown): value is FilePart {\n if (typeof value !== \"object\" || value === null) return false;\n const obj = value as Record<string, unknown>;\n return obj.type === \"file\" && typeof obj.name === \"string\" && typeof obj.mimeType === \"string\";\n}\n\n/**\n * Check if a value is a ChatEvent\n * @param value - Value to check\n * @returns True if value has a valid ChatEvent type\n */\nexport function isChatEvent(value: unknown): value is ChatEvent {\n if (typeof value !== \"object\" || value === null) return false;\n const obj = value as Record<string, unknown>;\n const validTypes: ChatEventType[] = [\n \"message:start\",\n \"message:delta\",\n \"message:complete\",\n \"tool:start\",\n \"tool:complete\",\n \"thinking:start\",\n \"thinking:delta\",\n \"thinking:end\",\n \"permission:request\",\n \"permission:response\",\n \"usage\",\n \"session:created\",\n \"session:updated\",\n \"error\",\n \"typing:start\",\n \"typing:end\",\n \"heartbeat\",\n \"done\",\n ];\n return validTypes.includes(obj.type as ChatEventType);\n}\n\n// ─── Agent Event Adapter ───────────────────────────────────────\n\n/**\n * Map a single AgentEvent to a ChatEvent (or null if no mapping)\n * @param event - The AgentEvent to convert\n * @param messageId - ChatId to associate with the event\n * @returns Corresponding ChatEvent or null if unmappable\n */\nexport function agentEventToChatEvent(\n event: AgentEvent,\n messageId: ChatId,\n): ChatEvent | null {\n switch (event.type) {\n case \"text_delta\":\n return { type: \"message:delta\", messageId, text: event.text };\n case \"thinking_start\":\n return { type: \"thinking:start\", messageId };\n case \"thinking_delta\":\n return { type: \"thinking:delta\", messageId, text: event.text };\n case \"thinking_end\":\n return { type: \"thinking:end\", messageId };\n case \"tool_call_start\":\n return {\n type: \"tool:start\",\n messageId,\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n args: event.args as Record<string, unknown>,\n };\n case \"tool_call_end\":\n return {\n type: \"tool:complete\",\n messageId,\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n result: event.result,\n };\n case \"permission_request\":\n return {\n type: \"permission:request\",\n messageId,\n toolName: event.request.toolName,\n toolArgs: event.request.toolArgs,\n };\n case \"permission_response\":\n return {\n type: \"permission:response\",\n messageId,\n toolName: event.toolName,\n allowed: event.decision.allowed,\n };\n case \"usage_update\":\n return {\n type: \"usage\",\n promptTokens: event.promptTokens,\n completionTokens: event.completionTokens,\n model: event.model,\n };\n case \"error\":\n return {\n type: \"error\",\n error: event.error,\n recoverable: event.recoverable,\n messageId,\n };\n case \"heartbeat\":\n return { type: \"heartbeat\" };\n case \"ask_user\":\n case \"ask_user_response\":\n case \"session_info\":\n case \"done\":\n return null;\n default:\n return null;\n }\n}\n\n/**\n * Convert AgentEvent async iterable to ChatEvent async iterable\n * @param events - Source agent events\n * @param messageId - ChatId to associate with converted events\n * @returns Async iterable of ChatEvent (nulls filtered out)\n */\nexport async function* adaptAgentEvents(\n events: AsyncIterable<AgentEvent>,\n messageId: ChatId,\n): AsyncIterable<ChatEvent> {\n for await (const event of events) {\n const chatEvent = agentEventToChatEvent(event, messageId);\n if (chatEvent !== null) {\n yield chatEvent;\n }\n }\n}\n\n/**\n * Map a ChatEvent back to an AgentEvent for accumulator consumption.\n * Returns null for events that don't map to accumulator-relevant AgentEvents\n * (e.g. message:start, message:complete, usage, permission:*, heartbeat).\n *\n * @param event - The ChatEvent to convert\n * @returns Corresponding AgentEvent or null if not accumulator-relevant\n */\nexport function chatEventToAgentEvent(event: ChatEvent): AgentEvent | null {\n switch (event.type) {\n case \"message:delta\":\n return { type: \"text_delta\", text: event.text };\n case \"thinking:start\":\n return { type: \"thinking_start\" };\n case \"thinking:delta\":\n return { type: \"thinking_delta\", text: event.text };\n case \"thinking:end\":\n return { type: \"thinking_end\" };\n case \"tool:start\":\n return {\n type: \"tool_call_start\",\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n args: event.args as JSONValue,\n };\n case \"tool:complete\":\n return {\n type: \"tool_call_end\",\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n result: event.result as JSONValue,\n };\n case \"error\":\n return { type: \"error\", error: event.error, recoverable: event.recoverable };\n default:\n return null;\n }\n}\n\n// ─── Message Conversion ────────────────────────────────────────\n\n/**\n * Convert a ChatMessage to agent-sdk Message format\n * @param message - The ChatMessage to convert\n * @returns agent-sdk Message\n */\nexport function toAgentMessage(message: ChatMessage): Message {\n const textContent = getMessageText(message);\n const toolCallParts = getMessageToolCalls(message);\n\n switch (message.role) {\n case \"user\":\n return { role: \"user\", content: textContent };\n case \"assistant\": {\n const toolCalls: ToolCall[] | undefined = toolCallParts.length > 0\n ? toolCallParts.map((p) => ({ id: p.toolCallId, name: p.name, args: p.args as JSONValue }))\n : undefined;\n return {\n role: \"assistant\",\n content: textContent,\n toolCalls,\n };\n }\n case \"system\":\n return { role: \"system\", content: textContent };\n }\n}\n\n/**\n * Convert an agent-sdk Message to ChatMessage\n * @param message - The agent-sdk Message to convert\n * @param id - Optional ChatId (auto-generated if omitted)\n * @returns ChatMessage with status \"complete\"\n */\nexport function fromAgentMessage(message: Message, id?: ChatId): ChatMessage {\n const chatId = id ?? createChatId();\n const now = new Date().toISOString();\n\n const parts: MessagePart[] = [];\n\n // Build text content\n const textContent =\n typeof message.content === \"string\"\n ? message.content\n : Array.isArray(message.content)\n ? message.content\n .filter((part) => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\\n\")\n : (message.content ?? \"\");\n\n if (textContent) {\n parts.push({ type: \"text\", text: textContent, status: \"complete\" });\n }\n\n // Add tool calls from assistant messages\n if (message.role === \"assistant\" && message.toolCalls) {\n for (const tc of message.toolCalls) {\n parts.push({\n type: \"tool_call\",\n toolCallId: tc.id,\n name: tc.name,\n args: tc.args,\n status: \"complete\",\n });\n }\n }\n\n // Add tool results — map 'tool' role to 'assistant'\n if (message.role === \"tool\" && message.toolResults) {\n for (const tr of message.toolResults) {\n parts.push({\n type: \"tool_call\",\n toolCallId: tr.toolCallId,\n name: tr.name,\n args: {},\n result: tr.result,\n status: \"complete\",\n });\n }\n }\n\n // Ensure at least an empty text part for empty messages\n if (parts.length === 0) {\n parts.push({ type: \"text\", text: \"\", status: \"complete\" });\n }\n\n const role: ChatRole = message.role === \"tool\" ? \"assistant\" : message.role;\n\n return {\n id: chatId,\n role,\n parts,\n createdAt: now,\n status: \"complete\",\n };\n}\n\n/**\n * Extract ToolResults from ToolCallParts that have results\n * @param message - The ChatMessage to extract results from\n * @returns Array of ToolResult for completed tool calls\n */\nexport function extractToolResults(message: ChatMessage): ToolResult[] {\n return getMessageToolCalls(message)\n .filter((p) => p.result !== undefined)\n .map((p) => ({\n toolCallId: p.toolCallId,\n name: p.name,\n result: p.result as JSONValue,\n isError: p.status === \"error\" ? true : undefined,\n }));\n}\n","/** Base error class for agent-sdk.\n *\n * Use `AgentSDKError.is(err)` for reliable cross-module `instanceof` checks\n * (works across separately bundled entry points where `instanceof` may fail). */\nexport class AgentSDKError extends Error {\n /** @internal Marker for cross-bundle identity checks */\n readonly _agentSDKError = true as const;\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"AgentSDKError\";\n }\n\n /** Check if an error is an AgentSDKError (works across bundled copies) */\n static is(error: unknown): error is AgentSDKError {\n return (\n error instanceof Error &&\n \"_agentSDKError\" in error &&\n (error as AgentSDKError)._agentSDKError === true\n );\n }\n}\n\n/** Thrown when agent.run() is called while already running (M8 re-entrancy guard) */\nexport class ReentrancyError extends AgentSDKError {\n constructor() {\n super(\"Agent is already running. Await the current run before starting another.\");\n this.name = \"ReentrancyError\";\n }\n}\n\n/** Thrown when an operation is attempted on a disposed agent/service */\nexport class DisposedError extends AgentSDKError {\n constructor(entity: string) {\n super(`${entity} has been disposed and cannot be used.`);\n this.name = \"DisposedError\";\n }\n}\n\n/** Thrown when a backend is not found in the registry */\nexport class BackendNotFoundError extends AgentSDKError {\n constructor(backend: string) {\n super(\n `Unknown backend: \"${backend}\". ` +\n `Built-in: copilot, claude, vercel-ai. ` +\n `Custom: use registerBackend() first.`,\n );\n this.name = \"BackendNotFoundError\";\n }\n}\n\n/** Thrown when a backend is already registered */\nexport class BackendAlreadyRegisteredError extends AgentSDKError {\n constructor(backend: string) {\n super(`Backend \"${backend}\" is already registered. Use a different name or unregister first.`);\n this.name = \"BackendAlreadyRegisteredError\";\n }\n}\n\n/** Thrown when subprocess management fails */\nexport class SubprocessError extends AgentSDKError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"SubprocessError\";\n }\n}\n\n/** Thrown when a required peer dependency is not installed */\nexport class DependencyError extends AgentSDKError {\n public readonly packageName: string;\n\n constructor(packageName: string) {\n super(`${packageName} is not installed. Install it: npm install ${packageName}`);\n this.name = \"DependencyError\";\n this.packageName = packageName;\n }\n}\n\n/** Thrown when an agent run is aborted */\nexport class AbortError extends AgentSDKError {\n constructor() {\n super(\"Agent run was aborted.\");\n this.name = \"AbortError\";\n }\n}\n\n/** Thrown when a tool execution fails */\nexport class ToolExecutionError extends AgentSDKError {\n public readonly toolName: string;\n\n constructor(toolName: string, message: string, options?: ErrorOptions) {\n super(`Tool \"${toolName}\" failed: ${message}`, options);\n this.name = \"ToolExecutionError\";\n this.toolName = toolName;\n }\n}\n\n/** Thrown when structured output parsing fails */\nexport class StructuredOutputError extends AgentSDKError {\n constructor(message: string, options?: ErrorOptions) {\n super(`Structured output error: ${message}`, options);\n this.name = \"StructuredOutputError\";\n }\n}\n","/**\n * @witqq/agent-sdk/chat/storage\n *\n * Generic storage adapter layer with pluggable backends.\n * Provides CRUD operations for any data type via `IStorageAdapter<T>`.\n * Implementations: `InMemoryStorage` (Map-based) and `FileStorage` (JSON files).\n */\n\nimport { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { AgentSDKError } from \"../errors.js\";\n\n// ─── Storage Errors ────────────────────────────────────────────\n\n/**\n * Error thrown by storage operations.\n *\n * @example\n * ```typescript\n * try {\n * await store.get(\"missing-id\");\n * } catch (e) {\n * if (e instanceof StorageError && e.code === \"NOT_FOUND\") {\n * // handle missing item\n * }\n * }\n * ```\n */\nexport class StorageError extends AgentSDKError {\n /** Machine-readable error code */\n readonly code: StorageErrorCode;\n\n constructor(message: string, code: StorageErrorCode) {\n super(message);\n this.name = \"StorageError\";\n this.code = code;\n }\n}\n\n/** Possible storage error codes */\nexport type StorageErrorCode =\n | \"NOT_FOUND\"\n | \"DUPLICATE_KEY\"\n | \"IO_ERROR\"\n | \"SERIALIZATION_ERROR\";\n\n// ─── Storage Adapter Interface ─────────────────────────────────\n\n/**\n * Options for listing stored items.\n *\n * @typeParam T - The type of stored items\n */\nexport interface ListOptions<T> {\n /** Filter predicate — return `true` to include the item */\n filter?: (item: T) => boolean;\n /** Sort comparator — standard Array.sort semantics */\n sort?: (a: T, b: T) => number;\n /** Maximum number of items to return */\n limit?: number;\n /** Number of items to skip (for pagination) */\n offset?: number;\n}\n\n/**\n * Generic storage adapter for CRUD operations on any data type.\n * Items are identified by a string key.\n *\n * @typeParam T - The type of stored items\n *\n * @example\n * ```typescript\n * const store: IStorageAdapter<{ name: string }> = new InMemoryStorage();\n * await store.create(\"key1\", { name: \"Alice\" });\n * const item = await store.get(\"key1\"); // { name: \"Alice\" }\n * ```\n */\nexport interface IStorageAdapter<T> {\n /**\n * Retrieve an item by key.\n * @param key - Unique identifier\n * @returns The item, or `null` if not found\n */\n get(key: string): Promise<T | null>;\n\n /**\n * List items with optional filtering, sorting, and pagination.\n * @param options - Filter, sort, limit, offset options\n * @returns Array of matching items\n */\n list(options?: ListOptions<T>): Promise<T[]>;\n\n /**\n * Create a new item. Throws `StorageError` with code `DUPLICATE_KEY` if key exists.\n * @param key - Unique identifier\n * @param item - Data to store\n */\n create(key: string, item: T): Promise<void>;\n\n /**\n * Update an existing item. Throws `StorageError` with code `NOT_FOUND` if key missing.\n * @param key - Unique identifier\n * @param item - Updated data\n */\n update(key: string, item: T): Promise<void>;\n\n /**\n * Delete an item by key. Throws `StorageError` with code `NOT_FOUND` if key missing.\n * @param key - Unique identifier\n */\n delete(key: string): Promise<void>;\n\n /**\n * Check whether a key exists.\n * @param key - Unique identifier\n * @returns `true` if key exists\n */\n has(key: string): Promise<boolean>;\n\n /**\n * Return the number of stored items.\n * @returns Count of items\n */\n count(): Promise<number>;\n\n /**\n * Remove all items from storage.\n */\n clear(): Promise<void>;\n}\n\n// ─── InMemoryStorage ───────────────────────────────────────────\n\n/**\n * In-memory storage adapter backed by a `Map`.\n * Suitable for development, testing, and short-lived processes.\n * Data is lost when the process exits.\n *\n * @typeParam T - The type of stored items\n *\n * @example\n * ```typescript\n * const store = new InMemoryStorage<{ name: string }>();\n * await store.create(\"k1\", { name: \"Alice\" });\n * await store.create(\"k2\", { name: \"Bob\" });\n * const items = await store.list({ filter: i => i.name.startsWith(\"A\") });\n * // [{ name: \"Alice\" }]\n * ```\n */\nexport class InMemoryStorage<T> implements IStorageAdapter<T> {\n private readonly data = new Map<string, T>();\n\n /** @inheritdoc */\n async get(key: string): Promise<T | null> {\n const item = this.data.get(key);\n return item !== undefined ? structuredClone(item) : null;\n }\n\n /** @inheritdoc */\n async list(options?: ListOptions<T>): Promise<T[]> {\n let items = Array.from(this.data.values()).map((item) => structuredClone(item));\n\n if (options?.filter) {\n items = items.filter(options.filter);\n }\n if (options?.sort) {\n items.sort(options.sort);\n }\n if (options?.offset !== undefined) {\n items = items.slice(options.offset);\n }\n if (options?.limit !== undefined) {\n items = items.slice(0, options.limit);\n }\n\n return items;\n }\n\n /** @inheritdoc */\n async create(key: string, item: T): Promise<void> {\n if (this.data.has(key)) {\n throw new StorageError(\n `Item with key \"${key}\" already exists`,\n \"DUPLICATE_KEY\",\n );\n }\n this.data.set(key, structuredClone(item));\n }\n\n /** @inheritdoc */\n async update(key: string, item: T): Promise<void> {\n if (!this.data.has(key)) {\n throw new StorageError(\n `Item with key \"${key}\" not found`,\n \"NOT_FOUND\",\n );\n }\n this.data.set(key, structuredClone(item));\n }\n\n /** @inheritdoc */\n async delete(key: string): Promise<void> {\n if (!this.data.has(key)) {\n throw new StorageError(\n `Item with key \"${key}\" not found`,\n \"NOT_FOUND\",\n );\n }\n this.data.delete(key);\n }\n\n /** @inheritdoc */\n async has(key: string): Promise<boolean> {\n return this.data.has(key);\n }\n\n /** @inheritdoc */\n async count(): Promise<number> {\n return this.data.size;\n }\n\n /** @inheritdoc */\n async clear(): Promise<void> {\n this.data.clear();\n }\n}\n\n// ─── FileStorage ───────────────────────────────────────────────\n\n/**\n * Options for configuring `FileStorage`.\n */\nexport interface FileStorageOptions {\n /** Directory path where JSON files are stored */\n directory: string;\n /** File extension (default: `.json`) */\n extension?: string;\n}\n\n/**\n * File-based storage adapter that persists each item as a JSON file.\n * Suitable for local applications, CLI tools, and development.\n * Creates the storage directory if it doesn't exist.\n *\n * @typeParam T - The type of stored items (must be JSON-serializable)\n *\n * @example\n * ```typescript\n * const store = new FileStorage<ChatSession>({\n * directory: \"./data/sessions\",\n * });\n * await store.create(\"session-1\", mySession);\n * ```\n */\nexport class FileStorage<T> implements IStorageAdapter<T> {\n private readonly directory: string;\n private readonly extension: string;\n\n constructor(options: FileStorageOptions) {\n this.directory = options.directory;\n this.extension = options.extension ?? \".json\";\n this.ensureDirectory();\n }\n\n /** @inheritdoc */\n async get(key: string): Promise<T | null> {\n const filePath = this.keyToPath(key);\n if (!existsSync(filePath)) {\n return null;\n }\n return this.readFile(filePath);\n }\n\n /** @inheritdoc */\n async list(options?: ListOptions<T>): Promise<T[]> {\n this.ensureDirectory();\n const files = readdirSync(this.directory).filter((f) =>\n f.endsWith(this.extension),\n );\n\n let items: T[] = [];\n for (const file of files) {\n const item = this.readFile(join(this.directory, file));\n items.push(item);\n }\n\n if (options?.filter) {\n items = items.filter(options.filter);\n }\n if (options?.sort) {\n items.sort(options.sort);\n }\n if (options?.offset !== undefined) {\n items = items.slice(options.offset);\n }\n if (options?.limit !== undefined) {\n items = items.slice(0, options.limit);\n }\n\n return items;\n }\n\n /** @inheritdoc */\n async create(key: string, item: T): Promise<void> {\n const filePath = this.keyToPath(key);\n if (existsSync(filePath)) {\n throw new StorageError(\n `Item with key \"${key}\" already exists`,\n \"DUPLICATE_KEY\",\n );\n }\n this.writeFile(filePath, item);\n }\n\n /** @inheritdoc */\n async update(key: string, item: T): Promise<void> {\n const filePath = this.keyToPath(key);\n if (!existsSync(filePath)) {\n throw new StorageError(\n `Item with key \"${key}\" not found`,\n \"NOT_FOUND\",\n );\n }\n this.writeFile(filePath, item);\n }\n\n /** @inheritdoc */\n async delete(key: string): Promise<void> {\n const filePath = this.keyToPath(key);\n if (!existsSync(filePath)) {\n throw new StorageError(\n `Item with key \"${key}\" not found`,\n \"NOT_FOUND\",\n );\n }\n unlinkSync(filePath);\n }\n\n /** @inheritdoc */\n async has(key: string): Promise<boolean> {\n return existsSync(this.keyToPath(key));\n }\n\n /** @inheritdoc */\n async count(): Promise<number> {\n this.ensureDirectory();\n return readdirSync(this.directory).filter((f) =>\n f.endsWith(this.extension),\n ).length;\n }\n\n /** @inheritdoc */\n async clear(): Promise<void> {\n this.ensureDirectory();\n const files = readdirSync(this.directory).filter((f) =>\n f.endsWith(this.extension),\n );\n for (const file of files) {\n unlinkSync(join(this.directory, file));\n }\n }\n\n private keyToPath(key: string): string {\n const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, (c) =>\n \"%\" + c.charCodeAt(0).toString(16).padStart(2, \"0\"),\n );\n return join(this.directory, `${safeKey}${this.extension}`);\n }\n\n private ensureDirectory(): void {\n if (!existsSync(this.directory)) {\n mkdirSync(this.directory, { recursive: true });\n }\n }\n\n private readFile(filePath: string): T {\n try {\n const content = readFileSync(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new StorageError(\n `Failed to parse file: ${filePath}`,\n \"SERIALIZATION_ERROR\",\n );\n }\n throw new StorageError(\n `Failed to read file: ${filePath}`,\n \"IO_ERROR\",\n );\n }\n }\n\n private writeFile(filePath: string, item: T): void {\n try {\n const content = JSON.stringify(item, null, 2);\n writeFileSync(filePath, content, \"utf-8\");\n } catch {\n throw new StorageError(\n `Failed to write file: ${filePath}`,\n \"IO_ERROR\",\n );\n }\n }\n}\n","/**\n * @witqq/agent-sdk/chat/sessions\n *\n * Session store layer wrapping generic storage adapters.\n * Provides session-specific operations: message management,\n * paginated retrieval, search, and session lifecycle.\n */\n\nimport type {\n ChatSession,\n ChatMessage,\n ChatId,\n ChatSessionConfig,\n} from \"./core.js\";\nimport { createChatId } from \"./core.js\";\nimport type { IStorageAdapter, ListOptions } from \"./storage.js\";\nimport { InMemoryStorage, FileStorage, StorageError } from \"./storage.js\";\n\n// ─── Session Store Interface ───────────────────────────────────\n\n/** Options for creating a new session */\nexport interface CreateSessionOptions<TCustom extends Record<string, unknown> = Record<string, unknown>> {\n /** Session title (defaults to \"Untitled\") */\n title?: string;\n /** Session configuration (optional — runtime defaults used when omitted) */\n config?: Partial<ChatSessionConfig>;\n /** Initial tags */\n tags?: string[];\n /** Custom metadata */\n custom?: TCustom;\n}\n\n/** Paginated result of messages */\nexport interface PaginatedMessages {\n /** Messages in this page */\n messages: ChatMessage[];\n /** Total number of messages in session */\n total: number;\n /** Whether there are more messages after this page */\n hasMore: boolean;\n}\n\n/** Options for listing sessions */\nexport interface SessionListOptions {\n /** Filter predicate */\n filter?: (session: ChatSession) => boolean;\n /** Sort comparator */\n sort?: (a: ChatSession, b: ChatSession) => number;\n /** Maximum number of sessions to return */\n limit?: number;\n /** Number of sessions to skip */\n offset?: number;\n}\n\n/** Search options for finding sessions */\nexport interface SessionSearchOptions {\n /** Text query to match against title and message content */\n query: string;\n /** Maximum results (default: 20) */\n limit?: number;\n}\n\n/**\n * Session store interface for managing chat sessions.\n * Wraps a storage adapter with session-specific operations.\n *\n * @example\n * ```typescript\n * const store = new InMemorySessionStore();\n * const session = await store.createSession({ config: { model: \"gpt-4\", backend: \"vercel-ai\" } });\n * await store.appendMessage(session.id, message);\n * const page = await store.loadMessages(session.id, { limit: 20, offset: 0 });\n * ```\n */\nexport interface IChatSessionStore {\n /**\n * Create a new session with defaults.\n * @param options - Session creation options\n * @returns The created session\n */\n createSession(options: CreateSessionOptions): Promise<ChatSession>;\n\n /**\n * Retrieve a session by ID.\n * @param id - Session ID\n * @returns The session, or `null` if not found\n */\n getSession(id: ChatId): Promise<ChatSession | null>;\n\n /**\n * List sessions with optional filtering, sorting, and pagination.\n * @param options - List options\n * @returns Array of sessions\n */\n listSessions(options?: SessionListOptions): Promise<ChatSession[]>;\n\n /**\n * Update session title.\n * @param id - Session ID\n * @param title - New title\n * @throws {StorageError} with code `NOT_FOUND` if session doesn't exist\n */\n updateTitle(id: ChatId, title: string): Promise<void>;\n\n /**\n * Update session configuration.\n * @param id - Session ID\n * @param config - Partial config to merge\n * @throws {StorageError} with code `NOT_FOUND` if session doesn't exist\n */\n updateConfig(\n id: ChatId,\n config: Partial<ChatSessionConfig>,\n ): Promise<void>;\n\n /**\n * Delete a session by ID.\n * @param id - Session ID\n * @throws {StorageError} with code `NOT_FOUND` if session doesn't exist\n */\n deleteSession(id: ChatId): Promise<void>;\n\n /**\n * Append a single message to a session.\n * Updates session metadata (messageCount, updatedAt).\n * @param sessionId - Session ID\n * @param message - Message to append\n * @throws {StorageError} with code `NOT_FOUND` if session doesn't exist\n */\n appendMessage(sessionId: ChatId, message: ChatMessage): Promise<void>;\n\n /**\n * Append multiple messages to a session in bulk.\n * No-op if messages array is empty.\n * @param sessionId - Session ID\n * @param messages - Messages to append\n * @throws {StorageError} with code `NOT_FOUND` if session doesn't exist\n */\n saveMessages(sessionId: ChatId, messages: ChatMessage[]): Promise<void>;\n\n /**\n * Get paginated messages from a session.\n * @param sessionId - Session ID\n * @param options - Pagination options (limit, offset)\n * @returns Paginated messages result\n * @throws {StorageError} with code `NOT_FOUND` if session doesn't exist\n */\n loadMessages(\n sessionId: ChatId,\n options?: { limit?: number; offset?: number },\n ): Promise<PaginatedMessages>;\n\n /**\n * Archive a session (set status to \"archived\").\n * @param id - Session ID\n * @throws {StorageError} with code `NOT_FOUND` if session doesn't exist\n */\n archiveSession(id: ChatId): Promise<void>;\n\n /**\n * Unarchive a session (set status back to \"active\").\n * @param id - Session ID\n * @throws {StorageError} with code `NOT_FOUND` if session doesn't exist\n */\n unarchiveSession(id: ChatId): Promise<void>;\n\n /**\n * Search sessions by title and message content.\n * Case-insensitive substring match.\n * @param options - Search query and limit\n * @returns Matching sessions (without full message content)\n */\n searchSessions(options: SessionSearchOptions): Promise<ChatSession[]>;\n\n /**\n * Return the number of stored sessions.\n */\n count(): Promise<number>;\n\n /**\n * Remove all sessions.\n */\n clear(): Promise<void>;\n\n // ── Deprecated Aliases ──────────────────────────────────────\n\n /**\n * @deprecated Use `appendMessage()` instead. Will be removed in next major.\n */\n addMessage(sessionId: ChatId, message: ChatMessage): Promise<void>;\n\n /**\n * @deprecated Use `loadMessages()` instead. Will be removed in next major.\n */\n getMessages(\n sessionId: ChatId,\n options?: { limit?: number; offset?: number },\n ): Promise<PaginatedMessages>;\n}\n\n// ─── Base Session Store ────────────────────────────────────────\n\n/**\n * Base session store implementation backed by any `IStorageAdapter<ChatSession>`.\n * Handles all session-specific logic; subclasses only need to provide the adapter.\n */\nclass BaseSessionStore implements IChatSessionStore {\n constructor(protected readonly adapter: IStorageAdapter<ChatSession>) {}\n\n async createSession(options: CreateSessionOptions): Promise<ChatSession> {\n const now = new Date().toISOString();\n const id = createChatId();\n const session: ChatSession = {\n id,\n title: options.title ?? \"Untitled\",\n messages: [],\n config: {\n model: options.config?.model ?? \"\",\n backend: options.config?.backend ?? \"\",\n ...options.config,\n },\n metadata: {\n messageCount: 0,\n totalTokens: 0,\n tags: options.tags ? [...options.tags] : undefined,\n custom: options.custom ? { ...options.custom } : undefined,\n },\n status: \"active\" as const,\n createdAt: now,\n updatedAt: now,\n };\n await this.adapter.create(id, session);\n return structuredClone(session);\n }\n\n async getSession(id: ChatId): Promise<ChatSession | null> {\n return this.adapter.get(id);\n }\n\n async listSessions(options?: SessionListOptions): Promise<ChatSession[]> {\n return this.adapter.list(options as ListOptions<ChatSession>);\n }\n\n async updateTitle(id: ChatId, title: string): Promise<void> {\n const session = await this.adapter.get(id);\n if (!session) {\n throw new StorageError(`Session \"${id}\" not found`, \"NOT_FOUND\");\n }\n session.title = title;\n session.updatedAt = new Date().toISOString();\n await this.adapter.update(id, session);\n }\n\n async updateConfig(\n id: ChatId,\n config: Partial<ChatSessionConfig>,\n ): Promise<void> {\n const session = await this.adapter.get(id);\n if (!session) {\n throw new StorageError(`Session \"${id}\" not found`, \"NOT_FOUND\");\n }\n session.config = { ...session.config, ...config };\n session.updatedAt = new Date().toISOString();\n await this.adapter.update(id, session);\n }\n\n async deleteSession(id: ChatId): Promise<void> {\n await this.adapter.delete(id);\n }\n\n async appendMessage(sessionId: ChatId, message: ChatMessage): Promise<void> {\n const session = await this.adapter.get(sessionId);\n if (!session) {\n throw new StorageError(`Session \"${sessionId}\" not found`, \"NOT_FOUND\");\n }\n session.messages.push(structuredClone(message));\n session.metadata.messageCount = session.messages.length;\n session.updatedAt = new Date().toISOString();\n await this.adapter.update(sessionId, session);\n }\n\n async saveMessages(sessionId: ChatId, messages: ChatMessage[]): Promise<void> {\n if (messages.length === 0) return;\n const session = await this.adapter.get(sessionId);\n if (!session) {\n throw new StorageError(`Session \"${sessionId}\" not found`, \"NOT_FOUND\");\n }\n for (const msg of messages) {\n session.messages.push(structuredClone(msg));\n }\n session.metadata.messageCount = session.messages.length;\n session.updatedAt = new Date().toISOString();\n await this.adapter.update(sessionId, session);\n }\n\n async loadMessages(\n sessionId: ChatId,\n options?: { limit?: number; offset?: number },\n ): Promise<PaginatedMessages> {\n const session = await this.adapter.get(sessionId);\n if (!session) {\n throw new StorageError(`Session \"${sessionId}\" not found`, \"NOT_FOUND\");\n }\n const total = session.messages.length;\n const offset = options?.offset ?? 0;\n const limit = options?.limit ?? total;\n const messages = session.messages.slice(offset, offset + limit);\n return {\n messages: structuredClone(messages),\n total,\n hasMore: offset + limit < total,\n };\n }\n\n async archiveSession(id: ChatId): Promise<void> {\n const session = await this.adapter.get(id);\n if (!session) {\n throw new StorageError(`Session \"${id}\" not found`, \"NOT_FOUND\");\n }\n session.status = \"archived\";\n session.updatedAt = new Date().toISOString();\n await this.adapter.update(id, session);\n }\n\n async unarchiveSession(id: ChatId): Promise<void> {\n const session = await this.adapter.get(id);\n if (!session) {\n throw new StorageError(`Session \"${id}\" not found`, \"NOT_FOUND\");\n }\n session.status = \"active\";\n session.updatedAt = new Date().toISOString();\n await this.adapter.update(id, session);\n }\n\n async searchSessions(\n options: SessionSearchOptions,\n ): Promise<ChatSession[]> {\n const query = options.query.toLowerCase();\n const limit = options.limit ?? 20;\n return this.adapter.list({\n filter: (session) => {\n if (session.title?.toLowerCase().includes(query)) return true;\n return session.messages.some((msg) => {\n return msg.parts.some(\n (part) =>\n part.type === \"text\" &&\n part.text.toLowerCase().includes(query),\n );\n });\n },\n limit,\n });\n }\n\n async count(): Promise<number> {\n return this.adapter.count();\n }\n\n async clear(): Promise<void> {\n return this.adapter.clear();\n }\n\n // ── Deprecated Aliases ──────────────────────────────────────\n\n /** @deprecated Use `appendMessage()` instead */\n async addMessage(sessionId: ChatId, message: ChatMessage): Promise<void> {\n return this.appendMessage(sessionId, message);\n }\n\n /** @deprecated Use `loadMessages()` instead */\n async getMessages(\n sessionId: ChatId,\n options?: { limit?: number; offset?: number },\n ): Promise<PaginatedMessages> {\n return this.loadMessages(sessionId, options);\n }\n}\n\n// ─── InMemorySessionStore ──────────────────────────────────────\n\n/**\n * In-memory session store. Data is lost when the process exits.\n * Uses `InMemoryStorage` internally.\n *\n * @example\n * ```typescript\n * const store = new InMemorySessionStore();\n * const session = await store.createSession({\n * config: { model: \"gpt-4\", backend: \"vercel-ai\" },\n * });\n * ```\n */\nexport class InMemorySessionStore extends BaseSessionStore {\n constructor() {\n super(new InMemoryStorage<ChatSession>());\n }\n}\n\n// ─── FileSessionStore ──────────────────────────────────────────\n\n/** Configuration for FileSessionStore */\nexport interface FileSessionStoreOptions {\n /** Directory to store session JSON files */\n directory: string;\n}\n\n/**\n * File-based session store. Each session is a JSON file on disk.\n * Uses `FileStorage` internally.\n *\n * @example\n * ```typescript\n * const store = new FileSessionStore({ directory: \"./data/sessions\" });\n * const session = await store.createSession({\n * config: { model: \"claude-3\", backend: \"claude\" },\n * });\n * ```\n */\nexport class FileSessionStore extends BaseSessionStore {\n constructor(options: FileSessionStoreOptions) {\n super(new FileStorage<ChatSession>({ directory: options.directory }));\n }\n}\n\n// Re-export StorageError for consumers that only import from chat/sessions\nexport { StorageError } from \"./storage.js\";\n"]}
1
+ {"version":3,"sources":["../../src/chat/types.ts","../../src/errors.ts","../../src/chat/storage.ts","../../src/chat/sessions.ts"],"names":[],"mappings":"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","file":"sessions.js","sourcesContent":["/**\n * @witqq/agent-sdk — Chat domain types\n *\n * All type definitions and interfaces for the chat layer.\n * Pure types + ChatId generation (tightly coupled to branded type).\n */\n\nimport type { UsageData, ToolDefinition, ErrorCode } from \"../types.js\";\nimport type { AuthToken } from \"../auth/types.js\";\n\n// ─── Unique ID ─────────────────────────────────────────────────\n\n/** Branded type for unique identifiers */\nexport type ChatId = string & { readonly __brand: \"ChatId\" };\n\n/**\n * Generate a new unique ChatId (crypto.randomUUID-based)\n * @returns Branded ChatId string\n */\nexport function createChatId(): ChatId {\n return crypto.randomUUID() as ChatId;\n}\n\n/** UUID v4 pattern for ChatId validation */\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n/**\n * Cast a string to ChatId with UUID format validation.\n * Use this instead of manual `as ChatId` type assertions.\n *\n * @param value - String to validate and cast\n * @returns Branded ChatId\n * @throws {TypeError} If value is not a valid UUID v4 format\n */\nexport function toChatId(value: string): ChatId {\n if (!UUID_RE.test(value)) {\n throw new TypeError(`Invalid ChatId: \"${value}\" is not a valid UUID`);\n }\n return value as ChatId;\n}\n\n/**\n * Accepts either a plain string or branded ChatId for API convenience.\n * Use this in public API signatures so consumers don't need `as ChatId` casts.\n */\nexport type ChatIdLike = string | ChatId;\n\n// ─── Status Types ──────────────────────────────────────────────\n\n/** Lifecycle status of a message part (text, reasoning, etc.) */\nexport type PartStatus = \"pending\" | \"streaming\" | \"complete\" | \"error\";\n/** Lifecycle status of a tool call within a message */\nexport type ToolCallStatus = \"pending\" | \"running\" | \"requires_approval\" | \"complete\" | \"error\" | \"denied\";\n/** Lifecycle status of an entire message */\nexport type MessageStatus = \"pending\" | \"streaming\" | \"complete\" | \"error\" | \"cancelled\";\n/** Lifecycle status of a chat session */\nexport type SessionStatus = \"active\";\n/** Lifecycle status of the chat runtime */\nexport type RuntimeStatus = \"idle\" | \"streaming\" | \"error\" | \"disposed\";\n\n// ─── Message Parts (union) ─────────────────────────────────────\n\n/** Plain text content part */\nexport interface TextPart { type: \"text\"; text: string; status: PartStatus; }\n/** Model reasoning/thinking content part */\nexport interface ReasoningPart { type: \"reasoning\"; text: string; status: PartStatus; }\n/** Tool invocation part with call ID, arguments, optional result */\nexport interface ToolCallPart { type: \"tool_call\"; toolCallId: string; name: string; args: unknown; result?: unknown; status: ToolCallStatus; error?: string; }\n/** Source reference part (URL citation) */\nexport interface SourcePart { type: \"source\"; url: string; title?: string; status: PartStatus; }\n/** File attachment part (base64-encoded data) */\nexport interface FilePart { type: \"file\"; name: string; mimeType: string; data: string; status: PartStatus; }\n/** Union of all message part types */\nexport type MessagePart = TextPart | ReasoningPart | ToolCallPart | SourcePart | FilePart;\n\n// ─── Chat Message ──────────────────────────────────────────────\n\n/** Role of message author */\nexport type ChatRole = \"user\" | \"assistant\" | \"system\";\n\n/** Metadata attached to messages — useful preset for the TMetadata generic */\nexport interface ChatMessageMetadata {\n model?: string;\n backend?: string;\n usage?: UsageData;\n isSummary?: boolean;\n estimatedTokens?: number;\n custom?: Record<string, unknown>;\n}\n\n/** Message status */\nexport type ChatMessageStatus = MessageStatus;\n\n/** A single chat message — the fundamental unit of conversation */\nexport interface ChatMessage<TMetadata = unknown> {\n id: ChatId;\n role: ChatRole;\n parts: MessagePart[];\n metadata?: TMetadata;\n createdAt: string;\n updatedAt?: string;\n status: MessageStatus;\n}\n\n// ─── Supporting Types ──────────────────────────────────────────\n\n// ─── Chat Session ──────────────────────────────────────────────\n\n/** Session configuration snapshot */\nexport interface ChatSessionConfig {\n model: string;\n backend: string;\n systemPrompt?: string;\n temperature?: number;\n maxTokens?: number;\n}\n\n/**\n * Session metadata tracking usage statistics and custom extensions.\n *\n * Updated automatically by session stores on each `addMessage()` call.\n * The generic `TCustom` parameter allows type-safe application-specific\n * metadata via the `custom` field.\n *\n * @typeParam TCustom - Shape of the `custom` field (defaults to `Record<string, unknown>`)\n */\nexport interface ChatSessionMetadata<TCustom extends Record<string, unknown> = Record<string, unknown>> {\n /** Number of messages in the session (updated by session store) */\n messageCount: number;\n /** Total token count across all messages in the session */\n totalTokens: number;\n /** Optional tags for session categorization and filtering */\n tags?: string[];\n /** Application-specific metadata — typed via the TCustom generic parameter */\n custom?: TCustom;\n}\n\n/** Chat session — a conversation with ordered messages (pure serializable data) */\nexport interface ChatSession<TCustom extends Record<string, unknown> = Record<string, unknown>> {\n id: ChatId;\n title?: string;\n messages: ChatMessage[];\n config: ChatSessionConfig;\n metadata: ChatSessionMetadata<TCustom>;\n status: SessionStatus;\n createdAt: string;\n updatedAt: string;\n backendSessionId?: string;\n}\n\n/**\n * Reactive wrapper around ChatSession — provides subscribe/getSnapshot for\n * React useSyncExternalStore integration and lastMessage convenience getter.\n * Session stores may optionally return ObservableSession instances.\n */\nexport interface ObservableSession<TCustom extends Record<string, unknown> = Record<string, unknown>>\n extends ChatSession<TCustom> {\n /** Subscribe to session changes (for React useSyncExternalStore) */\n subscribe(callback: () => void): () => void;\n /** Get immutable snapshot of session state (for React useSyncExternalStore) */\n getSnapshot(): ChatSession<TCustom>;\n /** Last message in the session */\n readonly lastMessage: ChatMessage | undefined;\n}\n\n/** Lightweight session info for listing (without full message array) */\nexport interface SessionInfo {\n id: ChatId;\n title?: string;\n status: SessionStatus;\n messageCount: number;\n lastMessage?: ChatMessage;\n createdAt: string;\n updatedAt: string;\n}\n\n// ─── Chat Events ───────────────────────────────────────────────\n\n/** Events emitted during chat operation */\nexport type ChatEvent =\n | { type: \"message:start\"; messageId: ChatId; role: ChatRole }\n | { type: \"message:delta\"; messageId: ChatId; text: string }\n | { type: \"message:complete\"; messageId: ChatId; message: ChatMessage }\n | {\n type: \"tool:start\";\n messageId: ChatId;\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n }\n | {\n type: \"tool:complete\";\n messageId: ChatId;\n toolCallId: string;\n toolName: string;\n result: unknown;\n isError?: boolean;\n }\n | { type: \"thinking:start\"; messageId: ChatId }\n | { type: \"thinking:delta\"; messageId: ChatId; text: string }\n | { type: \"thinking:end\"; messageId: ChatId }\n | {\n type: \"permission:request\";\n messageId: ChatId;\n toolName: string;\n toolArgs: Record<string, unknown>;\n }\n | {\n type: \"permission:response\";\n messageId: ChatId;\n toolName: string;\n allowed: boolean;\n }\n | {\n type: \"usage\";\n promptTokens: number;\n completionTokens: number;\n model?: string;\n }\n | { type: \"session:created\"; sessionId: ChatId }\n | { type: \"session:updated\"; sessionId: ChatId }\n | {\n type: \"error\";\n error: string;\n recoverable: boolean;\n code?: ErrorCode;\n messageId?: ChatId;\n }\n | { type: \"typing:start\" }\n | { type: \"typing:end\" }\n | { type: \"heartbeat\" }\n | { type: \"done\"; finalOutput?: string; finishReason?: string };\n\n/** All possible ChatEvent type strings */\nexport type ChatEventType = ChatEvent[\"type\"];\n\n// ─── Chat Middleware ───────────────────────────────────────────\n\n/** Context passed to ChatMiddleware hooks */\nexport interface ChatMiddlewareContext {\n sessionId: ChatId;\n signal: AbortSignal;\n}\n\n/** Runtime-level middleware for the send/receive lifecycle.\n * Different from EventMiddleware which operates at the event bus level. */\nexport interface ChatMiddleware {\n /** Transform message before sending to backend. Return null to reject the send. */\n onBeforeSend?(message: ChatMessage, context: ChatMiddlewareContext): ChatMessage | null | Promise<ChatMessage | null>;\n /** Transform/intercept stream events */\n onEvent?(event: ChatEvent, context: ChatMiddlewareContext): ChatEvent | null | Promise<ChatEvent | null>;\n /** Transform completed message after receiving from backend */\n onAfterReceive?(message: ChatMessage, context: ChatMiddlewareContext): ChatMessage | Promise<ChatMessage>;\n /** Intercept errors — return null to suppress, return error to propagate */\n onError?(error: Error, context: ChatMiddlewareContext): Error | null | Promise<Error | null>;\n}\n\n// ─── Chat Provider Abstraction ─────────────────────────────────\n\n/** Options for sending a message to a provider */\nexport interface SendMessageOptions {\n signal?: AbortSignal;\n /** Model to use for this request. Required for server-side runtime.send(). */\n model?: string;\n /** Per-call system prompt override (forwarded to the backend agent) */\n systemPrompt?: string;\n context?: Record<string, unknown>;\n /** Additional tools to include in this request */\n tools?: ToolDefinition[];\n}\n\n/** Options for runtime.send() — requires backend routing info */\nexport interface RuntimeSendOptions {\n /** Backend to route this request to (key in backends map) */\n backend: string;\n /** Authentication credentials for the backend factory */\n credentials: AuthToken;\n /** Model to use for this request */\n model: string;\n /** Per-call system prompt override (forwarded to the backend agent) */\n systemPrompt?: string;\n /** Abort signal */\n signal?: AbortSignal;\n /** Request-scoped context */\n context?: Record<string, unknown>;\n /** Additional tools */\n tools?: ToolDefinition[];\n}\n\n/**\n * @deprecated IChatProvider has been inlined into IChatBackend.\n * Import IChatBackend from \"@witqq/agent-sdk/chat/backends\" instead.\n * Kept as type alias for backward compatibility.\n */\nexport type IChatProvider = import(\"./backends/types.js\").IChatBackend;\n\n// ─── Factory Functions ─────────────────────────────────────────\n\n/**\n * Create a simple text ChatMessage.\n *\n * @param text - Message text content\n * @param role - Message role (default: \"user\")\n * @returns A complete ChatMessage with a single TextPart\n */\nexport function createTextMessage(text: string, role: ChatRole = \"user\"): ChatMessage {\n return {\n id: createChatId(),\n role,\n parts: [{ type: \"text\", text, status: \"complete\" }],\n createdAt: new Date().toISOString(),\n status: \"complete\",\n };\n}\n\n/** Type guard: checks if a session has reactive API (subscribe/getSnapshot) */\nexport function isObservableSession<TCustom extends Record<string, unknown> = Record<string, unknown>>(\n session: ChatSession<TCustom>,\n): session is ObservableSession<TCustom> {\n return \"subscribe\" in session && typeof (session as ObservableSession<TCustom>).subscribe === \"function\"\n && \"getSnapshot\" in session && typeof (session as ObservableSession<TCustom>).getSnapshot === \"function\";\n}\n","import { ErrorCode } from \"./types/errors.js\";\n\n/** Options for constructing an AgentSDKError */\nexport interface AgentSDKErrorOptions extends ErrorOptions {\n /** Machine-readable error code */\n code?: string;\n /** Whether this error is retryable (default: false) */\n retryable?: boolean;\n /** HTTP status code hint (e.g. 401, 429, 500) */\n httpStatus?: number;\n}\n\n/** Base error class for agent-sdk.\n *\n * Use `AgentSDKError.is(err)` for reliable cross-module `instanceof` checks\n * (works across separately bundled entry points where `instanceof` may fail). */\nexport class AgentSDKError extends Error {\n /** @internal Marker for cross-bundle identity checks */\n readonly _agentSDKError = true as const;\n /** Machine-readable error code. Prefer values from the ErrorCode enum. */\n readonly code?: string;\n /** Whether this error is safe to retry */\n readonly retryable: boolean;\n /** HTTP status code hint for error classification */\n readonly httpStatus?: number;\n\n constructor(message: string, options?: AgentSDKErrorOptions) {\n super(message, options);\n this.name = \"AgentSDKError\";\n this.code = options?.code;\n this.retryable = options?.retryable ?? false;\n this.httpStatus = options?.httpStatus;\n }\n\n /** Check if an error is an AgentSDKError (works across bundled copies) */\n static is(error: unknown): error is AgentSDKError {\n return (\n error instanceof Error &&\n \"_agentSDKError\" in error &&\n (error as AgentSDKError)._agentSDKError === true\n );\n }\n}\n\n/** Thrown when agent.run() is called while already running (M8 re-entrancy guard) */\nexport class ReentrancyError extends AgentSDKError {\n constructor() {\n super(\"Agent is already running. Await the current run before starting another.\", {\n code: ErrorCode.REENTRANCY,\n });\n this.name = \"ReentrancyError\";\n }\n}\n\n/** Thrown when an operation is attempted on a disposed agent/service */\nexport class DisposedError extends AgentSDKError {\n constructor(entity: string) {\n super(`${entity} has been disposed and cannot be used.`, {\n code: ErrorCode.DISPOSED,\n });\n this.name = \"DisposedError\";\n }\n}\n\n/** Thrown when a backend is not found in the registry */\nexport class BackendNotFoundError extends AgentSDKError {\n constructor(backend: string) {\n super(\n `Unknown backend: \"${backend}\". ` +\n `Built-in: copilot, claude, vercel-ai. ` +\n `Custom: use registerBackend() first.`,\n { code: ErrorCode.BACKEND_NOT_INSTALLED },\n );\n this.name = \"BackendNotFoundError\";\n }\n}\n\n/** Thrown when a backend is already registered */\nexport class BackendAlreadyRegisteredError extends AgentSDKError {\n constructor(backend: string) {\n super(`Backend \"${backend}\" is already registered. Use a different name or unregister first.`);\n this.name = \"BackendAlreadyRegisteredError\";\n }\n}\n\n/** Thrown when subprocess management fails */\nexport class SubprocessError extends AgentSDKError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, { ...options, code: ErrorCode.DEPENDENCY_MISSING });\n this.name = \"SubprocessError\";\n }\n}\n\n/** Thrown when a required peer dependency is not installed */\nexport class DependencyError extends AgentSDKError {\n public readonly packageName: string;\n\n constructor(packageName: string) {\n super(`${packageName} is not installed. Install it: npm install ${packageName}`, {\n code: ErrorCode.DEPENDENCY_MISSING,\n });\n this.name = \"DependencyError\";\n this.packageName = packageName;\n }\n}\n\n/** Thrown when an agent run is aborted */\nexport class AbortError extends AgentSDKError {\n constructor() {\n super(\"Agent run was aborted.\", { code: ErrorCode.ABORTED });\n this.name = \"AbortError\";\n }\n}\n\n/** Thrown when a tool execution fails */\nexport class ToolExecutionError extends AgentSDKError {\n public readonly toolName: string;\n\n constructor(toolName: string, message: string, options?: ErrorOptions) {\n super(`Tool \"${toolName}\" failed: ${message}`, { ...options, code: ErrorCode.TOOL_EXECUTION });\n this.name = \"ToolExecutionError\";\n this.toolName = toolName;\n }\n}\n\n/** Thrown when a stream has no activity within the configured timeout */\nexport class ActivityTimeoutError extends AgentSDKError {\n constructor(timeoutMs: number) {\n super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {\n code: ErrorCode.TIMEOUT,\n retryable: true,\n });\n this.name = \"ActivityTimeoutError\";\n }\n}\n\n/** Thrown when structured output parsing fails */\nexport class StructuredOutputError extends AgentSDKError {\n constructor(message: string, options?: ErrorOptions) {\n super(`Structured output error: ${message}`, { ...options, code: ErrorCode.INVALID_RESPONSE });\n this.name = \"StructuredOutputError\";\n }\n}\n","/**\n * @witqq/agent-sdk/chat/storage\n *\n * Generic storage adapter layer with pluggable backends.\n * Provides CRUD operations for any data type via `IStorageAdapter<T>`.\n * Implementations: `InMemoryStorage` (Map-based) and `FileStorage` (JSON files).\n */\n\nimport { existsSync, mkdirSync } from \"node:fs\";\nimport { access, mkdir, readdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { AgentSDKError } from \"../errors.js\";\nimport { ErrorCode } from \"../types/errors.js\";\n\n// ─── Storage Errors ────────────────────────────────────────────\n\n/**\n * Error thrown by storage operations.\n *\n * @example\n * ```typescript\n * try {\n * await store.get(\"missing-id\");\n * } catch (e) {\n * if (e instanceof StorageError && e.code === ErrorCode.STORAGE_NOT_FOUND) {\n * // handle missing item\n * }\n * }\n * ```\n */\nexport class StorageError extends AgentSDKError {\n /** Machine-readable error code from the unified ErrorCode enum */\n readonly code: StorageErrorCode;\n\n constructor(message: string, code: StorageErrorCode) {\n super(message);\n this.name = \"StorageError\";\n this.code = code;\n }\n}\n\n/** Storage-specific subset of ErrorCode */\nexport type StorageErrorCode =\n | ErrorCode.STORAGE_NOT_FOUND\n | ErrorCode.STORAGE_DUPLICATE_KEY\n | ErrorCode.STORAGE_IO_ERROR\n | ErrorCode.STORAGE_SERIALIZATION_ERROR;\n\n// ─── Storage Adapter Interface ─────────────────────────────────\n\n/**\n * Options for listing stored items.\n *\n * @typeParam T - The type of stored items\n */\nexport interface ListOptions<T> {\n /** Filter predicate — return `true` to include the item */\n filter?: (item: T) => boolean;\n /** Sort comparator — standard Array.sort semantics */\n sort?: (a: T, b: T) => number;\n /** Maximum number of items to return */\n limit?: number;\n /** Number of items to skip (for pagination) */\n offset?: number;\n}\n\n/**\n * Generic storage adapter for CRUD operations on any data type.\n * Items are identified by a string key.\n *\n * @typeParam T - The type of stored items\n *\n * @example\n * ```typescript\n * const store: IStorageAdapter<{ name: string }> = new InMemoryStorage();\n * await store.create(\"key1\", { name: \"Alice\" });\n * const item = await store.get(\"key1\"); // { name: \"Alice\" }\n * ```\n */\nexport interface IStorageAdapter<T> {\n /**\n * Retrieve an item by key.\n * @param key - Unique identifier\n * @returns The item, or `null` if not found\n */\n get(key: string): Promise<T | null>;\n\n /**\n * List items with optional filtering, sorting, and pagination.\n * @param options - Filter, sort, limit, offset options\n * @returns Array of matching items\n */\n list(options?: ListOptions<T>): Promise<T[]>;\n\n /**\n * Create a new item. Throws `StorageError` with code `DUPLICATE_KEY` if key exists.\n * @param key - Unique identifier\n * @param item - Data to store\n */\n create(key: string, item: T): Promise<void>;\n\n /**\n * Update an existing item. Throws `StorageError` with code `NOT_FOUND` if key missing.\n * @param key - Unique identifier\n * @param item - Updated data\n */\n update(key: string, item: T): Promise<void>;\n\n /**\n * Delete an item by key. Throws `StorageError` with code `NOT_FOUND` if key missing.\n * @param key - Unique identifier\n */\n delete(key: string): Promise<void>;\n\n /**\n * Check whether a key exists.\n * @param key - Unique identifier\n * @returns `true` if key exists\n */\n has(key: string): Promise<boolean>;\n\n /**\n * Return the number of stored items.\n * @returns Count of items\n */\n count(): Promise<number>;\n\n /**\n * Remove all items from storage.\n */\n clear(): Promise<void>;\n\n /**\n * Release any resources held by this adapter (DB connections, file handles).\n * Optional — adapters that don't hold resources need not implement this.\n */\n dispose?(): Promise<void>;\n}\n\n// ─── InMemoryStorage ───────────────────────────────────────────\n\n/**\n * In-memory storage adapter backed by a `Map`.\n * Suitable for development, testing, and short-lived processes.\n * Data is lost when the process exits.\n *\n * @typeParam T - The type of stored items\n *\n * @example\n * ```typescript\n * const store = new InMemoryStorage<{ name: string }>();\n * await store.create(\"k1\", { name: \"Alice\" });\n * await store.create(\"k2\", { name: \"Bob\" });\n * const items = await store.list({ filter: i => i.name.startsWith(\"A\") });\n * // [{ name: \"Alice\" }]\n * ```\n */\nexport class InMemoryStorage<T> implements IStorageAdapter<T> {\n private readonly data = new Map<string, T>();\n\n /** @inheritdoc */\n async get(key: string): Promise<T | null> {\n const item = this.data.get(key);\n return item !== undefined ? structuredClone(item) : null;\n }\n\n /** @inheritdoc */\n async list(options?: ListOptions<T>): Promise<T[]> {\n let items = Array.from(this.data.values()).map((item) => structuredClone(item));\n\n if (options?.filter) {\n items = items.filter(options.filter);\n }\n if (options?.sort) {\n items.sort(options.sort);\n }\n if (options?.offset !== undefined) {\n items = items.slice(options.offset);\n }\n if (options?.limit !== undefined) {\n items = items.slice(0, options.limit);\n }\n\n return items;\n }\n\n /** @inheritdoc */\n async create(key: string, item: T): Promise<void> {\n if (this.data.has(key)) {\n throw new StorageError(\n `Item with key \"${key}\" already exists`,\n ErrorCode.STORAGE_DUPLICATE_KEY,\n );\n }\n this.data.set(key, structuredClone(item));\n }\n\n /** @inheritdoc */\n async update(key: string, item: T): Promise<void> {\n if (!this.data.has(key)) {\n throw new StorageError(\n `Item with key \"${key}\" not found`,\n ErrorCode.STORAGE_NOT_FOUND,\n );\n }\n this.data.set(key, structuredClone(item));\n }\n\n /** @inheritdoc */\n async delete(key: string): Promise<void> {\n if (!this.data.has(key)) {\n throw new StorageError(\n `Item with key \"${key}\" not found`,\n ErrorCode.STORAGE_NOT_FOUND,\n );\n }\n this.data.delete(key);\n }\n\n /** @inheritdoc */\n async has(key: string): Promise<boolean> {\n return this.data.has(key);\n }\n\n /** @inheritdoc */\n async count(): Promise<number> {\n return this.data.size;\n }\n\n /** @inheritdoc */\n async clear(): Promise<void> {\n this.data.clear();\n }\n}\n\n// ─── FileStorage ───────────────────────────────────────────────\n\n/**\n * Options for configuring `FileStorage`.\n */\nexport interface FileStorageOptions {\n /** Directory path where JSON files are stored */\n directory: string;\n /** File extension (default: `.json`) */\n extension?: string;\n}\n\n/**\n * File-based storage adapter that persists each item as a JSON file.\n * Suitable for local applications, CLI tools, and development.\n * Creates the storage directory if it doesn't exist.\n *\n * @typeParam T - The type of stored items (must be JSON-serializable)\n *\n * @example\n * ```typescript\n * const store = new FileStorage<ChatSession>({\n * directory: \"./data/sessions\",\n * });\n * await store.create(\"session-1\", mySession);\n * ```\n */\nexport class FileStorage<T> implements IStorageAdapter<T> {\n private readonly directory: string;\n private readonly extension: string;\n\n constructor(options: FileStorageOptions) {\n this.directory = options.directory;\n this.extension = options.extension ?? \".json\";\n this.ensureDirectorySync();\n }\n\n /** @inheritdoc */\n async get(key: string): Promise<T | null> {\n const filePath = this.keyToPath(key);\n if (!(await this.fileExists(filePath))) {\n return null;\n }\n return this.readJsonFile(filePath);\n }\n\n /** @inheritdoc */\n async list(options?: ListOptions<T>): Promise<T[]> {\n await this.ensureDirectoryAsync();\n const files = (await readdir(this.directory)).filter((f) =>\n f.endsWith(this.extension),\n );\n\n let items: T[] = [];\n for (const file of files) {\n const item = await this.readJsonFile(join(this.directory, file));\n items.push(item);\n }\n\n if (options?.filter) {\n items = items.filter(options.filter);\n }\n if (options?.sort) {\n items.sort(options.sort);\n }\n if (options?.offset !== undefined) {\n items = items.slice(options.offset);\n }\n if (options?.limit !== undefined) {\n items = items.slice(0, options.limit);\n }\n\n return items;\n }\n\n /** @inheritdoc */\n async create(key: string, item: T): Promise<void> {\n const filePath = this.keyToPath(key);\n if (await this.fileExists(filePath)) {\n throw new StorageError(\n `Item with key \"${key}\" already exists`,\n ErrorCode.STORAGE_DUPLICATE_KEY,\n );\n }\n await this.writeJsonFile(filePath, item);\n }\n\n /** @inheritdoc */\n async update(key: string, item: T): Promise<void> {\n const filePath = this.keyToPath(key);\n if (!(await this.fileExists(filePath))) {\n throw new StorageError(\n `Item with key \"${key}\" not found`,\n ErrorCode.STORAGE_NOT_FOUND,\n );\n }\n await this.writeJsonFile(filePath, item);\n }\n\n /** @inheritdoc */\n async delete(key: string): Promise<void> {\n const filePath = this.keyToPath(key);\n if (!(await this.fileExists(filePath))) {\n throw new StorageError(\n `Item with key \"${key}\" not found`,\n ErrorCode.STORAGE_NOT_FOUND,\n );\n }\n await unlink(filePath);\n }\n\n /** @inheritdoc */\n async has(key: string): Promise<boolean> {\n return this.fileExists(this.keyToPath(key));\n }\n\n /** @inheritdoc */\n async count(): Promise<number> {\n await this.ensureDirectoryAsync();\n return (await readdir(this.directory)).filter((f) =>\n f.endsWith(this.extension),\n ).length;\n }\n\n /** @inheritdoc */\n async clear(): Promise<void> {\n await this.ensureDirectoryAsync();\n const files = (await readdir(this.directory)).filter((f) =>\n f.endsWith(this.extension),\n );\n for (const file of files) {\n await unlink(join(this.directory, file));\n }\n }\n\n private keyToPath(key: string): string {\n const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, (c) =>\n \"%\" + c.charCodeAt(0).toString(16).padStart(2, \"0\"),\n );\n return join(this.directory, `${safeKey}${this.extension}`);\n }\n\n /** Sync directory init — used only in constructor (one-time). */\n private ensureDirectorySync(): void {\n if (!existsSync(this.directory)) {\n mkdirSync(this.directory, { recursive: true });\n }\n }\n\n /** Async directory init — used in operations. */\n private async ensureDirectoryAsync(): Promise<void> {\n if (!(await this.fileExists(this.directory))) {\n await mkdir(this.directory, { recursive: true });\n }\n }\n\n private async fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n }\n\n private async readJsonFile(filePath: string): Promise<T> {\n try {\n const content = await readFile(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new StorageError(\n `Failed to parse file: ${filePath}`,\n ErrorCode.STORAGE_SERIALIZATION_ERROR,\n );\n }\n throw new StorageError(\n `Failed to read file: ${filePath}`,\n ErrorCode.STORAGE_IO_ERROR,\n );\n }\n }\n\n private async writeJsonFile(filePath: string, item: T): Promise<void> {\n try {\n const content = JSON.stringify(item, null, 2);\n await writeFile(filePath, content, \"utf-8\");\n } catch {\n throw new StorageError(\n `Failed to write file: ${filePath}`,\n ErrorCode.STORAGE_IO_ERROR,\n );\n }\n }\n}\n","/**\n * @witqq/agent-sdk/chat/sessions\n *\n * Session store layer wrapping generic storage adapters.\n * Provides session-specific operations: message management,\n * paginated retrieval, search, and session lifecycle.\n */\n\nimport type {\n ChatSession,\n ChatMessage,\n ChatId,\n ChatSessionConfig,\n} from \"./core.js\";\nimport { createChatId } from \"./core.js\";\nimport type { IStorageAdapter, ListOptions } from \"./storage.js\";\nimport { InMemoryStorage, FileStorage, StorageError } from \"./storage.js\";\nimport { ErrorCode } from \"../types/errors.js\";\n\n// ─── Session Store Interface ───────────────────────────────────\n\n/** Options for creating a new session */\nexport interface CreateSessionOptions<TCustom extends Record<string, unknown> = Record<string, unknown>> {\n /** Session title (defaults to \"Untitled\") */\n title?: string;\n /** Session configuration (optional — runtime defaults used when omitted) */\n config?: Partial<ChatSessionConfig>;\n /** Initial tags */\n tags?: string[];\n /** Custom metadata */\n custom?: TCustom;\n}\n\n/** Paginated result of messages */\nexport interface PaginatedMessages {\n /** Messages in this page */\n messages: ChatMessage[];\n /** Total number of messages in session */\n total: number;\n /** Whether there are more messages after this page */\n hasMore: boolean;\n}\n\n/** Options for listing sessions */\nexport interface SessionListOptions {\n /** Filter predicate */\n filter?: (session: ChatSession) => boolean;\n /** Sort comparator */\n sort?: (a: ChatSession, b: ChatSession) => number;\n /** Maximum number of sessions to return */\n limit?: number;\n /** Number of sessions to skip */\n offset?: number;\n}\n\n/** Search options for finding sessions */\nexport interface SessionSearchOptions {\n /** Text query to match against title and message content */\n query: string;\n /** Maximum results (default: 20) */\n limit?: number;\n}\n\n/**\n * Read-only session operations.\n * Consumers needing read-only access (dashboards, analytics) implement only this.\n */\nexport interface ISessionReader {\n getSession(id: ChatId): Promise<ChatSession | null>;\n listSessions(options?: SessionListOptions): Promise<ChatSession[]>;\n loadMessages(\n sessionId: ChatId,\n options?: { limit?: number; offset?: number },\n ): Promise<PaginatedMessages>;\n searchSessions(options: SessionSearchOptions): Promise<ChatSession[]>;\n count(): Promise<number>;\n}\n\n/**\n * Write/mutate session operations.\n * Consumers needing full access implement both ISessionReader & ISessionWriter.\n */\nexport interface ISessionWriter {\n createSession(options: CreateSessionOptions): Promise<ChatSession>;\n updateTitle(id: ChatId, title: string): Promise<void>;\n updateConfig(id: ChatId, config: Partial<ChatSessionConfig>): Promise<void>;\n deleteSession(id: ChatId): Promise<void>;\n appendMessage(sessionId: ChatId, message: ChatMessage): Promise<void>;\n saveMessages(sessionId: ChatId, messages: ChatMessage[]): Promise<void>;\n clear(): Promise<void>;\n /** Release any resources held by this store (optional). */\n dispose?(): Promise<void>;\n}\n\n/**\n * Full session store interface — union of reader and writer.\n * Backward-compatible: all existing implementations continue to work.\n *\n * @example\n * ```typescript\n * const store = new InMemorySessionStore();\n * const session = await store.createSession({ config: { model: \"gpt-4\", backend: \"vercel-ai\" } });\n * await store.appendMessage(session.id, message);\n * const page = await store.loadMessages(session.id, { limit: 20, offset: 0 });\n * ```\n */\nexport interface IChatSessionStore extends ISessionReader, ISessionWriter {}\n\n// ─── Base Session Store ────────────────────────────────────────\n\n/**\n * Base session store implementation backed by any `IStorageAdapter<ChatSession>`.\n * Handles all session-specific logic; subclasses only need to provide the adapter.\n */\nclass BaseSessionStore implements IChatSessionStore {\n constructor(protected readonly adapter: IStorageAdapter<ChatSession>) {}\n\n async createSession(options: CreateSessionOptions): Promise<ChatSession> {\n const now = new Date().toISOString();\n const id = createChatId();\n const session: ChatSession = {\n id,\n title: options.title ?? \"Untitled\",\n messages: [],\n config: {\n model: options.config?.model ?? \"\",\n backend: options.config?.backend ?? \"\",\n ...options.config,\n },\n metadata: {\n messageCount: 0,\n totalTokens: 0,\n tags: options.tags ? [...options.tags] : undefined,\n custom: options.custom ? { ...options.custom } : undefined,\n },\n status: \"active\" as const,\n createdAt: now,\n updatedAt: now,\n };\n await this.adapter.create(id, session);\n return structuredClone(session);\n }\n\n async getSession(id: ChatId): Promise<ChatSession | null> {\n return this.adapter.get(id);\n }\n\n async listSessions(options?: SessionListOptions): Promise<ChatSession[]> {\n return this.adapter.list(options as ListOptions<ChatSession>);\n }\n\n async updateTitle(id: ChatId, title: string): Promise<void> {\n const session = await this.adapter.get(id);\n if (!session) {\n throw new StorageError(`Session \"${id}\" not found`, ErrorCode.STORAGE_NOT_FOUND);\n }\n session.title = title;\n session.updatedAt = new Date().toISOString();\n await this.adapter.update(id, session);\n }\n\n async updateConfig(\n id: ChatId,\n config: Partial<ChatSessionConfig>,\n ): Promise<void> {\n const session = await this.adapter.get(id);\n if (!session) {\n throw new StorageError(`Session \"${id}\" not found`, ErrorCode.STORAGE_NOT_FOUND);\n }\n session.config = { ...session.config, ...config };\n session.updatedAt = new Date().toISOString();\n await this.adapter.update(id, session);\n }\n\n async deleteSession(id: ChatId): Promise<void> {\n await this.adapter.delete(id);\n }\n\n async appendMessage(sessionId: ChatId, message: ChatMessage): Promise<void> {\n const session = await this.adapter.get(sessionId);\n if (!session) {\n throw new StorageError(`Session \"${sessionId}\" not found`, ErrorCode.STORAGE_NOT_FOUND);\n }\n session.messages.push(structuredClone(message));\n session.metadata.messageCount = session.messages.length;\n session.updatedAt = new Date().toISOString();\n await this.adapter.update(sessionId, session);\n }\n\n async saveMessages(sessionId: ChatId, messages: ChatMessage[]): Promise<void> {\n if (messages.length === 0) return;\n const session = await this.adapter.get(sessionId);\n if (!session) {\n throw new StorageError(`Session \"${sessionId}\" not found`, ErrorCode.STORAGE_NOT_FOUND);\n }\n for (const msg of messages) {\n session.messages.push(structuredClone(msg));\n }\n session.metadata.messageCount = session.messages.length;\n session.updatedAt = new Date().toISOString();\n await this.adapter.update(sessionId, session);\n }\n\n async loadMessages(\n sessionId: ChatId,\n options?: { limit?: number; offset?: number },\n ): Promise<PaginatedMessages> {\n const session = await this.adapter.get(sessionId);\n if (!session) {\n throw new StorageError(`Session \"${sessionId}\" not found`, ErrorCode.STORAGE_NOT_FOUND);\n }\n const total = session.messages.length;\n const offset = options?.offset ?? 0;\n const limit = options?.limit ?? total;\n const messages = session.messages.slice(offset, offset + limit);\n return {\n messages: structuredClone(messages),\n total,\n hasMore: offset + limit < total,\n };\n }\n\n async searchSessions(\n options: SessionSearchOptions,\n ): Promise<ChatSession[]> {\n const query = options.query.toLowerCase();\n const limit = options.limit ?? 20;\n return this.adapter.list({\n filter: (session) => {\n if (session.title?.toLowerCase().includes(query)) return true;\n return session.messages.some((msg) => {\n return msg.parts.some(\n (part) =>\n part.type === \"text\" &&\n part.text.toLowerCase().includes(query),\n );\n });\n },\n limit,\n });\n }\n\n async count(): Promise<number> {\n return this.adapter.count();\n }\n\n async clear(): Promise<void> {\n return this.adapter.clear();\n }\n}\n\n// ─── InMemorySessionStore ──────────────────────────────────────\n\n/**\n * In-memory session store. Data is lost when the process exits.\n * Uses `InMemoryStorage` internally.\n *\n * @example\n * ```typescript\n * const store = new InMemorySessionStore();\n * const session = await store.createSession({\n * config: { model: \"gpt-4\", backend: \"vercel-ai\" },\n * });\n * ```\n */\nexport class InMemorySessionStore extends BaseSessionStore {\n constructor() {\n super(new InMemoryStorage<ChatSession>());\n }\n}\n\n// ─── FileSessionStore ──────────────────────────────────────────\n\n/** Configuration for FileSessionStore */\nexport interface FileSessionStoreOptions {\n /** Directory to store session JSON files */\n directory: string;\n}\n\n/**\n * File-based session store. Each session is a JSON file on disk.\n * Uses `FileStorage` internally.\n *\n * @example\n * ```typescript\n * const store = new FileSessionStore({ directory: \"./data/sessions\" });\n * const session = await store.createSession({\n * config: { model: \"claude-3\", backend: \"claude\" },\n * });\n * ```\n */\nexport class FileSessionStore extends BaseSessionStore {\n constructor(options: FileSessionStoreOptions) {\n super(new FileStorage<ChatSession>({ directory: options.directory }));\n }\n}\n\n// Re-export StorageError for consumers that only import from chat/sessions\nexport { StorageError } from \"./storage.js\";\n"]}