@aion0/forge 0.5.26 → 0.5.28
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/worktrees/pipeline-4dd8dc2d/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/README.md +136 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/RELEASE_NOTES.md +36 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-bell/route.ts +39 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/stream/route.ts +31 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/route.ts +79 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/mobile/page.tsx +10 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/page.tsx +22 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/WebTerminal.tsx +1743 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/install.sh +29 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-process.ts +361 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-mcp-server.ts +732 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline.ts +1565 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/task-manager.ts +951 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-standalone.ts +438 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/index.ts +84 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/orchestrator.ts +3415 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace-standalone.ts +978 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/package.json +74 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/publish.sh +133 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/start.sh +32 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/tsconfig.json +42 -0
- package/RELEASE_NOTES.md +10 -29
- package/app/api/terminal-bell/route.ts +6 -2
- package/app/api/terminal-cwd/route.ts +7 -4
- package/components/CodeViewer.tsx +3 -31
- package/components/Dashboard.tsx +34 -20
- package/components/WebTerminal.tsx +36 -2
- package/lib/terminal-standalone.ts +19 -2
- package/package.json +1 -1
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Backend — executes agent steps by spawning CLI tools headless.
|
|
3
|
+
*
|
|
4
|
+
* Supports subscription accounts (no API key needed).
|
|
5
|
+
* Each step = one `claude -p "..."` call.
|
|
6
|
+
* Multi-step context via --resume (Claude) or prompt injection (others).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { spawn, type ChildProcess } from 'node:child_process';
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
11
|
+
import { getAgent } from '../../agents';
|
|
12
|
+
import type { AgentBackend, AgentStep, StepExecutionParams, StepExecutionResult, Artifact } from '../types';
|
|
13
|
+
import type { TaskLogEntry } from '../../../src/types';
|
|
14
|
+
|
|
15
|
+
const esmRequire = createRequire(import.meta.url);
|
|
16
|
+
|
|
17
|
+
// ─── Stream-JSON parser (reused from task-manager pattern) ──
|
|
18
|
+
|
|
19
|
+
function parseStreamJson(parsed: any): TaskLogEntry[] {
|
|
20
|
+
const entries: TaskLogEntry[] = [];
|
|
21
|
+
const ts = new Date().toISOString();
|
|
22
|
+
|
|
23
|
+
if (parsed.type === 'system' && parsed.subtype === 'init') {
|
|
24
|
+
entries.push({ type: 'system', subtype: 'init', content: `Model: ${parsed.model || 'unknown'}`, timestamp: ts });
|
|
25
|
+
return entries;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (parsed.type === 'assistant' && parsed.message?.content) {
|
|
29
|
+
for (const block of parsed.message.content) {
|
|
30
|
+
if (block.type === 'text' && block.text) {
|
|
31
|
+
entries.push({ type: 'assistant', subtype: 'text', content: block.text, timestamp: ts });
|
|
32
|
+
} else if (block.type === 'tool_use') {
|
|
33
|
+
entries.push({
|
|
34
|
+
type: 'assistant',
|
|
35
|
+
subtype: 'tool_use',
|
|
36
|
+
content: typeof block.input === 'string' ? block.input : JSON.stringify(block.input || {}),
|
|
37
|
+
tool: block.name,
|
|
38
|
+
timestamp: ts,
|
|
39
|
+
});
|
|
40
|
+
} else if (block.type === 'tool_result') {
|
|
41
|
+
entries.push({
|
|
42
|
+
type: 'assistant',
|
|
43
|
+
subtype: 'tool_result',
|
|
44
|
+
content: typeof block.content === 'string' ? block.content : JSON.stringify(block.content || ''),
|
|
45
|
+
timestamp: ts,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return entries;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (parsed.type === 'result') {
|
|
53
|
+
entries.push({
|
|
54
|
+
type: 'result',
|
|
55
|
+
subtype: parsed.subtype || 'success',
|
|
56
|
+
content: typeof parsed.result === 'string' ? parsed.result : JSON.stringify(parsed.result || ''),
|
|
57
|
+
timestamp: ts,
|
|
58
|
+
});
|
|
59
|
+
return entries;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Ignore rate limit events
|
|
63
|
+
if (parsed.type === 'rate_limit_event') return entries;
|
|
64
|
+
|
|
65
|
+
// Unknown type — log raw
|
|
66
|
+
entries.push({ type: 'assistant', subtype: parsed.type || 'unknown', content: JSON.stringify(parsed), timestamp: ts });
|
|
67
|
+
return entries;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ─── Artifact detection from tool_use events ─────────────
|
|
71
|
+
|
|
72
|
+
const WRITE_TOOL_NAMES = new Set(['Write', 'write_to_file', 'Edit', 'create_file', 'write_file']);
|
|
73
|
+
|
|
74
|
+
function detectArtifacts(parsed: any): Artifact[] {
|
|
75
|
+
const artifacts: Artifact[] = [];
|
|
76
|
+
if (parsed.type !== 'assistant' || !parsed.message?.content) return artifacts;
|
|
77
|
+
|
|
78
|
+
for (const block of parsed.message.content) {
|
|
79
|
+
if (block.type === 'tool_use' && WRITE_TOOL_NAMES.has(block.name)) {
|
|
80
|
+
const path = block.input?.file_path || block.input?.path || block.input?.filename;
|
|
81
|
+
if (path) {
|
|
82
|
+
artifacts.push({ type: 'file', path, summary: `Written by ${block.name}` });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return artifacts;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ─── CLI Backend class ───────────────────────────────────
|
|
90
|
+
|
|
91
|
+
export class CliBackend implements AgentBackend {
|
|
92
|
+
private child: ChildProcess | null = null;
|
|
93
|
+
private sessionId: string | undefined;
|
|
94
|
+
headlessSessionId: string | undefined; // exposed for session file monitoring
|
|
95
|
+
/** Callback to persist sessionId back to agent state */
|
|
96
|
+
onSessionId?: (id: string) => void;
|
|
97
|
+
|
|
98
|
+
constructor(initialSessionId?: string) {
|
|
99
|
+
this.sessionId = initialSessionId;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async executeStep(params: StepExecutionParams): Promise<StepExecutionResult> {
|
|
103
|
+
const { config, step, history, projectPath, upstreamContext, onLog, abortSignal, workspaceId } = params;
|
|
104
|
+
const agentId = config.agentId || 'claude';
|
|
105
|
+
|
|
106
|
+
let adapter;
|
|
107
|
+
try {
|
|
108
|
+
adapter = getAgent(agentId);
|
|
109
|
+
} catch {
|
|
110
|
+
throw new Error(`Agent "${agentId}" not found or not installed`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Build prompt with context
|
|
114
|
+
const prompt = this.buildStepPrompt(step, history, upstreamContext);
|
|
115
|
+
|
|
116
|
+
// Use adapter to build spawn command (same as task-manager)
|
|
117
|
+
// Model priority: workspace config > profile config > adapter default
|
|
118
|
+
const effectiveModel = config.model || (adapter.config as any).model;
|
|
119
|
+
// Note: if no sessionId, each execution starts a new session (no resume).
|
|
120
|
+
// To maintain context, user can enable persistent terminal session per agent.
|
|
121
|
+
|
|
122
|
+
// Generate a session ID for headless execution so we can monitor the .jsonl file
|
|
123
|
+
const isClaude = adapter.config.type === 'claude-code';
|
|
124
|
+
if (isClaude && !this.sessionId) {
|
|
125
|
+
this.headlessSessionId = crypto.randomUUID();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const spawnOpts = adapter.buildTaskSpawn({
|
|
129
|
+
projectPath,
|
|
130
|
+
prompt,
|
|
131
|
+
model: effectiveModel,
|
|
132
|
+
conversationId: this.sessionId,
|
|
133
|
+
skipPermissions: true,
|
|
134
|
+
outputFormat: adapter.config.capabilities?.supportsStreamJson ? 'stream-json' : undefined,
|
|
135
|
+
extraFlags: this.headlessSessionId && !this.sessionId ? ['--session-id', this.headlessSessionId] : undefined,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
onLog?.({
|
|
139
|
+
type: 'system',
|
|
140
|
+
subtype: 'init',
|
|
141
|
+
content: `Step "${step.label}" — ${agentId}${config.model ? `/${config.model}` : ''}${this.sessionId ? ' (resume)' : ''}`,
|
|
142
|
+
timestamp: new Date().toISOString(),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return new Promise<StepExecutionResult>((resolve, reject) => {
|
|
146
|
+
// Merge env: process env → adapter spawn env → profile env → workspace context
|
|
147
|
+
const profileEnv = (adapter.config as any).env || {};
|
|
148
|
+
const env = {
|
|
149
|
+
...process.env,
|
|
150
|
+
...(spawnOpts.env || {}),
|
|
151
|
+
...profileEnv,
|
|
152
|
+
// Inject workspace context so forge skills can use them
|
|
153
|
+
FORGE_AGENT_ID: config.id,
|
|
154
|
+
FORGE_WORKSPACE_ID: workspaceId || '',
|
|
155
|
+
FORGE_PORT: String(process.env.PORT || 8403),
|
|
156
|
+
};
|
|
157
|
+
delete env.CLAUDECODE;
|
|
158
|
+
|
|
159
|
+
// Check if agent needs TTY — use cliType not agentId
|
|
160
|
+
const cliType = (adapter.config as any).cliType || adapter.config.type;
|
|
161
|
+
const needsTTY = adapter.config.capabilities?.requiresTTY
|
|
162
|
+
|| cliType === 'codex';
|
|
163
|
+
|
|
164
|
+
if (needsTTY) {
|
|
165
|
+
this.executePTY(spawnOpts, projectPath, env, onLog, abortSignal, resolve, reject);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.child = spawn(spawnOpts.cmd, spawnOpts.args, {
|
|
170
|
+
cwd: projectPath,
|
|
171
|
+
env,
|
|
172
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
173
|
+
});
|
|
174
|
+
this.child.stdin?.end();
|
|
175
|
+
|
|
176
|
+
let buffer = '';
|
|
177
|
+
let resultText = '';
|
|
178
|
+
let sessionId = '';
|
|
179
|
+
const artifacts: Artifact[] = [];
|
|
180
|
+
let inputTokens = 0;
|
|
181
|
+
let outputTokens = 0;
|
|
182
|
+
|
|
183
|
+
// Handle abort signal
|
|
184
|
+
const onAbort = () => {
|
|
185
|
+
this.child?.kill('SIGTERM');
|
|
186
|
+
};
|
|
187
|
+
abortSignal?.addEventListener('abort', onAbort, { once: true });
|
|
188
|
+
|
|
189
|
+
// Fatal error detection pattern — only used for stderr (stdout is structured JSON)
|
|
190
|
+
const FATAL_PATTERN = /usage limit|rate limit|hit your.*limit|upgrade to (plus|pro|max)|exceeded.*monthly|you've been rate limited|api key.*invalid|insufficient.*quota|billing.*not.*active/i;
|
|
191
|
+
let fatalDetected = false;
|
|
192
|
+
|
|
193
|
+
this.child.stdout?.on('data', (data: Buffer) => {
|
|
194
|
+
const raw = data.toString();
|
|
195
|
+
// Fatal detection on stdout — only on non-JSON lines (skip tool results, user messages)
|
|
196
|
+
// JSON lines start with { and contain structured data from claude CLI
|
|
197
|
+
if (!fatalDetected && FATAL_PATTERN.test(raw)) {
|
|
198
|
+
// Check each line individually — only flag if it's NOT inside a JSON payload
|
|
199
|
+
const nonJsonLines = raw.split('\n').filter(l => {
|
|
200
|
+
const trimmed = l.trim();
|
|
201
|
+
return trimmed && !trimmed.startsWith('{') && !trimmed.startsWith('"') && !trimmed.includes('tool_use_id');
|
|
202
|
+
});
|
|
203
|
+
const fatalLine = nonJsonLines.find(l => FATAL_PATTERN.test(l));
|
|
204
|
+
if (fatalLine) {
|
|
205
|
+
fatalDetected = true;
|
|
206
|
+
console.log(`[cli-backend] Fatal error detected: ${fatalLine.trim().slice(0, 100)}`);
|
|
207
|
+
onLog?.({ type: 'system', subtype: 'error', content: fatalLine.trim().slice(0, 200), timestamp: new Date().toISOString() });
|
|
208
|
+
this.child?.kill('SIGTERM');
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
buffer += raw;
|
|
213
|
+
const lines = buffer.split('\n');
|
|
214
|
+
buffer = lines.pop() || '';
|
|
215
|
+
|
|
216
|
+
for (const line of lines) {
|
|
217
|
+
if (!line.trim()) continue;
|
|
218
|
+
try {
|
|
219
|
+
const parsed = JSON.parse(line);
|
|
220
|
+
|
|
221
|
+
// Emit log entries
|
|
222
|
+
const entries = parseStreamJson(parsed);
|
|
223
|
+
for (const entry of entries) {
|
|
224
|
+
onLog?.(entry);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Track session ID for multi-step resume
|
|
228
|
+
if (parsed.session_id) sessionId = parsed.session_id;
|
|
229
|
+
|
|
230
|
+
// Track result
|
|
231
|
+
if (parsed.type === 'result') {
|
|
232
|
+
resultText = typeof parsed.result === 'string'
|
|
233
|
+
? parsed.result
|
|
234
|
+
: JSON.stringify(parsed.result || '');
|
|
235
|
+
if (parsed.total_cost_usd) {
|
|
236
|
+
// Cost tracking if available
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Track usage
|
|
241
|
+
if (parsed.usage) {
|
|
242
|
+
inputTokens += parsed.usage.input_tokens || 0;
|
|
243
|
+
outputTokens += parsed.usage.output_tokens || 0;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Detect file write artifacts
|
|
247
|
+
artifacts.push(...detectArtifacts(parsed));
|
|
248
|
+
|
|
249
|
+
} catch {
|
|
250
|
+
// Non-JSON line — emit as raw text log
|
|
251
|
+
if (line.trim()) {
|
|
252
|
+
onLog?.({
|
|
253
|
+
type: 'assistant',
|
|
254
|
+
subtype: 'text',
|
|
255
|
+
content: line,
|
|
256
|
+
timestamp: new Date().toISOString(),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
this.child.stderr?.on('data', (data: Buffer) => {
|
|
264
|
+
const text = data.toString().trim();
|
|
265
|
+
// Also check stderr for fatal errors
|
|
266
|
+
if (!fatalDetected && FATAL_PATTERN.test(text)) {
|
|
267
|
+
fatalDetected = true;
|
|
268
|
+
console.log(`[cli-backend] Fatal error in stderr: ${text.slice(0, 100)}`);
|
|
269
|
+
this.child?.kill('SIGTERM');
|
|
270
|
+
}
|
|
271
|
+
if (text) {
|
|
272
|
+
onLog?.({
|
|
273
|
+
type: 'system',
|
|
274
|
+
subtype: 'error',
|
|
275
|
+
content: text,
|
|
276
|
+
timestamp: new Date().toISOString(),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
this.child.on('error', (err) => {
|
|
282
|
+
abortSignal?.removeEventListener('abort', onAbort);
|
|
283
|
+
this.child = null;
|
|
284
|
+
reject(err);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
this.child.on('exit', (code) => {
|
|
288
|
+
abortSignal?.removeEventListener('abort', onAbort);
|
|
289
|
+
|
|
290
|
+
// Flush remaining buffer
|
|
291
|
+
if (buffer.trim()) {
|
|
292
|
+
try {
|
|
293
|
+
const parsed = JSON.parse(buffer);
|
|
294
|
+
const entries = parseStreamJson(parsed);
|
|
295
|
+
for (const entry of entries) onLog?.(entry);
|
|
296
|
+
if (parsed.type === 'result') {
|
|
297
|
+
resultText = typeof parsed.result === 'string' ? parsed.result : JSON.stringify(parsed.result || '');
|
|
298
|
+
}
|
|
299
|
+
if (parsed.session_id) sessionId = parsed.session_id;
|
|
300
|
+
artifacts.push(...detectArtifacts(parsed));
|
|
301
|
+
} catch {
|
|
302
|
+
if (buffer.trim()) {
|
|
303
|
+
onLog?.({ type: 'assistant', subtype: 'text', content: buffer.trim(), timestamp: new Date().toISOString() });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Persist session ID for multi-step resume
|
|
309
|
+
this.sessionId = sessionId || this.sessionId;
|
|
310
|
+
if (this.sessionId && this.onSessionId) this.onSessionId(this.sessionId);
|
|
311
|
+
this.child = null;
|
|
312
|
+
|
|
313
|
+
// Check for error patterns even if exit code is 0
|
|
314
|
+
const KNOWN_ERRORS = /usage limit|rate limit|upgrade to|authentication failed|api key.*invalid/i;
|
|
315
|
+
const errorInOutput = resultText.split('\n').find(l => KNOWN_ERRORS.test(l))?.trim();
|
|
316
|
+
|
|
317
|
+
if (errorInOutput) {
|
|
318
|
+
reject(new Error(errorInOutput.slice(0, 200)));
|
|
319
|
+
} else if (code === 0 || code === null) {
|
|
320
|
+
resolve({
|
|
321
|
+
response: resultText,
|
|
322
|
+
artifacts,
|
|
323
|
+
sessionId: this.sessionId,
|
|
324
|
+
inputTokens,
|
|
325
|
+
outputTokens,
|
|
326
|
+
});
|
|
327
|
+
} else if (abortSignal?.aborted || code === 143 || code === 130) {
|
|
328
|
+
// 143=SIGTERM, 130=SIGINT — normal shutdown, not an error
|
|
329
|
+
reject(new Error('Aborted'));
|
|
330
|
+
} else {
|
|
331
|
+
reject(new Error(`CLI exited with code ${code}`));
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
abort(): void {
|
|
338
|
+
this.child?.kill('SIGTERM');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Build the prompt for a step, injecting history context.
|
|
343
|
+
* If resuming (sessionId exists), the CLI already has conversation context.
|
|
344
|
+
* Otherwise, prepend a summary of prior steps.
|
|
345
|
+
*/
|
|
346
|
+
/** Execute step using node-pty for agents that require a TTY (e.g., codex) */
|
|
347
|
+
private executePTY(
|
|
348
|
+
spawnOpts: { cmd: string; args: string[] },
|
|
349
|
+
projectPath: string,
|
|
350
|
+
env: Record<string, string | undefined>,
|
|
351
|
+
onLog: StepExecutionParams['onLog'],
|
|
352
|
+
abortSignal: AbortSignal | undefined,
|
|
353
|
+
resolve: (r: StepExecutionResult) => void,
|
|
354
|
+
reject: (e: Error) => void,
|
|
355
|
+
): void {
|
|
356
|
+
try {
|
|
357
|
+
const pty = esmRequire('node-pty');
|
|
358
|
+
const stripAnsi = (s: string) => s
|
|
359
|
+
.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '')
|
|
360
|
+
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '')
|
|
361
|
+
.replace(/\x1b[()][0-9A-B]/g, '')
|
|
362
|
+
.replace(/\x1b[=>]/g, '')
|
|
363
|
+
.replace(/\r/g, '')
|
|
364
|
+
.replace(/\x07/g, '');
|
|
365
|
+
|
|
366
|
+
const ptyProcess = pty.spawn(spawnOpts.cmd, spawnOpts.args, {
|
|
367
|
+
name: 'xterm-256color',
|
|
368
|
+
cols: 120, rows: 40,
|
|
369
|
+
cwd: projectPath,
|
|
370
|
+
env,
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
let resultText = '';
|
|
374
|
+
let ptyBytes = 0;
|
|
375
|
+
let idleTimer: any = null;
|
|
376
|
+
const PTY_IDLE_MS = 15000;
|
|
377
|
+
|
|
378
|
+
const onAbort = () => { try { ptyProcess.kill(); } catch {} };
|
|
379
|
+
abortSignal?.addEventListener('abort', onAbort, { once: true });
|
|
380
|
+
|
|
381
|
+
// Noise filter: skip spinner fragments, partial redraws, and short garbage
|
|
382
|
+
const NOISE_PATTERNS = /^(W|Wo|Wor|Work|Worki|Workin|Working|orking|rking|king|ing|ng|g|•|[0-9]+s?|[0-9]+m [0-9]+s|›.*|─+|╭.*|│.*|╰.*|\[K|\[0m|;[0-9;m]+|\s*)$/;
|
|
383
|
+
const isNoise = (line: string) => {
|
|
384
|
+
const t = line.trim();
|
|
385
|
+
return !t || t.length < 3 || NOISE_PATTERNS.test(t) || /^[•\s]*$/.test(t);
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
let lineBuf = '';
|
|
389
|
+
|
|
390
|
+
ptyProcess.onData((data: string) => {
|
|
391
|
+
const clean = stripAnsi(data);
|
|
392
|
+
ptyBytes += clean.length;
|
|
393
|
+
resultText += clean;
|
|
394
|
+
if (resultText.length > 50000) resultText = resultText.slice(-25000);
|
|
395
|
+
|
|
396
|
+
// Buffer lines and only emit meaningful content
|
|
397
|
+
lineBuf += clean;
|
|
398
|
+
const lines = lineBuf.split('\n');
|
|
399
|
+
lineBuf = lines.pop() || '';
|
|
400
|
+
|
|
401
|
+
for (const line of lines) {
|
|
402
|
+
if (!isNoise(line)) {
|
|
403
|
+
onLog?.({ type: 'assistant', subtype: 'text', content: line.trim(), timestamp: new Date().toISOString() });
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Detect fatal errors in real-time — kill immediately instead of waiting for idle
|
|
408
|
+
if (/usage limit|rate limit|hit your.*limit|upgrade to (plus|pro)/i.test(clean)) {
|
|
409
|
+
console.log(`[cli-backend] Detected usage limit — killing PTY immediately`);
|
|
410
|
+
onLog?.({ type: 'system', subtype: 'error', content: 'Agent hit usage limit', timestamp: new Date().toISOString() });
|
|
411
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
412
|
+
try { ptyProcess.kill(); } catch {}
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Idle timer: kill after 15s of silence (interactive agents don't exit on their own)
|
|
417
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
418
|
+
if (ptyBytes > 500) {
|
|
419
|
+
idleTimer = setTimeout(() => { try { ptyProcess.kill(); } catch {} }, PTY_IDLE_MS);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
ptyProcess.onExit(({ exitCode }: { exitCode: number }) => {
|
|
424
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
425
|
+
abortSignal?.removeEventListener('abort', onAbort);
|
|
426
|
+
|
|
427
|
+
// Flush remaining buffer
|
|
428
|
+
if (lineBuf.trim() && !isNoise(lineBuf)) {
|
|
429
|
+
onLog?.({ type: 'assistant', subtype: 'text', content: lineBuf.trim(), timestamp: new Date().toISOString() });
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Detect error patterns in output (rate limit, auth failure, etc.)
|
|
433
|
+
const ERROR_PATTERNS = [
|
|
434
|
+
/usage limit/i,
|
|
435
|
+
/rate limit/i,
|
|
436
|
+
/upgrade to/i,
|
|
437
|
+
/authentication failed/i,
|
|
438
|
+
/api key/i,
|
|
439
|
+
/permission denied/i,
|
|
440
|
+
/error:.*fatal/i,
|
|
441
|
+
];
|
|
442
|
+
const errorMatch = ERROR_PATTERNS.find(p => p.test(resultText));
|
|
443
|
+
if (errorMatch) {
|
|
444
|
+
// Extract the error line
|
|
445
|
+
const errorLine = resultText.split('\n').find(l => errorMatch.test(l))?.trim() || 'Agent execution failed';
|
|
446
|
+
reject(new Error(errorLine.slice(0, 200)));
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const meaningful = resultText.split('\n').filter(l => !isNoise(l)).join('\n');
|
|
451
|
+
resolve({
|
|
452
|
+
response: meaningful.slice(-2000) || resultText.slice(-500),
|
|
453
|
+
artifacts: [],
|
|
454
|
+
inputTokens: 0,
|
|
455
|
+
outputTokens: 0,
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
} catch (err: any) {
|
|
459
|
+
reject(new Error(`PTY spawn failed: ${err.message}`));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
private buildStepPrompt(step: AgentStep, history: TaskLogEntry[], upstreamContext?: string): string {
|
|
464
|
+
let prompt = step.prompt;
|
|
465
|
+
|
|
466
|
+
// If resuming with session, Claude already has conversation context — skip history injection
|
|
467
|
+
if (!this.sessionId && history.length > 0) {
|
|
468
|
+
// Only inject last 3 step results (not full history) to save tokens
|
|
469
|
+
const MAX_HISTORY_STEPS = 3;
|
|
470
|
+
const stepResults = history
|
|
471
|
+
.filter(m => m.type === 'result' && m.subtype === 'step_complete')
|
|
472
|
+
.slice(-MAX_HISTORY_STEPS);
|
|
473
|
+
|
|
474
|
+
if (stepResults.length > 0) {
|
|
475
|
+
const contextSummary = stepResults
|
|
476
|
+
.map((m, i) => `Step ${i + 1}: ${m.content.slice(0, 500)}${m.content.length > 500 ? '...' : ''}`)
|
|
477
|
+
.join('\n\n');
|
|
478
|
+
prompt = `## Context from previous steps (last ${stepResults.length}):\n${contextSummary}\n\n---\n\n## Current task:\n${prompt}`;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (upstreamContext) {
|
|
483
|
+
prompt = `## Upstream agent output:\n${upstreamContext}\n\n---\n\n${prompt}`;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Note: [SEND:] bus markers disabled. Agent communication now uses forge skills (/forge-send, /forge-inbox).
|
|
487
|
+
// Phase 2 will add ticket system + causedBy protocol for structured agent-to-agent feedback.
|
|
488
|
+
|
|
489
|
+
return prompt;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Agent System — public API.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Types
|
|
6
|
+
export type {
|
|
7
|
+
WorkspaceAgentConfig,
|
|
8
|
+
AgentBackendType,
|
|
9
|
+
AgentStep,
|
|
10
|
+
AgentStatus,
|
|
11
|
+
AgentState,
|
|
12
|
+
Artifact,
|
|
13
|
+
BusMessage,
|
|
14
|
+
WorkspaceState,
|
|
15
|
+
AgentBackend,
|
|
16
|
+
StepExecutionParams,
|
|
17
|
+
StepExecutionResult,
|
|
18
|
+
WorkerEvent,
|
|
19
|
+
} from './types';
|
|
20
|
+
|
|
21
|
+
// Core
|
|
22
|
+
export { AgentWorker, type AgentWorkerOptions } from './agent-worker';
|
|
23
|
+
export { AgentBus } from './agent-bus';
|
|
24
|
+
export { WorkspaceOrchestrator, type OrchestratorEvent } from './orchestrator';
|
|
25
|
+
|
|
26
|
+
// Backends
|
|
27
|
+
export { ApiBackend } from './backends/api-backend';
|
|
28
|
+
export { CliBackend } from './backends/cli-backend';
|
|
29
|
+
|
|
30
|
+
// Presets
|
|
31
|
+
export { AGENT_PRESETS, createDeliveryPipeline, createFromPreset } from './presets';
|
|
32
|
+
|
|
33
|
+
// Manager (singleton orchestrator cache + SSE)
|
|
34
|
+
export {
|
|
35
|
+
getOrchestrator,
|
|
36
|
+
createOrchestratorFromState,
|
|
37
|
+
getOrchestratorByProject,
|
|
38
|
+
subscribeSSE,
|
|
39
|
+
shutdownOrchestrator,
|
|
40
|
+
shutdownAll,
|
|
41
|
+
} from './manager';
|
|
42
|
+
|
|
43
|
+
// Persistence
|
|
44
|
+
// NOTE: saveWorkspace is intentionally NOT exported here.
|
|
45
|
+
// The workspace daemon is the exclusive writer of state.json.
|
|
46
|
+
// All mutations must go through the daemon's HTTP API to prevent race conditions.
|
|
47
|
+
export {
|
|
48
|
+
loadWorkspace,
|
|
49
|
+
listWorkspaces,
|
|
50
|
+
findWorkspaceByProject,
|
|
51
|
+
deleteWorkspace,
|
|
52
|
+
readAgentLog,
|
|
53
|
+
readAgentLogTail,
|
|
54
|
+
appendAgentLog,
|
|
55
|
+
startAutoSave,
|
|
56
|
+
stopAutoSave,
|
|
57
|
+
type WorkspaceSummary,
|
|
58
|
+
} from './persistence';
|
|
59
|
+
|
|
60
|
+
// Smith Memory
|
|
61
|
+
export {
|
|
62
|
+
loadMemory,
|
|
63
|
+
saveMemory,
|
|
64
|
+
createMemory,
|
|
65
|
+
addObservation,
|
|
66
|
+
addSessionSummary,
|
|
67
|
+
formatMemoryForPrompt,
|
|
68
|
+
formatMemoryForDisplay,
|
|
69
|
+
getMemoryStats,
|
|
70
|
+
parseStepToObservations,
|
|
71
|
+
buildSessionSummary,
|
|
72
|
+
type SmithMemory,
|
|
73
|
+
type Observation,
|
|
74
|
+
type ObservationType,
|
|
75
|
+
type SessionSummary,
|
|
76
|
+
type MemoryDisplayEntry,
|
|
77
|
+
} from './smith-memory';
|
|
78
|
+
|
|
79
|
+
// Skill installer
|
|
80
|
+
export {
|
|
81
|
+
installForgeSkills,
|
|
82
|
+
hasForgeSkills,
|
|
83
|
+
removeForgeSkills,
|
|
84
|
+
} from './skill-installer';
|