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