zoe-agent 0.3.1

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 (267) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/LICENSE +96 -0
  3. package/README.md +568 -0
  4. package/dist/adapters/cli/agent.d.ts +59 -0
  5. package/dist/adapters/cli/agent.js +232 -0
  6. package/dist/adapters/cli/bootstrap.d.ts +25 -0
  7. package/dist/adapters/cli/bootstrap.js +204 -0
  8. package/dist/adapters/cli/commands/build-registry.d.ts +14 -0
  9. package/dist/adapters/cli/commands/build-registry.js +88 -0
  10. package/dist/adapters/cli/commands/clear.d.ts +7 -0
  11. package/dist/adapters/cli/commands/clear.js +10 -0
  12. package/dist/adapters/cli/commands/compact.d.ts +13 -0
  13. package/dist/adapters/cli/commands/compact.js +96 -0
  14. package/dist/adapters/cli/commands/exit.d.ts +7 -0
  15. package/dist/adapters/cli/commands/exit.js +9 -0
  16. package/dist/adapters/cli/commands/gateway.d.ts +7 -0
  17. package/dist/adapters/cli/commands/gateway.js +152 -0
  18. package/dist/adapters/cli/commands/help.d.ts +9 -0
  19. package/dist/adapters/cli/commands/help.js +12 -0
  20. package/dist/adapters/cli/commands/models.d.ts +10 -0
  21. package/dist/adapters/cli/commands/models.js +32 -0
  22. package/dist/adapters/cli/commands/registry.d.ts +70 -0
  23. package/dist/adapters/cli/commands/registry.js +111 -0
  24. package/dist/adapters/cli/commands/settings-utils.d.ts +38 -0
  25. package/dist/adapters/cli/commands/settings-utils.js +182 -0
  26. package/dist/adapters/cli/commands/settings.d.ts +9 -0
  27. package/dist/adapters/cli/commands/settings.js +395 -0
  28. package/dist/adapters/cli/commands/skills.d.ts +7 -0
  29. package/dist/adapters/cli/commands/skills.js +21 -0
  30. package/dist/adapters/cli/config-loader.d.ts +27 -0
  31. package/dist/adapters/cli/config-loader.js +48 -0
  32. package/dist/adapters/cli/docker-utils.d.ts +37 -0
  33. package/dist/adapters/cli/docker-utils.js +90 -0
  34. package/dist/adapters/cli/index.d.ts +2 -0
  35. package/dist/adapters/cli/index.js +88 -0
  36. package/dist/adapters/cli/repl.d.ts +22 -0
  37. package/dist/adapters/cli/repl.js +256 -0
  38. package/dist/adapters/cli/setup.d.ts +19 -0
  39. package/dist/adapters/cli/setup.js +613 -0
  40. package/dist/adapters/cli/system-prompts.d.ts +56 -0
  41. package/dist/adapters/cli/system-prompts.js +131 -0
  42. package/dist/adapters/cli/tui/app.d.ts +58 -0
  43. package/dist/adapters/cli/tui/app.js +314 -0
  44. package/dist/adapters/cli/tui/components/assistant-message.d.ts +5 -0
  45. package/dist/adapters/cli/tui/components/assistant-message.js +9 -0
  46. package/dist/adapters/cli/tui/components/autocomplete.d.ts +19 -0
  47. package/dist/adapters/cli/tui/components/autocomplete.js +75 -0
  48. package/dist/adapters/cli/tui/components/command-palette.d.ts +15 -0
  49. package/dist/adapters/cli/tui/components/command-palette.js +50 -0
  50. package/dist/adapters/cli/tui/components/diff-viewer.d.ts +5 -0
  51. package/dist/adapters/cli/tui/components/diff-viewer.js +109 -0
  52. package/dist/adapters/cli/tui/components/error-message.d.ts +5 -0
  53. package/dist/adapters/cli/tui/components/error-message.js +8 -0
  54. package/dist/adapters/cli/tui/components/footer.d.ts +20 -0
  55. package/dist/adapters/cli/tui/components/footer.js +19 -0
  56. package/dist/adapters/cli/tui/components/goal-status.d.ts +12 -0
  57. package/dist/adapters/cli/tui/components/goal-status.js +22 -0
  58. package/dist/adapters/cli/tui/components/info-message.d.ts +5 -0
  59. package/dist/adapters/cli/tui/components/info-message.js +8 -0
  60. package/dist/adapters/cli/tui/components/logo-banner.d.ts +7 -0
  61. package/dist/adapters/cli/tui/components/logo-banner.js +33 -0
  62. package/dist/adapters/cli/tui/components/markdown.d.ts +9 -0
  63. package/dist/adapters/cli/tui/components/markdown.js +92 -0
  64. package/dist/adapters/cli/tui/components/message-area.d.ts +19 -0
  65. package/dist/adapters/cli/tui/components/message-area.js +55 -0
  66. package/dist/adapters/cli/tui/components/permission-prompt.d.ts +13 -0
  67. package/dist/adapters/cli/tui/components/permission-prompt.js +32 -0
  68. package/dist/adapters/cli/tui/components/prompt-area.d.ts +22 -0
  69. package/dist/adapters/cli/tui/components/prompt-area.js +68 -0
  70. package/dist/adapters/cli/tui/components/text-input.d.ts +27 -0
  71. package/dist/adapters/cli/tui/components/text-input.js +142 -0
  72. package/dist/adapters/cli/tui/components/tool-call-block.d.ts +11 -0
  73. package/dist/adapters/cli/tui/components/tool-call-block.js +68 -0
  74. package/dist/adapters/cli/tui/components/user-message.d.ts +5 -0
  75. package/dist/adapters/cli/tui/components/user-message.js +8 -0
  76. package/dist/adapters/cli/tui/diff/file-write-meta.d.ts +11 -0
  77. package/dist/adapters/cli/tui/diff/file-write-meta.js +11 -0
  78. package/dist/adapters/cli/tui/diff/line-diff.d.ts +17 -0
  79. package/dist/adapters/cli/tui/diff/line-diff.js +44 -0
  80. package/dist/adapters/cli/tui/feed-serializer.d.ts +29 -0
  81. package/dist/adapters/cli/tui/feed-serializer.js +70 -0
  82. package/dist/adapters/cli/tui/file-index.d.ts +8 -0
  83. package/dist/adapters/cli/tui/file-index.js +41 -0
  84. package/dist/adapters/cli/tui/hooks/use-agent.d.ts +54 -0
  85. package/dist/adapters/cli/tui/hooks/use-agent.js +177 -0
  86. package/dist/adapters/cli/tui/hooks/use-feed.d.ts +16 -0
  87. package/dist/adapters/cli/tui/hooks/use-feed.js +25 -0
  88. package/dist/adapters/cli/tui/hooks/use-file-watcher.d.ts +10 -0
  89. package/dist/adapters/cli/tui/hooks/use-file-watcher.js +43 -0
  90. package/dist/adapters/cli/tui/hooks/use-keybindings.d.ts +16 -0
  91. package/dist/adapters/cli/tui/hooks/use-keybindings.js +25 -0
  92. package/dist/adapters/cli/tui/hooks/use-theme.d.ts +8 -0
  93. package/dist/adapters/cli/tui/hooks/use-theme.js +12 -0
  94. package/dist/adapters/cli/tui/index.d.ts +19 -0
  95. package/dist/adapters/cli/tui/index.js +206 -0
  96. package/dist/adapters/cli/tui/ink-reset.d.ts +29 -0
  97. package/dist/adapters/cli/tui/ink-reset.js +57 -0
  98. package/dist/adapters/cli/tui/layout.d.ts +15 -0
  99. package/dist/adapters/cli/tui/layout.js +15 -0
  100. package/dist/adapters/cli/tui/logo/gradient.d.ts +11 -0
  101. package/dist/adapters/cli/tui/logo/gradient.js +31 -0
  102. package/dist/adapters/cli/tui/overlays/help-dialog.d.ts +4 -0
  103. package/dist/adapters/cli/tui/overlays/help-dialog.js +26 -0
  104. package/dist/adapters/cli/tui/overlays/model-selector.d.ts +14 -0
  105. package/dist/adapters/cli/tui/overlays/model-selector.js +43 -0
  106. package/dist/adapters/cli/tui/overlays/session-selector.d.ts +35 -0
  107. package/dist/adapters/cli/tui/overlays/session-selector.js +162 -0
  108. package/dist/adapters/cli/tui/overlays/settings-overlay.d.ts +24 -0
  109. package/dist/adapters/cli/tui/overlays/settings-overlay.js +126 -0
  110. package/dist/adapters/cli/tui/session-export.d.ts +21 -0
  111. package/dist/adapters/cli/tui/session-export.js +63 -0
  112. package/dist/adapters/cli/tui/theme.d.ts +23 -0
  113. package/dist/adapters/cli/tui/theme.js +22 -0
  114. package/dist/adapters/cli/tui/types.d.ts +52 -0
  115. package/dist/adapters/cli/tui/types.js +12 -0
  116. package/dist/adapters/sdk/agent.d.ts +20 -0
  117. package/dist/adapters/sdk/agent.js +356 -0
  118. package/dist/adapters/sdk/http.d.ts +43 -0
  119. package/dist/adapters/sdk/http.js +61 -0
  120. package/dist/adapters/sdk/index.d.ts +58 -0
  121. package/dist/adapters/sdk/index.js +209 -0
  122. package/dist/adapters/sdk/settings.d.ts +18 -0
  123. package/dist/adapters/sdk/settings.js +57 -0
  124. package/dist/adapters/sdk/tools.d.ts +7 -0
  125. package/dist/adapters/sdk/tools.js +13 -0
  126. package/dist/adapters/server/auth.d.ts +53 -0
  127. package/dist/adapters/server/auth.js +168 -0
  128. package/dist/adapters/server/index.d.ts +40 -0
  129. package/dist/adapters/server/index.js +255 -0
  130. package/dist/adapters/server/rest-gateway.d.ts +13 -0
  131. package/dist/adapters/server/rest-gateway.js +218 -0
  132. package/dist/adapters/server/rest.d.ts +37 -0
  133. package/dist/adapters/server/rest.js +341 -0
  134. package/dist/adapters/server/server-core.d.ts +55 -0
  135. package/dist/adapters/server/server-core.js +121 -0
  136. package/dist/adapters/server/session-store.d.ts +81 -0
  137. package/dist/adapters/server/session-store.js +272 -0
  138. package/dist/adapters/server/settings-handlers.d.ts +24 -0
  139. package/dist/adapters/server/settings-handlers.js +360 -0
  140. package/dist/adapters/server/standalone.d.ts +19 -0
  141. package/dist/adapters/server/standalone.js +113 -0
  142. package/dist/adapters/server/websocket.d.ts +26 -0
  143. package/dist/adapters/server/websocket.js +68 -0
  144. package/dist/adapters/server/ws-handlers.d.ts +32 -0
  145. package/dist/adapters/server/ws-handlers.js +523 -0
  146. package/dist/adapters/server/ws-types.d.ts +304 -0
  147. package/dist/adapters/server/ws-types.js +7 -0
  148. package/dist/core/agent-loop.d.ts +68 -0
  149. package/dist/core/agent-loop.js +423 -0
  150. package/dist/core/config.d.ts +115 -0
  151. package/dist/core/config.js +189 -0
  152. package/dist/core/errors.d.ts +58 -0
  153. package/dist/core/errors.js +88 -0
  154. package/dist/core/hooks.d.ts +35 -0
  155. package/dist/core/hooks.js +49 -0
  156. package/dist/core/index.d.ts +23 -0
  157. package/dist/core/index.js +29 -0
  158. package/dist/core/message-convert.d.ts +41 -0
  159. package/dist/core/message-convert.js +94 -0
  160. package/dist/core/middleware/auth.d.ts +24 -0
  161. package/dist/core/middleware/auth.js +28 -0
  162. package/dist/core/middleware/logging.d.ts +23 -0
  163. package/dist/core/middleware/logging.js +28 -0
  164. package/dist/core/middleware/rate-limit.d.ts +27 -0
  165. package/dist/core/middleware/rate-limit.js +38 -0
  166. package/dist/core/middleware/semantic-tools.d.ts +10 -0
  167. package/dist/core/middleware/semantic-tools.js +43 -0
  168. package/dist/core/middleware.d.ts +48 -0
  169. package/dist/core/middleware.js +38 -0
  170. package/dist/core/permission.d.ts +25 -0
  171. package/dist/core/permission.js +50 -0
  172. package/dist/core/provider-config.d.ts +129 -0
  173. package/dist/core/provider-config.js +273 -0
  174. package/dist/core/provider-env.d.ts +39 -0
  175. package/dist/core/provider-env.js +142 -0
  176. package/dist/core/provider-resolver.d.ts +12 -0
  177. package/dist/core/provider-resolver.js +12 -0
  178. package/dist/core/session-store.d.ts +75 -0
  179. package/dist/core/session-store.js +245 -0
  180. package/dist/core/settings-manager.d.ts +57 -0
  181. package/dist/core/settings-manager.js +359 -0
  182. package/dist/core/settings-schema.d.ts +38 -0
  183. package/dist/core/settings-schema.js +171 -0
  184. package/dist/core/skill-catalog.d.ts +6 -0
  185. package/dist/core/skill-catalog.js +17 -0
  186. package/dist/core/skill-invoker.d.ts +127 -0
  187. package/dist/core/skill-invoker.js +182 -0
  188. package/dist/core/stream-accumulator.d.ts +21 -0
  189. package/dist/core/stream-accumulator.js +51 -0
  190. package/dist/core/stream-manager.d.ts +58 -0
  191. package/dist/core/stream-manager.js +212 -0
  192. package/dist/core/tool-executor.d.ts +84 -0
  193. package/dist/core/tool-executor.js +256 -0
  194. package/dist/core/types.d.ts +259 -0
  195. package/dist/core/types.js +11 -0
  196. package/dist/gateway/gateway.d.ts +52 -0
  197. package/dist/gateway/gateway.js +537 -0
  198. package/dist/gateway/index.d.ts +21 -0
  199. package/dist/gateway/index.js +31 -0
  200. package/dist/gateway/openapi-importer.d.ts +15 -0
  201. package/dist/gateway/openapi-importer.js +66 -0
  202. package/dist/gateway/semantic-scorer.d.ts +7 -0
  203. package/dist/gateway/semantic-scorer.js +24 -0
  204. package/dist/gateway/settings-adapter.d.ts +49 -0
  205. package/dist/gateway/settings-adapter.js +137 -0
  206. package/dist/gateway/tool-factory.d.ts +9 -0
  207. package/dist/gateway/tool-factory.js +414 -0
  208. package/dist/gateway/types.d.ts +68 -0
  209. package/dist/gateway/types.js +7 -0
  210. package/dist/models-catalog.js +46 -0
  211. package/dist/providers/anthropic.d.ts +22 -0
  212. package/dist/providers/anthropic.js +148 -0
  213. package/dist/providers/factory.d.ts +10 -0
  214. package/dist/providers/factory.js +25 -0
  215. package/dist/providers/openai.d.ts +15 -0
  216. package/dist/providers/openai.js +71 -0
  217. package/dist/providers/types.d.ts +48 -0
  218. package/dist/providers/types.js +1 -0
  219. package/dist/skills/args.d.ts +37 -0
  220. package/dist/skills/args.js +99 -0
  221. package/dist/skills/index.d.ts +11 -0
  222. package/dist/skills/index.js +23 -0
  223. package/dist/skills/loader.d.ts +3 -0
  224. package/dist/skills/loader.js +59 -0
  225. package/dist/skills/parser.d.ts +7 -0
  226. package/dist/skills/parser.js +152 -0
  227. package/dist/skills/registry.d.ts +13 -0
  228. package/dist/skills/registry.js +74 -0
  229. package/dist/skills/resolver.d.ts +19 -0
  230. package/dist/skills/resolver.js +116 -0
  231. package/dist/skills/types.d.ts +74 -0
  232. package/dist/skills/types.js +50 -0
  233. package/dist/tools/browser.d.ts +2 -0
  234. package/dist/tools/browser.js +68 -0
  235. package/dist/tools/core.d.ts +20 -0
  236. package/dist/tools/core.js +244 -0
  237. package/dist/tools/email.d.ts +2 -0
  238. package/dist/tools/email.js +61 -0
  239. package/dist/tools/image.d.ts +2 -0
  240. package/dist/tools/image.js +257 -0
  241. package/dist/tools/index.d.ts +2 -0
  242. package/dist/tools/index.js +88 -0
  243. package/dist/tools/interface.d.ts +22 -0
  244. package/dist/tools/interface.js +1 -0
  245. package/dist/tools/notify.d.ts +2 -0
  246. package/dist/tools/notify.js +100 -0
  247. package/dist/tools/prompt-optimizer.d.ts +2 -0
  248. package/dist/tools/prompt-optimizer.js +65 -0
  249. package/dist/tools/screenshot.d.ts +2 -0
  250. package/dist/tools/screenshot.js +184 -0
  251. package/dist/tools/search.d.ts +2 -0
  252. package/dist/tools/search.js +78 -0
  253. package/dist/tools/todos.d.ts +10 -0
  254. package/dist/tools/todos.js +50 -0
  255. package/package.json +119 -0
  256. package/skills/docker-ops/SKILL.md +329 -0
  257. package/skills/k8s-deploy/SKILL.md +397 -0
  258. package/skills/log-analyzer/SKILL.md +331 -0
  259. package/skills/speckit-analyze/SKILL.md +260 -0
  260. package/skills/speckit-checklist/SKILL.md +374 -0
  261. package/skills/speckit-clarify/SKILL.md +286 -0
  262. package/skills/speckit-constitution/SKILL.md +157 -0
  263. package/skills/speckit-implement/SKILL.md +224 -0
  264. package/skills/speckit-plan/SKILL.md +171 -0
  265. package/skills/speckit-specify/SKILL.md +346 -0
  266. package/skills/speckit-tasks/SKILL.md +215 -0
  267. package/skills/speckit-taskstoissues/SKILL.md +107 -0
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Zoe Core — Settings Schema
3
+ *
4
+ * Static data structures mapping all user-visible settings to their
5
+ * AppConfig paths, validation rules, env var overrides, and metadata.
6
+ */
7
+ export type SettingsCategory = 'providers' | 'permissions' | 'tools' | 'notifications' | 'skills' | 'gateway' | 'sessions';
8
+ export interface SettingsMapEntry {
9
+ dotKey: string;
10
+ configPath: string[];
11
+ category: SettingsCategory;
12
+ label: string;
13
+ }
14
+ export interface SettingsSchemaEntry {
15
+ type: 'string' | 'number' | 'boolean' | 'enum';
16
+ secret: boolean;
17
+ enumValues?: string[];
18
+ min?: number;
19
+ max?: number;
20
+ default?: string | number | boolean;
21
+ restartRequired: boolean;
22
+ envVar?: string;
23
+ }
24
+ export declare const SETTINGS_CATEGORIES: {
25
+ key: SettingsCategory;
26
+ label: string;
27
+ description: string;
28
+ }[];
29
+ export declare const SETTINGS_MAP: Map<string, SettingsMapEntry>;
30
+ export declare const CONFIG_PATH_TO_DOTKEY: Map<string, string>;
31
+ export declare const SETTINGS_SCHEMA: Map<string, SettingsSchemaEntry>;
32
+ export declare const ENV_VAR_MAP: Map<string, string>;
33
+ export declare function getSettingEntry(dotKey: string): SettingsMapEntry | undefined;
34
+ export declare function getSettingSchema(dotKey: string): SettingsSchemaEntry | undefined;
35
+ export declare function getDotKeyForConfigPath(path: string[]): string | undefined;
36
+ export declare function isSecretField(dotKey: string): boolean;
37
+ export declare function isRestartRequired(dotKey: string): boolean;
38
+ export declare function getSettingsByCategory(category: SettingsCategory): string[];
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Zoe Core — Settings Schema
3
+ *
4
+ * Static data structures mapping all user-visible settings to their
5
+ * AppConfig paths, validation rules, env var overrides, and metadata.
6
+ */
7
+ import { DEFAULT_MODELS } from "../models-catalog.js";
8
+ // ── Categories ─────────────────────────────────────────────────────────
9
+ export const SETTINGS_CATEGORIES = [
10
+ {
11
+ key: 'providers',
12
+ label: 'Providers & Models',
13
+ description: 'LLM provider configuration (API keys, models, base URLs)',
14
+ },
15
+ {
16
+ key: 'permissions',
17
+ label: 'Permissions & Safety',
18
+ description: 'Permission level and auto-confirm settings',
19
+ },
20
+ {
21
+ key: 'tools',
22
+ label: 'Tools & Integrations',
23
+ description: 'Image generation, SMTP email, and web search settings',
24
+ },
25
+ {
26
+ key: 'notifications',
27
+ label: 'Notifications',
28
+ description: 'Feishu, DingTalk, and WeCom webhook settings',
29
+ },
30
+ {
31
+ key: 'skills',
32
+ label: 'Skills',
33
+ description: 'Skill system configuration (reserved for future use)',
34
+ },
35
+ {
36
+ key: 'gateway',
37
+ label: 'Gateway',
38
+ description: 'MCP gateway, REST proxy, and OpenAPI adapter settings',
39
+ },
40
+ {
41
+ key: 'sessions',
42
+ label: 'Sessions',
43
+ description: 'Session persistence and cleanup settings',
44
+ },
45
+ ];
46
+ // ── Settings Map ───────────────────────────────────────────────────────
47
+ const entries = [
48
+ // Providers
49
+ ['providers.openai.apiKey', { dotKey: 'providers.openai.apiKey', configPath: ['models', 'openai', 'apiKey'], category: 'providers', label: 'OpenAI API Key' }],
50
+ ['providers.openai.model', { dotKey: 'providers.openai.model', configPath: ['models', 'openai', 'model'], category: 'providers', label: 'OpenAI Model' }],
51
+ ['providers.anthropic.apiKey', { dotKey: 'providers.anthropic.apiKey', configPath: ['models', 'anthropic', 'apiKey'], category: 'providers', label: 'Anthropic API Key' }],
52
+ ['providers.anthropic.model', { dotKey: 'providers.anthropic.model', configPath: ['models', 'anthropic', 'model'], category: 'providers', label: 'Anthropic Model' }],
53
+ ['providers.glm.apiKey', { dotKey: 'providers.glm.apiKey', configPath: ['models', 'glm', 'apiKey'], category: 'providers', label: 'GLM API Key' }],
54
+ ['providers.glm.model', { dotKey: 'providers.glm.model', configPath: ['models', 'glm', 'model'], category: 'providers', label: 'GLM Model' }],
55
+ ['providers.openai-compat.apiKey', { dotKey: 'providers.openai-compat.apiKey', configPath: ['models', 'openai-compatible', 'apiKey'], category: 'providers', label: 'OpenAI-Compatible API Key' }],
56
+ ['providers.openai-compat.baseUrl', { dotKey: 'providers.openai-compat.baseUrl', configPath: ['models', 'openai-compatible', 'baseUrl'], category: 'providers', label: 'OpenAI-Compatible Base URL' }],
57
+ ['providers.openai-compat.model', { dotKey: 'providers.openai-compat.model', configPath: ['models', 'openai-compatible', 'model'], category: 'providers', label: 'OpenAI-Compatible Model' }],
58
+ ['provider', { dotKey: 'provider', configPath: ['provider'], category: 'providers', label: 'Active Provider' }],
59
+ // Image
60
+ ['image.apiKey', { dotKey: 'image.apiKey', configPath: ['imageApiKey'], category: 'tools', label: 'Image Generation API Key' }],
61
+ ['image.baseUrl', { dotKey: 'image.baseUrl', configPath: ['imageBaseUrl'], category: 'tools', label: 'Image Generation Base URL' }],
62
+ ['image.model', { dotKey: 'image.model', configPath: ['imageModel'], category: 'tools', label: 'Image Generation Model' }],
63
+ ['image.size', { dotKey: 'image.size', configPath: ['imageSize'], category: 'tools', label: 'Image Size' }],
64
+ ['image.quality', { dotKey: 'image.quality', configPath: ['imageQuality'], category: 'tools', label: 'Image Quality' }],
65
+ ['image.style', { dotKey: 'image.style', configPath: ['imageStyle'], category: 'tools', label: 'Image Style' }],
66
+ ['image.n', { dotKey: 'image.n', configPath: ['imageN'], category: 'tools', label: 'Image Count' }],
67
+ // SMTP
68
+ ['smtp.host', { dotKey: 'smtp.host', configPath: ['smtpHost'], category: 'tools', label: 'SMTP Host' }],
69
+ ['smtp.port', { dotKey: 'smtp.port', configPath: ['smtpPort'], category: 'tools', label: 'SMTP Port' }],
70
+ ['smtp.user', { dotKey: 'smtp.user', configPath: ['smtpUser'], category: 'tools', label: 'SMTP Username' }],
71
+ ['smtp.pass', { dotKey: 'smtp.pass', configPath: ['smtpPass'], category: 'tools', label: 'SMTP Password' }],
72
+ ['smtp.from', { dotKey: 'smtp.from', configPath: ['smtpFrom'], category: 'tools', label: 'SMTP From Address' }],
73
+ // Search
74
+ ['search.tavilyApiKey', { dotKey: 'search.tavilyApiKey', configPath: ['tavilyApiKey'], category: 'tools', label: 'Tavily API Key' }],
75
+ // Notifications
76
+ ['notifications.feishu.webhook', { dotKey: 'notifications.feishu.webhook', configPath: ['feishuWebhook'], category: 'notifications', label: 'Feishu Webhook URL' }],
77
+ ['notifications.feishu.keyword', { dotKey: 'notifications.feishu.keyword', configPath: ['feishuKeyword'], category: 'notifications', label: 'Feishu Keyword' }],
78
+ ['notifications.dingtalk.webhook', { dotKey: 'notifications.dingtalk.webhook', configPath: ['dingtalkWebhook'], category: 'notifications', label: 'DingTalk Webhook URL' }],
79
+ ['notifications.dingtalk.keyword', { dotKey: 'notifications.dingtalk.keyword', configPath: ['dingtalkKeyword'], category: 'notifications', label: 'DingTalk Keyword' }],
80
+ ['notifications.wecom.webhook', { dotKey: 'notifications.wecom.webhook', configPath: ['wecomWebhook'], category: 'notifications', label: 'WeCom Webhook URL' }],
81
+ ['notifications.wecom.keyword', { dotKey: 'notifications.wecom.keyword', configPath: ['wecomKeyword'], category: 'notifications', label: 'WeCom Keyword' }],
82
+ // Permissions
83
+ ['agent.permissionLevel', { dotKey: 'agent.permissionLevel', configPath: ['permissionLevel'], category: 'permissions', label: 'Permission Level' }],
84
+ ['agent.autoConfirm', { dotKey: 'agent.autoConfirm', configPath: ['autoConfirm'], category: 'permissions', label: 'Auto-Confirm All Tools' }],
85
+ // Gateway
86
+ ['gateway.enabled', { dotKey: 'gateway.enabled', configPath: ['gatewayEnabled'], category: 'gateway', label: 'Gateway Enabled' }],
87
+ ['gateway.semanticTopK', { dotKey: 'gateway.semanticTopK', configPath: ['gatewaySemanticTopK'], category: 'gateway', label: 'Semantic Injection Top-K' }],
88
+ ['gateway.defaultRateLimitPerMin', { dotKey: 'gateway.defaultRateLimitPerMin', configPath: ['gatewayRateLimit'], category: 'gateway', label: 'Gateway Rate Limit (per min)' }],
89
+ ['gateway.maxAuditLogs', { dotKey: 'gateway.maxAuditLogs', configPath: ['gatewayMaxAuditLogs'], category: 'gateway', label: 'Max Audit Log Records' }],
90
+ // Sessions
91
+ ['sessions.maxAgeDays', { dotKey: 'sessions.maxAgeDays', configPath: ['sessions', 'maxAgeDays'], category: 'sessions', label: 'Max Session Age (days)' }],
92
+ ];
93
+ export const SETTINGS_MAP = new Map(entries);
94
+ // ── Reverse lookup ─────────────────────────────────────────────────────
95
+ export const CONFIG_PATH_TO_DOTKEY = new Map(entries.map(([, entry]) => [entry.configPath.join('.'), entry.dotKey]));
96
+ // ── Settings Schema ────────────────────────────────────────────────────
97
+ const schemaEntries = [
98
+ // Providers
99
+ ['providers.openai.apiKey', { type: 'string', secret: true, restartRequired: true, envVar: 'OPENAI_API_KEY' }],
100
+ ['providers.openai.model', { type: 'string', secret: false, default: DEFAULT_MODELS.openai, restartRequired: false, envVar: 'OPENAI_MODEL' }],
101
+ ['providers.anthropic.apiKey', { type: 'string', secret: true, restartRequired: true, envVar: 'ANTHROPIC_API_KEY' }],
102
+ ['providers.anthropic.model', { type: 'string', secret: false, default: DEFAULT_MODELS.anthropic, restartRequired: false, envVar: 'ANTHROPIC_MODEL' }],
103
+ ['providers.glm.apiKey', { type: 'string', secret: true, restartRequired: true, envVar: 'GLM_API_KEY' }],
104
+ ['providers.glm.model', { type: 'string', secret: false, default: DEFAULT_MODELS.glm, restartRequired: false, envVar: 'GLM_MODEL' }],
105
+ ['providers.openai-compat.apiKey', { type: 'string', secret: true, restartRequired: true, envVar: 'OPENAI_COMPAT_API_KEY' }],
106
+ ['providers.openai-compat.baseUrl', { type: 'string', secret: false, restartRequired: true, envVar: 'OPENAI_COMPAT_BASE_URL' }],
107
+ ['providers.openai-compat.model', { type: 'string', secret: false, default: DEFAULT_MODELS['openai-compatible'], restartRequired: false, envVar: 'OPENAI_MODEL' }],
108
+ ['provider', { type: 'enum', secret: false, enumValues: ['openai', 'openai-compatible', 'anthropic', 'glm'], default: 'openai-compatible', restartRequired: true, envVar: 'LLM_PROVIDER' }],
109
+ // Image
110
+ ['image.apiKey', { type: 'string', secret: true, restartRequired: false }],
111
+ ['image.baseUrl', { type: 'string', secret: false, restartRequired: false }],
112
+ ['image.model', { type: 'string', secret: false, default: 'dall-e-3', restartRequired: false }],
113
+ ['image.size', { type: 'string', secret: false, default: '1024x1024', restartRequired: false }],
114
+ ['image.quality', { type: 'enum', secret: false, enumValues: ['standard', 'hd'], default: 'standard', restartRequired: false }],
115
+ ['image.style', { type: 'enum', secret: false, enumValues: ['vivid', 'natural'], default: 'vivid', restartRequired: false }],
116
+ ['image.n', { type: 'number', secret: false, default: 1, min: 1, max: 10, restartRequired: false }],
117
+ // SMTP
118
+ ['smtp.host', { type: 'string', secret: false, restartRequired: false, envVar: 'SMTP_HOST' }],
119
+ ['smtp.port', { type: 'string', secret: false, restartRequired: false, envVar: 'SMTP_PORT' }],
120
+ ['smtp.user', { type: 'string', secret: false, restartRequired: false, envVar: 'SMTP_USER' }],
121
+ ['smtp.pass', { type: 'string', secret: true, restartRequired: false, envVar: 'SMTP_PASS' }],
122
+ ['smtp.from', { type: 'string', secret: false, restartRequired: false }],
123
+ // Search
124
+ ['search.tavilyApiKey', { type: 'string', secret: true, restartRequired: false, envVar: 'TAVILY_API_KEY' }],
125
+ // Notifications
126
+ ['notifications.feishu.webhook', { type: 'string', secret: true, restartRequired: false, envVar: 'FEISHU_WEBHOOK' }],
127
+ ['notifications.feishu.keyword', { type: 'string', secret: false, restartRequired: false, envVar: 'FEISHU_KEYWORD' }],
128
+ ['notifications.dingtalk.webhook', { type: 'string', secret: true, restartRequired: false, envVar: 'DINGTALK_WEBHOOK' }],
129
+ ['notifications.dingtalk.keyword', { type: 'string', secret: false, restartRequired: false, envVar: 'DINGTALK_KEYWORD' }],
130
+ ['notifications.wecom.webhook', { type: 'string', secret: true, restartRequired: false, envVar: 'WECOM_WEBHOOK' }],
131
+ ['notifications.wecom.keyword', { type: 'string', secret: false, restartRequired: false, envVar: 'WECOM_KEYWORD' }],
132
+ // Agent
133
+ ['agent.permissionLevel', { type: 'enum', secret: false, enumValues: ['strict', 'moderate', 'permissive'], default: 'moderate', restartRequired: false, envVar: 'ZOE_PERMISSION' }],
134
+ ['agent.autoConfirm', { type: 'boolean', secret: false, default: false, restartRequired: false }],
135
+ // Gateway
136
+ ['gateway.enabled', { type: 'boolean', secret: false, default: true, restartRequired: true, envVar: 'ZOE_GATEWAY_ENABLED' }],
137
+ ['gateway.semanticTopK', { type: 'number', secret: false, default: 3, min: 1, max: 10, restartRequired: false }],
138
+ ['gateway.defaultRateLimitPerMin', { type: 'number', secret: false, default: 60, min: 0, restartRequired: false, envVar: 'ZOE_GATEWAY_RATE_LIMIT' }],
139
+ ['gateway.maxAuditLogs', { type: 'number', secret: false, default: 1000, min: 10, max: 10000, restartRequired: false }],
140
+ // Sessions
141
+ ['sessions.maxAgeDays', { type: 'number', secret: false, default: 30, min: 0, restartRequired: false }],
142
+ ];
143
+ export const SETTINGS_SCHEMA = new Map(schemaEntries);
144
+ // ── Env Var Map ────────────────────────────────────────────────────────
145
+ export const ENV_VAR_MAP = new Map(schemaEntries
146
+ .filter(([, s]) => s.envVar !== undefined)
147
+ .map(([dotKey, s]) => [dotKey, s.envVar]));
148
+ // ── Helpers ────────────────────────────────────────────────────────────
149
+ export function getSettingEntry(dotKey) {
150
+ return SETTINGS_MAP.get(dotKey);
151
+ }
152
+ export function getSettingSchema(dotKey) {
153
+ return SETTINGS_SCHEMA.get(dotKey);
154
+ }
155
+ export function getDotKeyForConfigPath(path) {
156
+ return CONFIG_PATH_TO_DOTKEY.get(path.join('.'));
157
+ }
158
+ export function isSecretField(dotKey) {
159
+ return SETTINGS_SCHEMA.get(dotKey)?.secret ?? false;
160
+ }
161
+ export function isRestartRequired(dotKey) {
162
+ return SETTINGS_SCHEMA.get(dotKey)?.restartRequired ?? false;
163
+ }
164
+ export function getSettingsByCategory(category) {
165
+ const keys = [];
166
+ for (const entry of SETTINGS_MAP.values()) {
167
+ if (entry.category === category)
168
+ keys.push(entry.dotKey);
169
+ }
170
+ return keys;
171
+ }
@@ -0,0 +1,6 @@
1
+ import type { SkillMetadata } from './types.js';
2
+ /**
3
+ * Build a skill catalog string suitable for appending to the system prompt.
4
+ * Returns an empty string when no skills are available.
5
+ */
6
+ export declare function buildSkillCatalog(metadata: SkillMetadata[]): string;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Build a skill catalog string suitable for appending to the system prompt.
3
+ * Returns an empty string when no skills are available.
4
+ */
5
+ export function buildSkillCatalog(metadata) {
6
+ if (metadata.length === 0)
7
+ return '';
8
+ const lines = metadata.map(s => {
9
+ const tags = s.tags.length > 0 ? ` [${s.tags.join(', ')}]` : '';
10
+ return `- ${s.name}: ${s.description}${tags}`;
11
+ });
12
+ return [
13
+ 'AVAILABLE SKILLS (activate with use_skill tool):',
14
+ ...lines,
15
+ 'When a user request matches a skill, call use_skill with the skill name.',
16
+ ].join('\n');
17
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Skill Invoker — Central orchestrator for skill invocation flow.
3
+ *
4
+ * This module consolidates the skill invocation logic that was previously
5
+ * scattered across the CLI (src/index.ts) and agent (src/agent.ts).
6
+ *
7
+ * Invocation flow:
8
+ * 1. Parse input → extract skill name and arguments
9
+ * 2. Registry lookup → resolve skill metadata
10
+ * 3. @path resolution → inline file references
11
+ * 4. Prompt construction → build the final prompt
12
+ * 5. Return result with provider switching metadata
13
+ *
14
+ * The invoker does NOT handle provider switching itself — it returns
15
+ * metadata so the adapter can decide whether to switch.
16
+ */
17
+ import { type SkillRegistry } from '../skills/types.js';
18
+ import { type SkillMetadata } from './types.js';
19
+ import type { LLMProvider } from '../providers/types.js';
20
+ /**
21
+ * Result of a skill invocation.
22
+ *
23
+ * Contains the constructed prompt along with metadata about
24
+ * whether the skill has a preferred provider/model configuration.
25
+ */
26
+ export interface SkillInvocationResult {
27
+ /** The constructed prompt to send to the agent */
28
+ prompt: string;
29
+ /** Resolved skill metadata */
30
+ skill: SkillMetadata;
31
+ /** Whether the skill has a preferredProvider that needs switching */
32
+ providerSwitchNeeded: boolean;
33
+ /** The preferred provider type (if any) */
34
+ preferredProvider?: string;
35
+ /** The preferred model (if any) */
36
+ preferredModel?: string;
37
+ }
38
+ /**
39
+ * Invoke a skill by name with the provided arguments.
40
+ *
41
+ * This function orchestrates the complete skill invocation flow:
42
+ * 1. Parses the input to extract skill name and arguments
43
+ * 2. Looks up the skill in the registry
44
+ * 3. Substitutes arguments into the skill body
45
+ * 4. Resolves @path references
46
+ * 5. Constructs the final prompt
47
+ * 6. Returns metadata about provider switching if needed
48
+ *
49
+ * @param options - Invocation options
50
+ * @param options.input - Raw "/skillname args" input from user
51
+ * @param options.registry - The skill registry to look up skills from
52
+ * @param options.skillsPath - Optional path for @path resolution (defaults to cwd)
53
+ * @returns SkillInvocationResult or null if no skill matches
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * const result = await invokeSkill({
58
+ * input: '/code-review src/app.ts',
59
+ * registry: skillRegistry,
60
+ * });
61
+ *
62
+ * if (result) {
63
+ * if (result.providerSwitchNeeded) {
64
+ * await switchProvider(result.preferredProvider!, result.preferredModel!);
65
+ * }
66
+ * await agent.chat(result.prompt);
67
+ * }
68
+ * ```
69
+ */
70
+ export declare function invokeSkill(options: {
71
+ input: string;
72
+ registry: SkillRegistry;
73
+ skillsPath?: string;
74
+ }): Promise<SkillInvocationResult | null>;
75
+ /**
76
+ * Configuration for creating a skill provider switcher.
77
+ */
78
+ export interface ProviderSwitcherConfig {
79
+ /** The current active provider */
80
+ provider: LLMProvider;
81
+ /** The current active model name */
82
+ model: string;
83
+ /** Available model configurations keyed by provider type */
84
+ models: Record<string, {
85
+ apiKey: string;
86
+ baseUrl?: string;
87
+ model: string;
88
+ }>;
89
+ }
90
+ /**
91
+ * A switcher that temporarily changes the active provider/model based on
92
+ * skill preferences and can restore the original when done.
93
+ */
94
+ export interface SkillProviderSwitcher {
95
+ /** Switch provider if the skill requires it. Returns true if switched. */
96
+ switchIfNeeded(skillResult: SkillInvocationResult): Promise<boolean>;
97
+ /** Restore the original provider/model. */
98
+ restore(): void;
99
+ /** The current active provider. */
100
+ readonly activeProvider: LLMProvider;
101
+ /** The current active model name. */
102
+ readonly activeModel: string;
103
+ }
104
+ /**
105
+ * Create a skill provider switcher that captures the original provider/model
106
+ * state and can temporarily switch based on skill preferences.
107
+ *
108
+ * The switcher gracefully handles errors — if provider creation fails,
109
+ * `switchIfNeeded` returns `false` instead of throwing.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const switcher = createSkillProviderSwitcher({
114
+ * provider: currentProvider,
115
+ * model: 'gpt-4',
116
+ * models: config.models,
117
+ * });
118
+ *
119
+ * const switched = await switcher.switchIfNeeded(skillResult);
120
+ * try {
121
+ * await agent.chat(skillResult.prompt);
122
+ * } finally {
123
+ * if (switched) switcher.restore();
124
+ * }
125
+ * ```
126
+ */
127
+ export declare function createSkillProviderSwitcher(config: ProviderSwitcherConfig): SkillProviderSwitcher;
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Skill Invoker — Central orchestrator for skill invocation flow.
3
+ *
4
+ * This module consolidates the skill invocation logic that was previously
5
+ * scattered across the CLI (src/index.ts) and agent (src/agent.ts).
6
+ *
7
+ * Invocation flow:
8
+ * 1. Parse input → extract skill name and arguments
9
+ * 2. Registry lookup → resolve skill metadata
10
+ * 3. @path resolution → inline file references
11
+ * 4. Prompt construction → build the final prompt
12
+ * 5. Return result with provider switching metadata
13
+ *
14
+ * The invoker does NOT handle provider switching itself — it returns
15
+ * metadata so the adapter can decide whether to switch.
16
+ */
17
+ import { parseInvocation, substituteArgs } from '../skills/args.js';
18
+ import { limitSkillBody } from '../skills/types.js';
19
+ import { resolveReferences } from '../skills/resolver.js';
20
+ import { createProvider } from '../providers/factory.js';
21
+ /**
22
+ * Invoke a skill by name with the provided arguments.
23
+ *
24
+ * This function orchestrates the complete skill invocation flow:
25
+ * 1. Parses the input to extract skill name and arguments
26
+ * 2. Looks up the skill in the registry
27
+ * 3. Substitutes arguments into the skill body
28
+ * 4. Resolves @path references
29
+ * 5. Constructs the final prompt
30
+ * 6. Returns metadata about provider switching if needed
31
+ *
32
+ * @param options - Invocation options
33
+ * @param options.input - Raw "/skillname args" input from user
34
+ * @param options.registry - The skill registry to look up skills from
35
+ * @param options.skillsPath - Optional path for @path resolution (defaults to cwd)
36
+ * @returns SkillInvocationResult or null if no skill matches
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const result = await invokeSkill({
41
+ * input: '/code-review src/app.ts',
42
+ * registry: skillRegistry,
43
+ * });
44
+ *
45
+ * if (result) {
46
+ * if (result.providerSwitchNeeded) {
47
+ * await switchProvider(result.preferredProvider!, result.preferredModel!);
48
+ * }
49
+ * await agent.chat(result.prompt);
50
+ * }
51
+ * ```
52
+ */
53
+ export async function invokeSkill(options) {
54
+ const { input, registry, skillsPath } = options;
55
+ // Step 1: Parse the input to extract skill name and arguments
56
+ const parsed = parseInvocation(input);
57
+ if (!parsed) {
58
+ return null;
59
+ }
60
+ const { skillName, args } = parsed;
61
+ // Step 2: Registry lookup
62
+ const skill = registry.get(skillName);
63
+ if (!skill) {
64
+ return null;
65
+ }
66
+ // Step 3: Get the skill body
67
+ const skillBody = await registry.getBody(skillName);
68
+ if (!skillBody) {
69
+ return null;
70
+ }
71
+ // Step 4: Substitute arguments into skill body
72
+ const substitutedBody = substituteArgs(skillBody, args);
73
+ // Step 5: Resolve @path references in the query
74
+ let resolvedQuery = args.raw;
75
+ if (args.raw.includes('@')) {
76
+ try {
77
+ resolvedQuery = await resolveReferences(args.raw, skillsPath);
78
+ }
79
+ catch {
80
+ // Resolver failed, use raw args
81
+ }
82
+ }
83
+ // Step 6: Enforce body size limits (truncate if necessary)
84
+ const { body: limitedBody, truncated, originalTokenEstimate, finalTokenEstimate } = limitSkillBody(substitutedBody);
85
+ if (truncated) {
86
+ console.warn(`[SKILLS] Skill "${skillName}" body truncated: ` +
87
+ `${originalTokenEstimate} -> ${finalTokenEstimate} estimated tokens.`);
88
+ }
89
+ // Step 7: Construct the final prompt
90
+ const prompt = resolvedQuery
91
+ ? `[Skill: ${skill.name} activated]\n\n${limitedBody}\n\nUser request: ${resolvedQuery}`
92
+ : `[Skill: ${skill.name} activated]\n\n${limitedBody}\n\nSkill loaded. What would you like me to do?`;
93
+ // Step 8: Extract provider switching metadata
94
+ const extendedSkill = skill;
95
+ const modelConfig = extendedSkill.frontmatter?.model;
96
+ const providerSwitchNeeded = !!modelConfig?.provider;
97
+ return {
98
+ prompt,
99
+ skill: {
100
+ name: skill.name,
101
+ description: skill.description,
102
+ tags: skill.tags,
103
+ },
104
+ providerSwitchNeeded,
105
+ preferredProvider: modelConfig?.provider,
106
+ preferredModel: modelConfig?.model,
107
+ };
108
+ }
109
+ /**
110
+ * Create a skill provider switcher that captures the original provider/model
111
+ * state and can temporarily switch based on skill preferences.
112
+ *
113
+ * The switcher gracefully handles errors — if provider creation fails,
114
+ * `switchIfNeeded` returns `false` instead of throwing.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * const switcher = createSkillProviderSwitcher({
119
+ * provider: currentProvider,
120
+ * model: 'gpt-4',
121
+ * models: config.models,
122
+ * });
123
+ *
124
+ * const switched = await switcher.switchIfNeeded(skillResult);
125
+ * try {
126
+ * await agent.chat(skillResult.prompt);
127
+ * } finally {
128
+ * if (switched) switcher.restore();
129
+ * }
130
+ * ```
131
+ */
132
+ export function createSkillProviderSwitcher(config) {
133
+ let activeProvider = config.provider;
134
+ let activeModel = config.model;
135
+ let originalProvider = null;
136
+ let originalModel = null;
137
+ return {
138
+ get activeProvider() {
139
+ return activeProvider;
140
+ },
141
+ get activeModel() {
142
+ return activeModel;
143
+ },
144
+ async switchIfNeeded(skillResult) {
145
+ if (!skillResult.providerSwitchNeeded)
146
+ return false;
147
+ if (!skillResult.preferredProvider || !skillResult.preferredModel)
148
+ return false;
149
+ const providerType = skillResult.preferredProvider;
150
+ const providerModelConfig = config.models[providerType];
151
+ if (!providerModelConfig?.apiKey)
152
+ return false;
153
+ try {
154
+ const newProvider = await createProvider({
155
+ type: providerType,
156
+ apiKey: providerModelConfig.apiKey,
157
+ model: skillResult.preferredModel,
158
+ baseUrl: providerModelConfig.baseUrl,
159
+ });
160
+ // Capture originals before overwriting (only on first switch)
161
+ if (!originalProvider) {
162
+ originalProvider = activeProvider;
163
+ originalModel = activeModel;
164
+ }
165
+ activeProvider = newProvider;
166
+ activeModel = skillResult.preferredModel;
167
+ return true;
168
+ }
169
+ catch {
170
+ return false;
171
+ }
172
+ },
173
+ restore() {
174
+ if (originalProvider) {
175
+ activeProvider = originalProvider;
176
+ activeModel = originalModel ?? activeModel;
177
+ originalProvider = null;
178
+ originalModel = null;
179
+ }
180
+ },
181
+ };
182
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * StreamingResponseAccumulator — reassembles a complete `ProviderResponse`
3
+ * ({ content, tool_calls }) from a stream of `StreamDelta`s.
4
+ *
5
+ * Provider streams frequently split a tool call's JSON `arguments` across many
6
+ * deltas; this buffers each tool call by index and concatenates the fragments,
7
+ * so `runAgentLoop` receives well-formed, complete tool calls to execute.
8
+ */
9
+ import type { ProviderResponse } from '../providers/types.js';
10
+ import type { Usage } from './types.js';
11
+ export declare class StreamingResponseAccumulator {
12
+ private text;
13
+ private toolCalls;
14
+ private usage;
15
+ appendText(delta: string): void;
16
+ beginToolCall(index: number, id: string, name: string): void;
17
+ appendToolCallArgs(index: number, argsDelta: string): void;
18
+ setUsage(usage: Usage): void;
19
+ getUsage(): Usage | undefined;
20
+ toResponse(): ProviderResponse;
21
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * StreamingResponseAccumulator — reassembles a complete `ProviderResponse`
3
+ * ({ content, tool_calls }) from a stream of `StreamDelta`s.
4
+ *
5
+ * Provider streams frequently split a tool call's JSON `arguments` across many
6
+ * deltas; this buffers each tool call by index and concatenates the fragments,
7
+ * so `runAgentLoop` receives well-formed, complete tool calls to execute.
8
+ */
9
+ export class StreamingResponseAccumulator {
10
+ text = '';
11
+ toolCalls = new Map();
12
+ usage;
13
+ appendText(delta) {
14
+ this.text += delta;
15
+ }
16
+ beginToolCall(index, id, name) {
17
+ const existing = this.toolCalls.get(index);
18
+ if (existing) {
19
+ existing.id = id || existing.id;
20
+ existing.name = name || existing.name;
21
+ }
22
+ else {
23
+ this.toolCalls.set(index, { index, id, name, argumentsBuffer: '' });
24
+ }
25
+ }
26
+ appendToolCallArgs(index, argsDelta) {
27
+ const tc = this.toolCalls.get(index);
28
+ if (tc) {
29
+ tc.argumentsBuffer += argsDelta;
30
+ }
31
+ else {
32
+ // delta arrived before begin — create a placeholder at this index
33
+ this.toolCalls.set(index, { index, id: '', name: '', argumentsBuffer: argsDelta });
34
+ }
35
+ }
36
+ setUsage(usage) {
37
+ this.usage = usage;
38
+ }
39
+ getUsage() {
40
+ return this.usage;
41
+ }
42
+ toResponse() {
43
+ const tool_calls = [...this.toolCalls.values()]
44
+ .sort((a, b) => a.index - b.index)
45
+ .map((tc) => ({ id: tc.id, name: tc.name, arguments: tc.argumentsBuffer }));
46
+ return {
47
+ content: this.text || undefined,
48
+ tool_calls: tool_calls.length > 0 ? tool_calls : undefined,
49
+ };
50
+ }
51
+ }