@mcoda/mswarm 0.1.57 → 0.1.60
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 +19 -0
- package/dist/codali-executor.d.ts +266 -0
- package/dist/codali-executor.d.ts.map +1 -0
- package/dist/codali-executor.js +227 -0
- package/dist/codali-executor.js.map +1 -0
- package/dist/runtime.d.ts +36 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +219 -30
- package/dist/runtime.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +54 -0
- package/dist/server.js.map +1 -1
- package/dist/vendor/codali/agents/AgentProtocol.d.ts +287 -0
- package/dist/vendor/codali/agents/AgentProtocol.d.ts.map +1 -0
- package/dist/vendor/codali/agents/AgentProtocol.js +365 -0
- package/dist/vendor/codali/agents/AgentResolver.d.ts +23 -0
- package/dist/vendor/codali/agents/AgentResolver.d.ts.map +1 -0
- package/dist/vendor/codali/agents/AgentResolver.js +77 -0
- package/dist/vendor/codali/agents/PhaseAgentSelector.d.ts +23 -0
- package/dist/vendor/codali/agents/PhaseAgentSelector.d.ts.map +1 -0
- package/dist/vendor/codali/agents/PhaseAgentSelector.js +287 -0
- package/dist/vendor/codali/cli/EvalCommand.d.ts +37 -0
- package/dist/vendor/codali/cli/EvalCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/EvalCommand.js +333 -0
- package/dist/vendor/codali/cli/FeedbackCommand.d.ts +22 -0
- package/dist/vendor/codali/cli/FeedbackCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/FeedbackCommand.js +163 -0
- package/dist/vendor/codali/cli/RunCommand.d.ts +78 -0
- package/dist/vendor/codali/cli/RunCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/RunCommand.js +2261 -0
- package/dist/vendor/codali/cli.d.ts +3 -0
- package/dist/vendor/codali/cli.d.ts.map +1 -0
- package/dist/vendor/codali/cli.js +109 -0
- package/dist/vendor/codali/cognitive/ArchitectPlanner.d.ts +107 -0
- package/dist/vendor/codali/cognitive/ArchitectPlanner.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ArchitectPlanner.js +1726 -0
- package/dist/vendor/codali/cognitive/BuilderOutputParser.d.ts +25 -0
- package/dist/vendor/codali/cognitive/BuilderOutputParser.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/BuilderOutputParser.js +164 -0
- package/dist/vendor/codali/cognitive/BuilderRunner.d.ts +76 -0
- package/dist/vendor/codali/cognitive/BuilderRunner.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/BuilderRunner.js +1159 -0
- package/dist/vendor/codali/cognitive/ContextAssembler.d.ts +91 -0
- package/dist/vendor/codali/cognitive/ContextAssembler.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ContextAssembler.js +4547 -0
- package/dist/vendor/codali/cognitive/ContextBudget.d.ts +19 -0
- package/dist/vendor/codali/cognitive/ContextBudget.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ContextBudget.js +35 -0
- package/dist/vendor/codali/cognitive/ContextFileLoader.d.ts +30 -0
- package/dist/vendor/codali/cognitive/ContextFileLoader.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ContextFileLoader.js +307 -0
- package/dist/vendor/codali/cognitive/ContextManager.d.ts +47 -0
- package/dist/vendor/codali/cognitive/ContextManager.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ContextManager.js +272 -0
- package/dist/vendor/codali/cognitive/ContextRedactor.d.ts +18 -0
- package/dist/vendor/codali/cognitive/ContextRedactor.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ContextRedactor.js +53 -0
- package/dist/vendor/codali/cognitive/ContextSelector.d.ts +22 -0
- package/dist/vendor/codali/cognitive/ContextSelector.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ContextSelector.js +431 -0
- package/dist/vendor/codali/cognitive/ContextSerializer.d.ts +8 -0
- package/dist/vendor/codali/cognitive/ContextSerializer.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ContextSerializer.js +882 -0
- package/dist/vendor/codali/cognitive/ContextStore.d.ts +27 -0
- package/dist/vendor/codali/cognitive/ContextStore.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ContextStore.js +79 -0
- package/dist/vendor/codali/cognitive/ContextSummarizer.d.ts +16 -0
- package/dist/vendor/codali/cognitive/ContextSummarizer.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ContextSummarizer.js +45 -0
- package/dist/vendor/codali/cognitive/CostEstimator.d.ts +31 -0
- package/dist/vendor/codali/cognitive/CostEstimator.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/CostEstimator.js +66 -0
- package/dist/vendor/codali/cognitive/CriticEvaluator.d.ts +32 -0
- package/dist/vendor/codali/cognitive/CriticEvaluator.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/CriticEvaluator.js +297 -0
- package/dist/vendor/codali/cognitive/EvidenceGate.d.ts +9 -0
- package/dist/vendor/codali/cognitive/EvidenceGate.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/EvidenceGate.js +75 -0
- package/dist/vendor/codali/cognitive/GoldenExampleIndexer.d.ts +12 -0
- package/dist/vendor/codali/cognitive/GoldenExampleIndexer.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/GoldenExampleIndexer.js +34 -0
- package/dist/vendor/codali/cognitive/GoldenSetStore.d.ts +33 -0
- package/dist/vendor/codali/cognitive/GoldenSetStore.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/GoldenSetStore.js +159 -0
- package/dist/vendor/codali/cognitive/IntentSignals.d.ts +7 -0
- package/dist/vendor/codali/cognitive/IntentSignals.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/IntentSignals.js +285 -0
- package/dist/vendor/codali/cognitive/LearningGovernance.d.ts +100 -0
- package/dist/vendor/codali/cognitive/LearningGovernance.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/LearningGovernance.js +276 -0
- package/dist/vendor/codali/cognitive/MemoryWriteback.d.ts +64 -0
- package/dist/vendor/codali/cognitive/MemoryWriteback.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/MemoryWriteback.js +287 -0
- package/dist/vendor/codali/cognitive/PatchApplier.d.ts +49 -0
- package/dist/vendor/codali/cognitive/PatchApplier.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/PatchApplier.js +199 -0
- package/dist/vendor/codali/cognitive/PatchInterpreter.d.ts +35 -0
- package/dist/vendor/codali/cognitive/PatchInterpreter.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/PatchInterpreter.js +100 -0
- package/dist/vendor/codali/cognitive/PatchOutputNormalizer.d.ts +7 -0
- package/dist/vendor/codali/cognitive/PatchOutputNormalizer.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/PatchOutputNormalizer.js +59 -0
- package/dist/vendor/codali/cognitive/PostMortemAnalyzer.d.ts +17 -0
- package/dist/vendor/codali/cognitive/PostMortemAnalyzer.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/PostMortemAnalyzer.js +131 -0
- package/dist/vendor/codali/cognitive/PreferenceExtraction.d.ts +3 -0
- package/dist/vendor/codali/cognitive/PreferenceExtraction.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/PreferenceExtraction.js +85 -0
- package/dist/vendor/codali/cognitive/Prompts.d.ts +15 -0
- package/dist/vendor/codali/cognitive/Prompts.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/Prompts.js +326 -0
- package/dist/vendor/codali/cognitive/ProviderRouting.d.ts +16 -0
- package/dist/vendor/codali/cognitive/ProviderRouting.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ProviderRouting.js +24 -0
- package/dist/vendor/codali/cognitive/QueryExtraction.d.ts +12 -0
- package/dist/vendor/codali/cognitive/QueryExtraction.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/QueryExtraction.js +262 -0
- package/dist/vendor/codali/cognitive/RunHistoryIndexer.d.ts +13 -0
- package/dist/vendor/codali/cognitive/RunHistoryIndexer.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/RunHistoryIndexer.js +125 -0
- package/dist/vendor/codali/cognitive/SmartPipeline.d.ts +92 -0
- package/dist/vendor/codali/cognitive/SmartPipeline.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/SmartPipeline.js +4804 -0
- package/dist/vendor/codali/cognitive/Types.d.ts +474 -0
- package/dist/vendor/codali/cognitive/Types.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/Types.js +7 -0
- package/dist/vendor/codali/cognitive/ValidationRunner.d.ts +57 -0
- package/dist/vendor/codali/cognitive/ValidationRunner.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ValidationRunner.js +515 -0
- package/dist/vendor/codali/config/Config.d.ts +249 -0
- package/dist/vendor/codali/config/Config.d.ts.map +1 -0
- package/dist/vendor/codali/config/Config.js +200 -0
- package/dist/vendor/codali/config/ConfigLoader.d.ts +56 -0
- package/dist/vendor/codali/config/ConfigLoader.d.ts.map +1 -0
- package/dist/vendor/codali/config/ConfigLoader.js +1246 -0
- package/dist/vendor/codali/docdex/DocdexClient.d.ts +113 -0
- package/dist/vendor/codali/docdex/DocdexClient.d.ts.map +1 -0
- package/dist/vendor/codali/docdex/DocdexClient.js +524 -0
- package/dist/vendor/codali/eval/EvalRunner.d.ts +35 -0
- package/dist/vendor/codali/eval/EvalRunner.d.ts.map +1 -0
- package/dist/vendor/codali/eval/EvalRunner.js +38 -0
- package/dist/vendor/codali/eval/EvalTaskExecutor.d.ts +81 -0
- package/dist/vendor/codali/eval/EvalTaskExecutor.d.ts.map +1 -0
- package/dist/vendor/codali/eval/EvalTaskExecutor.js +371 -0
- package/dist/vendor/codali/eval/GateEvaluator.d.ts +31 -0
- package/dist/vendor/codali/eval/GateEvaluator.d.ts.map +1 -0
- package/dist/vendor/codali/eval/GateEvaluator.js +134 -0
- package/dist/vendor/codali/eval/MetricTypes.d.ts +28 -0
- package/dist/vendor/codali/eval/MetricTypes.d.ts.map +1 -0
- package/dist/vendor/codali/eval/MetricTypes.js +1 -0
- package/dist/vendor/codali/eval/MetricsAggregator.d.ts +4 -0
- package/dist/vendor/codali/eval/MetricsAggregator.d.ts.map +1 -0
- package/dist/vendor/codali/eval/MetricsAggregator.js +97 -0
- package/dist/vendor/codali/eval/RegressionComparator.d.ts +29 -0
- package/dist/vendor/codali/eval/RegressionComparator.d.ts.map +1 -0
- package/dist/vendor/codali/eval/RegressionComparator.js +155 -0
- package/dist/vendor/codali/eval/ReportInputAdapter.d.ts +52 -0
- package/dist/vendor/codali/eval/ReportInputAdapter.d.ts.map +1 -0
- package/dist/vendor/codali/eval/ReportInputAdapter.js +229 -0
- package/dist/vendor/codali/eval/ReportSerializer.d.ts +32 -0
- package/dist/vendor/codali/eval/ReportSerializer.d.ts.map +1 -0
- package/dist/vendor/codali/eval/ReportSerializer.js +33 -0
- package/dist/vendor/codali/eval/ReportStore.d.ts +18 -0
- package/dist/vendor/codali/eval/ReportStore.d.ts.map +1 -0
- package/dist/vendor/codali/eval/ReportStore.js +96 -0
- package/dist/vendor/codali/eval/SuiteLoader.d.ts +12 -0
- package/dist/vendor/codali/eval/SuiteLoader.d.ts.map +1 -0
- package/dist/vendor/codali/eval/SuiteLoader.js +51 -0
- package/dist/vendor/codali/eval/SuiteSchema.d.ts +56 -0
- package/dist/vendor/codali/eval/SuiteSchema.d.ts.map +1 -0
- package/dist/vendor/codali/eval/SuiteSchema.js +357 -0
- package/dist/vendor/codali/index.d.ts +11 -0
- package/dist/vendor/codali/index.d.ts.map +1 -0
- package/dist/vendor/codali/index.js +5 -0
- package/dist/vendor/codali/providers/CodexCliProvider.d.ts +8 -0
- package/dist/vendor/codali/providers/CodexCliProvider.d.ts.map +1 -0
- package/dist/vendor/codali/providers/CodexCliProvider.js +282 -0
- package/dist/vendor/codali/providers/OllamaRemoteProvider.d.ts +8 -0
- package/dist/vendor/codali/providers/OllamaRemoteProvider.d.ts.map +1 -0
- package/dist/vendor/codali/providers/OllamaRemoteProvider.js +300 -0
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.d.ts +8 -0
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.d.ts.map +1 -0
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.js +192 -0
- package/dist/vendor/codali/providers/ProviderRegistry.d.ts +12 -0
- package/dist/vendor/codali/providers/ProviderRegistry.d.ts.map +1 -0
- package/dist/vendor/codali/providers/ProviderRegistry.js +28 -0
- package/dist/vendor/codali/providers/ProviderTypes.d.ts +81 -0
- package/dist/vendor/codali/providers/ProviderTypes.d.ts.map +1 -0
- package/dist/vendor/codali/providers/ProviderTypes.js +1 -0
- package/dist/vendor/codali/runtime/CodaliRuntime.d.ts +183 -0
- package/dist/vendor/codali/runtime/CodaliRuntime.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/CodaliRuntime.js +1363 -0
- package/dist/vendor/codali/runtime/DeepInvestigationErrors.d.ts +39 -0
- package/dist/vendor/codali/runtime/DeepInvestigationErrors.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/DeepInvestigationErrors.js +57 -0
- package/dist/vendor/codali/runtime/RunContext.d.ts +27 -0
- package/dist/vendor/codali/runtime/RunContext.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/RunContext.js +51 -0
- package/dist/vendor/codali/runtime/RunLogQuery.d.ts +48 -0
- package/dist/vendor/codali/runtime/RunLogQuery.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/RunLogQuery.js +36 -0
- package/dist/vendor/codali/runtime/RunLogReader.d.ts +19 -0
- package/dist/vendor/codali/runtime/RunLogReader.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/RunLogReader.js +361 -0
- package/dist/vendor/codali/runtime/RunLogger.d.ts +71 -0
- package/dist/vendor/codali/runtime/RunLogger.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/RunLogger.js +100 -0
- package/dist/vendor/codali/runtime/RunTelemetryTypes.d.ts +117 -0
- package/dist/vendor/codali/runtime/RunTelemetryTypes.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/RunTelemetryTypes.js +299 -0
- package/dist/vendor/codali/runtime/Runner.d.ts +66 -0
- package/dist/vendor/codali/runtime/Runner.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/Runner.js +215 -0
- package/dist/vendor/codali/runtime/StoragePaths.d.ts +3 -0
- package/dist/vendor/codali/runtime/StoragePaths.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/StoragePaths.js +19 -0
- package/dist/vendor/codali/runtime/WorkspaceLock.d.ts +30 -0
- package/dist/vendor/codali/runtime/WorkspaceLock.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/WorkspaceLock.js +141 -0
- package/dist/vendor/codali/session/InstructionLoader.d.ts +14 -0
- package/dist/vendor/codali/session/InstructionLoader.d.ts.map +1 -0
- package/dist/vendor/codali/session/InstructionLoader.js +107 -0
- package/dist/vendor/codali/session/SessionStore.d.ts +81 -0
- package/dist/vendor/codali/session/SessionStore.d.ts.map +1 -0
- package/dist/vendor/codali/session/SessionStore.js +244 -0
- package/dist/vendor/codali/subagents/SubagentOrchestrator.d.ts +68 -0
- package/dist/vendor/codali/subagents/SubagentOrchestrator.d.ts.map +1 -0
- package/dist/vendor/codali/subagents/SubagentOrchestrator.js +150 -0
- package/dist/vendor/codali/tools/ToolRegistry.d.ts +9 -0
- package/dist/vendor/codali/tools/ToolRegistry.d.ts.map +1 -0
- package/dist/vendor/codali/tools/ToolRegistry.js +263 -0
- package/dist/vendor/codali/tools/ToolTypes.d.ts +66 -0
- package/dist/vendor/codali/tools/ToolTypes.d.ts.map +1 -0
- package/dist/vendor/codali/tools/ToolTypes.js +32 -0
- package/dist/vendor/codali/tools/diff/DiffTool.d.ts +3 -0
- package/dist/vendor/codali/tools/diff/DiffTool.d.ts.map +1 -0
- package/dist/vendor/codali/tools/diff/DiffTool.js +34 -0
- package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts +4 -0
- package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts.map +1 -0
- package/dist/vendor/codali/tools/docdex/DocdexTools.js +453 -0
- package/dist/vendor/codali/tools/filesystem/FileTools.d.ts +3 -0
- package/dist/vendor/codali/tools/filesystem/FileTools.d.ts.map +1 -0
- package/dist/vendor/codali/tools/filesystem/FileTools.js +141 -0
- package/dist/vendor/codali/tools/search/SearchTool.d.ts +3 -0
- package/dist/vendor/codali/tools/search/SearchTool.d.ts.map +1 -0
- package/dist/vendor/codali/tools/search/SearchTool.js +46 -0
- package/dist/vendor/codali/tools/shell/ShellTool.d.ts +3 -0
- package/dist/vendor/codali/tools/shell/ShellTool.d.ts.map +1 -0
- package/dist/vendor/codali/tools/shell/ShellTool.js +104 -0
- package/package.json +5 -3
|
@@ -0,0 +1,2261 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { createWriteStream, existsSync } from "node:fs";
|
|
4
|
+
import { readFile, mkdir } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { createInterface } from "node:readline/promises";
|
|
7
|
+
import process from "node:process";
|
|
8
|
+
import { loadConfig } from "../config/ConfigLoader.js";
|
|
9
|
+
import { createProvider } from "../providers/ProviderRegistry.js";
|
|
10
|
+
import { OpenAiCompatibleProvider } from "../providers/OpenAiCompatibleProvider.js";
|
|
11
|
+
import { OllamaRemoteProvider } from "../providers/OllamaRemoteProvider.js";
|
|
12
|
+
import { CodexCliProvider } from "../providers/CodexCliProvider.js";
|
|
13
|
+
import { ToolRegistry } from "../tools/ToolRegistry.js";
|
|
14
|
+
import { createFileTools } from "../tools/filesystem/FileTools.js";
|
|
15
|
+
import { createDiffTool } from "../tools/diff/DiffTool.js";
|
|
16
|
+
import { createSearchTool } from "../tools/search/SearchTool.js";
|
|
17
|
+
import { createShellTool } from "../tools/shell/ShellTool.js";
|
|
18
|
+
import { DocdexClient } from "../docdex/DocdexClient.js";
|
|
19
|
+
import { createDocdexTools } from "../tools/docdex/DocdexTools.js";
|
|
20
|
+
import { RunContext } from "../runtime/RunContext.js";
|
|
21
|
+
import { WorkspaceLock } from "../runtime/WorkspaceLock.js";
|
|
22
|
+
import { runCodaliTask } from "../runtime/CodaliRuntime.js";
|
|
23
|
+
import { RunLogger } from "../runtime/RunLogger.js";
|
|
24
|
+
import { RunLogReader } from "../runtime/RunLogReader.js";
|
|
25
|
+
import { registerProvider } from "../providers/ProviderRegistry.js";
|
|
26
|
+
import { ContextAssembler } from "../cognitive/ContextAssembler.js";
|
|
27
|
+
import { ArchitectPlanner } from "../cognitive/ArchitectPlanner.js";
|
|
28
|
+
import { BuilderRunner } from "../cognitive/BuilderRunner.js";
|
|
29
|
+
import { CriticEvaluator } from "../cognitive/CriticEvaluator.js";
|
|
30
|
+
import { ValidationRunner } from "../cognitive/ValidationRunner.js";
|
|
31
|
+
import { MemoryWriteback } from "../cognitive/MemoryWriteback.js";
|
|
32
|
+
import { PatchApplier } from "../cognitive/PatchApplier.js";
|
|
33
|
+
import { PatchInterpreter } from "../cognitive/PatchInterpreter.js";
|
|
34
|
+
import { SmartPipeline } from "../cognitive/SmartPipeline.js";
|
|
35
|
+
import { buildRoutedProvider } from "../cognitive/ProviderRouting.js";
|
|
36
|
+
import { ContextManager } from "../cognitive/ContextManager.js";
|
|
37
|
+
import { ContextStore } from "../cognitive/ContextStore.js";
|
|
38
|
+
import { ContextSummarizer } from "../cognitive/ContextSummarizer.js";
|
|
39
|
+
import { ContextRedactor } from "../cognitive/ContextRedactor.js";
|
|
40
|
+
import { estimateCostFromChars, estimateCostFromUsage, estimateUsageCostTelemetry, resolvePricing, } from "../cognitive/CostEstimator.js";
|
|
41
|
+
import { getGlobalWorkspaceDir } from "../runtime/StoragePaths.js";
|
|
42
|
+
import { resolveAgentConfig } from "../agents/AgentResolver.js";
|
|
43
|
+
import { selectPhaseAgents } from "../agents/PhaseAgentSelector.js";
|
|
44
|
+
export const parseArgs = (argv) => {
|
|
45
|
+
const parseNumberArg = (value) => {
|
|
46
|
+
if (!value)
|
|
47
|
+
return undefined;
|
|
48
|
+
const parsed = Number(value);
|
|
49
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
50
|
+
};
|
|
51
|
+
const parseBooleanArg = (value) => {
|
|
52
|
+
if (!value)
|
|
53
|
+
return undefined;
|
|
54
|
+
const normalized = value.trim().toLowerCase();
|
|
55
|
+
if (["1", "true", "yes", "on"].includes(normalized))
|
|
56
|
+
return true;
|
|
57
|
+
if (["0", "false", "no", "off"].includes(normalized))
|
|
58
|
+
return false;
|
|
59
|
+
return undefined;
|
|
60
|
+
};
|
|
61
|
+
const parseListArg = (value) => {
|
|
62
|
+
if (!value)
|
|
63
|
+
return undefined;
|
|
64
|
+
const items = value
|
|
65
|
+
.split(",")
|
|
66
|
+
.map((entry) => entry.trim())
|
|
67
|
+
.filter(Boolean);
|
|
68
|
+
return items.length ? items : undefined;
|
|
69
|
+
};
|
|
70
|
+
const parsed = {};
|
|
71
|
+
const positionals = [];
|
|
72
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
73
|
+
const arg = argv[i];
|
|
74
|
+
const next = argv[i + 1];
|
|
75
|
+
if (arg === "--workspace-root" && next) {
|
|
76
|
+
parsed.workspaceRoot = next;
|
|
77
|
+
i += 1;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (arg === "--project" && next) {
|
|
81
|
+
parsed.project = next;
|
|
82
|
+
i += 1;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (arg === "--command" && next) {
|
|
86
|
+
parsed.command = next;
|
|
87
|
+
i += 1;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (arg === "--command-run-id" && next) {
|
|
91
|
+
parsed.commandRunId = next;
|
|
92
|
+
i += 1;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (arg === "--job-id" && next) {
|
|
96
|
+
parsed.jobId = next;
|
|
97
|
+
i += 1;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (arg === "--run-id" && next) {
|
|
101
|
+
parsed.runId = next;
|
|
102
|
+
i += 1;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (arg === "--task-id" && next) {
|
|
106
|
+
parsed.taskId = next;
|
|
107
|
+
i += 1;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (arg === "--task-key" && next) {
|
|
111
|
+
parsed.taskKey = next;
|
|
112
|
+
i += 1;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (arg === "--agent" && next) {
|
|
116
|
+
parsed.agent = next;
|
|
117
|
+
i += 1;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (arg === "--agent-id" && next) {
|
|
121
|
+
parsed.agentId = next;
|
|
122
|
+
i += 1;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (arg === "--agent-slug" && next) {
|
|
126
|
+
parsed.agentSlug = next;
|
|
127
|
+
i += 1;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (arg === "--agent-librarian" && next) {
|
|
131
|
+
parsed.agentLibrarian = next;
|
|
132
|
+
i += 1;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (arg === "--agent-architect" && next) {
|
|
136
|
+
parsed.agentArchitect = next;
|
|
137
|
+
i += 1;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (arg === "--agent-builder" && next) {
|
|
141
|
+
parsed.agentBuilder = next;
|
|
142
|
+
i += 1;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (arg === "--agent-critic" && next) {
|
|
146
|
+
parsed.agentCritic = next;
|
|
147
|
+
i += 1;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (arg === "--agent-interpreter" && next) {
|
|
151
|
+
parsed.agentInterpreter = next;
|
|
152
|
+
i += 1;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (arg === "--plan-hint" && next) {
|
|
156
|
+
parsed.planHint = next;
|
|
157
|
+
i += 1;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (arg === "--smart") {
|
|
161
|
+
parsed.smart = true;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (arg === "--no-deep-investigation") {
|
|
165
|
+
parsed.deepInvestigationEnabled = false;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (arg === "--provider" && next) {
|
|
169
|
+
parsed.provider = next;
|
|
170
|
+
i += 1;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (arg === "--model" && next) {
|
|
174
|
+
parsed.model = next;
|
|
175
|
+
i += 1;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (arg === "--api-key" && next) {
|
|
179
|
+
parsed.apiKey = next;
|
|
180
|
+
i += 1;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (arg === "--base-url" && next) {
|
|
184
|
+
parsed.baseUrl = next;
|
|
185
|
+
i += 1;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if ((arg === "--workflow-profile" || arg === "--profile") && next) {
|
|
189
|
+
parsed.workflowProfile = next;
|
|
190
|
+
i += 1;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if ((arg === "--task" || arg === "--task-file") && next) {
|
|
194
|
+
parsed.taskFile = next;
|
|
195
|
+
i += 1;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (arg === "--config" && next) {
|
|
199
|
+
parsed.configPath = next;
|
|
200
|
+
i += 1;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (arg === "--docdex-base-url" && next) {
|
|
204
|
+
parsed.docdexBaseUrl = next;
|
|
205
|
+
i += 1;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (arg === "--docdex-repo-id" && next) {
|
|
209
|
+
parsed.docdexRepoId = next;
|
|
210
|
+
i += 1;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (arg === "--docdex-repo-root" && next) {
|
|
214
|
+
parsed.docdexRepoRoot = next;
|
|
215
|
+
i += 1;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (arg === "--context-mode" && next) {
|
|
219
|
+
if (next === "bundle_text" || next === "json")
|
|
220
|
+
parsed.contextMode = next;
|
|
221
|
+
i += 1;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (arg === "--context-max-files" && next) {
|
|
225
|
+
parsed.contextMaxFiles = parseNumberArg(next);
|
|
226
|
+
i += 1;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (arg === "--context-max-total-bytes" && next) {
|
|
230
|
+
parsed.contextMaxTotalBytes = parseNumberArg(next);
|
|
231
|
+
i += 1;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (arg === "--context-token-budget" && next) {
|
|
235
|
+
parsed.contextTokenBudget = parseNumberArg(next);
|
|
236
|
+
i += 1;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (arg === "--context-focus-max-bytes" && next) {
|
|
240
|
+
parsed.contextFocusMaxBytes = parseNumberArg(next);
|
|
241
|
+
i += 1;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (arg === "--context-periphery-max-bytes" && next) {
|
|
245
|
+
parsed.contextPeripheryMaxBytes = parseNumberArg(next);
|
|
246
|
+
i += 1;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (arg === "--context-include-repo-map" && next) {
|
|
250
|
+
parsed.contextIncludeRepoMap = parseBooleanArg(next);
|
|
251
|
+
i += 1;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (arg === "--context-include-impact" && next) {
|
|
255
|
+
parsed.contextIncludeImpact = parseBooleanArg(next);
|
|
256
|
+
i += 1;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (arg === "--context-include-snippets" && next) {
|
|
260
|
+
parsed.contextIncludeSnippets = parseBooleanArg(next);
|
|
261
|
+
i += 1;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (arg === "--context-read-strategy" && next) {
|
|
265
|
+
if (next === "docdex" || next === "fs")
|
|
266
|
+
parsed.contextReadStrategy = next;
|
|
267
|
+
i += 1;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (arg === "--context-max-refreshes" && next) {
|
|
271
|
+
parsed.contextMaxRefreshes = parseNumberArg(next);
|
|
272
|
+
i += 1;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (arg === "--context-skeletonize" && next) {
|
|
276
|
+
parsed.contextSkeletonize = parseBooleanArg(next);
|
|
277
|
+
i += 1;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (arg === "--context-redact-secrets" && next) {
|
|
281
|
+
parsed.contextRedactSecrets = parseBooleanArg(next);
|
|
282
|
+
i += 1;
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (arg === "--context-ignore-files-from" && next) {
|
|
286
|
+
parsed.contextIgnoreFilesFrom = parseListArg(next);
|
|
287
|
+
i += 1;
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (arg === "--security-redact-patterns" && next) {
|
|
291
|
+
parsed.securityRedactPatterns = parseListArg(next);
|
|
292
|
+
i += 1;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (arg === "--builder-mode" && next) {
|
|
296
|
+
if (next === "tool_calls" || next === "patch_json" || next === "freeform") {
|
|
297
|
+
parsed.builderMode = next;
|
|
298
|
+
}
|
|
299
|
+
i += 1;
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (arg === "--builder-patch-format" && next) {
|
|
303
|
+
if (next === "search_replace" || next === "file_writes")
|
|
304
|
+
parsed.builderPatchFormat = next;
|
|
305
|
+
i += 1;
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (arg === "--interpreter-provider" && next) {
|
|
309
|
+
parsed.interpreterProvider = next;
|
|
310
|
+
i += 1;
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (arg === "--interpreter-model" && next) {
|
|
314
|
+
parsed.interpreterModel = next;
|
|
315
|
+
i += 1;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (arg === "--interpreter-format" && next) {
|
|
319
|
+
parsed.interpreterFormat = next;
|
|
320
|
+
i += 1;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (arg === "--interpreter-grammar" && next) {
|
|
324
|
+
parsed.interpreterGrammar = next;
|
|
325
|
+
i += 1;
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (arg === "--interpreter-max-retries" && next) {
|
|
329
|
+
parsed.interpreterMaxRetries = parseNumberArg(next);
|
|
330
|
+
i += 1;
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (arg === "--interpreter-timeout-ms" && next) {
|
|
334
|
+
parsed.interpreterTimeoutMs = parseNumberArg(next);
|
|
335
|
+
i += 1;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (arg === "--streaming-flush-ms" && next) {
|
|
339
|
+
parsed.streamingFlushMs = parseNumberArg(next);
|
|
340
|
+
i += 1;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (arg === "--cost-max" && next) {
|
|
344
|
+
parsed.costMaxPerRun = parseNumberArg(next);
|
|
345
|
+
i += 1;
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (arg === "--cost-char-per-token" && next) {
|
|
349
|
+
parsed.costCharPerToken = parseNumberArg(next);
|
|
350
|
+
i += 1;
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (arg === "--cost-pricing-overrides" && next) {
|
|
354
|
+
parsed.costPricingOverrides = next;
|
|
355
|
+
i += 1;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (!arg.startsWith("-")) {
|
|
359
|
+
positionals.push(arg);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (positionals.length) {
|
|
363
|
+
parsed.inlineTask = positionals.join(" ");
|
|
364
|
+
}
|
|
365
|
+
return parsed;
|
|
366
|
+
};
|
|
367
|
+
const ROOT_MARKERS = [
|
|
368
|
+
".git",
|
|
369
|
+
"package.json",
|
|
370
|
+
"pnpm-workspace.yaml",
|
|
371
|
+
"yarn.lock",
|
|
372
|
+
"bun.lockb",
|
|
373
|
+
"go.mod",
|
|
374
|
+
"Cargo.toml",
|
|
375
|
+
"pyproject.toml",
|
|
376
|
+
"requirements.txt",
|
|
377
|
+
"Gemfile",
|
|
378
|
+
"composer.json",
|
|
379
|
+
"pom.xml",
|
|
380
|
+
"build.gradle",
|
|
381
|
+
"build.gradle.kts",
|
|
382
|
+
"codali.config.json",
|
|
383
|
+
".codalirc",
|
|
384
|
+
];
|
|
385
|
+
export const resolveWorkspaceRoot = (cwd, explicitRoot) => {
|
|
386
|
+
if (explicitRoot) {
|
|
387
|
+
return path.resolve(cwd, explicitRoot);
|
|
388
|
+
}
|
|
389
|
+
let current = path.resolve(cwd);
|
|
390
|
+
let previous = "";
|
|
391
|
+
while (current !== previous) {
|
|
392
|
+
for (const marker of ROOT_MARKERS) {
|
|
393
|
+
if (existsSync(path.join(current, marker))) {
|
|
394
|
+
return current;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
previous = current;
|
|
398
|
+
current = path.dirname(current);
|
|
399
|
+
}
|
|
400
|
+
return path.resolve(cwd);
|
|
401
|
+
};
|
|
402
|
+
const formatCost = (cost) => {
|
|
403
|
+
if (cost === undefined)
|
|
404
|
+
return "unknown";
|
|
405
|
+
return `$${cost.toFixed(4)}`;
|
|
406
|
+
};
|
|
407
|
+
const toSummaryPhase = (phase) => {
|
|
408
|
+
if (phase === "librarian" || phase === "plan")
|
|
409
|
+
return "plan";
|
|
410
|
+
if (phase === "architect" || phase === "retrieve")
|
|
411
|
+
return "retrieve";
|
|
412
|
+
if (phase === "builder" || phase === "act")
|
|
413
|
+
return "act";
|
|
414
|
+
if (phase === "critic" || phase === "verify" || phase === "architect_review")
|
|
415
|
+
return "verify";
|
|
416
|
+
return undefined;
|
|
417
|
+
};
|
|
418
|
+
const uniqueStrings = (values) => Array.from(new Set(values.map((value) => value.trim()).filter((value) => value.length > 0))).sort((left, right) => left.localeCompare(right));
|
|
419
|
+
const inferFailureClass = (stage, reasons, errorMessage) => {
|
|
420
|
+
const haystack = [stage ?? "", ...reasons, errorMessage ?? ""].join(" ").toLowerCase();
|
|
421
|
+
if (haystack.includes("verification"))
|
|
422
|
+
return "verification_failure";
|
|
423
|
+
if (haystack.includes("policy") || haystack.includes("guardrail"))
|
|
424
|
+
return "policy_failure";
|
|
425
|
+
if (haystack.includes("safety"))
|
|
426
|
+
return "safety_failure";
|
|
427
|
+
if (haystack.includes("patch"))
|
|
428
|
+
return "patch_failure";
|
|
429
|
+
if (haystack.includes("provider") || haystack.includes("rate limit") || haystack.includes("auth")) {
|
|
430
|
+
return "provider_failure";
|
|
431
|
+
}
|
|
432
|
+
if (haystack.includes("execution") || haystack.includes("runner") || haystack.includes("timeout")) {
|
|
433
|
+
return "execution_failure";
|
|
434
|
+
}
|
|
435
|
+
return "unknown_failure";
|
|
436
|
+
};
|
|
437
|
+
const summarizeContext = (context, fallbackContent) => {
|
|
438
|
+
const files = context?.files ?? [];
|
|
439
|
+
const focusCount = files.filter((file) => file.role === "focus").length;
|
|
440
|
+
const peripheryCount = files.filter((file) => file.role === "periphery").length;
|
|
441
|
+
const content = context?.serialized?.content ?? fallbackContent ?? "";
|
|
442
|
+
return {
|
|
443
|
+
focusCount,
|
|
444
|
+
peripheryCount,
|
|
445
|
+
content,
|
|
446
|
+
charCount: content.length,
|
|
447
|
+
};
|
|
448
|
+
};
|
|
449
|
+
const confirmOverage = async (message) => {
|
|
450
|
+
if (!process.stdin.isTTY) {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
454
|
+
try {
|
|
455
|
+
const answer = await rl.question(message);
|
|
456
|
+
const normalized = answer.trim().toLowerCase();
|
|
457
|
+
return normalized === "y" || normalized === "yes";
|
|
458
|
+
}
|
|
459
|
+
finally {
|
|
460
|
+
rl.close();
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
const createStreamState = (flushEveryMs, outputStream) => {
|
|
464
|
+
let buffer = "";
|
|
465
|
+
let lastFlush = Date.now();
|
|
466
|
+
let didStream = false;
|
|
467
|
+
const forceColor = process.env.FORCE_COLOR;
|
|
468
|
+
const supportsColor = (forceColor && forceColor !== "0") || (!!process.stderr.isTTY && !process.env.NO_COLOR);
|
|
469
|
+
const colorize = (code, text) => supportsColor ? `\u001b[${code}m${text}\u001b[0m` : text;
|
|
470
|
+
const indent = " ";
|
|
471
|
+
const statusColor = (phase) => {
|
|
472
|
+
if (phase === "executing")
|
|
473
|
+
return "35";
|
|
474
|
+
if (phase === "patching")
|
|
475
|
+
return "33";
|
|
476
|
+
if (phase === "done")
|
|
477
|
+
return "32";
|
|
478
|
+
return "36";
|
|
479
|
+
};
|
|
480
|
+
const tag = (phase) => colorize(statusColor(phase), `[${phase}]`);
|
|
481
|
+
const toolTag = (name) => colorize("34", `[tool:${name}]`);
|
|
482
|
+
const toolLead = () => colorize("34", "[tool]");
|
|
483
|
+
const okTag = () => colorize("32", "ok");
|
|
484
|
+
const errTag = () => colorize("31", "error");
|
|
485
|
+
const errorLine = (message) => colorize("31", message);
|
|
486
|
+
const flush = () => {
|
|
487
|
+
if (buffer.length > 0) {
|
|
488
|
+
process.stdout.write(buffer);
|
|
489
|
+
outputStream?.write(buffer);
|
|
490
|
+
buffer = "";
|
|
491
|
+
}
|
|
492
|
+
lastFlush = Date.now();
|
|
493
|
+
};
|
|
494
|
+
const writeStatus = (line) => {
|
|
495
|
+
flush();
|
|
496
|
+
process.stderr.write(`${line}\n`);
|
|
497
|
+
outputStream?.write(`${line.replace(/\u001b\[[0-9;]*m/g, "")}\n`);
|
|
498
|
+
};
|
|
499
|
+
const onEvent = (event) => {
|
|
500
|
+
if (event.type === "token") {
|
|
501
|
+
didStream = true;
|
|
502
|
+
buffer += event.content;
|
|
503
|
+
const now = Date.now();
|
|
504
|
+
if (now - lastFlush >= flushEveryMs) {
|
|
505
|
+
flush();
|
|
506
|
+
}
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (event.type === "status") {
|
|
510
|
+
const suffix = event.message ? ` ${event.message}` : "";
|
|
511
|
+
writeStatus(`${indent}${tag(event.phase)}${suffix}`);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
if (event.type === "tool_call") {
|
|
515
|
+
writeStatus(`${indent}${toolLead()} ${event.name}`);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
if (event.type === "tool_result") {
|
|
519
|
+
const outcome = event.ok === false ? errTag() : okTag();
|
|
520
|
+
const suffix = event.ok === false && event.errorCode
|
|
521
|
+
? ` ${colorize("31", `(${event.errorCode}${event.retryable ? ",retryable" : ""})`)}`
|
|
522
|
+
: "";
|
|
523
|
+
writeStatus(`${indent}${toolTag(event.name)} ${outcome}${suffix}`);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
if (event.type === "error") {
|
|
527
|
+
writeStatus(`${indent}${colorize("31", "[error]")} ${errorLine(event.message)}`);
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
const onToken = (token) => {
|
|
531
|
+
didStream = true;
|
|
532
|
+
buffer += token;
|
|
533
|
+
const now = Date.now();
|
|
534
|
+
if (now - lastFlush >= flushEveryMs) {
|
|
535
|
+
flush();
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
return {
|
|
539
|
+
onEvent,
|
|
540
|
+
onToken,
|
|
541
|
+
flush,
|
|
542
|
+
didStream: () => didStream,
|
|
543
|
+
writeOutput: (text) => {
|
|
544
|
+
outputStream?.write(text);
|
|
545
|
+
},
|
|
546
|
+
};
|
|
547
|
+
};
|
|
548
|
+
const createPatchValidator = (workspaceRoot, allowShell, shellAllowlist) => {
|
|
549
|
+
if (!allowShell)
|
|
550
|
+
return undefined;
|
|
551
|
+
const allowlist = new Set(shellAllowlist ?? []);
|
|
552
|
+
return async (filePath) => {
|
|
553
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
554
|
+
if (![".js", ".mjs", ".cjs"].includes(ext)) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (!allowlist.has("node")) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const result = spawnSync("node", ["--check", filePath], {
|
|
561
|
+
cwd: workspaceRoot,
|
|
562
|
+
encoding: "utf8",
|
|
563
|
+
});
|
|
564
|
+
if (result.error) {
|
|
565
|
+
throw result.error;
|
|
566
|
+
}
|
|
567
|
+
if (result.status !== 0) {
|
|
568
|
+
throw new Error(result.stderr?.toString() || "node --check failed");
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
};
|
|
572
|
+
const isPhaseProvider = (value) => value === "librarian" ||
|
|
573
|
+
value === "architect" ||
|
|
574
|
+
value === "builder" ||
|
|
575
|
+
value === "critic" ||
|
|
576
|
+
value === "interpreter";
|
|
577
|
+
const resolveInterpreterRoute = (config, phaseRoutes) => {
|
|
578
|
+
const requestedProvider = config.interpreter.provider?.trim() || "auto";
|
|
579
|
+
const requestedModel = config.interpreter.model?.trim() || "auto";
|
|
580
|
+
const fallbackRoute = phaseRoutes.interpreter ??
|
|
581
|
+
phaseRoutes.critic ??
|
|
582
|
+
phaseRoutes.architect ??
|
|
583
|
+
phaseRoutes.builder ??
|
|
584
|
+
phaseRoutes.librarian;
|
|
585
|
+
const routed = requestedProvider === "auto"
|
|
586
|
+
? fallbackRoute
|
|
587
|
+
: isPhaseProvider(requestedProvider)
|
|
588
|
+
? phaseRoutes[requestedProvider]
|
|
589
|
+
: undefined;
|
|
590
|
+
const provider = routed?.provider ?? requestedProvider;
|
|
591
|
+
const baseConfig = routed
|
|
592
|
+
? { ...routed.config }
|
|
593
|
+
: {
|
|
594
|
+
model: config.model,
|
|
595
|
+
apiKey: config.apiKey,
|
|
596
|
+
baseUrl: config.baseUrl,
|
|
597
|
+
timeoutMs: config.interpreter.timeoutMs,
|
|
598
|
+
};
|
|
599
|
+
if (!baseConfig.baseUrl) {
|
|
600
|
+
const sameProviderBaseUrl = Object.values(phaseRoutes)
|
|
601
|
+
.map((route) => (route.provider === provider ? route.config.baseUrl : undefined))
|
|
602
|
+
.find((value) => typeof value === "string" && value.length > 0);
|
|
603
|
+
baseConfig.baseUrl = sameProviderBaseUrl ?? config.baseUrl ?? baseConfig.baseUrl;
|
|
604
|
+
}
|
|
605
|
+
const resolvedModel = requestedModel !== "auto"
|
|
606
|
+
? requestedModel
|
|
607
|
+
: baseConfig.model ?? fallbackRoute?.config.model ?? config.model;
|
|
608
|
+
return {
|
|
609
|
+
provider,
|
|
610
|
+
config: {
|
|
611
|
+
...baseConfig,
|
|
612
|
+
model: resolvedModel,
|
|
613
|
+
timeoutMs: config.interpreter.timeoutMs ?? baseConfig.timeoutMs,
|
|
614
|
+
},
|
|
615
|
+
temperature: routed?.temperature,
|
|
616
|
+
};
|
|
617
|
+
};
|
|
618
|
+
export const isToolEnabled = (name, tools) => {
|
|
619
|
+
if (name === "run_shell" && !tools.allowShell) {
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
622
|
+
const enabled = tools.enabled?.length ? new Set(tools.enabled) : undefined;
|
|
623
|
+
if (!enabled)
|
|
624
|
+
return true;
|
|
625
|
+
return enabled.has(name);
|
|
626
|
+
};
|
|
627
|
+
const PATCH_JSON_STRUCTURED_CAPABILITIES = [
|
|
628
|
+
"strict_instruction_following",
|
|
629
|
+
"json_formatting",
|
|
630
|
+
"schema_adherence",
|
|
631
|
+
"structured_output",
|
|
632
|
+
];
|
|
633
|
+
const PATCH_JSON_CODE_CAPABILITIES = [
|
|
634
|
+
"code_write",
|
|
635
|
+
"iterative_coding",
|
|
636
|
+
"simple_refactor",
|
|
637
|
+
"minimal_diff_generation",
|
|
638
|
+
"migration_scripts",
|
|
639
|
+
"debugging",
|
|
640
|
+
"refactor_support",
|
|
641
|
+
];
|
|
642
|
+
const countCapabilityMatches = (capabilities, required) => required.filter((capability) => capabilities.includes(capability)).length;
|
|
643
|
+
export const assessPhaseFallbackSuitability = (phase, builderMode, selection) => {
|
|
644
|
+
if (phase !== "builder") {
|
|
645
|
+
return { ok: true, reason: "not_builder_phase", builderMode };
|
|
646
|
+
}
|
|
647
|
+
if (builderMode !== "patch_json") {
|
|
648
|
+
return { ok: true, reason: "builder_mode_not_patch_json", builderMode };
|
|
649
|
+
}
|
|
650
|
+
const capabilities = selection.capabilities ?? [];
|
|
651
|
+
const structuredHits = countCapabilityMatches(capabilities, PATCH_JSON_STRUCTURED_CAPABILITIES);
|
|
652
|
+
const codeHits = countCapabilityMatches(capabilities, PATCH_JSON_CODE_CAPABILITIES);
|
|
653
|
+
if (codeHits <= 0) {
|
|
654
|
+
return {
|
|
655
|
+
ok: false,
|
|
656
|
+
reason: "missing_patch_code_capability",
|
|
657
|
+
details: { codeHits },
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
if (structuredHits > 0) {
|
|
661
|
+
return {
|
|
662
|
+
ok: true,
|
|
663
|
+
reason: "capability_requirements_met",
|
|
664
|
+
details: { structuredHits, codeHits },
|
|
665
|
+
builderMode: "patch_json",
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
const hasToolRunner = capabilities.includes("tool_runner");
|
|
669
|
+
if (selection.supportsTools || hasToolRunner) {
|
|
670
|
+
return {
|
|
671
|
+
ok: true,
|
|
672
|
+
reason: "fallback_patch_json_without_structured_capability",
|
|
673
|
+
details: { structuredHits, codeHits, hasToolRunner: hasToolRunner ? 1 : 0 },
|
|
674
|
+
// Keep patch_json mode so fallback builders stay in the same patch contract path.
|
|
675
|
+
builderMode: "patch_json",
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
return {
|
|
679
|
+
ok: false,
|
|
680
|
+
reason: "missing_structured_output_capability",
|
|
681
|
+
details: { structuredHits, codeHits },
|
|
682
|
+
};
|
|
683
|
+
};
|
|
684
|
+
const readStdin = async () => {
|
|
685
|
+
const chunks = [];
|
|
686
|
+
return new Promise((resolve, reject) => {
|
|
687
|
+
process.stdin.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
688
|
+
process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
689
|
+
process.stdin.on("error", reject);
|
|
690
|
+
});
|
|
691
|
+
};
|
|
692
|
+
const resolveTaskInput = async (parsed) => {
|
|
693
|
+
if (parsed.taskFile) {
|
|
694
|
+
return readFile(parsed.taskFile, "utf8");
|
|
695
|
+
}
|
|
696
|
+
if (parsed.inlineTask) {
|
|
697
|
+
return parsed.inlineTask;
|
|
698
|
+
}
|
|
699
|
+
if (process.stdin.isTTY) {
|
|
700
|
+
return "";
|
|
701
|
+
}
|
|
702
|
+
return readStdin();
|
|
703
|
+
};
|
|
704
|
+
const isTrivialRequest = (request) => {
|
|
705
|
+
const normalized = request.trim().toLowerCase();
|
|
706
|
+
if (normalized.length === 0)
|
|
707
|
+
return false;
|
|
708
|
+
const shortEnough = normalized.length <= 120;
|
|
709
|
+
const signals = ["typo", "spelling", "format", "whitespace", "rename", "readme", "comment"];
|
|
710
|
+
return shortEnough && signals.some((signal) => normalized.includes(signal));
|
|
711
|
+
};
|
|
712
|
+
const buildWorkflowTaskInput = (request, profile) => {
|
|
713
|
+
if (!profile || profile.name === "run") {
|
|
714
|
+
return request;
|
|
715
|
+
}
|
|
716
|
+
const outputInstruction = profile.outputContract === "patch_summary"
|
|
717
|
+
? "Return a patch/change-focused summary with changed files and verification notes."
|
|
718
|
+
: profile.outputContract === "review_findings"
|
|
719
|
+
? "Return risk/findings-oriented review output. Do not propose write-oriented changes."
|
|
720
|
+
: profile.outputContract === "verification_summary"
|
|
721
|
+
? "Return verification-first output with test/validation summary."
|
|
722
|
+
: "Return explanation-first output. Do not propose write-oriented changes.";
|
|
723
|
+
const writePolicy = profile.allowWrites ? "writes_allowed" : "no_writes";
|
|
724
|
+
return [
|
|
725
|
+
`WORKFLOW PROFILE: ${profile.name}`,
|
|
726
|
+
`OUTPUT CONTRACT: ${profile.outputContract}`,
|
|
727
|
+
`VERIFICATION POLICY: ${profile.verificationPolicy}`,
|
|
728
|
+
`VERIFICATION MIN CHECKS: ${profile.verificationMinimumChecks}`,
|
|
729
|
+
`VERIFICATION HIGH-CONFIDENCE ENFORCEMENT: ${profile.verificationEnforceHighConfidence ? "strict" : "off"}`,
|
|
730
|
+
`WRITE POLICY: ${writePolicy}`,
|
|
731
|
+
outputInstruction,
|
|
732
|
+
"",
|
|
733
|
+
"USER TASK:",
|
|
734
|
+
request,
|
|
735
|
+
].join("\n");
|
|
736
|
+
};
|
|
737
|
+
const collectPhaseDurations = (artifacts) => {
|
|
738
|
+
const durations = {};
|
|
739
|
+
for (const artifact of artifacts) {
|
|
740
|
+
if (artifact.kind !== "summary")
|
|
741
|
+
continue;
|
|
742
|
+
const phase = toSummaryPhase(artifact.phase);
|
|
743
|
+
if (!phase)
|
|
744
|
+
continue;
|
|
745
|
+
if (typeof artifact.duration_ms !== "number" || !Number.isFinite(artifact.duration_ms))
|
|
746
|
+
continue;
|
|
747
|
+
durations[phase] = artifact.duration_ms;
|
|
748
|
+
}
|
|
749
|
+
return durations;
|
|
750
|
+
};
|
|
751
|
+
const buildArtifactReferences = (phaseArtifactEvents, requiredArtifacts) => {
|
|
752
|
+
const references = [];
|
|
753
|
+
for (const event of phaseArtifactEvents) {
|
|
754
|
+
if (!event.phase || !event.kind)
|
|
755
|
+
continue;
|
|
756
|
+
const phase = toSummaryPhase(event.phase) ?? event.phase;
|
|
757
|
+
references.push({
|
|
758
|
+
phase,
|
|
759
|
+
kind: event.kind,
|
|
760
|
+
path: event.path ?? null,
|
|
761
|
+
status: "present",
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
const seen = new Set(references.map((entry) => `${entry.phase}:${entry.kind}`));
|
|
765
|
+
const missing = [];
|
|
766
|
+
for (const required of requiredArtifacts) {
|
|
767
|
+
if (seen.has(required))
|
|
768
|
+
continue;
|
|
769
|
+
missing.push(required);
|
|
770
|
+
const [phase, kind] = required.split(":");
|
|
771
|
+
references.push({
|
|
772
|
+
phase: phase ?? "unknown",
|
|
773
|
+
kind: kind ?? "unknown",
|
|
774
|
+
status: "missing",
|
|
775
|
+
reason_code: "artifact_not_emitted",
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
references.sort((left, right) => `${left.phase}:${left.kind}:${left.status}`.localeCompare(`${right.phase}:${right.kind}:${right.status}`));
|
|
779
|
+
return { references, missing: uniqueStrings(missing) };
|
|
780
|
+
};
|
|
781
|
+
const buildQualityDimensions = (artifactReferences, disposition) => {
|
|
782
|
+
const hasPresent = (phase) => artifactReferences.some((entry) => entry.phase === phase && entry.status === "present");
|
|
783
|
+
const retrieval = hasPresent("retrieve")
|
|
784
|
+
? "available"
|
|
785
|
+
: disposition === "pass"
|
|
786
|
+
? "degraded"
|
|
787
|
+
: "missing";
|
|
788
|
+
const plan = hasPresent("plan")
|
|
789
|
+
? "available"
|
|
790
|
+
: disposition === "pass"
|
|
791
|
+
? "degraded"
|
|
792
|
+
: "missing";
|
|
793
|
+
const patch = hasPresent("act")
|
|
794
|
+
? "available"
|
|
795
|
+
: disposition === "pass"
|
|
796
|
+
? "degraded"
|
|
797
|
+
: "missing";
|
|
798
|
+
const verification = hasPresent("verify")
|
|
799
|
+
? "available"
|
|
800
|
+
: disposition === "pass"
|
|
801
|
+
? "degraded"
|
|
802
|
+
: "missing";
|
|
803
|
+
return {
|
|
804
|
+
plan,
|
|
805
|
+
retrieval,
|
|
806
|
+
patch,
|
|
807
|
+
verification,
|
|
808
|
+
final_disposition: "available",
|
|
809
|
+
};
|
|
810
|
+
};
|
|
811
|
+
const buildPhaseTelemetry = (params) => {
|
|
812
|
+
const phaseOrder = ["plan", "retrieve", "act", "verify"];
|
|
813
|
+
const usage = params.usage;
|
|
814
|
+
const usageTotals = usage
|
|
815
|
+
? {
|
|
816
|
+
input_tokens: usage.inputTokens,
|
|
817
|
+
output_tokens: usage.outputTokens,
|
|
818
|
+
total_tokens: usage.totalTokens
|
|
819
|
+
?? ((usage.inputTokens ?? 0) + (usage.outputTokens ?? 0) || undefined),
|
|
820
|
+
}
|
|
821
|
+
: undefined;
|
|
822
|
+
const costTelemetry = estimateUsageCostTelemetry(usage, params.pricingSpec);
|
|
823
|
+
return phaseOrder
|
|
824
|
+
.filter((phase) => {
|
|
825
|
+
const providerInfo = params.phaseProviders[phase];
|
|
826
|
+
return Boolean(providerInfo?.provider || providerInfo?.model || params.phaseDurations[phase] !== undefined);
|
|
827
|
+
})
|
|
828
|
+
.map((phase) => {
|
|
829
|
+
const providerInfo = params.phaseProviders[phase] ?? {};
|
|
830
|
+
if (phase === "act") {
|
|
831
|
+
return {
|
|
832
|
+
run_id: params.runId,
|
|
833
|
+
phase,
|
|
834
|
+
provider: providerInfo.provider,
|
|
835
|
+
model: providerInfo.model,
|
|
836
|
+
duration_ms: params.phaseDurations[phase],
|
|
837
|
+
usage: usageTotals,
|
|
838
|
+
cost: costTelemetry.costUsd !== undefined
|
|
839
|
+
? {
|
|
840
|
+
usd: costTelemetry.costUsd,
|
|
841
|
+
source: costTelemetry.source,
|
|
842
|
+
pricing_source: params.pricingSource,
|
|
843
|
+
}
|
|
844
|
+
: undefined,
|
|
845
|
+
missing_usage_reason: usageTotals === undefined
|
|
846
|
+
? "usage_missing"
|
|
847
|
+
: undefined,
|
|
848
|
+
missing_cost_reason: costTelemetry.costUsd === undefined
|
|
849
|
+
? costTelemetry.reasonCode ?? "cost_missing"
|
|
850
|
+
: undefined,
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
return {
|
|
854
|
+
run_id: params.runId,
|
|
855
|
+
phase,
|
|
856
|
+
provider: providerInfo.provider,
|
|
857
|
+
model: providerInfo.model,
|
|
858
|
+
duration_ms: params.phaseDurations[phase],
|
|
859
|
+
missing_usage_reason: "provider_usage_not_exposed",
|
|
860
|
+
missing_cost_reason: "usage_missing",
|
|
861
|
+
};
|
|
862
|
+
});
|
|
863
|
+
};
|
|
864
|
+
class StubProvider {
|
|
865
|
+
constructor() {
|
|
866
|
+
this.name = "stub";
|
|
867
|
+
}
|
|
868
|
+
async generate(request) {
|
|
869
|
+
const last = request.messages[request.messages.length - 1];
|
|
870
|
+
const promptText = request.messages.map((message) => message.content ?? "").join("\n");
|
|
871
|
+
const lowerPrompt = promptText.toLowerCase();
|
|
872
|
+
const isArchitectPrompt = /ROLE:\s*Technical Architect/i.test(promptText);
|
|
873
|
+
const isArchitectReviewPrompt = isArchitectPrompt
|
|
874
|
+
&& (/TASK:\s*Review the builder output/i.test(promptText)
|
|
875
|
+
|| (/OUTPUT FORMAT \(PLAIN TEXT\):/i.test(promptText) && /STATUS:\s*PASS\|RETRY/i.test(promptText)));
|
|
876
|
+
if (isArchitectReviewPrompt) {
|
|
877
|
+
return {
|
|
878
|
+
message: {
|
|
879
|
+
role: "assistant",
|
|
880
|
+
content: [
|
|
881
|
+
"STATUS: PASS",
|
|
882
|
+
"REASONS:",
|
|
883
|
+
"- Request intent and plan targets are covered.",
|
|
884
|
+
"FEEDBACK:",
|
|
885
|
+
].join("\n"),
|
|
886
|
+
},
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
const isArchitectPlanPrompt = isArchitectPrompt
|
|
890
|
+
&& (/TASK:\s*Produce an implementation plan/i.test(promptText)
|
|
891
|
+
|| /PREFERRED OUTPUT SHAPE \(PLAIN TEXT\)/i.test(promptText));
|
|
892
|
+
if (isArchitectPlanPrompt) {
|
|
893
|
+
return {
|
|
894
|
+
message: {
|
|
895
|
+
role: "assistant",
|
|
896
|
+
content: [
|
|
897
|
+
"WHAT IS REQUIRED:",
|
|
898
|
+
"- Apply the requested behavior update for the current task.",
|
|
899
|
+
"CURRENT CONTEXT:",
|
|
900
|
+
"- Existing implementation lives in src/index.ts.",
|
|
901
|
+
"FOLDER STRUCTURE:",
|
|
902
|
+
"- src/index.ts",
|
|
903
|
+
"FILES TO TOUCH:",
|
|
904
|
+
"- src/index.ts",
|
|
905
|
+
"IMPLEMENTATION PLAN:",
|
|
906
|
+
"- Update src/index.ts to implement the requested behavior change.",
|
|
907
|
+
"RISK: low scoped single-file update.",
|
|
908
|
+
"VERIFY:",
|
|
909
|
+
"- echo ok",
|
|
910
|
+
].join("\n"),
|
|
911
|
+
},
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
if (lowerPrompt.includes("\"patches\"") || lowerPrompt.includes("search_replace")) {
|
|
915
|
+
return {
|
|
916
|
+
message: {
|
|
917
|
+
role: "assistant",
|
|
918
|
+
content: JSON.stringify({
|
|
919
|
+
patches: [
|
|
920
|
+
{
|
|
921
|
+
action: "replace",
|
|
922
|
+
file: "src/index.ts",
|
|
923
|
+
search_block: "const value = 1;",
|
|
924
|
+
replace_block: "const value = 2;",
|
|
925
|
+
},
|
|
926
|
+
],
|
|
927
|
+
}),
|
|
928
|
+
},
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
if (lowerPrompt.includes("\"files\"") || lowerPrompt.includes("file_writes")) {
|
|
932
|
+
return {
|
|
933
|
+
message: {
|
|
934
|
+
role: "assistant",
|
|
935
|
+
content: JSON.stringify({
|
|
936
|
+
files: [{ path: "src/index.ts", content: "const value = 2;\n" }],
|
|
937
|
+
delete: [],
|
|
938
|
+
}),
|
|
939
|
+
},
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
if (request.responseFormat?.type === "json" || request.responseFormat?.type === "gbnf") {
|
|
943
|
+
if (promptText.includes("\"files\"")) {
|
|
944
|
+
return {
|
|
945
|
+
message: {
|
|
946
|
+
role: "assistant",
|
|
947
|
+
content: JSON.stringify({
|
|
948
|
+
files: [{ path: "src/index.ts", content: "const value = 2;\n" }],
|
|
949
|
+
delete: [],
|
|
950
|
+
}),
|
|
951
|
+
},
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
if (promptText.includes("\"patches\"")) {
|
|
955
|
+
return {
|
|
956
|
+
message: {
|
|
957
|
+
role: "assistant",
|
|
958
|
+
content: JSON.stringify({
|
|
959
|
+
patches: [
|
|
960
|
+
{
|
|
961
|
+
action: "replace",
|
|
962
|
+
file: "src/index.ts",
|
|
963
|
+
search_block: "const value = 1;",
|
|
964
|
+
replace_block: "const value = 2;",
|
|
965
|
+
},
|
|
966
|
+
],
|
|
967
|
+
}),
|
|
968
|
+
},
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
return {
|
|
972
|
+
message: {
|
|
973
|
+
role: "assistant",
|
|
974
|
+
content: JSON.stringify({
|
|
975
|
+
steps: ["1. Apply change"],
|
|
976
|
+
target_files: ["src/index.ts"],
|
|
977
|
+
risk_assessment: "low",
|
|
978
|
+
verification: ["Run unit tests: pnpm test --filter codali"],
|
|
979
|
+
}),
|
|
980
|
+
},
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
return { message: { role: "assistant", content: `stub:${last?.content ?? ""}` } };
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
const registerBuiltins = () => {
|
|
987
|
+
try {
|
|
988
|
+
registerProvider("openai-compatible", (config) => new OpenAiCompatibleProvider(config));
|
|
989
|
+
}
|
|
990
|
+
catch {
|
|
991
|
+
// ignore duplicate registrations
|
|
992
|
+
}
|
|
993
|
+
try {
|
|
994
|
+
registerProvider("ollama-remote", (config) => new OllamaRemoteProvider(config));
|
|
995
|
+
}
|
|
996
|
+
catch {
|
|
997
|
+
// ignore duplicate registrations
|
|
998
|
+
}
|
|
999
|
+
try {
|
|
1000
|
+
registerProvider("codex-cli", (config) => new CodexCliProvider(config));
|
|
1001
|
+
}
|
|
1002
|
+
catch {
|
|
1003
|
+
// ignore duplicate registrations
|
|
1004
|
+
}
|
|
1005
|
+
try {
|
|
1006
|
+
registerProvider("stub", () => new StubProvider());
|
|
1007
|
+
}
|
|
1008
|
+
catch {
|
|
1009
|
+
// ignore duplicate registrations
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
export class RunCommand {
|
|
1013
|
+
static async run(argv) {
|
|
1014
|
+
registerBuiltins();
|
|
1015
|
+
const parsed = parseArgs(argv);
|
|
1016
|
+
const resolvedWorkspaceRoot = resolveWorkspaceRoot(process.cwd(), parsed.workspaceRoot);
|
|
1017
|
+
const agentRef = parsed.agent ?? parsed.agentId ?? parsed.agentSlug;
|
|
1018
|
+
const resolvedAgent = agentRef
|
|
1019
|
+
? await resolveAgentConfig(agentRef, {
|
|
1020
|
+
provider: parsed.provider,
|
|
1021
|
+
model: parsed.model,
|
|
1022
|
+
baseUrl: parsed.baseUrl,
|
|
1023
|
+
apiKey: parsed.apiKey,
|
|
1024
|
+
})
|
|
1025
|
+
: undefined;
|
|
1026
|
+
const provider = parsed.provider ?? resolvedAgent?.provider;
|
|
1027
|
+
const model = parsed.model ?? resolvedAgent?.model;
|
|
1028
|
+
const apiKey = parsed.apiKey ?? resolvedAgent?.apiKey;
|
|
1029
|
+
const baseUrl = parsed.baseUrl ?? resolvedAgent?.baseUrl;
|
|
1030
|
+
const agentId = resolvedAgent?.agent.id ?? parsed.agentId;
|
|
1031
|
+
const agentSlug = resolvedAgent?.agent.slug ?? parsed.agentSlug;
|
|
1032
|
+
const cliConfig = {};
|
|
1033
|
+
if (parsed.workspaceRoot)
|
|
1034
|
+
cliConfig.workspaceRoot = parsed.workspaceRoot;
|
|
1035
|
+
if (parsed.project)
|
|
1036
|
+
cliConfig.project = parsed.project;
|
|
1037
|
+
if (parsed.command)
|
|
1038
|
+
cliConfig.command = parsed.command;
|
|
1039
|
+
if (parsed.commandRunId)
|
|
1040
|
+
cliConfig.commandRunId = parsed.commandRunId;
|
|
1041
|
+
if (parsed.jobId)
|
|
1042
|
+
cliConfig.jobId = parsed.jobId;
|
|
1043
|
+
if (parsed.runId)
|
|
1044
|
+
cliConfig.runId = parsed.runId;
|
|
1045
|
+
if (parsed.taskId)
|
|
1046
|
+
cliConfig.taskId = parsed.taskId;
|
|
1047
|
+
if (parsed.taskKey)
|
|
1048
|
+
cliConfig.taskKey = parsed.taskKey;
|
|
1049
|
+
if (agentId)
|
|
1050
|
+
cliConfig.agentId = agentId;
|
|
1051
|
+
if (agentSlug)
|
|
1052
|
+
cliConfig.agentSlug = agentSlug;
|
|
1053
|
+
if (parsed.smart !== undefined)
|
|
1054
|
+
cliConfig.smart = parsed.smart;
|
|
1055
|
+
if (parsed.deepInvestigationEnabled !== undefined) {
|
|
1056
|
+
cliConfig.deepInvestigation = { enabled: parsed.deepInvestigationEnabled };
|
|
1057
|
+
}
|
|
1058
|
+
if (parsed.planHint)
|
|
1059
|
+
cliConfig.planHint = parsed.planHint;
|
|
1060
|
+
if (provider)
|
|
1061
|
+
cliConfig.provider = provider;
|
|
1062
|
+
if (model)
|
|
1063
|
+
cliConfig.model = model;
|
|
1064
|
+
if (apiKey)
|
|
1065
|
+
cliConfig.apiKey = apiKey;
|
|
1066
|
+
if (baseUrl)
|
|
1067
|
+
cliConfig.baseUrl = baseUrl;
|
|
1068
|
+
if (parsed.workflowProfile) {
|
|
1069
|
+
cliConfig.workflow = { profile: parsed.workflowProfile };
|
|
1070
|
+
}
|
|
1071
|
+
const docdexOverrides = {};
|
|
1072
|
+
if (parsed.docdexBaseUrl)
|
|
1073
|
+
docdexOverrides.baseUrl = parsed.docdexBaseUrl;
|
|
1074
|
+
if (parsed.docdexRepoId)
|
|
1075
|
+
docdexOverrides.repoId = parsed.docdexRepoId;
|
|
1076
|
+
if (parsed.docdexRepoRoot)
|
|
1077
|
+
docdexOverrides.repoRoot = parsed.docdexRepoRoot;
|
|
1078
|
+
if (Object.keys(docdexOverrides).length) {
|
|
1079
|
+
cliConfig.docdex = docdexOverrides;
|
|
1080
|
+
}
|
|
1081
|
+
const contextOverrides = {};
|
|
1082
|
+
if (parsed.contextMode)
|
|
1083
|
+
contextOverrides.mode = parsed.contextMode;
|
|
1084
|
+
if (parsed.contextMaxFiles !== undefined)
|
|
1085
|
+
contextOverrides.maxFiles = parsed.contextMaxFiles;
|
|
1086
|
+
if (parsed.contextMaxTotalBytes !== undefined)
|
|
1087
|
+
contextOverrides.maxTotalBytes = parsed.contextMaxTotalBytes;
|
|
1088
|
+
if (parsed.contextTokenBudget !== undefined)
|
|
1089
|
+
contextOverrides.tokenBudget = parsed.contextTokenBudget;
|
|
1090
|
+
if (parsed.contextFocusMaxBytes !== undefined)
|
|
1091
|
+
contextOverrides.focusMaxFileBytes = parsed.contextFocusMaxBytes;
|
|
1092
|
+
if (parsed.contextPeripheryMaxBytes !== undefined)
|
|
1093
|
+
contextOverrides.peripheryMaxBytes = parsed.contextPeripheryMaxBytes;
|
|
1094
|
+
if (parsed.contextIncludeRepoMap !== undefined)
|
|
1095
|
+
contextOverrides.includeRepoMap = parsed.contextIncludeRepoMap;
|
|
1096
|
+
if (parsed.contextIncludeImpact !== undefined)
|
|
1097
|
+
contextOverrides.includeImpact = parsed.contextIncludeImpact;
|
|
1098
|
+
if (parsed.contextIncludeSnippets !== undefined)
|
|
1099
|
+
contextOverrides.includeSnippets = parsed.contextIncludeSnippets;
|
|
1100
|
+
if (parsed.contextReadStrategy)
|
|
1101
|
+
contextOverrides.readStrategy = parsed.contextReadStrategy;
|
|
1102
|
+
if (parsed.contextMaxRefreshes !== undefined)
|
|
1103
|
+
contextOverrides.maxContextRefreshes = parsed.contextMaxRefreshes;
|
|
1104
|
+
if (parsed.contextSkeletonize !== undefined)
|
|
1105
|
+
contextOverrides.skeletonizeLargeFiles = parsed.contextSkeletonize;
|
|
1106
|
+
if (parsed.contextRedactSecrets !== undefined)
|
|
1107
|
+
contextOverrides.redactSecrets = parsed.contextRedactSecrets;
|
|
1108
|
+
if (parsed.contextIgnoreFilesFrom)
|
|
1109
|
+
contextOverrides.ignoreFilesFrom = parsed.contextIgnoreFilesFrom;
|
|
1110
|
+
if (Object.keys(contextOverrides).length) {
|
|
1111
|
+
cliConfig.context = contextOverrides;
|
|
1112
|
+
}
|
|
1113
|
+
const securityOverrides = {};
|
|
1114
|
+
if (parsed.securityRedactPatterns)
|
|
1115
|
+
securityOverrides.redactPatterns = parsed.securityRedactPatterns;
|
|
1116
|
+
if (Object.keys(securityOverrides).length) {
|
|
1117
|
+
cliConfig.security = securityOverrides;
|
|
1118
|
+
}
|
|
1119
|
+
const builderOverrides = {};
|
|
1120
|
+
if (parsed.builderMode)
|
|
1121
|
+
builderOverrides.mode = parsed.builderMode;
|
|
1122
|
+
if (parsed.builderPatchFormat)
|
|
1123
|
+
builderOverrides.patchFormat = parsed.builderPatchFormat;
|
|
1124
|
+
if (Object.keys(builderOverrides).length) {
|
|
1125
|
+
cliConfig.builder = builderOverrides;
|
|
1126
|
+
}
|
|
1127
|
+
const interpreterOverrides = {};
|
|
1128
|
+
if (parsed.interpreterProvider)
|
|
1129
|
+
interpreterOverrides.provider = parsed.interpreterProvider;
|
|
1130
|
+
if (parsed.interpreterModel)
|
|
1131
|
+
interpreterOverrides.model = parsed.interpreterModel;
|
|
1132
|
+
if (parsed.interpreterFormat)
|
|
1133
|
+
interpreterOverrides.format = parsed.interpreterFormat;
|
|
1134
|
+
if (parsed.interpreterGrammar)
|
|
1135
|
+
interpreterOverrides.grammar = parsed.interpreterGrammar;
|
|
1136
|
+
if (parsed.interpreterMaxRetries !== undefined) {
|
|
1137
|
+
interpreterOverrides.maxRetries = parsed.interpreterMaxRetries;
|
|
1138
|
+
}
|
|
1139
|
+
if (parsed.interpreterTimeoutMs !== undefined) {
|
|
1140
|
+
interpreterOverrides.timeoutMs = parsed.interpreterTimeoutMs;
|
|
1141
|
+
}
|
|
1142
|
+
if (Object.keys(interpreterOverrides).length) {
|
|
1143
|
+
cliConfig.interpreter = interpreterOverrides;
|
|
1144
|
+
}
|
|
1145
|
+
const streamingOverrides = {};
|
|
1146
|
+
if (parsed.streamingFlushMs !== undefined)
|
|
1147
|
+
streamingOverrides.flushEveryMs = parsed.streamingFlushMs;
|
|
1148
|
+
if (Object.keys(streamingOverrides).length) {
|
|
1149
|
+
cliConfig.streaming = streamingOverrides;
|
|
1150
|
+
}
|
|
1151
|
+
const costOverrides = {};
|
|
1152
|
+
if (parsed.costMaxPerRun !== undefined)
|
|
1153
|
+
costOverrides.maxCostPerRun = parsed.costMaxPerRun;
|
|
1154
|
+
if (parsed.costCharPerToken !== undefined)
|
|
1155
|
+
costOverrides.charPerToken = parsed.costCharPerToken;
|
|
1156
|
+
if (parsed.costPricingOverrides) {
|
|
1157
|
+
try {
|
|
1158
|
+
costOverrides.pricingOverrides = JSON.parse(parsed.costPricingOverrides);
|
|
1159
|
+
}
|
|
1160
|
+
catch {
|
|
1161
|
+
// ignore malformed pricing overrides
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
if (Object.keys(costOverrides).length) {
|
|
1165
|
+
cliConfig.cost = costOverrides;
|
|
1166
|
+
}
|
|
1167
|
+
const routingOverrides = {};
|
|
1168
|
+
const setRoutingOverride = (phase, key, value) => {
|
|
1169
|
+
if (!value)
|
|
1170
|
+
return;
|
|
1171
|
+
routingOverrides[phase] = {
|
|
1172
|
+
...(routingOverrides[phase] ?? {}),
|
|
1173
|
+
[key]: value,
|
|
1174
|
+
};
|
|
1175
|
+
};
|
|
1176
|
+
setRoutingOverride("librarian", "agent", parsed.agentLibrarian);
|
|
1177
|
+
setRoutingOverride("architect", "agent", parsed.agentArchitect);
|
|
1178
|
+
setRoutingOverride("builder", "agent", parsed.agentBuilder);
|
|
1179
|
+
setRoutingOverride("critic", "agent", parsed.agentCritic);
|
|
1180
|
+
setRoutingOverride("interpreter", "agent", parsed.agentInterpreter);
|
|
1181
|
+
if (Object.keys(routingOverrides).length) {
|
|
1182
|
+
cliConfig.routing = routingOverrides;
|
|
1183
|
+
}
|
|
1184
|
+
const config = await loadConfig({
|
|
1185
|
+
cli: cliConfig,
|
|
1186
|
+
configPath: parsed.configPath,
|
|
1187
|
+
cwd: resolvedWorkspaceRoot,
|
|
1188
|
+
});
|
|
1189
|
+
if (config.deepInvestigation?.enabled && !config.smart) {
|
|
1190
|
+
throw new Error("Deep investigation requires --smart. Enable smart mode or disable deep investigation.");
|
|
1191
|
+
}
|
|
1192
|
+
if (!config.smart && resolvedAgent?.agent) {
|
|
1193
|
+
const { contextWindow, maxOutputTokens, supportsTools } = resolvedAgent.agent;
|
|
1194
|
+
if (contextWindow && model) {
|
|
1195
|
+
config.localContext.modelTokenLimits = {
|
|
1196
|
+
...config.localContext.modelTokenLimits,
|
|
1197
|
+
[model]: contextWindow,
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
if (maxOutputTokens && config.limits.maxTokens === undefined) {
|
|
1201
|
+
config.limits.maxTokens = maxOutputTokens;
|
|
1202
|
+
}
|
|
1203
|
+
if (supportsTools === false && config.builder.mode === "tool_calls") {
|
|
1204
|
+
config.builder.mode = "patch_json";
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
const taskInput = await resolveTaskInput(parsed);
|
|
1208
|
+
if (!taskInput.trim()) {
|
|
1209
|
+
throw new Error("Task input is empty; provide --task <file>, inline text, or pass text via stdin.");
|
|
1210
|
+
}
|
|
1211
|
+
const workflowProfile = config.resolvedWorkflowProfile;
|
|
1212
|
+
const writesAllowed = workflowProfile?.allowWrites !== false;
|
|
1213
|
+
const workflowTaskInput = buildWorkflowTaskInput(taskInput, workflowProfile);
|
|
1214
|
+
const runId = config.runId ?? randomUUID();
|
|
1215
|
+
const runContext = new RunContext(runId, config.workspaceRoot, {
|
|
1216
|
+
request: workflowTaskInput,
|
|
1217
|
+
command: config.command,
|
|
1218
|
+
workspaceRoot: config.workspaceRoot,
|
|
1219
|
+
smart: config.smart,
|
|
1220
|
+
provider: config.provider,
|
|
1221
|
+
model: config.model,
|
|
1222
|
+
builderMode: config.builder.mode,
|
|
1223
|
+
maxRetries: config.limits.maxRetries,
|
|
1224
|
+
maxContextRefreshes: config.context.maxContextRefreshes,
|
|
1225
|
+
maxSteps: config.limits.maxSteps,
|
|
1226
|
+
maxToolCalls: config.limits.maxToolCalls,
|
|
1227
|
+
maxTokens: config.limits.maxTokens,
|
|
1228
|
+
timeoutMs: config.limits.timeoutMs,
|
|
1229
|
+
});
|
|
1230
|
+
const storageRoot = getGlobalWorkspaceDir(config.workspaceRoot);
|
|
1231
|
+
const lock = new WorkspaceLock(storageRoot, runId);
|
|
1232
|
+
await lock.acquire();
|
|
1233
|
+
const logger = new RunLogger(storageRoot, config.logging.directory, runId);
|
|
1234
|
+
const outputLogPath = path.join(path.dirname(logger.logPath), `${runId}.output.log`);
|
|
1235
|
+
await mkdir(path.dirname(outputLogPath), { recursive: true });
|
|
1236
|
+
const outputStream = createWriteStream(outputLogPath, { flags: "a" });
|
|
1237
|
+
const unregisterSignals = lock.registerSignalHandlers({
|
|
1238
|
+
onSignal: async (signal) => {
|
|
1239
|
+
try {
|
|
1240
|
+
await logger.log("run_cancelled", {
|
|
1241
|
+
runId,
|
|
1242
|
+
signal,
|
|
1243
|
+
command: config.command,
|
|
1244
|
+
commandRunId: config.commandRunId,
|
|
1245
|
+
jobId: config.jobId,
|
|
1246
|
+
project: config.project,
|
|
1247
|
+
taskId: config.taskId,
|
|
1248
|
+
taskKey: config.taskKey,
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
catch {
|
|
1252
|
+
// ignore logging failures during shutdown
|
|
1253
|
+
}
|
|
1254
|
+
},
|
|
1255
|
+
});
|
|
1256
|
+
await logger.log("run_start", {
|
|
1257
|
+
runId,
|
|
1258
|
+
fingerprint: runContext.fingerprint ?? null,
|
|
1259
|
+
command: config.command,
|
|
1260
|
+
commandRunId: config.commandRunId,
|
|
1261
|
+
jobId: config.jobId,
|
|
1262
|
+
project: config.project,
|
|
1263
|
+
taskId: config.taskId,
|
|
1264
|
+
taskKey: config.taskKey,
|
|
1265
|
+
agentId: config.agentId,
|
|
1266
|
+
agentSlug: config.agentSlug,
|
|
1267
|
+
provider: config.provider,
|
|
1268
|
+
model: config.model,
|
|
1269
|
+
workflow: workflowProfile
|
|
1270
|
+
? {
|
|
1271
|
+
name: workflowProfile.name,
|
|
1272
|
+
source: workflowProfile.source,
|
|
1273
|
+
command: workflowProfile.command,
|
|
1274
|
+
outputContract: workflowProfile.outputContract,
|
|
1275
|
+
verificationPolicy: workflowProfile.verificationPolicy,
|
|
1276
|
+
verificationMinimumChecks: workflowProfile.verificationMinimumChecks,
|
|
1277
|
+
verificationEnforceHighConfidence: workflowProfile.verificationEnforceHighConfidence,
|
|
1278
|
+
allowWrites: workflowProfile.allowWrites,
|
|
1279
|
+
}
|
|
1280
|
+
: null,
|
|
1281
|
+
policy: {
|
|
1282
|
+
allowShell: config.tools.allowShell ?? false,
|
|
1283
|
+
allowDestructiveOperations: config.tools.allowDestructiveOperations ?? false,
|
|
1284
|
+
allowWrites: writesAllowed,
|
|
1285
|
+
},
|
|
1286
|
+
});
|
|
1287
|
+
let finalMessageContent = "";
|
|
1288
|
+
let usage;
|
|
1289
|
+
let toolCallsExecuted = 0;
|
|
1290
|
+
let pricingSpec;
|
|
1291
|
+
let pricingSource;
|
|
1292
|
+
let estimatedCost;
|
|
1293
|
+
let estimatedTokens;
|
|
1294
|
+
let estimatedChars;
|
|
1295
|
+
let estimatedFocus = 0;
|
|
1296
|
+
let estimatedPeriphery = 0;
|
|
1297
|
+
let smartRuntimeSummary;
|
|
1298
|
+
let summaryDisposition = "pass";
|
|
1299
|
+
let summaryFailureClass;
|
|
1300
|
+
let summaryReasonCodes = [];
|
|
1301
|
+
let summaryFailureStage;
|
|
1302
|
+
let summaryRetryable;
|
|
1303
|
+
let summaryVerification;
|
|
1304
|
+
const phaseProviders = {};
|
|
1305
|
+
let summaryEmitted = false;
|
|
1306
|
+
const emitRunSummary = async () => {
|
|
1307
|
+
const reader = new RunLogReader(config.workspaceRoot, config.logging.directory);
|
|
1308
|
+
const phaseArtifacts = await reader.getPhaseArtifacts(runId);
|
|
1309
|
+
const phaseArtifactQuery = await reader.queryEvents({
|
|
1310
|
+
filters: { run_id: runId, event_type: "phase_artifact" },
|
|
1311
|
+
limit: 2000,
|
|
1312
|
+
sort: "asc",
|
|
1313
|
+
});
|
|
1314
|
+
const phaseArtifactEvents = phaseArtifactQuery.events
|
|
1315
|
+
.map((event) => {
|
|
1316
|
+
const data = event.data;
|
|
1317
|
+
return {
|
|
1318
|
+
phase: typeof data.phase === "string" ? data.phase : undefined,
|
|
1319
|
+
kind: typeof data.kind === "string" ? data.kind : undefined,
|
|
1320
|
+
path: typeof data.path === "string" ? data.path : undefined,
|
|
1321
|
+
};
|
|
1322
|
+
});
|
|
1323
|
+
const requiredArtifacts = config.smart
|
|
1324
|
+
? [
|
|
1325
|
+
"retrieve:summary",
|
|
1326
|
+
"retrieve:retrieval_report",
|
|
1327
|
+
"plan:summary",
|
|
1328
|
+
"act:summary",
|
|
1329
|
+
"verify:plan",
|
|
1330
|
+
"verify:verification_report",
|
|
1331
|
+
]
|
|
1332
|
+
: [];
|
|
1333
|
+
const { references: artifactReferences, missing } = buildArtifactReferences(phaseArtifactEvents, requiredArtifacts);
|
|
1334
|
+
const phaseDurations = collectPhaseDurations(phaseArtifacts);
|
|
1335
|
+
const phaseTelemetry = buildPhaseTelemetry({
|
|
1336
|
+
runId,
|
|
1337
|
+
phaseProviders,
|
|
1338
|
+
phaseDurations,
|
|
1339
|
+
usage,
|
|
1340
|
+
pricingSpec,
|
|
1341
|
+
pricingSource,
|
|
1342
|
+
});
|
|
1343
|
+
for (const telemetry of phaseTelemetry) {
|
|
1344
|
+
await logger.logPhaseTelemetry(telemetry);
|
|
1345
|
+
}
|
|
1346
|
+
const actualCost = estimateCostFromUsage(usage, pricingSpec);
|
|
1347
|
+
const finalReasonCodes = uniqueStrings(summaryReasonCodes);
|
|
1348
|
+
const finalFailureClass = summaryDisposition === "pass"
|
|
1349
|
+
? undefined
|
|
1350
|
+
: summaryFailureClass
|
|
1351
|
+
?? inferFailureClass(summaryFailureStage, finalReasonCodes);
|
|
1352
|
+
const verificationSummary = summaryVerification
|
|
1353
|
+
? {
|
|
1354
|
+
outcome: summaryVerification.outcome,
|
|
1355
|
+
reasonCodes: summaryVerification.reason_codes,
|
|
1356
|
+
policy: summaryVerification.policy,
|
|
1357
|
+
totals: summaryVerification.totals,
|
|
1358
|
+
}
|
|
1359
|
+
: smartRuntimeSummary?.verification
|
|
1360
|
+
?? null;
|
|
1361
|
+
await logger.logRunSummary({
|
|
1362
|
+
run_id: runId,
|
|
1363
|
+
runId,
|
|
1364
|
+
fingerprint: runContext.fingerprint ?? null,
|
|
1365
|
+
toolCallsExecuted,
|
|
1366
|
+
touchedFiles: runContext.getTouchedFiles(),
|
|
1367
|
+
durationMs: Date.now() - runContext.startedAt,
|
|
1368
|
+
usage,
|
|
1369
|
+
costEstimate: {
|
|
1370
|
+
charCount: estimatedChars,
|
|
1371
|
+
estimatedTokens,
|
|
1372
|
+
estimatedCost,
|
|
1373
|
+
pricingSource,
|
|
1374
|
+
},
|
|
1375
|
+
actualCost,
|
|
1376
|
+
budget: {
|
|
1377
|
+
maxSteps: config.limits.maxSteps,
|
|
1378
|
+
maxToolCalls: config.limits.maxToolCalls,
|
|
1379
|
+
maxTokens: config.limits.maxTokens ?? null,
|
|
1380
|
+
timeoutMs: config.limits.timeoutMs ?? null,
|
|
1381
|
+
consumedDurationMs: Date.now() - runContext.startedAt,
|
|
1382
|
+
consumedToolCalls: toolCallsExecuted,
|
|
1383
|
+
},
|
|
1384
|
+
quality_dimensions: buildQualityDimensions(artifactReferences, summaryDisposition),
|
|
1385
|
+
final_disposition: {
|
|
1386
|
+
status: summaryDisposition,
|
|
1387
|
+
failure_class: finalFailureClass,
|
|
1388
|
+
reason_codes: finalReasonCodes,
|
|
1389
|
+
stage: summaryFailureStage,
|
|
1390
|
+
retryable: summaryRetryable ?? null,
|
|
1391
|
+
},
|
|
1392
|
+
artifact_references: artifactReferences,
|
|
1393
|
+
phase_telemetry: phaseTelemetry,
|
|
1394
|
+
missing_artifacts: missing,
|
|
1395
|
+
smartRuntime: smartRuntimeSummary ?? null,
|
|
1396
|
+
verification: verificationSummary,
|
|
1397
|
+
workflow: workflowProfile
|
|
1398
|
+
? {
|
|
1399
|
+
name: workflowProfile.name,
|
|
1400
|
+
source: workflowProfile.source,
|
|
1401
|
+
command: workflowProfile.command,
|
|
1402
|
+
outputContract: workflowProfile.outputContract,
|
|
1403
|
+
verificationPolicy: workflowProfile.verificationPolicy,
|
|
1404
|
+
verificationMinimumChecks: workflowProfile.verificationMinimumChecks,
|
|
1405
|
+
verificationEnforceHighConfidence: workflowProfile.verificationEnforceHighConfidence,
|
|
1406
|
+
allowWrites: workflowProfile.allowWrites,
|
|
1407
|
+
}
|
|
1408
|
+
: null,
|
|
1409
|
+
policy: {
|
|
1410
|
+
allowShell: config.tools.allowShell ?? false,
|
|
1411
|
+
allowDestructiveOperations: config.tools.allowDestructiveOperations ?? false,
|
|
1412
|
+
allowWrites: writesAllowed,
|
|
1413
|
+
},
|
|
1414
|
+
command: config.command,
|
|
1415
|
+
commandRunId: config.commandRunId,
|
|
1416
|
+
jobId: config.jobId,
|
|
1417
|
+
project: config.project,
|
|
1418
|
+
taskId: config.taskId,
|
|
1419
|
+
task_id: config.taskId,
|
|
1420
|
+
taskKey: config.taskKey,
|
|
1421
|
+
agentId: config.agentId,
|
|
1422
|
+
agentSlug: config.agentSlug,
|
|
1423
|
+
smart: config.smart ?? false,
|
|
1424
|
+
});
|
|
1425
|
+
};
|
|
1426
|
+
try {
|
|
1427
|
+
const registry = new ToolRegistry();
|
|
1428
|
+
const registerIfEnabled = (tool) => {
|
|
1429
|
+
if (!writesAllowed && tool.name === "write_file") {
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
if (isToolEnabled(tool.name, config.tools)) {
|
|
1433
|
+
registry.register(tool);
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
for (const tool of createFileTools())
|
|
1437
|
+
registerIfEnabled(tool);
|
|
1438
|
+
registerIfEnabled(createDiffTool());
|
|
1439
|
+
registerIfEnabled(createSearchTool());
|
|
1440
|
+
registerIfEnabled(createShellTool());
|
|
1441
|
+
const docdexClient = new DocdexClient({
|
|
1442
|
+
baseUrl: config.docdex.baseUrl,
|
|
1443
|
+
repoId: config.docdex.repoId,
|
|
1444
|
+
repoRoot: config.docdex.repoRoot ?? config.workspaceRoot,
|
|
1445
|
+
dagSessionId: runId,
|
|
1446
|
+
});
|
|
1447
|
+
for (const tool of createDocdexTools(docdexClient))
|
|
1448
|
+
registerIfEnabled(tool);
|
|
1449
|
+
const toolContext = {
|
|
1450
|
+
workspaceRoot: config.workspaceRoot,
|
|
1451
|
+
recordTouchedFile: (filePath) => runContext.recordTouchedFile(filePath),
|
|
1452
|
+
allowOutsideWorkspace: config.tools.allowOutsideWorkspace,
|
|
1453
|
+
allowShell: config.tools.allowShell,
|
|
1454
|
+
allowDestructiveOperations: config.tools.allowDestructiveOperations,
|
|
1455
|
+
shellAllowlist: config.tools.shellAllowlist,
|
|
1456
|
+
};
|
|
1457
|
+
const streamState = createStreamState(config.streaming.flushEveryMs, outputStream);
|
|
1458
|
+
if (config.smart) {
|
|
1459
|
+
const hasRoutingAgent = Object.values(config.routing ?? {}).some((phase) => Boolean(phase?.agent));
|
|
1460
|
+
const hasExplicitProvider = Boolean(parsed.provider || parsed.model || parsed.baseUrl || parsed.apiKey);
|
|
1461
|
+
const shouldForceAgentDefaults = !agentRef && !hasRoutingAgent && !hasExplicitProvider;
|
|
1462
|
+
const shouldSelectAgents = config.provider !== "stub";
|
|
1463
|
+
const emptySelection = (phase) => ({
|
|
1464
|
+
phase,
|
|
1465
|
+
capabilities: [],
|
|
1466
|
+
source: "none",
|
|
1467
|
+
});
|
|
1468
|
+
const phaseOverrides = {
|
|
1469
|
+
librarian: config.routing?.librarian?.agent,
|
|
1470
|
+
architect: config.routing?.architect?.agent,
|
|
1471
|
+
builder: config.routing?.builder?.agent,
|
|
1472
|
+
critic: config.routing?.critic?.agent,
|
|
1473
|
+
interpreter: config.routing?.interpreter?.agent,
|
|
1474
|
+
};
|
|
1475
|
+
const phaseExclusions = {};
|
|
1476
|
+
let phaseSelections = shouldSelectAgents
|
|
1477
|
+
? await selectPhaseAgents({
|
|
1478
|
+
overrides: phaseOverrides,
|
|
1479
|
+
builderMode: config.builder.mode,
|
|
1480
|
+
fallbackAgent: resolvedAgent,
|
|
1481
|
+
allowCloudModels: config.security.allowCloudModels,
|
|
1482
|
+
excludeAgentIds: phaseExclusions,
|
|
1483
|
+
})
|
|
1484
|
+
: {
|
|
1485
|
+
librarian: emptySelection("librarian"),
|
|
1486
|
+
architect: emptySelection("architect"),
|
|
1487
|
+
builder: emptySelection("builder"),
|
|
1488
|
+
critic: emptySelection("critic"),
|
|
1489
|
+
interpreter: emptySelection("interpreter"),
|
|
1490
|
+
};
|
|
1491
|
+
if (shouldSelectAgents) {
|
|
1492
|
+
const unresolvedPhases = Object.values(phaseSelections)
|
|
1493
|
+
.filter((selection) => !selection.resolved)
|
|
1494
|
+
.map((selection) => selection.phase);
|
|
1495
|
+
if (unresolvedPhases.length > 0) {
|
|
1496
|
+
throw new Error(`No eligible mcoda agents found for phase(s): ${unresolvedPhases.join(", ")}. ` +
|
|
1497
|
+
"Add/configure agents and verify with `mcoda agent list --json`.");
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
const fallbackResolved = phaseSelections.builder.resolved ??
|
|
1501
|
+
phaseSelections.architect.resolved ??
|
|
1502
|
+
phaseSelections.critic.resolved ??
|
|
1503
|
+
phaseSelections.librarian.resolved ??
|
|
1504
|
+
phaseSelections.interpreter.resolved ??
|
|
1505
|
+
resolvedAgent;
|
|
1506
|
+
if (shouldForceAgentDefaults) {
|
|
1507
|
+
if (!fallbackResolved) {
|
|
1508
|
+
throw new Error("No eligible agents found in the mcoda agent registry. Run `mcoda agent list` or supply --agent/--provider/--model.");
|
|
1509
|
+
}
|
|
1510
|
+
config.provider = fallbackResolved.provider;
|
|
1511
|
+
config.model = fallbackResolved.model;
|
|
1512
|
+
config.apiKey = fallbackResolved.apiKey;
|
|
1513
|
+
config.baseUrl = fallbackResolved.baseUrl;
|
|
1514
|
+
}
|
|
1515
|
+
else if (!config.provider || !config.model) {
|
|
1516
|
+
if (fallbackResolved) {
|
|
1517
|
+
config.provider = config.provider || fallbackResolved.provider;
|
|
1518
|
+
config.model = config.model || fallbackResolved.model;
|
|
1519
|
+
config.apiKey = config.apiKey ?? fallbackResolved.apiKey;
|
|
1520
|
+
config.baseUrl = config.baseUrl ?? fallbackResolved.baseUrl;
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
if (!config.provider || !config.model) {
|
|
1524
|
+
throw new Error("Missing provider/model after agent resolution. Provide --agent or configure routing defaults.");
|
|
1525
|
+
}
|
|
1526
|
+
const defaults = {
|
|
1527
|
+
provider: config.provider,
|
|
1528
|
+
config: {
|
|
1529
|
+
model: config.model,
|
|
1530
|
+
apiKey: config.apiKey,
|
|
1531
|
+
baseUrl: config.baseUrl,
|
|
1532
|
+
timeoutMs: config.limits.timeoutMs,
|
|
1533
|
+
},
|
|
1534
|
+
};
|
|
1535
|
+
const selectedPhaseModels = new Set(Object.values(phaseSelections)
|
|
1536
|
+
.map((selection) => selection.resolved?.model)
|
|
1537
|
+
.filter((value) => Boolean(value)));
|
|
1538
|
+
if (shouldSelectAgents &&
|
|
1539
|
+
config.localContext.summarize.model &&
|
|
1540
|
+
!selectedPhaseModels.has(config.localContext.summarize.model)) {
|
|
1541
|
+
throw new Error(`CODALI_LOCAL_CONTEXT_SUMMARIZE_MODEL (${config.localContext.summarize.model}) ` +
|
|
1542
|
+
"must match a model from selected mcoda agents.");
|
|
1543
|
+
}
|
|
1544
|
+
for (const selection of Object.values(phaseSelections)) {
|
|
1545
|
+
if (!selection.resolved)
|
|
1546
|
+
continue;
|
|
1547
|
+
const contextWindow = selection.resolved.agent.contextWindow;
|
|
1548
|
+
if (contextWindow) {
|
|
1549
|
+
config.localContext.modelTokenLimits = {
|
|
1550
|
+
...config.localContext.modelTokenLimits,
|
|
1551
|
+
[selection.resolved.model]: contextWindow,
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
const builderSelection = phaseSelections.builder;
|
|
1556
|
+
if (builderSelection.resolved?.agent.maxOutputTokens && config.limits.maxTokens === undefined) {
|
|
1557
|
+
config.limits.maxTokens = builderSelection.resolved.agent.maxOutputTokens;
|
|
1558
|
+
}
|
|
1559
|
+
if (builderSelection.resolved?.agent.supportsTools === false &&
|
|
1560
|
+
config.builder.mode === "tool_calls") {
|
|
1561
|
+
config.builder.mode = "patch_json";
|
|
1562
|
+
}
|
|
1563
|
+
for (const selection of Object.values(phaseSelections)) {
|
|
1564
|
+
if (!selection.resolved)
|
|
1565
|
+
continue;
|
|
1566
|
+
await logger.log("phase_agent_selected", {
|
|
1567
|
+
phase: selection.phase,
|
|
1568
|
+
agentId: selection.resolved.agent.id,
|
|
1569
|
+
agentSlug: selection.resolved.agent.slug,
|
|
1570
|
+
provider: selection.resolved.provider,
|
|
1571
|
+
model: selection.resolved.model,
|
|
1572
|
+
source: selection.source,
|
|
1573
|
+
score: selection.score,
|
|
1574
|
+
reason: selection.reason,
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
const buildDefaultsForPhase = (phase) => {
|
|
1578
|
+
const selected = phaseSelections[phase].resolved;
|
|
1579
|
+
return selected
|
|
1580
|
+
? {
|
|
1581
|
+
provider: selected.provider,
|
|
1582
|
+
config: {
|
|
1583
|
+
model: selected.model,
|
|
1584
|
+
apiKey: selected.apiKey,
|
|
1585
|
+
baseUrl: selected.baseUrl,
|
|
1586
|
+
timeoutMs: config.limits.timeoutMs,
|
|
1587
|
+
},
|
|
1588
|
+
}
|
|
1589
|
+
: defaults;
|
|
1590
|
+
};
|
|
1591
|
+
const librarianDefaults = buildDefaultsForPhase("librarian");
|
|
1592
|
+
const architectDefaults = buildDefaultsForPhase("architect");
|
|
1593
|
+
const builderDefaults = buildDefaultsForPhase("builder");
|
|
1594
|
+
const criticDefaults = buildDefaultsForPhase("critic");
|
|
1595
|
+
const interpreterDefaults = buildDefaultsForPhase("interpreter");
|
|
1596
|
+
const librarianRoute = buildRoutedProvider("librarian", librarianDefaults, config.routing, Boolean(phaseSelections.librarian.resolved));
|
|
1597
|
+
const architectRoute = buildRoutedProvider("architect", architectDefaults, config.routing, Boolean(phaseSelections.architect.resolved));
|
|
1598
|
+
const builderRoute = buildRoutedProvider("builder", builderDefaults, config.routing, Boolean(phaseSelections.builder.resolved));
|
|
1599
|
+
const criticRoute = buildRoutedProvider("critic", criticDefaults, config.routing, Boolean(phaseSelections.critic.resolved));
|
|
1600
|
+
const interpreterPhaseRoute = buildRoutedProvider("interpreter", interpreterDefaults, config.routing, Boolean(phaseSelections.interpreter.resolved));
|
|
1601
|
+
const phaseRoutes = {
|
|
1602
|
+
librarian: librarianRoute,
|
|
1603
|
+
architect: architectRoute,
|
|
1604
|
+
builder: builderRoute,
|
|
1605
|
+
critic: criticRoute,
|
|
1606
|
+
interpreter: interpreterPhaseRoute,
|
|
1607
|
+
};
|
|
1608
|
+
phaseProviders.retrieve = {
|
|
1609
|
+
provider: librarianRoute.provider,
|
|
1610
|
+
model: librarianRoute.config.model,
|
|
1611
|
+
};
|
|
1612
|
+
phaseProviders.plan = {
|
|
1613
|
+
provider: architectRoute.provider,
|
|
1614
|
+
model: architectRoute.config.model,
|
|
1615
|
+
};
|
|
1616
|
+
phaseProviders.act = {
|
|
1617
|
+
provider: builderRoute.provider,
|
|
1618
|
+
model: builderRoute.config.model,
|
|
1619
|
+
};
|
|
1620
|
+
phaseProviders.verify = {
|
|
1621
|
+
provider: criticRoute.provider,
|
|
1622
|
+
model: criticRoute.config.model,
|
|
1623
|
+
};
|
|
1624
|
+
const librarianProvider = createProvider(librarianRoute.provider, librarianRoute.config);
|
|
1625
|
+
const architectProvider = createProvider(architectRoute.provider, architectRoute.config);
|
|
1626
|
+
const builderProvider = createProvider(builderRoute.provider, builderRoute.config);
|
|
1627
|
+
const profileAgentId = config.agentId ??
|
|
1628
|
+
config.agentSlug ??
|
|
1629
|
+
phaseSelections.architect.resolved?.agent.id ??
|
|
1630
|
+
phaseSelections.critic.resolved?.agent.id ??
|
|
1631
|
+
phaseSelections.librarian.resolved?.agent.id ??
|
|
1632
|
+
phaseSelections.builder.resolved?.agent.id ??
|
|
1633
|
+
phaseSelections.interpreter.resolved?.agent.id ??
|
|
1634
|
+
"codali";
|
|
1635
|
+
const store = new ContextStore({
|
|
1636
|
+
workspaceRoot: storageRoot,
|
|
1637
|
+
storageDir: config.localContext.storageDir,
|
|
1638
|
+
});
|
|
1639
|
+
const redactor = config.context.redactSecrets
|
|
1640
|
+
? new ContextRedactor({
|
|
1641
|
+
workspaceRoot: config.workspaceRoot,
|
|
1642
|
+
ignoreFilesFrom: config.context.ignoreFilesFrom,
|
|
1643
|
+
redactPatterns: config.security.redactPatterns,
|
|
1644
|
+
})
|
|
1645
|
+
: undefined;
|
|
1646
|
+
if (redactor) {
|
|
1647
|
+
await redactor.loadIgnoreMatchers();
|
|
1648
|
+
}
|
|
1649
|
+
let summarizer;
|
|
1650
|
+
if (config.localContext.enabled && config.localContext.summarize.enabled) {
|
|
1651
|
+
const providerKey = config.localContext.summarize.provider;
|
|
1652
|
+
let summarizerProviderName = providerKey;
|
|
1653
|
+
let summarizerConfig = { ...defaults.config };
|
|
1654
|
+
let summarizerTemperature;
|
|
1655
|
+
if (isPhaseProvider(providerKey)) {
|
|
1656
|
+
const route = phaseRoutes[providerKey];
|
|
1657
|
+
summarizerProviderName = route.provider;
|
|
1658
|
+
summarizerConfig = { ...route.config };
|
|
1659
|
+
summarizerTemperature = route.temperature;
|
|
1660
|
+
}
|
|
1661
|
+
if (config.localContext.summarize.model) {
|
|
1662
|
+
summarizerConfig = {
|
|
1663
|
+
...summarizerConfig,
|
|
1664
|
+
model: config.localContext.summarize.model,
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
const summarizerProvider = createProvider(summarizerProviderName, summarizerConfig);
|
|
1668
|
+
summarizer = new ContextSummarizer(summarizerProvider, {
|
|
1669
|
+
maxTokens: config.localContext.summarize.targetTokens,
|
|
1670
|
+
temperature: summarizerTemperature,
|
|
1671
|
+
logger,
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1674
|
+
const contextManager = new ContextManager({
|
|
1675
|
+
config: config.localContext,
|
|
1676
|
+
store,
|
|
1677
|
+
redactor,
|
|
1678
|
+
summarizer,
|
|
1679
|
+
logger,
|
|
1680
|
+
charPerToken: config.cost.charPerToken,
|
|
1681
|
+
});
|
|
1682
|
+
const laneScope = {
|
|
1683
|
+
jobId: config.jobId ?? config.commandRunId,
|
|
1684
|
+
runId,
|
|
1685
|
+
taskId: config.taskId,
|
|
1686
|
+
taskKey: config.taskKey,
|
|
1687
|
+
};
|
|
1688
|
+
const deepMode = Boolean(config.deepInvestigation?.enabled);
|
|
1689
|
+
const contextAssembler = new ContextAssembler(docdexClient, {
|
|
1690
|
+
workspaceRoot: config.workspaceRoot,
|
|
1691
|
+
queryProvider: librarianProvider,
|
|
1692
|
+
queryTemperature: librarianRoute?.temperature,
|
|
1693
|
+
agentId: profileAgentId,
|
|
1694
|
+
maxFiles: config.context.maxFiles,
|
|
1695
|
+
maxTotalBytes: config.context.maxTotalBytes,
|
|
1696
|
+
tokenBudget: config.context.tokenBudget,
|
|
1697
|
+
includeRepoMap: config.context.includeRepoMap,
|
|
1698
|
+
includeImpact: config.context.includeImpact,
|
|
1699
|
+
includeSnippets: config.context.includeSnippets,
|
|
1700
|
+
readStrategy: config.context.readStrategy,
|
|
1701
|
+
focusMaxFileBytes: config.context.focusMaxFileBytes,
|
|
1702
|
+
peripheryMaxBytes: config.context.peripheryMaxBytes,
|
|
1703
|
+
skeletonizeLargeFiles: config.context.skeletonizeLargeFiles,
|
|
1704
|
+
serializationMode: config.context.mode,
|
|
1705
|
+
redactSecrets: config.context.redactSecrets,
|
|
1706
|
+
redactPatterns: config.security.redactPatterns,
|
|
1707
|
+
ignoreFilesFrom: config.context.ignoreFilesFrom,
|
|
1708
|
+
preferredFiles: config.context.preferredFiles,
|
|
1709
|
+
recentFiles: config.context.recentFiles,
|
|
1710
|
+
readOnlyPaths: config.security.readOnlyPaths,
|
|
1711
|
+
allowDocEdits: config.security.allowDocEdits,
|
|
1712
|
+
deepMode,
|
|
1713
|
+
contextManager,
|
|
1714
|
+
laneScope,
|
|
1715
|
+
onEvent: streamState.onEvent,
|
|
1716
|
+
logger,
|
|
1717
|
+
});
|
|
1718
|
+
const deepScanPreset = Boolean(config.deepInvestigation?.enabled && config.deepInvestigation?.deepScanPreset);
|
|
1719
|
+
if (deepScanPreset) {
|
|
1720
|
+
contextAssembler.applyDeepScanPreset();
|
|
1721
|
+
}
|
|
1722
|
+
const preflightContext = await contextAssembler.assemble(workflowTaskInput);
|
|
1723
|
+
const pricingResolution = resolvePricing(config.cost.pricingOverrides, builderRoute.provider, builderRoute.config.model);
|
|
1724
|
+
pricingSpec = pricingResolution.pricing;
|
|
1725
|
+
pricingSource = pricingResolution.source;
|
|
1726
|
+
const summary = summarizeContext(preflightContext, workflowTaskInput);
|
|
1727
|
+
estimatedFocus = summary.focusCount;
|
|
1728
|
+
estimatedPeriphery = summary.peripheryCount;
|
|
1729
|
+
const estimate = estimateCostFromChars(summary.charCount, config.cost.charPerToken, pricingSpec, pricingSource);
|
|
1730
|
+
estimatedCost = estimate.estimatedCost;
|
|
1731
|
+
estimatedTokens = estimate.estimatedTotalTokens;
|
|
1732
|
+
estimatedChars = estimate.charCount;
|
|
1733
|
+
await logger.log("cost_estimate", {
|
|
1734
|
+
provider: builderRoute.provider,
|
|
1735
|
+
model: builderRoute.config.model,
|
|
1736
|
+
focusCount: estimatedFocus,
|
|
1737
|
+
peripheryCount: estimatedPeriphery,
|
|
1738
|
+
charCount: estimatedChars,
|
|
1739
|
+
estimatedTokens,
|
|
1740
|
+
estimatedCost,
|
|
1741
|
+
pricingSource,
|
|
1742
|
+
});
|
|
1743
|
+
process.stderr.write(`[codali] Preflight: focus=${estimatedFocus} periphery=${estimatedPeriphery} ` +
|
|
1744
|
+
`chars=${estimatedChars} tokens~${estimatedTokens} est_cost=${formatCost(estimatedCost)}\n`);
|
|
1745
|
+
if (estimatedCost !== undefined && estimatedCost > config.cost.maxCostPerRun) {
|
|
1746
|
+
const proceed = await confirmOverage(`Estimated cost ${formatCost(estimatedCost)} exceeds max ${formatCost(config.cost.maxCostPerRun)}. Continue? [y/N] `);
|
|
1747
|
+
if (!proceed) {
|
|
1748
|
+
throw new Error("Run cancelled due to cost limit");
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
const architectPlanner = new ArchitectPlanner(architectProvider, {
|
|
1752
|
+
temperature: architectRoute.temperature,
|
|
1753
|
+
logger,
|
|
1754
|
+
model: architectRoute.config.model,
|
|
1755
|
+
// Architect is intentionally plain-text first; avoid hard response-format constraints.
|
|
1756
|
+
responseFormat: undefined,
|
|
1757
|
+
planHint: config.planHint,
|
|
1758
|
+
stream: config.streaming.enabled,
|
|
1759
|
+
onEvent: streamState.onEvent,
|
|
1760
|
+
});
|
|
1761
|
+
const builderResponseFormat = builderRoute.responseFormat ?? (config.builder.mode === "patch_json" ? { type: "json" } : undefined);
|
|
1762
|
+
const patchValidator = createPatchValidator(config.workspaceRoot, config.tools.allowShell ?? false, config.tools.shellAllowlist ?? []);
|
|
1763
|
+
const needsPatchApplier = config.builder.mode === "patch_json" || config.builder.mode === "freeform" || config.builder.mode === "tool_calls";
|
|
1764
|
+
const patchApplier = needsPatchApplier
|
|
1765
|
+
? new PatchApplier({
|
|
1766
|
+
workspaceRoot: config.workspaceRoot,
|
|
1767
|
+
validateFile: patchValidator,
|
|
1768
|
+
policy: {
|
|
1769
|
+
allowDestructiveOperations: config.tools.allowDestructiveOperations ?? false,
|
|
1770
|
+
allowWrites: writesAllowed,
|
|
1771
|
+
},
|
|
1772
|
+
})
|
|
1773
|
+
: undefined;
|
|
1774
|
+
const wantsInterpreter = config.builder.mode === "freeform" || config.builder.fallbackToInterpreter === true;
|
|
1775
|
+
const interpreterRoute = wantsInterpreter
|
|
1776
|
+
? resolveInterpreterRoute(config, phaseRoutes)
|
|
1777
|
+
: undefined;
|
|
1778
|
+
const interpreterProvider = interpreterRoute
|
|
1779
|
+
? createProvider(interpreterRoute.provider, {
|
|
1780
|
+
...interpreterRoute.config,
|
|
1781
|
+
timeoutMs: config.interpreter.timeoutMs ??
|
|
1782
|
+
interpreterRoute.config.timeoutMs ??
|
|
1783
|
+
config.limits.timeoutMs,
|
|
1784
|
+
})
|
|
1785
|
+
: undefined;
|
|
1786
|
+
const interpreterResponseFormat = config.interpreter.format
|
|
1787
|
+
? {
|
|
1788
|
+
type: config.interpreter.format,
|
|
1789
|
+
grammar: config.interpreter.grammar,
|
|
1790
|
+
}
|
|
1791
|
+
: { type: "json" };
|
|
1792
|
+
const patchInterpreter = interpreterProvider && wantsInterpreter
|
|
1793
|
+
? new PatchInterpreter({
|
|
1794
|
+
provider: interpreterProvider,
|
|
1795
|
+
patchFormat: config.builder.patchFormat,
|
|
1796
|
+
responseFormat: interpreterResponseFormat,
|
|
1797
|
+
maxRetries: config.interpreter.maxRetries,
|
|
1798
|
+
timeoutMs: config.interpreter.timeoutMs,
|
|
1799
|
+
logger,
|
|
1800
|
+
model: interpreterRoute?.config.model,
|
|
1801
|
+
temperature: interpreterRoute?.temperature,
|
|
1802
|
+
})
|
|
1803
|
+
: undefined;
|
|
1804
|
+
const builderRunner = new BuilderRunner({
|
|
1805
|
+
provider: builderProvider,
|
|
1806
|
+
tools: registry,
|
|
1807
|
+
context: toolContext,
|
|
1808
|
+
maxSteps: config.limits.maxSteps,
|
|
1809
|
+
maxToolCalls: config.limits.maxToolCalls,
|
|
1810
|
+
maxTokens: config.limits.maxTokens,
|
|
1811
|
+
timeoutMs: config.limits.timeoutMs,
|
|
1812
|
+
temperature: builderRoute.temperature,
|
|
1813
|
+
responseFormat: builderResponseFormat,
|
|
1814
|
+
mode: config.builder.mode,
|
|
1815
|
+
patchFormat: config.builder.patchFormat,
|
|
1816
|
+
patchApplier,
|
|
1817
|
+
interpreter: patchInterpreter,
|
|
1818
|
+
fallbackToInterpreter: config.builder.fallbackToInterpreter ?? false,
|
|
1819
|
+
allowDestructiveOperations: config.tools.allowDestructiveOperations ?? false,
|
|
1820
|
+
stream: config.streaming.enabled,
|
|
1821
|
+
onEvent: streamState.onEvent,
|
|
1822
|
+
onToken: streamState.onToken,
|
|
1823
|
+
streamFlushMs: config.streaming.flushEveryMs,
|
|
1824
|
+
logger,
|
|
1825
|
+
model: builderRoute.config.model,
|
|
1826
|
+
});
|
|
1827
|
+
const validator = new ValidationRunner({
|
|
1828
|
+
allowShell: config.tools.allowShell ?? false,
|
|
1829
|
+
shellAllowlist: config.tools.shellAllowlist ?? [],
|
|
1830
|
+
workspaceRoot: config.workspaceRoot,
|
|
1831
|
+
docdexClient,
|
|
1832
|
+
defaultPolicyName: workflowProfile?.verificationPolicy,
|
|
1833
|
+
defaultMinimumChecks: workflowProfile?.verificationMinimumChecks,
|
|
1834
|
+
defaultEnforceHighConfidence: workflowProfile?.verificationEnforceHighConfidence,
|
|
1835
|
+
});
|
|
1836
|
+
const criticEvaluator = new CriticEvaluator(validator, {
|
|
1837
|
+
model: criticRoute.config.model,
|
|
1838
|
+
logger,
|
|
1839
|
+
});
|
|
1840
|
+
const memoryWriteback = new MemoryWriteback(docdexClient, {
|
|
1841
|
+
agentId: profileAgentId,
|
|
1842
|
+
workspaceRoot: config.workspaceRoot,
|
|
1843
|
+
learning: config.learning,
|
|
1844
|
+
logger,
|
|
1845
|
+
});
|
|
1846
|
+
const phaseFallbackCounts = {};
|
|
1847
|
+
const maxPhaseFallbacks = 3;
|
|
1848
|
+
const recoverPhaseProvider = async (input) => {
|
|
1849
|
+
if (!shouldSelectAgents)
|
|
1850
|
+
return { switched: false };
|
|
1851
|
+
const phase = input.phase;
|
|
1852
|
+
const currentSelection = phaseSelections[phase];
|
|
1853
|
+
const currentResolved = currentSelection.resolved;
|
|
1854
|
+
if (!currentResolved)
|
|
1855
|
+
return { switched: false };
|
|
1856
|
+
const currentAgentId = currentResolved.agent.id;
|
|
1857
|
+
if (!currentAgentId)
|
|
1858
|
+
return { switched: false };
|
|
1859
|
+
const fallbackCount = phaseFallbackCounts[phase] ?? 0;
|
|
1860
|
+
if (fallbackCount >= maxPhaseFallbacks) {
|
|
1861
|
+
await logger.log("phase_agent_fallback_skipped", {
|
|
1862
|
+
phase,
|
|
1863
|
+
reason: "fallback_limit_reached",
|
|
1864
|
+
limit: maxPhaseFallbacks,
|
|
1865
|
+
error: input.error.message,
|
|
1866
|
+
});
|
|
1867
|
+
return { switched: false };
|
|
1868
|
+
}
|
|
1869
|
+
const excluded = Array.from(new Set([...(phaseExclusions[phase] ?? []), currentAgentId]));
|
|
1870
|
+
phaseExclusions[phase] = excluded;
|
|
1871
|
+
let currentExcluded = excluded;
|
|
1872
|
+
const evaluatedAgentIds = new Set();
|
|
1873
|
+
let reselection;
|
|
1874
|
+
let nextSelection;
|
|
1875
|
+
let nextResolved = undefined;
|
|
1876
|
+
let selectedBuilderMode = config.builder.mode;
|
|
1877
|
+
while (true) {
|
|
1878
|
+
reselection = await selectPhaseAgents({
|
|
1879
|
+
overrides: phaseOverrides,
|
|
1880
|
+
builderMode: config.builder.mode,
|
|
1881
|
+
fallbackAgent: resolvedAgent,
|
|
1882
|
+
allowCloudModels: config.security.allowCloudModels,
|
|
1883
|
+
excludeAgentIds: {
|
|
1884
|
+
...phaseExclusions,
|
|
1885
|
+
[phase]: currentExcluded,
|
|
1886
|
+
},
|
|
1887
|
+
});
|
|
1888
|
+
nextSelection = reselection[phase];
|
|
1889
|
+
nextResolved = nextSelection.resolved;
|
|
1890
|
+
if (!nextResolved || nextResolved.agent.id === currentAgentId) {
|
|
1891
|
+
await logger.log("phase_agent_fallback_skipped", {
|
|
1892
|
+
phase,
|
|
1893
|
+
reason: "no_alternate_agent",
|
|
1894
|
+
currentAgentId,
|
|
1895
|
+
excluded: currentExcluded,
|
|
1896
|
+
error: input.error.message,
|
|
1897
|
+
});
|
|
1898
|
+
return { switched: false };
|
|
1899
|
+
}
|
|
1900
|
+
const nextAgentId = nextResolved.agent.id;
|
|
1901
|
+
if (evaluatedAgentIds.has(nextAgentId)) {
|
|
1902
|
+
await logger.log("phase_agent_fallback_skipped", {
|
|
1903
|
+
phase,
|
|
1904
|
+
reason: "no_eligible_alternate_agent",
|
|
1905
|
+
currentAgentId,
|
|
1906
|
+
excluded: currentExcluded,
|
|
1907
|
+
error: input.error.message,
|
|
1908
|
+
});
|
|
1909
|
+
return { switched: false };
|
|
1910
|
+
}
|
|
1911
|
+
evaluatedAgentIds.add(nextAgentId);
|
|
1912
|
+
const suitability = assessPhaseFallbackSuitability(phase, config.builder.mode, {
|
|
1913
|
+
capabilities: nextSelection.capabilities,
|
|
1914
|
+
supportsTools: nextResolved.agent.supportsTools ?? false,
|
|
1915
|
+
});
|
|
1916
|
+
if (suitability.ok) {
|
|
1917
|
+
selectedBuilderMode = suitability.builderMode ?? config.builder.mode;
|
|
1918
|
+
break;
|
|
1919
|
+
}
|
|
1920
|
+
currentExcluded = Array.from(new Set([...currentExcluded, nextAgentId]));
|
|
1921
|
+
phaseExclusions[phase] = currentExcluded;
|
|
1922
|
+
await logger.log("phase_agent_fallback_rejected", {
|
|
1923
|
+
phase,
|
|
1924
|
+
reason: suitability.reason,
|
|
1925
|
+
agentId: nextAgentId,
|
|
1926
|
+
agentSlug: nextResolved.agent.slug,
|
|
1927
|
+
currentAgentId,
|
|
1928
|
+
excluded: currentExcluded,
|
|
1929
|
+
details: suitability.details ?? null,
|
|
1930
|
+
error: input.error.message,
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
phaseExclusions[phase] = currentExcluded;
|
|
1934
|
+
if (!reselection || !nextSelection || !nextResolved) {
|
|
1935
|
+
await logger.log("phase_agent_fallback_skipped", {
|
|
1936
|
+
phase,
|
|
1937
|
+
reason: "reselection_failed",
|
|
1938
|
+
currentAgentId,
|
|
1939
|
+
excluded: currentExcluded,
|
|
1940
|
+
error: input.error.message,
|
|
1941
|
+
});
|
|
1942
|
+
return { switched: false };
|
|
1943
|
+
}
|
|
1944
|
+
phaseSelections = reselection;
|
|
1945
|
+
phaseFallbackCounts[phase] = fallbackCount + 1;
|
|
1946
|
+
const phaseDefaults = {
|
|
1947
|
+
provider: nextResolved.provider,
|
|
1948
|
+
config: {
|
|
1949
|
+
model: nextResolved.model,
|
|
1950
|
+
apiKey: nextResolved.apiKey,
|
|
1951
|
+
baseUrl: nextResolved.baseUrl,
|
|
1952
|
+
timeoutMs: config.limits.timeoutMs,
|
|
1953
|
+
},
|
|
1954
|
+
};
|
|
1955
|
+
const nextRoute = buildRoutedProvider(phase, phaseDefaults, config.routing, true);
|
|
1956
|
+
const mappedPhase = toSummaryPhase(phase);
|
|
1957
|
+
if (mappedPhase) {
|
|
1958
|
+
phaseProviders[mappedPhase] = {
|
|
1959
|
+
provider: nextRoute.provider,
|
|
1960
|
+
model: nextRoute.config.model,
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
if (phase === "builder") {
|
|
1964
|
+
const priorBuilderMode = config.builder.mode;
|
|
1965
|
+
if (selectedBuilderMode !== priorBuilderMode) {
|
|
1966
|
+
config.builder.mode = selectedBuilderMode;
|
|
1967
|
+
await logger.log("phase_agent_fallback_mode_change", {
|
|
1968
|
+
phase,
|
|
1969
|
+
fromMode: priorBuilderMode,
|
|
1970
|
+
toMode: selectedBuilderMode,
|
|
1971
|
+
reason: "fallback_suitability",
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
const nextProvider = createProvider(nextRoute.provider, nextRoute.config);
|
|
1975
|
+
const nextResponseFormat = nextRoute.responseFormat
|
|
1976
|
+
?? (config.builder.mode === "patch_json" ? { type: "json" } : undefined);
|
|
1977
|
+
builderRunner.setProvider(nextProvider, {
|
|
1978
|
+
model: nextRoute.config.model,
|
|
1979
|
+
temperature: nextRoute.temperature,
|
|
1980
|
+
responseFormat: nextResponseFormat,
|
|
1981
|
+
mode: config.builder.mode,
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
await logger.log("phase_agent_selected", {
|
|
1985
|
+
phase,
|
|
1986
|
+
agentId: nextResolved.agent.id,
|
|
1987
|
+
agentSlug: nextResolved.agent.slug,
|
|
1988
|
+
provider: nextResolved.provider,
|
|
1989
|
+
model: nextResolved.model,
|
|
1990
|
+
source: "fallback",
|
|
1991
|
+
reason: `provider_failure_recovery:${input.error.message}`,
|
|
1992
|
+
});
|
|
1993
|
+
await logger.log("phase_agent_fallback", {
|
|
1994
|
+
phase,
|
|
1995
|
+
fromAgentId: currentAgentId,
|
|
1996
|
+
toAgentId: nextResolved.agent.id,
|
|
1997
|
+
attempt: input.attempt,
|
|
1998
|
+
fallback_count: phaseFallbackCounts[phase],
|
|
1999
|
+
error: input.error.message,
|
|
2000
|
+
});
|
|
2001
|
+
return {
|
|
2002
|
+
switched: true,
|
|
2003
|
+
note: `Provider failure (${input.error.message}). ` +
|
|
2004
|
+
`Switched ${phase} agent to ${nextResolved.agent.slug ?? nextResolved.agent.id}; continue with current plan.`,
|
|
2005
|
+
};
|
|
2006
|
+
};
|
|
2007
|
+
const pipeline = new SmartPipeline({
|
|
2008
|
+
contextAssembler,
|
|
2009
|
+
initialContext: preflightContext,
|
|
2010
|
+
architectPlanner,
|
|
2011
|
+
builderRunner,
|
|
2012
|
+
criticEvaluator,
|
|
2013
|
+
memoryWriteback,
|
|
2014
|
+
maxRetries: config.limits.maxRetries,
|
|
2015
|
+
maxContextRefreshes: config.context.maxContextRefreshes,
|
|
2016
|
+
fastPath: undefined,
|
|
2017
|
+
deepMode,
|
|
2018
|
+
deepScanPreset,
|
|
2019
|
+
deepInvestigation: config.deepInvestigation,
|
|
2020
|
+
getTouchedFiles: () => runContext.getTouchedFiles(),
|
|
2021
|
+
logger,
|
|
2022
|
+
contextManager,
|
|
2023
|
+
laneScope,
|
|
2024
|
+
verificationPolicyName: workflowProfile?.verificationPolicy,
|
|
2025
|
+
minimumVerificationChecks: workflowProfile?.verificationMinimumChecks,
|
|
2026
|
+
enforceVerificationHighConfidence: workflowProfile?.verificationEnforceHighConfidence,
|
|
2027
|
+
onEvent: streamState.onEvent,
|
|
2028
|
+
onPhaseProviderFailure: recoverPhaseProvider,
|
|
2029
|
+
});
|
|
2030
|
+
const result = await pipeline.run(workflowTaskInput);
|
|
2031
|
+
for (const file of result.builderResult.touchedFiles ?? []) {
|
|
2032
|
+
runContext.recordTouchedFile(file);
|
|
2033
|
+
}
|
|
2034
|
+
smartRuntimeSummary = {
|
|
2035
|
+
attempts: result.attempts,
|
|
2036
|
+
maxRetries: config.limits.maxRetries,
|
|
2037
|
+
maxContextRefreshes: config.context.maxContextRefreshes,
|
|
2038
|
+
contextRefreshTerminationReason: result.contextRefreshTerminationReason ?? null,
|
|
2039
|
+
phaseTrace: result.phaseTrace,
|
|
2040
|
+
verification: result.verification
|
|
2041
|
+
? {
|
|
2042
|
+
outcome: result.verification.outcome,
|
|
2043
|
+
reasonCodes: result.verification.reason_codes,
|
|
2044
|
+
highConfidence: result.criticResult.report?.high_confidence
|
|
2045
|
+
?? result.criticResult.high_confidence
|
|
2046
|
+
?? false,
|
|
2047
|
+
policy: result.verification.policy,
|
|
2048
|
+
totals: result.verification.totals,
|
|
2049
|
+
}
|
|
2050
|
+
: null,
|
|
2051
|
+
};
|
|
2052
|
+
summaryVerification = result.verification;
|
|
2053
|
+
if (result.criticResult.status !== "PASS") {
|
|
2054
|
+
const failureReasons = result.criticResult.reasons?.length
|
|
2055
|
+
? result.criticResult.reasons
|
|
2056
|
+
: ["smart_pipeline_failed"];
|
|
2057
|
+
summaryDisposition = "fail";
|
|
2058
|
+
summaryFailureStage = "smart_pipeline";
|
|
2059
|
+
summaryRetryable = result.criticResult.retryable ?? null;
|
|
2060
|
+
summaryReasonCodes = uniqueStrings(failureReasons);
|
|
2061
|
+
summaryFailureClass = inferFailureClass(summaryFailureStage, summaryReasonCodes);
|
|
2062
|
+
await logger.log("run_failed", {
|
|
2063
|
+
stage: "smart_pipeline",
|
|
2064
|
+
reasons: failureReasons,
|
|
2065
|
+
failure_class: summaryFailureClass,
|
|
2066
|
+
retryable: result.criticResult.retryable ?? null,
|
|
2067
|
+
report: result.criticResult.report ?? null,
|
|
2068
|
+
});
|
|
2069
|
+
throw new Error(`Smart pipeline failed: ${failureReasons.join("; ")}`);
|
|
2070
|
+
}
|
|
2071
|
+
if (result.verification && result.verification.outcome !== "verified_passed") {
|
|
2072
|
+
summaryDisposition = "degraded";
|
|
2073
|
+
summaryFailureStage = "verify";
|
|
2074
|
+
summaryReasonCodes = uniqueStrings(result.verification.reason_codes.map((code) => `verification_${code}`));
|
|
2075
|
+
summaryFailureClass = "verification_failure";
|
|
2076
|
+
}
|
|
2077
|
+
finalMessageContent = result.builderResult.finalMessage.content;
|
|
2078
|
+
usage = result.builderResult.usage;
|
|
2079
|
+
toolCallsExecuted = result.builderResult.toolCallsExecuted;
|
|
2080
|
+
}
|
|
2081
|
+
else {
|
|
2082
|
+
const pricingResolution = resolvePricing(config.cost.pricingOverrides, config.provider, config.model);
|
|
2083
|
+
pricingSpec = pricingResolution.pricing;
|
|
2084
|
+
pricingSource = pricingResolution.source;
|
|
2085
|
+
phaseProviders.act = {
|
|
2086
|
+
provider: config.provider,
|
|
2087
|
+
model: config.model,
|
|
2088
|
+
};
|
|
2089
|
+
const summary = summarizeContext(undefined, workflowTaskInput);
|
|
2090
|
+
estimatedFocus = summary.focusCount;
|
|
2091
|
+
estimatedPeriphery = summary.peripheryCount;
|
|
2092
|
+
const estimate = estimateCostFromChars(summary.charCount, config.cost.charPerToken, pricingSpec, pricingSource);
|
|
2093
|
+
estimatedCost = estimate.estimatedCost;
|
|
2094
|
+
estimatedTokens = estimate.estimatedTotalTokens;
|
|
2095
|
+
estimatedChars = estimate.charCount;
|
|
2096
|
+
await logger.log("cost_estimate", {
|
|
2097
|
+
provider: config.provider,
|
|
2098
|
+
model: config.model,
|
|
2099
|
+
focusCount: estimatedFocus,
|
|
2100
|
+
peripheryCount: estimatedPeriphery,
|
|
2101
|
+
charCount: estimatedChars,
|
|
2102
|
+
estimatedTokens,
|
|
2103
|
+
estimatedCost,
|
|
2104
|
+
pricingSource,
|
|
2105
|
+
});
|
|
2106
|
+
process.stderr.write(`[codali] Preflight: focus=${estimatedFocus} periphery=${estimatedPeriphery} ` +
|
|
2107
|
+
`chars=${estimatedChars} tokens~${estimatedTokens} est_cost=${formatCost(estimatedCost)}\n`);
|
|
2108
|
+
if (estimatedCost !== undefined && estimatedCost > config.cost.maxCostPerRun) {
|
|
2109
|
+
const proceed = await confirmOverage(`Estimated cost ${formatCost(estimatedCost)} exceeds max ${formatCost(config.cost.maxCostPerRun)}. Continue? [y/N] `);
|
|
2110
|
+
if (!proceed) {
|
|
2111
|
+
throw new Error("Run cancelled due to cost limit");
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
const provider = createProvider(config.provider, {
|
|
2115
|
+
model: config.model,
|
|
2116
|
+
apiKey: config.apiKey,
|
|
2117
|
+
baseUrl: config.baseUrl,
|
|
2118
|
+
timeoutMs: config.limits.timeoutMs,
|
|
2119
|
+
});
|
|
2120
|
+
const result = await runCodaliTask({
|
|
2121
|
+
task: workflowTaskInput,
|
|
2122
|
+
workspace: { root: config.workspaceRoot },
|
|
2123
|
+
provider: {
|
|
2124
|
+
name: config.provider,
|
|
2125
|
+
model: config.model,
|
|
2126
|
+
apiKey: config.apiKey,
|
|
2127
|
+
baseUrl: config.baseUrl,
|
|
2128
|
+
timeoutMs: config.limits.timeoutMs,
|
|
2129
|
+
},
|
|
2130
|
+
docdex: {
|
|
2131
|
+
baseUrl: config.docdex.baseUrl,
|
|
2132
|
+
repoId: config.docdex.repoId,
|
|
2133
|
+
repoRoot: config.docdex.repoRoot ?? config.workspaceRoot,
|
|
2134
|
+
dagSessionId: runId,
|
|
2135
|
+
},
|
|
2136
|
+
policy: {
|
|
2137
|
+
allowWrites: writesAllowed,
|
|
2138
|
+
allowShell: config.tools.allowShell ?? false,
|
|
2139
|
+
allowDestructiveOperations: config.tools.allowDestructiveOperations ?? false,
|
|
2140
|
+
allowOutsideWorkspace: config.tools.allowOutsideWorkspace ?? false,
|
|
2141
|
+
maxSteps: config.limits.maxSteps,
|
|
2142
|
+
maxToolCalls: config.limits.maxToolCalls,
|
|
2143
|
+
maxTokens: config.limits.maxTokens,
|
|
2144
|
+
timeoutMs: config.limits.timeoutMs,
|
|
2145
|
+
mode: "tool_loop",
|
|
2146
|
+
},
|
|
2147
|
+
streaming: config.streaming,
|
|
2148
|
+
metadata: {
|
|
2149
|
+
jobId: config.jobId,
|
|
2150
|
+
requestId: runId,
|
|
2151
|
+
tenantId: config.project,
|
|
2152
|
+
agentSlug: config.agentSlug,
|
|
2153
|
+
},
|
|
2154
|
+
providerInstance: provider,
|
|
2155
|
+
toolRegistry: registry,
|
|
2156
|
+
toolContext,
|
|
2157
|
+
logger,
|
|
2158
|
+
onEvent: (event) => {
|
|
2159
|
+
if (event.type === "usage" || event.type === "final") {
|
|
2160
|
+
return;
|
|
2161
|
+
}
|
|
2162
|
+
if (event.type === "subagent_start" || event.type === "subagent_result") {
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
if (event.type === "tool_call") {
|
|
2166
|
+
streamState.onEvent({ type: "tool_call", name: event.name, args: event.args });
|
|
2167
|
+
return;
|
|
2168
|
+
}
|
|
2169
|
+
if (event.type === "tool_result") {
|
|
2170
|
+
streamState.onEvent({
|
|
2171
|
+
type: "tool_result",
|
|
2172
|
+
name: event.name,
|
|
2173
|
+
output: event.output,
|
|
2174
|
+
ok: event.ok,
|
|
2175
|
+
errorCode: event.errorCode,
|
|
2176
|
+
retryable: event.retryable,
|
|
2177
|
+
});
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
streamState.onEvent(event);
|
|
2181
|
+
},
|
|
2182
|
+
});
|
|
2183
|
+
finalMessageContent = result.finalMessage;
|
|
2184
|
+
usage = result.usage;
|
|
2185
|
+
toolCallsExecuted = result.toolCallsExecuted;
|
|
2186
|
+
}
|
|
2187
|
+
streamState.flush();
|
|
2188
|
+
if (streamState.didStream()) {
|
|
2189
|
+
if (!finalMessageContent.endsWith("\n")) {
|
|
2190
|
+
process.stdout.write("\n");
|
|
2191
|
+
streamState.writeOutput("\n");
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
else {
|
|
2195
|
+
// eslint-disable-next-line no-console
|
|
2196
|
+
console.log(finalMessageContent);
|
|
2197
|
+
streamState.writeOutput(`${finalMessageContent}\n`);
|
|
2198
|
+
}
|
|
2199
|
+
await emitRunSummary();
|
|
2200
|
+
summaryEmitted = true;
|
|
2201
|
+
const meta = {
|
|
2202
|
+
runId,
|
|
2203
|
+
fingerprint: runContext.fingerprint ?? null,
|
|
2204
|
+
logPath: logger.logPath,
|
|
2205
|
+
outputLogPath,
|
|
2206
|
+
touchedFiles: runContext.getTouchedFiles(),
|
|
2207
|
+
command: config.command,
|
|
2208
|
+
commandRunId: config.commandRunId,
|
|
2209
|
+
jobId: config.jobId,
|
|
2210
|
+
project: config.project,
|
|
2211
|
+
taskId: config.taskId,
|
|
2212
|
+
taskKey: config.taskKey,
|
|
2213
|
+
agentId: config.agentId,
|
|
2214
|
+
agentSlug: config.agentSlug,
|
|
2215
|
+
workflow: workflowProfile
|
|
2216
|
+
? {
|
|
2217
|
+
name: workflowProfile.name,
|
|
2218
|
+
source: workflowProfile.source,
|
|
2219
|
+
outputContract: workflowProfile.outputContract,
|
|
2220
|
+
allowWrites: workflowProfile.allowWrites,
|
|
2221
|
+
}
|
|
2222
|
+
: null,
|
|
2223
|
+
};
|
|
2224
|
+
try {
|
|
2225
|
+
process.stderr.write(`CODALI_RUN_META ${JSON.stringify(meta)}\n`);
|
|
2226
|
+
}
|
|
2227
|
+
catch {
|
|
2228
|
+
// ignore stderr write failures
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
catch (error) {
|
|
2232
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2233
|
+
summaryDisposition = "fail";
|
|
2234
|
+
summaryFailureStage = summaryFailureStage ?? "run_command";
|
|
2235
|
+
summaryReasonCodes = summaryReasonCodes.length > 0 ? summaryReasonCodes : uniqueStrings([message]);
|
|
2236
|
+
summaryFailureClass = summaryFailureClass ?? inferFailureClass(summaryFailureStage, summaryReasonCodes, message);
|
|
2237
|
+
try {
|
|
2238
|
+
await logger.log("run_failed", {
|
|
2239
|
+
stage: summaryFailureStage,
|
|
2240
|
+
reasons: summaryReasonCodes,
|
|
2241
|
+
failure_class: summaryFailureClass,
|
|
2242
|
+
retryable: summaryRetryable ?? null,
|
|
2243
|
+
error: message,
|
|
2244
|
+
});
|
|
2245
|
+
if (!summaryEmitted) {
|
|
2246
|
+
await emitRunSummary();
|
|
2247
|
+
summaryEmitted = true;
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
catch {
|
|
2251
|
+
// Ignore secondary logging failures.
|
|
2252
|
+
}
|
|
2253
|
+
throw error;
|
|
2254
|
+
}
|
|
2255
|
+
finally {
|
|
2256
|
+
outputStream.end();
|
|
2257
|
+
unregisterSignals();
|
|
2258
|
+
await lock.release();
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
}
|