fleet-commander-ai 0.0.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.
- package/LICENSE +202 -0
- package/README.md +159 -0
- package/bin/fleet-commander-mcp.js +27 -0
- package/bin/fleet-commander.js +22 -0
- package/dist/client/assets/index-CHukC8Hq.js +188 -0
- package/dist/client/assets/index-CHukC8Hq.js.map +1 -0
- package/dist/client/assets/index-DvMjcYbg.css +1 -0
- package/dist/client/index.html +13 -0
- package/dist/server/config.d.ts +51 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +104 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/db.d.ts +388 -0
- package/dist/server/db.d.ts.map +1 -0
- package/dist/server/db.js +1524 -0
- package/dist/server/db.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +162 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/mcp/index.d.ts +2 -0
- package/dist/server/mcp/index.d.ts.map +1 -0
- package/dist/server/mcp/index.js +112 -0
- package/dist/server/mcp/index.js.map +1 -0
- package/dist/server/mcp/tools/add-project.d.ts +9 -0
- package/dist/server/mcp/tools/add-project.d.ts.map +1 -0
- package/dist/server/mcp/tools/add-project.js +58 -0
- package/dist/server/mcp/tools/add-project.js.map +1 -0
- package/dist/server/mcp/tools/get-team-timeline.d.ts +9 -0
- package/dist/server/mcp/tools/get-team-timeline.d.ts.map +1 -0
- package/dist/server/mcp/tools/get-team-timeline.js +48 -0
- package/dist/server/mcp/tools/get-team-timeline.js.map +1 -0
- package/dist/server/mcp/tools/get-team.d.ts +9 -0
- package/dist/server/mcp/tools/get-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/get-team.js +48 -0
- package/dist/server/mcp/tools/get-team.js.map +1 -0
- package/dist/server/mcp/tools/get-usage.d.ts +9 -0
- package/dist/server/mcp/tools/get-usage.d.ts.map +1 -0
- package/dist/server/mcp/tools/get-usage.js +33 -0
- package/dist/server/mcp/tools/get-usage.js.map +1 -0
- package/dist/server/mcp/tools/launch-team.d.ts +8 -0
- package/dist/server/mcp/tools/launch-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/launch-team.js +49 -0
- package/dist/server/mcp/tools/launch-team.js.map +1 -0
- package/dist/server/mcp/tools/list-issues.d.ts +9 -0
- package/dist/server/mcp/tools/list-issues.d.ts.map +1 -0
- package/dist/server/mcp/tools/list-issues.js +47 -0
- package/dist/server/mcp/tools/list-issues.js.map +1 -0
- package/dist/server/mcp/tools/list-projects.d.ts +9 -0
- package/dist/server/mcp/tools/list-projects.d.ts.map +1 -0
- package/dist/server/mcp/tools/list-projects.js +32 -0
- package/dist/server/mcp/tools/list-projects.js.map +1 -0
- package/dist/server/mcp/tools/list-teams.d.ts +9 -0
- package/dist/server/mcp/tools/list-teams.d.ts.map +1 -0
- package/dist/server/mcp/tools/list-teams.js +43 -0
- package/dist/server/mcp/tools/list-teams.js.map +1 -0
- package/dist/server/mcp/tools/restart-team.d.ts +8 -0
- package/dist/server/mcp/tools/restart-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/restart-team.js +46 -0
- package/dist/server/mcp/tools/restart-team.js.map +1 -0
- package/dist/server/mcp/tools/send-message.d.ts +9 -0
- package/dist/server/mcp/tools/send-message.d.ts.map +1 -0
- package/dist/server/mcp/tools/send-message.js +48 -0
- package/dist/server/mcp/tools/send-message.js.map +1 -0
- package/dist/server/mcp/tools/stop-team.d.ts +8 -0
- package/dist/server/mcp/tools/stop-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/stop-team.js +46 -0
- package/dist/server/mcp/tools/stop-team.js.map +1 -0
- package/dist/server/mcp/tools/system-health.d.ts +9 -0
- package/dist/server/mcp/tools/system-health.d.ts.map +1 -0
- package/dist/server/mcp/tools/system-health.js +32 -0
- package/dist/server/mcp/tools/system-health.js.map +1 -0
- package/dist/server/middleware/error-handler.d.ts +32 -0
- package/dist/server/middleware/error-handler.d.ts.map +1 -0
- package/dist/server/middleware/error-handler.js +65 -0
- package/dist/server/middleware/error-handler.js.map +1 -0
- package/dist/server/routes/events.d.ts +16 -0
- package/dist/server/routes/events.d.ts.map +1 -0
- package/dist/server/routes/events.js +164 -0
- package/dist/server/routes/events.js.map +1 -0
- package/dist/server/routes/issues.d.ts +4 -0
- package/dist/server/routes/issues.d.ts.map +1 -0
- package/dist/server/routes/issues.js +198 -0
- package/dist/server/routes/issues.js.map +1 -0
- package/dist/server/routes/project-groups.d.ts +4 -0
- package/dist/server/routes/project-groups.d.ts.map +1 -0
- package/dist/server/routes/project-groups.js +124 -0
- package/dist/server/routes/project-groups.js.map +1 -0
- package/dist/server/routes/projects.d.ts +4 -0
- package/dist/server/routes/projects.d.ts.map +1 -0
- package/dist/server/routes/projects.js +319 -0
- package/dist/server/routes/projects.js.map +1 -0
- package/dist/server/routes/prs.d.ts +4 -0
- package/dist/server/routes/prs.d.ts.map +1 -0
- package/dist/server/routes/prs.js +186 -0
- package/dist/server/routes/prs.js.map +1 -0
- package/dist/server/routes/query.d.ts +4 -0
- package/dist/server/routes/query.d.ts.map +1 -0
- package/dist/server/routes/query.js +82 -0
- package/dist/server/routes/query.js.map +1 -0
- package/dist/server/routes/state-machine.d.ts +4 -0
- package/dist/server/routes/state-machine.d.ts.map +1 -0
- package/dist/server/routes/state-machine.js +86 -0
- package/dist/server/routes/state-machine.js.map +1 -0
- package/dist/server/routes/stream.d.ts +18 -0
- package/dist/server/routes/stream.d.ts.map +1 -0
- package/dist/server/routes/stream.js +62 -0
- package/dist/server/routes/stream.js.map +1 -0
- package/dist/server/routes/system.d.ts +4 -0
- package/dist/server/routes/system.d.ts.map +1 -0
- package/dist/server/routes/system.js +225 -0
- package/dist/server/routes/system.js.map +1 -0
- package/dist/server/routes/teams.d.ts +4 -0
- package/dist/server/routes/teams.d.ts.map +1 -0
- package/dist/server/routes/teams.js +570 -0
- package/dist/server/routes/teams.js.map +1 -0
- package/dist/server/routes/usage.d.ts +4 -0
- package/dist/server/routes/usage.d.ts.map +1 -0
- package/dist/server/routes/usage.js +80 -0
- package/dist/server/routes/usage.js.map +1 -0
- package/dist/server/schema.sql +267 -0
- package/dist/server/services/cc-query.d.ts +20 -0
- package/dist/server/services/cc-query.d.ts.map +1 -0
- package/dist/server/services/cc-query.js +352 -0
- package/dist/server/services/cc-query.js.map +1 -0
- package/dist/server/services/cleanup.d.ts +15 -0
- package/dist/server/services/cleanup.d.ts.map +1 -0
- package/dist/server/services/cleanup.js +232 -0
- package/dist/server/services/cleanup.js.map +1 -0
- package/dist/server/services/diagnostics-service.d.ts +85 -0
- package/dist/server/services/diagnostics-service.d.ts.map +1 -0
- package/dist/server/services/diagnostics-service.js +242 -0
- package/dist/server/services/diagnostics-service.js.map +1 -0
- package/dist/server/services/event-collector.d.ts +125 -0
- package/dist/server/services/event-collector.d.ts.map +1 -0
- package/dist/server/services/event-collector.js +299 -0
- package/dist/server/services/event-collector.js.map +1 -0
- package/dist/server/services/event-service.d.ts +22 -0
- package/dist/server/services/event-service.d.ts.map +1 -0
- package/dist/server/services/event-service.js +53 -0
- package/dist/server/services/event-service.js.map +1 -0
- package/dist/server/services/github-poller.d.ts +68 -0
- package/dist/server/services/github-poller.d.ts.map +1 -0
- package/dist/server/services/github-poller.js +563 -0
- package/dist/server/services/github-poller.js.map +1 -0
- package/dist/server/services/issue-fetcher.d.ts +231 -0
- package/dist/server/services/issue-fetcher.d.ts.map +1 -0
- package/dist/server/services/issue-fetcher.js +1053 -0
- package/dist/server/services/issue-fetcher.js.map +1 -0
- package/dist/server/services/issue-service.d.ts +102 -0
- package/dist/server/services/issue-service.d.ts.map +1 -0
- package/dist/server/services/issue-service.js +279 -0
- package/dist/server/services/issue-service.js.map +1 -0
- package/dist/server/services/message-template-service.d.ts +39 -0
- package/dist/server/services/message-template-service.d.ts.map +1 -0
- package/dist/server/services/message-template-service.js +87 -0
- package/dist/server/services/message-template-service.js.map +1 -0
- package/dist/server/services/pr-service.d.ts +73 -0
- package/dist/server/services/pr-service.d.ts.map +1 -0
- package/dist/server/services/pr-service.js +231 -0
- package/dist/server/services/pr-service.js.map +1 -0
- package/dist/server/services/project-group-service.d.ts +64 -0
- package/dist/server/services/project-group-service.d.ts.map +1 -0
- package/dist/server/services/project-group-service.js +149 -0
- package/dist/server/services/project-group-service.js.map +1 -0
- package/dist/server/services/project-service.d.ts +161 -0
- package/dist/server/services/project-service.d.ts.map +1 -0
- package/dist/server/services/project-service.js +623 -0
- package/dist/server/services/project-service.js.map +1 -0
- package/dist/server/services/service-error.d.ts +25 -0
- package/dist/server/services/service-error.d.ts.map +1 -0
- package/dist/server/services/service-error.js +49 -0
- package/dist/server/services/service-error.js.map +1 -0
- package/dist/server/services/sse-broker.d.ts +144 -0
- package/dist/server/services/sse-broker.d.ts.map +1 -0
- package/dist/server/services/sse-broker.js +111 -0
- package/dist/server/services/sse-broker.js.map +1 -0
- package/dist/server/services/startup-recovery.d.ts +10 -0
- package/dist/server/services/startup-recovery.d.ts.map +1 -0
- package/dist/server/services/startup-recovery.js +122 -0
- package/dist/server/services/startup-recovery.js.map +1 -0
- package/dist/server/services/stuck-detector.d.ts +20 -0
- package/dist/server/services/stuck-detector.d.ts.map +1 -0
- package/dist/server/services/stuck-detector.js +167 -0
- package/dist/server/services/stuck-detector.js.map +1 -0
- package/dist/server/services/stuck-detector.test.d.ts +2 -0
- package/dist/server/services/stuck-detector.test.d.ts.map +1 -0
- package/dist/server/services/stuck-detector.test.js +363 -0
- package/dist/server/services/stuck-detector.test.js.map +1 -0
- package/dist/server/services/team-manager.d.ts +188 -0
- package/dist/server/services/team-manager.d.ts.map +1 -0
- package/dist/server/services/team-manager.js +1869 -0
- package/dist/server/services/team-manager.js.map +1 -0
- package/dist/server/services/team-service.d.ts +251 -0
- package/dist/server/services/team-service.d.ts.map +1 -0
- package/dist/server/services/team-service.js +707 -0
- package/dist/server/services/team-service.js.map +1 -0
- package/dist/server/services/usage-service.d.ts +42 -0
- package/dist/server/services/usage-service.d.ts.map +1 -0
- package/dist/server/services/usage-service.js +101 -0
- package/dist/server/services/usage-service.js.map +1 -0
- package/dist/server/services/usage-tracker.d.ts +68 -0
- package/dist/server/services/usage-tracker.d.ts.map +1 -0
- package/dist/server/services/usage-tracker.js +220 -0
- package/dist/server/services/usage-tracker.js.map +1 -0
- package/dist/server/utils/build-timeline.d.ts +32 -0
- package/dist/server/utils/build-timeline.d.ts.map +1 -0
- package/dist/server/utils/build-timeline.js +142 -0
- package/dist/server/utils/build-timeline.js.map +1 -0
- package/dist/server/utils/find-git-bash.d.ts +10 -0
- package/dist/server/utils/find-git-bash.d.ts.map +1 -0
- package/dist/server/utils/find-git-bash.js +46 -0
- package/dist/server/utils/find-git-bash.js.map +1 -0
- package/dist/server/utils/hook-installer.d.ts +20 -0
- package/dist/server/utils/hook-installer.d.ts.map +1 -0
- package/dist/server/utils/hook-installer.js +90 -0
- package/dist/server/utils/hook-installer.js.map +1 -0
- package/dist/server/utils/process-utils.d.ts +10 -0
- package/dist/server/utils/process-utils.d.ts.map +1 -0
- package/dist/server/utils/process-utils.js +33 -0
- package/dist/server/utils/process-utils.js.map +1 -0
- package/dist/server/utils/resolve-claude-path.d.ts +4 -0
- package/dist/server/utils/resolve-claude-path.d.ts.map +1 -0
- package/dist/server/utils/resolve-claude-path.js +66 -0
- package/dist/server/utils/resolve-claude-path.js.map +1 -0
- package/dist/server/utils/resolve-message.d.ts +10 -0
- package/dist/server/utils/resolve-message.d.ts.map +1 -0
- package/dist/server/utils/resolve-message.js +27 -0
- package/dist/server/utils/resolve-message.js.map +1 -0
- package/dist/shared/message-templates.d.ts +9 -0
- package/dist/shared/message-templates.d.ts.map +1 -0
- package/dist/shared/message-templates.js +88 -0
- package/dist/shared/message-templates.js.map +1 -0
- package/dist/shared/state-machine.d.ts +28 -0
- package/dist/shared/state-machine.d.ts.map +1 -0
- package/dist/shared/state-machine.js +282 -0
- package/dist/shared/state-machine.js.map +1 -0
- package/dist/shared/types.d.ts +404 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +5 -0
- package/dist/shared/types.js.map +1 -0
- package/hooks/DESIGN.md +562 -0
- package/hooks/on_notification.sh +9 -0
- package/hooks/on_post_tool_use.sh +9 -0
- package/hooks/on_pre_compact.sh +10 -0
- package/hooks/on_session_end.sh +8 -0
- package/hooks/on_session_start.sh +8 -0
- package/hooks/on_stop.sh +8 -0
- package/hooks/on_stop_failure.sh +8 -0
- package/hooks/on_subagent_start.sh +8 -0
- package/hooks/on_subagent_stop.sh +8 -0
- package/hooks/on_teammate_idle.sh +8 -0
- package/hooks/on_tool_error.sh +8 -0
- package/hooks/send_event.sh +101 -0
- package/hooks/settings.json.example +120 -0
- package/package.json +93 -0
- package/prompts/default-prompt.md +16 -0
- package/scripts/install.ps1 +22 -0
- package/scripts/install.sh +229 -0
- package/scripts/launch.js +64 -0
- package/scripts/uninstall.ps1 +22 -0
- package/scripts/uninstall.sh +123 -0
- package/templates/agents/fleet-dev.md +162 -0
- package/templates/agents/fleet-planner.md +263 -0
- package/templates/agents/fleet-reviewer.md +309 -0
- package/templates/archive/fleet-coordinator.md +128 -0
- package/templates/archive/fleet-dev-csharp.md +74 -0
- package/templates/archive/fleet-dev-devops.md +76 -0
- package/templates/archive/fleet-dev-fsharp.md +83 -0
- package/templates/archive/fleet-dev-generic.md +64 -0
- package/templates/archive/fleet-dev-python.md +75 -0
- package/templates/archive/fleet-dev-typescript.md +76 -0
- package/templates/guides/api-design.md +159 -0
- package/templates/guides/csharp-conventions.md +182 -0
- package/templates/guides/devops-conventions.md +192 -0
- package/templates/guides/fsharp-conventions.md +201 -0
- package/templates/guides/python-conventions.md +146 -0
- package/templates/guides/sql-database.md +125 -0
- package/templates/guides/testing-strategies.md +123 -0
- package/templates/guides/typescript-conventions.md +130 -0
- package/templates/workflow.md +513 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Collector — Hook event ingestion service
|
|
3
|
+
*
|
|
4
|
+
* Receives events from Claude Code hooks (via POST /api/events),
|
|
5
|
+
* stores them in SQLite, triggers state transitions, and broadcasts
|
|
6
|
+
* via SSE. Includes throttling for high-volume tool_use events.
|
|
7
|
+
*
|
|
8
|
+
* Data flow:
|
|
9
|
+
* Claude hook -> send_event.sh -> POST /api/events -> EventCollector
|
|
10
|
+
* -> SQLite insert -> state machine evaluation -> SSE broadcast
|
|
11
|
+
*
|
|
12
|
+
* Throttling:
|
|
13
|
+
* tool_use events from the same team within 5 seconds are deduplicated.
|
|
14
|
+
* last_event_at is ALWAYS updated (heartbeat must work for stuck detection).
|
|
15
|
+
* Non-tool_use events are NEVER throttled.
|
|
16
|
+
*/
|
|
17
|
+
import config from '../config.js';
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Throttle state — module-level, persists across requests
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/** Track last tool_use event time per team for throttling */
|
|
22
|
+
const lastToolUseByTeam = new Map();
|
|
23
|
+
const subagentTrackers = new Map();
|
|
24
|
+
/** Throttle window: tool_use events from the same team within this period are deduplicated */
|
|
25
|
+
const TOOL_USE_THROTTLE_MS = 5000; // 5 seconds
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Agent name normalization
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
/**
|
|
30
|
+
* Normalize agent name for consistent matching across roster and messages.
|
|
31
|
+
*
|
|
32
|
+
* - Strips `fleet-` prefix (e.g. "fleet-dev" -> "dev", "fleet-planner" -> "planner")
|
|
33
|
+
* - Maps empty/null/undefined to "team-lead" (the main CC process has no agent_type)
|
|
34
|
+
* - Returns lowercase trimmed name
|
|
35
|
+
*/
|
|
36
|
+
export function normalizeAgentName(name) {
|
|
37
|
+
if (!name || name.trim() === '')
|
|
38
|
+
return 'team-lead';
|
|
39
|
+
let normalized = name.trim();
|
|
40
|
+
// Strip "fleet-" prefix for consistent cross-reference
|
|
41
|
+
if (normalized.startsWith('fleet-')) {
|
|
42
|
+
normalized = normalized.slice(6);
|
|
43
|
+
}
|
|
44
|
+
return normalized;
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Event type normalization
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
/**
|
|
50
|
+
* Normalize event type strings from hooks to canonical EventType values.
|
|
51
|
+
* Hooks may send "tool_use", "session_start", etc. (snake_case).
|
|
52
|
+
* The DB schema uses PascalCase: "ToolUse", "SessionStart", etc.
|
|
53
|
+
*/
|
|
54
|
+
function normalizeEventType(raw) {
|
|
55
|
+
const map = {
|
|
56
|
+
'tool_use': 'ToolUse',
|
|
57
|
+
'session_start': 'SessionStart',
|
|
58
|
+
'session_end': 'SessionEnd',
|
|
59
|
+
'stop': 'Stop',
|
|
60
|
+
'stop_failure': 'StopFailure',
|
|
61
|
+
'subagent_start': 'SubagentStart',
|
|
62
|
+
'subagent_stop': 'SubagentStop',
|
|
63
|
+
'notification': 'Notification',
|
|
64
|
+
'tool_error': 'ToolError',
|
|
65
|
+
'pre_compact': 'PreCompact',
|
|
66
|
+
'teammate_idle': 'TeammateIdle',
|
|
67
|
+
};
|
|
68
|
+
return map[raw.toLowerCase()] || raw;
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Core processing
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
/**
|
|
74
|
+
* Process an incoming event from a Claude Code hook.
|
|
75
|
+
*
|
|
76
|
+
* Steps:
|
|
77
|
+
* 1. Look up team by worktree name
|
|
78
|
+
* 2. If team is idle or stuck, transition back to running
|
|
79
|
+
* 3. Always update last_event_at (heartbeat for stuck detection)
|
|
80
|
+
* 4. Throttle tool_use events (same team, within 5s window)
|
|
81
|
+
* 5. Insert event into DB
|
|
82
|
+
* 6. Broadcast via SSE
|
|
83
|
+
*
|
|
84
|
+
* @returns ProcessEventResult with event_id (null if deduplicated), team_id, and processed flag
|
|
85
|
+
*/
|
|
86
|
+
export function processEvent(payload, db, sse, messageSender) {
|
|
87
|
+
// ── Validate required fields ─────────────────────────────────────
|
|
88
|
+
if (!payload.event || !payload.team) {
|
|
89
|
+
throw new EventCollectorError('Missing required fields: event and team', 'VALIDATION_ERROR');
|
|
90
|
+
}
|
|
91
|
+
// ── Look up team ─────────────────────────────────────────────────
|
|
92
|
+
const team = db.getTeamByWorktree(payload.team);
|
|
93
|
+
if (!team) {
|
|
94
|
+
throw new EventCollectorError(`Team not found for worktree: ${payload.team}`, 'TEAM_NOT_FOUND');
|
|
95
|
+
}
|
|
96
|
+
const teamId = team.id;
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
const nowIso = new Date(now).toISOString();
|
|
99
|
+
// ── State transition: idle/stuck -> running on activity events ─────
|
|
100
|
+
// Most events from an idle or stuck team prove it is alive and doing
|
|
101
|
+
// work. However, dormancy-indicating events (stop, session_end) mean
|
|
102
|
+
// the agent finished its turn or the session terminated — they must
|
|
103
|
+
// NOT reset the idle/stuck status because the agent is not actively
|
|
104
|
+
// working. Those events still update lastEventAt (line 206) so the
|
|
105
|
+
// stuck detector has accurate timing data.
|
|
106
|
+
//
|
|
107
|
+
// This MUST happen before the throttle check so that even
|
|
108
|
+
// deduplicated tool_use events trigger the recovery transition.
|
|
109
|
+
const DORMANCY_EVENTS = new Set(['stop', 'stop_failure', 'session_end']);
|
|
110
|
+
const eventNameLower = payload.event.toLowerCase();
|
|
111
|
+
if ((team.status === 'idle' || team.status === 'stuck') && !DORMANCY_EVENTS.has(eventNameLower)) {
|
|
112
|
+
db.insertTransition({
|
|
113
|
+
teamId,
|
|
114
|
+
fromStatus: team.status,
|
|
115
|
+
toStatus: 'running',
|
|
116
|
+
trigger: 'hook',
|
|
117
|
+
reason: `Activity resumed (${payload.event} event received)`,
|
|
118
|
+
});
|
|
119
|
+
db.updateTeam(teamId, {
|
|
120
|
+
status: 'running',
|
|
121
|
+
});
|
|
122
|
+
sse.broadcast('team_status_changed', {
|
|
123
|
+
team_id: teamId,
|
|
124
|
+
status: 'running',
|
|
125
|
+
previous_status: team.status,
|
|
126
|
+
}, teamId);
|
|
127
|
+
}
|
|
128
|
+
// ── State transition: launching -> running only on session_start/subagent_start
|
|
129
|
+
// Other events during launching may be noise; wait for an actual session start.
|
|
130
|
+
if (team.status === 'launching') {
|
|
131
|
+
const evt = payload.event.toLowerCase();
|
|
132
|
+
if (evt === 'session_start' || evt === 'subagent_start') {
|
|
133
|
+
db.insertTransition({
|
|
134
|
+
teamId,
|
|
135
|
+
fromStatus: 'launching',
|
|
136
|
+
toStatus: 'running',
|
|
137
|
+
trigger: 'hook',
|
|
138
|
+
reason: `First ${evt} event received`,
|
|
139
|
+
});
|
|
140
|
+
db.updateTeam(teamId, {
|
|
141
|
+
status: 'running',
|
|
142
|
+
});
|
|
143
|
+
sse.broadcast('team_status_changed', {
|
|
144
|
+
team_id: teamId,
|
|
145
|
+
status: 'running',
|
|
146
|
+
previous_status: 'launching',
|
|
147
|
+
}, teamId);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// ── Always update last_event_at (heartbeat) ──────────────────────
|
|
151
|
+
// Stuck detection depends on last_event_at being fresh.
|
|
152
|
+
// Even throttled/deduplicated events must update this timestamp
|
|
153
|
+
// so the stuck detector doesn't falsely flag active teams.
|
|
154
|
+
db.updateTeam(teamId, { lastEventAt: nowIso });
|
|
155
|
+
// ── Throttle tool_use events ─────────────────────────────────────
|
|
156
|
+
if (payload.event.toLowerCase() === 'tool_use') {
|
|
157
|
+
const teamKey = payload.team;
|
|
158
|
+
const lastTime = lastToolUseByTeam.get(teamKey) || 0;
|
|
159
|
+
if (now - lastTime < TOOL_USE_THROTTLE_MS) {
|
|
160
|
+
// Deduplicated: don't insert into DB or broadcast SSE.
|
|
161
|
+
// Return 200 with processed: false (not an error, just deduped).
|
|
162
|
+
return { event_id: null, team_id: teamId, processed: false };
|
|
163
|
+
}
|
|
164
|
+
// Outside throttle window: allow this event through and record time
|
|
165
|
+
lastToolUseByTeam.set(teamKey, now);
|
|
166
|
+
// Prune stale entries to prevent unbounded growth
|
|
167
|
+
for (const [k, t] of lastToolUseByTeam) {
|
|
168
|
+
if (now - t > TOOL_USE_THROTTLE_MS * 2)
|
|
169
|
+
lastToolUseByTeam.delete(k);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// ── Normalize event type ─────────────────────────────────────────
|
|
173
|
+
const eventType = normalizeEventType(payload.event);
|
|
174
|
+
// ── Normalize agent name ────────────────────────────────────────
|
|
175
|
+
// Strip "fleet-" prefix and map empty agent_type to "team-lead"
|
|
176
|
+
// so roster names and message sender/recipient names match.
|
|
177
|
+
const agentName = normalizeAgentName(payload.agent_type);
|
|
178
|
+
// ── Insert event into database ───────────────────────────────────
|
|
179
|
+
const inserted = db.insertEvent({
|
|
180
|
+
teamId,
|
|
181
|
+
sessionId: payload.session_id || null,
|
|
182
|
+
agentName,
|
|
183
|
+
eventType,
|
|
184
|
+
toolName: payload.tool_name || null,
|
|
185
|
+
payload: JSON.stringify(payload),
|
|
186
|
+
});
|
|
187
|
+
const eventId = inserted.id;
|
|
188
|
+
// ── Broadcast via SSE ────────────────────────────────────────────
|
|
189
|
+
sse.broadcast('team_event', {
|
|
190
|
+
event_id: eventId,
|
|
191
|
+
team_id: teamId,
|
|
192
|
+
event_type: eventType,
|
|
193
|
+
session_id: payload.session_id || null,
|
|
194
|
+
agent_name: agentName,
|
|
195
|
+
tool_name: payload.tool_name || null,
|
|
196
|
+
timestamp: payload.timestamp || nowIso,
|
|
197
|
+
});
|
|
198
|
+
// ── Subagent crash detection (advisory) ───────────────────────
|
|
199
|
+
// Track SubagentStart/SubagentStop pairs. If a subagent stops very
|
|
200
|
+
// quickly (< 2 min) with minimal events (< 5), it likely crashed.
|
|
201
|
+
// Send an advisory message to the TL so they can decide to respawn.
|
|
202
|
+
const evtLower = payload.event.toLowerCase();
|
|
203
|
+
if (evtLower === 'subagent_start') {
|
|
204
|
+
const subagentName = payload.teammate_name || payload.agent_type || 'unknown';
|
|
205
|
+
const trackerKey = `${payload.team}:${subagentName}`;
|
|
206
|
+
subagentTrackers.set(trackerKey, { startTime: now, eventCount: 0 });
|
|
207
|
+
// Record spawn as a real agent message (TL -> subagent) so the CommGraph
|
|
208
|
+
// shows data-driven edges instead of synthetic spawn lines.
|
|
209
|
+
try {
|
|
210
|
+
const senderName = normalizeAgentName(null); // TL spawns subagents
|
|
211
|
+
const recipientName = normalizeAgentName(subagentName);
|
|
212
|
+
db.insertAgentMessage({
|
|
213
|
+
teamId,
|
|
214
|
+
eventId,
|
|
215
|
+
sender: senderName,
|
|
216
|
+
recipient: recipientName,
|
|
217
|
+
summary: 'spawned agent',
|
|
218
|
+
content: null,
|
|
219
|
+
sessionId: payload.session_id || null,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// Non-critical — silently ignore insert failures
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Increment event count for any tracked subagent on this team
|
|
227
|
+
if (payload.agent_type || payload.teammate_name) {
|
|
228
|
+
const subagentName = payload.teammate_name || payload.agent_type || 'unknown';
|
|
229
|
+
const trackerKey = `${payload.team}:${subagentName}`;
|
|
230
|
+
const tracker = subagentTrackers.get(trackerKey);
|
|
231
|
+
if (tracker) {
|
|
232
|
+
tracker.eventCount++;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (evtLower === 'subagent_stop' && messageSender) {
|
|
236
|
+
const subagentName = payload.teammate_name || payload.agent_type || 'unknown';
|
|
237
|
+
const trackerKey = `${payload.team}:${subagentName}`;
|
|
238
|
+
const tracker = subagentTrackers.get(trackerKey);
|
|
239
|
+
if (tracker) {
|
|
240
|
+
const durationMs = now - tracker.startTime;
|
|
241
|
+
const durationSec = Math.round(durationMs / 1000);
|
|
242
|
+
if (durationMs < config.earlyCrashThresholdSec * 1000 && tracker.eventCount < config.earlyCrashMinTools) {
|
|
243
|
+
const crashMsg = `Subagent '${subagentName}' appears to have crashed (${durationSec}s after start, ${tracker.eventCount} events). Consider respawning.`;
|
|
244
|
+
try {
|
|
245
|
+
messageSender.sendMessage(teamId, crashMsg, 'fc', 'subagent_crash');
|
|
246
|
+
console.log(`[EventCollector] Subagent crash advisory sent for ${subagentName} on team ${teamId}`);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// Non-critical — silently ignore send failures
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Clean up tracker regardless of crash detection
|
|
253
|
+
subagentTrackers.delete(trackerKey);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// ── Capture inter-agent message routing ───────────────────────
|
|
257
|
+
// When a SendMessage tool call is received with a msg_to field,
|
|
258
|
+
// record the message in the agent_messages table for routing visibility.
|
|
259
|
+
if (payload.tool_name === 'SendMessage' && payload.msg_to) {
|
|
260
|
+
try {
|
|
261
|
+
db.insertAgentMessage({
|
|
262
|
+
teamId,
|
|
263
|
+
eventId,
|
|
264
|
+
sender: normalizeAgentName(payload.agent_type),
|
|
265
|
+
recipient: normalizeAgentName(payload.msg_to),
|
|
266
|
+
summary: payload.msg_summary || null,
|
|
267
|
+
content: payload.message || null,
|
|
268
|
+
sessionId: payload.session_id || null,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
// Non-critical — silently ignore insert failures
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return { event_id: eventId, team_id: teamId, processed: true };
|
|
276
|
+
}
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
// Error class
|
|
279
|
+
// ---------------------------------------------------------------------------
|
|
280
|
+
export class EventCollectorError extends Error {
|
|
281
|
+
code;
|
|
282
|
+
constructor(message, code) {
|
|
283
|
+
super(message);
|
|
284
|
+
this.name = 'EventCollectorError';
|
|
285
|
+
this.code = code;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
// Utility: clear throttle state (for testing)
|
|
290
|
+
// ---------------------------------------------------------------------------
|
|
291
|
+
/** Reset all throttle state. Intended for use in tests only. */
|
|
292
|
+
export function resetThrottleState() {
|
|
293
|
+
lastToolUseByTeam.clear();
|
|
294
|
+
}
|
|
295
|
+
/** Reset subagent tracking state. Intended for use in tests only. */
|
|
296
|
+
export function resetSubagentTrackers() {
|
|
297
|
+
subagentTrackers.clear();
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=event-collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-collector.js","sourceRoot":"","sources":["../../../src/server/services/event-collector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare class EventService {
|
|
2
|
+
/**
|
|
3
|
+
* Query events with optional filters.
|
|
4
|
+
*
|
|
5
|
+
* @param filters - Optional query filters
|
|
6
|
+
* @returns Array of matching events
|
|
7
|
+
* @throws ServiceError with code VALIDATION for invalid filter values
|
|
8
|
+
*/
|
|
9
|
+
queryEvents(filters: {
|
|
10
|
+
teamId?: number;
|
|
11
|
+
eventType?: string;
|
|
12
|
+
since?: string;
|
|
13
|
+
limit?: number;
|
|
14
|
+
}): unknown[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get the singleton EventService instance.
|
|
18
|
+
*
|
|
19
|
+
* @returns EventService singleton
|
|
20
|
+
*/
|
|
21
|
+
export declare function getEventService(): EventService;
|
|
22
|
+
//# sourceMappingURL=event-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-service.d.ts","sourceRoot":"","sources":["../../../src/server/services/event-service.ts"],"names":[],"mappings":"AAeA,qBAAa,YAAY;IACvB;;;;;;OAMG;IACH,WAAW,CAAC,OAAO,EAAE;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,EAAE;CAmBd;AAQD;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,YAAY,CAK9C"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Fleet Commander — Event Service
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Wraps event queries for route consumption. The event ingestion (POST) stays
|
|
5
|
+
// in the route because it involves HTTP payload parsing and queue-processing
|
|
6
|
+
// side effects that are orchestration concerns.
|
|
7
|
+
// =============================================================================
|
|
8
|
+
import { getDatabase } from '../db.js';
|
|
9
|
+
import { validationError } from './service-error.js';
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Service
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
export class EventService {
|
|
14
|
+
/**
|
|
15
|
+
* Query events with optional filters.
|
|
16
|
+
*
|
|
17
|
+
* @param filters - Optional query filters
|
|
18
|
+
* @returns Array of matching events
|
|
19
|
+
* @throws ServiceError with code VALIDATION for invalid filter values
|
|
20
|
+
*/
|
|
21
|
+
queryEvents(filters) {
|
|
22
|
+
const { teamId, eventType, since, limit } = filters;
|
|
23
|
+
if (teamId !== undefined && (isNaN(teamId) || teamId < 1)) {
|
|
24
|
+
throw validationError('Invalid team_id');
|
|
25
|
+
}
|
|
26
|
+
if (limit !== undefined && (isNaN(limit) || limit < 1)) {
|
|
27
|
+
throw validationError('Invalid limit');
|
|
28
|
+
}
|
|
29
|
+
const db = getDatabase();
|
|
30
|
+
return db.getAllEvents({
|
|
31
|
+
teamId,
|
|
32
|
+
eventType,
|
|
33
|
+
since,
|
|
34
|
+
limit: limit ?? 100,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Singleton
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
let _instance = null;
|
|
42
|
+
/**
|
|
43
|
+
* Get the singleton EventService instance.
|
|
44
|
+
*
|
|
45
|
+
* @returns EventService singleton
|
|
46
|
+
*/
|
|
47
|
+
export function getEventService() {
|
|
48
|
+
if (!_instance) {
|
|
49
|
+
_instance = new EventService();
|
|
50
|
+
}
|
|
51
|
+
return _instance;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=event-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-service.js","sourceRoot":"","sources":["../../../src/server/services/event-service.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,kCAAkC;AAClC,gFAAgF;AAChF,8EAA8E;AAC9E,6EAA6E;AAC7E,gDAAgD;AAChD,gFAAgF;AAEhF,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,MAAM,OAAO,YAAY;IACvB;;;;;;OAMG;IACH,WAAW,CAAC,OAKX;QACC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;QAEpD,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;YAC1D,MAAM,eAAe,CAAC,iBAAiB,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;YACvD,MAAM,eAAe,CAAC,eAAe,CAAC,CAAC;QACzC,CAAC;QAED,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC,YAAY,CAAC;YACrB,MAAM;YACN,SAAS;YACT,KAAK;YACL,KAAK,EAAE,KAAK,IAAI,GAAG;SACpB,CAAC,CAAC;IACL,CAAC;CACF;AAED,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,IAAI,SAAS,GAAwB,IAAI,CAAC;AAE1C;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,IAAI,YAAY,EAAE,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
declare class GitHubPoller {
|
|
2
|
+
private interval;
|
|
3
|
+
/** Whether any team needs fast polling (pending CI or awaiting PR detection) */
|
|
4
|
+
private needsFastPoll;
|
|
5
|
+
/** Guard against concurrent poll() invocations */
|
|
6
|
+
private isPolling;
|
|
7
|
+
/**
|
|
8
|
+
* In-memory tracking of previously-blocked issues.
|
|
9
|
+
* Maps "projectId:issueNumber" -> array of blocking issue numbers.
|
|
10
|
+
* Used to detect when all blockers close so we can broadcast dependency_resolved.
|
|
11
|
+
* Lost on restart, which is acceptable since we do NOT auto-launch.
|
|
12
|
+
*/
|
|
13
|
+
private previouslyBlocked;
|
|
14
|
+
/**
|
|
15
|
+
* Start the polling loop. Uses adaptive intervals:
|
|
16
|
+
* - Normal: config.githubPollIntervalMs (default 30s)
|
|
17
|
+
* - Fast: 10s when teams are awaiting PR detection or have pending CI
|
|
18
|
+
*/
|
|
19
|
+
start(): void;
|
|
20
|
+
/** Schedule the next poll with adaptive interval */
|
|
21
|
+
private scheduleNextPoll;
|
|
22
|
+
/** Trigger an immediate extra poll (e.g. after a PR is detected) */
|
|
23
|
+
triggerPoll(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Stop the polling loop.
|
|
26
|
+
*/
|
|
27
|
+
stop(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Execute a single poll cycle. Iterates over all active projects,
|
|
30
|
+
* then checks teams within each project.
|
|
31
|
+
*/
|
|
32
|
+
poll(): Promise<void>;
|
|
33
|
+
private pollPR;
|
|
34
|
+
/**
|
|
35
|
+
* Check dependency resolution for issues that were previously blocked.
|
|
36
|
+
* When all blockers close, broadcasts a `dependency_resolved` SSE event
|
|
37
|
+
* and triggers processQueue() to auto-launch newly unblocked teams.
|
|
38
|
+
*/
|
|
39
|
+
private checkDependencyResolution;
|
|
40
|
+
/**
|
|
41
|
+
* Register an issue as blocked by dependencies.
|
|
42
|
+
* Called externally when a launch is blocked by the dependency check.
|
|
43
|
+
*/
|
|
44
|
+
trackBlockedIssue(projectId: number, issueNumber: number, blockerNumbers: number[]): void;
|
|
45
|
+
/**
|
|
46
|
+
* Remove an issue from blocked tracking (e.g. when force-launched).
|
|
47
|
+
*/
|
|
48
|
+
untrackBlockedIssue(projectId: number, issueNumber: number): void;
|
|
49
|
+
/**
|
|
50
|
+
* Read the current branch from a worktree. Returns null if the worktree
|
|
51
|
+
* doesn't exist or the branch can't be determined.
|
|
52
|
+
*
|
|
53
|
+
* The agent may rename the branch after launch (e.g. from "worktree-kea-767"
|
|
54
|
+
* to "refactor/fix/767-unit-user-role-logic"), so we must check the actual
|
|
55
|
+
* git state rather than relying on the initially stored branch name.
|
|
56
|
+
*/
|
|
57
|
+
private detectWorktreeBranch;
|
|
58
|
+
private detectPR;
|
|
59
|
+
private deriveCIStatus;
|
|
60
|
+
/**
|
|
61
|
+
* Execute a `gh` CLI command and return stdout, or `null` on error.
|
|
62
|
+
* Errors are logged but never thrown — the caller decides how to proceed.
|
|
63
|
+
*/
|
|
64
|
+
private execGH;
|
|
65
|
+
}
|
|
66
|
+
export declare const githubPoller: GitHubPoller;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=github-poller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-poller.d.ts","sourceRoot":"","sources":["../../../src/server/services/github-poller.ts"],"names":[],"mappings":"AA+DA,cAAM,YAAY;IAChB,OAAO,CAAC,QAAQ,CAA+B;IAE/C,gFAAgF;IAChF,OAAO,CAAC,aAAa,CAAS;IAE9B,kDAAkD;IAClD,OAAO,CAAC,SAAS,CAAS;IAE1B;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB,CAA2F;IAEpH;;;;OAIG;IACH,KAAK,IAAI,IAAI;IAkBb,oDAAoD;IACpD,OAAO,CAAC,gBAAgB;IAexB,oEAAoE;IACpE,WAAW,IAAI,IAAI;IAKnB;;OAEG;IACH,IAAI,IAAI,IAAI;IAQZ;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YA4Fb,MAAM;IA6QpB;;;;OAIG;YACW,yBAAyB;IAsEvC;;;OAGG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,IAAI;IAKzF;;OAEG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IASjE;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAkB5B,OAAO,CAAC,QAAQ;IA0BhB,OAAO,CAAC,cAAc;IA4BtB;;;OAGG;IACH,OAAO,CAAC,MAAM;CAgBf;AAMD,eAAO,MAAM,YAAY,cAAqB,CAAC"}
|