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,160 @@
|
|
|
1
|
+
import { createToolResultIdentity, freezeToolResultReplacementDecision, } from './replacement-state.js';
|
|
2
|
+
const MAX_SUBAGENT_ARTIFACTS = 4;
|
|
3
|
+
const MAX_READ_ARTIFACTS = 6;
|
|
4
|
+
const MAX_PREVIEW_ARTIFACTS = 6;
|
|
5
|
+
function isArtifactHandle(value) {
|
|
6
|
+
if (!value || typeof value !== 'object')
|
|
7
|
+
return false;
|
|
8
|
+
const candidate = value;
|
|
9
|
+
return (typeof candidate.handle === 'string' &&
|
|
10
|
+
typeof candidate.mimeType === 'string' &&
|
|
11
|
+
typeof candidate.sha256 === 'string' &&
|
|
12
|
+
typeof candidate.size === 'number');
|
|
13
|
+
}
|
|
14
|
+
function cloneArtifactHandle(artifact) {
|
|
15
|
+
if (!artifact)
|
|
16
|
+
return undefined;
|
|
17
|
+
return {
|
|
18
|
+
handle: artifact.handle,
|
|
19
|
+
mimeType: artifact.mimeType,
|
|
20
|
+
sha256: artifact.sha256,
|
|
21
|
+
size: artifact.size,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function normalizeArtifactHandles(artifacts, limit) {
|
|
25
|
+
if (!Array.isArray(artifacts) || artifacts.length === 0)
|
|
26
|
+
return undefined;
|
|
27
|
+
const unique = [];
|
|
28
|
+
const seen = new Set();
|
|
29
|
+
for (const artifact of artifacts) {
|
|
30
|
+
if (!isArtifactHandle(artifact))
|
|
31
|
+
continue;
|
|
32
|
+
if (seen.has(artifact.handle))
|
|
33
|
+
continue;
|
|
34
|
+
seen.add(artifact.handle);
|
|
35
|
+
unique.push(cloneArtifactHandle(artifact));
|
|
36
|
+
}
|
|
37
|
+
if (unique.length === 0)
|
|
38
|
+
return undefined;
|
|
39
|
+
return unique.slice(-limit);
|
|
40
|
+
}
|
|
41
|
+
function normalizeReadArtifacts(artifacts, limit) {
|
|
42
|
+
if (!Array.isArray(artifacts) || artifacts.length === 0)
|
|
43
|
+
return undefined;
|
|
44
|
+
const unique = [];
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
for (const item of artifacts) {
|
|
47
|
+
const path = typeof item?.path === 'string' ? item.path.trim() : '';
|
|
48
|
+
if (!path || !isArtifactHandle(item?.artifact))
|
|
49
|
+
continue;
|
|
50
|
+
const key = `${path}::${item.artifact.handle}`;
|
|
51
|
+
if (seen.has(key))
|
|
52
|
+
continue;
|
|
53
|
+
seen.add(key);
|
|
54
|
+
unique.push({
|
|
55
|
+
path,
|
|
56
|
+
artifact: cloneArtifactHandle(item.artifact),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (unique.length === 0)
|
|
60
|
+
return undefined;
|
|
61
|
+
return unique.slice(-limit);
|
|
62
|
+
}
|
|
63
|
+
function normalizePreviewArtifacts(artifacts, limit) {
|
|
64
|
+
if (!Array.isArray(artifacts) || artifacts.length === 0)
|
|
65
|
+
return undefined;
|
|
66
|
+
const unique = [];
|
|
67
|
+
const seen = new Set();
|
|
68
|
+
for (const item of artifacts) {
|
|
69
|
+
const label = typeof item?.label === 'string' ? item.label.trim() : '';
|
|
70
|
+
if (!label || !isArtifactHandle(item?.artifact))
|
|
71
|
+
continue;
|
|
72
|
+
const key = `${label}::${item.artifact.handle}`;
|
|
73
|
+
if (seen.has(key))
|
|
74
|
+
continue;
|
|
75
|
+
seen.add(key);
|
|
76
|
+
unique.push({
|
|
77
|
+
label,
|
|
78
|
+
artifact: cloneArtifactHandle(item.artifact),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (unique.length === 0)
|
|
82
|
+
return undefined;
|
|
83
|
+
return unique.slice(-limit);
|
|
84
|
+
}
|
|
85
|
+
function hasAnyArtifactState(state) {
|
|
86
|
+
return Boolean(state.verifyArtifact ||
|
|
87
|
+
state.subAgentPatchArtifacts?.length ||
|
|
88
|
+
state.subAgentAuditArtifacts?.length ||
|
|
89
|
+
state.recentReadArtifacts?.length ||
|
|
90
|
+
state.toolResultPreviewArtifacts?.length);
|
|
91
|
+
}
|
|
92
|
+
export function normalizeSessionArtifactState(state) {
|
|
93
|
+
if (!state)
|
|
94
|
+
return undefined;
|
|
95
|
+
const normalized = {
|
|
96
|
+
verifyArtifact: isArtifactHandle(state.verifyArtifact)
|
|
97
|
+
? cloneArtifactHandle(state.verifyArtifact)
|
|
98
|
+
: undefined,
|
|
99
|
+
subAgentPatchArtifacts: normalizeArtifactHandles(state.subAgentPatchArtifacts, MAX_SUBAGENT_ARTIFACTS),
|
|
100
|
+
subAgentAuditArtifacts: normalizeArtifactHandles(state.subAgentAuditArtifacts, MAX_SUBAGENT_ARTIFACTS),
|
|
101
|
+
recentReadArtifacts: normalizeReadArtifacts(state.recentReadArtifacts, MAX_READ_ARTIFACTS),
|
|
102
|
+
toolResultPreviewArtifacts: normalizePreviewArtifacts(state.toolResultPreviewArtifacts, MAX_PREVIEW_ARTIFACTS),
|
|
103
|
+
};
|
|
104
|
+
return hasAnyArtifactState(normalized) ? normalized : undefined;
|
|
105
|
+
}
|
|
106
|
+
export function mergeSessionArtifactState(existing, incoming) {
|
|
107
|
+
const base = normalizeSessionArtifactState(existing);
|
|
108
|
+
const next = normalizeSessionArtifactState(incoming);
|
|
109
|
+
if (!base)
|
|
110
|
+
return next;
|
|
111
|
+
if (!next)
|
|
112
|
+
return base;
|
|
113
|
+
return normalizeSessionArtifactState({
|
|
114
|
+
verifyArtifact: next.verifyArtifact ?? base.verifyArtifact,
|
|
115
|
+
subAgentPatchArtifacts: [
|
|
116
|
+
...(base.subAgentPatchArtifacts ?? []),
|
|
117
|
+
...(next.subAgentPatchArtifacts ?? []),
|
|
118
|
+
],
|
|
119
|
+
subAgentAuditArtifacts: [
|
|
120
|
+
...(base.subAgentAuditArtifacts ?? []),
|
|
121
|
+
...(next.subAgentAuditArtifacts ?? []),
|
|
122
|
+
],
|
|
123
|
+
recentReadArtifacts: [...(base.recentReadArtifacts ?? []), ...(next.recentReadArtifacts ?? [])],
|
|
124
|
+
toolResultPreviewArtifacts: [
|
|
125
|
+
...(base.toolResultPreviewArtifacts ?? []),
|
|
126
|
+
...(next.toolResultPreviewArtifacts ?? []),
|
|
127
|
+
],
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
export function buildSessionArtifactStateFromLoopResult(result) {
|
|
131
|
+
const hints = result.artifactHints;
|
|
132
|
+
const withVerifyFallback = hints || result.verifyArtifact
|
|
133
|
+
? {
|
|
134
|
+
...hints,
|
|
135
|
+
verifyArtifact: hints?.verifyArtifact ?? result.verifyArtifact,
|
|
136
|
+
}
|
|
137
|
+
: undefined;
|
|
138
|
+
return normalizeSessionArtifactState(withVerifyFallback);
|
|
139
|
+
}
|
|
140
|
+
export function mergeReplacementStateFromArtifactHints(existing, artifactHints, now = () => Date.now()) {
|
|
141
|
+
let next = existing;
|
|
142
|
+
for (const item of artifactHints?.toolResultPreviewArtifacts ?? []) {
|
|
143
|
+
const toolResultId = createToolResultIdentity({
|
|
144
|
+
canonicalToolCallIdentity: item.label,
|
|
145
|
+
payload: {
|
|
146
|
+
label: item.label,
|
|
147
|
+
handle: item.artifact.handle,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
next = freezeToolResultReplacementDecision(next, {
|
|
151
|
+
toolResultId,
|
|
152
|
+
decision: 'replaced',
|
|
153
|
+
preview: item.label,
|
|
154
|
+
sourceArtifactHandle: item.artifact.handle,
|
|
155
|
+
frozenAt: now(),
|
|
156
|
+
}, { maxEntries: 256 });
|
|
157
|
+
}
|
|
158
|
+
return next;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=artifact-state.js.map
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { getModelRecommendedBudget } from '../../context/token/adaptive-budget.js';
|
|
2
|
+
import { LlmError } from '../../llm/errors.js';
|
|
3
|
+
import { getLogger } from '../../observability/logger.js';
|
|
4
|
+
import { refreshSessionSummary } from '../summary-sync.js';
|
|
5
|
+
import { TokenTracker } from '../token-tracker.js';
|
|
6
|
+
import { isCircuitBreakerTripped, onCompactionFailure, onCompactionSuccess } from './tracking.js';
|
|
7
|
+
import { DEFAULT_AUTOCOMPACT_CONFIG } from './types.js';
|
|
8
|
+
function isContextOverflowLike(error) {
|
|
9
|
+
if (error instanceof LlmError && error.llmCode === 'LLM_CONTEXT_LENGTH_EXCEEDED') {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
const message = error instanceof Error
|
|
13
|
+
? error.message
|
|
14
|
+
: error && typeof error === 'object' && typeof error.message === 'string'
|
|
15
|
+
? String(error.message)
|
|
16
|
+
: '';
|
|
17
|
+
if (!message)
|
|
18
|
+
return false;
|
|
19
|
+
const lower = message.toLowerCase();
|
|
20
|
+
return (lower.includes('maximum context length') ||
|
|
21
|
+
lower.includes('context length') ||
|
|
22
|
+
lower.includes('too many tokens') ||
|
|
23
|
+
lower.includes('prompt is too long') ||
|
|
24
|
+
lower.includes('input is too long') ||
|
|
25
|
+
lower.includes('reduce the length') ||
|
|
26
|
+
lower.includes('please reduce'));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Autocompact (Level 1)
|
|
30
|
+
*
|
|
31
|
+
* Triggered when token count exceeds threshold.
|
|
32
|
+
* Uses existing summarization infrastructure to reduce context.
|
|
33
|
+
*/
|
|
34
|
+
export async function autocompact(params) {
|
|
35
|
+
const { sessionManager, llm, tracking, contextHash } = params;
|
|
36
|
+
const trigger = params.trigger ?? 'auto';
|
|
37
|
+
const modelId = llm.getModelId?.();
|
|
38
|
+
// Resolve dynamic threshold if not provided in config
|
|
39
|
+
let resolvedThreshold = params.config?.tokenThreshold;
|
|
40
|
+
if (resolvedThreshold === undefined) {
|
|
41
|
+
if (modelId) {
|
|
42
|
+
try {
|
|
43
|
+
resolvedThreshold = getModelRecommendedBudget(modelId);
|
|
44
|
+
}
|
|
45
|
+
catch (_error) {
|
|
46
|
+
// Fallback to default if resolution fails
|
|
47
|
+
resolvedThreshold = DEFAULT_AUTOCOMPACT_CONFIG.tokenThreshold;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
resolvedThreshold = DEFAULT_AUTOCOMPACT_CONFIG.tokenThreshold;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const config = {
|
|
55
|
+
...DEFAULT_AUTOCOMPACT_CONFIG,
|
|
56
|
+
...params.config,
|
|
57
|
+
tokenThreshold: resolvedThreshold,
|
|
58
|
+
};
|
|
59
|
+
// 1. Check circuit breaker
|
|
60
|
+
if (isCircuitBreakerTripped(tracking, config.maxFailures)) {
|
|
61
|
+
getLogger().audit('COMPACTION_SKIP', {
|
|
62
|
+
reason: 'circuit_breaker',
|
|
63
|
+
trigger,
|
|
64
|
+
modelId: modelId ?? 'unknown',
|
|
65
|
+
tokenThreshold: config.tokenThreshold,
|
|
66
|
+
consecutiveFailures: tracking.consecutiveFailures,
|
|
67
|
+
maxFailures: config.maxFailures,
|
|
68
|
+
}, {
|
|
69
|
+
source: 'session',
|
|
70
|
+
severity: 'low',
|
|
71
|
+
scope: 'session',
|
|
72
|
+
phase: 'COMPACTION',
|
|
73
|
+
});
|
|
74
|
+
return { performed: false, tracking };
|
|
75
|
+
}
|
|
76
|
+
// 2. Check threshold (only for auto trigger)
|
|
77
|
+
const messages = sessionManager.getMessages();
|
|
78
|
+
const totalTokens = TokenTracker.estimateMessagesTokens(messages);
|
|
79
|
+
if (trigger === 'auto' && totalTokens < config.tokenThreshold) {
|
|
80
|
+
getLogger().audit('COMPACTION_SKIP', {
|
|
81
|
+
reason: 'below_threshold',
|
|
82
|
+
trigger,
|
|
83
|
+
modelId: modelId ?? 'unknown',
|
|
84
|
+
preTokens: totalTokens,
|
|
85
|
+
tokenThreshold: config.tokenThreshold,
|
|
86
|
+
}, {
|
|
87
|
+
source: 'session',
|
|
88
|
+
severity: 'low',
|
|
89
|
+
scope: 'session',
|
|
90
|
+
phase: 'COMPACTION',
|
|
91
|
+
});
|
|
92
|
+
return { performed: false, tracking };
|
|
93
|
+
}
|
|
94
|
+
// 3. Perform summarization
|
|
95
|
+
try {
|
|
96
|
+
// Reuse existing refreshSessionSummary with 'force' strategy
|
|
97
|
+
// This will trigger the ConversationSummarizer logic
|
|
98
|
+
const summaryResult = await refreshSessionSummary({
|
|
99
|
+
sessionManager,
|
|
100
|
+
llm,
|
|
101
|
+
contextHash,
|
|
102
|
+
strategy: 'force',
|
|
103
|
+
strict: true,
|
|
104
|
+
});
|
|
105
|
+
if (!summaryResult.didSummarize) {
|
|
106
|
+
getLogger().audit('COMPACTION_SKIP', {
|
|
107
|
+
reason: 'no_op',
|
|
108
|
+
trigger,
|
|
109
|
+
modelId: modelId ?? 'unknown',
|
|
110
|
+
preTokens: totalTokens,
|
|
111
|
+
tokenThreshold: config.tokenThreshold,
|
|
112
|
+
}, {
|
|
113
|
+
source: 'session',
|
|
114
|
+
severity: 'low',
|
|
115
|
+
scope: 'session',
|
|
116
|
+
phase: 'COMPACTION',
|
|
117
|
+
});
|
|
118
|
+
return { performed: false, tracking };
|
|
119
|
+
}
|
|
120
|
+
const updatedSummary = sessionManager.getSummaryState();
|
|
121
|
+
getLogger().audit(`COMPACTION_${trigger.toUpperCase()}COMPACT`, {
|
|
122
|
+
trigger,
|
|
123
|
+
modelId: modelId ?? 'unknown',
|
|
124
|
+
preTokens: totalTokens,
|
|
125
|
+
tokenThreshold: config.tokenThreshold,
|
|
126
|
+
summaryTokens: updatedSummary?.summaryTokens,
|
|
127
|
+
hasRecoveryState: Boolean(updatedSummary?.recoveryState),
|
|
128
|
+
circuitBreakerState: { consecutiveFailures: 0 },
|
|
129
|
+
}, {
|
|
130
|
+
source: 'session',
|
|
131
|
+
severity: 'medium',
|
|
132
|
+
scope: 'session',
|
|
133
|
+
phase: 'COMPACTION',
|
|
134
|
+
});
|
|
135
|
+
return {
|
|
136
|
+
performed: true,
|
|
137
|
+
tracking: onCompactionSuccess(tracking),
|
|
138
|
+
preTokens: totalTokens,
|
|
139
|
+
trigger: trigger,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
const newTracking = onCompactionFailure(tracking);
|
|
144
|
+
getLogger().audit('COMPACTION_FAILURE', {
|
|
145
|
+
error: error instanceof Error ? error.message : String(error),
|
|
146
|
+
consecutiveFailures: newTracking.consecutiveFailures,
|
|
147
|
+
trigger,
|
|
148
|
+
}, {
|
|
149
|
+
source: 'session',
|
|
150
|
+
severity: 'medium',
|
|
151
|
+
scope: 'session',
|
|
152
|
+
phase: 'COMPACTION',
|
|
153
|
+
});
|
|
154
|
+
return {
|
|
155
|
+
performed: false,
|
|
156
|
+
tracking: newTracking,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Reactive Compact (Level 2)
|
|
162
|
+
*
|
|
163
|
+
* Emergency compaction when LLM returns prompt-too-long error.
|
|
164
|
+
*/
|
|
165
|
+
export async function reactiveCompact(params) {
|
|
166
|
+
if (!isContextOverflowLike(params.error)) {
|
|
167
|
+
return { performed: false, tracking: params.tracking };
|
|
168
|
+
}
|
|
169
|
+
return autocompact({
|
|
170
|
+
...params,
|
|
171
|
+
trigger: 'reactive',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Main Compaction Pipeline entry point
|
|
176
|
+
*/
|
|
177
|
+
export async function runCompactionPipeline(params) {
|
|
178
|
+
// Level 0 (Microcompact) is already integrated into buildEffectiveConversationContext
|
|
179
|
+
// and refreshSessionSummary.
|
|
180
|
+
// Level 1: Autocompact
|
|
181
|
+
return autocompact(params);
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { getLogger } from '../../observability/logger.js';
|
|
2
|
+
import { DEFAULT_MICROCOMPACT_CONFIG } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Microcompact is a rule-based context reduction utility.
|
|
5
|
+
* It's idempotent, zero-LLM-cost, and operates on the "View" layer.
|
|
6
|
+
*
|
|
7
|
+
* Rules:
|
|
8
|
+
* - Only affects messages with role === 'assistant' that contain tool results.
|
|
9
|
+
* - Preserves the most recent `keepRecentTurns` rounds.
|
|
10
|
+
* - EXCLUDES "stateful" tools (e.g. cd, export) to avoid environment desync.
|
|
11
|
+
* - Preserves the assistant's thought process (the text part) before tools.
|
|
12
|
+
*/
|
|
13
|
+
export function microcompact(messages, config = {}) {
|
|
14
|
+
const mergedConfig = {
|
|
15
|
+
...DEFAULT_MICROCOMPACT_CONFIG,
|
|
16
|
+
...config,
|
|
17
|
+
};
|
|
18
|
+
const { keepRecentTurns, placeholder, statefulTools } = mergedConfig;
|
|
19
|
+
// 1. Identify cutoff turn (1 turn = user + assistant pair, usually)
|
|
20
|
+
// We'll keep the last N assistant messages as "recent"
|
|
21
|
+
let assistantCount = 0;
|
|
22
|
+
const cutoffIndex = [...messages].reverse().findIndex((msg) => {
|
|
23
|
+
if (msg.role === 'assistant') {
|
|
24
|
+
assistantCount++;
|
|
25
|
+
}
|
|
26
|
+
return assistantCount > keepRecentTurns;
|
|
27
|
+
});
|
|
28
|
+
// Calculate the absolute index in the original array
|
|
29
|
+
const absCutoffIndex = cutoffIndex === -1 ? -1 : messages.length - 1 - cutoffIndex;
|
|
30
|
+
let totalClearedCount = 0;
|
|
31
|
+
const result = messages.map((msg, index) => {
|
|
32
|
+
// Only process assistant messages BEFORE the cutoff
|
|
33
|
+
if (index > absCutoffIndex || msg.role !== 'assistant' || !msg.content) {
|
|
34
|
+
return msg;
|
|
35
|
+
}
|
|
36
|
+
const { content } = msg;
|
|
37
|
+
// Pattern to match tool results while capturing tool name and content
|
|
38
|
+
// Improved regex to handle attributes more robustly
|
|
39
|
+
const toolResultRegex = /<tool_result\b[^>]*?name="([^"]+)"[^>]*?>([\s\S]*?)<\/tool_result>/g;
|
|
40
|
+
let hasMatched = false;
|
|
41
|
+
const newContent = content.replace(toolResultRegex, (match, toolName, toolOutput) => {
|
|
42
|
+
// Rule: Skip stateful tools
|
|
43
|
+
if (statefulTools.includes(toolName)) {
|
|
44
|
+
return match;
|
|
45
|
+
}
|
|
46
|
+
// Rule: Skip if already cleared
|
|
47
|
+
if (toolOutput.trim() === placeholder) {
|
|
48
|
+
return match;
|
|
49
|
+
}
|
|
50
|
+
hasMatched = true;
|
|
51
|
+
totalClearedCount++;
|
|
52
|
+
// Extract original tag prefix (including attributes) to preserve them
|
|
53
|
+
const tagMatch = match.match(/<tool_result\b[^>]*?>/);
|
|
54
|
+
const tagPrefix = tagMatch ? tagMatch[0] : `<tool_result name="${toolName}">`;
|
|
55
|
+
return `${tagPrefix}${placeholder}</tool_result>`;
|
|
56
|
+
});
|
|
57
|
+
if (!hasMatched) {
|
|
58
|
+
return msg;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
...msg,
|
|
62
|
+
content: newContent,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
if (totalClearedCount > 0) {
|
|
66
|
+
getLogger().audit('COMPACTION_MICROCOMPACT', {
|
|
67
|
+
clearedCount: totalClearedCount,
|
|
68
|
+
keepRecentTurns,
|
|
69
|
+
}, {
|
|
70
|
+
source: 'session',
|
|
71
|
+
severity: 'low',
|
|
72
|
+
scope: 'session',
|
|
73
|
+
phase: 'COMPACTION',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=microcompact.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Initial compaction tracking state
|
|
4
|
+
*/
|
|
5
|
+
export function createInitialTracking() {
|
|
6
|
+
return {
|
|
7
|
+
compacted: false,
|
|
8
|
+
turnCounter: 0,
|
|
9
|
+
consecutiveFailures: 0,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Update state on successful compaction
|
|
14
|
+
*/
|
|
15
|
+
export function onCompactionSuccess(_prev) {
|
|
16
|
+
return {
|
|
17
|
+
compacted: true,
|
|
18
|
+
compactId: randomUUID(),
|
|
19
|
+
turnCounter: 0,
|
|
20
|
+
consecutiveFailures: 0,
|
|
21
|
+
lastCompactedAt: Date.now(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Update state on compaction failure (for circuit breaker)
|
|
26
|
+
*/
|
|
27
|
+
export function onCompactionFailure(prev) {
|
|
28
|
+
return {
|
|
29
|
+
...prev,
|
|
30
|
+
consecutiveFailures: prev.consecutiveFailures + 1,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Increment turn counter after successful execution cycle
|
|
35
|
+
*/
|
|
36
|
+
export function onNormalTurnComplete(prev) {
|
|
37
|
+
return {
|
|
38
|
+
...prev,
|
|
39
|
+
turnCounter: prev.turnCounter + 1,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if circuit breaker is tripped
|
|
44
|
+
*/
|
|
45
|
+
export function isCircuitBreakerTripped(tracking, maxFailures) {
|
|
46
|
+
return tracking.consecutiveFailures >= maxFailures;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=tracking.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const DEFAULT_MICROCOMPACT_CONFIG = {
|
|
2
|
+
keepRecentTurns: 3, // Keep last 3 rounds (approx 6 messages)
|
|
3
|
+
placeholder: '[Previous tool output cleared for context efficiency]',
|
|
4
|
+
statefulTools: ['cd', 'export', 'env_set', 'enter_worktree', 'exit_worktree'],
|
|
5
|
+
};
|
|
6
|
+
export const DEFAULT_AUTOCOMPACT_CONFIG = {
|
|
7
|
+
tokenThreshold: 8000,
|
|
8
|
+
maxFailures: 3,
|
|
9
|
+
keepRecentMessages: 10,
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { promisify } from 'util';
|
|
2
2
|
import { gzip, gunzip } from 'zlib';
|
|
3
3
|
import { FileAdapter } from '../adapters/fs/index.js';
|
|
4
|
+
import { normalizeSessionArtifactState } from './artifact-state.js';
|
|
5
|
+
import { normalizeToolResultReplacementState, } from './replacement-state.js';
|
|
4
6
|
export const DEFAULT_COMPRESSION_CONFIG = {
|
|
5
7
|
maxKeyMessages: 20,
|
|
6
8
|
maxKeyIterations: 10,
|
|
@@ -53,6 +55,9 @@ export class SessionCompressor {
|
|
|
53
55
|
originalSize,
|
|
54
56
|
compressedSize: 0, // Will be updated after serialization
|
|
55
57
|
compressionRatio: 0, // Will be calculated after serialization
|
|
58
|
+
chatState: session.meta.chatState,
|
|
59
|
+
artifactState: normalizeSessionArtifactState(session.meta.artifactState),
|
|
60
|
+
replacementState: normalizeToolResultReplacementState(session.meta.replacementState),
|
|
56
61
|
},
|
|
57
62
|
compressed: {
|
|
58
63
|
summary: summary.text,
|
|
@@ -106,6 +111,9 @@ export class SessionCompressor {
|
|
|
106
111
|
successfulIterations: compressed.compressed.stats.successfulIterations,
|
|
107
112
|
totalTokens: compressed.compressed.stats.totalTokens,
|
|
108
113
|
snapshots: [], // Will be restored from full data
|
|
114
|
+
chatState: compressed.meta.chatState,
|
|
115
|
+
artifactState: normalizeSessionArtifactState(compressed.meta.artifactState),
|
|
116
|
+
replacementState: normalizeToolResultReplacementState(compressed.meta.replacementState),
|
|
109
117
|
},
|
|
110
118
|
messages: compressed.compressed.keyMessages.map((msg) => ({
|
|
111
119
|
role: msg.role,
|