salmon-loop 0.2.13 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/argv/headless-detection.js +27 -0
- package/dist/cli/chat-flow.js +11 -0
- package/dist/cli/chat.js +160 -24
- package/dist/cli/commands/chat.js +14 -7
- package/dist/cli/commands/flow-mode.js +63 -0
- package/dist/cli/commands/registry.js +2 -0
- package/dist/cli/commands/run/benchmark-artifacts.js +41 -0
- package/dist/cli/commands/run/early-errors.js +23 -0
- package/dist/cli/commands/run/handler.js +115 -27
- package/dist/cli/commands/run/headless-error-writer.js +8 -0
- package/dist/cli/commands/run/loop-params.js +2 -0
- package/dist/cli/commands/run/mode.js +2 -5
- package/dist/cli/commands/run/parse-options.js +16 -0
- package/dist/cli/commands/run/persist-session.js +10 -1
- package/dist/cli/commands/run/preflight.js +10 -0
- package/dist/cli/commands/run/reporter-factory.js +4 -0
- package/dist/cli/commands/run/runtime-llm.js +38 -11
- package/dist/cli/commands/run/runtime-options.js +2 -2
- package/dist/cli/commands/serve.js +97 -77
- package/dist/cli/commands/tool-names.js +78 -78
- package/dist/cli/headless/anthropic-stream-normalized-encoder.js +6 -1
- package/dist/cli/headless/json-protocol.js +37 -0
- package/dist/cli/headless/native-stream-normalized-encoder.js +6 -1
- package/dist/cli/headless/protocol-metadata.js +22 -0
- package/dist/cli/headless/stream-json-protocol.js +34 -1
- package/dist/cli/index.js +6 -4
- package/dist/cli/locales/en.js +30 -6
- package/dist/cli/program-bootstrap.js +10 -5
- package/dist/cli/program-commands.js +5 -1
- package/dist/cli/reporters/anthropic-stream.js +7 -1
- package/dist/cli/reporters/json.js +4 -0
- package/dist/cli/reporters/stream-json.js +17 -2
- package/dist/cli/run-cli.js +5 -3
- package/dist/cli/slash/runtime.js +27 -12
- package/dist/cli/ui/components/CommandInput.js +7 -3
- package/dist/cli/ui/components/CommandSuggestionList.js +1 -1
- package/dist/cli/utils/command-option-source.js +13 -0
- package/dist/cli/utils/verify-resolver.js +8 -4
- package/dist/cli/utils/worktree-prepare-resolver.js +7 -3
- package/dist/core/adapters/fs/file-adapter.js +6 -0
- package/dist/core/adapters/fs/filesystem.js +2 -1
- package/dist/core/adapters/git/git-adapter.js +78 -1
- package/dist/core/backends/salmon-loop/task-executor.js +1 -0
- package/dist/core/benchmark/patch-artifact.js +124 -0
- package/dist/core/benchmark/swe-bench.js +25 -0
- package/dist/core/config/load.js +18 -11
- package/dist/core/config/resolve-llm.js +12 -0
- package/dist/core/config/resolvers/server.js +0 -6
- package/dist/core/config/validate.js +73 -21
- package/dist/core/context/gatherers/metadata-gatherer.js +1 -0
- package/dist/core/context/gatherers/ripgrep-gatherer.js +84 -2
- package/dist/core/context/keywords.js +18 -4
- package/dist/core/context/service-deps.js +2 -2
- package/dist/core/context/service.js +8 -0
- package/dist/core/context/steps/context-gather.js +38 -0
- package/dist/core/context/summarization/summarizer.js +55 -12
- package/dist/core/context/targeting/target-resolver.js +4 -4
- package/dist/core/extensions/index.js +23 -5
- package/dist/core/extensions/merge.js +14 -0
- package/dist/core/extensions/paths.js +31 -0
- package/dist/core/extensions/schemas.js +8 -5
- package/dist/core/facades/cli-chat.js +6 -2
- package/dist/core/facades/cli-command-chat.js +1 -0
- package/dist/core/facades/cli-command-tool-names.js +2 -0
- package/dist/core/facades/cli-observability.js +1 -1
- package/dist/core/facades/cli-program-bootstrap.js +1 -0
- package/dist/core/facades/cli-run-handler.js +4 -2
- package/dist/core/facades/cli-run-persist-session.js +1 -0
- package/dist/core/facades/cli-serve.js +4 -4
- package/dist/core/facades/cli-utils-worktree.js +1 -1
- package/dist/core/failure/diagnostics.js +53 -1
- package/dist/core/grizzco/dsl/llm-strategy.js +4 -1
- package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +67 -9
- package/dist/core/grizzco/engine/pipeline/pipeline.js +6 -2
- package/dist/core/grizzco/engine/transaction/attempt-failure.js +90 -15
- package/dist/core/grizzco/engine/transaction/report-mapper.js +17 -3
- package/dist/core/grizzco/engine/transaction/transaction-runner.js +165 -7
- package/dist/core/grizzco/flows/AutopilotFlow.js +18 -0
- package/dist/core/grizzco/flows/flow-dispatch.js +11 -0
- package/dist/core/grizzco/steps/answer.js +13 -14
- package/dist/core/grizzco/steps/autopilot.js +396 -0
- package/dist/core/grizzco/steps/cache-sharing.js +29 -0
- package/dist/core/grizzco/steps/explore.js +37 -21
- package/dist/core/grizzco/steps/generateReview.js +2 -5
- package/dist/core/grizzco/steps/patch/apply-check.js +10 -0
- package/dist/core/grizzco/steps/patch/diff-normalization.js +70 -0
- package/dist/core/grizzco/steps/patch/diff-salvage.js +46 -0
- package/dist/core/grizzco/steps/patch/prompt-input.js +42 -0
- package/dist/core/grizzco/steps/patch.js +105 -146
- package/dist/core/grizzco/steps/plan.js +101 -25
- package/dist/core/grizzco/steps/preflight.js +5 -6
- package/dist/core/grizzco/steps/request-assembly.js +78 -0
- package/dist/core/grizzco/steps/research.js +39 -36
- package/dist/core/grizzco/steps/tool-runtime.js +47 -0
- package/dist/core/grizzco/steps/verify-shared.js +23 -0
- package/dist/core/grizzco/steps/verify.js +13 -21
- package/dist/core/interaction/orchestration/facade.js +1 -1
- package/dist/core/llm/ai-sdk/chat-executor.js +2 -0
- package/dist/core/llm/ai-sdk/high-level-phase-specs.js +63 -0
- package/dist/core/llm/ai-sdk/message-mapper.js +40 -10
- package/dist/core/llm/ai-sdk/provider-factory.js +14 -0
- package/dist/core/llm/ai-sdk/request-params.js +113 -1
- package/dist/core/llm/ai-sdk/result-mapper.js +16 -0
- package/dist/core/llm/ai-sdk.js +112 -27
- package/dist/core/llm/capabilities.js +12 -0
- package/dist/core/llm/contracts/repair.js +36 -30
- package/dist/core/llm/errors.js +83 -2
- package/dist/core/llm/message-composition.js +7 -22
- package/dist/core/llm/phase-router.js +29 -10
- package/dist/core/llm/redact.js +28 -3
- package/dist/core/llm/registry.js +2 -0
- package/dist/core/llm/request-augmentation.js +55 -0
- package/dist/core/llm/request-envelope.js +334 -0
- package/dist/core/llm/shared-request-assembly.js +35 -0
- package/dist/core/llm/stream-utils.js +13 -4
- package/dist/core/llm/utils.js +18 -29
- package/dist/core/memory/relevant-retrieval.js +144 -0
- package/dist/core/observability/logger.js +11 -2
- package/dist/core/patch/diff.js +1 -0
- package/dist/core/prompts/registry.js +39 -2
- package/dist/core/prompts/runtime.js +50 -12
- package/dist/core/prompts/templates/phases/patch_user.hbs +2 -5
- package/dist/core/prompts/templates/phases/research_user.hbs +11 -0
- package/dist/core/prompts/templates/phases/review_user.hbs +3 -0
- package/dist/core/prompts/templates/system/answer_system.hbs +5 -0
- package/dist/core/prompts/templates/system/autopilot_system.hbs +11 -0
- package/dist/core/prompts/templates/system/explore_system.hbs +14 -23
- package/dist/core/prompts/templates/system/main_system.hbs +4 -16
- package/dist/core/prompts/templates/system/patch_system.hbs +39 -8
- package/dist/core/prompts/templates/system/plan_system.hbs +86 -1
- package/dist/core/prompts/templates/system/research_system.hbs +2 -0
- package/dist/core/protocols/a2a/agent-card.js +5 -3
- package/dist/core/protocols/a2a/sdk/executor.js +2 -1
- package/dist/core/protocols/a2a/sdk/server.js +0 -1
- package/dist/core/protocols/acp/formal-agent.js +300 -58
- package/dist/core/protocols/acp/handlers.js +5 -1
- package/dist/core/protocols/acp/permission-provider.js +1 -1
- package/dist/core/protocols/shared/flow-mode-mapping.js +23 -0
- package/dist/core/public-capabilities/flow-mode-metadata.js +39 -0
- package/dist/core/public-capabilities/projections.js +29 -0
- package/dist/core/public-capabilities/registry.js +26 -0
- package/dist/core/public-capabilities/types.js +2 -0
- package/dist/core/runtime/agent-server-runtime.js +47 -43
- package/dist/core/runtime/execution-profile.js +67 -0
- package/dist/core/session/artifact-state.js +160 -0
- package/dist/core/session/compaction/index.js +183 -0
- package/dist/core/session/compaction/microcompact.js +78 -0
- package/dist/core/session/compaction/tracking.js +48 -0
- package/dist/core/session/compaction/types.js +11 -0
- package/dist/core/session/compression.js +8 -0
- package/dist/core/session/manager.js +244 -8
- package/dist/core/session/pruning-strategy.js +55 -9
- package/dist/core/session/replacement-preview-provider.js +24 -0
- package/dist/core/session/replacement-state.js +131 -0
- package/dist/core/session/resume-repair/pipeline.js +79 -0
- package/dist/core/session/resume-repair/stages/load-raw-archive-state.js +40 -0
- package/dist/core/session/resume-repair/stages/reattach-runtime-state.js +8 -0
- package/dist/core/session/resume-repair/stages/recover-orphaned-branches.js +10 -0
- package/dist/core/session/resume-repair/stages/relink-boundary-and-tail.js +36 -0
- package/dist/core/session/resume-repair/stages/replay-startup-hooks.js +23 -0
- package/dist/core/session/resume-repair/stages/rescue-stale-metadata.js +17 -0
- package/dist/core/session/resume-repair/types.js +2 -0
- package/dist/core/session/summary-sync.js +164 -13
- package/dist/core/session/token-tracker.js +6 -0
- package/dist/core/skills/audit.js +34 -0
- package/dist/core/skills/bridge.js +84 -7
- package/dist/core/skills/discovery.js +94 -0
- package/dist/core/skills/feature-flags.js +52 -0
- package/dist/core/skills/index.js +1 -1
- package/dist/core/skills/loader.js +195 -20
- package/dist/core/skills/parser.js +296 -24
- package/dist/core/skills/permissions.js +117 -0
- package/dist/core/skills/runtime/MicroTaskRunner.js +10 -4
- package/dist/core/skills/runtime/SkillRunner.js +240 -61
- package/dist/core/strata/layers/shadow-driver/shadow-driver.js +37 -7
- package/dist/core/strata/layers/worktree.js +67 -10
- package/dist/core/strata/runtime/synchronizer.js +29 -2
- package/dist/core/streaming/stream-assembler.js +75 -31
- package/dist/core/sub-agent/context-snapshot.js +156 -0
- package/dist/core/sub-agent/core/loop.js +1 -1
- package/dist/core/sub-agent/core/manager.js +119 -20
- package/dist/core/sub-agent/dispatch-policy.js +29 -0
- package/dist/core/sub-agent/prefix-consistency.js +48 -0
- package/dist/core/sub-agent/registry-defaults.js +4 -0
- package/dist/core/sub-agent/tools/task-spawn.js +79 -2
- package/dist/core/sub-agent/types.js +134 -5
- package/dist/core/tools/audit.js +13 -4
- package/dist/core/tools/builtin/ast-grep.js +1 -1
- package/dist/core/tools/builtin/ast.js +1 -1
- package/dist/core/tools/builtin/benchmark.js +360 -0
- package/dist/core/tools/builtin/code-search/backends/rg.js +2 -1
- package/dist/core/tools/builtin/code-search/executor.js +6 -1
- package/dist/core/tools/builtin/code-search/spec.js +26 -2
- package/dist/core/tools/builtin/fs.js +256 -23
- package/dist/core/tools/builtin/git.js +2 -2
- package/dist/core/tools/builtin/index.js +51 -2
- package/dist/core/tools/builtin/interaction.js +8 -1
- package/dist/core/tools/builtin/plan.js +37 -15
- package/dist/core/tools/builtin/shell.js +1 -1
- package/dist/core/tools/loader.js +39 -16
- package/dist/core/tools/mapper.js +17 -3
- package/dist/core/tools/mcp/client.js +2 -1
- package/dist/core/tools/parallel/scheduler.js +35 -4
- package/dist/core/tools/permissions/permission-rules.js +5 -10
- package/dist/core/tools/policy.js +6 -1
- package/dist/core/tools/recoverable-tool-errors.js +10 -0
- package/dist/core/tools/router.js +24 -6
- package/dist/core/tools/session.js +458 -48
- package/dist/core/tools/tool-visibility.js +62 -0
- package/dist/core/tools/types.js +9 -1
- package/dist/core/types/execution.js +4 -0
- package/dist/core/types/flow-mode.js +8 -0
- package/dist/core/utils/path.js +52 -0
- package/dist/core/verification/runner.js +4 -1
- package/dist/core/version.js +17 -0
- package/dist/languages/typescript/index.js +4 -1
- package/dist/locales/en.js +35 -2
- package/dist/utils/eol.js +1 -1
- package/package.json +14 -7
- package/scripts/fix-es-abstract-compat.js +77 -0
- package/dist/core/runtime/fastify-server-bundle.js +0 -26
- package/dist/core/runtime/sidecar-fastify-plugin.js +0 -35
- package/dist/core/runtime/sidecar-paths.js +0 -47
- package/dist/core/runtime/sidecar-route-catalog.js +0 -103
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const HEADLESS_OUTPUT_FORMATS = new Set(['json', 'stream-json']);
|
|
1
2
|
function readFlagValue(tokens, index) {
|
|
2
3
|
const token = tokens[index];
|
|
3
4
|
const eq = token.indexOf('=');
|
|
@@ -57,4 +58,30 @@ export function detectHeadlessOutputFromArgv(argv) {
|
|
|
57
58
|
outputProfile,
|
|
58
59
|
};
|
|
59
60
|
}
|
|
61
|
+
export function shouldForceColorForArgv(argv) {
|
|
62
|
+
if (process.env.NO_COLOR !== undefined)
|
|
63
|
+
return false;
|
|
64
|
+
const existingForceColor = process.env.FORCE_COLOR;
|
|
65
|
+
if (existingForceColor !== undefined && existingForceColor !== '')
|
|
66
|
+
return false;
|
|
67
|
+
const tokens = argv.slice(2);
|
|
68
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
69
|
+
const token = tokens[i];
|
|
70
|
+
if (token === '--')
|
|
71
|
+
break;
|
|
72
|
+
if (token === '--output-format') {
|
|
73
|
+
const value = tokens[i + 1];
|
|
74
|
+
if (HEADLESS_OUTPUT_FORMATS.has(String(value)))
|
|
75
|
+
return false;
|
|
76
|
+
i += 1;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (token.startsWith('--output-format=')) {
|
|
80
|
+
const value = token.slice('--output-format='.length);
|
|
81
|
+
if (HEADLESS_OUTPUT_FORMATS.has(value))
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
60
87
|
//# sourceMappingURL=headless-detection.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { resolveExecutionProfile, } from '../core/facades/cli-chat.js';
|
|
2
|
+
export function resolveActiveChatFlowMode(sessionFlowMode, defaultFlowMode) {
|
|
3
|
+
return sessionFlowMode ?? defaultFlowMode ?? 'autopilot';
|
|
4
|
+
}
|
|
5
|
+
export function resolveChatCheckpointStrategy(flowMode, configured) {
|
|
6
|
+
const profile = resolveExecutionProfile(flowMode);
|
|
7
|
+
return profile.readOnly
|
|
8
|
+
? 'direct'
|
|
9
|
+
: (configured ?? profile.defaultCheckpointStrategy ?? 'worktree');
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=chat-flow.js.map
|
package/dist/cli/chat.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildSessionArtifactStateFromLoopResult, buildEffectiveConversationContext, ChatSessionManager, DEFAULT_LLM_OUTPUT_POLICY, emitLlmOutput, getDefaultSessionContextBudgetTokens, InputHistoryManager, logIgnoredError, getLogger, refreshSessionSummary, runSalmonLoop, TokenTracker, createInitialTracking, onNormalTurnComplete, runCompactionPipeline, reactiveCompact, } from '../core/facades/cli-chat.js';
|
|
2
2
|
import { createSubAgentController } from '../core/facades/cli-subagent.js';
|
|
3
3
|
import { createUiAuthorizationProvider } from './authorization/provider.js';
|
|
4
|
+
import { resolveActiveChatFlowMode, resolveChatCheckpointStrategy } from './chat-flow.js';
|
|
4
5
|
import { commands } from './commands/registry.js';
|
|
5
6
|
import { CHAT_QUEUE_CONFIG } from './config.js';
|
|
6
7
|
import { text } from './locales/index.js';
|
|
@@ -49,7 +50,10 @@ export async function startChatMode(options) {
|
|
|
49
50
|
let hideTimer = null;
|
|
50
51
|
let currentInstruction = null;
|
|
51
52
|
let lastInterruptedInput = null;
|
|
53
|
+
let currentFlowMode = null;
|
|
54
|
+
let lastInterruptedFlowMode = null;
|
|
52
55
|
let currentLlmOutputPolicy = options.llmOutput ?? DEFAULT_LLM_OUTPUT_POLICY;
|
|
56
|
+
let currentCompactionTracking = createInitialTracking();
|
|
53
57
|
const authorizationProvider = createUiAuthorizationProvider({
|
|
54
58
|
emit: (event) => {
|
|
55
59
|
latestEmit?.({ ...event, timestamp: new Date() });
|
|
@@ -156,8 +160,28 @@ export async function startChatMode(options) {
|
|
|
156
160
|
return false;
|
|
157
161
|
return /cancelled by user/i.test(reason);
|
|
158
162
|
};
|
|
159
|
-
const
|
|
163
|
+
const buildRecoveryFailureSummary = (result) => {
|
|
164
|
+
if (result.success)
|
|
165
|
+
return null;
|
|
166
|
+
const isRecoveryCandidate = result.reasonCode === 'TOOL_CORRECTION_REQUIRED' ||
|
|
167
|
+
result.reasonCode === 'AWAITING_INPUT' ||
|
|
168
|
+
isInterruptResult(result.reason);
|
|
169
|
+
if (!isRecoveryCandidate)
|
|
170
|
+
return null;
|
|
171
|
+
const summary = {};
|
|
172
|
+
if (typeof result.reasonCode === 'string')
|
|
173
|
+
summary.reasonCode = result.reasonCode;
|
|
174
|
+
if (typeof result.diagnosticCode === 'string')
|
|
175
|
+
summary.diagnosticCode = result.diagnosticCode;
|
|
176
|
+
if (typeof result.safeHint === 'string')
|
|
177
|
+
summary.safeHint = result.safeHint;
|
|
178
|
+
if (typeof result.failurePhase === 'string')
|
|
179
|
+
summary.failurePhase = result.failurePhase;
|
|
180
|
+
return Object.keys(summary).length > 0 ? summary : null;
|
|
181
|
+
};
|
|
182
|
+
const markInterrupted = (input, flowMode) => {
|
|
160
183
|
lastInterruptedInput = input;
|
|
184
|
+
lastInterruptedFlowMode = flowMode ?? null;
|
|
161
185
|
queue.pause();
|
|
162
186
|
latestEmit?.({
|
|
163
187
|
type: 'log',
|
|
@@ -172,6 +196,8 @@ export async function startChatMode(options) {
|
|
|
172
196
|
return;
|
|
173
197
|
if (!latestDispatch)
|
|
174
198
|
return;
|
|
199
|
+
const queuedFlowMode = queueOptions?.flowMode ??
|
|
200
|
+
resolveActiveChatFlowMode(sessionManager.getChatFlowMode(), options.defaultFlowMode);
|
|
175
201
|
try {
|
|
176
202
|
const currentSessionId = sessionManager.getCurrent().meta.id;
|
|
177
203
|
historyManager
|
|
@@ -203,13 +229,30 @@ export async function startChatMode(options) {
|
|
|
203
229
|
started = true;
|
|
204
230
|
latestDispatch?.({ type: 'SHIFT_QUEUE_MESSAGE' });
|
|
205
231
|
currentInstruction = trimmed;
|
|
232
|
+
currentFlowMode = queuedFlowMode;
|
|
206
233
|
const timeoutAbort = new AbortController();
|
|
207
234
|
const mergedSignal = mergeAbortSignals([latestGuiOptions?.signal, timeoutAbort.signal]);
|
|
235
|
+
// Run compaction pipeline (Level 1 Autocompact) before building context
|
|
236
|
+
const compactionResult = await runCompactionPipeline({
|
|
237
|
+
sessionManager,
|
|
238
|
+
llm: options.llm,
|
|
239
|
+
tracking: currentCompactionTracking,
|
|
240
|
+
signal: mergedSignal.signal,
|
|
241
|
+
});
|
|
242
|
+
if (compactionResult.performed) {
|
|
243
|
+
currentCompactionTracking = compactionResult.tracking;
|
|
244
|
+
}
|
|
208
245
|
const modelIdForBudget = options.llm.getModelId?.() || process.env.SALMONLOOP_MODEL || process.env.S8P_MODEL;
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
246
|
+
const baseSessionBudgetTokens = getDefaultSessionContextBudgetTokens({
|
|
247
|
+
modelId: modelIdForBudget,
|
|
248
|
+
});
|
|
249
|
+
const conversationContext = buildEffectiveConversationContext({
|
|
250
|
+
llm: options.llm,
|
|
251
|
+
sessionManager,
|
|
252
|
+
budgetTokens: baseSessionBudgetTokens,
|
|
212
253
|
});
|
|
254
|
+
const artifactHints = sessionManager.getArtifactState();
|
|
255
|
+
const replacementState = sessionManager.getReplacementState();
|
|
213
256
|
// Single source of truth: chat runtime owns when a user message is appended to the UI list.
|
|
214
257
|
// The UI layer must not also append user messages (to avoid duplicates).
|
|
215
258
|
latestDispatch?.({
|
|
@@ -228,27 +271,15 @@ export async function startChatMode(options) {
|
|
|
228
271
|
timestamp: Date.now(),
|
|
229
272
|
});
|
|
230
273
|
const execution = await withTimeout((async () => {
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
signal: mergedSignal.signal,
|
|
234
|
-
});
|
|
235
|
-
latestEmit?.({
|
|
236
|
-
type: 'log',
|
|
237
|
-
level: 'info',
|
|
238
|
-
message: text.cli.chatIntentRouted(intentDecision.intent, intentDecision.confidence, intentDecision.reason),
|
|
239
|
-
timestamp: new Date(),
|
|
240
|
-
});
|
|
241
|
-
const nonMutating = intentDecision.intent === 'review' ||
|
|
242
|
-
intentDecision.intent === 'research' ||
|
|
243
|
-
intentDecision.intent === 'answer';
|
|
244
|
-
const strategy = nonMutating ? 'direct' : options.checkpointStrategy || 'worktree';
|
|
274
|
+
const flowMode = queuedFlowMode;
|
|
275
|
+
const strategy = resolveChatCheckpointStrategy(flowMode, options.checkpointStrategy);
|
|
245
276
|
const verboseLevel = options.verbose === true ? 'basic' : options.verbose;
|
|
246
277
|
const result = await runSalmonLoop({
|
|
247
278
|
instruction: trimmed,
|
|
248
279
|
verify: options.verifyCommand,
|
|
249
280
|
repoPath: options.repoPath,
|
|
250
281
|
llm: options.llm,
|
|
251
|
-
mode:
|
|
282
|
+
mode: flowMode,
|
|
252
283
|
strategy,
|
|
253
284
|
verbose: verboseLevel,
|
|
254
285
|
onEvent: latestEmit,
|
|
@@ -257,6 +288,8 @@ export async function startChatMode(options) {
|
|
|
257
288
|
outcomeReporter: options.outcomeReporter,
|
|
258
289
|
auditScope: options.auditScope,
|
|
259
290
|
conversationContext: conversationContext.length > 0 ? conversationContext : undefined,
|
|
291
|
+
artifactHints,
|
|
292
|
+
replacementState,
|
|
260
293
|
astValidation: options.astValidation,
|
|
261
294
|
languagePlugins: options.languagePlugins,
|
|
262
295
|
// Resolve sessionId at call time to support `/session` switching.
|
|
@@ -267,15 +300,99 @@ export async function startChatMode(options) {
|
|
|
267
300
|
userInputProvider,
|
|
268
301
|
subAgentController,
|
|
269
302
|
permissionMode: options.permissionMode,
|
|
303
|
+
}).catch(async (error) => {
|
|
304
|
+
// Level 2: Reactive Compact
|
|
305
|
+
// If the provider reports prompt-too-long, attempt emergency compaction AND retry ONCE
|
|
306
|
+
// with a smaller session context budget to guarantee prompt reduction.
|
|
307
|
+
const isContextOverflow = (() => {
|
|
308
|
+
if (!error || typeof error !== 'object')
|
|
309
|
+
return false;
|
|
310
|
+
if ('llmCode' in error &&
|
|
311
|
+
error.llmCode === 'LLM_CONTEXT_LENGTH_EXCEEDED') {
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
const message = error instanceof Error
|
|
315
|
+
? error.message
|
|
316
|
+
: typeof error.message === 'string'
|
|
317
|
+
? String(error.message)
|
|
318
|
+
: '';
|
|
319
|
+
const lower = message.toLowerCase();
|
|
320
|
+
return (lower.includes('maximum context length') ||
|
|
321
|
+
lower.includes('context length') ||
|
|
322
|
+
lower.includes('too many tokens') ||
|
|
323
|
+
lower.includes('prompt is too long') ||
|
|
324
|
+
lower.includes('input is too long') ||
|
|
325
|
+
lower.includes('please reduce'));
|
|
326
|
+
})();
|
|
327
|
+
if (!isContextOverflow) {
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
const reactiveResult = await reactiveCompact({
|
|
331
|
+
sessionManager,
|
|
332
|
+
llm: options.llm,
|
|
333
|
+
error,
|
|
334
|
+
tracking: currentCompactionTracking,
|
|
335
|
+
signal: mergedSignal.signal,
|
|
336
|
+
});
|
|
337
|
+
currentCompactionTracking = reactiveResult.tracking;
|
|
338
|
+
// Rebuild context and retry runSalmonLoop.
|
|
339
|
+
// Important: avoid duplicating the current instruction inside `conversationContext`,
|
|
340
|
+
// since `instruction: trimmed` is already provided as the primary input.
|
|
341
|
+
const history = sessionManager.getMessages();
|
|
342
|
+
const retryMessages = history.length > 0 &&
|
|
343
|
+
history[history.length - 1]?.role === 'user' &&
|
|
344
|
+
history[history.length - 1]?.content === trimmed
|
|
345
|
+
? history.slice(0, -1)
|
|
346
|
+
: history;
|
|
347
|
+
const reactiveBudgetTokens = Math.max(64, Math.min(baseSessionBudgetTokens - 1, Math.floor(baseSessionBudgetTokens * 0.5)));
|
|
348
|
+
getLogger().audit('COMPACTION_REACTIVE_RETRY', {
|
|
349
|
+
reason: 'context_overflow',
|
|
350
|
+
baseSessionBudgetTokens,
|
|
351
|
+
reactiveBudgetTokens,
|
|
352
|
+
baseIsMinimum: baseSessionBudgetTokens <= 256,
|
|
353
|
+
modelId: modelIdForBudget ?? 'unknown',
|
|
354
|
+
}, { source: 'chat', severity: 'low', scope: 'session', phase: 'COMPACTION' });
|
|
355
|
+
const newContext = buildEffectiveConversationContext({
|
|
356
|
+
llm: options.llm,
|
|
357
|
+
sessionManager,
|
|
358
|
+
messages: retryMessages,
|
|
359
|
+
budgetTokens: reactiveBudgetTokens,
|
|
360
|
+
});
|
|
361
|
+
return await runSalmonLoop({
|
|
362
|
+
instruction: trimmed,
|
|
363
|
+
verify: options.verifyCommand,
|
|
364
|
+
repoPath: options.repoPath,
|
|
365
|
+
llm: options.llm,
|
|
366
|
+
mode: flowMode,
|
|
367
|
+
strategy,
|
|
368
|
+
verbose: verboseLevel,
|
|
369
|
+
onEvent: latestEmit,
|
|
370
|
+
signal: mergedSignal.signal,
|
|
371
|
+
llmOutput: currentLlmOutputPolicy,
|
|
372
|
+
outcomeReporter: options.outcomeReporter,
|
|
373
|
+
auditScope: options.auditScope,
|
|
374
|
+
conversationContext: newContext.length > 0 ? newContext : undefined,
|
|
375
|
+
artifactHints,
|
|
376
|
+
replacementState,
|
|
377
|
+
astValidation: options.astValidation,
|
|
378
|
+
languagePlugins: options.languagePlugins,
|
|
379
|
+
langfuseSessionId: options.langfuseSessionId || sessionManager.getCurrent().meta.id,
|
|
380
|
+
langfuseUserId: options.langfuseUserId,
|
|
381
|
+
authorizationProvider,
|
|
382
|
+
authorizationMode: 'deferred',
|
|
383
|
+
userInputProvider,
|
|
384
|
+
subAgentController,
|
|
385
|
+
permissionMode: options.permissionMode,
|
|
386
|
+
});
|
|
270
387
|
});
|
|
271
|
-
return { kind: 'flow', mode:
|
|
388
|
+
return { kind: 'flow', mode: flowMode, result };
|
|
272
389
|
})(), CHAT_QUEUE_CONFIG.TASK_TIMEOUT_MS, () => timeoutAbort.abort()).finally(() => {
|
|
273
390
|
mergedSignal.cleanup();
|
|
274
391
|
});
|
|
275
392
|
const result = execution.result;
|
|
276
393
|
const mode = execution.mode;
|
|
277
394
|
if (!result.success && isInterruptResult(result.reason)) {
|
|
278
|
-
markInterrupted(trimmed);
|
|
395
|
+
markInterrupted(trimmed, mode);
|
|
279
396
|
}
|
|
280
397
|
// Add assistant message & iteration info
|
|
281
398
|
const changedFiles = result.changedFiles ?? [];
|
|
@@ -313,23 +430,38 @@ export async function startChatMode(options) {
|
|
|
313
430
|
if (usage) {
|
|
314
431
|
TokenTracker.accumulate(sessionManager.getCurrent(), usage);
|
|
315
432
|
}
|
|
433
|
+
sessionManager.mergeArtifactState(buildSessionArtifactStateFromLoopResult(result));
|
|
434
|
+
for (const preview of result.artifactHints?.toolResultPreviewArtifacts ?? []) {
|
|
435
|
+
sessionManager.freezeReplacementDecision({
|
|
436
|
+
toolResultId: `${preview.label}::${preview.artifact.handle}`,
|
|
437
|
+
decision: 'replaced',
|
|
438
|
+
preview: preview.label,
|
|
439
|
+
sourceArtifactHandle: preview.artifact.handle,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
316
442
|
await refreshSessionSummary({
|
|
317
443
|
sessionManager,
|
|
318
444
|
llm: options.llm,
|
|
319
445
|
contextHash: result.contextHash,
|
|
320
446
|
strategy: 'auto',
|
|
447
|
+
recoveryStatePatch: {
|
|
448
|
+
lastFailureSummary: buildRecoveryFailureSummary(result),
|
|
449
|
+
},
|
|
321
450
|
});
|
|
451
|
+
currentCompactionTracking = onNormalTurnComplete(currentCompactionTracking);
|
|
322
452
|
await sessionManager.save();
|
|
323
453
|
currentInstruction = null;
|
|
454
|
+
currentFlowMode = null;
|
|
324
455
|
return result;
|
|
325
456
|
}).catch((error) => {
|
|
326
457
|
if (!started) {
|
|
327
458
|
latestDispatch?.({ type: 'REMOVE_QUEUE_MESSAGE', payload: { id: queueMessageId } });
|
|
328
459
|
}
|
|
329
460
|
if (isInterruptError(error) && currentInstruction) {
|
|
330
|
-
markInterrupted(currentInstruction);
|
|
461
|
+
markInterrupted(currentInstruction, currentFlowMode ?? undefined);
|
|
331
462
|
}
|
|
332
463
|
currentInstruction = null;
|
|
464
|
+
currentFlowMode = null;
|
|
333
465
|
throw error;
|
|
334
466
|
});
|
|
335
467
|
};
|
|
@@ -341,11 +473,13 @@ export async function startChatMode(options) {
|
|
|
341
473
|
resume: () => {
|
|
342
474
|
queue.resume();
|
|
343
475
|
lastInterruptedInput = null;
|
|
476
|
+
lastInterruptedFlowMode = null;
|
|
344
477
|
getLogger().audit('QUEUE_RESUME', { status: 'resumed' }, { source: 'chat', severity: 'low', scope: 'session' });
|
|
345
478
|
},
|
|
346
479
|
clear: () => {
|
|
347
480
|
const cleared = queue.clear();
|
|
348
481
|
lastInterruptedInput = null;
|
|
482
|
+
lastInterruptedFlowMode = null;
|
|
349
483
|
latestDispatch?.({ type: 'CLEAR_QUEUE_MESSAGES' });
|
|
350
484
|
getLogger().audit('QUEUE_CLEAR', { cleared }, { source: 'chat', severity: 'low', scope: 'session' });
|
|
351
485
|
return cleared;
|
|
@@ -354,9 +488,11 @@ export async function startChatMode(options) {
|
|
|
354
488
|
if (!lastInterruptedInput)
|
|
355
489
|
return false;
|
|
356
490
|
const retryInput = lastInterruptedInput;
|
|
491
|
+
const retryFlowMode = lastInterruptedFlowMode ?? undefined;
|
|
357
492
|
lastInterruptedInput = null;
|
|
493
|
+
lastInterruptedFlowMode = null;
|
|
358
494
|
queue.resume();
|
|
359
|
-
enqueueInput(retryInput, { front: true });
|
|
495
|
+
enqueueInput(retryInput, { front: true, flowMode: retryFlowMode });
|
|
360
496
|
return true;
|
|
361
497
|
},
|
|
362
498
|
status: () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { createPluginRegistry, createPromptRegistry, createRuntimeLlm, setPluginRegistry, setPromptRegistry, ExtensionConfigError, getLogger, normalizePermissionMode, PluginLoader, resolveExtensions, } from '../../core/facades/cli-command-chat.js';
|
|
1
|
+
import { createPluginRegistry, createPromptRegistry, createRuntimeLlm, setPluginRegistry, setPromptRegistry, ExtensionConfigError, getLogger, normalizePermissionMode, PluginLoader, resolveExecutionProfile, resolveExtensions, } from '../../core/facades/cli-command-chat.js';
|
|
2
2
|
import { text } from '../locales/index.js';
|
|
3
|
+
import { getOptionValueSourceWithGlobalFallback } from '../utils/command-option-source.js';
|
|
3
4
|
import { resolveLlmOutputPolicyFromCli } from '../utils/llm-output.js';
|
|
4
5
|
import { createOutcomeReporter } from '../utils/outcome-reporter.js';
|
|
5
6
|
import { resolveCliConfig } from '../utils/resolve-cli-config.js';
|
|
@@ -40,7 +41,14 @@ export async function handleChatCommand(options, command) {
|
|
|
40
41
|
setPluginRegistry(languagePlugins);
|
|
41
42
|
setPromptRegistry(createPromptRegistry());
|
|
42
43
|
await PluginLoader.loadPlugins(languagePlugins, runPath);
|
|
43
|
-
const
|
|
44
|
+
const defaultFlowMode = 'autopilot';
|
|
45
|
+
const defaultFlowProfile = resolveExecutionProfile(defaultFlowMode);
|
|
46
|
+
const modeOptionSource = getOptionValueSourceWithGlobalFallback(command, 'mode');
|
|
47
|
+
const checkpointStrategyOptionSource = getOptionValueSourceWithGlobalFallback(command, 'checkpointStrategy');
|
|
48
|
+
const rawPermissionMode = (modeOptionSource === 'cli' ? allOptions.mode : undefined) ??
|
|
49
|
+
resolvedConfig.permissionMode ??
|
|
50
|
+
defaultFlowProfile.defaultPermissionMode ??
|
|
51
|
+
'interactive';
|
|
44
52
|
const permissionMode = normalizePermissionMode(rawPermissionMode);
|
|
45
53
|
if (!permissionMode) {
|
|
46
54
|
getLogger().error(`Invalid --mode "${String(rawPermissionMode)}". Expected "interactive" or "yolo".`);
|
|
@@ -86,11 +94,10 @@ export async function handleChatCommand(options, command) {
|
|
|
86
94
|
repoPath: runPath,
|
|
87
95
|
llm,
|
|
88
96
|
verifyCommand,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
: allOptions.checkpointStrategy || 'worktree',
|
|
97
|
+
defaultFlowMode,
|
|
98
|
+
checkpointStrategy: checkpointStrategyOptionSource === 'cli'
|
|
99
|
+
? allOptions.checkpointStrategy || 'worktree'
|
|
100
|
+
: undefined,
|
|
94
101
|
continue: continueSession,
|
|
95
102
|
resumeSessionId,
|
|
96
103
|
verbose: verboseLevel,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { FLOW_MODES, parseFlowMode } from '../../core/types/flow-mode.js';
|
|
2
|
+
import { text } from '../locales/index.js';
|
|
3
|
+
import { parseSuggestionContext } from './utils.js';
|
|
4
|
+
export const flowModeCommand = {
|
|
5
|
+
name: '/flow-mode',
|
|
6
|
+
description: text.cli.commandFlowMode,
|
|
7
|
+
order: 55,
|
|
8
|
+
getSuggestions: ({ input }) => {
|
|
9
|
+
const { argIndex, currentPrefix } = parseSuggestionContext(input);
|
|
10
|
+
if (argIndex !== 1)
|
|
11
|
+
return [];
|
|
12
|
+
const search = currentPrefix.toLowerCase();
|
|
13
|
+
return FLOW_MODES.filter((mode) => mode.startsWith(search)).map((mode) => ({
|
|
14
|
+
name: mode,
|
|
15
|
+
description: text.cli.flowModeSuggestion(mode),
|
|
16
|
+
}));
|
|
17
|
+
},
|
|
18
|
+
execute: async ({ emit, input, sessionManager }) => {
|
|
19
|
+
const args = input.trim().split(/\s+/).slice(1);
|
|
20
|
+
const rawValue = args[0];
|
|
21
|
+
if (!rawValue) {
|
|
22
|
+
const current = sessionManager.getChatFlowMode() ?? 'autopilot';
|
|
23
|
+
emit({
|
|
24
|
+
type: 'log',
|
|
25
|
+
level: 'info',
|
|
26
|
+
message: text.cli.flowModeCurrent(current),
|
|
27
|
+
timestamp: new Date(),
|
|
28
|
+
});
|
|
29
|
+
emit({
|
|
30
|
+
type: 'log',
|
|
31
|
+
level: 'info',
|
|
32
|
+
message: text.cli.flowModeUsage,
|
|
33
|
+
timestamp: new Date(),
|
|
34
|
+
});
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const normalized = parseFlowMode(rawValue);
|
|
38
|
+
if (!normalized) {
|
|
39
|
+
emit({
|
|
40
|
+
type: 'log',
|
|
41
|
+
level: 'error',
|
|
42
|
+
message: text.cli.flowModeInvalid(rawValue),
|
|
43
|
+
timestamp: new Date(),
|
|
44
|
+
});
|
|
45
|
+
emit({
|
|
46
|
+
type: 'log',
|
|
47
|
+
level: 'info',
|
|
48
|
+
message: text.cli.flowModeUsage,
|
|
49
|
+
timestamp: new Date(),
|
|
50
|
+
});
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
sessionManager.updateChatFlowMode(normalized);
|
|
54
|
+
await sessionManager.save();
|
|
55
|
+
emit({
|
|
56
|
+
type: 'log',
|
|
57
|
+
level: 'info',
|
|
58
|
+
message: text.cli.flowModeUpdated(normalized),
|
|
59
|
+
timestamp: new Date(),
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=flow-mode.js.map
|
|
@@ -2,6 +2,7 @@ import { text } from '../locales/index.js';
|
|
|
2
2
|
import { allowlistCommand } from './allowlist.js';
|
|
3
3
|
import { configCommand } from './config.js';
|
|
4
4
|
import { exitCommand } from './exit.js';
|
|
5
|
+
import { flowModeCommand } from './flow-mode.js';
|
|
5
6
|
import { formatHelpRows } from './help-format.js';
|
|
6
7
|
import { llmOutputCommand } from './llm-output.js';
|
|
7
8
|
import { logModeCommand } from './log-mode.js';
|
|
@@ -21,6 +22,7 @@ const baseCommands = [
|
|
|
21
22
|
allowlistCommand,
|
|
22
23
|
modeCommand,
|
|
23
24
|
logModeCommand,
|
|
25
|
+
flowModeCommand,
|
|
24
26
|
configCommand,
|
|
25
27
|
subAgentCommand,
|
|
26
28
|
newCommand,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FileAdapter } from '../../../core/adapters/fs/file-adapter.js';
|
|
2
|
+
import { buildBenchmarkPatchArtifact } from '../../../core/benchmark/patch-artifact.js';
|
|
3
|
+
import { encodeSweBenchPredictionJsonl } from '../../../core/benchmark/swe-bench.js';
|
|
4
|
+
export async function attachRunBenchmarkArtifacts(params) {
|
|
5
|
+
if (!params.exportPatchPath && !params.sweBenchPredictionsPath)
|
|
6
|
+
return;
|
|
7
|
+
const fileAdapter = new FileAdapter();
|
|
8
|
+
const artifact = await buildBenchmarkPatchArtifact({
|
|
9
|
+
repoPath: params.repoPath,
|
|
10
|
+
changedFilesHint: params.result.changedFiles,
|
|
11
|
+
excludePaths: [params.exportPatchPath, params.sweBenchPredictionsPath].filter((path) => typeof path === 'string'),
|
|
12
|
+
});
|
|
13
|
+
if (params.exportPatchPath) {
|
|
14
|
+
await fileAdapter.writeFile(params.exportPatchPath, artifact.patch);
|
|
15
|
+
}
|
|
16
|
+
params.result.benchmarkPatchArtifact = {
|
|
17
|
+
kind: 'git-unified-diff',
|
|
18
|
+
path: params.exportPatchPath,
|
|
19
|
+
sha256: artifact.sha256,
|
|
20
|
+
bytes: artifact.bytes,
|
|
21
|
+
changedFiles: artifact.changedFiles,
|
|
22
|
+
isEmpty: artifact.isEmpty,
|
|
23
|
+
};
|
|
24
|
+
if (params.sweBenchPredictionsPath) {
|
|
25
|
+
if (!params.sweBenchInstanceId || !params.sweBenchModelName) {
|
|
26
|
+
throw new Error('SWE-bench predictions require instance id and model name.');
|
|
27
|
+
}
|
|
28
|
+
await fileAdapter.appendFile(params.sweBenchPredictionsPath, encodeSweBenchPredictionJsonl({
|
|
29
|
+
instanceId: params.sweBenchInstanceId,
|
|
30
|
+
modelNameOrPath: params.sweBenchModelName,
|
|
31
|
+
modelPatch: artifact.patch,
|
|
32
|
+
}));
|
|
33
|
+
params.result.benchmarkArtifact = {
|
|
34
|
+
provider: 'swe-bench',
|
|
35
|
+
instanceId: params.sweBenchInstanceId,
|
|
36
|
+
modelNameOrPath: params.sweBenchModelName,
|
|
37
|
+
predictionsPath: params.sweBenchPredictionsPath,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=benchmark-artifacts.js.map
|
|
@@ -57,6 +57,7 @@ export function handleEarlyRunCommandErrors(params) {
|
|
|
57
57
|
instruction: params.instruction,
|
|
58
58
|
message: text.cli.outputProfileRequiresStreamJson,
|
|
59
59
|
exitCode: 1,
|
|
60
|
+
errorCode: 'USAGE_ERROR',
|
|
60
61
|
});
|
|
61
62
|
}
|
|
62
63
|
return { ok: false, exitCode: 1 };
|
|
@@ -103,6 +104,28 @@ export function handleEarlyRunCommandErrors(params) {
|
|
|
103
104
|
}
|
|
104
105
|
return { ok: false, exitCode: 1 };
|
|
105
106
|
}
|
|
107
|
+
if (params.sweBenchPredictionsPath && !params.sweBenchInstanceId) {
|
|
108
|
+
getLogger().error(text.cli.sweBenchInstanceRequired);
|
|
109
|
+
if (params.headlessOutput) {
|
|
110
|
+
params.headlessErrorWriter.writeUsageError({
|
|
111
|
+
sessionId: params.sessionIdForOutput ?? params.resumeSessionId,
|
|
112
|
+
message: text.cli.sweBenchInstanceRequired,
|
|
113
|
+
instruction: params.instruction,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return { ok: false, exitCode: 1 };
|
|
117
|
+
}
|
|
118
|
+
if (params.sweBenchPredictionsPath && !params.sweBenchModelName) {
|
|
119
|
+
getLogger().error(text.cli.sweBenchModelRequired);
|
|
120
|
+
if (params.headlessOutput) {
|
|
121
|
+
params.headlessErrorWriter.writeUsageError({
|
|
122
|
+
sessionId: params.sessionIdForOutput ?? params.resumeSessionId,
|
|
123
|
+
message: text.cli.sweBenchModelRequired,
|
|
124
|
+
instruction: params.instruction,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return { ok: false, exitCode: 1 };
|
|
128
|
+
}
|
|
106
129
|
return { ok: true };
|
|
107
130
|
}
|
|
108
131
|
//# sourceMappingURL=early-errors.js.map
|