evolcore 0.0.8 → 0.0.10

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