salmon-loop 0.4.1 → 0.5.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/authorization/provider.js +2 -10
- package/dist/cli/commands/config.js +2 -2
- package/dist/cli/commands/mode.js +2 -2
- package/dist/cli/commands/run/handler.js +3 -1
- package/dist/cli/commands/run/loop-params.js +1 -0
- package/dist/cli/commands/run/runtime-options.js +3 -1
- package/dist/cli/config.js +0 -8
- package/dist/cli/locales/en.js +2 -2
- package/dist/cli/reporters/standard.js +10 -0
- package/dist/core/adapters/fs/file-adapter.js +3 -1
- package/dist/core/adapters/git/git-adapter.js +6 -3
- package/dist/core/adapters/git/git-runner.js +5 -2
- package/dist/core/adapters/git/lock-manager.js +7 -4
- package/dist/core/checkpoint-domain/manifest-store.js +21 -13
- package/dist/core/checkpoint-domain/service.js +3 -1
- package/dist/core/config/limits.js +1 -1
- package/dist/core/config/model-pricing.js +61 -0
- package/dist/core/context/ast/skeleton-extractor.js +225 -0
- package/dist/core/context/ast/source-outline.js +24 -1
- package/dist/core/context/budget/dynamic-adjuster.js +20 -5
- package/dist/core/context/builder.js +7 -3
- package/dist/core/context/cache/store-factory.js +3 -1
- package/dist/core/context/dependencies.js +2 -1
- package/dist/core/context/effectiveness/persistence.js +50 -0
- package/dist/core/context/effectiveness/tracker.js +24 -0
- package/dist/core/context/gatherers/architecture-gatherer.js +2 -1
- package/dist/core/context/gatherers/artifact-gatherer.js +7 -4
- package/dist/core/context/gatherers/ast-gatherer.js +30 -28
- package/dist/core/context/gatherers/git-history-gatherer.js +3 -1
- package/dist/core/context/gatherers/knowledge-gatherer.js +18 -2
- package/dist/core/context/gatherers/metadata-gatherer.js +12 -7
- package/dist/core/context/gatherers/ripgrep-gatherer.js +6 -3
- package/dist/core/context/service.js +4 -2
- package/dist/core/context/steps/context-gather.js +14 -3
- package/dist/core/context/steps/context-targets.js +1 -0
- package/dist/core/context/targeting/target-resolver.js +29 -11
- package/dist/core/context/token/cache.js +5 -2
- package/dist/core/context/truncation/strategies/json.js +5 -2
- package/dist/core/context/truncation/type-detector.js +3 -1
- package/dist/core/extensions/paths.js +2 -2
- package/dist/core/facades/cli-authorization-provider.js +1 -0
- package/dist/core/feedback/parsers.js +290 -1
- package/dist/core/grizzco/dsl/llm-strategy.js +1 -1
- package/dist/core/grizzco/engine/observability/loop-telemetry.js +5 -2
- package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +15 -3
- package/dist/core/grizzco/engine/transaction/attempt-failure.js +44 -20
- package/dist/core/grizzco/engine/transaction/transaction-runner.js +40 -34
- package/dist/core/grizzco/execution/RejectionManager.js +7 -5
- package/dist/core/grizzco/runtime/apply-back-runtime.js +3 -1
- package/dist/core/grizzco/services/implementations/default/GitConfigService.js +2 -1
- package/dist/core/grizzco/steps/autopilot.js +21 -32
- package/dist/core/grizzco/steps/explore.js +5 -2
- package/dist/core/grizzco/steps/generateReview.js +3 -1
- package/dist/core/grizzco/steps/research.js +3 -1
- package/dist/core/grizzco/steps/verify.js +7 -1
- package/dist/core/grizzco/validation/AstValidationService.js +3 -1
- package/dist/core/history/input-history.js +3 -1
- package/dist/core/intent/chat-intent.js +3 -1
- package/dist/core/llm/ai-sdk/message-mapper.js +13 -8
- package/dist/core/llm/ai-sdk/request-params.js +1 -3
- package/dist/core/llm/ai-sdk/retry-classifier.js +12 -4
- package/dist/core/llm/ai-sdk/retry-executor.js +1 -1
- package/dist/core/llm/errors.js +5 -4
- package/dist/core/llm/retry-utils.js +8 -2
- package/dist/core/llm/stream-utils.js +5 -3
- package/dist/core/llm/sub-agent-factory.js +3 -0
- package/dist/core/mcp/bridge/resource-context-provider.js +3 -1
- package/dist/core/mcp/catalog/discovery.js +3 -1
- package/dist/core/mcp/client/connection-manager.js +4 -2
- package/dist/core/mcp/client/transport-factory.js +7 -3
- package/dist/core/observability/audit-file.js +2 -1
- package/dist/core/observability/audit-trail.js +3 -1
- package/dist/core/observability/logger.js +2 -1
- package/dist/core/observability/monitor.js +24 -0
- package/dist/core/observability/run-outcome-reporter.js +1 -0
- package/dist/core/permission-gate/default-gate.js +5 -8
- package/dist/core/plan/storage.js +7 -4
- package/dist/core/plugin/loader.js +3 -1
- package/dist/core/prompts/registry.js +1 -1
- package/dist/core/prompts/runtime.js +3 -1
- package/dist/core/prompts/templates/system/autopilot_system.hbs +28 -4
- package/dist/core/protocols/a2a/sdk/executor.js +3 -1
- package/dist/core/protocols/a2a/sdk/server.js +3 -1
- package/dist/core/protocols/acp/acp-command-runner.js +7 -6
- package/dist/core/protocols/acp/acp-session-persistence.js +13 -10
- package/dist/core/protocols/acp/formal-agent.js +3 -2
- package/dist/core/protocols/acp/permission-provider.js +3 -2
- package/dist/core/reflection/engine.js +114 -14
- package/dist/core/runtime/batch-runner.js +81 -0
- package/dist/core/runtime/initialize.js +2 -1
- package/dist/core/runtime/loop-finalize.js +3 -0
- package/dist/core/runtime/loop-session-runner.js +5 -0
- package/dist/core/runtime/loop.js +4 -0
- package/dist/core/runtime/paths.js +9 -6
- package/dist/core/runtime/spawn-interactive.js +5 -4
- package/dist/core/security/redaction.js +3 -2
- package/dist/core/session/compression.js +3 -1
- package/dist/core/session/manager.js +2 -1
- package/dist/core/session/pruning-strategy.js +2 -1
- package/dist/core/session/token-tracker.js +11 -4
- package/dist/core/skills/permissions.js +2 -2
- package/dist/core/strata/checkpoint/manager.js +16 -10
- package/dist/core/strata/checkpoint/snapshot-create.js +5 -4
- package/dist/core/strata/checkpoint/snapshot-write-tree.js +7 -3
- package/dist/core/strata/engine/shadow-merge-engine.js +4 -2
- package/dist/core/strata/interaction/file-system-provider.js +2 -1
- package/dist/core/strata/layers/file-state-resolver.js +9 -7
- package/dist/core/strata/layers/immutable-git-layer.js +3 -1
- package/dist/core/strata/layers/shadow-driver/readonly-lock.js +8 -6
- package/dist/core/strata/layers/shadow-driver/shadow-driver.js +2 -1
- package/dist/core/strata/layers/worktree.js +2 -1
- package/dist/core/strata/runtime/environment.js +2 -1
- package/dist/core/strata/runtime/synchronizer.js +18 -17
- package/dist/core/structured-output/json-extract.js +3 -1
- package/dist/core/sub-agent/artifacts/store.js +2 -1
- package/dist/core/sub-agent/core/manager.js +24 -1
- package/dist/core/sub-agent/registry-defaults.js +2 -2
- package/dist/core/sub-agent/summary.js +96 -0
- package/dist/core/sub-agent/tools/task-spawn.js +7 -4
- package/dist/core/target-runtime/profile.js +3 -1
- package/dist/core/tools/audit.js +3 -2
- package/dist/core/tools/budget.js +3 -1
- package/dist/core/tools/builtin/ast.js +144 -0
- package/dist/core/tools/builtin/code-search/backends/powershell.js +3 -1
- package/dist/core/tools/builtin/code-search/backends/rg.js +3 -1
- package/dist/core/tools/builtin/code-search/parse/plain-grep.js +3 -1
- package/dist/core/tools/builtin/code-search/parse/rg-json.js +3 -1
- package/dist/core/tools/builtin/fs.js +76 -1
- package/dist/core/tools/builtin/git.js +242 -0
- package/dist/core/tools/builtin/glob.js +79 -0
- package/dist/core/tools/builtin/index.js +12 -4
- package/dist/core/tools/builtin/knowledge.js +146 -4
- package/dist/core/tools/builtin/proposal.js +3 -1
- package/dist/core/tools/builtin/verify.js +35 -3
- package/dist/core/tools/permissions/permission-rules.js +3 -1
- package/dist/core/tools/router.js +88 -5
- package/dist/core/tools/session.js +10 -5
- package/dist/core/types/batch.js +2 -0
- package/dist/core/utils/sanitizer.js +5 -2
- package/dist/core/utils/serialize.js +5 -2
- package/dist/core/verification/detect-runner.js +86 -0
- package/dist/core/verification/runner.js +76 -0
- package/dist/core/version.js +3 -1
- package/dist/languages/python/index.js +154 -0
- package/dist/locales/en.js +6 -0
- package/package.json +2 -1
|
@@ -3,7 +3,7 @@ import { mapErrorForAudit } from '../../../observability/error-mapping.js';
|
|
|
3
3
|
import { ReflectionEngine } from '../../../reflection/engine.js';
|
|
4
4
|
import { resolveExecutionProfile } from '../../../runtime/execution-profile.js';
|
|
5
5
|
import { executeFlowAttempt } from '../../flows/flow-dispatch.js';
|
|
6
|
-
import { resolveAttemptFailure } from './attempt-failure.js';
|
|
6
|
+
import { extractErrorCode, resolveAttemptFailure } from './attempt-failure.js';
|
|
7
7
|
import { buildAuthorizationSummary } from './authorization-summary.js';
|
|
8
8
|
import { mapRetryExhaustedReport, mapSuccessReport, mapTerminalFailureReport, } from './report-mapper.js';
|
|
9
9
|
import { evaluateRetryPolicy } from './retry-policy.js';
|
|
@@ -128,7 +128,6 @@ export class FlowTransactionRunner {
|
|
|
128
128
|
lastRecentReadArtifacts;
|
|
129
129
|
lastToolResultPreviewArtifacts;
|
|
130
130
|
lastReplacementState;
|
|
131
|
-
pendingAutopilotVerification;
|
|
132
131
|
constructor(params) {
|
|
133
132
|
this.params = params;
|
|
134
133
|
this.lastVerifyArtifact = params.options.artifactHints?.verifyArtifact;
|
|
@@ -183,7 +182,6 @@ export class FlowTransactionRunner {
|
|
|
183
182
|
: undefined,
|
|
184
183
|
},
|
|
185
184
|
replacementState: this.lastReplacementState,
|
|
186
|
-
pendingVerification: this.pendingAutopilotVerification,
|
|
187
185
|
lastError: this.currentLastError,
|
|
188
186
|
applyBackRuntime: {
|
|
189
187
|
activeRepoPath: this.params.env.activeRepoPath,
|
|
@@ -212,20 +210,9 @@ export class FlowTransactionRunner {
|
|
|
212
210
|
flowReport: result,
|
|
213
211
|
context: shrinkCtx,
|
|
214
212
|
flowMode: this.params.flowMode,
|
|
213
|
+
verifyPolicy: this.params.options.verifyPolicy,
|
|
215
214
|
});
|
|
216
215
|
lastAttemptFailure = attemptFailure;
|
|
217
|
-
if (this.params.flowMode === 'autopilot') {
|
|
218
|
-
if (attemptFailure?.reasonCode === 'VERIFY_FAILED') {
|
|
219
|
-
this.pendingAutopilotVerification = {
|
|
220
|
-
changedFiles: terminalCtx && 'changedFiles' in terminalCtx
|
|
221
|
-
? (terminalCtx.changedFiles ?? undefined)
|
|
222
|
-
: undefined,
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
else if (!attemptFailure) {
|
|
226
|
-
this.pendingAutopilotVerification = undefined;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
216
|
const entry = {
|
|
230
217
|
attempt,
|
|
231
218
|
plan: shrinkCtx?.plan ?? null,
|
|
@@ -245,27 +232,24 @@ export class FlowTransactionRunner {
|
|
|
245
232
|
? 'SHRINK'
|
|
246
233
|
: 'APPLY_BACK';
|
|
247
234
|
recordAuditEvent('loop.attempt.success', { attempt, flowMode: this.params.flowMode }, { phase: successPhase, severity: 'low', scope: 'session' });
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
235
|
+
this.emitReflection({
|
|
236
|
+
success: true,
|
|
237
|
+
attempt,
|
|
238
|
+
input: {
|
|
252
239
|
instruction: this.params.options.instruction,
|
|
253
240
|
history: this.historyEntries,
|
|
254
241
|
success: true,
|
|
255
242
|
metadata: shrinkCtx?.context?.projectMetadata,
|
|
256
243
|
finalPlan: shrinkCtx?.plan,
|
|
257
244
|
finalPatch: shrinkCtx?.diff,
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.reflect(reflectionInput, this.params.options.repoPath)
|
|
261
|
-
.catch((e) => recordAuditEvent('reflection.error', { error: String(e) }, { severity: 'medium' }));
|
|
262
|
-
}
|
|
245
|
+
},
|
|
246
|
+
});
|
|
263
247
|
return mapSuccessReport({
|
|
264
248
|
attempt,
|
|
265
249
|
flowReport: result,
|
|
266
250
|
history: this.historyEntries,
|
|
267
251
|
authorizationSummary: this.authorizationSummary,
|
|
268
|
-
lastErrorCode:
|
|
252
|
+
lastErrorCode: extractErrorCode(result.error),
|
|
269
253
|
lastContext: terminalCtx,
|
|
270
254
|
lastVerifyArtifact: this.lastVerifyArtifact,
|
|
271
255
|
lastSubAgentPatchArtifacts: this.lastSubAgentPatchArtifacts,
|
|
@@ -317,12 +301,20 @@ export class FlowTransactionRunner {
|
|
|
317
301
|
if (retryDecision.retryExhausted) {
|
|
318
302
|
break;
|
|
319
303
|
}
|
|
304
|
+
this.emitReflection({
|
|
305
|
+
success: false,
|
|
306
|
+
input: {
|
|
307
|
+
instruction: this.params.options.instruction,
|
|
308
|
+
history: this.historyEntries,
|
|
309
|
+
success: false,
|
|
310
|
+
},
|
|
311
|
+
});
|
|
320
312
|
return mapTerminalFailureReport({
|
|
321
313
|
attempt,
|
|
322
314
|
flowReport: result,
|
|
323
315
|
history: this.historyEntries,
|
|
324
316
|
authorizationSummary: this.authorizationSummary,
|
|
325
|
-
lastErrorCode: attemptFailure.errorCode ??
|
|
317
|
+
lastErrorCode: attemptFailure.errorCode ?? extractErrorCode(result.error),
|
|
326
318
|
lastContext: terminalCtx,
|
|
327
319
|
lastVerifyArtifact: this.lastVerifyArtifact,
|
|
328
320
|
lastSubAgentPatchArtifacts: this.lastSubAgentPatchArtifacts,
|
|
@@ -333,7 +325,7 @@ export class FlowTransactionRunner {
|
|
|
333
325
|
});
|
|
334
326
|
}
|
|
335
327
|
this.currentContext = shrinkCtx?.context;
|
|
336
|
-
this.currentLastError = attemptFailure.reason;
|
|
328
|
+
this.currentLastError = shrinkCtx?.lastError ?? attemptFailure.reason;
|
|
337
329
|
this.params.emit({
|
|
338
330
|
type: 'retry',
|
|
339
331
|
fromAttempt: attempt,
|
|
@@ -346,13 +338,21 @@ export class FlowTransactionRunner {
|
|
|
346
338
|
if (!lastReport) {
|
|
347
339
|
throw new Error('SalmonLoop execution terminated without a FlowReport');
|
|
348
340
|
}
|
|
341
|
+
this.emitReflection({
|
|
342
|
+
success: false,
|
|
343
|
+
input: {
|
|
344
|
+
instruction: this.params.options.instruction,
|
|
345
|
+
history: this.historyEntries,
|
|
346
|
+
success: false,
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
349
|
return mapRetryExhaustedReport({
|
|
350
350
|
attempts: retries,
|
|
351
351
|
flowReport: lastReport,
|
|
352
352
|
history: this.historyEntries,
|
|
353
353
|
authorizationSummary: this.authorizationSummary,
|
|
354
354
|
failure: lastAttemptFailure,
|
|
355
|
-
lastErrorCode:
|
|
355
|
+
lastErrorCode: extractErrorCode(lastReport.error),
|
|
356
356
|
lastContext: this.lastContext,
|
|
357
357
|
lastVerifyArtifact: this.lastVerifyArtifact,
|
|
358
358
|
lastSubAgentPatchArtifacts: this.lastSubAgentPatchArtifacts,
|
|
@@ -361,13 +361,19 @@ export class FlowTransactionRunner {
|
|
|
361
361
|
lastToolResultPreviewArtifacts: this.lastToolResultPreviewArtifacts,
|
|
362
362
|
});
|
|
363
363
|
}
|
|
364
|
-
|
|
365
|
-
if (
|
|
366
|
-
return
|
|
367
|
-
|
|
368
|
-
|
|
364
|
+
emitReflection(params) {
|
|
365
|
+
if (!this.params.options.llm)
|
|
366
|
+
return;
|
|
367
|
+
const engine = new ReflectionEngine(this.params.options.llm);
|
|
368
|
+
const { repoPath } = this.params.options;
|
|
369
|
+
const fallback = (e) => recordAuditEvent('reflection.error', { error: String(e) }, { severity: 'medium' });
|
|
370
|
+
if (params.success) {
|
|
371
|
+
const method = (params.attempt ?? 1) > 1 ? 'reflect' : 'reflectOnSuccess';
|
|
372
|
+
engine[method](params.input, repoPath).catch(fallback);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
engine.reflectOnFailure(params.input, repoPath).catch(fallback);
|
|
369
376
|
}
|
|
370
|
-
return undefined;
|
|
371
377
|
}
|
|
372
378
|
}
|
|
373
379
|
//# sourceMappingURL=transaction-runner.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
2
|
import * as fs from '../../adapters/fs/node-fs.js';
|
|
3
|
+
import { getLogger } from '../../observability/logger.js';
|
|
3
4
|
/**
|
|
4
5
|
* RejectionManager
|
|
5
6
|
* Handles creation and management of .rej files for failed merges.
|
|
@@ -46,13 +47,14 @@ export class RejectionManager {
|
|
|
46
47
|
...header,
|
|
47
48
|
});
|
|
48
49
|
}
|
|
49
|
-
catch {
|
|
50
|
-
|
|
50
|
+
catch (error) {
|
|
51
|
+
getLogger().debug(`[RejectionManager] Malformed rejection file ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
return rejections;
|
|
54
55
|
}
|
|
55
|
-
catch {
|
|
56
|
+
catch (error) {
|
|
57
|
+
getLogger().debug(`[RejectionManager] Failed to read rejection directory: ${error instanceof Error ? error.message : String(error)}`);
|
|
56
58
|
return [];
|
|
57
59
|
}
|
|
58
60
|
}
|
|
@@ -63,8 +65,8 @@ export class RejectionManager {
|
|
|
63
65
|
try {
|
|
64
66
|
await fs.rm(this.rejectDir, { recursive: true, force: true });
|
|
65
67
|
}
|
|
66
|
-
catch {
|
|
67
|
-
|
|
68
|
+
catch (error) {
|
|
69
|
+
getLogger().warn(`[RejectionManager] Failed to clear rejection directory: ${error instanceof Error ? error.message : String(error)}`);
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
72
|
}
|
|
@@ -2,6 +2,7 @@ import { text } from '../../../locales/index.js';
|
|
|
2
2
|
import { recordAuditEvent } from '../../observability/audit-trail.js';
|
|
3
3
|
import { writeDebugArtifact } from '../../observability/debug-artifacts.js';
|
|
4
4
|
import { buildErrorEnvelope, toSafeErrorSummary } from '../../observability/error-envelope.js';
|
|
5
|
+
import { getLogger } from '../../observability/logger.js';
|
|
5
6
|
import { isRecord } from '../../utils/serialize.js';
|
|
6
7
|
import { collectSidecarPaths } from './apply-back-utils.js';
|
|
7
8
|
export async function runApplyBackPhase(params) {
|
|
@@ -95,8 +96,9 @@ export async function runApplyBackPhase(params) {
|
|
|
95
96
|
].join('\n'),
|
|
96
97
|
});
|
|
97
98
|
}
|
|
98
|
-
catch {
|
|
99
|
+
catch (artifactError) {
|
|
99
100
|
// Best-effort: do not mask the primary failure if artifact writing fails.
|
|
101
|
+
getLogger().debug(`[ApplyBackRuntime] Failed to write debug artifact: ${artifactError instanceof Error ? artifactError.message : String(artifactError)}`);
|
|
100
102
|
}
|
|
101
103
|
const envelope = buildErrorEnvelope({
|
|
102
104
|
domain: 'applyBack',
|
|
@@ -10,7 +10,8 @@ export class GitConfigService {
|
|
|
10
10
|
try {
|
|
11
11
|
return await git.exec(['config', '--get', key], { allowError: true });
|
|
12
12
|
}
|
|
13
|
-
catch {
|
|
13
|
+
catch (error) {
|
|
14
|
+
getLogger().debug(`[GitConfigService] git config --get failed for key "${key}": ${error instanceof Error ? error.message : String(error)}`);
|
|
14
15
|
return null;
|
|
15
16
|
}
|
|
16
17
|
};
|
|
@@ -6,6 +6,7 @@ import { GitAdapter } from '../../adapters/git/git-adapter.js';
|
|
|
6
6
|
import { LIMITS } from '../../config/limits.js';
|
|
7
7
|
import { supportsLlmStreaming } from '../../llm/capabilities.js';
|
|
8
8
|
import { emitLlmOutput } from '../../llm/output-policy.js';
|
|
9
|
+
import { getLogger } from '../../observability/logger.js';
|
|
9
10
|
import { getAutopilotSystemPrompt } from '../../prompts/runtime.js';
|
|
10
11
|
import { SessionReplacementPreviewProvider } from '../../session/replacement-preview-provider.js';
|
|
11
12
|
import { chatWithTools, chatWithToolsStreaming } from '../../tools/session.js';
|
|
@@ -286,13 +287,11 @@ function isRuntimeGeneratedPath(path) {
|
|
|
286
287
|
return false;
|
|
287
288
|
}
|
|
288
289
|
function lastFailedToolAuditEntry(entries) {
|
|
289
|
-
if (!Array.isArray(entries))
|
|
290
|
+
if (!Array.isArray(entries) || entries.length === 0)
|
|
290
291
|
return undefined;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
return entry;
|
|
295
|
-
}
|
|
292
|
+
const last = entries[entries.length - 1];
|
|
293
|
+
if (last?.toolResultStatus && last.toolResultStatus !== 'ok') {
|
|
294
|
+
return last;
|
|
296
295
|
}
|
|
297
296
|
return undefined;
|
|
298
297
|
}
|
|
@@ -370,12 +369,16 @@ export async function runAutopilot(ctx) {
|
|
|
370
369
|
try {
|
|
371
370
|
workspaceFingerprintBefore = await captureWorkspaceFingerprintForContext(ctx);
|
|
372
371
|
}
|
|
373
|
-
catch {
|
|
372
|
+
catch (error) {
|
|
373
|
+
getLogger().debug(`[Autopilot] Workspace fingerprint capture failed (before): ${error instanceof Error ? error.message : String(error)}`);
|
|
374
374
|
samplingFailedClosed = true;
|
|
375
375
|
}
|
|
376
376
|
}
|
|
377
|
-
|
|
378
|
-
|
|
377
|
+
let assistant = null;
|
|
378
|
+
let content = '';
|
|
379
|
+
const messages = shared.baseMessages;
|
|
380
|
+
assistant = supportsTools
|
|
381
|
+
? await (supportsStreaming ? chatWithToolsStreaming : chatWithTools)(messages, {
|
|
379
382
|
phase: AUTOPILOT_TOOL_PHASE,
|
|
380
383
|
providerHints: shared.envelope.providerHints,
|
|
381
384
|
temperature: 0.2,
|
|
@@ -400,7 +403,7 @@ export async function runAutopilot(ctx) {
|
|
|
400
403
|
},
|
|
401
404
|
emit: (event) => ctx.emit({ ...event, timestamp: event.timestamp ?? new Date() }),
|
|
402
405
|
})
|
|
403
|
-
: await llmClient.chat(
|
|
406
|
+
: await llmClient.chat(messages, {
|
|
404
407
|
phase: 'AUTOPILOT',
|
|
405
408
|
providerHints: shared.envelope.providerHints,
|
|
406
409
|
temperature: 0.2,
|
|
@@ -408,7 +411,7 @@ export async function runAutopilot(ctx) {
|
|
|
408
411
|
tools: [],
|
|
409
412
|
toolChoice: 'none',
|
|
410
413
|
});
|
|
411
|
-
|
|
414
|
+
content = String(assistant?.content ?? '').trim();
|
|
412
415
|
if (!supportsTools) {
|
|
413
416
|
emitLlmOutput({
|
|
414
417
|
emit: ctx.emit,
|
|
@@ -435,28 +438,18 @@ export async function runAutopilot(ctx) {
|
|
|
435
438
|
workspaceFingerprintBefore.head !== workspaceFingerprintAfter.head ||
|
|
436
439
|
workspaceFingerprintBefore.index !== workspaceFingerprintAfter.index;
|
|
437
440
|
}
|
|
438
|
-
catch {
|
|
441
|
+
catch (error) {
|
|
442
|
+
getLogger().debug(`[Autopilot] Workspace fingerprint capture failed (after): ${error instanceof Error ? error.message : String(error)}`);
|
|
439
443
|
mutated = true;
|
|
440
444
|
}
|
|
441
445
|
}
|
|
442
446
|
}
|
|
443
|
-
const
|
|
444
|
-
? ctx.pendingVerification.changedFiles
|
|
445
|
-
: undefined;
|
|
446
|
-
const effectiveChangedFiles = changedFiles && changedFiles.length > 0
|
|
447
|
-
? changedFiles
|
|
448
|
-
: pendingChangedFiles && pendingChangedFiles.length > 0
|
|
449
|
-
? pendingChangedFiles
|
|
450
|
-
: undefined;
|
|
451
|
-
const effectiveMutated = mutated || Boolean(ctx.pendingVerification);
|
|
447
|
+
const effectiveChangedFiles = changedFiles && changedFiles.length > 0 ? changedFiles : undefined;
|
|
452
448
|
return {
|
|
453
449
|
...ctx,
|
|
454
|
-
mutated
|
|
450
|
+
mutated,
|
|
455
451
|
changedFiles: effectiveChangedFiles,
|
|
456
|
-
|
|
457
|
-
? { changedFiles: effectiveChangedFiles }
|
|
458
|
-
: ctx.pendingVerification,
|
|
459
|
-
completion: resolveAutopilotCompletion({ content, mutated: effectiveMutated, localAudit }),
|
|
452
|
+
completion: resolveAutopilotCompletion({ content, mutated, localAudit }),
|
|
460
453
|
toolCallingAudit: mergedAudit,
|
|
461
454
|
report: {
|
|
462
455
|
kind: 'answer',
|
|
@@ -473,13 +466,10 @@ export async function runAutopilotVerifyGate(ctx) {
|
|
|
473
466
|
};
|
|
474
467
|
}
|
|
475
468
|
if (!ctx.options.verify) {
|
|
469
|
+
// Autopilot is LLM-driven: the agent verifies on its own via shell.exec.
|
|
470
|
+
// Skip the gate instead of failing — the agent's tool calls are the verification.
|
|
476
471
|
return {
|
|
477
472
|
...ctx,
|
|
478
|
-
completion: {
|
|
479
|
-
status: 'verification_missing',
|
|
480
|
-
reason: 'Autopilot changed the workspace but no verification command was configured.',
|
|
481
|
-
errorCode: 'VERIFY_COMMAND_MISSING',
|
|
482
|
-
},
|
|
483
473
|
verifyResult: undefined,
|
|
484
474
|
};
|
|
485
475
|
}
|
|
@@ -491,7 +481,6 @@ export async function runAutopilotVerifyGate(ctx) {
|
|
|
491
481
|
const nextCtx = {
|
|
492
482
|
...ctx,
|
|
493
483
|
verifyResult,
|
|
494
|
-
pendingVerification: verifyResult.ok ? undefined : ctx.pendingVerification,
|
|
495
484
|
};
|
|
496
485
|
return verifyArtifact ? { ...nextCtx, verifyArtifact } : nextCtx;
|
|
497
486
|
}
|
|
@@ -2,6 +2,7 @@ import path from 'path';
|
|
|
2
2
|
import { text } from '../../../locales/index.js';
|
|
3
3
|
import { supportsLlmStreaming } from '../../llm/capabilities.js';
|
|
4
4
|
import { recordAuditEvent } from '../../observability/audit-trail.js';
|
|
5
|
+
import { getLogger } from '../../observability/logger.js';
|
|
5
6
|
import { getExplorePrompt, getExploreSystemPrompt } from '../../prompts/runtime.js';
|
|
6
7
|
import { SessionReplacementPreviewProvider } from '../../session/replacement-preview-provider.js';
|
|
7
8
|
import { chatWithTools, chatWithToolsStreaming } from '../../tools/session.js';
|
|
@@ -119,8 +120,9 @@ export const exploreCodebase = async (ctx) => {
|
|
|
119
120
|
capturedFiles.set(filePath, content);
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
|
-
catch {
|
|
123
|
+
catch (error) {
|
|
123
124
|
// Ignore parsing errors, just don't capture
|
|
125
|
+
getLogger().debug(`[Explore] Failed to parse tool arguments for file capture: ${error instanceof Error ? error.message : String(error)}`);
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
128
|
}
|
|
@@ -196,8 +198,9 @@ export const exploreCodebase = async (ctx) => {
|
|
|
196
198
|
capturedFiles.set(rel, content);
|
|
197
199
|
}
|
|
198
200
|
}
|
|
199
|
-
catch {
|
|
201
|
+
catch (error) {
|
|
200
202
|
// Best-effort; failure here should not abort exploration.
|
|
203
|
+
getLogger().debug(`[Explore] Failed to read inferred file "${rel}": ${error instanceof Error ? error.message : String(error)}`);
|
|
201
204
|
}
|
|
202
205
|
}
|
|
203
206
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { text } from '../../../locales/index.js';
|
|
2
2
|
import { emitLlmOutput } from '../../llm/output-policy.js';
|
|
3
|
+
import { getLogger } from '../../observability/logger.js';
|
|
3
4
|
import { getReviewPrompt } from '../../prompts/runtime.js';
|
|
4
5
|
function normalizeReviewSuggestions(value) {
|
|
5
6
|
if (value == null)
|
|
@@ -23,7 +24,8 @@ function parseReviewResponse(content) {
|
|
|
23
24
|
try {
|
|
24
25
|
return normalizeReviewSuggestions(JSON.parse(content));
|
|
25
26
|
}
|
|
26
|
-
catch {
|
|
27
|
+
catch (error) {
|
|
28
|
+
getLogger().debug(`[GenerateReview] Failed to parse review response as JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
27
29
|
return normalizeReviewSuggestions(content);
|
|
28
30
|
}
|
|
29
31
|
}
|
|
@@ -2,6 +2,7 @@ import { text } from '../../../locales/index.js';
|
|
|
2
2
|
import { supportsLlmStreaming } from '../../llm/capabilities.js';
|
|
3
3
|
import { emitLlmOutput } from '../../llm/output-policy.js';
|
|
4
4
|
import { recordAuditEvent } from '../../observability/audit-trail.js';
|
|
5
|
+
import { getLogger } from '../../observability/logger.js';
|
|
5
6
|
import { getResearchPrompt, getResearchSystemPrompt } from '../../prompts/runtime.js';
|
|
6
7
|
import { SessionReplacementPreviewProvider } from '../../session/replacement-preview-provider.js';
|
|
7
8
|
import { chatWithTools, chatWithToolsStreaming } from '../../tools/session.js';
|
|
@@ -50,7 +51,8 @@ function parseResearchResponse(content, fallbackSources) {
|
|
|
50
51
|
try {
|
|
51
52
|
parsed = JSON.parse(content);
|
|
52
53
|
}
|
|
53
|
-
catch {
|
|
54
|
+
catch (error) {
|
|
55
|
+
getLogger().debug(`[Research] Failed to parse research response as JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
54
56
|
parsed = undefined;
|
|
55
57
|
}
|
|
56
58
|
const researchText = String(parsed?.researchText ?? content ?? text.grizzco.research.empty ?? '');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { text } from '../../../locales/index.js';
|
|
2
2
|
import { collectBudgetMetrics, evaluateBudgetAlert, getGlobalAdjuster, recordBudgetAlert, } from '../../context/budget/integration.js';
|
|
3
|
+
import { getEffectivenessTracker } from '../../context/effectiveness/tracker.js';
|
|
3
4
|
import { recordAuditEvent } from '../../observability/audit-trail.js';
|
|
4
5
|
import { executeVerifyForWorkspace } from './verify-shared.js';
|
|
5
6
|
function extractCommandProgram(command) {
|
|
@@ -32,7 +33,12 @@ export const runVerify = async (ctx) => {
|
|
|
32
33
|
scope: 'session',
|
|
33
34
|
phase: 'VERIFY',
|
|
34
35
|
});
|
|
35
|
-
// Collect budget metrics after verification
|
|
36
|
+
// Collect budget metrics and effectiveness data after verification
|
|
37
|
+
const effectivenessTracker = getEffectivenessTracker();
|
|
38
|
+
effectivenessTracker.recordExecution(verifyResult.ok, 0);
|
|
39
|
+
if (!verifyResult.ok) {
|
|
40
|
+
effectivenessTracker.recordFailure('missing_context', `Verification failed: ${verifyResult.output?.slice(0, 200) ?? 'unknown'}`);
|
|
41
|
+
}
|
|
36
42
|
if (ctx.contextResult) {
|
|
37
43
|
const metrics = collectBudgetMetrics({
|
|
38
44
|
contextResult: ctx.contextResult,
|
|
@@ -4,6 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import { readFile, rm } from '../../adapters/fs/node-fs.js';
|
|
5
5
|
import { GitAdapter } from '../../adapters/git/git-adapter.js';
|
|
6
6
|
import { AstParser } from '../../ast/index.js';
|
|
7
|
+
import { getLogger } from '../../observability/logger.js';
|
|
7
8
|
import { convertDiffToShadowOperations } from '../../patch/diff.js';
|
|
8
9
|
import { tryGetPluginRegistry } from '../../plugin/registry.js';
|
|
9
10
|
import { OpType } from '../domain/grizzco-types.js';
|
|
@@ -70,7 +71,8 @@ async function defaultBuildProposedSource(workPath, operation) {
|
|
|
70
71
|
return null;
|
|
71
72
|
return showResult.stdout.toString('utf8');
|
|
72
73
|
}
|
|
73
|
-
catch {
|
|
74
|
+
catch (error) {
|
|
75
|
+
getLogger().debug(`[AstValidationService] Failed to build proposed source for "${operation.path}": ${error instanceof Error ? error.message : String(error)}`);
|
|
74
76
|
return null;
|
|
75
77
|
}
|
|
76
78
|
finally {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { join } from 'path';
|
|
2
2
|
import { FileAdapter } from '../adapters/fs/index.js';
|
|
3
|
+
import { getLogger } from '../observability/logger.js';
|
|
3
4
|
/**
|
|
4
5
|
* Manages persistence of user input history isolated by Session.
|
|
5
6
|
* Storage path: .salmonloop/ui-history/{sessionId}.json
|
|
@@ -26,7 +27,8 @@ export class InputHistoryManager {
|
|
|
26
27
|
const data = await this.fileAdapter.readFile(filePath);
|
|
27
28
|
return JSON.parse(data);
|
|
28
29
|
}
|
|
29
|
-
catch {
|
|
30
|
+
catch (error) {
|
|
31
|
+
getLogger().debug(`[InputHistory] Failed to load history for session "${sessionId}": ${error instanceof Error ? error.message : String(error)}`);
|
|
30
32
|
return [];
|
|
31
33
|
}
|
|
32
34
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { getLogger } from '../observability/logger.js';
|
|
2
3
|
const LlmDecisionSchema = z
|
|
3
4
|
.object({
|
|
4
5
|
intent: z.enum(['answer', 'review', 'patch', 'debug', 'research']),
|
|
@@ -165,8 +166,9 @@ async function routeByLlm(input, options) {
|
|
|
165
166
|
try {
|
|
166
167
|
return JSON.parse(candidate);
|
|
167
168
|
}
|
|
168
|
-
catch {
|
|
169
|
+
catch (error) {
|
|
169
170
|
// Best-effort: extract the first JSON object from mixed output.
|
|
171
|
+
getLogger().debug(`[ChatIntent] Primary JSON parse failed, attempting extraction: ${error instanceof Error ? error.message : String(error)}`);
|
|
170
172
|
const start = candidate.indexOf('{');
|
|
171
173
|
const end = candidate.lastIndexOf('}');
|
|
172
174
|
if (start >= 0 && end > start) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsonSchema, tool } from 'ai';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
4
|
+
import { getLogger } from '../../observability/logger.js';
|
|
4
5
|
import { toolToOpenAI } from '../../tools/mapper.js';
|
|
5
6
|
import { isRecord } from '../../utils/serialize.js';
|
|
6
7
|
function formatOutputSchema(schema) {
|
|
@@ -20,8 +21,9 @@ function formatOutputSchema(schema) {
|
|
|
20
21
|
return JSON.stringify(cleanSchema);
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
|
-
catch {
|
|
24
|
+
catch (error) {
|
|
24
25
|
// Fallback to generic description for invalid/unsupported schema.
|
|
26
|
+
getLogger().debug(`[MessageMapper] Failed to format output schema to JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
25
27
|
}
|
|
26
28
|
return 'complex object';
|
|
27
29
|
}
|
|
@@ -31,8 +33,8 @@ function safeParseJsonObject(textValue) {
|
|
|
31
33
|
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
|
|
32
34
|
return parsed;
|
|
33
35
|
}
|
|
34
|
-
catch {
|
|
35
|
-
|
|
36
|
+
catch (error) {
|
|
37
|
+
getLogger().debug(`[MessageMapper] Failed to parse JSON object: ${error instanceof Error ? error.message : String(error)}`);
|
|
36
38
|
}
|
|
37
39
|
return {};
|
|
38
40
|
}
|
|
@@ -43,7 +45,8 @@ function deepCloneJson(value, fallback) {
|
|
|
43
45
|
return fallback;
|
|
44
46
|
return JSON.parse(serialized);
|
|
45
47
|
}
|
|
46
|
-
catch {
|
|
48
|
+
catch (error) {
|
|
49
|
+
getLogger().debug(`[MessageMapper] deepCloneJson failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
47
50
|
return fallback;
|
|
48
51
|
}
|
|
49
52
|
}
|
|
@@ -102,7 +105,8 @@ export function toAiSdkMessages(messages) {
|
|
|
102
105
|
try {
|
|
103
106
|
parsedContent = JSON.parse(m.content);
|
|
104
107
|
}
|
|
105
|
-
catch {
|
|
108
|
+
catch (error) {
|
|
109
|
+
getLogger().debug(`[MessageMapper] Failed to parse tool message content as JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
106
110
|
parsedContent = m.content;
|
|
107
111
|
}
|
|
108
112
|
if (isToolApprovalResponse(parsedContent)) {
|
|
@@ -247,14 +251,15 @@ export function toOpenAiToolCalls(toolCalls) {
|
|
|
247
251
|
try {
|
|
248
252
|
parsed = JSON.parse(nested);
|
|
249
253
|
}
|
|
250
|
-
catch {
|
|
251
|
-
|
|
254
|
+
catch (error) {
|
|
255
|
+
getLogger().debug(`[MessageMapper] Failed to parse nested JSON string: ${error instanceof Error ? error.message : String(error)}`);
|
|
252
256
|
}
|
|
253
257
|
}
|
|
254
258
|
}
|
|
255
259
|
return parsed;
|
|
256
260
|
}
|
|
257
|
-
catch {
|
|
261
|
+
catch (error) {
|
|
262
|
+
getLogger().debug(`[MessageMapper] Failed to normalize tool input JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
258
263
|
return raw;
|
|
259
264
|
}
|
|
260
265
|
};
|
|
@@ -113,9 +113,7 @@ export function buildAiSdkRequestParams(params) {
|
|
|
113
113
|
responseFormat: resolveResponseFormat(params.options),
|
|
114
114
|
toolChoice: (params.options.toolChoice === 'none'
|
|
115
115
|
? 'none'
|
|
116
|
-
: params.tools
|
|
117
|
-
? 'auto'
|
|
118
|
-
: undefined),
|
|
116
|
+
: (params.options.toolChoice ?? (params.tools ? 'auto' : undefined))),
|
|
119
117
|
providerOptions: mergeProviderOptions({
|
|
120
118
|
providerOptions: params.options.providerOptions,
|
|
121
119
|
providerHints: params.options.providerHints,
|
|
@@ -37,14 +37,15 @@ function findNetworkCode(err) {
|
|
|
37
37
|
}
|
|
38
38
|
return undefined;
|
|
39
39
|
}
|
|
40
|
-
function
|
|
40
|
+
function isUserAbortError(err) {
|
|
41
41
|
const unwrapped = unwrapRetryError(err);
|
|
42
42
|
const name = unwrapped instanceof Error ? unwrapped.name : '';
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
// Only treat as user abort if the error name is AbortError (from AbortController).
|
|
44
|
+
// Provider-side "aborted" messages are transient and should be retried.
|
|
45
|
+
return name === 'AbortError';
|
|
45
46
|
}
|
|
46
47
|
export function classifyRetryableApiError(err) {
|
|
47
|
-
if (
|
|
48
|
+
if (isUserAbortError(err))
|
|
48
49
|
return { retryable: false, reason: 'aborted' };
|
|
49
50
|
const statusCode = findStatusCode(err);
|
|
50
51
|
const networkCode = findNetworkCode(err);
|
|
@@ -69,6 +70,13 @@ export function classifyRetryableApiError(err) {
|
|
|
69
70
|
if (msg.includes('overloaded')) {
|
|
70
71
|
return { retryable: true, reason: 'overloaded', statusCode, networkCode };
|
|
71
72
|
}
|
|
73
|
+
// Provider-side aborts and unexpected errors are transient — retry them.
|
|
74
|
+
if (msg.includes('aborted')) {
|
|
75
|
+
return { retryable: true, reason: 'provider_abort', statusCode, networkCode };
|
|
76
|
+
}
|
|
77
|
+
if (msg.includes('unexpected error')) {
|
|
78
|
+
return { retryable: true, reason: 'unexpected', statusCode, networkCode };
|
|
79
|
+
}
|
|
72
80
|
if (typeof networkCode === 'string') {
|
|
73
81
|
const normalized = networkCode.toUpperCase();
|
|
74
82
|
if (normalized === 'ECONNRESET' ||
|
|
@@ -2,7 +2,7 @@ import { toLlmError } from '../errors.js';
|
|
|
2
2
|
import { withRetry, withStreamRetry } from '../retry-utils.js';
|
|
3
3
|
import { createAiSdkRetryLogger, isRetryableAiSdkError } from './request-runtime.js';
|
|
4
4
|
const DEFAULT_AI_SDK_RETRY_OPTIONS = {
|
|
5
|
-
maxRetries:
|
|
5
|
+
maxRetries: 3,
|
|
6
6
|
jitterRatio: 0.2,
|
|
7
7
|
};
|
|
8
8
|
export async function executeWithAiSdkRetry(params) {
|
package/dist/core/llm/errors.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getLogger } from '../observability/logger.js';
|
|
1
2
|
import { SalmonError } from '../types/errors.js';
|
|
2
3
|
import { sanitizeErrorMessage } from '../utils/sanitizer.js';
|
|
3
4
|
import { isRecord } from '../utils/serialize.js';
|
|
@@ -45,8 +46,8 @@ function extractProviderDetails(err) {
|
|
|
45
46
|
details.providerMessage = sanitizeError(parsed.message);
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
|
-
catch {
|
|
49
|
-
|
|
49
|
+
catch (error) {
|
|
50
|
+
getLogger().debug(`[LlmErrors] Failed to parse responseBody as JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
else if (candidate.responseBody) {
|
|
@@ -73,8 +74,8 @@ function extractProviderDetails(err) {
|
|
|
73
74
|
details.providerMessage = sanitizeError(parsed.error.message);
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
|
-
catch {
|
|
77
|
-
|
|
77
|
+
catch (error) {
|
|
78
|
+
getLogger().debug(`[LlmErrors] Failed to parse embedded JSON in message: ${error instanceof Error ? error.message : String(error)}`);
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
}
|