aws-runtime-bridge 1.9.41 → 1.9.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/adapter/AcodeSdkAdapter.d.ts +6 -0
  2. package/dist/adapter/AcodeSdkAdapter.d.ts.map +1 -1
  3. package/dist/adapter/AcodeSdkAdapter.js +3 -1
  4. package/dist/adapter/CodexSdkAdapter.js +1 -1
  5. package/dist/adapter/OpencodeSdkAdapter.js +1 -1
  6. package/dist/index.js +4 -0
  7. package/dist/routes/file-browser.d.ts.map +1 -1
  8. package/dist/routes/file-browser.js +47 -1
  9. package/dist/routes/file-diff-rollback.d.ts +56 -0
  10. package/dist/routes/file-diff-rollback.d.ts.map +1 -0
  11. package/dist/routes/file-diff-rollback.js +278 -0
  12. package/dist/routes/processes.d.ts.map +1 -1
  13. package/dist/routes/processes.js +7 -0
  14. package/dist/routes/sessions.js +4 -1
  15. package/dist/routes/task-context.d.ts +8 -0
  16. package/dist/routes/task-context.d.ts.map +1 -0
  17. package/dist/routes/task-context.js +78 -0
  18. package/dist/routes/terminal.d.ts +12 -0
  19. package/dist/routes/terminal.d.ts.map +1 -1
  20. package/dist/routes/terminal.js +40 -0
  21. package/dist/services/workspace-files.d.ts +48 -0
  22. package/dist/services/workspace-files.d.ts.map +1 -1
  23. package/dist/services/workspace-files.js +285 -0
  24. package/dist/services/workspaceTaskChangeWatcher.d.ts +87 -0
  25. package/dist/services/workspaceTaskChangeWatcher.d.ts.map +1 -0
  26. package/dist/services/workspaceTaskChangeWatcher.js +483 -0
  27. package/package/acode/dist/built-in-file-tools.d.ts +9 -1
  28. package/package/acode/dist/built-in-file-tools.d.ts.map +1 -1
  29. package/package/acode/dist/built-in-file-tools.js +86 -5
  30. package/package/acode/dist/mcpToolNames.js +1 -1
  31. package/package/acode/dist/runtime.d.ts +11 -1
  32. package/package/acode/dist/runtime.d.ts.map +1 -1
  33. package/package/acode/dist/runtime.js +63 -12
  34. package/package/acode/dist/sub-agent-tools.d.ts.map +1 -1
  35. package/package/acode/dist/sub-agent-tools.js +5 -4
  36. package/package/acode/dist/types.d.ts +6 -0
  37. package/package/acode/dist/types.d.ts.map +1 -1
  38. package/package/aws-client-agent-mcp/dist/agent-client.d.ts +3 -7
  39. package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +1 -1
  40. package/package/aws-client-agent-mcp/dist/agent-client.js +10 -16
  41. package/package/aws-client-agent-mcp/dist/agent-client.js.map +1 -1
  42. package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +1 -1
  43. package/package/aws-client-agent-mcp/dist/http-client.js +7 -2
  44. package/package/aws-client-agent-mcp/dist/http-client.js.map +1 -1
  45. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +10 -1
  46. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
  47. package/package/aws-client-agent-mcp/dist/mcp-server.js +37 -24
  48. package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
  49. package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +0 -4
  50. package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts.map +1 -1
  51. package/package/aws-client-agent-mcp/dist/mcp-tools.js +44 -55
  52. package/package/aws-client-agent-mcp/dist/mcp-tools.js.map +1 -1
  53. package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +8 -1
  54. package/package/aws-client-agent-mcp/dist/message-buffer.d.ts.map +1 -1
  55. package/package/aws-client-agent-mcp/dist/message-buffer.js +24 -21
  56. package/package/aws-client-agent-mcp/dist/message-buffer.js.map +1 -1
  57. package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +1 -1
  58. package/package/aws-client-agent-mcp/dist/status-reporter.js +0 -1
  59. package/package/aws-client-agent-mcp/dist/status-reporter.js.map +1 -1
  60. package/package/aws-client-agent-mcp/dist/types.d.ts +0 -11
  61. package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
  62. package/package.json +3 -1
@@ -43,6 +43,12 @@ type ACodeRuntimeConstructor = new (config: {
43
43
  memory?: string;
44
44
  env?: Record<string, string>;
45
45
  subAgentManager?: SubAgentManager;
46
+ onFileChange?: (record: {
47
+ filePath: string;
48
+ operation: 'CREATE' | 'UPDATE' | 'DELETE';
49
+ beforeContent: string | null;
50
+ afterContent: string | null;
51
+ }) => void | Promise<void>;
46
52
  }) => ACodeRuntimeInstance;
47
53
  export declare function toAcodeEsmImportSpecifier(modulePath: string): string;
48
54
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"AcodeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/AcodeSdkAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AcodeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/AcodeSdkAdapter.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,7 @@ import { getInstanceAiConfigDebugLogger } from "../utils/instance-ai-config-debu
21
21
  import { redactForLog } from "../services/instance-ai-config-service.js";
22
22
  import { subAgentManager } from "../services/sub-agent-manager-instance.js";
23
23
  import { resolveAcodeProviderConfig, } from "./instance-ai-config-resolver.js";
24
+ import { recordAgentFileChange } from "../services/workspaceTaskChangeWatcher.js";
24
25
  import { isMessagePollingIdleCommand, isPollMessageToolDescriptor, } from "./acode-tool-names.js";
25
26
  import { IdlePollLoop } from "./idle-poll-loop.js";
26
27
  import { getCanonicalMcpDetail, getToolActionInfo } from "./types.js";
@@ -58,7 +59,7 @@ const ACODE_SUBMIT_TIMEOUT_MS = 120000; // 120秒(2分钟)
58
59
  // 兜底超时:mcp_status 缓存能力后,等待 skill_status 的最长时间。
59
60
  // 超时后单独发出 MCP 能力清单,避免 skill_status 事件缺失导致"暂无能力清单"。
60
61
  const CAPABILITIES_FALLBACK_MS = 3000;
61
- const ACODE_MESSAGE_POLLING_IDLE_PROMPT = "系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看已由后台静默抢占的任务详情并直接处理;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。";
62
+ const ACODE_MESSAGE_POLLING_IDLE_PROMPT = "系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看已由后台静默抢占的任务详情并直接处理;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。 调用 submit_task_result 时请一并传入 changeLog(本次处理的文件/内容变更日志,包含 operation 与 path),用于 AI 回退。";
62
63
  function getAcodePackageEntryPath() {
63
64
  return path.resolve(getReleasedAcodeEntryPath());
64
65
  }
@@ -659,6 +660,7 @@ export class AcodeSdkAdapter extends EventEmitter {
659
660
  ...(config.subAgentModel ? { subAgentModel: config.subAgentModel } : {}),
660
661
  ...(config.subAgentEffort ? { subAgentEffort: config.subAgentEffort } : {}),
661
662
  ...(config.instanceAiConfig?.effort ? { effort: config.instanceAiConfig.effort } : {}),
663
+ onFileChange: (record) => recordAgentFileChange(record),
662
664
  });
663
665
  const debugLogger = getInstanceAiConfigDebugLogger();
664
666
  void debugLogger.log({
@@ -822,7 +822,7 @@ export class CodexSdkAdapter extends EventEmitter {
822
822
  toIdlePrompt(command) {
823
823
  const trimmed = command.trim();
824
824
  if (this.isMessagePollingIdleCommand(trimmed)) {
825
- return '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看已由后台静默抢占的任务详情并直接处理;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。';
825
+ return '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看已由后台静默抢占的任务详情并直接处理;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。 调用 submit_task_result 时请一并传入 changeLog(本次处理的文件/内容变更日志,包含 operation 与 path),用于 AI 回退。';
826
826
  }
827
827
  return trimmed;
828
828
  }
@@ -865,7 +865,7 @@ export class OpencodeSdkAdapter extends EventEmitter {
865
865
  toIdlePrompt(command) {
866
866
  const trimmed = command.trim();
867
867
  if (this.isMessagePollingIdleCommand(trimmed)) {
868
- return '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看已由后台静默抢占的任务详情并直接处理;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。';
868
+ return '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看已由后台静默抢占的任务详情并直接处理;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。 调用 submit_task_result 时请一并传入 changeLog(本次处理的文件/内容变更日志,包含 operation 与 path),用于 AI 回退。';
869
869
  }
870
870
  return trimmed;
871
871
  }
package/dist/index.js CHANGED
@@ -26,6 +26,8 @@ import { fileBrowserRouter } from "./routes/file-browser.js";
26
26
  import { aiSourcesRouter } from "./routes/ai-sources.js";
27
27
  import { processesRouter } from "./routes/processes.js";
28
28
  import { systemMetricsRouter } from "./routes/system-metrics.js";
29
+ import { taskContextRouter } from "./routes/task-context.js";
30
+ import { fileDiffRollbackRouter } from "./routes/file-diff-rollback.js";
29
31
  import { attachPtyWebSocketServer, closeAllPtySessions, ptyRouter, ptySessions, } from "./routes/pty.js";
30
32
  import { runtimeBindingRouter, logRuntimeBindingStartupState, } from "./routes/runtime-binding.js";
31
33
  import { dashboardRouter } from "./routes/dashboard.js";
@@ -309,6 +311,8 @@ app.use("/runtime", memoryRouter);
309
311
  app.use("/runtime", acodeTodosRouter);
310
312
  app.use("/runtime", subAgentsRouter);
311
313
  app.use("/runtime", aiSourcesRouter);
314
+ app.use("/runtime", taskContextRouter);
315
+ app.use("/runtime", fileDiffRollbackRouter);
312
316
  app.use("/pty", ptyRouter);
313
317
  app.use("/", dashboardRouter);
314
318
  app.use("/api/file-browser", fileBrowserRouter);
@@ -1 +1 @@
1
- {"version":3,"file":"file-browser.d.ts","sourceRoot":"","sources":["../../src/routes/file-browser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAyB5B,eAAO,MAAM,iBAAiB,4CAAW,CAAC;AAE1C,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAUhD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,mBAAmB;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAMD,UAAU,iBAAkB,SAAQ,eAAe;IACjD,QAAQ,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,qBAAsB,SAAQ,mBAAmB;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,IAAI,EAAE,iBAAiB,EAAE,CAAC;CAC3B;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAiBD,wBAAgB,kCAAkC,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAUvI;AAED,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAgBzF;AAyFD;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,WAAW,SAAK,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAkB5F;AAoCD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAuBpE;AA6BD,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,OAAO,CAWhH"}
1
+ {"version":3,"file":"file-browser.d.ts","sourceRoot":"","sources":["../../src/routes/file-browser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,MAAM,MAAM,QAAQ,CAAC;AA2B5B,eAAO,MAAM,iBAAiB,4CAAW,CAAC;AAE1C,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAUhD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,mBAAmB;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAMD,UAAU,iBAAkB,SAAQ,eAAe;IACjD,QAAQ,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,qBAAsB,SAAQ,mBAAmB;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,IAAI,EAAE,iBAAiB,EAAE,CAAC;CAC3B;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAiBD,wBAAgB,kCAAkC,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAUvI;AAED,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAgBzF;AAyFD;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,WAAW,SAAK,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAkB5F;AAoCD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAuBpE;AA6BD,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,OAAO,CAWhH"}
@@ -10,7 +10,7 @@ import { Router } from 'express';
10
10
  import multer from 'multer';
11
11
  import { allowHostFileBrowser } from '../config.js';
12
12
  import { validateToken } from '../middleware/auth.js';
13
- import { chmodWorkspaceEntry, createWorkspaceEntry, deleteWorkspaceEntry, extractWorkspaceArchive, getWorkspaceEntryProperties, listWorkspaceDirectory, moveWorkspaceEntry, previewWorkspaceDocument, readWorkspaceFile, renameWorkspaceEntry, resolveWorkspaceDownloadTarget, streamWorkspaceDirectoryZip, uploadWorkspaceFiles, writeWorkspaceFile, } from '../services/workspace-files.js';
13
+ import { chmodWorkspaceEntry, createWorkspaceEntry, deleteWorkspaceEntry, extractWorkspaceArchive, getWorkspaceEntryProperties, listWorkspaceDirectory, moveWorkspaceEntry, previewWorkspaceDocument, readWorkspaceFile, renameWorkspaceEntry, replaceWorkspaceFiles, resolveWorkspaceDownloadTarget, searchWorkspaceFiles, streamWorkspaceDirectoryZip, uploadWorkspaceFiles, writeWorkspaceFile, } from '../services/workspace-files.js';
14
14
  import { unwatchWorkspaceFile, watchWorkspaceFile } from '../services/workspace-watch.js';
15
15
  import { createLogger } from '../utils/logger.js';
16
16
  const log = createLogger('file-browser');
@@ -736,3 +736,49 @@ fileBrowserRouter.post('/workspace/extract', validateToken, async (req, res) =>
736
736
  res.status(400).json({ error: err.message });
737
737
  }
738
738
  });
739
+ /**
740
+ * 在工作区范围内搜索文件内容。
741
+ * POST /api/file-browser/workspace/search
742
+ */
743
+ fileBrowserRouter.post('/workspace/search', validateToken, async (req, res) => {
744
+ try {
745
+ const { workspacePath, keyword, fileMask, ignoreCase, maxResults, mode } = req.body || {};
746
+ const result = await searchWorkspaceFiles({
747
+ workspacePath: String(workspacePath || ''),
748
+ keyword: String(keyword || ''),
749
+ fileMask: fileMask !== undefined ? String(fileMask) : undefined,
750
+ ignoreCase: toBoolean(ignoreCase),
751
+ maxResults: typeof maxResults === 'number' ? maxResults : undefined,
752
+ mode: mode === 'filename' ? 'filename' : 'content'
753
+ });
754
+ res.json(result);
755
+ }
756
+ catch (error) {
757
+ const err = error;
758
+ log.error('failed to search workspace files:', err);
759
+ res.status(400).json({ error: err.message });
760
+ }
761
+ });
762
+ /**
763
+ * 在工作区范围内批量替换文件内容。
764
+ * POST /api/file-browser/workspace/replace
765
+ */
766
+ fileBrowserRouter.post('/workspace/replace', validateToken, async (req, res) => {
767
+ try {
768
+ const { workspacePath, keyword, replacement, fileMask, ignoreCase, filePath } = req.body || {};
769
+ const result = await replaceWorkspaceFiles({
770
+ workspacePath: String(workspacePath || ''),
771
+ keyword: String(keyword || ''),
772
+ replacement: String(replacement ?? ''),
773
+ fileMask: fileMask !== undefined ? String(fileMask) : undefined,
774
+ ignoreCase: toBoolean(ignoreCase),
775
+ filePath: filePath !== undefined ? String(filePath) : undefined
776
+ });
777
+ res.json(result);
778
+ }
779
+ catch (error) {
780
+ const err = error;
781
+ log.error('failed to replace workspace files:', err);
782
+ res.status(400).json({ error: err.message });
783
+ }
784
+ });
@@ -0,0 +1,56 @@
1
+ /**
2
+ * 文件差异回滚路由
3
+ *
4
+ * 根据变更记录的反向操作执行文件回滚。
5
+ * 与全量快照还原不同的是,只还原被修改过的文件。
6
+ *
7
+ * 核心流程(两步走):
8
+ * 1. 内存预检:在内存中按修改时间逆序模拟所有反向操作,验证完整性。不触碰文件系统。
9
+ * - CREATE:检查文件是否存在,准备删除
10
+ * - UPDATE:读取 diff patch,读取当前文件内容,在内存中应用反向 patch 验证是否成功
11
+ * - DELETE:读取删除前的完整内容,准备重建
12
+ * 2. 原子执行:预检全部通过后,批量写入文件系统;任一预检失败则直接返回错误,
13
+ * 由调用方决定是否使用快照进行完整覆盖。
14
+ *
15
+ * 反向操作映射:
16
+ * CREATE → DELETE(删除创建的文件)
17
+ * UPDATE → 应用反向 diff patch(精确撤销修改的部分,保留用户后续改动)
18
+ * DELETE → 重新创建被删除的文件(根据 cacheKey 从 bridge 本地缓存读取)
19
+ */
20
+ export declare const fileDiffRollbackRouter: import("express-serve-static-core").Router;
21
+ export interface ReverseOperation {
22
+ /** 原始操作类型 */
23
+ originalOperation: 'CREATE' | 'UPDATE' | 'DELETE';
24
+ /** 文件路径(相对于工作区) */
25
+ relativePath: string;
26
+ /** bridge 本地缓存 key,UPDATE/DELETE 时用于读取变更前内容或 diff */
27
+ cacheKey: string | null;
28
+ /** 原始创建时间(用于日志与排序) */
29
+ createdAt: string;
30
+ }
31
+ export interface DiffRollbackRequest {
32
+ workspacePath: string;
33
+ operations: ReverseOperation[];
34
+ }
35
+ /** 单条操作的执行状态 */
36
+ export interface OperationDetail {
37
+ relativePath: string;
38
+ originalOperation: string;
39
+ /** 当前阶段状态 */
40
+ status: 'preview_passed' | 'preview_failed' | 'success' | 'failed' | 'skipped';
41
+ /** 计划执行的操作描述 */
42
+ action?: string;
43
+ /** 失败或跳过的原因 */
44
+ reason?: string;
45
+ }
46
+ export interface DiffRollbackResult {
47
+ ok: boolean;
48
+ restored: number;
49
+ skipped: number;
50
+ errors: string[];
51
+ /** 当 ok=false 且未执行写盘时为 true */
52
+ previewFailed?: boolean;
53
+ /** 每条操作的详细状态 */
54
+ details: OperationDetail[];
55
+ }
56
+ //# sourceMappingURL=file-diff-rollback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-diff-rollback.d.ts","sourceRoot":"","sources":["../../src/routes/file-diff-rollback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAWH,eAAO,MAAM,sBAAsB,4CAAW,CAAC;AAE/C,MAAM,WAAW,gBAAgB;IAC/B,aAAa;IACb,iBAAiB,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAClD,mBAAmB;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED,gBAAgB;AAChB,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa;IACb,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC/E,gBAAgB;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,+BAA+B;IAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB;IAChB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B"}
@@ -0,0 +1,278 @@
1
+ /**
2
+ * 文件差异回滚路由
3
+ *
4
+ * 根据变更记录的反向操作执行文件回滚。
5
+ * 与全量快照还原不同的是,只还原被修改过的文件。
6
+ *
7
+ * 核心流程(两步走):
8
+ * 1. 内存预检:在内存中按修改时间逆序模拟所有反向操作,验证完整性。不触碰文件系统。
9
+ * - CREATE:检查文件是否存在,准备删除
10
+ * - UPDATE:读取 diff patch,读取当前文件内容,在内存中应用反向 patch 验证是否成功
11
+ * - DELETE:读取删除前的完整内容,准备重建
12
+ * 2. 原子执行:预检全部通过后,批量写入文件系统;任一预检失败则直接返回错误,
13
+ * 由调用方决定是否使用快照进行完整覆盖。
14
+ *
15
+ * 反向操作映射:
16
+ * CREATE → DELETE(删除创建的文件)
17
+ * UPDATE → 应用反向 diff patch(精确撤销修改的部分,保留用户后续改动)
18
+ * DELETE → 重新创建被删除的文件(根据 cacheKey 从 bridge 本地缓存读取)
19
+ */
20
+ import { promises as fs } from 'node:fs';
21
+ import path from 'node:path';
22
+ import { Router } from 'express';
23
+ import * as Diff from 'diff';
24
+ import { createLogger } from '../utils/logger.js';
25
+ import { validateToken } from '../middleware/auth.js';
26
+ import { readBeforeContentFromCache, readDiffFromCache } from '../services/workspaceTaskChangeWatcher.js';
27
+ const log = createLogger('file-diff-rollback');
28
+ export const fileDiffRollbackRouter = Router();
29
+ /**
30
+ * 内存预检:在内存中按时间逆序模拟所有反向操作,生成执行计划。
31
+ * 不实际触碰文件系统。
32
+ *
33
+ * 关键:使用内存文件状态跟踪(fileContentMap),每条回滚操作基于前一条执行后的内容,
34
+ * 确保同一文件多次 UPDATE 时链式回滚正确(D→C→B→A)。
35
+ */
36
+ async function previewRollback(workspacePath, operations) {
37
+ const errors = [];
38
+ const plan = [];
39
+ const details = [];
40
+ const normalizedWorkspace = path.resolve(String(workspacePath).trim());
41
+ // 按 createdAt 逆序排列(最新的变更最先回滚)
42
+ const sortedOps = [...operations].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
43
+ // 内存中的文件内容跟踪:key = relativePath,value = 当前内容(null = 文件不存在)
44
+ // 首次访问某文件时从磁盘读取,后续操作基于内存中的最新状态
45
+ const fileContentMap = new Map();
46
+ const fileExistedMap = new Map();
47
+ for (const op of sortedOps) {
48
+ const fullPath = path.join(normalizedWorkspace, op.relativePath);
49
+ const relativePath = op.relativePath.replace(/\\/g, '/');
50
+ // 安全检查:路径不能跳出工作区
51
+ const resolvedPath = path.resolve(fullPath);
52
+ if (!resolvedPath.startsWith(normalizedWorkspace)) {
53
+ const reason = `Path escape detected: ${relativePath}`;
54
+ errors.push(reason);
55
+ details.push({ relativePath, originalOperation: op.originalOperation, status: 'preview_failed', reason });
56
+ continue;
57
+ }
58
+ // 获取当前内存中的文件状态(首次从磁盘读取)
59
+ if (!fileContentMap.has(relativePath)) {
60
+ let diskContent = null;
61
+ let diskExists = false;
62
+ try {
63
+ diskContent = await fs.readFile(fullPath, 'utf-8');
64
+ diskExists = true;
65
+ }
66
+ catch {
67
+ diskExists = false;
68
+ }
69
+ fileContentMap.set(relativePath, diskContent);
70
+ fileExistedMap.set(relativePath, diskExists);
71
+ }
72
+ switch (op.originalOperation) {
73
+ case 'CREATE': {
74
+ // 逆操作:删除文件
75
+ plan.push({ type: 'DELETE', fullPath, relativePath, originalOperation: 'CREATE' });
76
+ details.push({ relativePath, originalOperation: 'CREATE', status: 'preview_passed', action: '删除文件' });
77
+ // 更新内存状态:文件已删除
78
+ fileContentMap.set(relativePath, null);
79
+ fileExistedMap.set(relativePath, false);
80
+ break;
81
+ }
82
+ case 'UPDATE': {
83
+ if (!op.cacheKey) {
84
+ const reason = `缺少 cacheKey,无法读取 diff 补丁`;
85
+ errors.push(`UPDATE rollback failed: ${reason} for ${relativePath}`);
86
+ details.push({ relativePath, originalOperation: 'UPDATE', status: 'preview_failed', reason });
87
+ break;
88
+ }
89
+ const currentContent = fileContentMap.get(relativePath) ?? null;
90
+ if (currentContent === null) {
91
+ const reason = `文件当前不存在,无法应用反向 diff 补丁`;
92
+ errors.push(`UPDATE rollback failed: ${reason} for ${relativePath}`);
93
+ details.push({ relativePath, originalOperation: 'UPDATE', status: 'preview_failed', reason });
94
+ break;
95
+ }
96
+ // 尝试用 diff patch 精确回滚
97
+ const patch = await readDiffFromCache(normalizedWorkspace, op.cacheKey);
98
+ let rollbackContent = null;
99
+ let rollbackMethod = 'beforeContent 兜底';
100
+ if (patch !== null) {
101
+ try {
102
+ const parsed = Diff.parsePatch(patch);
103
+ const reversed = Diff.reversePatch(parsed);
104
+ const result = Diff.applyPatch(currentContent, reversed);
105
+ if (result !== false) {
106
+ rollbackContent = result;
107
+ rollbackMethod = '反向 diff 补丁';
108
+ }
109
+ else {
110
+ log.warn(`Diff patch failed for ${relativePath}, falling back to beforeContent`);
111
+ }
112
+ }
113
+ catch (err) {
114
+ log.warn(`Diff patch error for ${relativePath}: ${err.message}, falling back to beforeContent`);
115
+ }
116
+ }
117
+ // diff patch 失败时,用 beforeContent 兜底
118
+ if (rollbackContent === null) {
119
+ const beforeContent = await readBeforeContentFromCache(normalizedWorkspace, op.cacheKey);
120
+ if (beforeContent === null) {
121
+ const reason = `diff 补丁应用失败且本地缓存中找不到 beforeContent (cacheKey=${op.cacheKey})`;
122
+ errors.push(`UPDATE rollback failed: ${reason} for ${relativePath}`);
123
+ details.push({ relativePath, originalOperation: 'UPDATE', status: 'preview_failed', reason });
124
+ break;
125
+ }
126
+ rollbackContent = beforeContent;
127
+ }
128
+ plan.push({ type: 'WRITE', fullPath, relativePath, originalOperation: 'UPDATE', content: rollbackContent });
129
+ details.push({ relativePath, originalOperation: 'UPDATE', status: 'preview_passed', action: rollbackMethod });
130
+ // 更新内存状态:文件内容已回滚
131
+ fileContentMap.set(relativePath, rollbackContent);
132
+ break;
133
+ }
134
+ case 'DELETE': {
135
+ if (!op.cacheKey) {
136
+ const reason = `缺少 cacheKey,无法读取删除前的文件内容`;
137
+ errors.push(`DELETE rollback failed: ${reason} for ${relativePath}`);
138
+ details.push({ relativePath, originalOperation: 'DELETE', status: 'preview_failed', reason });
139
+ break;
140
+ }
141
+ const content = await readBeforeContentFromCache(normalizedWorkspace, op.cacheKey);
142
+ if (content === null) {
143
+ const reason = `本地缓存中找不到删除前的文件内容 (cacheKey=${op.cacheKey})`;
144
+ errors.push(`DELETE rollback failed: ${reason} for ${relativePath}`);
145
+ details.push({ relativePath, originalOperation: 'DELETE', status: 'preview_failed', reason });
146
+ break;
147
+ }
148
+ plan.push({ type: 'WRITE', fullPath, relativePath, originalOperation: 'DELETE', content });
149
+ details.push({ relativePath, originalOperation: 'DELETE', status: 'preview_passed', action: '重建文件' });
150
+ // 更新内存状态:文件已重建
151
+ fileContentMap.set(relativePath, content);
152
+ fileExistedMap.set(relativePath, true);
153
+ break;
154
+ }
155
+ default: {
156
+ const reason = `未知的操作类型 "${op.originalOperation}"`;
157
+ errors.push(`${reason} for ${relativePath}`);
158
+ details.push({ relativePath, originalOperation: op.originalOperation, status: 'preview_failed', reason });
159
+ break;
160
+ }
161
+ }
162
+ }
163
+ return { ok: errors.length === 0, errors, plan, details };
164
+ }
165
+ /**
166
+ * 批量执行回滚计划。
167
+ * 返回每条操作的执行状态详情。
168
+ */
169
+ async function executePlan(plan, previewDetails) {
170
+ let restored = 0;
171
+ let skipped = 0;
172
+ const errors = [];
173
+ // 以预检 details 为基础,更新执行阶段状态
174
+ const details = previewDetails.map((d) => ({ ...d }));
175
+ for (const item of plan) {
176
+ const detail = details.find((d) => d.relativePath === item.relativePath && d.status === 'preview_passed');
177
+ try {
178
+ if (item.type === 'DELETE') {
179
+ await fs.unlink(item.fullPath);
180
+ log.info(`Rollback executed: deleted created file ${item.relativePath}`);
181
+ if (detail) {
182
+ detail.status = 'success';
183
+ detail.action = '删除文件';
184
+ }
185
+ restored++;
186
+ }
187
+ else if (item.type === 'WRITE') {
188
+ await fs.mkdir(path.dirname(item.fullPath), { recursive: true });
189
+ await fs.writeFile(item.fullPath, item.content, 'utf-8');
190
+ log.info(`Rollback executed: restored file ${item.relativePath}`);
191
+ if (detail) {
192
+ detail.status = 'success';
193
+ detail.action = item.originalOperation === 'UPDATE' ? '应用反向 diff 补丁' : '重建文件';
194
+ }
195
+ restored++;
196
+ }
197
+ }
198
+ catch (err) {
199
+ const reason = `${err.message}`;
200
+ const msg = `Rollback failed for ${item.relativePath}: ${reason}`;
201
+ errors.push(msg);
202
+ log.warn(msg);
203
+ if (detail) {
204
+ detail.status = 'failed';
205
+ detail.reason = reason;
206
+ }
207
+ skipped++;
208
+ }
209
+ }
210
+ return { restored, skipped, errors, details };
211
+ }
212
+ /**
213
+ * 差异回滚预检接口
214
+ * POST /runtime/file-diff-rollback/preview
215
+ *
216
+ * 仅做内存模拟,不触碰文件系统。
217
+ * 返回每条操作的预检状态(preview_passed / preview_failed / skipped)及原因。
218
+ */
219
+ fileDiffRollbackRouter.post('/file-diff-rollback/preview', validateToken, async (req, res) => {
220
+ const { workspacePath, operations } = req.body;
221
+ if (!workspacePath || !Array.isArray(operations)) {
222
+ res.status(400).json({ error: 'workspacePath and operations (array) are required' });
223
+ return;
224
+ }
225
+ const { ok, errors, plan, details } = await previewRollback(workspacePath, operations);
226
+ res.json({
227
+ ok,
228
+ errors,
229
+ plan: plan.map((p) => ({ type: p.type, relativePath: p.relativePath })),
230
+ totalOperations: operations.length,
231
+ plannedOperations: plan.length,
232
+ details,
233
+ });
234
+ });
235
+ /**
236
+ * 执行文件差异回滚
237
+ * POST /runtime/file-diff-rollback/execute
238
+ *
239
+ * 第一步:内存预检(previewRollback)。
240
+ * 第二步:若预检全部通过,则批量原子写入;若任一预检失败,直接返回错误,不写盘。
241
+ *
242
+ * 调用方在收到 previewFailed=true 时,可提示用户选择使用文件快照进行完整覆盖。
243
+ * 无论成功失败,都返回每条操作的详细状态。
244
+ */
245
+ fileDiffRollbackRouter.post('/file-diff-rollback/execute', validateToken, async (req, res) => {
246
+ const { workspacePath, operations } = req.body;
247
+ if (!workspacePath || !Array.isArray(operations)) {
248
+ res.status(400).json({ error: 'workspacePath and operations (array) are required' });
249
+ return;
250
+ }
251
+ // ── 第一步:内存预检 ──
252
+ const preview = await previewRollback(workspacePath, operations);
253
+ if (!preview.ok) {
254
+ const result = {
255
+ ok: false,
256
+ restored: 0,
257
+ skipped: operations.length,
258
+ errors: preview.errors,
259
+ previewFailed: true,
260
+ details: preview.details,
261
+ };
262
+ log.warn(`Diff rollback preview failed, no files touched: errors=${preview.errors.length}`);
263
+ res.json(result);
264
+ return;
265
+ }
266
+ // ── 第二步:批量原子执行 ──
267
+ const execResult = await executePlan(preview.plan, preview.details);
268
+ const result = {
269
+ ok: execResult.errors.length === 0,
270
+ restored: execResult.restored,
271
+ skipped: execResult.skipped,
272
+ errors: execResult.errors,
273
+ previewFailed: false,
274
+ details: execResult.details,
275
+ };
276
+ log.info(`Diff rollback completed: restored=${execResult.restored}, skipped=${execResult.skipped}, errors=${execResult.errors.length}`);
277
+ res.json(result);
278
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"processes.d.ts","sourceRoot":"","sources":["../../src/routes/processes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,eAAO,MAAM,eAAe,4CAAW,CAAC"}
1
+ {"version":3,"file":"processes.d.ts","sourceRoot":"","sources":["../../src/routes/processes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,eAAO,MAAM,eAAe,4CAAW,CAAC"}
@@ -8,6 +8,7 @@ import { validateToken } from '../middleware/auth.js';
8
8
  import { getAgentProcessManager } from '../services/agent-process-manager.js';
9
9
  import { discoverAllProcessesFull } from '../services/process-detector.js';
10
10
  import { createLogger } from '../utils/logger.js';
11
+ import { cleanupSdkSessionByAgentId } from './terminal.js';
11
12
  const log = createLogger('processes');
12
13
  export const processesRouter = Router();
13
14
  /**
@@ -158,6 +159,12 @@ processesRouter.post('/processes/:agentId/force-kill', validateToken, async (req
158
159
  try {
159
160
  const manager = getAgentProcessManager();
160
161
  const result = await manager.forceKill(agentId);
162
+ // ★ 关键修复:强制终止后清理 bridge 内部内存状态
163
+ // 防止 sdkSessions 残留条目阻塞后续启动
164
+ const cleanedSessionId = cleanupSdkSessionByAgentId(agentId);
165
+ if (cleanedSessionId) {
166
+ log.info(`[processes/force-kill] Cleaned up SDK session after force-kill: agentId=${agentId}, sessionId=${cleanedSessionId}`);
167
+ }
161
168
  res.json({
162
169
  ok: result.success,
163
170
  agentId,
@@ -10,7 +10,7 @@ import { validateToken } from '../middleware/auth.js';
10
10
  import { isProcessRunning, findOrphanProcessByAgentId, findOrphanProcessByWorkspace } from '../services/process-detector.js';
11
11
  import { findPersistedSessionByAgentId, loadPersistedSessions, removePersistedSession, } from '../services/terminal-persistence.js';
12
12
  import { createLogger } from '../utils/logger.js';
13
- import { sdkSessions } from './terminal.js';
13
+ import { sdkSessions, cleanupSdkSessionByAgentId } from './terminal.js';
14
14
  const log = createLogger('sessions');
15
15
  export const sessionsRouter = Router();
16
16
  /**
@@ -670,6 +670,9 @@ sessionsRouter.post('/terminate-tree', validateToken, async (req, res) => {
670
670
  const result = await terminateProcessTree(pid);
671
671
  // 4. 等待进程完全终止(最多 5 秒)
672
672
  const exited = await waitForProcessExit(pid, 5000);
673
+ // ★ 关键修复:terminate-tree 后清理 bridge 内部内存状态
674
+ // 防止 sdkSessions 残留条目阻塞后续启动
675
+ cleanupSdkSessionByAgentId(agentId);
673
676
  // 5. 清理持久化状�?
674
677
  await import('../services/terminal-persistence.js').then(m => m.removePersistedSession(targetSessionId));
675
678
  log.info(`[terminate-tree] Terminated process tree for agentId=${agentId}, pid=${pid}, mode=${mode}, terminated=${result.terminated}, failed=${result.failed}, exited=${exited}`);
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 任务上下文路由
3
+ *
4
+ * 提供调度中心设置/清除 Agent 当前活跃任务上下文的能力。
5
+ * 文件变更监听服务据此将变更自动归属到正确的任务。
6
+ */
7
+ export declare const taskContextRouter: import("express-serve-static-core").Router;
8
+ //# sourceMappingURL=task-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-context.d.ts","sourceRoot":"","sources":["../../src/routes/task-context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH,eAAO,MAAM,iBAAiB,4CAAW,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * 任务上下文路由
3
+ *
4
+ * 提供调度中心设置/清除 Agent 当前活跃任务上下文的能力。
5
+ * 文件变更监听服务据此将变更自动归属到正确的任务。
6
+ */
7
+ import { Router } from 'express';
8
+ import { createLogger } from '../utils/logger.js';
9
+ import { validateToken } from '../middleware/auth.js';
10
+ import { setTaskContext, clearTaskContext, getTaskContext, startWatching, stopWatching, } from '../services/workspaceTaskChangeWatcher.js';
11
+ const log = createLogger('task-context');
12
+ export const taskContextRouter = Router();
13
+ /**
14
+ * 设置活跃任务上下文 + 启动工作区文件监听
15
+ * POST /runtime/task-context
16
+ *
17
+ * Body:
18
+ * {
19
+ * agentId: string,
20
+ * taskInstanceId: string,
21
+ * taskNodeId: string,
22
+ * tenantId: string,
23
+ * workspacePath: string,
24
+ * projectName: string
25
+ * }
26
+ */
27
+ taskContextRouter.post('/task-context', validateToken, async (req, res) => {
28
+ try {
29
+ const { agentId, taskInstanceId, taskNodeId, tenantId, workspacePath, projectName } = req.body || {};
30
+ if (!agentId || !taskInstanceId || !taskNodeId || !tenantId || !workspacePath) {
31
+ res.status(400).json({ error: 'agentId, taskInstanceId, taskNodeId, tenantId, workspacePath are required' });
32
+ return;
33
+ }
34
+ // 启动工作区监听
35
+ await startWatching(String(workspacePath).trim());
36
+ // 设置任务上下文
37
+ setTaskContext({
38
+ agentId: String(agentId).trim(),
39
+ taskInstanceId: String(taskInstanceId).trim(),
40
+ taskNodeId: String(taskNodeId).trim(),
41
+ tenantId: String(tenantId).trim(),
42
+ workspacePath: String(workspacePath).trim(),
43
+ projectName: String(projectName || '').trim(),
44
+ });
45
+ log.info(`Task context set via API: agent=${agentId}, taskInstance=${taskInstanceId}`);
46
+ res.json({ ok: true });
47
+ }
48
+ catch (error) {
49
+ const err = error;
50
+ log.error(`Failed to set task context: ${err.message}`);
51
+ res.status(500).json({ error: err.message });
52
+ }
53
+ });
54
+ /**
55
+ * 清除活跃任务上下文 + 停止工作区文件监听
56
+ * DELETE /runtime/task-context
57
+ */
58
+ taskContextRouter.delete('/task-context', validateToken, async (_req, res) => {
59
+ try {
60
+ await stopWatching();
61
+ clearTaskContext();
62
+ log.info('Task context cleared via API');
63
+ res.json({ ok: true });
64
+ }
65
+ catch (error) {
66
+ const err = error;
67
+ log.error(`Failed to clear task context: ${err.message}`);
68
+ res.status(500).json({ error: err.message });
69
+ }
70
+ });
71
+ /**
72
+ * 获取当前任务上下文
73
+ * GET /runtime/task-context
74
+ */
75
+ taskContextRouter.get('/task-context', validateToken, async (_req, res) => {
76
+ const ctx = getTaskContext();
77
+ res.json({ ok: true, context: ctx });
78
+ });