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,575 +0,0 @@
1
- import {
2
- type ImageContent,
3
- type Message,
4
- type Model,
5
- type SimpleStreamOptions,
6
- streamSimple,
7
- type TextContent,
8
- type ThinkingBudgets,
9
- type Transport,
10
- } from "ecagent/runtime/compat";
11
- import { runAgentLoop, runAgentLoopContinue } from "./agent-loop.ts";
12
- import type {
13
- AfterToolCallContext,
14
- AfterToolCallResult,
15
- AgentContext,
16
- AgentEvent,
17
- AgentLoopConfig,
18
- AgentLoopTurnUpdate,
19
- AgentMessage,
20
- AgentState,
21
- AgentTool,
22
- BeforeToolCallContext,
23
- BeforeToolCallResult,
24
- PrepareNextTurnContext,
25
- QueueMode,
26
- StreamFn,
27
- ToolExecutionMode,
28
- } from "./types.ts";
29
-
30
- export type { QueueMode } from "./types.ts";
31
-
32
- function defaultConvertToLlm(messages: AgentMessage[]): Message[] {
33
- return messages.filter(
34
- (message) => message.role === "user" || message.role === "assistant" || message.role === "toolResult",
35
- );
36
- }
37
-
38
- const EMPTY_USAGE = {
39
- input: 0,
40
- output: 0,
41
- cacheRead: 0,
42
- cacheWrite: 0,
43
- totalTokens: 0,
44
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
45
- };
46
-
47
- const DEFAULT_MODEL = {
48
- id: "unknown",
49
- name: "unknown",
50
- api: "unknown",
51
- provider: "unknown",
52
- baseUrl: "",
53
- reasoning: false,
54
- input: [],
55
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
56
- contextWindow: 0,
57
- maxTokens: 0,
58
- } satisfies Model<any>;
59
-
60
- type MutableAgentState = Omit<AgentState, "isStreaming" | "streamingMessage" | "pendingToolCalls" | "errorMessage"> & {
61
- isStreaming: boolean;
62
- streamingMessage?: AgentMessage;
63
- pendingToolCalls: Set<string>;
64
- errorMessage?: string;
65
- };
66
-
67
- function createMutableAgentState(
68
- initialState?: Partial<Omit<AgentState, "pendingToolCalls" | "isStreaming" | "streamingMessage" | "errorMessage">>,
69
- ): MutableAgentState {
70
- let tools = initialState?.tools?.slice() ?? [];
71
- let messages = initialState?.messages?.slice() ?? [];
72
-
73
- return {
74
- systemPrompt: initialState?.systemPrompt ?? "",
75
- model: initialState?.model ?? DEFAULT_MODEL,
76
- thinkingLevel: initialState?.thinkingLevel ?? "off",
77
- get tools() {
78
- return tools;
79
- },
80
- set tools(nextTools: AgentTool<any>[]) {
81
- tools = nextTools.slice();
82
- },
83
- get messages() {
84
- return messages;
85
- },
86
- set messages(nextMessages: AgentMessage[]) {
87
- messages = nextMessages.slice();
88
- },
89
- isStreaming: false,
90
- streamingMessage: undefined,
91
- pendingToolCalls: new Set<string>(),
92
- errorMessage: undefined,
93
- };
94
- }
95
-
96
- /** Options for constructing an {@link Agent}. */
97
- export interface AgentOptions {
98
- initialState?: Partial<Omit<AgentState, "pendingToolCalls" | "isStreaming" | "streamingMessage" | "errorMessage">>;
99
- convertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;
100
- transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;
101
- streamFn?: StreamFn;
102
- getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
103
- onPayload?: SimpleStreamOptions["onPayload"];
104
- onResponse?: SimpleStreamOptions["onResponse"];
105
- beforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;
106
- afterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;
107
- prepareNextTurn?: (
108
- signal?: AbortSignal,
109
- ) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
110
- prepareNextTurnWithContext?: (
111
- context: PrepareNextTurnContext,
112
- signal?: AbortSignal,
113
- ) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
114
- steeringMode?: QueueMode;
115
- followUpMode?: QueueMode;
116
- sessionId?: string;
117
- thinkingBudgets?: ThinkingBudgets;
118
- transport?: Transport;
119
- maxRetryDelayMs?: number;
120
- toolExecution?: ToolExecutionMode;
121
- }
122
-
123
- class PendingMessageQueue {
124
- private messages: AgentMessage[] = [];
125
- public mode: QueueMode;
126
-
127
- constructor(mode: QueueMode) {
128
- this.mode = mode;
129
- }
130
-
131
- enqueue(message: AgentMessage): void {
132
- this.messages.push(message);
133
- }
134
-
135
- hasItems(): boolean {
136
- return this.messages.length > 0;
137
- }
138
-
139
- drain(): AgentMessage[] {
140
- if (this.mode === "all") {
141
- const drained = this.messages.slice();
142
- this.messages = [];
143
- return drained;
144
- }
145
-
146
- const first = this.messages[0];
147
- if (!first) {
148
- return [];
149
- }
150
- this.messages = this.messages.slice(1);
151
- return [first];
152
- }
153
-
154
- clear(): void {
155
- this.messages = [];
156
- }
157
- }
158
-
159
- type ActiveRun = {
160
- promise: Promise<void>;
161
- resolve: () => void;
162
- abortController: AbortController;
163
- };
164
-
165
- /**
166
- * Stateful wrapper around the low-level agent loop.
167
- *
168
- * `Agent` owns the current transcript, emits lifecycle events, executes tools,
169
- * and exposes queueing APIs for steering and follow-up messages.
170
- */
171
- export class Agent {
172
- private _state: MutableAgentState;
173
- private readonly listeners = new Set<(event: AgentEvent, signal: AbortSignal) => Promise<void> | void>();
174
- private readonly steeringQueue: PendingMessageQueue;
175
- private readonly followUpQueue: PendingMessageQueue;
176
-
177
- public convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;
178
- public transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;
179
- public streamFn: StreamFn;
180
- public getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
181
- public onPayload?: SimpleStreamOptions["onPayload"];
182
- public onResponse?: SimpleStreamOptions["onResponse"];
183
- public beforeToolCall?: (
184
- context: BeforeToolCallContext,
185
- signal?: AbortSignal,
186
- ) => Promise<BeforeToolCallResult | undefined>;
187
- public afterToolCall?: (
188
- context: AfterToolCallContext,
189
- signal?: AbortSignal,
190
- ) => Promise<AfterToolCallResult | undefined>;
191
- public prepareNextTurn?: (
192
- signal?: AbortSignal,
193
- ) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
194
- public prepareNextTurnWithContext?: (
195
- context: PrepareNextTurnContext,
196
- signal?: AbortSignal,
197
- ) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
198
- private activeRun?: ActiveRun;
199
- /** Session identifier forwarded to providers for cache-aware backends. */
200
- public sessionId?: string;
201
- /** Optional per-level thinking token budgets forwarded to the stream function. */
202
- public thinkingBudgets?: ThinkingBudgets;
203
- /** Preferred transport forwarded to the stream function. */
204
- public transport: Transport;
205
- /** Optional cap for provider-requested retry delays. */
206
- public maxRetryDelayMs?: number;
207
- /** Tool execution strategy for assistant messages that contain multiple tool calls. */
208
- public toolExecution: ToolExecutionMode;
209
-
210
- constructor(options: AgentOptions = {}) {
211
- this._state = createMutableAgentState(options.initialState);
212
- this.convertToLlm = options.convertToLlm ?? defaultConvertToLlm;
213
- this.transformContext = options.transformContext;
214
- this.streamFn = options.streamFn ?? streamSimple;
215
- this.getApiKey = options.getApiKey;
216
- this.onPayload = options.onPayload;
217
- this.onResponse = options.onResponse;
218
- this.beforeToolCall = options.beforeToolCall;
219
- this.afterToolCall = options.afterToolCall;
220
- this.prepareNextTurn = options.prepareNextTurn;
221
- this.prepareNextTurnWithContext = options.prepareNextTurnWithContext;
222
- this.steeringQueue = new PendingMessageQueue(options.steeringMode ?? "one-at-a-time");
223
- this.followUpQueue = new PendingMessageQueue(options.followUpMode ?? "one-at-a-time");
224
- this.sessionId = options.sessionId;
225
- this.thinkingBudgets = options.thinkingBudgets;
226
- this.transport = options.transport ?? "auto";
227
- this.maxRetryDelayMs = options.maxRetryDelayMs;
228
- this.toolExecution = options.toolExecution ?? "parallel";
229
- }
230
-
231
- /**
232
- * Subscribe to agent lifecycle events.
233
- *
234
- * Listener promises are awaited in subscription order and are included in
235
- * the current run's settlement. Listeners also receive the active abort
236
- * signal for the current run.
237
- *
238
- * `agent_end` is the final emitted event for a run, but the agent does not
239
- * become idle until all awaited listeners for that event have settled.
240
- */
241
- subscribe(listener: (event: AgentEvent, signal: AbortSignal) => Promise<void> | void): () => void {
242
- this.listeners.add(listener);
243
- return () => this.listeners.delete(listener);
244
- }
245
-
246
- /**
247
- * Current agent state.
248
- *
249
- * Assigning `state.tools` or `state.messages` copies the provided top-level array.
250
- */
251
- get state(): AgentState {
252
- return this._state;
253
- }
254
-
255
- /** Controls how queued steering messages are drained. */
256
- set steeringMode(mode: QueueMode) {
257
- this.steeringQueue.mode = mode;
258
- }
259
-
260
- get steeringMode(): QueueMode {
261
- return this.steeringQueue.mode;
262
- }
263
-
264
- /** Controls how queued follow-up messages are drained. */
265
- set followUpMode(mode: QueueMode) {
266
- this.followUpQueue.mode = mode;
267
- }
268
-
269
- get followUpMode(): QueueMode {
270
- return this.followUpQueue.mode;
271
- }
272
-
273
- /** Queue a message to be injected after the current assistant turn finishes. */
274
- steer(message: AgentMessage): void {
275
- this.steeringQueue.enqueue(message);
276
- }
277
-
278
- /** Queue a message to run only after the agent would otherwise stop. */
279
- followUp(message: AgentMessage): void {
280
- this.followUpQueue.enqueue(message);
281
- }
282
-
283
- /** Remove all queued steering messages. */
284
- clearSteeringQueue(): void {
285
- this.steeringQueue.clear();
286
- }
287
-
288
- /** Remove all queued follow-up messages. */
289
- clearFollowUpQueue(): void {
290
- this.followUpQueue.clear();
291
- }
292
-
293
- /** Remove all queued steering and follow-up messages. */
294
- clearAllQueues(): void {
295
- this.clearSteeringQueue();
296
- this.clearFollowUpQueue();
297
- }
298
-
299
- /** Returns true when either queue still contains pending messages. */
300
- hasQueuedMessages(): boolean {
301
- return this.steeringQueue.hasItems() || this.followUpQueue.hasItems();
302
- }
303
-
304
- /** Active abort signal for the current run, if any. */
305
- get signal(): AbortSignal | undefined {
306
- return this.activeRun?.abortController.signal;
307
- }
308
-
309
- /** Abort the current run, if one is active. */
310
- abort(): void {
311
- this.activeRun?.abortController.abort();
312
- }
313
-
314
- /**
315
- * Resolve when the current run and all awaited event listeners have finished.
316
- *
317
- * This resolves after `agent_end` listeners settle.
318
- */
319
- waitForIdle(): Promise<void> {
320
- return this.activeRun?.promise ?? Promise.resolve();
321
- }
322
-
323
- /** Clear transcript state, runtime state, and queued messages. */
324
- reset(): void {
325
- this._state.messages = [];
326
- this._state.isStreaming = false;
327
- this._state.streamingMessage = undefined;
328
- this._state.pendingToolCalls = new Set<string>();
329
- this._state.errorMessage = undefined;
330
- this.clearFollowUpQueue();
331
- this.clearSteeringQueue();
332
- }
333
-
334
- /** Start a new prompt from text, a single message, or a batch of messages. */
335
- async prompt(message: AgentMessage | AgentMessage[]): Promise<void>;
336
- async prompt(input: string, images?: ImageContent[]): Promise<void>;
337
- async prompt(input: string | AgentMessage | AgentMessage[], images?: ImageContent[]): Promise<void> {
338
- if (this.activeRun) {
339
- throw new Error(
340
- "Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.",
341
- );
342
- }
343
- const messages = this.normalizePromptInput(input, images);
344
- await this.runPromptMessages(messages);
345
- }
346
-
347
- /** Continue from the current transcript. The last message must be a user or tool-result message. */
348
- async continue(): Promise<void> {
349
- if (this.activeRun) {
350
- throw new Error("Agent is already processing. Wait for completion before continuing.");
351
- }
352
-
353
- const lastMessage = this._state.messages[this._state.messages.length - 1];
354
- if (!lastMessage) {
355
- throw new Error("No messages to continue from");
356
- }
357
-
358
- if (lastMessage.role === "assistant") {
359
- const queuedSteering = this.steeringQueue.drain();
360
- if (queuedSteering.length > 0) {
361
- await this.runPromptMessages(queuedSteering, { skipInitialSteeringPoll: true });
362
- return;
363
- }
364
-
365
- const queuedFollowUps = this.followUpQueue.drain();
366
- if (queuedFollowUps.length > 0) {
367
- await this.runPromptMessages(queuedFollowUps);
368
- return;
369
- }
370
-
371
- throw new Error("Cannot continue from message role: assistant");
372
- }
373
-
374
- await this.runContinuation();
375
- }
376
-
377
- private normalizePromptInput(
378
- input: string | AgentMessage | AgentMessage[],
379
- images?: ImageContent[],
380
- ): AgentMessage[] {
381
- if (Array.isArray(input)) {
382
- return input;
383
- }
384
-
385
- if (typeof input !== "string") {
386
- return [input];
387
- }
388
-
389
- const content: Array<TextContent | ImageContent> = [{ type: "text", text: input }];
390
- if (images && images.length > 0) {
391
- content.push(...images);
392
- }
393
- return [{ role: "user", content, timestamp: Date.now() }];
394
- }
395
-
396
- private async runPromptMessages(
397
- messages: AgentMessage[],
398
- options: { skipInitialSteeringPoll?: boolean } = {},
399
- ): Promise<void> {
400
- await this.runWithLifecycle(async (signal) => {
401
- await runAgentLoop(
402
- messages,
403
- this.createContextSnapshot(),
404
- this.createLoopConfig(options),
405
- (event) => this.processEvents(event),
406
- signal,
407
- this.streamFn,
408
- );
409
- });
410
- }
411
-
412
- private async runContinuation(): Promise<void> {
413
- await this.runWithLifecycle(async (signal) => {
414
- await runAgentLoopContinue(
415
- this.createContextSnapshot(),
416
- this.createLoopConfig(),
417
- (event) => this.processEvents(event),
418
- signal,
419
- this.streamFn,
420
- );
421
- });
422
- }
423
-
424
- private createContextSnapshot(): AgentContext {
425
- return {
426
- systemPrompt: this._state.systemPrompt,
427
- messages: this._state.messages.slice(),
428
- tools: this._state.tools.slice(),
429
- };
430
- }
431
-
432
- private createLoopConfig(options: { skipInitialSteeringPoll?: boolean } = {}): AgentLoopConfig {
433
- let skipInitialSteeringPoll = options.skipInitialSteeringPoll === true;
434
- return {
435
- model: this._state.model,
436
- reasoning: this._state.thinkingLevel === "off" ? undefined : this._state.thinkingLevel,
437
- sessionId: this.sessionId,
438
- onPayload: this.onPayload,
439
- onResponse: this.onResponse,
440
- transport: this.transport,
441
- thinkingBudgets: this.thinkingBudgets,
442
- maxRetryDelayMs: this.maxRetryDelayMs,
443
- toolExecution: this.toolExecution,
444
- beforeToolCall: this.beforeToolCall,
445
- afterToolCall: this.afterToolCall,
446
- prepareNextTurn:
447
- this.prepareNextTurnWithContext || this.prepareNextTurn
448
- ? async (context) => {
449
- if (this.prepareNextTurnWithContext) {
450
- return await this.prepareNextTurnWithContext(context, this.signal);
451
- }
452
- return await this.prepareNextTurn?.(this.signal);
453
- }
454
- : undefined,
455
- convertToLlm: this.convertToLlm,
456
- transformContext: this.transformContext,
457
- getApiKey: this.getApiKey,
458
- getSteeringMessages: async () => {
459
- if (skipInitialSteeringPoll) {
460
- skipInitialSteeringPoll = false;
461
- return [];
462
- }
463
- return this.steeringQueue.drain();
464
- },
465
- getFollowUpMessages: async () => this.followUpQueue.drain(),
466
- };
467
- }
468
-
469
- private async runWithLifecycle(executor: (signal: AbortSignal) => Promise<void>): Promise<void> {
470
- if (this.activeRun) {
471
- throw new Error("Agent is already processing.");
472
- }
473
-
474
- const abortController = new AbortController();
475
- let resolvePromise = () => {};
476
- const promise = new Promise<void>((resolve) => {
477
- resolvePromise = resolve;
478
- });
479
- this.activeRun = { promise, resolve: resolvePromise, abortController };
480
-
481
- this._state.isStreaming = true;
482
- this._state.streamingMessage = undefined;
483
- this._state.errorMessage = undefined;
484
-
485
- try {
486
- await executor(abortController.signal);
487
- } catch (error) {
488
- await this.handleRunFailure(error, abortController.signal.aborted);
489
- } finally {
490
- this.finishRun();
491
- }
492
- }
493
-
494
- private async handleRunFailure(error: unknown, aborted: boolean): Promise<void> {
495
- const failureMessage = {
496
- role: "assistant",
497
- content: [{ type: "text", text: "" }],
498
- api: this._state.model.api,
499
- provider: this._state.model.provider,
500
- model: this._state.model.id,
501
- usage: EMPTY_USAGE,
502
- stopReason: aborted ? "aborted" : "error",
503
- errorMessage: error instanceof Error ? error.message : String(error),
504
- timestamp: Date.now(),
505
- } satisfies AgentMessage;
506
- await this.processEvents({ type: "message_start", message: failureMessage });
507
- await this.processEvents({ type: "message_end", message: failureMessage });
508
- await this.processEvents({ type: "turn_end", message: failureMessage, toolResults: [] });
509
- await this.processEvents({ type: "agent_end", messages: [failureMessage] });
510
- }
511
-
512
- private finishRun(): void {
513
- this._state.isStreaming = false;
514
- this._state.streamingMessage = undefined;
515
- this._state.pendingToolCalls = new Set<string>();
516
- this.activeRun?.resolve();
517
- this.activeRun = undefined;
518
- }
519
-
520
- /**
521
- * Reduce internal state for a loop event, then await listeners.
522
- *
523
- * `agent_end` only means no further loop events will be emitted. The run is
524
- * considered idle later, after all awaited listeners for `agent_end` finish
525
- * and `finishRun()` clears runtime-owned state.
526
- */
527
- private async processEvents(event: AgentEvent): Promise<void> {
528
- switch (event.type) {
529
- case "message_start":
530
- this._state.streamingMessage = event.message;
531
- break;
532
-
533
- case "message_update":
534
- this._state.streamingMessage = event.message;
535
- break;
536
-
537
- case "message_end":
538
- this._state.streamingMessage = undefined;
539
- this._state.messages.push(event.message);
540
- break;
541
-
542
- case "tool_execution_start": {
543
- const pendingToolCalls = new Set(this._state.pendingToolCalls);
544
- pendingToolCalls.add(event.toolCallId);
545
- this._state.pendingToolCalls = pendingToolCalls;
546
- break;
547
- }
548
-
549
- case "tool_execution_end": {
550
- const pendingToolCalls = new Set(this._state.pendingToolCalls);
551
- pendingToolCalls.delete(event.toolCallId);
552
- this._state.pendingToolCalls = pendingToolCalls;
553
- break;
554
- }
555
-
556
- case "turn_end":
557
- if (event.message.role === "assistant" && event.message.errorMessage) {
558
- this._state.errorMessage = event.message.errorMessage;
559
- }
560
- break;
561
-
562
- case "agent_end":
563
- this._state.streamingMessage = undefined;
564
- break;
565
- }
566
-
567
- const signal = this.activeRun?.abortController.signal;
568
- if (!signal) {
569
- throw new Error("Agent listener invoked outside active run");
570
- }
571
- for (const listener of this.listeners) {
572
- await listener(event, signal);
573
- }
574
- }
575
- }