@intrect/openswarm 0.17.2 → 0.17.4
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 +21 -5
- package/config.example.yaml +18 -0
- package/dist/adapters/agenticLoop.d.ts +2 -0
- package/dist/adapters/agenticLoop.d.ts.map +1 -1
- package/dist/adapters/agenticLoop.js +6 -3
- package/dist/adapters/agenticLoop.js.map +1 -1
- package/dist/adapters/base.d.ts.map +1 -1
- package/dist/adapters/base.js +9 -0
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/claude.js +6 -3
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/codex.d.ts.map +1 -1
- package/dist/adapters/codex.js +3 -2
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/codexResponses.d.ts +2 -1
- package/dist/adapters/codexResponses.d.ts.map +1 -1
- package/dist/adapters/codexResponses.js +34 -30
- package/dist/adapters/codexResponses.js.map +1 -1
- package/dist/adapters/errorClassification.d.ts +11 -0
- package/dist/adapters/errorClassification.d.ts.map +1 -1
- package/dist/adapters/errorClassification.js +21 -0
- package/dist/adapters/errorClassification.js.map +1 -1
- package/dist/adapters/gpt.d.ts.map +1 -1
- package/dist/adapters/gpt.js +1 -0
- package/dist/adapters/gpt.js.map +1 -1
- package/dist/adapters/local.d.ts.map +1 -1
- package/dist/adapters/local.js +1 -0
- package/dist/adapters/local.js.map +1 -1
- package/dist/adapters/openrouter.d.ts.map +1 -1
- package/dist/adapters/openrouter.js +1 -0
- package/dist/adapters/openrouter.js.map +1 -1
- package/dist/adapters/processRegistry.d.ts.map +1 -1
- package/dist/adapters/processRegistry.js +8 -0
- package/dist/adapters/processRegistry.js.map +1 -1
- package/dist/adapters/types.d.ts +2 -0
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/agents/pairPipeline.d.ts +10 -139
- package/dist/agents/pairPipeline.d.ts.map +1 -1
- package/dist/agents/pairPipeline.js +161 -37
- package/dist/agents/pairPipeline.js.map +1 -1
- package/dist/agents/pairPipelineTypes.d.ts +111 -0
- package/dist/agents/pairPipelineTypes.d.ts.map +1 -0
- package/dist/agents/pairPipelineTypes.js +2 -0
- package/dist/agents/pairPipelineTypes.js.map +1 -0
- package/dist/agents/pipelineGuards.d.ts.map +1 -1
- package/dist/agents/pipelineGuards.js +346 -0
- package/dist/agents/pipelineGuards.js.map +1 -1
- package/dist/agents/pipelineTaskPrefix.d.ts +3 -0
- package/dist/agents/pipelineTaskPrefix.d.ts.map +1 -0
- package/dist/agents/pipelineTaskPrefix.js +16 -0
- package/dist/agents/pipelineTaskPrefix.js.map +1 -0
- package/dist/agents/reflection.d.ts +9 -1
- package/dist/agents/reflection.d.ts.map +1 -1
- package/dist/agents/reflection.js +27 -1
- package/dist/agents/reflection.js.map +1 -1
- package/dist/agents/reviewer.d.ts +6 -0
- package/dist/agents/reviewer.d.ts.map +1 -1
- package/dist/agents/reviewer.js +4 -1
- package/dist/agents/reviewer.js.map +1 -1
- package/dist/agents/stageErrorClassification.d.ts +16 -0
- package/dist/agents/stageErrorClassification.d.ts.map +1 -0
- package/dist/agents/stageErrorClassification.js +38 -0
- package/dist/agents/stageErrorClassification.js.map +1 -0
- package/dist/agents/stageModelResolver.d.ts +8 -0
- package/dist/agents/stageModelResolver.d.ts.map +1 -0
- package/dist/agents/stageModelResolver.js +22 -0
- package/dist/agents/stageModelResolver.js.map +1 -0
- package/dist/agents/worker.d.ts +33 -0
- package/dist/agents/worker.d.ts.map +1 -1
- package/dist/agents/worker.js +59 -4
- package/dist/agents/worker.js.map +1 -1
- package/dist/agents/workerFanout.d.ts +38 -0
- package/dist/agents/workerFanout.d.ts.map +1 -0
- package/dist/agents/workerFanout.js +292 -0
- package/dist/agents/workerFanout.js.map +1 -0
- package/dist/agents/workerFanoutGate.d.ts +48 -0
- package/dist/agents/workerFanoutGate.d.ts.map +1 -0
- package/dist/agents/workerFanoutGate.js +146 -0
- package/dist/agents/workerFanoutGate.js.map +1 -0
- package/dist/agents/workerValidationEvidence.d.ts +5 -0
- package/dist/agents/workerValidationEvidence.d.ts.map +1 -0
- package/dist/agents/workerValidationEvidence.js +75 -0
- package/dist/agents/workerValidationEvidence.js.map +1 -0
- package/dist/automation/autonomousRunner.d.ts +26 -6
- package/dist/automation/autonomousRunner.d.ts.map +1 -1
- package/dist/automation/autonomousRunner.js +243 -45
- package/dist/automation/autonomousRunner.js.map +1 -1
- package/dist/automation/backlogGrooming.d.ts +52 -0
- package/dist/automation/backlogGrooming.d.ts.map +1 -0
- package/dist/automation/backlogGrooming.js +231 -0
- package/dist/automation/backlogGrooming.js.map +1 -0
- package/dist/automation/runnerExecution.d.ts +1 -1
- package/dist/automation/runnerExecution.d.ts.map +1 -1
- package/dist/automation/runnerExecution.js +31 -9
- package/dist/automation/runnerExecution.js.map +1 -1
- package/dist/automation/runnerState.d.ts +9 -0
- package/dist/automation/runnerState.d.ts.map +1 -1
- package/dist/automation/runnerState.js +17 -0
- package/dist/automation/runnerState.js.map +1 -1
- package/dist/automation/runnerTypes.d.ts +3 -1
- package/dist/automation/runnerTypes.d.ts.map +1 -1
- package/dist/automation/taskSource.d.ts +6 -3
- package/dist/automation/taskSource.d.ts.map +1 -1
- package/dist/automation/taskSource.js +5 -0
- package/dist/automation/taskSource.js.map +1 -1
- package/dist/cli/daemon.d.ts +20 -3
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +42 -2
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/fixBoard.d.ts +14 -0
- package/dist/cli/fixBoard.d.ts.map +1 -0
- package/dist/cli/fixBoard.js +32 -0
- package/dist/cli/fixBoard.js.map +1 -0
- package/dist/cli/fixCommand.d.ts +8 -0
- package/dist/cli/fixCommand.d.ts.map +1 -1
- package/dist/cli/fixCommand.js +47 -8
- package/dist/cli/fixCommand.js.map +1 -1
- package/dist/cli/memoryCommand.d.ts +37 -0
- package/dist/cli/memoryCommand.d.ts.map +1 -0
- package/dist/cli/memoryCommand.js +156 -0
- package/dist/cli/memoryCommand.js.map +1 -0
- package/dist/cli/reviewAudit.d.ts +68 -0
- package/dist/cli/reviewAudit.d.ts.map +1 -1
- package/dist/cli/reviewAudit.js +147 -4
- package/dist/cli/reviewAudit.js.map +1 -1
- package/dist/cli/reviewMaxCommand.d.ts +2 -0
- package/dist/cli/reviewMaxCommand.d.ts.map +1 -1
- package/dist/cli/reviewMaxCommand.js +61 -36
- package/dist/cli/reviewMaxCommand.js.map +1 -1
- package/dist/cli.js +68 -13
- package/dist/cli.js.map +1 -1
- package/dist/core/config.d.ts +413 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +51 -5
- package/dist/core/config.js.map +1 -1
- package/dist/core/eventHub.d.ts +22 -0
- package/dist/core/eventHub.d.ts.map +1 -1
- package/dist/core/eventHub.js +1 -0
- package/dist/core/eventHub.js.map +1 -1
- package/dist/core/providerOverride.d.ts +9 -0
- package/dist/core/providerOverride.d.ts.map +1 -1
- package/dist/core/providerOverride.js +12 -0
- package/dist/core/providerOverride.js.map +1 -1
- package/dist/core/service.d.ts.map +1 -1
- package/dist/core/service.js +8 -2
- package/dist/core/service.js.map +1 -1
- package/dist/core/types.d.ts +61 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/linear/linear.d.ts +2 -1
- package/dist/linear/linear.d.ts.map +1 -1
- package/dist/linear/linear.js +16 -6
- package/dist/linear/linear.js.map +1 -1
- package/dist/locale/prompts/en.d.ts.map +1 -1
- package/dist/locale/prompts/en.js +11 -1
- 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 +11 -1
- package/dist/locale/prompts/ko.js.map +1 -1
- package/dist/memory/compaction.d.ts +2 -2
- package/dist/memory/compaction.d.ts.map +1 -1
- package/dist/memory/compaction.js +22 -17
- package/dist/memory/compaction.js.map +1 -1
- package/dist/memory/index.d.ts +3 -3
- package/dist/memory/index.js +3 -3
- package/dist/memory/memoryCore.d.ts +15 -27
- package/dist/memory/memoryCore.d.ts.map +1 -1
- package/dist/memory/memoryCore.js +112 -97
- package/dist/memory/memoryCore.js.map +1 -1
- package/dist/memory/memoryFilters.d.ts +8 -0
- package/dist/memory/memoryFilters.d.ts.map +1 -0
- package/dist/memory/memoryFilters.js +26 -0
- package/dist/memory/memoryFilters.js.map +1 -0
- package/dist/memory/memoryOps.d.ts +7 -23
- package/dist/memory/memoryOps.d.ts.map +1 -1
- package/dist/memory/memoryOps.js +49 -109
- package/dist/memory/memoryOps.js.map +1 -1
- package/dist/memory/repoKnowledge.d.ts.map +1 -1
- package/dist/memory/repoKnowledge.js +48 -3
- package/dist/memory/repoKnowledge.js.map +1 -1
- package/dist/orchestration/conflictDetector.d.ts.map +1 -1
- package/dist/orchestration/conflictDetector.js +26 -7
- package/dist/orchestration/conflictDetector.js.map +1 -1
- package/dist/orchestration/decisionEngine.d.ts +1 -0
- package/dist/orchestration/decisionEngine.d.ts.map +1 -1
- package/dist/orchestration/decisionEngine.js.map +1 -1
- package/dist/orchestration/taskScheduler.d.ts +5 -0
- package/dist/orchestration/taskScheduler.d.ts.map +1 -1
- package/dist/orchestration/taskScheduler.js +36 -12
- package/dist/orchestration/taskScheduler.js.map +1 -1
- package/dist/registry/memoryBridge.js +4 -4
- package/dist/registry/memoryBridge.js.map +1 -1
- package/dist/support/chatMemory.js +1 -1
- package/dist/support/chatMemory.js.map +1 -1
- package/dist/support/dashboardHtml.d.ts +1 -1
- package/dist/support/dashboardHtml.d.ts.map +1 -1
- package/dist/support/dashboardHtml.js +12 -0
- package/dist/support/dashboardHtml.js.map +1 -1
- package/dist/support/gitTracker.d.ts +30 -3
- package/dist/support/gitTracker.d.ts.map +1 -1
- package/dist/support/gitTracker.js +108 -29
- package/dist/support/gitTracker.js.map +1 -1
- package/dist/support/repoMetadata.d.ts +10 -0
- package/dist/support/repoMetadata.d.ts.map +1 -1
- package/dist/support/repoMetadata.js +31 -0
- package/dist/support/repoMetadata.js.map +1 -1
- package/dist/support/updateNotifier.d.ts +12 -0
- package/dist/support/updateNotifier.d.ts.map +1 -1
- package/dist/support/updateNotifier.js +71 -0
- package/dist/support/updateNotifier.js.map +1 -1
- package/dist/support/web.d.ts.map +1 -1
- package/dist/support/web.js +5 -3
- package/dist/support/web.js.map +1 -1
- package/dist/support/worktreeManager.d.ts +33 -0
- package/dist/support/worktreeManager.d.ts.map +1 -1
- package/dist/support/worktreeManager.js +172 -2
- package/dist/support/worktreeManager.js.map +1 -1
- package/dist/tui/App.js +1 -1
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/components/AuditBoard.d.ts +3 -2
- package/dist/tui/components/AuditBoard.d.ts.map +1 -1
- package/dist/tui/components/AuditBoard.js +13 -4
- package/dist/tui/components/AuditBoard.js.map +1 -1
- package/dist/tui/components/DataTable.d.ts +3 -1
- package/dist/tui/components/DataTable.d.ts.map +1 -1
- package/dist/tui/components/DataTable.js +22 -5
- package/dist/tui/components/DataTable.js.map +1 -1
- package/dist/tui/components/StageTimeline.d.ts +1 -1
- package/dist/tui/components/StageTimeline.d.ts.map +1 -1
- package/dist/tui/components/StageTimeline.js +5 -2
- package/dist/tui/components/StageTimeline.js.map +1 -1
- package/dist/tui/components/SubagentTree.d.ts +6 -6
- package/dist/tui/components/SubagentTree.d.ts.map +1 -1
- package/dist/tui/components/SubagentTree.js +33 -5
- package/dist/tui/components/SubagentTree.js.map +1 -1
- package/dist/tui/eventCoalescer.d.ts +29 -0
- package/dist/tui/eventCoalescer.d.ts.map +1 -0
- package/dist/tui/eventCoalescer.js +56 -0
- package/dist/tui/eventCoalescer.js.map +1 -0
- package/dist/tui/hooks/usePipelineEvents.d.ts +1 -1
- package/dist/tui/hooks/usePipelineEvents.d.ts.map +1 -1
- package/dist/tui/hooks/usePipelineEvents.js +18 -4
- package/dist/tui/hooks/usePipelineEvents.js.map +1 -1
- package/dist/tui/monitorRows.d.ts +41 -2
- package/dist/tui/monitorRows.d.ts.map +1 -1
- package/dist/tui/monitorRows.js +86 -7
- package/dist/tui/monitorRows.js.map +1 -1
- package/dist/tui/panels/MonitorPanel.d.ts +2 -1
- package/dist/tui/panels/MonitorPanel.d.ts.map +1 -1
- package/dist/tui/panels/MonitorPanel.js +2 -2
- package/dist/tui/panels/MonitorPanel.js.map +1 -1
- package/dist/tui/panels/PipelinePanel.d.ts.map +1 -1
- package/dist/tui/panels/PipelinePanel.js +6 -1
- package/dist/tui/panels/PipelinePanel.js.map +1 -1
- package/dist/tui/pipelineEvents.d.ts +14 -0
- package/dist/tui/pipelineEvents.d.ts.map +1 -1
- package/dist/tui/pipelineEvents.js +89 -1
- package/dist/tui/pipelineEvents.js.map +1 -1
- package/dist/tui/subagentTree.d.ts +26 -3
- package/dist/tui/subagentTree.d.ts.map +1 -1
- package/dist/tui/subagentTree.js +89 -14
- package/dist/tui/subagentTree.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,10 +4,13 @@ import type { AdapterName } from '../adapters/types.js';
|
|
|
4
4
|
export { setNotifier, setTaskSource } from './runnerExecution.js';
|
|
5
5
|
export type { AutonomousConfig, RunnerState } from './runnerTypes.js';
|
|
6
6
|
export type { ProjectInfo } from './runnerState.js';
|
|
7
|
+
export declare function decisionSelectionBudget(availableSlots: number, candidateCount: number): number;
|
|
7
8
|
export declare class AutonomousRunner {
|
|
8
9
|
private config;
|
|
9
10
|
private engine;
|
|
10
11
|
private scheduler;
|
|
12
|
+
/** Adapter default-model cache for the dashboard PAIR bar (INT-2393). */
|
|
13
|
+
private defaultModelCache;
|
|
11
14
|
private cronJob;
|
|
12
15
|
private state;
|
|
13
16
|
private _heartbeatRunning;
|
|
@@ -30,6 +33,9 @@ export declare class AutonomousRunner {
|
|
|
30
33
|
* untouched stays the legacy "run all allowed projects" fallback. (INT-2207)
|
|
31
34
|
*/
|
|
32
35
|
private shouldFilterByEnabled;
|
|
36
|
+
private sameProjectCandidateCap;
|
|
37
|
+
private currentProjectLoad;
|
|
38
|
+
private canQueueProjectCandidate;
|
|
33
39
|
/** Persist the project selection so it survives a restart. No-op under dryRun
|
|
34
40
|
* (tests) to avoid touching the real ~/.openswarm. (INT-2208) */
|
|
35
41
|
private persistSelection;
|
|
@@ -41,9 +47,11 @@ export declare class AutonomousRunner {
|
|
|
41
47
|
private completedTaskIds;
|
|
42
48
|
private failedTaskCounts;
|
|
43
49
|
private failedTaskRetryTimes;
|
|
50
|
+
private lastFailureDetails;
|
|
44
51
|
private static readonly MAX_RETRY_COUNT;
|
|
45
52
|
private rateLimitUntil;
|
|
46
53
|
private unresolvableIssueIds;
|
|
54
|
+
private lastBacklogGroomingAt;
|
|
47
55
|
private get taskStateRef();
|
|
48
56
|
private loadTaskState;
|
|
49
57
|
private saveTaskState;
|
|
@@ -78,8 +86,20 @@ export declare class AutonomousRunner {
|
|
|
78
86
|
* and stay silent while the summary is identical to the previous heartbeat.
|
|
79
87
|
*/
|
|
80
88
|
private syslogSkipSummary;
|
|
89
|
+
private groupTasksForGrooming;
|
|
90
|
+
private maybeRunBacklogGrooming;
|
|
81
91
|
heartbeat(): Promise<void>;
|
|
82
92
|
private heartbeatParallel;
|
|
93
|
+
private resolveRunnableCandidates;
|
|
94
|
+
private detectSafeCandidateIds;
|
|
95
|
+
/**
|
|
96
|
+
* Re-attempt of a previously failed/rejected issue: carry the last failure
|
|
97
|
+
* feedback into the run so the worker's first iteration addresses it instead
|
|
98
|
+
* of repeating the same mistake blind (INT-2474). Called on BOTH execution
|
|
99
|
+
* paths — parallel enqueue and the serial (maxConcurrentTasks=1) heartbeat.
|
|
100
|
+
*/
|
|
101
|
+
private attachPriorFeedback;
|
|
102
|
+
private enqueueCandidate;
|
|
83
103
|
/** Execute task in pair mode */
|
|
84
104
|
private executeTaskPairMode;
|
|
85
105
|
private getExecCtx;
|
|
@@ -110,29 +130,29 @@ export declare class AutonomousRunner {
|
|
|
110
130
|
};
|
|
111
131
|
getTurboMode(): boolean;
|
|
112
132
|
setTurboMode(enabled: boolean): void;
|
|
113
|
-
getAdapterSummary(): {
|
|
133
|
+
getAdapterSummary(): Promise<{
|
|
114
134
|
defaultAdapter: "codex" | "local" | "codex-responses" | "gpt" | "lmstudio" | "openrouter" | "claude";
|
|
115
135
|
worker: {
|
|
116
|
-
adapter: "
|
|
136
|
+
adapter: import("../core/types.js").AgentAdapterName;
|
|
117
137
|
model: string | undefined;
|
|
118
138
|
enabled: boolean;
|
|
119
139
|
};
|
|
120
140
|
reviewer: {
|
|
121
|
-
adapter: "
|
|
141
|
+
adapter: import("../core/types.js").AgentAdapterName;
|
|
122
142
|
model: string | undefined;
|
|
123
143
|
enabled: boolean;
|
|
124
144
|
};
|
|
125
145
|
tester: {
|
|
126
|
-
adapter: "
|
|
146
|
+
adapter: import("../core/types.js").AgentAdapterName;
|
|
127
147
|
model: string | undefined;
|
|
128
148
|
enabled: boolean;
|
|
129
149
|
} | undefined;
|
|
130
150
|
documenter: {
|
|
131
|
-
adapter: "
|
|
151
|
+
adapter: import("../core/types.js").AgentAdapterName;
|
|
132
152
|
model: string | undefined;
|
|
133
153
|
enabled: boolean;
|
|
134
154
|
} | undefined;
|
|
135
|
-
}
|
|
155
|
+
}>;
|
|
136
156
|
switchProvider(adapter: AdapterName): void;
|
|
137
157
|
pauseScheduler(): void;
|
|
138
158
|
resumeScheduler(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autonomousRunner.d.ts","sourceRoot":"","sources":["../../src/automation/autonomousRunner.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"autonomousRunner.d.ts","sourceRoot":"","sources":["../../src/automation/autonomousRunner.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
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, loadProjectSelection, saveProjectSelection, } from './runnerState.js';
|
|
4
|
+
import { loadTaskState, saveTaskState, buildProjectsInfo, appendPipelineHistory, getPipelineHistory, incrementRejection, clearRejection, isRejectionLimitReached, canRetryNow, setRetryTime, clearRetryTime, formatRetryTime, getDailyPaceInfo, recordProjectCompletion, loadProjectSelection, saveProjectSelection, recordLastFailureDetail, } from './runnerState.js';
|
|
5
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';
|
|
9
9
|
import { updateProjectAfterTask } from '../linear/projectUpdater.js';
|
|
10
|
-
import { initScheduler } from '../orchestration/taskScheduler.js';
|
|
10
|
+
import { initScheduler, normalizeProjectPath } from '../orchestration/taskScheduler.js';
|
|
11
11
|
import { formatPipelineResultEmbed, } from '../agents/pairPipeline.js';
|
|
12
12
|
import * as planner from '../support/planner.js';
|
|
13
13
|
import * as execution from './runnerExecution.js';
|
|
@@ -22,13 +22,25 @@ import { STUCK_LABEL } from '../linear/index.js';
|
|
|
22
22
|
import { refreshGraph, toProjectSlug } from '../knowledge/index.js';
|
|
23
23
|
import { checkAllMonitors, getActiveMonitors } from './longRunningMonitor.js';
|
|
24
24
|
import { detectFileConflicts } from '../orchestration/conflictDetector.js';
|
|
25
|
+
import { resolveAdapterDefaultModel } from '../agents/stageModelResolver.js';
|
|
26
|
+
import { applyBacklogGrooming, filterGroomableTasks, runBacklogGroomingPlanner, summarizeGroomingDecision, } from './backlogGrooming.js';
|
|
25
27
|
// Re-export types and integration setters (used by service.ts)
|
|
26
28
|
export { setNotifier, setTaskSource } from './runnerExecution.js';
|
|
27
29
|
let runnerInstance = null;
|
|
30
|
+
const DECISION_SELECTION_OVERSAMPLE = 3;
|
|
31
|
+
export function decisionSelectionBudget(availableSlots, candidateCount) {
|
|
32
|
+
const slots = Math.max(0, Math.floor(availableSlots));
|
|
33
|
+
const candidates = Math.max(0, Math.floor(candidateCount));
|
|
34
|
+
if (slots === 0 || candidates === 0)
|
|
35
|
+
return 0;
|
|
36
|
+
return Math.min(candidates, Math.max(slots, slots * DECISION_SELECTION_OVERSAMPLE));
|
|
37
|
+
}
|
|
28
38
|
export class AutonomousRunner {
|
|
29
39
|
config;
|
|
30
40
|
engine;
|
|
31
41
|
scheduler;
|
|
42
|
+
/** Adapter default-model cache for the dashboard PAIR bar (INT-2393). */
|
|
43
|
+
defaultModelCache = new Map();
|
|
32
44
|
cronJob = null;
|
|
33
45
|
state = {
|
|
34
46
|
isRunning: false,
|
|
@@ -80,6 +92,30 @@ export class AutonomousRunner {
|
|
|
80
92
|
shouldFilterByEnabled() {
|
|
81
93
|
return this.projectSelectionTouched || this.enabledProjects.size > 0;
|
|
82
94
|
}
|
|
95
|
+
sameProjectCandidateCap() {
|
|
96
|
+
const sameProjectParallel = (this.config.allowSameProjectConcurrent ?? true) && (this.config.worktreeMode ?? false);
|
|
97
|
+
if (!sameProjectParallel || this.config.maxConcurrentPerProject == null)
|
|
98
|
+
return null;
|
|
99
|
+
const cap = Math.floor(this.config.maxConcurrentPerProject);
|
|
100
|
+
const maxConcurrent = this.config.maxConcurrentTasks ?? 1;
|
|
101
|
+
return Math.max(1, Math.min(cap, maxConcurrent));
|
|
102
|
+
}
|
|
103
|
+
currentProjectLoad(projectPath) {
|
|
104
|
+
const target = normalizeProjectPath(projectPath);
|
|
105
|
+
const queued = this.scheduler.getQueuedTasks()
|
|
106
|
+
.filter(task => normalizeProjectPath(task.projectPath) === target)
|
|
107
|
+
.length;
|
|
108
|
+
const running = this.scheduler.getRunningTasks()
|
|
109
|
+
.filter(task => normalizeProjectPath(task.projectPath) === target)
|
|
110
|
+
.length;
|
|
111
|
+
return queued + running;
|
|
112
|
+
}
|
|
113
|
+
canQueueProjectCandidate(projectPath) {
|
|
114
|
+
const cap = this.sameProjectCandidateCap();
|
|
115
|
+
if (cap == null)
|
|
116
|
+
return true;
|
|
117
|
+
return this.currentProjectLoad(projectPath) < cap;
|
|
118
|
+
}
|
|
83
119
|
/** Persist the project selection so it survives a restart. No-op under dryRun
|
|
84
120
|
* (tests) to avoid touching the real ~/.openswarm. (INT-2208) */
|
|
85
121
|
persistSelection() {
|
|
@@ -99,6 +135,10 @@ export class AutonomousRunner {
|
|
|
99
135
|
completedTaskIds = new Set();
|
|
100
136
|
failedTaskCounts = new Map();
|
|
101
137
|
failedTaskRetryTimes = new Map(); // issueId → next retry timestamp (ms)
|
|
138
|
+
// Last failure feedback per issue — re-injected into the next attempt's worker
|
|
139
|
+
// prompt so re-picked tasks don't restart blind and repeat the same mistake
|
|
140
|
+
// the reviewer already called out (INT-2474). Persisted; cleared on success.
|
|
141
|
+
lastFailureDetails = new Map();
|
|
102
142
|
static MAX_RETRY_COUNT = 4; // Increased from 2 to allow more retries with backoff
|
|
103
143
|
// Rate-limit hold: epoch ms until which all task execution is paused.
|
|
104
144
|
// Set when any adapter returns a 429 / usage_limit_reached response (INT-1906).
|
|
@@ -107,11 +147,13 @@ export class AutonomousRunner {
|
|
|
107
147
|
// the first resolve failure so they aren't re-picked every heartbeat (which
|
|
108
148
|
// starved other actionable tasks — they were top-priority but never runnable). (INT-1875)
|
|
109
149
|
unresolvableIssueIds = new Set();
|
|
150
|
+
lastBacklogGroomingAt = 0;
|
|
110
151
|
get taskStateRef() {
|
|
111
152
|
return {
|
|
112
153
|
completedTaskIds: this.completedTaskIds,
|
|
113
154
|
failedTaskCounts: this.failedTaskCounts,
|
|
114
155
|
failedTaskRetryTimes: this.failedTaskRetryTimes,
|
|
156
|
+
lastFailureDetails: this.lastFailureDetails,
|
|
115
157
|
};
|
|
116
158
|
}
|
|
117
159
|
loadTaskState() {
|
|
@@ -158,6 +200,7 @@ export class AutonomousRunner {
|
|
|
158
200
|
this.scheduler = initScheduler({
|
|
159
201
|
maxConcurrent: config.maxConcurrentTasks ?? 1,
|
|
160
202
|
allowSameProjectConcurrent: config.allowSameProjectConcurrent ?? true,
|
|
203
|
+
maxConcurrentPerProject: config.maxConcurrentPerProject,
|
|
161
204
|
worktreeMode: config.worktreeMode ?? false,
|
|
162
205
|
});
|
|
163
206
|
// Set up scheduler event handling
|
|
@@ -180,6 +223,7 @@ export class AutonomousRunner {
|
|
|
180
223
|
this.completedTaskIds.add(task.issueId);
|
|
181
224
|
clearRejection(task.issueId); // Clear rejection count on success
|
|
182
225
|
clearRetryTime(task.issueId, this.failedTaskRetryTimes); // Clear retry backoff time
|
|
226
|
+
this.lastFailureDetails.delete(task.issueId); // Stale feedback must not haunt future work
|
|
183
227
|
this.saveTaskState();
|
|
184
228
|
// Track project-level pace (5h rolling window)
|
|
185
229
|
const projectName = task.linearProject?.name ?? 'unknown';
|
|
@@ -297,6 +341,8 @@ export class AutonomousRunner {
|
|
|
297
341
|
if (task.issueId && result.finalStatus === 'rejected') {
|
|
298
342
|
const feedback = result.reviewResult?.feedback || 'No feedback provided';
|
|
299
343
|
const rejectionCount = incrementRejection(task.issueId, feedback);
|
|
344
|
+
// Persist for prompt injection on the retry (same mechanism as failures).
|
|
345
|
+
recordLastFailureDetail(this.taskStateRef, task.issueId, feedback);
|
|
300
346
|
// Store the rejection reason as a repo pitfall (constraint) — blocks repeating the same mistake
|
|
301
347
|
if (result.taskContext?.projectPath) {
|
|
302
348
|
await recordTaskOutcome(result.taskContext.projectPath, {
|
|
@@ -344,17 +390,20 @@ export class AutonomousRunner {
|
|
|
344
390
|
if (task.issueId) {
|
|
345
391
|
const count = (this.failedTaskCounts.get(task.issueId) ?? 0) + 1;
|
|
346
392
|
this.failedTaskCounts.set(task.issueId, count);
|
|
393
|
+
// Surface the underlying failure (worker CLI error / review feedback) so the
|
|
394
|
+
// stuck comment is actionable instead of an opaque "failed N times", AND
|
|
395
|
+
// persist it so the NEXT attempt starts with this feedback instead of
|
|
396
|
+
// blind — re-picked tasks used to repeat the exact same mistake (INT-2474).
|
|
397
|
+
const failureDetail = result.workerResult?.error
|
|
398
|
+
|| result.reviewResult?.feedback
|
|
399
|
+
|| 'No error detail captured (worker produced no output).';
|
|
400
|
+
recordLastFailureDetail(this.taskStateRef, task.issueId, failureDetail);
|
|
347
401
|
if (count >= AutonomousRunner.MAX_RETRY_COUNT) {
|
|
348
402
|
// Max retries exceeded - permanently block
|
|
349
403
|
this.completedTaskIds.add(task.issueId); // Prevent re-selection
|
|
350
404
|
clearRetryTime(task.issueId, this.failedTaskRetryTimes); // Clear retry time
|
|
351
405
|
this.saveTaskState();
|
|
352
406
|
console.log(`[Scheduler] Task failure count: ${count}/${AutonomousRunner.MAX_RETRY_COUNT} for ${taskCtx} — STUCK`);
|
|
353
|
-
// Surface the underlying failure (worker CLI error / review feedback) so the
|
|
354
|
-
// stuck comment is actionable instead of an opaque "failed N times".
|
|
355
|
-
const failureDetail = result.workerResult?.error
|
|
356
|
-
|| result.reviewResult?.feedback
|
|
357
|
-
|| 'No error detail captured (worker produced no output).';
|
|
358
407
|
try {
|
|
359
408
|
await execution.syncFailureState(task, `Autonomous execution failed ${count} times: ${failureDetail}`);
|
|
360
409
|
await getTaskSource()?.logStuck(task.issueId, 'autonomous-runner', `Autonomous execution failed ${count} times in a row — automatic retries exhausted.\n\n` +
|
|
@@ -655,6 +704,87 @@ export class AutonomousRunner {
|
|
|
655
704
|
for (const line of lines)
|
|
656
705
|
this.syslog(line);
|
|
657
706
|
}
|
|
707
|
+
async groupTasksForGrooming(tasks) {
|
|
708
|
+
const byProjectId = new Map();
|
|
709
|
+
for (const repoPath of this.config.allowedProjects) {
|
|
710
|
+
try {
|
|
711
|
+
const resolvedPath = repoPath.replace('~', process.env.HOME || '');
|
|
712
|
+
const meta = await loadRepoMetadata(resolvedPath);
|
|
713
|
+
if (meta?.linear?.projectId)
|
|
714
|
+
byProjectId.set(meta.linear.projectId, resolvedPath);
|
|
715
|
+
}
|
|
716
|
+
catch {
|
|
717
|
+
// Grooming is advisory; unreadable metadata should not block normal work.
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
const groups = new Map();
|
|
721
|
+
for (const task of tasks) {
|
|
722
|
+
const projectPath = task.projectPath
|
|
723
|
+
?? (task.linearProject?.id ? byProjectId.get(task.linearProject.id) : undefined)
|
|
724
|
+
?? undefined;
|
|
725
|
+
if (!projectPath)
|
|
726
|
+
continue;
|
|
727
|
+
const list = groups.get(projectPath) ?? [];
|
|
728
|
+
list.push(task);
|
|
729
|
+
groups.set(projectPath, list);
|
|
730
|
+
}
|
|
731
|
+
return groups;
|
|
732
|
+
}
|
|
733
|
+
async maybeRunBacklogGrooming(tasks) {
|
|
734
|
+
const cfg = this.config.backlogGrooming;
|
|
735
|
+
if (!cfg?.enabled)
|
|
736
|
+
return tasks;
|
|
737
|
+
const cadenceMs = Math.max(1, cfg.cadenceHours ?? 24) * 60 * 60 * 1000;
|
|
738
|
+
const now = Date.now();
|
|
739
|
+
if (this.lastBacklogGroomingAt && now - this.lastBacklogGroomingAt < cadenceMs)
|
|
740
|
+
return tasks;
|
|
741
|
+
const source = getTaskSource();
|
|
742
|
+
if (!source) {
|
|
743
|
+
this.syslog('⚠ Backlog grooming skipped: no task source');
|
|
744
|
+
return tasks;
|
|
745
|
+
}
|
|
746
|
+
const groomable = filterGroomableTasks(tasks);
|
|
747
|
+
if (groomable.length === 0)
|
|
748
|
+
return tasks;
|
|
749
|
+
const mode = cfg.mode ?? 'comment';
|
|
750
|
+
const moved = new Set();
|
|
751
|
+
const groups = await this.groupTasksForGrooming(groomable);
|
|
752
|
+
if (groups.size === 0) {
|
|
753
|
+
this.syslog('⚠ Backlog grooming skipped: no mapped project paths');
|
|
754
|
+
return tasks;
|
|
755
|
+
}
|
|
756
|
+
let successfulPlannerRuns = 0;
|
|
757
|
+
for (const [projectPath, groupTasks] of groups) {
|
|
758
|
+
this.syslog(`⟳ Backlog grooming: ${groupTasks.length} issue(s) in ${projectPath.split('/').pop()}`);
|
|
759
|
+
const result = await runBacklogGroomingPlanner({
|
|
760
|
+
tasks: groupTasks,
|
|
761
|
+
projectPath,
|
|
762
|
+
projectName: groupTasks[0]?.linearProject?.name,
|
|
763
|
+
model: cfg.plannerModel ?? this.config.plannerModel,
|
|
764
|
+
timeoutMs: cfg.plannerTimeoutMs ?? this.config.plannerTimeoutMs,
|
|
765
|
+
maxIssues: cfg.maxIssues,
|
|
766
|
+
onLog: (line) => broadcastEvent({ type: 'log', data: { taskId: 'system', stage: 'groom', line } }),
|
|
767
|
+
});
|
|
768
|
+
if (!result.success) {
|
|
769
|
+
this.syslog(`⚠ Backlog grooming failed: ${result.error ?? 'unknown error'}`);
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
successfulPlannerRuns++;
|
|
773
|
+
const validIssueIds = new Set(groupTasks.map(task => task.issueId || task.id));
|
|
774
|
+
const applied = await applyBacklogGrooming(source, result, mode, validIssueIds);
|
|
775
|
+
for (const issueId of applied.movedIssueIds)
|
|
776
|
+
moved.add(issueId);
|
|
777
|
+
this.syslog(`✓ Backlog grooming: ${result.decisions.length} decision(s), ${applied.commented} comment(s), ${applied.failedComments} comment failure(s), ${applied.updatedDescriptions} description update(s), ${applied.moved} moved, ${applied.skippedUnknown} unknown skipped`);
|
|
778
|
+
for (const decision of result.decisions.slice(0, 5)) {
|
|
779
|
+
this.syslog(` ${summarizeGroomingDecision(decision)}`);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (successfulPlannerRuns > 0)
|
|
783
|
+
this.lastBacklogGroomingAt = now;
|
|
784
|
+
if (moved.size === 0)
|
|
785
|
+
return tasks;
|
|
786
|
+
return tasks.filter(task => !moved.has(task.issueId || task.id));
|
|
787
|
+
}
|
|
658
788
|
async heartbeat() {
|
|
659
789
|
if (this._heartbeatRunning) {
|
|
660
790
|
console.log('[AutonomousRunner] Heartbeat already running, skipping');
|
|
@@ -721,13 +851,19 @@ export class AutonomousRunner {
|
|
|
721
851
|
await reportToDiscord(`⚠️ Linear fetch failed: ${fetchResult.error}`);
|
|
722
852
|
return;
|
|
723
853
|
}
|
|
724
|
-
|
|
854
|
+
let tasks = fetchResult.tasks;
|
|
725
855
|
if (tasks.length === 0) {
|
|
726
856
|
this.syslog('— No tasks in backlog');
|
|
727
857
|
return;
|
|
728
858
|
}
|
|
729
859
|
this.lastFetchedTasks = tasks;
|
|
730
860
|
this.syslog(`✓ Found ${tasks.length} tasks from Linear`);
|
|
861
|
+
tasks = await this.maybeRunBacklogGrooming(tasks);
|
|
862
|
+
this.lastFetchedTasks = tasks;
|
|
863
|
+
if (tasks.length === 0) {
|
|
864
|
+
this.syslog('— No executable tasks after backlog grooming');
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
731
867
|
// Filter out completed and over-retried tasks
|
|
732
868
|
const filteredTasks = this.filterAlreadyProcessed(tasks);
|
|
733
869
|
if (filteredTasks.length === 0) {
|
|
@@ -836,20 +972,67 @@ export class AutonomousRunner {
|
|
|
836
972
|
}
|
|
837
973
|
this.syslog(` Tasks: ${tasksForEngine.length} enabled-or-uncached / ${executableTasks.length} executable / ${tasks.length} total`);
|
|
838
974
|
}
|
|
839
|
-
// Get validated task list from DecisionEngine
|
|
840
|
-
this.syslog('⟳ Decision Engine evaluating tasks...');
|
|
841
|
-
const decision = await this.engine.heartbeatMultiple(tasksForEngine, maxSlots, [] // No project exclusion — worktree mode isolates each task
|
|
842
|
-
);
|
|
843
|
-
console.log(`[AutonomousRunner] Decision: ${decision.action} — ${decision.reason} (${decision.tasks?.length ?? 0} tasks)`);
|
|
844
|
-
if (decision.action === 'skip' || decision.action === 'defer') {
|
|
845
|
-
this.syslog(`→ Decision: ${decision.action} — ${decision.reason}`);
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
// Add validated tasks to queue (with conflict detection)
|
|
849
975
|
let enqueuedCount = 0;
|
|
850
|
-
|
|
976
|
+
let skippedCount = 0;
|
|
977
|
+
const consideredTaskIds = new Set();
|
|
978
|
+
let pass = 0;
|
|
979
|
+
while (enqueuedCount < maxSlots) {
|
|
980
|
+
const remainingSlots = maxSlots - enqueuedCount;
|
|
981
|
+
const selectableTasks = tasksForEngine.filter(task => !consideredTaskIds.has(task.id));
|
|
982
|
+
const selectionBudget = decisionSelectionBudget(remainingSlots, selectableTasks.length);
|
|
983
|
+
if (selectionBudget === 0)
|
|
984
|
+
break;
|
|
985
|
+
this.syslog(pass === 0
|
|
986
|
+
? '⟳ Decision Engine evaluating tasks...'
|
|
987
|
+
: `⟳ Backfill pass (${remainingSlots} slot(s) open)...`);
|
|
988
|
+
const decision = await this.engine.heartbeatMultiple(selectableTasks, selectionBudget, [] // No project exclusion — worktree mode isolates each task
|
|
989
|
+
);
|
|
990
|
+
console.log(`[AutonomousRunner] Decision: ${decision.action} — ${decision.reason} (${decision.tasks?.length ?? 0} tasks)`);
|
|
991
|
+
skippedCount += decision.skippedCount ?? 0;
|
|
992
|
+
if (decision.action === 'skip' || decision.action === 'defer') {
|
|
993
|
+
this.syslog(`→ Decision: ${decision.action} — ${decision.reason}`);
|
|
994
|
+
break;
|
|
995
|
+
}
|
|
996
|
+
for (const { task } of decision.tasks) {
|
|
997
|
+
consideredTaskIds.add(task.id);
|
|
998
|
+
}
|
|
999
|
+
const candidates = await this.resolveRunnableCandidates(decision.tasks);
|
|
1000
|
+
const safeTasks = await this.detectSafeCandidateIds(candidates);
|
|
1001
|
+
const before = enqueuedCount;
|
|
1002
|
+
for (const { task, projectPath } of candidates) {
|
|
1003
|
+
if (enqueuedCount >= maxSlots)
|
|
1004
|
+
break;
|
|
1005
|
+
if (!safeTasks.has(task.id))
|
|
1006
|
+
continue;
|
|
1007
|
+
if (!this.canQueueProjectCandidate(projectPath)) {
|
|
1008
|
+
this.syslog(` Project cap reached: ${projectPath}`);
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
this.enqueueCandidate(task, projectPath);
|
|
1012
|
+
enqueuedCount++;
|
|
1013
|
+
}
|
|
1014
|
+
pass++;
|
|
1015
|
+
if (enqueuedCount >= maxSlots)
|
|
1016
|
+
break;
|
|
1017
|
+
if (decision.tasks.length === 0)
|
|
1018
|
+
break;
|
|
1019
|
+
if (selectionBudget >= selectableTasks.length)
|
|
1020
|
+
break;
|
|
1021
|
+
if (enqueuedCount === before)
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
if (enqueuedCount === 0 && skippedCount > 0) {
|
|
1025
|
+
this.syslog(`— No new tasks queued (skipped: ${skippedCount})`);
|
|
1026
|
+
}
|
|
1027
|
+
else {
|
|
1028
|
+
this.syslog(`✓ Enqueued ${enqueuedCount} task(s) | skipped: ${skippedCount}`);
|
|
1029
|
+
}
|
|
1030
|
+
// Execute tasks
|
|
1031
|
+
await this.runAvailableTasks();
|
|
1032
|
+
}
|
|
1033
|
+
async resolveRunnableCandidates(decisionTasks) {
|
|
851
1034
|
const candidates = [];
|
|
852
|
-
for (const { task } of
|
|
1035
|
+
for (const { task } of decisionTasks) {
|
|
853
1036
|
if (this.scheduler.isTaskQueued(task.id) || this.scheduler.isTaskRunning(task.id)) {
|
|
854
1037
|
this.syslog(` Skip (already queued/running): ${task.issueIdentifier || task.id.slice(0, 8)} ${task.title}`);
|
|
855
1038
|
continue;
|
|
@@ -877,6 +1060,9 @@ export class AutonomousRunner {
|
|
|
877
1060
|
// rate limiter only. Completions are still recorded for cost telemetry.
|
|
878
1061
|
candidates.push({ task, projectPath });
|
|
879
1062
|
}
|
|
1063
|
+
return candidates;
|
|
1064
|
+
}
|
|
1065
|
+
async detectSafeCandidateIds(candidates) {
|
|
880
1066
|
// Group candidates by projectPath for conflict detection
|
|
881
1067
|
const byProject = new Map();
|
|
882
1068
|
for (const c of candidates) {
|
|
@@ -916,30 +1102,36 @@ export class AutonomousRunner {
|
|
|
916
1102
|
safeTasks.add(c.task.id);
|
|
917
1103
|
}
|
|
918
1104
|
}
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1105
|
+
return safeTasks;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Re-attempt of a previously failed/rejected issue: carry the last failure
|
|
1109
|
+
* feedback into the run so the worker's first iteration addresses it instead
|
|
1110
|
+
* of repeating the same mistake blind (INT-2474). Called on BOTH execution
|
|
1111
|
+
* paths — parallel enqueue and the serial (maxConcurrentTasks=1) heartbeat.
|
|
1112
|
+
*/
|
|
1113
|
+
attachPriorFeedback(task) {
|
|
1114
|
+
if (!task.issueId)
|
|
1115
|
+
return;
|
|
1116
|
+
const prior = this.lastFailureDetails.get(task.issueId);
|
|
1117
|
+
if (prior)
|
|
1118
|
+
task.priorAttemptFeedback = prior.detail;
|
|
1119
|
+
}
|
|
1120
|
+
enqueueCandidate(task, projectPath) {
|
|
1121
|
+
this.attachPriorFeedback(task);
|
|
1122
|
+
this.scheduler.enqueue(task, projectPath);
|
|
1123
|
+
broadcastEvent({ type: 'task:queued', data: { taskId: task.id, title: task.title, projectPath, issueIdentifier: task.issueIdentifier } });
|
|
1124
|
+
this.syslog(`✓ Queued: ${task.issueIdentifier || ''} ${task.title} → ${projectPath.split('/').slice(-2).join('/')}`);
|
|
1125
|
+
// Claim the task immediately: set Linear to 'In Progress' so restarts don't re-queue it
|
|
1126
|
+
if (task.issueId) {
|
|
1127
|
+
getTaskSource()?.updateState(task.issueId, 'In Progress').catch((err) => console.warn(`[AutonomousRunner] Failed to claim issue ${task.issueIdentifier}:`, err));
|
|
937
1128
|
}
|
|
938
|
-
// Execute tasks
|
|
939
|
-
await this.runAvailableTasks();
|
|
940
1129
|
}
|
|
941
1130
|
/** Execute task in pair mode */
|
|
942
1131
|
async executeTaskPairMode(task) {
|
|
1132
|
+
// Serial path (maxConcurrentTasks=1) bypasses enqueueCandidate — attach the
|
|
1133
|
+
// prior-session feedback here too so both paths inject it (INT-2474).
|
|
1134
|
+
this.attachPriorFeedback(task);
|
|
943
1135
|
// Auto-resolve project path
|
|
944
1136
|
const projectPath = await this.resolveProjectPath(task);
|
|
945
1137
|
// Error if project path mapping failed
|
|
@@ -1146,19 +1338,25 @@ export class AutonomousRunner {
|
|
|
1146
1338
|
broadcastEvent({ type: 'log', data: { taskId: 'system', stage: 'turbo', line: 'TURBO OFF — normal pace resumed' } });
|
|
1147
1339
|
}
|
|
1148
1340
|
}
|
|
1149
|
-
getAdapterSummary() {
|
|
1341
|
+
async getAdapterSummary() {
|
|
1150
1342
|
const defaultAdapter = this.config.defaultAdapter ?? 'codex';
|
|
1151
1343
|
const defaultRoles = this.config.defaultRoles;
|
|
1344
|
+
const workerAdapter = defaultRoles?.worker?.adapter ?? defaultAdapter;
|
|
1345
|
+
const reviewerAdapter = defaultRoles?.reviewer?.adapter ?? defaultAdapter;
|
|
1152
1346
|
return {
|
|
1153
1347
|
defaultAdapter,
|
|
1154
1348
|
worker: {
|
|
1155
|
-
adapter:
|
|
1156
|
-
|
|
1349
|
+
adapter: workerAdapter,
|
|
1350
|
+
// Resolve the adapter's real default when config omits the model, so the
|
|
1351
|
+
// dashboard's PAIR bar shows what's running instead of "-". (INT-2393)
|
|
1352
|
+
model: defaultRoles?.worker?.model ?? this.config.workerModel
|
|
1353
|
+
?? await resolveAdapterDefaultModel(workerAdapter, this.defaultModelCache),
|
|
1157
1354
|
enabled: defaultRoles?.worker?.enabled !== false,
|
|
1158
1355
|
},
|
|
1159
1356
|
reviewer: {
|
|
1160
|
-
adapter:
|
|
1161
|
-
model: defaultRoles?.reviewer?.model ?? this.config.reviewerModel
|
|
1357
|
+
adapter: reviewerAdapter,
|
|
1358
|
+
model: defaultRoles?.reviewer?.model ?? this.config.reviewerModel
|
|
1359
|
+
?? await resolveAdapterDefaultModel(reviewerAdapter, this.defaultModelCache),
|
|
1162
1360
|
enabled: defaultRoles?.reviewer?.enabled !== false,
|
|
1163
1361
|
},
|
|
1164
1362
|
tester: defaultRoles?.tester ? {
|