evolcore 0.0.7 → 0.0.9

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 (230) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +38 -4
  3. package/dist/agents/baseagent.js +117 -24
  4. package/dist/agents/claude-runner.js +31 -2
  5. package/dist/agents/codex-app-server-client.js +37 -2
  6. package/dist/agents/codex-runner.js +11 -3
  7. package/dist/aun/aid/control-aid.js +40 -27
  8. package/dist/aun/aid/domain.js +23 -0
  9. package/dist/aun/msg/group.js +9 -9
  10. package/dist/aun/msg/p2p.js +11 -6
  11. package/dist/aun/msg/upload.js +43 -29
  12. package/dist/aun/outbox.js +48 -5
  13. package/dist/channels/aun.js +225 -71
  14. package/dist/cli/aun-commands.js +10 -7
  15. package/dist/cli/bench.js +4 -3
  16. package/dist/cli/daemon-commands.js +208 -68
  17. package/dist/cli/data-command.js +62 -35
  18. package/dist/cli/index.js +11 -1
  19. package/dist/cli/init-channel.js +1 -1
  20. package/dist/cli/init.js +112 -17
  21. package/dist/cli/model.js +5 -5
  22. package/dist/cli/restart-monitor.js +102 -22
  23. package/dist/config/builtin-role-templates.js +4 -3
  24. package/dist/config/config-field-policy.js +6 -2
  25. package/dist/config/config-manager.js +79 -11
  26. package/dist/config/gateway-config.js +80 -35
  27. package/dist/config/role-schema.js +18 -3
  28. package/dist/config/role-service.js +3 -3
  29. package/dist/config/role-store.js +4 -3
  30. package/dist/config-store.js +19 -3
  31. package/dist/core/auth/operation-authorizer.js +3 -1
  32. package/dist/core/baseagent-loader.js +5 -3
  33. package/dist/core/bootstrap-service.js +0 -12
  34. package/dist/core/capability/providers/codex-capability-provider.js +2 -2
  35. package/dist/core/channel-loader.js +15 -2
  36. package/dist/core/command/command-handler.js +1 -1
  37. package/dist/core/command/evol-menu-version-gate.js +3 -2
  38. package/dist/core/command/menu-handler.js +50 -28
  39. package/dist/core/command/menu-protocol.js +7 -4
  40. package/dist/core/command/slash-handler.js +4 -4
  41. package/dist/core/data-migration.js +517 -24
  42. package/dist/core/inference/text-inference.js +18 -5
  43. package/dist/core/message/message-bridge.js +17 -6
  44. package/dist/core/message/response-engine.js +22 -3
  45. package/dist/core/model/config-scope.js +3 -3
  46. package/dist/core/permission/tool-policy.js +18 -0
  47. package/dist/core/protected-paths.js +12 -1
  48. package/dist/index.js +794 -725
  49. package/dist/ipc.js +3 -1
  50. package/dist/product.js +1 -0
  51. package/dist/utils/autostart.js +27 -0
  52. package/dist/utils/codex-cli.js +39 -0
  53. package/dist/utils/cross-platform.js +147 -18
  54. package/dist/utils/ecweb-utils.js +15 -2
  55. package/dist/utils/instance-registry.js +43 -6
  56. package/dist/utils/macos-autostart.js +179 -0
  57. package/dist/utils/process-introspect.js +7 -3
  58. package/dist/utils/windows-autostart.js +130 -0
  59. package/ecagent/dist/agent-loop.d.ts +1 -1
  60. package/ecagent/dist/agent-loop.js +1 -1
  61. package/ecagent/dist/agent.d.ts +1 -1
  62. package/ecagent/dist/agent.js +1 -1
  63. package/ecagent/dist/harness/agent-harness.d.ts +1 -1
  64. package/ecagent/dist/harness/compaction/branch-summarization.d.ts +1 -1
  65. package/ecagent/dist/harness/compaction/compaction.d.ts +1 -1
  66. package/ecagent/dist/harness/compaction/utils.d.ts +1 -1
  67. package/ecagent/dist/harness/messages.d.ts +1 -1
  68. package/ecagent/dist/harness/session/session.d.ts +1 -1
  69. package/ecagent/dist/harness/types.d.ts +1 -1
  70. package/ecagent/dist/proxy.d.ts +1 -1
  71. package/ecagent/dist/proxy.js +1 -1
  72. package/ecagent/dist/types.d.ts +1 -1
  73. package/kits/migrations/migrate-role-config-v5.mjs +27 -2
  74. package/kits/rules/01-overview.md +3 -2
  75. package/kits/schemas/_meta.json +2 -1
  76. package/kits/schemas/agent-config.schema.10.json +19 -1
  77. package/kits/schemas/daemon.schema.4.json +136 -0
  78. package/kits/schemas/defaults.schema.4.json +15 -1
  79. package/kits/schemas/relation-config.schema.8.json +22 -1
  80. package/kits/schemas/role-config.schema.1.json +2 -2
  81. package/package.json +2 -4
  82. package/ecagent/dist/agent-loop.d.ts.map +0 -1
  83. package/ecagent/dist/agent-loop.js.map +0 -1
  84. package/ecagent/dist/agent.d.ts.map +0 -1
  85. package/ecagent/dist/agent.js.map +0 -1
  86. package/ecagent/dist/harness/agent-harness.d.ts.map +0 -1
  87. package/ecagent/dist/harness/agent-harness.js.map +0 -1
  88. package/ecagent/dist/harness/compaction/branch-summarization.d.ts.map +0 -1
  89. package/ecagent/dist/harness/compaction/branch-summarization.js.map +0 -1
  90. package/ecagent/dist/harness/compaction/compaction.d.ts.map +0 -1
  91. package/ecagent/dist/harness/compaction/compaction.js.map +0 -1
  92. package/ecagent/dist/harness/compaction/utils.d.ts.map +0 -1
  93. package/ecagent/dist/harness/compaction/utils.js.map +0 -1
  94. package/ecagent/dist/harness/env/nodejs.d.ts.map +0 -1
  95. package/ecagent/dist/harness/env/nodejs.js.map +0 -1
  96. package/ecagent/dist/harness/messages.d.ts.map +0 -1
  97. package/ecagent/dist/harness/messages.js.map +0 -1
  98. package/ecagent/dist/harness/prompt-templates.d.ts.map +0 -1
  99. package/ecagent/dist/harness/prompt-templates.js.map +0 -1
  100. package/ecagent/dist/harness/session/jsonl-repo.d.ts.map +0 -1
  101. package/ecagent/dist/harness/session/jsonl-repo.js.map +0 -1
  102. package/ecagent/dist/harness/session/jsonl-storage.d.ts.map +0 -1
  103. package/ecagent/dist/harness/session/jsonl-storage.js.map +0 -1
  104. package/ecagent/dist/harness/session/memory-repo.d.ts.map +0 -1
  105. package/ecagent/dist/harness/session/memory-repo.js.map +0 -1
  106. package/ecagent/dist/harness/session/memory-storage.d.ts.map +0 -1
  107. package/ecagent/dist/harness/session/memory-storage.js.map +0 -1
  108. package/ecagent/dist/harness/session/repo-utils.d.ts.map +0 -1
  109. package/ecagent/dist/harness/session/repo-utils.js.map +0 -1
  110. package/ecagent/dist/harness/session/session.d.ts.map +0 -1
  111. package/ecagent/dist/harness/session/session.js.map +0 -1
  112. package/ecagent/dist/harness/session/uuid.d.ts.map +0 -1
  113. package/ecagent/dist/harness/session/uuid.js.map +0 -1
  114. package/ecagent/dist/harness/skills.d.ts.map +0 -1
  115. package/ecagent/dist/harness/skills.js.map +0 -1
  116. package/ecagent/dist/harness/system-prompt.d.ts.map +0 -1
  117. package/ecagent/dist/harness/system-prompt.js.map +0 -1
  118. package/ecagent/dist/harness/types.d.ts.map +0 -1
  119. package/ecagent/dist/harness/types.js.map +0 -1
  120. package/ecagent/dist/harness/utils/shell-output.d.ts.map +0 -1
  121. package/ecagent/dist/harness/utils/shell-output.js.map +0 -1
  122. package/ecagent/dist/harness/utils/truncate.d.ts.map +0 -1
  123. package/ecagent/dist/harness/utils/truncate.js.map +0 -1
  124. package/ecagent/dist/index.d.ts.map +0 -1
  125. package/ecagent/dist/index.js.map +0 -1
  126. package/ecagent/dist/node.d.ts.map +0 -1
  127. package/ecagent/dist/node.js.map +0 -1
  128. package/ecagent/dist/proxy.d.ts.map +0 -1
  129. package/ecagent/dist/proxy.js.map +0 -1
  130. package/ecagent/dist/runtime/api/lazy.d.ts.map +0 -1
  131. package/ecagent/dist/runtime/api/lazy.js.map +0 -1
  132. package/ecagent/dist/runtime/api/pi-messages.d.ts.map +0 -1
  133. package/ecagent/dist/runtime/api/pi-messages.js.map +0 -1
  134. package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts.map +0 -1
  135. package/ecagent/dist/runtime/api/pi-messages.lazy.js.map +0 -1
  136. package/ecagent/dist/runtime/auth/context.d.ts.map +0 -1
  137. package/ecagent/dist/runtime/auth/context.js.map +0 -1
  138. package/ecagent/dist/runtime/auth/credential-store.d.ts.map +0 -1
  139. package/ecagent/dist/runtime/auth/credential-store.js.map +0 -1
  140. package/ecagent/dist/runtime/auth/helpers.d.ts.map +0 -1
  141. package/ecagent/dist/runtime/auth/helpers.js.map +0 -1
  142. package/ecagent/dist/runtime/auth/resolve.d.ts.map +0 -1
  143. package/ecagent/dist/runtime/auth/resolve.js.map +0 -1
  144. package/ecagent/dist/runtime/auth/types.d.ts.map +0 -1
  145. package/ecagent/dist/runtime/auth/types.js.map +0 -1
  146. package/ecagent/dist/runtime/compat.d.ts.map +0 -1
  147. package/ecagent/dist/runtime/compat.js.map +0 -1
  148. package/ecagent/dist/runtime/index.d.ts.map +0 -1
  149. package/ecagent/dist/runtime/index.js.map +0 -1
  150. package/ecagent/dist/runtime/models.d.ts.map +0 -1
  151. package/ecagent/dist/runtime/models.js.map +0 -1
  152. package/ecagent/dist/runtime/oauth.d.ts.map +0 -1
  153. package/ecagent/dist/runtime/oauth.js.map +0 -1
  154. package/ecagent/dist/runtime/providers/faux.d.ts.map +0 -1
  155. package/ecagent/dist/runtime/providers/faux.js.map +0 -1
  156. package/ecagent/dist/runtime/types.d.ts.map +0 -1
  157. package/ecagent/dist/runtime/types.js.map +0 -1
  158. package/ecagent/dist/runtime/utils/diagnostics.d.ts.map +0 -1
  159. package/ecagent/dist/runtime/utils/diagnostics.js.map +0 -1
  160. package/ecagent/dist/runtime/utils/event-stream.d.ts.map +0 -1
  161. package/ecagent/dist/runtime/utils/event-stream.js.map +0 -1
  162. package/ecagent/dist/runtime/utils/headers.d.ts.map +0 -1
  163. package/ecagent/dist/runtime/utils/headers.js.map +0 -1
  164. package/ecagent/dist/runtime/utils/json-parse.d.ts.map +0 -1
  165. package/ecagent/dist/runtime/utils/json-parse.js.map +0 -1
  166. package/ecagent/dist/runtime/utils/oauth/types.d.ts.map +0 -1
  167. package/ecagent/dist/runtime/utils/oauth/types.js.map +0 -1
  168. package/ecagent/dist/runtime/utils/overflow.d.ts.map +0 -1
  169. package/ecagent/dist/runtime/utils/overflow.js.map +0 -1
  170. package/ecagent/dist/runtime/utils/provider-env.d.ts.map +0 -1
  171. package/ecagent/dist/runtime/utils/provider-env.js.map +0 -1
  172. package/ecagent/dist/runtime/utils/retry.d.ts.map +0 -1
  173. package/ecagent/dist/runtime/utils/retry.js.map +0 -1
  174. package/ecagent/dist/runtime/utils/typebox-helpers.d.ts.map +0 -1
  175. package/ecagent/dist/runtime/utils/typebox-helpers.js.map +0 -1
  176. package/ecagent/dist/runtime/utils/validation.d.ts.map +0 -1
  177. package/ecagent/dist/runtime/utils/validation.js.map +0 -1
  178. package/ecagent/dist/types.d.ts.map +0 -1
  179. package/ecagent/dist/types.js.map +0 -1
  180. package/ecagent/package.json +0 -93
  181. package/ecagent/src/agent-loop.ts +0 -792
  182. package/ecagent/src/agent.ts +0 -575
  183. package/ecagent/src/harness/agent-harness.ts +0 -1029
  184. package/ecagent/src/harness/compaction/branch-summarization.ts +0 -261
  185. package/ecagent/src/harness/compaction/compaction.ts +0 -753
  186. package/ecagent/src/harness/compaction/utils.ts +0 -144
  187. package/ecagent/src/harness/env/nodejs.ts +0 -569
  188. package/ecagent/src/harness/messages.ts +0 -164
  189. package/ecagent/src/harness/prompt-templates.ts +0 -267
  190. package/ecagent/src/harness/session/jsonl-repo.ts +0 -179
  191. package/ecagent/src/harness/session/jsonl-storage.ts +0 -314
  192. package/ecagent/src/harness/session/memory-repo.ts +0 -50
  193. package/ecagent/src/harness/session/memory-storage.ts +0 -133
  194. package/ecagent/src/harness/session/repo-utils.ts +0 -51
  195. package/ecagent/src/harness/session/session.ts +0 -338
  196. package/ecagent/src/harness/session/uuid.ts +0 -54
  197. package/ecagent/src/harness/skills.ts +0 -375
  198. package/ecagent/src/harness/system-prompt.ts +0 -34
  199. package/ecagent/src/harness/types.ts +0 -838
  200. package/ecagent/src/harness/utils/shell-output.ts +0 -135
  201. package/ecagent/src/harness/utils/truncate.ts +0 -344
  202. package/ecagent/src/index.ts +0 -46
  203. package/ecagent/src/node.ts +0 -2
  204. package/ecagent/src/proxy.ts +0 -367
  205. package/ecagent/src/runtime/api/lazy.ts +0 -70
  206. package/ecagent/src/runtime/api/pi-messages.lazy.ts +0 -4
  207. package/ecagent/src/runtime/api/pi-messages.ts +0 -436
  208. package/ecagent/src/runtime/auth/context.ts +0 -45
  209. package/ecagent/src/runtime/auth/credential-store.ts +0 -47
  210. package/ecagent/src/runtime/auth/helpers.ts +0 -46
  211. package/ecagent/src/runtime/auth/resolve.ts +0 -141
  212. package/ecagent/src/runtime/auth/types.ts +0 -182
  213. package/ecagent/src/runtime/compat.ts +0 -158
  214. package/ecagent/src/runtime/index.ts +0 -31
  215. package/ecagent/src/runtime/models.ts +0 -452
  216. package/ecagent/src/runtime/oauth.ts +0 -1
  217. package/ecagent/src/runtime/providers/faux.ts +0 -538
  218. package/ecagent/src/runtime/types.ts +0 -718
  219. package/ecagent/src/runtime/utils/diagnostics.ts +0 -45
  220. package/ecagent/src/runtime/utils/event-stream.ts +0 -88
  221. package/ecagent/src/runtime/utils/headers.ts +0 -18
  222. package/ecagent/src/runtime/utils/json-parse.ts +0 -124
  223. package/ecagent/src/runtime/utils/oauth/types.ts +0 -79
  224. package/ecagent/src/runtime/utils/overflow.ts +0 -165
  225. package/ecagent/src/runtime/utils/provider-env.ts +0 -52
  226. package/ecagent/src/runtime/utils/retry.ts +0 -101
  227. package/ecagent/src/runtime/utils/typebox-helpers.ts +0 -24
  228. package/ecagent/src/runtime/utils/validation.ts +0 -310
  229. package/ecagent/src/types.ts +0 -430
  230. package/ecagent/tsconfig.build.json +0 -32
@@ -1,538 +0,0 @@
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
- }