@intrect/openswarm 0.9.3 → 0.10.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/README.md +76 -1
- package/config.example.yaml +6 -0
- package/dist/adapters/agenticLoop.d.ts +24 -0
- package/dist/adapters/agenticLoop.d.ts.map +1 -1
- package/dist/adapters/agenticLoop.js +130 -11
- package/dist/adapters/agenticLoop.js.map +1 -1
- package/dist/adapters/applyPatch.d.ts +21 -0
- package/dist/adapters/applyPatch.d.ts.map +1 -0
- package/dist/adapters/applyPatch.js +175 -0
- package/dist/adapters/applyPatch.js.map +1 -0
- package/dist/adapters/base.d.ts.map +1 -1
- package/dist/adapters/base.js +7 -0
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/codex.js +10 -0
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/codexResponses.d.ts +8 -0
- package/dist/adapters/codexResponses.d.ts.map +1 -1
- package/dist/adapters/codexResponses.js +86 -8
- package/dist/adapters/codexResponses.js.map +1 -1
- package/dist/adapters/errorClassification.d.ts +8 -0
- package/dist/adapters/errorClassification.d.ts.map +1 -0
- package/dist/adapters/errorClassification.js +54 -0
- package/dist/adapters/errorClassification.js.map +1 -0
- package/dist/adapters/gpt.d.ts.map +1 -1
- package/dist/adapters/gpt.js +12 -1
- package/dist/adapters/gpt.js.map +1 -1
- package/dist/adapters/local.d.ts.map +1 -1
- package/dist/adapters/local.js +9 -1
- package/dist/adapters/local.js.map +1 -1
- package/dist/adapters/openrouter.d.ts.map +1 -1
- package/dist/adapters/openrouter.js +9 -1
- package/dist/adapters/openrouter.js.map +1 -1
- package/dist/adapters/rateLimitError.d.ts +29 -0
- package/dist/adapters/rateLimitError.d.ts.map +1 -0
- package/dist/adapters/rateLimitError.js +64 -0
- package/dist/adapters/rateLimitError.js.map +1 -0
- package/dist/adapters/resultParsing.d.ts.map +1 -1
- package/dist/adapters/resultParsing.js +18 -0
- package/dist/adapters/resultParsing.js.map +1 -1
- package/dist/adapters/tools.d.ts +3 -0
- package/dist/adapters/tools.d.ts.map +1 -1
- package/dist/adapters/tools.js +148 -11
- package/dist/adapters/tools.js.map +1 -1
- package/dist/adapters/types.d.ts +15 -0
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/webTools.d.ts.map +1 -1
- package/dist/adapters/webTools.js +44 -21
- package/dist/adapters/webTools.js.map +1 -1
- package/dist/agents/agentPair.d.ts +9 -0
- package/dist/agents/agentPair.d.ts.map +1 -1
- package/dist/agents/agentPair.js.map +1 -1
- package/dist/agents/auditor.d.ts.map +1 -1
- package/dist/agents/auditor.js +3 -0
- package/dist/agents/auditor.js.map +1 -1
- package/dist/agents/documenter.d.ts.map +1 -1
- package/dist/agents/documenter.js +3 -0
- package/dist/agents/documenter.js.map +1 -1
- package/dist/agents/draftAnalyzer.d.ts +30 -7
- package/dist/agents/draftAnalyzer.d.ts.map +1 -1
- package/dist/agents/draftAnalyzer.js +181 -30
- package/dist/agents/draftAnalyzer.js.map +1 -1
- package/dist/agents/pairPipeline.d.ts +7 -1
- package/dist/agents/pairPipeline.d.ts.map +1 -1
- package/dist/agents/pairPipeline.js +110 -66
- package/dist/agents/pairPipeline.js.map +1 -1
- package/dist/agents/pipelineFormat.d.ts.map +1 -1
- package/dist/agents/pipelineFormat.js +4 -0
- package/dist/agents/pipelineFormat.js.map +1 -1
- package/dist/agents/reviewer.d.ts +16 -0
- package/dist/agents/reviewer.d.ts.map +1 -1
- package/dist/agents/reviewer.js +30 -0
- package/dist/agents/reviewer.js.map +1 -1
- package/dist/agents/skillDocumenter.d.ts.map +1 -1
- package/dist/agents/skillDocumenter.js +3 -0
- package/dist/agents/skillDocumenter.js.map +1 -1
- package/dist/agents/tester.d.ts.map +1 -1
- package/dist/agents/tester.js +3 -0
- package/dist/agents/tester.js.map +1 -1
- package/dist/agents/worker.d.ts +14 -0
- package/dist/agents/worker.d.ts.map +1 -1
- package/dist/agents/worker.js +123 -22
- package/dist/agents/worker.js.map +1 -1
- package/dist/automation/autonomousRunner.d.ts +12 -0
- package/dist/automation/autonomousRunner.d.ts.map +1 -1
- package/dist/automation/autonomousRunner.js +218 -29
- package/dist/automation/autonomousRunner.js.map +1 -1
- package/dist/automation/runnerExecution.d.ts +15 -0
- package/dist/automation/runnerExecution.d.ts.map +1 -1
- package/dist/automation/runnerExecution.js +77 -1
- package/dist/automation/runnerExecution.js.map +1 -1
- package/dist/automation/runnerState.d.ts +7 -0
- package/dist/automation/runnerState.d.ts.map +1 -1
- package/dist/automation/runnerState.js +23 -1
- package/dist/automation/runnerState.js.map +1 -1
- package/dist/automation/runnerTypes.d.ts +2 -0
- package/dist/automation/runnerTypes.d.ts.map +1 -1
- package/dist/automation/scheduler.d.ts +12 -0
- package/dist/automation/scheduler.d.ts.map +1 -1
- package/dist/automation/scheduler.js +29 -2
- package/dist/automation/scheduler.js.map +1 -1
- package/dist/automation/taskSource.d.ts +9 -1
- package/dist/automation/taskSource.d.ts.map +1 -1
- package/dist/automation/taskSource.js +13 -2
- package/dist/automation/taskSource.js.map +1 -1
- package/dist/cli/auditPM.d.ts +40 -0
- package/dist/cli/auditPM.d.ts.map +1 -0
- package/dist/cli/auditPM.js +132 -0
- package/dist/cli/auditPM.js.map +1 -0
- package/dist/cli/designPipeline.d.ts +30 -0
- package/dist/cli/designPipeline.d.ts.map +1 -0
- package/dist/cli/designPipeline.js +113 -0
- package/dist/cli/designPipeline.js.map +1 -0
- package/dist/cli/mcpCommand.d.ts +22 -0
- package/dist/cli/mcpCommand.d.ts.map +1 -0
- package/dist/cli/mcpCommand.js +93 -0
- package/dist/cli/mcpCommand.js.map +1 -0
- package/dist/cli/projectHandler.d.ts.map +1 -1
- package/dist/cli/projectHandler.js +4 -2
- package/dist/cli/projectHandler.js.map +1 -1
- package/dist/cli/reviewAudit.d.ts +130 -0
- package/dist/cli/reviewAudit.d.ts.map +1 -0
- package/dist/cli/reviewAudit.js +283 -0
- package/dist/cli/reviewAudit.js.map +1 -0
- package/dist/cli/reviewCommand.d.ts +53 -0
- package/dist/cli/reviewCommand.d.ts.map +1 -0
- package/dist/cli/reviewCommand.js +207 -0
- package/dist/cli/reviewCommand.js.map +1 -0
- package/dist/cli/reviewMaxCommand.d.ts +35 -0
- package/dist/cli/reviewMaxCommand.d.ts.map +1 -0
- package/dist/cli/reviewMaxCommand.js +272 -0
- package/dist/cli/reviewMaxCommand.js.map +1 -0
- package/dist/cli/reviewProgress.d.ts +34 -0
- package/dist/cli/reviewProgress.d.ts.map +1 -0
- package/dist/cli/reviewProgress.js +109 -0
- package/dist/cli/reviewProgress.js.map +1 -0
- package/dist/cli/scheduleCommand.d.ts +15 -0
- package/dist/cli/scheduleCommand.d.ts.map +1 -0
- package/dist/cli/scheduleCommand.js +68 -0
- package/dist/cli/scheduleCommand.js.map +1 -0
- package/dist/cli.js +182 -26
- package/dist/cli.js.map +1 -1
- package/dist/core/config.d.ts +24 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +42 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/providerOverride.d.ts.map +1 -1
- package/dist/core/providerOverride.js +16 -13
- package/dist/core/providerOverride.js.map +1 -1
- package/dist/core/service.d.ts.map +1 -1
- package/dist/core/service.js +11 -0
- package/dist/core/service.js.map +1 -1
- package/dist/core/types.d.ts +38 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/discord/discordHandlers.d.ts.map +1 -1
- package/dist/discord/discordHandlers.js +1 -0
- package/dist/discord/discordHandlers.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/linear/linear.d.ts +21 -0
- package/dist/linear/linear.d.ts.map +1 -1
- package/dist/linear/linear.js +109 -4
- package/dist/linear/linear.js.map +1 -1
- package/dist/linear/projectUpdater.js +3 -1
- package/dist/linear/projectUpdater.js.map +1 -1
- package/dist/locale/prompts/en.d.ts.map +1 -1
- package/dist/locale/prompts/en.js +104 -11
- package/dist/locale/prompts/en.js.map +1 -1
- package/dist/locale/prompts/ko.d.ts.map +1 -1
- package/dist/locale/prompts/ko.js +103 -11
- package/dist/locale/prompts/ko.js.map +1 -1
- package/dist/locale/types.d.ts +12 -0
- package/dist/locale/types.d.ts.map +1 -1
- package/dist/mcp/mcpClient.d.ts +28 -1
- package/dist/mcp/mcpClient.d.ts.map +1 -1
- package/dist/mcp/mcpClient.js +74 -3
- package/dist/mcp/mcpClient.js.map +1 -1
- package/dist/orchestration/decisionEngine.d.ts +20 -0
- package/dist/orchestration/decisionEngine.d.ts.map +1 -1
- package/dist/orchestration/decisionEngine.js +23 -0
- package/dist/orchestration/decisionEngine.js.map +1 -1
- package/dist/orchestration/taskScheduler.d.ts.map +1 -1
- package/dist/orchestration/taskScheduler.js +12 -1
- package/dist/orchestration/taskScheduler.js.map +1 -1
- package/dist/support/chatBackend.d.ts +18 -0
- package/dist/support/chatBackend.d.ts.map +1 -1
- package/dist/support/chatBackend.js +92 -8
- package/dist/support/chatBackend.js.map +1 -1
- package/dist/support/chatSession.d.ts +51 -0
- package/dist/support/chatSession.d.ts.map +1 -0
- package/dist/support/chatSession.js +134 -0
- package/dist/support/chatSession.js.map +1 -0
- package/dist/support/chatTui.d.ts.map +1 -1
- package/dist/support/chatTui.js +6 -75
- package/dist/support/chatTui.js.map +1 -1
- package/dist/support/concurrencyPool.d.ts +18 -0
- package/dist/support/concurrencyPool.d.ts.map +1 -0
- package/dist/support/concurrencyPool.js +46 -0
- package/dist/support/concurrencyPool.js.map +1 -0
- package/dist/support/dashboardHtml.d.ts +1 -1
- package/dist/support/dashboardHtml.d.ts.map +1 -1
- package/dist/support/dashboardHtml.js +0 -28
- package/dist/support/dashboardHtml.js.map +1 -1
- package/dist/support/editParser.d.ts +26 -0
- package/dist/support/editParser.d.ts.map +1 -1
- package/dist/support/editParser.js +43 -0
- package/dist/support/editParser.js.map +1 -1
- package/dist/support/goalCommand.d.ts +35 -0
- package/dist/support/goalCommand.d.ts.map +1 -0
- package/dist/support/goalCommand.js +112 -0
- package/dist/support/goalCommand.js.map +1 -0
- package/dist/support/index.d.ts +0 -1
- package/dist/support/index.d.ts.map +1 -1
- package/dist/support/index.js +0 -1
- package/dist/support/index.js.map +1 -1
- package/dist/support/web.d.ts.map +1 -1
- package/dist/support/web.js +0 -7
- package/dist/support/web.js.map +1 -1
- package/dist/taskState/store.d.ts +5 -0
- package/dist/taskState/store.d.ts.map +1 -1
- package/dist/taskState/store.js +16 -0
- package/dist/taskState/store.js.map +1 -1
- package/dist/telemetry/telemetry.d.ts +42 -0
- package/dist/telemetry/telemetry.d.ts.map +1 -0
- package/dist/telemetry/telemetry.js +138 -0
- package/dist/telemetry/telemetry.js.map +1 -0
- package/dist/tui/App.d.ts +20 -0
- package/dist/tui/App.d.ts.map +1 -0
- package/dist/tui/App.js +52 -0
- package/dist/tui/App.js.map +1 -0
- package/dist/tui/chatModel.d.ts +81 -0
- package/dist/tui/chatModel.d.ts.map +1 -0
- package/dist/tui/chatModel.js +129 -0
- package/dist/tui/chatModel.js.map +1 -0
- package/dist/tui/components/AuditBoard.d.ts +10 -0
- package/dist/tui/components/AuditBoard.d.ts.map +1 -0
- package/dist/tui/components/AuditBoard.js +50 -0
- package/dist/tui/components/AuditBoard.js.map +1 -0
- package/dist/tui/components/ChatInput.d.ts +14 -0
- package/dist/tui/components/ChatInput.d.ts.map +1 -0
- package/dist/tui/components/ChatInput.js +46 -0
- package/dist/tui/components/ChatInput.js.map +1 -0
- package/dist/tui/components/ChatLog.d.ts +12 -0
- package/dist/tui/components/ChatLog.d.ts.map +1 -0
- package/dist/tui/components/ChatLog.js +47 -0
- package/dist/tui/components/ChatLog.js.map +1 -0
- package/dist/tui/components/CommandPalette.d.ts +6 -0
- package/dist/tui/components/CommandPalette.d.ts.map +1 -0
- package/dist/tui/components/CommandPalette.js +14 -0
- package/dist/tui/components/CommandPalette.js.map +1 -0
- package/dist/tui/components/ContextBar.d.ts +9 -0
- package/dist/tui/components/ContextBar.d.ts.map +1 -0
- package/dist/tui/components/ContextBar.js +18 -0
- package/dist/tui/components/ContextBar.js.map +1 -0
- package/dist/tui/components/DataTable.d.ts +6 -0
- package/dist/tui/components/DataTable.d.ts.map +1 -0
- package/dist/tui/components/DataTable.js +13 -0
- package/dist/tui/components/DataTable.js.map +1 -0
- package/dist/tui/components/HelpBar.d.ts +2 -0
- package/dist/tui/components/HelpBar.d.ts.map +1 -0
- package/dist/tui/components/HelpBar.js +8 -0
- package/dist/tui/components/HelpBar.js.map +1 -0
- package/dist/tui/components/LiveLog.d.ts +6 -0
- package/dist/tui/components/LiveLog.d.ts.map +1 -0
- package/dist/tui/components/LiveLog.js +10 -0
- package/dist/tui/components/LiveLog.js.map +1 -0
- package/dist/tui/components/LogLine.d.ts +4 -0
- package/dist/tui/components/LogLine.d.ts.map +1 -0
- package/dist/tui/components/LogLine.js +8 -0
- package/dist/tui/components/LogLine.js.map +1 -0
- package/dist/tui/components/SelectList.d.ts +6 -0
- package/dist/tui/components/SelectList.d.ts.map +1 -0
- package/dist/tui/components/SelectList.js +15 -0
- package/dist/tui/components/SelectList.js.map +1 -0
- package/dist/tui/components/StageTimeline.d.ts +7 -0
- package/dist/tui/components/StageTimeline.d.ts.map +1 -0
- package/dist/tui/components/StageTimeline.js +18 -0
- package/dist/tui/components/StageTimeline.js.map +1 -0
- package/dist/tui/components/StatusBar.d.ts +7 -0
- package/dist/tui/components/StatusBar.d.ts.map +1 -0
- package/dist/tui/components/StatusBar.js +8 -0
- package/dist/tui/components/StatusBar.js.map +1 -0
- package/dist/tui/components/SubagentTree.d.ts +10 -0
- package/dist/tui/components/SubagentTree.d.ts.map +1 -0
- package/dist/tui/components/SubagentTree.js +12 -0
- package/dist/tui/components/SubagentTree.js.map +1 -0
- package/dist/tui/components/TabBar.d.ts +5 -0
- package/dist/tui/components/TabBar.d.ts.map +1 -0
- package/dist/tui/components/TabBar.js +9 -0
- package/dist/tui/components/TabBar.js.map +1 -0
- package/dist/tui/components/WorkingIndicator.d.ts +6 -0
- package/dist/tui/components/WorkingIndicator.d.ts.map +1 -0
- package/dist/tui/components/WorkingIndicator.js +20 -0
- package/dist/tui/components/WorkingIndicator.js.map +1 -0
- package/dist/tui/hooks/useMonitor.d.ts +8 -0
- package/dist/tui/hooks/useMonitor.d.ts.map +1 -0
- package/dist/tui/hooks/useMonitor.js +42 -0
- package/dist/tui/hooks/useMonitor.js.map +1 -0
- package/dist/tui/hooks/usePipelineEvents.d.ts +6 -0
- package/dist/tui/hooks/usePipelineEvents.d.ts.map +1 -0
- package/dist/tui/hooks/usePipelineEvents.js +21 -0
- package/dist/tui/hooks/usePipelineEvents.js.map +1 -0
- package/dist/tui/hooks/useTerminalSize.d.ts +6 -0
- package/dist/tui/hooks/useTerminalSize.d.ts.map +1 -0
- package/dist/tui/hooks/useTerminalSize.js +26 -0
- package/dist/tui/hooks/useTerminalSize.js.map +1 -0
- package/dist/tui/index.d.ts +22 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +18 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/inputDebug.d.ts +20 -0
- package/dist/tui/inputDebug.d.ts.map +1 -0
- package/dist/tui/inputDebug.js +42 -0
- package/dist/tui/inputDebug.js.map +1 -0
- package/dist/tui/loadingMessages.d.ts +11 -0
- package/dist/tui/loadingMessages.d.ts.map +1 -0
- package/dist/tui/loadingMessages.js +39 -0
- package/dist/tui/loadingMessages.js.map +1 -0
- package/dist/tui/logFormat.d.ts +10 -0
- package/dist/tui/logFormat.d.ts.map +1 -0
- package/dist/tui/logFormat.js +86 -0
- package/dist/tui/logFormat.js.map +1 -0
- package/dist/tui/markdown.d.ts +3 -0
- package/dist/tui/markdown.d.ts.map +1 -0
- package/dist/tui/markdown.js +33 -0
- package/dist/tui/markdown.js.map +1 -0
- package/dist/tui/monitorApi.d.ts +6 -0
- package/dist/tui/monitorApi.d.ts.map +1 -0
- package/dist/tui/monitorApi.js +35 -0
- package/dist/tui/monitorApi.js.map +1 -0
- package/dist/tui/monitorRows.d.ts +43 -0
- package/dist/tui/monitorRows.d.ts.map +1 -0
- package/dist/tui/monitorRows.js +65 -0
- package/dist/tui/monitorRows.js.map +1 -0
- package/dist/tui/panels/ChatPanel.d.ts +16 -0
- package/dist/tui/panels/ChatPanel.d.ts.map +1 -0
- package/dist/tui/panels/ChatPanel.js +305 -0
- package/dist/tui/panels/ChatPanel.js.map +1 -0
- package/dist/tui/panels/LogsPanel.d.ts +8 -0
- package/dist/tui/panels/LogsPanel.d.ts.map +1 -0
- package/dist/tui/panels/LogsPanel.js +19 -0
- package/dist/tui/panels/LogsPanel.js.map +1 -0
- package/dist/tui/panels/MonitorPanel.d.ts +8 -0
- package/dist/tui/panels/MonitorPanel.d.ts.map +1 -0
- package/dist/tui/panels/MonitorPanel.js +19 -0
- package/dist/tui/panels/MonitorPanel.js.map +1 -0
- package/dist/tui/panels/PipelinePanel.d.ts +6 -0
- package/dist/tui/panels/PipelinePanel.d.ts.map +1 -0
- package/dist/tui/panels/PipelinePanel.js +22 -0
- package/dist/tui/panels/PipelinePanel.js.map +1 -0
- package/dist/tui/pipelineEvents.d.ts +21 -0
- package/dist/tui/pipelineEvents.d.ts.map +1 -0
- package/dist/tui/pipelineEvents.js +30 -0
- package/dist/tui/pipelineEvents.js.map +1 -0
- package/dist/tui/sse.d.ts +24 -0
- package/dist/tui/sse.d.ts.map +1 -0
- package/dist/tui/sse.js +78 -0
- package/dist/tui/sse.js.map +1 -0
- package/dist/tui/subagentTree.d.ts +10 -0
- package/dist/tui/subagentTree.d.ts.map +1 -0
- package/dist/tui/subagentTree.js +30 -0
- package/dist/tui/subagentTree.js.map +1 -0
- package/dist/tui/tabs.d.ts +10 -0
- package/dist/tui/tabs.d.ts.map +1 -0
- package/dist/tui/tabs.js +26 -0
- package/dist/tui/tabs.js.map +1 -0
- package/dist/tui/theme.d.ts +29 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +34 -0
- package/dist/tui/theme.js.map +1 -0
- package/package.json +13 -3
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// OpenSwarm - Autonomous Runner
|
|
2
2
|
// Heartbeat → Decision → Execution → Report
|
|
3
3
|
import { Cron } from 'croner';
|
|
4
|
-
import { loadTaskState, saveTaskState, buildProjectsInfo, appendPipelineHistory, getPipelineHistory, incrementRejection, clearRejection, isRejectionLimitReached, canRetryNow, setRetryTime, clearRetryTime, formatRetryTime, getDailyPaceInfo, recordProjectCompletion, canProjectAcceptTask, getProjectWindowCount, } from './runnerState.js';
|
|
5
|
-
import { getDecisionEngine, } from '../orchestration/decisionEngine.js';
|
|
4
|
+
import { loadTaskState, saveTaskState, buildProjectsInfo, appendPipelineHistory, getPipelineHistory, incrementRejection, clearRejection, isRejectionLimitReached, canRetryNow, setRetryTime, clearRetryTime, formatRetryTime, getDailyPaceInfo, recordProjectCompletion, canProjectAcceptTask, getProjectWindowCount, loadProjectSelection, saveProjectSelection, } from './runnerState.js';
|
|
5
|
+
import { getDecisionEngine, classifyStuck, } from '../orchestration/decisionEngine.js';
|
|
6
6
|
// ExecutorResult used via execution.reportExecutionResult
|
|
7
7
|
import { checkWorkAllowed } from '../support/timeWindow.js';
|
|
8
8
|
import { recordTaskOutcome } from '../memory/repoKnowledge.js';
|
|
@@ -14,12 +14,14 @@ import * as execution from './runnerExecution.js';
|
|
|
14
14
|
import { reportToDiscord, fetchLinearTasks, getTaskSource } from './runnerExecution.js';
|
|
15
15
|
import { t } from '../locale/index.js';
|
|
16
16
|
import { broadcastEvent } from '../core/eventHub.js';
|
|
17
|
+
import { writeProviderOverride } from '../core/providerOverride.js';
|
|
18
|
+
import { getTaskState } from '../taskState/store.js';
|
|
17
19
|
import { pruneWorktrees } from '../support/worktreeManager.js';
|
|
18
20
|
import { loadRepoMetadata } from '../support/repoMetadata.js';
|
|
21
|
+
import { STUCK_LABEL } from '../linear/index.js';
|
|
19
22
|
import { refreshGraph, toProjectSlug } from '../knowledge/index.js';
|
|
20
23
|
import { checkAllMonitors, getActiveMonitors } from './longRunningMonitor.js';
|
|
21
24
|
import { detectFileConflicts } from '../orchestration/conflictDetector.js';
|
|
22
|
-
import { checkQuotaAllowance } from '../support/quotaTracker.js';
|
|
23
25
|
// Re-export types and integration setters (used by service.ts)
|
|
24
26
|
export { setNotifier, setTaskSource } from './runnerExecution.js';
|
|
25
27
|
let runnerInstance = null;
|
|
@@ -35,8 +37,14 @@ export class AutonomousRunner {
|
|
|
35
37
|
};
|
|
36
38
|
// Heartbeat concurrency guard
|
|
37
39
|
_heartbeatRunning = false;
|
|
38
|
-
// Explicitly enabled project paths (allow-list; empty = nothing runs
|
|
40
|
+
// Explicitly enabled project paths (allow-list; empty = nothing runs ONLY once
|
|
41
|
+
// the selection has been touched — see projectSelectionTouched).
|
|
39
42
|
enabledProjects = new Set();
|
|
43
|
+
// Whether the user has explicitly enabled/disabled any project (dashboard/CLI).
|
|
44
|
+
// Before this, an empty enabledProjects means "no selection yet → all allowed
|
|
45
|
+
// projects run" (legacy fallback). After, an empty set means "nothing runs" —
|
|
46
|
+
// so disabling every project actually stops the daemon. (INT-2207)
|
|
47
|
+
projectSelectionTouched = false;
|
|
40
48
|
/**
|
|
41
49
|
* macOS (APFS default) and Windows have case-insensitive filesystems by
|
|
42
50
|
* default, so `/Users/x/dev/AnalogModeling` and `/Users/x/dev/analogModeling`
|
|
@@ -64,6 +72,21 @@ export class AutonomousRunner {
|
|
|
64
72
|
}
|
|
65
73
|
return false;
|
|
66
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Whether to apply the enabledProjects allow-list. True once the user has made
|
|
77
|
+
* an explicit selection (touched), OR while any project is enabled. Empty +
|
|
78
|
+
* untouched stays the legacy "run all allowed projects" fallback. (INT-2207)
|
|
79
|
+
*/
|
|
80
|
+
shouldFilterByEnabled() {
|
|
81
|
+
return this.projectSelectionTouched || this.enabledProjects.size > 0;
|
|
82
|
+
}
|
|
83
|
+
/** Persist the project selection so it survives a restart. No-op under dryRun
|
|
84
|
+
* (tests) to avoid touching the real ~/.openswarm. (INT-2208) */
|
|
85
|
+
persistSelection() {
|
|
86
|
+
if (this.config.dryRun)
|
|
87
|
+
return;
|
|
88
|
+
saveProjectSelection({ enabled: [...this.enabledProjects], touched: this.projectSelectionTouched });
|
|
89
|
+
}
|
|
67
90
|
// Last fetched Linear tasks (for dashboard display)
|
|
68
91
|
lastFetchedTasks = [];
|
|
69
92
|
// Cache: linearProjectName → resolvedLocalPath (populated during task execution)
|
|
@@ -77,6 +100,13 @@ export class AutonomousRunner {
|
|
|
77
100
|
failedTaskCounts = new Map();
|
|
78
101
|
failedTaskRetryTimes = new Map(); // issueId → next retry timestamp (ms)
|
|
79
102
|
static MAX_RETRY_COUNT = 4; // Increased from 2 to allow more retries with backoff
|
|
103
|
+
// Rate-limit hold: epoch ms until which all task execution is paused.
|
|
104
|
+
// Set when any adapter returns a 429 / usage_limit_reached response (INT-1906).
|
|
105
|
+
rateLimitUntil = 0;
|
|
106
|
+
// Issues whose Linear project can't be mapped to a local repo path. Recorded on
|
|
107
|
+
// the first resolve failure so they aren't re-picked every heartbeat (which
|
|
108
|
+
// starved other actionable tasks — they were top-priority but never runnable). (INT-1875)
|
|
109
|
+
unresolvableIssueIds = new Set();
|
|
80
110
|
get taskStateRef() {
|
|
81
111
|
return {
|
|
82
112
|
completedTaskIds: this.completedTaskIds,
|
|
@@ -103,6 +133,13 @@ export class AutonomousRunner {
|
|
|
103
133
|
constructor(config) {
|
|
104
134
|
this.config = config;
|
|
105
135
|
this.loadTaskState(); // Restore completed/failed task IDs from disk
|
|
136
|
+
// Restore the persisted project selection so "disable all" survives a daemon
|
|
137
|
+
// restart. Skipped under dryRun (tests) so the real ~/.openswarm isn't touched. (INT-2208)
|
|
138
|
+
if (!config.dryRun) {
|
|
139
|
+
const sel = loadProjectSelection();
|
|
140
|
+
this.enabledProjects = new Set(sel.enabled);
|
|
141
|
+
this.projectSelectionTouched = sel.touched;
|
|
142
|
+
}
|
|
106
143
|
this.engine = getDecisionEngine({
|
|
107
144
|
allowedProjects: config.allowedProjects,
|
|
108
145
|
linearTeamId: config.linearTeamId,
|
|
@@ -113,9 +150,11 @@ export class AutonomousRunner {
|
|
|
113
150
|
includeBacklog: config.includeBacklog,
|
|
114
151
|
});
|
|
115
152
|
// Initialize TaskScheduler
|
|
153
|
+
// Same-repo parallelism is opt-in via config (default true) but the scheduler
|
|
154
|
+
// force-disables it unless worktreeMode is on — see TaskScheduler guard. (INT-1975)
|
|
116
155
|
this.scheduler = initScheduler({
|
|
117
156
|
maxConcurrent: config.maxConcurrentTasks ?? 1,
|
|
118
|
-
allowSameProjectConcurrent:
|
|
157
|
+
allowSameProjectConcurrent: config.allowSameProjectConcurrent ?? true,
|
|
119
158
|
worktreeMode: config.worktreeMode ?? false,
|
|
120
159
|
});
|
|
121
160
|
// Set up scheduler event handling
|
|
@@ -198,8 +237,55 @@ export class AutonomousRunner {
|
|
|
198
237
|
}
|
|
199
238
|
this.scheduleNextHeartbeat();
|
|
200
239
|
});
|
|
240
|
+
this.scheduler.on('cancelled', async ({ task, result }) => {
|
|
241
|
+
const taskCtx = this.formatTaskContext(task);
|
|
242
|
+
console.log(`[Scheduler] Task cancelled: ${taskCtx} ${task.title}`);
|
|
243
|
+
broadcastEvent({ type: 'task:completed', data: { taskId: task.id, success: false, duration: result.totalDuration } });
|
|
244
|
+
this.recordPipelineHistory(task, result);
|
|
245
|
+
await execution.syncCancellationState(task);
|
|
246
|
+
// Keep parity with the completed/failed handlers: kick the next heartbeat
|
|
247
|
+
// so discovery resumes immediately instead of waiting for the next cron
|
|
248
|
+
// tick (matters in serial mode, where slotFreed/runAvailableTasks is a no-op).
|
|
249
|
+
this.scheduleNextHeartbeat();
|
|
250
|
+
});
|
|
201
251
|
this.scheduler.on('failed', async ({ task, result }) => {
|
|
202
252
|
const taskCtx = this.formatTaskContext(task);
|
|
253
|
+
// Rate-limited: pause execution until the quota resets. Do NOT count it as a
|
|
254
|
+
// task failure, run the rejection/block path, or post a Linear comment —
|
|
255
|
+
// that is exactly the retry-spam this issue fixes. (INT-1906)
|
|
256
|
+
if (result.finalStatus === 'rate_limited') {
|
|
257
|
+
const resetsAt = result.rateLimitResetsAt ?? Date.now() + 60_000;
|
|
258
|
+
this.rateLimitUntil = resetsAt;
|
|
259
|
+
const waitSec = Math.max(0, Math.ceil((resetsAt - Date.now()) / 1000));
|
|
260
|
+
const resetsLabel = new Date(resetsAt).toISOString();
|
|
261
|
+
console.warn(`[Scheduler] Rate limit hit for ${taskCtx} — pausing until ${resetsLabel} (~${waitSec}s)`);
|
|
262
|
+
broadcastEvent({
|
|
263
|
+
type: 'log',
|
|
264
|
+
data: { taskId: task.issueId || task.id, stage: 'rate_limit', line: `⏸ Rate limited — pausing ~${waitSec}s (until ${resetsLabel})` },
|
|
265
|
+
});
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
// Infra/CLI failure: the worker/reviewer never actually ran (non-zero exit,
|
|
269
|
+
// auth expiry, spawn, timeout). This is NOT a task failure — do NOT increment
|
|
270
|
+
// the rejection/failure counters that mark an issue durably STUCK. Backoff-
|
|
271
|
+
// retry instead; the operator fixes the root cause (e.g. re-auth) and the task
|
|
272
|
+
// resumes on its own. This is what kept completable tasks (worker had already
|
|
273
|
+
// edited files) STUCK in production. (INT-2010)
|
|
274
|
+
if (result.finalStatus === 'infra_error') {
|
|
275
|
+
const detail = result.workerResult?.error || result.reviewResult?.feedback || 'infra/CLI execution error';
|
|
276
|
+
if (task.issueId) {
|
|
277
|
+
// Fixed mid-range backoff — we intentionally don't bump failure counts,
|
|
278
|
+
// so there's no attempt number to scale by.
|
|
279
|
+
const nextRetryTime = setRetryTime(task.issueId, 3, this.failedTaskRetryTimes);
|
|
280
|
+
this.saveTaskState();
|
|
281
|
+
console.warn(`[Scheduler] Infra error for ${taskCtx} (NOT counted toward STUCK) — backoff retry ${formatRetryTime(nextRetryTime)}: ${detail}`);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
console.warn(`[Scheduler] Infra error for ${taskCtx} (NOT counted toward STUCK): ${detail}`);
|
|
285
|
+
}
|
|
286
|
+
this.scheduleNextHeartbeat();
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
203
289
|
console.log(`[Scheduler] Task failed: ${taskCtx} ${task.title}`);
|
|
204
290
|
broadcastEvent({ type: 'task:completed', data: { taskId: task.id, success: false, duration: result.totalDuration } });
|
|
205
291
|
this.recordPipelineHistory(task, result);
|
|
@@ -225,11 +311,9 @@ export class AutonomousRunner {
|
|
|
225
311
|
this.saveTaskState();
|
|
226
312
|
try {
|
|
227
313
|
await execution.syncFailureState(task, `Max rejection limit reached (${rejectionCount} attempts): ${feedback}`);
|
|
228
|
-
await getTaskSource()?.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
`**Action required:** Please review the task requirements and code manually, or adjust the task scope.`);
|
|
232
|
-
console.log(`[Scheduler] Issue ${task.issueId} permanently blocked (max rejections reached)`);
|
|
314
|
+
await getTaskSource()?.logStuck(task.issueId, 'autonomous-runner', `Rejected ${rejectionCount} times by the reviewer — automatic retries exhausted.\n\n` +
|
|
315
|
+
`**Latest rejection reason:**\n${feedback}`);
|
|
316
|
+
console.log(`[Scheduler] Issue ${task.issueId} marked STUCK (max rejections reached)`);
|
|
233
317
|
}
|
|
234
318
|
catch (err) {
|
|
235
319
|
console.error(`[Scheduler] Failed to update issue state:`, err);
|
|
@@ -262,11 +346,17 @@ export class AutonomousRunner {
|
|
|
262
346
|
this.completedTaskIds.add(task.issueId); // Prevent re-selection
|
|
263
347
|
clearRetryTime(task.issueId, this.failedTaskRetryTimes); // Clear retry time
|
|
264
348
|
this.saveTaskState();
|
|
265
|
-
console.log(`[Scheduler] Task failure count: ${count}/${AutonomousRunner.MAX_RETRY_COUNT} for ${taskCtx} —
|
|
349
|
+
console.log(`[Scheduler] Task failure count: ${count}/${AutonomousRunner.MAX_RETRY_COUNT} for ${taskCtx} — STUCK`);
|
|
350
|
+
// Surface the underlying failure (worker CLI error / review feedback) so the
|
|
351
|
+
// stuck comment is actionable instead of an opaque "failed N times".
|
|
352
|
+
const failureDetail = result.workerResult?.error
|
|
353
|
+
|| result.reviewResult?.feedback
|
|
354
|
+
|| 'No error detail captured (worker produced no output).';
|
|
266
355
|
try {
|
|
267
|
-
await execution.syncFailureState(task, `Autonomous execution failed ${count} times`);
|
|
268
|
-
await getTaskSource()?.
|
|
269
|
-
|
|
356
|
+
await execution.syncFailureState(task, `Autonomous execution failed ${count} times: ${failureDetail}`);
|
|
357
|
+
await getTaskSource()?.logStuck(task.issueId, 'autonomous-runner', `Autonomous execution failed ${count} times in a row — automatic retries exhausted.\n\n` +
|
|
358
|
+
`**Last failure:**\n${failureDetail}`);
|
|
359
|
+
console.log(`[Scheduler] Issue ${task.issueId} marked STUCK (max retries exceeded)`);
|
|
270
360
|
}
|
|
271
361
|
catch (err) {
|
|
272
362
|
console.error(`[Scheduler] Failed to update issue state:`, err);
|
|
@@ -305,11 +395,14 @@ export class AutonomousRunner {
|
|
|
305
395
|
}
|
|
306
396
|
filterAlreadyProcessed(tasks) {
|
|
307
397
|
let recovered = 0;
|
|
398
|
+
let stuckSkipped = 0;
|
|
308
399
|
let backoffSkipped = 0;
|
|
309
400
|
let noProject = 0;
|
|
310
|
-
|
|
401
|
+
let unresolvable = 0;
|
|
402
|
+
const toUnstick = [];
|
|
311
403
|
const filtered = tasks.filter(task => {
|
|
312
404
|
const id = task.issueId || task.id;
|
|
405
|
+
const isStuck = task.labels?.includes(STUCK_LABEL) ?? false;
|
|
313
406
|
// No Linear project → can't be routed to a repo. Drop here (quietly, once per
|
|
314
407
|
// heartbeat) instead of letting a whole batch of project-less Todos reach the
|
|
315
408
|
// per-task selector and spam "No repo mapped to ... undefined" every cycle.
|
|
@@ -317,24 +410,54 @@ export class AutonomousRunner {
|
|
|
317
410
|
noProject++;
|
|
318
411
|
return false;
|
|
319
412
|
}
|
|
413
|
+
// Project resolved to no local repo on a previous heartbeat → don't re-pick
|
|
414
|
+
// it (it would starve runnable tasks behind it). (INT-1875)
|
|
415
|
+
if (this.unresolvableIssueIds.has(id)) {
|
|
416
|
+
unresolvable++;
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
320
419
|
// Check rejection limit first
|
|
321
420
|
if (isRejectionLimitReached(id)) {
|
|
322
421
|
return false; // Skip tasks that hit max rejection limit
|
|
323
422
|
}
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
|
|
423
|
+
// Stuck handling (INT-1908): a permanently-blocked issue is parked in Backlog
|
|
424
|
+
// with the `swarm:stuck` label and must NOT be retried automatically. The
|
|
425
|
+
// recovery branch only fires when the user pulls the issue back to an active
|
|
426
|
+
// state — the previous code re-selected it every heartbeat because blocking
|
|
427
|
+
// left it in Todo (a recoverable state), which the recovery branch then
|
|
428
|
+
// mistook for deliberate user intervention.
|
|
429
|
+
const hasFailureHistory = this.completedTaskIds.has(id) || (this.failedTaskCounts.get(id) ?? 0) >= AutonomousRunner.MAX_RETRY_COUNT;
|
|
430
|
+
const stuckDecision = classifyStuck({ isStuck, linearState: task.linearState, hasFailureHistory });
|
|
431
|
+
if (stuckDecision === 'recover') {
|
|
327
432
|
this.completedTaskIds.delete(id);
|
|
328
433
|
this.failedTaskCounts.delete(id);
|
|
329
434
|
clearRejection(id); // Clear rejection count on recovery
|
|
330
435
|
clearRetryTime(id, this.failedTaskRetryTimes); // Clear retry backoff time
|
|
436
|
+
if (isStuck)
|
|
437
|
+
toUnstick.push(id); // strip the stuck label so it is not re-skipped
|
|
331
438
|
recovered++;
|
|
332
439
|
return true;
|
|
333
440
|
}
|
|
441
|
+
if (stuckDecision === 'skip-stuck') {
|
|
442
|
+
// Durable across restarts: the label lives on the Linear issue, not in the
|
|
443
|
+
// in-memory counters that a restart would lose.
|
|
444
|
+
stuckSkipped++;
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
334
447
|
if (this.completedTaskIds.has(id))
|
|
335
448
|
return false;
|
|
336
449
|
if ((this.failedTaskCounts.get(id) ?? 0) >= AutonomousRunner.MAX_RETRY_COUNT)
|
|
337
450
|
return false;
|
|
451
|
+
// External-claim guard (INT-1979 dup): an issue set to 'In Progress' that THIS
|
|
452
|
+
// daemon never claimed is owned by a human or another agent — picking it up
|
|
453
|
+
// would re-decompose work someone is already doing (that spawned duplicate
|
|
454
|
+
// INT-1980 sub-issues + a redundant PR). markTaskInProgress writes
|
|
455
|
+
// execution.status='in_progress' when WE claim, so our own in-flight work
|
|
456
|
+
// (incl. resumption after a restart) still passes; a bare Linear 'In Progress'
|
|
457
|
+
// with no local claim record is skipped.
|
|
458
|
+
if (task.linearState === 'In Progress' && getTaskState(id)?.execution?.status !== 'in_progress') {
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
338
461
|
// Check if task is in exponential backoff period
|
|
339
462
|
if (!canRetryNow(id, this.failedTaskRetryTimes)) {
|
|
340
463
|
backoffSkipped++;
|
|
@@ -342,6 +465,14 @@ export class AutonomousRunner {
|
|
|
342
465
|
}
|
|
343
466
|
return true;
|
|
344
467
|
});
|
|
468
|
+
// Strip the stuck label from issues the user pulled back (fire-and-forget — a
|
|
469
|
+
// failed unstick just means the next heartbeat tries again).
|
|
470
|
+
for (const id of toUnstick) {
|
|
471
|
+
getTaskSource()?.unstick(id).catch(err => console.warn(`[AutonomousRunner] Failed to clear stuck label for ${id}:`, err));
|
|
472
|
+
}
|
|
473
|
+
if (stuckSkipped > 0) {
|
|
474
|
+
this.syslog(`🛑 Skipped ${stuckSkipped} stuck issue(s) (retries exhausted — remove the \`${STUCK_LABEL}\` label or move to Todo to retry)`);
|
|
475
|
+
}
|
|
345
476
|
if (recovered > 0) {
|
|
346
477
|
this.saveTaskState();
|
|
347
478
|
this.syslog(`♻ Recovered ${recovered} Todo issues from completed/failed/rejected list`);
|
|
@@ -352,6 +483,9 @@ export class AutonomousRunner {
|
|
|
352
483
|
if (noProject > 0) {
|
|
353
484
|
this.syslog(`— Skipped ${noProject} issue(s) with no Linear project (assign a project in Linear to enable)`);
|
|
354
485
|
}
|
|
486
|
+
if (unresolvable > 0) {
|
|
487
|
+
this.syslog(`— Skipped ${unresolvable} issue(s) whose Linear project maps to no local repo (fix the project or add the repo)`);
|
|
488
|
+
}
|
|
355
489
|
return filtered;
|
|
356
490
|
}
|
|
357
491
|
/**
|
|
@@ -531,16 +665,20 @@ export class AutonomousRunner {
|
|
|
531
665
|
return;
|
|
532
666
|
}
|
|
533
667
|
this.syslog('✓ Time window: allowed');
|
|
534
|
-
// 1.
|
|
535
|
-
|
|
536
|
-
if (
|
|
537
|
-
|
|
538
|
-
|
|
668
|
+
// 1.2 Rate-limit hold — skip the heartbeat while a 429 pause is still active.
|
|
669
|
+
// Cleared implicitly once the clock passes rateLimitUntil. (INT-1906)
|
|
670
|
+
if (Date.now() < this.rateLimitUntil) {
|
|
671
|
+
const remainSec = Math.max(0, Math.ceil((this.rateLimitUntil - Date.now()) / 1000));
|
|
672
|
+
const resetsLabel = new Date(this.rateLimitUntil).toISOString();
|
|
673
|
+
console.log(`[AutonomousRunner] Rate limit hold active — ${remainSec}s remaining (until ${resetsLabel})`);
|
|
674
|
+
this.syslog(`⏸ Rate limit hold: ~${remainSec}s remaining`);
|
|
675
|
+
broadcastEvent({ type: 'log', data: { taskId: 'system', stage: 'rate_limit', line: `⏸ Rate limit hold: ~${remainSec}s remaining` } });
|
|
539
676
|
return;
|
|
540
677
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
678
|
+
// 1.5 Quota gate (removed) — was a Claude Max quota check (api.anthropic.com
|
|
679
|
+
// /oauth/usage). OpenSwarm runs codex-responses now, not claude -p, so a Claude
|
|
680
|
+
// quota gate is irrelevant; it only spammed 401s and could wrongly skip codex
|
|
681
|
+
// work. codex-responses self-protects via RateLimitError (scheduler pause).
|
|
544
682
|
// 1.6 Pace gate (removed)
|
|
545
683
|
// The 5h rolling window cap (globalCap = projects × dailyTaskCap) and
|
|
546
684
|
// turbo-mode multiplier used to gate heartbeat here. Both were removed
|
|
@@ -619,7 +757,7 @@ export class AutonomousRunner {
|
|
|
619
757
|
// Only execute Todo tasks; Backlog is fetched for dashboard display only
|
|
620
758
|
const executableTasks = tasks.filter(t => t.linearState !== 'Backlog');
|
|
621
759
|
let tasksForEngine = executableTasks;
|
|
622
|
-
if (this.
|
|
760
|
+
if (this.shouldFilterByEnabled()) {
|
|
623
761
|
// Explicit repo↔Linear mapping — match fetched issues to repos by the Linear
|
|
624
762
|
// projectId the user picked in `openswarm add` (written to <repo>/openswarm.json),
|
|
625
763
|
// NOT by guessing from the repo directory name. Built fresh each cycle so a
|
|
@@ -685,6 +823,8 @@ export class AutonomousRunner {
|
|
|
685
823
|
const projectPath = await this.resolveProjectPath(task);
|
|
686
824
|
if (!projectPath) {
|
|
687
825
|
this.syslog(`✗ Cannot resolve project path for "${task.linearProject?.name || task.title}" — skipping`);
|
|
826
|
+
// Record so it isn't re-picked every heartbeat (starvation). (INT-1875)
|
|
827
|
+
this.unresolvableIssueIds.add(task.issueId || task.id);
|
|
688
828
|
continue;
|
|
689
829
|
}
|
|
690
830
|
if (task.linearProject?.name) {
|
|
@@ -694,7 +834,7 @@ export class AutonomousRunner {
|
|
|
694
834
|
this.syslog(` Project busy: ${projectPath}`);
|
|
695
835
|
continue;
|
|
696
836
|
}
|
|
697
|
-
if (this.
|
|
837
|
+
if (this.shouldFilterByEnabled() && !this.isProjectEnabled(projectPath)) {
|
|
698
838
|
this.syslog(` Project not enabled: ${projectPath}`);
|
|
699
839
|
continue;
|
|
700
840
|
}
|
|
@@ -777,11 +917,16 @@ export class AutonomousRunner {
|
|
|
777
917
|
if (!projectPath) {
|
|
778
918
|
const errorMsg = `Failed to resolve project path for "${task.linearProject?.name || task.title}"`;
|
|
779
919
|
console.error(`[AutonomousRunner] ${errorMsg}`);
|
|
920
|
+
// Record so this issue isn't re-picked every heartbeat (it would starve
|
|
921
|
+
// runnable tasks behind it). Cleared on restart. (INT-1875)
|
|
922
|
+
this.unresolvableIssueIds.add(task.issueId || task.id);
|
|
780
923
|
await reportToDiscord(t('runner.projectMappingFailed', { title: task.title, project: task.linearProject?.name || 'unknown' }));
|
|
924
|
+
// Move on to the next actionable task instead of ending the heartbeat here.
|
|
925
|
+
this.scheduleNextHeartbeat();
|
|
781
926
|
return;
|
|
782
927
|
}
|
|
783
928
|
// Skip if project is not in enabled list (allow-list; empty = nothing runs)
|
|
784
|
-
if (this.
|
|
929
|
+
if (this.shouldFilterByEnabled() && !this.isProjectEnabled(projectPath)) {
|
|
785
930
|
console.log(`[AutonomousRunner] Project not enabled, skipping: ${projectPath}`);
|
|
786
931
|
return;
|
|
787
932
|
}
|
|
@@ -814,6 +959,18 @@ export class AutonomousRunner {
|
|
|
814
959
|
}
|
|
815
960
|
// Single execution (legacy)
|
|
816
961
|
const result = await this.executePipeline(task, projectPath);
|
|
962
|
+
// Rate-limited: pause until quota resets. Return before any Discord/Linear
|
|
963
|
+
// reporting or state change — no failure count, no card spam. Same as the
|
|
964
|
+
// scheduler 'failed' handler's rate_limited branch. (INT-1906)
|
|
965
|
+
if (result.finalStatus === 'rate_limited') {
|
|
966
|
+
const resetsAt = result.rateLimitResetsAt ?? Date.now() + 60_000;
|
|
967
|
+
this.rateLimitUntil = resetsAt;
|
|
968
|
+
const waitSec = Math.max(0, Math.ceil((resetsAt - Date.now()) / 1000));
|
|
969
|
+
const resetsLabel = new Date(resetsAt).toISOString();
|
|
970
|
+
console.warn(`[AutonomousRunner] Rate limit hit for ${this.formatTaskContext(task)} — pausing until ${resetsLabel} (~${waitSec}s)`);
|
|
971
|
+
broadcastEvent({ type: 'log', data: { taskId: task.issueId || task.id, stage: 'rate_limit', line: `⏸ Rate limited — pausing ~${waitSec}s (until ${resetsLabel})` } });
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
817
974
|
await reportToDiscord(formatPipelineResultEmbed(result));
|
|
818
975
|
// Update Linear issue state
|
|
819
976
|
if (task.issueId) {
|
|
@@ -1050,6 +1207,26 @@ export class AutonomousRunner {
|
|
|
1050
1207
|
if (this.config.reviewerModel) {
|
|
1051
1208
|
this.config.reviewerModel = mapModelForProvider(this.config.reviewerModel, 'reviewer');
|
|
1052
1209
|
}
|
|
1210
|
+
// jobProfiles ALSO pin per-role models (config's light/heavy → e.g. qwen), and getModelForRole
|
|
1211
|
+
// gives the profile model precedence over defaultRoles. Remapping only defaultRoles left every
|
|
1212
|
+
// estimate-matched task on its old provider's model → "I switched to Codex but it still uses the
|
|
1213
|
+
// old provider". Remap the profiles too: an incompatible id becomes undefined so the adapter
|
|
1214
|
+
// falls back to its OWN default model.
|
|
1215
|
+
if (this.config.jobProfiles) {
|
|
1216
|
+
for (const profile of this.config.jobProfiles) {
|
|
1217
|
+
if (!profile.roles)
|
|
1218
|
+
continue;
|
|
1219
|
+
for (const role of Object.keys(profile.roles)) {
|
|
1220
|
+
const mapped = mapModelForProvider(profile.roles[role], role);
|
|
1221
|
+
if (mapped === undefined)
|
|
1222
|
+
delete profile.roles[role];
|
|
1223
|
+
else
|
|
1224
|
+
profile.roles[role] = mapped;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
// Persist the choice so a daemon restart keeps it (in-memory switch was lost every restart).
|
|
1229
|
+
writeProviderOverride(adapter);
|
|
1053
1230
|
console.log(`[AutonomousRunner] Provider switched: ${adapter}`);
|
|
1054
1231
|
}
|
|
1055
1232
|
pauseScheduler() { this.scheduler.pause(); }
|
|
@@ -1072,6 +1249,7 @@ export class AutonomousRunner {
|
|
|
1072
1249
|
});
|
|
1073
1250
|
}
|
|
1074
1251
|
disableProject(projectPath) {
|
|
1252
|
+
this.projectSelectionTouched = true; // empty set now means "nothing runs" (INT-2207)
|
|
1075
1253
|
this.enabledProjects.delete(projectPath);
|
|
1076
1254
|
console.log(`[AutonomousRunner] Project disabled: ${projectPath}`);
|
|
1077
1255
|
// Disabling gates new selection AND cancels any in-flight pipeline for this
|
|
@@ -1080,10 +1258,21 @@ export class AutonomousRunner {
|
|
|
1080
1258
|
if (cancelled > 0) {
|
|
1081
1259
|
this.syslog(`⏹ Cancelled ${cancelled} in-flight task(s) for disabled project ${projectPath.split('/').pop()}`);
|
|
1082
1260
|
}
|
|
1261
|
+
this.persistSelection();
|
|
1083
1262
|
}
|
|
1084
1263
|
enableProject(projectPath) {
|
|
1264
|
+
this.projectSelectionTouched = true; // explicit selection from here on (INT-2207)
|
|
1085
1265
|
this.enabledProjects.add(projectPath);
|
|
1266
|
+
// Enabling a repo (via `openswarm add` / the dashboard) must also ALLOW it:
|
|
1267
|
+
// resolveProjectPath only reads a repo's openswarm.json for paths in
|
|
1268
|
+
// allowedProjects, so an enabled-but-not-allowed repo never resolves
|
|
1269
|
+
// ("No repo mapped"). Keep config + DecisionEngine in sync. (INT-1970)
|
|
1270
|
+
const allowed = this.config.allowedProjects ?? [];
|
|
1271
|
+
if (!allowed.includes(projectPath)) {
|
|
1272
|
+
this.updateAllowedProjects([...allowed, projectPath]);
|
|
1273
|
+
}
|
|
1086
1274
|
console.log(`[AutonomousRunner] Project enabled: ${projectPath}`);
|
|
1275
|
+
this.persistSelection();
|
|
1087
1276
|
}
|
|
1088
1277
|
/** Get all currently enabled project paths */
|
|
1089
1278
|
getEnabledProjects() {
|