better-symphony 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +60 -0
- package/LICENSE +21 -0
- package/README.md +292 -0
- package/dist/web/app.css +2 -0
- package/dist/web/index.html +13 -0
- package/dist/web/main.js +235 -0
- package/package.json +62 -0
- package/src/agent/claude-runner.ts +576 -0
- package/src/agent/protocol.ts +2 -0
- package/src/agent/runner.ts +2 -0
- package/src/agent/session.ts +113 -0
- package/src/cli.ts +354 -0
- package/src/config/loader.ts +379 -0
- package/src/config/types.ts +382 -0
- package/src/index.ts +53 -0
- package/src/linear-cli.ts +414 -0
- package/src/logging/logger.ts +143 -0
- package/src/orchestrator/multi-orchestrator.ts +266 -0
- package/src/orchestrator/orchestrator.ts +1357 -0
- package/src/orchestrator/scheduler.ts +195 -0
- package/src/orchestrator/state.ts +201 -0
- package/src/prompts/github-system-prompt.md +51 -0
- package/src/prompts/linear-system-prompt.md +44 -0
- package/src/tracker/client.ts +577 -0
- package/src/tracker/github-issues-tracker.ts +280 -0
- package/src/tracker/github-pr-tracker.ts +298 -0
- package/src/tracker/index.ts +9 -0
- package/src/tracker/interface.ts +76 -0
- package/src/tracker/linear-tracker.ts +147 -0
- package/src/tracker/queries.ts +281 -0
- package/src/tracker/types.ts +125 -0
- package/src/tui/App.tsx +157 -0
- package/src/tui/LogView.tsx +120 -0
- package/src/tui/StatusBar.tsx +72 -0
- package/src/tui/TabBar.tsx +55 -0
- package/src/tui/sink.ts +47 -0
- package/src/tui/types.ts +6 -0
- package/src/tui/useOrchestrator.ts +244 -0
- package/src/web/server.ts +182 -0
- package/src/web/sink.ts +67 -0
- package/src/web-ui/App.tsx +60 -0
- package/src/web-ui/components/agent-table.tsx +57 -0
- package/src/web-ui/components/header.tsx +72 -0
- package/src/web-ui/components/log-stream.tsx +111 -0
- package/src/web-ui/components/retry-table.tsx +58 -0
- package/src/web-ui/components/stats-cards.tsx +142 -0
- package/src/web-ui/components/ui/badge.tsx +30 -0
- package/src/web-ui/components/ui/button.tsx +39 -0
- package/src/web-ui/components/ui/card.tsx +32 -0
- package/src/web-ui/globals.css +27 -0
- package/src/web-ui/index.html +13 -0
- package/src/web-ui/lib/use-sse.ts +98 -0
- package/src/web-ui/lib/utils.ts +25 -0
- package/src/web-ui/main.tsx +4 -0
- package/src/workspace/hooks.ts +97 -0
- package/src/workspace/manager.ts +211 -0
- package/src/workspace/render-hook.ts +13 -0
- package/workflows/dev.md +127 -0
- package/workflows/github-issues.md +107 -0
- package/workflows/pr-review.md +89 -0
- package/workflows/prd.md +170 -0
- package/workflows/ralph.md +95 -0
- package/workflows/smoke.md +66 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symphony Configuration Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// ── Issue Domain Model ──────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
export interface BlockerRef {
|
|
8
|
+
id: string | null;
|
|
9
|
+
identifier: string | null;
|
|
10
|
+
state: string | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ChildIssue {
|
|
14
|
+
id: string;
|
|
15
|
+
identifier: string;
|
|
16
|
+
title: string;
|
|
17
|
+
description: string | null;
|
|
18
|
+
priority: number | null;
|
|
19
|
+
state: string;
|
|
20
|
+
state_type: string; // "triage" | "backlog" | "unstarted" | "started" | "completed" | "canceled"
|
|
21
|
+
sort_order: number;
|
|
22
|
+
assignee: string | null;
|
|
23
|
+
created_at: Date | null;
|
|
24
|
+
updated_at: Date | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Comment {
|
|
28
|
+
id: string;
|
|
29
|
+
body: string;
|
|
30
|
+
user: string | null;
|
|
31
|
+
created_at: Date | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface Issue {
|
|
35
|
+
id: string;
|
|
36
|
+
identifier: string;
|
|
37
|
+
title: string;
|
|
38
|
+
description: string | null;
|
|
39
|
+
priority: number | null;
|
|
40
|
+
state: string;
|
|
41
|
+
branch_name: string | null;
|
|
42
|
+
url: string | null;
|
|
43
|
+
labels: string[];
|
|
44
|
+
blocked_by: BlockerRef[];
|
|
45
|
+
children: ChildIssue[];
|
|
46
|
+
comments: Comment[];
|
|
47
|
+
created_at: Date | null;
|
|
48
|
+
updated_at: Date | null;
|
|
49
|
+
// GitHub PR specific (optional)
|
|
50
|
+
base_branch?: string;
|
|
51
|
+
author?: string;
|
|
52
|
+
files_changed?: number;
|
|
53
|
+
number?: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── Workflow Definition ─────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
export interface WorkflowDefinition {
|
|
59
|
+
config: WorkflowConfig;
|
|
60
|
+
prompt_template: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface WorkflowConfig {
|
|
64
|
+
tracker?: TrackerConfig;
|
|
65
|
+
polling?: PollingConfig;
|
|
66
|
+
workspace?: WorkspaceConfig;
|
|
67
|
+
hooks?: HooksConfig;
|
|
68
|
+
agent?: AgentConfig;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface TrackerConfig {
|
|
72
|
+
kind: "linear" | "github-pr" | "github-issues";
|
|
73
|
+
// Linear-specific
|
|
74
|
+
endpoint?: string;
|
|
75
|
+
api_key?: string;
|
|
76
|
+
project_slug?: string;
|
|
77
|
+
active_states?: string[] | string;
|
|
78
|
+
terminal_states?: string[] | string;
|
|
79
|
+
error_states?: string[] | string;
|
|
80
|
+
// GitHub-specific
|
|
81
|
+
repo?: string;
|
|
82
|
+
// Shared
|
|
83
|
+
/** Labels that must be present on an issue for it to be picked up */
|
|
84
|
+
required_labels?: string[] | string;
|
|
85
|
+
/** Labels that exclude an issue from being picked up */
|
|
86
|
+
excluded_labels?: string[] | string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface PollingConfig {
|
|
90
|
+
interval_ms?: number | string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface WorkspaceConfig {
|
|
94
|
+
root?: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface HooksConfig {
|
|
98
|
+
after_create?: string;
|
|
99
|
+
before_run?: string;
|
|
100
|
+
after_run?: string;
|
|
101
|
+
before_remove?: string;
|
|
102
|
+
timeout_ms?: number | string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type AgentBinary = "claude" | "codex" | "opencode";
|
|
106
|
+
/** @deprecated Use AgentBinary instead */
|
|
107
|
+
export type AgentHarness = AgentBinary;
|
|
108
|
+
|
|
109
|
+
export interface AgentConfig {
|
|
110
|
+
/** Which CLI binary to use (default: "claude"). Can also be a path. */
|
|
111
|
+
binary?: string;
|
|
112
|
+
/** @deprecated Use `binary` instead */
|
|
113
|
+
harness?: AgentBinary;
|
|
114
|
+
/** Agent mode: "default" or "ralph_loop" (external subtask orchestration) */
|
|
115
|
+
mode?: "default" | "ralph_loop";
|
|
116
|
+
max_concurrent_agents?: number | string;
|
|
117
|
+
max_turns?: number | string;
|
|
118
|
+
max_retries?: number | string;
|
|
119
|
+
max_retry_backoff_ms?: number | string;
|
|
120
|
+
max_concurrent_agents_by_state?: Record<string, number | string>;
|
|
121
|
+
turn_timeout_ms?: number | string;
|
|
122
|
+
stall_timeout_ms?: number | string;
|
|
123
|
+
/** Ralph loop: max subtasks per run (default: unlimited) */
|
|
124
|
+
max_iterations?: number | string;
|
|
125
|
+
/** Run the agent binary inside yolobox */
|
|
126
|
+
yolobox?: boolean;
|
|
127
|
+
/** Extra arguments passed to yolobox (before the -- separator) */
|
|
128
|
+
yolobox_arguments?: string[];
|
|
129
|
+
/** Permission mode (default: "acceptEdits") */
|
|
130
|
+
permission_mode?: string;
|
|
131
|
+
/** Append to system prompt */
|
|
132
|
+
append_system_prompt?: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ── Service Config (Typed View) ─────────────────────────────────
|
|
136
|
+
|
|
137
|
+
export interface ServiceConfig {
|
|
138
|
+
tracker: {
|
|
139
|
+
kind: "linear" | "github-pr" | "github-issues";
|
|
140
|
+
// Linear-specific (required for linear, empty string for github-pr)
|
|
141
|
+
endpoint: string;
|
|
142
|
+
api_key: string;
|
|
143
|
+
project_slug: string;
|
|
144
|
+
active_states: string[];
|
|
145
|
+
terminal_states: string[];
|
|
146
|
+
error_states: string[];
|
|
147
|
+
// GitHub-specific
|
|
148
|
+
repo: string;
|
|
149
|
+
// Shared
|
|
150
|
+
required_labels: string[];
|
|
151
|
+
excluded_labels: string[];
|
|
152
|
+
};
|
|
153
|
+
polling: {
|
|
154
|
+
interval_ms: number;
|
|
155
|
+
};
|
|
156
|
+
workspace: {
|
|
157
|
+
root: string;
|
|
158
|
+
};
|
|
159
|
+
hooks: {
|
|
160
|
+
after_create: string | null;
|
|
161
|
+
before_run: string | null;
|
|
162
|
+
after_run: string | null;
|
|
163
|
+
before_remove: string | null;
|
|
164
|
+
timeout_ms: number;
|
|
165
|
+
};
|
|
166
|
+
agent: {
|
|
167
|
+
binary: AgentBinary;
|
|
168
|
+
mode: "default" | "ralph_loop";
|
|
169
|
+
max_concurrent_agents: number;
|
|
170
|
+
max_turns: number;
|
|
171
|
+
max_retries: number;
|
|
172
|
+
max_retry_backoff_ms: number;
|
|
173
|
+
max_concurrent_agents_by_state: Map<string, number>;
|
|
174
|
+
turn_timeout_ms: number;
|
|
175
|
+
stall_timeout_ms: number;
|
|
176
|
+
max_iterations: number;
|
|
177
|
+
yolobox: boolean;
|
|
178
|
+
yolobox_arguments: string[];
|
|
179
|
+
permission_mode: string;
|
|
180
|
+
append_system_prompt: string | null;
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── Workspace ───────────────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
export interface Workspace {
|
|
187
|
+
path: string;
|
|
188
|
+
workspace_key: string;
|
|
189
|
+
created_now: boolean;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ── Run Attempt ─────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
export type RunAttemptStatus =
|
|
195
|
+
| "PreparingWorkspace"
|
|
196
|
+
| "BuildingPrompt"
|
|
197
|
+
| "LaunchingAgentProcess"
|
|
198
|
+
| "InitializingSession"
|
|
199
|
+
| "StreamingTurn"
|
|
200
|
+
| "Finishing"
|
|
201
|
+
| "Succeeded"
|
|
202
|
+
| "Failed"
|
|
203
|
+
| "TimedOut"
|
|
204
|
+
| "Stalled"
|
|
205
|
+
| "CanceledByReconciliation";
|
|
206
|
+
|
|
207
|
+
export interface RunAttempt {
|
|
208
|
+
issue_id: string;
|
|
209
|
+
issue_identifier: string;
|
|
210
|
+
attempt: number | null;
|
|
211
|
+
workspace_path: string;
|
|
212
|
+
started_at: Date;
|
|
213
|
+
status: RunAttemptStatus;
|
|
214
|
+
error?: string;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ── Live Session ────────────────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
export interface LiveSession {
|
|
220
|
+
session_id: string;
|
|
221
|
+
thread_id: string;
|
|
222
|
+
turn_id: string;
|
|
223
|
+
process_pid: string | null;
|
|
224
|
+
last_event: string | null;
|
|
225
|
+
last_activity_at: Date | null;
|
|
226
|
+
last_message: string | null;
|
|
227
|
+
input_tokens: number;
|
|
228
|
+
output_tokens: number;
|
|
229
|
+
total_tokens: number;
|
|
230
|
+
last_reported_input_tokens: number;
|
|
231
|
+
last_reported_output_tokens: number;
|
|
232
|
+
last_reported_total_tokens: number;
|
|
233
|
+
turn_count: number;
|
|
234
|
+
cost_usd: number;
|
|
235
|
+
duration_ms: number;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ── Retry Entry ─────────────────────────────────────────────────
|
|
239
|
+
|
|
240
|
+
export interface RetryEntry {
|
|
241
|
+
issue_id: string;
|
|
242
|
+
identifier: string;
|
|
243
|
+
attempt: number;
|
|
244
|
+
due_at_ms: number;
|
|
245
|
+
timer_handle: Timer;
|
|
246
|
+
error: string | null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ── Running Entry ───────────────────────────────────────────────
|
|
250
|
+
|
|
251
|
+
export interface RunningEntry {
|
|
252
|
+
issue: Issue;
|
|
253
|
+
attempt: RunAttempt;
|
|
254
|
+
session: LiveSession | null;
|
|
255
|
+
worker: Promise<void>;
|
|
256
|
+
abortController: AbortController;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// ── Token/Rate Limit Tracking ───────────────────────────────────
|
|
260
|
+
|
|
261
|
+
export interface TokenTotals {
|
|
262
|
+
input_tokens: number;
|
|
263
|
+
output_tokens: number;
|
|
264
|
+
total_tokens: number;
|
|
265
|
+
seconds_running: number;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export interface RateLimitInfo {
|
|
269
|
+
requests_limit?: number;
|
|
270
|
+
requests_remaining?: number;
|
|
271
|
+
requests_reset?: number;
|
|
272
|
+
tokens_limit?: number;
|
|
273
|
+
tokens_remaining?: number;
|
|
274
|
+
tokens_reset?: number;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ── Orchestrator Runtime State ──────────────────────────────────
|
|
278
|
+
|
|
279
|
+
export interface OrchestratorState {
|
|
280
|
+
poll_interval_ms: number;
|
|
281
|
+
max_concurrent_agents: number;
|
|
282
|
+
running: Map<string, RunningEntry>;
|
|
283
|
+
claimed: Set<string>;
|
|
284
|
+
retry_attempts: Map<string, RetryEntry>;
|
|
285
|
+
completed: Set<string>;
|
|
286
|
+
token_totals: TokenTotals;
|
|
287
|
+
rate_limits: RateLimitInfo | null;
|
|
288
|
+
ended_seconds: number;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ── Agent Events ────────────────────────────────────────────────
|
|
292
|
+
|
|
293
|
+
export type AgentEventType =
|
|
294
|
+
| "session_started"
|
|
295
|
+
| "startup_failed"
|
|
296
|
+
| "turn_completed"
|
|
297
|
+
| "turn_failed"
|
|
298
|
+
| "turn_cancelled"
|
|
299
|
+
| "turn_ended_with_error"
|
|
300
|
+
| "turn_input_required"
|
|
301
|
+
| "tool_use"
|
|
302
|
+
| "tool_result"
|
|
303
|
+
| "assistant_message"
|
|
304
|
+
| "notification"
|
|
305
|
+
| "other_message"
|
|
306
|
+
| "malformed"
|
|
307
|
+
| "token_usage_updated";
|
|
308
|
+
|
|
309
|
+
export interface AgentEvent {
|
|
310
|
+
event: AgentEventType;
|
|
311
|
+
timestamp: Date;
|
|
312
|
+
process_pid?: string;
|
|
313
|
+
usage?: {
|
|
314
|
+
input_tokens?: number;
|
|
315
|
+
output_tokens?: number;
|
|
316
|
+
total_tokens?: number;
|
|
317
|
+
};
|
|
318
|
+
cost_usd?: number;
|
|
319
|
+
duration_ms?: number;
|
|
320
|
+
payload?: unknown;
|
|
321
|
+
message?: string;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ── Errors ──────────────────────────────────────────────────────
|
|
325
|
+
|
|
326
|
+
export type WorkflowErrorClass =
|
|
327
|
+
| "missing_workflow_file"
|
|
328
|
+
| "workflow_parse_error"
|
|
329
|
+
| "workflow_front_matter_not_a_map"
|
|
330
|
+
| "template_parse_error"
|
|
331
|
+
| "template_render_error";
|
|
332
|
+
|
|
333
|
+
export class WorkflowError extends Error {
|
|
334
|
+
constructor(
|
|
335
|
+
public readonly errorClass: WorkflowErrorClass,
|
|
336
|
+
message: string
|
|
337
|
+
) {
|
|
338
|
+
super(message);
|
|
339
|
+
this.name = "WorkflowError";
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export type TrackerErrorClass =
|
|
344
|
+
| "unsupported_tracker_kind"
|
|
345
|
+
| "missing_tracker_api_key"
|
|
346
|
+
| "missing_tracker_project_slug"
|
|
347
|
+
| "linear_api_request"
|
|
348
|
+
| "linear_api_status"
|
|
349
|
+
| "linear_graphql_errors"
|
|
350
|
+
| "linear_unknown_payload"
|
|
351
|
+
| "linear_missing_end_cursor";
|
|
352
|
+
|
|
353
|
+
export class TrackerError extends Error {
|
|
354
|
+
constructor(
|
|
355
|
+
public readonly errorClass: TrackerErrorClass,
|
|
356
|
+
message: string
|
|
357
|
+
) {
|
|
358
|
+
super(message);
|
|
359
|
+
this.name = "TrackerError";
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export type AgentErrorClass =
|
|
364
|
+
| "agent_not_found"
|
|
365
|
+
| "invalid_workspace_cwd"
|
|
366
|
+
| "response_timeout"
|
|
367
|
+
| "turn_timeout"
|
|
368
|
+
| "process_exit"
|
|
369
|
+
| "response_error"
|
|
370
|
+
| "turn_failed"
|
|
371
|
+
| "turn_cancelled"
|
|
372
|
+
| "turn_input_required";
|
|
373
|
+
|
|
374
|
+
export class AgentError extends Error {
|
|
375
|
+
constructor(
|
|
376
|
+
public readonly errorClass: AgentErrorClass,
|
|
377
|
+
message: string
|
|
378
|
+
) {
|
|
379
|
+
super(message);
|
|
380
|
+
this.name = "AgentError";
|
|
381
|
+
}
|
|
382
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symphony - Coding Agent Orchestrator
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Config
|
|
6
|
+
export * from "./config/types.js";
|
|
7
|
+
export * from "./config/loader.js";
|
|
8
|
+
|
|
9
|
+
// Tracker
|
|
10
|
+
export { LinearClient } from "./tracker/client.js";
|
|
11
|
+
export * from "./tracker/types.js";
|
|
12
|
+
|
|
13
|
+
// Workspace
|
|
14
|
+
export { WorkspaceManager, sanitizeWorkspaceKey, validateWorkspacePath } from "./workspace/manager.js";
|
|
15
|
+
export { executeHook } from "./workspace/hooks.js";
|
|
16
|
+
|
|
17
|
+
// Agent
|
|
18
|
+
export { ClaudeRunner } from "./agent/claude-runner.js";
|
|
19
|
+
export {
|
|
20
|
+
createSession,
|
|
21
|
+
updateSessionTurnId,
|
|
22
|
+
updateSessionEvent,
|
|
23
|
+
updateSessionTokens,
|
|
24
|
+
createEmptyTotals,
|
|
25
|
+
parseRateLimits,
|
|
26
|
+
} from "./agent/session.js";
|
|
27
|
+
|
|
28
|
+
// Orchestrator
|
|
29
|
+
export { Orchestrator } from "./orchestrator/orchestrator.js";
|
|
30
|
+
export { MultiOrchestrator } from "./orchestrator/multi-orchestrator.js";
|
|
31
|
+
export {
|
|
32
|
+
createOrchestratorState,
|
|
33
|
+
claimIssue,
|
|
34
|
+
releaseClaim,
|
|
35
|
+
isIssueClaimed,
|
|
36
|
+
addRunning,
|
|
37
|
+
removeRunning,
|
|
38
|
+
getRunning,
|
|
39
|
+
isIssueRunning,
|
|
40
|
+
getRunningCount,
|
|
41
|
+
getRunningByState,
|
|
42
|
+
addRetry,
|
|
43
|
+
removeRetry,
|
|
44
|
+
getRetry,
|
|
45
|
+
updateRateLimits,
|
|
46
|
+
createSnapshot,
|
|
47
|
+
type RuntimeSnapshot,
|
|
48
|
+
} from "./orchestrator/state.js";
|
|
49
|
+
export * from "./orchestrator/scheduler.js";
|
|
50
|
+
|
|
51
|
+
// Logging
|
|
52
|
+
export { logger, createConsoleSink, createFileSink } from "./logging/logger.js";
|
|
53
|
+
export type { LogLevel, LogContext, LogEntry, LogSink } from "./logging/logger.js";
|