mulmoclaude 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/mulmoclaude.js +7 -24
- package/client/assets/html2canvas-Cx501zZr-DiKaqnKs.js +5 -0
- package/client/assets/{index-eHWB79u5.js → index-C94GcmNa.js} +203 -198
- package/client/assets/index-CY-WpQUm.css +2 -0
- package/client/assets/{index.es-D4YyL_Dg-BfRHLTZV.js → index.es-D4YyL_Dg-5ipqh8Pe.js} +5 -5
- package/client/assets/material-symbols-outlined-NzYEeyps.woff2 +0 -0
- package/client/index.html +2 -4
- package/package.json +17 -15
- package/server/agent/attachmentConverter.ts +2 -2
- package/server/agent/backend/claude-code.ts +170 -0
- package/server/agent/backend/index.ts +14 -0
- package/server/agent/backend/types.ts +65 -0
- package/server/agent/index.ts +31 -159
- package/server/agent/mcp-server.ts +88 -10
- package/server/agent/mcp-tools/index.ts +8 -7
- package/server/agent/mcp-tools/notify.ts +76 -0
- package/server/agent/mcp-tools/x.ts +12 -2
- package/server/agent/plugin-names.ts +10 -4
- package/server/agent/prompt.ts +187 -26
- package/server/agent/resumeFailover.ts +5 -5
- package/server/agent/sandboxMounts.ts +3 -3
- package/server/api/auth/bearerAuth.ts +3 -3
- package/server/api/auth/token.ts +2 -2
- package/server/api/routes/agent.ts +99 -4
- package/server/api/routes/chart.ts +13 -0
- package/server/api/routes/chat-index.ts +2 -1
- package/server/api/routes/config.ts +35 -8
- package/server/api/routes/files.ts +75 -24
- package/server/api/routes/html.ts +15 -2
- package/server/api/routes/image.ts +75 -20
- package/server/api/routes/mulmo-script.ts +33 -31
- package/server/api/routes/news.ts +146 -0
- package/server/api/routes/notifications.ts +58 -2
- package/server/api/routes/pdf.ts +2 -2
- package/server/api/routes/plugins.ts +73 -91
- package/server/api/routes/presentHtml.ts +9 -0
- package/server/api/routes/roles.ts +12 -2
- package/server/api/routes/scheduler.ts +20 -11
- package/server/api/routes/schedulerTasks.ts +58 -21
- package/server/api/routes/sessions.ts +15 -4
- package/server/api/routes/sessionsCursor.ts +4 -4
- package/server/api/routes/skills.ts +26 -5
- package/server/api/routes/sources.ts +8 -7
- package/server/api/routes/todos.ts +30 -0
- package/server/api/routes/todosColumnsHandlers.ts +13 -27
- package/server/api/routes/todosHandlers.ts +1 -1
- package/server/api/routes/todosItemsHandlers.ts +14 -14
- package/server/api/routes/wiki/frontmatter.ts +86 -0
- package/server/api/routes/wiki.ts +335 -75
- package/server/api/sandboxStatus.ts +1 -1
- package/server/events/notifications.ts +32 -8
- package/server/events/pub-sub/index.ts +3 -3
- package/server/events/relay-client.ts +26 -16
- package/server/events/resolveRelayBridgeOptions.ts +125 -0
- package/server/index.ts +72 -49
- package/server/system/config.ts +5 -5
- package/server/system/credentials.ts +7 -5
- package/server/system/env.ts +15 -5
- package/server/system/macosNotify.ts +152 -0
- package/server/utils/errors.ts +11 -2
- package/server/utils/fetch.ts +54 -0
- package/server/utils/files/atomic.ts +18 -17
- package/server/utils/files/image-store.ts +19 -13
- package/server/utils/files/journal-io.ts +2 -2
- package/server/utils/files/json.ts +5 -5
- package/server/utils/files/markdown-image-fill.ts +131 -0
- package/server/utils/files/markdown-store.ts +22 -6
- package/server/utils/files/naming.ts +20 -10
- package/server/utils/files/reference-dirs-io.ts +3 -3
- package/server/utils/files/roles-io.ts +4 -4
- package/server/utils/files/safe.ts +14 -14
- package/server/utils/files/scheduler-overrides-io.ts +2 -2
- package/server/utils/files/spreadsheet-store.ts +15 -10
- package/server/utils/files/workspace-io.ts +12 -12
- package/server/utils/gemini.ts +30 -4
- package/server/utils/gitignore.ts +9 -9
- package/server/utils/id.ts +40 -8
- package/server/utils/json.ts +5 -5
- package/server/utils/logBackgroundError.ts +12 -3
- package/server/utils/logPreview.ts +24 -0
- package/server/utils/markdown.ts +5 -5
- package/server/utils/port.d.mts +6 -0
- package/server/utils/port.mjs +48 -0
- package/server/utils/promptMeta.ts +32 -0
- package/server/utils/request.ts +12 -6
- package/server/utils/slug.ts +65 -4
- package/server/utils/spawn.ts +1 -1
- package/server/utils/types.ts +2 -2
- package/server/workspace/chat-index/index.ts +1 -1
- package/server/workspace/chat-index/summarizer.ts +5 -5
- package/server/workspace/custom-dirs.ts +5 -5
- package/server/workspace/helps/gemini.md +57 -0
- package/server/workspace/helps/index.md +2 -1
- package/server/workspace/helps/sources.md +42 -0
- package/server/workspace/helps/wiki.md +40 -5
- package/server/workspace/journal/archivist-cli.ts +121 -0
- package/server/workspace/journal/{archivist.ts → archivist-schemas.ts} +12 -120
- package/server/workspace/journal/dailyPass.ts +78 -38
- package/server/workspace/journal/diff.ts +2 -2
- package/server/workspace/journal/index.ts +56 -5
- package/server/workspace/journal/memoryExtractor.ts +1 -1
- package/server/workspace/journal/optimizationPass.ts +4 -5
- package/server/workspace/journal/paths.ts +8 -24
- package/server/workspace/journal/state.ts +18 -8
- package/server/workspace/news/reader.ts +248 -0
- package/server/workspace/paths.ts +4 -3
- package/server/workspace/reference-dirs.ts +3 -3
- package/server/workspace/skills/parser.ts +6 -6
- package/server/workspace/skills/scheduler.ts +5 -4
- package/server/workspace/skills/user-tasks.ts +3 -2
- package/server/workspace/skills/writer.ts +3 -3
- package/server/workspace/sources/arxivDiscovery.ts +2 -2
- package/server/workspace/sources/classifier.ts +1 -1
- package/server/workspace/sources/fetchers/rss.ts +5 -5
- package/server/workspace/sources/fetchers/rssParser.ts +4 -4
- package/server/workspace/sources/interests.ts +3 -3
- package/server/workspace/sources/paths.ts +6 -6
- package/server/workspace/sources/pipeline/fetch.ts +59 -13
- package/server/workspace/sources/pipeline/index.ts +59 -7
- package/server/workspace/sources/pipeline/notify.ts +13 -5
- package/server/workspace/sources/pipeline/plan.ts +11 -9
- package/server/workspace/sources/pipeline/summarize.ts +1 -1
- package/server/workspace/sources/pipeline/write.ts +5 -5
- package/server/workspace/sources/rateLimiter.ts +1 -1
- package/server/workspace/sources/sourceState.ts +9 -4
- package/server/workspace/sources/types.ts +9 -0
- package/server/workspace/sources/urls.ts +1 -1
- package/server/workspace/tool-trace/classify.ts +4 -4
- package/server/workspace/workspace.ts +7 -7
- package/src/App.vue +477 -251
- package/src/components/CanvasViewToggle.vue +12 -10
- package/src/components/ChatInput.vue +112 -105
- package/src/components/FileContentHeader.vue +10 -7
- package/src/components/FileContentRenderer.vue +37 -10
- package/src/components/FileTree.vue +34 -4
- package/src/components/FileTreePane.vue +32 -27
- package/src/components/FilesView.vue +5 -3
- package/src/components/FilterChip.vue +22 -0
- package/src/components/LockStatusPopup.vue +19 -13
- package/src/components/NewsView.vue +252 -0
- package/src/components/NotificationBell.vue +35 -9
- package/src/components/NotificationToast.vue +4 -1
- package/src/components/PageChatComposer.vue +101 -0
- package/src/components/PluginLauncher.vue +36 -62
- package/src/components/RightSidebar.vue +13 -10
- package/src/components/RoleSelector.vue +3 -2
- package/src/components/SessionHeaderControls.vue +63 -0
- package/src/components/SessionHistoryExpandButton.vue +30 -0
- package/src/components/SessionHistoryPanel.vue +64 -93
- package/src/components/SessionHistoryToggleButton.vue +40 -0
- package/src/components/SessionRoleIcon.vue +72 -0
- package/src/components/SessionSidebar.vue +96 -0
- package/src/components/SessionTabBar.vue +44 -51
- package/src/components/SettingsMcpTab.vue +361 -52
- package/src/components/SettingsModal.vue +203 -72
- package/src/components/SettingsReferenceDirsTab.vue +72 -51
- package/src/components/SettingsWorkspaceDirsTab.vue +74 -51
- package/src/components/SidebarHeader.vue +50 -16
- package/src/components/SourcesManager.vue +900 -0
- package/src/components/SourcesView.vue +45 -0
- package/src/components/StackView.vue +84 -48
- package/src/components/SuggestionsPanel.vue +25 -36
- package/src/components/SystemFileBanner.vue +106 -0
- package/src/components/ThinkingIndicator.vue +41 -0
- package/src/components/TodoExplorer.vue +72 -22
- package/src/components/todo/TodoAddDialog.vue +17 -12
- package/src/components/todo/TodoEditDialog.vue +7 -2
- package/src/components/todo/TodoEditPanel.vue +15 -10
- package/src/components/todo/TodoKanbanView.vue +16 -6
- package/src/components/todo/TodoListView.vue +14 -3
- package/src/components/todo/TodoTableView.vue +36 -5
- package/src/composables/favicon/conditions.ts +76 -0
- package/src/composables/favicon/resolveColor.ts +93 -0
- package/src/composables/favicon/types.ts +61 -0
- package/src/composables/useAppApi.ts +23 -0
- package/src/composables/useChatScroll.ts +5 -5
- package/src/composables/useCurrentRole.ts +32 -0
- package/src/composables/useDynamicFavicon.ts +174 -58
- package/src/composables/useEventListeners.ts +7 -12
- package/src/composables/useFaviconState.ts +93 -12
- package/src/composables/useFileSelection.ts +25 -6
- package/src/composables/useHealth.ts +76 -7
- package/src/composables/useLayoutMode.ts +27 -0
- package/src/composables/useNewsItems.ts +38 -0
- package/src/composables/useNewsReadState.ts +75 -0
- package/src/composables/useNotifications.ts +76 -13
- package/src/composables/usePendingCalls.ts +11 -1
- package/src/composables/useRoles.ts +6 -10
- package/src/composables/useRunElapsed.ts +80 -0
- package/src/composables/useSessionDerived.ts +21 -5
- package/src/composables/useSessionHistory.ts +7 -17
- package/src/composables/useSidePanelVisible.ts +25 -0
- package/src/composables/useViewLayout.ts +16 -37
- package/src/config/apiRoutes.ts +19 -6
- package/src/config/historyFilters.ts +30 -0
- package/src/config/mcpCatalog.ts +285 -0
- package/src/config/mcpTypes.ts +26 -0
- package/src/config/roles.ts +19 -51
- package/src/config/systemFileDescriptors.ts +170 -0
- package/src/config/toolNames.ts +6 -1
- package/src/config/workspacePaths.ts +1 -0
- package/src/index.css +14 -0
- package/src/lang/de.ts +706 -0
- package/src/lang/en.ts +726 -0
- package/src/lang/es.ts +712 -0
- package/src/lang/fr.ts +704 -0
- package/src/lang/ja.ts +707 -0
- package/src/lang/ko.ts +709 -0
- package/src/lang/pt-BR.ts +702 -0
- package/src/lang/zh.ts +705 -0
- package/src/lib/vue-i18n.ts +97 -0
- package/src/main.ts +3 -0
- package/src/plugins/canvas/View.vue +104 -186
- package/src/plugins/canvas/definition.ts +0 -8
- package/src/plugins/canvas/index.ts +3 -2
- package/src/plugins/chart/Preview.vue +1 -1
- package/src/plugins/chart/View.vue +9 -4
- package/src/plugins/chart/index.ts +3 -2
- package/src/plugins/editImage/index.ts +3 -2
- package/src/plugins/generateImage/index.ts +3 -2
- package/src/plugins/manageRoles/Preview.vue +4 -1
- package/src/plugins/manageRoles/View.vue +67 -46
- package/src/plugins/manageRoles/index.ts +3 -2
- package/src/plugins/manageSkills/Preview.vue +8 -3
- package/src/plugins/manageSkills/View.vue +39 -34
- package/src/plugins/manageSkills/index.ts +3 -2
- package/src/plugins/manageSource/Preview.vue +1 -1
- package/src/plugins/manageSource/View.vue +3 -687
- package/src/plugins/manageSource/index.ts +3 -2
- package/src/plugins/markdown/Preview.vue +1 -1
- package/src/plugins/markdown/View.vue +164 -73
- package/src/plugins/markdown/definition.ts +6 -4
- package/src/plugins/markdown/index.ts +3 -2
- package/src/plugins/presentForm/Preview.vue +99 -0
- package/src/plugins/presentForm/View.vue +675 -0
- package/src/plugins/presentForm/definition.ts +127 -0
- package/src/plugins/presentForm/index.ts +18 -0
- package/src/plugins/presentForm/plugin.ts +94 -0
- package/src/plugins/presentForm/types.ts +109 -0
- package/src/plugins/presentHtml/Preview.vue +1 -1
- package/src/plugins/presentHtml/View.vue +7 -4
- package/src/plugins/presentHtml/index.ts +3 -2
- package/src/plugins/presentMulmoScript/Preview.vue +1 -1
- package/src/plugins/presentMulmoScript/View.vue +36 -26
- package/src/plugins/presentMulmoScript/index.ts +3 -2
- package/src/plugins/scheduler/AutomationsPreview.vue +37 -0
- package/src/plugins/scheduler/AutomationsView.vue +23 -0
- package/src/plugins/scheduler/CalendarView.vue +23 -0
- package/src/plugins/scheduler/LegacySchedulerView.vue +32 -0
- package/src/plugins/scheduler/Preview.vue +7 -4
- package/src/plugins/scheduler/TasksTab.vue +119 -28
- package/src/plugins/scheduler/View.vue +75 -32
- package/src/plugins/scheduler/automationsDefinition.ts +58 -0
- package/src/plugins/scheduler/calendarDefinition.ts +46 -0
- package/src/plugins/scheduler/formatSchedule.ts +93 -0
- package/src/plugins/scheduler/index.ts +68 -14
- package/src/plugins/scheduler/legacyShape.ts +34 -0
- package/src/plugins/spreadsheet/Preview.vue +9 -5
- package/src/plugins/spreadsheet/View.vue +43 -57
- package/src/plugins/spreadsheet/engine/responseDecoder.ts +2 -1
- package/src/plugins/spreadsheet/index.ts +3 -2
- package/src/plugins/textResponse/Preview.vue +15 -58
- package/src/plugins/textResponse/View.vue +42 -45
- package/src/plugins/textResponse/utils.ts +25 -0
- package/src/plugins/todo/Preview.vue +11 -6
- package/src/plugins/todo/View.vue +27 -13
- package/src/plugins/todo/composables/useTodos.ts +3 -1
- package/src/plugins/todo/index.ts +3 -2
- package/src/plugins/ui-image/ImagePreview.vue +6 -3
- package/src/plugins/ui-image/ImageView.vue +7 -4
- package/src/plugins/wiki/Preview.vue +5 -2
- package/src/plugins/wiki/View.vue +539 -92
- package/src/plugins/wiki/index.ts +5 -2
- package/src/plugins/wiki/route.ts +121 -0
- package/src/router/guards.ts +43 -24
- package/src/router/index.ts +53 -26
- package/src/router/pageRoutes.ts +23 -0
- package/src/tools/index.ts +12 -5
- package/src/tools/legacyPluginNames.ts +13 -0
- package/src/types/notification.ts +31 -6
- package/src/types/vue-i18n.d.ts +20 -0
- package/src/utils/agent/eventDispatch.ts +3 -6
- package/src/utils/agent/formatElapsed.ts +37 -0
- package/src/utils/agent/request.ts +22 -1
- package/src/utils/canvas/layoutMode.ts +26 -0
- package/src/utils/canvas/sidePanelVisible.ts +19 -0
- package/src/utils/dom/scrollIntoViewByTestId.ts +38 -0
- package/src/utils/errors.ts +9 -2
- package/src/utils/files/filename.ts +24 -0
- package/src/utils/filesPreview/schedulerPreview.ts +9 -3
- package/src/utils/id.ts +18 -0
- package/src/utils/image/cacheBust.ts +16 -0
- package/src/utils/image/resolve.ts +16 -0
- package/src/utils/markdown/taskList.ts +175 -0
- package/src/utils/mcp/interpolateSpec.ts +97 -0
- package/src/utils/notification/dispatch.ts +51 -15
- package/src/utils/path/workspaceLinkRouter.ts +99 -0
- package/src/utils/session/mergeSessions.ts +5 -0
- package/src/utils/sources/filter.ts +69 -0
- package/src/vite-env.d.ts +9 -0
- package/client/assets/chunk-vKJrgz-R-C_I3GbVV.js +0 -1
- package/client/assets/html2canvas-Cx501zZr-BF5dYYkY.js +0 -5
- package/client/assets/index-Bm70FDU2.css +0 -1
- package/client/assets/typeof-DBp4T-Ny-BC0P-2DM.js +0 -1
- package/server/workspace/journal/linkRewrite.ts +0 -4
- package/src/components/ToolResultsPanel.vue +0 -77
- package/src/composables/useCanvasViewMode.ts +0 -121
- package/src/plugins/scheduler/definition.ts +0 -57
- package/src/utils/canvas/viewMode.ts +0 -46
- package/src/utils/role/plugins.ts +0 -12
- package/src/utils/session/seedRoleDefault.ts +0 -35
- /package/client/assets/{purify.es-Fx1Nqyry-PeS5RUhs.js → purify.es-Fx1Nqyry-BwJECkqS.js} +0 -0
|
Binary file
|
package/client/index.html
CHANGED
|
@@ -17,10 +17,8 @@
|
|
|
17
17
|
<title>MulmoClaude</title>
|
|
18
18
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='30' height='30' x='1' y='1' rx='6' fill='%236B7280'/><text x='16' y='17' text-anchor='middle' dominant-baseline='central' font-family='sans-serif' font-weight='bold' font-size='20' fill='white'>M</text></svg>" />
|
|
19
19
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
|
20
|
-
<script type="module" crossorigin src="/assets/index-
|
|
21
|
-
<link rel="
|
|
22
|
-
<link rel="modulepreload" crossorigin href="/assets/typeof-DBp4T-Ny-BC0P-2DM.js">
|
|
23
|
-
<link rel="stylesheet" crossorigin href="/assets/index-Bm70FDU2.css">
|
|
20
|
+
<script type="module" crossorigin src="/assets/index-C94GcmNa.js"></script>
|
|
21
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CY-WpQUm.css">
|
|
24
22
|
</head>
|
|
25
23
|
<body>
|
|
26
24
|
<div id="app"></div>
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mulmoclaude",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "MulmoClaude — GUI-chat with Claude Code + long-term memory. One command to start.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mulmoclaude": "bin/mulmoclaude.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"
|
|
10
|
+
"prepack": "node bin/prepare-dist.js",
|
|
11
11
|
"typecheck": "echo \"mulmoclaude: no typecheck (launcher is plain JS; server and src are dist artifacts)\"",
|
|
12
12
|
"test": "echo no tests yet"
|
|
13
13
|
},
|
|
@@ -18,20 +18,19 @@
|
|
|
18
18
|
"src/"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@mulmobridge/chat-service": "^0.1.1",
|
|
22
|
-
"@mulmobridge/client": "^0.1.1",
|
|
23
|
-
"@mulmobridge/protocol": "^0.1.3",
|
|
24
|
-
"@receptron/task-scheduler": "^0.1.0",
|
|
25
21
|
"@google/genai": "^1.50.1",
|
|
26
|
-
"@mulmocast/types": "^2.6.7",
|
|
27
|
-
"@gui-chat-plugin/mindmap": "^0.4.0",
|
|
28
|
-
"@gui-chat-plugin/present3d": "^0.1.0",
|
|
29
22
|
"@gui-chat-plugin/browse": "^0.2.0",
|
|
30
23
|
"@gui-chat-plugin/camera": "^0.4.0",
|
|
24
|
+
"@gui-chat-plugin/mindmap": "^0.4.0",
|
|
25
|
+
"@gui-chat-plugin/present3d": "^0.1.0",
|
|
31
26
|
"@gui-chat-plugin/weather": "^0.1.0",
|
|
32
|
-
"@
|
|
27
|
+
"@mulmobridge/chat-service": "^0.1.2",
|
|
28
|
+
"@mulmobridge/client": "^0.1.4",
|
|
29
|
+
"@mulmobridge/protocol": "^0.1.4",
|
|
30
|
+
"@mulmocast/types": "^2.6.8",
|
|
33
31
|
"@mulmochat-plugin/quiz": "0.4.0",
|
|
34
32
|
"@mulmochat-plugin/ui-image": "^0.3.0",
|
|
33
|
+
"@receptron/task-scheduler": "^0.1.0",
|
|
35
34
|
"cors": "^2.8.6",
|
|
36
35
|
"dotenv": "^17.4.2",
|
|
37
36
|
"express": "^5.2.1",
|
|
@@ -40,15 +39,18 @@
|
|
|
40
39
|
"ignore": "^7.0.5",
|
|
41
40
|
"mammoth": "^1.12.0",
|
|
42
41
|
"marked": "^18.0.2",
|
|
43
|
-
"mulmocast": "^2.6.
|
|
44
|
-
"puppeteer": "^24.
|
|
42
|
+
"mulmocast": "^2.6.8",
|
|
43
|
+
"puppeteer": "^24.42.0",
|
|
45
44
|
"socket.io": "^4.8.3",
|
|
46
45
|
"socket.io-client": "^4.8.3",
|
|
47
|
-
"
|
|
46
|
+
"tsx": "^4.19.0",
|
|
47
|
+
"uuid": "^14.0.0",
|
|
48
48
|
"ws": "^8.20.0",
|
|
49
49
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
|
50
|
-
"zod": "^4.3.6"
|
|
51
|
-
|
|
50
|
+
"zod": "^4.3.6"
|
|
51
|
+
},
|
|
52
|
+
"optionalDependencies": {
|
|
53
|
+
"node-pty": "^1.1.0"
|
|
52
54
|
},
|
|
53
55
|
"engines": {
|
|
54
56
|
"node": ">=20"
|
|
@@ -17,7 +17,7 @@ import * as XLSX from "xlsx";
|
|
|
17
17
|
import { execFile } from "child_process";
|
|
18
18
|
import { mkdtemp, readFile, writeFile, rm } from "fs/promises";
|
|
19
19
|
import path from "path";
|
|
20
|
-
import
|
|
20
|
+
import { tmpdir } from "os";
|
|
21
21
|
import { promisify } from "util";
|
|
22
22
|
|
|
23
23
|
const execFileAsync = promisify(execFile);
|
|
@@ -116,7 +116,7 @@ async function tryDockerLibreOffice(): Promise<boolean> {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
async function convertPptxToPdf(data: string): Promise<Buffer | null> {
|
|
119
|
-
const tmpDir = await mkdtemp(path.join(
|
|
119
|
+
const tmpDir = await mkdtemp(path.join(tmpdir(), "pptx-"));
|
|
120
120
|
const inputPath = path.join(tmpDir, "input.pptx");
|
|
121
121
|
const outputPath = path.join(tmpDir, "input.pdf");
|
|
122
122
|
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Claude Code backend: spawns the `claude` CLI as a subprocess (or
|
|
2
|
+
// inside the mulmoclaude-sandbox Docker image) and translates its
|
|
3
|
+
// stream-json output into portable AgentEvents.
|
|
4
|
+
//
|
|
5
|
+
// This file is the single seam between the orchestrator in
|
|
6
|
+
// server/agent/index.ts (which is backend-agnostic) and the Claude
|
|
7
|
+
// CLI specifics. Pure helpers it depends on (CLI arg construction,
|
|
8
|
+
// Docker arg construction, stream parsing) stay in their existing
|
|
9
|
+
// home so the existing test suite under test/agent/ keeps working
|
|
10
|
+
// unchanged.
|
|
11
|
+
|
|
12
|
+
import { spawn, type ChildProcessByStdio } from "child_process";
|
|
13
|
+
import type { Readable, Writable } from "stream";
|
|
14
|
+
import { buildCliArgs, buildDockerSpawnArgs, buildUserMessageLine } from "../config.js";
|
|
15
|
+
import { resolveSandboxAuth } from "../sandboxMounts.js";
|
|
16
|
+
import { getCachedReferenceDirs, referenceDirMountArgs } from "../../workspace/reference-dirs.js";
|
|
17
|
+
import { createStreamParser, type AgentEvent, type RawStreamEvent } from "../stream.js";
|
|
18
|
+
import { log } from "../../system/logger/index.js";
|
|
19
|
+
import { EVENT_TYPES } from "../../../src/types/events.js";
|
|
20
|
+
import { env } from "../../system/env.js";
|
|
21
|
+
import type { AgentInput, LLMBackend } from "./types.js";
|
|
22
|
+
|
|
23
|
+
type ClaudeProc = ChildProcessByStdio<Writable, Readable, Readable>;
|
|
24
|
+
|
|
25
|
+
function spawnClaude(useDocker: boolean, workspacePath: string, cliArgs: string[]): ClaudeProc {
|
|
26
|
+
if (!useDocker) {
|
|
27
|
+
return spawn("claude", cliArgs, {
|
|
28
|
+
cwd: workspacePath,
|
|
29
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
const sandboxAuth = resolveSandboxAuth({
|
|
33
|
+
sshAgentForward: env.sandboxSshAgentForward,
|
|
34
|
+
sshAllowedHosts: env.sandboxSshAllowedHosts,
|
|
35
|
+
configMountNames: env.sandboxMountConfigs,
|
|
36
|
+
sshAuthSock: process.env.SSH_AUTH_SOCK,
|
|
37
|
+
});
|
|
38
|
+
const refDirArgs = referenceDirMountArgs(getCachedReferenceDirs());
|
|
39
|
+
const dockerArgs = buildDockerSpawnArgs({
|
|
40
|
+
workspacePath,
|
|
41
|
+
cliArgs,
|
|
42
|
+
uid: process.getuid?.() ?? 1000,
|
|
43
|
+
gid: process.getgid?.() ?? 1000,
|
|
44
|
+
platform: process.platform,
|
|
45
|
+
sandboxAuthArgs: [...sandboxAuth.args, ...refDirArgs],
|
|
46
|
+
sshAgentForward: env.sandboxSshAgentForward,
|
|
47
|
+
});
|
|
48
|
+
return spawn("docker", dockerArgs, { stdio: ["pipe", "pipe", "pipe"] });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Track MCP tool usage to detect silent MCP server failures.
|
|
52
|
+
// If ToolSearch was called but no mcp__* tool was ever invoked,
|
|
53
|
+
// the MCP server likely crashed on startup (e.g. module resolution
|
|
54
|
+
// failure inside Docker). See #430.
|
|
55
|
+
function createMcpTracker() {
|
|
56
|
+
let toolSearchCalled = false;
|
|
57
|
+
let mcpToolCalled = false;
|
|
58
|
+
return {
|
|
59
|
+
track(event: AgentEvent) {
|
|
60
|
+
if (event.type !== EVENT_TYPES.toolCall) return;
|
|
61
|
+
if (event.toolName === "ToolSearch") toolSearchCalled = true;
|
|
62
|
+
if (event.toolName.startsWith("mcp__")) mcpToolCalled = true;
|
|
63
|
+
},
|
|
64
|
+
logIfSuspicious() {
|
|
65
|
+
if (toolSearchCalled && !mcpToolCalled) {
|
|
66
|
+
log.warn(
|
|
67
|
+
"agent",
|
|
68
|
+
"ToolSearch was used but no MCP tool was called — the MCP server may have crashed. " +
|
|
69
|
+
"Check Docker volume mounts and package.json exports. " +
|
|
70
|
+
"Run: npx tsx --test test/agent/test_mcp_docker_smoke.ts",
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function* readAgentEvents(proc: ClaudeProc): AsyncGenerator<AgentEvent> {
|
|
78
|
+
let stderrOutput = "";
|
|
79
|
+
let stderrBuffer = "";
|
|
80
|
+
proc.stderr.on("data", (chunk: Buffer) => {
|
|
81
|
+
const text = chunk.toString();
|
|
82
|
+
stderrOutput += text;
|
|
83
|
+
stderrBuffer += text;
|
|
84
|
+
const lines = stderrBuffer.split("\n");
|
|
85
|
+
stderrBuffer = lines.pop() ?? "";
|
|
86
|
+
for (const line of lines) {
|
|
87
|
+
if (line.trim()) log.error("agent-stderr", line);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Stateful parser tracks whether text was already streamed via
|
|
92
|
+
// assistant content blocks so the final `result` event's duplicate
|
|
93
|
+
// text is suppressed. See createStreamParser() in stream.ts.
|
|
94
|
+
const parser = createStreamParser();
|
|
95
|
+
|
|
96
|
+
const mcpTracker = createMcpTracker();
|
|
97
|
+
|
|
98
|
+
let buffer = "";
|
|
99
|
+
for await (const chunk of proc.stdout) {
|
|
100
|
+
buffer += (chunk as Buffer).toString();
|
|
101
|
+
const lines = buffer.split("\n");
|
|
102
|
+
buffer = lines.pop() ?? "";
|
|
103
|
+
|
|
104
|
+
for (const line of lines) {
|
|
105
|
+
if (!line.trim()) continue;
|
|
106
|
+
let event: RawStreamEvent;
|
|
107
|
+
try {
|
|
108
|
+
event = JSON.parse(line);
|
|
109
|
+
} catch {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
for (const agentEvent of parser.parse(event)) {
|
|
113
|
+
mcpTracker.track(agentEvent);
|
|
114
|
+
yield agentEvent;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const exitCode = await new Promise<number>((resolve) => proc.on("close", resolve));
|
|
120
|
+
|
|
121
|
+
if (stderrBuffer.trim()) log.error("agent-stderr", stderrBuffer);
|
|
122
|
+
log.info("agent", "claude exited", { exitCode });
|
|
123
|
+
mcpTracker.logIfSuspicious();
|
|
124
|
+
|
|
125
|
+
if (exitCode !== 0) {
|
|
126
|
+
yield {
|
|
127
|
+
type: EVENT_TYPES.error,
|
|
128
|
+
message: stderrOutput || `claude exited with code ${exitCode}`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function* runClaudeAgent(input: AgentInput): AsyncGenerator<AgentEvent> {
|
|
134
|
+
const cliArgs = buildCliArgs({
|
|
135
|
+
systemPrompt: input.systemPrompt,
|
|
136
|
+
activePlugins: input.activePlugins,
|
|
137
|
+
claudeSessionId: input.sessionToken,
|
|
138
|
+
mcpConfigPath: input.mcpConfigPath,
|
|
139
|
+
extraAllowedTools: input.extraAllowedTools,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const proc = spawnClaude(input.useDocker, input.workspacePath, cliArgs);
|
|
143
|
+
|
|
144
|
+
// stream-json input mode: stream the user turn as a single JSON
|
|
145
|
+
// line to stdin, then close the pipe so the CLI knows no further
|
|
146
|
+
// turns are coming. Writing before attaching the abort handler is
|
|
147
|
+
// fine — if the write fails because the process already died for
|
|
148
|
+
// other reasons, the readAgentEvents loop below surfaces it.
|
|
149
|
+
const messageLine = await buildUserMessageLine(input.message, input.attachments);
|
|
150
|
+
proc.stdin.write(messageLine);
|
|
151
|
+
proc.stdin.end();
|
|
152
|
+
|
|
153
|
+
const onAbort = () => {
|
|
154
|
+
if (!proc.killed) proc.kill();
|
|
155
|
+
};
|
|
156
|
+
input.abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
yield* readAgentEvents(proc);
|
|
160
|
+
} finally {
|
|
161
|
+
input.abortSignal?.removeEventListener("abort", onAbort);
|
|
162
|
+
if (!proc.killed) proc.kill();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const claudeCodeBackend: LLMBackend = {
|
|
167
|
+
id: "claude-code",
|
|
168
|
+
capabilities: { sessionResume: true, mcp: true },
|
|
169
|
+
runAgent: runClaudeAgent,
|
|
170
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Backend factory. Today there is only ClaudeCodeBackend; future
|
|
2
|
+
// backends (OpenAI, Ollama native, Gemini) are selected here based on
|
|
3
|
+
// env / settings. Callers go through getActiveBackend() rather than
|
|
4
|
+
// importing a concrete adapter so adding a backend doesn't require
|
|
5
|
+
// touching every call site.
|
|
6
|
+
|
|
7
|
+
import { claudeCodeBackend } from "./claude-code.js";
|
|
8
|
+
import type { LLMBackend } from "./types.js";
|
|
9
|
+
|
|
10
|
+
export type { AgentInput, BackendCapabilities, LLMBackend } from "./types.js";
|
|
11
|
+
|
|
12
|
+
export function getActiveBackend(): LLMBackend {
|
|
13
|
+
return claudeCodeBackend;
|
|
14
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// LLM backend abstraction. Today the only implementation is
|
|
2
|
+
// ClaudeCodeBackend (server/agent/backend/claude-code.ts), which spawns
|
|
3
|
+
// the `claude` CLI as a subprocess. The interface exists so future
|
|
4
|
+
// backends (OpenAI, Ollama native, Gemini, etc.) can plug in here
|
|
5
|
+
// without the orchestrator in server/agent/index.ts knowing which one
|
|
6
|
+
// is active.
|
|
7
|
+
//
|
|
8
|
+
// See plans/refactor-llm-backend-abstraction.md for the broader plan.
|
|
9
|
+
|
|
10
|
+
import type { Attachment } from "@mulmobridge/protocol";
|
|
11
|
+
import type { Role } from "../../../src/config/roles.js";
|
|
12
|
+
import type { AgentEvent } from "../stream.js";
|
|
13
|
+
|
|
14
|
+
/** Inputs the orchestrator passes to a backend for one user turn.
|
|
15
|
+
* The orchestrator owns role expansion, system prompt building, and
|
|
16
|
+
* MCP config writing. The backend owns the LLM call itself plus
|
|
17
|
+
* translation of provider-specific stream events into AgentEvent. */
|
|
18
|
+
export interface AgentInput {
|
|
19
|
+
systemPrompt: string;
|
|
20
|
+
message: string;
|
|
21
|
+
role: Role;
|
|
22
|
+
workspacePath: string;
|
|
23
|
+
sessionId: string;
|
|
24
|
+
port: number;
|
|
25
|
+
/** Opaque, backend-specific resume token. For Claude this is the
|
|
26
|
+
* CLI's session id passed to --resume; other backends may
|
|
27
|
+
* interpret it differently or ignore it entirely
|
|
28
|
+
* (capabilities.sessionResume === false). */
|
|
29
|
+
sessionToken?: string;
|
|
30
|
+
attachments?: Attachment[];
|
|
31
|
+
/** Active MCP plugin names (the subset of role.availablePlugins
|
|
32
|
+
* that is actually registered as an MCP plugin). The orchestrator
|
|
33
|
+
* has already filtered these — backends should not re-derive. */
|
|
34
|
+
activePlugins: string[];
|
|
35
|
+
/** When set, the path the backend should hand to its MCP loader.
|
|
36
|
+
* Pre-resolved for host-vs-container by the orchestrator. */
|
|
37
|
+
mcpConfigPath?: string;
|
|
38
|
+
/** Extra allowed-tool names from settings + user MCP servers. */
|
|
39
|
+
extraAllowedTools: string[];
|
|
40
|
+
/** When fired, the backend must terminate any in-flight
|
|
41
|
+
* subprocess / connection. */
|
|
42
|
+
abortSignal?: AbortSignal;
|
|
43
|
+
userTimezone?: string;
|
|
44
|
+
/** Whether the orchestrator detected a usable Docker sandbox.
|
|
45
|
+
* Backends that don't sandbox can ignore. */
|
|
46
|
+
useDocker: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface BackendCapabilities {
|
|
50
|
+
/** Can the backend resume a prior conversation by an opaque token?
|
|
51
|
+
* Claude: yes (--resume <id>). OpenAI / Ollama: no — the
|
|
52
|
+
* orchestrator must replay transcript instead. */
|
|
53
|
+
sessionResume: boolean;
|
|
54
|
+
/** Does the backend speak MCP natively? Claude: yes. Others:
|
|
55
|
+
* emulate or skip. Today only Claude consumes activePlugins /
|
|
56
|
+
* mcpConfigPath. */
|
|
57
|
+
mcp: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface LLMBackend {
|
|
61
|
+
readonly id: string;
|
|
62
|
+
readonly capabilities: BackendCapabilities;
|
|
63
|
+
/** Run one user turn. Yields portable AgentEvents. */
|
|
64
|
+
runAgent(input: AgentInput): AsyncIterable<AgentEvent>;
|
|
65
|
+
}
|
package/server/agent/index.ts
CHANGED
|
@@ -1,141 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { mkdir, unlink } from "fs/promises";
|
|
2
|
+
import { writeJsonAtomic } from "../utils/files/json.js";
|
|
3
3
|
import { dirname } from "path";
|
|
4
|
-
import type { Readable, Writable } from "stream";
|
|
5
4
|
import { isDockerAvailable } from "../system/docker.js";
|
|
6
5
|
import { refreshCredentials } from "../system/credentials.js";
|
|
7
6
|
import { loadMcpConfig, loadSettings } from "../system/config.js";
|
|
8
7
|
import type { Role } from "../../src/config/roles.js";
|
|
9
8
|
import { loadAllRoles } from "../workspace/roles.js";
|
|
10
9
|
import { buildSystemPrompt } from "./prompt.js";
|
|
11
|
-
import {
|
|
12
|
-
CONTAINER_WORKSPACE_PATH,
|
|
13
|
-
buildCliArgs,
|
|
14
|
-
buildDockerSpawnArgs,
|
|
15
|
-
buildMcpConfig,
|
|
16
|
-
buildUserMessageLine,
|
|
17
|
-
getActivePlugins,
|
|
18
|
-
prepareUserServers,
|
|
19
|
-
resolveMcpConfigPaths,
|
|
20
|
-
userServerAllowedToolNames,
|
|
21
|
-
} from "./config.js";
|
|
10
|
+
import { CONTAINER_WORKSPACE_PATH, buildMcpConfig, getActivePlugins, prepareUserServers, resolveMcpConfigPaths, userServerAllowedToolNames } from "./config.js";
|
|
22
11
|
import type { Attachment } from "@mulmobridge/protocol";
|
|
23
|
-
import {
|
|
12
|
+
import type { AgentEvent } from "./stream.js";
|
|
24
13
|
import { log } from "../system/logger/index.js";
|
|
25
|
-
import {
|
|
26
|
-
import { env } from "../system/env.js";
|
|
27
|
-
import { resolveSandboxAuth } from "./sandboxMounts.js";
|
|
28
|
-
import { getCachedReferenceDirs, referenceDirMountArgs } from "../workspace/reference-dirs.js";
|
|
29
|
-
|
|
30
|
-
type ClaudeProc = ChildProcessByStdio<Writable, Readable, Readable>;
|
|
31
|
-
|
|
32
|
-
function spawnClaude(useDocker: boolean, workspacePath: string, cliArgs: string[]): ClaudeProc {
|
|
33
|
-
if (!useDocker) {
|
|
34
|
-
return spawn("claude", cliArgs, {
|
|
35
|
-
cwd: workspacePath,
|
|
36
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
const sandboxAuth = resolveSandboxAuth({
|
|
40
|
-
sshAgentForward: env.sandboxSshAgentForward,
|
|
41
|
-
sshAllowedHosts: env.sandboxSshAllowedHosts,
|
|
42
|
-
configMountNames: env.sandboxMountConfigs,
|
|
43
|
-
sshAuthSock: process.env.SSH_AUTH_SOCK,
|
|
44
|
-
});
|
|
45
|
-
const refDirArgs = referenceDirMountArgs(getCachedReferenceDirs());
|
|
46
|
-
const dockerArgs = buildDockerSpawnArgs({
|
|
47
|
-
workspacePath,
|
|
48
|
-
cliArgs,
|
|
49
|
-
uid: process.getuid?.() ?? 1000,
|
|
50
|
-
gid: process.getgid?.() ?? 1000,
|
|
51
|
-
platform: process.platform,
|
|
52
|
-
sandboxAuthArgs: [...sandboxAuth.args, ...refDirArgs],
|
|
53
|
-
sshAgentForward: env.sandboxSshAgentForward,
|
|
54
|
-
});
|
|
55
|
-
return spawn("docker", dockerArgs, { stdio: ["pipe", "pipe", "pipe"] });
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Track MCP tool usage to detect silent MCP server failures.
|
|
59
|
-
// If ToolSearch was called but no mcp__* tool was ever invoked,
|
|
60
|
-
// the MCP server likely crashed on startup (e.g. module resolution
|
|
61
|
-
// failure inside Docker). See #430.
|
|
62
|
-
function createMcpTracker() {
|
|
63
|
-
let toolSearchCalled = false;
|
|
64
|
-
let mcpToolCalled = false;
|
|
65
|
-
return {
|
|
66
|
-
track(event: AgentEvent) {
|
|
67
|
-
if (event.type !== EVENT_TYPES.toolCall) return;
|
|
68
|
-
if (event.toolName === "ToolSearch") toolSearchCalled = true;
|
|
69
|
-
if (event.toolName.startsWith("mcp__")) mcpToolCalled = true;
|
|
70
|
-
},
|
|
71
|
-
logIfSuspicious() {
|
|
72
|
-
if (toolSearchCalled && !mcpToolCalled) {
|
|
73
|
-
log.warn(
|
|
74
|
-
"agent",
|
|
75
|
-
"ToolSearch was used but no MCP tool was called — the MCP server may have crashed. " +
|
|
76
|
-
"Check Docker volume mounts and package.json exports. " +
|
|
77
|
-
"Run: npx tsx --test test/agent/test_mcp_docker_smoke.ts",
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function* readAgentEvents(proc: ClaudeProc): AsyncGenerator<AgentEvent> {
|
|
85
|
-
let stderrOutput = "";
|
|
86
|
-
let stderrBuffer = "";
|
|
87
|
-
proc.stderr.on("data", (chunk: Buffer) => {
|
|
88
|
-
const text = chunk.toString();
|
|
89
|
-
stderrOutput += text;
|
|
90
|
-
stderrBuffer += text;
|
|
91
|
-
const lines = stderrBuffer.split("\n");
|
|
92
|
-
stderrBuffer = lines.pop() ?? "";
|
|
93
|
-
for (const line of lines) {
|
|
94
|
-
if (line.trim()) log.error("agent-stderr", line);
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// Stateful parser tracks whether text was already streamed via
|
|
99
|
-
// assistant content blocks so the final `result` event's duplicate
|
|
100
|
-
// text is suppressed. See createStreamParser() in stream.ts.
|
|
101
|
-
const parser = createStreamParser();
|
|
102
|
-
|
|
103
|
-
const mcpTracker = createMcpTracker();
|
|
104
|
-
|
|
105
|
-
let buffer = "";
|
|
106
|
-
for await (const chunk of proc.stdout) {
|
|
107
|
-
buffer += (chunk as Buffer).toString();
|
|
108
|
-
const lines = buffer.split("\n");
|
|
109
|
-
buffer = lines.pop() ?? "";
|
|
110
|
-
|
|
111
|
-
for (const line of lines) {
|
|
112
|
-
if (!line.trim()) continue;
|
|
113
|
-
let event: RawStreamEvent;
|
|
114
|
-
try {
|
|
115
|
-
event = JSON.parse(line);
|
|
116
|
-
} catch {
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
for (const agentEvent of parser.parse(event)) {
|
|
120
|
-
mcpTracker.track(agentEvent);
|
|
121
|
-
yield agentEvent;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const exitCode = await new Promise<number>((resolve) => proc.on("close", resolve));
|
|
127
|
-
|
|
128
|
-
if (stderrBuffer.trim()) log.error("agent-stderr", stderrBuffer);
|
|
129
|
-
log.info("agent", "claude exited", { exitCode });
|
|
130
|
-
mcpTracker.logIfSuspicious();
|
|
131
|
-
|
|
132
|
-
if (exitCode !== 0) {
|
|
133
|
-
yield {
|
|
134
|
-
type: EVENT_TYPES.error,
|
|
135
|
-
message: stderrOutput || `claude exited with code ${exitCode}`,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
}
|
|
14
|
+
import { getActiveBackend } from "./backend/index.js";
|
|
139
15
|
|
|
140
16
|
export interface RunAgentOptions {
|
|
141
17
|
message: string;
|
|
@@ -157,6 +33,7 @@ export async function* runAgent(
|
|
|
157
33
|
claudeSessionId?: string,
|
|
158
34
|
abortSignal?: AbortSignal,
|
|
159
35
|
attachments?: Attachment[],
|
|
36
|
+
userTimezone?: string,
|
|
160
37
|
): AsyncGenerator<AgentEvent> {
|
|
161
38
|
const activePlugins = getActivePlugins(role);
|
|
162
39
|
const useDocker = await isDockerAvailable();
|
|
@@ -180,6 +57,7 @@ export async function* runAgent(
|
|
|
180
57
|
role,
|
|
181
58
|
workspacePath: useDocker ? CONTAINER_WORKSPACE_PATH : workspacePath,
|
|
182
59
|
useDocker,
|
|
60
|
+
userTimezone,
|
|
183
61
|
});
|
|
184
62
|
|
|
185
63
|
// In debug mode (--debug), dump the full system prompt on the first
|
|
@@ -202,11 +80,14 @@ export async function* runAgent(
|
|
|
202
80
|
chatSessionId: sessionId,
|
|
203
81
|
port,
|
|
204
82
|
activePlugins,
|
|
205
|
-
roleIds: loadAllRoles().map((
|
|
83
|
+
roleIds: loadAllRoles().map((loadedRole) => loadedRole.id),
|
|
206
84
|
useDocker,
|
|
207
85
|
userServers,
|
|
208
86
|
});
|
|
209
|
-
|
|
87
|
+
// Write atomically so a partially-written file can't be picked
|
|
88
|
+
// up by a concurrent claude spawn (they share the --mcp-config
|
|
89
|
+
// path under the session dir).
|
|
90
|
+
await writeJsonAtomic(mcpPaths.hostPath, mcpConfig);
|
|
210
91
|
}
|
|
211
92
|
|
|
212
93
|
// Fresh read on every invocation so the Settings UI can change
|
|
@@ -214,47 +95,38 @@ export async function* runAgent(
|
|
|
214
95
|
const settings = loadSettings();
|
|
215
96
|
const userServerAllowedTools = userServerAllowedToolNames(userServers, useDocker);
|
|
216
97
|
|
|
217
|
-
const cliArgs = buildCliArgs({
|
|
218
|
-
systemPrompt: fullSystemPrompt,
|
|
219
|
-
activePlugins,
|
|
220
|
-
claudeSessionId,
|
|
221
|
-
mcpConfigPath: hasMcp ? mcpPaths.argPath : undefined,
|
|
222
|
-
extraAllowedTools: [...settings.extraAllowedTools, ...userServerAllowedTools],
|
|
223
|
-
});
|
|
224
|
-
|
|
225
98
|
// Don't persist raw sessionId into log sinks (esp. the retained
|
|
226
99
|
// file sink). A boolean presence flag is enough for operational
|
|
227
100
|
// debugging and avoids writing identifiers that route back to a
|
|
228
101
|
// specific session into long-lived log files.
|
|
229
|
-
|
|
102
|
+
const backend = getActiveBackend();
|
|
103
|
+
log.info("agent", "spawning agent", {
|
|
104
|
+
backend: backend.id,
|
|
230
105
|
roleId: role.id,
|
|
231
106
|
useDocker,
|
|
232
107
|
hasMcp,
|
|
233
108
|
resumed: Boolean(claudeSessionId),
|
|
234
109
|
hasSessionId: Boolean(sessionId),
|
|
235
110
|
});
|
|
236
|
-
const proc = spawnClaude(useDocker, workspacePath, cliArgs);
|
|
237
|
-
|
|
238
|
-
// stream-json input mode: stream the user turn as a single JSON
|
|
239
|
-
// line to stdin, then close the pipe so the CLI knows no further
|
|
240
|
-
// turns are coming. Writing before attaching the abort handler
|
|
241
|
-
// is fine — if the write fails because the process already died
|
|
242
|
-
// for other reasons, the `readAgentEvents` loop below surfaces it.
|
|
243
|
-
const messageLine = await buildUserMessageLine(message, attachments);
|
|
244
|
-
proc.stdin.write(messageLine);
|
|
245
|
-
proc.stdin.end();
|
|
246
|
-
|
|
247
|
-
// If an abort signal is provided, kill the process when it fires.
|
|
248
|
-
const onAbort = () => {
|
|
249
|
-
if (!proc.killed) proc.kill();
|
|
250
|
-
};
|
|
251
|
-
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
252
111
|
|
|
253
112
|
try {
|
|
254
|
-
yield*
|
|
113
|
+
yield* backend.runAgent({
|
|
114
|
+
systemPrompt: fullSystemPrompt,
|
|
115
|
+
message,
|
|
116
|
+
role,
|
|
117
|
+
workspacePath,
|
|
118
|
+
sessionId,
|
|
119
|
+
port,
|
|
120
|
+
sessionToken: claudeSessionId,
|
|
121
|
+
attachments,
|
|
122
|
+
activePlugins,
|
|
123
|
+
mcpConfigPath: hasMcp ? mcpPaths.argPath : undefined,
|
|
124
|
+
extraAllowedTools: [...settings.extraAllowedTools, ...userServerAllowedTools],
|
|
125
|
+
abortSignal,
|
|
126
|
+
userTimezone,
|
|
127
|
+
useDocker,
|
|
128
|
+
});
|
|
255
129
|
} finally {
|
|
256
|
-
abortSignal?.removeEventListener("abort", onAbort);
|
|
257
|
-
if (!proc.killed) proc.kill();
|
|
258
130
|
if (hasMcp) unlink(mcpPaths.hostPath).catch(() => {});
|
|
259
131
|
}
|
|
260
132
|
}
|