@aion0/forge 0.5.24 → 0.5.26
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/.forge/memory/graph.json +1 -0
- package/.forge/memory/knowledge.json +18 -0
- package/.forge/memory/meta.json +7 -0
- package/.forge/worktrees/pipeline-0a33c50d/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-0a33c50d/README.md +136 -0
- package/.forge/worktrees/pipeline-0a33c50d/RELEASE_NOTES.md +11 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/terminal-bell/route.ts +35 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/workspace/[id]/stream/route.ts +28 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/api/workspace/route.ts +100 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/mobile/page.tsx +9 -0
- package/.forge/worktrees/pipeline-0a33c50d/app/page.tsx +21 -0
- package/.forge/worktrees/pipeline-0a33c50d/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-0a33c50d/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-0a33c50d/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/WebTerminal.tsx +1683 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-0a33c50d/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-0a33c50d/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-0a33c50d/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-0a33c50d/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-0a33c50d/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-0a33c50d/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-0a33c50d/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-0a33c50d/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-0a33c50d/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-0a33c50d/install.sh +29 -0
- package/.forge/worktrees/pipeline-0a33c50d/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/claude-process.ts +352 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/forge-mcp-server.ts +717 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/pipeline.ts +1438 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/task-manager.ts +949 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/terminal-standalone.ts +421 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/index.ts +82 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/orchestrator.ts +3400 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/workspace-standalone.ts +911 -0
- package/.forge/worktrees/pipeline-0a33c50d/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-0a33c50d/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-0a33c50d/package.json +74 -0
- package/.forge/worktrees/pipeline-0a33c50d/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-0a33c50d/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-0a33c50d/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-0a33c50d/publish.sh +133 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-0a33c50d/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-0a33c50d/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-0a33c50d/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-0a33c50d/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-0a33c50d/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-0a33c50d/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-0a33c50d/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-0a33c50d/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-0a33c50d/start.sh +31 -0
- package/.forge/worktrees/pipeline-0a33c50d/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-0a33c50d/tsconfig.json +42 -0
- package/.forge/worktrees/pipeline-2ba01c10/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-2ba01c10/README.md +136 -0
- package/.forge/worktrees/pipeline-2ba01c10/RELEASE_NOTES.md +11 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/terminal-bell/route.ts +35 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/workspace/[id]/stream/route.ts +28 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/api/workspace/route.ts +100 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/mobile/page.tsx +9 -0
- package/.forge/worktrees/pipeline-2ba01c10/app/page.tsx +21 -0
- package/.forge/worktrees/pipeline-2ba01c10/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-2ba01c10/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-2ba01c10/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/WebTerminal.tsx +1683 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-2ba01c10/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-2ba01c10/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-2ba01c10/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-2ba01c10/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-2ba01c10/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-2ba01c10/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-2ba01c10/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-2ba01c10/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-2ba01c10/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-2ba01c10/install.sh +29 -0
- package/.forge/worktrees/pipeline-2ba01c10/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/claude-process.ts +352 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/forge-mcp-server.ts +717 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/pipeline.ts +1438 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/task-manager.ts +949 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/terminal-standalone.ts +421 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/index.ts +82 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/orchestrator.ts +3400 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-2ba01c10/lib/workspace-standalone.ts +911 -0
- package/.forge/worktrees/pipeline-2ba01c10/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-2ba01c10/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-2ba01c10/package.json +74 -0
- package/.forge/worktrees/pipeline-2ba01c10/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-2ba01c10/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-2ba01c10/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-2ba01c10/publish.sh +133 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-2ba01c10/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-2ba01c10/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-2ba01c10/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-2ba01c10/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-2ba01c10/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-2ba01c10/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-2ba01c10/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-2ba01c10/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-2ba01c10/start.sh +31 -0
- package/.forge/worktrees/pipeline-2ba01c10/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-2ba01c10/tsconfig.json +42 -0
- package/.forge/worktrees/pipeline-3156a8b3/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-3156a8b3/README.md +136 -0
- package/.forge/worktrees/pipeline-3156a8b3/RELEASE_NOTES.md +11 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/terminal-bell/route.ts +35 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/workspace/[id]/stream/route.ts +28 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/api/workspace/route.ts +100 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/mobile/page.tsx +9 -0
- package/.forge/worktrees/pipeline-3156a8b3/app/page.tsx +21 -0
- package/.forge/worktrees/pipeline-3156a8b3/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-3156a8b3/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-3156a8b3/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/WebTerminal.tsx +1683 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-3156a8b3/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-3156a8b3/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-3156a8b3/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-3156a8b3/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-3156a8b3/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-3156a8b3/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-3156a8b3/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-3156a8b3/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-3156a8b3/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-3156a8b3/install.sh +29 -0
- package/.forge/worktrees/pipeline-3156a8b3/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/claude-process.ts +351 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/forge-mcp-server.ts +717 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/pipeline.ts +1441 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/task-manager.ts +949 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/terminal-standalone.ts +421 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/index.ts +82 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/orchestrator.ts +3400 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-3156a8b3/lib/workspace-standalone.ts +911 -0
- package/.forge/worktrees/pipeline-3156a8b3/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-3156a8b3/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-3156a8b3/package.json +74 -0
- package/.forge/worktrees/pipeline-3156a8b3/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-3156a8b3/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-3156a8b3/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-3156a8b3/publish.sh +133 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-3156a8b3/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-3156a8b3/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-3156a8b3/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-3156a8b3/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-3156a8b3/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-3156a8b3/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-3156a8b3/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-3156a8b3/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-3156a8b3/start.sh +31 -0
- package/.forge/worktrees/pipeline-3156a8b3/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-3156a8b3/tsconfig.json +42 -0
- package/.forge/worktrees/pipeline-316c6574/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-316c6574/README.md +136 -0
- package/.forge/worktrees/pipeline-316c6574/RELEASE_NOTES.md +11 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/terminal-bell/route.ts +35 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/workspace/[id]/stream/route.ts +28 -0
- package/.forge/worktrees/pipeline-316c6574/app/api/workspace/route.ts +100 -0
- package/.forge/worktrees/pipeline-316c6574/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-316c6574/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-316c6574/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-316c6574/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-316c6574/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-316c6574/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-316c6574/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-316c6574/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-316c6574/app/mobile/page.tsx +9 -0
- package/.forge/worktrees/pipeline-316c6574/app/page.tsx +21 -0
- package/.forge/worktrees/pipeline-316c6574/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-316c6574/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-316c6574/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-316c6574/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-316c6574/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-316c6574/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-316c6574/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-316c6574/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-316c6574/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-316c6574/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-316c6574/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-316c6574/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-316c6574/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-316c6574/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-316c6574/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-316c6574/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-316c6574/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-316c6574/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-316c6574/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-316c6574/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-316c6574/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-316c6574/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-316c6574/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-316c6574/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-316c6574/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-316c6574/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-316c6574/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-316c6574/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-316c6574/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-316c6574/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-316c6574/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-316c6574/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-316c6574/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-316c6574/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-316c6574/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-316c6574/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-316c6574/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-316c6574/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-316c6574/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-316c6574/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-316c6574/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-316c6574/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-316c6574/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-316c6574/components/WebTerminal.tsx +1683 -0
- package/.forge/worktrees/pipeline-316c6574/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-316c6574/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-316c6574/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-316c6574/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-316c6574/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-316c6574/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-316c6574/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-316c6574/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-316c6574/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-316c6574/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-316c6574/install.sh +29 -0
- package/.forge/worktrees/pipeline-316c6574/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-316c6574/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-316c6574/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-316c6574/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-316c6574/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-316c6574/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-316c6574/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-316c6574/lib/claude-process.ts +361 -0
- package/.forge/worktrees/pipeline-316c6574/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-316c6574/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-316c6574/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-316c6574/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-316c6574/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-316c6574/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-316c6574/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-316c6574/lib/forge-mcp-server.ts +717 -0
- package/.forge/worktrees/pipeline-316c6574/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-316c6574/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-316c6574/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-316c6574/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-316c6574/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-316c6574/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-316c6574/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-316c6574/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-316c6574/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-316c6574/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-316c6574/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-316c6574/lib/pipeline.ts +1441 -0
- package/.forge/worktrees/pipeline-316c6574/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-316c6574/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-316c6574/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-316c6574/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-316c6574/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-316c6574/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-316c6574/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-316c6574/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-316c6574/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-316c6574/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-316c6574/lib/task-manager.ts +949 -0
- package/.forge/worktrees/pipeline-316c6574/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-316c6574/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-316c6574/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-316c6574/lib/terminal-standalone.ts +421 -0
- package/.forge/worktrees/pipeline-316c6574/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/index.ts +82 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/orchestrator.ts +3400 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-316c6574/lib/workspace-standalone.ts +911 -0
- package/.forge/worktrees/pipeline-316c6574/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-316c6574/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-316c6574/package.json +74 -0
- package/.forge/worktrees/pipeline-316c6574/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-316c6574/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-316c6574/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-316c6574/publish.sh +133 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-316c6574/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-316c6574/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-316c6574/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-316c6574/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-316c6574/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-316c6574/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-316c6574/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-316c6574/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-316c6574/start.sh +31 -0
- package/.forge/worktrees/pipeline-316c6574/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-316c6574/tsconfig.json +42 -0
- package/.forge/worktrees/pipeline-44a94121/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-44a94121/README.md +136 -0
- package/.forge/worktrees/pipeline-44a94121/RELEASE_NOTES.md +11 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/terminal-bell/route.ts +35 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/workspace/[id]/stream/route.ts +28 -0
- package/.forge/worktrees/pipeline-44a94121/app/api/workspace/route.ts +100 -0
- package/.forge/worktrees/pipeline-44a94121/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-44a94121/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-44a94121/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-44a94121/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-44a94121/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-44a94121/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-44a94121/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-44a94121/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-44a94121/app/mobile/page.tsx +9 -0
- package/.forge/worktrees/pipeline-44a94121/app/page.tsx +21 -0
- package/.forge/worktrees/pipeline-44a94121/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-44a94121/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-44a94121/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-44a94121/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-44a94121/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-44a94121/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-44a94121/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-44a94121/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-44a94121/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-44a94121/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-44a94121/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-44a94121/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-44a94121/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-44a94121/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-44a94121/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-44a94121/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-44a94121/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-44a94121/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-44a94121/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-44a94121/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-44a94121/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-44a94121/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-44a94121/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-44a94121/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-44a94121/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-44a94121/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-44a94121/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-44a94121/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-44a94121/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-44a94121/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-44a94121/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-44a94121/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-44a94121/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-44a94121/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-44a94121/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-44a94121/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-44a94121/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-44a94121/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-44a94121/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-44a94121/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-44a94121/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-44a94121/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-44a94121/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-44a94121/components/WebTerminal.tsx +1683 -0
- package/.forge/worktrees/pipeline-44a94121/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-44a94121/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-44a94121/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-44a94121/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-44a94121/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-44a94121/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-44a94121/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-44a94121/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-44a94121/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-44a94121/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-44a94121/install.sh +29 -0
- package/.forge/worktrees/pipeline-44a94121/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-44a94121/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-44a94121/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-44a94121/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-44a94121/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-44a94121/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-44a94121/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-44a94121/lib/claude-process.ts +351 -0
- package/.forge/worktrees/pipeline-44a94121/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-44a94121/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-44a94121/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-44a94121/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-44a94121/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-44a94121/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-44a94121/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-44a94121/lib/forge-mcp-server.ts +717 -0
- package/.forge/worktrees/pipeline-44a94121/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-44a94121/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-44a94121/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-44a94121/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-44a94121/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-44a94121/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-44a94121/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-44a94121/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-44a94121/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-44a94121/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-44a94121/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-44a94121/lib/pipeline.ts +1441 -0
- package/.forge/worktrees/pipeline-44a94121/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-44a94121/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-44a94121/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-44a94121/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-44a94121/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-44a94121/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-44a94121/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-44a94121/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-44a94121/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-44a94121/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-44a94121/lib/task-manager.ts +949 -0
- package/.forge/worktrees/pipeline-44a94121/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-44a94121/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-44a94121/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-44a94121/lib/terminal-standalone.ts +421 -0
- package/.forge/worktrees/pipeline-44a94121/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/index.ts +82 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/orchestrator.ts +3400 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-44a94121/lib/workspace-standalone.ts +911 -0
- package/.forge/worktrees/pipeline-44a94121/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-44a94121/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-44a94121/package.json +74 -0
- package/.forge/worktrees/pipeline-44a94121/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-44a94121/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-44a94121/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-44a94121/publish.sh +133 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-44a94121/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-44a94121/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-44a94121/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-44a94121/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-44a94121/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-44a94121/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-44a94121/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-44a94121/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-44a94121/start.sh +31 -0
- package/.forge/worktrees/pipeline-44a94121/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-44a94121/tsconfig.json +42 -0
- package/.forge/worktrees/pipeline-d1757a50/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-d1757a50/README.md +136 -0
- package/.forge/worktrees/pipeline-d1757a50/RELEASE_NOTES.md +11 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/terminal-bell/route.ts +35 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/workspace/[id]/stream/route.ts +28 -0
- package/.forge/worktrees/pipeline-d1757a50/app/api/workspace/route.ts +100 -0
- package/.forge/worktrees/pipeline-d1757a50/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-d1757a50/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-d1757a50/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-d1757a50/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-d1757a50/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-d1757a50/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-d1757a50/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-d1757a50/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-d1757a50/app/mobile/page.tsx +9 -0
- package/.forge/worktrees/pipeline-d1757a50/app/page.tsx +21 -0
- package/.forge/worktrees/pipeline-d1757a50/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-d1757a50/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-d1757a50/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-d1757a50/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-d1757a50/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-d1757a50/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-d1757a50/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-d1757a50/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-d1757a50/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-d1757a50/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-d1757a50/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-d1757a50/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-d1757a50/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-d1757a50/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-d1757a50/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-d1757a50/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-d1757a50/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-d1757a50/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-d1757a50/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-d1757a50/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-d1757a50/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-d1757a50/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-d1757a50/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-d1757a50/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-d1757a50/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-d1757a50/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-d1757a50/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-d1757a50/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-d1757a50/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-d1757a50/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-d1757a50/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-d1757a50/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-d1757a50/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-d1757a50/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-d1757a50/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-d1757a50/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-d1757a50/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-d1757a50/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-d1757a50/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-d1757a50/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-d1757a50/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-d1757a50/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-d1757a50/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-d1757a50/components/WebTerminal.tsx +1683 -0
- package/.forge/worktrees/pipeline-d1757a50/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-d1757a50/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-d1757a50/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-d1757a50/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-d1757a50/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-d1757a50/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-d1757a50/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-d1757a50/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-d1757a50/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-d1757a50/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-d1757a50/install.sh +29 -0
- package/.forge/worktrees/pipeline-d1757a50/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/claude-process.ts +361 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/forge-mcp-server.ts +717 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/pipeline.ts +1441 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/task-manager.ts +949 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/terminal-standalone.ts +421 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/index.ts +82 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/orchestrator.ts +3400 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-d1757a50/lib/workspace-standalone.ts +911 -0
- package/.forge/worktrees/pipeline-d1757a50/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-d1757a50/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-d1757a50/package.json +74 -0
- package/.forge/worktrees/pipeline-d1757a50/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-d1757a50/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-d1757a50/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-d1757a50/publish.sh +133 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-d1757a50/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-d1757a50/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-d1757a50/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-d1757a50/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-d1757a50/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-d1757a50/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-d1757a50/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-d1757a50/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-d1757a50/start.sh +31 -0
- package/.forge/worktrees/pipeline-d1757a50/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-d1757a50/tsconfig.json +42 -0
- package/.forge/worktrees/pipeline-d59c2fe2/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-d59c2fe2/README.md +136 -0
- package/.forge/worktrees/pipeline-d59c2fe2/RELEASE_NOTES.md +11 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/terminal-bell/route.ts +35 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/workspace/[id]/stream/route.ts +28 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/api/workspace/route.ts +100 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/mobile/page.tsx +9 -0
- package/.forge/worktrees/pipeline-d59c2fe2/app/page.tsx +21 -0
- package/.forge/worktrees/pipeline-d59c2fe2/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-d59c2fe2/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-d59c2fe2/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/WebTerminal.tsx +1683 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-d59c2fe2/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-d59c2fe2/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-d59c2fe2/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-d59c2fe2/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-d59c2fe2/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-d59c2fe2/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-d59c2fe2/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-d59c2fe2/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-d59c2fe2/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-d59c2fe2/install.sh +29 -0
- package/.forge/worktrees/pipeline-d59c2fe2/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/claude-process.ts +361 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/forge-mcp-server.ts +717 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/pipeline.ts +1441 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/task-manager.ts +949 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/terminal-standalone.ts +421 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/index.ts +82 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/orchestrator.ts +3400 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace-standalone.ts +911 -0
- package/.forge/worktrees/pipeline-d59c2fe2/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-d59c2fe2/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-d59c2fe2/package.json +74 -0
- package/.forge/worktrees/pipeline-d59c2fe2/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-d59c2fe2/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-d59c2fe2/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-d59c2fe2/publish.sh +133 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-d59c2fe2/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-d59c2fe2/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-d59c2fe2/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-d59c2fe2/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-d59c2fe2/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-d59c2fe2/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-d59c2fe2/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-d59c2fe2/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-d59c2fe2/start.sh +31 -0
- package/.forge/worktrees/pipeline-d59c2fe2/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-d59c2fe2/tsconfig.json +42 -0
- package/.forge/worktrees/pipeline-d6a6ef23/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-d6a6ef23/README.md +136 -0
- package/.forge/worktrees/pipeline-d6a6ef23/RELEASE_NOTES.md +11 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/terminal-bell/route.ts +35 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/workspace/[id]/stream/route.ts +28 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/api/workspace/route.ts +100 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/mobile/page.tsx +9 -0
- package/.forge/worktrees/pipeline-d6a6ef23/app/page.tsx +21 -0
- package/.forge/worktrees/pipeline-d6a6ef23/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-d6a6ef23/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-d6a6ef23/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/WebTerminal.tsx +1683 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-d6a6ef23/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-d6a6ef23/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-d6a6ef23/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-d6a6ef23/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-d6a6ef23/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-d6a6ef23/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-d6a6ef23/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-d6a6ef23/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-d6a6ef23/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-d6a6ef23/install.sh +29 -0
- package/.forge/worktrees/pipeline-d6a6ef23/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/claude-process.ts +352 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/forge-mcp-server.ts +717 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/pipeline.ts +1438 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/task-manager.ts +949 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/terminal-standalone.ts +421 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/index.ts +82 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/orchestrator.ts +3400 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace-standalone.ts +911 -0
- package/.forge/worktrees/pipeline-d6a6ef23/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-d6a6ef23/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-d6a6ef23/package.json +74 -0
- package/.forge/worktrees/pipeline-d6a6ef23/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-d6a6ef23/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-d6a6ef23/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-d6a6ef23/publish.sh +133 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-d6a6ef23/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-d6a6ef23/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-d6a6ef23/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-d6a6ef23/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-d6a6ef23/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-d6a6ef23/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-d6a6ef23/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-d6a6ef23/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-d6a6ef23/start.sh +31 -0
- package/.forge/worktrees/pipeline-d6a6ef23/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-d6a6ef23/tsconfig.json +42 -0
- package/.forge/worktrees/pipeline-e7f78b7a/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-e7f78b7a/README.md +136 -0
- package/.forge/worktrees/pipeline-e7f78b7a/RELEASE_NOTES.md +11 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/terminal-bell/route.ts +35 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/workspace/[id]/stream/route.ts +28 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/api/workspace/route.ts +100 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/mobile/page.tsx +9 -0
- package/.forge/worktrees/pipeline-e7f78b7a/app/page.tsx +21 -0
- package/.forge/worktrees/pipeline-e7f78b7a/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-e7f78b7a/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-e7f78b7a/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/WebTerminal.tsx +1683 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-e7f78b7a/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-e7f78b7a/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-e7f78b7a/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-e7f78b7a/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-e7f78b7a/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-e7f78b7a/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-e7f78b7a/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-e7f78b7a/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-e7f78b7a/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-e7f78b7a/install.sh +29 -0
- package/.forge/worktrees/pipeline-e7f78b7a/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/claude-process.ts +352 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/forge-mcp-server.ts +717 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/pipeline.ts +1441 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/task-manager.ts +949 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/terminal-standalone.ts +421 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/index.ts +82 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/orchestrator.ts +3400 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace-standalone.ts +911 -0
- package/.forge/worktrees/pipeline-e7f78b7a/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-e7f78b7a/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-e7f78b7a/package.json +74 -0
- package/.forge/worktrees/pipeline-e7f78b7a/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-e7f78b7a/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-e7f78b7a/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-e7f78b7a/publish.sh +133 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-e7f78b7a/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-e7f78b7a/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-e7f78b7a/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-e7f78b7a/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-e7f78b7a/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-e7f78b7a/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-e7f78b7a/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-e7f78b7a/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-e7f78b7a/start.sh +31 -0
- package/.forge/worktrees/pipeline-e7f78b7a/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-e7f78b7a/tsconfig.json +42 -0
- package/.forge/worktrees/pipeline-e97c13c7/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-e97c13c7/README.md +136 -0
- package/.forge/worktrees/pipeline-e97c13c7/RELEASE_NOTES.md +11 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/terminal-bell/route.ts +35 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/workspace/[id]/stream/route.ts +28 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/api/workspace/route.ts +100 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/mobile/page.tsx +9 -0
- package/.forge/worktrees/pipeline-e97c13c7/app/page.tsx +21 -0
- package/.forge/worktrees/pipeline-e97c13c7/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-e97c13c7/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-e97c13c7/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/WebTerminal.tsx +1683 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-e97c13c7/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-e97c13c7/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-e97c13c7/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-e97c13c7/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-e97c13c7/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-e97c13c7/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-e97c13c7/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-e97c13c7/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-e97c13c7/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-e97c13c7/install.sh +29 -0
- package/.forge/worktrees/pipeline-e97c13c7/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/claude-process.ts +352 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/forge-mcp-server.ts +717 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/pipeline.ts +1438 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/task-manager.ts +949 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/terminal-standalone.ts +421 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/index.ts +82 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/orchestrator.ts +3400 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-e97c13c7/lib/workspace-standalone.ts +911 -0
- package/.forge/worktrees/pipeline-e97c13c7/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-e97c13c7/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-e97c13c7/package.json +74 -0
- package/.forge/worktrees/pipeline-e97c13c7/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-e97c13c7/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-e97c13c7/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-e97c13c7/publish.sh +133 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-e97c13c7/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-e97c13c7/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-e97c13c7/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-e97c13c7/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-e97c13c7/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-e97c13c7/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-e97c13c7/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-e97c13c7/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-e97c13c7/start.sh +31 -0
- package/.forge/worktrees/pipeline-e97c13c7/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-e97c13c7/tsconfig.json +42 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/README.md +136 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/RELEASE_NOTES.md +11 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/terminal-bell/route.ts +35 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/workspace/[id]/stream/route.ts +28 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/api/workspace/route.ts +100 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/mobile/page.tsx +9 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/app/page.tsx +21 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/WebTerminal.tsx +1683 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/install.sh +29 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/claude-process.ts +352 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/forge-mcp-server.ts +717 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/pipeline.ts +1441 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/task-manager.ts +949 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/terminal-standalone.ts +421 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/index.ts +82 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/orchestrator.ts +3400 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace-standalone.ts +911 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/package.json +74 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/publish.sh +133 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/start.sh +31 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-ecd7cb0f/tsconfig.json +42 -0
- package/RELEASE_NOTES.md +26 -15
- package/app/api/workspace/[id]/stream/route.ts +3 -0
- package/app/api/workspace/route.ts +20 -41
- package/app/mobile/page.tsx +2 -1
- package/app/page.tsx +2 -1
- package/components/WebTerminal.tsx +27 -1
- package/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/lib/claude-process.ts +14 -5
- package/lib/forge-mcp-server.ts +20 -5
- package/lib/help-docs/05-pipelines.md +44 -2
- package/lib/pipeline.ts +181 -24
- package/lib/project-sessions.ts +1 -1
- package/lib/task-manager.ts +5 -3
- package/lib/terminal-standalone.ts +2 -2
- package/lib/workspace/index.ts +3 -1
- package/lib/workspace/manager.ts +1 -1
- package/lib/workspace/orchestrator.ts +23 -8
- package/lib/workspace-standalone.ts +67 -0
- package/middleware.ts +2 -2
- package/package.json +1 -1
- package/pnpm-workspace.yaml +1 -0
- package/start.sh +1 -0
|
@@ -0,0 +1,3400 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkspaceOrchestrator — manages a group of agents within a workspace.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Create/remove agents
|
|
6
|
+
* - Run agents (auto-select backend, inject upstream context)
|
|
7
|
+
* - Listen to agent events → trigger downstream agents
|
|
8
|
+
* - Approval gating
|
|
9
|
+
* - Parallel execution (independent agents run concurrently)
|
|
10
|
+
* - Error recovery (restart from lastCheckpoint)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { EventEmitter } from 'node:events';
|
|
14
|
+
import { readFileSync, existsSync, writeFileSync, unlinkSync, mkdirSync, statSync, readdirSync } from 'node:fs';
|
|
15
|
+
import { execSync } from 'node:child_process';
|
|
16
|
+
import { resolve, join } from 'node:path';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
import type {
|
|
19
|
+
WorkspaceAgentConfig,
|
|
20
|
+
AgentState,
|
|
21
|
+
SmithStatus,
|
|
22
|
+
TaskStatus,
|
|
23
|
+
WorkerEvent,
|
|
24
|
+
BusMessage,
|
|
25
|
+
Artifact,
|
|
26
|
+
WorkspaceState,
|
|
27
|
+
DaemonWakeReason,
|
|
28
|
+
} from './types';
|
|
29
|
+
import { AgentWorker } from './agent-worker';
|
|
30
|
+
import { AgentBus } from './agent-bus';
|
|
31
|
+
import { WatchManager } from './watch-manager';
|
|
32
|
+
// ApiBackend loaded dynamically — its dependency chain uses @/src path aliases
|
|
33
|
+
// that only work in Next.js context, not in standalone tsx process
|
|
34
|
+
// import { ApiBackend } from './backends/api-backend';
|
|
35
|
+
import { CliBackend } from './backends/cli-backend';
|
|
36
|
+
import { appendAgentLog, saveWorkspace, saveWorkspaceSync, startAutoSave, stopAutoSave } from './persistence';
|
|
37
|
+
import { hasForgeSkills, installForgeSkills } from './skill-installer';
|
|
38
|
+
import {
|
|
39
|
+
loadMemory, saveMemory, createMemory, formatMemoryForPrompt,
|
|
40
|
+
addObservation, addSessionSummary, parseStepToObservations, buildSessionSummary,
|
|
41
|
+
} from './smith-memory';
|
|
42
|
+
import { getFixedSession } from '../project-sessions';
|
|
43
|
+
|
|
44
|
+
// ─── Workspace Topology Cache ────────────────────────────
|
|
45
|
+
|
|
46
|
+
export interface TopoAgent {
|
|
47
|
+
id: string;
|
|
48
|
+
label: string;
|
|
49
|
+
icon: string;
|
|
50
|
+
role: string; // full role text
|
|
51
|
+
roleSummary: string; // first line, ≤150 chars
|
|
52
|
+
primary: boolean;
|
|
53
|
+
dependsOn: string[]; // agent labels (not IDs)
|
|
54
|
+
dependsOnIds: string[];
|
|
55
|
+
workDir: string;
|
|
56
|
+
outputs: string[];
|
|
57
|
+
steps: string[]; // step labels
|
|
58
|
+
smithStatus: string;
|
|
59
|
+
taskStatus: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface WorkspaceTopo {
|
|
63
|
+
agents: TopoAgent[];
|
|
64
|
+
flow: string; // "Lead → Engineer → QA → Reviewer"
|
|
65
|
+
updatedAt: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── Orchestrator Events ─────────────────────────────────
|
|
69
|
+
|
|
70
|
+
export type OrchestratorEvent =
|
|
71
|
+
| WorkerEvent
|
|
72
|
+
| { type: 'bus_message'; message: BusMessage }
|
|
73
|
+
| { type: 'approval_required'; agentId: string; upstreamId: string }
|
|
74
|
+
| { type: 'user_input_request'; agentId: string; fromAgent: string; question: string }
|
|
75
|
+
| { type: 'workspace_status'; running: number; done: number; total: number }
|
|
76
|
+
| { type: 'workspace_complete' }
|
|
77
|
+
| { type: 'watch_alert'; agentId: string; changes: any[]; summary: string; timestamp: number };
|
|
78
|
+
|
|
79
|
+
// ─── Orchestrator class ──────────────────────────────────
|
|
80
|
+
|
|
81
|
+
export class WorkspaceOrchestrator extends EventEmitter {
|
|
82
|
+
readonly workspaceId: string;
|
|
83
|
+
readonly projectPath: string;
|
|
84
|
+
readonly projectName: string;
|
|
85
|
+
|
|
86
|
+
private agents = new Map<string, { config: WorkspaceAgentConfig; worker: AgentWorker | null; state: AgentState }>();
|
|
87
|
+
private bus: AgentBus;
|
|
88
|
+
private watchManager: WatchManager;
|
|
89
|
+
private sessionMonitor: import('./session-monitor').SessionFileMonitor | null = null;
|
|
90
|
+
private approvalQueue = new Set<string>();
|
|
91
|
+
private daemonActive = false;
|
|
92
|
+
private createdAt = Date.now();
|
|
93
|
+
private healthCheckTimer: NodeJS.Timeout | null = null;
|
|
94
|
+
private settingsValidCache = new Map<string, number>(); // filePath → mtime (validated ok)
|
|
95
|
+
private agentRunningMsg = new Map<string, string>(); // agentId → messageId currently being processed
|
|
96
|
+
private reconcileTick = 0; // counts health check ticks for 60s reconcile
|
|
97
|
+
private _topoCache: WorkspaceTopo | null = null; // cached workspace topology
|
|
98
|
+
private roleInjectState = new Map<string, { lastInjectAt: number; msgsSinceInject: number }>(); // per-agent role reminder tracking
|
|
99
|
+
|
|
100
|
+
/** Emit a log event (auto-persisted via constructor listener) */
|
|
101
|
+
emitLog(agentId: string, entry: any): void {
|
|
102
|
+
this.emit('event', { type: 'log', agentId, entry } as any);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
constructor(workspaceId: string, projectPath: string, projectName: string) {
|
|
106
|
+
super();
|
|
107
|
+
this.workspaceId = workspaceId;
|
|
108
|
+
this.projectPath = projectPath;
|
|
109
|
+
this.projectName = projectName;
|
|
110
|
+
this.bus = new AgentBus();
|
|
111
|
+
this.watchManager = new WatchManager(workspaceId, projectPath, () => this.agents as any);
|
|
112
|
+
|
|
113
|
+
// Auto-persist all log events to disk (so LogPanel can read them)
|
|
114
|
+
this.on('event', (event: any) => {
|
|
115
|
+
if (event.type === 'log' && event.agentId && event.entry) {
|
|
116
|
+
appendAgentLog(this.workspaceId, event.agentId, event.entry).catch(() => {});
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// Handle watch events
|
|
120
|
+
this.watchManager.on('watch_alert', (event) => {
|
|
121
|
+
this.emit('event', event);
|
|
122
|
+
// Push alert to agent history so Log panel shows it
|
|
123
|
+
const alertEntry = this.agents.get(event.agentId);
|
|
124
|
+
if (alertEntry && event.entry) {
|
|
125
|
+
alertEntry.state.history.push(event.entry);
|
|
126
|
+
this.emit('event', { type: 'log', agentId: event.agentId, entry: event.entry } as any);
|
|
127
|
+
}
|
|
128
|
+
this.handleWatchAlert(event.agentId, event.summary);
|
|
129
|
+
});
|
|
130
|
+
// Note: watch_heartbeat (no changes) only logs to console, not to agent history/logs.jsonl
|
|
131
|
+
|
|
132
|
+
// Forward bus messages as orchestrator events (after dedup, skip ACKs)
|
|
133
|
+
this.bus.on('message', (msg: BusMessage) => {
|
|
134
|
+
if (msg.type === 'ack') return; // ACKs are internal, don't emit to UI
|
|
135
|
+
if (msg.to === '_system') {
|
|
136
|
+
this.emit('event', { type: 'bus_message', message: msg } satisfies OrchestratorEvent);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
this.handleBusMessage(msg);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Start auto-save (every 10 seconds)
|
|
143
|
+
startAutoSave(workspaceId, () => this.getFullState());
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ─── Agent Management ──────────────────────────────────
|
|
147
|
+
|
|
148
|
+
/** Check if agent outputs or workDir conflict with existing agents */
|
|
149
|
+
private validateOutputs(config: WorkspaceAgentConfig, excludeId?: string): string | null {
|
|
150
|
+
if (config.type === 'input') return null;
|
|
151
|
+
|
|
152
|
+
const normalize = (p: string) => p.replace(/^\.?\//, '').replace(/\/$/, '') || '.';
|
|
153
|
+
|
|
154
|
+
// Validate workDir is within project (no ../ escape)
|
|
155
|
+
if (config.workDir) {
|
|
156
|
+
const relativeDir = config.workDir.replace(/^\.?\//, '');
|
|
157
|
+
if (relativeDir.includes('..')) {
|
|
158
|
+
return `Work directory "${config.workDir}" contains "..". Must be a subdirectory of the project.`;
|
|
159
|
+
}
|
|
160
|
+
const projectRoot = this.projectPath.endsWith('/') ? this.projectPath : this.projectPath + '/';
|
|
161
|
+
const resolved = resolve(this.projectPath, relativeDir);
|
|
162
|
+
if (resolved !== this.projectPath && !resolved.startsWith(projectRoot)) {
|
|
163
|
+
return `Work directory "${config.workDir}" is outside the project. Must be a subdirectory.`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Every non-input smith must have a unique workDir
|
|
168
|
+
const newDir = normalize(config.workDir || '.');
|
|
169
|
+
|
|
170
|
+
for (const [id, entry] of this.agents) {
|
|
171
|
+
if (id === excludeId || entry.config.type === 'input') continue;
|
|
172
|
+
|
|
173
|
+
const existingDir = normalize(entry.config.workDir || '.');
|
|
174
|
+
|
|
175
|
+
// Same workDir → conflict
|
|
176
|
+
if (newDir === existingDir) {
|
|
177
|
+
return `Work directory conflict: "${config.label}" and "${entry.config.label}" both use "${newDir === '.' ? 'project root' : newDir}/". Each smith must have a unique work directory.`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// One is parent of the other → conflict (e.g., "src" and "src/components")
|
|
181
|
+
if (newDir.startsWith(existingDir + '/') || existingDir.startsWith(newDir + '/')) {
|
|
182
|
+
return `Work directory conflict: "${config.label}" (${newDir}/) overlaps with "${entry.config.label}" (${existingDir}/). Nested directories not allowed.`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Output path overlap is allowed — multiple agents can write to the same
|
|
186
|
+
// output directory (e.g., Engineer and UI Designer both output to src/).
|
|
187
|
+
// Work directory uniqueness already prevents file-level conflicts.
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Check for non-blocking output overlaps — returns a warning message if any */
|
|
193
|
+
private getOutputWarnings(config: WorkspaceAgentConfig, excludeId?: string): string | null {
|
|
194
|
+
if (config.type === 'input' || !config.outputs?.length) return null;
|
|
195
|
+
const normalize = (p: string) => p.replace(/^\.?\//, '').replace(/\/$/, '') || '.';
|
|
196
|
+
const overlaps: string[] = [];
|
|
197
|
+
for (const [id, entry] of this.agents) {
|
|
198
|
+
if (id === excludeId || entry.config.type === 'input') continue;
|
|
199
|
+
for (const out of config.outputs) {
|
|
200
|
+
for (const existing of entry.config.outputs || []) {
|
|
201
|
+
if (normalize(out) === normalize(existing)) {
|
|
202
|
+
overlaps.push(`${entry.config.label} also outputs to "${out}"`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return overlaps.length ? `Output overlap (non-blocking): ${overlaps.join('; ')}` : null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Detect if adding dependsOn edges would create a cycle in the DAG */
|
|
211
|
+
private detectCycle(agentId: string, dependsOn: string[]): string | null {
|
|
212
|
+
// Build adjacency: agent → agents it depends on
|
|
213
|
+
const deps = new Map<string, string[]>();
|
|
214
|
+
for (const [id, entry] of this.agents) {
|
|
215
|
+
if (id !== agentId) deps.set(id, [...entry.config.dependsOn]);
|
|
216
|
+
}
|
|
217
|
+
deps.set(agentId, [...dependsOn]);
|
|
218
|
+
|
|
219
|
+
// DFS cycle detection
|
|
220
|
+
const visited = new Set<string>();
|
|
221
|
+
const inStack = new Set<string>();
|
|
222
|
+
|
|
223
|
+
const dfs = (node: string): string | null => {
|
|
224
|
+
if (inStack.has(node)) return node; // cycle found
|
|
225
|
+
if (visited.has(node)) return null;
|
|
226
|
+
visited.add(node);
|
|
227
|
+
inStack.add(node);
|
|
228
|
+
for (const dep of deps.get(node) || []) {
|
|
229
|
+
const cycle = dfs(dep);
|
|
230
|
+
if (cycle) return cycle;
|
|
231
|
+
}
|
|
232
|
+
inStack.delete(node);
|
|
233
|
+
return null;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
for (const id of deps.keys()) {
|
|
237
|
+
const cycle = dfs(id);
|
|
238
|
+
if (cycle) {
|
|
239
|
+
const cycleName = this.agents.get(cycle)?.config.label || cycle;
|
|
240
|
+
return `Circular dependency detected involving "${cycleName}". Dependencies must form a DAG (no cycles).`;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Check if agentA is upstream of agentB (A is in B's dependency chain) */
|
|
247
|
+
isUpstream(agentA: string, agentB: string): boolean {
|
|
248
|
+
const visited = new Set<string>();
|
|
249
|
+
const check = (current: string): boolean => {
|
|
250
|
+
if (current === agentA) return true;
|
|
251
|
+
if (visited.has(current)) return false;
|
|
252
|
+
visited.add(current);
|
|
253
|
+
const entry = this.agents.get(current);
|
|
254
|
+
if (!entry) return false;
|
|
255
|
+
return entry.config.dependsOn.some(dep => check(dep));
|
|
256
|
+
};
|
|
257
|
+
return check(agentB);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Get the primary agent for this workspace (if any) */
|
|
261
|
+
getPrimaryAgent(): { config: WorkspaceAgentConfig; state: AgentState } | null {
|
|
262
|
+
for (const [, entry] of this.agents) {
|
|
263
|
+
if (entry.config.primary) return entry;
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
addAgent(config: WorkspaceAgentConfig): void {
|
|
269
|
+
const conflict = this.validateOutputs(config);
|
|
270
|
+
if (conflict) throw new Error(conflict);
|
|
271
|
+
const warning = this.getOutputWarnings(config);
|
|
272
|
+
if (warning) console.warn(`[workspace] ${warning}`);
|
|
273
|
+
|
|
274
|
+
// Check DAG cycle before adding
|
|
275
|
+
const cycleErr = this.detectCycle(config.id, config.dependsOn);
|
|
276
|
+
if (cycleErr) throw new Error(cycleErr);
|
|
277
|
+
|
|
278
|
+
// Primary agent validation
|
|
279
|
+
this.validatePrimaryRules(config);
|
|
280
|
+
|
|
281
|
+
const state: AgentState = {
|
|
282
|
+
smithStatus: 'down',
|
|
283
|
+
taskStatus: 'idle',
|
|
284
|
+
history: [],
|
|
285
|
+
artifacts: [],
|
|
286
|
+
};
|
|
287
|
+
// Primary agent: force terminal-only, root dir
|
|
288
|
+
if (config.primary) {
|
|
289
|
+
config.persistentSession = true;
|
|
290
|
+
config.workDir = './';
|
|
291
|
+
}
|
|
292
|
+
this.agents.set(config.id, { config, worker: null, state });
|
|
293
|
+
// If daemon active, start persistent session + worker
|
|
294
|
+
if (this.daemonActive && config.type !== 'input' && config.persistentSession) {
|
|
295
|
+
this.enterDaemonListening(config.id);
|
|
296
|
+
const entry = this.agents.get(config.id)!;
|
|
297
|
+
entry.state.smithStatus = 'active';
|
|
298
|
+
this.ensurePersistentSession(config.id, config).then(() => {
|
|
299
|
+
this.startMessageLoop(config.id);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
this.saveNow();
|
|
303
|
+
this.emitAgentsChanged();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
removeAgent(id: string): void {
|
|
307
|
+
const entry = this.agents.get(id);
|
|
308
|
+
if (entry?.config.primary) throw new Error('Cannot remove the primary agent');
|
|
309
|
+
if (entry?.worker) {
|
|
310
|
+
entry.worker.stop();
|
|
311
|
+
}
|
|
312
|
+
this.agents.delete(id);
|
|
313
|
+
this.approvalQueue.delete(id);
|
|
314
|
+
|
|
315
|
+
// Clean up dangling dependsOn references in other agents
|
|
316
|
+
for (const [, other] of this.agents) {
|
|
317
|
+
const idx = other.config.dependsOn.indexOf(id);
|
|
318
|
+
if (idx !== -1) {
|
|
319
|
+
other.config.dependsOn.splice(idx, 1);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
this.saveNow();
|
|
324
|
+
this.emitAgentsChanged();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Validate primary agent rules */
|
|
328
|
+
private validatePrimaryRules(config: WorkspaceAgentConfig, excludeId?: string): void {
|
|
329
|
+
if (config.primary) {
|
|
330
|
+
// Only one primary allowed
|
|
331
|
+
for (const [id, entry] of this.agents) {
|
|
332
|
+
if (id !== excludeId && entry.config.primary) {
|
|
333
|
+
throw new Error(`Only one primary agent allowed. "${entry.config.label}" is already primary.`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// Non-primary agents cannot use root directory if a primary exists
|
|
338
|
+
if (!config.primary && config.type !== 'input') {
|
|
339
|
+
const workDir = config.workDir?.replace(/\/+$/, '') || '';
|
|
340
|
+
if (!workDir || workDir === '.' || workDir === './') {
|
|
341
|
+
const primary = this.getPrimaryAgent();
|
|
342
|
+
if (primary && primary.config.id !== excludeId) {
|
|
343
|
+
throw new Error(`Root directory is reserved for primary agent "${primary.config.label}". Choose a subdirectory.`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
updateAgentConfig(id: string, config: WorkspaceAgentConfig): void {
|
|
350
|
+
const entry = this.agents.get(id);
|
|
351
|
+
if (!entry) return;
|
|
352
|
+
// Validate agentId exists — fallback to default if deleted from Settings
|
|
353
|
+
if (config.agentId) {
|
|
354
|
+
try {
|
|
355
|
+
const { listAgents, getDefaultAgentId } = require('../agents/index');
|
|
356
|
+
const validAgents = new Set((listAgents() as any[]).map((a: any) => a.id));
|
|
357
|
+
if (!validAgents.has(config.agentId)) {
|
|
358
|
+
const fallback = getDefaultAgentId() || 'claude';
|
|
359
|
+
console.log(`[workspace] ${config.label}: agent "${config.agentId}" not found, falling back to "${fallback}"`);
|
|
360
|
+
config.agentId = fallback;
|
|
361
|
+
}
|
|
362
|
+
} catch {}
|
|
363
|
+
}
|
|
364
|
+
const conflict = this.validateOutputs(config, id);
|
|
365
|
+
if (conflict) throw new Error(conflict);
|
|
366
|
+
const warning = this.getOutputWarnings(config, id);
|
|
367
|
+
if (warning) console.warn(`[workspace] ${warning}`);
|
|
368
|
+
const cycleErr = this.detectCycle(id, config.dependsOn);
|
|
369
|
+
if (cycleErr) throw new Error(cycleErr);
|
|
370
|
+
this.validatePrimaryRules(config, id);
|
|
371
|
+
// Primary agent: force terminal-only, root dir
|
|
372
|
+
if (config.primary) {
|
|
373
|
+
config.persistentSession = true;
|
|
374
|
+
config.workDir = './';
|
|
375
|
+
}
|
|
376
|
+
if (entry.worker && entry.state.taskStatus === 'running') {
|
|
377
|
+
entry.worker.stop();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// If agent CLI changed (claude→codex, etc.), kill old terminal and clear bound session
|
|
381
|
+
const agentChanged = entry.config.agentId !== config.agentId;
|
|
382
|
+
if (agentChanged) {
|
|
383
|
+
console.log(`[workspace] ${config.label}: agent changed ${entry.config.agentId} → ${config.agentId}`);
|
|
384
|
+
if (entry.state.tmuxSession) {
|
|
385
|
+
try { execSync(`tmux kill-session -t "${entry.state.tmuxSession}" 2>/dev/null`, { timeout: 3000 }); } catch {}
|
|
386
|
+
console.log(`[workspace] ${config.label}: killed tmux session ${entry.state.tmuxSession}`);
|
|
387
|
+
}
|
|
388
|
+
entry.state.tmuxSession = undefined;
|
|
389
|
+
config.boundSessionId = undefined;
|
|
390
|
+
} else {
|
|
391
|
+
// Preserve server-managed fields the client doesn't track
|
|
392
|
+
if (!config.boundSessionId && entry.config.boundSessionId) {
|
|
393
|
+
config.boundSessionId = entry.config.boundSessionId;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
entry.config = config;
|
|
398
|
+
// Reset status but keep history/artifacts (don't wipe logs)
|
|
399
|
+
entry.state.taskStatus = 'idle';
|
|
400
|
+
entry.state.error = undefined;
|
|
401
|
+
if (entry.worker) {
|
|
402
|
+
entry.worker.removeAllListeners();
|
|
403
|
+
entry.worker.stop();
|
|
404
|
+
}
|
|
405
|
+
entry.worker = null;
|
|
406
|
+
|
|
407
|
+
if (this.daemonActive) {
|
|
408
|
+
// Set 'starting' BEFORE creating worker — worker.executeDaemon emits 'active' synchronously
|
|
409
|
+
// which would cause a race: frontend sees active before boundSessionId is ready
|
|
410
|
+
entry.state.smithStatus = 'starting';
|
|
411
|
+
this.emit('event', { type: 'smith_status', agentId: id, smithStatus: 'starting' } as any);
|
|
412
|
+
// Restart watch if config changed
|
|
413
|
+
this.watchManager.startWatch(id, config);
|
|
414
|
+
this.ensurePersistentSession(id, config).then(() => {
|
|
415
|
+
const e = this.agents.get(id);
|
|
416
|
+
if (e) {
|
|
417
|
+
// Rebuild worker + message loop AFTER session is ready (boundSessionId set)
|
|
418
|
+
this.enterDaemonListening(id);
|
|
419
|
+
e.state.smithStatus = 'active';
|
|
420
|
+
this.emit('event', { type: 'smith_status', agentId: id, smithStatus: 'active' } as any);
|
|
421
|
+
this.emitAgentsChanged();
|
|
422
|
+
}
|
|
423
|
+
this.startMessageLoop(id);
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
this.saveNow();
|
|
427
|
+
this.emitAgentsChanged();
|
|
428
|
+
this.emit('event', { type: 'task_status', agentId: id, taskStatus: 'idle' } satisfies WorkerEvent);
|
|
429
|
+
this.emit('event', { type: 'smith_status', agentId: id, smithStatus: entry.state.smithStatus } as any);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
getAgentState(id: string): Readonly<AgentState> | undefined {
|
|
433
|
+
return this.agents.get(id)?.state;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
getAllAgentStates(): Record<string, AgentState> {
|
|
437
|
+
const result: Record<string, AgentState> = {};
|
|
438
|
+
for (const [id, entry] of this.agents) {
|
|
439
|
+
const workerState = entry.worker?.getState();
|
|
440
|
+
// Merge: worker state for task/smith, entry.state for mode (orchestrator controls mode)
|
|
441
|
+
result[id] = workerState
|
|
442
|
+
? { ...workerState, taskStatus: entry.state.taskStatus, tmuxSession: entry.state.tmuxSession, currentMessageId: entry.state.currentMessageId }
|
|
443
|
+
: entry.state;
|
|
444
|
+
}
|
|
445
|
+
return result;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ─── Execution ─────────────────────────────────────────
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Complete an Input node — set its content and mark as done.
|
|
452
|
+
* If re-submitted, resets downstream agents so they can re-run.
|
|
453
|
+
*/
|
|
454
|
+
completeInput(agentId: string, content: string): void {
|
|
455
|
+
const entry = this.agents.get(agentId);
|
|
456
|
+
if (!entry || entry.config.type !== 'input') return;
|
|
457
|
+
|
|
458
|
+
const isUpdate = entry.state.taskStatus === 'done';
|
|
459
|
+
|
|
460
|
+
// Append to entries (incremental, not overwrite)
|
|
461
|
+
if (!entry.config.entries) entry.config.entries = [];
|
|
462
|
+
entry.config.entries.push({ content, timestamp: Date.now() });
|
|
463
|
+
// Keep bounded — max 100 entries, oldest removed
|
|
464
|
+
if (entry.config.entries.length > 100) {
|
|
465
|
+
entry.config.entries = entry.config.entries.slice(-100);
|
|
466
|
+
}
|
|
467
|
+
// Also set content to latest for backward compat
|
|
468
|
+
entry.config.content = content;
|
|
469
|
+
|
|
470
|
+
entry.state.taskStatus = 'done';
|
|
471
|
+
entry.state.completedAt = Date.now();
|
|
472
|
+
entry.state.artifacts = [{ type: 'text', summary: content.slice(0, 200) }];
|
|
473
|
+
|
|
474
|
+
this.emit('event', { type: 'task_status', agentId, taskStatus: 'done' } satisfies WorkerEvent);
|
|
475
|
+
this.emit('event', { type: 'done', agentId, summary: 'Input provided' } satisfies WorkerEvent);
|
|
476
|
+
this.emitAgentsChanged(); // push updated entries to frontend
|
|
477
|
+
this.bus.notifyTaskComplete(agentId, [], content.slice(0, 200));
|
|
478
|
+
|
|
479
|
+
// Send input_updated messages to downstream agents via bus
|
|
480
|
+
// routeMessageToAgent handles auto-execution for active smiths
|
|
481
|
+
for (const [id, downstream] of this.agents) {
|
|
482
|
+
if (downstream.config.type === 'input') continue;
|
|
483
|
+
if (!downstream.config.dependsOn.includes(agentId)) continue;
|
|
484
|
+
this.bus.send(agentId, id, 'notify', {
|
|
485
|
+
action: 'input_updated',
|
|
486
|
+
content: content.slice(0, 500),
|
|
487
|
+
});
|
|
488
|
+
console.log(`[bus] Input → ${downstream.config.label}: input_updated`);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
this.saveNow();
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/** Reset an agent and all its downstream to idle (for re-run) */
|
|
495
|
+
resetAgent(agentId: string): void {
|
|
496
|
+
const entry = this.agents.get(agentId);
|
|
497
|
+
if (!entry) return;
|
|
498
|
+
if (entry.worker) entry.worker.stop();
|
|
499
|
+
entry.worker = null;
|
|
500
|
+
// Kill orphaned tmux session if manual agent
|
|
501
|
+
if (entry.state.tmuxSession) {
|
|
502
|
+
try {
|
|
503
|
+
const { execSync } = require('node:child_process');
|
|
504
|
+
execSync(`tmux kill-session -t "${entry.state.tmuxSession}" 2>/dev/null`, { timeout: 3000 });
|
|
505
|
+
console.log(`[workspace] Killed tmux session ${entry.state.tmuxSession}`);
|
|
506
|
+
} catch {} // session might already be dead
|
|
507
|
+
}
|
|
508
|
+
entry.state = { smithStatus: 'down', taskStatus: 'idle', history: entry.state.history, artifacts: [] };
|
|
509
|
+
this.emit('event', { type: 'task_status', agentId, taskStatus: 'idle' } satisfies WorkerEvent);
|
|
510
|
+
this.emitAgentsChanged();
|
|
511
|
+
this.saveNow();
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** Reset all agents that depend on the given agent (recursively) */
|
|
515
|
+
private resetDownstream(agentId: string, visited = new Set<string>()): void {
|
|
516
|
+
if (visited.has(agentId)) return; // cycle protection
|
|
517
|
+
visited.add(agentId);
|
|
518
|
+
|
|
519
|
+
for (const [id, entry] of this.agents) {
|
|
520
|
+
if (id === agentId) continue;
|
|
521
|
+
if (!entry.config.dependsOn.includes(agentId)) continue;
|
|
522
|
+
if (entry.state.taskStatus === 'idle') continue;
|
|
523
|
+
console.log(`[workspace] Resetting ${entry.config.label} (${id}) to idle (upstream ${agentId} changed)`);
|
|
524
|
+
if (entry.worker) entry.worker.stop();
|
|
525
|
+
entry.worker = null;
|
|
526
|
+
entry.state = { smithStatus: entry.state.smithStatus, taskStatus: 'idle', history: entry.state.history, artifacts: [], cliSessionId: entry.state.cliSessionId };
|
|
527
|
+
this.emit('event', { type: 'task_status', agentId: id, taskStatus: 'idle' } satisfies WorkerEvent);
|
|
528
|
+
this.resetDownstream(id, visited);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/** Validate that an agent can run (sync check). Throws on error. */
|
|
533
|
+
validateCanRun(agentId: string): void {
|
|
534
|
+
const entry = this.agents.get(agentId);
|
|
535
|
+
if (!entry) throw new Error(`Agent "${agentId}" not found`);
|
|
536
|
+
if (entry.config.type === 'input') return;
|
|
537
|
+
if (entry.state.taskStatus === 'running') throw new Error(`Agent "${entry.config.label}" is already running`);
|
|
538
|
+
for (const depId of entry.config.dependsOn) {
|
|
539
|
+
const dep = this.agents.get(depId);
|
|
540
|
+
if (!dep) throw new Error(`Dependency "${depId}" not found (deleted?). Edit the agent to fix.`);
|
|
541
|
+
if (dep.state.taskStatus !== 'done') {
|
|
542
|
+
const hint = dep.state.taskStatus === 'idle' ? ' (never executed — run it first)'
|
|
543
|
+
: dep.state.taskStatus === 'failed' ? ' (failed — retry it first)'
|
|
544
|
+
: dep.state.taskStatus === 'running' ? ' (still running — wait for it to finish)'
|
|
545
|
+
: '';
|
|
546
|
+
throw new Error(`Dependency "${dep.config.label}" not completed yet${hint}`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/** Run a specific agent. Requires daemon mode. force=true bypasses status checks (for retry). */
|
|
552
|
+
async runAgent(agentId: string, userInput?: string, force = false): Promise<void> {
|
|
553
|
+
if (!this.daemonActive) {
|
|
554
|
+
throw new Error('Start daemon first before running agents');
|
|
555
|
+
}
|
|
556
|
+
const label = this.agents.get(agentId)?.config.label || agentId;
|
|
557
|
+
console.log(`[workspace] runAgent(${label}, force=${force})`, new Error().stack?.split('\n').slice(2, 5).join(' <- '));
|
|
558
|
+
return this.runAgentDaemon(agentId, userInput, force);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/** @deprecated Use runAgent (which now delegates to daemon mode) */
|
|
562
|
+
private async runAgentLegacy(agentId: string, userInput?: string): Promise<void> {
|
|
563
|
+
const entry = this.agents.get(agentId);
|
|
564
|
+
if (!entry) throw new Error(`Agent "${agentId}" not found`);
|
|
565
|
+
|
|
566
|
+
// Input nodes are completed via completeInput(), not run
|
|
567
|
+
if (entry.config.type === 'input') {
|
|
568
|
+
if (userInput) this.completeInput(agentId, userInput);
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (entry.state.taskStatus === 'running') return;
|
|
573
|
+
|
|
574
|
+
// Allow re-running done/failed/idle(was interrupted)/waiting_approval agents — reset them first
|
|
575
|
+
let resumeFromCheckpoint = false;
|
|
576
|
+
if (entry.state.taskStatus === 'done' || entry.state.taskStatus === 'failed' || entry.state.taskStatus === 'idle' || this.approvalQueue.has(agentId)) {
|
|
577
|
+
this.approvalQueue.delete(agentId);
|
|
578
|
+
console.log(`[workspace] Re-running ${entry.config.label} (was taskStatus=${entry.state.taskStatus})`);
|
|
579
|
+
// For failed: keep lastCheckpoint for resume
|
|
580
|
+
resumeFromCheckpoint = (entry.state.taskStatus === 'failed')
|
|
581
|
+
&& entry.state.lastCheckpoint !== undefined;
|
|
582
|
+
if (entry.worker) entry.worker.stop();
|
|
583
|
+
entry.worker = null;
|
|
584
|
+
if (!resumeFromCheckpoint) {
|
|
585
|
+
entry.state = { smithStatus: entry.state.smithStatus, taskStatus: 'idle', history: entry.state.history, artifacts: [], cliSessionId: entry.state.cliSessionId };
|
|
586
|
+
} else {
|
|
587
|
+
entry.state.taskStatus = 'idle';
|
|
588
|
+
entry.state.error = undefined;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const { config } = entry;
|
|
593
|
+
|
|
594
|
+
// Check if all dependencies are done
|
|
595
|
+
for (const depId of config.dependsOn) {
|
|
596
|
+
const dep = this.agents.get(depId);
|
|
597
|
+
if (!dep || dep.state.taskStatus !== 'done') {
|
|
598
|
+
throw new Error(`Dependency "${dep?.config.label || depId}" not completed yet`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Build upstream context from dependencies (includes Input node content)
|
|
603
|
+
let upstreamContext = this.buildUpstreamContext(config);
|
|
604
|
+
if (userInput) {
|
|
605
|
+
const prefix = '## Additional Instructions:\n' + userInput;
|
|
606
|
+
upstreamContext = upstreamContext ? prefix + '\n\n---\n\n' + upstreamContext : prefix;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Create backend
|
|
610
|
+
const backend = this.createBackend(config, agentId);
|
|
611
|
+
|
|
612
|
+
// Create worker with bus callbacks for inter-agent communication
|
|
613
|
+
// Load agent memory
|
|
614
|
+
const memory = loadMemory(this.workspaceId, agentId);
|
|
615
|
+
const memoryContext = formatMemoryForPrompt(memory);
|
|
616
|
+
|
|
617
|
+
const peerAgentIds = Array.from(this.agents.keys()).filter(id => id !== agentId);
|
|
618
|
+
const worker = new AgentWorker({
|
|
619
|
+
config,
|
|
620
|
+
backend,
|
|
621
|
+
projectPath: this.projectPath, workspaceId: this.workspaceId,
|
|
622
|
+
peerAgentIds,
|
|
623
|
+
memoryContext: memoryContext || undefined,
|
|
624
|
+
onBusSend: (to, content) => {
|
|
625
|
+
this.bus.send(agentId, to, 'notify', { action: 'agent_message', content });
|
|
626
|
+
},
|
|
627
|
+
onBusRequest: async (to, question) => {
|
|
628
|
+
const response = await this.bus.request(agentId, to, { action: 'question', content: question });
|
|
629
|
+
return response.payload.content || '(no response)';
|
|
630
|
+
},
|
|
631
|
+
onMemoryUpdate: (stepResults) => {
|
|
632
|
+
this.updateAgentMemory(agentId, config, stepResults);
|
|
633
|
+
},
|
|
634
|
+
});
|
|
635
|
+
entry.worker = worker;
|
|
636
|
+
|
|
637
|
+
// Forward worker events
|
|
638
|
+
worker.on('event', (event: WorkerEvent) => {
|
|
639
|
+
// Sync state
|
|
640
|
+
entry.state = worker.getState() as AgentState;
|
|
641
|
+
|
|
642
|
+
// Persist log entries to disk
|
|
643
|
+
if (event.type === 'log') {
|
|
644
|
+
appendAgentLog(this.workspaceId, agentId, event.entry).catch(() => {});
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
this.emit('event', event);
|
|
648
|
+
|
|
649
|
+
// Update liveness
|
|
650
|
+
if (event.type === 'task_status' || event.type === 'smith_status') {
|
|
651
|
+
this.updateAgentLiveness(agentId);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// On step complete → capture observation + notify bus
|
|
655
|
+
if (event.type === 'step') {
|
|
656
|
+
const step = config.steps[event.stepIndex];
|
|
657
|
+
if (step) {
|
|
658
|
+
this.bus.notifyStepComplete(agentId, step.label);
|
|
659
|
+
|
|
660
|
+
// Capture memory observation from the previous step's result
|
|
661
|
+
const prevStepIdx = event.stepIndex - 1;
|
|
662
|
+
if (prevStepIdx >= 0) {
|
|
663
|
+
const prevStep = config.steps[prevStepIdx];
|
|
664
|
+
const prevResult = entry.state.history
|
|
665
|
+
.filter(h => h.type === 'result' && h.subtype === 'step_complete')
|
|
666
|
+
.slice(-1)[0];
|
|
667
|
+
if (prevResult && prevStep) {
|
|
668
|
+
const obs = parseStepToObservations(prevStep.label, prevResult.content, entry.state.artifacts);
|
|
669
|
+
for (const o of obs) {
|
|
670
|
+
addObservation(this.workspaceId, agentId, config.label, config.role, o).catch(() => {});
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// On done → notify + trigger downstream (or reply to sender if from downstream)
|
|
678
|
+
if (event.type === 'done') {
|
|
679
|
+
this.handleAgentDone(agentId, entry, event.summary);
|
|
680
|
+
|
|
681
|
+
this.emitWorkspaceStatus();
|
|
682
|
+
this.checkWorkspaceComplete();
|
|
683
|
+
|
|
684
|
+
// Note: no auto-rerun. Bus messages that need re-run go through user approval.
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// On error → notify bus
|
|
688
|
+
if (event.type === 'error') {
|
|
689
|
+
this.bus.notifyError(agentId, event.error);
|
|
690
|
+
this.emitWorkspaceStatus();
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
// Inject only undelivered (pending) bus messages addressed to this agent
|
|
695
|
+
const pendingMsgs = this.bus.getPendingMessagesFor(agentId)
|
|
696
|
+
.filter(m => m.from !== agentId); // don't inject own messages
|
|
697
|
+
for (const msg of pendingMsgs) {
|
|
698
|
+
const fromLabel = this.agents.get(msg.from)?.config.label || msg.from;
|
|
699
|
+
worker.injectMessage({
|
|
700
|
+
type: 'system',
|
|
701
|
+
subtype: 'bus_message',
|
|
702
|
+
content: `[From ${fromLabel}]: ${msg.payload.content || msg.payload.action}`,
|
|
703
|
+
timestamp: new Date(msg.timestamp).toISOString(),
|
|
704
|
+
});
|
|
705
|
+
// Mark as delivered + ACK so sender knows it was received
|
|
706
|
+
msg.status = 'done';
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Start from checkpoint if recovering from failure
|
|
710
|
+
const startStep = resumeFromCheckpoint && entry.state.lastCheckpoint !== undefined
|
|
711
|
+
? entry.state.lastCheckpoint + 1
|
|
712
|
+
: 0;
|
|
713
|
+
|
|
714
|
+
this.emitWorkspaceStatus();
|
|
715
|
+
|
|
716
|
+
// Execute (non-blocking — fire and forget, events handle the rest)
|
|
717
|
+
worker.execute(startStep, upstreamContext).catch(err => {
|
|
718
|
+
// Only set failed if worker didn't already handle it (avoid duplicate error events)
|
|
719
|
+
if (entry.state.taskStatus !== 'failed') {
|
|
720
|
+
entry.state.taskStatus = 'failed';
|
|
721
|
+
entry.state.error = err?.message || String(err);
|
|
722
|
+
this.emit('event', { type: 'error', agentId, error: entry.state.error! } satisfies WorkerEvent);
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/** Run all agents — starts daemon if not active, then runs all ready agents */
|
|
728
|
+
async runAll(): Promise<void> {
|
|
729
|
+
if (!this.daemonActive) {
|
|
730
|
+
return this.startDaemon();
|
|
731
|
+
}
|
|
732
|
+
const ready = this.getDaemonReadyAgents();
|
|
733
|
+
await Promise.all(ready.map(id => this.runAgentDaemon(id)));
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/** Run a single agent in daemon mode. force=true resets failed/interrupted agents. triggerMessageId tracks which bus message started this. */
|
|
737
|
+
async runAgentDaemon(agentId: string, userInput?: string, force = false, triggerMessageId?: string): Promise<void> {
|
|
738
|
+
const entry = this.agents.get(agentId);
|
|
739
|
+
if (!entry) throw new Error(`Agent "${agentId}" not found`);
|
|
740
|
+
|
|
741
|
+
if (entry.config.type === 'input') {
|
|
742
|
+
if (userInput) this.completeInput(agentId, userInput);
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
if (entry.state.taskStatus === 'running' && !force) return;
|
|
747
|
+
// Already has a daemon worker running → skip (unless force retry)
|
|
748
|
+
if (entry.worker && entry.state.smithStatus === 'active' && !force) return;
|
|
749
|
+
|
|
750
|
+
// Already done → enter daemon listening directly (don't re-run steps)
|
|
751
|
+
if (entry.state.taskStatus === 'done' && !force) {
|
|
752
|
+
return this.enterDaemonListening(agentId);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
if (!force) {
|
|
756
|
+
// Failed → leave as-is, user must retry explicitly
|
|
757
|
+
if (entry.state.taskStatus === 'failed') return;
|
|
758
|
+
// waiting_approval → leave as-is
|
|
759
|
+
if (this.approvalQueue.has(agentId)) return;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Reset state for fresh start — preserve smithStatus and mode
|
|
763
|
+
if (entry.state.taskStatus !== 'idle') {
|
|
764
|
+
this.approvalQueue.delete(agentId);
|
|
765
|
+
if (entry.worker) entry.worker.stop();
|
|
766
|
+
entry.worker = null;
|
|
767
|
+
entry.state = {
|
|
768
|
+
smithStatus: entry.state.smithStatus,
|
|
769
|
+
taskStatus: 'idle',
|
|
770
|
+
history: [],
|
|
771
|
+
artifacts: [],
|
|
772
|
+
cliSessionId: entry.state.cliSessionId, // preserve session for --resume
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Ensure smith is active when daemon starts this agent
|
|
777
|
+
// Skip if 'starting': ensurePersistentSession is in progress and will set 'active' when done.
|
|
778
|
+
if (this.daemonActive && entry.state.smithStatus !== 'active' && entry.state.smithStatus !== 'starting') {
|
|
779
|
+
entry.state.smithStatus = 'active';
|
|
780
|
+
this.emit('event', { type: 'smith_status', agentId, smithStatus: 'active' } satisfies WorkerEvent);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
const { config } = entry;
|
|
784
|
+
|
|
785
|
+
// Check dependencies
|
|
786
|
+
for (const depId of config.dependsOn) {
|
|
787
|
+
const dep = this.agents.get(depId);
|
|
788
|
+
if (!dep) throw new Error(`Dependency "${depId}" not found`);
|
|
789
|
+
if (force) {
|
|
790
|
+
// Manual trigger: only require upstream smith to be active (online)
|
|
791
|
+
if (dep.config.type !== 'input' && dep.state.smithStatus !== 'active') {
|
|
792
|
+
throw new Error(`Dependency "${dep.config.label}" smith is not active — start daemon first`);
|
|
793
|
+
}
|
|
794
|
+
} else {
|
|
795
|
+
// Auto trigger: require upstream task completed
|
|
796
|
+
if (dep.state.taskStatus !== 'done') {
|
|
797
|
+
throw new Error(`Dependency "${dep.config.label}" not completed yet`);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// Role is now injected via buildUpstreamContext (headless) and persistent session preamble (terminal).
|
|
803
|
+
// No longer writes to CLAUDE.md — project files stay clean when daemon stops.
|
|
804
|
+
|
|
805
|
+
let upstreamContext = this.buildUpstreamContext(config);
|
|
806
|
+
if (userInput) {
|
|
807
|
+
const prefix = '## Additional Instructions:\n' + userInput;
|
|
808
|
+
upstreamContext = upstreamContext ? prefix + '\n\n---\n\n' + upstreamContext : prefix;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
const backend = this.createBackend(config, agentId);
|
|
812
|
+
const memory = loadMemory(this.workspaceId, agentId);
|
|
813
|
+
const memoryContext = formatMemoryForPrompt(memory);
|
|
814
|
+
const peerAgentIds = Array.from(this.agents.keys()).filter(id => id !== agentId);
|
|
815
|
+
|
|
816
|
+
const worker = new AgentWorker({
|
|
817
|
+
config, backend,
|
|
818
|
+
projectPath: this.projectPath, workspaceId: this.workspaceId,
|
|
819
|
+
peerAgentIds,
|
|
820
|
+
memoryContext: memoryContext || undefined,
|
|
821
|
+
onBusSend: (to, content) => {
|
|
822
|
+
this.bus.send(agentId, to, 'notify', { action: 'agent_message', content });
|
|
823
|
+
},
|
|
824
|
+
onBusRequest: async (to, question) => {
|
|
825
|
+
const response = await this.bus.request(agentId, to, { action: 'question', content: question });
|
|
826
|
+
return response.payload.content || '(no response)';
|
|
827
|
+
},
|
|
828
|
+
onMessageDone: (messageId) => {
|
|
829
|
+
const busMsg = this.bus.getLog().find(m => m.id === messageId);
|
|
830
|
+
if (busMsg) {
|
|
831
|
+
busMsg.status = 'done';
|
|
832
|
+
this.emit('event', { type: 'bus_message_status', messageId, status: 'done' } as any);
|
|
833
|
+
this.emitAgentsChanged();
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
onMessageFailed: (messageId) => {
|
|
837
|
+
const busMsg = this.bus.getLog().find(m => m.id === messageId);
|
|
838
|
+
if (busMsg) {
|
|
839
|
+
busMsg.status = 'failed';
|
|
840
|
+
this.emit('event', { type: 'bus_message_status', messageId, status: 'failed' } as any);
|
|
841
|
+
this.emitAgentsChanged();
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
onMemoryUpdate: (stepResults) => {
|
|
845
|
+
try {
|
|
846
|
+
const observations = stepResults.flatMap((r, i) =>
|
|
847
|
+
parseStepToObservations(config.steps[i]?.label || `Step ${i}`, r, entry.state.artifacts)
|
|
848
|
+
);
|
|
849
|
+
for (const obs of observations) addObservation(this.workspaceId, agentId, config.label, config.role, obs);
|
|
850
|
+
const stepLabels = config.steps.map(s => s.label);
|
|
851
|
+
const summary = buildSessionSummary(stepLabels, stepResults, entry.state.artifacts);
|
|
852
|
+
addSessionSummary(this.workspaceId, agentId, summary);
|
|
853
|
+
} catch {}
|
|
854
|
+
},
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
entry.worker = worker;
|
|
858
|
+
|
|
859
|
+
// Track trigger message so smith can mark it done/failed on completion
|
|
860
|
+
if (triggerMessageId) {
|
|
861
|
+
worker.setProcessingMessage(triggerMessageId);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Forward events (same as runAgent)
|
|
865
|
+
worker.on('event', (event: WorkerEvent) => {
|
|
866
|
+
if (event.type === 'task_status') {
|
|
867
|
+
entry.state.taskStatus = event.taskStatus;
|
|
868
|
+
entry.state.error = event.error;
|
|
869
|
+
if (event.taskStatus === 'running') entry.state.startedAt = Date.now();
|
|
870
|
+
const workerState = worker.getState();
|
|
871
|
+
entry.state.daemonIteration = workerState.daemonIteration;
|
|
872
|
+
}
|
|
873
|
+
if (event.type === 'smith_status') {
|
|
874
|
+
entry.state.smithStatus = event.smithStatus;
|
|
875
|
+
}
|
|
876
|
+
if (event.type === 'log') {
|
|
877
|
+
appendAgentLog(this.workspaceId, agentId, event.entry).catch(() => {});
|
|
878
|
+
}
|
|
879
|
+
this.emit('event', event);
|
|
880
|
+
if (event.type === 'task_status' || event.type === 'smith_status') {
|
|
881
|
+
this.updateAgentLiveness(agentId);
|
|
882
|
+
}
|
|
883
|
+
if (event.type === 'step' && event.stepIndex >= 0) {
|
|
884
|
+
const step = config.steps[event.stepIndex];
|
|
885
|
+
if (step) this.bus.notifyStepComplete(agentId, step.label);
|
|
886
|
+
}
|
|
887
|
+
if (event.type === 'done') {
|
|
888
|
+
this.handleAgentDone(agentId, entry, event.summary);
|
|
889
|
+
}
|
|
890
|
+
if (event.type === 'error') {
|
|
891
|
+
this.agentRunningMsg.delete(agentId);
|
|
892
|
+
this.bus.notifyError(agentId, event.error);
|
|
893
|
+
this.emitWorkspaceStatus();
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
// Inject pending messages
|
|
898
|
+
const pendingMsgs = this.bus.getPendingMessagesFor(agentId)
|
|
899
|
+
.filter(m => m.from !== agentId);
|
|
900
|
+
for (const msg of pendingMsgs) {
|
|
901
|
+
const fromLabel = this.agents.get(msg.from)?.config.label || msg.from;
|
|
902
|
+
worker.injectMessage({
|
|
903
|
+
type: 'system', subtype: 'bus_message',
|
|
904
|
+
content: `[From ${fromLabel}]: ${msg.payload.content || msg.payload.action}`,
|
|
905
|
+
timestamp: new Date(msg.timestamp).toISOString(),
|
|
906
|
+
});
|
|
907
|
+
msg.status = 'done';
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
this.emitWorkspaceStatus();
|
|
911
|
+
|
|
912
|
+
// Execute in daemon mode (non-blocking)
|
|
913
|
+
worker.executeDaemon(0, upstreamContext).catch(err => {
|
|
914
|
+
if (entry.state.taskStatus !== 'failed') {
|
|
915
|
+
this.agentRunningMsg.delete(agentId);
|
|
916
|
+
entry.state.taskStatus = 'failed';
|
|
917
|
+
entry.state.error = err?.message || String(err);
|
|
918
|
+
this.emit('event', { type: 'error', agentId, error: entry.state.error! } satisfies WorkerEvent);
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/** Start all agents in daemon mode — orchestrator manages each smith's lifecycle */
|
|
924
|
+
async startDaemon(): Promise<void> {
|
|
925
|
+
if (this.daemonActive) return;
|
|
926
|
+
this.daemonActive = true;
|
|
927
|
+
console.log(`[workspace] Starting daemon mode...`);
|
|
928
|
+
|
|
929
|
+
// Clean up stale state from previous run
|
|
930
|
+
this.bus.markAllRunningAsFailed();
|
|
931
|
+
|
|
932
|
+
// Install forge skills globally (once per daemon start)
|
|
933
|
+
try {
|
|
934
|
+
installForgeSkills(this.projectPath, this.workspaceId, '', Number(process.env.PORT) || 8403);
|
|
935
|
+
} catch {}
|
|
936
|
+
|
|
937
|
+
// Validate agent IDs — fallback to default if configured agent was deleted from Settings
|
|
938
|
+
let defaultAgentId = 'claude';
|
|
939
|
+
try {
|
|
940
|
+
const { listAgents, getDefaultAgentId } = await import('../agents/index') as any;
|
|
941
|
+
const validAgents = new Set((listAgents() as any[]).map(a => a.id));
|
|
942
|
+
defaultAgentId = getDefaultAgentId() || 'claude';
|
|
943
|
+
for (const [id, entry] of this.agents) {
|
|
944
|
+
if (entry.config.type === 'input') continue;
|
|
945
|
+
if (entry.config.agentId && !validAgents.has(entry.config.agentId)) {
|
|
946
|
+
console.log(`[daemon] ${entry.config.label}: agent "${entry.config.agentId}" not found, falling back to "${defaultAgentId}"`);
|
|
947
|
+
entry.config.agentId = defaultAgentId;
|
|
948
|
+
this.emit('event', { type: 'log', agentId: id, entry: { type: 'system', subtype: 'warning', content: `Agent "${entry.config.agentId}" not found in Settings — using default "${defaultAgentId}"`, timestamp: new Date().toISOString() } } as any);
|
|
949
|
+
this.saveNow();
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
} catch {}
|
|
953
|
+
|
|
954
|
+
// Start each smith one by one, verify each starts correctly
|
|
955
|
+
let started = 0;
|
|
956
|
+
let failed = 0;
|
|
957
|
+
for (const [id, entry] of this.agents) {
|
|
958
|
+
if (entry.config.type === 'input') continue;
|
|
959
|
+
|
|
960
|
+
// Kill any stale worker from previous run
|
|
961
|
+
if (entry.worker) {
|
|
962
|
+
entry.worker.stop();
|
|
963
|
+
entry.worker = null;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// Stop any existing message loop
|
|
967
|
+
this.stopMessageLoop(id);
|
|
968
|
+
|
|
969
|
+
try {
|
|
970
|
+
// 1. Start daemon listening loop (creates worker)
|
|
971
|
+
this.enterDaemonListening(id);
|
|
972
|
+
|
|
973
|
+
// 2. Verify worker was created
|
|
974
|
+
if (!entry.worker) {
|
|
975
|
+
throw new Error('Worker not created');
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// 3. Set smith status — persistent session agents stay 'starting' until ensurePersistentSession completes
|
|
979
|
+
entry.state.smithStatus = entry.config.persistentSession ? 'starting' : 'active';
|
|
980
|
+
entry.state.error = undefined;
|
|
981
|
+
|
|
982
|
+
// 4. Start message loop (delayed for persistent session agents — session must exist first)
|
|
983
|
+
if (!entry.config.persistentSession) {
|
|
984
|
+
this.startMessageLoop(id);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// 5. Update liveness for bus routing
|
|
988
|
+
this.updateAgentLiveness(id);
|
|
989
|
+
|
|
990
|
+
// 6. Notify frontend
|
|
991
|
+
this.emit('event', { type: 'smith_status', agentId: id, smithStatus: entry.state.smithStatus } as any);
|
|
992
|
+
|
|
993
|
+
started++;
|
|
994
|
+
console.log(`[daemon] ✓ ${entry.config.label}: ${entry.state.smithStatus} (task=${entry.state.taskStatus})`);
|
|
995
|
+
} catch (err: any) {
|
|
996
|
+
entry.state.smithStatus = 'down';
|
|
997
|
+
entry.state.error = `Failed to start: ${err.message}`;
|
|
998
|
+
this.emit('event', { type: 'smith_status', agentId: id, smithStatus: 'down' } satisfies WorkerEvent);
|
|
999
|
+
failed++;
|
|
1000
|
+
console.error(`[daemon] ✗ ${entry.config.label}: failed — ${err.message}`);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// Migration: clean any legacy Forge blocks from CLAUDE.md files (role/topo no longer written to disk)
|
|
1005
|
+
this.cleanLegacyClaudeMdBlocks();
|
|
1006
|
+
|
|
1007
|
+
// Create persistent terminal sessions, then start their message loops
|
|
1008
|
+
for (const [id, entry] of this.agents) {
|
|
1009
|
+
if (entry.config.type === 'input' || !entry.config.persistentSession) continue;
|
|
1010
|
+
await this.ensurePersistentSession(id, entry.config);
|
|
1011
|
+
// Set active now that session + boundSessionId are ready
|
|
1012
|
+
if (entry.state.smithStatus === 'starting') {
|
|
1013
|
+
entry.state.smithStatus = 'active';
|
|
1014
|
+
this.emit('event', { type: 'smith_status', agentId: id, smithStatus: 'active' } as any);
|
|
1015
|
+
}
|
|
1016
|
+
if (entry.state.smithStatus === 'active') {
|
|
1017
|
+
// Inject role preamble so Claude knows its role (replaces CLAUDE.md writing)
|
|
1018
|
+
if (entry.config.role?.trim()) {
|
|
1019
|
+
const preamble = this.buildRolePreamble(entry.config);
|
|
1020
|
+
if (this.injectIntoSession(id, preamble)) {
|
|
1021
|
+
this.markRoleInjected(id);
|
|
1022
|
+
console.log(`[daemon] ${entry.config.label}: injected role preamble`);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
this.startMessageLoop(id);
|
|
1026
|
+
} else {
|
|
1027
|
+
console.log(`[daemon] ${entry.config.label}: skipped message loop (smith=${entry.state.smithStatus})`);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Build workspace topology cache (all smiths can query via MCP get_agents)
|
|
1032
|
+
this.rebuildTopo();
|
|
1033
|
+
|
|
1034
|
+
// Start watch loops for agents with watch config
|
|
1035
|
+
this.watchManager.start();
|
|
1036
|
+
|
|
1037
|
+
// Start session file monitors for agents with known session IDs
|
|
1038
|
+
this.startSessionMonitors().catch(err => console.error('[session-monitor] Failed to start:', err.message));
|
|
1039
|
+
|
|
1040
|
+
// Start health check — monitor all agents every 10s, auto-heal
|
|
1041
|
+
this.startHealthCheck();
|
|
1042
|
+
|
|
1043
|
+
console.log(`[workspace] Daemon started: ${started} smiths active, ${failed} failed`);
|
|
1044
|
+
this.emitAgentsChanged();
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/** Get agents that can start in daemon mode (idle, done — with deps met) */
|
|
1048
|
+
private getDaemonReadyAgents(): string[] {
|
|
1049
|
+
const ready: string[] = [];
|
|
1050
|
+
for (const [id, entry] of this.agents) {
|
|
1051
|
+
if (entry.config.type === 'input') continue;
|
|
1052
|
+
if (entry.state.taskStatus === 'running' || entry.state.smithStatus === 'active') {
|
|
1053
|
+
console.log(`[daemon] ${entry.config.label}: already smithStatus=${entry.state.smithStatus} taskStatus=${entry.state.taskStatus}`);
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
const allDepsDone = entry.config.dependsOn.every(depId => {
|
|
1057
|
+
const dep = this.agents.get(depId);
|
|
1058
|
+
return dep && (dep.state.taskStatus === 'done');
|
|
1059
|
+
});
|
|
1060
|
+
if (allDepsDone) {
|
|
1061
|
+
console.log(`[daemon] ${entry.config.label}: ready (taskStatus=${entry.state.taskStatus})`);
|
|
1062
|
+
ready.push(id);
|
|
1063
|
+
} else {
|
|
1064
|
+
const unmet = entry.config.dependsOn.filter(d => {
|
|
1065
|
+
const dep = this.agents.get(d);
|
|
1066
|
+
return !dep || (dep.state.taskStatus !== 'done');
|
|
1067
|
+
}).map(d => this.agents.get(d)?.config.label || d);
|
|
1068
|
+
console.log(`[daemon] ${entry.config.label}: not ready — deps unmet: ${unmet.join(', ')} (taskStatus=${entry.state.taskStatus})`);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
return ready;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/** Put a done agent into daemon listening mode without re-running steps */
|
|
1075
|
+
private enterDaemonListening(agentId: string): void {
|
|
1076
|
+
const entry = this.agents.get(agentId);
|
|
1077
|
+
if (!entry) return;
|
|
1078
|
+
|
|
1079
|
+
// Stop existing worker first to prevent duplicate execution
|
|
1080
|
+
if (entry.worker) {
|
|
1081
|
+
entry.worker.removeAllListeners();
|
|
1082
|
+
entry.worker.stop();
|
|
1083
|
+
entry.worker = null;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
const { config } = entry;
|
|
1087
|
+
|
|
1088
|
+
const backend = this.createBackend(config, agentId);
|
|
1089
|
+
const peerAgentIds = Array.from(this.agents.keys()).filter(id => id !== agentId);
|
|
1090
|
+
|
|
1091
|
+
const worker = new AgentWorker({
|
|
1092
|
+
config, backend,
|
|
1093
|
+
projectPath: this.projectPath, workspaceId: this.workspaceId,
|
|
1094
|
+
peerAgentIds,
|
|
1095
|
+
initialTaskStatus: entry.state.taskStatus, // preserve current task status
|
|
1096
|
+
onBusSend: (to, content) => {
|
|
1097
|
+
this.bus.send(agentId, to, 'notify', { action: 'agent_message', content });
|
|
1098
|
+
},
|
|
1099
|
+
onBusRequest: async (to, question) => {
|
|
1100
|
+
const response = await this.bus.request(agentId, to, { action: 'question', content: question });
|
|
1101
|
+
return response.payload.content || '(no response)';
|
|
1102
|
+
},
|
|
1103
|
+
onMessageDone: (messageId) => {
|
|
1104
|
+
const busMsg = this.bus.getLog().find(m => m.id === messageId);
|
|
1105
|
+
if (busMsg) {
|
|
1106
|
+
busMsg.status = 'done';
|
|
1107
|
+
this.emit('event', { type: 'bus_message_status', messageId, status: 'done' } as any);
|
|
1108
|
+
this.emitAgentsChanged();
|
|
1109
|
+
}
|
|
1110
|
+
},
|
|
1111
|
+
onMessageFailed: (messageId) => {
|
|
1112
|
+
const busMsg = this.bus.getLog().find(m => m.id === messageId);
|
|
1113
|
+
if (busMsg) {
|
|
1114
|
+
busMsg.status = 'failed';
|
|
1115
|
+
this.emit('event', { type: 'bus_message_status', messageId, status: 'failed' } as any);
|
|
1116
|
+
this.emitAgentsChanged();
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
entry.worker = worker;
|
|
1122
|
+
|
|
1123
|
+
// Forward events (same handler as runAgentDaemon)
|
|
1124
|
+
worker.on('event', (event: WorkerEvent) => {
|
|
1125
|
+
if (event.type === 'task_status') {
|
|
1126
|
+
entry.state.taskStatus = event.taskStatus;
|
|
1127
|
+
entry.state.error = event.error;
|
|
1128
|
+
const workerState = worker.getState();
|
|
1129
|
+
entry.state.daemonIteration = workerState.daemonIteration;
|
|
1130
|
+
}
|
|
1131
|
+
if (event.type === 'smith_status') {
|
|
1132
|
+
entry.state.smithStatus = event.smithStatus;
|
|
1133
|
+
}
|
|
1134
|
+
if (event.type === 'log') {
|
|
1135
|
+
appendAgentLog(this.workspaceId, agentId, event.entry).catch(() => {});
|
|
1136
|
+
}
|
|
1137
|
+
this.emit('event', event);
|
|
1138
|
+
if (event.type === 'task_status' || event.type === 'smith_status') {
|
|
1139
|
+
this.updateAgentLiveness(agentId);
|
|
1140
|
+
}
|
|
1141
|
+
if (event.type === 'done') {
|
|
1142
|
+
this.handleAgentDone(agentId, entry, event.summary);
|
|
1143
|
+
}
|
|
1144
|
+
if (event.type === 'error') {
|
|
1145
|
+
this.agentRunningMsg.delete(agentId);
|
|
1146
|
+
this.bus.notifyError(agentId, event.error);
|
|
1147
|
+
}
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
// Message loop (startMessageLoop) handles auto-consumption of pending messages
|
|
1151
|
+
|
|
1152
|
+
console.log(`[workspace] Agent "${config.label}" entering daemon listening (task=${entry.state.taskStatus})`);
|
|
1153
|
+
|
|
1154
|
+
// executeDaemon with skipSteps=true → goes directly to listening loop
|
|
1155
|
+
worker.executeDaemon(0, undefined, true).catch(err => {
|
|
1156
|
+
console.error(`[workspace] enterDaemonListening error for ${config.label}:`, err.message);
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
/** Stop all agents (exit daemon mode) */
|
|
1161
|
+
/** Stop all agents — orchestrator shuts down each smith */
|
|
1162
|
+
stopDaemon(): void {
|
|
1163
|
+
this.daemonActive = false;
|
|
1164
|
+
console.log('[workspace] Stopping daemon...');
|
|
1165
|
+
|
|
1166
|
+
for (const [id, entry] of this.agents) {
|
|
1167
|
+
if (entry.config.type === 'input') continue;
|
|
1168
|
+
|
|
1169
|
+
// 1. Stop message loop
|
|
1170
|
+
this.stopMessageLoop(id);
|
|
1171
|
+
|
|
1172
|
+
// 2. Stop worker
|
|
1173
|
+
if (entry.worker) {
|
|
1174
|
+
entry.worker.stop();
|
|
1175
|
+
entry.worker = null;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// 2b. For persistent sessions: send /clear to reset Claude's context if user is attached.
|
|
1179
|
+
// This is a Claude Code slash command — no LLM call, just a local context reset.
|
|
1180
|
+
if (entry.state.tmuxSession && entry.config.role?.trim()) {
|
|
1181
|
+
let isAttached = false;
|
|
1182
|
+
try {
|
|
1183
|
+
const info = execSync(`tmux display-message -t "${entry.state.tmuxSession}" -p "#{session_attached}" 2>/dev/null`, { timeout: 3000, encoding: 'utf-8' }).trim();
|
|
1184
|
+
isAttached = info !== '0';
|
|
1185
|
+
} catch {}
|
|
1186
|
+
if (isAttached) {
|
|
1187
|
+
try { this.injectIntoSession(id, '/clear'); } catch {}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
this.roleInjectState.delete(id);
|
|
1191
|
+
|
|
1192
|
+
// 3. Kill tmux session (skip if user is attached to it)
|
|
1193
|
+
if (entry.state.tmuxSession) {
|
|
1194
|
+
let isAttached = false;
|
|
1195
|
+
try {
|
|
1196
|
+
const info = execSync(`tmux display-message -t "${entry.state.tmuxSession}" -p "#{session_attached}" 2>/dev/null`, { timeout: 3000, encoding: 'utf-8' }).trim();
|
|
1197
|
+
isAttached = info !== '0';
|
|
1198
|
+
} catch {}
|
|
1199
|
+
if (isAttached) {
|
|
1200
|
+
console.log(`[daemon] ${entry.config.label}: tmux session attached by user, not killing`);
|
|
1201
|
+
} else {
|
|
1202
|
+
try { execSync(`tmux kill-session -t "${entry.state.tmuxSession}" 2>/dev/null`, { timeout: 3000 }); } catch {}
|
|
1203
|
+
}
|
|
1204
|
+
entry.state.tmuxSession = undefined;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// 4. Set smith down, reset running tasks
|
|
1208
|
+
entry.state.smithStatus = 'down';
|
|
1209
|
+
if (entry.state.taskStatus === 'running') {
|
|
1210
|
+
entry.state.taskStatus = 'idle';
|
|
1211
|
+
}
|
|
1212
|
+
entry.state.error = undefined;
|
|
1213
|
+
this.updateAgentLiveness(id);
|
|
1214
|
+
this.emit('event', { type: 'smith_status', agentId: id, smithStatus: 'down' } satisfies WorkerEvent);
|
|
1215
|
+
|
|
1216
|
+
console.log(`[daemon] ■ ${entry.config.label}: stopped`);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// Mark running messages as failed
|
|
1220
|
+
this.bus.markAllRunningAsFailed();
|
|
1221
|
+
this.emitAgentsChanged();
|
|
1222
|
+
this.watchManager.stop();
|
|
1223
|
+
this.stopAllTerminalMonitors();
|
|
1224
|
+
if (this.sessionMonitor) { this.sessionMonitor.stopAll(); this.sessionMonitor = null; }
|
|
1225
|
+
this.stopHealthCheck();
|
|
1226
|
+
this.forgeActedMessages.clear();
|
|
1227
|
+
this.busMarkerScanned.clear();
|
|
1228
|
+
this.forgeAgentStartTime = 0;
|
|
1229
|
+
this.agentRunningMsg.clear();
|
|
1230
|
+
this.reconcileTick = 0;
|
|
1231
|
+
console.log('[workspace] Daemon stopped');
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// ─── Hook-based completion ─────────────────────────────
|
|
1235
|
+
|
|
1236
|
+
/** Called by Claude Code Stop hook via HTTP — agent finished a turn */
|
|
1237
|
+
handleHookDone(agentId: string): void {
|
|
1238
|
+
const entry = this.agents.get(agentId);
|
|
1239
|
+
if (!entry) return;
|
|
1240
|
+
if (!this.daemonActive) return;
|
|
1241
|
+
|
|
1242
|
+
console.log(`[hook] ${entry.config.label}: Stop hook → done (was ${entry.state.taskStatus})`);
|
|
1243
|
+
entry.state.taskStatus = 'done';
|
|
1244
|
+
entry.state.completedAt = Date.now();
|
|
1245
|
+
this.emit('event', { type: 'task_status', agentId, taskStatus: 'done' } as any);
|
|
1246
|
+
this.emit('event', { type: 'log', agentId, entry: { type: 'system', subtype: 'hook_done', content: 'Claude Code Stop hook: turn completed', timestamp: new Date().toISOString() } } as any);
|
|
1247
|
+
this.handleAgentDone(agentId, entry, 'Stop hook');
|
|
1248
|
+
this.sessionMonitor?.resetState(agentId);
|
|
1249
|
+
this.saveNow();
|
|
1250
|
+
this.emitAgentsChanged();
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// ─── Session File Monitor ──────────────────────────────
|
|
1254
|
+
|
|
1255
|
+
private async startSessionMonitors(): Promise<void> {
|
|
1256
|
+
console.log('[session-monitor] Initializing...');
|
|
1257
|
+
const { SessionFileMonitor } = await import('./session-monitor');
|
|
1258
|
+
this.sessionMonitor = new SessionFileMonitor();
|
|
1259
|
+
|
|
1260
|
+
// Listen for state changes from session file monitor
|
|
1261
|
+
this.sessionMonitor.on('stateChange', (event: any) => {
|
|
1262
|
+
const entry = this.agents.get(event.agentId);
|
|
1263
|
+
if (!entry) {
|
|
1264
|
+
console.log(`[session-monitor] stateChange: agent ${event.agentId} not found in map`);
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
console.log(`[session-monitor] stateChange: ${entry.config.label} ${event.state} (current taskStatus=${entry.state.taskStatus})`);
|
|
1268
|
+
|
|
1269
|
+
if (event.state === 'running' && entry.state.taskStatus !== 'running') {
|
|
1270
|
+
entry.state.taskStatus = 'running';
|
|
1271
|
+
console.log(`[session-monitor] → emitting task_status=running for ${entry.config.label}`);
|
|
1272
|
+
this.emit('event', { type: 'task_status', agentId: event.agentId, taskStatus: 'running' } as any);
|
|
1273
|
+
this.emitAgentsChanged();
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
if (event.state === 'done' && entry.state.taskStatus === 'running') {
|
|
1277
|
+
entry.state.taskStatus = 'done';
|
|
1278
|
+
this.emit('event', { type: 'task_status', agentId: event.agentId, taskStatus: 'done' } as any);
|
|
1279
|
+
console.log(`[session-monitor] ${event.agentId}: done — ${event.detail || 'turn completed'}`);
|
|
1280
|
+
this.handleAgentDone(event.agentId, entry, event.detail);
|
|
1281
|
+
this.emitAgentsChanged();
|
|
1282
|
+
}
|
|
1283
|
+
});
|
|
1284
|
+
|
|
1285
|
+
// Start monitors for all agents with known session IDs
|
|
1286
|
+
for (const [id, entry] of this.agents) {
|
|
1287
|
+
if (entry.config.type === 'input') continue;
|
|
1288
|
+
await this.startAgentSessionMonitor(id, entry.config);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
private async startAgentSessionMonitor(agentId: string, config: WorkspaceAgentConfig): Promise<void> {
|
|
1293
|
+
if (!this.sessionMonitor) return;
|
|
1294
|
+
|
|
1295
|
+
// Determine session file path
|
|
1296
|
+
let sessionId: string | undefined;
|
|
1297
|
+
|
|
1298
|
+
if (config.primary) {
|
|
1299
|
+
sessionId = getFixedSession(this.projectPath);
|
|
1300
|
+
console.log(`[session-monitor] ${config.label}: primary fixedSession=${sessionId || 'NONE'}`);
|
|
1301
|
+
} else {
|
|
1302
|
+
sessionId = config.boundSessionId;
|
|
1303
|
+
console.log(`[session-monitor] ${config.label}: boundSession=${sessionId || 'NONE'}`);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
if (!sessionId) {
|
|
1307
|
+
// Try to auto-bind from session files on disk
|
|
1308
|
+
sessionId = this.getLatestSessionId(config.workDir);
|
|
1309
|
+
if (sessionId && !config.primary) {
|
|
1310
|
+
config.boundSessionId = sessionId;
|
|
1311
|
+
this.saveNow();
|
|
1312
|
+
console.log(`[session-monitor] ${config.label}: auto-bound to ${sessionId}`);
|
|
1313
|
+
}
|
|
1314
|
+
if (!sessionId) {
|
|
1315
|
+
console.log(`[session-monitor] ${config.label}: no sessionId, skipping`);
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
const { SessionFileMonitor } = await import('./session-monitor');
|
|
1321
|
+
const filePath = SessionFileMonitor.resolveSessionPath(this.projectPath, config.workDir, sessionId);
|
|
1322
|
+
this.sessionMonitor.startMonitoring(agentId, filePath);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// ─── Health Check — auto-heal agents ─────────────────
|
|
1326
|
+
|
|
1327
|
+
private startHealthCheck(): void {
|
|
1328
|
+
if (this.healthCheckTimer) return;
|
|
1329
|
+
this.healthCheckTimer = setInterval(() => this.runHealthCheck(), 10_000);
|
|
1330
|
+
this.healthCheckTimer.unref();
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
private stopHealthCheck(): void {
|
|
1334
|
+
if (this.healthCheckTimer) {
|
|
1335
|
+
clearInterval(this.healthCheckTimer);
|
|
1336
|
+
this.healthCheckTimer = null;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
private runHealthCheck(): void {
|
|
1341
|
+
if (!this.daemonActive) return;
|
|
1342
|
+
|
|
1343
|
+
// Every 60s (6 ticks × 10s): reconcile agentRunningMsg cache with actual bus log
|
|
1344
|
+
this.reconcileTick++;
|
|
1345
|
+
if (this.reconcileTick >= 6) {
|
|
1346
|
+
this.reconcileTick = 0;
|
|
1347
|
+
const log = this.bus.getLog();
|
|
1348
|
+
for (const [agentId, messageId] of this.agentRunningMsg) {
|
|
1349
|
+
const msg = log.find(m => m.id === messageId);
|
|
1350
|
+
if (!msg || msg.status !== 'running') {
|
|
1351
|
+
console.log(`[health] reconcile: clearing stale agentRunningMsg for ${agentId} (msg=${messageId.slice(0, 8)}, status=${msg?.status || 'not found'})`);
|
|
1352
|
+
this.agentRunningMsg.delete(agentId);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
for (const [id, entry] of this.agents) {
|
|
1358
|
+
if (entry.config.type === 'input') continue;
|
|
1359
|
+
|
|
1360
|
+
// Check 1: Worker should exist for all active agents
|
|
1361
|
+
if (!entry.worker) {
|
|
1362
|
+
console.log(`[health] ${entry.config.label}: no worker — recreating`);
|
|
1363
|
+
this.enterDaemonListening(id);
|
|
1364
|
+
entry.state.smithStatus = 'active';
|
|
1365
|
+
this.emit('event', { type: 'smith_status', agentId: id, smithStatus: 'active' } as any);
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
// Check 2: SmithStatus should be active
|
|
1370
|
+
// Skip: 'starting' means ensurePersistentSession is in progress — overriding would race with it.
|
|
1371
|
+
if (entry.state.smithStatus !== 'active' && entry.state.smithStatus !== 'starting') {
|
|
1372
|
+
console.log(`[health] ${entry.config.label}: smith=${entry.state.smithStatus} — setting active`);
|
|
1373
|
+
entry.state.smithStatus = 'active';
|
|
1374
|
+
this.emit('event', { type: 'smith_status', agentId: id, smithStatus: 'active' } as any);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// Check 3: Message loop should be running
|
|
1378
|
+
if (!this.messageLoopTimers.has(id)) {
|
|
1379
|
+
console.log(`[health] ${entry.config.label}: message loop stopped — restarting`);
|
|
1380
|
+
this.startMessageLoop(id);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// Check 4: Stale running messages (agent not actually running) → mark failed
|
|
1384
|
+
if (entry.state.taskStatus !== 'running') {
|
|
1385
|
+
const staleRunning = this.bus.getLog().filter(m => m.to === id && m.status === 'running' && m.type !== 'ack');
|
|
1386
|
+
for (const m of staleRunning) {
|
|
1387
|
+
const age = Date.now() - m.timestamp;
|
|
1388
|
+
if (age > 60_000) { // running for 60s+ but agent is idle = stale
|
|
1389
|
+
console.log(`[health] ${entry.config.label}: stale running message ${m.id.slice(0, 8)} (${Math.round(age/1000)}s) — marking failed`);
|
|
1390
|
+
m.status = 'failed';
|
|
1391
|
+
this.emit('event', { type: 'bus_message_status', messageId: m.id, status: 'failed' } as any);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// Check 5: Pending messages but agent idle — try wake
|
|
1397
|
+
if (entry.state.taskStatus !== 'running') {
|
|
1398
|
+
const pending = this.bus.getPendingMessagesFor(id).filter(m => m.from !== id && m.type !== 'ack');
|
|
1399
|
+
if (pending.length > 0 && entry.worker?.isListening()) {
|
|
1400
|
+
// Message loop should handle this, but if it didn't, log it
|
|
1401
|
+
const age = Date.now() - pending[0].timestamp;
|
|
1402
|
+
if (age > 30_000) { // stuck for 30+ seconds
|
|
1403
|
+
console.log(`[health] ${entry.config.label}: ${pending.length} pending msg(s) stuck for ${Math.round(age/1000)}s — message loop should pick up`);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// Check 6: persistentSession agent without tmux → auto-restart terminal
|
|
1409
|
+
// Skip if smithStatus='starting': ensurePersistentSession is already in progress.
|
|
1410
|
+
if (entry.config.persistentSession && !entry.state.tmuxSession && entry.state.smithStatus === 'active') {
|
|
1411
|
+
console.log(`[health] ${entry.config.label}: persistentSession but no tmux — restarting terminal`);
|
|
1412
|
+
this.ensurePersistentSession(id, entry.config).catch(err => {
|
|
1413
|
+
console.error(`[health] ${entry.config.label}: failed to restart terminal: ${err.message}`);
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
// ── Forge Agent: autonomous bus monitor ──
|
|
1419
|
+
this.runForgeAgentCheck();
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
// Track which messages Forge agent already acted on (avoid duplicate nudges)
|
|
1423
|
+
private forgeActedMessages = new Set<string>();
|
|
1424
|
+
private forgeAgentStartTime = 0;
|
|
1425
|
+
|
|
1426
|
+
/** Forge agent scans bus for actionable states (only recent messages) */
|
|
1427
|
+
private runForgeAgentCheck(): void {
|
|
1428
|
+
if (!this.forgeAgentStartTime) this.forgeAgentStartTime = Date.now();
|
|
1429
|
+
const log = this.bus.getLog();
|
|
1430
|
+
const now = Date.now();
|
|
1431
|
+
|
|
1432
|
+
// Pre-build reply index: "from→to" → latest non-ack message timestamp (only after daemon start)
|
|
1433
|
+
// Used for O(1) hasReply lookups instead of O(n) log.some() per message
|
|
1434
|
+
const replyIndex = new Map<string, number>();
|
|
1435
|
+
for (const r of log) {
|
|
1436
|
+
if (r.timestamp < this.forgeAgentStartTime) continue;
|
|
1437
|
+
if (r.type === 'ack') continue;
|
|
1438
|
+
const key = `${r.from}→${r.to}`;
|
|
1439
|
+
if ((replyIndex.get(key) || 0) < r.timestamp) replyIndex.set(key, r.timestamp);
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// Only scan messages from after daemon start (skip all history)
|
|
1443
|
+
for (const msg of log) {
|
|
1444
|
+
if (msg.timestamp < this.forgeAgentStartTime) continue;
|
|
1445
|
+
if (msg.type === 'ack' || msg.from === '_forge') continue;
|
|
1446
|
+
if (this.forgeActedMessages.has(msg.id)) continue;
|
|
1447
|
+
|
|
1448
|
+
// Case 1: Message done but no reply from target → ask target to send summary (once per pair)
|
|
1449
|
+
// Skip notification-only messages that don't need replies
|
|
1450
|
+
if (msg.status === 'done') {
|
|
1451
|
+
const action = msg.payload?.action;
|
|
1452
|
+
if (action === 'upstream_complete' || action === 'task_complete' || action === 'ack') { this.forgeActedMessages.add(msg.id); continue; }
|
|
1453
|
+
if (msg.from === '_system' || msg.from === '_watch') { this.forgeActedMessages.add(msg.id); continue; }
|
|
1454
|
+
const age = now - msg.timestamp;
|
|
1455
|
+
if (age < 30_000) continue;
|
|
1456
|
+
|
|
1457
|
+
// Dedup by target→sender pair (only nudge once per relationship)
|
|
1458
|
+
const nudgeKey = `nudge-${msg.to}->${msg.from}`;
|
|
1459
|
+
if (this.forgeActedMessages.has(nudgeKey)) { this.forgeActedMessages.add(msg.id); continue; }
|
|
1460
|
+
|
|
1461
|
+
const hasReply = (replyIndex.get(`${msg.to}→${msg.from}`) || 0) > msg.timestamp;
|
|
1462
|
+
if (!hasReply) {
|
|
1463
|
+
const senderLabel = this.agents.get(msg.from)?.config.label || msg.from;
|
|
1464
|
+
const targetEntry = this.agents.get(msg.to);
|
|
1465
|
+
if (targetEntry && targetEntry.state.smithStatus === 'active') {
|
|
1466
|
+
this.bus.send('_forge', msg.to, 'notify', {
|
|
1467
|
+
action: 'info_request',
|
|
1468
|
+
content: `[IMPORTANT] You finished a task requested by ${senderLabel} but did not send them the results. You MUST call the MCP tool "send_message" (NOT the forge-send skill) with to="${senderLabel}" and include a summary of what you did and the outcome. Do not do any other work until you have sent this reply.`,
|
|
1469
|
+
});
|
|
1470
|
+
this.forgeActedMessages.add(msg.id);
|
|
1471
|
+
this.forgeActedMessages.add(nudgeKey);
|
|
1472
|
+
console.log(`[forge-agent] Nudged ${targetEntry.config.label} to reply to ${senderLabel} (once)`);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// Case 2: Message running too long (>5min) → log warning
|
|
1478
|
+
if (msg.status === 'running') {
|
|
1479
|
+
const age = now - msg.timestamp;
|
|
1480
|
+
if (age > 300_000 && !this.forgeActedMessages.has(`running-${msg.id}`)) {
|
|
1481
|
+
const targetLabel = this.agents.get(msg.to)?.config.label || msg.to;
|
|
1482
|
+
console.log(`[forge-agent] Warning: ${targetLabel} has been running message ${msg.id.slice(0, 8)} for ${Math.round(age / 60000)}min`);
|
|
1483
|
+
this.emit('event', { type: 'log', agentId: msg.to, entry: { type: 'system', subtype: 'warning', content: `Message running for ${Math.round(age / 60000)}min — may be stuck`, timestamp: new Date().toISOString() } } as any);
|
|
1484
|
+
this.forgeActedMessages.add(`running-${msg.id}`);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// Case 3: Pending too long (>2min) → try to restart message loop
|
|
1489
|
+
if (msg.status === 'pending') {
|
|
1490
|
+
const age = now - msg.timestamp;
|
|
1491
|
+
if (age > 120_000 && !this.forgeActedMessages.has(`pending-${msg.id}`)) {
|
|
1492
|
+
const targetEntry = this.agents.get(msg.to);
|
|
1493
|
+
const targetLabel = targetEntry?.config.label || msg.to;
|
|
1494
|
+
|
|
1495
|
+
// If agent is active but not running a task, restart message loop
|
|
1496
|
+
if (targetEntry && targetEntry.state.smithStatus === 'active' && targetEntry.state.taskStatus !== 'running') {
|
|
1497
|
+
if (!this.messageLoopTimers.has(msg.to)) {
|
|
1498
|
+
this.startMessageLoop(msg.to);
|
|
1499
|
+
console.log(`[forge-agent] Restarted message loop for ${targetLabel} (pending ${Math.round(age / 60000)}min)`);
|
|
1500
|
+
} else {
|
|
1501
|
+
console.log(`[forge-agent] ${targetLabel} has pending message ${msg.id.slice(0, 8)} for ${Math.round(age / 60000)}min — loop running but not consuming`);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
this.emit('event', { type: 'log', agentId: msg.to, entry: { type: 'system', subtype: 'warning', content: `Pending message from ${this.agents.get(msg.from)?.config.label || msg.from} waiting for ${Math.round(age / 60000)}min`, timestamp: new Date().toISOString() } } as any);
|
|
1506
|
+
this.forgeActedMessages.add(`pending-${msg.id}`);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
// Case 4: Failed → notify sender (once per sender→target pair)
|
|
1511
|
+
if (msg.status === 'failed') {
|
|
1512
|
+
const failKey = `failed-${msg.from}->${msg.to}`;
|
|
1513
|
+
if (!this.forgeActedMessages.has(failKey)) {
|
|
1514
|
+
const senderEntry = this.agents.get(msg.from);
|
|
1515
|
+
const targetLabel = this.agents.get(msg.to)?.config.label || msg.to;
|
|
1516
|
+
if (senderEntry && msg.from !== '_forge' && msg.from !== '_system') {
|
|
1517
|
+
this.bus.send('_forge', msg.from, 'notify', {
|
|
1518
|
+
action: 'update_notify',
|
|
1519
|
+
content: `Your message to ${targetLabel} has failed. You may want to retry or take a different approach.`,
|
|
1520
|
+
});
|
|
1521
|
+
console.log(`[forge-agent] Notified ${senderEntry.config.label} that message to ${targetLabel} failed (once)`);
|
|
1522
|
+
}
|
|
1523
|
+
this.forgeActedMessages.add(failKey);
|
|
1524
|
+
}
|
|
1525
|
+
this.forgeActedMessages.add(`failed-${msg.id}`);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
// Case 5: Pending approval too long (>5min) → log reminder
|
|
1529
|
+
if (msg.status === 'pending_approval') {
|
|
1530
|
+
const age = now - msg.timestamp;
|
|
1531
|
+
if (age > 300_000 && !this.forgeActedMessages.has(`approval-${msg.id}`)) {
|
|
1532
|
+
const targetLabel = this.agents.get(msg.to)?.config.label || msg.to;
|
|
1533
|
+
this.emit('event', { type: 'log', agentId: msg.to, entry: { type: 'system', subtype: 'warning', content: `Message awaiting approval for ${Math.round(age / 60000)}min — requires manual action`, timestamp: new Date().toISOString() } } as any);
|
|
1534
|
+
this.forgeActedMessages.add(`approval-${msg.id}`);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
/** Handle watch alert based on agent's configured action */
|
|
1541
|
+
private handleWatchAlert(agentId: string, summary: string): void {
|
|
1542
|
+
const entry = this.agents.get(agentId);
|
|
1543
|
+
if (!entry) return;
|
|
1544
|
+
const action = entry.config.watch?.action || 'log';
|
|
1545
|
+
|
|
1546
|
+
if (action === 'log') {
|
|
1547
|
+
// Already logged by watch-manager, nothing more to do
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
if (action === 'analyze') {
|
|
1552
|
+
// Auto-wake agent to analyze changes (skip if busy/manual)
|
|
1553
|
+
if (entry.state.taskStatus === 'running') {
|
|
1554
|
+
console.log(`[watch] ${entry.config.label}: skipped analyze (task=${entry.state.taskStatus})`);
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
if (!entry.worker?.isListening()) {
|
|
1558
|
+
console.log(`[watch] ${entry.config.label}: skipped analyze (worker=${!!entry.worker} listening=${entry.worker?.isListening()})`);
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
console.log(`[watch] ${entry.config.label}: triggering analyze`);
|
|
1562
|
+
|
|
1563
|
+
const prompt = entry.config.watch?.prompt || 'Analyze the following changes and produce a report:';
|
|
1564
|
+
const logEntry = {
|
|
1565
|
+
type: 'system' as const,
|
|
1566
|
+
subtype: 'watch_trigger',
|
|
1567
|
+
content: `[Watch] ${prompt}\n\n${summary}`,
|
|
1568
|
+
timestamp: new Date().toISOString(),
|
|
1569
|
+
};
|
|
1570
|
+
entry.worker.wake({ type: 'bus_message', messages: [logEntry] });
|
|
1571
|
+
console.log(`[watch] ${entry.config.label}: auto-analyzing detected changes`);
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
if (action === 'approve') {
|
|
1576
|
+
// Create message with pending_approval status — user must approve to execute
|
|
1577
|
+
const msg = this.bus.send('_watch', agentId, 'notify', {
|
|
1578
|
+
action: 'watch_changes',
|
|
1579
|
+
content: `Watch detected changes (awaiting approval):\n${summary}`,
|
|
1580
|
+
});
|
|
1581
|
+
msg.status = 'pending_approval';
|
|
1582
|
+
this.emit('event', { type: 'bus_message_status', messageId: msg.id, status: 'pending_approval' } as any);
|
|
1583
|
+
console.log(`[watch] ${entry.config.label}: changes detected, awaiting approval`);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
if (action === 'send_message') {
|
|
1587
|
+
const targetId = entry.config.watch?.sendTo;
|
|
1588
|
+
if (!targetId) {
|
|
1589
|
+
console.log(`[watch] ${entry.config.label}: send_message but no sendTo configured`);
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
const targetEntry = this.agents.get(targetId);
|
|
1593
|
+
if (!targetEntry) {
|
|
1594
|
+
console.log(`[watch] ${entry.config.label}: sendTo agent ${targetId} not found`);
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
const prompt = entry.config.watch?.prompt;
|
|
1599
|
+
// For terminal injection: send the configured prompt directly (pattern is the trigger, not the payload)
|
|
1600
|
+
// If no prompt configured, send the summary
|
|
1601
|
+
const message = prompt || summary;
|
|
1602
|
+
|
|
1603
|
+
// Try to inject directly into an open terminal session
|
|
1604
|
+
// Verify stored session is alive, clear if dead
|
|
1605
|
+
if (targetEntry.state.tmuxSession) {
|
|
1606
|
+
try { execSync(`tmux has-session -t "${targetEntry.state.tmuxSession}" 2>/dev/null`, { timeout: 3000 }); }
|
|
1607
|
+
catch { targetEntry.state.tmuxSession = undefined; }
|
|
1608
|
+
}
|
|
1609
|
+
const tmuxSession = targetEntry.state.tmuxSession || this.findTmuxSession(targetEntry.config.label);
|
|
1610
|
+
if (tmuxSession) {
|
|
1611
|
+
try {
|
|
1612
|
+
const tmpFile = `/tmp/forge-watch-${Date.now()}.txt`;
|
|
1613
|
+
writeFileSync(tmpFile, message);
|
|
1614
|
+
execSync(`tmux load-buffer ${tmpFile}`, { timeout: 5000 });
|
|
1615
|
+
execSync(`tmux paste-buffer -t "${tmuxSession}" && sleep 0.2 && tmux send-keys -t "${tmuxSession}" Enter`, { timeout: 5000 });
|
|
1616
|
+
try { unlinkSync(tmpFile); } catch {}
|
|
1617
|
+
console.log(`[watch] ${entry.config.label} → ${targetEntry.config.label}: injected into terminal (${tmuxSession})`);
|
|
1618
|
+
} catch (err: any) {
|
|
1619
|
+
console.error(`[watch] Terminal inject failed: ${err.message}, falling back to bus`);
|
|
1620
|
+
this.bus.send(agentId, targetId, 'notify', { action: 'watch_alert', content: message });
|
|
1621
|
+
}
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
// No terminal open — send via bus (will start new session)
|
|
1626
|
+
const hasPendingFromWatch = this.bus.getLog().some(m =>
|
|
1627
|
+
m.from === agentId && m.to === targetId &&
|
|
1628
|
+
(m.status === 'pending' || m.status === 'running' || m.status === 'pending_approval') &&
|
|
1629
|
+
m.type !== 'ack'
|
|
1630
|
+
);
|
|
1631
|
+
if (hasPendingFromWatch) {
|
|
1632
|
+
console.log(`[watch] ${entry.config.label}: skipping bus send — target still processing`);
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
this.bus.send(agentId, targetId, 'notify', { action: 'watch_alert', content: message });
|
|
1637
|
+
console.log(`[watch] ${entry.config.label} → ${targetEntry.config.label}: sent via bus`);
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
/** Check if daemon mode is active */
|
|
1642
|
+
isDaemonActive(): boolean {
|
|
1643
|
+
return this.daemonActive;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
/** Pause a running agent */
|
|
1647
|
+
pauseAgent(agentId: string): void {
|
|
1648
|
+
const entry = this.agents.get(agentId);
|
|
1649
|
+
entry?.worker?.pause();
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
/** Resume a paused agent */
|
|
1653
|
+
resumeAgent(agentId: string): void {
|
|
1654
|
+
const entry = this.agents.get(agentId);
|
|
1655
|
+
entry?.worker?.resume();
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
/** Stop a running agent */
|
|
1659
|
+
/** Mark agent task as done (user manually confirms completion) */
|
|
1660
|
+
markAgentDone(agentId: string, notify: boolean): void {
|
|
1661
|
+
const entry = this.agents.get(agentId);
|
|
1662
|
+
if (!entry) return;
|
|
1663
|
+
|
|
1664
|
+
// Mark running inbox messages as done
|
|
1665
|
+
const runningMsgs = this.bus.getLog().filter(m => m.to === agentId && m.status === 'running' && m.type !== 'ack');
|
|
1666
|
+
for (const m of runningMsgs) {
|
|
1667
|
+
m.status = 'done' as any;
|
|
1668
|
+
this.emit('event', { type: 'bus_message_status', messageId: m.id, status: 'done' } as any);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
entry.state.taskStatus = notify ? 'done' : 'idle';
|
|
1672
|
+
entry.state.completedAt = Date.now();
|
|
1673
|
+
this.emit('event', { type: 'task_status', agentId, taskStatus: entry.state.taskStatus } as any);
|
|
1674
|
+
if (notify) {
|
|
1675
|
+
this.handleAgentDone(agentId, entry, 'Manually marked done');
|
|
1676
|
+
}
|
|
1677
|
+
this.sessionMonitor?.resetState(agentId);
|
|
1678
|
+
this.saveNow();
|
|
1679
|
+
this.emitAgentsChanged();
|
|
1680
|
+
console.log(`[workspace] ${entry.config.label}: manually marked ${notify ? 'done' : 'idle'} (${runningMsgs.length} messages completed)`);
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
/** Mark agent task as failed (user manually marks failure) */
|
|
1684
|
+
markAgentFailed(agentId: string, notify: boolean): void {
|
|
1685
|
+
const entry = this.agents.get(agentId);
|
|
1686
|
+
if (!entry) return;
|
|
1687
|
+
|
|
1688
|
+
// Mark running inbox messages as failed
|
|
1689
|
+
const runningMsgs = this.bus.getLog().filter(m => m.to === agentId && m.status === 'running' && m.type !== 'ack');
|
|
1690
|
+
for (const m of runningMsgs) {
|
|
1691
|
+
m.status = 'failed' as any;
|
|
1692
|
+
this.emit('event', { type: 'bus_message_status', messageId: m.id, status: 'failed' } as any);
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
entry.state.taskStatus = 'failed';
|
|
1696
|
+
entry.state.error = 'Manually marked as failed';
|
|
1697
|
+
this.emit('event', { type: 'task_status', agentId, taskStatus: 'failed' } as any);
|
|
1698
|
+
if (notify) {
|
|
1699
|
+
this.bus.notifyTaskComplete(agentId, [], 'Task failed');
|
|
1700
|
+
}
|
|
1701
|
+
this.sessionMonitor?.resetState(agentId);
|
|
1702
|
+
this.saveNow();
|
|
1703
|
+
this.emitAgentsChanged();
|
|
1704
|
+
console.log(`[workspace] ${entry.config.label}: manually marked failed (${runningMsgs.length} messages failed)`);
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
/** Legacy stop — for headless mode */
|
|
1708
|
+
stopAgent(agentId: string): void {
|
|
1709
|
+
const entry = this.agents.get(agentId);
|
|
1710
|
+
if (!entry) return;
|
|
1711
|
+
if (entry.config.persistentSession) {
|
|
1712
|
+
this.markAgentDone(agentId, false);
|
|
1713
|
+
} else {
|
|
1714
|
+
entry.worker?.stop();
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
/** Retry a failed agent from its last checkpoint */
|
|
1719
|
+
async retryAgent(agentId: string): Promise<void> {
|
|
1720
|
+
const entry = this.agents.get(agentId);
|
|
1721
|
+
if (!entry) throw new Error(`Agent "${agentId}" not found`);
|
|
1722
|
+
if (entry.state.taskStatus === 'running') {
|
|
1723
|
+
throw new Error(`Agent "${entry.config.label}" is already running`);
|
|
1724
|
+
}
|
|
1725
|
+
if (entry.state.taskStatus !== 'failed') {
|
|
1726
|
+
throw new Error(`Agent "${entry.config.label}" is ${entry.state.taskStatus}, not failed`);
|
|
1727
|
+
}
|
|
1728
|
+
// force=true: skip dep taskStatus check, only require upstream smith active
|
|
1729
|
+
await this.runAgent(agentId, undefined, true);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
/** Send a message to a running agent (human intervention) */
|
|
1733
|
+
/** Send a message to a smith — becomes a pending inbox message, processed by message loop */
|
|
1734
|
+
sendMessageToAgent(agentId: string, content: string): void {
|
|
1735
|
+
const entry = this.agents.get(agentId);
|
|
1736
|
+
if (!entry) return;
|
|
1737
|
+
|
|
1738
|
+
// Send via bus → becomes pending inbox message → message loop will consume it
|
|
1739
|
+
this.bus.send('user', agentId, 'notify', {
|
|
1740
|
+
action: 'user_message',
|
|
1741
|
+
content,
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
/** Approve a waiting agent to start execution */
|
|
1746
|
+
approveAgent(agentId: string): void {
|
|
1747
|
+
if (!this.approvalQueue.has(agentId)) return;
|
|
1748
|
+
this.approvalQueue.delete(agentId);
|
|
1749
|
+
this.runAgent(agentId).catch(() => {});
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
/** Save tmux session name for an agent (for reattach after refresh) */
|
|
1753
|
+
setTmuxSession(agentId: string, sessionName: string): void {
|
|
1754
|
+
const entry = this.agents.get(agentId);
|
|
1755
|
+
if (!entry) return;
|
|
1756
|
+
entry.state.tmuxSession = sessionName;
|
|
1757
|
+
this.saveNow();
|
|
1758
|
+
this.emitAgentsChanged();
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
clearTmuxSession(agentId: string): void {
|
|
1762
|
+
const entry = this.agents.get(agentId);
|
|
1763
|
+
if (!entry) return;
|
|
1764
|
+
entry.state.tmuxSession = undefined;
|
|
1765
|
+
// Reset session monitor warmup so it doesn't immediately trigger running on restart
|
|
1766
|
+
this.sessionMonitor?.resetState(agentId);
|
|
1767
|
+
this.saveNow();
|
|
1768
|
+
this.emitAgentsChanged();
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
/** Record that an agent has an open terminal (tmux session tracking) */
|
|
1772
|
+
setManualMode(agentId: string): void {
|
|
1773
|
+
const entry = this.agents.get(agentId);
|
|
1774
|
+
if (!entry) return;
|
|
1775
|
+
// tmuxSession is set separately when terminal opens
|
|
1776
|
+
this.emitAgentsChanged();
|
|
1777
|
+
this.saveNow();
|
|
1778
|
+
console.log(`[workspace] Agent "${entry.config.label}" terminal opened`);
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
/** Called when agent's terminal is closed */
|
|
1782
|
+
restartAgentDaemon(agentId: string): void {
|
|
1783
|
+
if (!this.daemonActive) return;
|
|
1784
|
+
const entry = this.agents.get(agentId);
|
|
1785
|
+
if (!entry || entry.config.type === 'input') return;
|
|
1786
|
+
|
|
1787
|
+
entry.state.error = undefined;
|
|
1788
|
+
// Don't clear tmuxSession here — it may still be alive (persistent session)
|
|
1789
|
+
// Terminal close just means the UI panel is closed, not necessarily tmux killed
|
|
1790
|
+
|
|
1791
|
+
// Recreate worker if needed
|
|
1792
|
+
if (!entry.worker) {
|
|
1793
|
+
this.enterDaemonListening(agentId);
|
|
1794
|
+
this.startMessageLoop(agentId);
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
entry.state.smithStatus = 'active';
|
|
1798
|
+
this.emit('event', { type: 'smith_status', agentId, smithStatus: 'active' } satisfies WorkerEvent);
|
|
1799
|
+
this.emitAgentsChanged();
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
/** Complete an agent from terminal — called by forge-done skill */
|
|
1803
|
+
completeManualAgent(agentId: string, changedFiles: string[]): void {
|
|
1804
|
+
const entry = this.agents.get(agentId);
|
|
1805
|
+
if (!entry) return;
|
|
1806
|
+
|
|
1807
|
+
entry.state.taskStatus = 'done';
|
|
1808
|
+
entry.state.completedAt = Date.now();
|
|
1809
|
+
entry.state.artifacts = changedFiles.map(f => ({ type: 'file' as const, path: f }));
|
|
1810
|
+
|
|
1811
|
+
console.log(`[workspace] Manual agent "${entry.config.label}" marked done. ${changedFiles.length} files changed.`);
|
|
1812
|
+
|
|
1813
|
+
this.emit('event', { type: 'task_status', agentId, taskStatus: 'done' } satisfies WorkerEvent);
|
|
1814
|
+
this.emit('event', { type: 'done', agentId, summary: `Manual: ${changedFiles.length} files changed` } satisfies WorkerEvent);
|
|
1815
|
+
this.emitAgentsChanged();
|
|
1816
|
+
|
|
1817
|
+
// Notify ALL agents that depend on this one (not just direct downstream)
|
|
1818
|
+
this.bus.notifyTaskComplete(agentId, changedFiles, `Manual work: ${changedFiles.length} files`);
|
|
1819
|
+
|
|
1820
|
+
// Send individual bus messages to all downstream agents so they know
|
|
1821
|
+
for (const [id, other] of this.agents) {
|
|
1822
|
+
if (id === agentId || other.config.type === 'input') continue;
|
|
1823
|
+
if (other.config.dependsOn.includes(agentId)) {
|
|
1824
|
+
this.bus.send(agentId, id, 'notify', {
|
|
1825
|
+
action: 'update_notify',
|
|
1826
|
+
content: `${entry.config.label} completed manual work: ${changedFiles.length} files changed`,
|
|
1827
|
+
files: changedFiles,
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
if (this.daemonActive) {
|
|
1833
|
+
this.broadcastCompletion(agentId);
|
|
1834
|
+
}
|
|
1835
|
+
this.notifyDownstreamForRevalidation(agentId, changedFiles);
|
|
1836
|
+
this.emitWorkspaceStatus();
|
|
1837
|
+
this.checkWorkspaceComplete();
|
|
1838
|
+
this.saveNow();
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
/** Reject an approval (set agent back to idle) */
|
|
1842
|
+
rejectApproval(agentId: string): void {
|
|
1843
|
+
this.approvalQueue.delete(agentId);
|
|
1844
|
+
const entry = this.agents.get(agentId);
|
|
1845
|
+
if (entry) {
|
|
1846
|
+
entry.state.taskStatus = 'idle';
|
|
1847
|
+
this.emit('event', { type: 'task_status', agentId, taskStatus: 'idle' } satisfies WorkerEvent);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
// ─── Bus Access ────────────────────────────────────────
|
|
1852
|
+
|
|
1853
|
+
getBus(): AgentBus {
|
|
1854
|
+
return this.bus;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
getBusLog(): readonly BusMessage[] {
|
|
1858
|
+
return this.bus.getLog();
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
// ─── State Snapshot (for persistence) ──────────────────
|
|
1862
|
+
|
|
1863
|
+
/** Get full workspace state for auto-save */
|
|
1864
|
+
getFullState(): WorkspaceState {
|
|
1865
|
+
return {
|
|
1866
|
+
id: this.workspaceId,
|
|
1867
|
+
projectPath: this.projectPath,
|
|
1868
|
+
projectName: this.projectName,
|
|
1869
|
+
agents: Array.from(this.agents.values()).map(e => e.config),
|
|
1870
|
+
agentStates: this.getAllAgentStates(),
|
|
1871
|
+
nodePositions: {},
|
|
1872
|
+
busLog: [...this.bus.getLog()],
|
|
1873
|
+
busOutbox: this.bus.getAllOutbox(),
|
|
1874
|
+
createdAt: this.createdAt,
|
|
1875
|
+
updatedAt: Date.now(),
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
getSnapshot(): {
|
|
1880
|
+
agents: WorkspaceAgentConfig[];
|
|
1881
|
+
agentStates: Record<string, AgentState>;
|
|
1882
|
+
busLog: BusMessage[];
|
|
1883
|
+
daemonActive: boolean;
|
|
1884
|
+
} {
|
|
1885
|
+
return {
|
|
1886
|
+
agents: Array.from(this.agents.values()).map(e => e.config),
|
|
1887
|
+
agentStates: this.getAllAgentStates(),
|
|
1888
|
+
busLog: [...this.bus.getLog()],
|
|
1889
|
+
daemonActive: this.daemonActive,
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
/** Restore from persisted state */
|
|
1894
|
+
loadSnapshot(data: {
|
|
1895
|
+
agents: WorkspaceAgentConfig[];
|
|
1896
|
+
agentStates: Record<string, AgentState>;
|
|
1897
|
+
busLog: BusMessage[];
|
|
1898
|
+
busOutbox?: Record<string, BusMessage[]>;
|
|
1899
|
+
}): void {
|
|
1900
|
+
this.agents.clear();
|
|
1901
|
+
this.daemonActive = false; // Reset daemon — user must click Start Daemon again after restart
|
|
1902
|
+
for (const config of data.agents) {
|
|
1903
|
+
const state = data.agentStates[config.id] || { smithStatus: 'down' as const, taskStatus: 'idle' as const, history: [], artifacts: [] };
|
|
1904
|
+
|
|
1905
|
+
// Migrate old format if loading from pre-two-layer state
|
|
1906
|
+
if ('status' in state && !('smithStatus' in state)) {
|
|
1907
|
+
const oldStatus = (state as any).status;
|
|
1908
|
+
(state as any).smithStatus = 'down';
|
|
1909
|
+
(state as any).taskStatus = (oldStatus === 'running' || oldStatus === 'listening') ? 'idle' :
|
|
1910
|
+
(oldStatus === 'interrupted') ? 'idle' :
|
|
1911
|
+
(oldStatus === 'waiting_approval') ? 'idle' :
|
|
1912
|
+
(oldStatus === 'paused') ? 'idle' :
|
|
1913
|
+
oldStatus;
|
|
1914
|
+
delete (state as any).status;
|
|
1915
|
+
delete (state as any).runMode;
|
|
1916
|
+
delete (state as any).daemonMode;
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
// Mark running agents as failed (interrupted by restart)
|
|
1920
|
+
if (state.taskStatus === 'running') {
|
|
1921
|
+
state.taskStatus = 'failed';
|
|
1922
|
+
state.error = 'Interrupted by restart';
|
|
1923
|
+
}
|
|
1924
|
+
// Smith is down after restart (no daemon loop running)
|
|
1925
|
+
state.smithStatus = 'down';
|
|
1926
|
+
state.daemonIteration = undefined;
|
|
1927
|
+
this.agents.set(config.id, { config, worker: null, state });
|
|
1928
|
+
}
|
|
1929
|
+
this.bus.loadLog(data.busLog);
|
|
1930
|
+
if (data.busOutbox) {
|
|
1931
|
+
this.bus.loadOutbox(data.busOutbox);
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
// Mark all pending messages as failed (they were lost on shutdown)
|
|
1935
|
+
// Users can retry agents manually if needed
|
|
1936
|
+
// Running messages from before crash → failed (pending stays pending for retry)
|
|
1937
|
+
this.bus.markAllRunningAsFailed();
|
|
1938
|
+
|
|
1939
|
+
// Initialize liveness for all loaded agents so bus delivery works
|
|
1940
|
+
for (const [agentId] of this.agents) {
|
|
1941
|
+
this.updateAgentLiveness(agentId);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
/** Stop all agents, save final state, and clean up */
|
|
1946
|
+
shutdown(): void {
|
|
1947
|
+
this.daemonActive = false;
|
|
1948
|
+
this.stopHealthCheck();
|
|
1949
|
+
this.stopAllMessageLoops();
|
|
1950
|
+
stopAutoSave(this.workspaceId);
|
|
1951
|
+
// Sync save — must complete before process exits
|
|
1952
|
+
try { saveWorkspaceSync(this.getFullState()); } catch (err) {
|
|
1953
|
+
console.error(`[workspace] Failed to save on shutdown:`, err);
|
|
1954
|
+
}
|
|
1955
|
+
for (const [, entry] of this.agents) {
|
|
1956
|
+
entry.worker?.stop();
|
|
1957
|
+
}
|
|
1958
|
+
this.bus.clear();
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// ─── Private ───────────────────────────────────────────
|
|
1962
|
+
|
|
1963
|
+
/** Rebuild workspace topology cache — called on agent changes and daemon start */
|
|
1964
|
+
private rebuildTopo(): void {
|
|
1965
|
+
// Topological sort
|
|
1966
|
+
const sorted: WorkspaceAgentConfig[] = [];
|
|
1967
|
+
const visited = new Set<string>();
|
|
1968
|
+
const entries = Array.from(this.agents.values()).filter(e => e.config.type !== 'input');
|
|
1969
|
+
const configMap = new Map(entries.map(e => [e.config.id, e.config]));
|
|
1970
|
+
|
|
1971
|
+
const visit = (id: string) => {
|
|
1972
|
+
if (visited.has(id)) return;
|
|
1973
|
+
visited.add(id);
|
|
1974
|
+
const c = configMap.get(id);
|
|
1975
|
+
if (!c) return;
|
|
1976
|
+
for (const dep of c.dependsOn) visit(dep);
|
|
1977
|
+
sorted.push(c);
|
|
1978
|
+
};
|
|
1979
|
+
for (const e of entries) visit(e.config.id);
|
|
1980
|
+
|
|
1981
|
+
const agents: TopoAgent[] = sorted.map(c => {
|
|
1982
|
+
const state = this.agents.get(c.id)?.state;
|
|
1983
|
+
return {
|
|
1984
|
+
id: c.id,
|
|
1985
|
+
label: c.label,
|
|
1986
|
+
icon: c.icon,
|
|
1987
|
+
role: c.role || '',
|
|
1988
|
+
roleSummary: (c.role || '').split('\n')[0].slice(0, 150),
|
|
1989
|
+
primary: !!c.primary,
|
|
1990
|
+
dependsOn: c.dependsOn.map(d => this.agents.get(d)?.config.label || d),
|
|
1991
|
+
dependsOnIds: [...c.dependsOn],
|
|
1992
|
+
workDir: c.workDir || './',
|
|
1993
|
+
outputs: c.outputs || [],
|
|
1994
|
+
steps: (c.steps || []).map(s => s.label),
|
|
1995
|
+
smithStatus: state?.smithStatus || 'down',
|
|
1996
|
+
taskStatus: state?.taskStatus || 'idle',
|
|
1997
|
+
};
|
|
1998
|
+
});
|
|
1999
|
+
|
|
2000
|
+
this._topoCache = {
|
|
2001
|
+
agents,
|
|
2002
|
+
flow: sorted.map(c => c.label).join(' → '),
|
|
2003
|
+
updatedAt: Date.now(),
|
|
2004
|
+
};
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
/** Get cached workspace topology (always fresh — rebuilt on every agent change) */
|
|
2008
|
+
getWorkspaceTopo(): WorkspaceTopo {
|
|
2009
|
+
if (!this._topoCache) this.rebuildTopo();
|
|
2010
|
+
return this._topoCache!;
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
/** Remove stale Forge blocks from all agents' CLAUDE.md files (migration cleanup).
|
|
2014
|
+
* Role is no longer written to CLAUDE.md — this just removes legacy artifacts. */
|
|
2015
|
+
private cleanLegacyClaudeMdBlocks(): void {
|
|
2016
|
+
const roleRegex = /<!-- forge:agent-role -->[\s\S]*?<!-- \/forge:agent-role -->\n?/;
|
|
2017
|
+
const topoRegex = /<!-- forge:workspace-topo -->[\s\S]*?<!-- \/forge:workspace-topo -->\n?/;
|
|
2018
|
+
|
|
2019
|
+
for (const [, entry] of this.agents) {
|
|
2020
|
+
if (entry.config.type === 'input') continue;
|
|
2021
|
+
const workDir = join(this.projectPath, entry.config.workDir || '');
|
|
2022
|
+
const claudeMdPath = join(workDir, 'CLAUDE.md');
|
|
2023
|
+
if (!existsSync(claudeMdPath)) continue;
|
|
2024
|
+
|
|
2025
|
+
try {
|
|
2026
|
+
let content = readFileSync(claudeMdPath, 'utf-8');
|
|
2027
|
+
const before = content;
|
|
2028
|
+
if (roleRegex.test(content)) content = content.replace(roleRegex, '');
|
|
2029
|
+
if (topoRegex.test(content)) content = content.replace(topoRegex, '');
|
|
2030
|
+
if (content !== before) {
|
|
2031
|
+
content = content.trimEnd() + (content.trim() ? '\n' : '');
|
|
2032
|
+
if (content.trim()) writeFileSync(claudeMdPath, content);
|
|
2033
|
+
else unlinkSync(claudeMdPath); // was only forge content, delete empty file
|
|
2034
|
+
console.log(`[workspace] Cleaned legacy Forge blocks from ${claudeMdPath}`);
|
|
2035
|
+
}
|
|
2036
|
+
} catch (err: any) {
|
|
2037
|
+
console.warn(`[workspace] Failed to clean CLAUDE.md at ${claudeMdPath}: ${err.message}`);
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
private createBackend(config: WorkspaceAgentConfig, agentId?: string) {
|
|
2043
|
+
switch (config.backend) {
|
|
2044
|
+
case 'api':
|
|
2045
|
+
// TODO: ApiBackend uses @/src path aliases that don't work in standalone tsx.
|
|
2046
|
+
// Need to refactor api-backend imports before enabling.
|
|
2047
|
+
throw new Error('API backend not yet supported in workspace daemon. Use CLI backend instead.');
|
|
2048
|
+
case 'cli':
|
|
2049
|
+
default: {
|
|
2050
|
+
// Resume existing claude session if available
|
|
2051
|
+
const existingSessionId = agentId ? this.agents.get(agentId)?.state.cliSessionId : undefined;
|
|
2052
|
+
const backend = new CliBackend(existingSessionId);
|
|
2053
|
+
// Persist new sessionId back to agent state
|
|
2054
|
+
if (agentId) {
|
|
2055
|
+
backend.onSessionId = (id) => {
|
|
2056
|
+
const entry = this.agents.get(agentId);
|
|
2057
|
+
if (entry) entry.state.cliSessionId = id;
|
|
2058
|
+
};
|
|
2059
|
+
}
|
|
2060
|
+
return backend;
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
/** Build a compact topo summary injected into every agent task */
|
|
2066
|
+
private buildTopoSummary(selfId: string): string {
|
|
2067
|
+
const topo = this.getWorkspaceTopo();
|
|
2068
|
+
if (topo.agents.length === 0) return '';
|
|
2069
|
+
|
|
2070
|
+
const lines: string[] = ['## Workspace Team'];
|
|
2071
|
+
lines.push(`Flow: ${topo.flow}`);
|
|
2072
|
+
|
|
2073
|
+
// Missing roles hint
|
|
2074
|
+
const labels = new Set(topo.agents.map(a => a.label.toLowerCase()));
|
|
2075
|
+
const standard = ['architect', 'engineer', 'qa', 'reviewer', 'pm', 'lead'];
|
|
2076
|
+
const missing = standard.filter(r => !labels.has(r));
|
|
2077
|
+
if (missing.length > 0) lines.push(`Missing: ${missing.join(', ')}`);
|
|
2078
|
+
|
|
2079
|
+
for (const a of topo.agents) {
|
|
2080
|
+
const me = a.id === selfId ? ' ← you' : '';
|
|
2081
|
+
const status = `${a.smithStatus}/${a.taskStatus}`;
|
|
2082
|
+
lines.push(`- ${a.icon} ${a.label}${me} [${status}]: ${a.roleSummary}`);
|
|
2083
|
+
}
|
|
2084
|
+
return lines.join('\n');
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
/** Build full role preamble — injected once when persistent session starts */
|
|
2088
|
+
private buildRolePreamble(config: WorkspaceAgentConfig): string {
|
|
2089
|
+
const roleText = (config.role || '').trim() || '(no role defined)';
|
|
2090
|
+
return `=== SMITH CONTEXT (managed by Forge) ===
|
|
2091
|
+
You are ${config.label}. Work dir: ${config.workDir || './'}.
|
|
2092
|
+
|
|
2093
|
+
Your role and responsibilities:
|
|
2094
|
+
${roleText}
|
|
2095
|
+
|
|
2096
|
+
---
|
|
2097
|
+
Silently ingest this context. Do NOT respond — await an actual task.`;
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
/** Build short role reminder — injected periodically to combat auto-compaction */
|
|
2101
|
+
private buildRoleReminder(config: WorkspaceAgentConfig): string {
|
|
2102
|
+
const roleText = (config.role || '').trim();
|
|
2103
|
+
// First 300 chars of role, plus label
|
|
2104
|
+
const summary = roleText.length > 300 ? roleText.slice(0, 300) + '...' : roleText;
|
|
2105
|
+
return `[Role reminder — you are ${config.label}]\n${summary}\n---`;
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
/** Check if a role reminder should be re-injected before next bus message */
|
|
2109
|
+
private needsRoleReminder(agentId: string): boolean {
|
|
2110
|
+
const st = this.roleInjectState.get(agentId);
|
|
2111
|
+
if (!st) return false; // no preamble sent yet — session is starting
|
|
2112
|
+
const now = Date.now();
|
|
2113
|
+
const REMIND_EVERY_MSGS = 10;
|
|
2114
|
+
const REMIND_EVERY_MS = 30 * 60 * 1000; // 30 min
|
|
2115
|
+
if (st.msgsSinceInject >= REMIND_EVERY_MSGS) return true;
|
|
2116
|
+
if (now - st.lastInjectAt >= REMIND_EVERY_MS) return true;
|
|
2117
|
+
return false;
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
/** Mark that role was just injected for this agent */
|
|
2121
|
+
private markRoleInjected(agentId: string): void {
|
|
2122
|
+
this.roleInjectState.set(agentId, { lastInjectAt: Date.now(), msgsSinceInject: 0 });
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
/** Increment message counter since last role injection */
|
|
2126
|
+
private incrementMsgCount(agentId: string): void {
|
|
2127
|
+
const st = this.roleInjectState.get(agentId);
|
|
2128
|
+
if (st) st.msgsSinceInject++;
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
/** Build context string from upstream agents' outputs */
|
|
2132
|
+
private buildUpstreamContext(config: WorkspaceAgentConfig): string | undefined {
|
|
2133
|
+
// Always prepend: role + workspace team summary
|
|
2134
|
+
const roleBlock = config.role?.trim() ? `## Your Role (${config.label})\n${config.role.trim()}` : '';
|
|
2135
|
+
const topoSummary = this.buildTopoSummary(config.id);
|
|
2136
|
+
|
|
2137
|
+
const headerSections: string[] = [];
|
|
2138
|
+
if (roleBlock) headerSections.push(roleBlock);
|
|
2139
|
+
if (topoSummary) headerSections.push(topoSummary);
|
|
2140
|
+
const header = headerSections.join('\n\n');
|
|
2141
|
+
|
|
2142
|
+
if (config.dependsOn.length === 0) {
|
|
2143
|
+
return header || undefined;
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
const sections: string[] = [];
|
|
2147
|
+
if (header) sections.push(header);
|
|
2148
|
+
|
|
2149
|
+
for (const depId of config.dependsOn) {
|
|
2150
|
+
const dep = this.agents.get(depId);
|
|
2151
|
+
if (!dep || (dep.state.taskStatus !== 'done')) continue;
|
|
2152
|
+
|
|
2153
|
+
const label = dep.config.label;
|
|
2154
|
+
|
|
2155
|
+
// Input nodes: only send latest entry (not full history)
|
|
2156
|
+
if (dep.config.type === 'input') {
|
|
2157
|
+
const entries = dep.config.entries;
|
|
2158
|
+
if (entries && entries.length > 0) {
|
|
2159
|
+
const latest = entries[entries.length - 1];
|
|
2160
|
+
sections.push(`### ${label} (latest input):\n${latest.content}`);
|
|
2161
|
+
} else if (dep.config.content) {
|
|
2162
|
+
// Legacy fallback
|
|
2163
|
+
sections.push(`### ${label}:\n${dep.config.content}`);
|
|
2164
|
+
}
|
|
2165
|
+
continue;
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
const artifacts = dep.state.artifacts.filter(a => a.path);
|
|
2169
|
+
|
|
2170
|
+
if (artifacts.length === 0) {
|
|
2171
|
+
const lastResult = [...dep.state.history].reverse().find(h => h.type === 'result');
|
|
2172
|
+
if (lastResult) {
|
|
2173
|
+
sections.push(`### From ${label}:\n${lastResult.content}`);
|
|
2174
|
+
}
|
|
2175
|
+
continue;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
// Read file artifacts
|
|
2179
|
+
for (const artifact of artifacts) {
|
|
2180
|
+
if (!artifact.path) continue;
|
|
2181
|
+
const fullPath = resolve(this.projectPath, artifact.path);
|
|
2182
|
+
try {
|
|
2183
|
+
if (existsSync(fullPath)) {
|
|
2184
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
2185
|
+
const truncated = content.length > 10000
|
|
2186
|
+
? content.slice(0, 10000) + '\n... (truncated)'
|
|
2187
|
+
: content;
|
|
2188
|
+
sections.push(`### From ${label} — ${artifact.path}:\n${truncated}`);
|
|
2189
|
+
}
|
|
2190
|
+
} catch {
|
|
2191
|
+
sections.push(`### From ${label} — ${artifact.path}: (could not read file)`);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
if (sections.length === 0) return undefined;
|
|
2197
|
+
|
|
2198
|
+
let combined = sections.join('\n\n---\n\n');
|
|
2199
|
+
|
|
2200
|
+
// Cap total upstream context to ~50K chars (~12K tokens) to prevent token explosion
|
|
2201
|
+
const MAX_UPSTREAM_CHARS = 50000;
|
|
2202
|
+
if (combined.length > MAX_UPSTREAM_CHARS) {
|
|
2203
|
+
combined = combined.slice(0, MAX_UPSTREAM_CHARS) + '\n\n... (upstream context truncated, ' + combined.length + ' chars total)';
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
return combined;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
/** After an agent completes, check if any downstream agents should be triggered */
|
|
2210
|
+
/**
|
|
2211
|
+
* Broadcast completion to all downstream agents via bus messages.
|
|
2212
|
+
* Replaces direct triggerDownstream — all execution is now message-driven.
|
|
2213
|
+
* If no artifacts/changes, no message is sent → downstream stays idle.
|
|
2214
|
+
*/
|
|
2215
|
+
/** Build causedBy from the message currently being processed */
|
|
2216
|
+
private buildCausedBy(agentId: string, entry: { worker: AgentWorker | null }): BusMessage['causedBy'] | undefined {
|
|
2217
|
+
const msgId = entry.worker?.getCurrentMessageId?.();
|
|
2218
|
+
if (!msgId) return undefined;
|
|
2219
|
+
const msg = this.bus.getLog().find(m => m.id === msgId);
|
|
2220
|
+
if (!msg) return undefined;
|
|
2221
|
+
return { messageId: msg.id, from: msg.from, to: msg.to };
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
/** Unified done handler: broadcast downstream or reply to sender based on message source */
|
|
2225
|
+
private handleAgentDone(agentId: string, entry: { config: WorkspaceAgentConfig; worker: AgentWorker | null; state: AgentState }, summary?: string): void {
|
|
2226
|
+
this.agentRunningMsg.delete(agentId);
|
|
2227
|
+
const files = entry.state.artifacts.filter(a => a.path).map(a => a.path!);
|
|
2228
|
+
console.log(`[workspace] Agent "${entry.config.label}" (${agentId}) completed. Artifacts: ${files.length}.`);
|
|
2229
|
+
|
|
2230
|
+
this.bus.notifyTaskComplete(agentId, files, summary);
|
|
2231
|
+
|
|
2232
|
+
// Check what message triggered this execution
|
|
2233
|
+
const causedBy = this.buildCausedBy(agentId, entry);
|
|
2234
|
+
const processedMsg = causedBy ? this.bus.getLog().find(m => m.id === causedBy.messageId) : null;
|
|
2235
|
+
|
|
2236
|
+
this.broadcastCompletion(agentId, causedBy);
|
|
2237
|
+
// Note: Forge agent (runForgeAgentCheck) monitors for missing replies
|
|
2238
|
+
// and nudges agents to send summaries. No action needed here.
|
|
2239
|
+
|
|
2240
|
+
this.emitWorkspaceStatus();
|
|
2241
|
+
this.checkWorkspaceComplete?.();
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
private broadcastCompletion(completedAgentId: string, causedBy?: BusMessage['causedBy']): void {
|
|
2245
|
+
const completed = this.agents.get(completedAgentId);
|
|
2246
|
+
if (!completed) return;
|
|
2247
|
+
|
|
2248
|
+
const completedLabel = completed.config.label;
|
|
2249
|
+
const files = completed.state.artifacts.filter(a => a.path).map(a => a.path!);
|
|
2250
|
+
const summary = completed.state.history
|
|
2251
|
+
.filter(h => h.subtype === 'final_summary' || h.subtype === 'step_summary')
|
|
2252
|
+
.slice(-1)[0]?.content || '';
|
|
2253
|
+
|
|
2254
|
+
// Keep notification concise — agent can read files/git diff for details
|
|
2255
|
+
const shortSummary = summary.split('\n')[0]?.slice(0, 100) || '';
|
|
2256
|
+
const content = files.length > 0
|
|
2257
|
+
? `${completedLabel} completed: ${files.length} files changed.${shortSummary ? ' ' + shortSummary : ''} Run \`git diff --stat HEAD~1\` for details.`
|
|
2258
|
+
: `${completedLabel} completed.${shortSummary ? ' ' + shortSummary : ''}`;
|
|
2259
|
+
|
|
2260
|
+
// Find all downstream agents — skip if already sent upstream_complete recently (60s)
|
|
2261
|
+
const now = Date.now();
|
|
2262
|
+
|
|
2263
|
+
// Pre-scan log once: build dedup set and pending-to-complete list
|
|
2264
|
+
const recentSentTo = new Set<string>(); // agentIds that received upstream_complete within 60s
|
|
2265
|
+
const pendingToComplete: { m: any; to: string }[] = [];
|
|
2266
|
+
for (const m of this.bus.getLog()) {
|
|
2267
|
+
if (m.from !== completedAgentId || m.payload?.action !== 'upstream_complete') continue;
|
|
2268
|
+
if (now - m.timestamp < 60_000) recentSentTo.add(m.to);
|
|
2269
|
+
if (m.status === 'pending') pendingToComplete.push({ m, to: m.to });
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
let sent = 0;
|
|
2273
|
+
for (const [id, entry] of this.agents) {
|
|
2274
|
+
if (id === completedAgentId) continue;
|
|
2275
|
+
if (entry.config.type === 'input') continue;
|
|
2276
|
+
if (!entry.config.dependsOn.includes(completedAgentId)) continue;
|
|
2277
|
+
|
|
2278
|
+
// Dedup: skip if upstream_complete was sent to this target within last 60s
|
|
2279
|
+
if (recentSentTo.has(id)) {
|
|
2280
|
+
console.log(`[bus] ${completedLabel} → ${entry.config.label}: upstream_complete skipped (sent <60s ago)`);
|
|
2281
|
+
continue;
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
// Merge: auto-complete older pending upstream_complete from same sender to this target
|
|
2285
|
+
for (const { m, to } of pendingToComplete) {
|
|
2286
|
+
if (to === id) {
|
|
2287
|
+
m.status = 'done' as any;
|
|
2288
|
+
this.emit('event', { type: 'bus_message_status', messageId: m.id, status: 'done' } as any);
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
this.bus.send(completedAgentId, id, 'notify', {
|
|
2293
|
+
action: 'upstream_complete',
|
|
2294
|
+
content,
|
|
2295
|
+
files,
|
|
2296
|
+
}, { category: 'notification', causedBy });
|
|
2297
|
+
sent++;
|
|
2298
|
+
console.log(`[bus] ${completedLabel} → ${entry.config.label}: upstream_complete (${files.length} files)`);
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
if (sent === 0) {
|
|
2302
|
+
console.log(`[bus] ${completedLabel} completed — no downstream agents`);
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
// ─── Agent liveness ─────────────────────────────────────
|
|
2307
|
+
|
|
2308
|
+
/** Find an active tmux session for an agent by checking naming conventions */
|
|
2309
|
+
// ─── Persistent Terminal Sessions ────────────────────────
|
|
2310
|
+
|
|
2311
|
+
/** Resolve the CLI session directory for a given project path */
|
|
2312
|
+
private getCliSessionDir(workDir?: string): string {
|
|
2313
|
+
const projectPath = workDir && workDir !== './' && workDir !== '.'
|
|
2314
|
+
? join(this.projectPath, workDir) : this.projectPath;
|
|
2315
|
+
// Claude Code encodes paths by replacing all non-alphanumeric chars with '-'
|
|
2316
|
+
const encoded = resolve(projectPath).replace(/[^a-zA-Z0-9]/g, '-');
|
|
2317
|
+
return join(homedir(), '.claude', 'projects', encoded);
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
/** Return the latest session ID (by mtime) from the CLI session dir, or undefined if none. */
|
|
2321
|
+
private getLatestSessionId(workDir?: string): string | undefined {
|
|
2322
|
+
try {
|
|
2323
|
+
const sessionDir = this.getCliSessionDir(workDir);
|
|
2324
|
+
if (!existsSync(sessionDir)) return undefined;
|
|
2325
|
+
const files = readdirSync(sessionDir).filter(f => f.endsWith('.jsonl'));
|
|
2326
|
+
if (files.length === 0) return undefined;
|
|
2327
|
+
const latest = files
|
|
2328
|
+
.map(f => ({ name: f, mtime: statSync(join(sessionDir, f)).mtimeMs }))
|
|
2329
|
+
.sort((a, b) => b.mtime - a.mtime)[0];
|
|
2330
|
+
return latest.name.replace('.jsonl', '');
|
|
2331
|
+
} catch {
|
|
2332
|
+
return undefined;
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
/** Lightweight session bind for non-persistentSession agents.
|
|
2337
|
+
* Checks if the tmux session already exists and sets entry.state.tmuxSession.
|
|
2338
|
+
* Also auto-binds boundSessionId from the latest session file if not already set.
|
|
2339
|
+
* Does NOT create any session or run any launch script.
|
|
2340
|
+
*/
|
|
2341
|
+
private tryBindExistingSession(agentId: string, config: WorkspaceAgentConfig): void {
|
|
2342
|
+
const entry = this.agents.get(agentId);
|
|
2343
|
+
if (!entry) return;
|
|
2344
|
+
|
|
2345
|
+
const safeName = (s: string) => s.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').slice(0, 20);
|
|
2346
|
+
const sessionName = `mw-forge-${safeName(this.projectName)}-${safeName(config.label)}`;
|
|
2347
|
+
|
|
2348
|
+
// Check if tmux session is alive → set tmuxSession so open_terminal can return it
|
|
2349
|
+
try {
|
|
2350
|
+
execSync(`tmux has-session -t "${sessionName}" 2>/dev/null`, { timeout: 3000 });
|
|
2351
|
+
if (entry.state.tmuxSession !== sessionName) {
|
|
2352
|
+
entry.state.tmuxSession = sessionName;
|
|
2353
|
+
this.saveNow();
|
|
2354
|
+
console.log(`[daemon] ${config.label}: found existing tmux session, bound tmuxSession`);
|
|
2355
|
+
}
|
|
2356
|
+
} catch {
|
|
2357
|
+
// No existing session — leave tmuxSession undefined, open_terminal will let FloatingTerminal create one
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
// Auto-bind boundSessionId from latest session file if not already set
|
|
2361
|
+
if (!config.boundSessionId) {
|
|
2362
|
+
const sessionId = this.getLatestSessionId(config.workDir);
|
|
2363
|
+
if (sessionId) {
|
|
2364
|
+
config.boundSessionId = sessionId;
|
|
2365
|
+
this.saveNow();
|
|
2366
|
+
console.log(`[daemon] ${config.label}: auto-bound boundSessionId=${sessionId}`);
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
/**
|
|
2372
|
+
* Create or attach to the tmux session for an agent (terminal-open path).
|
|
2373
|
+
* Unlike ensurePersistentSession, skips the 3s startup verification so the
|
|
2374
|
+
* HTTP response is fast. Returns the session name, or null on failure.
|
|
2375
|
+
*/
|
|
2376
|
+
async openTerminalSession(agentId: string, forceRestart = false): Promise<string | null> {
|
|
2377
|
+
const entry = this.agents.get(agentId);
|
|
2378
|
+
if (!entry || entry.config.type === 'input') return null;
|
|
2379
|
+
const config = entry.config;
|
|
2380
|
+
|
|
2381
|
+
const safeName = (s: string) => s.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').slice(0, 20);
|
|
2382
|
+
const sessionName = `mw-forge-${safeName(this.projectName)}-${safeName(config.label)}`;
|
|
2383
|
+
|
|
2384
|
+
if (forceRestart) {
|
|
2385
|
+
// Kill existing tmux session so ensurePersistentSession rewrites the launch script
|
|
2386
|
+
// with the current boundSessionId (--resume flag). Safe — claude session data is in jsonl files.
|
|
2387
|
+
try { execSync(`tmux kill-session -t "${sessionName}" 2>/dev/null`, { timeout: 3000 }); } catch {}
|
|
2388
|
+
entry.state.tmuxSession = undefined;
|
|
2389
|
+
} else if (entry.state.tmuxSession) {
|
|
2390
|
+
// Attach to existing session if still alive
|
|
2391
|
+
try {
|
|
2392
|
+
execSync(`tmux has-session -t "${entry.state.tmuxSession}" 2>/dev/null`, { timeout: 3000 });
|
|
2393
|
+
return entry.state.tmuxSession;
|
|
2394
|
+
} catch {
|
|
2395
|
+
entry.state.tmuxSession = undefined;
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
// Create (or recreate) session without startup verification delay
|
|
2400
|
+
await this.ensurePersistentSession(agentId, config, true);
|
|
2401
|
+
return entry.state.tmuxSession || null;
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
/** Create a persistent tmux session with the CLI agent */
|
|
2405
|
+
private async ensurePersistentSession(agentId: string, config: WorkspaceAgentConfig, skipStartupCheck = false): Promise<void> {
|
|
2406
|
+
const safeName = (s: string) => s.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').slice(0, 20);
|
|
2407
|
+
const sessionName = `mw-forge-${safeName(this.projectName)}-${safeName(config.label)}`;
|
|
2408
|
+
|
|
2409
|
+
// Pre-flight: check project's .claude/settings.json is valid (cached by mtime)
|
|
2410
|
+
const workDir = config.workDir && config.workDir !== './' && config.workDir !== '.'
|
|
2411
|
+
? `${this.projectPath}/${config.workDir}` : this.projectPath;
|
|
2412
|
+
const projectSettingsFile = join(workDir, '.claude', 'settings.json');
|
|
2413
|
+
if (existsSync(projectSettingsFile)) {
|
|
2414
|
+
try {
|
|
2415
|
+
const mtime = statSync(projectSettingsFile).mtimeMs;
|
|
2416
|
+
if (this.settingsValidCache.get(projectSettingsFile) !== mtime) {
|
|
2417
|
+
const raw = readFileSync(projectSettingsFile, 'utf-8');
|
|
2418
|
+
JSON.parse(raw);
|
|
2419
|
+
this.settingsValidCache.set(projectSettingsFile, mtime);
|
|
2420
|
+
}
|
|
2421
|
+
} catch (err: any) {
|
|
2422
|
+
this.settingsValidCache.delete(projectSettingsFile);
|
|
2423
|
+
const errorMsg = `Invalid .claude/settings.json: ${err.message}`;
|
|
2424
|
+
console.error(`[daemon] ${config.label}: ${errorMsg}`);
|
|
2425
|
+
const entry = this.agents.get(agentId);
|
|
2426
|
+
if (entry) {
|
|
2427
|
+
entry.state.error = errorMsg;
|
|
2428
|
+
entry.state.smithStatus = 'down';
|
|
2429
|
+
this.emit('event', { type: 'smith_status', agentId, smithStatus: 'down' } as any);
|
|
2430
|
+
this.emit('event', { type: 'log', agentId, entry: { type: 'system', subtype: 'error', content: `⚠️ ${errorMsg}`, timestamp: new Date().toISOString() } } as any);
|
|
2431
|
+
this.emitAgentsChanged();
|
|
2432
|
+
}
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
// Write agent context file for hooks to read (workDir/.forge/agent-context.json)
|
|
2438
|
+
try {
|
|
2439
|
+
const forgeDir = join(workDir, '.forge');
|
|
2440
|
+
mkdirSync(forgeDir, { recursive: true });
|
|
2441
|
+
const ctxPath = join(forgeDir, 'agent-context.json');
|
|
2442
|
+
writeFileSync(ctxPath, JSON.stringify({
|
|
2443
|
+
workspaceId: this.workspaceId,
|
|
2444
|
+
agentId: config.id,
|
|
2445
|
+
agentLabel: config.label,
|
|
2446
|
+
forgePort: Number(process.env.PORT) || 8403,
|
|
2447
|
+
}, null, 2));
|
|
2448
|
+
console.log(`[daemon] ${config.label}: wrote agent-context.json to ${ctxPath}`);
|
|
2449
|
+
} catch (err: any) {
|
|
2450
|
+
console.error(`[daemon] ${config.label}: failed to write agent-context.json: ${err.message}`);
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
// Check if tmux session already exists and Claude is still alive inside
|
|
2454
|
+
let sessionAlreadyExists = false;
|
|
2455
|
+
let tmuxSessionExists = false;
|
|
2456
|
+
console.log(`[daemon] ${config.label}: ensurePersistentSession called — sessionName=${sessionName} boundSessionId=${config.boundSessionId || 'NONE'} skipStartupCheck=${skipStartupCheck}`);
|
|
2457
|
+
try {
|
|
2458
|
+
execSync(`tmux has-session -t "${sessionName}" 2>/dev/null`, { timeout: 3000 });
|
|
2459
|
+
tmuxSessionExists = true;
|
|
2460
|
+
} catch {}
|
|
2461
|
+
console.log(`[daemon] ${config.label}: tmuxSessionExists=${tmuxSessionExists}`);
|
|
2462
|
+
|
|
2463
|
+
if (tmuxSessionExists) {
|
|
2464
|
+
// Check if Claude process is still alive inside the tmux pane
|
|
2465
|
+
let claudeAlive = true;
|
|
2466
|
+
try {
|
|
2467
|
+
const paneContent = execSync(`tmux capture-pane -t "${sessionName}" -p -S -5`, { timeout: 3000, encoding: 'utf-8' });
|
|
2468
|
+
const exitedPatterns = [/^\$\s*$/, /\$ $/m, /Process exited/i, /command not found/i];
|
|
2469
|
+
if (exitedPatterns.some(p => p.test(paneContent))) {
|
|
2470
|
+
console.log(`[daemon] ${config.label}: Claude appears to have exited, recreating session`);
|
|
2471
|
+
try { execSync(`tmux kill-session -t "${sessionName}" 2>/dev/null`, { timeout: 3000 }); } catch {}
|
|
2472
|
+
claudeAlive = false;
|
|
2473
|
+
}
|
|
2474
|
+
} catch {
|
|
2475
|
+
// pane capture failed — assume alive
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
if (claudeAlive) {
|
|
2479
|
+
sessionAlreadyExists = true;
|
|
2480
|
+
console.log(`[daemon] ${config.label}: persistent session alive (${sessionName}) — skipping script generation`);
|
|
2481
|
+
// Ensure FORGE env vars are set in the tmux session environment
|
|
2482
|
+
try {
|
|
2483
|
+
execSync(`tmux set-environment -t "${sessionName}" FORGE_WORKSPACE_ID "${this.workspaceId}" && tmux set-environment -t "${sessionName}" FORGE_AGENT_ID "${config.id}" && tmux set-environment -t "${sessionName}" FORGE_PORT "${Number(process.env.PORT) || 8403}"`, { timeout: 5000 });
|
|
2484
|
+
} catch {}
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
if (!sessionAlreadyExists) {
|
|
2489
|
+
// Pre-bind: find existing session file BEFORE starting CLI so --resume is available from the start.
|
|
2490
|
+
// This avoids starting a fresh CLI then restarting it after polling finds the session.
|
|
2491
|
+
if (!config.primary && !config.boundSessionId) {
|
|
2492
|
+
const existingSessionId = this.getLatestSessionId(config.workDir);
|
|
2493
|
+
if (existingSessionId) {
|
|
2494
|
+
config.boundSessionId = existingSessionId;
|
|
2495
|
+
this.saveNow();
|
|
2496
|
+
console.log(`[daemon] ${config.label}: pre-bound to existing session ${existingSessionId}`);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
// Create new tmux session and start the CLI agent
|
|
2501
|
+
try {
|
|
2502
|
+
// Resolve agent launch info
|
|
2503
|
+
let cliCmd = 'claude';
|
|
2504
|
+
let cliType = 'claude-code';
|
|
2505
|
+
let supportsSession = true;
|
|
2506
|
+
let skipPermissionsFlag = '--dangerously-skip-permissions';
|
|
2507
|
+
let envExports = '';
|
|
2508
|
+
let modelFlag = '';
|
|
2509
|
+
try {
|
|
2510
|
+
const { resolveTerminalLaunch, listAgents } = await import('../agents/index') as any;
|
|
2511
|
+
const info = resolveTerminalLaunch(config.agentId);
|
|
2512
|
+
cliCmd = info.cliCmd || 'claude';
|
|
2513
|
+
cliType = info.cliType || 'claude-code';
|
|
2514
|
+
supportsSession = info.supportsSession ?? true;
|
|
2515
|
+
const agents = listAgents();
|
|
2516
|
+
const agentDef = agents.find((a: any) => a.id === config.agentId);
|
|
2517
|
+
if (agentDef?.skipPermissionsFlag) skipPermissionsFlag = agentDef.skipPermissionsFlag;
|
|
2518
|
+
if (info.env) {
|
|
2519
|
+
envExports = Object.entries(info.env)
|
|
2520
|
+
.filter(([k]) => k !== 'CLAUDE_MODEL')
|
|
2521
|
+
.map(([k, v]) => `export ${k}="${v}"`)
|
|
2522
|
+
.join(' && ');
|
|
2523
|
+
if (envExports) envExports += ' && ';
|
|
2524
|
+
}
|
|
2525
|
+
// Workspace agent model takes priority over profile/settings model
|
|
2526
|
+
const effectiveModel = config.model || info.model;
|
|
2527
|
+
if (effectiveModel) modelFlag = ` --model ${effectiveModel}`;
|
|
2528
|
+
} catch {}
|
|
2529
|
+
|
|
2530
|
+
// Generate MCP config for Claude Code agents
|
|
2531
|
+
let mcpConfigFlag = '';
|
|
2532
|
+
if (cliType === 'claude-code') {
|
|
2533
|
+
try {
|
|
2534
|
+
const mcpPort = Number(process.env.MCP_PORT) || 8406;
|
|
2535
|
+
const mcpConfigPath = join(workDir, '.forge', 'mcp.json');
|
|
2536
|
+
const mcpConfig = {
|
|
2537
|
+
mcpServers: {
|
|
2538
|
+
forge: {
|
|
2539
|
+
type: 'sse',
|
|
2540
|
+
url: `http://localhost:${mcpPort}/sse?workspaceId=${this.workspaceId}&agentId=${config.id}`,
|
|
2541
|
+
},
|
|
2542
|
+
},
|
|
2543
|
+
};
|
|
2544
|
+
mkdirSync(join(workDir, '.forge'), { recursive: true });
|
|
2545
|
+
writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
|
|
2546
|
+
mcpConfigFlag = ` --mcp-config "${mcpConfigPath}"`;
|
|
2547
|
+
} catch (err: any) {
|
|
2548
|
+
console.log(`[daemon] ${config.label}: MCP config generation failed: ${err.message}`);
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
execSync(`tmux new-session -d -s "${sessionName}" -c "${workDir}"`, { timeout: 5000 });
|
|
2553
|
+
|
|
2554
|
+
// Build launch script to avoid tmux send-keys truncation
|
|
2555
|
+
const scriptLines: string[] = ['#!/bin/bash', `cd "${workDir}"`];
|
|
2556
|
+
|
|
2557
|
+
// Unset old profile vars
|
|
2558
|
+
scriptLines.push('unset ANTHROPIC_AUTH_TOKEN ANTHROPIC_BASE_URL ANTHROPIC_SMALL_FAST_MODEL CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC DISABLE_TELEMETRY DISABLE_ERROR_REPORTING DISABLE_AUTOUPDATER DISABLE_NON_ESSENTIAL_MODEL_CALLS CLAUDE_MODEL');
|
|
2559
|
+
|
|
2560
|
+
// Set FORGE env vars
|
|
2561
|
+
scriptLines.push(`export FORGE_WORKSPACE_ID="${this.workspaceId}" FORGE_AGENT_ID="${config.id}" FORGE_PORT="${Number(process.env.PORT) || 8403}"`);
|
|
2562
|
+
|
|
2563
|
+
// Set profile env vars
|
|
2564
|
+
if (envExports) {
|
|
2565
|
+
scriptLines.push(envExports.replace(/ && /g, '\n').replace(/\n$/, ''));
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
// Build CLI command
|
|
2569
|
+
let cmd = cliCmd;
|
|
2570
|
+
if (supportsSession) {
|
|
2571
|
+
let sessionId: string | undefined;
|
|
2572
|
+
if (config.primary) {
|
|
2573
|
+
sessionId = getFixedSession(this.projectPath);
|
|
2574
|
+
console.log(`[daemon] ${config.label}: fixedSession=${sessionId || 'NONE'} for ${this.projectPath}`);
|
|
2575
|
+
} else {
|
|
2576
|
+
sessionId = config.boundSessionId;
|
|
2577
|
+
console.log(`[daemon] ${config.label}: script-gen boundSessionId=${sessionId || 'NONE'}`);
|
|
2578
|
+
}
|
|
2579
|
+
if (sessionId) {
|
|
2580
|
+
const sessionFile = join(this.getCliSessionDir(config.workDir), `${sessionId}.jsonl`);
|
|
2581
|
+
if (existsSync(sessionFile)) {
|
|
2582
|
+
cmd += ` --resume ${sessionId}`;
|
|
2583
|
+
console.log(`[daemon] ${config.label}: script-gen adding --resume ${sessionId}`);
|
|
2584
|
+
} else {
|
|
2585
|
+
console.log(`[daemon] ${config.label}: bound session ${sessionId} missing, starting fresh`);
|
|
2586
|
+
}
|
|
2587
|
+
} else {
|
|
2588
|
+
console.log(`[daemon] ${config.label}: script-gen no boundSessionId → no --resume (skipStartupCheck=${skipStartupCheck})`);
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
if (modelFlag) cmd += modelFlag;
|
|
2592
|
+
if (config.skipPermissions !== false && skipPermissionsFlag) cmd += ` ${skipPermissionsFlag}`;
|
|
2593
|
+
if (mcpConfigFlag) cmd += mcpConfigFlag;
|
|
2594
|
+
scriptLines.push(`exec ${cmd}`);
|
|
2595
|
+
|
|
2596
|
+
// Write script and execute in tmux
|
|
2597
|
+
const scriptPath = `/tmp/forge-launch-${config.id.replace(/[^a-z0-9-]/g, '')}.sh`;
|
|
2598
|
+
console.log(`[daemon] ${config.label}: writing launch script ${scriptPath}`);
|
|
2599
|
+
console.log(`[daemon] ${config.label}: exec line → ${cmd}`);
|
|
2600
|
+
writeFileSync(scriptPath, scriptLines.join('\n'), { mode: 0o755 });
|
|
2601
|
+
execSync(`tmux send-keys -t "${sessionName}" 'bash ${scriptPath}' Enter`, { timeout: 5000 });
|
|
2602
|
+
|
|
2603
|
+
console.log(`[daemon] ${config.label}: persistent session created (${sessionName}) [${cliType}: ${cliCmd}]`);
|
|
2604
|
+
|
|
2605
|
+
// Verify CLI started successfully (check after 3s if process is still alive)
|
|
2606
|
+
// Skip when called from openTerminalSession (terminal-open path) for fast response.
|
|
2607
|
+
if (skipStartupCheck) {
|
|
2608
|
+
// Set tmuxSession here only — normal path sets it after startup verification passes.
|
|
2609
|
+
const entrySkip = this.agents.get(agentId);
|
|
2610
|
+
if (entrySkip) {
|
|
2611
|
+
entrySkip.state.tmuxSession = sessionName;
|
|
2612
|
+
this.saveNow();
|
|
2613
|
+
this.emitAgentsChanged();
|
|
2614
|
+
}
|
|
2615
|
+
// Fire boundSessionId binding in background (no await — don't block terminal open)
|
|
2616
|
+
if (!config.primary && !config.boundSessionId) {
|
|
2617
|
+
const bgScriptPath = `/tmp/forge-launch-${config.id.replace(/[^a-z0-9-]/g, '')}.sh`;
|
|
2618
|
+
setTimeout(() => {
|
|
2619
|
+
const sid = this.getLatestSessionId(config.workDir);
|
|
2620
|
+
if (sid) {
|
|
2621
|
+
config.boundSessionId = sid;
|
|
2622
|
+
this.saveNow();
|
|
2623
|
+
console.log(`[daemon] ${config.label}: background bound to session ${sid}`);
|
|
2624
|
+
// Also update launch script for future restarts
|
|
2625
|
+
if (existsSync(bgScriptPath)) {
|
|
2626
|
+
try {
|
|
2627
|
+
const lines = readFileSync(bgScriptPath, 'utf-8').split('\n');
|
|
2628
|
+
const execIdx = lines.findIndex(l => l.startsWith('exec '));
|
|
2629
|
+
if (execIdx >= 0) {
|
|
2630
|
+
const withoutResume = lines[execIdx].replace(/\s--resume\s+\S+/, '');
|
|
2631
|
+
const afterExec = withoutResume.startsWith('exec ') ? 5 : 0;
|
|
2632
|
+
const cmdEnd = withoutResume.indexOf(' ', afterExec);
|
|
2633
|
+
lines[execIdx] = cmdEnd >= 0
|
|
2634
|
+
? withoutResume.slice(0, cmdEnd) + ` --resume ${sid}` + withoutResume.slice(cmdEnd)
|
|
2635
|
+
: withoutResume + ` --resume ${sid}`;
|
|
2636
|
+
writeFileSync(bgScriptPath, lines.join('\n'), { mode: 0o755 });
|
|
2637
|
+
console.log(`[daemon] ${config.label}: background updated launch script with --resume ${sid}`);
|
|
2638
|
+
}
|
|
2639
|
+
} catch {}
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
}, 5000);
|
|
2643
|
+
}
|
|
2644
|
+
return;
|
|
2645
|
+
}
|
|
2646
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
2647
|
+
try {
|
|
2648
|
+
const paneContent = execSync(`tmux capture-pane -t "${sessionName}" -p -S -20`, { timeout: 3000, encoding: 'utf-8' });
|
|
2649
|
+
// Check for common startup errors
|
|
2650
|
+
const errorPatterns = [
|
|
2651
|
+
/error.*settings\.json/i,
|
|
2652
|
+
/invalid.*json/i,
|
|
2653
|
+
/SyntaxError/i,
|
|
2654
|
+
/ENOENT.*settings/i,
|
|
2655
|
+
/failed to parse/i,
|
|
2656
|
+
/could not read/i,
|
|
2657
|
+
/fatal/i,
|
|
2658
|
+
/No conversation found/i,
|
|
2659
|
+
/could not connect/i,
|
|
2660
|
+
/ECONNREFUSED/i,
|
|
2661
|
+
];
|
|
2662
|
+
const hasError = errorPatterns.some(p => p.test(paneContent));
|
|
2663
|
+
if (hasError) {
|
|
2664
|
+
const errorLines = paneContent.split('\n').filter(l => /error|invalid|syntax|fatal|failed|No conversation|ECONNREFUSED/i.test(l)).slice(0, 3);
|
|
2665
|
+
const errorMsg = errorLines.join(' ').slice(0, 200) || 'CLI failed to start (check project settings)';
|
|
2666
|
+
console.error(`[daemon] ${config.label}: CLI startup error detected: ${errorMsg}`);
|
|
2667
|
+
|
|
2668
|
+
const entry = this.agents.get(agentId);
|
|
2669
|
+
if (entry) {
|
|
2670
|
+
entry.state.error = `Terminal failed: ${errorMsg}. Falling back to headless mode.`;
|
|
2671
|
+
entry.state.tmuxSession = undefined; // clear so message loop uses headless (claude -p)
|
|
2672
|
+
this.emit('event', { type: 'log', agentId, entry: { type: 'system', subtype: 'error', content: `Terminal startup failed: ${errorMsg}. Auto-fallback to headless.`, timestamp: new Date().toISOString() } } as any);
|
|
2673
|
+
this.emitAgentsChanged();
|
|
2674
|
+
}
|
|
2675
|
+
// Kill the failed tmux session
|
|
2676
|
+
try { execSync(`tmux kill-session -t "${sessionName}" 2>/dev/null`, { timeout: 3000 }); } catch {}
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2679
|
+
} catch {}
|
|
2680
|
+
} catch (err: any) {
|
|
2681
|
+
console.error(`[daemon] ${config.label}: failed to create persistent session: ${err.message}`);
|
|
2682
|
+
const entry = this.agents.get(agentId);
|
|
2683
|
+
if (entry) {
|
|
2684
|
+
entry.state.error = `Failed to create terminal: ${err.message}`;
|
|
2685
|
+
entry.state.smithStatus = 'down';
|
|
2686
|
+
this.emit('event', { type: 'smith_status', agentId, smithStatus: 'down' } as any);
|
|
2687
|
+
this.emitAgentsChanged();
|
|
2688
|
+
}
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
// Store tmux session name in agent state
|
|
2694
|
+
const entry = this.agents.get(agentId);
|
|
2695
|
+
if (entry) {
|
|
2696
|
+
entry.state.tmuxSession = sessionName;
|
|
2697
|
+
this.saveNow();
|
|
2698
|
+
this.emitAgentsChanged();
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
// Ensure boundSessionId is set before returning (required for session monitor + --resume)
|
|
2702
|
+
// Also re-bind if existing boundSessionId points to a deleted session file
|
|
2703
|
+
if (!config.primary && config.boundSessionId) {
|
|
2704
|
+
const boundFile = join(this.getCliSessionDir(config.workDir), `${config.boundSessionId}.jsonl`);
|
|
2705
|
+
if (!existsSync(boundFile)) {
|
|
2706
|
+
console.log(`[daemon] ${config.label}: boundSession ${config.boundSessionId} file missing, re-binding`);
|
|
2707
|
+
config.boundSessionId = undefined;
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
if (!config.primary && !config.boundSessionId) {
|
|
2711
|
+
const pollStart = Date.now();
|
|
2712
|
+
const pollDelay = sessionAlreadyExists ? 500 : 2000;
|
|
2713
|
+
await new Promise<void>(resolve => {
|
|
2714
|
+
const attempt = () => {
|
|
2715
|
+
if (!this.daemonActive) { resolve(); return; }
|
|
2716
|
+
const sessionId = this.getLatestSessionId(config.workDir);
|
|
2717
|
+
if (sessionId) {
|
|
2718
|
+
config.boundSessionId = sessionId;
|
|
2719
|
+
this.saveNow();
|
|
2720
|
+
console.log(`[daemon] ${config.label}: bound to session ${config.boundSessionId}`);
|
|
2721
|
+
// Update launch script with the now-known sessionId — script was written before
|
|
2722
|
+
// polling completed so it had no --resume flag. Fix it for future restarts.
|
|
2723
|
+
const scriptPath = `/tmp/forge-launch-${config.id.replace(/[^a-z0-9-]/g, '')}.sh`;
|
|
2724
|
+
if (existsSync(scriptPath)) {
|
|
2725
|
+
try {
|
|
2726
|
+
const lines = readFileSync(scriptPath, 'utf-8').split('\n');
|
|
2727
|
+
const execIdx = lines.findIndex(l => l.startsWith('exec '));
|
|
2728
|
+
if (execIdx >= 0) {
|
|
2729
|
+
// Remove any existing --resume flag, then re-insert after command name.
|
|
2730
|
+
// Line format: 'exec <cmd> [args...]'
|
|
2731
|
+
// Insert after cmd (skip 'exec ' prefix + cmd word) so bash doesn't
|
|
2732
|
+
// misinterpret --resume as an exec builtin option.
|
|
2733
|
+
const withoutResume = lines[execIdx].replace(/\s--resume\s+\S+/, '');
|
|
2734
|
+
const afterExec = withoutResume.startsWith('exec ') ? 5 : 0;
|
|
2735
|
+
const cmdEnd = withoutResume.indexOf(' ', afterExec);
|
|
2736
|
+
lines[execIdx] = cmdEnd >= 0
|
|
2737
|
+
? withoutResume.slice(0, cmdEnd) + ` --resume ${sessionId}` + withoutResume.slice(cmdEnd)
|
|
2738
|
+
: withoutResume + ` --resume ${sessionId}`;
|
|
2739
|
+
writeFileSync(scriptPath, lines.join('\n'), { mode: 0o755 });
|
|
2740
|
+
console.log(`[daemon] ${config.label}: updated launch script with --resume ${sessionId}`);
|
|
2741
|
+
}
|
|
2742
|
+
} catch {}
|
|
2743
|
+
}
|
|
2744
|
+
return resolve();
|
|
2745
|
+
}
|
|
2746
|
+
if (Date.now() - pollStart < 10_000) {
|
|
2747
|
+
setTimeout(attempt, 1000);
|
|
2748
|
+
} else {
|
|
2749
|
+
console.log(`[daemon] ${config.label}: no session file after 10s, skipping bind`);
|
|
2750
|
+
const entry = this.agents.get(agentId);
|
|
2751
|
+
if (entry) {
|
|
2752
|
+
entry.state.error = 'Terminal session not ready — click "Open Terminal" to start it manually';
|
|
2753
|
+
this.emit('event', { type: 'smith_status', agentId, smithStatus: entry.state.smithStatus } as any);
|
|
2754
|
+
this.emitAgentsChanged();
|
|
2755
|
+
}
|
|
2756
|
+
resolve();
|
|
2757
|
+
}
|
|
2758
|
+
};
|
|
2759
|
+
setTimeout(attempt, pollDelay);
|
|
2760
|
+
});
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
/** Inject text into an agent's persistent terminal session */
|
|
2765
|
+
injectIntoSession(agentId: string, text: string): boolean {
|
|
2766
|
+
const entry = this.agents.get(agentId);
|
|
2767
|
+
// Verify stored session is alive
|
|
2768
|
+
if (entry?.state.tmuxSession) {
|
|
2769
|
+
try { execSync(`tmux has-session -t "${entry.state.tmuxSession}" 2>/dev/null`, { timeout: 3000 }); }
|
|
2770
|
+
catch { entry.state.tmuxSession = undefined; }
|
|
2771
|
+
}
|
|
2772
|
+
const tmuxSession = entry?.state.tmuxSession || this.findTmuxSession(entry?.config.label || '');
|
|
2773
|
+
if (!tmuxSession) return false;
|
|
2774
|
+
// Cache found session for future use
|
|
2775
|
+
if (entry && !entry.state.tmuxSession) entry.state.tmuxSession = tmuxSession;
|
|
2776
|
+
|
|
2777
|
+
try {
|
|
2778
|
+
const tmpFile = `/tmp/forge-inject-${Date.now()}.txt`;
|
|
2779
|
+
writeFileSync(tmpFile, text);
|
|
2780
|
+
execSync(`tmux load-buffer ${tmpFile}`, { timeout: 5000 });
|
|
2781
|
+
execSync(`tmux paste-buffer -t "${tmuxSession}" && sleep 0.2 && tmux send-keys -t "${tmuxSession}" Enter`, { timeout: 5000 });
|
|
2782
|
+
try { unlinkSync(tmpFile); } catch {}
|
|
2783
|
+
return true;
|
|
2784
|
+
} catch (err: any) {
|
|
2785
|
+
console.error(`[inject] Failed for ${tmuxSession}: ${err.message}`);
|
|
2786
|
+
return false;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
/** Check if agent has a persistent session available */
|
|
2791
|
+
hasPersistentSession(agentId: string): boolean {
|
|
2792
|
+
const entry = this.agents.get(agentId);
|
|
2793
|
+
if (!entry) return false;
|
|
2794
|
+
if (entry.state.tmuxSession) return true;
|
|
2795
|
+
return !!this.findTmuxSession(entry.config.label);
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
private findTmuxSession(agentLabel: string): string | null {
|
|
2799
|
+
const safeName = (s: string) => s.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').slice(0, 20);
|
|
2800
|
+
const projectSafe = safeName(this.projectName);
|
|
2801
|
+
const agentSafe = safeName(agentLabel);
|
|
2802
|
+
|
|
2803
|
+
// Try workspace naming: mw-forge-{project}-{agent}
|
|
2804
|
+
const wsName = `mw-forge-${projectSafe}-${agentSafe}`;
|
|
2805
|
+
try { execSync(`tmux has-session -t "${wsName}" 2>/dev/null`, { timeout: 3000 }); return wsName; } catch {}
|
|
2806
|
+
|
|
2807
|
+
// Try VibeCoding naming: mw-{project}
|
|
2808
|
+
const vcName = `mw-${projectSafe}`;
|
|
2809
|
+
try { execSync(`tmux has-session -t "${vcName}" 2>/dev/null`, { timeout: 3000 }); return vcName; } catch {}
|
|
2810
|
+
|
|
2811
|
+
// Search terminal-state.json for project matching tmux session
|
|
2812
|
+
try {
|
|
2813
|
+
const statePath = join(homedir(), '.forge', 'data', 'terminal-state.json');
|
|
2814
|
+
if (existsSync(statePath)) {
|
|
2815
|
+
const termState = JSON.parse(readFileSync(statePath, 'utf-8'));
|
|
2816
|
+
for (const tab of termState.tabs || []) {
|
|
2817
|
+
if (tab.projectPath === this.projectPath) {
|
|
2818
|
+
const findSession = (tree: any): string | null => {
|
|
2819
|
+
if (tree?.type === 'terminal' && tree.sessionName) return tree.sessionName;
|
|
2820
|
+
for (const child of tree?.children || []) {
|
|
2821
|
+
const found = findSession(child);
|
|
2822
|
+
if (found) return found;
|
|
2823
|
+
}
|
|
2824
|
+
return null;
|
|
2825
|
+
};
|
|
2826
|
+
const sess = findSession(tab.tree);
|
|
2827
|
+
if (sess) {
|
|
2828
|
+
try { execSync(`tmux has-session -t "${sess}" 2>/dev/null`, { timeout: 3000 }); return sess; } catch {}
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
} catch {}
|
|
2834
|
+
|
|
2835
|
+
return null;
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
private updateAgentLiveness(agentId: string): void {
|
|
2839
|
+
const entry = this.agents.get(agentId);
|
|
2840
|
+
if (!entry) {
|
|
2841
|
+
this.bus.setAgentStatus(agentId, 'down');
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
if (entry.state.taskStatus === 'running') this.bus.setAgentStatus(agentId, 'busy');
|
|
2845
|
+
else if (entry.state.smithStatus === 'active') this.bus.setAgentStatus(agentId, 'alive');
|
|
2846
|
+
else this.bus.setAgentStatus(agentId, 'down');
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
// ─── Bus message handling ──────────────────────────────
|
|
2850
|
+
|
|
2851
|
+
private handleBusMessage(msg: BusMessage): void {
|
|
2852
|
+
// Dedup
|
|
2853
|
+
if (this.bus.isDuplicate(msg.id)) return;
|
|
2854
|
+
|
|
2855
|
+
// Emit to UI after dedup (no duplicates, no ACKs)
|
|
2856
|
+
this.emit('event', { type: 'bus_message', message: msg } satisfies OrchestratorEvent);
|
|
2857
|
+
|
|
2858
|
+
// Route to target
|
|
2859
|
+
this.routeMessageToAgent(msg.to, msg);
|
|
2860
|
+
this.checkWorkspaceComplete();
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2863
|
+
private routeMessageToAgent(targetId: string, msg: BusMessage): void {
|
|
2864
|
+
const target = this.agents.get(targetId);
|
|
2865
|
+
if (!target) return;
|
|
2866
|
+
|
|
2867
|
+
const fromLabel = this.agents.get(msg.from)?.config.label || msg.from;
|
|
2868
|
+
const action = msg.payload.action;
|
|
2869
|
+
const content = msg.payload.content || '';
|
|
2870
|
+
|
|
2871
|
+
console.log(`[bus] ${fromLabel} → ${target.config.label}: ${action} "${content.slice(0, 80)}"`);
|
|
2872
|
+
|
|
2873
|
+
const logEntry = {
|
|
2874
|
+
type: 'system' as const,
|
|
2875
|
+
subtype: 'bus_message',
|
|
2876
|
+
content: `[From ${fromLabel}]: ${content || action}`,
|
|
2877
|
+
timestamp: new Date(msg.timestamp).toISOString(),
|
|
2878
|
+
};
|
|
2879
|
+
|
|
2880
|
+
// Helper: mark message as processed when actually consumed
|
|
2881
|
+
const ackAndDeliver = () => {
|
|
2882
|
+
msg.status = 'done';
|
|
2883
|
+
};
|
|
2884
|
+
|
|
2885
|
+
// ── Input node: request user input ──
|
|
2886
|
+
if (target.config.type === 'input') {
|
|
2887
|
+
if (action === 'info_request' || action === 'question') {
|
|
2888
|
+
ackAndDeliver();
|
|
2889
|
+
this.emit('event', {
|
|
2890
|
+
type: 'user_input_request',
|
|
2891
|
+
agentId: targetId,
|
|
2892
|
+
fromAgent: msg.from,
|
|
2893
|
+
question: content,
|
|
2894
|
+
} satisfies OrchestratorEvent);
|
|
2895
|
+
}
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
// ── Store message in agent history ──
|
|
2900
|
+
target.state.history.push(logEntry);
|
|
2901
|
+
|
|
2902
|
+
// ── requiresApproval → set pending_approval on arrival ──
|
|
2903
|
+
if (target.config.requiresApproval) {
|
|
2904
|
+
msg.status = 'pending_approval';
|
|
2905
|
+
this.emit('event', { type: 'bus_message_status', messageId: msg.id, status: 'pending_approval' } as any);
|
|
2906
|
+
console.log(`[bus] ${target.config.label}: received ${action} — pending approval`);
|
|
2907
|
+
return;
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
// ── Message stays pending — message loop will consume it when smith is ready ──
|
|
2911
|
+
console.log(`[bus] ${target.config.label}: received ${action} — queued in inbox (${msg.status})`);
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
// ─── Message consumption loop ─────────────────────────
|
|
2915
|
+
private messageLoopTimers = new Map<string, NodeJS.Timeout>();
|
|
2916
|
+
|
|
2917
|
+
/** Start the message consumption loop for a smith */
|
|
2918
|
+
private startMessageLoop(agentId: string): void {
|
|
2919
|
+
if (this.messageLoopTimers.has(agentId)) return; // already running
|
|
2920
|
+
|
|
2921
|
+
let debugTick = 0;
|
|
2922
|
+
const tick = () => {
|
|
2923
|
+
const entry = this.agents.get(agentId);
|
|
2924
|
+
if (!entry) {
|
|
2925
|
+
this.stopMessageLoop(agentId);
|
|
2926
|
+
return;
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
// Don't stop loop if smith is down — just skip this tick
|
|
2930
|
+
// (loop stays alive so it works when smith comes back)
|
|
2931
|
+
if (entry.state.smithStatus !== 'active') return;
|
|
2932
|
+
|
|
2933
|
+
// Skip if already busy
|
|
2934
|
+
if (entry.state.taskStatus === 'running') return;
|
|
2935
|
+
|
|
2936
|
+
// Skip if any message is already running for this agent (O(1) cache lookup)
|
|
2937
|
+
if (this.agentRunningMsg.has(agentId)) return;
|
|
2938
|
+
|
|
2939
|
+
// Execution path determined by config, not runtime tmux state
|
|
2940
|
+
const isTerminalMode = entry.config.persistentSession;
|
|
2941
|
+
if (isTerminalMode) {
|
|
2942
|
+
// Terminal mode: need tmux session. If missing, skip this tick (health check will restart it)
|
|
2943
|
+
if (!entry.state.tmuxSession) {
|
|
2944
|
+
if (++debugTick % 15 === 0) {
|
|
2945
|
+
console.log(`[inbox] ${entry.config.label}: terminal mode but no tmux session — waiting for auto-restart`);
|
|
2946
|
+
}
|
|
2947
|
+
return;
|
|
2948
|
+
}
|
|
2949
|
+
} else {
|
|
2950
|
+
// Headless mode: need worker ready
|
|
2951
|
+
if (!entry.worker) {
|
|
2952
|
+
if (this.daemonActive) {
|
|
2953
|
+
console.log(`[inbox] ${entry.config.label}: no worker, recreating...`);
|
|
2954
|
+
this.enterDaemonListening(agentId);
|
|
2955
|
+
}
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
if (!entry.worker.isListening()) {
|
|
2959
|
+
if (++debugTick % 15 === 0) {
|
|
2960
|
+
console.log(`[inbox] ${entry.config.label}: not listening (smith=${entry.state.smithStatus} task=${entry.state.taskStatus})`);
|
|
2961
|
+
}
|
|
2962
|
+
return;
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
// requiresApproval is handled at message arrival time (routeMessageToAgent),
|
|
2967
|
+
// not in the message loop. Approved messages come through as normal 'pending'.
|
|
2968
|
+
|
|
2969
|
+
// Dedup: if multiple upstream_complete from same sender are pending, keep only latest
|
|
2970
|
+
const allRaw = this.bus.getPendingMessagesFor(agentId).filter(m => m.from !== agentId && m.type !== 'ack');
|
|
2971
|
+
const upstreamSeen = new Set<string>();
|
|
2972
|
+
for (let i = allRaw.length - 1; i >= 0; i--) {
|
|
2973
|
+
const m = allRaw[i];
|
|
2974
|
+
if (m.payload?.action === 'upstream_complete') {
|
|
2975
|
+
const key = `upstream-${m.from}`;
|
|
2976
|
+
if (upstreamSeen.has(key)) {
|
|
2977
|
+
m.status = 'done' as any;
|
|
2978
|
+
this.emit('event', { type: 'bus_message_status', messageId: m.id, status: 'done' } as any);
|
|
2979
|
+
}
|
|
2980
|
+
upstreamSeen.add(key);
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
// Find next pending message, applying causedBy rules
|
|
2985
|
+
const allPending = allRaw.filter(m => m.status === 'pending');
|
|
2986
|
+
const pending = allPending.filter(m => {
|
|
2987
|
+
// Tickets: accepted but check retry limit
|
|
2988
|
+
if (m.category === 'ticket') {
|
|
2989
|
+
const maxRetries = m.maxRetries ?? 3;
|
|
2990
|
+
if ((m.ticketRetries || 0) >= maxRetries) {
|
|
2991
|
+
console.log(`[inbox] ${entry.config.label}: ticket ${m.id.slice(0, 8)} exceeded max retries (${maxRetries}), marking failed`);
|
|
2992
|
+
m.status = 'failed' as any;
|
|
2993
|
+
m.ticketStatus = 'closed';
|
|
2994
|
+
this.emit('event', { type: 'bus_message_status', messageId: m.id, status: 'failed' } as any);
|
|
2995
|
+
return false;
|
|
2996
|
+
}
|
|
2997
|
+
return true;
|
|
2998
|
+
}
|
|
2999
|
+
|
|
3000
|
+
// System messages (from _watch, _system, user) bypass causedBy rules
|
|
3001
|
+
if (m.from.startsWith('_') || m.from === 'user') return true;
|
|
3002
|
+
|
|
3003
|
+
// Notifications: check causedBy for loop prevention
|
|
3004
|
+
if (m.causedBy) {
|
|
3005
|
+
// Rule 1: Is this a response to something I sent? → accept (for verification)
|
|
3006
|
+
const myOutbox = this.bus.getOutboxFor(agentId);
|
|
3007
|
+
if (myOutbox.some(o => o.id === m.causedBy!.messageId)) return true;
|
|
3008
|
+
|
|
3009
|
+
// Rule 2: Notification from downstream → discard (prevents reverse flow)
|
|
3010
|
+
if (!this.isUpstream(m.from, agentId)) {
|
|
3011
|
+
console.log(`[inbox] ${entry.config.label}: discarding notification from downstream ${this.agents.get(m.from)?.config.label || m.from}`);
|
|
3012
|
+
m.status = 'done' as any; // silently consume
|
|
3013
|
+
return false;
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
// Default: accept (upstream notifications, no causedBy = initial trigger)
|
|
3018
|
+
return true;
|
|
3019
|
+
});
|
|
3020
|
+
if (pending.length === 0) return;
|
|
3021
|
+
|
|
3022
|
+
const nextMsg = pending[0];
|
|
3023
|
+
const fromLabel = this.agents.get(nextMsg.from)?.config.label || nextMsg.from;
|
|
3024
|
+
console.log(`[inbox] ${entry.config.label}: consuming message from ${fromLabel} (${nextMsg.payload.action})`);
|
|
3025
|
+
|
|
3026
|
+
// Mark message as running (being processed)
|
|
3027
|
+
nextMsg.status = 'running' as any;
|
|
3028
|
+
this.agentRunningMsg.set(agentId, nextMsg.id);
|
|
3029
|
+
this.emit('event', { type: 'bus_message_status', messageId: nextMsg.id, status: 'running' } as any);
|
|
3030
|
+
|
|
3031
|
+
const logEntry = {
|
|
3032
|
+
type: 'system' as const,
|
|
3033
|
+
subtype: 'bus_message',
|
|
3034
|
+
content: `[From ${fromLabel}]: ${nextMsg.payload.content || nextMsg.payload.action}`,
|
|
3035
|
+
timestamp: new Date(nextMsg.timestamp).toISOString(),
|
|
3036
|
+
};
|
|
3037
|
+
|
|
3038
|
+
// Terminal mode → inject; headless → worker (claude -p)
|
|
3039
|
+
if (isTerminalMode) {
|
|
3040
|
+
// Check if role reminder needed (combats auto-compaction over long sessions)
|
|
3041
|
+
let messageText = nextMsg.payload.content || nextMsg.payload.action;
|
|
3042
|
+
if (this.needsRoleReminder(agentId) && entry.config.role?.trim()) {
|
|
3043
|
+
messageText = this.buildRoleReminder(entry.config) + '\n\n' + messageText;
|
|
3044
|
+
this.markRoleInjected(agentId);
|
|
3045
|
+
console.log(`[inbox] ${entry.config.label}: prepended role reminder`);
|
|
3046
|
+
} else {
|
|
3047
|
+
this.incrementMsgCount(agentId);
|
|
3048
|
+
}
|
|
3049
|
+
const injected = this.injectIntoSession(agentId, messageText);
|
|
3050
|
+
if (injected) {
|
|
3051
|
+
entry.state.taskStatus = 'running';
|
|
3052
|
+
this.emit('event', { type: 'task_status', agentId, taskStatus: 'running' } as any);
|
|
3053
|
+
this.emit('event', { type: 'log', agentId, entry: { type: 'system', subtype: 'execution_method', content: '📺 Injected into terminal, monitoring for completion...', timestamp: new Date().toISOString() } } as any);
|
|
3054
|
+
console.log(`[inbox] ${entry.config.label}: injected into terminal, starting completion monitor`);
|
|
3055
|
+
entry.state.currentMessageId = nextMsg.id;
|
|
3056
|
+
this.monitorTerminalCompletion(agentId, nextMsg.id, entry.state.tmuxSession!);
|
|
3057
|
+
} else {
|
|
3058
|
+
// Terminal inject failed — clear dead session, message stays pending
|
|
3059
|
+
// Health check will auto-restart the terminal session
|
|
3060
|
+
entry.state.tmuxSession = undefined;
|
|
3061
|
+
nextMsg.status = 'pending' as any; // revert to pending for retry
|
|
3062
|
+
this.agentRunningMsg.delete(agentId);
|
|
3063
|
+
this.emit('event', { type: 'bus_message_status', messageId: nextMsg.id, status: 'pending' } as any);
|
|
3064
|
+
this.emit('event', { type: 'log', agentId, entry: { type: 'system', subtype: 'warning', content: '⚠️ Terminal session down — waiting for auto-restart, message will retry', timestamp: new Date().toISOString() } } as any);
|
|
3065
|
+
console.log(`[inbox] ${entry.config.label}: terminal inject failed, cleared session — waiting for health check restart`);
|
|
3066
|
+
this.emitAgentsChanged();
|
|
3067
|
+
}
|
|
3068
|
+
} else {
|
|
3069
|
+
entry.worker!.setProcessingMessage(nextMsg.id);
|
|
3070
|
+
entry.worker!.wake({ type: 'bus_message', messages: [logEntry] });
|
|
3071
|
+
this.emit('event', { type: 'log', agentId, entry: { type: 'system', subtype: 'execution_method', content: `⚡ Executed via headless (agent: ${entry.config.agentId || 'claude'})`, timestamp: new Date().toISOString() } } as any);
|
|
3072
|
+
}
|
|
3073
|
+
};
|
|
3074
|
+
|
|
3075
|
+
// Check every 2 seconds
|
|
3076
|
+
const timer = setInterval(tick, 2000);
|
|
3077
|
+
timer.unref(); // Don't prevent process exit in tests
|
|
3078
|
+
this.messageLoopTimers.set(agentId, timer);
|
|
3079
|
+
// Also run immediately
|
|
3080
|
+
tick();
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
/** Stop the message consumption loop for a smith */
|
|
3084
|
+
private stopMessageLoop(agentId: string): void {
|
|
3085
|
+
const timer = this.messageLoopTimers.get(agentId);
|
|
3086
|
+
if (timer) {
|
|
3087
|
+
clearInterval(timer);
|
|
3088
|
+
this.messageLoopTimers.delete(agentId);
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
/** Stop all message loops */
|
|
3093
|
+
private stopAllMessageLoops(): void {
|
|
3094
|
+
for (const [id] of this.messageLoopTimers) {
|
|
3095
|
+
this.stopMessageLoop(id);
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
// ─── Terminal completion monitor ──────────────────────
|
|
3100
|
+
private terminalMonitors = new Map<string, NodeJS.Timeout>();
|
|
3101
|
+
|
|
3102
|
+
/**
|
|
3103
|
+
* Monitor a tmux session for completion after injecting a message.
|
|
3104
|
+
* Detects CLI prompt patterns (❯, $, >) indicating the agent is idle.
|
|
3105
|
+
* Requires 2 consecutive prompt detections (10s) to confirm completion.
|
|
3106
|
+
*/
|
|
3107
|
+
private monitorTerminalCompletion(agentId: string, messageId: string, tmuxSession: string): void {
|
|
3108
|
+
// Stop any existing monitor for this agent
|
|
3109
|
+
const existing = this.terminalMonitors.get(agentId);
|
|
3110
|
+
if (existing) clearInterval(existing);
|
|
3111
|
+
|
|
3112
|
+
// Prompt patterns that indicate the CLI is idle and waiting for input
|
|
3113
|
+
// Claude Code: ❯ Codex: > Aider: > Generic shell: $ #
|
|
3114
|
+
const PROMPT_PATTERNS = [
|
|
3115
|
+
/^❯\s*$/, // Claude Code idle prompt
|
|
3116
|
+
/^>\s*$/, // Codex / generic prompt
|
|
3117
|
+
/^\$\s*$/, // Shell prompt
|
|
3118
|
+
/^#\s*$/, // Root shell prompt
|
|
3119
|
+
/^aider>\s*$/, // Aider prompt
|
|
3120
|
+
];
|
|
3121
|
+
|
|
3122
|
+
let promptCount = 0;
|
|
3123
|
+
let started = false;
|
|
3124
|
+
const CONFIRM_CHECKS = 2; // 2 consecutive prompt detections = done
|
|
3125
|
+
const CHECK_INTERVAL = 5000; // 5s between checks
|
|
3126
|
+
|
|
3127
|
+
const timer = setInterval(() => {
|
|
3128
|
+
try {
|
|
3129
|
+
const output = execSync(`tmux capture-pane -t "${tmuxSession}" -p -S -30`, { timeout: 3000, encoding: 'utf-8' });
|
|
3130
|
+
|
|
3131
|
+
// Strip ANSI escape sequences for clean matching
|
|
3132
|
+
const clean = output.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
3133
|
+
// Get last few non-empty lines
|
|
3134
|
+
const lines = clean.split('\n').map(l => l.trim()).filter(Boolean);
|
|
3135
|
+
const tail = lines.slice(-5);
|
|
3136
|
+
|
|
3137
|
+
// First check: detect that agent started working (output changed from inject)
|
|
3138
|
+
if (!started && lines.length > 3) {
|
|
3139
|
+
started = true;
|
|
3140
|
+
}
|
|
3141
|
+
if (!started) return;
|
|
3142
|
+
|
|
3143
|
+
// Check if any of the last lines match a prompt pattern
|
|
3144
|
+
const hasPrompt = tail.some(line => PROMPT_PATTERNS.some(p => p.test(line)));
|
|
3145
|
+
|
|
3146
|
+
if (hasPrompt) {
|
|
3147
|
+
promptCount++;
|
|
3148
|
+
if (promptCount >= CONFIRM_CHECKS) {
|
|
3149
|
+
clearInterval(timer);
|
|
3150
|
+
this.terminalMonitors.delete(agentId);
|
|
3151
|
+
this.agentRunningMsg.delete(agentId);
|
|
3152
|
+
|
|
3153
|
+
// Extract output summary (skip prompt lines)
|
|
3154
|
+
const contentLines = lines.filter(l => !PROMPT_PATTERNS.some(p => p.test(l)));
|
|
3155
|
+
const summary = contentLines.slice(-15).join('\n');
|
|
3156
|
+
|
|
3157
|
+
// Mark message done
|
|
3158
|
+
const msg = this.bus.getLog().find(m => m.id === messageId);
|
|
3159
|
+
if (msg && msg.status !== 'done') {
|
|
3160
|
+
msg.status = 'done' as any;
|
|
3161
|
+
this.emit('event', { type: 'bus_message_status', messageId, status: 'done' } as any);
|
|
3162
|
+
}
|
|
3163
|
+
|
|
3164
|
+
// Emit output to log panel
|
|
3165
|
+
this.emit('event', { type: 'log', agentId, entry: { type: 'assistant', subtype: 'terminal_output', content: `📺 Terminal completed:\n${summary.slice(0, 500)}`, timestamp: new Date().toISOString() } } as any);
|
|
3166
|
+
|
|
3167
|
+
// Trigger downstream notifications
|
|
3168
|
+
const entry = this.agents.get(agentId);
|
|
3169
|
+
if (entry) {
|
|
3170
|
+
entry.state.currentMessageId = undefined;
|
|
3171
|
+
this.handleAgentDone(agentId, entry, summary.slice(0, 300));
|
|
3172
|
+
}
|
|
3173
|
+
console.log(`[terminal-monitor] ${agentId}: prompt detected, completed`);
|
|
3174
|
+
}
|
|
3175
|
+
} else {
|
|
3176
|
+
promptCount = 0; // reset — still working
|
|
3177
|
+
}
|
|
3178
|
+
} catch {
|
|
3179
|
+
// Session died
|
|
3180
|
+
clearInterval(timer);
|
|
3181
|
+
this.terminalMonitors.delete(agentId);
|
|
3182
|
+
this.agentRunningMsg.delete(agentId);
|
|
3183
|
+
const msg = this.bus.getLog().find(m => m.id === messageId);
|
|
3184
|
+
if (msg && msg.status !== 'done' && msg.status !== 'failed') {
|
|
3185
|
+
msg.status = 'failed' as any;
|
|
3186
|
+
this.emit('event', { type: 'bus_message_status', messageId, status: 'failed' } as any);
|
|
3187
|
+
}
|
|
3188
|
+
const entry = this.agents.get(agentId);
|
|
3189
|
+
if (entry) entry.state.currentMessageId = undefined;
|
|
3190
|
+
console.error(`[terminal-monitor] ${agentId}: session died, marked message failed`);
|
|
3191
|
+
}
|
|
3192
|
+
}, CHECK_INTERVAL);
|
|
3193
|
+
timer.unref();
|
|
3194
|
+
this.terminalMonitors.set(agentId, timer);
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
/** Stop all terminal monitors (on daemon stop) */
|
|
3198
|
+
private stopAllTerminalMonitors(): void {
|
|
3199
|
+
for (const [, timer] of this.terminalMonitors) clearInterval(timer);
|
|
3200
|
+
this.terminalMonitors.clear();
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
/** Check if all agents are done and no pending work remains */
|
|
3204
|
+
private checkWorkspaceComplete(): void {
|
|
3205
|
+
let allDone = true;
|
|
3206
|
+
for (const [id, entry] of this.agents) {
|
|
3207
|
+
const ws = entry.worker?.getState();
|
|
3208
|
+
const taskSt = ws?.taskStatus ?? entry.state.taskStatus;
|
|
3209
|
+
if (taskSt === 'running' || this.approvalQueue.has(id)) {
|
|
3210
|
+
allDone = false;
|
|
3211
|
+
break;
|
|
3212
|
+
}
|
|
3213
|
+
// idle agents with unmet deps don't block completion
|
|
3214
|
+
if (taskSt === 'idle' && entry.config.dependsOn.length > 0) {
|
|
3215
|
+
const allDepsDone = entry.config.dependsOn.every(depId => {
|
|
3216
|
+
const dep = this.agents.get(depId);
|
|
3217
|
+
return dep && (dep.state.taskStatus === 'done');
|
|
3218
|
+
});
|
|
3219
|
+
if (allDepsDone) {
|
|
3220
|
+
allDone = false; // idle but ready to run = not complete
|
|
3221
|
+
break;
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3226
|
+
if (allDone && this.agents.size > 0) {
|
|
3227
|
+
const hasPendingRequests = this.bus.getLog().some(m =>
|
|
3228
|
+
m.type === 'request' && !this.bus.getLog().some(r =>
|
|
3229
|
+
r.type === 'response' && r.payload.replyTo === m.id
|
|
3230
|
+
)
|
|
3231
|
+
);
|
|
3232
|
+
// Also check request documents are all done
|
|
3233
|
+
let requestsComplete = true;
|
|
3234
|
+
try {
|
|
3235
|
+
const { listRequests } = require('./requests');
|
|
3236
|
+
const allReqs = listRequests(this.projectPath);
|
|
3237
|
+
if (allReqs.length > 0) {
|
|
3238
|
+
requestsComplete = allReqs.every((r: any) => r.status === 'done' || r.status === 'rejected');
|
|
3239
|
+
}
|
|
3240
|
+
} catch {}
|
|
3241
|
+
|
|
3242
|
+
if (!hasPendingRequests && requestsComplete) {
|
|
3243
|
+
console.log('[workspace] All agents complete, no pending requests, all request docs done. Workspace done.');
|
|
3244
|
+
this.emit('event', { type: 'workspace_complete' } satisfies OrchestratorEvent);
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3249
|
+
/** Get agents that are idle and have all dependencies met */
|
|
3250
|
+
private getReadyAgents(): string[] {
|
|
3251
|
+
const ready: string[] = [];
|
|
3252
|
+
for (const [id, entry] of this.agents) {
|
|
3253
|
+
if (entry.state.taskStatus !== 'idle') continue;
|
|
3254
|
+
const allDepsDone = entry.config.dependsOn.every(depId => {
|
|
3255
|
+
const dep = this.agents.get(depId);
|
|
3256
|
+
return dep && dep.state.taskStatus === 'done';
|
|
3257
|
+
});
|
|
3258
|
+
if (allDepsDone) ready.push(id);
|
|
3259
|
+
}
|
|
3260
|
+
return ready;
|
|
3261
|
+
}
|
|
3262
|
+
|
|
3263
|
+
/**
|
|
3264
|
+
* Parse CLI agent output for bus message markers.
|
|
3265
|
+
* Format: [SEND:TargetLabel:action] content
|
|
3266
|
+
* Example: [SEND:Engineer:fix_request] SQL injection found in auth module
|
|
3267
|
+
*/
|
|
3268
|
+
/**
|
|
3269
|
+
* After an agent completes, notify downstream agents that already ran (done/failed)
|
|
3270
|
+
* to re-validate their work. Sets them to waiting_approval so user decides.
|
|
3271
|
+
*/
|
|
3272
|
+
private notifyDownstreamForRevalidation(completedAgentId: string, files: string[]): void {
|
|
3273
|
+
const completedLabel = this.agents.get(completedAgentId)?.config.label || completedAgentId;
|
|
3274
|
+
|
|
3275
|
+
for (const [id, entry] of this.agents) {
|
|
3276
|
+
if (id === completedAgentId) continue;
|
|
3277
|
+
if (!entry.config.dependsOn.includes(completedAgentId)) continue;
|
|
3278
|
+
|
|
3279
|
+
// Only notify agents that already completed — they need to re-validate
|
|
3280
|
+
if (entry.state.taskStatus !== 'done' && entry.state.taskStatus !== 'failed') continue;
|
|
3281
|
+
|
|
3282
|
+
console.log(`[workspace] ${completedLabel} changed → ${entry.config.label} needs re-validation`);
|
|
3283
|
+
|
|
3284
|
+
// Send bus message
|
|
3285
|
+
this.bus.send(completedAgentId, id, 'notify', {
|
|
3286
|
+
action: 'update_notify',
|
|
3287
|
+
content: `${completedLabel} completed with changes. Please re-validate.`,
|
|
3288
|
+
files,
|
|
3289
|
+
});
|
|
3290
|
+
|
|
3291
|
+
// Set to waiting_approval so user confirms re-run
|
|
3292
|
+
entry.state.taskStatus = 'idle';
|
|
3293
|
+
entry.state.history.push({
|
|
3294
|
+
type: 'system',
|
|
3295
|
+
subtype: 'revalidation_request',
|
|
3296
|
+
content: `[${completedLabel}] completed with changes — approve to re-run validation`,
|
|
3297
|
+
timestamp: new Date().toISOString(),
|
|
3298
|
+
});
|
|
3299
|
+
this.approvalQueue.add(id);
|
|
3300
|
+
this.emit('event', { type: 'task_status', agentId: id, taskStatus: 'idle' } satisfies WorkerEvent);
|
|
3301
|
+
this.emit('event', {
|
|
3302
|
+
type: 'approval_required',
|
|
3303
|
+
agentId: id,
|
|
3304
|
+
upstreamId: completedAgentId,
|
|
3305
|
+
} satisfies OrchestratorEvent);
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3309
|
+
/** Track how many history entries have been scanned per agent to avoid re-parsing */
|
|
3310
|
+
private busMarkerScanned = new Map<string, number>();
|
|
3311
|
+
|
|
3312
|
+
private parseBusMarkers(fromAgentId: string, history: { type: string; content: string }[]): void {
|
|
3313
|
+
const markerRegex = /\[SEND:([^:]+):([^\]]+)\]\s*(.+)/g;
|
|
3314
|
+
const labelToId = new Map<string, string>();
|
|
3315
|
+
for (const [id, e] of this.agents) {
|
|
3316
|
+
labelToId.set(e.config.label.toLowerCase(), id);
|
|
3317
|
+
}
|
|
3318
|
+
|
|
3319
|
+
// Only scan new entries since last parse (avoid re-sending from old history)
|
|
3320
|
+
const lastScanned = this.busMarkerScanned.get(fromAgentId) || 0;
|
|
3321
|
+
const newEntries = history.slice(lastScanned);
|
|
3322
|
+
this.busMarkerScanned.set(fromAgentId, history.length);
|
|
3323
|
+
|
|
3324
|
+
for (const entry of newEntries) {
|
|
3325
|
+
let match;
|
|
3326
|
+
while ((match = markerRegex.exec(entry.content)) !== null) {
|
|
3327
|
+
const targetLabel = match[1].trim();
|
|
3328
|
+
const action = match[2].trim();
|
|
3329
|
+
const content = match[3].trim();
|
|
3330
|
+
const targetId = labelToId.get(targetLabel.toLowerCase());
|
|
3331
|
+
|
|
3332
|
+
if (targetId && targetId !== fromAgentId) {
|
|
3333
|
+
console.log(`[bus] Parsed marker from ${fromAgentId}: → ${targetLabel} (${action}): ${content.slice(0, 60)}`);
|
|
3334
|
+
this.bus.send(fromAgentId, targetId, 'notify', { action, content });
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
private saveNow(): void {
|
|
3341
|
+
saveWorkspace(this.getFullState()).catch(() => {});
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
/** Emit agents_changed so SSE pushes the updated list to frontend */
|
|
3345
|
+
private emitAgentsChanged(): void {
|
|
3346
|
+
// Refresh topology cache so MCP queries always return current state
|
|
3347
|
+
this.rebuildTopo();
|
|
3348
|
+
const agents = Array.from(this.agents.values()).map(e => e.config);
|
|
3349
|
+
const agentStates = this.getAllAgentStates();
|
|
3350
|
+
this.emit('event', { type: 'agents_changed', agents, agentStates } satisfies WorkerEvent);
|
|
3351
|
+
}
|
|
3352
|
+
|
|
3353
|
+
private emitWorkspaceStatus(): void {
|
|
3354
|
+
let running = 0, done = 0;
|
|
3355
|
+
for (const [, entry] of this.agents) {
|
|
3356
|
+
const ws = entry.worker?.getState();
|
|
3357
|
+
const taskSt = ws?.taskStatus ?? entry.state.taskStatus;
|
|
3358
|
+
if (taskSt === 'running') running++;
|
|
3359
|
+
if (taskSt === 'done') done++;
|
|
3360
|
+
}
|
|
3361
|
+
this.emit('event', {
|
|
3362
|
+
type: 'workspace_status',
|
|
3363
|
+
running,
|
|
3364
|
+
done,
|
|
3365
|
+
total: this.agents.size,
|
|
3366
|
+
} satisfies OrchestratorEvent);
|
|
3367
|
+
}
|
|
3368
|
+
|
|
3369
|
+
/**
|
|
3370
|
+
* Update agent memory after execution completes.
|
|
3371
|
+
* Parses step results into structured memory entries.
|
|
3372
|
+
*/
|
|
3373
|
+
private async updateAgentMemory(agentId: string, config: WorkspaceAgentConfig, stepResults: string[]): Promise<void> {
|
|
3374
|
+
try {
|
|
3375
|
+
const entry = this.agents.get(agentId);
|
|
3376
|
+
|
|
3377
|
+
// Capture observation from the last step (previous steps captured in 'step' event handler)
|
|
3378
|
+
const lastStep = config.steps[config.steps.length - 1];
|
|
3379
|
+
const lastResult = stepResults[stepResults.length - 1];
|
|
3380
|
+
if (lastStep && lastResult) {
|
|
3381
|
+
const obs = parseStepToObservations(lastStep.label, lastResult, entry?.state.artifacts || []);
|
|
3382
|
+
for (const o of obs) {
|
|
3383
|
+
await addObservation(this.workspaceId, agentId, config.label, config.role, o);
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
// Add session summary
|
|
3388
|
+
const summary = buildSessionSummary(
|
|
3389
|
+
config.steps.map(s => s.label),
|
|
3390
|
+
stepResults,
|
|
3391
|
+
entry?.state.artifacts || [],
|
|
3392
|
+
);
|
|
3393
|
+
await addSessionSummary(this.workspaceId, agentId, summary);
|
|
3394
|
+
|
|
3395
|
+
console.log(`[workspace] Updated memory for ${config.label}`);
|
|
3396
|
+
} catch (err: any) {
|
|
3397
|
+
console.error(`[workspace] Failed to update memory for ${config.label}:`, err.message);
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
}
|