@sudocode-ai/local-server 0.1.7 → 0.1.8

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 (274) hide show
  1. package/README.md +6 -0
  2. package/dist/errors/agent-errors.d.ts +43 -0
  3. package/dist/errors/agent-errors.d.ts.map +1 -0
  4. package/dist/errors/agent-errors.js +69 -0
  5. package/dist/errors/agent-errors.js.map +1 -0
  6. package/dist/execution/adapters/claude-adapter.d.ts +63 -0
  7. package/dist/execution/adapters/claude-adapter.d.ts.map +1 -0
  8. package/dist/execution/adapters/claude-adapter.js +82 -0
  9. package/dist/execution/adapters/claude-adapter.js.map +1 -0
  10. package/dist/execution/adapters/codex-adapter.d.ts +67 -0
  11. package/dist/execution/adapters/codex-adapter.d.ts.map +1 -0
  12. package/dist/execution/adapters/codex-adapter.js +183 -0
  13. package/dist/execution/adapters/codex-adapter.js.map +1 -0
  14. package/dist/execution/adapters/codex-config-builder.d.ts +30 -0
  15. package/dist/execution/adapters/codex-config-builder.d.ts.map +1 -0
  16. package/dist/execution/adapters/codex-config-builder.js +110 -0
  17. package/dist/execution/adapters/codex-config-builder.js.map +1 -0
  18. package/dist/execution/adapters/copilot-adapter.d.ts +94 -0
  19. package/dist/execution/adapters/copilot-adapter.d.ts.map +1 -0
  20. package/dist/execution/adapters/copilot-adapter.js +163 -0
  21. package/dist/execution/adapters/copilot-adapter.js.map +1 -0
  22. package/dist/execution/adapters/copilot-config-builder.d.ts +48 -0
  23. package/dist/execution/adapters/copilot-config-builder.d.ts.map +1 -0
  24. package/dist/execution/adapters/copilot-config-builder.js +125 -0
  25. package/dist/execution/adapters/copilot-config-builder.js.map +1 -0
  26. package/dist/execution/adapters/cursor-adapter.d.ts +66 -0
  27. package/dist/execution/adapters/cursor-adapter.d.ts.map +1 -0
  28. package/dist/execution/adapters/cursor-adapter.js +121 -0
  29. package/dist/execution/adapters/cursor-adapter.js.map +1 -0
  30. package/dist/execution/adapters/cursor-config-builder.d.ts +29 -0
  31. package/dist/execution/adapters/cursor-config-builder.d.ts.map +1 -0
  32. package/dist/execution/adapters/cursor-config-builder.js +49 -0
  33. package/dist/execution/adapters/cursor-config-builder.js.map +1 -0
  34. package/dist/execution/adapters/shared/config-presets.d.ts +102 -0
  35. package/dist/execution/adapters/shared/config-presets.d.ts.map +1 -0
  36. package/dist/execution/adapters/shared/config-presets.js +205 -0
  37. package/dist/execution/adapters/shared/config-presets.js.map +1 -0
  38. package/dist/execution/adapters/shared/config-utils.d.ts +95 -0
  39. package/dist/execution/adapters/shared/config-utils.d.ts.map +1 -0
  40. package/dist/execution/adapters/shared/config-utils.js +163 -0
  41. package/dist/execution/adapters/shared/config-utils.js.map +1 -0
  42. package/dist/execution/adapters/shared/index.d.ts +8 -0
  43. package/dist/execution/adapters/shared/index.d.ts.map +1 -0
  44. package/dist/execution/adapters/shared/index.js +8 -0
  45. package/dist/execution/adapters/shared/index.js.map +1 -0
  46. package/dist/execution/executors/agent-executor-wrapper.d.ts +153 -0
  47. package/dist/execution/executors/agent-executor-wrapper.d.ts.map +1 -0
  48. package/dist/execution/executors/agent-executor-wrapper.js +652 -0
  49. package/dist/execution/executors/agent-executor-wrapper.js.map +1 -0
  50. package/dist/execution/executors/executor-factory.d.ts +95 -0
  51. package/dist/execution/executors/executor-factory.d.ts.map +1 -0
  52. package/dist/execution/executors/executor-factory.js +120 -0
  53. package/dist/execution/executors/executor-factory.js.map +1 -0
  54. package/dist/execution/output/ag-ui-adapter.d.ts +0 -2
  55. package/dist/execution/output/ag-ui-adapter.d.ts.map +1 -1
  56. package/dist/execution/output/ag-ui-adapter.js +0 -2
  57. package/dist/execution/output/ag-ui-adapter.js.map +1 -1
  58. package/dist/execution/output/index.d.ts +0 -3
  59. package/dist/execution/output/index.d.ts.map +1 -1
  60. package/dist/execution/output/index.js +0 -2
  61. package/dist/execution/output/index.js.map +1 -1
  62. package/dist/execution/output/normalized-to-ag-ui-adapter.d.ts +108 -0
  63. package/dist/execution/output/normalized-to-ag-ui-adapter.d.ts.map +1 -0
  64. package/dist/execution/output/normalized-to-ag-ui-adapter.js +321 -0
  65. package/dist/execution/output/normalized-to-ag-ui-adapter.js.map +1 -0
  66. package/dist/execution/process/builders/claude.d.ts +24 -57
  67. package/dist/execution/process/builders/claude.d.ts.map +1 -1
  68. package/dist/execution/process/builders/claude.js +153 -19
  69. package/dist/execution/process/builders/claude.js.map +1 -1
  70. package/dist/execution/transport/ipc-transport-manager.d.ts +74 -0
  71. package/dist/execution/transport/ipc-transport-manager.d.ts.map +1 -0
  72. package/dist/execution/transport/ipc-transport-manager.js +104 -0
  73. package/dist/execution/transport/ipc-transport-manager.js.map +1 -0
  74. package/dist/execution/transport/transport-manager.d.ts.map +1 -1
  75. package/dist/execution/transport/transport-manager.js +3 -0
  76. package/dist/execution/transport/transport-manager.js.map +1 -1
  77. package/dist/execution/worktree/conflict-detector.d.ts +85 -0
  78. package/dist/execution/worktree/conflict-detector.d.ts.map +1 -0
  79. package/dist/execution/worktree/conflict-detector.js +129 -0
  80. package/dist/execution/worktree/conflict-detector.js.map +1 -0
  81. package/dist/execution/worktree/git-cli.d.ts +9 -0
  82. package/dist/execution/worktree/git-cli.d.ts.map +1 -1
  83. package/dist/execution/worktree/git-cli.js +10 -0
  84. package/dist/execution/worktree/git-cli.js.map +1 -1
  85. package/dist/execution/worktree/git-sync-cli.d.ts +187 -0
  86. package/dist/execution/worktree/git-sync-cli.d.ts.map +1 -0
  87. package/dist/execution/worktree/git-sync-cli.js +350 -0
  88. package/dist/execution/worktree/git-sync-cli.js.map +1 -0
  89. package/dist/execution/worktree/manager.d.ts +18 -0
  90. package/dist/execution/worktree/manager.d.ts.map +1 -1
  91. package/dist/execution/worktree/manager.js +9 -3
  92. package/dist/execution/worktree/manager.js.map +1 -1
  93. package/dist/index.d.ts +1 -3
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +132 -211
  96. package/dist/index.js.map +1 -1
  97. package/dist/middleware/project-context.d.ts +37 -0
  98. package/dist/middleware/project-context.d.ts.map +1 -0
  99. package/dist/middleware/project-context.js +91 -0
  100. package/dist/middleware/project-context.js.map +1 -0
  101. package/dist/public/assets/index-Bb_W5bUr.css +1 -0
  102. package/dist/public/assets/index-CFKL113G.js +710 -0
  103. package/dist/public/assets/index-CFKL113G.js.map +1 -0
  104. package/dist/public/assets/{react-vendor-ByUx1V_q.js → react-vendor-DiL5hC7l.js} +2 -2
  105. package/dist/public/assets/{react-vendor-ByUx1V_q.js.map → react-vendor-DiL5hC7l.js.map} +1 -1
  106. package/dist/public/assets/ui-vendor-B4WMPEfa.js +54 -0
  107. package/dist/public/assets/ui-vendor-B4WMPEfa.js.map +1 -0
  108. package/dist/public/index.html +4 -4
  109. package/dist/routes/agents.d.ts +3 -0
  110. package/dist/routes/agents.d.ts.map +1 -0
  111. package/dist/routes/agents.js +62 -0
  112. package/dist/routes/agents.js.map +1 -0
  113. package/dist/routes/config.d.ts +3 -0
  114. package/dist/routes/config.d.ts.map +1 -0
  115. package/dist/routes/config.js +25 -0
  116. package/dist/routes/config.js.map +1 -0
  117. package/dist/routes/editors.d.ts +15 -0
  118. package/dist/routes/editors.d.ts.map +1 -0
  119. package/dist/routes/editors.js +98 -0
  120. package/dist/routes/editors.js.map +1 -0
  121. package/dist/routes/executions-stream.d.ts +8 -5
  122. package/dist/routes/executions-stream.d.ts.map +1 -1
  123. package/dist/routes/executions-stream.js +10 -6
  124. package/dist/routes/executions-stream.js.map +1 -1
  125. package/dist/routes/executions.d.ts +6 -10
  126. package/dist/routes/executions.d.ts.map +1 -1
  127. package/dist/routes/executions.js +506 -54
  128. package/dist/routes/executions.js.map +1 -1
  129. package/dist/routes/feedback.d.ts +3 -2
  130. package/dist/routes/feedback.d.ts.map +1 -1
  131. package/dist/routes/feedback.js +12 -10
  132. package/dist/routes/feedback.js.map +1 -1
  133. package/dist/routes/files.d.ts +18 -0
  134. package/dist/routes/files.d.ts.map +1 -0
  135. package/dist/routes/files.js +89 -0
  136. package/dist/routes/files.js.map +1 -0
  137. package/dist/routes/issues.d.ts +3 -2
  138. package/dist/routes/issues.d.ts.map +1 -1
  139. package/dist/routes/issues.js +19 -18
  140. package/dist/routes/issues.js.map +1 -1
  141. package/dist/routes/projects.d.ts +11 -0
  142. package/dist/routes/projects.d.ts.map +1 -0
  143. package/dist/routes/projects.js +447 -0
  144. package/dist/routes/projects.js.map +1 -0
  145. package/dist/routes/relationships.d.ts +3 -2
  146. package/dist/routes/relationships.d.ts.map +1 -1
  147. package/dist/routes/relationships.js +12 -10
  148. package/dist/routes/relationships.js.map +1 -1
  149. package/dist/routes/repo-info.d.ts +3 -0
  150. package/dist/routes/repo-info.d.ts.map +1 -0
  151. package/dist/routes/repo-info.js +126 -0
  152. package/dist/routes/repo-info.js.map +1 -0
  153. package/dist/routes/specs.d.ts +3 -2
  154. package/dist/routes/specs.d.ts.map +1 -1
  155. package/dist/routes/specs.js +19 -18
  156. package/dist/routes/specs.js.map +1 -1
  157. package/dist/services/agent-registry.d.ts +140 -0
  158. package/dist/services/agent-registry.d.ts.map +1 -0
  159. package/dist/services/agent-registry.js +272 -0
  160. package/dist/services/agent-registry.js.map +1 -0
  161. package/dist/services/editor-service.d.ts +57 -0
  162. package/dist/services/editor-service.d.ts.map +1 -0
  163. package/dist/services/editor-service.js +204 -0
  164. package/dist/services/editor-service.js.map +1 -0
  165. package/dist/services/execution-changes-service.d.ts +92 -0
  166. package/dist/services/execution-changes-service.d.ts.map +1 -0
  167. package/dist/services/execution-changes-service.js +546 -0
  168. package/dist/services/execution-changes-service.js.map +1 -0
  169. package/dist/services/execution-lifecycle.d.ts +1 -0
  170. package/dist/services/execution-lifecycle.d.ts.map +1 -1
  171. package/dist/services/execution-lifecycle.js +37 -7
  172. package/dist/services/execution-lifecycle.js.map +1 -1
  173. package/dist/services/execution-logs-store.d.ts +75 -0
  174. package/dist/services/execution-logs-store.d.ts.map +1 -1
  175. package/dist/services/execution-logs-store.js +142 -2
  176. package/dist/services/execution-logs-store.js.map +1 -1
  177. package/dist/services/execution-service.d.ts +50 -58
  178. package/dist/services/execution-service.d.ts.map +1 -1
  179. package/dist/services/execution-service.js +433 -469
  180. package/dist/services/execution-service.js.map +1 -1
  181. package/dist/services/execution-worker-pool.d.ts +116 -0
  182. package/dist/services/execution-worker-pool.d.ts.map +1 -0
  183. package/dist/services/execution-worker-pool.js +326 -0
  184. package/dist/services/execution-worker-pool.js.map +1 -0
  185. package/dist/services/executions.d.ts +3 -0
  186. package/dist/services/executions.d.ts.map +1 -1
  187. package/dist/services/executions.js +11 -17
  188. package/dist/services/executions.js.map +1 -1
  189. package/dist/services/export.d.ts +8 -2
  190. package/dist/services/export.d.ts.map +1 -1
  191. package/dist/services/export.js +29 -23
  192. package/dist/services/export.js.map +1 -1
  193. package/dist/services/file-search/git-ls-files-strategy.d.ts +72 -0
  194. package/dist/services/file-search/git-ls-files-strategy.d.ts.map +1 -0
  195. package/dist/services/file-search/git-ls-files-strategy.js +176 -0
  196. package/dist/services/file-search/git-ls-files-strategy.js.map +1 -0
  197. package/dist/services/file-search/index.d.ts +9 -0
  198. package/dist/services/file-search/index.d.ts.map +1 -0
  199. package/dist/services/file-search/index.js +10 -0
  200. package/dist/services/file-search/index.js.map +1 -0
  201. package/dist/services/file-search/registry.d.ts +97 -0
  202. package/dist/services/file-search/registry.d.ts.map +1 -0
  203. package/dist/services/file-search/registry.js +140 -0
  204. package/dist/services/file-search/registry.js.map +1 -0
  205. package/dist/services/file-search/strategy.d.ts +58 -0
  206. package/dist/services/file-search/strategy.d.ts.map +1 -0
  207. package/dist/services/file-search/strategy.js +8 -0
  208. package/dist/services/file-search/strategy.js.map +1 -0
  209. package/dist/services/project-context.d.ts +69 -0
  210. package/dist/services/project-context.d.ts.map +1 -0
  211. package/dist/services/project-context.js +113 -0
  212. package/dist/services/project-context.js.map +1 -0
  213. package/dist/services/project-manager.d.ts +95 -0
  214. package/dist/services/project-manager.d.ts.map +1 -0
  215. package/dist/services/project-manager.js +388 -0
  216. package/dist/services/project-manager.js.map +1 -0
  217. package/dist/services/project-registry.d.ts +98 -0
  218. package/dist/services/project-registry.d.ts.map +1 -0
  219. package/dist/services/project-registry.js +289 -0
  220. package/dist/services/project-registry.js.map +1 -0
  221. package/dist/services/prompt-resolver.d.ts +97 -0
  222. package/dist/services/prompt-resolver.d.ts.map +1 -0
  223. package/dist/services/prompt-resolver.js +377 -0
  224. package/dist/services/prompt-resolver.js.map +1 -0
  225. package/dist/services/repo-info.d.ts +12 -0
  226. package/dist/services/repo-info.d.ts.map +1 -1
  227. package/dist/services/repo-info.js +46 -0
  228. package/dist/services/repo-info.js.map +1 -1
  229. package/dist/services/watcher.d.ts +3 -4
  230. package/dist/services/watcher.d.ts.map +1 -1
  231. package/dist/services/watcher.js +18 -35
  232. package/dist/services/watcher.js.map +1 -1
  233. package/dist/services/websocket.d.ts +30 -16
  234. package/dist/services/websocket.d.ts.map +1 -1
  235. package/dist/services/websocket.js +102 -37
  236. package/dist/services/websocket.js.map +1 -1
  237. package/dist/services/worktree-sync-service.d.ts +228 -0
  238. package/dist/services/worktree-sync-service.d.ts.map +1 -0
  239. package/dist/services/worktree-sync-service.js +563 -0
  240. package/dist/services/worktree-sync-service.js.map +1 -0
  241. package/dist/types/editor.d.ts +49 -0
  242. package/dist/types/editor.d.ts.map +1 -0
  243. package/dist/types/editor.js +50 -0
  244. package/dist/types/editor.js.map +1 -0
  245. package/dist/types/project.d.ts +58 -0
  246. package/dist/types/project.d.ts.map +1 -0
  247. package/dist/types/project.js +10 -0
  248. package/dist/types/project.js.map +1 -0
  249. package/dist/utils/executable-check.d.ts +36 -0
  250. package/dist/utils/executable-check.d.ts.map +1 -0
  251. package/dist/utils/executable-check.js +79 -0
  252. package/dist/utils/executable-check.js.map +1 -0
  253. package/dist/workers/execution-worker.d.ts +18 -0
  254. package/dist/workers/execution-worker.d.ts.map +1 -0
  255. package/dist/workers/execution-worker.js +340 -0
  256. package/dist/workers/execution-worker.js.map +1 -0
  257. package/dist/workers/worker-ipc.d.ts +84 -0
  258. package/dist/workers/worker-ipc.d.ts.map +1 -0
  259. package/dist/workers/worker-ipc.js +29 -0
  260. package/dist/workers/worker-ipc.js.map +1 -0
  261. package/package.json +6 -5
  262. package/dist/execution/output/ag-ui-integration.d.ts +0 -96
  263. package/dist/execution/output/ag-ui-integration.d.ts.map +0 -1
  264. package/dist/execution/output/ag-ui-integration.js +0 -96
  265. package/dist/execution/output/ag-ui-integration.js.map +0 -1
  266. package/dist/execution/output/claude-code-output-processor.d.ts +0 -321
  267. package/dist/execution/output/claude-code-output-processor.d.ts.map +0 -1
  268. package/dist/execution/output/claude-code-output-processor.js +0 -769
  269. package/dist/execution/output/claude-code-output-processor.js.map +0 -1
  270. package/dist/public/assets/index-B3SEMufD.js +0 -580
  271. package/dist/public/assets/index-B3SEMufD.js.map +0 -1
  272. package/dist/public/assets/index-D2YGL3gX.css +0 -1
  273. package/dist/public/assets/ui-vendor-CotR6bx9.js +0 -54
  274. package/dist/public/assets/ui-vendor-CotR6bx9.js.map +0 -1
@@ -2,50 +2,64 @@
2
2
  * Executions API routes (mapped to /api)
3
3
  *
4
4
  * Provides REST API for managing issue executions.
5
+ *
6
+ * Note: All routes require X-Project-ID header via requireProject() middleware
5
7
  */
6
8
  import { Router } from "express";
7
- import { ExecutionService } from "../services/execution-service.js";
8
- import { ExecutionLogsStore } from "../services/execution-logs-store.js";
9
+ import { execSync } from "child_process";
10
+ import { NormalizedEntryToAgUiAdapter } from "../execution/output/normalized-to-ag-ui-adapter.js";
11
+ import { AgUiEventAdapter } from "../execution/output/ag-ui-adapter.js";
12
+ import { agentRegistryService } from "../services/agent-registry.js";
13
+ import { AgentNotFoundError, AgentNotImplementedError, AgentError, } from "../errors/agent-errors.js";
14
+ import { WorktreeSyncService, WorktreeSyncError, WorktreeSyncErrorCode, } from "../services/worktree-sync-service.js";
15
+ import { ExecutionChangesService } from "../services/execution-changes-service.js";
16
+ /**
17
+ * Get WorktreeSyncService instance for a request
18
+ *
19
+ * @param req - Express request with project context
20
+ * @returns WorktreeSyncService instance
21
+ */
22
+ function getWorktreeSyncService(req) {
23
+ const db = req.project.db;
24
+ const repoPath = req.project.path;
25
+ return new WorktreeSyncService(db, repoPath);
26
+ }
27
+ /**
28
+ * Get HTTP status code for WorktreeSyncError
29
+ *
30
+ * @param error - WorktreeSyncError instance
31
+ * @returns HTTP status code
32
+ */
33
+ function getStatusCodeForSyncError(error) {
34
+ switch (error.code) {
35
+ case WorktreeSyncErrorCode.NO_WORKTREE:
36
+ case WorktreeSyncErrorCode.WORKTREE_MISSING:
37
+ case WorktreeSyncErrorCode.BRANCH_MISSING:
38
+ case WorktreeSyncErrorCode.TARGET_BRANCH_MISSING:
39
+ case WorktreeSyncErrorCode.EXECUTION_NOT_FOUND:
40
+ return 404; // Not found
41
+ case WorktreeSyncErrorCode.DIRTY_WORKING_TREE:
42
+ case WorktreeSyncErrorCode.CODE_CONFLICTS:
43
+ case WorktreeSyncErrorCode.NO_COMMON_BASE:
44
+ return 400; // Bad request (user must fix)
45
+ case WorktreeSyncErrorCode.MERGE_FAILED:
46
+ case WorktreeSyncErrorCode.JSONL_RESOLUTION_FAILED:
47
+ case WorktreeSyncErrorCode.DATABASE_SYNC_FAILED:
48
+ return 500; // Internal error
49
+ default:
50
+ return 500;
51
+ }
52
+ }
9
53
  /**
10
54
  * Create executions router
11
55
  *
12
- * @param db - Database instance
13
- * @param repoPath - Path to git repository
14
- * @param transportManager - Optional transport manager for SSE streaming
15
- * @param executionService - Optional execution service instance
16
- * @param logsStore - Optional execution logs store instance
56
+ * Note: ExecutionService and ExecutionLogsStore are accessed via req.project
57
+ * which is injected by the requireProject() middleware
58
+ *
17
59
  * @returns Express router with execution endpoints
18
60
  */
19
- export function createExecutionsRouter(db, repoPath, transportManager, executionService, logsStore) {
61
+ export function createExecutionsRouter() {
20
62
  const router = Router();
21
- const service = executionService ||
22
- new ExecutionService(db, repoPath, undefined, transportManager);
23
- const store = logsStore || new ExecutionLogsStore(db);
24
- /**
25
- * POST /api/issues/:issueId/executions/prepare
26
- *
27
- * Prepare an execution - render template and show preview
28
- */
29
- router.post("/issues/:issueId/executions/prepare", async (req, res) => {
30
- try {
31
- const { issueId } = req.params;
32
- const options = req.body || {};
33
- const result = await service.prepareExecution(issueId, options);
34
- res.json({
35
- success: true,
36
- data: result,
37
- });
38
- }
39
- catch (error) {
40
- console.error("[API Route] ERROR: Failed to prepare execution:", error);
41
- res.status(500).json({
42
- success: false,
43
- data: null,
44
- error_data: error instanceof Error ? error.message : String(error),
45
- message: "Failed to prepare execution",
46
- });
47
- }
48
- });
49
63
  /**
50
64
  * POST /api/issues/:issueId/executions
51
65
  *
@@ -54,7 +68,7 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
54
68
  router.post("/issues/:issueId/executions", async (req, res) => {
55
69
  try {
56
70
  const { issueId } = req.params;
57
- const { config, prompt } = req.body;
71
+ const { config, prompt, agentType } = req.body;
58
72
  // Validate required fields
59
73
  if (!prompt) {
60
74
  res.status(400).json({
@@ -64,7 +78,22 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
64
78
  });
65
79
  return;
66
80
  }
67
- const execution = await service.createExecution(issueId, config || {}, prompt);
81
+ // Validate agentType if provided
82
+ if (agentType) {
83
+ // Check if agent exists in registry
84
+ if (!agentRegistryService.hasAgent(agentType)) {
85
+ const availableAgents = agentRegistryService
86
+ .getAvailableAgents()
87
+ .map((a) => a.name);
88
+ throw new AgentNotFoundError(agentType, availableAgents);
89
+ }
90
+ // Check if agent is implemented
91
+ if (!agentRegistryService.isAgentImplemented(agentType)) {
92
+ throw new AgentNotImplementedError(agentType);
93
+ }
94
+ }
95
+ const execution = await req.project.executionService.createExecution(issueId, config || {}, prompt, agentType // Optional, defaults to 'claude-code' in service
96
+ );
68
97
  res.status(201).json({
69
98
  success: true,
70
99
  data: execution,
@@ -72,7 +101,39 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
72
101
  }
73
102
  catch (error) {
74
103
  console.error("[API Route] ERROR: Failed to create execution:", error);
75
- // Handle specific error cases
104
+ // Handle agent-specific errors with enhanced error responses
105
+ if (error instanceof AgentNotFoundError) {
106
+ res.status(400).json({
107
+ success: false,
108
+ data: null,
109
+ error: error.message,
110
+ code: error.code,
111
+ details: error.details,
112
+ });
113
+ return;
114
+ }
115
+ if (error instanceof AgentNotImplementedError) {
116
+ res.status(501).json({
117
+ success: false,
118
+ data: null,
119
+ error: error.message,
120
+ code: error.code,
121
+ details: error.details,
122
+ });
123
+ return;
124
+ }
125
+ if (error instanceof AgentError) {
126
+ // Generic agent error (400 by default)
127
+ res.status(400).json({
128
+ success: false,
129
+ data: null,
130
+ error: error.message,
131
+ code: error.code,
132
+ details: error.details,
133
+ });
134
+ return;
135
+ }
136
+ // Handle other errors (backwards compatibility)
76
137
  const errorMessage = error instanceof Error ? error.message : String(error);
77
138
  const statusCode = errorMessage.includes("not found") ? 404 : 500;
78
139
  res.status(statusCode).json({
@@ -91,7 +152,7 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
91
152
  router.get("/executions/:executionId", (req, res) => {
92
153
  try {
93
154
  const { executionId } = req.params;
94
- const execution = service.getExecution(executionId);
155
+ const execution = req.project.executionService.getExecution(executionId);
95
156
  if (!execution) {
96
157
  res.status(404).json({
97
158
  success: false,
@@ -115,16 +176,86 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
115
176
  });
116
177
  }
117
178
  });
179
+ /**
180
+ * GET /api/executions/:executionId/chain
181
+ *
182
+ * Get execution chain (root execution + all follow-ups)
183
+ *
184
+ * Returns the full chain of executions starting from the root.
185
+ * If the requested execution is a follow-up, finds the root and returns the full chain.
186
+ * Executions are ordered chronologically (oldest first).
187
+ */
188
+ router.get("/executions/:executionId/chain", (req, res) => {
189
+ try {
190
+ const { executionId } = req.params;
191
+ const db = req.project.db;
192
+ // Get the requested execution
193
+ const execution = req.project.executionService.getExecution(executionId);
194
+ if (!execution) {
195
+ res.status(404).json({
196
+ success: false,
197
+ data: null,
198
+ message: `Execution not found: ${executionId}`,
199
+ });
200
+ return;
201
+ }
202
+ // Find the root execution by traversing up parent_execution_id
203
+ let rootId = executionId;
204
+ let current = execution;
205
+ while (current.parent_execution_id) {
206
+ rootId = current.parent_execution_id;
207
+ const parent = req.project.executionService.getExecution(rootId);
208
+ if (!parent)
209
+ break;
210
+ current = parent;
211
+ }
212
+ // Get all executions in the chain (root + all descendants)
213
+ // Using recursive CTE to get all descendants
214
+ const chain = db
215
+ .prepare(`
216
+ WITH RECURSIVE execution_chain AS (
217
+ -- Base case: the root execution
218
+ SELECT * FROM executions WHERE id = ?
219
+ UNION ALL
220
+ -- Recursive case: children of executions in the chain
221
+ SELECT e.* FROM executions e
222
+ INNER JOIN execution_chain ec ON e.parent_execution_id = ec.id
223
+ )
224
+ SELECT * FROM execution_chain
225
+ ORDER BY created_at ASC
226
+ `)
227
+ .all(rootId);
228
+ res.json({
229
+ success: true,
230
+ data: {
231
+ rootId,
232
+ executions: chain,
233
+ },
234
+ });
235
+ }
236
+ catch (error) {
237
+ console.error("Error getting execution chain:", error);
238
+ res.status(500).json({
239
+ success: false,
240
+ data: null,
241
+ error_data: error instanceof Error ? error.message : String(error),
242
+ message: "Failed to get execution chain",
243
+ });
244
+ }
245
+ });
118
246
  /**
119
247
  * GET /api/executions/:executionId/logs
120
248
  *
121
- * Get raw execution logs for historical replay
249
+ * Get AG-UI events for historical replay
250
+ *
251
+ * Fetches NormalizedEntry logs from storage and converts them to AG-UI events on-demand.
252
+ * This preserves full structured data in storage while serving UI-ready events to frontend.
122
253
  */
123
- router.get("/executions/:executionId/logs", (req, res) => {
254
+ router.get("/executions/:executionId/logs", async (req, res) => {
124
255
  try {
125
256
  const { executionId } = req.params;
126
257
  // Verify execution exists
127
- const execution = service.getExecution(executionId);
258
+ const execution = req.project.executionService.getExecution(executionId);
128
259
  if (!execution) {
129
260
  res.status(404).json({
130
261
  success: false,
@@ -133,14 +264,27 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
133
264
  });
134
265
  return;
135
266
  }
136
- // Fetch raw logs and metadata
137
- const logs = store.getRawLogs(executionId);
138
- const metadata = store.getLogMetadata(executionId);
267
+ // Fetch normalized entries from storage
268
+ const normalizedEntries = req.project.logsStore.getNormalizedEntries(executionId);
269
+ const metadata = req.project.logsStore.getLogMetadata(executionId);
270
+ // Convert NormalizedEntry to AG-UI events on-demand
271
+ const events = [];
272
+ // Create a temporary AG-UI adapter to collect events
273
+ const agUiAdapter = new AgUiEventAdapter(executionId);
274
+ agUiAdapter.onEvent((event) => {
275
+ events.push(event);
276
+ });
277
+ // Create normalized adapter to transform entries
278
+ const normalizedAdapter = new NormalizedEntryToAgUiAdapter(agUiAdapter);
279
+ // Process all normalized entries through the adapter
280
+ for (const entry of normalizedEntries) {
281
+ await normalizedAdapter.processEntry(entry);
282
+ }
139
283
  res.json({
140
284
  success: true,
141
285
  data: {
142
286
  executionId,
143
- logs,
287
+ events,
144
288
  metadata: metadata
145
289
  ? {
146
290
  lineCount: metadata.line_count,
@@ -167,6 +311,40 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
167
311
  });
168
312
  }
169
313
  });
314
+ /**
315
+ * GET /api/executions/:executionId/changes
316
+ *
317
+ * Get code changes (file list + diff statistics) for an execution
318
+ *
319
+ * Calculates changes on-demand from commit SHAs. Supports:
320
+ * - Committed changes (commit-to-commit diff)
321
+ * - Uncommitted changes (working tree diff)
322
+ * - Unavailable states with clear error reasons
323
+ */
324
+ router.get("/executions/:executionId/changes", async (req, res) => {
325
+ try {
326
+ const { executionId } = req.params;
327
+ const db = req.project.db;
328
+ const repoPath = req.project.path;
329
+ // Create changes service
330
+ const changesService = new ExecutionChangesService(db, repoPath);
331
+ // Get changes
332
+ const result = await changesService.getChanges(executionId);
333
+ res.json({
334
+ success: true,
335
+ data: result,
336
+ });
337
+ }
338
+ catch (error) {
339
+ console.error("[GET /executions/:id/changes] Error:", error);
340
+ res.status(500).json({
341
+ success: false,
342
+ data: null,
343
+ error_data: error instanceof Error ? error.message : String(error),
344
+ message: "Failed to calculate changes",
345
+ });
346
+ }
347
+ });
170
348
  /**
171
349
  * GET /api/issues/:issueId/executions
172
350
  *
@@ -175,7 +353,7 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
175
353
  router.get("/issues/:issueId/executions", (req, res) => {
176
354
  try {
177
355
  const { issueId } = req.params;
178
- const executions = service.listExecutions(issueId);
356
+ const executions = req.project.executionService.listExecutions(issueId);
179
357
  res.json({
180
358
  success: true,
181
359
  data: executions,
@@ -209,7 +387,7 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
209
387
  });
210
388
  return;
211
389
  }
212
- const followUpExecution = await service.createFollowUp(executionId, feedback);
390
+ const followUpExecution = await req.project.executionService.createFollowUp(executionId, feedback);
213
391
  res.status(201).json({
214
392
  success: true,
215
393
  data: followUpExecution,
@@ -232,14 +410,14 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
232
410
  }
233
411
  });
234
412
  /**
235
- * DELETE /api/executions/:executionId
413
+ * POST /api/executions/:executionId/cancel
236
414
  *
237
415
  * Cancel a running execution
238
416
  */
239
- router.delete("/executions/:executionId", async (req, res) => {
417
+ router.post("/executions/:executionId/cancel", async (req, res) => {
240
418
  try {
241
419
  const { executionId } = req.params;
242
- await service.cancelExecution(executionId);
420
+ await req.project.executionService.cancelExecution(executionId);
243
421
  res.json({
244
422
  success: true,
245
423
  data: { executionId },
@@ -259,6 +437,51 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
259
437
  });
260
438
  }
261
439
  });
440
+ /**
441
+ * DELETE /api/executions/:executionId
442
+ *
443
+ * Delete an execution and its entire chain (or cancel if ?cancel=true)
444
+ *
445
+ * Query parameters:
446
+ * - cancel: if "true", cancel the execution instead of deleting it
447
+ * - deleteBranch: if "true", also delete the execution's branch
448
+ * - deleteWorktree: if "true", also delete the execution's worktree
449
+ */
450
+ router.delete("/executions/:executionId", async (req, res) => {
451
+ try {
452
+ const { executionId } = req.params;
453
+ const { cancel, deleteBranch, deleteWorktree } = req.query;
454
+ // If cancel query param is true, cancel the execution
455
+ if (cancel === "true") {
456
+ await req.project.executionService.cancelExecution(executionId);
457
+ res.json({
458
+ success: true,
459
+ data: { executionId },
460
+ message: "Execution cancelled successfully",
461
+ });
462
+ return;
463
+ }
464
+ // Otherwise, delete the execution and its chain
465
+ await req.project.executionService.deleteExecution(executionId, deleteBranch === "true", deleteWorktree === "true");
466
+ res.json({
467
+ success: true,
468
+ data: { executionId },
469
+ message: "Execution deleted successfully",
470
+ });
471
+ }
472
+ catch (error) {
473
+ console.error("Error deleting/cancelling execution:", error);
474
+ // Handle specific error cases
475
+ const errorMessage = error instanceof Error ? error.message : String(error);
476
+ const statusCode = errorMessage.includes("not found") ? 404 : 500;
477
+ res.status(statusCode).json({
478
+ success: false,
479
+ data: null,
480
+ error_data: errorMessage,
481
+ message: "Failed to delete/cancel execution",
482
+ });
483
+ }
484
+ });
262
485
  /**
263
486
  * GET /api/executions/:executionId/worktree
264
487
  *
@@ -267,7 +490,7 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
267
490
  router.get("/executions/:executionId/worktree", async (req, res) => {
268
491
  try {
269
492
  const { executionId } = req.params;
270
- const exists = await service.worktreeExists(executionId);
493
+ const exists = await req.project.executionService.worktreeExists(executionId);
271
494
  res.json({
272
495
  success: true,
273
496
  data: { exists },
@@ -287,11 +510,15 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
287
510
  * DELETE /api/executions/:executionId/worktree
288
511
  *
289
512
  * Delete the worktree for an execution
513
+ *
514
+ * Query parameters:
515
+ * - deleteBranch: if "true", also delete the execution's branch
290
516
  */
291
517
  router.delete("/executions/:executionId/worktree", async (req, res) => {
292
518
  try {
293
519
  const { executionId } = req.params;
294
- await service.deleteWorktree(executionId);
520
+ const { deleteBranch } = req.query;
521
+ await req.project.executionService.deleteWorktree(executionId, deleteBranch === "true");
295
522
  res.json({
296
523
  success: true,
297
524
  data: { executionId },
@@ -318,6 +545,231 @@ export function createExecutionsRouter(db, repoPath, transportManager, execution
318
545
  });
319
546
  }
320
547
  });
548
+ /**
549
+ * GET /api/executions/:executionId/sync/preview
550
+ *
551
+ * Preview sync changes and detect conflicts
552
+ *
553
+ * Returns preview of what would happen if sync is performed,
554
+ * including conflicts, diff, commits, and warnings.
555
+ */
556
+ router.get("/executions/:executionId/sync/preview", async (req, res) => {
557
+ try {
558
+ const { executionId } = req.params;
559
+ // Get worktree sync service
560
+ const syncService = getWorktreeSyncService(req);
561
+ // Preview sync
562
+ const preview = await syncService.previewSync(executionId);
563
+ res.json({
564
+ success: true,
565
+ data: preview,
566
+ });
567
+ }
568
+ catch (error) {
569
+ console.error(`Failed to preview sync for execution ${req.params.executionId}:`, error);
570
+ if (error instanceof WorktreeSyncError) {
571
+ const statusCode = getStatusCodeForSyncError(error);
572
+ res.status(statusCode).json({
573
+ success: false,
574
+ data: null,
575
+ error: error.message,
576
+ code: error.code,
577
+ });
578
+ }
579
+ else {
580
+ res.status(500).json({
581
+ success: false,
582
+ data: null,
583
+ error: "Internal server error",
584
+ message: error instanceof Error ? error.message : String(error),
585
+ });
586
+ }
587
+ }
588
+ });
589
+ /**
590
+ * POST /api/executions/:executionId/sync/squash
591
+ *
592
+ * Perform squash sync operation
593
+ *
594
+ * Combines all worktree changes into a single commit on the target branch.
595
+ * Automatically resolves JSONL conflicts using merge-resolver.
596
+ *
597
+ * Request body:
598
+ * - commitMessage?: string - Optional custom commit message
599
+ */
600
+ router.post("/executions/:executionId/sync/squash", async (req, res) => {
601
+ try {
602
+ const { executionId } = req.params;
603
+ const { commitMessage } = req.body || {};
604
+ // Get worktree sync service
605
+ const syncService = getWorktreeSyncService(req);
606
+ // Check if squashSync method exists
607
+ if (typeof syncService.squashSync !== "function") {
608
+ res.status(501).json({
609
+ success: false,
610
+ data: null,
611
+ error: "Squash sync not yet implemented",
612
+ message: "The squashSync operation is not available yet",
613
+ });
614
+ return;
615
+ }
616
+ // Perform squash sync
617
+ const result = await syncService.squashSync(executionId, commitMessage);
618
+ res.json({
619
+ success: true,
620
+ data: result,
621
+ });
622
+ }
623
+ catch (error) {
624
+ console.error(`Failed to squash sync execution ${req.params.executionId}:`, error);
625
+ if (error instanceof WorktreeSyncError) {
626
+ const statusCode = getStatusCodeForSyncError(error);
627
+ res.status(statusCode).json({
628
+ success: false,
629
+ data: null,
630
+ error: error.message,
631
+ code: error.code,
632
+ });
633
+ }
634
+ else {
635
+ res.status(500).json({
636
+ success: false,
637
+ data: null,
638
+ error: "Internal server error",
639
+ message: error instanceof Error ? error.message : String(error),
640
+ });
641
+ }
642
+ }
643
+ });
644
+ /**
645
+ * POST /api/executions/:executionId/commit
646
+ *
647
+ * Commit uncommitted changes for an execution
648
+ *
649
+ * Commits changes to the appropriate branch based on execution mode:
650
+ * - Local mode: Commits to target_branch (current branch)
651
+ * - Worktree mode: Commits to branch_name (temp branch) in worktree
652
+ *
653
+ * Request body:
654
+ * - message: string (required) - Commit message
655
+ */
656
+ router.post("/executions/:executionId/commit", async (req, res) => {
657
+ try {
658
+ const { executionId } = req.params;
659
+ const { message } = req.body;
660
+ // Validate commit message
661
+ if (!message || typeof message !== "string" || !message.trim()) {
662
+ res.status(400).json({
663
+ success: false,
664
+ data: null,
665
+ message: "Commit message is required and must be non-empty",
666
+ });
667
+ return;
668
+ }
669
+ const db = req.project.db;
670
+ const repoPath = req.project.path;
671
+ // Load execution from database
672
+ const execution = db
673
+ .prepare("SELECT * FROM executions WHERE id = ?")
674
+ .get(executionId);
675
+ if (!execution) {
676
+ res.status(404).json({
677
+ success: false,
678
+ data: null,
679
+ message: "Execution not found",
680
+ });
681
+ return;
682
+ }
683
+ // Parse files_changed
684
+ let filesChanged = [];
685
+ try {
686
+ if (execution.files_changed) {
687
+ const parsed = typeof execution.files_changed === "string"
688
+ ? JSON.parse(execution.files_changed)
689
+ : execution.files_changed;
690
+ filesChanged = Array.isArray(parsed) ? parsed : [parsed];
691
+ }
692
+ }
693
+ catch (error) {
694
+ console.error("Failed to parse files_changed:", error);
695
+ }
696
+ // Validate has uncommitted changes
697
+ if (filesChanged.length === 0) {
698
+ res.status(400).json({
699
+ success: false,
700
+ data: null,
701
+ message: "No files to commit",
702
+ });
703
+ return;
704
+ }
705
+ // Determine working directory and target branch based on mode
706
+ const mode = execution.mode || "local";
707
+ const workingDir = mode === "worktree" && execution.worktree_path
708
+ ? execution.worktree_path
709
+ : repoPath;
710
+ const targetBranch = mode === "worktree"
711
+ ? execution.branch_name
712
+ : execution.target_branch || "main";
713
+ console.log(`[Commit] Execution ${executionId}: mode=${mode}, workingDir=${workingDir}, targetBranch=${targetBranch}`);
714
+ // Escape commit message for shell
715
+ const escapedMessage = message.replace(/"/g, '\\"');
716
+ // Execute git operations
717
+ try {
718
+ // Add files
719
+ const addCommand = `git add ${filesChanged.map((f) => `"${f}"`).join(" ")}`;
720
+ execSync(addCommand, {
721
+ cwd: workingDir,
722
+ encoding: "utf-8",
723
+ stdio: "pipe",
724
+ });
725
+ // Commit with message
726
+ const commitCommand = `git commit -m "${escapedMessage}"`;
727
+ execSync(commitCommand, {
728
+ cwd: workingDir,
729
+ encoding: "utf-8",
730
+ stdio: "pipe",
731
+ });
732
+ // Get commit SHA
733
+ const commitSha = execSync("git rev-parse HEAD", {
734
+ cwd: workingDir,
735
+ encoding: "utf-8",
736
+ stdio: "pipe",
737
+ }).trim();
738
+ console.log(`[Commit] Successfully committed ${filesChanged.length} files: ${commitSha}`);
739
+ // Note: We do NOT update execution.after_commit here
740
+ // That field represents the state at execution completion time
741
+ // Manual commits after execution are tracked separately
742
+ res.json({
743
+ success: true,
744
+ data: {
745
+ commitSha,
746
+ filesCommitted: filesChanged.length,
747
+ branch: targetBranch,
748
+ },
749
+ message: `Successfully committed ${filesChanged.length} file${filesChanged.length !== 1 ? "s" : ""}`,
750
+ });
751
+ }
752
+ catch (gitError) {
753
+ console.error("Git operation failed:", gitError);
754
+ const errorMessage = gitError instanceof Error ? gitError.message : String(gitError);
755
+ res.status(500).json({
756
+ success: false,
757
+ data: null,
758
+ message: "Git commit failed",
759
+ error: errorMessage,
760
+ });
761
+ }
762
+ }
763
+ catch (error) {
764
+ console.error(`Failed to commit for execution ${req.params.executionId}:`, error);
765
+ res.status(500).json({
766
+ success: false,
767
+ data: null,
768
+ error: "Internal server error",
769
+ message: error instanceof Error ? error.message : String(error),
770
+ });
771
+ }
772
+ });
321
773
  return router;
322
774
  }
323
775
  //# sourceMappingURL=executions.js.map