@aion0/forge 0.4.16 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +27 -2
  2. package/RELEASE_NOTES.md +21 -14
  3. package/app/api/agents/route.ts +17 -0
  4. package/app/api/delivery/[id]/route.ts +62 -0
  5. package/app/api/delivery/route.ts +40 -0
  6. package/app/api/mobile-chat/route.ts +13 -7
  7. package/app/api/monitor/route.ts +10 -6
  8. package/app/api/pipelines/[id]/route.ts +16 -3
  9. package/app/api/tasks/route.ts +2 -1
  10. package/app/api/workspace/[id]/agents/route.ts +35 -0
  11. package/app/api/workspace/[id]/memory/route.ts +23 -0
  12. package/app/api/workspace/[id]/smith/route.ts +22 -0
  13. package/app/api/workspace/[id]/stream/route.ts +28 -0
  14. package/app/api/workspace/route.ts +100 -0
  15. package/app/global-error.tsx +10 -4
  16. package/app/icon.ico +0 -0
  17. package/app/layout.tsx +2 -2
  18. package/app/login/LoginForm.tsx +96 -0
  19. package/app/login/page.tsx +7 -98
  20. package/app/page.tsx +2 -2
  21. package/bin/forge-server.mjs +13 -1
  22. package/check-forge-status.sh +9 -0
  23. package/components/ConversationEditor.tsx +411 -0
  24. package/components/ConversationGraphView.tsx +347 -0
  25. package/components/ConversationTerminalView.tsx +303 -0
  26. package/components/Dashboard.tsx +36 -39
  27. package/components/DashboardWrapper.tsx +9 -0
  28. package/components/DeliveryFlowEditor.tsx +491 -0
  29. package/components/DeliveryList.tsx +230 -0
  30. package/components/DeliveryWorkspace.tsx +589 -0
  31. package/components/DocTerminal.tsx +10 -2
  32. package/components/DocsViewer.tsx +10 -2
  33. package/components/HelpTerminal.tsx +11 -6
  34. package/components/InlinePipelineView.tsx +111 -0
  35. package/components/MobileView.tsx +20 -0
  36. package/components/MonitorPanel.tsx +9 -4
  37. package/components/NewTaskModal.tsx +32 -0
  38. package/components/PipelineEditor.tsx +49 -6
  39. package/components/PipelineView.tsx +482 -64
  40. package/components/ProjectDetail.tsx +314 -56
  41. package/components/ProjectManager.tsx +49 -4
  42. package/components/SessionView.tsx +27 -13
  43. package/components/SettingsModal.tsx +790 -124
  44. package/components/SkillsPanel.tsx +31 -8
  45. package/components/TaskBoard.tsx +3 -0
  46. package/components/WebTerminal.tsx +257 -43
  47. package/components/WorkspaceTree.tsx +221 -0
  48. package/components/WorkspaceView.tsx +2245 -0
  49. package/install.sh +2 -2
  50. package/lib/agents/claude-adapter.ts +104 -0
  51. package/lib/agents/generic-adapter.ts +64 -0
  52. package/lib/agents/index.ts +242 -0
  53. package/lib/agents/types.ts +70 -0
  54. package/lib/artifacts.ts +106 -0
  55. package/lib/delivery.ts +787 -0
  56. package/lib/forge-skills/forge-inbox.md +37 -0
  57. package/lib/forge-skills/forge-send.md +40 -0
  58. package/lib/forge-skills/forge-status.md +32 -0
  59. package/lib/forge-skills/forge-workspace-sync.md +37 -0
  60. package/lib/help-docs/00-overview.md +7 -1
  61. package/lib/help-docs/01-settings.md +159 -2
  62. package/lib/help-docs/05-pipelines.md +89 -0
  63. package/lib/help-docs/07-projects.md +35 -1
  64. package/lib/help-docs/11-workspace.md +254 -0
  65. package/lib/help-docs/CLAUDE.md +7 -2
  66. package/lib/init.ts +60 -10
  67. package/lib/pipeline.ts +537 -1
  68. package/lib/settings.ts +115 -22
  69. package/lib/skills.ts +249 -372
  70. package/lib/task-manager.ts +113 -33
  71. package/lib/telegram-bot.ts +33 -1
  72. package/lib/workspace/__tests__/state-machine.test.ts +388 -0
  73. package/lib/workspace/__tests__/workspace.test.ts +311 -0
  74. package/lib/workspace/agent-bus.ts +416 -0
  75. package/lib/workspace/agent-worker.ts +667 -0
  76. package/lib/workspace/backends/api-backend.ts +262 -0
  77. package/lib/workspace/backends/cli-backend.ts +479 -0
  78. package/lib/workspace/index.ts +82 -0
  79. package/lib/workspace/manager.ts +136 -0
  80. package/lib/workspace/orchestrator.ts +1914 -0
  81. package/lib/workspace/persistence.ts +310 -0
  82. package/lib/workspace/presets.ts +170 -0
  83. package/lib/workspace/skill-installer.ts +188 -0
  84. package/lib/workspace/smith-memory.ts +498 -0
  85. package/lib/workspace/types.ts +231 -0
  86. package/lib/workspace/watch-manager.ts +288 -0
  87. package/lib/workspace-standalone.ts +814 -0
  88. package/middleware.ts +1 -0
  89. package/next-env.d.ts +1 -1
  90. package/package.json +4 -1
  91. package/src/config/index.ts +12 -1
  92. package/src/core/db/database.ts +1 -0
  93. package/start.sh +7 -0
@@ -0,0 +1,254 @@
1
+ # Workspace (Forge Smiths)
2
+
3
+ ## Overview
4
+
5
+ Workspace is a multi-agent orchestration system. You define a team of **Smiths** (agents) with roles, dependencies, and steps. Smiths run as long-lived daemons, communicate via a message bus, and can be controlled manually or automatically.
6
+
7
+ ## Concepts
8
+
9
+ | Term | Description |
10
+ |------|-------------|
11
+ | **Smith** | A long-running agent instance in the workspace |
12
+ | **Agent Profile** | A reusable configuration (CLI type + env vars + model) that can be assigned to any smith |
13
+ | **Message Bus** | Inter-agent communication system with two message categories |
14
+ | **Input Node** | User-provided requirements node (append-only history) |
15
+ | **Daemon** | Background execution loop that keeps smiths alive and consuming messages |
16
+
17
+ ## Three-Layer State Model
18
+
19
+ Each smith has three independent status layers displayed on the node:
20
+
21
+ | Layer | Values | Description |
22
+ |-------|--------|-------------|
23
+ | **Smith Status** | `down` / `active` | Whether the daemon process is running |
24
+ | **Mode** | `auto` / `manual` | `auto` = daemon-driven, `manual` = user in terminal (purple) |
25
+ | **Task Status** | `idle` / `running` / `done` / `failed` | Current work status |
26
+
27
+ - **Mode controls message consumption**: `manual` pauses inbox processing (same as `running`)
28
+ - **Smith Status controls the daemon loop**: `down` stops the message loop entirely
29
+ - **Task Status tracks work**: unaffected by mode changes
30
+
31
+ ## Dependencies (DAG)
32
+
33
+ Dependencies must form a **directed acyclic graph** (DAG). Circular dependencies are rejected when adding or editing agents.
34
+
35
+ ```
36
+ Input → PM → Engineer → QA → Reviewer
37
+ ```
38
+
39
+ - Upstream agents complete first, then broadcast to downstream
40
+ - Each agent knows its upstream (dependsOn) and the system prevents cycles
41
+
42
+ ## Message System
43
+
44
+ ### Two Message Categories
45
+
46
+ | Category | Direction | Behavior | Use Case |
47
+ |----------|-----------|----------|----------|
48
+ | **Notification** | Follows DAG (upstream → downstream) | Auto-broadcast on completion, downstream discards reverse notifications | "I'm done, here's what I did" |
49
+ | **Ticket** | Any direction (ignores DAG) | 1-to-1, independent lifecycle, retry limits | Bug reports, fix requests |
50
+
51
+ ### Notification Messages (Default)
52
+
53
+ When a smith completes:
54
+ - If it was processing an **upstream** message → broadcast to all downstream agents
55
+ - If it was processing a **downstream** message → no broadcast, just mark the message done (sender checks outbox status)
56
+ - Each message carries a `causedBy` field tracing which inbox message triggered it
57
+
58
+ ### Ticket Messages
59
+
60
+ - Created via `create_ticket` API or forge skills
61
+ - Have their own lifecycle: `open → in_progress → fixed → verified → closed`
62
+ - Retry limit (default 3), exceeding marks ticket as failed
63
+ - Not affected by DAG direction — any agent can ticket any agent
64
+
65
+ ### CausedBy Chain
66
+
67
+ Every outgoing message carries `causedBy` linking to the inbox message that triggered it:
68
+
69
+ ```json
70
+ {
71
+ "from": "qa-123",
72
+ "to": "reviewer-456",
73
+ "action": "upstream_complete",
74
+ "causedBy": {
75
+ "messageId": "msg-789",
76
+ "from": "engineer-111",
77
+ "to": "qa-123"
78
+ }
79
+ }
80
+ ```
81
+
82
+ This enables:
83
+ - **Loop prevention**: Notifications from downstream are silently discarded
84
+ - **Outbox tracking**: Sender can verify their message was processed
85
+ - **Audit trail**: Every message traces back to its trigger
86
+
87
+ ### Message Receive Rules
88
+
89
+ When a message arrives at an agent's inbox:
90
+
91
+ 1. **Tickets** → always accepted (with retry limit check)
92
+ 2. **Notification with causedBy matching own outbox** → accepted (response to my request)
93
+ 3. **Notification from downstream agent** → silently discarded (prevents reverse flow)
94
+ 4. **Notification from upstream or no causedBy** → accepted (normal DAG flow)
95
+
96
+ ### Message Status Flow
97
+
98
+ `pending` → `running` → `done` / `failed`
99
+
100
+ - Only one message processed at a time per agent
101
+ - `currentMessageId` persisted in agent state for crash recovery
102
+
103
+ ## Manual Mode
104
+
105
+ Click the **⌨️** button on any smith to open a terminal:
106
+ - Mode switches to `manual` (purple indicator on node)
107
+ - Inbox message processing pauses (messages stay pending)
108
+ - A tmux session opens with CLI + profile env + forge env vars (`FORGE_AGENT_ID`, `FORGE_WORKSPACE_ID`, `FORGE_PORT`)
109
+ - Forge Skills auto-installed for inter-agent communication
110
+ - Close terminal → mode returns to `auto`, pending messages resume processing
111
+
112
+ ### Forge Skills in Terminal
113
+
114
+ | Skill | Description |
115
+ |-------|-------------|
116
+ | `/forge-send` | Send a message to another smith (blocked if replying to current sender — use for NEW issues only) |
117
+ | `/forge-inbox` | Check incoming messages |
118
+ | `/forge-status` | Check all smiths' status |
119
+ | `/forge-workspace-sync` | Sync progress back to workspace |
120
+
121
+ **Note**: When processing a message from another agent, do NOT use `/forge-send` to reply — the system auto-delivers results via `markMessageDone`. Only use `/forge-send` for new issues to other agents.
122
+
123
+ ## Inbox Management
124
+
125
+ Each smith has an inbox panel with two tabs:
126
+
127
+ ### Inbox Tab
128
+ - Messages received from other smiths
129
+ - Status badges: pending (yellow), running (blue), done (green), failed (red)
130
+ - Ticket messages have purple border + TICKET badge + lifecycle status
131
+ - CausedBy trace shows which agent triggered the message
132
+
133
+ ### Outbox Tab
134
+ - Messages sent by this smith
135
+ - Track delivery status and responses
136
+
137
+ ### Batch Operations
138
+ - **Select all completed** → batch delete done/failed messages
139
+ - **Abort all pending (N)** → cancel all pending messages at once
140
+ - Checkbox selection on individual done/failed messages
141
+
142
+ ## Controls
143
+
144
+ | Action | Description |
145
+ |--------|-------------|
146
+ | **Start** | Launch daemon, begin executing all agents |
147
+ | **Stop** | Stop daemon, kill all workers |
148
+ | **Pause** | Pause a specific smith (stops consuming messages) |
149
+ | **Resume** | Resume a paused smith |
150
+ | **Retry** | Retry a failed smith |
151
+ | **Reset** | Reset smith to idle, clear history |
152
+ | **Open Terminal** | Switch to manual mode, open floating terminal |
153
+ | **Close Terminal** | Return to auto mode, resume message processing |
154
+
155
+ ## Workspace API
156
+
157
+ ```bash
158
+ # List workspaces
159
+ curl http://localhost:8403/api/workspace
160
+
161
+ # Get workspace state
162
+ curl http://localhost:8403/api/workspace/<id>
163
+
164
+ # Agent operations
165
+ curl -X POST http://localhost:8403/api/workspace/<id>/agents \
166
+ -H 'Content-Type: application/json' \
167
+ -d '{"action":"start"}' # start daemon
168
+ -d '{"action":"stop"}' # stop daemon
169
+ -d '{"action":"run", "agentId":"engineer-123"}' # run one smith
170
+ -d '{"action":"open_terminal", "agentId":"engineer-123"}' # manual mode
171
+ -d '{"action":"close_terminal", "agentId":"engineer-123"}' # back to auto
172
+ -d '{"action":"reset", "agentId":"engineer-123"}' # reset to idle
173
+
174
+ # Smith API (via workspace daemon)
175
+ curl -X POST http://localhost:8403/api/workspace/<id>/smith \
176
+ -H 'Content-Type: application/json' \
177
+ -d '{"action":"send","agentId":"$ID","to":"QA","msgAction":"review","content":"Please check"}'
178
+ -d '{"action":"inbox","agentId":"$ID"}'
179
+ -d '{"action":"status","agentId":"$ID"}'
180
+ -d '{"action":"create_ticket","agentId":"$FROM","targetId":"$TO","content":"Bug found"}'
181
+ -d '{"action":"update_ticket","messageId":"$ID","ticketStatus":"fixed"}'
182
+
183
+ # Stream real-time events (SSE)
184
+ curl http://localhost:8403/api/workspace/<id>/stream
185
+ ```
186
+
187
+ ## Watch (Autonomous Monitoring)
188
+
189
+ Agents can autonomously monitor file changes, git commits, or custom commands without relying on messages.
190
+
191
+ ### Configuration
192
+
193
+ In the agent config modal, enable Watch and configure:
194
+ - **Interval**: Check frequency in seconds (min 10, default 60)
195
+ - **Targets**: What to monitor
196
+ - `Directory` — select from project folders, detect file mtime changes
197
+ - `Git` — detect new commits via HEAD hash comparison
198
+ - `Agent Output` — monitor another agent's declared output paths
199
+ - `Command` — run a shell command, detect output changes
200
+ - **On Change**: Action when changes detected
201
+ - `Log` — write to agent log only (default, no token cost)
202
+ - `Analyze` — auto-wake agent to analyze changes (costs tokens)
203
+ - `Approve` — create pending approval, user decides whether to trigger
204
+
205
+ ### Watch Behavior
206
+
207
+ - First check builds a baseline (no alert)
208
+ - Subsequent checks compare timestamps — only files modified since last check are reported
209
+ - No-change heartbeats log to console only (not to files)
210
+ - Change alerts write to `logs.jsonl` and appear in Log panel
211
+ - Watch never sends bus messages — report only, no auto-triggering other agents
212
+
213
+ ## Agent Logs
214
+
215
+ Each agent has a persistent log file (`logs.jsonl`) that survives daemon restarts and agent re-execution.
216
+
217
+ - **Log panel**: Click the log button on any agent node to view
218
+ - **Persistent**: Logs are append-only, not cleared on reset or re-run
219
+ - **Clear**: Use the "Clear" button in the Log panel header to manually wipe logs
220
+ - **Content**: Execution output, watch alerts, bus message receipts, system events
221
+
222
+ ## Forge Skills (Terminal Communication)
223
+
224
+ When in manual mode, agents have forge env vars injected (`FORGE_AGENT_ID`, `FORGE_WORKSPACE_ID`, `FORGE_PORT`) and can use:
225
+
226
+ | Skill | Description |
227
+ |-------|-------------|
228
+ | `/forge-send` | Send a message to another smith |
229
+ | `/forge-inbox` | Check incoming messages |
230
+ | `/forge-status` | Check all smiths' status |
231
+ | `/forge-workspace-sync` | Sync progress back to workspace |
232
+
233
+ **Send protection**: If an agent is currently processing a message from another agent, `/forge-send` to that agent is blocked (returns `skipped: true`). Results are delivered automatically via the message system. Only use `/forge-send` for new issues to other agents.
234
+
235
+ ## Persistence
236
+
237
+ - Workspace state: `~/.forge/workspaces/<id>/state.json`
238
+ - Agent logs: `~/.forge/workspaces/<id>/agents/<agentId>/logs.jsonl`
239
+ - Auto-saved every 10 seconds
240
+ - Atomic writes (temp file → rename) for crash safety
241
+ - Synchronous save on daemon shutdown
242
+ - `currentMessageId` persisted per agent for crash recovery
243
+
244
+ ## Tips
245
+
246
+ 1. **Dependencies must be a DAG** — no circular dependencies allowed
247
+ 2. **Start with Input nodes** — define requirements before adding agent smiths
248
+ 3. **Use profiles** for agents that need custom API endpoints or models
249
+ 4. **Notifications flow downstream** — upstream agents won't receive downstream broadcasts
250
+ 5. **Use tickets for bugs** — tickets ignore DAG direction, have retry limits
251
+ 6. **Open Terminal** for manual intervention — mode switches to manual, inbox pauses
252
+ 7. **Use Watch for monitoring** — detect file changes without message overhead (set action to `log` to avoid token costs)
253
+ 8. **Check Log panel** for execution history and watch alerts — logs persist across restarts
254
+ 9. **Batch operations** — select all completed messages for bulk delete, or abort all pending at once
@@ -25,6 +25,7 @@ Your job is to answer user questions about Forge features, configuration, and tr
25
25
  | `08-rules.md` | CLAUDE.md templates and rule injection |
26
26
  | `09-issue-autofix.md` | GitHub issue auto-fix pipeline |
27
27
  | `10-troubleshooting.md` | Common issues and solutions |
28
+ | `11-workspace.md` | Workspace (Forge Smiths) — multi-agent orchestration, daemon, message bus, profiles |
28
29
 
29
30
  ## Matching questions to docs
30
31
 
@@ -33,9 +34,13 @@ Your job is to answer user questions about Forge features, configuration, and tr
33
34
  - Telegram/notification → `02-telegram.md`
34
35
  - Tunnel/remote/cloudflare → `03-tunnel.md`
35
36
  - Task/background/queue → `04-tasks.md`
36
- - Settings/config → `01-settings.md`
37
+ - Settings/config/agent/profile/provider → `01-settings.md`
37
38
  - Install/start/update → `00-overview.md`
38
39
  - Error/bug/crash → `10-troubleshooting.md`
39
40
  - Skill/marketplace → `06-skills.md`
40
- - Project/favorite → `07-projects.md`
41
+ - Project/favorite/terminal → `07-projects.md`
41
42
  - Rules/CLAUDE.md/template → `08-rules.md`
43
+ - Workspace/smith/daemon/multi-agent/bus/message/ticket → `11-workspace.md`
44
+ - Watch/monitor/detect/file changes/autonomous → `11-workspace.md`
45
+ - Agent profile/env/model/cliType → `01-settings.md` + `11-workspace.md`
46
+ - Agent log/logs/history/clear logs → `11-workspace.md`
package/lib/init.ts CHANGED
@@ -45,18 +45,25 @@ function migrateSecrets() {
45
45
  }
46
46
  }
47
47
 
48
- /** Auto-detect claude binary path if not configured */
49
- function autoDetectClaude() {
48
+ /** Auto-detect agent binaries */
49
+ function autoDetectAgents() {
50
50
  try {
51
51
  const settings = loadSettings();
52
- if (settings.claudePath) return; // already configured
53
- const { execSync } = require('node:child_process');
54
- const path = execSync('which claude', { encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
55
- if (path) {
56
- settings.claudePath = path;
57
- saveSettings(settings);
58
- console.log(`[init] Auto-detected claude: ${path}`);
52
+ // Backward compat: detect claude if not configured
53
+ if (!settings.claudePath) {
54
+ const { execSync } = require('node:child_process');
55
+ try {
56
+ const path = execSync('which claude', { encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
57
+ if (path) {
58
+ settings.claudePath = path;
59
+ saveSettings(settings);
60
+ console.log(`[init] Auto-detected claude: ${path}`);
61
+ }
62
+ } catch {}
59
63
  }
64
+ // Detect all agents
65
+ const { autoDetectAgents: detect } = require('./agents');
66
+ detect();
60
67
  } catch {}
61
68
  }
62
69
 
@@ -80,7 +87,14 @@ export function ensureInitialized() {
80
87
  } catch {}
81
88
 
82
89
  // Auto-detect claude path if not configured
83
- autoDetectClaude();
90
+ autoDetectAgents();
91
+
92
+ // Install/update forge skills to ~/.claude/skills/ on every startup
93
+ try {
94
+ const { installForgeSkills } = require('./workspace/skill-installer');
95
+ installForgeSkills('', '', '', Number(process.env.PORT) || 8403);
96
+ console.log('[init] Forge skills installed/updated');
97
+ } catch {}
84
98
 
85
99
  // Sync help docs + CLAUDE.md to data dir on startup
86
100
  try {
@@ -148,6 +162,7 @@ export function ensureInitialized() {
148
162
  startTelegramBot(); // registers task event listener only
149
163
  startTerminalProcess();
150
164
  startTelegramProcess(); // spawns telegram-standalone
165
+ startWorkspaceProcess(); // spawns workspace-standalone
151
166
 
152
167
  const settings = loadSettings();
153
168
  if (settings.tunnelAutoStart) {
@@ -214,3 +229,38 @@ function startTerminalProcess() {
214
229
  });
215
230
  tester.listen(termPort);
216
231
  }
232
+
233
+ let workspaceChild: ReturnType<typeof spawn> | null = null;
234
+
235
+ function startWorkspaceProcess() {
236
+ if (workspaceChild) return;
237
+
238
+ const wsPort = Number(process.env.WORKSPACE_PORT) || 8405;
239
+
240
+ const net = require('node:net');
241
+ const tester = net.createServer();
242
+ tester.once('error', () => {
243
+ // Port in use — kill stale process and retry after 2s
244
+ console.log(`[workspace] Port ${wsPort} in use, killing stale process...`);
245
+ try { require('node:child_process').execSync(`lsof -ti:${wsPort} | xargs kill -9 2>/dev/null`, { timeout: 3000 }); } catch {}
246
+ setTimeout(() => {
247
+ if (!workspaceChild) launchWorkspaceDaemon();
248
+ }, 2000);
249
+ });
250
+ tester.once('listening', () => {
251
+ tester.close();
252
+ launchWorkspaceDaemon();
253
+ });
254
+ tester.listen(wsPort);
255
+ }
256
+
257
+ function launchWorkspaceDaemon() {
258
+ const script = join(process.cwd(), 'lib', 'workspace-standalone.ts');
259
+ workspaceChild = spawn('npx', ['tsx', script], {
260
+ stdio: ['ignore', 'inherit', 'inherit'],
261
+ env: { ...process.env },
262
+ detached: false,
263
+ });
264
+ workspaceChild.on('exit', () => { workspaceChild = null; });
265
+ console.log('[workspace] Started daemon (pid:', workspaceChild.pid, ')');
266
+ }