@agent-spaces/server 0.3.65 → 0.4.0

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 (278) hide show
  1. package/dist/adapters/agent-runtime.js +8 -0
  2. package/dist/adapters/codex-function-tool-bridge.js +152 -0
  3. package/dist/adapters/codex-runtime.js +23 -4
  4. package/dist/adapters/git.js +25 -3
  5. package/dist/adapters/hermes-runtime.js +813 -0
  6. package/dist/adapters/langchain-runtime.js +384 -21
  7. package/dist/adapters/oh-my-pi-runtime.js +858 -0
  8. package/dist/adapters/open-agent-sdk-runtime.js +202 -5
  9. package/dist/adapters/open-agent-sdk-runtime.test.js +35 -0
  10. package/dist/agents/agent-message-parts.js +52 -2
  11. package/dist/agents/issue-task-controller.js +71 -2
  12. package/dist/agents/pull-request-agent.js +115 -0
  13. package/dist/agents/scheduler-agent.js +1 -1
  14. package/dist/agents/title-generator-agent.js +120 -0
  15. package/dist/app.js +61 -4
  16. package/dist/middleware/auth.js +1 -1
  17. package/dist/package.json +2 -1
  18. package/dist/routes/agent-sse.js +70 -6
  19. package/dist/routes/channel.js +23 -2
  20. package/dist/routes/chat-run.js +191 -0
  21. package/dist/routes/chat.js +142 -0
  22. package/dist/routes/command.js +16 -0
  23. package/dist/routes/data.js +189 -0
  24. package/dist/routes/git.js +10 -1
  25. package/dist/routes/import.js +199 -0
  26. package/dist/routes/issue.js +17 -5
  27. package/dist/routes/notification.js +10 -0
  28. package/dist/routes/plugin.js +69 -0
  29. package/dist/routes/version.js +95 -0
  30. package/dist/routes/workflow-hook.js +71 -0
  31. package/dist/routes/workflow.js +282 -7
  32. package/dist/routes/workspace.js +13 -4
  33. package/dist/routes/worktree.js +93 -0
  34. package/dist/services/agent.js +123 -36
  35. package/dist/services/ai-text.js +185 -0
  36. package/dist/services/builtin-tools/index.js +1 -0
  37. package/dist/services/builtin-tools/workflow-editor-tools.js +509 -0
  38. package/dist/services/builtin-tools/workflow-exec-tools.js +320 -0
  39. package/dist/services/chat.js +134 -0
  40. package/dist/services/command-process-manager.js +16 -0
  41. package/dist/services/execution-manager.js +1346 -0
  42. package/dist/services/generated-title.js +59 -0
  43. package/dist/services/gitignore.js +22 -18
  44. package/dist/services/interaction-manager.js +114 -0
  45. package/dist/services/issue-retry.js +25 -0
  46. package/dist/services/notification-center.js +9 -0
  47. package/dist/services/output-style.js +8 -1
  48. package/dist/services/plugin.js +257 -0
  49. package/dist/services/prompt-template.js +8 -1
  50. package/dist/services/pty.js +20 -0
  51. package/dist/services/search.js +16 -6
  52. package/dist/services/skill.js +2 -0
  53. package/dist/services/version.js +69 -0
  54. package/dist/services/workflow-command-runner.js +24 -0
  55. package/dist/services/workflow-trigger-service.js +137 -0
  56. package/dist/services/workflow.js +219 -39
  57. package/dist/services/workspace.js +23 -1
  58. package/dist/services/worktree.js +207 -0
  59. package/dist/storage/agent-store.js +8 -0
  60. package/dist/storage/chat-store.js +151 -0
  61. package/dist/storage/database-store.js +6 -0
  62. package/dist/storage/json-store.js +2 -1
  63. package/dist/storage/kanban-store.js +6 -0
  64. package/dist/storage/workflow-store.js +386 -22
  65. package/dist/storage/worktree-store.js +33 -0
  66. package/dist/web/404.html +1 -1
  67. package/dist/web/__next.__PAGE__.txt +4 -4
  68. package/dist/web/__next._full.txt +21 -21
  69. package/dist/web/__next._head.txt +4 -4
  70. package/dist/web/__next._index.txt +13 -13
  71. package/dist/web/__next._tree.txt +2 -2
  72. package/dist/web/_next/static/chunks/{08x85faoz58i7.js → 0-92u-tiuy40a.js} +4 -4
  73. package/dist/web/_next/static/chunks/{0db14450oqzhg.js → 0-yizd8osmas2.js} +1 -1
  74. package/dist/web/_next/static/chunks/0.ln~3mnv8.69.js +1 -0
  75. package/dist/web/_next/static/chunks/{11cchuwoupmbf.js → 01y3e9pemva7b.js} +10 -10
  76. package/dist/web/_next/static/chunks/02l~tmzrgny.~.js +1 -0
  77. package/dist/web/_next/static/chunks/03f1bwuynjdmk.js +1 -0
  78. package/dist/web/_next/static/chunks/04cb4kc9r2pd1.js +1 -0
  79. package/dist/web/_next/static/chunks/04de6ha5_e3pp.js +1 -0
  80. package/dist/web/_next/static/chunks/09he0p79ov1rn.js +1 -0
  81. package/dist/web/_next/static/chunks/09vpocs4zuc8h.js +1 -0
  82. package/dist/web/_next/static/chunks/0_o2exgr0e_nh.js +1 -0
  83. package/dist/web/_next/static/chunks/{0xbaakj0dfc0..js → 0_y9nskc-xt2k.js} +1 -1
  84. package/dist/web/_next/static/chunks/0clnj-1fi6u8j.js +3 -0
  85. package/dist/web/_next/static/chunks/0gv40by0zzxs_.js +1 -0
  86. package/dist/web/_next/static/chunks/0iusr8y88.kjg.js +1 -0
  87. package/dist/web/_next/static/chunks/{0-a.-hl3wv99k.js → 0j-8418fxvkd1.js} +1 -1
  88. package/dist/web/_next/static/chunks/0j.wxxyoclma5.js +1 -0
  89. package/dist/web/_next/static/chunks/{0.6b64sy0g5.3.js → 0jq.16sozqik..js} +1 -1
  90. package/dist/web/_next/static/chunks/0jw8xdk_fpnp0.css +1 -0
  91. package/dist/web/_next/static/chunks/0l1lkd-wcotma.js +1 -0
  92. package/dist/web/_next/static/chunks/{0u6h-hqc_i52e.js → 0ncxf_zr4n839.js} +1 -1
  93. package/dist/web/_next/static/chunks/0ndo7um~v7vco.js +1 -0
  94. package/dist/web/_next/static/chunks/0ngk.~kan8wq_.js +1 -0
  95. package/dist/web/_next/static/chunks/0ppr271hz0xoq.js +1 -0
  96. package/dist/web/_next/static/chunks/0rt9bofnn6qvn.js +1 -0
  97. package/dist/web/_next/static/chunks/0t64.9jd00hio.js +3 -0
  98. package/dist/web/_next/static/chunks/0taui9--5n6-5.js +1 -0
  99. package/dist/web/_next/static/chunks/0ty39169lh~z3.js +1 -0
  100. package/dist/web/_next/static/chunks/0v8nof~waedlu.js +1 -0
  101. package/dist/web/_next/static/chunks/{0-pi.67w.ymsr.js → 0vcfoagnpf.aw.js} +1 -1
  102. package/dist/web/_next/static/chunks/{0vis~lx0nxgn-.js → 0wwwq.gyyz0vg.js} +3 -3
  103. package/dist/web/_next/static/chunks/0x5lak8yz7pm7.js +1 -0
  104. package/dist/web/_next/static/chunks/0xon_.4-o0_zq.js +7 -0
  105. package/dist/web/_next/static/chunks/0zjnrbt7inime.js +1 -0
  106. package/dist/web/_next/static/chunks/0zwebpitvpuv1.js +1 -0
  107. package/dist/web/_next/static/chunks/0~02o4g-o6n~m.js +1 -0
  108. package/dist/web/_next/static/chunks/{0w7.tb29958zj.js → 0~fjezp.k4qk3.js} +1 -1
  109. package/dist/web/_next/static/chunks/0~sh93re1-_rp.js +1 -0
  110. package/dist/web/_next/static/chunks/10_niiq0k6o-p.js +5 -0
  111. package/dist/web/_next/static/chunks/12tjr0_2ccrtq.js +2 -0
  112. package/dist/web/_next/static/chunks/12x5.go_x9mfk.js +1 -0
  113. package/dist/web/_next/static/chunks/{0l2w0qz1ea7ql.js → 12~5dk9i9d16t.js} +2 -2
  114. package/dist/web/_next/static/chunks/13iye4rv5p31b.js +1 -0
  115. package/dist/web/_next/static/chunks/{01~quun6sefgl.js → 13~y-b.b9rrw4.js} +1 -1
  116. package/dist/web/_next/static/chunks/151wzhb5q0lo5.js +1 -0
  117. package/dist/web/_not-found/__next._full.txt +19 -19
  118. package/dist/web/_not-found/__next._head.txt +4 -4
  119. package/dist/web/_not-found/__next._index.txt +13 -13
  120. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
  121. package/dist/web/_not-found/__next._not-found.txt +3 -3
  122. package/dist/web/_not-found/__next._tree.txt +2 -2
  123. package/dist/web/_not-found.html +1 -1
  124. package/dist/web/_not-found.txt +19 -19
  125. package/dist/web/index.html +1 -1
  126. package/dist/web/index.txt +21 -21
  127. package/dist/web/login/__next._full.txt +21 -21
  128. package/dist/web/login/__next._head.txt +4 -4
  129. package/dist/web/login/__next._index.txt +13 -13
  130. package/dist/web/login/__next._tree.txt +2 -2
  131. package/dist/web/login/__next.login.__PAGE__.txt +4 -4
  132. package/dist/web/login/__next.login.txt +3 -3
  133. package/dist/web/login.html +1 -1
  134. package/dist/web/login.txt +21 -21
  135. package/dist/web/settings/__next._full.txt +23 -23
  136. package/dist/web/settings/__next._head.txt +4 -4
  137. package/dist/web/settings/__next._index.txt +13 -13
  138. package/dist/web/settings/__next._tree.txt +2 -2
  139. package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
  140. package/dist/web/settings/__next.settings.txt +5 -5
  141. package/dist/web/settings/agents/__next._full.txt +23 -23
  142. package/dist/web/settings/agents/__next._head.txt +4 -4
  143. package/dist/web/settings/agents/__next._index.txt +13 -13
  144. package/dist/web/settings/agents/__next._tree.txt +2 -2
  145. package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
  146. package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
  147. package/dist/web/settings/agents/__next.settings.txt +5 -5
  148. package/dist/web/settings/agents.html +1 -1
  149. package/dist/web/settings/agents.txt +23 -23
  150. package/dist/web/settings/mcps/__next._full.txt +23 -23
  151. package/dist/web/settings/mcps/__next._head.txt +4 -4
  152. package/dist/web/settings/mcps/__next._index.txt +13 -13
  153. package/dist/web/settings/mcps/__next._tree.txt +2 -2
  154. package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
  155. package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
  156. package/dist/web/settings/mcps/__next.settings.txt +5 -5
  157. package/dist/web/settings/mcps.html +1 -1
  158. package/dist/web/settings/mcps.txt +23 -23
  159. package/dist/web/settings/models/__next._full.txt +23 -23
  160. package/dist/web/settings/models/__next._head.txt +4 -4
  161. package/dist/web/settings/models/__next._index.txt +13 -13
  162. package/dist/web/settings/models/__next._tree.txt +2 -2
  163. package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
  164. package/dist/web/settings/models/__next.settings.models.txt +3 -3
  165. package/dist/web/settings/models/__next.settings.txt +5 -5
  166. package/dist/web/settings/models.html +1 -1
  167. package/dist/web/settings/models.txt +23 -23
  168. package/dist/web/settings/output-styles/__next._full.txt +23 -23
  169. package/dist/web/settings/output-styles/__next._head.txt +4 -4
  170. package/dist/web/settings/output-styles/__next._index.txt +13 -13
  171. package/dist/web/settings/output-styles/__next._tree.txt +2 -2
  172. package/dist/web/settings/output-styles/__next.settings.output-styles.__PAGE__.txt +4 -4
  173. package/dist/web/settings/output-styles/__next.settings.output-styles.txt +3 -3
  174. package/dist/web/settings/output-styles/__next.settings.txt +5 -5
  175. package/dist/web/settings/output-styles.html +1 -1
  176. package/dist/web/settings/output-styles.txt +23 -23
  177. package/dist/web/settings/prompts/__next._full.txt +23 -23
  178. package/dist/web/settings/prompts/__next._head.txt +4 -4
  179. package/dist/web/settings/prompts/__next._index.txt +13 -13
  180. package/dist/web/settings/prompts/__next._tree.txt +2 -2
  181. package/dist/web/settings/prompts/__next.settings.prompts.__PAGE__.txt +4 -4
  182. package/dist/web/settings/prompts/__next.settings.prompts.txt +3 -3
  183. package/dist/web/settings/prompts/__next.settings.txt +5 -5
  184. package/dist/web/settings/prompts.html +1 -1
  185. package/dist/web/settings/prompts.txt +23 -23
  186. package/dist/web/settings/providers/__next._full.txt +23 -23
  187. package/dist/web/settings/providers/__next._head.txt +4 -4
  188. package/dist/web/settings/providers/__next._index.txt +13 -13
  189. package/dist/web/settings/providers/__next._tree.txt +2 -2
  190. package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
  191. package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
  192. package/dist/web/settings/providers/__next.settings.txt +5 -5
  193. package/dist/web/settings/providers.html +1 -1
  194. package/dist/web/settings/providers.txt +23 -23
  195. package/dist/web/settings/skills/__next._full.txt +23 -23
  196. package/dist/web/settings/skills/__next._head.txt +4 -4
  197. package/dist/web/settings/skills/__next._index.txt +13 -13
  198. package/dist/web/settings/skills/__next._tree.txt +2 -2
  199. package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
  200. package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
  201. package/dist/web/settings/skills/__next.settings.txt +5 -5
  202. package/dist/web/settings/skills.html +1 -1
  203. package/dist/web/settings/skills.txt +23 -23
  204. package/dist/web/settings/tools/__next._full.txt +23 -23
  205. package/dist/web/settings/tools/__next._head.txt +4 -4
  206. package/dist/web/settings/tools/__next._index.txt +13 -13
  207. package/dist/web/settings/tools/__next._tree.txt +2 -2
  208. package/dist/web/settings/tools/__next.settings.tools.__PAGE__.txt +4 -4
  209. package/dist/web/settings/tools/__next.settings.tools.txt +3 -3
  210. package/dist/web/settings/tools/__next.settings.txt +5 -5
  211. package/dist/web/settings/tools.html +1 -1
  212. package/dist/web/settings/tools.txt +23 -23
  213. package/dist/web/settings.html +1 -1
  214. package/dist/web/settings.txt +23 -23
  215. package/dist/web/workflows/__next._full.txt +22 -22
  216. package/dist/web/workflows/__next._head.txt +4 -4
  217. package/dist/web/workflows/__next._index.txt +13 -13
  218. package/dist/web/workflows/__next._tree.txt +2 -2
  219. package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
  220. package/dist/web/workflows/__next.workflows.txt +3 -3
  221. package/dist/web/workflows.html +1 -1
  222. package/dist/web/workflows.txt +22 -22
  223. package/dist/web/workspace/_/__next._full.txt +20 -20
  224. package/dist/web/workspace/_/__next._head.txt +4 -4
  225. package/dist/web/workspace/_/__next._index.txt +13 -13
  226. package/dist/web/workspace/_/__next._tree.txt +2 -2
  227. package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
  228. package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
  229. package/dist/web/workspace/_/__next.workspace.txt +3 -3
  230. package/dist/web/workspace/_.html +1 -1
  231. package/dist/web/workspace/_.txt +20 -20
  232. package/dist/web/workspaces/__next._full.txt +21 -21
  233. package/dist/web/workspaces/__next._head.txt +4 -4
  234. package/dist/web/workspaces/__next._index.txt +13 -13
  235. package/dist/web/workspaces/__next._tree.txt +2 -2
  236. package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
  237. package/dist/web/workspaces/__next.workspaces.txt +3 -3
  238. package/dist/web/workspaces.html +1 -1
  239. package/dist/web/workspaces.txt +21 -21
  240. package/dist/ws/agent-prompt.js +36 -3
  241. package/dist/ws/agent-runner.js +30 -18
  242. package/dist/ws/chat-handler.js +89 -0
  243. package/dist/ws/connection-manager.js +49 -2
  244. package/dist/ws/execution-channels.js +88 -0
  245. package/dist/ws/handler.js +32 -5
  246. package/dist/ws/message-parts.js +130 -12
  247. package/dist/ws/terminal-handler.js +26 -0
  248. package/package.json +13 -1
  249. package/public/avatars/user.jpg +0 -0
  250. package/dist/web/_next/static/chunks/0-v_p6j5u3_9l.js +0 -1
  251. package/dist/web/_next/static/chunks/0.-x29t-8gk0r.js +0 -1
  252. package/dist/web/_next/static/chunks/0.l5e8~r7_0~p.js +0 -1
  253. package/dist/web/_next/static/chunks/0.n~yfwf4~6w-.js +0 -1
  254. package/dist/web/_next/static/chunks/00-syqzi-6zxr.js +0 -1
  255. package/dist/web/_next/static/chunks/02j4sr9psucf-.js +0 -1
  256. package/dist/web/_next/static/chunks/06si8l0doqe8p.js +0 -1
  257. package/dist/web/_next/static/chunks/08kfx8on9oty~.js +0 -1
  258. package/dist/web/_next/static/chunks/08ugbvzr20drp.js +0 -2
  259. package/dist/web/_next/static/chunks/0_cqojbge48kq.js +0 -5
  260. package/dist/web/_next/static/chunks/0d6~~lv0-6kex.js +0 -3
  261. package/dist/web/_next/static/chunks/0gdwop9cnpmtu.js +0 -1
  262. package/dist/web/_next/static/chunks/0ghx3j_.-gdyd.js +0 -1
  263. package/dist/web/_next/static/chunks/0kbvoxla7f751.js +0 -1
  264. package/dist/web/_next/static/chunks/0paaat8y96ayc.js +0 -7
  265. package/dist/web/_next/static/chunks/0pq318f8xgafg.js +0 -1
  266. package/dist/web/_next/static/chunks/0t6s8of0qr6nc.js +0 -1
  267. package/dist/web/_next/static/chunks/0xt-8wk21gs7g.js +0 -1
  268. package/dist/web/_next/static/chunks/0yd.m1afqg.2t.js +0 -3
  269. package/dist/web/_next/static/chunks/0yhbr8hl09b-5.js +0 -1
  270. package/dist/web/_next/static/chunks/0z~vqzbm_q371.js +0 -1
  271. package/dist/web/_next/static/chunks/0~~_ltu6dx_ch.js +0 -1
  272. package/dist/web/_next/static/chunks/12oxcgyhpaxw~.js +0 -1
  273. package/dist/web/_next/static/chunks/1452eibz62gs6.css +0 -1
  274. package/dist/web/_next/static/chunks/16zlreiems5cn.js +0 -1
  275. package/dist/web/_next/static/chunks/18awqjk9-o5m0.js +0 -1
  276. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_buildManifest.js +0 -0
  277. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_clientMiddlewareManifest.js +0 -0
  278. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_ssgManifest.js +0 -0
@@ -1,6 +1,10 @@
1
+ import { existsSync, readFileSync, statSync } from 'node:fs';
2
+ import { basename, join } from 'node:path';
3
+ import { MultiServerMCPClient } from '@langchain/mcp-adapters';
1
4
  import { createAgent, initChatModel, tool } from 'langchain';
2
5
  import { z } from 'zod';
3
6
  import { appendOutputStyleToPrompt, summarizeResult } from './agent-runtime-types.js';
7
+ const DEFAULT_DATA_DIR = join(process.env.HOME || '~', '.agent-spaces-data');
4
8
  /**
5
9
  * Runtime backed by LangChain.js.
6
10
  * Uses LangChain's provider-neutral createAgent API and adapts Agent Spaces tools.
@@ -8,6 +12,7 @@ import { appendOutputStyleToPrompt, summarizeResult } from './agent-runtime-type
8
12
  export class LangChainRuntime {
9
13
  config;
10
14
  abortController = null;
15
+ static nextRunId = 1;
11
16
  constructor(config = {}) {
12
17
  this.config = config;
13
18
  }
@@ -16,23 +21,58 @@ export class LangChainRuntime {
16
21
  const output = [];
17
22
  const cwd = workingDir || process.cwd();
18
23
  const startTime = Date.now();
19
- const d = (msg) => console.log(`[langchain] ${msg}`);
20
- const model = buildModelIdentifier(this.config);
21
- d(`starting | cwd=${cwd} provider=${this.config.provider ?? 'auto'} model=${model} baseURL=${this.config.baseURL ?? 'default'} maxTurns=${options?.maxTurns ?? '∞'} tools=${options?.functionTools?.map((runtimeTool) => runtimeTool.name).join(',') || '-'} sandboxDirs=${options?.sandboxDirs?.join(',') ?? '-'}`);
22
- d(`prompt: ${prompt.slice(0, 300)}${prompt.length > 300 ? '...' : ''}`);
24
+ const runId = LangChainRuntime.nextRunId++;
25
+ const d = (msg) => console.log(`[langchain:${runId}] ${msg}`);
26
+ const modelSettings = resolveLangChainModelSettings(this.config);
27
+ const model = modelSettings.modelIdentifier;
28
+ let mcpClient;
29
+ const skillPrompts = loadConfiguredSkillPrompts(options?.configDir, options?.skills);
30
+ d(`starting | cwd=${cwd} provider=${this.config.provider ?? 'auto'} langchainProvider=${modelSettings.provider ?? 'auto'} model=${model} baseURL=${this.config.baseURL ?? 'default'} apiKey=${this.config.apiKey ? 'set' : 'unset'} maxTurns=${options?.maxTurns ?? '∞'} tools=${options?.functionTools?.map((runtimeTool) => runtimeTool.name).join(',') || '-'} mcpServers=${Object.keys(options?.mcpServers ?? {}).join(',') || '-'} skills=${skillPrompts.map((skill) => skill.name).join(',') || '-'} sandboxDirs=${options?.sandboxDirs?.join(',') ?? '-'}`);
31
+ d(`options | ${summarizeOptionsForLog(options)}`);
32
+ d(`model config | ${summarizeForLog(maskSensitiveConfig(buildModelConfig(this.config)), 1000)}`);
33
+ d(`provider env | ${summarizeEnvForLog(buildProviderEnv(this.config, modelSettings.provider))}`);
34
+ if (modelSettings.providerCorrectionReason)
35
+ d(`provider adjusted | ${modelSettings.providerCorrectionReason}`);
36
+ d(`prompt | chars=${prompt.length} preview=${truncateForLog(prompt, 1000)}`);
37
+ if (skillPrompts.length) {
38
+ d(`skills loaded | ${skillPrompts.map((skill) => `${skill.name}:${skill.content.length}chars`).join(',')}`);
39
+ }
40
+ else if (options?.skills?.length) {
41
+ d(`skills requested but not loaded | requested=${options.skills.join(',')}`);
42
+ }
23
43
  try {
24
- const chatModel = await withTemporaryEnv(buildProviderEnv(this.config), () => initChatModel(model, buildModelConfig(this.config)));
44
+ d('initializing chat model');
45
+ const chatModel = await withTemporaryEnv(buildProviderEnv(this.config, modelSettings.provider), () => initChatModel(model, buildModelConfig(this.config)));
46
+ d(`chat model initialized | type=${getObjectTypeName(chatModel)}`);
47
+ mcpClient = createLangChainMcpClient(options?.mcpServers, output, options, d);
48
+ d(`MCP client ${mcpClient ? 'created' : 'not created'}`);
49
+ const mcpTools = mcpClient ? await mcpClient.getTools() : [];
50
+ if (mcpClient)
51
+ d(`resolved MCP tools | servers=${Object.keys(options?.mcpServers ?? {}).join(',') || '-'} count=${mcpTools.length} tools=${mcpTools.map((mcpTool) => mcpTool.name).join(',') || '-'}`);
52
+ const runtimeTools = buildLangChainTools(options?.functionTools, output, options, d);
53
+ d(`creating agent | runtimeTools=${runtimeTools.length} mcpTools=${mcpTools.length} systemPrompt=${options?.systemPrompt ? `${options.systemPrompt.length}chars` : '-'} outputStyle=${options?.outputStyle ?? '-'}`);
25
54
  const agent = createAgent({
26
55
  model: chatModel,
27
- tools: buildLangChainTools(options?.functionTools, output, options),
56
+ tools: [
57
+ ...runtimeTools,
58
+ ...mcpTools,
59
+ ],
28
60
  systemPrompt: options?.systemPrompt,
29
61
  });
30
- const result = await agent.invoke({ messages: [{ role: 'user', content: appendOutputStyleToPrompt(prompt, options?.outputStyle) }] }, {
62
+ d(`agent created | type=${getObjectTypeName(agent)}`);
63
+ const finalPrompt = appendOutputStyleToPrompt(injectSkillPrompts(prompt, skillPrompts), options?.outputStyle);
64
+ d(`final prompt | chars=${finalPrompt.length} skillCount=${skillPrompts.length} outputStyle=${options?.outputStyle ?? '-'} preview=${truncateForLog(finalPrompt, 1200)}`);
65
+ const recursionLimit = options?.maxTurns ? Math.max(2, options.maxTurns * 2 + 1) : undefined;
66
+ d(`invoking agent | recursionLimit=${recursionLimit ?? '-'} aborted=${this.abortController?.signal.aborted ? 'yes' : 'no'}`);
67
+ const result = await agent.invoke({ messages: [{ role: 'user', content: finalPrompt }] }, {
31
68
  signal: this.abortController?.signal,
32
- recursionLimit: options?.maxTurns ? Math.max(2, options.maxTurns * 2 + 1) : undefined,
69
+ recursionLimit,
33
70
  });
71
+ d(`agent result | ${summarizeAgentResultForLog(result)}`);
34
72
  const text = extractFinalText(result);
35
73
  const usage = extractUsage(result);
74
+ d(`final text | chars=${text.length} preview=${truncateForLog(text, 1200) || '-'}`);
75
+ d(`usage | ${usage ? summarizeForLog(usage, 500) : '-'}`);
36
76
  if (text) {
37
77
  output.push(text);
38
78
  options?.onEvent?.({ type: 'output', line: text });
@@ -43,7 +83,7 @@ export class LangChainRuntime {
43
83
  options?.onEvent?.({ type: 'output', line: usageLine });
44
84
  }
45
85
  const elapsed = Date.now() - startTime;
46
- d(`done ${elapsed}ms | tokens=${usage?.totalTokens ?? 'unknown'}`);
86
+ d(`done ${elapsed}ms | outputLines=${output.length} tokens=${usage?.totalTokens ?? 'unknown'}`);
47
87
  return {
48
88
  success: true,
49
89
  summary: summarizeResult(text),
@@ -55,41 +95,146 @@ export class LangChainRuntime {
55
95
  catch (err) {
56
96
  const elapsed = Date.now() - startTime;
57
97
  const message = err instanceof Error ? err.message : String(err);
58
- d(`failed ${elapsed}ms | ${message}`);
98
+ d(`failed ${elapsed}ms | name=${err instanceof Error ? err.name : typeof err} message=${truncateForLog(message, 1000)} outputLines=${output.length}`);
59
99
  if (err instanceof Error && err.stack)
60
100
  console.error(err.stack);
61
101
  return { success: false, summary: 'LangChain execution failed', artifacts: [], error: message, output };
62
102
  }
63
103
  finally {
104
+ if (mcpClient) {
105
+ d('closing MCP client');
106
+ try {
107
+ await mcpClient.close();
108
+ d('MCP client closed');
109
+ }
110
+ catch (err) {
111
+ d(`MCP client close failed | ${err instanceof Error ? err.message : String(err)}`);
112
+ }
113
+ }
64
114
  this.abortController = null;
115
+ d('runtime reset');
65
116
  }
66
117
  }
67
118
  stop() {
68
119
  this.abortController?.abort();
69
120
  }
70
121
  }
71
- function buildLangChainTools(functionTools, output, options) {
122
+ export function buildLangChainPromptWithSkills(prompt, agentDir, skills, outputStyle) {
123
+ return appendOutputStyleToPrompt(injectSkillPrompts(prompt, loadConfiguredSkillPrompts(agentDir, skills)), outputStyle);
124
+ }
125
+ function injectSkillPrompts(prompt, skills) {
126
+ if (!skills.length)
127
+ return prompt;
128
+ return [
129
+ prompt.trimEnd(),
130
+ '',
131
+ 'Configured skill instructions:',
132
+ `Enabled skill names: ${skills.map((skill) => skill.name).join(', ')}`,
133
+ 'These are configured skills for this run, even if a skill body looks like a design note, plan, or reference document.',
134
+ 'If the user asks what skills are available, answer from the enabled skill names above. Do not say that no skills are available when this list is non-empty.',
135
+ 'Follow the skill contents below when they are relevant to the user request.',
136
+ '',
137
+ ...skills.flatMap((skill) => [
138
+ `## Skill: ${skill.name}`,
139
+ '',
140
+ skill.content,
141
+ '',
142
+ ]),
143
+ ].join('\n').trimEnd();
144
+ }
145
+ function loadConfiguredSkillPrompts(agentDir, skills) {
146
+ if (!Array.isArray(skills))
147
+ return [];
148
+ return skills.flatMap((rawSkill) => {
149
+ const skillName = sanitizeSkillName(rawSkill);
150
+ if (!skillName)
151
+ return [];
152
+ const skillFile = resolveSkillFile(agentDir, skillName);
153
+ if (!skillFile)
154
+ return [];
155
+ const source = readFileSync(skillFile, 'utf-8');
156
+ const parsed = parseSkillMarkdown(source);
157
+ const content = parsed.body || source.trim();
158
+ if (!content)
159
+ return [];
160
+ return [{ name: sanitizeSkillName(parsed.meta.name) || skillName, content }];
161
+ });
162
+ }
163
+ function resolveSkillFile(agentDir, skillName) {
164
+ const candidates = [
165
+ ...(agentDir ? [
166
+ join(agentDir, 'skills', skillName, 'SKILL.md'),
167
+ join(agentDir, 'skills', `${skillName}.md`),
168
+ ] : []),
169
+ join(getDataDir(), 'skills', skillName, 'SKILL.md'),
170
+ join(getDataDir(), 'skills', `${skillName}.md`),
171
+ ];
172
+ return candidates.find((file) => existsSync(file) && statSync(file).size > 0);
173
+ }
174
+ function getDataDir() {
175
+ return process.env.AGENT_SPACES_DATA_DIR || DEFAULT_DATA_DIR;
176
+ }
177
+ function parseSkillMarkdown(source) {
178
+ const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
179
+ if (!match)
180
+ return { meta: {}, body: source.trim() };
181
+ const meta = {};
182
+ for (const line of match[1].split(/\r?\n/)) {
183
+ const parsed = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
184
+ if (!parsed)
185
+ continue;
186
+ meta[parsed[1]] = parsed[2].trim().replace(/^['"]|['"]$/g, '');
187
+ }
188
+ return { meta, body: source.slice(match[0].length).trim() };
189
+ }
190
+ function sanitizeSkillName(name) {
191
+ const raw = basename(name ?? '').replace(/\.md$/i, '').trim();
192
+ return raw.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
193
+ }
194
+ function buildLangChainTools(functionTools, output, options, log) {
72
195
  if (!functionTools?.length)
73
196
  return [];
74
197
  return functionTools.map((runtimeTool) => tool(async (input) => {
198
+ const startedAt = Date.now();
75
199
  const line = `Tool: ${runtimeTool.name} input=${JSON.stringify(input)}`;
200
+ log(`tool use | source=function name=${runtimeTool.name} descriptionChars=${runtimeTool.description.length} input=${summarizeForLog(input, 800)}`);
76
201
  output.push(line);
77
202
  options?.onEvent?.({ type: 'tool_use', id: runtimeTool.name, name: runtimeTool.name, input, line });
78
- const result = await runtimeTool.execute(input);
79
- options?.onEvent?.({ type: 'tool_result', toolUseId: runtimeTool.name, result });
80
- return result;
203
+ try {
204
+ const result = await runtimeTool.execute(input);
205
+ log(`tool result | source=function name=${runtimeTool.name} elapsedMs=${Date.now() - startedAt} output=${summarizeForLog(result, 1000)}`);
206
+ options?.onEvent?.({ type: 'tool_result', toolUseId: runtimeTool.name, result });
207
+ return stringifyToolResult(result);
208
+ }
209
+ catch (err) {
210
+ const message = err instanceof Error ? err.message : String(err);
211
+ log(`tool error | source=function name=${runtimeTool.name} elapsedMs=${Date.now() - startedAt} error=${truncateForLog(message, 1000)}`);
212
+ if (err instanceof Error && err.stack)
213
+ console.error(err.stack);
214
+ throw err;
215
+ }
81
216
  }, {
82
217
  name: runtimeTool.name,
83
218
  description: runtimeTool.description,
84
219
  schema: z.object({}).passthrough(),
85
220
  }));
86
221
  }
87
- function buildModelIdentifier(config) {
222
+ export function resolveLangChainModelSettings(config) {
88
223
  const model = config.model || 'gpt-4o-mini';
89
- const provider = normalizeLangChainProvider(config.provider);
90
- if (!provider || model.includes(':'))
91
- return model;
92
- return `${provider}:${model}`;
224
+ const configuredProvider = normalizeLangChainProvider(config.provider);
225
+ const inferredProvider = inferLangChainProviderFromBaseURL(config.baseURL);
226
+ const provider = configuredProvider === 'anthropic' && inferredProvider === 'openai'
227
+ ? inferredProvider
228
+ : configuredProvider;
229
+ const plainModel = provider ? stripLangChainProviderPrefix(model) : model;
230
+ const providerCorrectionReason = provider !== configuredProvider
231
+ ? `baseURL=${config.baseURL} is OpenAI-compatible, using ${provider} instead of ${configuredProvider}`
232
+ : undefined;
233
+ return {
234
+ provider,
235
+ modelIdentifier: provider ? `${provider}:${plainModel}` : plainModel,
236
+ providerCorrectionReason,
237
+ };
93
238
  }
94
239
  function normalizeLangChainProvider(provider) {
95
240
  switch (provider) {
@@ -113,8 +258,223 @@ function buildModelConfig(config) {
113
258
  configuration: config.baseURL ? { baseURL: config.baseURL } : undefined,
114
259
  });
115
260
  }
116
- function buildProviderEnv(config) {
117
- const provider = normalizeLangChainProvider(config.provider);
261
+ function inferLangChainProviderFromBaseURL(baseURL) {
262
+ if (!baseURL)
263
+ return undefined;
264
+ try {
265
+ const hostname = new URL(baseURL).hostname.toLowerCase();
266
+ if (hostname === 'open.bigmodel.cn' || hostname.endsWith('.bigmodel.cn'))
267
+ return 'openai';
268
+ }
269
+ catch {
270
+ return undefined;
271
+ }
272
+ return undefined;
273
+ }
274
+ function stripLangChainProviderPrefix(model) {
275
+ const match = /^(anthropic|openai|google-genai):(.+)$/i.exec(model);
276
+ return match?.[2] || model;
277
+ }
278
+ export function stringifyToolResult(result) {
279
+ if (result === undefined)
280
+ return 'null';
281
+ if (typeof result === 'string')
282
+ return result;
283
+ try {
284
+ return JSON.stringify(result, null, 2) ?? String(result);
285
+ }
286
+ catch {
287
+ return String(result);
288
+ }
289
+ }
290
+ function createLangChainMcpClient(mcpServers, output, options, log) {
291
+ log(`normalizing MCP servers | requested=${Object.keys(mcpServers ?? {}).join(',') || '-'}`);
292
+ const normalizedMcpServers = normalizeLangChainMcpServers(mcpServers);
293
+ if (!normalizedMcpServers) {
294
+ log('normalizing MCP servers | none');
295
+ return undefined;
296
+ }
297
+ log(`normalized MCP servers | ${summarizeMcpServersForLog(normalizedMcpServers)}`);
298
+ return new MultiServerMCPClient({
299
+ throwOnLoadError: true,
300
+ prefixToolNameWithServerName: true,
301
+ additionalToolNamePrefix: 'mcp',
302
+ useStandardContentBlocks: false,
303
+ outputHandling: 'content',
304
+ mcpServers: normalizedMcpServers,
305
+ beforeToolCall: (request) => {
306
+ const name = formatMcpToolName(request.serverName, request.name);
307
+ const line = `Tool: ${name} input=${JSON.stringify(request.args ?? {})}`;
308
+ log(`tool use | source=mcp server=${request.serverName} name=${name} input=${summarizeForLog(request.args ?? {}, 800)}`);
309
+ output.push(line);
310
+ options?.onEvent?.({ type: 'tool_use', id: name, name, input: request.args ?? {}, line });
311
+ },
312
+ afterToolCall: (result) => {
313
+ const name = formatMcpToolName(result.serverName, result.name);
314
+ const outputText = stringifyToolResult(result.result);
315
+ log(`tool result | source=mcp server=${result.serverName} name=${name} chars=${outputText.length} output=${truncateForLog(outputText, 1000)}`);
316
+ options?.onEvent?.({ type: 'tool_result', toolUseId: name, result: outputText });
317
+ return { result: outputText };
318
+ },
319
+ onProgress: (progress, source) => {
320
+ if (source.type !== 'tool')
321
+ return;
322
+ const name = formatMcpToolName(source.server, source.name);
323
+ const progressText = progress.total
324
+ ? `${progress.progress}/${progress.total}`
325
+ : String(progress.progress);
326
+ log(`tool progress | source=mcp server=${source.server} name=${name} progress=${progressText}${progress.message ? ` message=${truncateForLog(progress.message, 300)}` : ''}`);
327
+ },
328
+ onMessage: (message, source) => {
329
+ log(`mcp message | server=${source.server} level=${message.level ?? '-'} data=${summarizeForLog(message.data, 500)}`);
330
+ },
331
+ });
332
+ }
333
+ export function normalizeLangChainMcpServers(mcpServers) {
334
+ if (!mcpServers || Object.keys(mcpServers).length === 0)
335
+ return undefined;
336
+ return Object.fromEntries(Object.entries(mcpServers).map(([name, server]) => [name, normalizeLangChainMcpServer(server)]));
337
+ }
338
+ function normalizeLangChainMcpServer(server) {
339
+ if (!isRecord(server)) {
340
+ throw new Error('MCP server config must be an object.');
341
+ }
342
+ const normalized = normalizeOpenAgentCompatibleFetchMcpServer(server);
343
+ if (typeof normalized.url === 'string') {
344
+ return removeUndefined({
345
+ ...normalized,
346
+ transport: normalized.transport === 'sse' || normalized.type === 'sse' ? 'sse' : 'http',
347
+ url: normalized.url,
348
+ headers: isStringRecord(normalized.headers) ? normalized.headers : undefined,
349
+ });
350
+ }
351
+ if (typeof normalized.command === 'string') {
352
+ return removeUndefined({
353
+ ...normalized,
354
+ transport: 'stdio',
355
+ command: normalized.command,
356
+ args: Array.isArray(normalized.args) ? normalized.args.map(String) : [],
357
+ env: isStringRecord(normalized.env) ? normalized.env : undefined,
358
+ cwd: typeof normalized.cwd === 'string' ? normalized.cwd : undefined,
359
+ });
360
+ }
361
+ throw new Error('MCP server config requires either url or command.');
362
+ }
363
+ function normalizeOpenAgentCompatibleFetchMcpServer(server) {
364
+ const args = Array.isArray(server.args) ? server.args.map(String) : undefined;
365
+ const badFetchPackageIndex = args?.indexOf('@modelcontextprotocol/server-fetch') ?? -1;
366
+ if (String(server.command) !== 'npx' || !args || badFetchPackageIndex < 0)
367
+ return server;
368
+ return {
369
+ ...server,
370
+ command: 'uvx',
371
+ args: ['mcp-server-fetch', ...args.slice(badFetchPackageIndex + 1)],
372
+ env: {
373
+ PYTHONIOENCODING: 'utf-8',
374
+ ...(isStringRecord(server.env) ? server.env : {}),
375
+ },
376
+ };
377
+ }
378
+ function formatMcpToolName(serverName, toolName) {
379
+ return `mcp__${serverName}__${toolName}`;
380
+ }
381
+ function summarizeOptionsForLog(options) {
382
+ if (!options)
383
+ return '-';
384
+ return summarizeForLog({
385
+ maxTurns: options.maxTurns,
386
+ tools: options.tools,
387
+ functionTools: options.functionTools?.map((runtimeTool) => ({
388
+ name: runtimeTool.name,
389
+ descriptionChars: runtimeTool.description.length,
390
+ annotations: runtimeTool.annotations,
391
+ inputSchema: runtimeTool.inputSchema,
392
+ })),
393
+ mcpServers: Object.keys(options.mcpServers ?? {}),
394
+ skills: options.skills,
395
+ configDir: options.configDir,
396
+ sandboxDirs: options.sandboxDirs,
397
+ systemPromptChars: options.systemPrompt?.length,
398
+ userPromptChars: options.userPrompt?.length,
399
+ outputStyle: options.outputStyle,
400
+ resumeSessionId: options.resumeSessionId,
401
+ onEvent: options.onEvent ? 'set' : undefined,
402
+ }, 2000);
403
+ }
404
+ function summarizeAgentResultForLog(result) {
405
+ if (!isRecord(result))
406
+ return summarizeForLog(result, 1200);
407
+ const messages = Array.isArray(result.messages) ? result.messages : [];
408
+ return summarizeForLog({
409
+ keys: Object.keys(result),
410
+ messageCount: messages.length,
411
+ messages: messages.map((message, index) => summarizeMessageForLog(message, index)),
412
+ }, 2000);
413
+ }
414
+ function summarizeMessageForLog(message, index) {
415
+ if (!isRecord(message))
416
+ return { index, type: typeof message };
417
+ const content = stringifyMessageContent(message.content);
418
+ return removeUndefined({
419
+ index,
420
+ type: getMessageType(message) ?? message.type,
421
+ role: message.role,
422
+ name: message.name,
423
+ id: message.id,
424
+ contentChars: content.length,
425
+ contentPreview: truncateForLog(content, 300),
426
+ usage: message.usage_metadata,
427
+ responseMetadataKeys: isRecord(message.response_metadata) ? Object.keys(message.response_metadata) : undefined,
428
+ additionalKwargsKeys: isRecord(message.additional_kwargs) ? Object.keys(message.additional_kwargs) : undefined,
429
+ toolCallCount: Array.isArray(message.tool_calls) ? message.tool_calls.length : undefined,
430
+ invalidToolCallCount: Array.isArray(message.invalid_tool_calls) ? message.invalid_tool_calls.length : undefined,
431
+ });
432
+ }
433
+ function summarizeMcpServersForLog(mcpServers) {
434
+ return summarizeForLog(Object.fromEntries(Object.entries(mcpServers).map(([name, server]) => [name, summarizeMcpServerForLog(server)])), 2000);
435
+ }
436
+ function summarizeMcpServerForLog(server) {
437
+ const record = server;
438
+ return removeUndefined({
439
+ transport: record.transport,
440
+ url: typeof record.url === 'string' ? record.url : undefined,
441
+ command: typeof record.command === 'string' ? record.command : undefined,
442
+ args: Array.isArray(record.args) ? record.args : undefined,
443
+ cwd: typeof record.cwd === 'string' ? record.cwd : undefined,
444
+ headers: isStringRecord(record.headers) ? Object.keys(record.headers) : undefined,
445
+ env: isStringRecord(record.env) ? Object.keys(record.env) : undefined,
446
+ });
447
+ }
448
+ function summarizeEnvForLog(env) {
449
+ const configured = Object.entries(env)
450
+ .filter(([, value]) => Boolean(value))
451
+ .map(([key]) => key);
452
+ return configured.length ? configured.join(',') : '-';
453
+ }
454
+ function maskSensitiveConfig(config) {
455
+ return Object.fromEntries(Object.entries(config).map(([key, value]) => {
456
+ if (/api[_-]?key/i.test(key))
457
+ return [key, value ? 'set' : value];
458
+ if (key === 'configuration' && isRecord(value))
459
+ return [key, maskSensitiveConfig(value)];
460
+ return [key, value];
461
+ }));
462
+ }
463
+ function getObjectTypeName(value) {
464
+ return isRecord(value) && typeof value.constructor === 'function'
465
+ ? value.constructor.name
466
+ : typeof value;
467
+ }
468
+ function summarizeForLog(value, maxLength = 300) {
469
+ if (typeof value === 'string')
470
+ return truncateForLog(value, maxLength);
471
+ return truncateForLog(stringifyToolResult(value), maxLength);
472
+ }
473
+ function truncateForLog(value, maxLength = 300) {
474
+ return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
475
+ }
476
+ function buildProviderEnv(config, resolvedProvider) {
477
+ const provider = resolvedProvider ?? normalizeLangChainProvider(config.provider);
118
478
  return {
119
479
  OPENAI_API_KEY: provider === 'openai' ? config.apiKey : undefined,
120
480
  OPENAI_BASE_URL: provider === 'openai' ? config.baseURL : undefined,
@@ -201,6 +561,9 @@ function numberFrom(value) {
201
561
  function isRecord(value) {
202
562
  return Boolean(value && typeof value === 'object');
203
563
  }
564
+ function isStringRecord(value) {
565
+ return isRecord(value) && Object.values(value).every((entry) => typeof entry === 'string');
566
+ }
204
567
  function removeUndefined(value) {
205
568
  return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
206
569
  }