salmon-loop 0.2.13 → 0.2.16
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 +91 -71
- 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 +8 -3
- 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/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/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-run-handler.js +4 -2
- package/dist/core/facades/cli-run-persist-session.js +1 -0
- package/dist/core/facades/cli-serve.js +2 -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/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 +73 -0
- 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 +3 -2
- 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 +74 -51
- 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/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/languages/typescript/index.js +4 -1
- package/dist/locales/en.js +35 -2
- package/dist/utils/eol.js +1 -1
- package/package.json +13 -6
- 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
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { extractAndValidatePatch } from './diff-normalization.js';
|
|
2
|
+
const SALVAGEABLE_PATCH_CODES = new Set(['LLM_PATCH_NOT_UNIFIED_DIFF', 'LLM_PATCH_EMPTY']);
|
|
3
|
+
function errorMessage(error) {
|
|
4
|
+
if (error instanceof Error)
|
|
5
|
+
return error.message;
|
|
6
|
+
return String(error);
|
|
7
|
+
}
|
|
8
|
+
export function isSalvageablePatchError(error) {
|
|
9
|
+
if (!error || typeof error !== 'object')
|
|
10
|
+
return false;
|
|
11
|
+
const llmCode = error.llmCode;
|
|
12
|
+
return typeof llmCode === 'string' && SALVAGEABLE_PATCH_CODES.has(llmCode);
|
|
13
|
+
}
|
|
14
|
+
export async function salvagePatchDiff(args) {
|
|
15
|
+
if (!isSalvageablePatchError(args.initialError))
|
|
16
|
+
return null;
|
|
17
|
+
args.onAttempt?.({
|
|
18
|
+
reason: errorMessage(args.initialError),
|
|
19
|
+
badContentLength: args.rawContent.length,
|
|
20
|
+
});
|
|
21
|
+
const repaired = await args.repair({ badContent: args.rawContent });
|
|
22
|
+
const repairedContent = repaired.content || '';
|
|
23
|
+
try {
|
|
24
|
+
const validated = extractAndValidatePatch({
|
|
25
|
+
rawContent: repairedContent,
|
|
26
|
+
plannedFiles: args.plannedFiles,
|
|
27
|
+
});
|
|
28
|
+
args.onResult?.({
|
|
29
|
+
ok: true,
|
|
30
|
+
contentLength: repairedContent.length,
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
...validated,
|
|
34
|
+
rawContent: repairedContent,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (salvageError) {
|
|
38
|
+
args.onResult?.({
|
|
39
|
+
ok: false,
|
|
40
|
+
contentLength: repairedContent.length,
|
|
41
|
+
error: errorMessage(salvageError).slice(0, 400),
|
|
42
|
+
});
|
|
43
|
+
throw args.initialError;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=diff-salvage.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { LIMITS } from '../../../config/limits.js';
|
|
2
|
+
import { getPatchPrompt, getPatchSystemPrompt } from '../../../prompts/runtime.js';
|
|
3
|
+
import { SessionReplacementPreviewProvider } from '../../../session/replacement-preview-provider.js';
|
|
4
|
+
import { Phase } from '../../../types/runtime.js';
|
|
5
|
+
import { buildPhaseRequestEnvelope } from '../request-assembly.js';
|
|
6
|
+
export async function buildPatchPromptInput(args) {
|
|
7
|
+
const planStr = JSON.stringify(args.plan, null, 2);
|
|
8
|
+
const systemPrompt = await getPatchSystemPrompt(args.promptVisibleTools, {
|
|
9
|
+
plan: args.planRuntime,
|
|
10
|
+
});
|
|
11
|
+
const requestEnvelope = await buildPhaseRequestEnvelope({
|
|
12
|
+
phase: args.phase ?? Phase.PATCH,
|
|
13
|
+
defaultNamespace: 'patch',
|
|
14
|
+
context: args.context,
|
|
15
|
+
contextResult: args.contextResult,
|
|
16
|
+
cacheSharing: args.cacheSharing,
|
|
17
|
+
onCacheMismatch: args.onCacheMismatch,
|
|
18
|
+
systemPrompt,
|
|
19
|
+
buildUserPrompt: (contextPrompt) => getPatchPrompt(planStr, contextPrompt, LIMITS.maxFilesChanged, LIMITS.maxDiffLines, args.lastError),
|
|
20
|
+
conversationContext: args.conversationContext,
|
|
21
|
+
artifactHints: args.artifactHints,
|
|
22
|
+
previewProvider: new SessionReplacementPreviewProvider(args.replacementState),
|
|
23
|
+
toolCallingAudit: args.toolCallingAudit,
|
|
24
|
+
toolVisibility: args.toolVisibility,
|
|
25
|
+
extraAttachments: [
|
|
26
|
+
{
|
|
27
|
+
key: 'plan-json',
|
|
28
|
+
kind: 'plan',
|
|
29
|
+
label: 'Plan JSON',
|
|
30
|
+
content: planStr,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
planStr,
|
|
36
|
+
systemPrompt,
|
|
37
|
+
envelope: requestEnvelope.envelope,
|
|
38
|
+
baseMessages: requestEnvelope.baseMessages,
|
|
39
|
+
cacheSurface: requestEnvelope.cacheSurface,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=prompt-input.js.map
|
|
@@ -1,98 +1,116 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { GitAdapter } from '../../adapters/git/git-adapter.js';
|
|
3
|
-
import { LIMITS } from '../../config/limits.js';
|
|
1
|
+
import { supportsLlmStreaming } from '../../llm/capabilities.js';
|
|
4
2
|
import { repairToUnifiedDiff } from '../../llm/contracts/repair.js';
|
|
5
|
-
import { wrapPatchEmpty, wrapPatchInvalid, wrapPatchNotUnifiedDiff } from '../../llm/errors.js';
|
|
6
|
-
import { composeChatMessages } from '../../llm/message-composition.js';
|
|
7
3
|
import { emitLlmOutput } from '../../llm/output-policy.js';
|
|
8
|
-
import {
|
|
9
|
-
import { normalizeDiff, validateDiff } from '../../patch/diff.js';
|
|
10
|
-
import { getPatchPrompt, getPatchSystemPrompt } from '../../prompts/runtime.js';
|
|
4
|
+
import { recordAuditEvent } from '../../observability/audit-trail.js';
|
|
11
5
|
import { chatWithTools, chatWithToolsStreaming } from '../../tools/session.js';
|
|
12
|
-
import {
|
|
6
|
+
import { resolveVisibleToolSpecs } from '../../tools/tool-visibility.js';
|
|
13
7
|
import { Phase } from '../../types/runtime.js';
|
|
14
8
|
import { resolveLlmToolCallingPolicy } from '../dsl/llm-strategy.js';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
import { checkPatchApplies } from './patch/apply-check.js';
|
|
10
|
+
import { extractAndValidatePatch } from './patch/diff-normalization.js';
|
|
11
|
+
import { salvagePatchDiff } from './patch/diff-salvage.js';
|
|
12
|
+
import { buildPatchPromptInput } from './patch/prompt-input.js';
|
|
13
|
+
import { buildPhaseToolRuntimeContext, buildToolVisibilityRuntime } from './tool-runtime.js';
|
|
14
|
+
const recordPatchSalvageAttempt = (args) => {
|
|
15
|
+
recordAuditEvent('patch.salvage.attempt', {
|
|
16
|
+
reason: args.reason,
|
|
17
|
+
badContentLength: args.badContentLength,
|
|
18
|
+
toolsDisabled: true,
|
|
19
|
+
}, { source: 'patch', severity: 'low', scope: 'session', phase: 'PATCH' });
|
|
20
|
+
};
|
|
21
|
+
const recordPatchSalvageResult = (args) => {
|
|
22
|
+
recordAuditEvent('patch.salvage.result', {
|
|
23
|
+
ok: args.ok,
|
|
24
|
+
contentLength: args.contentLength,
|
|
25
|
+
error: args.error,
|
|
26
|
+
}, {
|
|
27
|
+
source: 'patch',
|
|
28
|
+
severity: args.ok ? 'low' : 'medium',
|
|
29
|
+
scope: 'session',
|
|
30
|
+
phase: 'PATCH',
|
|
21
31
|
});
|
|
22
|
-
}
|
|
32
|
+
};
|
|
23
33
|
export const generatePatch = async (ctx) => {
|
|
24
34
|
const toolstack = ctx.toolstack;
|
|
25
35
|
const toolPolicy = resolveLlmToolCallingPolicy(Phase.PATCH, ctx.options.llm);
|
|
26
36
|
// Backwards-compatible fallback for non-tool-capable LLMs.
|
|
27
37
|
if (!toolstack || !toolPolicy.enabled) {
|
|
28
|
-
const
|
|
38
|
+
const legacyPatch = await ctx.options.llm.createPatch(ctx.context, ctx.plan, ctx.lastError, ctx.options.signal);
|
|
29
39
|
emitLlmOutput({
|
|
30
40
|
emit: ctx.emit,
|
|
31
41
|
policy: ctx.options.llmOutput,
|
|
32
42
|
kind: 'patch',
|
|
33
43
|
step: 'PATCH',
|
|
34
|
-
content:
|
|
44
|
+
content: legacyPatch,
|
|
45
|
+
});
|
|
46
|
+
const validated = extractAndValidatePatch({
|
|
47
|
+
rawContent: legacyPatch,
|
|
48
|
+
plannedFiles: ctx.plan.files,
|
|
35
49
|
});
|
|
36
|
-
const normalizedPatch = normalizeDiff(patch);
|
|
37
|
-
let diffMeta;
|
|
38
|
-
try {
|
|
39
|
-
diffMeta = validateDiff(normalizedPatch);
|
|
40
|
-
}
|
|
41
|
-
catch (e) {
|
|
42
|
-
if (e instanceof DiffValidationError) {
|
|
43
|
-
if (e.message === text.diff.notUnifiedFormat)
|
|
44
|
-
throw wrapPatchNotUnifiedDiff();
|
|
45
|
-
if (e.message.startsWith(text.llm.patchEmpty()))
|
|
46
|
-
throw wrapPatchEmpty();
|
|
47
|
-
throw wrapPatchInvalid(e.message);
|
|
48
|
-
}
|
|
49
|
-
throw e;
|
|
50
|
-
}
|
|
51
50
|
ctx.emit({
|
|
52
51
|
type: 'log',
|
|
53
52
|
level: 'debug',
|
|
54
|
-
message: `Patch generated: ${diffMeta.changedFiles.length} files changed`,
|
|
53
|
+
message: `Patch generated: ${validated.diffMeta.changedFiles.length} files changed`,
|
|
55
54
|
timestamp: new Date(),
|
|
56
55
|
});
|
|
57
56
|
return {
|
|
58
57
|
...ctx,
|
|
59
|
-
diff: normalizedPatch,
|
|
60
|
-
diffMeta,
|
|
61
|
-
changedFiles: diffMeta.changedFiles,
|
|
58
|
+
diff: validated.normalizedPatch,
|
|
59
|
+
diffMeta: validated.diffMeta,
|
|
60
|
+
changedFiles: validated.diffMeta.changedFiles,
|
|
62
61
|
};
|
|
63
62
|
}
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
const toolVisibility = buildToolVisibilityRuntime(ctx);
|
|
64
|
+
const promptVisibleTools = resolveVisibleToolSpecs({
|
|
65
|
+
phase: Phase.PATCH,
|
|
66
|
+
toolstack,
|
|
67
|
+
worktreeRoot: ctx.workspace.strategy === 'worktree' ? ctx.workspace.workPath : undefined,
|
|
68
|
+
flowMode: ctx.mode,
|
|
69
|
+
runtime: toolVisibility,
|
|
70
|
+
});
|
|
71
|
+
const patchPromptInput = await buildPatchPromptInput({
|
|
72
|
+
context: ctx.context,
|
|
73
|
+
contextResult: ctx.contextResult,
|
|
74
|
+
plan: ctx.plan,
|
|
75
|
+
planRuntime: ctx.planRuntime,
|
|
76
|
+
lastError: ctx.lastError,
|
|
77
|
+
promptVisibleTools,
|
|
78
|
+
toolVisibility: {
|
|
79
|
+
toolstack,
|
|
80
|
+
runtime: toolVisibility,
|
|
81
|
+
worktreeRoot: ctx.workspace.strategy === 'worktree' ? ctx.workspace.workPath : undefined,
|
|
82
|
+
flowMode: ctx.mode,
|
|
83
|
+
},
|
|
84
|
+
phase: Phase.PATCH,
|
|
85
|
+
cacheSharing: ctx.cacheSharing,
|
|
86
|
+
onCacheMismatch: (mismatch) => {
|
|
87
|
+
recordAuditEvent('request.cache_sharing_hash_mismatch', mismatch, {
|
|
88
|
+
source: 'llm',
|
|
89
|
+
severity: 'low',
|
|
90
|
+
scope: 'session',
|
|
91
|
+
phase: Phase.PATCH,
|
|
92
|
+
});
|
|
93
|
+
},
|
|
70
94
|
conversationContext: ctx.options.conversationContext,
|
|
95
|
+
artifactHints: ctx.artifactHints,
|
|
96
|
+
replacementState: ctx.replacementState,
|
|
97
|
+
toolCallingAudit: ctx.toolCallingAudit,
|
|
71
98
|
});
|
|
72
|
-
const
|
|
99
|
+
const { cacheSurface, envelope, baseMessages } = patchPromptInput;
|
|
100
|
+
const supportsStreaming = supportsLlmStreaming(ctx.options.llm, Phase.PATCH);
|
|
73
101
|
const llmOutput = {
|
|
74
102
|
policy: ctx.options.llmOutput,
|
|
75
103
|
kind: 'patch',
|
|
76
104
|
step: 'PATCH',
|
|
77
105
|
};
|
|
78
106
|
const response = await (supportsStreaming ? chatWithToolsStreaming : chatWithTools)(baseMessages, {
|
|
107
|
+
providerHints: envelope.providerHints,
|
|
79
108
|
signal: ctx.options.signal,
|
|
80
109
|
}, {
|
|
81
110
|
phase: Phase.PATCH,
|
|
82
111
|
llm: ctx.options.llm,
|
|
83
|
-
runtime:
|
|
84
|
-
|
|
85
|
-
persistenceRoot: ctx.workspace.baseRepoPath || ctx.workspace.workPath,
|
|
86
|
-
worktreeRoot: ctx.workspace.strategy === 'worktree' ? ctx.workspace.workPath : undefined,
|
|
87
|
-
attemptId: ctx.attempt ?? 1,
|
|
88
|
-
dryRun: Boolean(ctx.options?.dryRun),
|
|
89
|
-
llm: ctx.options.llm,
|
|
90
|
-
model: ctx.options.llm.getModelId?.() || process.env.SALMONLOOP_MODEL || process.env.S8P_MODEL,
|
|
91
|
-
userInputProvider: ctx.options.userInputProvider,
|
|
92
|
-
agentKind: ctx.options.agentKind ?? 'primary',
|
|
93
|
-
languagePlugins: ctx.options.languagePlugins,
|
|
94
|
-
subAgentController: ctx.options.subAgentController,
|
|
95
|
-
},
|
|
112
|
+
runtime: buildPhaseToolRuntimeContext(ctx, Phase.PATCH, cacheSurface),
|
|
113
|
+
toolVisibility,
|
|
96
114
|
toolstack,
|
|
97
115
|
eventPayload: ctx.options.eventPayload,
|
|
98
116
|
toolCallingAudit: {
|
|
@@ -107,114 +125,55 @@ export const generatePatch = async (ctx) => {
|
|
|
107
125
|
emit: (event) => ctx.emit({ ...event, timestamp: event.timestamp ?? new Date() }),
|
|
108
126
|
});
|
|
109
127
|
let rawContent = response.content || '';
|
|
110
|
-
let
|
|
111
|
-
let normalizedPatch = '';
|
|
112
|
-
let diffMeta;
|
|
113
|
-
const validateWithLlmErrors = (diffText) => {
|
|
114
|
-
const normalized = normalizeDiff(diffText);
|
|
115
|
-
try {
|
|
116
|
-
return validateDiff(normalized);
|
|
117
|
-
}
|
|
118
|
-
catch (e) {
|
|
119
|
-
if (e instanceof DiffValidationError) {
|
|
120
|
-
if (e.message === text.diff.notUnifiedFormat)
|
|
121
|
-
throw wrapPatchNotUnifiedDiff();
|
|
122
|
-
if (e.message.startsWith(text.llm.patchEmpty()))
|
|
123
|
-
throw wrapPatchEmpty();
|
|
124
|
-
throw wrapPatchInvalid(e.message);
|
|
125
|
-
}
|
|
126
|
-
throw e;
|
|
127
|
-
}
|
|
128
|
-
};
|
|
128
|
+
let parsedPatch;
|
|
129
129
|
try {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
parsedPatch = extractAndValidatePatch({
|
|
131
|
+
rawContent,
|
|
132
|
+
plannedFiles: ctx.plan.files,
|
|
133
|
+
});
|
|
133
134
|
}
|
|
134
135
|
catch (e) {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
chatOptions: { signal: ctx.options.signal },
|
|
146
|
-
badContent: rawContent,
|
|
147
|
-
reason: asLlmError.message,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
throw asLlmError;
|
|
152
|
-
}
|
|
153
|
-
rawContent = repaired.content || '';
|
|
154
|
-
patch = extractUnifiedDiffFromLLMContent(rawContent);
|
|
155
|
-
normalizedPatch = normalizeDiff(patch);
|
|
156
|
-
diffMeta = validateWithLlmErrors(patch);
|
|
157
|
-
emitLlmOutput({
|
|
158
|
-
emit: ctx.emit,
|
|
159
|
-
policy: ctx.options.llmOutput,
|
|
160
|
-
kind: 'patch',
|
|
161
|
-
step: 'PATCH',
|
|
162
|
-
content: patch,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
throw asLlmError;
|
|
136
|
+
const salvaged = await salvagePatchDiff({
|
|
137
|
+
initialError: e,
|
|
138
|
+
rawContent,
|
|
139
|
+
plannedFiles: ctx.plan.files,
|
|
140
|
+
repair: ({ badContent }) => repairToUnifiedDiff({ badContent }),
|
|
141
|
+
onAttempt: recordPatchSalvageAttempt,
|
|
142
|
+
onResult: recordPatchSalvageResult,
|
|
143
|
+
});
|
|
144
|
+
if (!salvaged) {
|
|
145
|
+
throw e;
|
|
167
146
|
}
|
|
147
|
+
rawContent = salvaged.rawContent;
|
|
148
|
+
parsedPatch = salvaged;
|
|
149
|
+
emitLlmOutput({
|
|
150
|
+
emit: ctx.emit,
|
|
151
|
+
policy: ctx.options.llmOutput,
|
|
152
|
+
kind: 'patch',
|
|
153
|
+
step: 'PATCH',
|
|
154
|
+
content: salvaged.patch,
|
|
155
|
+
});
|
|
168
156
|
}
|
|
169
157
|
// Deterministic contract: the patch should be applicable (git apply --check) before moving on.
|
|
170
158
|
// When this fails, attempt a single LLM repair pass using the git error message as feedback.
|
|
171
159
|
const applyCheck = await checkPatchApplies({
|
|
172
160
|
repoRoot: ctx.workspace.workPath,
|
|
173
|
-
diff: normalizedPatch,
|
|
161
|
+
diff: parsedPatch.normalizedPatch,
|
|
174
162
|
});
|
|
175
163
|
if (!applyCheck.ok) {
|
|
176
|
-
|
|
177
|
-
try {
|
|
178
|
-
const repaired = await repairToUnifiedDiff({
|
|
179
|
-
llm: ctx.options.llm,
|
|
180
|
-
baseMessages,
|
|
181
|
-
chatOptions: { signal: ctx.options.signal },
|
|
182
|
-
badContent: patch,
|
|
183
|
-
reason: `Patch does not apply cleanly: ${details.slice(0, 1200)}`,
|
|
184
|
-
});
|
|
185
|
-
rawContent = repaired.content || '';
|
|
186
|
-
patch = extractUnifiedDiffFromLLMContent(rawContent);
|
|
187
|
-
normalizedPatch = normalizeDiff(patch);
|
|
188
|
-
diffMeta = validateWithLlmErrors(patch);
|
|
189
|
-
const applyCheck2 = await checkPatchApplies({
|
|
190
|
-
repoRoot: ctx.workspace.workPath,
|
|
191
|
-
diff: normalizedPatch,
|
|
192
|
-
});
|
|
193
|
-
if (applyCheck2.ok) {
|
|
194
|
-
emitLlmOutput({
|
|
195
|
-
emit: ctx.emit,
|
|
196
|
-
policy: ctx.options.llmOutput,
|
|
197
|
-
kind: 'patch',
|
|
198
|
-
step: 'PATCH',
|
|
199
|
-
content: patch,
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
catch {
|
|
204
|
-
// Preserve the original patch and let VALIDATE provide the canonical failure if needed.
|
|
205
|
-
}
|
|
164
|
+
// Preserve the original patch and let VALIDATE provide the canonical failure if needed.
|
|
206
165
|
}
|
|
207
166
|
ctx.emit({
|
|
208
167
|
type: 'log',
|
|
209
168
|
level: 'debug',
|
|
210
|
-
message: `Patch generated: ${diffMeta.changedFiles.length} files changed`,
|
|
169
|
+
message: `Patch generated: ${parsedPatch.diffMeta.changedFiles.length} files changed`,
|
|
211
170
|
timestamp: new Date(),
|
|
212
171
|
});
|
|
213
172
|
return {
|
|
214
173
|
...ctx,
|
|
215
|
-
diff: normalizedPatch,
|
|
216
|
-
diffMeta,
|
|
217
|
-
changedFiles: diffMeta.changedFiles,
|
|
174
|
+
diff: parsedPatch.normalizedPatch,
|
|
175
|
+
diffMeta: parsedPatch.diffMeta,
|
|
176
|
+
changedFiles: parsedPatch.diffMeta.changedFiles,
|
|
218
177
|
};
|
|
219
178
|
};
|
|
220
179
|
//# sourceMappingURL=patch.js.map
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { text } from '../../../locales/index.js';
|
|
2
2
|
import { LIMITS } from '../../config/limits.js';
|
|
3
|
+
import { supportsLlmStreaming } from '../../llm/capabilities.js';
|
|
3
4
|
import { repairToJsonObject } from '../../llm/contracts/repair.js';
|
|
4
5
|
import { sanitizeError } from '../../llm/errors.js';
|
|
5
|
-
import { composeChatMessages } from '../../llm/message-composition.js';
|
|
6
6
|
import { emitLlmOutput } from '../../llm/output-policy.js';
|
|
7
|
-
import {
|
|
7
|
+
import { parsePlanFromLLMContent } from '../../llm/utils.js';
|
|
8
|
+
import { recordAuditEvent } from '../../observability/audit-trail.js';
|
|
8
9
|
import { logIgnoredError } from '../../observability/ignored-error.js';
|
|
9
10
|
import { readPlan, updatePlan } from '../../plan/index.js';
|
|
10
11
|
import { getPlanPrompt, getPlanSystemPrompt } from '../../prompts/runtime.js';
|
|
12
|
+
import { SessionReplacementPreviewProvider } from '../../session/replacement-preview-provider.js';
|
|
11
13
|
import { chatWithTools, chatWithToolsStreaming } from '../../tools/session.js';
|
|
14
|
+
import { resolveVisibleToolSpecs } from '../../tools/tool-visibility.js';
|
|
12
15
|
import { Phase } from '../../types/runtime.js';
|
|
13
16
|
import { resolveLlmToolCallingPolicy } from '../dsl/llm-strategy.js';
|
|
17
|
+
import { buildPhaseRequestEnvelope } from './request-assembly.js';
|
|
18
|
+
import { buildPhaseToolRuntimeContext, buildToolVisibilityRuntime } from './tool-runtime.js';
|
|
14
19
|
function sanitizeSubtaskText(raw) {
|
|
15
20
|
const oneLine = String(raw ?? '')
|
|
16
21
|
.replace(/\r?\n/g, ' ')
|
|
@@ -27,6 +32,25 @@ function sanitizeSubtaskText(raw) {
|
|
|
27
32
|
return withoutComments;
|
|
28
33
|
return withoutComments.slice(0, maxLen - 1).trimEnd() + '…';
|
|
29
34
|
}
|
|
35
|
+
function recordPlanRepairAttempt(args) {
|
|
36
|
+
recordAuditEvent('plan.repair.attempt', {
|
|
37
|
+
reason: args.reason,
|
|
38
|
+
badContentLength: args.badContentLength,
|
|
39
|
+
toolsDisabled: true,
|
|
40
|
+
}, { source: 'plan', severity: 'low', scope: 'session', phase: 'PLAN' });
|
|
41
|
+
}
|
|
42
|
+
function recordPlanRepairResult(args) {
|
|
43
|
+
recordAuditEvent('plan.repair.result', {
|
|
44
|
+
ok: args.ok,
|
|
45
|
+
contentLength: args.contentLength,
|
|
46
|
+
error: args.error,
|
|
47
|
+
}, {
|
|
48
|
+
source: 'plan',
|
|
49
|
+
severity: args.ok ? 'low' : 'medium',
|
|
50
|
+
scope: 'session',
|
|
51
|
+
phase: 'PLAN',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
30
54
|
async function hydrateRuntimePlanTodos(ctx, plan) {
|
|
31
55
|
const runtime = ctx.planRuntime;
|
|
32
56
|
if (!runtime?.sessionId)
|
|
@@ -64,6 +88,15 @@ async function hydrateRuntimePlanTodos(ctx, plan) {
|
|
|
64
88
|
},
|
|
65
89
|
});
|
|
66
90
|
}
|
|
91
|
+
export function hasSuccessfulPlanUpdateDuringPlan(ctx) {
|
|
92
|
+
const auditEntries = ctx.toolCallingAudit;
|
|
93
|
+
if (!Array.isArray(auditEntries) || auditEntries.length === 0)
|
|
94
|
+
return false;
|
|
95
|
+
return auditEntries.some((entry) => entry.phase === Phase.PLAN &&
|
|
96
|
+
entry.toolName === 'plan.update' &&
|
|
97
|
+
entry.toolResultStatus === 'ok' &&
|
|
98
|
+
entry.toolResultOutputOk === true);
|
|
99
|
+
}
|
|
67
100
|
export const generatePlan = async (ctx) => {
|
|
68
101
|
const toolstack = ctx.toolstack;
|
|
69
102
|
const toolPolicy = resolveLlmToolCallingPolicy(Phase.PLAN, ctx.options.llm);
|
|
@@ -83,21 +116,53 @@ export const generatePlan = async (ctx) => {
|
|
|
83
116
|
message: `Plan generated: ${plan.goal}`,
|
|
84
117
|
timestamp: new Date(),
|
|
85
118
|
});
|
|
86
|
-
|
|
87
|
-
|
|
119
|
+
if (ctx.planRuntime?.sessionId) {
|
|
120
|
+
// Transitional fallback: legacy/non-tool PLAN runs may hydrate runtime subtasks from final JSON.
|
|
121
|
+
await hydrateRuntimePlanTodos(ctx, plan).catch((error) => logIgnoredError('[Plan] hydrate runtime plan todos (legacy)', error));
|
|
122
|
+
}
|
|
88
123
|
return {
|
|
89
124
|
...ctx,
|
|
90
125
|
plan,
|
|
91
126
|
};
|
|
92
127
|
}
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
128
|
+
const toolVisibility = buildToolVisibilityRuntime(ctx);
|
|
129
|
+
const promptVisibleTools = resolveVisibleToolSpecs({
|
|
130
|
+
phase: Phase.PLAN,
|
|
131
|
+
toolstack,
|
|
132
|
+
worktreeRoot: ctx.workspace.strategy === 'worktree' ? ctx.workspace.workPath : undefined,
|
|
133
|
+
flowMode: ctx.mode,
|
|
134
|
+
runtime: toolVisibility,
|
|
135
|
+
});
|
|
136
|
+
const systemPrompt = await getPlanSystemPrompt(promptVisibleTools, toolVisibility);
|
|
137
|
+
const requestEnvelope = await buildPhaseRequestEnvelope({
|
|
138
|
+
phase: Phase.PLAN,
|
|
139
|
+
defaultNamespace: 'plan',
|
|
140
|
+
context: ctx.context,
|
|
141
|
+
contextResult: ctx.contextResult,
|
|
142
|
+
cacheSharing: ctx.cacheSharing,
|
|
143
|
+
onCacheMismatch: (mismatch) => {
|
|
144
|
+
recordAuditEvent('request.cache_sharing_hash_mismatch', mismatch, {
|
|
145
|
+
source: 'llm',
|
|
146
|
+
severity: 'low',
|
|
147
|
+
scope: 'session',
|
|
148
|
+
phase: Phase.PLAN,
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
systemPrompt,
|
|
152
|
+
buildUserPrompt: (contextPrompt) => getPlanPrompt(contextPrompt, ctx.options.instruction, LIMITS.maxFilesChanged, ctx.lastError),
|
|
98
153
|
conversationContext: ctx.options.conversationContext,
|
|
154
|
+
artifactHints: ctx.artifactHints,
|
|
155
|
+
toolCallingAudit: ctx.toolCallingAudit,
|
|
156
|
+
previewProvider: new SessionReplacementPreviewProvider(ctx.replacementState),
|
|
157
|
+
toolVisibility: {
|
|
158
|
+
toolstack,
|
|
159
|
+
runtime: toolVisibility,
|
|
160
|
+
worktreeRoot: ctx.workspace.strategy === 'worktree' ? ctx.workspace.workPath : undefined,
|
|
161
|
+
flowMode: ctx.mode,
|
|
162
|
+
},
|
|
99
163
|
});
|
|
100
|
-
const
|
|
164
|
+
const { cacheSurface, envelope, baseMessages } = requestEnvelope;
|
|
165
|
+
const supportsStreaming = supportsLlmStreaming(ctx.options.llm, Phase.PLAN);
|
|
101
166
|
const llmOutput = {
|
|
102
167
|
policy: ctx.options.llmOutput,
|
|
103
168
|
kind: 'plan',
|
|
@@ -105,23 +170,13 @@ export const generatePlan = async (ctx) => {
|
|
|
105
170
|
};
|
|
106
171
|
const response = await (supportsStreaming ? chatWithToolsStreaming : chatWithTools)(baseMessages, {
|
|
107
172
|
responseFormat: 'json_object',
|
|
173
|
+
providerHints: envelope.providerHints,
|
|
108
174
|
signal: ctx.options.signal,
|
|
109
175
|
}, {
|
|
110
176
|
phase: Phase.PLAN,
|
|
111
177
|
llm: ctx.options.llm,
|
|
112
|
-
runtime:
|
|
113
|
-
|
|
114
|
-
persistenceRoot: ctx.workspace.baseRepoPath || ctx.workspace.workPath,
|
|
115
|
-
worktreeRoot: ctx.workspace.strategy === 'worktree' ? ctx.workspace.workPath : undefined,
|
|
116
|
-
attemptId: ctx.attempt ?? 1,
|
|
117
|
-
dryRun: Boolean(ctx.options?.dryRun),
|
|
118
|
-
llm: ctx.options.llm,
|
|
119
|
-
model: ctx.options.llm.getModelId?.() || process.env.SALMONLOOP_MODEL || process.env.S8P_MODEL,
|
|
120
|
-
userInputProvider: ctx.options.userInputProvider,
|
|
121
|
-
agentKind: ctx.options.agentKind ?? 'primary',
|
|
122
|
-
languagePlugins: ctx.options.languagePlugins,
|
|
123
|
-
subAgentController: ctx.options.subAgentController,
|
|
124
|
-
},
|
|
178
|
+
runtime: buildPhaseToolRuntimeContext(ctx, Phase.PLAN, cacheSurface),
|
|
179
|
+
toolVisibility,
|
|
125
180
|
toolstack,
|
|
126
181
|
eventPayload: ctx.options.eventPayload,
|
|
127
182
|
toolCallingAudit: {
|
|
@@ -145,6 +200,10 @@ export const generatePlan = async (ctx) => {
|
|
|
145
200
|
plan = parsePlanFromLLMContent(finalContent);
|
|
146
201
|
}
|
|
147
202
|
catch (e) {
|
|
203
|
+
recordPlanRepairAttempt({
|
|
204
|
+
reason: sanitizeError(e),
|
|
205
|
+
badContentLength: finalContent.length,
|
|
206
|
+
});
|
|
148
207
|
let repaired;
|
|
149
208
|
try {
|
|
150
209
|
repaired = await repairToJsonObject({
|
|
@@ -156,6 +215,11 @@ export const generatePlan = async (ctx) => {
|
|
|
156
215
|
});
|
|
157
216
|
}
|
|
158
217
|
catch (repairError) {
|
|
218
|
+
recordPlanRepairResult({
|
|
219
|
+
ok: false,
|
|
220
|
+
contentLength: 0,
|
|
221
|
+
error: sanitizeError(repairError).slice(0, 400),
|
|
222
|
+
});
|
|
159
223
|
throw new Error(text.llm.planParseFailed(finalContent, sanitizeError(repairError)));
|
|
160
224
|
}
|
|
161
225
|
finalContent = repaired.content || '';
|
|
@@ -172,8 +236,17 @@ export const generatePlan = async (ctx) => {
|
|
|
172
236
|
plan = parsePlanFromLLMContent(finalContent);
|
|
173
237
|
}
|
|
174
238
|
catch (e2) {
|
|
239
|
+
recordPlanRepairResult({
|
|
240
|
+
ok: false,
|
|
241
|
+
contentLength: finalContent.length,
|
|
242
|
+
error: sanitizeError(e2).slice(0, 400),
|
|
243
|
+
});
|
|
175
244
|
throw new Error(text.llm.planParseFailed(finalContent, sanitizeError(e2)));
|
|
176
245
|
}
|
|
246
|
+
recordPlanRepairResult({
|
|
247
|
+
ok: true,
|
|
248
|
+
contentLength: finalContent.length,
|
|
249
|
+
});
|
|
177
250
|
}
|
|
178
251
|
ctx.emit({
|
|
179
252
|
type: 'log',
|
|
@@ -181,8 +254,11 @@ export const generatePlan = async (ctx) => {
|
|
|
181
254
|
message: `Plan generated: ${plan.goal}`,
|
|
182
255
|
timestamp: new Date(),
|
|
183
256
|
});
|
|
184
|
-
|
|
185
|
-
|
|
257
|
+
const hasModelPlanUpdate = hasSuccessfulPlanUpdateDuringPlan(ctx);
|
|
258
|
+
if (ctx.planRuntime?.sessionId && !hasModelPlanUpdate) {
|
|
259
|
+
// Transitional fallback: only hydrate when PLAN did not successfully persist via plan.update.
|
|
260
|
+
await hydrateRuntimePlanTodos(ctx, plan).catch((error) => logIgnoredError('[Plan] hydrate runtime plan todos (tools)', error));
|
|
261
|
+
}
|
|
186
262
|
return {
|
|
187
263
|
...ctx,
|
|
188
264
|
plan,
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { text } from '../../../locales/index.js';
|
|
2
2
|
import { recordAuditEvent } from '../../observability/audit-trail.js';
|
|
3
|
+
import { resolveExecutionProfile } from '../../runtime/execution-profile.js';
|
|
3
4
|
import { createStandardToolstack } from '../../tools/loader.js';
|
|
4
|
-
import { Phase } from '../../types/runtime.js';
|
|
5
5
|
import { preflight } from '../../verification/runner.js';
|
|
6
6
|
import { resolveLlmToolCallingPolicy } from '../dsl/llm-strategy.js';
|
|
7
7
|
export const runPreflight = async (ctx) => {
|
|
8
|
+
const executionProfile = resolveExecutionProfile(ctx.mode);
|
|
8
9
|
const result = await preflight(ctx.workspace, ctx.emit, {
|
|
9
|
-
ignoreDirty:
|
|
10
|
-
ctx.mode === 'research' ||
|
|
11
|
-
ctx.mode === 'answer' ||
|
|
12
|
-
ctx.options.permissionMode === 'yolo',
|
|
10
|
+
ignoreDirty: executionProfile.ignoreDirtyPreflight,
|
|
13
11
|
});
|
|
14
12
|
if (!result.ok) {
|
|
15
13
|
const reason = result.reason || text.loop.preflightFailedNotGit;
|
|
@@ -29,7 +27,8 @@ export const runPreflight = async (ctx) => {
|
|
|
29
27
|
message: text.loop.preflightPassed,
|
|
30
28
|
timestamp: new Date(),
|
|
31
29
|
});
|
|
32
|
-
const toolstack = resolveLlmToolCallingPolicy(
|
|
30
|
+
const toolstack = resolveLlmToolCallingPolicy(executionProfile.entryPhase, ctx.options.llm)
|
|
31
|
+
.enabled
|
|
33
32
|
? await createStandardToolstack({
|
|
34
33
|
repoRoot: ctx.workspace.workPath,
|
|
35
34
|
persistenceRoot: ctx.workspace.baseRepoPath || ctx.workspace.workPath,
|