aws-runtime-bridge 1.9.149 → 1.9.150
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.
- package/CHANGELOG.md +31 -0
- package/dist/index.js +1 -1
- package/dist/routes/background-tasks.js +4 -4
- package/dist/routes/ltr.d.ts +3 -0
- package/dist/routes/ltr.js +2 -0
- package/dist/services/acode-bg-manager.d.ts +17 -0
- package/package/acode/dist/background-command-manager.d.ts +10 -0
- package/package/acode/dist/background-command-manager.js +5 -5
- package/package/acode/dist/background-command-store.d.ts +8 -0
- package/package/acode/dist/background-command-store.js +1 -1
- package/package/acode/dist/built-in-file-tools.js +2 -2
- package/package/aws-client-agent-mcp/dist/mcp-server.js +14 -14
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,37 @@ files 白名单),供调度中心「查看本次更新内容」弹窗按 `##
|
|
|
5
5
|
展示。发布新版本时须在仓库中先写好 `## v<version>` 段(prepack-guard 校验缺段
|
|
6
6
|
即阻断发布);Bridge 自更新成功后的运行时追加仅作为包内缺段时的兜底。
|
|
7
7
|
|
|
8
|
+
## v1.9.150
|
|
9
|
+
|
|
10
|
+
_发布日期:2026-09-07_
|
|
11
|
+
|
|
12
|
+
- 长文引用(LTR)全链路化:私信超长文本粘贴不再全文进消息,改为引用上传——MCP 与 bridge
|
|
13
|
+
新增 `/api/ltr/upload`,把大文本片段落盘到目标机器 `<系统临时目录>/aw-temp/LTR/YYYYMMDD/{uuid}.txt`
|
|
14
|
+
(单篇 ≤512KB,按天归档、写入时顺带清理超 7 天的目录),消息只传 `[bridgeName:绝对路径]` 引用,
|
|
15
|
+
agent 用 read_file 读取全文,避免超长文本撑爆消息表(MySQL TEXT 65535 字节)与 LLM 上下文。
|
|
16
|
+
前端配套:粘贴转胶囊 token(发送时统一上传 + 进度提示、hover 预览全文、未上传时随草稿保留并在
|
|
17
|
+
切换会话后恢复);LTR 图标与 capsule 颜色跟随主题。
|
|
18
|
+
- 后台命令僵尸状态根治(L1–L4):读路径对账(面板每次刷新先翻"磁盘 running 但本进程内存不命中"
|
|
19
|
+
的孤儿为 killed(runtime_crashed) 并原子回写);状态写入事务化(per-agent 串行写链消除并发
|
|
20
|
+
heartbeat/finalize 的丢失更新);生命周期补齐(exit+close 双监听,exit 后 2s 宽限强制 finalize,
|
|
21
|
+
孙进程持续持有管道不再留下永远 running 的状态;进程退出时落盘 killed(process_exit));running
|
|
22
|
+
任务心跳节流落盘(5s 或输出增量 64KB 触发)+ 启动时全量扫描所有 agent 目录孤儿。
|
|
23
|
+
面板改为只展示"真后台/运行中"命令,前台同步执行、从未转后台的命令不再占用面板。
|
|
24
|
+
- open-pencil CLI 多路径探测:pencil_export_document 依次探测 `AWS_OPEN_PENCIL_HOME` →
|
|
25
|
+
`<工作区>/open-pencil` → 仓库相对路径 → 逐级向上回溯,未命中时错误信息列出全部探测路径;
|
|
26
|
+
README 补充生产部署前置(工作区锚点或环境变量二选一,须用仓库构建版 CLI 以支持 GraphSnapshot)。
|
|
27
|
+
- create_meeting 有效性预检:角色占坑席位无可认领实例(如该角色唯一实例就是创建人)、或创建人把
|
|
28
|
+
自己重复加入受邀成员时直接返回错误,不再进入无谓的等待。
|
|
29
|
+
- 群消息撤回语义修正:发送者本人游标越过已撤回消息仍可撤回;他人游标越过则拒绝撤回(撤回不再
|
|
30
|
+
被"阅读过即作废"误伤)。两项修复均补齐单测。
|
|
31
|
+
- 输入框编辑体验:自建 undo/redo 栈(浏览器原生撤销对 contenteditable + 手动 DOM 不可靠),
|
|
32
|
+
mention / command / LTR 原子 token 可整体撤销;@mention 输入确认后自动把自由文本原地转换为
|
|
33
|
+
mention 高亮 token(`--cp-mention-highlight-*` 亮暗主题双套);「调用式回复」更名为「强调回复」。
|
|
34
|
+
- 文件下载回源文件名:下载/预览响应 Content-Disposition 改用上传时的原始文件名(URL name 参数),
|
|
35
|
+
并清洗控制字符、引号、分号与路径分隔符,避免 header 畸形与路径穿越。
|
|
36
|
+
- 消息中心:项目在线成员列表去重(同一 agent 绑定多实例时不再重复头像)。
|
|
37
|
+
- 健壮性:后台命令状态持久化失败仅告警不再崩进程;LTR 上传上游失败统一 502 且不回传内部错误细节。
|
|
38
|
+
|
|
8
39
|
## v1.9.149
|
|
9
40
|
|
|
10
41
|
_发布日期:2026-09-06_
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{existsSync as T,readFileSync as D}from"node:fs";import M from"node:os";import S from"node:path";import E from"cors";import P from"express";import{adapterRegistry as x}from"./adapter/index.js";import{attachBrowserWebSocketServer as F}from"./browser/browser-ws-bridge.js";import{getAcodeDataHome as G,getActiveBridgeHttpPort as I,getAutoRegisterConfigFilePath as O,getBridgeDataHome as _,getBridgeRuntimePropertiesFilePath as K,getBridgeTemplateConfigFile as L,reserveBridgeListenPort as N,validateProductionToken as j,writeBridgeRuntimePropertiesFile as W}from"./config.js";import{attachRemoteDesktopWebSocketServer as H}from"./remote-desktop/index.js";import{acodeTodosRouter as U}from"./routes/acode-todos.js";import{aiSourcesRouter as z}from"./routes/ai-sources.js";import{backgroundTasksRouter as J}from"./routes/background-tasks.js";import{browserListenersRouter as Y}from"./routes/browser-listeners.js";import{browserSessionsRouter as Z}from"./routes/browser-sessions.js";import{capabilitiesRouter as q}from"./routes/capabilities.js";import{computerRouter as Q}from"./routes/computer.js";import{dashboardRouter as V}from"./routes/dashboard.js";import{eventsRouter as X}from"./routes/events.js";import{fileBrowserRouter as ee}from"./routes/file-browser.js";import{fileChangeDiffRouter as re}from"./routes/file-change-diff.js";import{fileDiffRollbackRouter as te}from"./routes/file-diff-rollback.js";import{fileSnapshotRouter as ie}from"./routes/file-snapshot.js";import{gitRouter as oe}from"./routes/git.js";import{instanceRouter as ne,setGracefulShutdownFn as se}from"./routes/instance.js";import{jdksRouter as ae}from"./routes/jdks.js";import{launchRouter as me}from"./routes/launch.js";import{mcpExtraRouter as ue}from"./routes/mcp-extra.js";import{mcpRouter as ce}from"./routes/mcp.js";import{pluginsRouter as de}from"./routes/plugins.js";import{processesRouter as ge}from"./routes/processes.js";import{propertiesRouter as pe}from"./routes/properties.js";import{attachPtyWebSocketServer as fe,closeAllPtySessions as le,ptyRouter as be,ptySessions as we}from"./routes/pty.js";import{runtimeBindingRouter as he,logRuntimeBindingStartupState as Re}from"./routes/runtime-binding.js";import{sessionsRouter as Se}from"./routes/sessions.js";import{skillsRouter as Pe}from"./routes/skills.js";import{subAgentsRouter as Ie}from"./routes/sub-agents.js";import{systemMetricsRouter as ye}from"./routes/system-metrics.js";import{taskContextRouter as $e}from"./routes/task-context.js";import{terminalRouter as ke,sdkSessions as h}from"./routes/terminal.js";import{ymlRouter as Ce}from"./routes/yml.js";import{accessControlMiddleware as ve}from"./services/access-control.js";import{ensureAcodePackageReleased as Ae}from"./services/acode-package.js";import{getAgentProcessManager as y}from"./services/agent-process-manager.js";import{autoRegister as Be,unregister as Te,bridgeRestartCleanup as De,cancelBackgroundRegisterRetries as Me}from"./services/auto-register.js";import{ensureAwsClientAgentMcpReleased as Ee}from"./services/aws-client-agent-mcp.js";import{disposeBrowserSessionManager as xe}from"./services/browser-session-manager.js";import{handleCliCommand as Fe}from"./services/cli-commands.js";import{disposeDesktopMcpManager as Ge}from"./services/desktop-mcp-manager.js";import{getLaunchManager as Oe}from"./services/launch-manager.js";import{migrateOldPathsOnStartup as _e}from"./services/legacy-path-migration.js";import{deliverPendingBridgeLifecycleNotifications as Ke}from"./services/lifecycle-state.js";import{startOrphanMonitor as Le,stopOrphanMonitor as Ne}from"./services/orphan-monitor.js";import{pluginManager as je}from"./services/plugin-manager.js";import{detectOrphanProcesses as We}from"./services/process-detector.js";import{buildGracefulShutdownPlan as He,shouldCleanupPersistedSessionsOnStartup as Ue}from"./services/runtime-lifecycle-policy.js";import{ensureStartupConfig as ze}from"./services/startup-config-wizard.js";import{subAgentStore as Je}from"./services/sub-agent-manager-instance.js";import{loadPersistedSessions as $,savePersistedSessions as k}from"./services/terminal-persistence.js";import{stopTunnelClient as Ye}from"./services/tunnel-client.js";import{logger as e}from"./utils/logger.js";const C=await Fe();C.handled&&process.exit(C.exitCode),await ze(),j(),Ee(),Ae(),_e(),Re(),await N(),W(),e.info(`[runtime-bridge] \u5DF2\u5199\u5165\u8FD0\u884C\u65F6\u914D\u7F6E ${K()}\uFF08port=${I()}\uFF09`),e.info(`[runtime-bridge] ACode \u6570\u636E\u76EE\u5F55(\u7528\u6237): ${G()}`),e.info(`[runtime-bridge] Bridge \u6570\u636E\u76EE\u5F55: ${_()}`),e.info(`[runtime-bridge] \u6A21\u677F\u914D\u7F6E: ${L()}`);async function Ze(){try{if(await Be())e.info("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6210\u529F");else{const{loadConfig:o}=await import("./services/auto-register.js"),s=o();!!s.userKey&&s.enabled?e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6682\u65F6\u5931\u8D25\uFF0C\u5C06\u5728\u540E\u53F0\u6301\u7EED\u91CD\u8BD5\u76F4\u5230\u4E0E\u8C03\u5EA6\u4E2D\u5FC3\u5EFA\u7ACB\u8FDE\u63A5"):(e.info("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u672A\u914D\u7F6E\uFF0C\u7EE7\u7EED\u542F\u52A8\u670D\u52A1"),e.info(`[runtime-bridge] \u8981\u542F\u7528\u81EA\u52A8\u6CE8\u518C\uFF0C\u8BF7\u521B\u5EFA\u914D\u7F6E\u6587\u4EF6 ${O()}`))}}catch(r){const o=r;e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u51FA\u9519:",o.message)}}Ze().then(()=>qe()).then(()=>Ke()).then(()=>{e.info("[runtime-bridge] Bridge \u751F\u547D\u5468\u671F\u901A\u77E5\u540C\u6B65\u5B8C\u6210")}).catch(r=>{e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6216\u6E05\u7406\u51FA\u9519:",r)}),import("./services/update-notifier.js").then(({startUpdateNotifier:r})=>r()).catch(r=>{e.warn("[runtime-bridge] \u7248\u672C\u68C0\u67E5\u670D\u52A1\u542F\u52A8\u5931\u8D25:",r.message)});async function qe(){try{if(process.env.AWS_BRIDGE_RESTART_CLEANUP!=="true"){e.info("[runtime-bridge] \u8DF3\u8FC7 Bridge \u91CD\u542F\u6E05\u7406\uFF0C\u4FDD\u7559 Agent \u72B6\u6001\u4F9B\u8C03\u5EA6\u4E2D\u5FC3\u6062\u590D");return}const{getRegistrationState:r}=await import("./services/auto-register.js"),o=r();if(!o.registered||!o.instanceId){e.info("[runtime-bridge] \u5B9E\u4F8B\u672A\u6CE8\u518C\uFF0C\u8DF3\u8FC7 Bridge \u91CD\u542F\u6E05\u7406");return}e.info("[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u5DF2\u663E\u5F0F\u542F\u7528\uFF0C\u6B63\u5728\u901A\u77E5\u8C03\u5EA6\u4E2D\u5FC3\u6E05\u7406 Agent \u72B6\u6001...");const s=await De();s.success?e.info(`[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u6210\u529F\uFF0C\u6E05\u7406\u4E86 ${s.agentCount||0} \u4E2A Agent`):e.warn(`[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u5931\u8D25: ${s.error}`)}catch(r){const o=r;e.warn("[runtime-bridge] Bridge \u91CD\u542F\u6E05\u7406\u51FA\u9519:",o.message)}}async function Qe(){try{const r=await $();if(r.length===0){e.info("[runtime-bridge] \u65E0\u6301\u4E45\u5316\u4F1A\u8BDD\uFF0C\u8DF3\u8FC7\u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA");return}await y().rebuildRegistry(r.map(s=>({agentId:s.agentId,sessionId:s.sessionId,pid:s.pid,workspacePath:s.workspacePath,command:s.command,mode:"sdk"}))),e.info(`[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u5B8C\u6210: ${r.length} \u6761\u8BB0\u5F55`)}catch(r){const o=r;e.warn("[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u5931\u8D25:",o.message)}}Qe().catch(r=>{e.warn("[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u51FA\u9519:",r)});async function Ve(){try{const r=await import("node:fs/promises"),o=await import("node:path"),s=await import("node:os"),f=o.join(s.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");let u=!1;try{const t=await r.readFile(f,"utf-8");u=JSON.parse(t).preserveSessions===!0,await r.unlink(f),e.info(`[runtime-bridge] \u2605 \u68C0\u6D4B\u5230\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\uFF0CpreserveSessions=${u}`)}catch{}const a=await $();if(a.length===0){e.info("[runtime-bridge] \u542F\u52A8\u65F6\u65E0\u6301\u4E45\u5316\u4F1A\u8BDD\uFF0C\u65E0\u9700\u68C0\u6D4B\u5B64\u513F\u8FDB\u7A0B");return}if(!Ue(u)){e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${a.length} \u4E2A\u6301\u4E45\u5316\u4F1A\u8BDD\u5E76\u91CD\u5EFA\u8FDB\u7A0B\u6CE8\u518C\u8868`),await y().rebuildRegistry(a.map(n=>({agentId:n.agentId,sessionId:n.sessionId,pid:n.pid,workspacePath:n.workspacePath,command:n.command,mode:"sdk"})));return}e.info(`[runtime-bridge] \u2605 \u5DF2\u663E\u5F0F\u542F\u7528\u542F\u52A8\u6E05\u7406\uFF0C\u68C0\u6D4B ${a.length} \u4E2A\u6301\u4E45\u5316\u4F1A\u8BDD\u7684\u5B64\u513F\u8FDB\u7A0B...`);const d=await We(a.map(t=>({agentId:t.agentId,pid:t.pid,workspacePath:t.workspacePath,command:t.command})));if(d.length>0){e.warn(`[runtime-bridge] \u2605 \u68C0\u6D4B\u5230 ${d.length} \u4E2A\u5B64\u513F\u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF1A`);for(const t of d)e.warn(` - agentId=${t.agentId}, pid=${t.process.pid}, command=${t.process.command?.slice(0,50)}`);e.info("[runtime-bridge] \u6B63\u5728\u81EA\u52A8\u6E05\u7406\u6240\u6709\u5B64\u513F\u8FDB\u7A0B...");for(const t of d)try{if(t.process.pid)if(process.platform==="win32"){const{execSync:n}=await import("node:child_process");n(`taskkill /PID ${t.process.pid} /T /F`,{encoding:"utf8",timeout:3e3}),e.info(`[runtime-bridge] \u5DF2\u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B: agentId=${t.agentId}, PID=${t.process.pid}`)}else process.kill(t.process.pid,"SIGKILL"),e.info(`[runtime-bridge] \u5DF2\u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B: agentId=${t.agentId}, PID=${t.process.pid}`)}catch(n){const c=n;e.warn(`[runtime-bridge] \u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B\u5931\u8D25: agentId=${t.agentId}, PID=${t.process.pid}, error=${c.message}`)}await new Promise(t=>setTimeout(t,1e3))}await k([]),e.info(`[runtime-bridge] \u2605 \u542F\u52A8\u6E05\u7406\u5B8C\u6210\uFF0C\u5DF2\u6E05\u7A7A\u6240\u6709\u6301\u4E45\u5316\u4F1A\u8BDD\uFF08\u6E05\u7406\u4E86 ${d.length} \u4E2A\u5B64\u513F\u8FDB\u7A0B\uFF09`)}catch(r){const o=r;e.warn("[runtime-bridge] \u542F\u52A8\u65F6\u5B64\u513F\u8FDB\u7A0B\u68C0\u6D4B\u5931\u8D25:",o.message)}}Ve().catch(r=>{e.warn("[runtime-bridge] \u5B64\u513F\u8FDB\u7A0B\u68C0\u6D4B\u51FA\u9519:",r)}),Le().catch(r=>{e.warn("[runtime-bridge] \u542F\u52A8\u5B64\u513F\u8FDB\u7A0B\u76D1\u63A7\u5931\u8D25:",r)});const i=P();i.use(ve),i.use(E()),i.use(P.json({limit:"1mb"})),i.use("/runtime",he),i.use("/runtime",pe),i.use("/runtime",Ce),i.use("/runtime",ne),i.use("/runtime",Pe),i.use("/runtime",ce),i.use("/runtime",q),i.use("/runtime",ke),i.use("/runtime",Se),i.use("/runtime",ge),i.use("/runtime",me),i.use("/runtime",ae),i.use("/runtime",ye),i.use("/runtime",X),i.use("/runtime",oe),i.use("/runtime",ie),i.use("/runtime",U),i.use("/runtime",J),i.use("/runtime",Ie),i.use("/runtime",Z),i.use("/runtime",Y),i.use("/runtime",de),i.use("/runtime",ue),i.use("/runtime",z),i.use("/runtime",$e),i.use("/runtime",te),i.use("/runtime",re),i.use("/runtime/computer",Q),i.use("/pty",be),i.use("/",V),i.use("/api/file-browser",ee);function Xe(){const r=I(),o=i.listen(r,()=>{e.info(`[runtime-bridge] listening on ${r}`)});return fe(o),H(o),F(o),o.on("error",s=>{if(s.code==="EADDRINUSE"){e.error(`[runtime-bridge] port ${r} became unavailable after reservation (another process grabbed it concurrently). Set AWS_RUNTIME_BRIDGE_PORT to a free port and restart.`),process.exitCode=1;return}e.error("[runtime-bridge] failed to start server:",s),process.exitCode=1}),o}let v=!1;async function b(r,o,s=!1){if(v){e.info(`[runtime-bridge] \u5DF2\u5728\u5173\u95ED\u4E2D\uFF0C\u5FFD\u7565 ${r}`);return}v=!0,s||R()&&(s=!0,e.info("[runtime-bridge] \u2605 \u68C0\u6D4B\u5230 .preserve-sessions \u6807\u8BB0\u6587\u4EF6\uFF0C\u81EA\u52A8\u5347\u7EA7\u4E3A\u4FDD\u7559\u4F1A\u8BDD\u6A21\u5F0F")),e.info(`[runtime-bridge] \u6536\u5230 ${r} \u4FE1\u53F7\uFF0C\u5F00\u59CB\u4F18\u96C5\u5173\u95ED... (preserveSessions=${s})`);const f=Date.now(),u=He(s);try{const a=h.size;if(u.terminateSdkSessions){e.info(`[runtime-bridge] \u6B63\u5728\u505C\u6B62 ${a} \u4E2A SDK \u4F1A\u8BDD...`);for(const[t,n]of h.entries())try{const c=n.providerId||"claude-code",g=x.get(c);let p="";try{const m=g.getSessionPid?.(t);m&&(p=` (PID: ${m})`)}catch{}await g.terminateSession(t),e.info(`[runtime-bridge] SDK \u4F1A\u8BDD ${t} \u5DF2\u7EC8\u6B62${p}`),h.delete(t)}catch(c){const g=c;e.error(`[runtime-bridge] SDK \u4F1A\u8BDD ${t} \u7EC8\u6B62\u5931\u8D25:`,g.message),h.delete(t)}}else e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${a} \u4E2A SDK \u4F1A\u8BDD\uFF0C\u4E0D\u5728\u91CD\u542F\u6062\u590D\u573A\u666F\u4E2D\u7EC8\u6B62 Agent \u8FDB\u7A0B`);const l=we.size;u.closePtySessions?(e.info(`[runtime-bridge] \u6B63\u5728\u505C\u6B62 ${l} \u4E2A PTY \u4F1A\u8BDD...`),le("shutdown")):e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${l} \u4E2A PTY \u4F1A\u8BDD\uFF0C\u4E0D\u5728\u91CD\u542F\u6062\u590D\u573A\u666F\u4E2D\u5173\u95ED\u7EC8\u7AEF\u8FDB\u7A0B`);try{const{running:t,stopped:n}=await Oe().stopAll();t>0&&e.info(`[runtime-bridge] \u542F\u52A8\u9879\u8FDB\u7A0B\u5DF2\u505C\u6B62: running=${t}, stopped=${n}`)}catch(t){e.warn("[runtime-bridge] \u505C\u6B62\u542F\u52A8\u9879\u8FDB\u7A0B\u5931\u8D25:",t.message)}e.info("[runtime-bridge] \u6B63\u5728\u505C\u6B62\u5B64\u513F\u8FDB\u7A0B\u76D1\u63A7...");try{const c=(await import("file://"+S.join(__dirname,"..","package","acode","dist","background-command-manager.js")).catch(()=>({backgroundCommandManager:null}))).backgroundCommandManager;c&&(e.info("[runtime-bridge] \u6B63\u5728\u6E05\u7406\u540E\u53F0\u547D\u4EE4..."),await c.disposeAll())}catch(t){e.warn("[runtime-bridge] \u540E\u53F0\u547D\u4EE4\u6E05\u7406\u5931\u8D25: %s",t?.message)}if(Ne(),u.terminateResidualProcesses&&(e.info("[runtime-bridge] \u7B49\u5F85\u8FDB\u7A0B\u5B8C\u5168\u7EC8\u6B62..."),await new Promise(t=>setTimeout(t,1e3))),u.terminateResidualProcesses)try{const{detectOrphanProcesses:t,terminateProcessTree:n}=await import("./services/process-detector.js"),{loadPersistedSessions:c}=await import("./services/terminal-persistence.js"),g=await c();if(g.length>0){const p=await t(g.map(m=>({agentId:m.agentId,pid:m.pid,workspacePath:m.workspacePath,command:m.command})));if(p.length>0){e.warn(`[runtime-bridge] \u68C0\u6D4B\u5230 ${p.length} \u4E2A\u6B8B\u7559\u8FDB\u7A0B\uFF0C\u6B63\u5728\u5F3A\u5236\u7EC8\u6B62...`);for(const m of p)if(m.process.pid){const B=await n(m.process.pid);e.info(`[runtime-bridge] \u5DF2\u5F3A\u5236\u7EC8\u6B62\u6B8B\u7559\u8FDB\u7A0B\u6811\uFF1AagentId=${m.agentId}, PID=${m.process.pid}, \u7EC8\u6B62\u4E86 ${B.terminated} \u4E2A\u8FDB\u7A0B`)}}}}catch(t){const n=t;e.warn("[runtime-bridge] \u6B8B\u7559\u8FDB\u7A0B\u68C0\u67E5\u5931\u8D25:",n.message)}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6A21\u5F0F\u8DF3\u8FC7\u6B8B\u7559\u8FDB\u7A0B\u5F3A\u5236\u7EC8\u6B62\uFF0C\u7B49\u5F85\u8C03\u5EA6\u4E2D\u5FC3\u6062\u590D\u4F1A\u8BDD");if(u.unregisterInstance){e.info("[runtime-bridge] \u6B63\u5728\u6CE8\u9500\u5B9E\u4F8B...");try{await Te()}catch(t){const n=t;e.error("[runtime-bridge] \u6CE8\u9500\u5B9E\u4F8B\u5931\u8D25:",n.message)}}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6A21\u5F0F\u8DF3\u8FC7\u5B9E\u4F8B\u6CE8\u9500\uFF0C\u907F\u514D\u8C03\u5EA6\u4E2D\u5FC3\u5C06 Agent \u6807\u8BB0\u4E3A stopped");if(Me(),u.stopTunnelClient&&(e.info("[runtime-bridge] \u6B63\u5728\u5173\u95ED\u96A7\u9053\u5BA2\u6237\u7AEF..."),Ye()),u.clearPersistedSessions){e.info("[runtime-bridge] \u6B63\u5728\u6E05\u7A7A\u6301\u4E45\u5316\u4F1A\u8BDD\u72B6\u6001...");try{await k([])}catch(t){const n=t;e.error("[runtime-bridge] \u6E05\u7A7A\u6301\u4E45\u5316\u72B6\u6001\u5931\u8D25:",n.message)}}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6301\u4E45\u5316\u4F1A\u8BDD\u72B6\u6001\uFF08aws-mcp-server \u91CD\u542F\u6062\u590D\u573A\u666F\uFF09");try{await xe(),e.info("[runtime-bridge] browser \u5DE5\u5177\u6808\u5DF2\u6E05\u7406")}catch(t){e.warn("[runtime-bridge] browser \u5DE5\u5177\u6808\u6E05\u7406\u5931\u8D25:",t.message)}try{await Ge(),e.info("[runtime-bridge] desktop \u5DE5\u5177\u6808\u5DF2\u6E05\u7406")}catch(t){e.warn("[runtime-bridge] desktop \u5DE5\u5177\u6808\u6E05\u7406\u5931\u8D25:",t.message)}try{await je.stopAll(),e.info("[runtime-bridge] \u63D2\u4EF6\u76D1\u542C\u8FDB\u7A0B\u5DF2\u6E05\u7406")}catch(t){e.warn("[runtime-bridge] \u63D2\u4EF6\u76D1\u542C\u8FDB\u7A0B\u6E05\u7406\u5931\u8D25:",t.message)}const d=Date.now()-f;e.info(`[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u5B8C\u6210 (\u8017\u65F6 ${d}ms)\uFF0C\u505C\u6B62 ${a} SDK \u4F1A\u8BDD`),o.close(()=>{e.info("[runtime-bridge] HTTP \u670D\u52A1\u5668\u5DF2\u5173\u95ED"),process.exit(process.exitCode||0)}),setTimeout(()=>{e.warn("[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u8D85\u65F6\uFF0C\u5F3A\u5236\u9000\u51FA"),process.exit(1)},15e3)}catch(a){const l=a;e.error("[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF:",l),process.exit(1)}}Je.reconcileZombies().then(r=>{r>0&&e.info(`[runtime-bridge] \u5DF2\u6E05\u7406 ${r} \u4E2A\u50F5\u5C38 sub-agent \u8BB0\u5F55`)}).catch(r=>{e.warn("[runtime-bridge] \u6E05\u7406\u50F5\u5C38 sub-agent \u8BB0\u5F55\u5931\u8D25:",r.message)});const w=Xe(),A=S.join(M.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");function R(){try{if(T(A)){const r=D(A,"utf-8");return JSON.parse(r).preserveSessions===!0}}catch{}return!1}se(async(r,o)=>{await b(r,w,o)}),process.on("SIGTERM",()=>{const r=R();b("SIGTERM",w,r)}),process.on("SIGINT",()=>{const r=R();b("SIGINT",w,r)}),process.platform==="win32"&&process.on("SIGBREAK",()=>{const r=R();b("SIGBREAK",w,r)}),process.on("uncaughtException",r=>{e.error("[runtime-bridge] \u672A\u6355\u83B7\u5F02\u5E38:",r),b("uncaughtException",w)}),process.on("unhandledRejection",r=>{e.error("[runtime-bridge] \u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:",r)});
|
|
2
|
+
import{existsSync as T,readFileSync as D}from"node:fs";import M from"node:os";import S from"node:path";import{pathToFileURL as E}from"node:url";import x from"cors";import P from"express";import{adapterRegistry as F}from"./adapter/index.js";import{attachBrowserWebSocketServer as G}from"./browser/browser-ws-bridge.js";import{getAcodeDataHome as O,getActiveBridgeHttpPort as y,getAutoRegisterConfigFilePath as L,getBridgeDataHome as _,getBridgeRuntimePropertiesFilePath as j,getBridgeTemplateConfigFile as K,reserveBridgeListenPort as N,validateProductionToken as W,writeBridgeRuntimePropertiesFile as H}from"./config.js";import{attachRemoteDesktopWebSocketServer as U}from"./remote-desktop/index.js";import{acodeTodosRouter as z}from"./routes/acode-todos.js";import{aiSourcesRouter as J}from"./routes/ai-sources.js";import{backgroundTasksRouter as Y}from"./routes/background-tasks.js";import{browserListenersRouter as Z}from"./routes/browser-listeners.js";import{browserSessionsRouter as q}from"./routes/browser-sessions.js";import{capabilitiesRouter as Q}from"./routes/capabilities.js";import{computerRouter as V}from"./routes/computer.js";import{dashboardRouter as X}from"./routes/dashboard.js";import{eventsRouter as ee}from"./routes/events.js";import{fileBrowserRouter as re}from"./routes/file-browser.js";import{fileChangeDiffRouter as te}from"./routes/file-change-diff.js";import{fileDiffRollbackRouter as ie}from"./routes/file-diff-rollback.js";import{fileSnapshotRouter as oe}from"./routes/file-snapshot.js";import{gitRouter as ne}from"./routes/git.js";import{instanceRouter as se,setGracefulShutdownFn as ae}from"./routes/instance.js";import{jdksRouter as me}from"./routes/jdks.js";import{launchRouter as ue}from"./routes/launch.js";import{ltrRouter as ce}from"./routes/ltr.js";import{mcpExtraRouter as de}from"./routes/mcp-extra.js";import{mcpRouter as ge}from"./routes/mcp.js";import{pluginsRouter as pe}from"./routes/plugins.js";import{processesRouter as fe}from"./routes/processes.js";import{propertiesRouter as le}from"./routes/properties.js";import{attachPtyWebSocketServer as be,closeAllPtySessions as he,ptyRouter as we,ptySessions as Re}from"./routes/pty.js";import{runtimeBindingRouter as Se,logRuntimeBindingStartupState as Pe}from"./routes/runtime-binding.js";import{sessionsRouter as ye}from"./routes/sessions.js";import{skillsRouter as Ie}from"./routes/skills.js";import{subAgentsRouter as $e}from"./routes/sub-agents.js";import{systemMetricsRouter as ke}from"./routes/system-metrics.js";import{taskContextRouter as Ae}from"./routes/task-context.js";import{terminalRouter as Ce,sdkSessions as w}from"./routes/terminal.js";import{ymlRouter as ve}from"./routes/yml.js";import{accessControlMiddleware as Be}from"./services/access-control.js";import{ensureAcodePackageReleased as Te,getReleasedAcodeEntryPath as De}from"./services/acode-package.js";import{getAgentProcessManager as I}from"./services/agent-process-manager.js";import{autoRegister as Me,unregister as Ee,bridgeRestartCleanup as xe,cancelBackgroundRegisterRetries as Fe}from"./services/auto-register.js";import{ensureAwsClientAgentMcpReleased as Ge}from"./services/aws-client-agent-mcp.js";import{disposeBrowserSessionManager as Oe}from"./services/browser-session-manager.js";import{handleCliCommand as Le}from"./services/cli-commands.js";import{disposeDesktopMcpManager as _e}from"./services/desktop-mcp-manager.js";import{getLaunchManager as je}from"./services/launch-manager.js";import{migrateOldPathsOnStartup as Ke}from"./services/legacy-path-migration.js";import{deliverPendingBridgeLifecycleNotifications as Ne}from"./services/lifecycle-state.js";import{startOrphanMonitor as We,stopOrphanMonitor as He}from"./services/orphan-monitor.js";import{pluginManager as Ue}from"./services/plugin-manager.js";import{detectOrphanProcesses as ze}from"./services/process-detector.js";import{buildGracefulShutdownPlan as Je,shouldCleanupPersistedSessionsOnStartup as Ye}from"./services/runtime-lifecycle-policy.js";import{ensureStartupConfig as Ze}from"./services/startup-config-wizard.js";import{subAgentStore as qe}from"./services/sub-agent-manager-instance.js";import{loadPersistedSessions as $,savePersistedSessions as k}from"./services/terminal-persistence.js";import{stopTunnelClient as Qe}from"./services/tunnel-client.js";import{logger as e}from"./utils/logger.js";const A=await Le();A.handled&&process.exit(A.exitCode),await Ze(),W(),Ge(),Te(),Ke(),Pe(),await N(),H(),e.info(`[runtime-bridge] \u5DF2\u5199\u5165\u8FD0\u884C\u65F6\u914D\u7F6E ${j()}\uFF08port=${y()}\uFF09`),e.info(`[runtime-bridge] ACode \u6570\u636E\u76EE\u5F55(\u7528\u6237): ${O()}`),e.info(`[runtime-bridge] Bridge \u6570\u636E\u76EE\u5F55: ${_()}`),e.info(`[runtime-bridge] \u6A21\u677F\u914D\u7F6E: ${K()}`);async function Ve(){try{if(await Me())e.info("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6210\u529F");else{const{loadConfig:o}=await import("./services/auto-register.js"),n=o();!!n.userKey&&n.enabled?e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6682\u65F6\u5931\u8D25\uFF0C\u5C06\u5728\u540E\u53F0\u6301\u7EED\u91CD\u8BD5\u76F4\u5230\u4E0E\u8C03\u5EA6\u4E2D\u5FC3\u5EFA\u7ACB\u8FDE\u63A5"):(e.info("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u672A\u914D\u7F6E\uFF0C\u7EE7\u7EED\u542F\u52A8\u670D\u52A1"),e.info(`[runtime-bridge] \u8981\u542F\u7528\u81EA\u52A8\u6CE8\u518C\uFF0C\u8BF7\u521B\u5EFA\u914D\u7F6E\u6587\u4EF6 ${L()}`))}}catch(r){const o=r;e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u51FA\u9519:",o.message)}}Ve().then(()=>Xe()).then(()=>Ne()).then(()=>{e.info("[runtime-bridge] Bridge \u751F\u547D\u5468\u671F\u901A\u77E5\u540C\u6B65\u5B8C\u6210")}).catch(r=>{e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6216\u6E05\u7406\u51FA\u9519:",r)}),import("./services/update-notifier.js").then(({startUpdateNotifier:r})=>r()).catch(r=>{e.warn("[runtime-bridge] \u7248\u672C\u68C0\u67E5\u670D\u52A1\u542F\u52A8\u5931\u8D25:",r.message)});async function Xe(){try{if(process.env.AWS_BRIDGE_RESTART_CLEANUP!=="true"){e.info("[runtime-bridge] \u8DF3\u8FC7 Bridge \u91CD\u542F\u6E05\u7406\uFF0C\u4FDD\u7559 Agent \u72B6\u6001\u4F9B\u8C03\u5EA6\u4E2D\u5FC3\u6062\u590D");return}const{getRegistrationState:r}=await import("./services/auto-register.js"),o=r();if(!o.registered||!o.instanceId){e.info("[runtime-bridge] \u5B9E\u4F8B\u672A\u6CE8\u518C\uFF0C\u8DF3\u8FC7 Bridge \u91CD\u542F\u6E05\u7406");return}e.info("[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u5DF2\u663E\u5F0F\u542F\u7528\uFF0C\u6B63\u5728\u901A\u77E5\u8C03\u5EA6\u4E2D\u5FC3\u6E05\u7406 Agent \u72B6\u6001...");const n=await xe();n.success?e.info(`[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u6210\u529F\uFF0C\u6E05\u7406\u4E86 ${n.agentCount||0} \u4E2A Agent`):e.warn(`[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u5931\u8D25: ${n.error}`)}catch(r){const o=r;e.warn("[runtime-bridge] Bridge \u91CD\u542F\u6E05\u7406\u51FA\u9519:",o.message)}}async function er(){try{const r=await $();if(r.length===0){e.info("[runtime-bridge] \u65E0\u6301\u4E45\u5316\u4F1A\u8BDD\uFF0C\u8DF3\u8FC7\u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA");return}await I().rebuildRegistry(r.map(n=>({agentId:n.agentId,sessionId:n.sessionId,pid:n.pid,workspacePath:n.workspacePath,command:n.command,mode:"sdk"}))),e.info(`[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u5B8C\u6210: ${r.length} \u6761\u8BB0\u5F55`)}catch(r){const o=r;e.warn("[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u5931\u8D25:",o.message)}}er().catch(r=>{e.warn("[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u5931\u8D25:",r)}),import(E(S.join(De(),"..","background-command-manager.js")).href).then(async r=>{const o=r.backgroundCommandManager;if(!o||typeof o.reconcileAllAgents!="function"){e.warn("[runtime-bridge] \u540E\u53F0\u547D\u4EE4\u7BA1\u7406\u5668\u4E0D\u53EF\u7528\uFF0C\u8DF3\u8FC7\u542F\u52A8\u5B64\u513F\u5BF9\u8D26");return}const n=await o.reconcileAllAgents();n>0&&e.info(`[runtime-bridge] \u2605 \u542F\u52A8\u5B64\u513F\u5BF9\u8D26\u5B8C\u6210\uFF1A\u4FEE\u590D ${n} \u6761\u540E\u53F0\u547D\u4EE4\u72B6\u6001`)}).catch(r=>{e.warn("[runtime-bridge] \u542F\u52A8\u5B64\u513F\u5BF9\u8D26\u5931\u8D25:",r.message)});async function rr(){try{const r=await import("node:fs/promises"),o=await import("node:path"),n=await import("node:os"),f=o.join(n.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");let u=!1;try{const t=await r.readFile(f,"utf-8");u=JSON.parse(t).preserveSessions===!0,await r.unlink(f),e.info(`[runtime-bridge] \u2605 \u68C0\u6D4B\u5230\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\uFF0CpreserveSessions=${u}`)}catch{}const a=await $();if(a.length===0){e.info("[runtime-bridge] \u542F\u52A8\u65F6\u65E0\u6301\u4E45\u5316\u4F1A\u8BDD\uFF0C\u65E0\u9700\u68C0\u6D4B\u5B64\u513F\u8FDB\u7A0B");return}if(!Ye(u)){e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${a.length} \u4E2A\u6301\u4E45\u5316\u4F1A\u8BDD\u5E76\u91CD\u5EFA\u8FDB\u7A0B\u6CE8\u518C\u8868`),await I().rebuildRegistry(a.map(s=>({agentId:s.agentId,sessionId:s.sessionId,pid:s.pid,workspacePath:s.workspacePath,command:s.command,mode:"sdk"})));return}e.info(`[runtime-bridge] \u2605 \u5DF2\u663E\u5F0F\u542F\u7528\u542F\u52A8\u6E05\u7406\uFF0C\u68C0\u6D4B ${a.length} \u4E2A\u6301\u4E45\u5316\u4F1A\u8BDD\u7684\u5B64\u513F\u8FDB\u7A0B...`);const d=await ze(a.map(t=>({agentId:t.agentId,pid:t.pid,workspacePath:t.workspacePath,command:t.command})));if(d.length>0){e.warn(`[runtime-bridge] \u2605 \u68C0\u6D4B\u5230 ${d.length} \u4E2A\u5B64\u513F\u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF1A`);for(const t of d)e.warn(` - agentId=${t.agentId}, pid=${t.process.pid}, command=${t.process.command?.slice(0,50)}`);e.info("[runtime-bridge] \u6B63\u5728\u81EA\u52A8\u6E05\u7406\u6240\u6709\u5B64\u513F\u8FDB\u7A0B...");for(const t of d)try{if(t.process.pid)if(process.platform==="win32"){const{execSync:s}=await import("node:child_process");s(`taskkill /PID ${t.process.pid} /T /F`,{encoding:"utf8",timeout:3e3}),e.info(`[runtime-bridge] \u5DF2\u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B: agentId=${t.agentId}, PID=${t.process.pid}`)}else process.kill(t.process.pid,"SIGKILL"),e.info(`[runtime-bridge] \u5DF2\u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B: agentId=${t.agentId}, PID=${t.process.pid}`)}catch(s){const c=s;e.warn(`[runtime-bridge] \u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B\u5931\u8D25: agentId=${t.agentId}, PID=${t.process.pid}, error=${c.message}`)}await new Promise(t=>setTimeout(t,1e3))}await k([]),e.info(`[runtime-bridge] \u2605 \u542F\u52A8\u6E05\u7406\u5B8C\u6210\uFF0C\u5DF2\u6E05\u7A7A\u6240\u6709\u6301\u4E45\u5316\u4F1A\u8BDD\uFF08\u6E05\u7406\u4E86 ${d.length} \u4E2A\u5B64\u513F\u8FDB\u7A0B\uFF09`)}catch(r){const o=r;e.warn("[runtime-bridge] \u542F\u52A8\u65F6\u5B64\u513F\u8FDB\u7A0B\u68C0\u6D4B\u5931\u8D25:",o.message)}}rr().catch(r=>{e.warn("[runtime-bridge] \u5B64\u513F\u8FDB\u7A0B\u68C0\u6D4B\u51FA\u9519:",r)}),We().catch(r=>{e.warn("[runtime-bridge] \u542F\u52A8\u5B64\u513F\u8FDB\u7A0B\u76D1\u63A7\u5931\u8D25:",r)});const i=P();i.use(Be),i.use(x()),i.use(P.json({limit:"1mb"})),i.use("/runtime",Se),i.use("/runtime",le),i.use("/runtime",ve),i.use("/runtime",se),i.use("/runtime",Ie),i.use("/runtime",ge),i.use("/runtime",Q),i.use("/runtime",Ce),i.use("/runtime",ye),i.use("/runtime",fe),i.use("/runtime",ue),i.use("/runtime",me),i.use("/runtime",ke),i.use("/runtime",ee),i.use("/runtime",ne),i.use("/runtime",oe),i.use("/runtime",z),i.use("/runtime",Y),i.use("/runtime",$e),i.use("/runtime",q),i.use("/runtime",Z),i.use("/runtime",pe),i.use("/runtime",de),i.use("/runtime",J),i.use("/runtime",Ae),i.use("/runtime",ie),i.use("/runtime",te),i.use("/runtime/computer",V),i.use("/pty",we),i.use("/",X),i.use("/api/file-browser",re),i.use("/api/ltr",ce);function tr(){const r=y(),o=i.listen(r,()=>{e.info(`[runtime-bridge] listening on ${r}`)});return be(o),U(o),G(o),o.on("error",n=>{if(n.code==="EADDRINUSE"){e.error(`[runtime-bridge] port ${r} became unavailable after reservation (another process grabbed it concurrently). Set AWS_RUNTIME_BRIDGE_PORT to a free port and restart.`),process.exitCode=1;return}e.error("[runtime-bridge] failed to start server:",n),process.exitCode=1}),o}let C=!1;async function b(r,o,n=!1){if(C){e.info(`[runtime-bridge] \u5DF2\u5728\u5173\u95ED\u4E2D\uFF0C\u5FFD\u7565 ${r}`);return}C=!0,n||R()&&(n=!0,e.info("[runtime-bridge] \u2605 \u68C0\u6D4B\u5230 .preserve-sessions \u6807\u8BB0\u6587\u4EF6\uFF0C\u81EA\u52A8\u5347\u7EA7\u4E3A\u4FDD\u7559\u4F1A\u8BDD\u6A21\u5F0F")),e.info(`[runtime-bridge] \u6536\u5230 ${r} \u4FE1\u53F7\uFF0C\u5F00\u59CB\u4F18\u96C5\u5173\u95ED... (preserveSessions=${n})`);const f=Date.now(),u=Je(n);try{const a=w.size;if(u.terminateSdkSessions){e.info(`[runtime-bridge] \u6B63\u5728\u505C\u6B62 ${a} \u4E2A SDK \u4F1A\u8BDD...`);for(const[t,s]of w.entries())try{const c=s.providerId||"claude-code",g=F.get(c);let p="";try{const m=g.getSessionPid?.(t);m&&(p=` (PID: ${m})`)}catch{}await g.terminateSession(t),e.info(`[runtime-bridge] SDK \u4F1A\u8BDD ${t} \u5DF2\u7EC8\u6B62${p}`),w.delete(t)}catch(c){const g=c;e.error(`[runtime-bridge] SDK \u4F1A\u8BDD ${t} \u7EC8\u6B62\u5931\u8D25:`,g.message),w.delete(t)}}else e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${a} \u4E2A SDK \u4F1A\u8BDD\uFF0C\u4E0D\u5728\u91CD\u542F\u6062\u590D\u573A\u666F\u4E2D\u7EC8\u6B62 Agent \u8FDB\u7A0B`);const l=Re.size;u.closePtySessions?(e.info(`[runtime-bridge] \u6B63\u5728\u505C\u6B62 ${l} \u4E2A PTY \u4F1A\u8BDD...`),he("shutdown")):e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${l} \u4E2A PTY \u4F1A\u8BDD\uFF0C\u4E0D\u5728\u91CD\u542F\u6062\u590D\u573A\u666F\u4E2D\u5173\u95ED\u7EC8\u7AEF\u8FDB\u7A0B`);try{const{running:t,stopped:s}=await je().stopAll();t>0&&e.info(`[runtime-bridge] \u542F\u52A8\u9879\u8FDB\u7A0B\u5DF2\u505C\u6B62: running=${t}, stopped=${s}`)}catch(t){e.warn("[runtime-bridge] \u505C\u6B62\u542F\u52A8\u9879\u8FDB\u7A0B\u5931\u8D25:",t.message)}e.info("[runtime-bridge] \u6B63\u5728\u505C\u6B62\u5B64\u513F\u8FDB\u7A0B\u76D1\u63A7...");try{const c=(await import("file://"+S.join(__dirname,"..","package","acode","dist","background-command-manager.js")).catch(()=>({backgroundCommandManager:null}))).backgroundCommandManager;c&&(e.info("[runtime-bridge] \u6B63\u5728\u6E05\u7406\u540E\u53F0\u547D\u4EE4..."),await c.disposeAll())}catch(t){e.warn("[runtime-bridge] \u540E\u53F0\u547D\u4EE4\u6E05\u7406\u5931\u8D25: %s",t?.message)}if(He(),u.terminateResidualProcesses&&(e.info("[runtime-bridge] \u7B49\u5F85\u8FDB\u7A0B\u5B8C\u5168\u7EC8\u6B62..."),await new Promise(t=>setTimeout(t,1e3))),u.terminateResidualProcesses)try{const{detectOrphanProcesses:t,terminateProcessTree:s}=await import("./services/process-detector.js"),{loadPersistedSessions:c}=await import("./services/terminal-persistence.js"),g=await c();if(g.length>0){const p=await t(g.map(m=>({agentId:m.agentId,pid:m.pid,workspacePath:m.workspacePath,command:m.command})));if(p.length>0){e.warn(`[runtime-bridge] \u68C0\u6D4B\u5230 ${p.length} \u4E2A\u6B8B\u7559\u8FDB\u7A0B\uFF0C\u6B63\u5728\u5F3A\u5236\u7EC8\u6B62...`);for(const m of p)if(m.process.pid){const B=await s(m.process.pid);e.info(`[runtime-bridge] \u5DF2\u5F3A\u5236\u7EC8\u6B62\u6B8B\u7559\u8FDB\u7A0B\u6811\uFF1AagentId=${m.agentId}, PID=${m.process.pid}, \u7EC8\u6B62\u4E86 ${B.terminated} \u4E2A\u8FDB\u7A0B`)}}}}catch(t){const s=t;e.warn("[runtime-bridge] \u6B8B\u7559\u8FDB\u7A0B\u68C0\u67E5\u5931\u8D25:",s.message)}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6A21\u5F0F\u8DF3\u8FC7\u6B8B\u7559\u8FDB\u7A0B\u5F3A\u5236\u7EC8\u6B62\uFF0C\u7B49\u5F85\u8C03\u5EA6\u4E2D\u5FC3\u6062\u590D\u4F1A\u8BDD");if(u.unregisterInstance){e.info("[runtime-bridge] \u6B63\u5728\u6CE8\u9500\u5B9E\u4F8B...");try{await Ee()}catch(t){const s=t;e.error("[runtime-bridge] \u6CE8\u9500\u5B9E\u4F8B\u5931\u8D25:",s.message)}}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6A21\u5F0F\u8DF3\u8FC7\u5B9E\u4F8B\u6CE8\u9500\uFF0C\u907F\u514D\u8C03\u5EA6\u4E2D\u5FC3\u5C06 Agent \u6807\u8BB0\u4E3A stopped");if(Fe(),u.stopTunnelClient&&(e.info("[runtime-bridge] \u6B63\u5728\u5173\u95ED\u96A7\u9053\u5BA2\u6237\u7AEF..."),Qe()),u.clearPersistedSessions){e.info("[runtime-bridge] \u6B63\u5728\u6E05\u7A7A\u6301\u4E45\u5316\u4F1A\u8BDD\u72B6\u6001...");try{await k([])}catch(t){const s=t;e.error("[runtime-bridge] \u6E05\u7A7A\u6301\u4E45\u5316\u72B6\u6001\u5931\u8D25:",s.message)}}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6301\u4E45\u5316\u4F1A\u8BDD\u72B6\u6001\uFF08aws-mcp-server \u91CD\u542F\u6062\u590D\u573A\u666F\uFF09");try{await Oe(),e.info("[runtime-bridge] browser \u5DE5\u5177\u6808\u5DF2\u6E05\u7406")}catch(t){e.warn("[runtime-bridge] browser \u5DE5\u5177\u6808\u6E05\u7406\u5931\u8D25:",t.message)}try{await _e(),e.info("[runtime-bridge] desktop \u5DE5\u5177\u6808\u5DF2\u6E05\u7406")}catch(t){e.warn("[runtime-bridge] desktop \u5DE5\u5177\u6808\u6E05\u7406\u5931\u8D25:",t.message)}try{await Ue.stopAll(),e.info("[runtime-bridge] \u63D2\u4EF6\u76D1\u542C\u8FDB\u7A0B\u5DF2\u6E05\u7406")}catch(t){e.warn("[runtime-bridge] \u63D2\u4EF6\u76D1\u542C\u8FDB\u7A0B\u6E05\u7406\u5931\u8D25:",t.message)}const d=Date.now()-f;e.info(`[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u5B8C\u6210 (\u8017\u65F6 ${d}ms)\uFF0C\u505C\u6B62 ${a} SDK \u4F1A\u8BDD`),o.close(()=>{e.info("[runtime-bridge] HTTP \u670D\u52A1\u5668\u5DF2\u5173\u95ED"),process.exit(process.exitCode||0)}),setTimeout(()=>{e.warn("[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u8D85\u65F6\uFF0C\u5F3A\u5236\u9000\u51FA"),process.exit(1)},15e3)}catch(a){const l=a;e.error("[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF:",l),process.exit(1)}}qe.reconcileZombies().then(r=>{r>0&&e.info(`[runtime-bridge] \u5DF2\u6E05\u7406 ${r} \u4E2A\u50F5\u5C38 sub-agent \u8BB0\u5F55`)}).catch(r=>{e.warn("[runtime-bridge] \u6E05\u7406\u50F5\u5C38 sub-agent \u8BB0\u5F55\u5931\u8D25:",r.message)});const h=tr(),v=S.join(M.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");function R(){try{if(T(v)){const r=D(v,"utf-8");return JSON.parse(r).preserveSessions===!0}}catch{}return!1}ae(async(r,o)=>{await b(r,h,o)}),process.on("SIGTERM",()=>{const r=R();b("SIGTERM",h,r)}),process.on("SIGINT",()=>{const r=R();b("SIGINT",h,r)}),process.platform==="win32"&&process.on("SIGBREAK",()=>{const r=R();b("SIGBREAK",h,r)}),process.on("uncaughtException",r=>{e.error("[runtime-bridge] \u672A\u6355\u83B7\u5F02\u5E38:",r),b("uncaughtException",h)}),process.on("unhandledRejection",r=>{e.error("[runtime-bridge] \u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:",r)});
|
|
3
3
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{promises as
|
|
2
|
-
`);return n>0&&
|
|
1
|
+
import{promises as S}from"node:fs";import y from"node:path";import{pathToFileURL as C}from"node:url";import{Router as j}from"express";import{getAcodeDataHome as T}from"../config.js";import{validateToken as k}from"../middleware/auth.js";import{getAcodeBgManager as h}from"../services/acode-bg-manager.js";import{getReleasedAcodeEntryPath as B}from"../services/acode-package.js";import{createLogger as E}from"../utils/logger.js";import{listPtySessions as _}from"./pty.js";let m=null;function I(){return m||(m=(async()=>{try{const e=B(),t=y.join(y.dirname(e),"background-command-store.js"),d=await import(C(t).href);return d.BackgroundCommandStore?new d.BackgroundCommandStore:(l.warn("[acode-bg-tasks] BackgroundCommandStore \u4E0D\u53EF\u7528,\u78C1\u76D8\u515C\u5E95\u7981\u7528"),null)}catch(e){return l.warn("[acode-bg-tasks] \u52A0\u8F7D BackgroundCommandStore \u5931\u8D25: %s",e?.message),null}})().then(e=>(e||(m=null),e)),m)}const l=E("acode-bg-tasks"),p=j();function L(e){return String(e).replace(/[^a-zA-Z0-9_-]/g,"_")}function v(e){return y.join(T(),"bg-tasks",L(e),"state.json")}p.get("/acode-bg-tasks/:agentId",k,async(e,t)=>{const d=e.params.agentId,o=Array.isArray(d)?d[0]:d;if(!o||!String(o).trim()){t.status(400).json({ok:!1,error:"agentId is required"});return}try{const s=await h();if(s&&typeof s.listTasksWithReconcile=="function"){const r=e.query.status&&String(e.query.status)!=="all"?String(e.query.status):void 0,n=await s.listTasksWithReconcile(String(o),r);t.json({ok:!0,agentId:String(o),updatedAt:Date.now(),tasks:n});return}}catch(s){l.warn("[acode-bg-tasks] \u5185\u5B58\u5BF9\u8D26\u5217\u8868\u5931\u8D25\uFF0C\u964D\u7EA7\u78C1\u76D8\u8BFB\u53D6: %s",s?.message)}const i=v(String(o));try{const s=await S.readFile(i,"utf-8"),r=JSON.parse(s),u=(Array.isArray(r.tasks)?r.tasks:[]).map(c=>{const a=c;return{taskId:a.taskId,status:a.status,command:a.commandPreview??a.command,commandPreview:a.commandPreview,description:typeof a.description=="string"?a.description:void 0,cwd:a.cwd,startedAt:a.startedAt,finishedAt:a.finishedAt,exitCode:a.exitCode,signal:a.signal,outputLineCount:a.outputLineCount,outputBytes:a.outputBytes}});t.json({ok:!0,agentId:String(o),updatedAt:typeof r.updatedAt=="number"?r.updatedAt:null,tasks:u})}catch(s){if((typeof s=="object"&&s!==null?Reflect.get(s,"code"):void 0)==="ENOENT"){t.json({ok:!0,agentId:String(o),updatedAt:null,tasks:[]});return}if(s instanceof SyntaxError){l.warn("[acode-bg-tasks] \u6587\u4EF6\u635F\u574F\uFF08JSON \u89E3\u6790\u5931\u8D25\uFF09: %s",i),t.json({ok:!0,agentId:String(o),updatedAt:null,tasks:[]});return}const n=s;l.error("[acode-bg-tasks] \u8BFB\u53D6\u5931\u8D25: %s",n.message),t.status(500).json({ok:!1,error:n.message})}}),p.get("/acode-agent-pty-sessions/:agentId",k,(e,t)=>{const d=e.params.agentId,o=Array.isArray(d)?d[0]:d;if(!o||!String(o).trim()){t.status(400).json({ok:!1,error:"agentId is required"});return}const i=String(o),r=_().filter(n=>n.ownerUserId===i);t.json({ok:!0,agentId:i,sessions:r})});const P=15e3;function w(e){return{taskId:e.taskId,status:e.status,exitCode:e.exitCode,signal:e.signal,startedAt:e.startedAt,finishedAt:e.finishedAt,command:e.command,lines:e.lines,nextSeq:e.nextSeq,totalLines:e.totalLines,totalBytes:e.totalBytes,truncated:e.truncated}}async function x(e,t){let o=null;try{o=await S.open(e,"r")}catch(i){if(i?.code==="ENOENT")return{text:[],fileExists:!1,truncated:!1};throw i}try{const s=(await o.stat()).size;if(s===0)return{text:[],fileExists:!0,truncated:!1};const r=Math.min(s,2097152),n=s-r,u=Buffer.alloc(r);await o.read(u,0,r,n);const a=u.toString("utf-8").split(`
|
|
2
|
+
`);return n>0&&a.shift(),{text:a.slice(-t),fileExists:!0,truncated:r<s}}finally{await o.close()}}p.get("/acode-bg-tasks/:agentId/:taskId/output",k,(e,t)=>{(async()=>{const d=String(e.params.agentId),o=String(e.params.taskId),i=Math.min(2e3,Math.max(100,Number(e.query.tailLines)||500));try{const s=await h();if(s){const r=s.getTaskOutputSnapshot(d,o);if(r){t.json({ok:!0,source:"memory",...w(r)});return}}}catch(s){l.warn("[acode-bg-tasks] \u5185\u5B58\u5FEB\u7167\u8BFB\u53D6\u5931\u8D25,\u964D\u7EA7\u8D70\u78C1\u76D8: %s",s?.message)}try{const s=await I();if(!s){t.status(503).json({ok:!1,error:"store unavailable"});return}const r=await s.readSession(d);if(!r){t.status(404).json({ok:!1,error:"task_not_found"});return}const n=r.tasks.find(f=>f.taskId===o);if(!n){t.status(404).json({ok:!1,error:"task_not_found"});return}const{text:u,fileExists:c,truncated:a}=await x(n.outputPath,i);t.json({ok:!0,source:"file",taskId:n.taskId,status:n.status,exitCode:n.exitCode,signal:n.signal,startedAt:n.startedAt,finishedAt:n.finishedAt,command:n.command,lines:u,nextSeq:0,totalLines:n.outputLineCount,totalBytes:n.outputBytes,truncated:a||!c})}catch(s){l.error("[acode-bg-tasks] \u78C1\u76D8\u515C\u5E95\u8BFB\u53D6\u5931\u8D25: %s",s?.message),t.status(500).json({ok:!1,error:"internal_error"})}})().catch(d=>{l.error("[acode-bg-tasks] output \u8BFB\u53D6\u5931\u8D25: %s",d?.message),t.status(500).json({ok:!1,error:"internal_error"})})}),p.get("/acode-bg-tasks/:agentId/:taskId/stream",k,(e,t)=>{(async()=>{const d=String(e.params.agentId),o=String(e.params.taskId);t.status(200),t.set({"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive","X-Accel-Buffering":"no"}),t.flushHeaders?.();const i=(u,c)=>{t.writableEnded||t.destroyed||(c!==void 0&&t.write(`id: ${c}
|
|
3
3
|
`),t.write(`data: ${JSON.stringify(u)}
|
|
4
4
|
|
|
5
|
-
`))};let s,
|
|
5
|
+
`))};let s,r=null,n=null;try{s=await h(),s&&(r=s,n=s.getTaskOutputSnapshot(d,o))}catch(u){l.warn("[acode-bg-tasks] \u5185\u5B58\u8BFB\u53D6\u5931\u8D25,\u964D\u7EA7\u8D70\u78C1\u76D8: %s",u?.message)}if(n){if(!r){i({type:"done",status:"killed",exitCode:null,signal:null}),t.end();return}if(i({type:"snapshot",source:"memory",...w(n)}),n.status!=="running"){i({type:"done",status:n.status,exitCode:n.exitCode,signal:n.signal}),t.end();return}let u=null,c=!1;const a=()=>{c||(c=!0,u?.(),clearInterval(f))},f=setInterval(()=>{if(t.writableEnded||t.destroyed){a();return}try{t.write(`: ping
|
|
6
6
|
|
|
7
|
-
`)}catch{
|
|
7
|
+
`)}catch{a()}},P);if(f.unref?.(),u=r.subscribeTaskOutput(d,o,{onLine:g=>i({type:"output",stream:g.stream,text:g.text,seq:g.seq},g.seq),onDone:g=>{i({type:"done",status:g.status,exitCode:g.exitCode,signal:g.signal}),a(),t.end()}}),!u){i({type:"done",status:n.status,exitCode:n.exitCode,signal:n.signal}),a(),t.end();return}e.on("close",()=>{a(),t.writableEnded||t.end()}),e.on("aborted",()=>a());return}try{const u=await I();if(!u){i({type:"done",status:"killed",exitCode:null,signal:null}),t.end();return}const c=await u.readSession(d);if(!c){i({type:"done",status:"killed",exitCode:null,signal:null}),t.end();return}const a=c.tasks.find(b=>b.taskId===o);if(!a){i({type:"done",status:"killed",exitCode:null,signal:null}),t.end();return}const{text:f,fileExists:g,truncated:A}=await x(a.outputPath,500);i({type:"snapshot",source:"file",taskId:a.taskId,status:a.status,exitCode:a.exitCode,signal:a.signal,startedAt:a.startedAt,finishedAt:a.finishedAt,command:a.command,lines:f,nextSeq:0,totalLines:a.outputLineCount,totalBytes:a.outputBytes,truncated:A||!g}),i({type:"done",status:a.status,exitCode:a.exitCode,signal:a.signal}),t.end()}catch(u){l.error("[acode-bg-tasks] \u78C1\u76D8\u515C\u5E95 stream \u5931\u8D25: %s",u?.message),t.headersSent?t.end():t.status(500).json({ok:!1,error:"internal_error"})}})().catch(d=>{l.error("[acode-bg-tasks] stream \u5EFA\u7ACB\u5931\u8D25: %s",d?.message),t.headersSent?t.end():t.status(500).json({ok:!1,error:"internal_error"})})});export{p as backgroundTasksRouter};
|
|
8
8
|
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import m from"node:os";import n from"node:path";import{promises as i}from"node:fs";import{randomUUID as p}from"node:crypto";import{Router as l}from"express";import{validateToken as u}from"../middleware/auth.js";import{createLogger as d}from"../utils/logger.js";const s=d("ltr"),w=l(),c=n.join(m.tmpdir(),"aw-temp","LTR"),y=7,f=512*1024;function g(o=new Date){const e=o.getFullYear(),t=String(o.getMonth()+1).padStart(2,"0"),r=String(o.getDate()).padStart(2,"0");return n.join(c,`${e}${t}${r}`)}async function h(){const o=Date.now()-y*24*60*60*1e3;try{const e=await i.readdir(c,{withFileTypes:!0});for(const t of e){if(!t.isDirectory())continue;const r=n.join(c,t.name);try{(await i.stat(r)).mtimeMs<o&&(await i.rm(r,{recursive:!0,force:!0}),s.info(`[ltr] removed expired temp dir: ${r}`))}catch{}}}catch(e){const t=e;t.code!=="ENOENT"&&s.warn("[ltr] cleanup expired dirs failed:",t)}}async function T(o){const e=String(o??"");if(!e.trim())throw new Error("ltr content is empty");const t=Buffer.byteLength(e,"utf8");if(t>f)throw new Error(`ltr content too large: ${t} bytes, max ${f} bytes`);const r=g();await i.mkdir(r,{recursive:!0});const a=n.join(r,`${p()}.txt`);return await i.writeFile(a,e,"utf8"),h(),a}w.post("/upload",u,async(o,e)=>{try{const{content:t}=o.body||{},r=await T(String(t??""));e.json({path:r})}catch(t){const r=t;s.error("[ltr] failed to write temp text file:",r),e.status(400).json({error:r.message})}});export{c as LTR_ROOT,w as ltrRouter};
|
|
2
|
+
|
|
@@ -28,9 +28,26 @@ export interface BgTaskOutputSnapshot {
|
|
|
28
28
|
truncated: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export interface BgTaskSummary {
|
|
32
|
+
taskId: string;
|
|
33
|
+
status: string;
|
|
34
|
+
command: string;
|
|
35
|
+
commandPreview: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
cwd: string;
|
|
38
|
+
startedAt: number;
|
|
39
|
+
finishedAt: number | null;
|
|
40
|
+
exitCode: number | null;
|
|
41
|
+
signal: string | null;
|
|
42
|
+
outputLineCount: number;
|
|
43
|
+
outputBytes: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
31
46
|
export interface BgManagerLike {
|
|
32
47
|
getTaskOutputSnapshot(agentId: string, taskId: string, tailLines?: number): BgTaskOutputSnapshot | null;
|
|
33
48
|
subscribeTaskOutput(agentId: string, taskId: string, subscriber: BgTaskOutputSubscriber): (() => void) | null;
|
|
49
|
+
|
|
50
|
+
listTasksWithReconcile?(agentId: string, statusFilter?: string): Promise<BgTaskSummary[]>;
|
|
34
51
|
}
|
|
35
52
|
|
|
36
53
|
export declare function getAcodeBgManager(): Promise<BgManagerLike | null>;
|
|
@@ -176,6 +176,8 @@ export declare class BackgroundCommandManager {
|
|
|
176
176
|
private spawnAndAttach;
|
|
177
177
|
|
|
178
178
|
disableInactivityTimeout(agentId: string, taskId: string): boolean;
|
|
179
|
+
|
|
180
|
+
markBackgrounded(agentId: string, taskId: string): Promise<boolean>;
|
|
179
181
|
private onOutput;
|
|
180
182
|
|
|
181
183
|
private scheduleInactivity;
|
|
@@ -209,6 +211,12 @@ export declare class BackgroundCommandManager {
|
|
|
209
211
|
|
|
210
212
|
disposeAll(): Promise<void>;
|
|
211
213
|
|
|
214
|
+
reconcileDiskRunning(agentId: string): Promise<number>;
|
|
215
|
+
|
|
216
|
+
listTasksWithReconcile(agentId: string, statusFilter?: string): Promise<TaskSummary[]>;
|
|
217
|
+
|
|
218
|
+
reconcileAllAgents(): Promise<number>;
|
|
219
|
+
|
|
212
220
|
reconcileOrphans(agentId: string): Promise<void>;
|
|
213
221
|
|
|
214
222
|
readSessionState(agentId: string): Promise<import("./background-command-store.js").BackgroundSessionState | null>;
|
|
@@ -218,6 +226,8 @@ export declare class BackgroundCommandManager {
|
|
|
218
226
|
private getTask;
|
|
219
227
|
private generateTaskId;
|
|
220
228
|
private persistTask;
|
|
229
|
+
|
|
230
|
+
private persistTaskSafe;
|
|
221
231
|
}
|
|
222
232
|
|
|
223
233
|
export declare class BackgroundTaskError extends Error {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{spawn as E}from"node:child_process";import{promises as
|
|
2
|
-
`),this.finalizeTask(
|
|
3
|
-
`),t.finalized||this.finalizeTask(t,-1,null,"child_error")}),s.on("
|
|
1
|
+
import{spawn as E}from"node:child_process";import{promises as S}from"node:fs";import{createInterface as R}from"node:readline";import{randomBytes as M}from"node:crypto";import{prepareShellCommand as O,shellExecutable as D}from"./builtins/shellCommandPreparation.js";import{createShellOutputDecoder as b,decodeShellOutputChunk as A}from"./builtins/shellOutputDecoder.js";import{BackgroundOutputRingBuffer as G}from"./background-output-ringbuffer.js";import{BackgroundCommandStore as z}from"./background-command-store.js";const F=1e3,N=30*60*1e3,W=24*60*60*1e3,$=5*1e3,k=64*1024,X=2e3,C=50*1024*1024,_=16,P=256,q=256*1024,K=100,U=5e3,Y=256,v=20,B=5*60*1e3,L=1024*1024,x=`pid-${process.pid}-${Date.now().toString(36)}`;class H{constructor(){this.tasksByAgent=new Map,this.store=new z}globalCount(){let t=0;for(const i of this.tasksByAgent.values())t+=i.size;return t}startCommand(t){const i=t.agentId,e=this.getOrCreateBucket(i);if(e.size>=_)throw new I("too_many_tasks",`Agent ${i} has ${e.size} background tasks (limit ${_}). Kill or wait for completion first.`);if(this.globalCount()>=P)throw new I("global_limit_reached",`Global background task limit ${P} reached.`);const n=this.generateTaskId(e),r=t.timeoutMs===void 0?N:t.timeoutMs>0?Math.min(t.timeoutMs,W):0,s=typeof t.inactivityTimeoutMs=="number"&&Number.isFinite(t.inactivityTimeoutMs)&&t.inactivityTimeoutMs>0?t.inactivityTimeoutMs:0,o=this.store.outputPathFor(i,n),a=t.commandText.length>80?`${t.commandText.slice(0,77)}...`:t.commandText,u={taskId:n,agentId:i,command:t.commandText,commandPreview:a,description:t.description,cwd:t.cwd,status:"running",startedAt:Date.now(),finishedAt:null,exitCode:null,signal:null,killedReason:null,timeoutMs:r,inactivityTimeoutMs:s,child:null,ringBuffer:new G,outputPath:o,outputWriteChain:Promise.resolve(),outputTruncated:!1,outputLineCount:0,outputBytes:0,notified:!1,completionWaiters:[],onCompleted:t.onCompleted,onOutput:t.onOutput,outputSeq:0,outputSubscribers:new Set,timeoutTimer:null,killEscalationTimer:null,retentionTimer:null,inactivityTimer:null,exitGraceTimer:null,inactivityTimedOut:!1,finalized:!1,stdoutDecoder:b(),stderrDecoder:b(),stdoutText:"",stderrText:"",commandLineCount:0,bootId:x,lastPulseAt:Date.now(),lastPulseBytes:0,backgrounded:!1},l=new Promise(c=>{u.completionResolve=c});return e.set(n,u),r>0&&(u.timeoutTimer=setTimeout(()=>{u.finalized||(u.killedReason="timeout",u.child?this.killProcessGroup(u.child):this.finalizeTask(u,null,null,"timeout"))},r),u.timeoutTimer.unref?.()),this.spawnAndAttach(u,t).catch(c=>{u.ringBuffer.push(`[ACode] spawn failed: ${c?.message??String(c)}
|
|
2
|
+
`),this.finalizeTask(u,-1,null,"spawn_error")}),this.persistTaskSafe(u),{taskId:n,completion:l}}async spawnAndAttach(t,i){const e=D(),n=await O(i.commandText,i.workspaceRoot,e),r=process.platform==="win32",s=E(e.command,[...e.argsPrefix,n.commandText],{cwd:i.cwd,env:process.env,detached:!r,stdio:["ignore","pipe","pipe"],windowsVerbatimArguments:r,windowsHide:!0});t.child=s,t.commandFilePath=n.commandFilePath,t.commandLineCount=n.commandLineCount,t.inactivityTimeoutMs>0&&this.scheduleInactivity(t),s.stdout?.on("data",o=>{const a=A(t.stdoutDecoder,o);this.onOutput(t,"stdout",a)}),s.stderr?.on("data",o=>{const a=A(t.stderrDecoder,o);this.onOutput(t,"stderr",a)}),s.on("error",o=>{this.onOutput(t,"stderr",`[ACode] child error: ${o.message}
|
|
3
|
+
`),t.finalized||this.finalizeTask(t,-1,null,"child_error")}),s.on("exit",(o,a)=>{if(t.finalized)return;const u=()=>{if(t.finalized||!t.child)return;const l=A(t.stdoutDecoder,void 0,!0),c=A(t.stderrDecoder,void 0,!0);l&&this.onOutput(t,"stdout",l),c&&this.onOutput(t,"stderr",c),t.ringBuffer.flush();const p=t.killedReason??null;this.finalizeTask(t,o,a,p??void 0)};t.exitGraceTimer=setTimeout(u,X),t.exitGraceTimer.unref?.()}),s.on("close",(o,a)=>{t.exitGraceTimer&&(clearTimeout(t.exitGraceTimer),t.exitGraceTimer=null);const u=A(t.stdoutDecoder,void 0,!0),l=A(t.stderrDecoder,void 0,!0);if(u&&this.onOutput(t,"stdout",u),l&&this.onOutput(t,"stderr",l),t.ringBuffer.flush(),!t.finalized){const c=t.killedReason??null;this.finalizeTask(t,o,a,c??void 0)}})}disableInactivityTimeout(t,i){const e=this.getTask(t,i);return e?(e.inactivityTimeoutMs=0,e.inactivityTimer&&(clearTimeout(e.inactivityTimer),e.inactivityTimer=null),!0):!1}async markBackgrounded(t,i){const e=this.getTask(t,i);return e?(e.backgrounded=!0,await this.persistTask(e),!0):!1}onOutput(t,i,e){if(!e)return;t.outputSeq++;const n=t.outputSeq;if(t.outputSubscribers.size>0)for(const o of t.outputSubscribers)try{o.onLine({stream:i,text:e,seq:n})}catch{}const r=i==="stdout"?"stdoutText":"stderrText";if(Buffer.byteLength(t[r],"utf-8")<L){const o=L-Buffer.byteLength(t[r],"utf-8");t[r]+=o>0?e.slice(0,o):""}if(t.inactivityTimeoutMs>0&&this.scheduleInactivity(t),t.status==="running"){const o=Date.now(),a=t.outputBytes-t.lastPulseBytes;(o-t.lastPulseAt>=$||a>=k)&&(t.lastPulseAt=o,t.lastPulseBytes=t.outputBytes,this.persistTaskSafe(t))}if(t.outputBytes>=C){if(!t.outputTruncated){t.outputTruncated=!0;const o=`
|
|
4
4
|
[ACode] output truncated at 50MB
|
|
5
|
-
`;t.ringBuffer.push(
|
|
6
|
-
`)}),t.outputWriteChain)}finalizeTask(t,i,e,n){if(t.finalized)return;t.finalized=!0,t.timeoutTimer&&(clearTimeout(t.timeoutTimer),t.timeoutTimer=null),t.killEscalationTimer&&(clearTimeout(t.killEscalationTimer),t.killEscalationTimer=null),t.inactivityTimer&&(clearTimeout(t.inactivityTimer),t.inactivityTimer=null),t.child=null,t.finishedAt=Date.now();let o;n?(o="killed",t.killedReason=n):i===0?o="completed":o="failed",t.status=o,t.exitCode=i,t.signal=e;const s=n==="inactivity"||t.inactivityTimedOut,r={taskId:t.taskId,exitCode:i,signal:e,timedOut:n==="timeout"||s,inactivityTimedOut:s,outputFilePath:t.outputPath,outputLineCount:t.outputLineCount,outputSpilled:t.outputTruncated,commandFilePath:t.commandFilePath,commandLineCount:t.commandLineCount,timeoutMs:t.timeoutMs,inactivityTimeoutMs:t.inactivityTimeoutMs,stdout:t.stdoutText,stderr:t.stderrText,durationMs:t.finishedAt-t.startedAt};t.completionResolve?.(r);const u={taskId:t.taskId,status:o,exitCode:i,signal:e,timedOut:n==="timeout"||s,aborted:!1,durationMs:t.finishedAt-t.startedAt},a=t.completionWaiters;t.completionWaiters=[];for(const c of a)c(u);if(this.persistTask(t),t.onCompleted?.({taskId:t.taskId,agentId:t.agentId,status:o,exitCode:i,signal:e,commandPreview:t.commandPreview,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,outputPath:t.outputPath,finishedAt:t.finishedAt,killedReason:t.killedReason}),t.outputSubscribers.size>0){const c=Array.from(t.outputSubscribers);t.outputSubscribers.clear();for(const d of c)try{d.onDone({status:o,exitCode:i,signal:e})}catch{}}t.retentionTimer=setTimeout(()=>{this.evictTask(t.agentId,t.taskId)},v),t.retentionTimer.unref?.()}evictTask(t,i){const e=this.tasksByAgent.get(t);if(!e)return;const n=e.get(i);n&&(A.unlink(n.outputPath).catch(()=>{}),e.delete(i),e.size===0&&this.tasksByAgent.delete(t))}listTasks(t,i){const e=this.tasksByAgent.get(t);if(!e)return[];const n=[];for(const o of e.values())i&&i!=="all"&&o.status!==i||n.push(this.toSummary(o));return n.sort((o,s)=>s.startedAt-o.startedAt)}toSummary(t){return{taskId:t.taskId,status:t.status,command:t.command,commandPreview:t.commandPreview,description:t.description,cwd:t.cwd,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes}}getTaskOutputSnapshot(t,i,e=500){const n=this.getTask(t,i);if(!n)return null;const o=n.ringBuffer.tail(e);return{taskId:n.taskId,status:n.status,exitCode:n.exitCode,signal:n.signal,startedAt:n.startedAt,finishedAt:n.finishedAt,command:n.command,lines:o,nextSeq:n.outputSeq+1,totalLines:n.outputLineCount,totalBytes:n.outputBytes,truncated:n.outputTruncated}}subscribeTaskOutput(t,i,e){const n=this.getTask(t,i);if(!n)return null;n.outputSubscribers.add(e);let o=!0;return()=>{o&&(o=!1,n.outputSubscribers.delete(e))}}async readOutput(t,i){const e=String(i.task_id??""),n=this.getTask(t,e),o=i.mode==="lines"||i.mode==="grep"?i.mode:"tail",s=Math.min(Math.max(1,i.line_size??100),X),r=Number.isFinite(i.start_line)&&(i.start_line??0)>0?Math.floor(i.start_line??0):0;if(!n)return{taskId:e,status:"killed",mode:o,lines:[],lineCount:0,metadata:{totalLines:0,totalBytes:0,returnedBytes:0,truncated:!1,outputTruncated:!1,hasMore:!1,nextStartLine:null,outputFile:""}};let u=[],a=!1,c=null;if(o==="tail"){const m=n.ringBuffer.tail(s),f=Math.max(0,n.outputLineCount-m.length);u=m.map((l,g)=>({line:f+g,text:l})),c=null}else if(o==="lines"){const m=n.ringBuffer.firstLineIndex(),f=m+n.ringBuffer.getLineCount();if(r>=m&&r<f)u=n.ringBuffer.offset(r,s).map((g,h)=>({line:r+h,text:g})),a=r+u.length<f,c=a?r+u.length:null;else if(r<f){const l=await this.readFromFile(n,"lines",r,s,0,0,void 0);u=l.rows,a=l.hasMore,c=l.nextStartLine}}else{const m=String(i.pattern??"");if(!m||m.length>q)return this._emptyResult(e,n,o);let f;try{f=new RegExp(m,"i")}catch{return this._emptyResult(e,n,o)}const l=Math.min(Math.max(0,i.context_before??0),b),g=Math.min(Math.max(0,i.context_after??0),b),h=await this.readFromFile(n,"grep",r,s,l,g,f);u=h.rows,a=h.hasMore,c=h.nextStartLine}const d=[];let p=0,T=!1;for(const m of u){const f=Buffer.byteLength(m.text,"utf-8")+1;if(d.length>=s||p+f>W){T=!0,a=!0;break}d.push(m),p+=f}const y=i.include_metadata!==!1;return{taskId:e,status:n.status,mode:o,lines:d,lineCount:d.length,metadata:y?{totalLines:n.outputLineCount,totalBytes:n.outputBytes,returnedBytes:p,truncated:T,outputTruncated:n.outputTruncated,hasMore:a,nextStartLine:a?c:null,outputFile:n.outputPath}:void 0}}_emptyResult(t,i,e){return{taskId:t,status:i.status,mode:e,lines:[],lineCount:0,metadata:{totalLines:i.outputLineCount,totalBytes:i.outputBytes,returnedBytes:0,truncated:!1,outputTruncated:i.outputTruncated,hasMore:!1,nextStartLine:null,outputFile:i.outputPath}}}async readFromFile(t,i,e,n,o,s,r){const u=[],a=new Set;let c=0,d=!1;const p=[];let T=-1/0;try{const y=await A.open(t.outputPath,"r"),m=I({input:y.createReadStream({encoding:"utf-8"}),crlfDelay:1/0});for await(const f of m){const l=c;if(c++,i==="grep"&&l-e>=$&&l>=e){d=!0;break}if(l>=e){if(i==="lines"){if(u.length>=n){d=!0;break}a.has(l)||(a.add(l),u.push({line:l,text:f}))}else if(r.test(f)){for(const h of p)h.line>=e&&!a.has(h.line)&&(a.add(h.line),u.push(h));if(a.has(l)||(a.add(l),u.push({line:l,text:f})),T=l,u.length>=n){d=!0;break}}else if(o>0&&(p.push({line:l,text:f}),p.length>o&&p.shift()),T>=e&&l-T<=s&&!a.has(l)&&(a.add(l),u.push({line:l,text:f}),u.length>=n)){d=!0;break}}}m.close(),await y.close()}catch{return{rows:[],hasMore:!1,nextStartLine:null}}return d?{rows:u,hasMore:d,nextStartLine:c}:{rows:u,hasMore:d,nextStartLine:null}}async awaitCompletion(t,i,e){return Promise.all(i.map(n=>this.awaitOne(t,n,e)))}async awaitOne(t,i,e){const n=this.getTask(t,i);if(n)return n.status!=="running"?this.taskToAwaitResult(n):new Promise(r=>{let u=null;const a=c=>{u&&clearTimeout(u),r(c)};n.completionWaiters.push(a),e&&e>0&&(u=setTimeout(()=>{n.completionWaiters=n.completionWaiters.filter(c=>c!==a),r({taskId:i,status:"running",exitCode:null,signal:null,timedOut:!0,aborted:!1,durationMs:Date.now()-n.startedAt})},e),u.unref?.())});const s=(await this.store.readSession(t))?.tasks.find(r=>r.taskId===i);return s?s.status!=="running"?{taskId:i,status:s.status,exitCode:s.exitCode,signal:s.signal,timedOut:s.killedReason==="timeout",aborted:!1,durationMs:(s.finishedAt??Date.now())-s.startedAt,...s.outputPath?{outputFilePath:s.outputPath}:{}}:{taskId:i,status:"killed",exitCode:null,signal:null,timedOut:!1,aborted:!1,durationMs:(s.finishedAt??Date.now())-s.startedAt}:null}taskToAwaitResult(t){return{taskId:t.taskId,status:t.status,exitCode:t.exitCode,signal:t.signal,timedOut:t.killedReason==="timeout",aborted:!1,durationMs:(t.finishedAt??Date.now())-t.startedAt,...t.status!=="running"&&t.outputPath?{outputFilePath:t.outputPath}:{}}}interruptAwaits(t,i){const e=this.tasksByAgent.get(t);if(e)for(const n of i){const o=e.get(n);if(!o)continue;const s=o.completionWaiters;if(s.length===0)continue;o.completionWaiters=[];const r={taskId:n,status:"running",exitCode:null,signal:null,timedOut:!1,aborted:!0,durationMs:Date.now()-o.startedAt};for(const u of s)u(r)}}async killTask(t,i){const e=this.getTask(t,i);return e?e.status!=="running"?{taskId:i,status:e.status,exitCode:e.exitCode??void 0}:e.child?(e.killedReason="user_kill",this.killProcessGroup(e.child)?(e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.killEscalationTimer=setTimeout(()=>{e.child&&!e.finalized&&this.killProcessGroup(e.child,!0)},z),e.killEscalationTimer.unref?.(),{taskId:i,status:"killed",signal:"SIGTERM"}):{taskId:i,status:e.status,error:"kill_failed",reason:"permission_denied"}):{taskId:i,status:e.status,error:"kill_failed",reason:"no_child"}:{taskId:i,status:"killed",error:"task_not_found"}}killProcessGroup(t,i=!1){const e=t.pid;if(!e)return!1;try{if(process.platform==="win32"){const n="/F",{spawnSync:o}=require("node:child_process");return o("taskkill",["/PID",String(e),"/T",n],{windowsHide:!0}),!0}try{process.kill(-e,i?"SIGKILL":"SIGTERM")}catch{process.kill(e,i?"SIGKILL":"SIGTERM")}return!0}catch{return!1}}async disposeAgent(t){const i=this.tasksByAgent.get(t);if(i){for(const e of i.values())e.status==="running"&&e.child&&(e.killedReason="session_disposed",this.killProcessGroup(e.child,!0)),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer);this.tasksByAgent.delete(t)}await this.store.deleteAgent(t)}async disposeAll(){for(const[t,i]of this.tasksByAgent){for(const e of i.values())e.status==="running"&&e.child&&(e.killedReason="process_exit",this.killProcessGroup(e.child,!0)),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer);this.tasksByAgent.delete(t)}}async reconcileOrphans(t){const i=await this.store.readSession(t);if(!i||i.tasks.length===0)return;let e=!1;for(const n of i.tasks)n.status==="running"&&(n.status="killed",n.killedReason="runtime_crashed",n.finishedAt=Date.now(),n.notified=!0,e=!0);e&&await this.store.persistSession(t,i.tasks)}async readSessionState(t){return this.store.readSession(t)}async markNotified(t,i){await this.store.markNotified(t,i)}getOrCreateBucket(t){let i=this.tasksByAgent.get(t);return i||(i=new Map,this.tasksByAgent.set(t,i)),i}getTask(t,i){const e=this.tasksByAgent.get(t);if(e&&/^[A-Za-z0-9]{8}$/.test(i))return e.get(i)}generateTaskId(t){for(let i=0;i<3;i++){const e=S(4).toString("hex").slice(0,8);if(!t.has(e))return e}return S(6).toString("hex").slice(0,8)}async persistTask(t){const i=await this.store.readSession(t.agentId)??{agentId:t.agentId,updatedAt:0,tasks:[]},e={taskId:t.taskId,agentId:t.agentId,command:t.command,commandPreview:t.commandPreview,description:t.description,cwd:t.cwd,status:t.status,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputPath:t.outputPath,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,notified:t.notified,killedReason:t.killedReason},n=i.tasks.findIndex(s=>s.taskId===t.taskId);n>=0?i.tasks[n]=e:i.tasks.push(e);const o=Date.now();i.tasks=i.tasks.filter(s=>s.status==="running"||!s.finishedAt||o-s.finishedAt<v),await this.store.persistSession(t.agentId,i.tasks)}}class x extends Error{constructor(t,i){super(i),this.name="BackgroundTaskError",this.code=t}}const Q=new K;export{K as BackgroundCommandManager,x as BackgroundTaskError,Q as backgroundCommandManager};
|
|
5
|
+
`;t.ringBuffer.push(o),this.appendToFile(t,o)}t.ringBuffer.push(e);return}t.ringBuffer.push(e),t.onOutput?.({stream:i,text:e});const s=(e.match(/\n/g)??[]).length;t.outputLineCount+=s,t.outputBytes+=Buffer.byteLength(e,"utf-8"),this.appendToFile(t,e)}scheduleInactivity(t){t.finalized||t.inactivityTimeoutMs<=0||(t.inactivityTimer&&clearTimeout(t.inactivityTimer),t.inactivityTimer=setTimeout(()=>{t.finalized||!t.child||(t.inactivityTimedOut=!0,t.killedReason="inactivity",this.killProcessGroup(t.child))},t.inactivityTimeoutMs),t.inactivityTimer.unref?.())}appendToFile(t,i){return t.outputBytes>C?Promise.resolve():(t.outputWriteChain=t.outputWriteChain.then(()=>S.appendFile(t.outputPath,i,"utf-8")).catch(e=>{t.ringBuffer.push(`[ACode] output write failed: ${e?.message??e}
|
|
6
|
+
`)}),t.outputWriteChain)}finalizeTask(t,i,e,n){if(t.finalized)return;t.finalized=!0,t.timeoutTimer&&(clearTimeout(t.timeoutTimer),t.timeoutTimer=null),t.killEscalationTimer&&(clearTimeout(t.killEscalationTimer),t.killEscalationTimer=null),t.inactivityTimer&&(clearTimeout(t.inactivityTimer),t.inactivityTimer=null),t.exitGraceTimer&&(clearTimeout(t.exitGraceTimer),t.exitGraceTimer=null),t.child=null,t.finishedAt=Date.now();let r;n?(r="killed",t.killedReason=n):i===0?r="completed":r="failed",t.status=r,t.exitCode=i,t.signal=e;const s=n==="inactivity"||t.inactivityTimedOut,o={taskId:t.taskId,exitCode:i,signal:e,timedOut:n==="timeout"||s,inactivityTimedOut:s,outputFilePath:t.outputPath,outputLineCount:t.outputLineCount,outputSpilled:t.outputTruncated,commandFilePath:t.commandFilePath,commandLineCount:t.commandLineCount,timeoutMs:t.timeoutMs,inactivityTimeoutMs:t.inactivityTimeoutMs,stdout:t.stdoutText,stderr:t.stderrText,durationMs:t.finishedAt-t.startedAt};t.completionResolve?.(o);const a={taskId:t.taskId,status:r,exitCode:i,signal:e,timedOut:n==="timeout"||s,aborted:!1,durationMs:t.finishedAt-t.startedAt},u=t.completionWaiters;t.completionWaiters=[];for(const l of u)l(a);if(this.persistTaskSafe(t),t.onCompleted?.({taskId:t.taskId,agentId:t.agentId,status:r,exitCode:i,signal:e,commandPreview:t.commandPreview,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,outputPath:t.outputPath,finishedAt:t.finishedAt,killedReason:t.killedReason}),t.outputSubscribers.size>0){const l=Array.from(t.outputSubscribers);t.outputSubscribers.clear();for(const c of l)try{c.onDone({status:r,exitCode:i,signal:e})}catch{}}t.retentionTimer=setTimeout(()=>{this.evictTask(t.agentId,t.taskId)},B),t.retentionTimer.unref?.()}evictTask(t,i){const e=this.tasksByAgent.get(t);if(!e)return;const n=e.get(i);n&&(S.unlink(n.outputPath).catch(()=>{}),e.delete(i),e.size===0&&this.tasksByAgent.delete(t))}listTasks(t,i){const e=this.tasksByAgent.get(t);if(!e)return[];const n=[];for(const r of e.values())i&&i!=="all"&&r.status!==i||n.push(this.toSummary(r));return n.sort((r,s)=>s.startedAt-r.startedAt)}toSummary(t){return{taskId:t.taskId,status:t.status,command:t.command,commandPreview:t.commandPreview,description:t.description,cwd:t.cwd,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes}}getTaskOutputSnapshot(t,i,e=500){const n=this.getTask(t,i);if(!n)return null;const r=n.ringBuffer.tail(e);return{taskId:n.taskId,status:n.status,exitCode:n.exitCode,signal:n.signal,startedAt:n.startedAt,finishedAt:n.finishedAt,command:n.command,lines:r,nextSeq:n.outputSeq+1,totalLines:n.outputLineCount,totalBytes:n.outputBytes,truncated:n.outputTruncated}}subscribeTaskOutput(t,i,e){const n=this.getTask(t,i);if(!n)return null;n.outputSubscribers.add(e);let r=!0;return()=>{r&&(r=!1,n.outputSubscribers.delete(e))}}async readOutput(t,i){const e=String(i.task_id??""),n=this.getTask(t,e),r=i.mode==="lines"||i.mode==="grep"?i.mode:"tail",s=Math.min(Math.max(1,i.line_size??100),K),o=Number.isFinite(i.start_line)&&(i.start_line??0)>0?Math.floor(i.start_line??0):0;if(!n)return{taskId:e,status:"killed",mode:r,lines:[],lineCount:0,metadata:{totalLines:0,totalBytes:0,returnedBytes:0,truncated:!1,outputTruncated:!1,hasMore:!1,nextStartLine:null,outputFile:""}};let a=[],u=!1,l=null;if(r==="tail"){const m=n.ringBuffer.tail(s),f=Math.max(0,n.outputLineCount-m.length);a=m.map((d,y)=>({line:f+y,text:d})),l=null}else if(r==="lines"){const m=n.ringBuffer.firstLineIndex(),f=m+n.ringBuffer.getLineCount();if(o>=m&&o<f)a=n.ringBuffer.offset(o,s).map((y,T)=>({line:o+T,text:y})),u=o+a.length<f,l=u?o+a.length:null;else if(o<f){const d=await this.readFromFile(n,"lines",o,s,0,0,void 0);a=d.rows,u=d.hasMore,l=d.nextStartLine}}else{const m=String(i.pattern??"");if(!m||m.length>Y)return this._emptyResult(e,n,r);let f;try{f=new RegExp(m,"i")}catch{return this._emptyResult(e,n,r)}const d=Math.min(Math.max(0,i.context_before??0),v),y=Math.min(Math.max(0,i.context_after??0),v),T=await this.readFromFile(n,"grep",o,s,d,y,f);a=T.rows,u=T.hasMore,l=T.nextStartLine}const c=[];let p=0,g=!1;for(const m of a){const f=Buffer.byteLength(m.text,"utf-8")+1;if(c.length>=s||p+f>q){g=!0,u=!0;break}c.push(m),p+=f}const w=i.include_metadata!==!1;return{taskId:e,status:n.status,mode:r,lines:c,lineCount:c.length,metadata:w?{totalLines:n.outputLineCount,totalBytes:n.outputBytes,returnedBytes:p,truncated:g,outputTruncated:n.outputTruncated,hasMore:u,nextStartLine:u?l:null,outputFile:n.outputPath}:void 0}}_emptyResult(t,i,e){return{taskId:t,status:i.status,mode:e,lines:[],lineCount:0,metadata:{totalLines:i.outputLineCount,totalBytes:i.outputBytes,returnedBytes:0,truncated:!1,outputTruncated:i.outputTruncated,hasMore:!1,nextStartLine:null,outputFile:i.outputPath}}}async readFromFile(t,i,e,n,r,s,o){const a=[],u=new Set;let l=0,c=!1;const p=[];let g=-1/0;try{const w=await S.open(t.outputPath,"r"),m=R({input:w.createReadStream({encoding:"utf-8"}),crlfDelay:1/0});for await(const f of m){const d=l;if(l++,i==="grep"&&d-e>=U&&d>=e){c=!0;break}if(d>=e){if(i==="lines"){if(a.length>=n){c=!0;break}u.has(d)||(u.add(d),a.push({line:d,text:f}))}else if(o.test(f)){for(const T of p)T.line>=e&&!u.has(T.line)&&(u.add(T.line),a.push(T));if(u.has(d)||(u.add(d),a.push({line:d,text:f})),g=d,a.length>=n){c=!0;break}}else if(r>0&&(p.push({line:d,text:f}),p.length>r&&p.shift()),g>=e&&d-g<=s&&!u.has(d)&&(u.add(d),a.push({line:d,text:f}),a.length>=n)){c=!0;break}}}m.close(),await w.close()}catch{return{rows:[],hasMore:!1,nextStartLine:null}}return c?{rows:a,hasMore:c,nextStartLine:l}:{rows:a,hasMore:c,nextStartLine:null}}async awaitCompletion(t,i,e){return Promise.all(i.map(n=>this.awaitOne(t,n,e)))}async awaitOne(t,i,e){const n=this.getTask(t,i);if(n)return n.status!=="running"?this.taskToAwaitResult(n):new Promise(o=>{let a=null;const u=l=>{a&&clearTimeout(a),o(l)};n.completionWaiters.push(u),e&&e>0&&(a=setTimeout(()=>{n.completionWaiters=n.completionWaiters.filter(l=>l!==u),o({taskId:i,status:"running",exitCode:null,signal:null,timedOut:!0,aborted:!1,durationMs:Date.now()-n.startedAt})},e),a.unref?.())});const s=(await this.store.readSession(t))?.tasks.find(o=>o.taskId===i);return s?s.status!=="running"?{taskId:i,status:s.status,exitCode:s.exitCode,signal:s.signal,timedOut:s.killedReason==="timeout",aborted:!1,durationMs:(s.finishedAt??Date.now())-s.startedAt,...s.outputPath?{outputFilePath:s.outputPath}:{}}:{taskId:i,status:"killed",exitCode:null,signal:null,timedOut:!1,aborted:!1,durationMs:(s.finishedAt??Date.now())-s.startedAt}:null}taskToAwaitResult(t){return{taskId:t.taskId,status:t.status,exitCode:t.exitCode,signal:t.signal,timedOut:t.killedReason==="timeout",aborted:!1,durationMs:(t.finishedAt??Date.now())-t.startedAt,...t.status!=="running"&&t.outputPath?{outputFilePath:t.outputPath}:{}}}interruptAwaits(t,i){const e=this.tasksByAgent.get(t);if(e)for(const n of i){const r=e.get(n);if(!r)continue;const s=r.completionWaiters;if(s.length===0)continue;r.completionWaiters=[];const o={taskId:n,status:"running",exitCode:null,signal:null,timedOut:!1,aborted:!0,durationMs:Date.now()-r.startedAt};for(const a of s)a(o)}}async killTask(t,i){const e=this.getTask(t,i);return e?e.status!=="running"?{taskId:i,status:e.status,exitCode:e.exitCode??void 0}:e.child?(e.killedReason="user_kill",this.killProcessGroup(e.child)?(e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.killEscalationTimer=setTimeout(()=>{e.child&&!e.finalized&&this.killProcessGroup(e.child,!0)},F),e.killEscalationTimer.unref?.(),{taskId:i,status:"killed",signal:"SIGTERM"}):{taskId:i,status:e.status,error:"kill_failed",reason:"permission_denied"}):{taskId:i,status:e.status,error:"kill_failed",reason:"no_child"}:{taskId:i,status:"killed",error:"task_not_found"}}killProcessGroup(t,i=!1){const e=t.pid;if(!e)return!1;try{if(process.platform==="win32"){const n="/F",{spawnSync:r}=require("node:child_process");return r("taskkill",["/PID",String(e),"/T",n],{windowsHide:!0}),!0}try{process.kill(-e,i?"SIGKILL":"SIGTERM")}catch{process.kill(e,i?"SIGKILL":"SIGTERM")}return!0}catch{return!1}}async disposeAgent(t){const i=this.tasksByAgent.get(t);if(i){for(const e of i.values())e.status==="running"&&e.child&&(e.killedReason="session_disposed",this.killProcessGroup(e.child,!0)),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer),e.exitGraceTimer&&clearTimeout(e.exitGraceTimer);this.tasksByAgent.delete(t)}await this.store.deleteAgent(t)}async disposeAll(){for(const[t,i]of this.tasksByAgent){for(const e of i.values())e.status==="running"&&(e.killedReason="process_exit",e.child&&this.killProcessGroup(e.child,!0),e.finalized||this.finalizeTask(e,null,null,"process_exit")),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer),e.exitGraceTimer&&clearTimeout(e.exitGraceTimer);await this.store.updateSession(t,()=>null),this.tasksByAgent.delete(t)}}async reconcileDiskRunning(t){const i=this.tasksByAgent.get(t);let e=0;return await this.store.updateSession(t,n=>{let r=!1;for(const s of n.tasks){if(s.status!=="running")continue;const o=i?.get(s.taskId)?.status==="running",a=s.ownerBootId===x;if(o){a||(s.ownerBootId=x,r=!0);continue}s.status="killed",s.killedReason="runtime_crashed",s.finishedAt=Date.now(),s.notified=!0,e++,r=!0}return r?n.tasks:null}),e}async listTasksWithReconcile(t,i){try{await this.reconcileDiskRunning(t)}catch{}const e=this.tasksByAgent.get(t),n=e?[...e.values()]:[],r=n.filter(o=>o.backgrounded).map(o=>this.toSummary(o)),s=new Set(n.map(o=>o.taskId));try{const o=await this.store.readSession(t);if(o){const a=Date.now();for(const u of o.tasks)s.has(u.taskId)||u.backgrounded===!0&&u.status!=="running"&&(u.finishedAt&&a-u.finishedAt>=B||i&&i!=="all"&&u.status!==i||r.push(V(u)))}}catch{}return i&&i!=="all"?r.filter(o=>o.status===i).sort((o,a)=>a.startedAt-o.startedAt):r.sort((o,a)=>a.startedAt-o.startedAt)}async reconcileAllAgents(){const t=await this.store.listAgents();let i=0;for(const e of t)try{i+=await this.reconcileDiskRunning(e)}catch{}return i}async reconcileOrphans(t){await this.reconcileDiskRunning(t)}async readSessionState(t){return this.store.readSession(t)}async markNotified(t,i){await this.store.markNotified(t,i)}getOrCreateBucket(t){let i=this.tasksByAgent.get(t);return i||(i=new Map,this.tasksByAgent.set(t,i)),i}getTask(t,i){const e=this.tasksByAgent.get(t);if(e&&/^[A-Za-z0-9]{8}$/.test(i))return e.get(i)}generateTaskId(t){for(let i=0;i<3;i++){const e=M(4).toString("hex").slice(0,8);if(!t.has(e))return e}return M(6).toString("hex").slice(0,8)}async persistTask(t){await this.store.updateSession(t.agentId,i=>{const e={taskId:t.taskId,agentId:t.agentId,command:t.command,commandPreview:t.commandPreview,description:t.description,cwd:t.cwd,status:t.status,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputPath:t.outputPath,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,notified:t.notified,killedReason:t.killedReason,ownerBootId:t.bootId,lastPulseAt:t.status==="running"?t.lastPulseAt:void 0,backgrounded:t.backgrounded},n=i.tasks.findIndex(s=>s.taskId===t.taskId);n>=0?i.tasks[n]=e:i.tasks.push(e);const r=Date.now();return i.tasks.filter(s=>s.status==="running"||!s.finishedAt||r-s.finishedAt<B)})}persistTaskSafe(t){this.persistTask(t).catch(i=>{console.error(`[BackgroundCommandManager] persist ${t.agentId}/${t.taskId} failed:`,i)})}}class I extends Error{constructor(t,i){super(i),this.name="BackgroundTaskError",this.code=t}}function V(h){return{taskId:h.taskId,status:h.status,command:h.command,commandPreview:h.commandPreview,description:h.description,cwd:h.cwd,startedAt:h.startedAt,finishedAt:h.finishedAt,exitCode:h.exitCode,signal:h.signal,outputLineCount:h.outputLineCount,outputBytes:h.outputBytes}}const ot=new H;export{H as BackgroundCommandManager,I as BackgroundTaskError,ot as backgroundCommandManager};
|
|
7
7
|
|
|
@@ -21,6 +21,12 @@ export interface BackgroundTaskRecord {
|
|
|
21
21
|
notified: boolean;
|
|
22
22
|
|
|
23
23
|
killedReason?: string | null;
|
|
24
|
+
|
|
25
|
+
ownerBootId?: string;
|
|
26
|
+
|
|
27
|
+
lastPulseAt?: number;
|
|
28
|
+
|
|
29
|
+
backgrounded?: boolean;
|
|
24
30
|
}
|
|
25
31
|
export interface BackgroundSessionState {
|
|
26
32
|
agentId: string;
|
|
@@ -37,6 +43,8 @@ export declare class BackgroundCommandStore {
|
|
|
37
43
|
|
|
38
44
|
persistSession(agentId: string, tasks: BackgroundTaskRecord[]): Promise<void>;
|
|
39
45
|
|
|
46
|
+
updateSession(agentId: string, mutator: (state: BackgroundSessionState) => BackgroundTaskRecord[] | null | undefined): Promise<BackgroundSessionState | null>;
|
|
47
|
+
|
|
40
48
|
markNotified(agentId: string, taskIds: string[]): Promise<void>;
|
|
41
49
|
|
|
42
50
|
outputPathFor(agentId: string, taskId: string): string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{promises as a}from"node:fs";import
|
|
1
|
+
import{promises as a}from"node:fs";import u from"node:path";import{renameWithRetry as l}from"./fs-utils.js";import{resolveAcodeHome as m}from"./paths.js";function d(o){return String(o).replace(/[^a-zA-Z0-9_-]/g,"_")}function c(o){return u.join(m(),"bg-tasks",d(o))}function h(o){return u.join(c(o),"state.json")}class C{constructor(){this.writeChains=new Map}chain(t){return this.writeChains.get(t)??Promise.resolve()}setChain(t,r){this.writeChains.set(t,r.catch(()=>{}))}async readSession(t){try{const r=await a.readFile(h(t),"utf-8"),s=JSON.parse(r);return{agentId:t,updatedAt:typeof s.updatedAt=="number"?s.updatedAt:0,tasks:Array.isArray(s.tasks)?s.tasks:[]}}catch(r){if(r?.code==="ENOENT")return null;if(r instanceof SyntaxError)return await this.backupCorrupt(t).catch(()=>{}),null;throw r}}async persistSession(t,r){const s=h(t),i=this.chain(t).then(async()=>{await a.mkdir(c(t),{recursive:!0});const n={agentId:t,updatedAt:Date.now(),tasks:r},e=`${s}.tmp`;await a.writeFile(e,JSON.stringify(n,null,2),"utf-8"),await l(e,s)});this.setChain(t,i),await i}async updateSession(t,r){const s=h(t);let i=null;const n=this.chain(t).then(async()=>{const e=await this.readSession(t)??{agentId:t,updatedAt:0,tasks:[]},p=r(e);if(!p){i=e;return}const f={agentId:t,updatedAt:Date.now(),tasks:p};await a.mkdir(c(t),{recursive:!0});const w=`${s}.tmp`;await a.writeFile(w,JSON.stringify(f,null,2),"utf-8"),await l(w,s),i=f});return this.setChain(t,n),await n,i}async markNotified(t,r){const s=await this.readSession(t);if(!s)return;const i=new Set(r);let n=!1;for(const e of s.tasks)i.has(e.taskId)&&!e.notified&&(e.notified=!0,n=!0);n&&await this.persistSession(t,s.tasks)}outputPathFor(t,r){return u.join(c(t),`${r}.out`)}async deleteAgent(t){const r=this.chain(t).then(()=>a.rm(c(t),{recursive:!0,force:!0}));this.setChain(t,r),await r.catch(()=>{})}async listAgents(){try{return(await a.readdir(u.join(m(),"bg-tasks"),{withFileTypes:!0})).filter(r=>r.isDirectory()).map(r=>r.name)}catch(t){if(t?.code==="ENOENT")return[];throw t}}async backupCorrupt(t){const r=h(t),s=`${r}.corrupt-${Date.now()}`;await a.copyFile(r,s).catch(()=>{})}}export{C as BackgroundCommandStore};
|
|
2
2
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{randomUUID as nt}from"node:crypto";import{promises as p,createReadStream as X}from"node:fs";import it from"node:os";import d from"node:path";import Y from"node:readline";import{runShellCommand as at}from"./builtins/shellCommandRunner.js";import{createOutputEventCollector as rt}from"./builtins/commandOutputEvents.js";import{buildCommandOutputSpilloverContent as st,COMMAND_OUTPUT_SPILLOVER_LINE_LIMIT as ct,countOutputLines as H,formatCommandOutputSpilloverStdout as lt,writeCommandArtifactFile as ut}from"./builtins/commandArtifacts.js";import{symbolsTool as dt}from"./built-in-symbols-tool.js";import{createListBackgroundCommandsTool as mt,createKillBackgroundCommandTool as ht,createReadBackgroundOutputTool as ft}from"./background-command-tools.js";import{detectImageFormatFromBuffer as pt,formatBytes as S,imageMediaType as K,readImageDimensions as gt,tryCompressImageWithSharp as wt,tryResizeWithSharp as _t}from"./image-utils.js";const yt=2e3,L=2e3,bt=50*1024,V=4096,kt=.3,vt=new Set([".png",".jpg",".jpeg",".gif",".bmp",".ico",".webp",".tiff",".tif",".zip",".gz",".tar",".rar",".7z",".bz2",".xz",".lz",".exe",".dll",".so",".dylib",".bin",".o",".a",".pdf",".doc",".docx",".xls",".xlsx",".ppt",".pptx",".mp3",".mp4",".avi",".mov",".wav",".flv",".mkv",".ogg",".woff",".woff2",".ttf",".otf",".eot",".class",".jar",".war",".ear",".pyc",".pyo",".node",".sqlite",".db",".mdb",".wasm"]),Et=new Set(["png","jpg","jpeg","gif","webp"]),J=20*1024*1024,A=10*1024*1024,D=Math.floor(3.75*1024*1024),C=2e3,xt=50*1024,Tt=100*1024,Pt=15e3,St=1e3,$t=1024*1024,Mt=256*1024,Rt=1e3,It=new Set([".git","node_modules","dist","build",".next",".nuxt",".cache",".turbo",".output","coverage",".nyc_output"]),de={fileReadOutside:"allow",commandExecution:"allow",fileModification:"allow"};async function N(n,e,o){if(!n)return;const t=n.getLevel(e);if(t==="deny")throw new Error(`Operation blocked by tool permission policy (${e} = deny). Explain this restriction to the user if needed.`);if(t==="ask"&&!await n.requestPermission(e,o))throw new Error(`Operation denied by the user (${e}). Do not retry the same operation without asking the user.`)}function $(n){return n.replace(/\\/g,"/")}async function Z(n){try{return await p.access(n),!0}catch{return!1}}function Ot(n,e){if(!e)return 0;let o=0,t=n.indexOf(e);for(;t>=0;)o+=1,t=n.indexOf(e,t+e.length);return o}function M(n,e,o){for(const t of e){const i=n[t];if(typeof i=="string")return i}throw new Error(`${o} is required`)}function R(n,e){for(const o of e){const t=n[o];if(typeof t=="string"&&t.trim())return t}}function U(n,e,o){for(const t of e){const i=n[t],a=typeof i=="number"?i:typeof i=="string"?Number(i):NaN;if(Number.isFinite(a)&&a>0)return Math.floor(a)}return o}function Lt(n,e,o){for(const t of e){const i=n[t],a=typeof i=="number"?i:typeof i=="string"?Number(i):NaN;if(Number.isFinite(a)&&a>0)return Math.floor(a);if(Number.isFinite(a)&&a===0)return 0}return o}function At(n){const e=n.compress;if(e==null)return"auto";if(typeof e=="boolean")return e;if(e==="auto")return"auto";if(e==="true")return!0;if(e==="false")return!1;throw new Error(`Invalid compress argument: ${JSON.stringify(e)} (expected true, false, or "auto")`)}async function Q(n){const e=d.extname(n).toLowerCase();if(vt.has(e))return!0;const o=await p.open(n,"r");try{const t=Buffer.alloc(V),{bytesRead:i}=await o.read(t,0,V,0);if(i===0)return!1;const a=t.subarray(0,i);if(a.includes(0))return!0;let s=0;for(let r=0;r<i;r++){const c=a[r];(c<9||c>13&&c<32)&&s++}return s/i>kt}finally{await o.close()}}function O(n,e){const o=d.resolve(e),t=d.relative(n,o);if(t.startsWith("..")||d.isAbsolute(t))throw new Error(`Target path is outside workspace: ${o}`);return o}function W(n){return n==="~"||n.startsWith("~/")?d.join(it.homedir(),n.slice(1)):n}async function Bt(n,e){const o=d.resolve(String(n||"").trim()),t=await p.realpath(o);if(!(await p.stat(t)).isDirectory())throw new Error(`Workspace path is not a directory: ${o}`);if(!e?.trim())return{workspaceRoot:t,cwd:t};const a=W(e.trim()),s=d.isAbsolute(a)?d.resolve(a):d.resolve(o,a),r=d.relative(o,s);if(r.startsWith("..")||d.isAbsolute(r))throw new Error(`Command cwd is outside workspace: ${s}`);const c=O(t,await p.realpath(s));if(!(await p.stat(c)).isDirectory())throw new Error(`Command cwd is not a directory: ${c}`);return{workspaceRoot:t,cwd:c}}async function Nt(n,e){const o=await p.realpath(d.dirname(e));O(n,o);try{if((await p.lstat(e)).isSymbolicLink())throw new Error(`Refusing to write through symbolic link: ${e}`)}catch(t){if((typeof t=="object"&&t!==null?Reflect.get(t,"code"):void 0)!=="ENOENT")throw t}}async function B(n,e){const o=d.resolve(String(n||"").trim()),t=await p.realpath(o);if(!(await p.stat(t)).isDirectory())throw new Error(`Workspace path is not a directory: ${o}`);const a=W(e.trim());if(!a)throw new Error("file_path is required");const s=d.isAbsolute(a)?d.resolve(a):d.resolve(o,a),r=d.relative(o,s);if(r.startsWith("..")||d.isAbsolute(r))throw new Error(`Target path is outside workspace: ${s}`);return{workspaceRoot:t,requestedPath:s,targetPath:O(t,d.join(t,r))}}async function tt(n,e,o){const t=d.resolve(String(n||"").trim()),i=await p.realpath(t);if(!(await p.stat(i)).isDirectory())throw new Error(`Workspace path is not a directory: ${t}`);const s=W(e.trim());if(!s)throw new Error("file_path is required");const r=d.isAbsolute(s)?d.resolve(s):d.resolve(t,s),c=d.relative(t,r),l=await p.realpath(r).catch(()=>r),u=d.relative(i,l);if(c.startsWith("..")||d.isAbsolute(c)||u.startsWith("..")||d.isAbsolute(u)){if(!o)throw new Error(`Target path is outside workspace: ${r}`);return await N(o,"fileReadOutside",{path:r}),{workspaceRoot:i,requestedPath:r,targetPath:l}}return{workspaceRoot:i,requestedPath:r,targetPath:O(i,l)}}async function et(n,e,o){const t=await B(n,e),i=d.dirname(t.targetPath),a=!await Z(i);await p.mkdir(i,{recursive:!0}),await Nt(t.workspaceRoot,t.targetPath);const s=d.join(i,`.${d.basename(t.targetPath)}.${process.pid}.${nt()}.tmp`);try{await p.writeFile(s,o,"utf-8"),await p.rename(s,t.targetPath)}catch(r){throw await p.rm(s,{force:!0}),r}return{workspaceRoot:t.workspaceRoot,targetPath:t.targetPath,bytes:Buffer.byteLength(o,"utf-8"),createdParentDirectory:a}}async function Ft(n,e){const o=await B(n,e),t=O(o.workspaceRoot,await p.realpath(o.targetPath));if(!(await p.stat(t)).isFile())throw new Error(`Path is not a file: ${t}`);return{workspaceRoot:o.workspaceRoot,targetPath:t,content:await p.readFile(t,"utf-8")}}async function Ct(n,e,o,t,i){const a=await tt(n,e,i),s=a.targetPath;if(!(await p.stat(s)).isFile())throw new Error(`Path is not a file: ${s}`);if(await Q(s))return{workspaceRoot:a.workspaceRoot,targetPath:s,content:"[Binary file, content skipped]",isBinary:!0,truncated:!1,totalLines:0,shownFromLine:0,shownToLine:0};const c=X(s,{encoding:"utf-8"}),l=Y.createInterface({input:c,crlfDelay:1/0});try{const u=[];let w=0,g=0,_=0,y=!1,
|
|
1
|
+
import{randomUUID as nt}from"node:crypto";import{promises as p,createReadStream as X}from"node:fs";import it from"node:os";import d from"node:path";import Y from"node:readline";import{runShellCommand as at}from"./builtins/shellCommandRunner.js";import{createOutputEventCollector as rt}from"./builtins/commandOutputEvents.js";import{buildCommandOutputSpilloverContent as st,COMMAND_OUTPUT_SPILLOVER_LINE_LIMIT as ct,countOutputLines as H,formatCommandOutputSpilloverStdout as lt,writeCommandArtifactFile as ut}from"./builtins/commandArtifacts.js";import{symbolsTool as dt}from"./built-in-symbols-tool.js";import{createListBackgroundCommandsTool as mt,createKillBackgroundCommandTool as ht,createReadBackgroundOutputTool as ft}from"./background-command-tools.js";import{detectImageFormatFromBuffer as pt,formatBytes as S,imageMediaType as K,readImageDimensions as gt,tryCompressImageWithSharp as wt,tryResizeWithSharp as _t}from"./image-utils.js";const yt=2e3,L=2e3,bt=50*1024,V=4096,kt=.3,vt=new Set([".png",".jpg",".jpeg",".gif",".bmp",".ico",".webp",".tiff",".tif",".zip",".gz",".tar",".rar",".7z",".bz2",".xz",".lz",".exe",".dll",".so",".dylib",".bin",".o",".a",".pdf",".doc",".docx",".xls",".xlsx",".ppt",".pptx",".mp3",".mp4",".avi",".mov",".wav",".flv",".mkv",".ogg",".woff",".woff2",".ttf",".otf",".eot",".class",".jar",".war",".ear",".pyc",".pyo",".node",".sqlite",".db",".mdb",".wasm"]),Et=new Set(["png","jpg","jpeg","gif","webp"]),J=20*1024*1024,A=10*1024*1024,D=Math.floor(3.75*1024*1024),C=2e3,xt=50*1024,Tt=100*1024,Pt=15e3,St=1e3,$t=1024*1024,Mt=256*1024,Rt=1e3,It=new Set([".git","node_modules","dist","build",".next",".nuxt",".cache",".turbo",".output","coverage",".nyc_output"]),de={fileReadOutside:"allow",commandExecution:"allow",fileModification:"allow"};async function N(n,e,o){if(!n)return;const t=n.getLevel(e);if(t==="deny")throw new Error(`Operation blocked by tool permission policy (${e} = deny). Explain this restriction to the user if needed.`);if(t==="ask"&&!await n.requestPermission(e,o))throw new Error(`Operation denied by the user (${e}). Do not retry the same operation without asking the user.`)}function $(n){return n.replace(/\\/g,"/")}async function Z(n){try{return await p.access(n),!0}catch{return!1}}function Ot(n,e){if(!e)return 0;let o=0,t=n.indexOf(e);for(;t>=0;)o+=1,t=n.indexOf(e,t+e.length);return o}function M(n,e,o){for(const t of e){const i=n[t];if(typeof i=="string")return i}throw new Error(`${o} is required`)}function R(n,e){for(const o of e){const t=n[o];if(typeof t=="string"&&t.trim())return t}}function U(n,e,o){for(const t of e){const i=n[t],a=typeof i=="number"?i:typeof i=="string"?Number(i):NaN;if(Number.isFinite(a)&&a>0)return Math.floor(a)}return o}function Lt(n,e,o){for(const t of e){const i=n[t],a=typeof i=="number"?i:typeof i=="string"?Number(i):NaN;if(Number.isFinite(a)&&a>0)return Math.floor(a);if(Number.isFinite(a)&&a===0)return 0}return o}function At(n){const e=n.compress;if(e==null)return"auto";if(typeof e=="boolean")return e;if(e==="auto")return"auto";if(e==="true")return!0;if(e==="false")return!1;throw new Error(`Invalid compress argument: ${JSON.stringify(e)} (expected true, false, or "auto")`)}async function Q(n){const e=d.extname(n).toLowerCase();if(vt.has(e))return!0;const o=await p.open(n,"r");try{const t=Buffer.alloc(V),{bytesRead:i}=await o.read(t,0,V,0);if(i===0)return!1;const a=t.subarray(0,i);if(a.includes(0))return!0;let s=0;for(let r=0;r<i;r++){const c=a[r];(c<9||c>13&&c<32)&&s++}return s/i>kt}finally{await o.close()}}function O(n,e){const o=d.resolve(e),t=d.relative(n,o);if(t.startsWith("..")||d.isAbsolute(t))throw new Error(`Target path is outside workspace: ${o}`);return o}function W(n){return n==="~"||n.startsWith("~/")?d.join(it.homedir(),n.slice(1)):n}async function Bt(n,e){const o=d.resolve(String(n||"").trim()),t=await p.realpath(o);if(!(await p.stat(t)).isDirectory())throw new Error(`Workspace path is not a directory: ${o}`);if(!e?.trim())return{workspaceRoot:t,cwd:t};const a=W(e.trim()),s=d.isAbsolute(a)?d.resolve(a):d.resolve(o,a),r=d.relative(o,s);if(r.startsWith("..")||d.isAbsolute(r))throw new Error(`Command cwd is outside workspace: ${s}`);const c=O(t,await p.realpath(s));if(!(await p.stat(c)).isDirectory())throw new Error(`Command cwd is not a directory: ${c}`);return{workspaceRoot:t,cwd:c}}async function Nt(n,e){const o=await p.realpath(d.dirname(e));O(n,o);try{if((await p.lstat(e)).isSymbolicLink())throw new Error(`Refusing to write through symbolic link: ${e}`)}catch(t){if((typeof t=="object"&&t!==null?Reflect.get(t,"code"):void 0)!=="ENOENT")throw t}}async function B(n,e){const o=d.resolve(String(n||"").trim()),t=await p.realpath(o);if(!(await p.stat(t)).isDirectory())throw new Error(`Workspace path is not a directory: ${o}`);const a=W(e.trim());if(!a)throw new Error("file_path is required");const s=d.isAbsolute(a)?d.resolve(a):d.resolve(o,a),r=d.relative(o,s);if(r.startsWith("..")||d.isAbsolute(r))throw new Error(`Target path is outside workspace: ${s}`);return{workspaceRoot:t,requestedPath:s,targetPath:O(t,d.join(t,r))}}async function tt(n,e,o){const t=d.resolve(String(n||"").trim()),i=await p.realpath(t);if(!(await p.stat(i)).isDirectory())throw new Error(`Workspace path is not a directory: ${t}`);const s=W(e.trim());if(!s)throw new Error("file_path is required");const r=d.isAbsolute(s)?d.resolve(s):d.resolve(t,s),c=d.relative(t,r),l=await p.realpath(r).catch(()=>r),u=d.relative(i,l);if(c.startsWith("..")||d.isAbsolute(c)||u.startsWith("..")||d.isAbsolute(u)){if(!o)throw new Error(`Target path is outside workspace: ${r}`);return await N(o,"fileReadOutside",{path:r}),{workspaceRoot:i,requestedPath:r,targetPath:l}}return{workspaceRoot:i,requestedPath:r,targetPath:O(i,l)}}async function et(n,e,o){const t=await B(n,e),i=d.dirname(t.targetPath),a=!await Z(i);await p.mkdir(i,{recursive:!0}),await Nt(t.workspaceRoot,t.targetPath);const s=d.join(i,`.${d.basename(t.targetPath)}.${process.pid}.${nt()}.tmp`);try{await p.writeFile(s,o,"utf-8"),await p.rename(s,t.targetPath)}catch(r){throw await p.rm(s,{force:!0}),r}return{workspaceRoot:t.workspaceRoot,targetPath:t.targetPath,bytes:Buffer.byteLength(o,"utf-8"),createdParentDirectory:a}}async function Ft(n,e){const o=await B(n,e),t=O(o.workspaceRoot,await p.realpath(o.targetPath));if(!(await p.stat(t)).isFile())throw new Error(`Path is not a file: ${t}`);return{workspaceRoot:o.workspaceRoot,targetPath:t,content:await p.readFile(t,"utf-8")}}async function Ct(n,e,o,t,i){const a=await tt(n,e,i),s=a.targetPath;if(!(await p.stat(s)).isFile())throw new Error(`Path is not a file: ${s}`);if(await Q(s))return{workspaceRoot:a.workspaceRoot,targetPath:s,content:"[Binary file, content skipped]",isBinary:!0,truncated:!1,totalLines:0,shownFromLine:0,shownToLine:0};const c=X(s,{encoding:"utf-8"}),l=Y.createInterface({input:c,crlfDelay:1/0});try{const u=[];let w=0,g=0,_=0,y=!1,m=!1;const P=o-1;for await(const f of l){if(g++,w<P){w++;continue}if(u.length>=t){y=!0,w++;continue}let b=f;b.length>L&&(b=b.slice(0,L)+`... (line truncated to ${L} chars)`);const x=`${w+1}: ${b}`,k=Buffer.byteLength(x,"utf-8")+1;if(_+k>bt){m=!0;break}u.push(x),_+=k,w++}if(g>0&&u.length===0&&!y&&!m&&P>=g)throw new Error(`offset ${o} exceeds total line count ${g} in file: ${s}`);const h=u.length>0?o:0,v=u.length>0?o+u.length-1:0;let E;m?E=`Output capped at 50 KB. Showing lines ${h}-${v}. Use offset=${v+1} to continue.`:y?E=`Showing lines ${h}-${v} of ${g}. Use offset=${v+1} to continue.`:E=`End of file - total ${g} lines`;const T=u.length>0?u.join(`
|
|
2
2
|
`)+`
|
|
3
|
-
`+E:E;return{workspaceRoot:a.workspaceRoot,targetPath:s,content:T,isBinary:!1,truncated:y||h,totalLines:h?-1:g,shownFromLine:m,shownToLine:v}}finally{l.close(),c.destroy()}}function z(n){return/^https?:\/\//i.test(n)}async function Ut(n){let e;try{e=new URL(n)}catch{throw new Error(`Invalid image URL: ${n}`)}if(e.protocol!=="http:"&&e.protocol!=="https:")throw new Error(`Unsupported image URL protocol: ${e.protocol} (only http/https)`);let o;try{o=await fetch(n,{signal:AbortSignal.timeout(Pt)})}catch(l){const u=l instanceof Error?l.message:String(l);throw new Error(`Image URL fetch failed: ${n} (${u})`)}if(!o.ok)throw new Error(`Image URL fetch failed: ${n} (HTTP ${o.status})`);const t=(o.headers.get("content-type")||"").split(";")[0]?.trim().toLowerCase()||"";if(t&&!t.startsWith("image/"))throw new Error(`URL did not return an image (content-type: ${t}): ${n}`);const i=Number(o.headers.get("content-length")||"");if(Number.isFinite(i)&&i>A)throw new Error(`Image too large to fetch: ${n} (${S(i)} > ${S(A)})`);const a=o.body;if(!a){const l=await o.arrayBuffer();if(l.byteLength>A)throw new Error(`Image too large to fetch: ${n} (${S(l.byteLength)} > ${S(A)})`);return Buffer.from(l)}const s=a.getReader(),r=[];let c=0;for(;;){const{done:l,value:u}=await s.read();if(l)break;if(u){if(c+=u.byteLength,c>A)throw s.cancel().catch(()=>{}),new Error(`Image too large to fetch: ${n} (${S(c)} > ${S(A)})`);r.push(Buffer.from(u))}}return Buffer.concat(r,c)}async function Dt(n,e,o,t="auto"){let i,a;if(z(e))i=await Ut(e),a=e;else{const x=(await tt(n,e,o)).targetPath,k=await p.stat(x);if(!k.isFile())throw new Error(`Path is not a file: ${x}`);if(k.size>J)throw new Error(`Image file too large to read: ${$(x)} (${S(k.size)} > ${S(J)}). Downscale it first.`);i=await p.readFile(x),a=$(x)}if(i.length===0)throw new Error(`Image file is empty: ${a}`);const s=pt(i);if(!s)throw new Error(`Unsupported or corrupt image (unrecognized format): ${a}`);const r=gt(i);let c=s,l=i,u=r?.width,w=r?.height,g=null;if(t!==!1){const b=await wt(i,s,{normalMaxBytes:xt,clearMaxBytes:Tt});b?.compressed&&(l=b.buffer,c=b.mediaType,u=b.width,w=b.height,g={level:b.level,originalSize:b.originalSize})}let _=!1;const y=await _t(l,c,{maxWidth:C,maxHeight:C,maxBytes:D});if(y)l=y.buffer,c=y.mediaType,u=y.width,w=y.height,_=y.changed;else if(l.length>D)throw new Error(`Image too large to inline: ${a} (${S(l.length)} > ${S(D)}). Downscale it first, e.g. via Bash: magick "${e}" -resize ${C}x${C} "${e}". Installing sharp enables automatic downscaling.`);const h=u&&w?`${u}x${w}`:"unknown dimensions",P=_&&r&&r.width!==u?` (resized from ${r.width}x${r.height})`:_?" (resized)":"",m=g!==null?g.level:null,v=g!==null?Math.max(1,Math.round((1-l.length/g.originalSize)*100)):0,E=g!==null?`${S(l.length)} (compressed from ${S(g.originalSize)}, saved ${v}%, ${m} level)`:S(l.length),T=`[Image: ${a}, ${h}${P}, ${E}]`,f=z(e)?d.basename(new URL(e).pathname)||"image":d.basename(a);return{ok:!0,filePath:a,content:T,isBinary:!1,image:{mediaType:K(c),width:u??null,height:w??null,originalWidth:r?.width??null,originalHeight:r?.height??null,resized:_,size:l.length,compressed:g!==null,compressionLevel:m,compressionRatio:g!==null?Math.round((1-l.length/g.originalSize)*100)/100:null,originalSize:i.length},attachments:[{type:"image",name:f,mediaType:K(c),base64Data:l.toString("base64")}]}}function ot(n){let e="",o=0;for(;o<n.length;){const t=n[o];if(t==="*")e+="[^/]*";else if(t==="?")e+="[^/]";else if(t==="{"){const i=n.indexOf("}",o);if(i>=0){const a=n.slice(o+1,i);e+=`(${a.split(",").join("|")})`,o=i}else e+="\\{"}else if(t==="["){const i=n.indexOf("]",o);i>=0?(e+=n.slice(o,i+1),o=i):e+="\\["}else"+^$.()|\\".includes(t)?e+="\\"+t:e+=t;o++}return new RegExp(`^${e}$`)}async function Wt(n,e,o,t){const i=[];async function a(s){if(i.length>=t)return;const r=await p.readdir(s,{withFileTypes:!0});for(const c of r){if(i.length>=t)return;if(c.name.startsWith(".")&&c.isDirectory())continue;const l=d.join(s,c.name);if(c.isDirectory()){if(It.has(c.name))continue;await a(l)}else if(c.isFile()){if(e&&!e.test(c.name)||o&&o.test(c.name))continue;i.push(l)}}}return await a(n),i}async function zt(n,e,o){if((await p.stat(n)).size>$t)return{matches:[],count:0};if(await Q(n))return{matches:[],count:0};const i=d.relative(e,n).replace(/\\/g,"/"),a=[];let s=0;const r=X(n,{encoding:"utf-8"}),c=Y.createInterface({input:r,crlfDelay:1/0});try{let l=0;for await(const u of c)if(l++,o.regex.test(u)){if(s++,o.collectContent){let w=u;if(w.length>L&&(w=w.slice(0,L)+`... (line truncated to ${L} chars)`),a.push({file:i,line:l,content:w}),a.length>=o.maxResults)break}if(o.stopOnFirstMatch)break}}finally{c.close(),r.destroy()}return{matches:a,count:s}}const qt=12*60*6e4,jt=30*6e4,Gt=6e4,Xt=10*6e4;function Yt(n,e,o){return{kind:"builtin",toolName:"write_file",exposedName:"write_file",description:"Create or overwrite a UTF-8 text file inside the current workspace. Parent directories are created automatically.",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Workspace-relative file path to write."},content:{type:"string",description:"Complete UTF-8 file content."},reason:{type:"string",description:"Optional reason for this change, e.g. what bug is being fixed. Helps humans understand the change later."}},required:["file_path","content"],additionalProperties:!1},async execute(t){const i=M(t,["file_path","filePath"],"file_path"),a=M(t,["content"],"content"),s=R(t,["reason"]),r=await B(n,i);await N(o,"fileModification",{operation:"write",path:r.targetPath});const c=await Z(r.targetPath),l=c?await p.readFile(r.targetPath,"utf-8").catch(()=>null):null,u=await et(n,i,a);if(e){const w=d.relative(u.workspaceRoot,u.targetPath).replace(/\\/g,"/");await e({filePath:w,operation:c?"UPDATE":"CREATE",beforeContent:l,afterContent:a,reason:s})}return{ok:!0,filePath:$(u.targetPath),bytes:u.bytes,createdParentDirectory:u.createdParentDirectory}}}}function Ht(n,e,o){return{kind:"builtin",toolName:"edit_file",exposedName:"edit_file",description:"Replace exactly one occurrence of old_string with new_string in a UTF-8 workspace text file.",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Workspace-relative file path to edit."},old_string:{type:"string",description:"Existing text to replace. Must occur exactly once."},new_string:{type:"string",description:"Replacement text."},reason:{type:"string",description:"Optional reason for this change, e.g. what bug is being fixed. Helps humans understand the change later."}},required:["file_path","old_string","new_string"],additionalProperties:!1},async execute(t){const i=M(t,["file_path","filePath"],"file_path"),a=M(t,["old_string","oldString"],"old_string"),s=M(t,["new_string","newString"],"new_string"),r=R(t,["reason"]);if(!a)throw new Error("old_string must not be empty");const c=await B(n,i);await N(o,"fileModification",{operation:"edit",path:c.targetPath});const l=await Ft(n,i),u=Ot(l.content,a);if(u===0)throw new Error("old_string was not found in file");if(u>1)throw new Error(`old_string occurs ${u} times; provide a unique match`);const w=l.content.replace(a,s),g=await et(n,i,w);if(e){const _=d.relative(g.workspaceRoot,g.targetPath).replace(/\\/g,"/");await e({filePath:_,operation:"UPDATE",beforeContent:l.content,afterContent:w,reason:r})}return{ok:!0,filePath:$(g.targetPath),bytes:g.bytes}}}}function Kt(n,e,o){return{kind:"builtin",toolName:"delete_file",exposedName:"delete_file",description:"Delete a file inside the current workspace. Returns an error if the file does not exist.",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Workspace-relative file path to delete."},reason:{type:"string",description:"Optional reason for this change, e.g. what bug is being fixed. Helps humans understand the change later."}},required:["file_path"],additionalProperties:!1},async execute(t){const i=M(t,["file_path","filePath"],"file_path"),a=R(t,["reason"]),s=await B(n,i);await N(o,"fileModification",{operation:"delete",path:s.targetPath});const r=O(s.workspaceRoot,await p.realpath(s.targetPath).catch(()=>s.targetPath)),c=await p.stat(r).catch(()=>null);if(!c)throw new Error(`File does not exist: ${r}`);if(!c.isFile())throw new Error(`Path is not a file: ${r}`);const l=await p.readFile(r,"utf-8").catch(()=>null);if(await p.rm(r,{force:!0}),e){const u=d.relative(s.workspaceRoot,r).replace(/\\/g,"/");await e({filePath:u,operation:"DELETE",beforeContent:l,afterContent:null,reason:a})}return{ok:!0,filePath:$(r),deleted:!0}}}}function Vt(n,e){return{kind:"builtin",toolName:"read_file",exposedName:"read_file",description:"Read a UTF-8 text file inside the current workspace. Returns content with line numbers in the format '<line>: <content>'. Supports offset (1-indexed line number to start from) and limit (max lines to return, default 2000). Output is capped at 2000 lines or 50 KB, whichever is reached first. Individual lines longer than 2000 characters are truncated. Avoid tiny repeated slices (e.g. 30-line chunks); read a larger window if more context is needed. Images (.png/.jpg/.jpeg/.gif/.webp) and http(s) image URLs are also supported: the image is inlined into the conversation so you can see it (requires a model with image input). offset/limit are ignored for images. By default (compress='auto') images are compressed before inlining using a tiered ladder that judges by the compressed result, not the original size: lossless first (pixel-exact, dimensions preserved), then high-quality lossy downscaling only if the lossless result is still over 50 KB (normal level), then more aggressive downscaling if still over 100 KB (clear level). When compression applies, the result reports the compression level, compressed size, and compression ratio (e.g. saved 80%). Pass compress=true to also attempt compression, or compress=false to disable it (images exceeding the hard 2000px / 3.75MB inline limits are still auto-downscaled when sharp is available).",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Workspace-relative file path to read. Also accepts an http(s) URL pointing to an image."},offset:{type:"number",description:"Line number to start reading from (1-indexed). Defaults to 1."},limit:{type:"number",description:"Maximum number of lines to return. Defaults to 2000."},compress:{anyOf:[{type:"boolean"},{type:"string",enum:["auto"]}],description:'Image compression mode (ignored for text files). "auto" (default): compress with a tiered ladder driven by the compressed result \u2014 lossless, then high-quality downscale if still >50 KB, then more aggressive if still >100 KB. true: same as auto. false: skip tiered compression entirely (hard inline limits still apply).'}},required:["file_path"],additionalProperties:!1},async execute(o){const t=M(o,["file_path","filePath"],"file_path"),i=d.extname(t).toLowerCase().slice(1);if(z(t)||Et.has(i))return await Dt(n,t,e,At(o));const a=U(o,["offset"],1),s=U(o,["limit"],yt),r=await Ct(n,t,a,s,e);return{ok:!0,filePath:$(r.targetPath),content:r.content,isBinary:r.isBinary,truncated:r.truncated,totalLines:r.totalLines,shownFromLine:r.shownFromLine,shownToLine:r.shownToLine}}}}function Jt(n,e,o,t){return{kind:"builtin",toolName:"Bash",exposedName:"Bash",description:"Execute a shell command in the current workspace. Use for local diagnostics, builds, tests, and OS commands. The command runs with a bounded total timeout (timeout_ms) measured from command start \u2014 by default 30 minutes, after which the process is force-terminated; timeout_ms can raise this (up to 12 hours) or set 0 to disable. The tool returns stdout, stderr, exit code, and (if converted to background) a task_id. By default (wait_seconds omitted or -1) the command runs in the FOREGROUND and streams output in real time; if the 5-minute adapter watchdog fires while it is still running, the command is moved to the BACKGROUND and the tool returns a task_id plus an explanation (backgrounded=true, reason=watchdog_timeout). The background command stays bound by the same total timeout (timeout_ms, default 30 minutes, still measured from command start) and can be managed via await_complete / list_background_commands / read_background_output / kill_background_command. For immediate backgrounding use wait_seconds=0, or wait_seconds=n>0 to wait n seconds then background.",inputSchema:{type:"object",properties:{command:{type:"string",description:"Shell command to execute."},cwd:{type:"string",description:"Optional workspace-relative working directory. Defaults to the workspace root."},timeout_ms:{type:"number",description:"Optional total command timeout in milliseconds, measured from command start. Defaults to 1800000 (30 minutes) and is capped at 43200000 (12 hours). Set 0 to disable the timeout entirely (run until finished or killed)."},inactivity_timeout_ms:{type:"number",description:"Optional no-output timeout in milliseconds. Defaults to 60000 and is capped at 600000."},wait_seconds:{type:"number",description:"How to wait for the command to finish. Omitted values (or -1, the default): run in the FOREGROUND until completion; if the 5-minute adapter watchdog fires first, the command is moved to the background and a task_id is returned (backgrounded=true, reason=watchdog_timeout). 0: spawn and immediately execute in the background, return task_id. n>0: wait up to n seconds; if still running, move to the background and return task_id. The command remains bound by the total timeout (timeout_ms, default 30 minutes, measured from command start) whether foreground or background; inactivity_timeout_ms is ignored once backgrounded.",minimum:-1},description:{type:"string",description:"Short reason for running this command (what it does and why it is needed), shown to the user in the background commands panel. Always provide a concise reason, e.g. 'Start local dev server for manual verification'."}},required:["command"],additionalProperties:!1},async execute(i,a){const s=M(i,["command"],"command").trim();if(!s)throw new Error("command must not be empty");const r=Lt(i,["timeout_ms","timeoutMs"],jt),c=r===0?0:Math.min(r,qt),l=U(i,["inactivity_timeout_ms","inactivityTimeoutMs"],Gt),u=Math.min(l,Xt),w=R(i,["cwd"]),g=R(i,["description"]),_=await Bt(n,w);await N(t,"commandExecution",{command:s,cwd:$(_.cwd)});const y=i.wait_seconds,P=typeof y=="number"&&Number.isFinite(y)?y:-1;if(o)return await Zt({deps:o,command:s,agentId:e,workspaceRoot:_.workspaceRoot,cwd:_.cwd,timeoutMs:c,inactivityTimeoutMs:u,waitSeconds:P,description:g,onOutput:a?.onOutput,settleSignal:a?.settleSignal});const m=await at({commandText:s,agentId:e,workspaceRoot:_.workspaceRoot,cwd:_.cwd,timeoutMs:c,inactivityTimeoutMs:u,onOutput:a?.onOutput,watchdogSignal:a?.settleSignal}),v={ok:m.exitCode===0&&!m.timedOut,tool:"Bash",command:s,cwd:$(_.cwd),exitCode:m.exitCode,signal:m.signal,timedOut:m.timedOut,inactivityTimedOut:m.inactivityTimedOut,watchdogInterrupted:m.watchdogInterrupted,timeoutMs:c,inactivityTimeoutMs:u,durationMs:m.durationMs,outputSpilled:m.outputSpilled,outputFilePath:m.outputFilePath,outputLineCount:m.outputLineCount,commandFilePath:m.commandFilePath,commandLineCount:m.commandLineCount,stdout:m.stdout,stderr:m.stderr};return m.watchdogInterrupted?{...v,message:"Command was interrupted by the adapter watchdog after ~5 minutes of synchronous (foreground) execution. Only the partial output produced so far is returned above"+(m.outputFilePath?` (full output spilled to "${m.outputFilePath}"; read it if you need more)`:"")+". For long-running commands, re-run with wait_seconds=0 (or n>0) to execute in the background, then manage via await_complete / list_background_commands / read_background_output / kill_background_command."}:v}}}async function Zt(n){const{deps:e,command:o,agentId:t,workspaceRoot:i,cwd:a,timeoutMs:s,inactivityTimeoutMs:r,waitSeconds:c,description:l,onOutput:u,settleSignal:w}=n,g=u?rt({cwd:a,commandLineCount:0,onOutput:u}):void 0;let _=!!g;const y=()=>{_&&(_=!1,g?.close())};try{const{taskId:h,completion:P}=e.manager.startCommand({commandText:o,agentId:t,workspaceRoot:i,cwd:a,description:l,timeoutMs:s,inactivityTimeoutMs:r,onOutput:_?T=>{_&&g?.push(T.stream,T.text)}:void 0,onCompleted:e.emitCompleted});if(c===0)return e.manager.disableInactivityTimeout(t,h),y(),{ok:!0,tool:"Bash",command:o,backgrounded:!0,taskId:h,status:"backgrounded",reason:"immediate",message:`Command moved to background. Use await_complete(bg_cmd_ids=["${h}"]) to block until it finishes, read_background_output(task_id="${h}", mode="tail") to read output, list_background_commands to view all, or kill_background_command(task_id="${h}") to stop.`};const m=c===-1?0:Math.max(0,Math.floor(c*1e3)),v=[P];let E;c!==-1&&v.push(new Promise(T=>{E=setTimeout(()=>T("__timeout__"),m),E.unref?.()})),w&&v.push(w.then(()=>"__settle__"));try{const T=await Promise.race(v);if(T==="__settle__")return e.manager.disableInactivityTimeout(t,h),{ok:!0,tool:"Bash",command:o,backgrounded:!0,taskId:h,status:"backgrounded",reason:"watchdog_timeout",waitedMs:m,message:`Command still running after watchdog timeout; moved to background. Use await_complete(bg_cmd_ids=["${h}"]) to block until it finishes, read_background_output(task_id="${h}", mode="tail") to read output, or kill_background_command(task_id="${h}") to stop.`};if(T==="__timeout__")return e.manager.disableInactivityTimeout(t,h),{ok:!0,tool:"Bash",command:o,backgrounded:!0,taskId:h,status:"backgrounded",reason:"timeout",waitedMs:m,message:`Command still running after ${c}s; moved to background. Use await_complete(bg_cmd_ids=["${h}"]) to block until it finishes, or read_background_output(task_id="${h}", mode="tail") / kill_background_command(task_id="${h}") to inspect.`};const f=T;let b=f.stdout,x=f.stderr,k=f.outputSpilled,F=f.outputFilePath;if(!k&&H(`${f.stdout}${f.stderr}`)>ct){const I=await ut(t,"cmd","out",st({commandText:o,commandFilePath:f.commandFilePath,commandLineCount:f.commandLineCount,cwd:a,startedAt:new Date(Date.now()-f.durationMs),endedAt:new Date,exitCode:f.exitCode,signal:f.signal??null,timedOut:f.timedOut,inactivityTimedOut:f.inactivityTimedOut??!1,stdout:b,stderr:x}));b=lt($(I),H(`${f.stdout}${f.stderr}`)),k=!0,F=$(I)}return{ok:f.exitCode===0&&!f.timedOut,tool:"Bash",command:o,backgrounded:!1,taskId:h,exitCode:f.exitCode,signal:f.signal,timedOut:f.timedOut,inactivityTimedOut:f.inactivityTimedOut,timeoutMs:f.timeoutMs,inactivityTimeoutMs:f.inactivityTimeoutMs,durationMs:f.durationMs,outputSpilled:k,outputFilePath:F,outputLineCount:f.outputLineCount,commandFilePath:f.commandFilePath,commandLineCount:f.commandLineCount,stdout:b,stderr:x}}finally{E&&clearTimeout(E),y()}}catch(h){if(h?.code==="too_many_tasks"||h?.code==="global_limit_reached")return{ok:!1,tool:"Bash",command:o,backgrounded:!1,status:"not_executed",error:h.code,message:`Command was NOT executed: ${h.message}. Wait for some background tasks to finish/cleanup (per-agent limit 16, global 256), or reduce concurrent background commands, then retry.`};throw h}}function Qt(n){return{kind:"builtin",toolName:"grep",exposedName:"grep",description:"Search file contents using regular expressions within the workspace. Returns matched lines with file paths and line numbers. Supports include/exclude glob patterns, case-insensitive matching, and three output modes (content, files_with_matches, count). Searches recursively through directories, skipping binary files, hidden directories, and common build/dependency directories (node_modules, .git, dist, etc.).",inputSchema:{type:"object",properties:{pattern:{type:"string",description:"Regular expression pattern to search for in file contents."},path:{type:"string",description:"Workspace-relative file or directory path to search in. Defaults to the workspace root."},include:{type:"string",description:"Glob pattern to filter files by name (e.g. '*.ts', '*.{js,ts}'). Only matching files are searched."},exclude:{type:"string",description:"Glob pattern to exclude files by name (e.g. '*.test.ts'). Matching files are skipped."},case_insensitive:{type:"boolean",description:"Case-insensitive matching. Default false."},output_mode:{type:"string",enum:["content","files_with_matches","count"],description:"Output format: 'content' returns matched lines with line numbers (default), 'files_with_matches' returns only file paths with matches, 'count' returns match counts per file."},max_results:{type:"number",description:"Maximum number of matched lines to return (content mode). Default 1000."}},required:["pattern"],additionalProperties:!1},async execute(e){const o=M(e,["pattern"],"pattern"),t=R(e,["path"])||".",i=R(e,["include"]),a=R(e,["exclude"]),s=e.case_insensitive===!0,r=(()=>{const k=e.output_mode;return k==="files_with_matches"||k==="count"?k:"content"})(),c=U(e,["max_results"],Rt),l=s?"i":"";let u;try{u=new RegExp(o,l)}catch(k){throw new Error(`Invalid regex pattern: ${k instanceof Error?k.message:String(k)}`)}const w=i?ot(i):null,g=a?ot(a):null,_=await B(n,t),y=O(_.workspaceRoot,await p.realpath(_.targetPath)),h=await p.stat(y);let P;if(h.isFile())P=[y];else if(h.isDirectory())P=await Wt(y,w,g,St);else throw new Error(`Path is neither a file nor a directory: ${y}`);const m=[],v=[],E=[];let T=0,f=0,b=!1;for(const k of P){if(r==="content"&&m.length>=c){b=!0;break}const F={regex:u,maxResults:r==="content"?c-m.length:Number.MAX_SAFE_INTEGER,collectContent:r==="content",stopOnFirstMatch:r==="files_with_matches"},I=await zt(k,_.workspaceRoot,F);if(I.count>0){const q=d.relative(_.workspaceRoot,k).replace(/\\/g,"/");if(E.push(q),T+=I.count,r==="content"){for(const j of I.matches){const G=Buffer.byteLength(j.content,"utf-8")+64;if(f+G>Mt){b=!0;break}f+=G,m.push(j)}if(b)break;if(m.length>=c){b=!0;break}}else r==="count"&&v.push({file:q,count:I.count})}}const x={ok:!0,tool:"grep",pattern:o,searchPath:$(_.targetPath),outputMode:r,filesSearched:P.length,filesMatched:E.length,totalMatches:T,truncated:b};return r==="content"?x.matches=m:r==="files_with_matches"?x.matches=E:x.matches=v,x}}}function me(n,e,o,t,i){const a=e||"unknown",s=[Vt(n,i),Yt(n,o,i),Ht(n,o,i),Kt(n,o,i),Qt(n),Jt(n,a,t,i),dt(n)];return t&&s.push(mt(a,t),ht(a,t),ft(a,t)),s}export{de as DEFAULT_TOOL_PERMISSION_POLICY,me as createBuiltInFileTools,O as ensureInsideWorkspace,B as resolveWorkspaceTarget};
|
|
3
|
+
`+E:E;return{workspaceRoot:a.workspaceRoot,targetPath:s,content:T,isBinary:!1,truncated:y||m,totalLines:m?-1:g,shownFromLine:h,shownToLine:v}}finally{l.close(),c.destroy()}}function z(n){return/^https?:\/\//i.test(n)}async function Ut(n){let e;try{e=new URL(n)}catch{throw new Error(`Invalid image URL: ${n}`)}if(e.protocol!=="http:"&&e.protocol!=="https:")throw new Error(`Unsupported image URL protocol: ${e.protocol} (only http/https)`);let o;try{o=await fetch(n,{signal:AbortSignal.timeout(Pt)})}catch(l){const u=l instanceof Error?l.message:String(l);throw new Error(`Image URL fetch failed: ${n} (${u})`)}if(!o.ok)throw new Error(`Image URL fetch failed: ${n} (HTTP ${o.status})`);const t=(o.headers.get("content-type")||"").split(";")[0]?.trim().toLowerCase()||"";if(t&&!t.startsWith("image/"))throw new Error(`URL did not return an image (content-type: ${t}): ${n}`);const i=Number(o.headers.get("content-length")||"");if(Number.isFinite(i)&&i>A)throw new Error(`Image too large to fetch: ${n} (${S(i)} > ${S(A)})`);const a=o.body;if(!a){const l=await o.arrayBuffer();if(l.byteLength>A)throw new Error(`Image too large to fetch: ${n} (${S(l.byteLength)} > ${S(A)})`);return Buffer.from(l)}const s=a.getReader(),r=[];let c=0;for(;;){const{done:l,value:u}=await s.read();if(l)break;if(u){if(c+=u.byteLength,c>A)throw s.cancel().catch(()=>{}),new Error(`Image too large to fetch: ${n} (${S(c)} > ${S(A)})`);r.push(Buffer.from(u))}}return Buffer.concat(r,c)}async function Dt(n,e,o,t="auto"){let i,a;if(z(e))i=await Ut(e),a=e;else{const x=(await tt(n,e,o)).targetPath,k=await p.stat(x);if(!k.isFile())throw new Error(`Path is not a file: ${x}`);if(k.size>J)throw new Error(`Image file too large to read: ${$(x)} (${S(k.size)} > ${S(J)}). Downscale it first.`);i=await p.readFile(x),a=$(x)}if(i.length===0)throw new Error(`Image file is empty: ${a}`);const s=pt(i);if(!s)throw new Error(`Unsupported or corrupt image (unrecognized format): ${a}`);const r=gt(i);let c=s,l=i,u=r?.width,w=r?.height,g=null;if(t!==!1){const b=await wt(i,s,{normalMaxBytes:xt,clearMaxBytes:Tt});b?.compressed&&(l=b.buffer,c=b.mediaType,u=b.width,w=b.height,g={level:b.level,originalSize:b.originalSize})}let _=!1;const y=await _t(l,c,{maxWidth:C,maxHeight:C,maxBytes:D});if(y)l=y.buffer,c=y.mediaType,u=y.width,w=y.height,_=y.changed;else if(l.length>D)throw new Error(`Image too large to inline: ${a} (${S(l.length)} > ${S(D)}). Downscale it first, e.g. via Bash: magick "${e}" -resize ${C}x${C} "${e}". Installing sharp enables automatic downscaling.`);const m=u&&w?`${u}x${w}`:"unknown dimensions",P=_&&r&&r.width!==u?` (resized from ${r.width}x${r.height})`:_?" (resized)":"",h=g!==null?g.level:null,v=g!==null?Math.max(1,Math.round((1-l.length/g.originalSize)*100)):0,E=g!==null?`${S(l.length)} (compressed from ${S(g.originalSize)}, saved ${v}%, ${h} level)`:S(l.length),T=`[Image: ${a}, ${m}${P}, ${E}]`,f=z(e)?d.basename(new URL(e).pathname)||"image":d.basename(a);return{ok:!0,filePath:a,content:T,isBinary:!1,image:{mediaType:K(c),width:u??null,height:w??null,originalWidth:r?.width??null,originalHeight:r?.height??null,resized:_,size:l.length,compressed:g!==null,compressionLevel:h,compressionRatio:g!==null?Math.round((1-l.length/g.originalSize)*100)/100:null,originalSize:i.length},attachments:[{type:"image",name:f,mediaType:K(c),base64Data:l.toString("base64")}]}}function ot(n){let e="",o=0;for(;o<n.length;){const t=n[o];if(t==="*")e+="[^/]*";else if(t==="?")e+="[^/]";else if(t==="{"){const i=n.indexOf("}",o);if(i>=0){const a=n.slice(o+1,i);e+=`(${a.split(",").join("|")})`,o=i}else e+="\\{"}else if(t==="["){const i=n.indexOf("]",o);i>=0?(e+=n.slice(o,i+1),o=i):e+="\\["}else"+^$.()|\\".includes(t)?e+="\\"+t:e+=t;o++}return new RegExp(`^${e}$`)}async function Wt(n,e,o,t){const i=[];async function a(s){if(i.length>=t)return;const r=await p.readdir(s,{withFileTypes:!0});for(const c of r){if(i.length>=t)return;if(c.name.startsWith(".")&&c.isDirectory())continue;const l=d.join(s,c.name);if(c.isDirectory()){if(It.has(c.name))continue;await a(l)}else if(c.isFile()){if(e&&!e.test(c.name)||o&&o.test(c.name))continue;i.push(l)}}}return await a(n),i}async function zt(n,e,o){if((await p.stat(n)).size>$t)return{matches:[],count:0};if(await Q(n))return{matches:[],count:0};const i=d.relative(e,n).replace(/\\/g,"/"),a=[];let s=0;const r=X(n,{encoding:"utf-8"}),c=Y.createInterface({input:r,crlfDelay:1/0});try{let l=0;for await(const u of c)if(l++,o.regex.test(u)){if(s++,o.collectContent){let w=u;if(w.length>L&&(w=w.slice(0,L)+`... (line truncated to ${L} chars)`),a.push({file:i,line:l,content:w}),a.length>=o.maxResults)break}if(o.stopOnFirstMatch)break}}finally{c.close(),r.destroy()}return{matches:a,count:s}}const qt=12*60*6e4,jt=30*6e4,Gt=6e4,Xt=10*6e4;function Yt(n,e,o){return{kind:"builtin",toolName:"write_file",exposedName:"write_file",description:"Create or overwrite a UTF-8 text file inside the current workspace. Parent directories are created automatically.",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Workspace-relative file path to write."},content:{type:"string",description:"Complete UTF-8 file content."},reason:{type:"string",description:"Optional reason for this change, e.g. what bug is being fixed. Helps humans understand the change later."}},required:["file_path","content"],additionalProperties:!1},async execute(t){const i=M(t,["file_path","filePath"],"file_path"),a=M(t,["content"],"content"),s=R(t,["reason"]),r=await B(n,i);await N(o,"fileModification",{operation:"write",path:r.targetPath});const c=await Z(r.targetPath),l=c?await p.readFile(r.targetPath,"utf-8").catch(()=>null):null,u=await et(n,i,a);if(e){const w=d.relative(u.workspaceRoot,u.targetPath).replace(/\\/g,"/");await e({filePath:w,operation:c?"UPDATE":"CREATE",beforeContent:l,afterContent:a,reason:s})}return{ok:!0,filePath:$(u.targetPath),bytes:u.bytes,createdParentDirectory:u.createdParentDirectory}}}}function Ht(n,e,o){return{kind:"builtin",toolName:"edit_file",exposedName:"edit_file",description:"Replace exactly one occurrence of old_string with new_string in a UTF-8 workspace text file.",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Workspace-relative file path to edit."},old_string:{type:"string",description:"Existing text to replace. Must occur exactly once."},new_string:{type:"string",description:"Replacement text."},reason:{type:"string",description:"Optional reason for this change, e.g. what bug is being fixed. Helps humans understand the change later."}},required:["file_path","old_string","new_string"],additionalProperties:!1},async execute(t){const i=M(t,["file_path","filePath"],"file_path"),a=M(t,["old_string","oldString"],"old_string"),s=M(t,["new_string","newString"],"new_string"),r=R(t,["reason"]);if(!a)throw new Error("old_string must not be empty");const c=await B(n,i);await N(o,"fileModification",{operation:"edit",path:c.targetPath});const l=await Ft(n,i),u=Ot(l.content,a);if(u===0)throw new Error("old_string was not found in file");if(u>1)throw new Error(`old_string occurs ${u} times; provide a unique match`);const w=l.content.replace(a,s),g=await et(n,i,w);if(e){const _=d.relative(g.workspaceRoot,g.targetPath).replace(/\\/g,"/");await e({filePath:_,operation:"UPDATE",beforeContent:l.content,afterContent:w,reason:r})}return{ok:!0,filePath:$(g.targetPath),bytes:g.bytes}}}}function Kt(n,e,o){return{kind:"builtin",toolName:"delete_file",exposedName:"delete_file",description:"Delete a file inside the current workspace. Returns an error if the file does not exist.",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Workspace-relative file path to delete."},reason:{type:"string",description:"Optional reason for this change, e.g. what bug is being fixed. Helps humans understand the change later."}},required:["file_path"],additionalProperties:!1},async execute(t){const i=M(t,["file_path","filePath"],"file_path"),a=R(t,["reason"]),s=await B(n,i);await N(o,"fileModification",{operation:"delete",path:s.targetPath});const r=O(s.workspaceRoot,await p.realpath(s.targetPath).catch(()=>s.targetPath)),c=await p.stat(r).catch(()=>null);if(!c)throw new Error(`File does not exist: ${r}`);if(!c.isFile())throw new Error(`Path is not a file: ${r}`);const l=await p.readFile(r,"utf-8").catch(()=>null);if(await p.rm(r,{force:!0}),e){const u=d.relative(s.workspaceRoot,r).replace(/\\/g,"/");await e({filePath:u,operation:"DELETE",beforeContent:l,afterContent:null,reason:a})}return{ok:!0,filePath:$(r),deleted:!0}}}}function Vt(n,e){return{kind:"builtin",toolName:"read_file",exposedName:"read_file",description:"Read a UTF-8 text file inside the current workspace. Returns content with line numbers in the format '<line>: <content>'. Supports offset (1-indexed line number to start from) and limit (max lines to return, default 2000). Output is capped at 2000 lines or 50 KB, whichever is reached first. Individual lines longer than 2000 characters are truncated. Avoid tiny repeated slices (e.g. 30-line chunks); read a larger window if more context is needed. Images (.png/.jpg/.jpeg/.gif/.webp) and http(s) image URLs are also supported: the image is inlined into the conversation so you can see it (requires a model with image input). offset/limit are ignored for images. By default (compress='auto') images are compressed before inlining using a tiered ladder that judges by the compressed result, not the original size: lossless first (pixel-exact, dimensions preserved), then high-quality lossy downscaling only if the lossless result is still over 50 KB (normal level), then more aggressive downscaling if still over 100 KB (clear level). When compression applies, the result reports the compression level, compressed size, and compression ratio (e.g. saved 80%). Pass compress=true to also attempt compression, or compress=false to disable it (images exceeding the hard 2000px / 3.75MB inline limits are still auto-downscaled when sharp is available).",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Workspace-relative file path to read. Also accepts an http(s) URL pointing to an image."},offset:{type:"number",description:"Line number to start reading from (1-indexed). Defaults to 1."},limit:{type:"number",description:"Maximum number of lines to return. Defaults to 2000."},compress:{anyOf:[{type:"boolean"},{type:"string",enum:["auto"]}],description:'Image compression mode (ignored for text files). "auto" (default): compress with a tiered ladder driven by the compressed result \u2014 lossless, then high-quality downscale if still >50 KB, then more aggressive if still >100 KB. true: same as auto. false: skip tiered compression entirely (hard inline limits still apply).'}},required:["file_path"],additionalProperties:!1},async execute(o){const t=M(o,["file_path","filePath"],"file_path"),i=d.extname(t).toLowerCase().slice(1);if(z(t)||Et.has(i))return await Dt(n,t,e,At(o));const a=U(o,["offset"],1),s=U(o,["limit"],yt),r=await Ct(n,t,a,s,e);return{ok:!0,filePath:$(r.targetPath),content:r.content,isBinary:r.isBinary,truncated:r.truncated,totalLines:r.totalLines,shownFromLine:r.shownFromLine,shownToLine:r.shownToLine}}}}function Jt(n,e,o,t){return{kind:"builtin",toolName:"Bash",exposedName:"Bash",description:"Execute a shell command in the current workspace. Use for local diagnostics, builds, tests, and OS commands. The command runs with a bounded total timeout (timeout_ms) measured from command start \u2014 by default 30 minutes, after which the process is force-terminated; timeout_ms can raise this (up to 12 hours) or set 0 to disable. The tool returns stdout, stderr, exit code, and (if converted to background) a task_id. By default (wait_seconds omitted or -1) the command runs in the FOREGROUND and streams output in real time; if the 5-minute adapter watchdog fires while it is still running, the command is moved to the BACKGROUND and the tool returns a task_id plus an explanation (backgrounded=true, reason=watchdog_timeout). The background command stays bound by the same total timeout (timeout_ms, default 30 minutes, still measured from command start) and can be managed via await_complete / list_background_commands / read_background_output / kill_background_command. For immediate backgrounding use wait_seconds=0, or wait_seconds=n>0 to wait n seconds then background.",inputSchema:{type:"object",properties:{command:{type:"string",description:"Shell command to execute."},cwd:{type:"string",description:"Optional workspace-relative working directory. Defaults to the workspace root."},timeout_ms:{type:"number",description:"Optional total command timeout in milliseconds, measured from command start. Defaults to 1800000 (30 minutes) and is capped at 43200000 (12 hours). Set 0 to disable the timeout entirely (run until finished or killed)."},inactivity_timeout_ms:{type:"number",description:"Optional no-output timeout in milliseconds. Defaults to 60000 and is capped at 600000."},wait_seconds:{type:"number",description:"How to wait for the command to finish. Omitted values (or -1, the default): run in the FOREGROUND until completion; if the 5-minute adapter watchdog fires first, the command is moved to the background and a task_id is returned (backgrounded=true, reason=watchdog_timeout). 0: spawn and immediately execute in the background, return task_id. n>0: wait up to n seconds; if still running, move to the background and return task_id. The command remains bound by the total timeout (timeout_ms, default 30 minutes, measured from command start) whether foreground or background; inactivity_timeout_ms is ignored once backgrounded.",minimum:-1},description:{type:"string",description:"Short reason for running this command (what it does and why it is needed), shown to the user in the background commands panel. Always provide a concise reason, e.g. 'Start local dev server for manual verification'."}},required:["command"],additionalProperties:!1},async execute(i,a){const s=M(i,["command"],"command").trim();if(!s)throw new Error("command must not be empty");const r=Lt(i,["timeout_ms","timeoutMs"],jt),c=r===0?0:Math.min(r,qt),l=U(i,["inactivity_timeout_ms","inactivityTimeoutMs"],Gt),u=Math.min(l,Xt),w=R(i,["cwd"]),g=R(i,["description"]),_=await Bt(n,w);await N(t,"commandExecution",{command:s,cwd:$(_.cwd)});const y=i.wait_seconds,P=typeof y=="number"&&Number.isFinite(y)?y:-1;if(o)return await Zt({deps:o,command:s,agentId:e,workspaceRoot:_.workspaceRoot,cwd:_.cwd,timeoutMs:c,inactivityTimeoutMs:u,waitSeconds:P,description:g,onOutput:a?.onOutput,settleSignal:a?.settleSignal});const h=await at({commandText:s,agentId:e,workspaceRoot:_.workspaceRoot,cwd:_.cwd,timeoutMs:c,inactivityTimeoutMs:u,onOutput:a?.onOutput,watchdogSignal:a?.settleSignal}),v={ok:h.exitCode===0&&!h.timedOut,tool:"Bash",command:s,cwd:$(_.cwd),exitCode:h.exitCode,signal:h.signal,timedOut:h.timedOut,inactivityTimedOut:h.inactivityTimedOut,watchdogInterrupted:h.watchdogInterrupted,timeoutMs:c,inactivityTimeoutMs:u,durationMs:h.durationMs,outputSpilled:h.outputSpilled,outputFilePath:h.outputFilePath,outputLineCount:h.outputLineCount,commandFilePath:h.commandFilePath,commandLineCount:h.commandLineCount,stdout:h.stdout,stderr:h.stderr};return h.watchdogInterrupted?{...v,message:"Command was interrupted by the adapter watchdog after ~5 minutes of synchronous (foreground) execution. Only the partial output produced so far is returned above"+(h.outputFilePath?` (full output spilled to "${h.outputFilePath}"; read it if you need more)`:"")+". For long-running commands, re-run with wait_seconds=0 (or n>0) to execute in the background, then manage via await_complete / list_background_commands / read_background_output / kill_background_command."}:v}}}async function Zt(n){const{deps:e,command:o,agentId:t,workspaceRoot:i,cwd:a,timeoutMs:s,inactivityTimeoutMs:r,waitSeconds:c,description:l,onOutput:u,settleSignal:w}=n,g=u?rt({cwd:a,commandLineCount:0,onOutput:u}):void 0;let _=!!g;const y=()=>{_&&(_=!1,g?.close())};try{const{taskId:m,completion:P}=e.manager.startCommand({commandText:o,agentId:t,workspaceRoot:i,cwd:a,description:l,timeoutMs:s,inactivityTimeoutMs:r,onOutput:_?T=>{_&&g?.push(T.stream,T.text)}:void 0,onCompleted:e.emitCompleted});if(c===0)return e.manager.disableInactivityTimeout(t,m),await e.manager.markBackgrounded(t,m).catch(()=>{}),y(),{ok:!0,tool:"Bash",command:o,backgrounded:!0,taskId:m,status:"backgrounded",reason:"immediate",message:`Command moved to background. Use await_complete(bg_cmd_ids=["${m}"]) to block until it finishes, read_background_output(task_id="${m}", mode="tail") to read output, list_background_commands to view all, or kill_background_command(task_id="${m}") to stop.`};const h=c===-1?0:Math.max(0,Math.floor(c*1e3)),v=[P];let E;c!==-1&&v.push(new Promise(T=>{E=setTimeout(()=>T("__timeout__"),h),E.unref?.()})),w&&v.push(w.then(()=>"__settle__"));try{const T=await Promise.race(v);if(T==="__settle__")return e.manager.disableInactivityTimeout(t,m),await e.manager.markBackgrounded(t,m).catch(()=>{}),{ok:!0,tool:"Bash",command:o,backgrounded:!0,taskId:m,status:"backgrounded",reason:"watchdog_timeout",waitedMs:h,message:`Command still running after watchdog timeout; moved to background. Use await_complete(bg_cmd_ids=["${m}"]) to block until it finishes, read_background_output(task_id="${m}", mode="tail") to read output, or kill_background_command(task_id="${m}") to stop.`};if(T==="__timeout__")return e.manager.disableInactivityTimeout(t,m),await e.manager.markBackgrounded(t,m).catch(()=>{}),{ok:!0,tool:"Bash",command:o,backgrounded:!0,taskId:m,status:"backgrounded",reason:"timeout",waitedMs:h,message:`Command still running after ${c}s; moved to background. Use await_complete(bg_cmd_ids=["${m}"]) to block until it finishes, or read_background_output(task_id="${m}", mode="tail") / kill_background_command(task_id="${m}") to inspect.`};const f=T;let b=f.stdout,x=f.stderr,k=f.outputSpilled,F=f.outputFilePath;if(!k&&H(`${f.stdout}${f.stderr}`)>ct){const I=await ut(t,"cmd","out",st({commandText:o,commandFilePath:f.commandFilePath,commandLineCount:f.commandLineCount,cwd:a,startedAt:new Date(Date.now()-f.durationMs),endedAt:new Date,exitCode:f.exitCode,signal:f.signal??null,timedOut:f.timedOut,inactivityTimedOut:f.inactivityTimedOut??!1,stdout:b,stderr:x}));b=lt($(I),H(`${f.stdout}${f.stderr}`)),k=!0,F=$(I)}return{ok:f.exitCode===0&&!f.timedOut,tool:"Bash",command:o,backgrounded:!1,taskId:m,exitCode:f.exitCode,signal:f.signal,timedOut:f.timedOut,inactivityTimedOut:f.inactivityTimedOut,timeoutMs:f.timeoutMs,inactivityTimeoutMs:f.inactivityTimeoutMs,durationMs:f.durationMs,outputSpilled:k,outputFilePath:F,outputLineCount:f.outputLineCount,commandFilePath:f.commandFilePath,commandLineCount:f.commandLineCount,stdout:b,stderr:x}}finally{E&&clearTimeout(E),y()}}catch(m){if(m?.code==="too_many_tasks"||m?.code==="global_limit_reached")return{ok:!1,tool:"Bash",command:o,backgrounded:!1,status:"not_executed",error:m.code,message:`Command was NOT executed: ${m.message}. Wait for some background tasks to finish/cleanup (per-agent limit 16, global 256), or reduce concurrent background commands, then retry.`};throw m}}function Qt(n){return{kind:"builtin",toolName:"grep",exposedName:"grep",description:"Search file contents using regular expressions within the workspace. Returns matched lines with file paths and line numbers. Supports include/exclude glob patterns, case-insensitive matching, and three output modes (content, files_with_matches, count). Searches recursively through directories, skipping binary files, hidden directories, and common build/dependency directories (node_modules, .git, dist, etc.).",inputSchema:{type:"object",properties:{pattern:{type:"string",description:"Regular expression pattern to search for in file contents."},path:{type:"string",description:"Workspace-relative file or directory path to search in. Defaults to the workspace root."},include:{type:"string",description:"Glob pattern to filter files by name (e.g. '*.ts', '*.{js,ts}'). Only matching files are searched."},exclude:{type:"string",description:"Glob pattern to exclude files by name (e.g. '*.test.ts'). Matching files are skipped."},case_insensitive:{type:"boolean",description:"Case-insensitive matching. Default false."},output_mode:{type:"string",enum:["content","files_with_matches","count"],description:"Output format: 'content' returns matched lines with line numbers (default), 'files_with_matches' returns only file paths with matches, 'count' returns match counts per file."},max_results:{type:"number",description:"Maximum number of matched lines to return (content mode). Default 1000."}},required:["pattern"],additionalProperties:!1},async execute(e){const o=M(e,["pattern"],"pattern"),t=R(e,["path"])||".",i=R(e,["include"]),a=R(e,["exclude"]),s=e.case_insensitive===!0,r=(()=>{const k=e.output_mode;return k==="files_with_matches"||k==="count"?k:"content"})(),c=U(e,["max_results"],Rt),l=s?"i":"";let u;try{u=new RegExp(o,l)}catch(k){throw new Error(`Invalid regex pattern: ${k instanceof Error?k.message:String(k)}`)}const w=i?ot(i):null,g=a?ot(a):null,_=await B(n,t),y=O(_.workspaceRoot,await p.realpath(_.targetPath)),m=await p.stat(y);let P;if(m.isFile())P=[y];else if(m.isDirectory())P=await Wt(y,w,g,St);else throw new Error(`Path is neither a file nor a directory: ${y}`);const h=[],v=[],E=[];let T=0,f=0,b=!1;for(const k of P){if(r==="content"&&h.length>=c){b=!0;break}const F={regex:u,maxResults:r==="content"?c-h.length:Number.MAX_SAFE_INTEGER,collectContent:r==="content",stopOnFirstMatch:r==="files_with_matches"},I=await zt(k,_.workspaceRoot,F);if(I.count>0){const q=d.relative(_.workspaceRoot,k).replace(/\\/g,"/");if(E.push(q),T+=I.count,r==="content"){for(const j of I.matches){const G=Buffer.byteLength(j.content,"utf-8")+64;if(f+G>Mt){b=!0;break}f+=G,h.push(j)}if(b)break;if(h.length>=c){b=!0;break}}else r==="count"&&v.push({file:q,count:I.count})}}const x={ok:!0,tool:"grep",pattern:o,searchPath:$(_.targetPath),outputMode:r,filesSearched:P.length,filesMatched:E.length,totalMatches:T,truncated:b};return r==="content"?x.matches=h:r==="files_with_matches"?x.matches=E:x.matches=v,x}}}function me(n,e,o,t,i){const a=e||"unknown",s=[Vt(n,i),Yt(n,o,i),Ht(n,o,i),Kt(n,o,i),Qt(n),Jt(n,a,t,i),dt(n)];return t&&s.push(mt(a,t),ht(a,t),ft(a,t)),s}export{de as DEFAULT_TOOL_PERMISSION_POLICY,me as createBuiltInFileTools,O as ensureInsideWorkspace,B as resolveWorkspaceTarget};
|
|
4
4
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import P from"node:fs";import x from"node:os";import
|
|
1
|
+
import P from"node:fs";import x from"node:os";import g from"node:path";import{execFile as X}from"node:child_process";import{fileURLToPath as Z}from"node:url";import{Server as Q}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as ee}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as te,ListToolsRequestSchema as re}from"@modelcontextprotocol/sdk/types.js";import{AgentClient as se,PollLongPollTimeoutError as ie}from"./agent-client.js";import{normalizeServerHttpBase as ne}from"./http-client.js";import{MCP_SERVER_NAME as oe,MCP_SERVER_VERSION as ae,REGISTER_MAX_RETRIES as D,REGISTER_RETRY_INTERVAL_MS as le,DEGRADED_MODE_ENABLED as H,DEGRADED_MODE_RETRY_INTERVAL_MS as ce,DEGRADED_MODE_MAX_RETRIES as me}from"./constants.js";import{resolveAcodeDataHome as U}from"./config.js";import{logger as l}from"./logger.js";import{meetingDebugFingerprints as O,meetingDebugLog as I}from"./meeting-debug-log.js";import{MCP_TOOLS as j,MEMORY_TOOL_NAMES as W,LAUNCH_CONFIG_TOOL_NAMES as q,HIRE_TOOL_NAMES as z,PENCIL_TOOL_NAMES as G}from"./mcp-tools.js";import{MessageBuffer as ue}from"./message-buffer.js";import{ContextManager as de}from"./context-manager.js";import{MemoryCapacityError as he,MemoryStore as L}from"./memory-store.js";import{ServerMemoryStore as pe}from"./server-memory-store.js";const ge=3e3,F=10,Y=(()=>{const C=Number.parseInt(process.env.AWS_MCP_FILE_CHANGE_RENOTIFY_COOLDOWN_MS||"",10);return Number.isFinite(C)&&C>0?C:30*6e4})(),K=500,fe=30,ye=new Set(["<",">",":",'"',"/","\\","|","?","*"]),J=20*1024*1024,_e={".pdf":"application/pdf",".txt":"text/plain",".md":"text/markdown",".csv":"text/csv",".json":"application/json",".html":"text/html",".xml":"application/xml",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation",".zip":"application/zip",".gz":"application/gzip",".tar":"application/x-tar",".7z":"application/x-7z-compressed",".rar":"application/vnd.rar",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".svg":"image/svg+xml",".mp3":"audio/mpeg",".mp4":"video/mp4"};function Me(C){return _e[g.extname(C).toLowerCase()]||"application/octet-stream"}function Se(C){return(C||"").replace(/[|[\]\r\n]/g,"_").trim()||"\u6587\u4EF6"}function we(C){if(!Number.isFinite(C)||C<=0)return"0 B";const e=["B","KB","MB","GB"],t=Math.min(e.length-1,Math.floor(Math.log(C)/Math.log(1024)));return`${Number.parseFloat((C/1024**t).toFixed(2))} ${e[t]}`}function ve(C){return`[\u6587\u4EF6: ${Se(C.name)} | ${C.url} | ${C.mediaType||"application/octet-stream"} | ${we(C.size)}]`}function Ee(C){return P.existsSync(g.join(C,"packages","cli","dist","index.mjs"))||P.existsSync(g.join(C,"packages","cli","src","index.ts"))}function Te(C){const e=[],t=String(process.env.AWS_OPEN_PENCIL_HOME||"").trim();t&&e.push(g.resolve(t));const r=g.resolve(C);e.push(g.join(r,"open-pencil")),e.push(g.resolve(Z(new URL("../../../../open-pencil",import.meta.url))));for(let s=g.dirname(r);e.push(g.join(s,"open-pencil")),g.dirname(s)!==s;s=g.dirname(s));return{home:e.find(Ee)??e[0],searched:e}}class E{server;agentClient;messageBuffer;memoryStore;roleMemoryStore;sessionContextMemoryStore;initialRoleName="";hasRoleName=!1;contextManager;isSubAgent;fileChangeNotifyHistory=new Map;featuresCache=null;static FEATURES_CACHE_TTL_MS=5e3;static LAUNCH_ITEM_FEATURE_KEY="agentSys.launchItem";static LAUNCH_TOOLS_ENV_KEY="AWS_LAUNCH_CONFIG_TOOLS";static HIRE_FEATURE_KEY="agentSys.hire";static HIRE_TOOLS_ENV_KEY="AWS_HIRE_TOOLS";static PENCIL_FEATURE_KEY="pencil";static PENCIL_TOOLS_ENV_KEY="AWS_PENCIL_TOOLS";leaseRenewalTimer=null;activeLeaseUris=new Set;static LEASE_DURATION_MINUTES=10;static LEASE_RENEW_INTERVAL_MS=6e4;profileSynced=!1;urgentMeetingHydrateRequested=!1;cachedPluginCatalog=null;deliveredPluginNames=new Set;pluginCatalogDrainCompactionSeq=0;pluginCatalogLastSeenCompactionSeq=0;static MEETING_HYDRATE_RETRY_COUNT=3;static MEETING_HYDRATE_RETRY_DELAY_MS=150;static MEETING_HYDRATE_TIMEOUT_REARM_DELAY_MS=1e4;meetingHydrateTimeoutRearmDelayMs=E.MEETING_HYDRATE_TIMEOUT_REARM_DELAY_MS;static MEETING_HYDRATE_POLL_TIMEOUT_MS=65e3;static MEETING_PREEMPTIVE_JOIN_WAIT_MS=45e3;pendingPollMessage=null;syncRoleMemoryStore(e){if(l.info(`[McpServer] syncRoleMemoryStore \u88AB\u8C03\u7528: displayName=${e.displayName}, roleName="${e.roleName??"(\u7A7A)"}", id=${e.id}`),!e.roleName){l.warn(`[McpServer] syncRoleMemoryStore: profile.roleName \u4E3A\u7A7A\uFF0ChasRoleName \u4FDD\u6301 false\u3002profile=${JSON.stringify({id:e.id,displayName:e.displayName,roleName:e.roleName,instanceName:e.instanceName})}`);return}const t=E.sanitizeRoleDirectoryName(e.roleName);this.hasRoleName=!0,t!==this.initialRoleName&&(l.info(`[McpServer] \u89D2\u8272\u76EE\u5F55\u4E0E\u521D\u59CB\u503C\u4E0D\u4E00\u81F4\uFF0C\u91CD\u5EFA roleMemoryStore: "${this.initialRoleName}" -> "${t}"`),this.roleMemoryStore=new L(E.buildRoleMemoryStorePath(t)),this.initialRoleName=t)}maybeSyncPermanentMemoryToServer(){this.doMaybeSyncPermanentMemoryToServer().catch(e=>{l.warn("[McpServer] maybeSyncPermanentMemoryToServer \u5F02\u5E38: %s, stack=%s",e instanceof Error?e.message:String(e),e instanceof Error?e.stack:"(no stack)")})}async doMaybeSyncPermanentMemoryToServer(){l.info("[McpServer] maybeSyncPermanentMemoryToServer \u5F00\u59CB\uFF0CinitialRoleName="+this.initialRoleName);let e;try{e=await this.memoryStore.getStats()}catch(n){l.warn("[McpServer] \u83B7\u53D6 server \u8BB0\u5FC6\u7EDF\u8BA1\u5931\u8D25\uFF0C\u8DF3\u8FC7\u540C\u6B65:",n instanceof Error?n.message:n);return}const t=e.by_type.project_memory?.count??0;l.info(`[McpServer] server memory \u8BA1\u6570=${t}`);let r=[];try{r=await this.roleMemoryStore.listByType("project_memory"),l.info(`[McpServer] \u89D2\u8272\u5B50\u76EE\u5F55\u8BFB\u53D6\u5230 ${r.length} \u6761\u6C38\u4E45\u8BB0\u5FC6`)}catch(n){l.warn("[McpServer] \u8BFB\u53D6\u89D2\u8272\u5B50\u76EE\u5F55\u6C38\u4E45\u8BB0\u5FC6\u5931\u8D25:",n instanceof Error?n.message:n)}if(r.length===0){const n=g.dirname(E.buildRoleMemoryStorePath(this.initialRoleName));l.info(`[McpServer] \u5C1D\u8BD5\u4ECE\u9057\u7559\u76EE\u5F55\u8BFB\u53D6: ${n}`);try{const a=await new L(n).listByType("project_memory");a.length>0&&(l.info(`[McpServer] \u4ECE\u9057\u7559\u76EE\u5F55\u8BFB\u53D6\u5230 ${a.length} \u6761\u6C38\u4E45\u8BB0\u5FC6`),r=a)}catch(o){l.warn("[McpServer] \u8BFB\u53D6\u9057\u7559\u76EE\u5F55\u6C38\u4E45\u8BB0\u5FC6\u5931\u8D25:",o instanceof Error?o.message:o)}}if(r.length===0){l.info("[McpServer] \u672C\u5730\u4E5F\u65E0\u6C38\u4E45\u8BB0\u5FC6\uFF0C\u8DF3\u8FC7\u540C\u6B65");return}if(t>=r.length){l.info(`[McpServer] \u670D\u52A1\u7AEF\u5DF2\u6709 ${t} \u6761 >= \u672C\u5730 ${r.length} \u6761\uFF0C\u65E0\u9700\u56DE\u586B`);return}l.info(`[McpServer] \u68C0\u6D4B\u5230\u7F3A\u91CF\uFF1A\u672C\u5730 ${r.length} \u6761 > \u670D\u52A1\u7AEF ${t} \u6761\uFF0C\u5F00\u59CB\u589E\u91CF\u56DE\u586B`);let i=0,s=0;for(const n of r)try{await this.memoryStore.createMemory({memory_type:"project_memory",uri:n.uri,title:n.title,content:n.content,keywords:n.keywords,bound_files:n.bound_files,file_md5_map:n.file_md5_map,last_check_time:n.last_check_time}),i++,l.info(`[McpServer] \u540C\u6B65\u6C38\u4E45\u8BB0\u5FC6\u6210\u529F: ${n.uri}`)}catch(o){const a=o instanceof Error?o.message:String(o);a.includes("already exists")||a.includes("Memory already exists")||l.warn(`[McpServer] \u540C\u6B65\u6C38\u4E45\u8BB0\u5FC6\u5931\u8D25 (${n.uri}): ${a}`),s++}l.info(`[McpServer] \u6C38\u4E45\u8BB0\u5FC6\u540C\u6B65\u5B8C\u6210: \u6210\u529F ${i}, \u8DF3\u8FC7 ${s}, \u5171 ${r.length}`),await this.reportMemoryStatsToServer()}normalizeBoundFilePath(e){const t=String(e||"").trim().replace(/\\/g,"/");if(!t)return t;const r=t.startsWith("@/")?t.substring(1):t;return r.startsWith("./")?r:`./${r.replace(/^\/+/,"")}`}normalizeBoundFiles(e){return!e||!Array.isArray(e)?e:e.map(t=>this.normalizeBoundFilePath(String(t)))}async validateBoundFilesExist(e){if(!e||e.length===0)return;const t=process.env.AWS_WORKSPACE_PATH||process.cwd(),{access:r}=await import("node:fs/promises"),i=[];for(const s of e)try{const n=g.resolve(t,s);await r(n)}catch{i.push(s)}if(i.length>0)throw new Error(`\u7ED1\u5B9A\u7684\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8BF7\u786E\u8BA4\u6587\u4EF6\u8DEF\u5F84\u6B63\u786E\u540E\u518D\u91CD\u65B0\u63D0\u4EA4:
|
|
2
2
|
${i.map(s=>` - ${s}`).join(`
|
|
3
|
-
`)}`)}toMemoryWriteResult(e){return{id:e.id,uri:e.uri,domain:e.domain,path:e.path,title:e.title,memory_type:e.memory_type,createdAt:e.createdAt,updatedAt:e.updatedAt,version:e.version,keywords:e.keywords,bound_files:e.bound_files}}toMemoryRecordWithMetadata(e){return{id:e.id,uri:e.uri,domain:e.domain,path:e.path,title:e.title,content:e.content,memory_type:e.memory_type,createdAt:e.createdAt,updatedAt:e.updatedAt,version:e.version,keywords:e.keywords,bound_files:e.bound_files,file_md5_map:e.file_md5_map,memory_status:e.memory_status,updating_agent_id:e.updating_agent_id,last_check_time:e.last_check_time,path_prefix:e.path_prefix,readonly:e.readonly,source_agent_id:e.source_agent_id,source_display_name:e.source_display_name,source_role_name:e.source_role_name,source_workspace_path:e.source_workspace_path}}constructor(){this.agentClient=new se,this.messageBuffer=new ue,this.initialRoleName=T.resolveRoleMemoryDirectoryName(),this.hasRoleName=!!process.env.AWS_ROLE_NAME;const e=T.resolveMemoryStorePaths();this.memoryStore=new pe(this.agentClient),this.roleMemoryStore=new L(e.rolePath),this.sessionContextMemoryStore=new L(_.join(_.dirname(e.rolePath),"..","session_context")),this.isSubAgent=process.env.AWS_SUB_AGENT==="true",this.contextManager=new de(this.sessionContextMemoryStore),this.server=new Q({name:oe,version:ae},{capabilities:{tools:{}}}),this.setupHandlers()}setupHandlers(){this.server.setRequestHandler(re,async()=>{let e=this.isSubAgent?F.filter(t=>W.has(t.name)):F;return!this.isSubAgent&&!await this.isLaunchConfigToolsEnabled()&&(e=e.filter(t=>!q.has(t.name))),!this.isSubAgent&&!await this.isHireToolsEnabled()&&(e=e.filter(t=>!z.has(t.name))),!this.isSubAgent&&!await this.isPencilToolsEnabled()&&(e=e.filter(t=>!G.has(t.name))),{tools:e}}),this.server.setRequestHandler(te,async(e,t)=>{const{name:r,arguments:i}=e.params;try{const s=await this.handleToolCall(r,i,t?.signal);return{content:this.buildToolResultContent(s)}}catch(s){const n=s instanceof Error?s.message:String(s),o=s instanceof he?{error:n,...s.details}:{error:n};return l.error("[McpServer] \u5DE5\u5177\u8C03\u7528\u5F02\u5E38: tool=%s, error=%s, stack=%s",r,n,s instanceof Error?s.stack:"(no stack)"),{content:[{type:"text",text:JSON.stringify(o,null,2)}],isError:!0}}})}async handlePencilExportImage(e){const t=typeof e.savePath=="string"?e.savePath.trim():"",r=await this.agentClient.callAgentScopedTool("pencil_export_image",e),i=typeof r?.image=="string"?r.image:"";if(!i)return r;const s=i.startsWith("data:")?i.slice(i.indexOf(",")+1):i,n={...r};delete n.image,delete n.mimeType,n.sizeKb=Math.ceil(s.length*3/4/1024);try{const{mkdir:o,writeFile:a}=await import("node:fs/promises");let m;if(t){const d=_.resolve(process.env.AWS_WORKSPACE_PATH||process.cwd());m=_.resolve(d,t)}else{const p=(typeof r?.docId=="string"?r.docId:"").replace(/[^a-zA-Z0-9_-]/g,"").slice(0,40)||"doc",u=_.join(x.tmpdir(),"pencil-export");await o(u,{recursive:!0}),m=_.join(u,`${p}-${Date.now()}-${Math.random().toString(36).slice(2,8)}.png`)}return await o(_.dirname(m),{recursive:!0}),await a(m,Buffer.from(s,"base64")),{...n,savedTo:m,temp:!t}}catch(o){return{...n,savedTo:null,saveError:o instanceof Error?o.message:String(o)}}}static PENCIL_EXPORT_FORMATS=new Set(["svg","html","png","jpg","webp","pptx","fig","jsx"]);async handlePencilExportDocument(e){const t=typeof e.format=="string"&&e.format.trim()?e.format.trim().toLowerCase():"svg";if(!T.PENCIL_EXPORT_FORMATS.has(t))throw new Error(`\u4E0D\u652F\u6301\u7684\u5BFC\u51FA\u683C\u5F0F: ${t}\uFF08\u652F\u6301 svg/html/png/jpg/webp/pptx/fig/jsx\uFF1Bpdf \u6682\u4E0D\u652F\u6301\uFF09`);const r=typeof e.nodeId=="string"&&e.nodeId.trim()?e.nodeId.trim():"";if(t==="jsx"&&!r)throw new Error("jsx \u5BFC\u51FA\u9700\u8981\u4F20 nodeId\uFF08\u6765\u81EA pencil_get_tree\uFF09\uFF0C\u5B83\u6309\u5355\u8282\u70B9\u751F\u6210 React \u7EC4\u4EF6");const i=typeof e.savePath=="string"?e.savePath.trim():"",s=_.resolve(process.env.AWS_WORKSPACE_PATH||process.cwd()),n=await this.agentClient.callAgentScopedTool("pencil_export_document",e),o=typeof n?.snapshotJson=="string"?n.snapshotJson:"";if(!o)return n;const{mkdir:a,writeFile:m,stat:d}=await import("node:fs/promises"),u=(typeof n?.docId=="string"?n.docId:"").replace(/[^a-zA-Z0-9_-]/g,"").slice(0,40)||"doc",M=`${Date.now()}-${Math.random().toString(36).slice(2,8)}`,R=_.join(x.tmpdir(),"pencil-export");await a(R,{recursive:!0});const h=_.join(R,`${u}-${M}.snapshot.json`);await m(h,o,"utf8");const w=t,c=i?_.resolve(s,i):_.join(R,`${u}-${M}.${w}`),y=_.resolve(process.env.AWS_OPEN_PENCIL_HOME||Z(new URL("../../../../open-pencil",import.meta.url))),g=[{cmd:process.execPath,entry:_.join(y,"packages","cli","dist","index.mjs")},{cmd:"bun",entry:_.join(y,"packages","cli","src","index.ts")}].filter(E=>P.existsSync(E.entry));if(g.length===0)throw new Error(`\u672A\u627E\u5230 open-pencil CLI\uFF08\u67E5\u627E\u76EE\u5F55 ${y}\uFF09\uFF0C\u8BF7\u786E\u8BA4\u5B89\u88C5\u5E03\u5C40\u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF AWS_OPEN_PENCIL_HOME \u6307\u5411 open-pencil \u4ED3\u5E93\u6839\u76EE\u5F55`);const S=["export",h,"--snapshot","-f",t,"-o",c];r&&S.push("--node",r),t==="html"&&S.push("--html","standalone"),(t==="png"||t==="jpg"||t==="webp")&&(typeof e.scale=="number"&&e.scale>0&&S.push("--scale",String(e.scale)),typeof e.quality=="number"&&e.quality>0&&S.push("--quality",String(e.quality)));const v=[];let k=!1;for(const E of g)try{await this.runPencilCli(E.cmd,[E.entry,...S],y),k=!0;break}catch(A){v.push(`${_.basename(E.cmd)}: ${A instanceof Error?A.message:String(A)}`)}if(!k)throw new Error(`\u753B\u5E03\u5BFC\u51FA\u5931\u8D25\uFF08format=${t}\uFF09\uFF1A${v.join("\uFF1B")||"\u672A\u77E5\u9519\u8BEF"}`);let f=null;try{f=Math.ceil((await d(c)).size/1024)}catch{f=null}const b={...n};return delete b.snapshotJson,{...b,format:t,savedTo:c,temp:!i,sizeKb:f}}runPencilCli(e,t,r){return new Promise((i,s)=>{X(e,t,{cwd:r,timeout:18e4,maxBuffer:16*1024*1024},(n,o,a)=>{if(n){const m=String(a||o||"").trim().split(/\r?\n/).filter(Boolean).slice(-3).join(" | ");s(new Error(m||n.message));return}i(String(o||""))})})}stripRenderImageForReporting(e){if(e&&typeof e=="object"&&!Array.isArray(e)){const t=e;let r=null;if(typeof t.image=="string"){const s=t.image.length,n=Math.ceil(s*3/4/1024);r={...t,image:`<PNG base64 \u5DF2\u7701\u7565\uFF0C\u7EA6 ${n}KB\uFF0C\u6B63\u6587\u89C1 t_pencil_doc.render_image>`}}const i=r??t;if(typeof i.snapshotJson=="string"){const s=Math.ceil(i.snapshotJson.length/1024);r={...i,snapshotJson:`<GraphSnapshot JSON \u5DF2\u7701\u7565\uFF0C\u7EA6 ${s}KB\uFF0C\u6743\u5A01\u6570\u636E\u89C1 t_pencil_doc.snapshot>`}}if(r)return r}return e}buildToolResultContent(e){if(e&&typeof e=="object"&&!Array.isArray(e)){const t=e;if(typeof t.image=="string"&&typeof t.mimeType=="string"){const r={...t};delete r.image;const i=[{type:"image",data:t.image,mimeType:t.mimeType}];return Object.keys(r).length>0&&i.push({type:"text",text:JSON.stringify(r,null,2)}),i}}return[{type:"text",text:JSON.stringify(e,null,2)}]}async handleToolCall(e,t,r){const i=t?._aws_sub_agent===!0,s=t?._aws_silent_poll===!0,n=t?._aws_force_inject===!0,o={...t};delete o._aws_sub_agent,delete o._aws_silent_poll,delete o._aws_force_inject;const a=i?null:this.agentClient.getStatusReporter();a&&!s&&this.safeReportStatus(()=>a.reportToolUse(e,this.sanitizeToolUseArgs(e,o)));try{const m=await this.executeToolCall(e,o,{forceInject:n,signal:r});if(a&&!s){const d=this.stripAutoDelegateForReporting(m);d!==null&&(this.safeReportStatus(()=>a.reportToolResult(this.stripRenderImageForReporting(d))),this.safeReportStatus(()=>a.reportThinking()))}return m}catch(m){throw a&&!s&&this.safeReportStatus(()=>a.reportToolError(m)),m}}safeReportStatus(e){try{e()}catch(t){l.warn("[McpServer] \u72B6\u6001\u4E0A\u62A5\u5931\u8D25\uFF08\u5DF2\u5FFD\u7565\uFF0C\u4E0D\u5F71\u54CD\u5DE5\u5177\u8C03\u7528\uFF09: %s",t instanceof Error?t.message:String(t))}}async executeToolCall(e,t,r={}){if(this.isSubAgent&&!W.has(e))throw new Error(`Sub-agent is not allowed to call tool: ${e}`);if(q.has(e)&&!await this.isLaunchConfigToolsEnabled())throw new Error("\u542F\u52A8\u9879\u7BA1\u7406\u80FD\u529B\u672A\u5F00\u542F\uFF08\u8BF7\u5148\u5728\u80FD\u529B\u914D\u7F6E\u4E2D\u5F00\u542F\u300CAgent \u7CFB\u7EDF\u7BA1\u7406 \u2192 \u542F\u52A8\u9879\u7BA1\u7406\u300D\u540E\u518D\u8C03\u7528 launch_config_* \u5DE5\u5177\uFF09");if(z.has(e)&&!await this.isHireToolsEnabled())throw new Error("\u62DB\u8058\u5DE5\u5177\u80FD\u529B\u672A\u5F00\u542F\uFF08\u8BF7\u5148\u5728\u80FD\u529B\u914D\u7F6E\u4E2D\u5F00\u542F\u300CAgent \u7CFB\u7EDF\u7BA1\u7406 \u2192 \u62DB\u8058\u5DE5\u5177\u300D\u540E\u518D\u8C03\u7528 hire_colleague\uFF09");if(G.has(e)&&!await this.isPencilToolsEnabled())throw new Error("\u8BBE\u8BA1\u753B\u5E03\u80FD\u529B\u672A\u5F00\u542F\uFF08\u8BF7\u5148\u5728\u80FD\u529B\u914D\u7F6E\u4E2D\u5F00\u542F\u300C\u8BBE\u8BA1\u753B\u5E03 (Pencil)\u300D\u540E\u518D\u8C03\u7528 pencil_* \u5DE5\u5177\uFF09");switch(e){case"get_colleague":return await this.agentClient.discoverColleagues();case"get_group_rooms":return await this.agentClient.discoverGroupRooms();case"get_profile":return await this.agentClient.getProfile();case"hire_colleague":return await this.handleHireColleague(t);case"send_group":return await this.handleSendGroup(t);case"send_private":return await this.handleSendPrivate(t);case"poll_message":return await this.handlePollMessage(r.signal);case"get_message":return this.handleGetMessage(r.forceInject===!0);case"get_dm_history":return await this.handleGetDmHistory(t);case"get_group_history":return await this.handleGetGroupHistory(t);case"create_meeting":case"leave_meeting":case"end_meeting":case"meeting_speak":case"get_meeting_info":case"my_meetings":return await this.agentClient.callAgentScopedTool(e,t);case"pencil_create_document":case"pencil_list_documents":case"pencil_open_document":case"pencil_close_document":case"pencil_get_tree":case"pencil_get_changes":case"pencil_create_nodes":case"pencil_update_nodes":case"pencil_delete_nodes":case"pencil_share_document":case"pencil_revoke_share":case"pencil_delete_document":case"pencil_rename_document":return await this.agentClient.callAgentScopedTool(e,t);case"pencil_export_image":return await this.handlePencilExportImage(t);case"pencil_export_document":return await this.handlePencilExportDocument(t);case"my_task":return await this.agentClient.getMyTasks();case"create_task_instance_comment":return await this.handleCreateTaskInstanceComment(t);case"submit_task_result":return await this.handleSubmitTaskResult(t);case"start_task_rollback":return await this.handleStartTaskRollback(t);case"finish_task_rollback":return await this.handleFinishTaskRollback(t);case"reject_task":return await this.handleRejectTask(t);case"terminate_task":return await this.handleTerminateTask(t);case"create_memory":return await this.handleCreateMemory(t);case"read_memory":return await this.handleReadMemory(t);case"update_memory":return await this.handleUpdateMemory(t);case"delete_memory":return await this.handleDeleteMemory(t);case"search_memory":return await this.handleSearchMemory(t);case"list_memories":return await this.handleListMemories(t);case"get_memory_stats":return await this.handleGetMemoryStats(t);case"list_memories_by_file":return await this.handleListMemoriesByFile(t);case"abandon_memory_update":return await this.handleAbandonMemoryUpdate(t);case"memory_recurrence":return await this.handleMemoryRecurrence(t);case"memory_recurrence_complete":return await this.handleMemoryRecurrenceComplete(t);case"get_debug_log_config":return await this.agentClient.getDebugLogConfig();case"query_debug_logs":return await this.agentClient.queryDebugLogs(t);case"query_file_changes_history":return await this.agentClient.queryFileChangesHistory(t);case"launch_config_list":return await this.agentClient.listLaunchConfigs();case"launch_config_create":return await this.agentClient.createLaunchConfig(t);case"launch_config_update":return await this.agentClient.updateLaunchConfig(t);case"launch_config_delete":return await this.agentClient.deleteLaunchConfig(String(t?.configId??""));case"launch_config_start":return await this.agentClient.startLaunchConfig(t);case"launch_config_restart":return await this.agentClient.restartLaunchConfig(t);case"launch_config_stop":return await this.agentClient.stopLaunchConfig(String(t?.configId??""));case"launch_config_logs":return await this.agentClient.getLaunchLogs(t);case"launch_config_status":return await this.agentClient.listLaunchRuns();default:throw new Error(`Unknown tool: ${e}`)}}async isLaunchConfigToolsEnabled(){const{features:e,ok:t}=await this.fetchRuntimeFeaturesWithStatus();return t&&Object.keys(e).length>0?e[T.LAUNCH_ITEM_FEATURE_KEY]===!0:process.env[T.LAUNCH_TOOLS_ENV_KEY]==="1"}async isHireToolsEnabled(){const{features:e,ok:t}=await this.fetchRuntimeFeaturesWithStatus();return t&&Object.keys(e).length>0?e[T.HIRE_FEATURE_KEY]===!0:process.env[T.HIRE_TOOLS_ENV_KEY]==="1"}async isPencilToolsEnabled(){const{features:e,ok:t}=await this.fetchRuntimeFeaturesWithStatus();return t&&Object.keys(e).length>0?e[T.PENCIL_FEATURE_KEY]===!0:process.env[T.PENCIL_TOOLS_ENV_KEY]==="1"}async fetchRuntimeFeaturesWithStatus(){const e=Date.now();if(this.featuresCache&&e-this.featuresCache.fetchedAt<T.FEATURES_CACHE_TTL_MS)return{features:this.featuresCache.features,ok:this.featuresCache.ok};let t={},r=!1;try{t=await this.agentClient.getRuntimeFeatures(),r=!0}catch(i){l.warn("[McpServer] \u83B7\u53D6\u80FD\u529B\u5F00\u5173\u5931\u8D25\uFF0C\u6309\u542F\u52A8\u671F env \u515C\u5E95: %s",i instanceof Error?i.message:String(i))}return this.featuresCache={features:t,ok:r,fetchedAt:e},{features:t,ok:r}}static MEMORY_TYPES=["recently_memory","agent_memory","project_memory","session_context"];static MEMORY_TOOL_NAMES=new Set(["create_memory","read_memory","update_memory","delete_memory","search_memory","list_memories","get_memory_stats","list_memories_by_file","abandon_memory_update","memory_recurrence","memory_recurrence_complete"]);static findAgentsworkDirSync(e){let t=_.resolve(e);for(;;){if(P.existsSync(_.join(t,".agentswork")))return _.join(t,".agentswork");const r=_.dirname(t);if(r===t)break;t=r}return _.join(_.resolve(e),".agentswork")}static sanitizeRoleDirectoryName(e){return String(e||"").trim().split("").map(r=>ye.has(r)||r.charCodeAt(0)<32?"_":r).join("").replace(/^\.+|\.+$/g,"").trim()||"default-role"}static resolveRoleMemoryDirectoryName(){return T.sanitizeRoleDirectoryName(process.env.AWS_ROLE_NAME||"")}static buildRoleMemoryStorePath(e){const t=process.env.AWS_WORKSPACE_PATH||process.cwd(),r=T.findAgentsworkDirSync(t);return _.join(r,"memory","memory",T.sanitizeRoleDirectoryName(e))}static resolveMemoryStorePaths(){const e=String(process.env.AWS_MEMORY_STORE_PATH||"").trim();if(e)return{instancePath:e,rolePath:e};const t=process.env.AWS_WORKSPACE_PATH||process.cwd(),r=T.findAgentsworkDirSync(t),i=String(process.env.AWS_INSTANCE_MEMORY_STORE_PATH||"").trim()||_.join(r,"memory","server-managed"),s=String(process.env.AWS_ROLE_MEMORY_STORE_PATH||"").trim()||_.join(r,"memory","memory",T.resolveRoleMemoryDirectoryName());return{instancePath:i,rolePath:s}}getRequiredString(e,t){const r=e[t];if(typeof r!="string"||r.trim().length===0)throw new Error(`${t} is required`);return r}getOptionalString(e,t){const r=e[t];return typeof r=="string"?r:void 0}getOptionalNumber(e,t){const r=e[t];return typeof r=="number"?r:void 0}getOptionalStringArray(e,t){const r=e[t];return Array.isArray(r)?r.map(i=>String(i)):void 0}getOptionalMemoryType(e){const t=e.memory_type;if(t!==void 0)return this.parseMemoryType(t)}parseMemoryType(e){if(typeof e!="string")throw new Error(`Invalid memory_type: ${String(e)}`);const t=e.trim();if(!this.isMemoryType(t))throw new Error(`Invalid memory_type: ${e}`);return t}isMemoryType(e){return T.MEMORY_TYPES.some(t=>t===e)}getMemoryStoreForType(e){return this.memoryStore}async readMemoryFromScopedStores(e){if(e.memory_type)return await this.memoryStore.readMemory(e);const t=await this.memoryStore.readMemory({...e,memory_type:"project_memory"}).catch(()=>null);return t||await this.memoryStore.readMemory(e)}async backupProjectMemoryToLocal(e){try{const t=await this.memoryStore.readMemory({uri:e.uri,memory_type:"project_memory"});t&&(await this.roleMemoryStore.deleteMemory({uri:e.uri,memory_type:"project_memory"}).catch(()=>{}),await this.roleMemoryStore.createMemory(t))}catch(t){l.warn("[McpServer] backupProjectMemoryToLocal \u5931\u8D25: uri=%s",e.uri,t)}}async removeProjectMemoryLocalBackup(e){try{await this.roleMemoryStore.deleteMemory({uri:e,memory_type:"project_memory"})}catch(t){l.warn("[McpServer] removeProjectMemoryLocalBackup \u5931\u8D25: uri=%s",e,t)}}async findMemoryStoreForMutation(e){return e.memory_type?{store:this.memoryStore,memory_type:e.memory_type}:await this.memoryStore.readMemory({uri:e.uri,memory_type:"project_memory"}).catch(()=>null)?{store:this.memoryStore,memory_type:"project_memory"}:{store:this.memoryStore}}async reportMemoryStatsToServer(){const e=await this.memoryStore.getStats();try{await this.agentClient.reportMemoryStats(e)}catch(t){l.warn("[McpServer] \u4E0A\u62A5 memory \u7EDF\u8BA1\u5931\u8D25:",t)}}sanitizeToolUseArgs(e,t){if(!T.MEMORY_TOOL_NAMES.has(e))return t;const r={},i=this.getOptionalString(t,"uri"),s=this.getOptionalString(t,"memory_type"),n=this.getOptionalNumber(t,"limit");return i&&(r.uri=i),s&&(r.memory_type=s),typeof n=="number"&&(r.limit=n),e==="search_memory"&&(r.queryProvided=typeof t.query=="string"&&t.query.length>0,t.keyword_mode!==void 0&&(r.keyword_mode=t.keyword_mode)),r}stripAutoDelegateForReporting(e){if(!e||typeof e!="object")return e;const t=e,r=Array.isArray(t.directMessages)?t.directMessages:[],i=Array.isArray(t.groupMessages)?t.groupMessages:[],s=a=>!!a&&typeof a=="object"&&"_acodeAutoDelegate"in a;if(!r.some(s))return e;const o=r.filter(a=>!s(a));return o.length===0&&i.length===0?null:{...t,directMessages:o}}async handleCreateMemory(e){const t=this.normalizeBoundFiles(this.getOptionalStringArray(e,"bound_files"));await this.validateBoundFilesExist(t);let r,i;if(t&&t.length>0){r={};for(const o of t){const a=await this.calculateFileMd5(o);a&&(r[o]=a)}i=new Date().toISOString()}const s={memory_type:this.parseMemoryType(this.getRequiredString(e,"memory_type")),uri:this.getRequiredString(e,"uri"),title:this.getOptionalString(e,"title"),content:this.getRequiredString(e,"content"),keywords:this.getOptionalStringArray(e,"keywords"),bound_files:t,file_md5_map:r,last_check_time:i},n=await this.memoryStore.createMemory(s);return s.memory_type==="project_memory"&&this.backupProjectMemoryToLocal(s).catch(o=>{l.warn("[McpServer] \u5907\u4EFD project_memory \u5230\u672C\u5730\u5931\u8D25:",o)}),await this.reportMemoryStatsToServer(),this.toMemoryWriteResult(n)}async handleReadMemory(e){const t={uri:this.getRequiredString(e,"uri"),memory_type:this.getOptionalMemoryType(e)};return await this.readMemoryFromScopedStores(t)}async handleUpdateMemory(e){const t=typeof e.old_string=="string"||typeof e.new_string=="string",r=typeof e.old_string=="string"&&typeof e.new_string=="string",i=this.getOptionalString(e,"append");if(t&&!r)throw new Error("old_string and new_string must be provided together");const s={uri:this.getRequiredString(e,"uri"),memory_type:this.getOptionalMemoryType(e),title:this.getOptionalString(e,"title"),old_string:this.getOptionalString(e,"old_string"),new_string:this.getOptionalString(e,"new_string"),append:i,keywords:this.getOptionalStringArray(e,"keywords"),bound_files:this.normalizeBoundFiles(this.getOptionalStringArray(e,"bound_files"))},n=await this.findMemoryStoreForMutation(s),o=await n.store.readMemory({uri:s.uri,memory_type:n.memory_type??s.memory_type});s.bound_files!==void 0?await this.validateBoundFilesExist(s.bound_files):o?.bound_files&&o.bound_files.length>0&&await this.validateBoundFilesExist(o.bound_files);const a=s.bound_files??o?.bound_files;let m,d;if(a&&a.length>0){m={};for(const u of a){const M=await this.calculateFileMd5(u);M&&(m[u]=M)}d=new Date().toISOString()}const p=await n.store.updateMemory({...s,memory_type:n.memory_type??s.memory_type,file_md5_map:m,last_check_time:d});if(n.memory_type==="project_memory"&&this.backupProjectMemoryToLocal({...s,memory_type:"project_memory",file_md5_map:m,last_check_time:d}).catch(u=>{l.warn("[McpServer] \u5907\u4EFD project_memory \u66F4\u65B0\u5230\u672C\u5730\u5931\u8D25:",u)}),await this.reportMemoryStatsToServer(),n.memory_type==="project_memory"&&o?.memory_status==="updating")try{await this.memoryStore.releaseMemoryClaim(s.uri),this.activeLeaseUris.delete(s.uri),this.activeLeaseUris.size===0&&this.stopLeaseRenewal()}catch(u){l.warn("[McpServer] \u91CA\u653E memory claim \u5931\u8D25: uri=%s, error=%s",s.uri,u instanceof Error?u.message:String(u))}return this.toMemoryWriteResult(p)}async handleDeleteMemory(e){const t=typeof e.uri=="string"?e.uri:void 0,r=Array.isArray(e.memories)?e.memories:void 0;if(t&&r&&r.length>0)throw new Error("delete_memory \u4E0D\u652F\u6301\u540C\u65F6\u4F20\u5165 uri \u548C memories\uFF0C\u8BF7\u4E8C\u9009\u4E00");if(!t&&(!r||r.length===0))throw new Error("delete_memory \u9700\u8981 uri \u6216 memories \u53C2\u6570");if(t){const o=this.getOptionalMemoryType(e);return{deleted:await this.deleteMemorySingle(t,o)}}let i=0;const s=[],n=[];for(let o=0;o<r.length;o++){const a=r[o];if(typeof a!="object"||a===null){n.push({uri:String(a),reason:`memories[${o}] \u4E0D\u662F\u5BF9\u8C61`});continue}const m=a,d=typeof m.uri=="string"?m.uri:"";if(!d){n.push({uri:"",reason:`memories[${o}].uri \u7F3A\u5931`});continue}const p=typeof m.memory_type=="string"?this.parseMemoryType(m.memory_type):void 0;try{await this.deleteMemorySingle(d,p,!0)?i++:s.push(d)}catch(u){n.push({uri:d,reason:u instanceof Error?u.message:String(u)})}}return i>0&&await this.reportMemoryStatsToServer(),{deleted:i,not_found:s,failed:n}}async deleteMemorySingle(e,t,r=!1){const i={uri:e,memory_type:t},s=await this.findMemoryStoreForMutation(i),n=s.memory_type??i.memory_type,o=await s.store.deleteMemory({...i,memory_type:n});return o&&(n==="project_memory"&&this.removeProjectMemoryLocalBackup(e).catch(a=>{l.warn("[McpServer] \u5220\u9664 project_memory \u672C\u5730\u5907\u4EFD\u5931\u8D25:",a)}),r||await this.reportMemoryStatsToServer()),o}async handleSearchMemory(e){const t=this.getOptionalString(e,"scope"),r=t==="children"||t==="all"?t:"own",i=this.getOptionalString(e,"keyword_mode"),s=i==="all"?"all":i==="any"?"any":void 0,n={query:this.getRequiredString(e,"query"),limit:this.getOptionalNumber(e,"limit"),memory_type:this.getOptionalMemoryType(e),keywords:this.getOptionalStringArray(e,"keywords"),keyword_mode:s,scope:r};if(r==="children")return await this.memoryStore.searchMemory(n);const o=await this.searchOwnMemories(n);if(r==="own")return o;const a=await this.memoryStore.searchMemory({...n,scope:"children"});return this.mergeSearchResults(o,a,n.limit)}async searchOwnMemories(e){return await this.memoryStore.searchMemory(e)}mergeSearchResults(e,t,r){const i=new Set,s=typeof r=="number"&&r>0?r:10,n=[...e.results,...t.results].filter(o=>{const a=o.uri;return i.has(a)?!1:(i.add(a),!0)}).sort((o,a)=>a.updatedAt.localeCompare(o.updatedAt)).slice(0,s);return{query:e.query||t.query,results:n}}async handleListMemories(e){const t=this.getRequiredString(e,"memory_type"),r=this.getOptionalNumber(e,"offset"),i=this.getOptionalNumber(e,"limit"),s=Math.max(0,Math.floor(r??0)),n=i!==void 0&&i>=1?Math.floor(i):fe,o=await this.memoryStore.listByType(t),a=o.length,m=o.slice(s,s+n);return{memory_type:t,total:a,offset:s,limit:n,has_more:s+n<a,memories:m}}async handleGetMemoryStats(e){const t=await this.memoryStore.getStats(),r=this.getOptionalMemoryType(e);return r?{memory_type:r,total:t.by_type[r].count,by_type:{[r]:t.by_type[r]}}:t}async handleListMemoriesByFile(e){const t=this.getRequiredString(e,"file_path"),r=this.normalizeBoundFilePath(t),i=this.getOptionalMemoryType(e),s=this.getOptionalString(e,"scope"),n=s==="own"||s==="children"?s:"all",o=async()=>await this.memoryStore.listByFile(r,{memory_type:i,scope:"children"}).catch(()=>[]);if(n==="children"){const u=await o();return{file_path:r,count:u.length,memories:u.map(M=>this.toMemoryRecordWithMetadata(M))}}if(n==="own"){const u=await this.collectOwnMemoriesByFile(r,i);return{file_path:r,count:u.length,memories:u.map(M=>this.toMemoryRecordWithMetadata(M))}}const[a,m,d]=await Promise.all([this.memoryStore.listByFile(r,{memory_type:i,scope:"all"}).catch(()=>[]),this.searchChildMemoriesByFileLocal(r,i).catch(()=>[]),this.searchPeerMemoriesByFileLocal(r,i).catch(()=>[])]),p=this.mergeIdDedupe(this.mergeIdDedupe(a,m),d);return{file_path:r,count:p.length,memories:p.map(u=>this.toMemoryRecordWithMetadata(u))}}mergeIdDedupe(e,t){const r=new Set;for(const s of e)s.id&&r.add(s.id);const i=[...e];for(const s of t)s.id&&!r.has(s.id)&&(i.push(s),r.add(s.id));return i.sort((s,n)=>n.updatedAt.localeCompare(s.updatedAt)),i}async searchChildMemoriesByFileLocal(e,t){const r=process.env.AWS_WORKSPACE_PATH||process.cwd(),i=this.normalizeBoundFilePath(e),n=(i.startsWith("./")?i.substring(2):i).split("/"),o=[],a=new Set,{access:m,readdir:d,readFile:p}=await import("node:fs/promises");for(let u=n.length-1;u>=1;u--){const M=n.slice(0,u),h="./"+n.slice(u).join("/"),w=_.join(r,...M);if(w===r)continue;const c=_.join(w,".agentswork");try{await m(c)}catch{continue}const y=_.join(c,"memory","memory");let g;try{g=(await d(y,{withFileTypes:!0})).filter(v=>v.isDirectory()).map(v=>v.name)}catch{continue}for(const S of g){const v=_.join(y,S);let k;try{k=await d(v)}catch{continue}for(const f of k){if(!f.endsWith(".json"))continue;const b=_.join(v,f);let E;try{E=await p(b,"utf-8")}catch{continue}let A;try{A=JSON.parse(E)}catch{continue}if(A.by_uri)for(const[N,$]of Object.entries(A.by_uri)){if(!$)continue;const B=$.id||N;a.has(B)||t&&$.memory_type!==t||$.bound_files&&$.bound_files.length>0&&$.bound_files.some(V=>this.normalizeBoundFilePath(V)===h)&&(a.add(B),o.push({...$,uri:$.uri||N}))}}}}return o.sort((u,M)=>String(M.updatedAt??"").localeCompare(String(u.updatedAt??""))),o}async searchPeerMemoriesByFileLocal(e,t){if(!this.hasRoleName||!this.initialRoleName)return[];const r=process.env.AWS_WORKSPACE_PATH||process.cwd(),i=this.normalizeBoundFilePath(e),s=_.join(r,".agentswork","memory","memory"),{access:n,readdir:o,readFile:a}=await import("node:fs/promises");try{await n(s)}catch{return[]}let m;try{m=(await o(s,{withFileTypes:!0})).filter(M=>M.isDirectory()).map(M=>M.name)}catch{return[]}const d=[],p=new Set;for(const u of m){if(u===this.initialRoleName)continue;const M=_.join(s,u);let R;try{R=await o(M)}catch{continue}for(const h of R){if(!h.endsWith(".json"))continue;let w;try{w=JSON.parse(await a(_.join(M,h),"utf-8"))}catch{continue}if(w.by_uri)for(const[c,y]of Object.entries(w.by_uri)){if(!y)continue;const g=y.id||c;p.has(g)||y.memory_type==="project_memory"&&(t&&y.memory_type!==t||y.bound_files&&y.bound_files.length>0&&y.bound_files.some(v=>this.normalizeBoundFilePath(v)===i)&&(p.add(g),d.push({...y,uri:y.uri||c,readonly:!0,source_role_name:u})))}}}return d.sort((u,M)=>String(M.updatedAt??"").localeCompare(String(u.updatedAt??""))),d}async collectOwnMemoriesByFile(e,t){return await this.memoryStore.listByFile(e,{memory_type:t})}async handleAbandonMemoryUpdate(e){const t=this.getRequiredString(e,"uri"),r=this.getOptionalMemoryType(e),i=await this.memoryStore.readMemory({uri:t,memory_type:r});return i?.bound_files&&i.bound_files.length>0&&await this.validateBoundFilesExist(i.bound_files),await this.memoryStore.releaseMemoryClaim(t),i?.memory_type==="project_memory"&&this.backupProjectMemoryToLocal({uri:t,memory_type:"project_memory"}).catch(s=>{l.info(`[McpServer] abandon_memory_update: \u540C\u6B65\u672C\u5730\u5907\u4EFD\u5931\u8D25: ${t}`,s)}),this.activeLeaseUris.delete(t),this.activeLeaseUris.size===0&&this.stopLeaseRenewal(),{success:!0,uri:t,message:"Memory \u66F4\u65B0\u72B6\u6001\u5DF2\u6E05\u9664"}}async handleMemoryRecurrence(e){const t=Math.max(1,e?.limit??10);try{((await this.memoryStore.markExpiredMemoriesRecalling()).uris?.length??0)>0&&this.startLeaseRenewal()}catch(a){l.warn("[McpServer] \u539F\u5B50\u8BA4\u9886\u8FC7\u671F\u8BB0\u5FC6\u5931\u8D25: %s",a instanceof Error?a.message:String(a))}const r=await this.memoryStore.listExpiredRecentlyMemories();if(r.length===0)return{memories:[],count:0,limit:t,remaining:0,totalExpired:0,message:"\u6CA1\u6709\u5F85\u56DE\u5FC6\u7684\u5373\u5C06\u9057\u5FD8\u8BB0\u5FC6\u3002"};const s=[...r].sort((a,m)=>{const d=new Date(a.expiresAt||a.createdAt||0).getTime(),p=new Date(m.expiresAt||m.createdAt||0).getTime();return d-p}).slice(0,t),n=Math.max(0,r.length-s.length);return{memories:s.map(a=>({uri:a.uri,title:a.title,domain:a.domain,path:a.path,memory_status:"recalling",contentPreview:typeof a.content=="string"&&a.content.length>200?a.content.substring(0,200)+"...":a.content,createdAt:a.createdAt,updatedAt:a.updatedAt})),count:s.length,limit:t,remaining:n,totalExpired:r.length,message:n>0?`\u672C\u6B21\u5904\u7406 ${s.length} \u6761\u5373\u5C06\u9057\u5FD8\u7684\u8BB0\u5FC6\uFF08\u6700\u65E9\u8FC7\u671F\u4F18\u5148\uFF09\uFF0C\u5269\u4F59 ${n} \u6761\u5F85\u5904\u7406\u3002
|
|
3
|
+
`)}`)}toMemoryWriteResult(e){return{id:e.id,uri:e.uri,domain:e.domain,path:e.path,title:e.title,memory_type:e.memory_type,createdAt:e.createdAt,updatedAt:e.updatedAt,version:e.version,keywords:e.keywords,bound_files:e.bound_files}}toMemoryRecordWithMetadata(e){return{id:e.id,uri:e.uri,domain:e.domain,path:e.path,title:e.title,content:e.content,memory_type:e.memory_type,createdAt:e.createdAt,updatedAt:e.updatedAt,version:e.version,keywords:e.keywords,bound_files:e.bound_files,file_md5_map:e.file_md5_map,memory_status:e.memory_status,updating_agent_id:e.updating_agent_id,last_check_time:e.last_check_time,path_prefix:e.path_prefix,readonly:e.readonly,source_agent_id:e.source_agent_id,source_display_name:e.source_display_name,source_role_name:e.source_role_name,source_workspace_path:e.source_workspace_path}}constructor(){this.agentClient=new se,this.messageBuffer=new ue,this.initialRoleName=E.resolveRoleMemoryDirectoryName(),this.hasRoleName=!!process.env.AWS_ROLE_NAME;const e=E.resolveMemoryStorePaths();this.memoryStore=new pe(this.agentClient),this.roleMemoryStore=new L(e.rolePath),this.sessionContextMemoryStore=new L(g.join(g.dirname(e.rolePath),"..","session_context")),this.isSubAgent=process.env.AWS_SUB_AGENT==="true",this.contextManager=new de(this.sessionContextMemoryStore),this.server=new Q({name:oe,version:ae},{capabilities:{tools:{}}}),this.setupHandlers()}setupHandlers(){this.server.setRequestHandler(re,async()=>{let e=this.isSubAgent?j.filter(t=>W.has(t.name)):j;return!this.isSubAgent&&!await this.isLaunchConfigToolsEnabled()&&(e=e.filter(t=>!q.has(t.name))),!this.isSubAgent&&!await this.isHireToolsEnabled()&&(e=e.filter(t=>!z.has(t.name))),!this.isSubAgent&&!await this.isPencilToolsEnabled()&&(e=e.filter(t=>!G.has(t.name))),{tools:e}}),this.server.setRequestHandler(te,async(e,t)=>{const{name:r,arguments:i}=e.params;try{const s=await this.handleToolCall(r,i,t?.signal);return{content:this.buildToolResultContent(s)}}catch(s){const n=s instanceof Error?s.message:String(s),o=s instanceof he?{error:n,...s.details}:{error:n};return l.error("[McpServer] \u5DE5\u5177\u8C03\u7528\u5F02\u5E38: tool=%s, error=%s, stack=%s",r,n,s instanceof Error?s.stack:"(no stack)"),{content:[{type:"text",text:JSON.stringify(o,null,2)}],isError:!0}}})}async handlePencilExportImage(e){const t=typeof e.savePath=="string"?e.savePath.trim():"",r=await this.agentClient.callAgentScopedTool("pencil_export_image",e),i=typeof r?.image=="string"?r.image:"";if(!i)return r;const s=i.startsWith("data:")?i.slice(i.indexOf(",")+1):i,n={...r};delete n.image,delete n.mimeType,n.sizeKb=Math.ceil(s.length*3/4/1024);try{const{mkdir:o,writeFile:a}=await import("node:fs/promises");let m;if(t){const d=g.resolve(process.env.AWS_WORKSPACE_PATH||process.cwd());m=g.resolve(d,t)}else{const p=(typeof r?.docId=="string"?r.docId:"").replace(/[^a-zA-Z0-9_-]/g,"").slice(0,40)||"doc",u=g.join(x.tmpdir(),"pencil-export");await o(u,{recursive:!0}),m=g.join(u,`${p}-${Date.now()}-${Math.random().toString(36).slice(2,8)}.png`)}return await o(g.dirname(m),{recursive:!0}),await a(m,Buffer.from(s,"base64")),{...n,savedTo:m,temp:!t}}catch(o){return{...n,savedTo:null,saveError:o instanceof Error?o.message:String(o)}}}static PENCIL_EXPORT_FORMATS=new Set(["svg","html","png","jpg","webp","pptx","fig","jsx"]);async handlePencilExportDocument(e){const t=typeof e.format=="string"&&e.format.trim()?e.format.trim().toLowerCase():"svg";if(!E.PENCIL_EXPORT_FORMATS.has(t))throw new Error(`\u4E0D\u652F\u6301\u7684\u5BFC\u51FA\u683C\u5F0F: ${t}\uFF08\u652F\u6301 svg/html/png/jpg/webp/pptx/fig/jsx\uFF1Bpdf \u6682\u4E0D\u652F\u6301\uFF09`);const r=typeof e.nodeId=="string"&&e.nodeId.trim()?e.nodeId.trim():"";if(t==="jsx"&&!r)throw new Error("jsx \u5BFC\u51FA\u9700\u8981\u4F20 nodeId\uFF08\u6765\u81EA pencil_get_tree\uFF09\uFF0C\u5B83\u6309\u5355\u8282\u70B9\u751F\u6210 React \u7EC4\u4EF6");const i=typeof e.savePath=="string"?e.savePath.trim():"",s=g.resolve(process.env.AWS_WORKSPACE_PATH||process.cwd()),n=await this.agentClient.callAgentScopedTool("pencil_export_document",e),o=typeof n?.snapshotJson=="string"?n.snapshotJson:"";if(!o)return n;const{mkdir:a,writeFile:m,stat:d}=await import("node:fs/promises"),u=(typeof n?.docId=="string"?n.docId:"").replace(/[^a-zA-Z0-9_-]/g,"").slice(0,40)||"doc",M=`${Date.now()}-${Math.random().toString(36).slice(2,8)}`,R=g.join(x.tmpdir(),"pencil-export");await a(R,{recursive:!0});const h=g.join(R,`${u}-${M}.snapshot.json`);await m(h,o,"utf8");const w=t,c=i?g.resolve(s,i):g.join(R,`${u}-${M}.${w}`),{home:_,searched:f}=Te(s),v=[{cmd:process.execPath,entry:g.join(_,"packages","cli","dist","index.mjs"),cwd:_},{cmd:"bun",entry:g.join(_,"packages","cli","src","index.ts"),cwd:_}].filter(A=>P.existsSync(A.entry));if(v.length===0)throw new Error(`\u672A\u627E\u5230 open-pencil CLI\uFF08\u5DF2\u67E5\u627E\u76EE\u5F55\uFF1A${f.join("\uFF1B")}\uFF09\uFF0C\u8BF7\u786E\u8BA4\u5B89\u88C5\u5E03\u5C40\u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF AWS_OPEN_PENCIL_HOME \u6307\u5411 open-pencil \u4ED3\u5E93\u6839\u76EE\u5F55`);const S=["export",h,"--snapshot","-f",t,"-o",c];r&&S.push("--node",r),t==="html"&&S.push("--html","standalone"),(t==="png"||t==="jpg"||t==="webp")&&(typeof e.scale=="number"&&e.scale>0&&S.push("--scale",String(e.scale)),typeof e.quality=="number"&&e.quality>0&&S.push("--quality",String(e.quality)));const k=[];let y=!1;for(const A of v)try{await this.runPencilCli(A.cmd,[A.entry,...S],A.cwd),y=!0;break}catch($){k.push(`${g.basename(A.cmd)}: ${$ instanceof Error?$.message:String($)}`)}if(!y)throw new Error(`\u753B\u5E03\u5BFC\u51FA\u5931\u8D25\uFF08format=${t}\uFF09\uFF1A${k.join("\uFF1B")||"\u672A\u77E5\u9519\u8BEF"}`);let b=null;try{b=Math.ceil((await d(c)).size/1024)}catch{b=null}const T={...n};return delete T.snapshotJson,{...T,format:t,savedTo:c,temp:!i,sizeKb:b}}runPencilCli(e,t,r){return new Promise((i,s)=>{X(e,t,{cwd:r,timeout:18e4,maxBuffer:16*1024*1024},(n,o,a)=>{if(n){const m=String(a||o||"").trim().split(/\r?\n/).filter(Boolean).slice(-3).join(" | ");s(new Error(m||n.message));return}i(String(o||""))})})}stripRenderImageForReporting(e){if(e&&typeof e=="object"&&!Array.isArray(e)){const t=e;let r=null;if(typeof t.image=="string"){const s=t.image.length,n=Math.ceil(s*3/4/1024);r={...t,image:`<PNG base64 \u5DF2\u7701\u7565\uFF0C\u7EA6 ${n}KB\uFF0C\u6B63\u6587\u89C1 t_pencil_doc.render_image>`}}const i=r??t;if(typeof i.snapshotJson=="string"){const s=Math.ceil(i.snapshotJson.length/1024);r={...i,snapshotJson:`<GraphSnapshot JSON \u5DF2\u7701\u7565\uFF0C\u7EA6 ${s}KB\uFF0C\u6743\u5A01\u6570\u636E\u89C1 t_pencil_doc.snapshot>`}}if(r)return r}return e}buildToolResultContent(e){if(e&&typeof e=="object"&&!Array.isArray(e)){const t=e;if(typeof t.image=="string"&&typeof t.mimeType=="string"){const r={...t};delete r.image;const i=[{type:"image",data:t.image,mimeType:t.mimeType}];return Object.keys(r).length>0&&i.push({type:"text",text:JSON.stringify(r,null,2)}),i}}return[{type:"text",text:JSON.stringify(e,null,2)}]}async handleToolCall(e,t,r){const i=t?._aws_sub_agent===!0,s=t?._aws_silent_poll===!0,n=t?._aws_force_inject===!0,o={...t};delete o._aws_sub_agent,delete o._aws_silent_poll,delete o._aws_force_inject;const a=i?null:this.agentClient.getStatusReporter();a&&!s&&this.safeReportStatus(()=>a.reportToolUse(e,this.sanitizeToolUseArgs(e,o)));try{const m=await this.executeToolCall(e,o,{forceInject:n,signal:r});if(a&&!s){const d=this.stripAutoDelegateForReporting(m);d!==null&&(this.safeReportStatus(()=>a.reportToolResult(this.stripRenderImageForReporting(d))),this.safeReportStatus(()=>a.reportThinking()))}return m}catch(m){throw a&&!s&&this.safeReportStatus(()=>a.reportToolError(m)),m}}safeReportStatus(e){try{e()}catch(t){l.warn("[McpServer] \u72B6\u6001\u4E0A\u62A5\u5931\u8D25\uFF08\u5DF2\u5FFD\u7565\uFF0C\u4E0D\u5F71\u54CD\u5DE5\u5177\u8C03\u7528\uFF09: %s",t instanceof Error?t.message:String(t))}}async executeToolCall(e,t,r={}){if(this.isSubAgent&&!W.has(e))throw new Error(`Sub-agent is not allowed to call tool: ${e}`);if(q.has(e)&&!await this.isLaunchConfigToolsEnabled())throw new Error("\u542F\u52A8\u9879\u7BA1\u7406\u80FD\u529B\u672A\u5F00\u542F\uFF08\u8BF7\u5148\u5728\u80FD\u529B\u914D\u7F6E\u4E2D\u5F00\u542F\u300CAgent \u7CFB\u7EDF\u7BA1\u7406 \u2192 \u542F\u52A8\u9879\u7BA1\u7406\u300D\u540E\u518D\u8C03\u7528 launch_config_* \u5DE5\u5177\uFF09");if(z.has(e)&&!await this.isHireToolsEnabled())throw new Error("\u62DB\u8058\u5DE5\u5177\u80FD\u529B\u672A\u5F00\u542F\uFF08\u8BF7\u5148\u5728\u80FD\u529B\u914D\u7F6E\u4E2D\u5F00\u542F\u300CAgent \u7CFB\u7EDF\u7BA1\u7406 \u2192 \u62DB\u8058\u5DE5\u5177\u300D\u540E\u518D\u8C03\u7528 hire_colleague\uFF09");if(G.has(e)&&!await this.isPencilToolsEnabled())throw new Error("\u8BBE\u8BA1\u753B\u5E03\u80FD\u529B\u672A\u5F00\u542F\uFF08\u8BF7\u5148\u5728\u80FD\u529B\u914D\u7F6E\u4E2D\u5F00\u542F\u300C\u8BBE\u8BA1\u753B\u5E03 (Pencil)\u300D\u540E\u518D\u8C03\u7528 pencil_* \u5DE5\u5177\uFF09");switch(e){case"get_colleague":return await this.agentClient.discoverColleagues();case"get_group_rooms":return await this.agentClient.discoverGroupRooms();case"get_profile":return await this.agentClient.getProfile();case"hire_colleague":return await this.handleHireColleague(t);case"send_group":return await this.handleSendGroup(t);case"send_private":return await this.handleSendPrivate(t);case"poll_message":return await this.handlePollMessage(r.signal);case"get_message":return this.handleGetMessage(r.forceInject===!0);case"get_dm_history":return await this.handleGetDmHistory(t);case"get_group_history":return await this.handleGetGroupHistory(t);case"create_meeting":case"leave_meeting":case"end_meeting":case"meeting_speak":case"get_meeting_info":case"my_meetings":return await this.agentClient.callAgentScopedTool(e,t);case"pencil_create_document":case"pencil_list_documents":case"pencil_open_document":case"pencil_close_document":case"pencil_get_tree":case"pencil_get_changes":case"pencil_create_nodes":case"pencil_update_nodes":case"pencil_delete_nodes":case"pencil_share_document":case"pencil_revoke_share":case"pencil_delete_document":case"pencil_rename_document":return await this.agentClient.callAgentScopedTool(e,t);case"pencil_export_image":return await this.handlePencilExportImage(t);case"pencil_export_document":return await this.handlePencilExportDocument(t);case"my_task":return await this.agentClient.getMyTasks();case"create_task_instance_comment":return await this.handleCreateTaskInstanceComment(t);case"submit_task_result":return await this.handleSubmitTaskResult(t);case"start_task_rollback":return await this.handleStartTaskRollback(t);case"finish_task_rollback":return await this.handleFinishTaskRollback(t);case"reject_task":return await this.handleRejectTask(t);case"terminate_task":return await this.handleTerminateTask(t);case"create_memory":return await this.handleCreateMemory(t);case"read_memory":return await this.handleReadMemory(t);case"update_memory":return await this.handleUpdateMemory(t);case"delete_memory":return await this.handleDeleteMemory(t);case"search_memory":return await this.handleSearchMemory(t);case"list_memories":return await this.handleListMemories(t);case"get_memory_stats":return await this.handleGetMemoryStats(t);case"list_memories_by_file":return await this.handleListMemoriesByFile(t);case"abandon_memory_update":return await this.handleAbandonMemoryUpdate(t);case"memory_recurrence":return await this.handleMemoryRecurrence(t);case"memory_recurrence_complete":return await this.handleMemoryRecurrenceComplete(t);case"get_debug_log_config":return await this.agentClient.getDebugLogConfig();case"query_debug_logs":return await this.agentClient.queryDebugLogs(t);case"query_file_changes_history":return await this.agentClient.queryFileChangesHistory(t);case"launch_config_list":return await this.agentClient.listLaunchConfigs();case"launch_config_create":return await this.agentClient.createLaunchConfig(t);case"launch_config_update":return await this.agentClient.updateLaunchConfig(t);case"launch_config_delete":return await this.agentClient.deleteLaunchConfig(String(t?.configId??""));case"launch_config_start":return await this.agentClient.startLaunchConfig(t);case"launch_config_restart":return await this.agentClient.restartLaunchConfig(t);case"launch_config_stop":return await this.agentClient.stopLaunchConfig(String(t?.configId??""));case"launch_config_logs":return await this.agentClient.getLaunchLogs(t);case"launch_config_status":return await this.agentClient.listLaunchRuns();default:throw new Error(`Unknown tool: ${e}`)}}async isLaunchConfigToolsEnabled(){const{features:e,ok:t}=await this.fetchRuntimeFeaturesWithStatus();return t&&Object.keys(e).length>0?e[E.LAUNCH_ITEM_FEATURE_KEY]===!0:process.env[E.LAUNCH_TOOLS_ENV_KEY]==="1"}async isHireToolsEnabled(){const{features:e,ok:t}=await this.fetchRuntimeFeaturesWithStatus();return t&&Object.keys(e).length>0?e[E.HIRE_FEATURE_KEY]===!0:process.env[E.HIRE_TOOLS_ENV_KEY]==="1"}async isPencilToolsEnabled(){const{features:e,ok:t}=await this.fetchRuntimeFeaturesWithStatus();return t&&Object.keys(e).length>0?e[E.PENCIL_FEATURE_KEY]===!0:process.env[E.PENCIL_TOOLS_ENV_KEY]==="1"}async fetchRuntimeFeaturesWithStatus(){const e=Date.now();if(this.featuresCache&&e-this.featuresCache.fetchedAt<E.FEATURES_CACHE_TTL_MS)return{features:this.featuresCache.features,ok:this.featuresCache.ok};let t={},r=!1;try{t=await this.agentClient.getRuntimeFeatures(),r=!0}catch(i){l.warn("[McpServer] \u83B7\u53D6\u80FD\u529B\u5F00\u5173\u5931\u8D25\uFF0C\u6309\u542F\u52A8\u671F env \u515C\u5E95: %s",i instanceof Error?i.message:String(i))}return this.featuresCache={features:t,ok:r,fetchedAt:e},{features:t,ok:r}}static MEMORY_TYPES=["recently_memory","agent_memory","project_memory","session_context"];static MEMORY_TOOL_NAMES=new Set(["create_memory","read_memory","update_memory","delete_memory","search_memory","list_memories","get_memory_stats","list_memories_by_file","abandon_memory_update","memory_recurrence","memory_recurrence_complete"]);static findAgentsworkDirSync(e){let t=g.resolve(e);for(;;){if(P.existsSync(g.join(t,".agentswork")))return g.join(t,".agentswork");const r=g.dirname(t);if(r===t)break;t=r}return g.join(g.resolve(e),".agentswork")}static sanitizeRoleDirectoryName(e){return String(e||"").trim().split("").map(r=>ye.has(r)||r.charCodeAt(0)<32?"_":r).join("").replace(/^\.+|\.+$/g,"").trim()||"default-role"}static resolveRoleMemoryDirectoryName(){return E.sanitizeRoleDirectoryName(process.env.AWS_ROLE_NAME||"")}static buildRoleMemoryStorePath(e){const t=process.env.AWS_WORKSPACE_PATH||process.cwd(),r=E.findAgentsworkDirSync(t);return g.join(r,"memory","memory",E.sanitizeRoleDirectoryName(e))}static resolveMemoryStorePaths(){const e=String(process.env.AWS_MEMORY_STORE_PATH||"").trim();if(e)return{instancePath:e,rolePath:e};const t=process.env.AWS_WORKSPACE_PATH||process.cwd(),r=E.findAgentsworkDirSync(t),i=String(process.env.AWS_INSTANCE_MEMORY_STORE_PATH||"").trim()||g.join(r,"memory","server-managed"),s=String(process.env.AWS_ROLE_MEMORY_STORE_PATH||"").trim()||g.join(r,"memory","memory",E.resolveRoleMemoryDirectoryName());return{instancePath:i,rolePath:s}}getRequiredString(e,t){const r=e[t];if(typeof r!="string"||r.trim().length===0)throw new Error(`${t} is required`);return r}getOptionalString(e,t){const r=e[t];return typeof r=="string"?r:void 0}getOptionalNumber(e,t){const r=e[t];return typeof r=="number"?r:void 0}getOptionalStringArray(e,t){const r=e[t];return Array.isArray(r)?r.map(i=>String(i)):void 0}getOptionalMemoryType(e){const t=e.memory_type;if(t!==void 0)return this.parseMemoryType(t)}parseMemoryType(e){if(typeof e!="string")throw new Error(`Invalid memory_type: ${String(e)}`);const t=e.trim();if(!this.isMemoryType(t))throw new Error(`Invalid memory_type: ${e}`);return t}isMemoryType(e){return E.MEMORY_TYPES.some(t=>t===e)}getMemoryStoreForType(e){return this.memoryStore}async readMemoryFromScopedStores(e){if(e.memory_type)return await this.memoryStore.readMemory(e);const t=await this.memoryStore.readMemory({...e,memory_type:"project_memory"}).catch(()=>null);return t||await this.memoryStore.readMemory(e)}async backupProjectMemoryToLocal(e){try{const t=await this.memoryStore.readMemory({uri:e.uri,memory_type:"project_memory"});t&&(await this.roleMemoryStore.deleteMemory({uri:e.uri,memory_type:"project_memory"}).catch(()=>{}),await this.roleMemoryStore.createMemory(t))}catch(t){l.warn("[McpServer] backupProjectMemoryToLocal \u5931\u8D25: uri=%s",e.uri,t)}}async removeProjectMemoryLocalBackup(e){try{await this.roleMemoryStore.deleteMemory({uri:e,memory_type:"project_memory"})}catch(t){l.warn("[McpServer] removeProjectMemoryLocalBackup \u5931\u8D25: uri=%s",e,t)}}async findMemoryStoreForMutation(e){return e.memory_type?{store:this.memoryStore,memory_type:e.memory_type}:await this.memoryStore.readMemory({uri:e.uri,memory_type:"project_memory"}).catch(()=>null)?{store:this.memoryStore,memory_type:"project_memory"}:{store:this.memoryStore}}async reportMemoryStatsToServer(){const e=await this.memoryStore.getStats();try{await this.agentClient.reportMemoryStats(e)}catch(t){l.warn("[McpServer] \u4E0A\u62A5 memory \u7EDF\u8BA1\u5931\u8D25:",t)}}sanitizeToolUseArgs(e,t){if(!E.MEMORY_TOOL_NAMES.has(e))return t;const r={},i=this.getOptionalString(t,"uri"),s=this.getOptionalString(t,"memory_type"),n=this.getOptionalNumber(t,"limit");return i&&(r.uri=i),s&&(r.memory_type=s),typeof n=="number"&&(r.limit=n),e==="search_memory"&&(r.queryProvided=typeof t.query=="string"&&t.query.length>0,t.keyword_mode!==void 0&&(r.keyword_mode=t.keyword_mode)),r}stripAutoDelegateForReporting(e){if(!e||typeof e!="object")return e;const t=e,r=Array.isArray(t.directMessages)?t.directMessages:[],i=Array.isArray(t.groupMessages)?t.groupMessages:[],s=a=>!!a&&typeof a=="object"&&"_acodeAutoDelegate"in a;if(!r.some(s))return e;const o=r.filter(a=>!s(a));return o.length===0&&i.length===0?null:{...t,directMessages:o}}async handleCreateMemory(e){const t=this.normalizeBoundFiles(this.getOptionalStringArray(e,"bound_files"));await this.validateBoundFilesExist(t);let r,i;if(t&&t.length>0){r={};for(const o of t){const a=await this.calculateFileMd5(o);a&&(r[o]=a)}i=new Date().toISOString()}const s={memory_type:this.parseMemoryType(this.getRequiredString(e,"memory_type")),uri:this.getRequiredString(e,"uri"),title:this.getOptionalString(e,"title"),content:this.getRequiredString(e,"content"),keywords:this.getOptionalStringArray(e,"keywords"),bound_files:t,file_md5_map:r,last_check_time:i},n=await this.memoryStore.createMemory(s);return s.memory_type==="project_memory"&&this.backupProjectMemoryToLocal(s).catch(o=>{l.warn("[McpServer] \u5907\u4EFD project_memory \u5230\u672C\u5730\u5931\u8D25:",o)}),await this.reportMemoryStatsToServer(),this.toMemoryWriteResult(n)}async handleReadMemory(e){const t={uri:this.getRequiredString(e,"uri"),memory_type:this.getOptionalMemoryType(e)};return await this.readMemoryFromScopedStores(t)}async handleUpdateMemory(e){const t=typeof e.old_string=="string"||typeof e.new_string=="string",r=typeof e.old_string=="string"&&typeof e.new_string=="string",i=this.getOptionalString(e,"append");if(t&&!r)throw new Error("old_string and new_string must be provided together");const s={uri:this.getRequiredString(e,"uri"),memory_type:this.getOptionalMemoryType(e),title:this.getOptionalString(e,"title"),old_string:this.getOptionalString(e,"old_string"),new_string:this.getOptionalString(e,"new_string"),append:i,keywords:this.getOptionalStringArray(e,"keywords"),bound_files:this.normalizeBoundFiles(this.getOptionalStringArray(e,"bound_files"))},n=await this.findMemoryStoreForMutation(s),o=await n.store.readMemory({uri:s.uri,memory_type:n.memory_type??s.memory_type});s.bound_files!==void 0?await this.validateBoundFilesExist(s.bound_files):o?.bound_files&&o.bound_files.length>0&&await this.validateBoundFilesExist(o.bound_files);const a=s.bound_files??o?.bound_files;let m,d;if(a&&a.length>0){m={};for(const u of a){const M=await this.calculateFileMd5(u);M&&(m[u]=M)}d=new Date().toISOString()}const p=await n.store.updateMemory({...s,memory_type:n.memory_type??s.memory_type,file_md5_map:m,last_check_time:d});if(n.memory_type==="project_memory"&&this.backupProjectMemoryToLocal({...s,memory_type:"project_memory",file_md5_map:m,last_check_time:d}).catch(u=>{l.warn("[McpServer] \u5907\u4EFD project_memory \u66F4\u65B0\u5230\u672C\u5730\u5931\u8D25:",u)}),await this.reportMemoryStatsToServer(),n.memory_type==="project_memory"&&o?.memory_status==="updating")try{await this.memoryStore.releaseMemoryClaim(s.uri),this.activeLeaseUris.delete(s.uri),this.activeLeaseUris.size===0&&this.stopLeaseRenewal()}catch(u){l.warn("[McpServer] \u91CA\u653E memory claim \u5931\u8D25: uri=%s, error=%s",s.uri,u instanceof Error?u.message:String(u))}return this.toMemoryWriteResult(p)}async handleDeleteMemory(e){const t=typeof e.uri=="string"?e.uri:void 0,r=Array.isArray(e.memories)?e.memories:void 0;if(t&&r&&r.length>0)throw new Error("delete_memory \u4E0D\u652F\u6301\u540C\u65F6\u4F20\u5165 uri \u548C memories\uFF0C\u8BF7\u4E8C\u9009\u4E00");if(!t&&(!r||r.length===0))throw new Error("delete_memory \u9700\u8981 uri \u6216 memories \u53C2\u6570");if(t){const o=this.getOptionalMemoryType(e);return{deleted:await this.deleteMemorySingle(t,o)}}let i=0;const s=[],n=[];for(let o=0;o<r.length;o++){const a=r[o];if(typeof a!="object"||a===null){n.push({uri:String(a),reason:`memories[${o}] \u4E0D\u662F\u5BF9\u8C61`});continue}const m=a,d=typeof m.uri=="string"?m.uri:"";if(!d){n.push({uri:"",reason:`memories[${o}].uri \u7F3A\u5931`});continue}const p=typeof m.memory_type=="string"?this.parseMemoryType(m.memory_type):void 0;try{await this.deleteMemorySingle(d,p,!0)?i++:s.push(d)}catch(u){n.push({uri:d,reason:u instanceof Error?u.message:String(u)})}}return i>0&&await this.reportMemoryStatsToServer(),{deleted:i,not_found:s,failed:n}}async deleteMemorySingle(e,t,r=!1){const i={uri:e,memory_type:t},s=await this.findMemoryStoreForMutation(i),n=s.memory_type??i.memory_type,o=await s.store.deleteMemory({...i,memory_type:n});return o&&(n==="project_memory"&&this.removeProjectMemoryLocalBackup(e).catch(a=>{l.warn("[McpServer] \u5220\u9664 project_memory \u672C\u5730\u5907\u4EFD\u5931\u8D25:",a)}),r||await this.reportMemoryStatsToServer()),o}async handleSearchMemory(e){const t=this.getOptionalString(e,"scope"),r=t==="children"||t==="all"?t:"own",i=this.getOptionalString(e,"keyword_mode"),s=i==="all"?"all":i==="any"?"any":void 0,n={query:this.getRequiredString(e,"query"),limit:this.getOptionalNumber(e,"limit"),memory_type:this.getOptionalMemoryType(e),keywords:this.getOptionalStringArray(e,"keywords"),keyword_mode:s,scope:r};if(r==="children")return await this.memoryStore.searchMemory(n);const o=await this.searchOwnMemories(n);if(r==="own")return o;const a=await this.memoryStore.searchMemory({...n,scope:"children"});return this.mergeSearchResults(o,a,n.limit)}async searchOwnMemories(e){return await this.memoryStore.searchMemory(e)}mergeSearchResults(e,t,r){const i=new Set,s=typeof r=="number"&&r>0?r:10,n=[...e.results,...t.results].filter(o=>{const a=o.uri;return i.has(a)?!1:(i.add(a),!0)}).sort((o,a)=>a.updatedAt.localeCompare(o.updatedAt)).slice(0,s);return{query:e.query||t.query,results:n}}async handleListMemories(e){const t=this.getRequiredString(e,"memory_type"),r=this.getOptionalNumber(e,"offset"),i=this.getOptionalNumber(e,"limit"),s=Math.max(0,Math.floor(r??0)),n=i!==void 0&&i>=1?Math.floor(i):fe,o=await this.memoryStore.listByType(t),a=o.length,m=o.slice(s,s+n);return{memory_type:t,total:a,offset:s,limit:n,has_more:s+n<a,memories:m}}async handleGetMemoryStats(e){const t=await this.memoryStore.getStats(),r=this.getOptionalMemoryType(e);return r?{memory_type:r,total:t.by_type[r].count,by_type:{[r]:t.by_type[r]}}:t}async handleListMemoriesByFile(e){const t=this.getRequiredString(e,"file_path"),r=this.normalizeBoundFilePath(t),i=this.getOptionalMemoryType(e),s=this.getOptionalString(e,"scope"),n=s==="own"||s==="children"?s:"all",o=async()=>await this.memoryStore.listByFile(r,{memory_type:i,scope:"children"}).catch(()=>[]);if(n==="children"){const u=await o();return{file_path:r,count:u.length,memories:u.map(M=>this.toMemoryRecordWithMetadata(M))}}if(n==="own"){const u=await this.collectOwnMemoriesByFile(r,i);return{file_path:r,count:u.length,memories:u.map(M=>this.toMemoryRecordWithMetadata(M))}}const[a,m,d]=await Promise.all([this.memoryStore.listByFile(r,{memory_type:i,scope:"all"}).catch(()=>[]),this.searchChildMemoriesByFileLocal(r,i).catch(()=>[]),this.searchPeerMemoriesByFileLocal(r,i).catch(()=>[])]),p=this.mergeIdDedupe(this.mergeIdDedupe(a,m),d);return{file_path:r,count:p.length,memories:p.map(u=>this.toMemoryRecordWithMetadata(u))}}mergeIdDedupe(e,t){const r=new Set;for(const s of e)s.id&&r.add(s.id);const i=[...e];for(const s of t)s.id&&!r.has(s.id)&&(i.push(s),r.add(s.id));return i.sort((s,n)=>n.updatedAt.localeCompare(s.updatedAt)),i}async searchChildMemoriesByFileLocal(e,t){const r=process.env.AWS_WORKSPACE_PATH||process.cwd(),i=this.normalizeBoundFilePath(e),n=(i.startsWith("./")?i.substring(2):i).split("/"),o=[],a=new Set,{access:m,readdir:d,readFile:p}=await import("node:fs/promises");for(let u=n.length-1;u>=1;u--){const M=n.slice(0,u),h="./"+n.slice(u).join("/"),w=g.join(r,...M);if(w===r)continue;const c=g.join(w,".agentswork");try{await m(c)}catch{continue}const _=g.join(c,"memory","memory");let f;try{f=(await d(_,{withFileTypes:!0})).filter(S=>S.isDirectory()).map(S=>S.name)}catch{continue}for(const v of f){const S=g.join(_,v);let k;try{k=await d(S)}catch{continue}for(const y of k){if(!y.endsWith(".json"))continue;const b=g.join(S,y);let T;try{T=await p(b,"utf-8")}catch{continue}let A;try{A=JSON.parse(T)}catch{continue}if(A.by_uri)for(const[$,N]of Object.entries(A.by_uri)){if(!N)continue;const B=N.id||$;a.has(B)||t&&N.memory_type!==t||N.bound_files&&N.bound_files.length>0&&N.bound_files.some(V=>this.normalizeBoundFilePath(V)===h)&&(a.add(B),o.push({...N,uri:N.uri||$}))}}}}return o.sort((u,M)=>String(M.updatedAt??"").localeCompare(String(u.updatedAt??""))),o}async searchPeerMemoriesByFileLocal(e,t){if(!this.hasRoleName||!this.initialRoleName)return[];const r=process.env.AWS_WORKSPACE_PATH||process.cwd(),i=this.normalizeBoundFilePath(e),s=g.join(r,".agentswork","memory","memory"),{access:n,readdir:o,readFile:a}=await import("node:fs/promises");try{await n(s)}catch{return[]}let m;try{m=(await o(s,{withFileTypes:!0})).filter(M=>M.isDirectory()).map(M=>M.name)}catch{return[]}const d=[],p=new Set;for(const u of m){if(u===this.initialRoleName)continue;const M=g.join(s,u);let R;try{R=await o(M)}catch{continue}for(const h of R){if(!h.endsWith(".json"))continue;let w;try{w=JSON.parse(await a(g.join(M,h),"utf-8"))}catch{continue}if(w.by_uri)for(const[c,_]of Object.entries(w.by_uri)){if(!_)continue;const f=_.id||c;p.has(f)||_.memory_type==="project_memory"&&(t&&_.memory_type!==t||_.bound_files&&_.bound_files.length>0&&_.bound_files.some(S=>this.normalizeBoundFilePath(S)===i)&&(p.add(f),d.push({..._,uri:_.uri||c,readonly:!0,source_role_name:u})))}}}return d.sort((u,M)=>String(M.updatedAt??"").localeCompare(String(u.updatedAt??""))),d}async collectOwnMemoriesByFile(e,t){return await this.memoryStore.listByFile(e,{memory_type:t})}async handleAbandonMemoryUpdate(e){const t=this.getRequiredString(e,"uri"),r=this.getOptionalMemoryType(e),i=await this.memoryStore.readMemory({uri:t,memory_type:r});return i?.bound_files&&i.bound_files.length>0&&await this.validateBoundFilesExist(i.bound_files),await this.memoryStore.releaseMemoryClaim(t),i?.memory_type==="project_memory"&&this.backupProjectMemoryToLocal({uri:t,memory_type:"project_memory"}).catch(s=>{l.info(`[McpServer] abandon_memory_update: \u540C\u6B65\u672C\u5730\u5907\u4EFD\u5931\u8D25: ${t}`,s)}),this.activeLeaseUris.delete(t),this.activeLeaseUris.size===0&&this.stopLeaseRenewal(),{success:!0,uri:t,message:"Memory \u66F4\u65B0\u72B6\u6001\u5DF2\u6E05\u9664"}}async handleMemoryRecurrence(e){const t=Math.max(1,e?.limit??10);try{((await this.memoryStore.markExpiredMemoriesRecalling()).uris?.length??0)>0&&this.startLeaseRenewal()}catch(a){l.warn("[McpServer] \u539F\u5B50\u8BA4\u9886\u8FC7\u671F\u8BB0\u5FC6\u5931\u8D25: %s",a instanceof Error?a.message:String(a))}const r=await this.memoryStore.listExpiredRecentlyMemories();if(r.length===0)return{memories:[],count:0,limit:t,remaining:0,totalExpired:0,message:"\u6CA1\u6709\u5F85\u56DE\u5FC6\u7684\u5373\u5C06\u9057\u5FD8\u8BB0\u5FC6\u3002"};const s=[...r].sort((a,m)=>{const d=new Date(a.expiresAt||a.createdAt||0).getTime(),p=new Date(m.expiresAt||m.createdAt||0).getTime();return d-p}).slice(0,t),n=Math.max(0,r.length-s.length);return{memories:s.map(a=>({uri:a.uri,title:a.title,domain:a.domain,path:a.path,memory_status:"recalling",contentPreview:typeof a.content=="string"&&a.content.length>200?a.content.substring(0,200)+"...":a.content,createdAt:a.createdAt,updatedAt:a.updatedAt})),count:s.length,limit:t,remaining:n,totalExpired:r.length,message:n>0?`\u672C\u6B21\u5904\u7406 ${s.length} \u6761\u5373\u5C06\u9057\u5FD8\u7684\u8BB0\u5FC6\uFF08\u6700\u65E9\u8FC7\u671F\u4F18\u5148\uFF09\uFF0C\u5269\u4F59 ${n} \u6761\u5F85\u5904\u7406\u3002
|
|
4
4
|
\u8BF7\u5BA1\u67E5\u540E\u51B3\u5B9A\uFF08update_memory \u7EED\u671F\uFF0CTTL \xD72 / \u4E0D\u5904\u7406\uFF09\uFF0C
|
|
5
5
|
\u672C\u6279\u5BA1\u67E5\u5B8C\u6BD5\u540E\u8C03\u7528 memory_recurrence_complete(uris=[\u672C\u6279\u786E\u5B9A\u5220\u9664\u7684URI]) \u6E05\u7406\uFF0C\u518D\u8C03 memory_recurrence \u5904\u7406\u4E0B\u4E00\u6279\u3002`:`\u5DF2\u5904\u7406\u5168\u90E8 ${s.length} \u6761\u5373\u5C06\u9057\u5FD8\u7684\u8BB0\u5FC6\u3002
|
|
6
6
|
\u8BF7\u5BA1\u67E5\u540E\u51B3\u5B9A\uFF08update_memory \u7EED\u671F\uFF0CTTL \xD72 / \u4E0D\u5904\u7406\uFF09\uFF0C
|
|
7
|
-
\u672C\u6279\u5BA1\u67E5\u5B8C\u6BD5\u540E\u8C03\u7528 memory_recurrence_complete(uris=[\u672C\u6279\u786E\u5B9A\u5220\u9664\u7684URI]) \u6E05\u7406\u3002`}}async handleMemoryRecurrenceComplete(e){let t={deleted:0,uris:[]};try{t=await this.memoryStore.clearRecallingExpiredMemories(e?.uris)}catch(i){l.warn("[McpServer] \u6E05\u7406\u56DE\u5FC6\u4E2D\u8BB0\u5FC6\u5931\u8D25: %s",i instanceof Error?i.message:String(i))}await this.reportMemoryStatsToServer();const r=t.deleted??0;return{deleted:r,uris:t.uris??[],message:r===0?"\u6CA1\u6709\u5F85\u758F\u7406\u7684\u56DE\u5FC6\u4E2D\u8BB0\u5FC6\u3002":`\u5DF2\u6E05\u7406 ${r} \u6761\u5DF2\u8FC7\u671F\u8FD1\u671F\u8BB0\u5FC6\u3002`}}async fetchRecentMessages(){const[e,t]=await Promise.all([this.agentClient.getDmHistory(void 0,200).catch(()=>({messages:[],count:0})),this.agentClient.getGroupHistory(200).catch(()=>({messages:[],count:0}))]),r=[...e.messages,...t.messages];return r.sort((i,s)=>new Date(i.timestamp).getTime()-new Date(s.timestamp).getTime()),r}async handleSendGroup(e){const t=typeof e.content=="string"?e.content:"",r=Array.isArray(e.attachments)?e.attachments:[];if(!t.trim()&&r.length===0)throw new Error("content \u4E0E attachments \u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u975E\u7A7A\u503C");const i=r.length>0?this.mergeContentWithAttachmentMarkers(t,await this.uploadAttachmentFiles(r)):t;return this.recordMessageTokenUsage(i).catch(s=>l.warn("[McpServer] Failed to record token usage",s)),await this.agentClient.sendGroupMessage(i,e.projectId,e.roomId)}async uploadAttachmentFiles(e){if(e.length===0)return[];const t=process.env.AWS_WORKSPACE_PATH||process.cwd(),r=
|
|
7
|
+
\u672C\u6279\u5BA1\u67E5\u5B8C\u6BD5\u540E\u8C03\u7528 memory_recurrence_complete(uris=[\u672C\u6279\u786E\u5B9A\u5220\u9664\u7684URI]) \u6E05\u7406\u3002`}}async handleMemoryRecurrenceComplete(e){let t={deleted:0,uris:[]};try{t=await this.memoryStore.clearRecallingExpiredMemories(e?.uris)}catch(i){l.warn("[McpServer] \u6E05\u7406\u56DE\u5FC6\u4E2D\u8BB0\u5FC6\u5931\u8D25: %s",i instanceof Error?i.message:String(i))}await this.reportMemoryStatsToServer();const r=t.deleted??0;return{deleted:r,uris:t.uris??[],message:r===0?"\u6CA1\u6709\u5F85\u758F\u7406\u7684\u56DE\u5FC6\u4E2D\u8BB0\u5FC6\u3002":`\u5DF2\u6E05\u7406 ${r} \u6761\u5DF2\u8FC7\u671F\u8FD1\u671F\u8BB0\u5FC6\u3002`}}async fetchRecentMessages(){const[e,t]=await Promise.all([this.agentClient.getDmHistory(void 0,200).catch(()=>({messages:[],count:0})),this.agentClient.getGroupHistory(200).catch(()=>({messages:[],count:0}))]),r=[...e.messages,...t.messages];return r.sort((i,s)=>new Date(i.timestamp).getTime()-new Date(s.timestamp).getTime()),r}async handleSendGroup(e){const t=typeof e.content=="string"?e.content:"",r=Array.isArray(e.attachments)?e.attachments:[];if(!t.trim()&&r.length===0)throw new Error("content \u4E0E attachments \u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u975E\u7A7A\u503C");const i=r.length>0?this.mergeContentWithAttachmentMarkers(t,await this.uploadAttachmentFiles(r)):t;return this.recordMessageTokenUsage(i).catch(s=>l.warn("[McpServer] Failed to record token usage",s)),await this.agentClient.sendGroupMessage(i,e.projectId,e.roomId)}async uploadAttachmentFiles(e){if(e.length===0)return[];const t=process.env.AWS_WORKSPACE_PATH||process.cwd(),r=g.resolve(t),i=this.agentClient.getServerConfig(),s=ne(i.serverUrl,{fromAgentWebSocketUrl:!0})||"",n=i.runtimeAccessToken||"",o=[];for(const a of e){if(typeof a!="string"||!a.trim())throw new Error("attachments \u4E2D\u5B58\u5728\u7A7A\u8DEF\u5F84");const m=g.resolve(r,a);let d;try{d=await P.promises.stat(m)}catch{throw new Error(`\u9644\u4EF6\u6587\u4EF6\u4E0D\u5B58\u5728: ${a}\uFF08\u89E3\u6790\u4E3A ${m}\uFF09`)}if(!d.isFile())throw new Error(`\u9644\u4EF6\u4E0D\u662F\u666E\u901A\u6587\u4EF6: ${a}`);if(d.size>J)throw new Error(`\u9644\u4EF6\u6587\u4EF6\u8FC7\u5927\uFF08\u4E0A\u9650 ${Math.floor(J/1024/1024)}MB\uFF09: ${a}`);const p=g.basename(m),u=Me(m),M=new FormData;M.append("file",new Blob([await P.promises.readFile(m)],{type:u}),p);const R=await this.uploadFileRequest(`${s}/api/files/upload`,M,n,p);if(!R.ok){const w=await R.text().catch(()=>"");throw new Error(`\u9644\u4EF6\u4E0A\u4F20\u5931\u8D25 (HTTP ${R.status}): ${p}${w?` - ${w.slice(0,200)}`:""}`)}const h=await R.json();if(!h?.url)throw new Error(`\u9644\u4EF6\u4E0A\u4F20\u54CD\u5E94\u7F3A\u5C11 url: ${p}`);o.push({name:h.name||p,url:h.url.startsWith("http")?h.url:`${s}${h.url}`,mediaType:h.mediaType||u,size:typeof h.size=="number"?h.size:d.size})}return o}async uploadFileRequest(e,t,r,i){try{return await fetch(e,{method:"POST",headers:r?{"X-Runtime-Token":r}:void 0,body:t})}catch(s){const n=s?.cause,o=n?.code||n?.message||(s instanceof Error?s.message:String(s));throw new Error(`\u9644\u4EF6\u4E0A\u4F20\u8BF7\u6C42\u5931\u8D25\uFF08\u670D\u52A1\u7AEF\u4E0D\u53EF\u8FBE ${e}\uFF09: ${i} - ${o}`)}}mergeContentWithAttachmentMarkers(e,t){if(t.length===0)return e;const r=t.map(ve).join(`
|
|
8
8
|
`);return e.trim()?`${e}
|
|
9
9
|
|
|
10
|
-
${r}`:r}async recordMessageTokenUsage(e){try{const t=await this.agentClient.getProfile().catch(()=>null);if(!t?.runtimeSessionId)return;const r=this.contextManager.estimateTokens(e);await this.contextManager.recordUsage(t.runtimeSessionId,{input:0,output:r})}catch{}}async handleHireColleague(e){if(Array.isArray(e.items)){if(e.items.length===0)throw new Error("items must contain at least one hire item");return e.items.forEach((t,r)=>{if(!t.workspacePath||typeof t.workspacePath!="string")throw new Error(`items[${r}].workspacePath is required`)}),await this.agentClient.hireColleague(e)}if(!e.workspacePath||typeof e.workspacePath!="string")throw new Error("workspacePath is required");return await this.agentClient.hireColleague(e)}async handleSendPrivate(e){if(!e.targetId||typeof e.targetId!="string")throw new Error("targetId is required");const t=typeof e.content=="string"?e.content:"",r=Array.isArray(e.attachments)?e.attachments:[];if(!t.trim()&&r.length===0)throw new Error("content \u4E0E attachments \u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u975E\u7A7A\u503C");const i=r.length>0?this.mergeContentWithAttachmentMarkers(t,await this.uploadAttachmentFiles(r)):t;return this.recordMessageTokenUsage(i).catch(s=>l.warn("[McpServer] Failed to record token usage",s)),await this.agentClient.sendDirectMessage(e.targetId,i,e.waitReply,e.replyToCallId,e.timeoutMs)}async handleCreateTaskInstanceComment(e){const t=this.getRequiredString(e,"taskInstanceId"),r=this.getRequiredString(e,"content");return await this.agentClient.createTaskInstanceComment({taskInstanceId:t,content:r})}async prependMemoryNotification(e,t){const r=await t();return r?(e.directMessages.unshift(r),!0):!1}async handlePollMessage(e){if(l.info("[McpServer] handlePollMessage \u88AB\u8C03\u7528"),this.pendingPollMessage)return l.info("[McpServer] poll_message \u5DF2\u6709\u8FDB\u884C\u4E2D\u7684\u8C03\u7528\uFF0C\u7B49\u5F85\u5176\u5B8C\u6210"),I("poll.singleton-join","\u7B2C\u4E8C\u4E2A poll_message \u8C03\u7528\u5230\u8FBE\uFF0C\u5408\u5E76\u5230\u8FDB\u884C\u4E2D\u7684 poll Promise\uFF08\u82E5\u9996\u4E2A\u5FAA\u73AF\u5361\u6B7B\u5219\u4E24\u8005\u4E00\u8D77\u6302\u8D77\uFF09"),await this.pendingPollMessage;const t=Date.now();this.pendingPollMessage=(async()=>await this.doPollMessage(e))();try{const r=await this.pendingPollMessage;return l.info("[McpServer] poll_message Promise resolved\uFF0C\u8017\u65F6 %dms, directMessages=%d, groupMessages=%d",Date.now()-t,Array.isArray(r?.directMessages)?r.directMessages.length:-1,Array.isArray(r?.groupMessages)?r.groupMessages.length:-1),I("poll.resolved",`elapsedMs=${Date.now()-t} dm=${O(r?.directMessages)} gm=${O(r?.groupMessages)}`),r}catch(r){throw l.warn("[McpServer] poll_message Promise rejected\uFF0C\u8017\u65F6 %dms: %s",Date.now()-t,r instanceof Error?r.message:String(r)),I("poll.rejected",`elapsedMs=${Date.now()-t} error=${r instanceof Error?r.message:String(r)}`),r}finally{this.pendingPollMessage=null}}async doPollMessage(e){if(l.info("[McpServer] doPollMessage \u88AB\u8C03\u7528"),I("poll.enter","doPollMessage \u5165\u53E3"),e?.aborted)return l.info("[McpServer] poll_message \u8C03\u7528\u5DF2\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u8DF3\u8FC7\u963B\u585E\u7B49\u5F85"),this.buildPollResult({directMessages:[],groupMessages:[]});const t=await this.buildTodoBlockNotification();if(t)return this.buildPollResult({directMessages:[t],groupMessages:[]});await this.hydrateUnreadMessagesFromServer({blockIfEmpty:!1,includePipelineTasks:!0,pollMessageTimeoutMs:5e3,preemptiveJoinWaitMs:0}),await this.hydrateBrowserListenerEvents(),await this.hydratePluginEvents();const r=await this.buildBackgroundTaskNotification(),i=await this.confirmServerMessages(this.messageBuffer.get());if(i.directMessages.length>0||i.groupMessages.length>0)return e?.aborted?(this.messageBuffer.requeue(i),l.info("[McpServer] poll_message \u5165\u53E3\u68C0\u67E5\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u56DE\u6EDA\u672A\u4EA4\u4ED8\u6D88\u606F"),this.buildPollResult({directMessages:[],groupMessages:[]})):(r&&i.directMessages.unshift(r),this.buildPollResult(i));if(r)return this.buildPollResult({directMessages:[r],groupMessages:[]});const s=await this.checkAllLocalNonUrgentNotifications();if(s.length>0)return this.buildPollResult({directMessages:s,groupMessages:[]});let n=!1,o=0,a=0;for(;;){if(e?.aborted)return l.info("[McpServer] poll_message \u7B49\u5F85\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u9000\u51FA\u963B\u585E\u5FAA\u73AF"),I("poll.loop-abort","\u7B49\u5F85\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u9000\u51FA\u963B\u585E\u5FAA\u73AF"),this.buildPollResult({directMessages:[],groupMessages:[]});if(!n){const g=this.agentClient.getStatusReporter();if(g){const S=g.getCurrentStatus();S.status==="tool_using"&&S.actionDetail!=null&&S.actionDetail.includes("poll_message")||g.reportToolUse("poll_message",{}),n=!0}}const m=await this.confirmServerMessages(await this.messageBuffer.poll(ge));if(a++,I("poll.loop-tick",`\u7B2C ${a} \u62CD: polled dm=${m.directMessages.length} gm=${m.groupMessages.length}`),e?.aborted)return this.messageBuffer.requeue(m),l.info("[McpServer] poll_message \u7B49\u5F85\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u56DE\u6EDA %d \u6761\u672A\u4EA4\u4ED8\u6D88\u606F\u540E\u9000\u51FA\u963B\u585E\u5FAA\u73AF",m.directMessages.length+m.groupMessages.length),this.buildPollResult({directMessages:[],groupMessages:[]});if(m.directMessages.length>0||m.groupMessages.length>0)return this.buildPollResult(m);if(l.debug("[McpServer] poll \u8D85\u65F6\uFF0C\u68C0\u67E5\u6D4F\u89C8\u5668\u76D1\u542C\u5668\u4E8B\u4EF6"),await this.hydrateBrowserListenerEvents()){if(e?.aborted)return l.info("[McpServer] poll_message \u76D1\u542C\u5668\u4E8B\u4EF6\u68C0\u67E5\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u9000\u51FA\u963B\u585E\u5FAA\u73AF"),this.buildPollResult({directMessages:[],groupMessages:[]});const g=await this.confirmServerMessages(this.messageBuffer.get());if(g.directMessages.length>0||g.groupMessages.length>0)return e?.aborted?(this.messageBuffer.requeue(g),l.info("[McpServer] poll_message \u76D1\u542C\u5668\u4E8B\u4EF6\u786E\u8BA4\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u56DE\u6EDA %d \u6761\u672A\u4EA4\u4ED8\u6D88\u606F",g.directMessages.length+g.groupMessages.length),this.buildPollResult({directMessages:[],groupMessages:[]})):(l.info("[McpServer] \u76D1\u542C\u5668\u4E8B\u4EF6\u5DF2\u6CE8\u5165\uFF0Cpoll_message \u8FD4\u56DE %d \u6761\u79C1\u4FE1",g.directMessages.length),this.buildPollResult(g))}if(l.debug("[McpServer] poll \u8D85\u65F6\uFF0C\u68C0\u67E5\u63D2\u4EF6\u4E8B\u4EF6"),await this.hydratePluginEvents()){if(e?.aborted)return l.info("[McpServer] poll_message \u63D2\u4EF6\u4E8B\u4EF6\u68C0\u67E5\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u9000\u51FA\u963B\u585E\u5FAA\u73AF"),this.buildPollResult({directMessages:[],groupMessages:[]});const g=await this.confirmServerMessages(this.messageBuffer.get());if(g.directMessages.length>0||g.groupMessages.length>0)return e?.aborted?(this.messageBuffer.requeue(g),l.info("[McpServer] poll_message \u63D2\u4EF6\u4E8B\u4EF6\u786E\u8BA4\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u56DE\u6EDA %d \u6761\u672A\u4EA4\u4ED8\u6D88\u606F",g.directMessages.length+g.groupMessages.length),this.buildPollResult({directMessages:[],groupMessages:[]})):(l.info("[McpServer] \u63D2\u4EF6\u4E8B\u4EF6\u5DF2\u6CE8\u5165\uFF0Cpoll_message \u8FD4\u56DE %d \u6761\u79C1\u4FE1",g.directMessages.length),this.buildPollResult(g))}o++;let u=!1;if(this.urgentMeetingHydrateRequested&&(this.urgentMeetingHydrateRequested=!1,o=j,u=!0),o<j)continue;o=0;const M=await this.buildTodoBlockNotification();if(M)return this.buildPollResult({directMessages:[M],groupMessages:[]});let R=!1;const h=u?T.MEETING_HYDRATE_RETRY_COUNT:1;for(let g=1;g<=h;g++){l.info("[McpServer] %s hydrate poll_message\uFF0C\u7B2C %d/%d \u6B21",u?"\u4F1A\u8BAE\u9080\u8BF7\u5F3A\u5236":"\u666E\u901A",g,h);const S=this.messageBuffer.size();if(u&&S>0){l.info("[McpServer] \u4F1A\u8BAE\u9080\u8BF7\u5F3A\u5236 hydrate \u524D\u53D1\u73B0\u672C\u5730\u7F13\u51B2\u5DF2\u6709 %d \u6761\u6D88\u606F\uFF0C\u8DF3\u8FC7 hydrate \u76F4\u63A5\u4EA4\u4ED8",S);break}const v={blockIfEmpty:!1,includePipelineTasks:!0,pollMessageTimeoutMs:u?T.MEETING_HYDRATE_POLL_TIMEOUT_MS:void 0,skipGroupMessages:u,preemptiveJoinWaitMs:u?T.MEETING_PREEMPTIVE_JOIN_WAIT_MS:0,propagatePollTimeout:u},k=Date.now(),f=await this.hydrateUnreadMessagesFromServer(v);if(u&&I("poll.forced-hydrate-outcome",`\u7B2C ${g}/${h} \u6B21\u5F3A\u5236 hydrate \u5B8C\u6210\uFF08\u8017\u65F6 ${Date.now()-k}ms\uFF09: timedOut=${f.timedOut} failed=${f.failed} \u7F13\u51B2\u533A=${this.messageBuffer.size()} \u6761`),f.timedOut){l.warn("[McpServer] \u4F1A\u8BAE\u9080\u8BF7\u5F3A\u5236 hydrate \u7B2C %d/%d \u6B21\u5BA2\u6237\u7AEF\u8D85\u65F6\uFF08\u670D\u52A1\u7AEF\u53EF\u80FD\u4ECD\u5728\u6267\u884C\u4E0A\u4E00\u8BF7\u6C42\uFF09\uFF0C\u505C\u6B62\u7ACB\u5373\u91CD\u8BD5",g,h),R=!0;break}const b=this.messageBuffer.size();if(l.info("[McpServer] hydrate poll_message \u5B8C\u6210\uFF0C\u7B2C %d/%d \u6B21\uFF0C\u7F13\u51B2\u533A\u6D88\u606F\u6570=%d",g,h,b),b>0||!u)break;g<h&&await new Promise(E=>setTimeout(E,T.MEETING_HYDRATE_RETRY_DELAY_MS))}R&&(I("poll.forced-hydrate-rearm",`\u5F3A\u5236 hydrate \u8D85\u65F6\uFF0C${this.meetingHydrateTimeoutRearmDelayMs}ms \u540E\u91CD\u65B0\u7F6E\u4F4D\u5524\u9192\u6807\u8BB0`),setTimeout(()=>{l.info("[McpServer] \u4F1A\u8BAE\u5F3A\u5236 hydrate \u8D85\u65F6\u81EA\u6108\uFF1A\u91CD\u65B0\u7F6E\u4F4D\u5524\u9192\u6807\u8BB0\u89E6\u53D1\u4E0B\u4E00\u8F6E\u5F3A\u5236 hydrate"),this.urgentMeetingHydrateRequested=!0,this.messageBuffer.wakeUpWaiters()},this.meetingHydrateTimeoutRearmDelayMs).unref());const w=await this.buildBackgroundTaskNotification();if(e?.aborted)return l.info("[McpServer] poll_message hydrate \u540E\u68C0\u67E5\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u9000\u51FA\u963B\u585E\u5FAA\u73AF"),this.buildPollResult({directMessages:[],groupMessages:[]});const c=await this.confirmServerMessages(this.messageBuffer.get());if(c.directMessages.length>0||c.groupMessages.length>0)return e?.aborted?(this.messageBuffer.requeue(c),l.info("[McpServer] poll_message hydrate \u786E\u8BA4\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u56DE\u6EDA %d \u6761\u672A\u4EA4\u4ED8\u6D88\u606F",c.directMessages.length+c.groupMessages.length),this.buildPollResult({directMessages:[],groupMessages:[]})):(w&&c.directMessages.unshift(w),this.buildPollResult(c));if(u&&!w&&!R&&(l.info("[McpServer] \u4F1A\u8BAE\u9080\u8BF7\u5524\u9192\u540E\u7684\u5F3A\u5236 hydrate \u672A\u53D6\u5230\u53EF\u6295\u9012\u6D88\u606F\uFF08\u670D\u52A1\u7AEF\u672A\u62A2\u5360\u6216\u65E0\u65B0\u4F1A\u8BAE\u5185\u5BB9\uFF09\uFF0C\u7EE7\u7EED\u7B49\u5F85"),I("poll.forced-hydrate-empty","\u4F1A\u8BAE\u9080\u8BF7\u5524\u9192\u540E\u7684\u5F3A\u5236 hydrate \u672A\u53D6\u5230\u4EFB\u4F55\u53EF\u6295\u9012\u6D88\u606F\uFF08\u670D\u52A1\u7AEF\u672A\u62A2\u5360/\u65E0\u65B0\u5185\u5BB9/\u6D88\u606F\u88AB\u53BB\u91CD\u62E6\u622A\uFF09\uFF0C\u7EE7\u7EED\u7B49\u5F85"),o=j-1),w)return this.buildPollResult({directMessages:[w],groupMessages:[]});const y=await this.checkAllLocalNonUrgentNotifications(!1);if(y.length>0)return this.buildPollResult({directMessages:y,groupMessages:[]})}}async checkAllLocalNonUrgentNotifications(e=!0){const t=[],r=await this.buildMemoryRecurrenceNotification();if(r&&t.push(r),e){const s=await this.buildMemoryFileChangeNotification();s&&t.push(s)}const i=await this.buildMemoryCleanupNotification();return i&&t.push(i),t}buildPollResult(e,t=!0){l.info("[McpServer][DEBUG-poll] \u8FD4\u56DE poll \u7ED3\u679C: directMessages=%d, groupMessages=%d, \u660E\u7EC6=%s",e.directMessages.length,e.groupMessages.length,JSON.stringify(e.directMessages.map(i=>({id:i.msgId,channel:i.channel,sender:i.senderId}))));const r=this.shouldDeliverPluginCatalog();return{directMessages:e.directMessages,groupMessages:e.groupMessages,fromBuffer:t,systemMessages:[],...r?{pluginCatalog:r}:{}}}shouldDeliverPluginCatalog(){if(!this.cachedPluginCatalog||this.cachedPluginCatalog.plugins.length===0)return null;const e=this.pluginCatalogDrainCompactionSeq;e!==this.pluginCatalogLastSeenCompactionSeq&&(this.deliveredPluginNames.clear(),this.pluginCatalogLastSeenCompactionSeq=e);const t=[];for(const r of this.cachedPluginCatalog.plugins)this.deliveredPluginNames.has(r.plugin)||(this.deliveredPluginNames.add(r.plugin),t.push(r));return t.length===0?null:(l.debug("[McpServer] \u4EA4\u4ED8 pluginCatalog: plugins=[%s], compactionSeq=%d",t.map(r=>r.plugin).join(", "),e),{revision:this.cachedPluginCatalog.revision,plugins:t})}parsePositiveIntegerEnv(e,t){const r=process.env[e];if(!r||!r.trim())return t;const i=Number.parseInt(r,10);return!Number.isFinite(i)||i<=0?(l.warn(`[McpServer] ${e} \u65E0\u6548\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C ${t}ms`),t):i}sleep(e){return e<=0?Promise.resolve():new Promise(t=>setTimeout(t,e))}async buildMemoryCleanupNotification(){try{const e=await this.memoryStore.getStats(),t=Object.entries(e.by_type).filter(([,o])=>o.listable&&o.exceeded),r=Object.entries(e.by_type).flatMap(([o,a])=>a.listable?Object.entries(a.by_domain).filter(([,m])=>m.exceeded).map(([m,d])=>({memoryType:o,domain:m,domainStats:d})):[]);if(t.length===0&&r.length===0)return null;const i=t.map(([o,a])=>`${o} \u5F53\u524D ${a.domainCount} \u4E2A URI domain \u7C7B\u578B\uFF0C\u5EFA\u8BAE\u4E0A\u9650 ${a.limit} \u4E2A\uFF0C\u786C\u4E0A\u9650 ${a.hardLimit} \u4E2A`),s=r.map(({memoryType:o,domain:a,domainStats:m})=>`${o}/${a} \u7C7B\u578B\u4E0B\u5F53\u524D ${m.count} \u6761 memory\uFF0C\u5EFA\u8BAE\u4E0A\u9650 ${m.limit} \u6761\uFF0C\u786C\u4E0A\u9650 ${m.hardLimit} \u6761`),n=["\u8BF7\u8C03\u7528 get_memory_stats \u67E5\u770B\u5BB9\u91CF\u8BE6\u60C5","\u8C03\u7528 list_memories \u6309\u7C7B\u578B\u67E5\u770B\u8D85\u9650\u8BB0\u5FC6","\u5220\u9664\u4E0D\u518D\u9700\u8981\u7684 recently_memory","\u66F4\u65B0 agent_memory \u7EED\u671F\uFF08TTL \xD72\uFF09\u6216\u5220\u9664\u8FC7\u671F\u9879"].join("\uFF1B");return{msgId:`memory-cleanup-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:this.getCurrentAgentId()||void 0,content:`\u672C\u5730 memory \u5DF2\u8D85\u8FC7\u5EFA\u8BAE\u5BB9\u91CF\uFF0C\u9700\u8981\u6574\u7406\u3002${[...i,...s].join("\uFF1B")}\u3002${n}\u3002`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_cleanup",uris:r.map(o=>`${o.memoryType}/${o.domain}`)}}}catch(e){return l.warn("[McpServer] \u6784\u5EFA\u672C\u5730 memory \u6574\u7406\u63D0\u9192\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),null}}async buildMemoryRecurrenceNotification(){try{const e=await this.memoryStore.listExpiredRecentlyMemories();if(e.length===0)return null;const t=this.getCurrentAgentId();if(!t)return null;const r=e.filter(s=>s.memory_status==="recalling"),i=e.filter(s=>!r.includes(s));if(r.length>0){const s=r.slice(0,5).map(n=>n.uri).join(`
|
|
10
|
+
${r}`:r}async recordMessageTokenUsage(e){try{const t=await this.agentClient.getProfile().catch(()=>null);if(!t?.runtimeSessionId)return;const r=this.contextManager.estimateTokens(e);await this.contextManager.recordUsage(t.runtimeSessionId,{input:0,output:r})}catch{}}async handleHireColleague(e){if(Array.isArray(e.items)){if(e.items.length===0)throw new Error("items must contain at least one hire item");return e.items.forEach((t,r)=>{if(!t.workspacePath||typeof t.workspacePath!="string")throw new Error(`items[${r}].workspacePath is required`)}),await this.agentClient.hireColleague(e)}if(!e.workspacePath||typeof e.workspacePath!="string")throw new Error("workspacePath is required");return await this.agentClient.hireColleague(e)}async handleSendPrivate(e){if(!e.targetId||typeof e.targetId!="string")throw new Error("targetId is required");const t=typeof e.content=="string"?e.content:"",r=Array.isArray(e.attachments)?e.attachments:[];if(!t.trim()&&r.length===0)throw new Error("content \u4E0E attachments \u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u975E\u7A7A\u503C");const i=r.length>0?this.mergeContentWithAttachmentMarkers(t,await this.uploadAttachmentFiles(r)):t;return this.recordMessageTokenUsage(i).catch(s=>l.warn("[McpServer] Failed to record token usage",s)),await this.agentClient.sendDirectMessage(e.targetId,i,e.waitReply,e.replyToCallId,e.timeoutMs)}async handleCreateTaskInstanceComment(e){const t=this.getRequiredString(e,"taskInstanceId"),r=this.getRequiredString(e,"content");return await this.agentClient.createTaskInstanceComment({taskInstanceId:t,content:r})}async prependMemoryNotification(e,t){const r=await t();return r?(e.directMessages.unshift(r),!0):!1}async handlePollMessage(e){if(l.info("[McpServer] handlePollMessage \u88AB\u8C03\u7528"),this.pendingPollMessage)return l.info("[McpServer] poll_message \u5DF2\u6709\u8FDB\u884C\u4E2D\u7684\u8C03\u7528\uFF0C\u7B49\u5F85\u5176\u5B8C\u6210"),I("poll.singleton-join","\u7B2C\u4E8C\u4E2A poll_message \u8C03\u7528\u5230\u8FBE\uFF0C\u5408\u5E76\u5230\u8FDB\u884C\u4E2D\u7684 poll Promise\uFF08\u82E5\u9996\u4E2A\u5FAA\u73AF\u5361\u6B7B\u5219\u4E24\u8005\u4E00\u8D77\u6302\u8D77\uFF09"),await this.pendingPollMessage;const t=Date.now();this.pendingPollMessage=(async()=>await this.doPollMessage(e))();try{const r=await this.pendingPollMessage;return l.info("[McpServer] poll_message Promise resolved\uFF0C\u8017\u65F6 %dms, directMessages=%d, groupMessages=%d",Date.now()-t,Array.isArray(r?.directMessages)?r.directMessages.length:-1,Array.isArray(r?.groupMessages)?r.groupMessages.length:-1),I("poll.resolved",`elapsedMs=${Date.now()-t} dm=${O(r?.directMessages)} gm=${O(r?.groupMessages)}`),r}catch(r){throw l.warn("[McpServer] poll_message Promise rejected\uFF0C\u8017\u65F6 %dms: %s",Date.now()-t,r instanceof Error?r.message:String(r)),I("poll.rejected",`elapsedMs=${Date.now()-t} error=${r instanceof Error?r.message:String(r)}`),r}finally{this.pendingPollMessage=null}}async doPollMessage(e){if(l.info("[McpServer] doPollMessage \u88AB\u8C03\u7528"),I("poll.enter","doPollMessage \u5165\u53E3"),e?.aborted)return l.info("[McpServer] poll_message \u8C03\u7528\u5DF2\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u8DF3\u8FC7\u963B\u585E\u7B49\u5F85"),this.buildPollResult({directMessages:[],groupMessages:[]});const t=await this.buildTodoBlockNotification();if(t)return this.buildPollResult({directMessages:[t],groupMessages:[]});await this.hydrateUnreadMessagesFromServer({blockIfEmpty:!1,includePipelineTasks:!0,pollMessageTimeoutMs:5e3,preemptiveJoinWaitMs:0}),await this.hydrateBrowserListenerEvents(),await this.hydratePluginEvents();const r=await this.buildBackgroundTaskNotification(),i=await this.confirmServerMessages(this.messageBuffer.get());if(i.directMessages.length>0||i.groupMessages.length>0)return e?.aborted?(this.messageBuffer.requeue(i),l.info("[McpServer] poll_message \u5165\u53E3\u68C0\u67E5\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u56DE\u6EDA\u672A\u4EA4\u4ED8\u6D88\u606F"),this.buildPollResult({directMessages:[],groupMessages:[]})):(r&&i.directMessages.unshift(r),this.buildPollResult(i));if(r)return this.buildPollResult({directMessages:[r],groupMessages:[]});const s=await this.checkAllLocalNonUrgentNotifications();if(s.length>0)return this.buildPollResult({directMessages:s,groupMessages:[]});let n=!1,o=0,a=0;for(;;){if(e?.aborted)return l.info("[McpServer] poll_message \u7B49\u5F85\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u9000\u51FA\u963B\u585E\u5FAA\u73AF"),I("poll.loop-abort","\u7B49\u5F85\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u9000\u51FA\u963B\u585E\u5FAA\u73AF"),this.buildPollResult({directMessages:[],groupMessages:[]});if(!n){const f=this.agentClient.getStatusReporter();if(f){const v=f.getCurrentStatus();v.status==="tool_using"&&v.actionDetail!=null&&v.actionDetail.includes("poll_message")||f.reportToolUse("poll_message",{}),n=!0}}const m=await this.confirmServerMessages(await this.messageBuffer.poll(ge));if(a++,I("poll.loop-tick",`\u7B2C ${a} \u62CD: polled dm=${m.directMessages.length} gm=${m.groupMessages.length}`),e?.aborted)return this.messageBuffer.requeue(m),l.info("[McpServer] poll_message \u7B49\u5F85\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u56DE\u6EDA %d \u6761\u672A\u4EA4\u4ED8\u6D88\u606F\u540E\u9000\u51FA\u963B\u585E\u5FAA\u73AF",m.directMessages.length+m.groupMessages.length),this.buildPollResult({directMessages:[],groupMessages:[]});if(m.directMessages.length>0||m.groupMessages.length>0)return this.buildPollResult(m);if(l.debug("[McpServer] poll \u8D85\u65F6\uFF0C\u68C0\u67E5\u6D4F\u89C8\u5668\u76D1\u542C\u5668\u4E8B\u4EF6"),await this.hydrateBrowserListenerEvents()){if(e?.aborted)return l.info("[McpServer] poll_message \u76D1\u542C\u5668\u4E8B\u4EF6\u68C0\u67E5\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u9000\u51FA\u963B\u585E\u5FAA\u73AF"),this.buildPollResult({directMessages:[],groupMessages:[]});const f=await this.confirmServerMessages(this.messageBuffer.get());if(f.directMessages.length>0||f.groupMessages.length>0)return e?.aborted?(this.messageBuffer.requeue(f),l.info("[McpServer] poll_message \u76D1\u542C\u5668\u4E8B\u4EF6\u786E\u8BA4\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u56DE\u6EDA %d \u6761\u672A\u4EA4\u4ED8\u6D88\u606F",f.directMessages.length+f.groupMessages.length),this.buildPollResult({directMessages:[],groupMessages:[]})):(l.info("[McpServer] \u76D1\u542C\u5668\u4E8B\u4EF6\u5DF2\u6CE8\u5165\uFF0Cpoll_message \u8FD4\u56DE %d \u6761\u79C1\u4FE1",f.directMessages.length),this.buildPollResult(f))}if(l.debug("[McpServer] poll \u8D85\u65F6\uFF0C\u68C0\u67E5\u63D2\u4EF6\u4E8B\u4EF6"),await this.hydratePluginEvents()){if(e?.aborted)return l.info("[McpServer] poll_message \u63D2\u4EF6\u4E8B\u4EF6\u68C0\u67E5\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u9000\u51FA\u963B\u585E\u5FAA\u73AF"),this.buildPollResult({directMessages:[],groupMessages:[]});const f=await this.confirmServerMessages(this.messageBuffer.get());if(f.directMessages.length>0||f.groupMessages.length>0)return e?.aborted?(this.messageBuffer.requeue(f),l.info("[McpServer] poll_message \u63D2\u4EF6\u4E8B\u4EF6\u786E\u8BA4\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u56DE\u6EDA %d \u6761\u672A\u4EA4\u4ED8\u6D88\u606F",f.directMessages.length+f.groupMessages.length),this.buildPollResult({directMessages:[],groupMessages:[]})):(l.info("[McpServer] \u63D2\u4EF6\u4E8B\u4EF6\u5DF2\u6CE8\u5165\uFF0Cpoll_message \u8FD4\u56DE %d \u6761\u79C1\u4FE1",f.directMessages.length),this.buildPollResult(f))}o++;let u=!1;if(this.urgentMeetingHydrateRequested&&(this.urgentMeetingHydrateRequested=!1,o=F,u=!0),o<F)continue;o=0;const M=await this.buildTodoBlockNotification();if(M)return this.buildPollResult({directMessages:[M],groupMessages:[]});let R=!1;const h=u?E.MEETING_HYDRATE_RETRY_COUNT:1;for(let f=1;f<=h;f++){l.info("[McpServer] %s hydrate poll_message\uFF0C\u7B2C %d/%d \u6B21",u?"\u4F1A\u8BAE\u9080\u8BF7\u5F3A\u5236":"\u666E\u901A",f,h);const v=this.messageBuffer.size();if(u&&v>0){l.info("[McpServer] \u4F1A\u8BAE\u9080\u8BF7\u5F3A\u5236 hydrate \u524D\u53D1\u73B0\u672C\u5730\u7F13\u51B2\u5DF2\u6709 %d \u6761\u6D88\u606F\uFF0C\u8DF3\u8FC7 hydrate \u76F4\u63A5\u4EA4\u4ED8",v);break}const S={blockIfEmpty:!1,includePipelineTasks:!0,pollMessageTimeoutMs:u?E.MEETING_HYDRATE_POLL_TIMEOUT_MS:void 0,skipGroupMessages:u,preemptiveJoinWaitMs:u?E.MEETING_PREEMPTIVE_JOIN_WAIT_MS:0,propagatePollTimeout:u},k=Date.now(),y=await this.hydrateUnreadMessagesFromServer(S);if(u&&I("poll.forced-hydrate-outcome",`\u7B2C ${f}/${h} \u6B21\u5F3A\u5236 hydrate \u5B8C\u6210\uFF08\u8017\u65F6 ${Date.now()-k}ms\uFF09: timedOut=${y.timedOut} failed=${y.failed} \u7F13\u51B2\u533A=${this.messageBuffer.size()} \u6761`),y.timedOut){l.warn("[McpServer] \u4F1A\u8BAE\u9080\u8BF7\u5F3A\u5236 hydrate \u7B2C %d/%d \u6B21\u5BA2\u6237\u7AEF\u8D85\u65F6\uFF08\u670D\u52A1\u7AEF\u53EF\u80FD\u4ECD\u5728\u6267\u884C\u4E0A\u4E00\u8BF7\u6C42\uFF09\uFF0C\u505C\u6B62\u7ACB\u5373\u91CD\u8BD5",f,h),R=!0;break}const b=this.messageBuffer.size();if(l.info("[McpServer] hydrate poll_message \u5B8C\u6210\uFF0C\u7B2C %d/%d \u6B21\uFF0C\u7F13\u51B2\u533A\u6D88\u606F\u6570=%d",f,h,b),b>0||!u)break;f<h&&await new Promise(T=>setTimeout(T,E.MEETING_HYDRATE_RETRY_DELAY_MS))}R&&(I("poll.forced-hydrate-rearm",`\u5F3A\u5236 hydrate \u8D85\u65F6\uFF0C${this.meetingHydrateTimeoutRearmDelayMs}ms \u540E\u91CD\u65B0\u7F6E\u4F4D\u5524\u9192\u6807\u8BB0`),setTimeout(()=>{l.info("[McpServer] \u4F1A\u8BAE\u5F3A\u5236 hydrate \u8D85\u65F6\u81EA\u6108\uFF1A\u91CD\u65B0\u7F6E\u4F4D\u5524\u9192\u6807\u8BB0\u89E6\u53D1\u4E0B\u4E00\u8F6E\u5F3A\u5236 hydrate"),this.urgentMeetingHydrateRequested=!0,this.messageBuffer.wakeUpWaiters()},this.meetingHydrateTimeoutRearmDelayMs).unref());const w=await this.buildBackgroundTaskNotification();if(e?.aborted)return l.info("[McpServer] poll_message hydrate \u540E\u68C0\u67E5\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u9000\u51FA\u963B\u585E\u5FAA\u73AF"),this.buildPollResult({directMessages:[],groupMessages:[]});const c=await this.confirmServerMessages(this.messageBuffer.get());if(c.directMessages.length>0||c.groupMessages.length>0)return e?.aborted?(this.messageBuffer.requeue(c),l.info("[McpServer] poll_message hydrate \u786E\u8BA4\u671F\u95F4\u88AB\u5BA2\u6237\u7AEF\u53D6\u6D88\uFF0C\u56DE\u6EDA %d \u6761\u672A\u4EA4\u4ED8\u6D88\u606F",c.directMessages.length+c.groupMessages.length),this.buildPollResult({directMessages:[],groupMessages:[]})):(w&&c.directMessages.unshift(w),this.buildPollResult(c));if(u&&!w&&!R&&(l.info("[McpServer] \u4F1A\u8BAE\u9080\u8BF7\u5524\u9192\u540E\u7684\u5F3A\u5236 hydrate \u672A\u53D6\u5230\u53EF\u6295\u9012\u6D88\u606F\uFF08\u670D\u52A1\u7AEF\u672A\u62A2\u5360\u6216\u65E0\u65B0\u4F1A\u8BAE\u5185\u5BB9\uFF09\uFF0C\u7EE7\u7EED\u7B49\u5F85"),I("poll.forced-hydrate-empty","\u4F1A\u8BAE\u9080\u8BF7\u5524\u9192\u540E\u7684\u5F3A\u5236 hydrate \u672A\u53D6\u5230\u4EFB\u4F55\u53EF\u6295\u9012\u6D88\u606F\uFF08\u670D\u52A1\u7AEF\u672A\u62A2\u5360/\u65E0\u65B0\u5185\u5BB9/\u6D88\u606F\u88AB\u53BB\u91CD\u62E6\u622A\uFF09\uFF0C\u7EE7\u7EED\u7B49\u5F85"),o=F-1),w)return this.buildPollResult({directMessages:[w],groupMessages:[]});const _=await this.checkAllLocalNonUrgentNotifications(!1);if(_.length>0)return this.buildPollResult({directMessages:_,groupMessages:[]})}}async checkAllLocalNonUrgentNotifications(e=!0){const t=[],r=await this.buildMemoryRecurrenceNotification();if(r&&t.push(r),e){const s=await this.buildMemoryFileChangeNotification();s&&t.push(s)}const i=await this.buildMemoryCleanupNotification();return i&&t.push(i),t}buildPollResult(e,t=!0){l.info("[McpServer][DEBUG-poll] \u8FD4\u56DE poll \u7ED3\u679C: directMessages=%d, groupMessages=%d, \u660E\u7EC6=%s",e.directMessages.length,e.groupMessages.length,JSON.stringify(e.directMessages.map(i=>({id:i.msgId,channel:i.channel,sender:i.senderId}))));const r=this.shouldDeliverPluginCatalog();return{directMessages:e.directMessages,groupMessages:e.groupMessages,fromBuffer:t,systemMessages:[],...r?{pluginCatalog:r}:{}}}shouldDeliverPluginCatalog(){if(!this.cachedPluginCatalog||this.cachedPluginCatalog.plugins.length===0)return null;const e=this.pluginCatalogDrainCompactionSeq;e!==this.pluginCatalogLastSeenCompactionSeq&&(this.deliveredPluginNames.clear(),this.pluginCatalogLastSeenCompactionSeq=e);const t=[];for(const r of this.cachedPluginCatalog.plugins)this.deliveredPluginNames.has(r.plugin)||(this.deliveredPluginNames.add(r.plugin),t.push(r));return t.length===0?null:(l.debug("[McpServer] \u4EA4\u4ED8 pluginCatalog: plugins=[%s], compactionSeq=%d",t.map(r=>r.plugin).join(", "),e),{revision:this.cachedPluginCatalog.revision,plugins:t})}parsePositiveIntegerEnv(e,t){const r=process.env[e];if(!r||!r.trim())return t;const i=Number.parseInt(r,10);return!Number.isFinite(i)||i<=0?(l.warn(`[McpServer] ${e} \u65E0\u6548\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C ${t}ms`),t):i}sleep(e){return e<=0?Promise.resolve():new Promise(t=>setTimeout(t,e))}async buildMemoryCleanupNotification(){try{const e=await this.memoryStore.getStats(),t=Object.entries(e.by_type).filter(([,o])=>o.listable&&o.exceeded),r=Object.entries(e.by_type).flatMap(([o,a])=>a.listable?Object.entries(a.by_domain).filter(([,m])=>m.exceeded).map(([m,d])=>({memoryType:o,domain:m,domainStats:d})):[]);if(t.length===0&&r.length===0)return null;const i=t.map(([o,a])=>`${o} \u5F53\u524D ${a.domainCount} \u4E2A URI domain \u7C7B\u578B\uFF0C\u5EFA\u8BAE\u4E0A\u9650 ${a.limit} \u4E2A\uFF0C\u786C\u4E0A\u9650 ${a.hardLimit} \u4E2A`),s=r.map(({memoryType:o,domain:a,domainStats:m})=>`${o}/${a} \u7C7B\u578B\u4E0B\u5F53\u524D ${m.count} \u6761 memory\uFF0C\u5EFA\u8BAE\u4E0A\u9650 ${m.limit} \u6761\uFF0C\u786C\u4E0A\u9650 ${m.hardLimit} \u6761`),n=["\u8BF7\u8C03\u7528 get_memory_stats \u67E5\u770B\u5BB9\u91CF\u8BE6\u60C5","\u8C03\u7528 list_memories \u6309\u7C7B\u578B\u67E5\u770B\u8D85\u9650\u8BB0\u5FC6","\u5220\u9664\u4E0D\u518D\u9700\u8981\u7684 recently_memory","\u66F4\u65B0 agent_memory \u7EED\u671F\uFF08TTL \xD72\uFF09\u6216\u5220\u9664\u8FC7\u671F\u9879"].join("\uFF1B");return{msgId:`memory-cleanup-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:this.getCurrentAgentId()||void 0,content:`\u672C\u5730 memory \u5DF2\u8D85\u8FC7\u5EFA\u8BAE\u5BB9\u91CF\uFF0C\u9700\u8981\u6574\u7406\u3002${[...i,...s].join("\uFF1B")}\u3002${n}\u3002`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_cleanup",uris:r.map(o=>`${o.memoryType}/${o.domain}`)}}}catch(e){return l.warn("[McpServer] \u6784\u5EFA\u672C\u5730 memory \u6574\u7406\u63D0\u9192\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),null}}async buildMemoryRecurrenceNotification(){try{const e=await this.memoryStore.listExpiredRecentlyMemories();if(e.length===0)return null;const t=this.getCurrentAgentId();if(!t)return null;const r=e.filter(s=>s.memory_status==="recalling"),i=e.filter(s=>!r.includes(s));if(r.length>0){const s=r.slice(0,5).map(n=>n.uri).join(`
|
|
11
11
|
`);return{msgId:`memory-recurrence-pending-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:t,content:`\u5B58\u5728\u5F85\u758F\u7406\u7684\u5373\u5C06\u9057\u5FD8\u8BB0\u5FC6\uFF08\u5171 ${r.length} \u6761\uFF09\uFF0C\u8BF7\u901A\u8FC7 memory_recurrence \u83B7\u53D6\u8FDB\u884C\u6574\u7406\u3002
|
|
12
12
|
|
|
13
13
|
\u5F85\u758F\u7406\u8BB0\u5FC6\uFF1A
|
|
@@ -17,36 +17,36 @@ ${s}${r.length>5?`
|
|
|
17
17
|
|
|
18
18
|
\u5373\u5C06\u9057\u5FD8\u7684\u8BB0\u5FC6\uFF1A
|
|
19
19
|
${s}${i.length>5?`
|
|
20
|
-
...\u53CA\u5176\u4ED6 ${i.length-5} \u6761`:""}`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_expiration",uris:i.map(n=>n.uri)}}}return null}catch(e){return l.warn("[McpServer] \u6784\u5EFA\u8FD1\u671F\u8BB0\u5FC6\u8FC7\u671F\u56DE\u5FC6\u901A\u77E5\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),null}}async buildMemoryFileChangeNotification(){if(l.info(`[McpServer] buildMemoryFileChangeNotification \u88AB\u8C03\u7528: hasRoleName=${this.hasRoleName}, agentId=${this.getCurrentAgentId()??"(\u7A7A)"}`),!this.hasRoleName)return l.info("[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B\u8DF3\u8FC7: hasRoleName=false"),null;try{const e=this.getCurrentAgentId();if(!e)return l.info("[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B\u8DF3\u8FC7: agentId \u4E3A\u7A7A"),null;const t=await this.memoryStore.listByType("project_memory"),r=t.filter(c=>c.memory_status==="updating"&&c.updating_agent_id===e);if(r.length>0){this.startLeaseRenewal();const c=this.filterFileChangesByRenotifyCooldown(await this.checkMemoriesFileChanges(r));if(c.length>0){for(const
|
|
21
|
-
`);return{msgId:`memory-file-change-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:e,content:`\u68C0\u6D4B\u5230\u4EE5\u4E0B ${
|
|
20
|
+
...\u53CA\u5176\u4ED6 ${i.length-5} \u6761`:""}`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_expiration",uris:i.map(n=>n.uri)}}}return null}catch(e){return l.warn("[McpServer] \u6784\u5EFA\u8FD1\u671F\u8BB0\u5FC6\u8FC7\u671F\u56DE\u5FC6\u901A\u77E5\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),null}}async buildMemoryFileChangeNotification(){if(l.info(`[McpServer] buildMemoryFileChangeNotification \u88AB\u8C03\u7528: hasRoleName=${this.hasRoleName}, agentId=${this.getCurrentAgentId()??"(\u7A7A)"}`),!this.hasRoleName)return l.info("[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B\u8DF3\u8FC7: hasRoleName=false"),null;try{const e=this.getCurrentAgentId();if(!e)return l.info("[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B\u8DF3\u8FC7: agentId \u4E3A\u7A7A"),null;const t=await this.memoryStore.listByType("project_memory"),r=t.filter(c=>c.memory_status==="updating"&&c.updating_agent_id===e);if(r.length>0){this.startLeaseRenewal();const c=this.filterFileChangesByRenotifyCooldown(await this.checkMemoriesFileChanges(r));if(c.length>0){for(const T of c){const A=r.find($=>$.id===T.memoryId);A&&await this.memoryStore.updateMemory({uri:A.uri,memory_type:"project_memory",memory_status:"updating",updating_agent_id:e,file_md5_map:A.file_md5_map,last_check_time:A.last_check_time,bound_files:A.bound_files})}const y=Math.min(c.length,5),b=c.slice(0,5).map(T=>`- ${T.memoryUri} (\u6587\u4EF6: ${T.changedFiles.join(", ")})`).join(`
|
|
21
|
+
`);return{msgId:`memory-file-change-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:e,content:`\u68C0\u6D4B\u5230\u4EE5\u4E0B ${y} \u4E2A\u5904\u4E8E\u66F4\u65B0\u4E2D\u7684 Memory \u7ED1\u5B9A\u7684\u6587\u4EF6\u518D\u6B21\u53D8\u66F4\uFF0C\u5DF2\u66F4\u65B0 MD5 \u8BB0\u5F55\uFF1A
|
|
22
22
|
|
|
23
23
|
${b}
|
|
24
24
|
|
|
25
|
-
\u8BF7\u8C03\u7528 read_memory \u67E5\u770B\u6700\u65B0\u5185\u5BB9\u5E76\u8C03\u7528 update_memory \u66F4\u65B0\u3002`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_file_change",uris:c.slice(0,5).map(
|
|
26
|
-
`);return l.info(`[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B Step1: ${
|
|
25
|
+
\u8BF7\u8C03\u7528 read_memory \u67E5\u770B\u6700\u65B0\u5185\u5BB9\u5E76\u8C03\u7528 update_memory \u66F4\u65B0\u3002`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_file_change",uris:c.slice(0,5).map(T=>T.memoryUri),fileChanges:c.slice(0,5).map(T=>{const A=r.find($=>$.id===T.memoryId);return{uri:T.memoryUri,boundFiles:A?.bound_files??[],fileStatuses:T.fileStatuses,changedFiles:T.changedFiles}})}}}const _=r,f=new Map;for(const y of _.slice(0,5))f.set(y.uri,await this.computeBoundFileStatuses(y));const v=_.filter(y=>{const b=f.get(y.uri);return!b||b.length===0?!1:b.some(T=>T.status==="content_changed"||T.status==="not_found")}),S=_.filter(y=>!v.includes(y));if(S.length>0){l.info(`[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B Step1: ${S.length} \u4E2A updating memory \u7ED1\u5B9A\u6587\u4EF6\u65E0\u771F\u5B9E\u53D8\u66F4\uFF0C\u81EA\u52A8\u91CA\u653E: ${S.map(y=>y.uri).join(", ")}`);for(const y of S)try{await this.memoryStore.releaseMemoryClaim(y.uri),this.activeLeaseUris.delete(y.uri),await this.memoryStore.updateMemory({uri:y.uri,memory_type:"project_memory"})}catch(b){l.info(`[McpServer] \u81EA\u52A8\u91CA\u653E stale updating memory \u5931\u8D25: ${y.uri}`,b)}this.activeLeaseUris.size===0&&this.stopLeaseRenewal()}if(v.length===0)return l.info("[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B Step1: \u6240\u6709 updating memory \u7ED1\u5B9A\u6587\u4EF6\u5747\u65E0\u771F\u5B9E\u53D8\u66F4\uFF0C\u5DF2\u81EA\u52A8\u91CA\u653E\uFF0C\u8DF3\u8FC7\u59D4\u6258"),null;const k=v.slice(0,5).map(y=>`- ${y.uri} (\u6587\u4EF6: ${y.bound_files?.join(", ")||"\u672A\u77E5"})`).join(`
|
|
26
|
+
`);return l.info(`[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B Step1: ${v.length}/${r.length} \u4E2A updating memory \u6709\u771F\u5B9E\u6587\u4EF6\u53D8\u66F4\uFF0C\u89E6\u53D1\u59D4\u6258`),{msgId:`memory-update-pending-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:e,content:`\u60A8\u6709\u4EE5\u4E0B Memory \u6B63\u5904\u4E8E\u66F4\u65B0\u4E2D\u72B6\u6001\u4F46\u5C1A\u672A\u5B8C\u6210\u66F4\u65B0\uFF1A
|
|
27
27
|
|
|
28
28
|
${k}
|
|
29
29
|
|
|
30
|
-
\u8BF7\u7EE7\u7EED\u5B8C\u6210\u66F4\u65B0\u5DE5\u4F5C\u3002`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_file_change",uris:
|
|
30
|
+
\u8BF7\u7EE7\u7EED\u5B8C\u6210\u66F4\u65B0\u5DE5\u4F5C\u3002`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_file_change",uris:v.slice(0,5).map(y=>y.uri),fileChanges:v.slice(0,5).map(y=>({uri:y.uri,boundFiles:y.bound_files??[],fileStatuses:f.get(y.uri)??[]}))}}}const i=await this.memoryStore.listByType("recently_memory"),s=await this.memoryStore.listByType("agent_memory"),n=[...i,...s],o=n.filter(c=>c.bound_files&&c.bound_files.length>0);l.info(`[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B Step2: recently=${i.length}, agent=${s.length}, \u6709\u7ED1\u5B9A\u6587\u4EF6=${o.length}/${n.length}\u3002\u8BE6\u60C5: ${n.map(c=>`${c.uri}(bound=${c.bound_files?.length??0},md5=${Object.keys(c.file_md5_map??{}).length})`).join("; ")||"\u65E0 memory"}`);const a=new Map;for(const c of n)c.bound_files&&c.bound_files.length>0&&a.set(c.id,JSON.stringify(c.file_md5_map??{}));const m=this.filterFileChangesByRenotifyCooldown(await this.checkMemoriesFileChanges(n)),d=m;l.info(`[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B Step2: \u68C0\u6D4B\u5230\u53D8\u66F4=${m.length}`+(m.length>0?` \u53D8\u66F4\u9879: ${m.map(c=>`${c.memoryUri}(${c.changedFiles.join(",")})`).join("; ")}`:""));const p=n.filter(c=>{if(!c.bound_files||c.bound_files.length===0||m.some(v=>v.memoryId===c.id))return!1;const _=a.get(c.id),f=JSON.stringify(c.file_md5_map??{});return _!==f});if(p.length>0){l.info(`[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B Step2: \u56DE\u5199 ${p.length} \u4E2A listable memory \u7684\u4FEE\u6B63 MD5 map`);for(const c of p)try{await this.memoryStore.updateMemory({uri:c.uri,memory_type:c.memory_type,file_md5_map:c.file_md5_map,last_check_time:c.last_check_time})}catch(_){l.info(`[McpServer] \u56DE\u5199 listable MD5 map \u5931\u8D25: ${c.uri}`,_)}}if(d.length>0){const c=d.map(_=>`- ${_.memoryUri} (\u6587\u4EF6: ${_.changedFiles.join(", ")})`).join(`
|
|
31
31
|
`);return{msgId:`memory-file-change-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:e,content:`\u68C0\u6D4B\u5230\u4EE5\u4E0B Memory \u7ED1\u5B9A\u7684\u6587\u4EF6\u5DF2\u53D8\u66F4\uFF0C\u8BF7\u53CA\u65F6\u66F4\u65B0\uFF1A
|
|
32
32
|
|
|
33
33
|
${c}
|
|
34
34
|
|
|
35
|
-
\u8BF7\u8C03\u7528 read_memory \u67E5\u770B\u5185\u5BB9\u5E76\u8C03\u7528 update_memory \u66F4\u65B0\u3002`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_file_change",uris:d.map(
|
|
36
|
-
`);return{msgId:`memory-file-change-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:e,content:`\u68C0\u6D4B\u5230\u4EE5\u4E0B ${
|
|
35
|
+
\u8BF7\u8C03\u7528 read_memory \u67E5\u770B\u5185\u5BB9\u5E76\u8C03\u7528 update_memory \u66F4\u65B0\u3002`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_file_change",uris:d.map(_=>_.memoryUri),fileChanges:d.map(_=>{const f=n.find(v=>v.id===_.memoryId);return{uri:_.memoryUri,boundFiles:f?.bound_files??[],fileStatuses:_.fileStatuses,changedFiles:_.changedFiles}})}}}const u=t.filter(c=>c.bound_files&&c.bound_files.length>0&&(c.memory_status==="normal"||!c.memory_status)&&!this.isClaimCached(c.uri)),M=t.filter(c=>c.bound_files&&c.bound_files.length>0);l.info(`[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B Step3: project_memory \u603B\u6570=${t.length}, \u6709\u7ED1\u5B9A\u6587\u4EF6=${M.length}, \u5F85\u68C0\u6D4B(\u8FC7\u6EE4updating/claimed\u540E)=${u.length}\u3002\u88AB\u8FC7\u6EE4\u539F\u56E0: ${M.length-u.length} \u9879(memory_status=updating \u6216 isClaimCached)`);const R=new Map;for(const c of u)R.set(c.id,JSON.stringify(c.file_md5_map??{}));const h=this.filterFileChangesByRenotifyCooldown(await this.checkMemoriesFileChanges(u)),w=u.filter(c=>{if(h.some(v=>v.memoryId===c.id))return!1;const _=R.get(c.id),f=JSON.stringify(c.file_md5_map??{});return _!==f&&c.file_md5_map&&Object.keys(c.file_md5_map).length>0});if(w.length>0){l.info(`[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B Step3: \u56DE\u5199 ${w.length} \u4E2A memory \u7684\u4FEE\u6B63 MD5 map \u5230\u670D\u52A1\u7AEF\uFF08\u65E0\u57FA\u7EBF/\u952E\u7F3A\u5931\u4FEE\u590D\uFF09`);for(const c of w)try{await this.memoryStore.updateMemory({uri:c.uri,memory_type:"project_memory",file_md5_map:c.file_md5_map,last_check_time:c.last_check_time})}catch(_){l.info(`[McpServer] \u56DE\u5199\u57FA\u7EBF MD5 \u5931\u8D25: ${c.uri}`,_)}}if(l.info(`[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B Step3: \u68C0\u6D4B\u5230\u53D8\u66F4=${h.length}`+(h.length>0?` \u53D8\u66F4\u9879: ${h.map(c=>`${c.memoryUri}(${c.changedFiles.join(",")})`).join("; ")}`:"")),h.length>0){const c=h.slice(0,5),_=c.map(S=>S.memoryUri);let f=!1,v=new Set;try{const S=await this.memoryStore.claimMemoryUpdate(_,E.LEASE_DURATION_MINUTES,"project_memory");v=new Set(S.claimed_uris??[]),f=!0}catch(S){l.info("[McpServer] Server claim API \u4E0D\u53EF\u7528\uFF0C\u964D\u7EA7\u5230\u672C\u5730\u76F4\u63A5\u6807\u8BB0:",S),v=new Set(_)}if(v.size>0){const S=c.filter(y=>v.has(y.memoryUri));for(const y of S){const b=t.find(T=>T.id===y.memoryId);b&&(await this.memoryStore.updateMemory({uri:b.uri,memory_type:"project_memory",memory_status:"updating",updating_agent_id:e,file_md5_map:b.file_md5_map,last_check_time:b.last_check_time,bound_files:b.bound_files}),this.activeLeaseUris.add(b.uri))}f&&v.size>0&&this.startLeaseRenewal();const k=S.map(y=>`- ${y.memoryUri} (\u6587\u4EF6: ${y.changedFiles.join(", ")})`).join(`
|
|
36
|
+
`);return{msgId:`memory-file-change-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:e,content:`\u68C0\u6D4B\u5230\u4EE5\u4E0B ${S.length} \u4E2A\u6C38\u4E45\u8BB0\u5FC6\u7ED1\u5B9A\u7684\u6587\u4EF6\u5DF2\u53D8\u66F4\uFF0C\u5DF2\u4E3A\u60A8\u6807\u8BB0\u4E3A\u66F4\u65B0\u4E2D\uFF1A
|
|
37
37
|
|
|
38
38
|
${k}
|
|
39
39
|
|
|
40
|
-
\u8BF7\u8C03\u7528 read_memory \u67E5\u770B\u5185\u5BB9\u5E76\u8C03\u7528 update_memory \u66F4\u65B0\u3002\u66F4\u65B0\u5B8C\u6210\u540E\u72B6\u6001\u5C06\u81EA\u52A8\u6062\u590D\u4E3A\u6B63\u5E38\u3002`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_file_change",uris:v.map(f=>f.memoryUri),fileChanges:v.map(f=>{const b=t.find(E=>E.id===f.memoryId);return{uri:f.memoryUri,boundFiles:b?.bound_files??[],fileStatuses:f.fileStatuses,changedFiles:f.changedFiles}})}}}}return l.info("[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B\u5B8C\u6210: \u65E0\u53D8\u66F4\u901A\u77E5\u4EA7\u751F"),null}catch(e){return l.warn("[McpServer] \u6784\u5EFA Memory \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B\u901A\u77E5\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),null}}isClaimCached(e){return this.activeLeaseUris.has(e)}async computeBoundFileStatuses(e){const t=[];if(!e.bound_files||e.bound_files.length===0)return t;const r=e.file_md5_map||{},i=process.env.AWS_WORKSPACE_PATH||process.cwd();for(const s of e.bound_files)try{const n=await this.calculateFileMd5(s);if(n===null){const a=_.resolve(i,s);await this.verifyFileNotExist(a)&&r[s]?t.push({path:s,status:"not_found",serverMd5:r[s]}):t.push({path:s,status:"no_change"});continue}const o=r[s];o?o!==n?t.push({path:s,status:"content_changed",localMd5:n,serverMd5:o}):t.push({path:s,status:"no_change"}):t.push({path:s,status:"no_change"})}catch{t.push({path:s,status:"no_change"})}return t}fileChangeFingerprint(e){const t=e.fileStatuses.filter(r=>r.status!=="no_change").map(r=>`${r.path}:${r.status}:${r.localMd5??""}`).sort();return`${e.memoryId}|${t.join("|")}`}filterFileChangesByRenotifyCooldown(e){if(e.length===0)return e;const t=Date.now(),r=[];let i=0;for(const s of e){const n=this.fileChangeFingerprint(s),o=this.fileChangeNotifyHistory.get(n);if(o!==void 0&&t-o<Y){i++;continue}this.fileChangeNotifyHistory.set(n,t),r.push(s)}if(i>0&&l.info("[McpServer] \u6587\u4EF6\u53D8\u66F4\u63D0\u9192\u51B7\u5374\u4E2D\uFF0C\u672C\u8F6E\u6291\u5236 %d \u6761\u91CD\u590D\u901A\u77E5\uFF08cooldown=%dms\uFF09",i,Y),this.fileChangeNotifyHistory.size>K){const s=this.fileChangeNotifyHistory.size-K;let n=0;for(const o of this.fileChangeNotifyHistory.keys()){if(n>=s)break;this.fileChangeNotifyHistory.delete(o),n++}}return r}async checkMemoriesFileChanges(e){const t=[];for(const r of e)if(!(!r.bound_files||r.bound_files.length===0))try{const i=[],s=[],n={},o=r.file_md5_map||{},a=process.env.AWS_WORKSPACE_PATH||process.cwd(),m=Object.keys(o).length===0;let d=!1;for(const p of r.bound_files)try{const u=await this.calculateFileMd5(p);if(u===null){const R=_.resolve(a,p);l.info(`[McpServer] MD5 \u8BA1\u7B97\u8FD4\u56DE null: ${p} -> ${R} (workspace=${a})`);const h=o[p];h?await this.verifyFileNotExist(R)?(s.push({path:p,status:"not_found",serverMd5:h}),i.push(`${p}\uFF08\u6587\u4EF6\u627E\u4E0D\u5230 Server: ${h}\uFF09`),r.bound_files&&(r.bound_files=r.bound_files.filter(c=>c!==p))):(l.info(`[McpServer] \u6587\u4EF6\u5B58\u5728\u4F46 MD5 \u8BA1\u7B97\u5931\u8D25\uFF08\u77AC\u6001\u9519\u8BEF\uFF09\uFF0C\u8DF3\u8FC7: ${p}`),s.push({path:p,status:"no_change"})):s.push({path:p,status:"no_change"});continue}n[p]=u;const M=o[p];M?M!==u?(l.info(`[McpServer] \u6587\u4EF6\u53D8\u66F4: ${p} old=${M} new=${u} (workspace=${a})`),s.push({path:p,status:"content_changed",localMd5:u,serverMd5:M}),i.push(`${p}\uFF08\u5185\u5BB9\u53D8\u66F4 \u672C\u5730: ${u} Server: ${M}\uFF09`)):s.push({path:p,status:"no_change"}):(s.push({path:p,status:"no_change"}),d=!0,l.info(`[McpServer] \u5EFA\u7ACB\u5355\u6587\u4EF6\u57FA\u7EBF: ${p} new=${u} (workspace=${a}, memory=${r.uri})`))}catch(u){l.info(`[McpServer] \u8BA1\u7B97\u6587\u4EF6 MD5 \u5931\u8D25: ${p}`,u),s.push({path:p,status:"no_change"})}if(m&&Object.keys(n).length>0){r.file_md5_map=n,r.last_check_time=new Date().toISOString();continue}d&&Object.keys(n).length>0&&(r.file_md5_map=n,r.last_check_time=new Date().toISOString()),i.length>0&&(t.push({memoryId:r.id,memoryUri:r.uri,fileStatuses:s,changedFiles:i}),r.last_check_time=new Date().toISOString())}catch(i){l.warn(`[McpServer] \u68C0\u67E5 Memory ${r.id} \u6587\u4EF6\u53D8\u66F4\u5931\u8D25:`,i)}return t}async calculateFileMd5(e){try{const{createReadStream:t}=await import("node:fs"),{createHash:r}=await import("node:crypto"),i=process.env.AWS_WORKSPACE_PATH||process.cwd(),s=_.resolve(i,e),n=_.resolve(i),o=_.resolve(s);return!o.startsWith(n+_.sep)&&o!==n?(l.warn(`[McpServer] calculateFileMd5: \u8DEF\u5F84\u9003\u9038\u88AB\u62D2\u7EDD: ${e}`),null):new Promise(a=>{const m=r("md5"),d=t(s);d.on("data",p=>m.update(p)),d.on("end",()=>a(m.digest("hex"))),d.on("error",()=>a(null))})}catch(t){return l.info(`[McpServer] \u8BA1\u7B97\u6587\u4EF6 MD5 \u5931\u8D25: ${e}`,t),null}}async verifyFileNotExist(e){try{return await P.promises.access(e,P.constants.F_OK),!1}catch{return!0}}async handleGetMessage(e=!1){if(e){const n=await this.confirmServerMessages(this.messageBuffer.takeForceDirectMessages());return this.buildPollResult(n)}const t=this.messageBuffer.size()>0;await this.hydrateUnreadMessagesFromServer();const r=await this.confirmServerMessages(this.messageBuffer.get()),i=r.directMessages.length>0||r.groupMessages.length>0,s=t||!i;return await this.prependMemoryNotification(r,()=>this.buildMemoryRecurrenceNotification())?this.buildPollResult(r,s):await this.prependMemoryNotification(r,()=>this.buildMemoryCleanupNotification())?this.buildPollResult(r,s):this.buildPollResult(r,s)}async confirmServerMessages(e){const t=a=>!!a.msgId&&(a.channel==="meeting_invite"||a.channel==="notification")&&!a._awsServerMessage,r=a=>!!a._awsServerMessage&&!!a.msgId&&a.channel==="meeting",i=e.directMessages.filter(a=>a._awsServerMessage&&a.msgId&&!r(a)),s=e.directMessages.filter(r),n=e.groupMessages.filter(a=>a._awsServerMessage&&a.msgId!==void 0),o=e.directMessages.filter(t);if(i.length===0&&s.length===0&&n.length===0&&o.length===0)return e;try{const a=Date.now();let m=null;if(i.length>0&&(I("confirm.dm-read.start",`mark_dm_read ${i.length} \u6761\uFF08\u6807\u51C6 callTool\uFF0C\u5E26\u91CD\u8BD5\u6700\u957F\u53EF\u8FBE 30s\uFF09`),m=await this.agentClient.markDmRead(i.map(h=>h.msgId)),I("confirm.dm-read.done",`\u8017\u65F6 ${Date.now()-a}ms\uFF0C\u53EF\u4EA4\u4ED8=${m.size}/${i.length}`)),s.length>0){l.info("[McpServer][DEBUG-poll] \u786E\u8BA4\u4EA4\u4ED8\u4F1A\u8BAE\u6D88\u606F %d \u6761: %s",s.length,JSON.stringify(s.map(w=>({id:w.msgId,channel:w.channel})))),I("confirm.meeting-messages",`count=${s.length} msgs=${O(s)}\uFF08\u5F00\u59CB mark_meeting_read\uFF09`);const h=Date.now();await this.agentClient.markMeetingRead(s.map(w=>w.msgId)),I("confirm.meeting-messages.done",`mark_meeting_read \u5B8C\u6210\uFF0C\u8017\u65F6 ${Date.now()-h}ms`)}const d=new Set,p=new Map;for(const h of n){if(!h.roomId){d.add(h.msgId);continue}const w=p.get(h.roomId)||[];w.push(h.msgId),p.set(h.roomId,w)}for(const[h,w]of p){const c=await this.agentClient.markGroupRead(h,w);for(const y of c)d.add(y)}o.length>0&&(l.info("[McpServer][DEBUG-poll] \u786E\u8BA4\u4EA4\u4ED8\u901A\u77E5 %d \u6761: %s",o.length,JSON.stringify(o.map(h=>({id:h.msgId,channel:h.channel})))),I("confirm.notifications",`count=${o.length} msgs=${O(o)}\uFF08\u5F00\u59CB mark_notification_read\uFF09`),await this.agentClient.markNotificationRead(o.map(h=>h.msgId)),I("confirm.notifications.done",`mark_notification_read \u5B8C\u6210\uFF0C\u7D2F\u8BA1\u8017\u65F6 ${Date.now()-a}ms`));const u=e.directMessages.filter(h=>t(h)||r(h)||!h._awsServerMessage||!h.msgId?!0:m?m.has(h.msgId):!0),M=e.groupMessages.filter(h=>!h._awsServerMessage||h.msgId===void 0?!0:d.has(h.msgId)),R=e.directMessages.length-u.length+e.groupMessages.length-M.length;R>0&&l.info("[McpServer] \u4EA4\u4ED8\u786E\u8BA4\uFF1A\u4E22\u5F03\u5DF2\u64A4\u56DE\u6D88\u606F %d \u6761\uFF08\u79C1\u4FE1 %d / \u7FA4\u804A %d\uFF09",R,e.directMessages.length-u.length,e.groupMessages.length-M.length);for(const h of u)delete h._awsServerMessage;for(const h of M)delete h._awsServerMessage;return{directMessages:u,groupMessages:M}}catch(a){l.warn("[McpServer] \u6D88\u606F\u4EA4\u4ED8\u786E\u8BA4\u5931\u8D25\uFF0C\u964D\u7EA7\u4E3A\u76F4\u63A5\u4EA4\u4ED8: %s",a instanceof Error?a.message:String(a)),I("confirm.failed-degrade",`\u786E\u8BA4\u5931\u8D25\u964D\u7EA7\u4E3A\u76F4\u63A5\u4EA4\u4ED8: ${a instanceof Error?a.message:String(a)}\uFF08\u82E5\u6B64\u5904\u957F\u65F6\u95F4\u65E0\u4E0B\u6587\uFF0C\u8BF4\u660E\u5361\u5728\u6807\u51C6 callTool \u7684\u91CD\u8BD5\u5FAA\u73AF\u5185\uFF09`);for(const m of e.directMessages)delete m._awsServerMessage;for(const m of e.groupMessages)delete m._awsServerMessage;return e}}async hydrateUnreadMessagesFromServer(e={}){try{let t;try{t=await this.agentClient.fetchUnreadMessages({blockIfEmpty:e.blockIfEmpty??!1,blockTimeoutMs:e.blockTimeoutMs,includePipelineTasks:e.includePipelineTasks,pollMessageTimeoutMs:e.pollMessageTimeoutMs,skipGroupMessages:e.skipGroupMessages,preemptiveJoinWaitMs:e.preemptiveJoinWaitMs,propagatePollTimeout:e.propagatePollTimeout===!0})}catch(n){if(n instanceof ie)return I("hydrate.poll-timeout",`pollMessageTimeoutMs=${String(e.pollMessageTimeoutMs??"-")} \u5BA2\u6237\u7AEF\u8D85\u65F6\uFF0C\u670D\u52A1\u7AEF\u5927\u6982\u7387\u4ECD\u5728\u6267\u884C\u540C\u4E00\u8BF7\u6C42`),{failed:!0,timedOut:!0};throw n}if(t.directMessages.length===0&&t.groupMessages.length===0)return I("hydrate.empty",`blockIfEmpty=${String(e.blockIfEmpty??!1)} preemptiveJoinWaitMs=${String(e.preemptiveJoinWaitMs??"-")} \u670D\u52A1\u7AEF\u672C\u6B21\u8FD4\u56DE 0 \u6761`),{failed:!1,timedOut:!1};const r=t.directMessages.filter(n=>!this.shouldBufferIncomingMessage(n)),i=t.directMessages.filter(n=>this.shouldBufferIncomingMessage(n));r.length>0&&I("hydrate.dropped-by-filter",`dropped=${O(r)}`);const s=t.groupMessages.filter(n=>this.shouldBufferIncomingMessage(n));return i.forEach(n=>{const o=n.channel;o!=="meeting_invite"&&o!=="notification"&&(n._awsServerMessage=!0),this.messageBuffer.push(n)}),s.forEach(n=>{n._awsServerMessage=!0,this.messageBuffer.push(n)}),l.info(`[McpServer] \u5DF2\u8865\u62C9\u672A\u8BFB\u6D88\u606F\uFF0C\u79C1\u804A: ${i.length}\uFF0C\u7FA4\u804A: ${s.length}`),I("hydrate.buffered",`dm=${O(i)} gm=${s.length}`),{failed:!1,timedOut:!1}}catch(t){return l.error("[McpServer] \u8865\u62C9\u672A\u8BFB\u6D88\u606F\u5931\u8D25: %s, stack=%s",t instanceof Error?t.message:String(t),t instanceof Error?t.stack:"(no stack)"),I("hydrate.failed",`${t instanceof Error?t.message:String(t)}`),{failed:!0,timedOut:!1}}}async hydrateBrowserListenerEvents(){try{const e=await this.agentClient.fetchBrowserListenerEvents();if(e.length===0)return!1;l.info(`[McpServer] \u62C9\u53D6\u5230 ${e.length} \u6761\u6D4F\u89C8\u5668\u76D1\u542C\u5668\u4E8B\u4EF6\uFF0C\u6CE8\u5165\u6D88\u606F\u7F13\u51B2`);for(const t of e){const r=this.buildListenerEventNotification(t);r&&this.messageBuffer.push(r)}return!0}catch(e){return l.warn("[McpServer] \u62C9\u53D6\u6D4F\u89C8\u5668\u76D1\u542C\u5668\u4E8B\u4EF6\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),!1}}async hydratePluginEvents(){try{const e=await this.agentClient.fetchPluginEvents();if(e.catalog&&(this.cachedPluginCatalog=e.catalog,this.pluginCatalogDrainCompactionSeq=e.compactionSeq),e.events.length===0)return!1;l.info(`[McpServer] \u62C9\u53D6\u5230 ${e.events.length} \u6761\u63D2\u4EF6\u4E8B\u4EF6\uFF0C\u6CE8\u5165\u6D88\u606F\u7F13\u51B2`);for(const t of e.events)this.messageBuffer.push(t);return!0}catch(e){return l.warn("[McpServer] \u62C9\u53D6\u63D2\u4EF6\u4E8B\u4EF6\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),!1}}buildListenerEventNotification(e){try{const t=this.getCurrentAgentId(),r=e.browserId&&e.browserId!=="default"?`\u6D4F\u89C8\u5668[${e.browserId}]`:"\u6D4F\u89C8\u5668",i=e.selector?`\u5143\u7D20\u9009\u62E9\u5668: ${e.selector}
|
|
40
|
+
\u8BF7\u8C03\u7528 read_memory \u67E5\u770B\u5185\u5BB9\u5E76\u8C03\u7528 update_memory \u66F4\u65B0\u3002\u66F4\u65B0\u5B8C\u6210\u540E\u72B6\u6001\u5C06\u81EA\u52A8\u6062\u590D\u4E3A\u6B63\u5E38\u3002`,requireReply:!1,timestamp:new Date().toISOString(),_acodeAutoDelegate:{type:"memory_file_change",uris:S.map(y=>y.memoryUri),fileChanges:S.map(y=>{const b=t.find(T=>T.id===y.memoryId);return{uri:y.memoryUri,boundFiles:b?.bound_files??[],fileStatuses:y.fileStatuses,changedFiles:y.changedFiles}})}}}}return l.info("[McpServer] \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B\u5B8C\u6210: \u65E0\u53D8\u66F4\u901A\u77E5\u4EA7\u751F"),null}catch(e){return l.warn("[McpServer] \u6784\u5EFA Memory \u6587\u4EF6\u53D8\u66F4\u68C0\u6D4B\u901A\u77E5\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),null}}isClaimCached(e){return this.activeLeaseUris.has(e)}async computeBoundFileStatuses(e){const t=[];if(!e.bound_files||e.bound_files.length===0)return t;const r=e.file_md5_map||{},i=process.env.AWS_WORKSPACE_PATH||process.cwd();for(const s of e.bound_files)try{const n=await this.calculateFileMd5(s);if(n===null){const a=g.resolve(i,s);await this.verifyFileNotExist(a)&&r[s]?t.push({path:s,status:"not_found",serverMd5:r[s]}):t.push({path:s,status:"no_change"});continue}const o=r[s];o?o!==n?t.push({path:s,status:"content_changed",localMd5:n,serverMd5:o}):t.push({path:s,status:"no_change"}):t.push({path:s,status:"no_change"})}catch{t.push({path:s,status:"no_change"})}return t}fileChangeFingerprint(e){const t=e.fileStatuses.filter(r=>r.status!=="no_change").map(r=>`${r.path}:${r.status}:${r.localMd5??""}`).sort();return`${e.memoryId}|${t.join("|")}`}filterFileChangesByRenotifyCooldown(e){if(e.length===0)return e;const t=Date.now(),r=[];let i=0;for(const s of e){const n=this.fileChangeFingerprint(s),o=this.fileChangeNotifyHistory.get(n);if(o!==void 0&&t-o<Y){i++;continue}this.fileChangeNotifyHistory.set(n,t),r.push(s)}if(i>0&&l.info("[McpServer] \u6587\u4EF6\u53D8\u66F4\u63D0\u9192\u51B7\u5374\u4E2D\uFF0C\u672C\u8F6E\u6291\u5236 %d \u6761\u91CD\u590D\u901A\u77E5\uFF08cooldown=%dms\uFF09",i,Y),this.fileChangeNotifyHistory.size>K){const s=this.fileChangeNotifyHistory.size-K;let n=0;for(const o of this.fileChangeNotifyHistory.keys()){if(n>=s)break;this.fileChangeNotifyHistory.delete(o),n++}}return r}async checkMemoriesFileChanges(e){const t=[];for(const r of e)if(!(!r.bound_files||r.bound_files.length===0))try{const i=[],s=[],n={},o=r.file_md5_map||{},a=process.env.AWS_WORKSPACE_PATH||process.cwd(),m=Object.keys(o).length===0;let d=!1;for(const p of r.bound_files)try{const u=await this.calculateFileMd5(p);if(u===null){const R=g.resolve(a,p);l.info(`[McpServer] MD5 \u8BA1\u7B97\u8FD4\u56DE null: ${p} -> ${R} (workspace=${a})`);const h=o[p];h?await this.verifyFileNotExist(R)?(s.push({path:p,status:"not_found",serverMd5:h}),i.push(`${p}\uFF08\u6587\u4EF6\u627E\u4E0D\u5230 Server: ${h}\uFF09`),r.bound_files&&(r.bound_files=r.bound_files.filter(c=>c!==p))):(l.info(`[McpServer] \u6587\u4EF6\u5B58\u5728\u4F46 MD5 \u8BA1\u7B97\u5931\u8D25\uFF08\u77AC\u6001\u9519\u8BEF\uFF09\uFF0C\u8DF3\u8FC7: ${p}`),s.push({path:p,status:"no_change"})):s.push({path:p,status:"no_change"});continue}n[p]=u;const M=o[p];M?M!==u?(l.info(`[McpServer] \u6587\u4EF6\u53D8\u66F4: ${p} old=${M} new=${u} (workspace=${a})`),s.push({path:p,status:"content_changed",localMd5:u,serverMd5:M}),i.push(`${p}\uFF08\u5185\u5BB9\u53D8\u66F4 \u672C\u5730: ${u} Server: ${M}\uFF09`)):s.push({path:p,status:"no_change"}):(s.push({path:p,status:"no_change"}),d=!0,l.info(`[McpServer] \u5EFA\u7ACB\u5355\u6587\u4EF6\u57FA\u7EBF: ${p} new=${u} (workspace=${a}, memory=${r.uri})`))}catch(u){l.info(`[McpServer] \u8BA1\u7B97\u6587\u4EF6 MD5 \u5931\u8D25: ${p}`,u),s.push({path:p,status:"no_change"})}if(m&&Object.keys(n).length>0){r.file_md5_map=n,r.last_check_time=new Date().toISOString();continue}d&&Object.keys(n).length>0&&(r.file_md5_map=n,r.last_check_time=new Date().toISOString()),i.length>0&&(t.push({memoryId:r.id,memoryUri:r.uri,fileStatuses:s,changedFiles:i}),r.last_check_time=new Date().toISOString())}catch(i){l.warn(`[McpServer] \u68C0\u67E5 Memory ${r.id} \u6587\u4EF6\u53D8\u66F4\u5931\u8D25:`,i)}return t}async calculateFileMd5(e){try{const{createReadStream:t}=await import("node:fs"),{createHash:r}=await import("node:crypto"),i=process.env.AWS_WORKSPACE_PATH||process.cwd(),s=g.resolve(i,e),n=g.resolve(i),o=g.resolve(s);return!o.startsWith(n+g.sep)&&o!==n?(l.warn(`[McpServer] calculateFileMd5: \u8DEF\u5F84\u9003\u9038\u88AB\u62D2\u7EDD: ${e}`),null):new Promise(a=>{const m=r("md5"),d=t(s);d.on("data",p=>m.update(p)),d.on("end",()=>a(m.digest("hex"))),d.on("error",()=>a(null))})}catch(t){return l.info(`[McpServer] \u8BA1\u7B97\u6587\u4EF6 MD5 \u5931\u8D25: ${e}`,t),null}}async verifyFileNotExist(e){try{return await P.promises.access(e,P.constants.F_OK),!1}catch{return!0}}async handleGetMessage(e=!1){if(e){const n=await this.confirmServerMessages(this.messageBuffer.takeForceDirectMessages());return this.buildPollResult(n)}const t=this.messageBuffer.size()>0;await this.hydrateUnreadMessagesFromServer();const r=await this.confirmServerMessages(this.messageBuffer.get()),i=r.directMessages.length>0||r.groupMessages.length>0,s=t||!i;return await this.prependMemoryNotification(r,()=>this.buildMemoryRecurrenceNotification())?this.buildPollResult(r,s):await this.prependMemoryNotification(r,()=>this.buildMemoryCleanupNotification())?this.buildPollResult(r,s):this.buildPollResult(r,s)}async confirmServerMessages(e){const t=a=>!!a.msgId&&(a.channel==="meeting_invite"||a.channel==="notification")&&!a._awsServerMessage,r=a=>!!a._awsServerMessage&&!!a.msgId&&a.channel==="meeting",i=e.directMessages.filter(a=>a._awsServerMessage&&a.msgId&&!r(a)),s=e.directMessages.filter(r),n=e.groupMessages.filter(a=>a._awsServerMessage&&a.msgId!==void 0),o=e.directMessages.filter(t);if(i.length===0&&s.length===0&&n.length===0&&o.length===0)return e;try{const a=Date.now();let m=null;if(i.length>0&&(I("confirm.dm-read.start",`mark_dm_read ${i.length} \u6761\uFF08\u6807\u51C6 callTool\uFF0C\u5E26\u91CD\u8BD5\u6700\u957F\u53EF\u8FBE 30s\uFF09`),m=await this.agentClient.markDmRead(i.map(h=>h.msgId)),I("confirm.dm-read.done",`\u8017\u65F6 ${Date.now()-a}ms\uFF0C\u53EF\u4EA4\u4ED8=${m.size}/${i.length}`)),s.length>0){l.info("[McpServer][DEBUG-poll] \u786E\u8BA4\u4EA4\u4ED8\u4F1A\u8BAE\u6D88\u606F %d \u6761: %s",s.length,JSON.stringify(s.map(w=>({id:w.msgId,channel:w.channel})))),I("confirm.meeting-messages",`count=${s.length} msgs=${O(s)}\uFF08\u5F00\u59CB mark_meeting_read\uFF09`);const h=Date.now();await this.agentClient.markMeetingRead(s.map(w=>w.msgId)),I("confirm.meeting-messages.done",`mark_meeting_read \u5B8C\u6210\uFF0C\u8017\u65F6 ${Date.now()-h}ms`)}const d=new Set,p=new Map;for(const h of n){if(!h.roomId){d.add(h.msgId);continue}const w=p.get(h.roomId)||[];w.push(h.msgId),p.set(h.roomId,w)}for(const[h,w]of p){const c=await this.agentClient.markGroupRead(h,w);for(const _ of c)d.add(_)}o.length>0&&(l.info("[McpServer][DEBUG-poll] \u786E\u8BA4\u4EA4\u4ED8\u901A\u77E5 %d \u6761: %s",o.length,JSON.stringify(o.map(h=>({id:h.msgId,channel:h.channel})))),I("confirm.notifications",`count=${o.length} msgs=${O(o)}\uFF08\u5F00\u59CB mark_notification_read\uFF09`),await this.agentClient.markNotificationRead(o.map(h=>h.msgId)),I("confirm.notifications.done",`mark_notification_read \u5B8C\u6210\uFF0C\u7D2F\u8BA1\u8017\u65F6 ${Date.now()-a}ms`));const u=e.directMessages.filter(h=>t(h)||r(h)||!h._awsServerMessage||!h.msgId?!0:m?m.has(h.msgId):!0),M=e.groupMessages.filter(h=>!h._awsServerMessage||h.msgId===void 0?!0:d.has(h.msgId)),R=e.directMessages.length-u.length+e.groupMessages.length-M.length;R>0&&l.info("[McpServer] \u4EA4\u4ED8\u786E\u8BA4\uFF1A\u4E22\u5F03\u5DF2\u64A4\u56DE\u6D88\u606F %d \u6761\uFF08\u79C1\u4FE1 %d / \u7FA4\u804A %d\uFF09",R,e.directMessages.length-u.length,e.groupMessages.length-M.length);for(const h of u)delete h._awsServerMessage;for(const h of M)delete h._awsServerMessage;return{directMessages:u,groupMessages:M}}catch(a){l.warn("[McpServer] \u6D88\u606F\u4EA4\u4ED8\u786E\u8BA4\u5931\u8D25\uFF0C\u964D\u7EA7\u4E3A\u76F4\u63A5\u4EA4\u4ED8: %s",a instanceof Error?a.message:String(a)),I("confirm.failed-degrade",`\u786E\u8BA4\u5931\u8D25\u964D\u7EA7\u4E3A\u76F4\u63A5\u4EA4\u4ED8: ${a instanceof Error?a.message:String(a)}\uFF08\u82E5\u6B64\u5904\u957F\u65F6\u95F4\u65E0\u4E0B\u6587\uFF0C\u8BF4\u660E\u5361\u5728\u6807\u51C6 callTool \u7684\u91CD\u8BD5\u5FAA\u73AF\u5185\uFF09`);for(const m of e.directMessages)delete m._awsServerMessage;for(const m of e.groupMessages)delete m._awsServerMessage;return e}}async hydrateUnreadMessagesFromServer(e={}){try{let t;try{t=await this.agentClient.fetchUnreadMessages({blockIfEmpty:e.blockIfEmpty??!1,blockTimeoutMs:e.blockTimeoutMs,includePipelineTasks:e.includePipelineTasks,pollMessageTimeoutMs:e.pollMessageTimeoutMs,skipGroupMessages:e.skipGroupMessages,preemptiveJoinWaitMs:e.preemptiveJoinWaitMs,propagatePollTimeout:e.propagatePollTimeout===!0})}catch(n){if(n instanceof ie)return I("hydrate.poll-timeout",`pollMessageTimeoutMs=${String(e.pollMessageTimeoutMs??"-")} \u5BA2\u6237\u7AEF\u8D85\u65F6\uFF0C\u670D\u52A1\u7AEF\u5927\u6982\u7387\u4ECD\u5728\u6267\u884C\u540C\u4E00\u8BF7\u6C42`),{failed:!0,timedOut:!0};throw n}if(t.directMessages.length===0&&t.groupMessages.length===0)return I("hydrate.empty",`blockIfEmpty=${String(e.blockIfEmpty??!1)} preemptiveJoinWaitMs=${String(e.preemptiveJoinWaitMs??"-")} \u670D\u52A1\u7AEF\u672C\u6B21\u8FD4\u56DE 0 \u6761`),{failed:!1,timedOut:!1};const r=t.directMessages.filter(n=>!this.shouldBufferIncomingMessage(n)),i=t.directMessages.filter(n=>this.shouldBufferIncomingMessage(n));r.length>0&&I("hydrate.dropped-by-filter",`dropped=${O(r)}`);const s=t.groupMessages.filter(n=>this.shouldBufferIncomingMessage(n));return i.forEach(n=>{const o=n.channel;o!=="meeting_invite"&&o!=="notification"&&(n._awsServerMessage=!0),this.messageBuffer.push(n)}),s.forEach(n=>{n._awsServerMessage=!0,this.messageBuffer.push(n)}),l.info(`[McpServer] \u5DF2\u8865\u62C9\u672A\u8BFB\u6D88\u606F\uFF0C\u79C1\u804A: ${i.length}\uFF0C\u7FA4\u804A: ${s.length}`),I("hydrate.buffered",`dm=${O(i)} gm=${s.length}`),{failed:!1,timedOut:!1}}catch(t){return l.error("[McpServer] \u8865\u62C9\u672A\u8BFB\u6D88\u606F\u5931\u8D25: %s, stack=%s",t instanceof Error?t.message:String(t),t instanceof Error?t.stack:"(no stack)"),I("hydrate.failed",`${t instanceof Error?t.message:String(t)}`),{failed:!0,timedOut:!1}}}async hydrateBrowserListenerEvents(){try{const e=await this.agentClient.fetchBrowserListenerEvents();if(e.length===0)return!1;l.info(`[McpServer] \u62C9\u53D6\u5230 ${e.length} \u6761\u6D4F\u89C8\u5668\u76D1\u542C\u5668\u4E8B\u4EF6\uFF0C\u6CE8\u5165\u6D88\u606F\u7F13\u51B2`);for(const t of e){const r=this.buildListenerEventNotification(t);r&&this.messageBuffer.push(r)}return!0}catch(e){return l.warn("[McpServer] \u62C9\u53D6\u6D4F\u89C8\u5668\u76D1\u542C\u5668\u4E8B\u4EF6\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),!1}}async hydratePluginEvents(){try{const e=await this.agentClient.fetchPluginEvents();if(e.catalog&&(this.cachedPluginCatalog=e.catalog,this.pluginCatalogDrainCompactionSeq=e.compactionSeq),e.events.length===0)return!1;l.info(`[McpServer] \u62C9\u53D6\u5230 ${e.events.length} \u6761\u63D2\u4EF6\u4E8B\u4EF6\uFF0C\u6CE8\u5165\u6D88\u606F\u7F13\u51B2`);for(const t of e.events)this.messageBuffer.push(t);return!0}catch(e){return l.warn("[McpServer] \u62C9\u53D6\u63D2\u4EF6\u4E8B\u4EF6\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),!1}}buildListenerEventNotification(e){try{const t=this.getCurrentAgentId(),r=e.browserId&&e.browserId!=="default"?`\u6D4F\u89C8\u5668[${e.browserId}]`:"\u6D4F\u89C8\u5668",i=e.selector?`\u5143\u7D20\u9009\u62E9\u5668: ${e.selector}
|
|
41
41
|
`:"",s=this.formatChangeType(e.changeType),n=new Date(e.triggeredAt).toISOString(),o=`\u3010\u6D4F\u89C8\u5668\u76D1\u542C\u5668\u89E6\u53D1 | ${r} | URL: ${e.url}\u3011
|
|
42
42
|
${i}\u53D8\u5316\u7C7B\u578B: ${s}
|
|
43
43
|
\u89E6\u53D1\u65F6\u95F4: ${n}
|
|
44
|
-
\u63D0\u793A\u8BCD: ${e.prompt}`;return{msgId:`browser-listener-${e.listenerId}-${e.triggeredAt}`,senderId:"MCP-BrowserListener",senderName:"\u6D4F\u89C8\u5668\u76D1\u542C\u5668",receiverId:t||void 0,content:o,requireReply:!1,role:"system",timestamp:n}}catch{return null}}formatChangeType(e){switch(e){case"added":return"\u65B0\u589E";case"removed":return"\u5220\u9664";case"changed":return"\u6539\u53D8";case"exists":return"\u5B58\u5728";default:return"\u6539\u53D8"}}async handleGetDmHistory(e){return await this.agentClient.getDmHistory(e.targetUserId,e.limit,e.since)}async handleGetGroupHistory(e){return await this.agentClient.getGroupHistory(e.limit,e.roomId,e.since)}async handleSubmitTaskResult(e){if(!e.nodeId||typeof e.nodeId!="string")throw new Error("nodeId is required");if(!e.outputPayload||typeof e.outputPayload!="string")throw new Error("outputPayload is required");const t=Array.isArray(e.changeLog)?e.changeLog:void 0;return await this.agentClient.submitTaskResult(e.nodeId,e.outputPayload,t)}async handleStartTaskRollback(e){const t={rollbackTaskId:this.getRequiredString(e,"rollbackTaskId")};return await this.agentClient.startTaskRollback(t)}async handleFinishTaskRollback(e){const t={rollbackTaskId:this.getRequiredString(e,"rollbackTaskId"),resultMessage:this.getOptionalString(e,"resultMessage")};return await this.agentClient.finishTaskRollback(t)}async handleRejectTask(e){if(!e.nodeId||typeof e.nodeId!="string")throw new Error("nodeId is required");if(!e.reason||typeof e.reason!="string")throw new Error("reason is required");return await this.agentClient.rejectTask(e.nodeId,e.reason)}async handleTerminateTask(e){if(!e.taskInstanceId||typeof e.taskInstanceId!="string")throw new Error("taskInstanceId is required");if(!e.reason||typeof e.reason!="string")throw new Error("reason is required");return await this.agentClient.terminateTask(e.taskInstanceId,e.reason)}async handleIncomingMessage(e){if(!this.shouldBufferIncomingMessage(e)){!this.isGroupMessage(e)&&this.isPipelineTaskAssignedNotification(e)&&(l.info("[McpServer] \u6536\u5230 pipeline_task_assigned \u5524\u9192\u4FE1\u53F7\uFF0C\u7ACB\u5373\u5524\u9192 poll \u8D70 hydrate \u91CD\u67E5"),this.messageBuffer.wakeUpWaiters());return}l.info(`[McpServer] \u6536\u5230\u6D88\u606F: ${e.content?.substring(0,50)||"(\u65E0\u5185\u5BB9)"}`);const t=e.channel;t!=="meeting_invite"&&t!=="notification"&&(e._awsServerMessage=!0),this.messageBuffer.push(e),!this.isGroupMessage(e)&&e.force===!0&&this.agentClient.notifyBridgeForceMessage().catch(()=>{})}handleSystemNotify(e){const t=e?.type;if(typeof t!="string"||t!=="meeting_invite"&&t!=="meeting_creator_prompt")return;const r=this.parseNotificationPayload(e.payload);l.info("[McpServer] \u6536\u5230\u4F1A\u8BAE\u57DF\u7CFB\u7EDF\u901A\u77E5(type=%s, competitive=%s, meetingId=%s)\uFF0C\u5524\u9192 poll \u89E6\u53D1\u5F3A\u5236 hydrate",t,r?.competitive===!0?"true(\u89D2\u8272\u5E2D\u4F4D)":"false(\u76F4\u63A5\u6307\u540D)",typeof r?.meetingId=="string"?r.meetingId:typeof e.meetingId=="string"?e.meetingId:"unknown"),this.urgentMeetingHydrateRequested=!0,this.messageBuffer.wakeUpWaiters(),I("systemNotify.meeting-domain",`type=${t} \u5DF2\u7F6E\u4F4D\u5F3A\u5236 hydrate \u5E76\u5524\u9192 poll`)}parseNotificationPayload(e){if(e&&typeof e=="object")return e;if(!(typeof e!="string"||e.length===0))try{const t=JSON.parse(e);return t&&typeof t=="object"?t:void 0}catch{return}}shouldBufferIncomingMessage(e){return!e||typeof e!="object"?(l.warn("[McpServer] \u5FFD\u7565\u7ED3\u6784\u5F02\u5E38\u7684\u6D88\u606F\uFF0C\u907F\u514D\u7F13\u51B2\u533A\u6C61\u67D3"),!1):this.isSelfSentMessage(e)?(l.info("[McpServer] \u5FFD\u7565\u5F53\u524D Agent \u81EA\u5DF1\u53D1\u9001\u7684\u6D88\u606F\uFF0C\u907F\u514D poll_message/get_message \u56DE\u73AF"),!1):this.isGroupMessage(e)?!0:this.isPipelineTaskAssignedNotification(e)?(l.info("[McpServer] \u5FFD\u7565\u6301\u4E45\u5316 pipeline_task_assigned \u79C1\u4FE1\uFF0C\u4EC5\u4F5C\u4E3A poll_message \u5524\u9192\u4FE1\u53F7\u5904\u7406"),!1):!0}isGroupMessage(e){return!!e&&typeof e=="object"&&typeof e.msgId=="number"}isPipelineTaskAssignedNotification(e){const t=typeof e.content=="string"?e.content:"",r=typeof e.prompt=="string"?e.prompt:"";return t.includes("pipeline_task_assigned")||r.includes("pipeline_task_assigned")}isSelfSentMessage(e){if(!e||typeof e!="object")return!1;const t=this.getCurrentAgentId();return!!t&&typeof e.senderId=="string"&&e.senderId===t}getCurrentAgentId(){const e=this.agentClient;return typeof e.getState!="function"?null:e.getState().agentId??null}sanitizeAgentIdForTodoFile(e){return String(e).replace(/[^a-zA-Z0-9_-]/g,"_")}async buildTodoBlockNotification(){const e=this.getCurrentAgentId();if(!e)return null;const t=
|
|
44
|
+
\u63D0\u793A\u8BCD: ${e.prompt}`;return{msgId:`browser-listener-${e.listenerId}-${e.triggeredAt}`,senderId:"MCP-BrowserListener",senderName:"\u6D4F\u89C8\u5668\u76D1\u542C\u5668",receiverId:t||void 0,content:o,requireReply:!1,role:"system",timestamp:n}}catch{return null}}formatChangeType(e){switch(e){case"added":return"\u65B0\u589E";case"removed":return"\u5220\u9664";case"changed":return"\u6539\u53D8";case"exists":return"\u5B58\u5728";default:return"\u6539\u53D8"}}async handleGetDmHistory(e){return await this.agentClient.getDmHistory(e.targetUserId,e.limit,e.since)}async handleGetGroupHistory(e){return await this.agentClient.getGroupHistory(e.limit,e.roomId,e.since)}async handleSubmitTaskResult(e){if(!e.nodeId||typeof e.nodeId!="string")throw new Error("nodeId is required");if(!e.outputPayload||typeof e.outputPayload!="string")throw new Error("outputPayload is required");const t=Array.isArray(e.changeLog)?e.changeLog:void 0;return await this.agentClient.submitTaskResult(e.nodeId,e.outputPayload,t)}async handleStartTaskRollback(e){const t={rollbackTaskId:this.getRequiredString(e,"rollbackTaskId")};return await this.agentClient.startTaskRollback(t)}async handleFinishTaskRollback(e){const t={rollbackTaskId:this.getRequiredString(e,"rollbackTaskId"),resultMessage:this.getOptionalString(e,"resultMessage")};return await this.agentClient.finishTaskRollback(t)}async handleRejectTask(e){if(!e.nodeId||typeof e.nodeId!="string")throw new Error("nodeId is required");if(!e.reason||typeof e.reason!="string")throw new Error("reason is required");return await this.agentClient.rejectTask(e.nodeId,e.reason)}async handleTerminateTask(e){if(!e.taskInstanceId||typeof e.taskInstanceId!="string")throw new Error("taskInstanceId is required");if(!e.reason||typeof e.reason!="string")throw new Error("reason is required");return await this.agentClient.terminateTask(e.taskInstanceId,e.reason)}async handleIncomingMessage(e){if(!this.shouldBufferIncomingMessage(e)){!this.isGroupMessage(e)&&this.isPipelineTaskAssignedNotification(e)&&(l.info("[McpServer] \u6536\u5230 pipeline_task_assigned \u5524\u9192\u4FE1\u53F7\uFF0C\u7ACB\u5373\u5524\u9192 poll \u8D70 hydrate \u91CD\u67E5"),this.messageBuffer.wakeUpWaiters());return}l.info(`[McpServer] \u6536\u5230\u6D88\u606F: ${e.content?.substring(0,50)||"(\u65E0\u5185\u5BB9)"}`);const t=e.channel;t!=="meeting_invite"&&t!=="notification"&&(e._awsServerMessage=!0),this.messageBuffer.push(e),!this.isGroupMessage(e)&&e.force===!0&&this.agentClient.notifyBridgeForceMessage().catch(()=>{})}handleSystemNotify(e){const t=e?.type;if(typeof t!="string"||t!=="meeting_invite"&&t!=="meeting_creator_prompt")return;const r=this.parseNotificationPayload(e.payload);l.info("[McpServer] \u6536\u5230\u4F1A\u8BAE\u57DF\u7CFB\u7EDF\u901A\u77E5(type=%s, competitive=%s, meetingId=%s)\uFF0C\u5524\u9192 poll \u89E6\u53D1\u5F3A\u5236 hydrate",t,r?.competitive===!0?"true(\u89D2\u8272\u5E2D\u4F4D)":"false(\u76F4\u63A5\u6307\u540D)",typeof r?.meetingId=="string"?r.meetingId:typeof e.meetingId=="string"?e.meetingId:"unknown"),this.urgentMeetingHydrateRequested=!0,this.messageBuffer.wakeUpWaiters(),I("systemNotify.meeting-domain",`type=${t} \u5DF2\u7F6E\u4F4D\u5F3A\u5236 hydrate \u5E76\u5524\u9192 poll`)}parseNotificationPayload(e){if(e&&typeof e=="object")return e;if(!(typeof e!="string"||e.length===0))try{const t=JSON.parse(e);return t&&typeof t=="object"?t:void 0}catch{return}}shouldBufferIncomingMessage(e){return!e||typeof e!="object"?(l.warn("[McpServer] \u5FFD\u7565\u7ED3\u6784\u5F02\u5E38\u7684\u6D88\u606F\uFF0C\u907F\u514D\u7F13\u51B2\u533A\u6C61\u67D3"),!1):this.isSelfSentMessage(e)?(l.info("[McpServer] \u5FFD\u7565\u5F53\u524D Agent \u81EA\u5DF1\u53D1\u9001\u7684\u6D88\u606F\uFF0C\u907F\u514D poll_message/get_message \u56DE\u73AF"),!1):this.isGroupMessage(e)?!0:this.isPipelineTaskAssignedNotification(e)?(l.info("[McpServer] \u5FFD\u7565\u6301\u4E45\u5316 pipeline_task_assigned \u79C1\u4FE1\uFF0C\u4EC5\u4F5C\u4E3A poll_message \u5524\u9192\u4FE1\u53F7\u5904\u7406"),!1):!0}isGroupMessage(e){return!!e&&typeof e=="object"&&typeof e.msgId=="number"}isPipelineTaskAssignedNotification(e){const t=typeof e.content=="string"?e.content:"",r=typeof e.prompt=="string"?e.prompt:"";return t.includes("pipeline_task_assigned")||r.includes("pipeline_task_assigned")}isSelfSentMessage(e){if(!e||typeof e!="object")return!1;const t=this.getCurrentAgentId();return!!t&&typeof e.senderId=="string"&&e.senderId===t}getCurrentAgentId(){const e=this.agentClient;return typeof e.getState!="function"?null:e.getState().agentId??null}sanitizeAgentIdForTodoFile(e){return String(e).replace(/[^a-zA-Z0-9_-]/g,"_")}async buildTodoBlockNotification(){const e=this.getCurrentAgentId();if(!e)return null;const t=g.join(U(),"todos",`${this.sanitizeAgentIdForTodoFile(e)}.json`);let r;try{r=await P.promises.readFile(t,"utf-8")}catch(o){return o?.code==="ENOENT"||l.warn("[McpServer] \u8BFB\u53D6 Todo \u6587\u4EF6\u5931\u8D25\uFF0C\u653E\u884C poll_message: %s",o instanceof Error?o.message:String(o)),null}let i;try{i=JSON.parse(r)}catch(o){return l.warn("[McpServer] \u89E3\u6790 Todo \u6587\u4EF6\u5931\u8D25\uFF0C\u653E\u884C poll_message: %s",o instanceof Error?o.message:String(o)),null}return(Array.isArray(i.todos)?i.todos:[]).filter(o=>o&&typeof o=="object"&&typeof o.content=="string"&&(o.status==="pending"||o.status==="in_progress"||o.status==="cancelled")).length===0?null:{msgId:`todo-block-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:e,content:"\u5F53\u524D\u6709\u672A\u5904\u7406\u7684todo list\u4E0D\u80FD\u4F7F\u7528poll_message\u963B\u585E\u83B7\u53D6\u6D88\u606F",requireReply:!1,timestamp:new Date().toISOString()}}async buildBackgroundTaskNotification(){try{const e=this.getCurrentAgentId();if(!e)return null;const t=g.join(U(),"bg-tasks",this.sanitizeAgentIdForTodoFile(e),"state.json");let r;try{r=await P.promises.readFile(t,"utf-8")}catch(d){if(d?.code==="ENOENT")return null;throw d}const i=JSON.parse(r),s=Array.isArray(i.tasks)?i.tasks:[],n=s.filter(d=>d&&typeof d=="object"&&typeof d.taskId=="string"&&d.status!=="running"&&!d.notified);if(n.length===0)return null;const o=n.slice(0,5),a=o.map(d=>`[bg:${d.taskId}] ${d.status} (exitCode=${d.exitCode??"?"}) ${d.commandPreview??""}
|
|
45
45
|
lines=${d.outputLineCount??0} bytes=${d.outputBytes??0}
|
|
46
46
|
Use read_background_output(task_id="${d.taskId}", mode="tail", lines=50) for full output.`).join(`
|
|
47
47
|
|
|
48
48
|
`),m=n.length>o.length?`
|
|
49
49
|
|
|
50
50
|
\uFF08\u53E6\u6709 ${n.length-o.length} \u6761\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u7528 list_background_commands \u67E5\u770B\uFF09`:"";return await P.promises.writeFile(t,JSON.stringify({...i,updatedAt:Date.now(),tasks:s.map(d=>n.some(p=>p.taskId===d.taskId)?{...d,notified:!0}:d)},null,2),"utf-8").catch(d=>{l.warn("[McpServer] \u6807\u8BB0\u540E\u53F0\u4EFB\u52A1\u5DF2\u901A\u77E5\u5931\u8D25: %s",d?.message??d)}),{msgId:`bg-notif-${Date.now()}`,senderId:"MCP-System",senderName:"ACode Background",receiverId:e,content:`[Background commands completed]
|
|
51
|
-
${a}${m}`,requireReply:!1,timestamp:new Date().toISOString()}}catch(e){return l.warn("[McpServer] \u6784\u5EFA\u540E\u53F0\u547D\u4EE4\u5B8C\u6210\u901A\u77E5\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),null}}async start(){const e=new ee;await this.server.connect(e),await this.agentClient.initialize(),l.info("[McpServer] MCP Server \u5DF2\u542F\u52A8\uFF0C\u7B49\u5F85\u8FDE\u63A5..."),l.info(`[McpServer] \u53EF\u7528\u5DE5\u5177: ${
|
|
51
|
+
${a}${m}`,requireReply:!1,timestamp:new Date().toISOString()}}catch(e){return l.warn("[McpServer] \u6784\u5EFA\u540E\u53F0\u547D\u4EE4\u5B8C\u6210\u901A\u77E5\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),null}}async start(){const e=new ee;await this.server.connect(e),await this.agentClient.initialize(),l.info("[McpServer] MCP Server \u5DF2\u542F\u52A8\uFF0C\u7B49\u5F85\u8FDE\u63A5..."),l.info(`[McpServer] \u53EF\u7528\u5DE5\u5177: ${j.map(t=>t.name).join(", ")}`),l.info(`[McpServer] \u5DE5\u4F5C\u76EE\u5F55: ${process.env.AWS_WORKSPACE_PATH||process.cwd()}`),H&&l.info("[McpServer] \u964D\u7EA7\u6A21\u5F0F\u5DF2\u542F\u7528\uFF0C\u6CE8\u518C\u5931\u8D25\u65F6\u5C06\u4EE5\u79BB\u7EBF\u6A21\u5F0F\u7EE7\u7EED\u8FD0\u884C"),this.autoRegisterInBackground(),l.info("[McpServer] MCP Server \u5DF2\u5C31\u7EEA\uFF0C\u5DE5\u5177\u5DF2\u53EF\u7528")}startLeaseRenewal(){this.leaseRenewalTimer||(this.leaseRenewalTimer=setInterval(async()=>{try{if(!this.getCurrentAgentId())return;(await this.memoryStore.renewMemoryLease(E.LEASE_DURATION_MINUTES)).renewed===0&&(l.warn("[McpServer] \u79DF\u7EA6\u5DF2\u4E22\u5931\uFF08renewed=0\uFF09\uFF0C\u505C\u6B62\u7EED\u671F\u5B9A\u65F6\u5668"),this.stopLeaseRenewal())}catch(e){const t=e instanceof Error?e.message:String(e);t.includes("not supported")?(l.info("[McpServer] \u5F53\u524D\u5B58\u50A8\u4E0D\u652F\u6301\u79DF\u7EA6\u7EED\u671F\uFF0C\u505C\u6B62\u7EED\u671F\u5B9A\u65F6\u5668"),this.stopLeaseRenewal()):l.warn("[McpServer] \u7EED\u671F\u79DF\u7EA6\u5931\u8D25: %s",t)}},E.LEASE_RENEW_INTERVAL_MS),l.info("[McpServer] \u79DF\u7EA6\u7EED\u671F\u5B9A\u65F6\u5668\u5DF2\u542F\u52A8\uFF0C\u95F4\u9694: 60s"))}stopLeaseRenewal(){this.leaseRenewalTimer&&(clearInterval(this.leaseRenewalTimer),this.leaseRenewalTimer=null,l.info("[McpServer] \u79DF\u7EA6\u7EED\u671F\u5B9A\u65F6\u5668\u5DF2\u505C\u6B62"))}stop(){this.stopLeaseRenewal(),this.getCurrentAgentId()&&this.memoryStore.releaseAllMemoryClaims().catch(t=>l.warn("[McpServer] \u91CA\u653E claim \u5931\u8D25: %s",t instanceof Error?t.message:String(t))),this.agentClient.stop(),l.info("[McpServer] MCP Server \u5DF2\u505C\u6B62")}async autoRegister(){const e=this.agentClient.getAgentConfig();l.info(`[McpServer] \u89D2\u8272: "${e.roleName}"`),l.info(`[McpServer] AgentID: "${e.agentId||"(\u81EA\u52A8\u5206\u914D)"}"`),l.info("[McpServer] \u6B63\u5728\u8BF7\u6C42\u8C03\u5EA6\u4E2D\u5FC3\u6CE8\u518C...");let t=0;for(;;)try{const r=await this.agentClient.register();l.info(`[McpServer] \u6CE8\u518C\u6210\u529F: ${r.displayName} (${r.agentId})`),l.info(`[McpServer] \u72B6\u6001: ${r.status}`),this.startListeningInternal(),l.info("[McpServer] \u6D88\u606F\u76D1\u542C\u5DF2\u81EA\u52A8\u542F\u52A8");const i=this.agentClient.getStatusReporter();i&&i.reportWaitingInput(),await this.reportMemoryStatsToServer(),this.maybeSyncPermanentMemoryToServer();return}catch(r){const i=r instanceof Error?r.message:String(r);if(t++,D>0&&t>=D){if(H){l.warn(`[McpServer] \u6CE8\u518C\u8D85\u8FC7\u6700\u5927\u91CD\u8BD5\u6B21\u6570 (${D}\u6B21)\uFF0C\u8FDB\u5165\u964D\u7EA7\u6A21\u5F0F`),this.agentClient.enterDegradedMode(),this.startListeningInternal(),l.info("[McpServer] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u6D88\u606F\u76D1\u542C\u5DF2\u542F\u52A8"),this.agentClient.retryRegistrationInBackground(ce,me);const s=this.agentClient.getStatusReporter();s&&s.reportWaitingInput(),await this.reportMemoryStatsToServer();return}throw new Error(`\u6CE8\u518C\u8D85\u8FC7\u6700\u5927\u91CD\u8BD5\u6B21\u6570 (${D}\u6B21)\uFF0C\u653E\u5F03\u542F\u52A8: ${i}`)}l.warn(`[McpServer] \u6CE8\u518C\u5931\u8D25\uFF0C\u7B49\u5F85\u91CD\u8BD5... (${t}/${D}): ${i}`),await this.delay(le)}}autoRegisterInBackground(){this.autoRegister().catch(e=>{l.error("[McpServer] \u540E\u53F0\u6CE8\u518C\u5F02\u5E38: %s, stack=%s",e instanceof Error?e.message:String(e),e instanceof Error?e.stack:"(no stack)")})}startListeningInternal(){l.info("[McpServer] startListeningInternal \u88AB\u8C03\u7528");const e=t=>{l.info(`[McpServer] profileHandler \u88AB\u8C03\u7528\uFF0CdisplayName: ${t.displayName}`),this.messageBuffer.setProfileMessage(t),this.profileSynced=!0,this.syncRoleMemoryStore(t),this.maybeSyncPermanentMemoryToServer()};if(this.agentClient.isInDegradedMode()){l.info("[McpServer] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u8DF3\u8FC7 WebSocket \u8FDE\u63A5\uFF0C\u4EC5\u542F\u52A8\u672C\u5730\u6D88\u606F\u76D1\u542C"),this.agentClient.start(this.handleIncomingMessage.bind(this),{pollMode:!1,profileHandler:e,systemNotifyHandler:this.handleSystemNotify.bind(this)}),this.activelySyncProfile();return}this.agentClient.start(this.handleIncomingMessage.bind(this),{pollMode:!1,profileHandler:e,systemNotifyHandler:this.handleSystemNotify.bind(this)}),this.activelySyncProfile()}activelySyncProfile(){this.profileSynced||this.agentClient.getProfile().then(e=>{this.profileSynced||(this.profileSynced=!0,l.info(`[McpServer] \u4E3B\u52A8\u83B7\u53D6 profile \u6210\u529F: displayName=${e.displayName}, roleName="${e.roleName??"(\u7A7A)"}"`),this.messageBuffer.setProfileMessage(e),this.syncRoleMemoryStore(e),this.maybeSyncPermanentMemoryToServer())}).catch(e=>{l.warn("[McpServer] \u4E3B\u52A8\u83B7\u53D6 profile \u5931\u8D25: %s",e instanceof Error?e.message:String(e))})}delay(e){return new Promise(t=>setTimeout(t,e))}}export{E as McpServer};
|
|
52
52
|
|