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.
Files changed (218) hide show
  1. package/dist/cli/argv/headless-detection.js +27 -0
  2. package/dist/cli/chat-flow.js +11 -0
  3. package/dist/cli/chat.js +160 -24
  4. package/dist/cli/commands/chat.js +14 -7
  5. package/dist/cli/commands/flow-mode.js +63 -0
  6. package/dist/cli/commands/registry.js +2 -0
  7. package/dist/cli/commands/run/benchmark-artifacts.js +41 -0
  8. package/dist/cli/commands/run/early-errors.js +23 -0
  9. package/dist/cli/commands/run/handler.js +115 -27
  10. package/dist/cli/commands/run/headless-error-writer.js +8 -0
  11. package/dist/cli/commands/run/loop-params.js +2 -0
  12. package/dist/cli/commands/run/mode.js +2 -5
  13. package/dist/cli/commands/run/parse-options.js +16 -0
  14. package/dist/cli/commands/run/persist-session.js +10 -1
  15. package/dist/cli/commands/run/preflight.js +10 -0
  16. package/dist/cli/commands/run/reporter-factory.js +4 -0
  17. package/dist/cli/commands/run/runtime-llm.js +38 -11
  18. package/dist/cli/commands/run/runtime-options.js +2 -2
  19. package/dist/cli/commands/serve.js +91 -71
  20. package/dist/cli/commands/tool-names.js +78 -78
  21. package/dist/cli/headless/anthropic-stream-normalized-encoder.js +6 -1
  22. package/dist/cli/headless/json-protocol.js +37 -0
  23. package/dist/cli/headless/native-stream-normalized-encoder.js +6 -1
  24. package/dist/cli/headless/protocol-metadata.js +22 -0
  25. package/dist/cli/headless/stream-json-protocol.js +34 -1
  26. package/dist/cli/index.js +6 -4
  27. package/dist/cli/locales/en.js +30 -6
  28. package/dist/cli/program-bootstrap.js +8 -3
  29. package/dist/cli/program-commands.js +5 -1
  30. package/dist/cli/reporters/anthropic-stream.js +7 -1
  31. package/dist/cli/reporters/json.js +4 -0
  32. package/dist/cli/reporters/stream-json.js +17 -2
  33. package/dist/cli/run-cli.js +5 -3
  34. package/dist/cli/slash/runtime.js +27 -12
  35. package/dist/cli/ui/components/CommandInput.js +7 -3
  36. package/dist/cli/ui/components/CommandSuggestionList.js +1 -1
  37. package/dist/cli/utils/command-option-source.js +13 -0
  38. package/dist/cli/utils/verify-resolver.js +8 -4
  39. package/dist/cli/utils/worktree-prepare-resolver.js +7 -3
  40. package/dist/core/adapters/fs/file-adapter.js +6 -0
  41. package/dist/core/adapters/fs/filesystem.js +2 -1
  42. package/dist/core/adapters/git/git-adapter.js +78 -1
  43. package/dist/core/benchmark/patch-artifact.js +124 -0
  44. package/dist/core/benchmark/swe-bench.js +25 -0
  45. package/dist/core/config/load.js +18 -11
  46. package/dist/core/config/resolve-llm.js +12 -0
  47. package/dist/core/config/resolvers/server.js +0 -6
  48. package/dist/core/config/validate.js +73 -21
  49. package/dist/core/context/gatherers/metadata-gatherer.js +1 -0
  50. package/dist/core/context/gatherers/ripgrep-gatherer.js +84 -2
  51. package/dist/core/context/keywords.js +18 -4
  52. package/dist/core/context/service-deps.js +2 -2
  53. package/dist/core/context/service.js +8 -0
  54. package/dist/core/context/steps/context-gather.js +38 -0
  55. package/dist/core/context/summarization/summarizer.js +55 -12
  56. package/dist/core/context/targeting/target-resolver.js +4 -4
  57. package/dist/core/extensions/index.js +23 -5
  58. package/dist/core/extensions/paths.js +31 -0
  59. package/dist/core/extensions/schemas.js +8 -5
  60. package/dist/core/facades/cli-chat.js +6 -2
  61. package/dist/core/facades/cli-command-chat.js +1 -0
  62. package/dist/core/facades/cli-command-tool-names.js +2 -0
  63. package/dist/core/facades/cli-observability.js +1 -1
  64. package/dist/core/facades/cli-run-handler.js +4 -2
  65. package/dist/core/facades/cli-run-persist-session.js +1 -0
  66. package/dist/core/facades/cli-serve.js +2 -4
  67. package/dist/core/facades/cli-utils-worktree.js +1 -1
  68. package/dist/core/failure/diagnostics.js +53 -1
  69. package/dist/core/grizzco/dsl/llm-strategy.js +4 -1
  70. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +67 -9
  71. package/dist/core/grizzco/engine/pipeline/pipeline.js +6 -2
  72. package/dist/core/grizzco/engine/transaction/attempt-failure.js +90 -15
  73. package/dist/core/grizzco/engine/transaction/report-mapper.js +17 -3
  74. package/dist/core/grizzco/engine/transaction/transaction-runner.js +165 -7
  75. package/dist/core/grizzco/flows/AutopilotFlow.js +18 -0
  76. package/dist/core/grizzco/flows/flow-dispatch.js +11 -0
  77. package/dist/core/grizzco/steps/answer.js +13 -14
  78. package/dist/core/grizzco/steps/autopilot.js +396 -0
  79. package/dist/core/grizzco/steps/cache-sharing.js +29 -0
  80. package/dist/core/grizzco/steps/explore.js +37 -21
  81. package/dist/core/grizzco/steps/generateReview.js +2 -5
  82. package/dist/core/grizzco/steps/patch/apply-check.js +10 -0
  83. package/dist/core/grizzco/steps/patch/diff-normalization.js +70 -0
  84. package/dist/core/grizzco/steps/patch/diff-salvage.js +46 -0
  85. package/dist/core/grizzco/steps/patch/prompt-input.js +42 -0
  86. package/dist/core/grizzco/steps/patch.js +105 -146
  87. package/dist/core/grizzco/steps/plan.js +101 -25
  88. package/dist/core/grizzco/steps/preflight.js +5 -6
  89. package/dist/core/grizzco/steps/request-assembly.js +78 -0
  90. package/dist/core/grizzco/steps/research.js +39 -36
  91. package/dist/core/grizzco/steps/tool-runtime.js +47 -0
  92. package/dist/core/grizzco/steps/verify-shared.js +23 -0
  93. package/dist/core/grizzco/steps/verify.js +13 -21
  94. package/dist/core/llm/ai-sdk/chat-executor.js +2 -0
  95. package/dist/core/llm/ai-sdk/high-level-phase-specs.js +63 -0
  96. package/dist/core/llm/ai-sdk/message-mapper.js +40 -10
  97. package/dist/core/llm/ai-sdk/provider-factory.js +14 -0
  98. package/dist/core/llm/ai-sdk/request-params.js +73 -0
  99. package/dist/core/llm/ai-sdk/result-mapper.js +16 -0
  100. package/dist/core/llm/ai-sdk.js +112 -27
  101. package/dist/core/llm/capabilities.js +12 -0
  102. package/dist/core/llm/contracts/repair.js +36 -30
  103. package/dist/core/llm/errors.js +83 -2
  104. package/dist/core/llm/message-composition.js +7 -22
  105. package/dist/core/llm/phase-router.js +29 -10
  106. package/dist/core/llm/redact.js +28 -3
  107. package/dist/core/llm/registry.js +2 -0
  108. package/dist/core/llm/request-augmentation.js +55 -0
  109. package/dist/core/llm/request-envelope.js +334 -0
  110. package/dist/core/llm/shared-request-assembly.js +35 -0
  111. package/dist/core/llm/stream-utils.js +13 -4
  112. package/dist/core/llm/utils.js +18 -29
  113. package/dist/core/memory/relevant-retrieval.js +144 -0
  114. package/dist/core/observability/logger.js +11 -2
  115. package/dist/core/patch/diff.js +1 -0
  116. package/dist/core/prompts/registry.js +39 -2
  117. package/dist/core/prompts/runtime.js +50 -12
  118. package/dist/core/prompts/templates/phases/patch_user.hbs +2 -5
  119. package/dist/core/prompts/templates/phases/research_user.hbs +11 -0
  120. package/dist/core/prompts/templates/phases/review_user.hbs +3 -0
  121. package/dist/core/prompts/templates/system/answer_system.hbs +5 -0
  122. package/dist/core/prompts/templates/system/autopilot_system.hbs +11 -0
  123. package/dist/core/prompts/templates/system/explore_system.hbs +14 -23
  124. package/dist/core/prompts/templates/system/main_system.hbs +4 -16
  125. package/dist/core/prompts/templates/system/patch_system.hbs +39 -8
  126. package/dist/core/prompts/templates/system/plan_system.hbs +86 -1
  127. package/dist/core/prompts/templates/system/research_system.hbs +2 -0
  128. package/dist/core/protocols/a2a/agent-card.js +3 -2
  129. package/dist/core/protocols/a2a/sdk/executor.js +2 -1
  130. package/dist/core/protocols/a2a/sdk/server.js +0 -1
  131. package/dist/core/protocols/acp/formal-agent.js +74 -51
  132. package/dist/core/protocols/acp/handlers.js +5 -1
  133. package/dist/core/protocols/acp/permission-provider.js +1 -1
  134. package/dist/core/protocols/shared/flow-mode-mapping.js +23 -0
  135. package/dist/core/public-capabilities/flow-mode-metadata.js +39 -0
  136. package/dist/core/public-capabilities/projections.js +29 -0
  137. package/dist/core/public-capabilities/registry.js +26 -0
  138. package/dist/core/public-capabilities/types.js +2 -0
  139. package/dist/core/runtime/agent-server-runtime.js +47 -43
  140. package/dist/core/runtime/execution-profile.js +67 -0
  141. package/dist/core/session/artifact-state.js +160 -0
  142. package/dist/core/session/compaction/index.js +183 -0
  143. package/dist/core/session/compaction/microcompact.js +78 -0
  144. package/dist/core/session/compaction/tracking.js +48 -0
  145. package/dist/core/session/compaction/types.js +11 -0
  146. package/dist/core/session/compression.js +8 -0
  147. package/dist/core/session/manager.js +244 -8
  148. package/dist/core/session/pruning-strategy.js +55 -9
  149. package/dist/core/session/replacement-preview-provider.js +24 -0
  150. package/dist/core/session/replacement-state.js +131 -0
  151. package/dist/core/session/resume-repair/pipeline.js +79 -0
  152. package/dist/core/session/resume-repair/stages/load-raw-archive-state.js +40 -0
  153. package/dist/core/session/resume-repair/stages/reattach-runtime-state.js +8 -0
  154. package/dist/core/session/resume-repair/stages/recover-orphaned-branches.js +10 -0
  155. package/dist/core/session/resume-repair/stages/relink-boundary-and-tail.js +36 -0
  156. package/dist/core/session/resume-repair/stages/replay-startup-hooks.js +23 -0
  157. package/dist/core/session/resume-repair/stages/rescue-stale-metadata.js +17 -0
  158. package/dist/core/session/resume-repair/types.js +2 -0
  159. package/dist/core/session/summary-sync.js +164 -13
  160. package/dist/core/session/token-tracker.js +6 -0
  161. package/dist/core/skills/audit.js +34 -0
  162. package/dist/core/skills/bridge.js +84 -7
  163. package/dist/core/skills/discovery.js +94 -0
  164. package/dist/core/skills/feature-flags.js +52 -0
  165. package/dist/core/skills/index.js +1 -1
  166. package/dist/core/skills/loader.js +195 -20
  167. package/dist/core/skills/parser.js +296 -24
  168. package/dist/core/skills/permissions.js +117 -0
  169. package/dist/core/skills/runtime/MicroTaskRunner.js +10 -4
  170. package/dist/core/skills/runtime/SkillRunner.js +240 -61
  171. package/dist/core/strata/layers/shadow-driver/shadow-driver.js +37 -7
  172. package/dist/core/strata/layers/worktree.js +67 -10
  173. package/dist/core/strata/runtime/synchronizer.js +29 -2
  174. package/dist/core/streaming/stream-assembler.js +75 -31
  175. package/dist/core/sub-agent/context-snapshot.js +156 -0
  176. package/dist/core/sub-agent/core/loop.js +1 -1
  177. package/dist/core/sub-agent/core/manager.js +119 -20
  178. package/dist/core/sub-agent/dispatch-policy.js +29 -0
  179. package/dist/core/sub-agent/prefix-consistency.js +48 -0
  180. package/dist/core/sub-agent/registry-defaults.js +4 -0
  181. package/dist/core/sub-agent/tools/task-spawn.js +79 -2
  182. package/dist/core/sub-agent/types.js +134 -5
  183. package/dist/core/tools/audit.js +13 -4
  184. package/dist/core/tools/builtin/ast-grep.js +1 -1
  185. package/dist/core/tools/builtin/ast.js +1 -1
  186. package/dist/core/tools/builtin/benchmark.js +360 -0
  187. package/dist/core/tools/builtin/code-search/backends/rg.js +2 -1
  188. package/dist/core/tools/builtin/code-search/executor.js +6 -1
  189. package/dist/core/tools/builtin/code-search/spec.js +26 -2
  190. package/dist/core/tools/builtin/fs.js +256 -23
  191. package/dist/core/tools/builtin/git.js +2 -2
  192. package/dist/core/tools/builtin/index.js +51 -2
  193. package/dist/core/tools/builtin/interaction.js +8 -1
  194. package/dist/core/tools/builtin/plan.js +37 -15
  195. package/dist/core/tools/builtin/shell.js +1 -1
  196. package/dist/core/tools/loader.js +39 -16
  197. package/dist/core/tools/mapper.js +17 -3
  198. package/dist/core/tools/parallel/scheduler.js +35 -4
  199. package/dist/core/tools/permissions/permission-rules.js +5 -10
  200. package/dist/core/tools/policy.js +6 -1
  201. package/dist/core/tools/recoverable-tool-errors.js +10 -0
  202. package/dist/core/tools/router.js +24 -6
  203. package/dist/core/tools/session.js +458 -48
  204. package/dist/core/tools/tool-visibility.js +62 -0
  205. package/dist/core/tools/types.js +9 -1
  206. package/dist/core/types/execution.js +4 -0
  207. package/dist/core/types/flow-mode.js +8 -0
  208. package/dist/core/utils/path.js +52 -0
  209. package/dist/core/verification/runner.js +4 -1
  210. package/dist/languages/typescript/index.js +4 -1
  211. package/dist/locales/en.js +35 -2
  212. package/dist/utils/eol.js +1 -1
  213. package/package.json +13 -6
  214. package/scripts/fix-es-abstract-compat.js +77 -0
  215. package/dist/core/runtime/fastify-server-bundle.js +0 -26
  216. package/dist/core/runtime/sidecar-fastify-plugin.js +0 -35
  217. package/dist/core/runtime/sidecar-paths.js +0 -47
  218. 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,