@witqq/agent-sdk 0.8.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 (113) hide show
  1. package/dist/{agent-DxY68NZL.d.cts → agent-C6H2CgJA.d.cts} +2 -0
  2. package/dist/{agent-CW9XbmG_.d.ts → agent-F7oB6eKp.d.ts} +2 -0
  3. package/dist/backends/claude.cjs.map +1 -1
  4. package/dist/backends/claude.d.cts +2 -2
  5. package/dist/backends/claude.d.ts +2 -2
  6. package/dist/backends/claude.js.map +1 -1
  7. package/dist/backends/copilot.cjs +8 -15
  8. package/dist/backends/copilot.cjs.map +1 -1
  9. package/dist/backends/copilot.d.cts +2 -2
  10. package/dist/backends/copilot.d.ts +2 -2
  11. package/dist/backends/copilot.js +8 -15
  12. package/dist/backends/copilot.js.map +1 -1
  13. package/dist/backends/mock-llm.cjs +719 -0
  14. package/dist/backends/mock-llm.cjs.map +1 -0
  15. package/dist/backends/mock-llm.d.cts +37 -0
  16. package/dist/backends/mock-llm.d.ts +37 -0
  17. package/dist/backends/mock-llm.js +717 -0
  18. package/dist/backends/mock-llm.js.map +1 -0
  19. package/dist/backends/vercel-ai.cjs +8 -1
  20. package/dist/backends/vercel-ai.cjs.map +1 -1
  21. package/dist/backends/vercel-ai.d.cts +2 -2
  22. package/dist/backends/vercel-ai.d.ts +2 -2
  23. package/dist/backends/vercel-ai.js +8 -1
  24. package/dist/backends/vercel-ai.js.map +1 -1
  25. package/dist/backends-Cno0gZjy.d.cts +114 -0
  26. package/dist/backends-Cno0gZjy.d.ts +114 -0
  27. package/dist/chat/accumulator.cjs.map +1 -1
  28. package/dist/chat/accumulator.d.cts +2 -2
  29. package/dist/chat/accumulator.d.ts +2 -2
  30. package/dist/chat/accumulator.js.map +1 -1
  31. package/dist/chat/backends.cjs +350 -77
  32. package/dist/chat/backends.cjs.map +1 -1
  33. package/dist/chat/backends.d.cts +7 -7
  34. package/dist/chat/backends.d.ts +7 -7
  35. package/dist/chat/backends.js +349 -78
  36. package/dist/chat/backends.js.map +1 -1
  37. package/dist/chat/context.d.cts +2 -2
  38. package/dist/chat/context.d.ts +2 -2
  39. package/dist/chat/core.cjs +35 -25
  40. package/dist/chat/core.cjs.map +1 -1
  41. package/dist/chat/core.d.cts +15 -5
  42. package/dist/chat/core.d.ts +15 -5
  43. package/dist/chat/core.js +35 -26
  44. package/dist/chat/core.js.map +1 -1
  45. package/dist/chat/events.d.cts +2 -2
  46. package/dist/chat/events.d.ts +2 -2
  47. package/dist/chat/index.cjs +418 -122
  48. package/dist/chat/index.cjs.map +1 -1
  49. package/dist/chat/index.d.cts +7 -7
  50. package/dist/chat/index.d.ts +7 -7
  51. package/dist/chat/index.js +418 -124
  52. package/dist/chat/index.js.map +1 -1
  53. package/dist/chat/react.cjs +216 -12
  54. package/dist/chat/react.cjs.map +1 -1
  55. package/dist/chat/react.d.cts +78 -4
  56. package/dist/chat/react.d.ts +78 -4
  57. package/dist/chat/react.js +215 -13
  58. package/dist/chat/react.js.map +1 -1
  59. package/dist/chat/runtime.cjs +6 -2
  60. package/dist/chat/runtime.cjs.map +1 -1
  61. package/dist/chat/runtime.d.cts +2 -2
  62. package/dist/chat/runtime.d.ts +2 -2
  63. package/dist/chat/runtime.js +6 -2
  64. package/dist/chat/runtime.js.map +1 -1
  65. package/dist/chat/server.cjs +15 -5
  66. package/dist/chat/server.cjs.map +1 -1
  67. package/dist/chat/server.d.cts +3 -3
  68. package/dist/chat/server.d.ts +3 -3
  69. package/dist/chat/server.js +15 -5
  70. package/dist/chat/server.js.map +1 -1
  71. package/dist/chat/sessions.cjs +39 -23
  72. package/dist/chat/sessions.cjs.map +1 -1
  73. package/dist/chat/sessions.d.cts +2 -2
  74. package/dist/chat/sessions.d.ts +2 -2
  75. package/dist/chat/sessions.js +40 -24
  76. package/dist/chat/sessions.js.map +1 -1
  77. package/dist/chat/sqlite.cjs +95 -0
  78. package/dist/chat/sqlite.cjs.map +1 -1
  79. package/dist/chat/sqlite.d.cts +39 -3
  80. package/dist/chat/sqlite.d.ts +39 -3
  81. package/dist/chat/sqlite.js +93 -1
  82. package/dist/chat/sqlite.js.map +1 -1
  83. package/dist/chat/state.d.cts +2 -2
  84. package/dist/chat/state.d.ts +2 -2
  85. package/dist/chat/storage.cjs +39 -23
  86. package/dist/chat/storage.cjs.map +1 -1
  87. package/dist/chat/storage.d.cts +7 -3
  88. package/dist/chat/storage.d.ts +7 -3
  89. package/dist/chat/storage.js +40 -24
  90. package/dist/chat/storage.js.map +1 -1
  91. package/dist/{in-process-transport-C1JnJGVR.d.ts → in-process-transport-7EIit9Xk.d.ts} +51 -17
  92. package/dist/{in-process-transport-C7DSqPyX.d.cts → in-process-transport-Ct9YcX8I.d.cts} +51 -17
  93. package/dist/index.cjs +14 -14
  94. package/dist/index.cjs.map +1 -1
  95. package/dist/index.d.cts +4 -2
  96. package/dist/index.d.ts +4 -2
  97. package/dist/index.js +13 -13
  98. package/dist/index.js.map +1 -1
  99. package/dist/testing.cjs +724 -0
  100. package/dist/testing.cjs.map +1 -1
  101. package/dist/testing.d.cts +14 -2
  102. package/dist/testing.d.ts +14 -2
  103. package/dist/testing.js +724 -0
  104. package/dist/testing.js.map +1 -1
  105. package/dist/{transport-Cdh3M0tS.d.cts → transport-DLWCN18G.d.cts} +1 -1
  106. package/dist/{transport-Ciap4PWK.d.ts → transport-DsuS-GeM.d.ts} +1 -1
  107. package/dist/{types-ajANVzf7.d.ts → types-DgtI1hzh.d.ts} +2 -1
  108. package/dist/{types-DRgd_9R7.d.cts → types-DkSXALKg.d.cts} +2 -1
  109. package/package.json +18 -7
  110. package/LICENSE +0 -21
  111. package/README.md +0 -1054
  112. package/dist/backends-BSrsBYFn.d.cts +0 -39
  113. package/dist/backends-BSrsBYFn.d.ts +0 -39
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var fs = require('fs');
4
+ var promises = require('fs/promises');
4
5
  var path = require('path');
5
6
 
6
7
  var __defProp = Object.defineProperty;
@@ -758,17 +759,6 @@ function convertParameters(params) {
758
759
  }
759
760
  return params;
760
761
  }
761
- async function mapToolsToSDKAsync(tools) {
762
- return tools.map((tool) => ({
763
- name: tool.name,
764
- description: tool.description,
765
- parameters: convertParameters(tool.parameters),
766
- handler: async (args) => {
767
- const result = await tool.execute(args);
768
- return typeof result === "string" ? result : JSON.stringify(result);
769
- }
770
- }));
771
- }
772
762
  function buildPermissionHandler(config) {
773
763
  const onPermission = config.supervisor?.onPermission;
774
764
  if (!onPermission) {
@@ -1006,15 +996,15 @@ var init_copilot = __esm({
1006
996
  },
1007
997
  onPermissionRequest: buildPermissionHandler(config),
1008
998
  onUserInputRequest: buildUserInputHandler(config),
1009
- ...config.availableTools?.length ? { availableTools: config.availableTools } : {}
999
+ ...config.availableTools ? { availableTools: config.availableTools } : {}
1010
1000
  };
1011
1001
  this._toolsReady = this._initToolsAsync(config);
1012
1002
  this._resumeSessionId = resumeSessionId;
1013
1003
  }
1014
- /** Pre-convert Zod schemas to JSON Schema asynchronously.
1004
+ /** Pre-convert Zod schemas to JSON Schema.
1015
1005
  * Updates sdkTools and sessionConfig.tools before first session creation. */
1016
1006
  async _initToolsAsync(config) {
1017
- this.sdkTools = await mapToolsToSDKAsync(config.tools ?? []);
1007
+ this.sdkTools = mapToolsToSDK(config.tools ?? []);
1018
1008
  this.sessionConfig.tools = this.sdkTools;
1019
1009
  }
1020
1010
  get sessionId() {
@@ -1305,7 +1295,11 @@ You MUST respond with ONLY valid JSON matching this schema:
1305
1295
  githubToken: this.options.githubToken,
1306
1296
  useLoggedInUser: this.options.useLoggedInUser ?? !this.options.githubToken,
1307
1297
  ...this.options.cliArgs ? { cliArgs: this.options.cliArgs } : {},
1308
- ...this.options.env ? { env: { ...process.env, ...this.options.env } } : {}
1298
+ env: {
1299
+ ...process.env,
1300
+ ...this.options.githubToken ? { GITHUB_TOKEN: this.options.githubToken } : {},
1301
+ ...this.options.env
1302
+ }
1309
1303
  });
1310
1304
  const startupTimeout = this.options.startupTimeoutMs ?? 3e4;
1311
1305
  await withTimeout(client.start(), startupTimeout, "CLI startup timed out");
@@ -2725,6 +2719,7 @@ var init_vercel_ai = __esm({
2725
2719
  }
2726
2720
  });
2727
2721
  let finalText = "";
2722
+ let lastFinishReason;
2728
2723
  try {
2729
2724
  for await (const part of result.fullStream) {
2730
2725
  if (signal.aborted) throw new AbortError();
@@ -2735,10 +2730,15 @@ var init_vercel_ai = __esm({
2735
2730
  }
2736
2731
  if (part.type === "finish-step") {
2737
2732
  const p = part;
2733
+ lastFinishReason = p.finishReason;
2738
2734
  if (p.finishReason === "tool-calls") {
2739
2735
  finalText = "";
2740
2736
  }
2741
2737
  }
2738
+ if (part.type === "finish") {
2739
+ const p = part;
2740
+ lastFinishReason = p.finishReason;
2741
+ }
2742
2742
  }
2743
2743
  const totalUsage = await result.totalUsage;
2744
2744
  yield {
@@ -2750,7 +2750,8 @@ var init_vercel_ai = __esm({
2750
2750
  yield {
2751
2751
  type: "done",
2752
2752
  finalOutput: hasStreamed ? null : finalText || null,
2753
- ...hasStreamed ? { streamed: true } : {}
2753
+ ...hasStreamed ? { streamed: true } : {},
2754
+ ...lastFinishReason ? { finishReason: lastFinishReason } : {}
2754
2755
  };
2755
2756
  } catch (e) {
2756
2757
  if (signal.aborted) throw new AbortError();
@@ -2912,30 +2913,30 @@ function isFilePart(value) {
2912
2913
  const obj = value;
2913
2914
  return obj.type === "file" && typeof obj.name === "string" && typeof obj.mimeType === "string";
2914
2915
  }
2916
+ var VALID_CHAT_EVENT_TYPES = /* @__PURE__ */ new Set([
2917
+ "message:start",
2918
+ "message:delta",
2919
+ "message:complete",
2920
+ "tool:start",
2921
+ "tool:complete",
2922
+ "thinking:start",
2923
+ "thinking:delta",
2924
+ "thinking:end",
2925
+ "permission:request",
2926
+ "permission:response",
2927
+ "usage",
2928
+ "session:created",
2929
+ "session:updated",
2930
+ "error",
2931
+ "typing:start",
2932
+ "typing:end",
2933
+ "heartbeat",
2934
+ "done"
2935
+ ]);
2915
2936
  function isChatEvent(value) {
2916
2937
  if (typeof value !== "object" || value === null) return false;
2917
2938
  const obj = value;
2918
- const validTypes = [
2919
- "message:start",
2920
- "message:delta",
2921
- "message:complete",
2922
- "tool:start",
2923
- "tool:complete",
2924
- "thinking:start",
2925
- "thinking:delta",
2926
- "thinking:end",
2927
- "permission:request",
2928
- "permission:response",
2929
- "usage",
2930
- "session:created",
2931
- "session:updated",
2932
- "error",
2933
- "typing:start",
2934
- "typing:end",
2935
- "heartbeat",
2936
- "done"
2937
- ];
2938
- return validTypes.includes(obj.type);
2939
+ return VALID_CHAT_EVENT_TYPES.has(obj.type);
2939
2940
  }
2940
2941
 
2941
2942
  // src/chat/bridge.ts
@@ -2999,8 +3000,9 @@ function agentEventToChatEvent(event, messageId) {
2999
3000
  case "ask_user":
3000
3001
  case "ask_user_response":
3001
3002
  case "session_info":
3002
- case "done":
3003
3003
  return null;
3004
+ case "done":
3005
+ return { type: "done", finalOutput: event.finalOutput ?? void 0, finishReason: event.finishReason };
3004
3006
  default:
3005
3007
  return null;
3006
3008
  }
@@ -3046,21 +3048,29 @@ function chatEventToAgentEvent(event) {
3046
3048
 
3047
3049
  // src/chat/conversion.ts
3048
3050
  function toAgentMessage(message) {
3051
+ return toAgentMessages(message)[0];
3052
+ }
3053
+ function toAgentMessages(message) {
3049
3054
  const textContent = getMessageText(message);
3050
3055
  const toolCallParts = getMessageToolCalls(message);
3051
3056
  switch (message.role) {
3052
3057
  case "user":
3053
- return { role: "user", content: textContent };
3058
+ return [{ role: "user", content: textContent }];
3054
3059
  case "assistant": {
3055
3060
  const toolCalls = toolCallParts.length > 0 ? toolCallParts.map((p) => ({ id: p.toolCallId, name: p.name, args: p.args })) : void 0;
3056
- return {
3061
+ const assistantMsg = {
3057
3062
  role: "assistant",
3058
3063
  content: textContent,
3059
3064
  toolCalls
3060
3065
  };
3066
+ const toolResults = extractToolResults(message);
3067
+ if (toolResults.length > 0) {
3068
+ return [assistantMsg, { role: "tool", toolResults }];
3069
+ }
3070
+ return [assistantMsg];
3061
3071
  }
3062
3072
  case "system":
3063
- return { role: "system", content: textContent };
3073
+ return [{ role: "system", content: textContent }];
3064
3074
  }
3065
3075
  }
3066
3076
  function fromAgentMessage(message, id) {
@@ -3106,6 +3116,14 @@ function fromAgentMessage(message, id) {
3106
3116
  status: "complete"
3107
3117
  };
3108
3118
  }
3119
+ function extractToolResults(message) {
3120
+ return getMessageToolCalls(message).filter((p) => p.result !== void 0).map((p) => ({
3121
+ toolCallId: p.toolCallId,
3122
+ name: p.name,
3123
+ result: p.result,
3124
+ isError: p.status === "error" ? true : void 0
3125
+ }));
3126
+ }
3109
3127
 
3110
3128
  // src/chat/context.ts
3111
3129
  function estimateTokens(message, options) {
@@ -3938,6 +3956,7 @@ var ChatRuntime = class {
3938
3956
  _backends;
3939
3957
  _sessionStore;
3940
3958
  _contextConfig;
3959
+ _ctxManager;
3941
3960
  _middleware;
3942
3961
  _tools = /* @__PURE__ */ new Map();
3943
3962
  _retryConfig;
@@ -3957,6 +3976,9 @@ var ChatRuntime = class {
3957
3976
  this._defaultBackend = options.defaultBackend;
3958
3977
  this._sessionStore = options.sessionStore;
3959
3978
  this._contextConfig = options.context;
3979
+ if (this._contextConfig) {
3980
+ this._ctxManager = new ContextWindowManager(this._contextConfig);
3981
+ }
3960
3982
  this._middleware = [...options.middleware ?? []];
3961
3983
  this._retryConfig = options.retryConfig;
3962
3984
  this._onContextTrimmed = options.onContextTrimmed;
@@ -4137,8 +4159,8 @@ var ChatRuntime = class {
4137
4159
  }
4138
4160
  /** Stage 5: Auto-trim context window if configured. Returns session snapshot for adapter. */
4139
4161
  async trimSessionContext(cid, session, model) {
4140
- if (!this._contextConfig) return session;
4141
- const ctxManager = new ContextWindowManager(this._contextConfig);
4162
+ if (!this._ctxManager) return session;
4163
+ const ctxManager = this._ctxManager;
4142
4164
  const lastUsage = this._sessionUsage.get(cid);
4143
4165
  const modelContextWindow = model ? this._modelContextWindows.get(model) : void 0;
4144
4166
  if (lastUsage && modelContextWindow) {
@@ -4554,25 +4576,25 @@ var FileStorage = class {
4554
4576
  constructor(options) {
4555
4577
  this.directory = options.directory;
4556
4578
  this.extension = options.extension ?? ".json";
4557
- this.ensureDirectory();
4579
+ this.ensureDirectorySync();
4558
4580
  }
4559
4581
  /** @inheritdoc */
4560
4582
  async get(key) {
4561
4583
  const filePath = this.keyToPath(key);
4562
- if (!fs.existsSync(filePath)) {
4584
+ if (!await this.fileExists(filePath)) {
4563
4585
  return null;
4564
4586
  }
4565
- return this.readFile(filePath);
4587
+ return this.readJsonFile(filePath);
4566
4588
  }
4567
4589
  /** @inheritdoc */
4568
4590
  async list(options) {
4569
- this.ensureDirectory();
4570
- const files = fs.readdirSync(this.directory).filter(
4591
+ await this.ensureDirectoryAsync();
4592
+ const files = (await promises.readdir(this.directory)).filter(
4571
4593
  (f) => f.endsWith(this.extension)
4572
4594
  );
4573
4595
  let items = [];
4574
4596
  for (const file of files) {
4575
- const item = this.readFile(path.join(this.directory, file));
4597
+ const item = await this.readJsonFile(path.join(this.directory, file));
4576
4598
  items.push(item);
4577
4599
  }
4578
4600
  if (options?.filter) {
@@ -4592,55 +4614,55 @@ var FileStorage = class {
4592
4614
  /** @inheritdoc */
4593
4615
  async create(key, item) {
4594
4616
  const filePath = this.keyToPath(key);
4595
- if (fs.existsSync(filePath)) {
4617
+ if (await this.fileExists(filePath)) {
4596
4618
  throw new StorageError(
4597
4619
  `Item with key "${key}" already exists`,
4598
4620
  "STORAGE_DUPLICATE_KEY" /* STORAGE_DUPLICATE_KEY */
4599
4621
  );
4600
4622
  }
4601
- this.writeFile(filePath, item);
4623
+ await this.writeJsonFile(filePath, item);
4602
4624
  }
4603
4625
  /** @inheritdoc */
4604
4626
  async update(key, item) {
4605
4627
  const filePath = this.keyToPath(key);
4606
- if (!fs.existsSync(filePath)) {
4628
+ if (!await this.fileExists(filePath)) {
4607
4629
  throw new StorageError(
4608
4630
  `Item with key "${key}" not found`,
4609
4631
  "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
4610
4632
  );
4611
4633
  }
4612
- this.writeFile(filePath, item);
4634
+ await this.writeJsonFile(filePath, item);
4613
4635
  }
4614
4636
  /** @inheritdoc */
4615
4637
  async delete(key) {
4616
4638
  const filePath = this.keyToPath(key);
4617
- if (!fs.existsSync(filePath)) {
4639
+ if (!await this.fileExists(filePath)) {
4618
4640
  throw new StorageError(
4619
4641
  `Item with key "${key}" not found`,
4620
4642
  "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
4621
4643
  );
4622
4644
  }
4623
- fs.unlinkSync(filePath);
4645
+ await promises.unlink(filePath);
4624
4646
  }
4625
4647
  /** @inheritdoc */
4626
4648
  async has(key) {
4627
- return fs.existsSync(this.keyToPath(key));
4649
+ return this.fileExists(this.keyToPath(key));
4628
4650
  }
4629
4651
  /** @inheritdoc */
4630
4652
  async count() {
4631
- this.ensureDirectory();
4632
- return fs.readdirSync(this.directory).filter(
4653
+ await this.ensureDirectoryAsync();
4654
+ return (await promises.readdir(this.directory)).filter(
4633
4655
  (f) => f.endsWith(this.extension)
4634
4656
  ).length;
4635
4657
  }
4636
4658
  /** @inheritdoc */
4637
4659
  async clear() {
4638
- this.ensureDirectory();
4639
- const files = fs.readdirSync(this.directory).filter(
4660
+ await this.ensureDirectoryAsync();
4661
+ const files = (await promises.readdir(this.directory)).filter(
4640
4662
  (f) => f.endsWith(this.extension)
4641
4663
  );
4642
4664
  for (const file of files) {
4643
- fs.unlinkSync(path.join(this.directory, file));
4665
+ await promises.unlink(path.join(this.directory, file));
4644
4666
  }
4645
4667
  }
4646
4668
  keyToPath(key) {
@@ -4650,14 +4672,29 @@ var FileStorage = class {
4650
4672
  );
4651
4673
  return path.join(this.directory, `${safeKey}${this.extension}`);
4652
4674
  }
4653
- ensureDirectory() {
4675
+ /** Sync directory init — used only in constructor (one-time). */
4676
+ ensureDirectorySync() {
4654
4677
  if (!fs.existsSync(this.directory)) {
4655
4678
  fs.mkdirSync(this.directory, { recursive: true });
4656
4679
  }
4657
4680
  }
4658
- readFile(filePath) {
4681
+ /** Async directory init — used in operations. */
4682
+ async ensureDirectoryAsync() {
4683
+ if (!await this.fileExists(this.directory)) {
4684
+ await promises.mkdir(this.directory, { recursive: true });
4685
+ }
4686
+ }
4687
+ async fileExists(filePath) {
4688
+ try {
4689
+ await promises.access(filePath);
4690
+ return true;
4691
+ } catch {
4692
+ return false;
4693
+ }
4694
+ }
4695
+ async readJsonFile(filePath) {
4659
4696
  try {
4660
- const content = fs.readFileSync(filePath, "utf-8");
4697
+ const content = await promises.readFile(filePath, "utf-8");
4661
4698
  return JSON.parse(content);
4662
4699
  } catch (error) {
4663
4700
  if (error instanceof SyntaxError) {
@@ -4672,10 +4709,10 @@ var FileStorage = class {
4672
4709
  );
4673
4710
  }
4674
4711
  }
4675
- writeFile(filePath, item) {
4712
+ async writeJsonFile(filePath, item) {
4676
4713
  try {
4677
4714
  const content = JSON.stringify(item, null, 2);
4678
- fs.writeFileSync(filePath, content, "utf-8");
4715
+ await promises.writeFile(filePath, content, "utf-8");
4679
4716
  } catch {
4680
4717
  throw new StorageError(
4681
4718
  `Failed to write file: ${filePath}`,
@@ -4894,7 +4931,7 @@ var BaseBackendAdapter = class {
4894
4931
  async *streamMessage(session, message, options) {
4895
4932
  this.assertNotDisposed();
4896
4933
  const agent = this.getOrCreateAgent(options);
4897
- const messages = session.messages.map(toAgentMessage);
4934
+ const messages = session.messages.flatMap(toAgentMessages);
4898
4935
  messages.push({ role: "user", content: message });
4899
4936
  yield* this.streamAgentEvents(agent, messages, options);
4900
4937
  }
@@ -4971,7 +5008,8 @@ var BaseBackendAdapter = class {
4971
5008
  }
4972
5009
  const config = {
4973
5010
  ...this._agentConfig,
4974
- ...model !== void 0 && { model }
5011
+ ...model !== void 0 && { model },
5012
+ ...options?.tools?.length ? { tools: options.tools } : {}
4975
5013
  };
4976
5014
  const agent = this.agentService.createAgent(config);
4977
5015
  this._currentAgent = { instance: agent, model };
@@ -4986,21 +5024,15 @@ var BaseBackendAdapter = class {
4986
5024
  }
4987
5025
  };
4988
5026
 
4989
- // src/chat/backends/copilot.ts
4990
- var CopilotChatAdapter = class extends BaseBackendAdapter {
5027
+ // src/chat/backends/resumable.ts
5028
+ var ResumableChatAdapter = class extends BaseBackendAdapter {
4991
5029
  _backendSessionId = null;
4992
- _copilotOptions;
4993
- constructor(options) {
5030
+ constructor(name, options) {
4994
5031
  const agentConfig = {
4995
5032
  ...options.agentConfig,
4996
5033
  sessionMode: "persistent"
4997
5034
  };
4998
- super("copilot", { ...options, agentConfig });
4999
- this._copilotOptions = options.copilotOptions;
5000
- }
5001
- createService() {
5002
- const { createCopilotService: createCopilotService2 } = (init_copilot(), __toCommonJS(copilot_exports));
5003
- return createCopilotService2(this._copilotOptions || {});
5035
+ super(name, { ...options, agentConfig });
5004
5036
  }
5005
5037
  get backendSessionId() {
5006
5038
  return this._backendSessionId;
@@ -5029,7 +5061,7 @@ var CopilotChatAdapter = class extends BaseBackendAdapter {
5029
5061
  { code: "SESSION_EXPIRED" /* SESSION_EXPIRED */ }
5030
5062
  );
5031
5063
  }
5032
- const messages = session.messages.map(toAgentMessage);
5064
+ const messages = session.messages.flatMap(toAgentMessages);
5033
5065
  yield* this.streamAgentEvents(agent, messages, options);
5034
5066
  }
5035
5067
  captureSessionId(agent) {
@@ -5039,69 +5071,325 @@ var CopilotChatAdapter = class extends BaseBackendAdapter {
5039
5071
  }
5040
5072
  };
5041
5073
 
5074
+ // src/chat/backends/copilot.ts
5075
+ var CopilotChatAdapter = class extends ResumableChatAdapter {
5076
+ _copilotOptions;
5077
+ constructor(options) {
5078
+ super("copilot", options);
5079
+ this._copilotOptions = options.copilotOptions;
5080
+ }
5081
+ createService() {
5082
+ const { createCopilotService: createCopilotService2 } = (init_copilot(), __toCommonJS(copilot_exports));
5083
+ return createCopilotService2(this._copilotOptions || {});
5084
+ }
5085
+ };
5086
+
5042
5087
  // src/chat/backends/claude.ts
5043
- var ClaudeChatAdapter = class extends BaseBackendAdapter {
5044
- _backendSessionId = null;
5088
+ var ClaudeChatAdapter = class extends ResumableChatAdapter {
5045
5089
  _claudeOptions;
5046
5090
  constructor(options) {
5047
- const agentConfig = {
5048
- ...options.agentConfig,
5049
- sessionMode: "persistent"
5050
- };
5051
- super("claude", { ...options, agentConfig });
5091
+ super("claude", options);
5052
5092
  this._claudeOptions = options.claudeOptions;
5053
5093
  }
5054
5094
  createService() {
5055
5095
  const { createClaudeService: createClaudeService2 } = (init_claude(), __toCommonJS(claude_exports));
5056
5096
  return createClaudeService2(this._claudeOptions || {});
5057
5097
  }
5058
- get backendSessionId() {
5059
- return this._backendSessionId;
5098
+ };
5099
+
5100
+ // src/chat/backends/vercel-ai.ts
5101
+ var VercelAIChatAdapter = class extends BaseBackendAdapter {
5102
+ _vercelOptions;
5103
+ constructor(options) {
5104
+ super("vercel-ai", options);
5105
+ this._vercelOptions = options.vercelOptions;
5060
5106
  }
5061
- canResume() {
5062
- return this._backendSessionId !== null;
5107
+ createService() {
5108
+ const { createVercelAIService: createVercelAIService2 } = (init_vercel_ai(), __toCommonJS(vercel_ai_exports));
5109
+ return createVercelAIService2(this._vercelOptions || {});
5063
5110
  }
5064
- async *resume(session, backendSessionId, options) {
5065
- this.assertNotDisposed();
5066
- if (!backendSessionId) {
5067
- throw new ChatError("Backend session ID is required for resume", {
5068
- code: "INVALID_INPUT" /* INVALID_INPUT */
5111
+ captureSessionId(_agent) {
5112
+ }
5113
+ };
5114
+
5115
+ // src/backends/mock-llm.ts
5116
+ init_base_agent();
5117
+ init_errors2();
5118
+ function extractPrompt(messages) {
5119
+ for (let i = messages.length - 1; i >= 0; i--) {
5120
+ const msg = messages[i];
5121
+ if (msg.role === "user") {
5122
+ return typeof msg.content === "string" ? msg.content : msg.content.filter((p) => p.type === "text").map((p) => p.text).join("");
5123
+ }
5124
+ }
5125
+ return "";
5126
+ }
5127
+ function resolveResponse(mode, messages, callIndex) {
5128
+ switch (mode.type) {
5129
+ case "echo":
5130
+ return extractPrompt(messages);
5131
+ case "static":
5132
+ return mode.response;
5133
+ case "scripted": {
5134
+ if (mode.loop) {
5135
+ return mode.responses[callIndex % mode.responses.length];
5136
+ }
5137
+ if (callIndex < mode.responses.length) {
5138
+ return mode.responses[callIndex];
5139
+ }
5140
+ return mode.responses[mode.responses.length - 1];
5141
+ }
5142
+ case "error":
5143
+ throw new AgentSDKError(mode.error, {
5144
+ code: mode.code ?? "backend_error",
5145
+ retryable: mode.recoverable ?? false
5069
5146
  });
5147
+ }
5148
+ }
5149
+ async function applyLatency(latency, signal) {
5150
+ if (!latency) return;
5151
+ const ms = latency.type === "fixed" ? latency.ms : latency.minMs + Math.random() * (latency.maxMs - latency.minMs);
5152
+ if (ms <= 0) return;
5153
+ await new Promise((resolve, reject) => {
5154
+ const timer = setTimeout(resolve, ms);
5155
+ const onAbort = () => {
5156
+ clearTimeout(timer);
5157
+ reject(new Error("aborted"));
5158
+ };
5159
+ if (signal.aborted) {
5160
+ clearTimeout(timer);
5161
+ reject(new Error("aborted"));
5162
+ return;
5070
5163
  }
5071
- const agent = this.getOrCreateAgent(options);
5072
- const currentSessionId = agent.sessionId;
5073
- if (!currentSessionId) {
5074
- throw new ChatError(
5075
- `No active session to resume (requested: ${backendSessionId})`,
5076
- { code: "SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */ }
5077
- );
5164
+ signal.addEventListener("abort", onAbort, { once: true });
5165
+ });
5166
+ }
5167
+ function chunkText(text, streaming) {
5168
+ if (streaming?.chunkSize && streaming.chunkSize > 0) {
5169
+ const chunks = [];
5170
+ for (let i = 0; i < text.length; i += streaming.chunkSize) {
5171
+ chunks.push(text.slice(i, i + streaming.chunkSize));
5078
5172
  }
5079
- if (currentSessionId !== backendSessionId) {
5080
- throw new ChatError(
5081
- `Session expired: expected ${backendSessionId}, got ${currentSessionId}`,
5082
- { code: "SESSION_EXPIRED" /* SESSION_EXPIRED */ }
5083
- );
5173
+ return chunks;
5174
+ }
5175
+ return text.split(/(\s+)/).filter(Boolean);
5176
+ }
5177
+ async function chunkDelay(streaming, signal) {
5178
+ const ms = streaming?.chunkDelayMs;
5179
+ if (!ms || ms <= 0) return;
5180
+ await new Promise((resolve, reject) => {
5181
+ const timer = setTimeout(resolve, ms);
5182
+ const onAbort = () => {
5183
+ clearTimeout(timer);
5184
+ reject(new Error("aborted"));
5185
+ };
5186
+ if (signal.aborted) {
5187
+ clearTimeout(timer);
5188
+ reject(new Error("aborted"));
5189
+ return;
5084
5190
  }
5085
- const messages = session.messages.map(toAgentMessage);
5086
- yield* this.streamAgentEvents(agent, messages, options);
5191
+ signal.addEventListener("abort", onAbort, { once: true });
5192
+ });
5193
+ }
5194
+ var MockLLMAgent = class extends BaseAgent {
5195
+ backendName = "mock-llm";
5196
+ mode;
5197
+ latency;
5198
+ streaming;
5199
+ finishReason;
5200
+ permissions;
5201
+ toolCallConfigs;
5202
+ configuredStructuredOutput;
5203
+ callIndex = 0;
5204
+ constructor(config, options) {
5205
+ super(config);
5206
+ this.mode = options.mode ?? { type: "echo" };
5207
+ this.latency = options.latency;
5208
+ this.streaming = options.streaming;
5209
+ this.finishReason = options.finishReason ?? "stop";
5210
+ this.permissions = options.permissions;
5211
+ this.toolCallConfigs = options.toolCalls ?? [];
5212
+ this.configuredStructuredOutput = options.structuredOutput;
5213
+ }
5214
+ async executeRun(messages, _options, signal) {
5215
+ this.checkAbort(signal);
5216
+ await applyLatency(this.latency, signal);
5217
+ this.checkAbort(signal);
5218
+ const idx = this.callIndex++;
5219
+ const output = resolveResponse(this.mode, messages, idx);
5220
+ const toolCalls = this.toolCallConfigs.map((tc) => ({
5221
+ toolName: tc.toolName,
5222
+ args: tc.args ?? {},
5223
+ result: tc.result ?? null,
5224
+ approved: true
5225
+ }));
5226
+ return {
5227
+ output,
5228
+ structuredOutput: void 0,
5229
+ toolCalls,
5230
+ messages: [
5231
+ ...messages,
5232
+ { role: "assistant", content: output }
5233
+ ],
5234
+ usage: { promptTokens: 10, completionTokens: output.length }
5235
+ };
5087
5236
  }
5088
- captureSessionId(agent) {
5089
- if (agent.sessionId) {
5090
- this._backendSessionId = agent.sessionId;
5237
+ async executeRunStructured(messages, _schema, _options, signal) {
5238
+ this.checkAbort(signal);
5239
+ await applyLatency(this.latency, signal);
5240
+ this.checkAbort(signal);
5241
+ const idx = this.callIndex++;
5242
+ const output = resolveResponse(this.mode, messages, idx);
5243
+ let parsed;
5244
+ if (this.configuredStructuredOutput !== void 0) {
5245
+ parsed = this.configuredStructuredOutput;
5246
+ } else {
5247
+ try {
5248
+ parsed = JSON.parse(output);
5249
+ } catch {
5250
+ parsed = output;
5251
+ }
5091
5252
  }
5253
+ return {
5254
+ output,
5255
+ structuredOutput: parsed,
5256
+ toolCalls: [],
5257
+ messages: [
5258
+ ...messages,
5259
+ { role: "assistant", content: output }
5260
+ ],
5261
+ usage: { promptTokens: 10, completionTokens: output.length }
5262
+ };
5263
+ }
5264
+ async *executeStream(messages, _options, signal) {
5265
+ this.checkAbort(signal);
5266
+ await applyLatency(this.latency, signal);
5267
+ this.checkAbort(signal);
5268
+ if (this.permissions) {
5269
+ yield* this.simulatePermissions(signal);
5270
+ }
5271
+ if (this.toolCallConfigs.length > 0) {
5272
+ yield* this.simulateToolCalls(signal);
5273
+ }
5274
+ const idx = this.callIndex++;
5275
+ const output = resolveResponse(this.mode, messages, idx);
5276
+ const chunks = chunkText(output, this.streaming);
5277
+ for (let i = 0; i < chunks.length; i++) {
5278
+ this.checkAbort(signal);
5279
+ if (i > 0) {
5280
+ await chunkDelay(this.streaming, signal);
5281
+ }
5282
+ yield { type: "text_delta", text: chunks[i] };
5283
+ }
5284
+ yield {
5285
+ type: "usage_update",
5286
+ promptTokens: 10,
5287
+ completionTokens: output.length
5288
+ };
5289
+ yield {
5290
+ type: "done",
5291
+ finalOutput: output,
5292
+ finishReason: this.finishReason
5293
+ };
5294
+ }
5295
+ async *simulateToolCalls(signal) {
5296
+ for (let i = 0; i < this.toolCallConfigs.length; i++) {
5297
+ this.checkAbort(signal);
5298
+ const tc = this.toolCallConfigs[i];
5299
+ const toolCallId = tc.toolCallId ?? `mock-tc-${i}`;
5300
+ yield {
5301
+ type: "tool_call_start",
5302
+ toolCallId,
5303
+ toolName: tc.toolName,
5304
+ args: tc.args ?? {}
5305
+ };
5306
+ yield {
5307
+ type: "tool_call_end",
5308
+ toolCallId,
5309
+ toolName: tc.toolName,
5310
+ result: tc.result ?? null
5311
+ };
5312
+ }
5313
+ }
5314
+ async *simulatePermissions(signal) {
5315
+ const perms = this.permissions;
5316
+ for (const toolName of perms.toolNames) {
5317
+ this.checkAbort(signal);
5318
+ const request = {
5319
+ toolName,
5320
+ toolArgs: {}
5321
+ };
5322
+ yield { type: "permission_request", request };
5323
+ if (perms.denyTools?.includes(toolName)) {
5324
+ yield {
5325
+ type: "permission_response",
5326
+ toolName,
5327
+ decision: { allowed: false, reason: "Denied by mock configuration" }
5328
+ };
5329
+ } else if (perms.autoApprove) {
5330
+ yield {
5331
+ type: "permission_response",
5332
+ toolName,
5333
+ decision: { allowed: true, scope: "once" }
5334
+ };
5335
+ } else {
5336
+ const supervisor = this.getConfig().supervisor;
5337
+ if (supervisor?.onPermission) {
5338
+ const decision = await supervisor.onPermission(request, signal);
5339
+ yield { type: "permission_response", toolName, decision };
5340
+ } else {
5341
+ yield {
5342
+ type: "permission_response",
5343
+ toolName,
5344
+ decision: { allowed: true, scope: "once" }
5345
+ };
5346
+ }
5347
+ }
5348
+ }
5349
+ }
5350
+ };
5351
+ var MockLLMService = class {
5352
+ name = "mock-llm";
5353
+ options;
5354
+ models;
5355
+ constructor(options = {}) {
5356
+ this.options = options;
5357
+ this.models = (options.models ?? [
5358
+ { id: "mock-fast", name: "Mock Fast" },
5359
+ { id: "mock-quality", name: "Mock Quality" }
5360
+ ]).map((m) => ({
5361
+ id: m.id,
5362
+ name: m.name,
5363
+ description: m.description
5364
+ }));
5365
+ }
5366
+ createAgent(config) {
5367
+ return new MockLLMAgent(config, this.options);
5368
+ }
5369
+ async listModels() {
5370
+ return this.models;
5371
+ }
5372
+ async validate() {
5373
+ return { valid: true, errors: [] };
5374
+ }
5375
+ async dispose() {
5092
5376
  }
5093
5377
  };
5378
+ function createMockLLMService(options = {}) {
5379
+ return new MockLLMService(options);
5380
+ }
5094
5381
 
5095
- // src/chat/backends/vercel-ai.ts
5096
- var VercelAIChatAdapter = class extends BaseBackendAdapter {
5097
- _vercelOptions;
5382
+ // src/chat/backends/mock-llm.ts
5383
+ var MockLLMChatAdapter = class extends BaseBackendAdapter {
5098
5384
  constructor(options) {
5099
- super("vercel-ai", options);
5100
- this._vercelOptions = options.vercelOptions;
5385
+ const mockOpts = options.mockOptions;
5386
+ super("mock-llm", {
5387
+ ...options,
5388
+ agentServiceFactory: () => createMockLLMService(mockOpts || {})
5389
+ });
5101
5390
  }
5102
5391
  createService() {
5103
- const { createVercelAIService: createVercelAIService2 } = (init_vercel_ai(), __toCommonJS(vercel_ai_exports));
5104
- return createVercelAIService2(this._vercelOptions || {});
5392
+ return createMockLLMService({});
5105
5393
  }
5106
5394
  captureSessionId(_agent) {
5107
5395
  }
@@ -5182,16 +5470,22 @@ var SSEChatTransport = class {
5182
5470
  };
5183
5471
  async function streamToTransport(events, transport) {
5184
5472
  try {
5185
- let accumulatedText = "";
5473
+ const textChunks = [];
5474
+ let finishReason;
5186
5475
  for await (const event of events) {
5187
5476
  if (!transport.isOpen) break;
5477
+ if (event.type === "done") {
5478
+ finishReason = event.finishReason;
5479
+ continue;
5480
+ }
5188
5481
  transport.send(event);
5189
5482
  if (event.type === "message:delta") {
5190
- accumulatedText += event.text;
5483
+ textChunks.push(event.text);
5191
5484
  }
5192
5485
  }
5193
5486
  if (transport.isOpen) {
5194
- transport.send({ type: "done", finalOutput: accumulatedText || void 0 });
5487
+ const finalOutput = textChunks.length > 0 ? textChunks.join("") : void 0;
5488
+ transport.send({ type: "done", finalOutput, finishReason });
5195
5489
  }
5196
5490
  transport.close();
5197
5491
  } catch (err) {
@@ -5514,6 +5808,7 @@ exports.InMemorySessionStore = InMemorySessionStore;
5514
5808
  exports.InProcessChatTransport = InProcessChatTransport;
5515
5809
  exports.ListenerSet = ListenerSet;
5516
5810
  exports.MessageAccumulator = MessageAccumulator;
5811
+ exports.MockLLMChatAdapter = MockLLMChatAdapter;
5517
5812
  exports.SSEChatTransport = SSEChatTransport;
5518
5813
  exports.TypedEventEmitter = TypedEventEmitter;
5519
5814
  exports.VercelAIChatAdapter = VercelAIChatAdapter;
@@ -5543,6 +5838,7 @@ exports.isTextPart = isTextPart;
5543
5838
  exports.isToolCallPart = isToolCallPart;
5544
5839
  exports.streamToTransport = streamToTransport;
5545
5840
  exports.toAgentMessage = toAgentMessage;
5841
+ exports.toAgentMessages = toAgentMessages;
5546
5842
  exports.toChatId = toChatId;
5547
5843
  exports.withRetry = withRetry;
5548
5844
  exports.withStreamWatchdog = withStreamWatchdog;