evolcore 0.0.5 → 0.0.6

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 (322) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +4 -12
  3. package/dist/agents/baseagent.js +39 -0
  4. package/dist/agents/claude-runner.js +10 -2
  5. package/dist/agents/codex-runner.js +37 -8
  6. package/dist/agents/ecagent-runner.js +1159 -0
  7. package/dist/agents/gemini-runner.js +4 -1
  8. package/dist/agents/runner-types.js +20 -4
  9. package/dist/aun/msg/history.js +32 -9
  10. package/dist/aun/outbox.js +4 -9
  11. package/dist/channels/aun.js +32 -6
  12. package/dist/channels/daemon.js +7 -4
  13. package/dist/channels/dingtalk.js +3 -5
  14. package/dist/channels/feishu.js +16 -108
  15. package/dist/channels/wechat.js +4 -4
  16. package/dist/channels/wecom.js +4 -6
  17. package/dist/cli/agent-command.js +1 -1
  18. package/dist/cli/agent.js +42 -11
  19. package/dist/cli/aun-commands.js +50 -3
  20. package/dist/cli/daemon-commands.js +81 -6
  21. package/dist/cli/data-command.js +132 -0
  22. package/dist/cli/index.js +11 -3
  23. package/dist/cli/init.js +76 -17
  24. package/dist/cli/net-check.js +9 -20
  25. package/dist/cli/restart-monitor.js +11 -2
  26. package/dist/cli/trigger-command.js +1 -1
  27. package/dist/cli/watch-msg.js +12 -25
  28. package/dist/config/builtin-roles.js +3 -1
  29. package/dist/config/config-field-policy.js +1 -1
  30. package/dist/config/gateway-config.js +21 -4
  31. package/dist/config/resolved-config-op.js +10 -0
  32. package/dist/core/command/command-handler.js +2 -2
  33. package/dist/core/command/menu-handler.js +11 -9
  34. package/dist/core/command/role-menu.js +11 -0
  35. package/dist/core/command/slash-handler.js +18 -14
  36. package/dist/core/data-migration.js +1434 -0
  37. package/dist/core/handoff/store.js +14 -4
  38. package/dist/core/inference/text-inference.js +42 -1
  39. package/dist/core/message/pending-hints.js +1 -1
  40. package/dist/core/message/response-engine.js +406 -167
  41. package/dist/core/message/retry-scheduler.js +71 -0
  42. package/dist/core/model/model-catalog.js +7 -2
  43. package/dist/core/model/model-fallback.js +81 -0
  44. package/dist/core/protected-paths.js +41 -8
  45. package/dist/core/session/adapters/ecagent-session-file-adapter.js +112 -0
  46. package/dist/core/session/session-fs-store.js +283 -59
  47. package/dist/core/session/session-manager.js +19 -21
  48. package/dist/core/session/session-title.js +1 -0
  49. package/dist/core/session/session-turn-coordinator.js +8 -1
  50. package/dist/core/system-channels.js +1 -0
  51. package/dist/index.js +10 -9
  52. package/dist/paths.js +35 -27
  53. package/dist/trigger/feedback.js +16 -0
  54. package/dist/trigger/legacy-session-history.js +19 -0
  55. package/dist/trigger/manager.js +12 -1
  56. package/dist/trigger/parser.js +4 -2
  57. package/dist/trigger/scheduler.js +185 -15
  58. package/dist/trigger/session-lock.js +62 -0
  59. package/dist/trigger/state.js +64 -0
  60. package/dist/trigger/validation.js +32 -2
  61. package/dist/utils/aid-bind.js +1 -1
  62. package/dist/utils/error-utils.js +9 -0
  63. package/dist/utils/welcome.js +2 -3
  64. package/ecagent/LICENSE +21 -0
  65. package/ecagent/NOTICE +11 -0
  66. package/ecagent/README.md +61 -0
  67. package/ecagent/dist/agent-loop.d.ts +24 -0
  68. package/ecagent/dist/agent-loop.d.ts.map +1 -0
  69. package/ecagent/dist/agent-loop.js +547 -0
  70. package/ecagent/dist/agent-loop.js.map +1 -0
  71. package/ecagent/dist/agent.d.ts +127 -0
  72. package/ecagent/dist/agent.d.ts.map +1 -0
  73. package/ecagent/dist/agent.js +386 -0
  74. package/ecagent/dist/agent.js.map +1 -0
  75. package/ecagent/dist/harness/agent-harness.d.ts +95 -0
  76. package/ecagent/dist/harness/agent-harness.d.ts.map +1 -0
  77. package/ecagent/dist/harness/agent-harness.js +926 -0
  78. package/ecagent/dist/harness/agent-harness.js.map +1 -0
  79. package/ecagent/dist/harness/compaction/branch-summarization.d.ts +51 -0
  80. package/ecagent/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
  81. package/ecagent/dist/harness/compaction/branch-summarization.js +174 -0
  82. package/ecagent/dist/harness/compaction/branch-summarization.js.map +1 -0
  83. package/ecagent/dist/harness/compaction/compaction.d.ts +95 -0
  84. package/ecagent/dist/harness/compaction/compaction.d.ts.map +1 -0
  85. package/ecagent/dist/harness/compaction/compaction.js +528 -0
  86. package/ecagent/dist/harness/compaction/compaction.js.map +1 -0
  87. package/ecagent/dist/harness/compaction/utils.d.ts +25 -0
  88. package/ecagent/dist/harness/compaction/utils.d.ts.map +1 -0
  89. package/ecagent/dist/harness/compaction/utils.js +131 -0
  90. package/ecagent/dist/harness/compaction/utils.js.map +1 -0
  91. package/ecagent/dist/harness/env/nodejs.d.ts +51 -0
  92. package/ecagent/dist/harness/env/nodejs.d.ts.map +1 -0
  93. package/ecagent/dist/harness/env/nodejs.js +513 -0
  94. package/ecagent/dist/harness/env/nodejs.js.map +1 -0
  95. package/ecagent/dist/harness/messages.d.ts +51 -0
  96. package/ecagent/dist/harness/messages.d.ts.map +1 -0
  97. package/ecagent/dist/harness/messages.js +102 -0
  98. package/ecagent/dist/harness/messages.js.map +1 -0
  99. package/ecagent/dist/harness/prompt-templates.d.ts +48 -0
  100. package/ecagent/dist/harness/prompt-templates.d.ts.map +1 -0
  101. package/ecagent/dist/harness/prompt-templates.js +230 -0
  102. package/ecagent/dist/harness/prompt-templates.js.map +1 -0
  103. package/ecagent/dist/harness/session/jsonl-repo.d.ts +26 -0
  104. package/ecagent/dist/harness/session/jsonl-repo.d.ts.map +1 -0
  105. package/ecagent/dist/harness/session/jsonl-repo.js +100 -0
  106. package/ecagent/dist/harness/session/jsonl-repo.js.map +1 -0
  107. package/ecagent/dist/harness/session/jsonl-storage.d.ts +34 -0
  108. package/ecagent/dist/harness/session/jsonl-storage.d.ts.map +1 -0
  109. package/ecagent/dist/harness/session/jsonl-storage.js +234 -0
  110. package/ecagent/dist/harness/session/jsonl-storage.js.map +1 -0
  111. package/ecagent/dist/harness/session/memory-repo.d.ts +18 -0
  112. package/ecagent/dist/harness/session/memory-repo.d.ts.map +1 -0
  113. package/ecagent/dist/harness/session/memory-repo.js +44 -0
  114. package/ecagent/dist/harness/session/memory-repo.js.map +1 -0
  115. package/ecagent/dist/harness/session/memory-storage.d.ts +25 -0
  116. package/ecagent/dist/harness/session/memory-storage.d.ts.map +1 -0
  117. package/ecagent/dist/harness/session/memory-storage.js +111 -0
  118. package/ecagent/dist/harness/session/memory-storage.js.map +1 -0
  119. package/ecagent/dist/harness/session/repo-utils.d.ts +11 -0
  120. package/ecagent/dist/harness/session/repo-utils.d.ts.map +1 -0
  121. package/ecagent/dist/harness/session/repo-utils.js +39 -0
  122. package/ecagent/dist/harness/session/repo-utils.js.map +1 -0
  123. package/ecagent/dist/harness/session/session.d.ts +47 -0
  124. package/ecagent/dist/harness/session/session.d.ts.map +1 -0
  125. package/ecagent/dist/harness/session/session.js +245 -0
  126. package/ecagent/dist/harness/session/session.js.map +1 -0
  127. package/ecagent/dist/harness/session/uuid.d.ts +2 -0
  128. package/ecagent/dist/harness/session/uuid.d.ts.map +1 -0
  129. package/ecagent/dist/harness/session/uuid.js +50 -0
  130. package/ecagent/dist/harness/session/uuid.js.map +1 -0
  131. package/ecagent/dist/harness/skills.d.ts +44 -0
  132. package/ecagent/dist/harness/skills.d.ts.map +1 -0
  133. package/ecagent/dist/harness/skills.js +311 -0
  134. package/ecagent/dist/harness/skills.js.map +1 -0
  135. package/ecagent/dist/harness/system-prompt.d.ts +3 -0
  136. package/ecagent/dist/harness/system-prompt.d.ts.map +1 -0
  137. package/ecagent/dist/harness/system-prompt.js +30 -0
  138. package/ecagent/dist/harness/system-prompt.js.map +1 -0
  139. package/ecagent/dist/harness/types.d.ts +619 -0
  140. package/ecagent/dist/harness/types.d.ts.map +1 -0
  141. package/ecagent/dist/harness/types.js +81 -0
  142. package/ecagent/dist/harness/types.js.map +1 -0
  143. package/ecagent/dist/harness/utils/shell-output.d.ts +14 -0
  144. package/ecagent/dist/harness/utils/shell-output.d.ts.map +1 -0
  145. package/ecagent/dist/harness/utils/shell-output.js +126 -0
  146. package/ecagent/dist/harness/utils/shell-output.js.map +1 -0
  147. package/ecagent/dist/harness/utils/truncate.d.ts +70 -0
  148. package/ecagent/dist/harness/utils/truncate.d.ts.map +1 -0
  149. package/ecagent/dist/harness/utils/truncate.js +290 -0
  150. package/ecagent/dist/harness/utils/truncate.js.map +1 -0
  151. package/ecagent/dist/index.d.ts +22 -0
  152. package/ecagent/dist/index.d.ts.map +1 -0
  153. package/ecagent/dist/index.js +27 -0
  154. package/ecagent/dist/index.js.map +1 -0
  155. package/ecagent/dist/node.d.ts +3 -0
  156. package/ecagent/dist/node.d.ts.map +1 -0
  157. package/ecagent/dist/node.js +3 -0
  158. package/ecagent/dist/node.js.map +1 -0
  159. package/ecagent/dist/proxy.d.ts +69 -0
  160. package/ecagent/dist/proxy.d.ts.map +1 -0
  161. package/ecagent/dist/proxy.js +278 -0
  162. package/ecagent/dist/proxy.js.map +1 -0
  163. package/ecagent/dist/runtime/api/lazy.d.ts +15 -0
  164. package/ecagent/dist/runtime/api/lazy.d.ts.map +1 -0
  165. package/ecagent/dist/runtime/api/lazy.js +59 -0
  166. package/ecagent/dist/runtime/api/lazy.js.map +1 -0
  167. package/ecagent/dist/runtime/api/pi-messages.d.ts +97 -0
  168. package/ecagent/dist/runtime/api/pi-messages.d.ts.map +1 -0
  169. package/ecagent/dist/runtime/api/pi-messages.js +307 -0
  170. package/ecagent/dist/runtime/api/pi-messages.js.map +1 -0
  171. package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts +3 -0
  172. package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts.map +1 -0
  173. package/ecagent/dist/runtime/api/pi-messages.lazy.js +3 -0
  174. package/ecagent/dist/runtime/api/pi-messages.lazy.js.map +1 -0
  175. package/ecagent/dist/runtime/auth/context.d.ts +7 -0
  176. package/ecagent/dist/runtime/auth/context.d.ts.map +1 -0
  177. package/ecagent/dist/runtime/auth/context.js +42 -0
  178. package/ecagent/dist/runtime/auth/context.js.map +1 -0
  179. package/ecagent/dist/runtime/auth/credential-store.d.ts +16 -0
  180. package/ecagent/dist/runtime/auth/credential-store.d.ts.map +1 -0
  181. package/ecagent/dist/runtime/auth/credential-store.js +39 -0
  182. package/ecagent/dist/runtime/auth/credential-store.js.map +1 -0
  183. package/ecagent/dist/runtime/auth/helpers.d.ts +20 -0
  184. package/ecagent/dist/runtime/auth/helpers.d.ts.map +1 -0
  185. package/ecagent/dist/runtime/auth/helpers.js +46 -0
  186. package/ecagent/dist/runtime/auth/helpers.js.map +1 -0
  187. package/ecagent/dist/runtime/auth/resolve.d.ts +26 -0
  188. package/ecagent/dist/runtime/auth/resolve.d.ts.map +1 -0
  189. package/ecagent/dist/runtime/auth/resolve.js +100 -0
  190. package/ecagent/dist/runtime/auth/resolve.js.map +1 -0
  191. package/ecagent/dist/runtime/auth/types.d.ts +180 -0
  192. package/ecagent/dist/runtime/auth/types.d.ts.map +1 -0
  193. package/ecagent/dist/runtime/auth/types.js +2 -0
  194. package/ecagent/dist/runtime/auth/types.js.map +1 -0
  195. package/ecagent/dist/runtime/compat.d.ts +28 -0
  196. package/ecagent/dist/runtime/compat.d.ts.map +1 -0
  197. package/ecagent/dist/runtime/compat.js +86 -0
  198. package/ecagent/dist/runtime/compat.js.map +1 -0
  199. package/ecagent/dist/runtime/index.d.ts +20 -0
  200. package/ecagent/dist/runtime/index.d.ts.map +1 -0
  201. package/ecagent/dist/runtime/index.js +17 -0
  202. package/ecagent/dist/runtime/index.js.map +1 -0
  203. package/ecagent/dist/runtime/models.d.ts +142 -0
  204. package/ecagent/dist/runtime/models.d.ts.map +1 -0
  205. package/ecagent/dist/runtime/models.js +245 -0
  206. package/ecagent/dist/runtime/models.js.map +1 -0
  207. package/ecagent/dist/runtime/oauth.d.ts +2 -0
  208. package/ecagent/dist/runtime/oauth.d.ts.map +1 -0
  209. package/ecagent/dist/runtime/oauth.js +2 -0
  210. package/ecagent/dist/runtime/oauth.js.map +1 -0
  211. package/ecagent/dist/runtime/providers/faux.d.ts +97 -0
  212. package/ecagent/dist/runtime/providers/faux.d.ts.map +1 -0
  213. package/ecagent/dist/runtime/providers/faux.js +395 -0
  214. package/ecagent/dist/runtime/providers/faux.js.map +1 -0
  215. package/ecagent/dist/runtime/types.d.ts +607 -0
  216. package/ecagent/dist/runtime/types.d.ts.map +1 -0
  217. package/ecagent/dist/runtime/types.js +2 -0
  218. package/ecagent/dist/runtime/types.js.map +1 -0
  219. package/ecagent/dist/runtime/utils/diagnostics.d.ts +19 -0
  220. package/ecagent/dist/runtime/utils/diagnostics.d.ts.map +1 -0
  221. package/ecagent/dist/runtime/utils/diagnostics.js +25 -0
  222. package/ecagent/dist/runtime/utils/diagnostics.js.map +1 -0
  223. package/ecagent/dist/runtime/utils/event-stream.d.ts +21 -0
  224. package/ecagent/dist/runtime/utils/event-stream.d.ts.map +1 -0
  225. package/ecagent/dist/runtime/utils/event-stream.js +77 -0
  226. package/ecagent/dist/runtime/utils/event-stream.js.map +1 -0
  227. package/ecagent/dist/runtime/utils/headers.d.ts +4 -0
  228. package/ecagent/dist/runtime/utils/headers.d.ts.map +1 -0
  229. package/ecagent/dist/runtime/utils/headers.js +18 -0
  230. package/ecagent/dist/runtime/utils/headers.js.map +1 -0
  231. package/ecagent/dist/runtime/utils/json-parse.d.ts +16 -0
  232. package/ecagent/dist/runtime/utils/json-parse.d.ts.map +1 -0
  233. package/ecagent/dist/runtime/utils/json-parse.js +113 -0
  234. package/ecagent/dist/runtime/utils/json-parse.js.map +1 -0
  235. package/ecagent/dist/runtime/utils/oauth/types.d.ts +64 -0
  236. package/ecagent/dist/runtime/utils/oauth/types.d.ts.map +1 -0
  237. package/ecagent/dist/runtime/utils/oauth/types.js +2 -0
  238. package/ecagent/dist/runtime/utils/oauth/types.js.map +1 -0
  239. package/ecagent/dist/runtime/utils/overflow.d.ts +58 -0
  240. package/ecagent/dist/runtime/utils/overflow.d.ts.map +1 -0
  241. package/ecagent/dist/runtime/utils/overflow.js +158 -0
  242. package/ecagent/dist/runtime/utils/overflow.js.map +1 -0
  243. package/ecagent/dist/runtime/utils/provider-env.d.ts +7 -0
  244. package/ecagent/dist/runtime/utils/provider-env.d.ts.map +1 -0
  245. package/ecagent/dist/runtime/utils/provider-env.js +44 -0
  246. package/ecagent/dist/runtime/utils/provider-env.js.map +1 -0
  247. package/ecagent/dist/runtime/utils/retry.d.ts +12 -0
  248. package/ecagent/dist/runtime/utils/retry.d.ts.map +1 -0
  249. package/ecagent/dist/runtime/utils/retry.js +90 -0
  250. package/ecagent/dist/runtime/utils/retry.js.map +1 -0
  251. package/ecagent/dist/runtime/utils/typebox-helpers.d.ts +17 -0
  252. package/ecagent/dist/runtime/utils/typebox-helpers.d.ts.map +1 -0
  253. package/ecagent/dist/runtime/utils/typebox-helpers.js +21 -0
  254. package/ecagent/dist/runtime/utils/typebox-helpers.js.map +1 -0
  255. package/ecagent/dist/runtime/utils/validation.d.ts +18 -0
  256. package/ecagent/dist/runtime/utils/validation.d.ts.map +1 -0
  257. package/ecagent/dist/runtime/utils/validation.js +269 -0
  258. package/ecagent/dist/runtime/utils/validation.js.map +1 -0
  259. package/ecagent/dist/types.d.ts +401 -0
  260. package/ecagent/dist/types.d.ts.map +1 -0
  261. package/ecagent/dist/types.js +2 -0
  262. package/ecagent/dist/types.js.map +1 -0
  263. package/ecagent/package.json +93 -0
  264. package/ecagent/src/agent-loop.ts +792 -0
  265. package/ecagent/src/agent.ts +575 -0
  266. package/ecagent/src/harness/agent-harness.ts +1029 -0
  267. package/ecagent/src/harness/compaction/branch-summarization.ts +261 -0
  268. package/ecagent/src/harness/compaction/compaction.ts +753 -0
  269. package/ecagent/src/harness/compaction/utils.ts +144 -0
  270. package/ecagent/src/harness/env/nodejs.ts +569 -0
  271. package/ecagent/src/harness/messages.ts +164 -0
  272. package/ecagent/src/harness/prompt-templates.ts +267 -0
  273. package/ecagent/src/harness/session/jsonl-repo.ts +179 -0
  274. package/ecagent/src/harness/session/jsonl-storage.ts +314 -0
  275. package/ecagent/src/harness/session/memory-repo.ts +50 -0
  276. package/ecagent/src/harness/session/memory-storage.ts +133 -0
  277. package/ecagent/src/harness/session/repo-utils.ts +51 -0
  278. package/ecagent/src/harness/session/session.ts +338 -0
  279. package/ecagent/src/harness/session/uuid.ts +54 -0
  280. package/ecagent/src/harness/skills.ts +375 -0
  281. package/ecagent/src/harness/system-prompt.ts +34 -0
  282. package/ecagent/src/harness/types.ts +838 -0
  283. package/ecagent/src/harness/utils/shell-output.ts +135 -0
  284. package/ecagent/src/harness/utils/truncate.ts +344 -0
  285. package/ecagent/src/index.ts +46 -0
  286. package/ecagent/src/node.ts +2 -0
  287. package/ecagent/src/proxy.ts +367 -0
  288. package/ecagent/src/runtime/api/lazy.ts +70 -0
  289. package/ecagent/src/runtime/api/pi-messages.lazy.ts +4 -0
  290. package/ecagent/src/runtime/api/pi-messages.ts +436 -0
  291. package/ecagent/src/runtime/auth/context.ts +45 -0
  292. package/ecagent/src/runtime/auth/credential-store.ts +47 -0
  293. package/ecagent/src/runtime/auth/helpers.ts +46 -0
  294. package/ecagent/src/runtime/auth/resolve.ts +141 -0
  295. package/ecagent/src/runtime/auth/types.ts +182 -0
  296. package/ecagent/src/runtime/compat.ts +158 -0
  297. package/ecagent/src/runtime/index.ts +31 -0
  298. package/ecagent/src/runtime/models.ts +452 -0
  299. package/ecagent/src/runtime/oauth.ts +1 -0
  300. package/ecagent/src/runtime/providers/faux.ts +538 -0
  301. package/ecagent/src/runtime/types.ts +718 -0
  302. package/ecagent/src/runtime/utils/diagnostics.ts +45 -0
  303. package/ecagent/src/runtime/utils/event-stream.ts +88 -0
  304. package/ecagent/src/runtime/utils/headers.ts +18 -0
  305. package/ecagent/src/runtime/utils/json-parse.ts +124 -0
  306. package/ecagent/src/runtime/utils/oauth/types.ts +79 -0
  307. package/ecagent/src/runtime/utils/overflow.ts +165 -0
  308. package/ecagent/src/runtime/utils/provider-env.ts +52 -0
  309. package/ecagent/src/runtime/utils/retry.ts +101 -0
  310. package/ecagent/src/runtime/utils/typebox-helpers.ts +24 -0
  311. package/ecagent/src/runtime/utils/validation.ts +310 -0
  312. package/ecagent/src/types.ts +430 -0
  313. package/ecagent/tsconfig.build.json +32 -0
  314. package/kits/schemas/_meta.json +7 -4
  315. package/kits/schemas/agent-config.schema.10.json +370 -0
  316. package/kits/schemas/defaults.schema.4.json +93 -0
  317. package/kits/schemas/relation-config.schema.8.json +69 -0
  318. package/kits/templates/roles/admin.json +1 -1
  319. package/package.json +22 -6
  320. package/skills/eclink/SKILL.md +70 -0
  321. package/skills/eclink/agents/openai.yaml +4 -0
  322. package/assets/.env.template +0 -4
@@ -0,0 +1,538 @@
1
+ import { createProvider, type Provider } from "../models.ts";
2
+ import type {
3
+ AssistantMessage,
4
+ AssistantMessageEventStream,
5
+ Context,
6
+ ImageContent,
7
+ Message,
8
+ Model,
9
+ SimpleStreamOptions,
10
+ StreamFunction,
11
+ StreamOptions,
12
+ TextContent,
13
+ ThinkingContent,
14
+ ToolCall,
15
+ ToolResultMessage,
16
+ Usage,
17
+ } from "../types.ts";
18
+ import { createAssistantMessageEventStream } from "../utils/event-stream.ts";
19
+
20
+ const DEFAULT_API = "faux";
21
+ const DEFAULT_PROVIDER = "faux";
22
+ const DEFAULT_MODEL_ID = "faux-1";
23
+ const DEFAULT_MODEL_NAME = "Faux Model";
24
+ const DEFAULT_BASE_URL = "http://localhost:0";
25
+ const DEFAULT_MIN_TOKEN_SIZE = 3;
26
+ const DEFAULT_MAX_TOKEN_SIZE = 5;
27
+
28
+ const DEFAULT_USAGE: Usage = {
29
+ input: 0,
30
+ output: 0,
31
+ cacheRead: 0,
32
+ cacheWrite: 0,
33
+ totalTokens: 0,
34
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
35
+ };
36
+
37
+ export interface FauxModelDefinition {
38
+ id: string;
39
+ name?: string;
40
+ reasoning?: boolean;
41
+ input?: ("text" | "image")[];
42
+ cost?: { input: number; output: number; cacheRead: number; cacheWrite: number };
43
+ contextWindow?: number;
44
+ maxTokens?: number;
45
+ }
46
+
47
+ export type FauxContentBlock = TextContent | ThinkingContent | ToolCall;
48
+
49
+ export function fauxText(text: string): TextContent {
50
+ return { type: "text", text };
51
+ }
52
+
53
+ export function fauxThinking(thinking: string): ThinkingContent {
54
+ return { type: "thinking", thinking };
55
+ }
56
+
57
+ export function fauxToolCall(name: string, arguments_: ToolCall["arguments"], options: { id?: string } = {}): ToolCall {
58
+ return {
59
+ type: "toolCall",
60
+ id: options.id ?? randomId("tool"),
61
+ name,
62
+ arguments: arguments_,
63
+ };
64
+ }
65
+
66
+ function normalizeFauxAssistantContent(content: string | FauxContentBlock | FauxContentBlock[]): FauxContentBlock[] {
67
+ if (typeof content === "string") {
68
+ return [fauxText(content)];
69
+ }
70
+ return Array.isArray(content) ? content : [content];
71
+ }
72
+
73
+ export function fauxAssistantMessage(
74
+ content: string | FauxContentBlock | FauxContentBlock[],
75
+ options: {
76
+ stopReason?: AssistantMessage["stopReason"];
77
+ errorMessage?: string;
78
+ responseId?: string;
79
+ timestamp?: number;
80
+ } = {},
81
+ ): AssistantMessage {
82
+ return {
83
+ role: "assistant",
84
+ content: normalizeFauxAssistantContent(content),
85
+ api: DEFAULT_API,
86
+ provider: DEFAULT_PROVIDER,
87
+ model: DEFAULT_MODEL_ID,
88
+ usage: DEFAULT_USAGE,
89
+ stopReason: options.stopReason ?? "stop",
90
+ errorMessage: options.errorMessage,
91
+ responseId: options.responseId,
92
+ timestamp: options.timestamp ?? Date.now(),
93
+ };
94
+ }
95
+
96
+ export type FauxResponseFactory = (
97
+ context: Context,
98
+ options: StreamOptions | undefined,
99
+ state: { callCount: number },
100
+ model: Model<string>,
101
+ ) => AssistantMessage | Promise<AssistantMessage>;
102
+
103
+ export type FauxResponseStep = AssistantMessage | FauxResponseFactory;
104
+
105
+ export interface RegisterFauxProviderOptions {
106
+ api?: string;
107
+ provider?: string;
108
+ models?: FauxModelDefinition[];
109
+ tokensPerSecond?: number;
110
+ tokenSize?: {
111
+ min?: number;
112
+ max?: number;
113
+ };
114
+ }
115
+
116
+ export interface FauxProviderRegistration {
117
+ api: string;
118
+ models: [Model<string>, ...Model<string>[]];
119
+ getModel(): Model<string>;
120
+ getModel(modelId: string): Model<string> | undefined;
121
+ state: { callCount: number };
122
+ setResponses: (responses: FauxResponseStep[]) => void;
123
+ appendResponses: (responses: FauxResponseStep[]) => void;
124
+ getPendingResponseCount: () => number;
125
+ unregister: () => void;
126
+ }
127
+
128
+ export interface FauxProviderHandle {
129
+ provider: Provider;
130
+ api: string;
131
+ models: [Model<string>, ...Model<string>[]];
132
+ getModel(): Model<string>;
133
+ getModel(modelId: string): Model<string> | undefined;
134
+ state: { callCount: number };
135
+ setResponses: (responses: FauxResponseStep[]) => void;
136
+ appendResponses: (responses: FauxResponseStep[]) => void;
137
+ getPendingResponseCount: () => number;
138
+ }
139
+
140
+ function estimateTokens(text: string): number {
141
+ return Math.ceil(text.length / 4);
142
+ }
143
+
144
+ function randomId(prefix: string): string {
145
+ return `${prefix}:${Date.now()}:${Math.random().toString(36).slice(2)}`;
146
+ }
147
+
148
+ function contentToText(content: string | Array<TextContent | ImageContent>): string {
149
+ if (typeof content === "string") {
150
+ return content;
151
+ }
152
+ return content
153
+ .map((block) => {
154
+ if (block.type === "text") {
155
+ return block.text;
156
+ }
157
+ return `[image:${block.mimeType}:${block.data.length}]`;
158
+ })
159
+ .join("\n");
160
+ }
161
+
162
+ function assistantContentToText(content: Array<TextContent | ThinkingContent | ToolCall>): string {
163
+ return content
164
+ .map((block) => {
165
+ if (block.type === "text") {
166
+ return block.text;
167
+ }
168
+ if (block.type === "thinking") {
169
+ return block.thinking;
170
+ }
171
+ return `${block.name}:${JSON.stringify(block.arguments)}`;
172
+ })
173
+ .join("\n");
174
+ }
175
+
176
+ function toolResultToText(message: ToolResultMessage): string {
177
+ return [message.toolName, ...message.content.map((block) => contentToText([block]))].join("\n");
178
+ }
179
+
180
+ function messageToText(message: Message): string {
181
+ if (message.role === "user") {
182
+ return contentToText(message.content);
183
+ }
184
+ if (message.role === "assistant") {
185
+ return assistantContentToText(message.content);
186
+ }
187
+ return toolResultToText(message);
188
+ }
189
+
190
+ function serializeContext(context: Context): string {
191
+ const parts: string[] = [];
192
+ if (context.systemPrompt) {
193
+ parts.push(`system:${context.systemPrompt}`);
194
+ }
195
+ for (const message of context.messages) {
196
+ parts.push(`${message.role}:${messageToText(message)}`);
197
+ }
198
+ if (context.tools?.length) {
199
+ parts.push(`tools:${JSON.stringify(context.tools)}`);
200
+ }
201
+ return parts.join("\n\n");
202
+ }
203
+
204
+ function commonPrefixLength(a: string, b: string): number {
205
+ const length = Math.min(a.length, b.length);
206
+ let index = 0;
207
+ while (index < length && a[index] === b[index]) {
208
+ index++;
209
+ }
210
+ return index;
211
+ }
212
+
213
+ function withUsageEstimate(
214
+ message: AssistantMessage,
215
+ context: Context,
216
+ options: StreamOptions | undefined,
217
+ promptCache: Map<string, string>,
218
+ ): AssistantMessage {
219
+ const promptText = serializeContext(context);
220
+ const promptTokens = estimateTokens(promptText);
221
+ const outputTokens = estimateTokens(assistantContentToText(message.content));
222
+ let input = promptTokens;
223
+ let cacheRead = 0;
224
+ let cacheWrite = 0;
225
+ const sessionId = options?.sessionId;
226
+
227
+ if (sessionId && options?.cacheRetention !== "none") {
228
+ const previousPrompt = promptCache.get(sessionId);
229
+ if (previousPrompt) {
230
+ const cachedChars = commonPrefixLength(previousPrompt, promptText);
231
+ cacheRead = estimateTokens(previousPrompt.slice(0, cachedChars));
232
+ cacheWrite = estimateTokens(promptText.slice(cachedChars));
233
+ input = Math.max(0, promptTokens - cacheRead);
234
+ } else {
235
+ cacheWrite = promptTokens;
236
+ }
237
+ promptCache.set(sessionId, promptText);
238
+ }
239
+
240
+ return {
241
+ ...message,
242
+ usage: {
243
+ input,
244
+ output: outputTokens,
245
+ cacheRead,
246
+ cacheWrite,
247
+ totalTokens: input + outputTokens + cacheRead + cacheWrite,
248
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
249
+ },
250
+ };
251
+ }
252
+
253
+ function splitStringByTokenSize(text: string, minTokenSize: number, maxTokenSize: number): string[] {
254
+ const chunks: string[] = [];
255
+ let index = 0;
256
+ while (index < text.length) {
257
+ const tokenSize = minTokenSize + Math.floor(Math.random() * (maxTokenSize - minTokenSize + 1));
258
+ const charSize = Math.max(1, tokenSize * 4);
259
+ chunks.push(text.slice(index, index + charSize));
260
+ index += charSize;
261
+ }
262
+ return chunks.length > 0 ? chunks : [""];
263
+ }
264
+
265
+ function cloneMessage(message: AssistantMessage, api: string, provider: string, modelId: string): AssistantMessage {
266
+ const cloned = structuredClone(message);
267
+ return {
268
+ ...cloned,
269
+ api,
270
+ provider,
271
+ model: modelId,
272
+ timestamp: cloned.timestamp ?? Date.now(),
273
+ usage: cloned.usage ?? DEFAULT_USAGE,
274
+ };
275
+ }
276
+
277
+ function createErrorMessage(error: unknown, api: string, provider: string, modelId: string): AssistantMessage {
278
+ return {
279
+ role: "assistant",
280
+ content: [],
281
+ api,
282
+ provider,
283
+ model: modelId,
284
+ usage: DEFAULT_USAGE,
285
+ stopReason: "error",
286
+ errorMessage: error instanceof Error ? error.message : String(error),
287
+ timestamp: Date.now(),
288
+ };
289
+ }
290
+
291
+ function createAbortedMessage(partial: AssistantMessage): AssistantMessage {
292
+ return {
293
+ ...partial,
294
+ stopReason: "aborted",
295
+ errorMessage: "Request was aborted",
296
+ timestamp: Date.now(),
297
+ };
298
+ }
299
+
300
+ function scheduleChunk(chunk: string, tokensPerSecond: number | undefined): Promise<void> {
301
+ if (!tokensPerSecond || tokensPerSecond <= 0) {
302
+ return new Promise((resolve) => queueMicrotask(resolve));
303
+ }
304
+ const delayMs = (estimateTokens(chunk) / tokensPerSecond) * 1000;
305
+ return new Promise((resolve) => setTimeout(resolve, delayMs));
306
+ }
307
+
308
+ async function streamWithDeltas(
309
+ stream: AssistantMessageEventStream,
310
+ message: AssistantMessage,
311
+ minTokenSize: number,
312
+ maxTokenSize: number,
313
+ tokensPerSecond: number | undefined,
314
+ signal: AbortSignal | undefined,
315
+ ): Promise<void> {
316
+ const partial: AssistantMessage = { ...message, content: [] };
317
+ if (signal?.aborted) {
318
+ const aborted = createAbortedMessage(partial);
319
+ stream.push({ type: "error", reason: "aborted", error: aborted });
320
+ stream.end(aborted);
321
+ return;
322
+ }
323
+
324
+ stream.push({ type: "start", partial: { ...partial } });
325
+
326
+ for (let index = 0; index < message.content.length; index++) {
327
+ if (signal?.aborted) {
328
+ const aborted = createAbortedMessage(partial);
329
+ stream.push({ type: "error", reason: "aborted", error: aborted });
330
+ stream.end(aborted);
331
+ return;
332
+ }
333
+
334
+ const block = message.content[index];
335
+
336
+ if (block.type === "thinking") {
337
+ partial.content = [...partial.content, { type: "thinking", thinking: "" }];
338
+ stream.push({ type: "thinking_start", contentIndex: index, partial: { ...partial } });
339
+ for (const chunk of splitStringByTokenSize(block.thinking, minTokenSize, maxTokenSize)) {
340
+ await scheduleChunk(chunk, tokensPerSecond);
341
+ if (signal?.aborted) {
342
+ const aborted = createAbortedMessage(partial);
343
+ stream.push({ type: "error", reason: "aborted", error: aborted });
344
+ stream.end(aborted);
345
+ return;
346
+ }
347
+ (partial.content[index] as ThinkingContent).thinking += chunk;
348
+ stream.push({ type: "thinking_delta", contentIndex: index, delta: chunk, partial: { ...partial } });
349
+ }
350
+ stream.push({
351
+ type: "thinking_end",
352
+ contentIndex: index,
353
+ content: block.thinking,
354
+ partial: { ...partial },
355
+ });
356
+ continue;
357
+ }
358
+
359
+ if (block.type === "text") {
360
+ partial.content = [...partial.content, { type: "text", text: "" }];
361
+ stream.push({ type: "text_start", contentIndex: index, partial: { ...partial } });
362
+ for (const chunk of splitStringByTokenSize(block.text, minTokenSize, maxTokenSize)) {
363
+ await scheduleChunk(chunk, tokensPerSecond);
364
+ if (signal?.aborted) {
365
+ const aborted = createAbortedMessage(partial);
366
+ stream.push({ type: "error", reason: "aborted", error: aborted });
367
+ stream.end(aborted);
368
+ return;
369
+ }
370
+ (partial.content[index] as TextContent).text += chunk;
371
+ stream.push({ type: "text_delta", contentIndex: index, delta: chunk, partial: { ...partial } });
372
+ }
373
+ stream.push({ type: "text_end", contentIndex: index, content: block.text, partial: { ...partial } });
374
+ continue;
375
+ }
376
+
377
+ partial.content = [...partial.content, { type: "toolCall", id: block.id, name: block.name, arguments: {} }];
378
+ stream.push({ type: "toolcall_start", contentIndex: index, partial: { ...partial } });
379
+ for (const chunk of splitStringByTokenSize(JSON.stringify(block.arguments), minTokenSize, maxTokenSize)) {
380
+ await scheduleChunk(chunk, tokensPerSecond);
381
+ if (signal?.aborted) {
382
+ const aborted = createAbortedMessage(partial);
383
+ stream.push({ type: "error", reason: "aborted", error: aborted });
384
+ stream.end(aborted);
385
+ return;
386
+ }
387
+ stream.push({ type: "toolcall_delta", contentIndex: index, delta: chunk, partial: { ...partial } });
388
+ }
389
+ (partial.content[index] as ToolCall).arguments = block.arguments;
390
+ stream.push({ type: "toolcall_end", contentIndex: index, toolCall: block, partial: { ...partial } });
391
+ }
392
+
393
+ if (message.stopReason === "error" || message.stopReason === "aborted") {
394
+ stream.push({ type: "error", reason: message.stopReason, error: message });
395
+ stream.end(message);
396
+ return;
397
+ }
398
+
399
+ stream.push({ type: "done", reason: message.stopReason, message });
400
+ stream.end(message);
401
+ }
402
+
403
+ export function createFauxCore(options: RegisterFauxProviderOptions) {
404
+ const api = options.api ?? randomId(DEFAULT_API);
405
+ const provider = options.provider ?? DEFAULT_PROVIDER;
406
+ const minTokenSize = Math.max(
407
+ 1,
408
+ Math.min(options.tokenSize?.min ?? DEFAULT_MIN_TOKEN_SIZE, options.tokenSize?.max ?? DEFAULT_MAX_TOKEN_SIZE),
409
+ );
410
+ const maxTokenSize = Math.max(minTokenSize, options.tokenSize?.max ?? DEFAULT_MAX_TOKEN_SIZE);
411
+ let pendingResponses: FauxResponseStep[] = [];
412
+ const tokensPerSecond = options.tokensPerSecond;
413
+ const state = { callCount: 0 };
414
+ const promptCache = new Map<string, string>();
415
+
416
+ const modelDefinitions = options.models?.length
417
+ ? options.models
418
+ : [
419
+ {
420
+ id: DEFAULT_MODEL_ID,
421
+ name: DEFAULT_MODEL_NAME,
422
+ reasoning: false,
423
+ input: ["text", "image"] as ("text" | "image")[],
424
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
425
+ contextWindow: 128000,
426
+ maxTokens: 16384,
427
+ },
428
+ ];
429
+ const models = modelDefinitions.map((definition) => ({
430
+ id: definition.id,
431
+ name: definition.name ?? definition.id,
432
+ api,
433
+ provider,
434
+ baseUrl: DEFAULT_BASE_URL,
435
+ reasoning: definition.reasoning ?? false,
436
+ input: definition.input ?? ["text", "image"],
437
+ cost: definition.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
438
+ contextWindow: definition.contextWindow ?? 128000,
439
+ maxTokens: definition.maxTokens ?? 16384,
440
+ })) as [Model<string>, ...Model<string>[]];
441
+
442
+ const stream: StreamFunction<string, StreamOptions> = (requestModel, context, streamOptions) => {
443
+ const outer = createAssistantMessageEventStream();
444
+ const step = pendingResponses.shift();
445
+ state.callCount++;
446
+
447
+ queueMicrotask(async () => {
448
+ try {
449
+ await streamOptions?.onResponse?.({ status: 200, headers: {} }, requestModel);
450
+ if (!step) {
451
+ let message = createErrorMessage(
452
+ new Error("No more faux responses queued"),
453
+ api,
454
+ provider,
455
+ requestModel.id,
456
+ );
457
+ message = withUsageEstimate(message, context, streamOptions, promptCache);
458
+ outer.push({ type: "error", reason: "error", error: message });
459
+ outer.end(message);
460
+ return;
461
+ }
462
+
463
+ const resolved =
464
+ typeof step === "function" ? await step(context, streamOptions, state, requestModel) : step;
465
+ let message = cloneMessage(resolved, api, provider, requestModel.id);
466
+ message = withUsageEstimate(message, context, streamOptions, promptCache);
467
+ await streamWithDeltas(outer, message, minTokenSize, maxTokenSize, tokensPerSecond, streamOptions?.signal);
468
+ } catch (error) {
469
+ const message = createErrorMessage(error, api, provider, requestModel.id);
470
+ outer.push({ type: "error", reason: "error", error: message });
471
+ outer.end(message);
472
+ }
473
+ });
474
+
475
+ return outer;
476
+ };
477
+
478
+ const streamSimple: StreamFunction<string, SimpleStreamOptions> = (streamModel, context, streamOptions) =>
479
+ stream(streamModel, context, streamOptions);
480
+
481
+ function getModel(): Model<string>;
482
+ function getModel(requestedModelId: string): Model<string> | undefined;
483
+ function getModel(requestedModelId?: string): Model<string> | undefined {
484
+ if (!requestedModelId) {
485
+ return models[0];
486
+ }
487
+ return models.find((candidate) => candidate.id === requestedModelId);
488
+ }
489
+
490
+ return {
491
+ api,
492
+ provider,
493
+ models,
494
+ stream,
495
+ streamSimple,
496
+ getModel,
497
+ state,
498
+ setResponses(responses: FauxResponseStep[]) {
499
+ pendingResponses = [...responses];
500
+ },
501
+ appendResponses(responses: FauxResponseStep[]) {
502
+ pendingResponses.push(...responses);
503
+ },
504
+ getPendingResponseCount() {
505
+ return pendingResponses.length;
506
+ },
507
+ };
508
+ }
509
+
510
+ /**
511
+ * Faux provider for tests built on explicit `Models` collections:
512
+ *
513
+ * ```ts
514
+ * const faux = fauxProvider();
515
+ * const models = createModels();
516
+ * models.setProvider(faux.provider);
517
+ * faux.setResponses([fauxAssistantMessage("hi")]);
518
+ * ```
519
+ */
520
+ export function fauxProvider(options: RegisterFauxProviderOptions = {}): FauxProviderHandle {
521
+ const core = createFauxCore(options);
522
+ const provider = createProvider({
523
+ id: core.provider,
524
+ auth: { apiKey: { name: "Faux", resolve: async () => ({ auth: {} }) } },
525
+ models: core.models,
526
+ api: { stream: core.stream, streamSimple: core.streamSimple },
527
+ });
528
+ return {
529
+ provider,
530
+ api: core.api,
531
+ models: core.models,
532
+ getModel: core.getModel,
533
+ state: core.state,
534
+ setResponses: core.setResponses,
535
+ appendResponses: core.appendResponses,
536
+ getPendingResponseCount: core.getPendingResponseCount,
537
+ };
538
+ }