@bastani/atomic 0.9.5-alpha.3 → 0.9.5-alpha.5

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 (83) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/builtin/cursor/package.json +2 -2
  3. package/dist/builtin/intercom/package.json +1 -1
  4. package/dist/builtin/mcp/package.json +1 -1
  5. package/dist/builtin/subagents/CHANGELOG.md +7 -0
  6. package/dist/builtin/subagents/agents/debugger.md +2 -2
  7. package/dist/builtin/subagents/package.json +1 -1
  8. package/dist/builtin/web-access/package.json +1 -1
  9. package/dist/builtin/workflows/CHANGELOG.md +18 -0
  10. package/dist/builtin/workflows/builtin/deep-research-codebase-utils.ts +5 -5
  11. package/dist/builtin/workflows/builtin/goal-prompts.ts +2 -0
  12. package/dist/builtin/workflows/builtin/goal-runner.ts +5 -5
  13. package/dist/builtin/workflows/builtin/goal-types.ts +1 -0
  14. package/dist/builtin/workflows/builtin/goal.d.ts +2 -0
  15. package/dist/builtin/workflows/builtin/goal.ts +9 -0
  16. package/dist/builtin/workflows/builtin/index.d.ts +2 -0
  17. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +5 -5
  18. package/dist/builtin/workflows/builtin/ralph-models.ts +25 -25
  19. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +138 -0
  20. package/dist/builtin/workflows/builtin/ralph-runner.ts +10 -118
  21. package/dist/builtin/workflows/builtin/shared-prompts.ts +5 -0
  22. package/dist/builtin/workflows/package.json +1 -1
  23. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  24. package/dist/core/agent-session-auto-compaction.js +3 -2
  25. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  26. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  27. package/dist/core/agent-session-compaction.js +25 -4
  28. package/dist/core/agent-session-compaction.js.map +1 -1
  29. package/dist/core/compaction/context-compaction-critical.d.ts +15 -0
  30. package/dist/core/compaction/context-compaction-critical.d.ts.map +1 -0
  31. package/dist/core/compaction/context-compaction-critical.js +57 -0
  32. package/dist/core/compaction/context-compaction-critical.js.map +1 -0
  33. package/dist/core/compaction/context-compaction-eviction.d.ts +4 -0
  34. package/dist/core/compaction/context-compaction-eviction.d.ts.map +1 -0
  35. package/dist/core/compaction/context-compaction-eviction.js +172 -0
  36. package/dist/core/compaction/context-compaction-eviction.js.map +1 -0
  37. package/dist/core/compaction/context-compaction-runner.d.ts +7 -1
  38. package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
  39. package/dist/core/compaction/context-compaction-runner.js +97 -21
  40. package/dist/core/compaction/context-compaction-runner.js.map +1 -1
  41. package/dist/core/compaction/context-transcript-analysis.d.ts +3 -0
  42. package/dist/core/compaction/context-transcript-analysis.d.ts.map +1 -1
  43. package/dist/core/compaction/context-transcript-analysis.js +3 -3
  44. package/dist/core/compaction/context-transcript-analysis.js.map +1 -1
  45. package/dist/modes/interactive/interactive-auth-login.d.ts.map +1 -1
  46. package/dist/modes/interactive/interactive-auth-login.js +0 -1
  47. package/dist/modes/interactive/interactive-auth-login.js.map +1 -1
  48. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  49. package/dist/modes/interactive/interactive-hotkeys-debug.js +3 -3
  50. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  51. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  52. package/dist/modes/interactive/interactive-input-handling.js +0 -51
  53. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  54. package/dist/modes/interactive/interactive-mode-base.d.ts +2 -4
  55. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  56. package/dist/modes/interactive/interactive-mode-base.js +2 -4
  57. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  58. package/dist/modes/interactive/interactive-mode-surface.d.ts +3 -9
  59. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  60. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  61. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  62. package/dist/modes/interactive/interactive-model-routing.js +0 -3
  63. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  64. package/dist/modes/interactive/interactive-onboarding.d.ts +0 -9
  65. package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -1
  66. package/dist/modes/interactive/interactive-onboarding.js +23 -201
  67. package/dist/modes/interactive/interactive-onboarding.js.map +1 -1
  68. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  69. package/dist/modes/interactive/interactive-session-routing.js +2 -2
  70. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  71. package/dist/modes/interactive/interactive-slash-commands.d.ts.map +1 -1
  72. package/dist/modes/interactive/interactive-slash-commands.js +1 -1
  73. package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
  74. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  75. package/dist/modes/interactive/interactive-startup.js +36 -43
  76. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  77. package/docs/compaction.md +45 -27
  78. package/docs/quickstart.md +3 -3
  79. package/docs/settings.md +1 -1
  80. package/docs/usage.md +1 -1
  81. package/docs/workflows.md +7 -3
  82. package/npm-shrinkwrap.json +23 -23
  83. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-compaction-critical.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/context-compaction-critical.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAKpI,eAAO,MAAM,4CAA4C,IAAI,CAAC;AAE9D,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,2BAA2B,GAAG,2BAA2B,CAKjH;AAED,wBAAgB,yCAAyC,CACxD,KAAK,EAAE,0BAA0B,EACjC,UAAU,EAAE,qBAAqB,GAC/B,OAAO,CAUT;AAED;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,qBAAqB,GAAG,qBAAqB,CAgB3G;AAED,eAAO,MAAM,2CAA2C,0iCAK9B,CAAC","sourcesContent":["import type { CompactableTranscript, CompactableTranscriptEntry, ContextCompactionParameters } from \"./context-compaction-types.ts\";\nimport { getTranscriptCompactionParameters } from \"./context-compaction-strategy.ts\";\nimport { isTaskBearingEntry } from \"./context-deletion-targets.ts\";\nimport { hasAssistantError, hasFailedBashExecution, hasToolResultError } from \"./context-transcript-analysis.ts\";\n\nexport const CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT = 5;\n\nexport function criticalCompactionParameters(parameters: ContextCompactionParameters): ContextCompactionParameters {\n\treturn {\n\t\t...parameters,\n\t\tpreserve_recent: Math.max(parameters.preserve_recent, CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT),\n\t};\n}\n\nexport function isCriticalOverflowProtectedEntryDeletable(\n\tentry: CompactableTranscriptEntry,\n\ttranscript: CompactableTranscript,\n): boolean {\n\tif (!entry.protected) return true;\n\tconst entryIndex = transcript.entries.findIndex((candidate) => candidate.entryId === entry.entryId);\n\tif (entryIndex < 0) return false;\n\tconst recentBoundary = Math.max(0, transcript.entries.length - CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT);\n\tif (entryIndex >= recentBoundary) return false;\n\tif (hasAssistantError(entry.message) || hasToolResultError(entry.message) || hasFailedBashExecution(entry.message)) {\n\t\treturn false;\n\t}\n\treturn isTaskBearingEntry(entry);\n}\n\n/**\n * Return a transcript for critical overflow passes. The effective recent guard is\n * widened to max(configured preserve_recent, 5) over all entries, restoring the\n * pre-#1399 critical-overflow last-5 floor through the existing recent-target\n * validation path. Only stale protected task-bearing entries outside that floor\n * are relaxed; `protectedEntryIds` is rebuilt from still-protected entries so\n * persisted results/events stay disjoint from deleted targets.\n */\nexport function relaxTranscriptForCriticalEviction(transcript: CompactableTranscript): CompactableTranscript {\n\tconst entries = transcript.entries.map((entry) => {\n\t\tif (!entry.protected || !isCriticalOverflowProtectedEntryDeletable(entry, transcript)) return entry;\n\t\treturn {\n\t\t\t...entry,\n\t\t\tprotected: false,\n\t\t\tcontentBlocks: entry.contentBlocks.map((block) => ({ ...block, protected: false })),\n\t\t};\n\t});\n\tconst parameters = criticalCompactionParameters(getTranscriptCompactionParameters(transcript));\n\treturn {\n\t\t...transcript,\n\t\tentries,\n\t\tparameters,\n\t\tprotectedEntryIds: entries.filter((entry) => entry.protected).map((entry) => entry.entryId),\n\t};\n}\n\nexport const CONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT = `\n<critical-overflow-mode>\nThe previous model request overflowed its context window. This is a critical LRU-style compaction pass. First delete stale unprotected context. If that is not enough, you may also delete the earliest formerly-protected entries or protected content shown in the manifest. Evict old low-signal context first, including old reasoning/thinking traces when they are not part of the latest retained assistant message, then older user/custom/summary context while preserving recent entries, unresolved errors, failed commands, and enough task-bearing context for the assistant to continue.\n\nSafety invariant: the latest retained assistant message cannot be modified when it contains content blocks with type \"thinking\" or \"redacted_thinking\". Do not delete, target, or suggest any content block in that latest thinking-bearing assistant message, including sibling text or tool-call blocks. Older non-latest thinking/redacted_thinking blocks may be deleted during critical overflow when validation allows it.\n</critical-overflow-mode>`;\n"]}
@@ -0,0 +1,57 @@
1
+ import { getTranscriptCompactionParameters } from "./context-compaction-strategy.js";
2
+ import { isTaskBearingEntry } from "./context-deletion-targets.js";
3
+ import { hasAssistantError, hasFailedBashExecution, hasToolResultError } from "./context-transcript-analysis.js";
4
+ export const CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT = 5;
5
+ export function criticalCompactionParameters(parameters) {
6
+ return {
7
+ ...parameters,
8
+ preserve_recent: Math.max(parameters.preserve_recent, CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT),
9
+ };
10
+ }
11
+ export function isCriticalOverflowProtectedEntryDeletable(entry, transcript) {
12
+ if (!entry.protected)
13
+ return true;
14
+ const entryIndex = transcript.entries.findIndex((candidate) => candidate.entryId === entry.entryId);
15
+ if (entryIndex < 0)
16
+ return false;
17
+ const recentBoundary = Math.max(0, transcript.entries.length - CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT);
18
+ if (entryIndex >= recentBoundary)
19
+ return false;
20
+ if (hasAssistantError(entry.message) || hasToolResultError(entry.message) || hasFailedBashExecution(entry.message)) {
21
+ return false;
22
+ }
23
+ return isTaskBearingEntry(entry);
24
+ }
25
+ /**
26
+ * Return a transcript for critical overflow passes. The effective recent guard is
27
+ * widened to max(configured preserve_recent, 5) over all entries, restoring the
28
+ * pre-#1399 critical-overflow last-5 floor through the existing recent-target
29
+ * validation path. Only stale protected task-bearing entries outside that floor
30
+ * are relaxed; `protectedEntryIds` is rebuilt from still-protected entries so
31
+ * persisted results/events stay disjoint from deleted targets.
32
+ */
33
+ export function relaxTranscriptForCriticalEviction(transcript) {
34
+ const entries = transcript.entries.map((entry) => {
35
+ if (!entry.protected || !isCriticalOverflowProtectedEntryDeletable(entry, transcript))
36
+ return entry;
37
+ return {
38
+ ...entry,
39
+ protected: false,
40
+ contentBlocks: entry.contentBlocks.map((block) => ({ ...block, protected: false })),
41
+ };
42
+ });
43
+ const parameters = criticalCompactionParameters(getTranscriptCompactionParameters(transcript));
44
+ return {
45
+ ...transcript,
46
+ entries,
47
+ parameters,
48
+ protectedEntryIds: entries.filter((entry) => entry.protected).map((entry) => entry.entryId),
49
+ };
50
+ }
51
+ export const CONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT = `
52
+ <critical-overflow-mode>
53
+ The previous model request overflowed its context window. This is a critical LRU-style compaction pass. First delete stale unprotected context. If that is not enough, you may also delete the earliest formerly-protected entries or protected content shown in the manifest. Evict old low-signal context first, including old reasoning/thinking traces when they are not part of the latest retained assistant message, then older user/custom/summary context while preserving recent entries, unresolved errors, failed commands, and enough task-bearing context for the assistant to continue.
54
+
55
+ Safety invariant: the latest retained assistant message cannot be modified when it contains content blocks with type "thinking" or "redacted_thinking". Do not delete, target, or suggest any content block in that latest thinking-bearing assistant message, including sibling text or tool-call blocks. Older non-latest thinking/redacted_thinking blocks may be deleted during critical overflow when validation allows it.
56
+ </critical-overflow-mode>`;
57
+ //# sourceMappingURL=context-compaction-critical.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-compaction-critical.js","sourceRoot":"","sources":["../../../src/core/compaction/context-compaction-critical.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iCAAiC,EAAE,MAAM,kCAAkC,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAEjH,MAAM,CAAC,MAAM,4CAA4C,GAAG,CAAC,CAAC;AAE9D,MAAM,UAAU,4BAA4B,CAAC,UAAuC;IACnF,OAAO;QACN,GAAG,UAAU;QACb,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,eAAe,EAAE,4CAA4C,CAAC;KACnG,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yCAAyC,CACxD,KAAiC,EACjC,UAAiC;IAEjC,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC;IACpG,IAAI,UAAU,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM,GAAG,4CAA4C,CAAC,CAAC;IAC7G,IAAI,UAAU,IAAI,cAAc;QAAE,OAAO,KAAK,CAAC;IAC/C,IAAI,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,sBAAsB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACpH,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kCAAkC,CAAC,UAAiC;IACnF,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAChD,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,yCAAyC,CAAC,KAAK,EAAE,UAAU,CAAC;YAAE,OAAO,KAAK,CAAC;QACpG,OAAO;YACN,GAAG,KAAK;YACR,SAAS,EAAE,KAAK;YAChB,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;SACnF,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,4BAA4B,CAAC,iCAAiC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/F,OAAO;QACN,GAAG,UAAU;QACb,OAAO;QACP,UAAU;QACV,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;KAC3F,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,2CAA2C,GAAG;;;;;0BAKjC,CAAC","sourcesContent":["import type { CompactableTranscript, CompactableTranscriptEntry, ContextCompactionParameters } from \"./context-compaction-types.ts\";\nimport { getTranscriptCompactionParameters } from \"./context-compaction-strategy.ts\";\nimport { isTaskBearingEntry } from \"./context-deletion-targets.ts\";\nimport { hasAssistantError, hasFailedBashExecution, hasToolResultError } from \"./context-transcript-analysis.ts\";\n\nexport const CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT = 5;\n\nexport function criticalCompactionParameters(parameters: ContextCompactionParameters): ContextCompactionParameters {\n\treturn {\n\t\t...parameters,\n\t\tpreserve_recent: Math.max(parameters.preserve_recent, CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT),\n\t};\n}\n\nexport function isCriticalOverflowProtectedEntryDeletable(\n\tentry: CompactableTranscriptEntry,\n\ttranscript: CompactableTranscript,\n): boolean {\n\tif (!entry.protected) return true;\n\tconst entryIndex = transcript.entries.findIndex((candidate) => candidate.entryId === entry.entryId);\n\tif (entryIndex < 0) return false;\n\tconst recentBoundary = Math.max(0, transcript.entries.length - CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT);\n\tif (entryIndex >= recentBoundary) return false;\n\tif (hasAssistantError(entry.message) || hasToolResultError(entry.message) || hasFailedBashExecution(entry.message)) {\n\t\treturn false;\n\t}\n\treturn isTaskBearingEntry(entry);\n}\n\n/**\n * Return a transcript for critical overflow passes. The effective recent guard is\n * widened to max(configured preserve_recent, 5) over all entries, restoring the\n * pre-#1399 critical-overflow last-5 floor through the existing recent-target\n * validation path. Only stale protected task-bearing entries outside that floor\n * are relaxed; `protectedEntryIds` is rebuilt from still-protected entries so\n * persisted results/events stay disjoint from deleted targets.\n */\nexport function relaxTranscriptForCriticalEviction(transcript: CompactableTranscript): CompactableTranscript {\n\tconst entries = transcript.entries.map((entry) => {\n\t\tif (!entry.protected || !isCriticalOverflowProtectedEntryDeletable(entry, transcript)) return entry;\n\t\treturn {\n\t\t\t...entry,\n\t\t\tprotected: false,\n\t\t\tcontentBlocks: entry.contentBlocks.map((block) => ({ ...block, protected: false })),\n\t\t};\n\t});\n\tconst parameters = criticalCompactionParameters(getTranscriptCompactionParameters(transcript));\n\treturn {\n\t\t...transcript,\n\t\tentries,\n\t\tparameters,\n\t\tprotectedEntryIds: entries.filter((entry) => entry.protected).map((entry) => entry.entryId),\n\t};\n}\n\nexport const CONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT = `\n<critical-overflow-mode>\nThe previous model request overflowed its context window. This is a critical LRU-style compaction pass. First delete stale unprotected context. If that is not enough, you may also delete the earliest formerly-protected entries or protected content shown in the manifest. Evict old low-signal context first, including old reasoning/thinking traces when they are not part of the latest retained assistant message, then older user/custom/summary context while preserving recent entries, unresolved errors, failed commands, and enough task-bearing context for the assistant to continue.\n\nSafety invariant: the latest retained assistant message cannot be modified when it contains content blocks with type \"thinking\" or \"redacted_thinking\". Do not delete, target, or suggest any content block in that latest thinking-bearing assistant message, including sibling text or tool-call blocks. Older non-latest thinking/redacted_thinking blocks may be deleted during critical overflow when validation allows it.\n</critical-overflow-mode>`;\n"]}
@@ -0,0 +1,4 @@
1
+ import type { CompactableTranscript, ValidatedContextDeletionResult } from "./context-compaction-types.ts";
2
+ export declare const CONTEXT_COMPACTION_MAX_EVICTION_PASSES = 50;
3
+ export declare function runDeterministicContextEviction(transcript: CompactableTranscript, tokenBudget: number): ValidatedContextDeletionResult;
4
+ //# sourceMappingURL=context-compaction-eviction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-compaction-eviction.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/context-compaction-eviction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAM3G,eAAO,MAAM,sCAAsC,KAAK,CAAC;AAgKzD,wBAAgB,+BAA+B,CAC9C,UAAU,EAAE,qBAAqB,EACjC,WAAW,EAAE,MAAM,GACjB,8BAA8B,CAmDhC","sourcesContent":["import type { ContextDeletionTarget } from \"../session-manager.ts\";\nimport type { CompactableTranscript, ValidatedContextDeletionResult } from \"./context-compaction-types.ts\";\nimport { validateContextDeletionRequest } from \"./context-deletion-application.ts\";\nimport { canDeleteTarget, deletionRequestFromTargets, isTaskBearingEntry } from \"./context-deletion-targets.ts\";\nimport { relaxTranscriptForCriticalEviction } from \"./context-compaction-critical.ts\";\nimport { assistantEntryHasThinkingContentBlock } from \"./context-transcript-analysis.ts\";\n\nexport const CONTEXT_COMPACTION_MAX_EVICTION_PASSES = 50;\n\nfunction terminalDeterministicEvictionError(\n\treason: string,\n\tlastValidated: ValidatedContextDeletionResult | undefined,\n\ttokenBudget: number,\n): Error {\n\tconst statsText = lastValidated\n\t\t? `achieved tokensAfter=${lastValidated.stats.tokensAfter}, reduction=${lastValidated.stats.percentReduction}%, deletionTargets=${lastValidated.deletedTargets.length}`\n\t\t: \"achieved no validated deletion targets\";\n\treturn new Error(\n\t\t`Context deterministic overflow eviction failed: ${reason}; ${statsText}; budget=${tokenBudget}; nothing more was safely deletable`,\n\t);\n}\n\n\nfunction hasFitBudget(result: ValidatedContextDeletionResult | undefined, tokenBudget: number): result is ValidatedContextDeletionResult {\n\treturn result !== undefined && result.deletedTargets.length > 0 && result.stats.tokensAfter <= tokenBudget;\n}\n\nfunction isThinkingAssistantEntryTarget(transcript: CompactableTranscript, target: ContextDeletionTarget): boolean {\n\tif (target.kind !== \"entry\") return false;\n\tconst entry = transcript.entries.find((candidate) => candidate.entryId === target.entryId);\n\treturn entry?.role === \"assistant\" && assistantEntryHasThinkingContentBlock(entry);\n}\n\nfunction isTaskBearingEntryTarget(transcript: CompactableTranscript, target: ContextDeletionTarget): boolean {\n\tif (target.kind !== \"entry\") return false;\n\tconst entry = transcript.entries.find((candidate) => candidate.entryId === target.entryId);\n\treturn entry !== undefined && isTaskBearingEntry(entry);\n}\n\nfunction targetSignature(targets: readonly ContextDeletionTarget[]): string {\n\treturn targets\n\t\t.map((target) => `${target.kind}:${target.entryId}:${target.kind === \"content_block\" ? target.blockIndex : \"\"}`)\n\t\t.join(\"|\");\n}\n\nfunction entryOrder(transcript: CompactableTranscript, entryId: string): number {\n\tconst index = transcript.entries.findIndex((entry) => entry.entryId === entryId);\n\treturn index < 0 ? Number.MAX_SAFE_INTEGER : index;\n}\n\nfunction entryTokenEstimate(transcript: CompactableTranscript, entryId: string): number {\n\treturn transcript.entries.find((entry) => entry.entryId === entryId)?.tokenEstimate ?? 0;\n}\n\nfunction smallestTaskTarget(transcript: CompactableTranscript, targets: readonly ContextDeletionTarget[]): ContextDeletionTarget | undefined {\n\treturn [...targets]\n\t\t.filter((target) => isTaskBearingEntryTarget(transcript, target))\n\t\t.sort((left: ContextDeletionTarget, right: ContextDeletionTarget) => {\n\t\t\tconst tokenDelta = entryTokenEstimate(transcript, left.entryId) - entryTokenEstimate(transcript, right.entryId);\n\t\t\tif (tokenDelta !== 0) return tokenDelta;\n\t\t\treturn entryOrder(transcript, left.entryId) - entryOrder(transcript, right.entryId);\n\t\t})[0];\n}\n\nfunction newestThinkingTarget(transcript: CompactableTranscript, targets: readonly ContextDeletionTarget[]): ContextDeletionTarget | undefined {\n\treturn [...targets]\n\t\t.filter((target) => isThinkingAssistantEntryTarget(transcript, target))\n\t\t.sort((left: ContextDeletionTarget, right: ContextDeletionTarget) => entryOrder(transcript, right.entryId) - entryOrder(transcript, left.entryId))[0];\n}\n\ntype ThinkingExchange = \"keep-all\" | \"drop-all\" | \"retain-newest\";\ntype TaskExchange = \"keep-all\" | \"drop-all\" | \"retain-smallest\";\n\nconst THINKING_EXCHANGES: readonly ThinkingExchange[] = [\"keep-all\", \"drop-all\", \"retain-newest\"];\nconst TASK_EXCHANGES: readonly TaskExchange[] = [\"keep-all\", \"drop-all\", \"retain-smallest\"];\n\nfunction shouldDeleteTarget(\n\ttranscript: CompactableTranscript,\n\ttarget: ContextDeletionTarget,\n\tthinkingExchange: ThinkingExchange,\n\ttaskExchange: TaskExchange,\n\tretainedThinking: ContextDeletionTarget | undefined,\n\tretainedTask: ContextDeletionTarget | undefined,\n): boolean {\n\tif (thinkingExchange === \"drop-all\" && isThinkingAssistantEntryTarget(transcript, target)) return false;\n\tif (thinkingExchange === \"retain-newest\" && retainedThinking !== undefined && targetSignature([target]) === targetSignature([retainedThinking])) return false;\n\tif (taskExchange === \"drop-all\" && isTaskBearingEntryTarget(transcript, target)) return false;\n\tif (taskExchange === \"retain-smallest\" && retainedTask !== undefined && targetSignature([target]) === targetSignature([retainedTask])) return false;\n\treturn true;\n}\n\nfunction buildExchangePlan(\n\ttranscript: CompactableTranscript,\n\twithCandidate: readonly ContextDeletionTarget[],\n\tthinkingExchange: ThinkingExchange,\n\ttaskExchange: TaskExchange,\n): ContextDeletionTarget[] {\n\tconst retainedThinking = thinkingExchange === \"retain-newest\" ? newestThinkingTarget(transcript, withCandidate) : undefined;\n\tconst retainedTask = taskExchange === \"retain-smallest\" ? smallestTaskTarget(transcript, withCandidate) : undefined;\n\treturn withCandidate.filter((target) =>\n\t\tshouldDeleteTarget(transcript, target, thinkingExchange, taskExchange, retainedThinking, retainedTask),\n\t);\n}\n\nfunction shouldBuildExchangePlan(\n\twithCandidate: readonly ContextDeletionTarget[],\n\tthinkingExchange: ThinkingExchange,\n\ttaskExchange: TaskExchange,\n): boolean {\n\tif (thinkingExchange === \"keep-all\" && taskExchange === \"keep-all\") return false;\n\tif (thinkingExchange === \"retain-newest\" && withCandidate.filter((target) => target.kind === \"entry\").length === 0) return false;\n\treturn true;\n}\n\nfunction addExchangePlan(\n\tplans: ContextDeletionTarget[][],\n\tseen: Set<string>,\n\toriginalTargets: readonly ContextDeletionTarget[],\n\ttargets: readonly ContextDeletionTarget[],\n): void {\n\tif (targetSignature(targets) === targetSignature(originalTargets)) return;\n\tconst signature = targetSignature(targets);\n\tif (seen.has(signature)) return;\n\tseen.add(signature);\n\tplans.push([...targets]);\n}\n\nfunction exchangePlans(\n\ttranscript: CompactableTranscript,\n\tplanned: readonly ContextDeletionTarget[],\n\tentryId: string,\n): ContextDeletionTarget[][] {\n\tconst currentTarget: ContextDeletionTarget = { kind: \"entry\", entryId };\n\tconst withCandidate = [...planned, currentTarget];\n\tconst plans: ContextDeletionTarget[][] = [];\n\tconst seen = new Set<string>();\n\t// Deterministic cross-product order: thinking handling outermost, then task handling.\n\t// Variants are keep/drop-all/retain-one for each non-monotone category, minus the\n\t// keep-all/keep-all no-op; signature dedup collapses degenerate variants.\n\tfor (const thinkingExchange of THINKING_EXCHANGES) {\n\t\tfor (const taskExchange of TASK_EXCHANGES) {\n\t\t\tif (!shouldBuildExchangePlan(withCandidate, thinkingExchange, taskExchange)) continue;\n\t\t\taddExchangePlan(plans, seen, withCandidate, buildExchangePlan(transcript, withCandidate, thinkingExchange, taskExchange));\n\t\t}\n\t}\n\treturn plans;\n}\n\nfunction validatedExchange(\n\ttranscript: CompactableTranscript,\n\tplanned: readonly ContextDeletionTarget[],\n\tentryId: string,\n\tlastValidated: ValidatedContextDeletionResult | undefined,\n): ValidatedContextDeletionResult | undefined {\n\tlet best: ValidatedContextDeletionResult | undefined;\n\tfor (const retryTargets of exchangePlans(transcript, planned, entryId)) {\n\t\ttry {\n\t\t\tconst result = validateContextDeletionRequest(deletionRequestFromTargets(retryTargets), transcript);\n\t\t\tif (lastValidated !== undefined && result.stats.tokensAfter >= lastValidated.stats.tokensAfter) continue;\n\t\t\tif (best === undefined || result.stats.tokensAfter < best.stats.tokensAfter) best = result;\n\t\t} catch {\n\t\t\t// Try the next deterministic exchange variant.\n\t\t}\n\t}\n\treturn best;\n}\n\nexport function runDeterministicContextEviction(\n\ttranscript: CompactableTranscript,\n\ttokenBudget: number,\n): ValidatedContextDeletionResult {\n\tconst relaxed = relaxTranscriptForCriticalEviction(transcript);\n\tconst latestAssistant = [...relaxed.entries].reverse().find((entry) => entry.role === \"assistant\");\n\tconst protectedThinkingAssistant = latestAssistant && assistantEntryHasThinkingContentBlock(latestAssistant) ? latestAssistant.entryId : undefined;\n\tconst candidates = relaxed.entries\n\t\t.filter((entry) => entry.entryId !== protectedThinkingAssistant)\n\t\t.filter((entry) => canDeleteTarget(relaxed, { kind: \"entry\", entryId: entry.entryId }))\n\t\t.map((entry) => entry.entryId);\n\tconst planned: ContextDeletionTarget[] = [];\n\tconst excluded = new Set<string>();\n\tlet lastValidated: ValidatedContextDeletionResult | undefined;\n\n\t// Hard iteration cap required by the compaction-ladder design: every compaction loop must be\n\t// bounded (max 50) so no tier can spin indefinitely. Non-monotone validation rules can require\n\t// local exchange: a prior thinking-bearing or task-bearing deletion can make a newer candidate\n\t// invalid, even though replacing the prior deletion with that candidate is safe and fits better.\n\t// Exchanges are deterministic and adopted only when validation succeeds and tokensAfter strictly\n\t// decreases; exclusions grow monotonically, so no oscillation is possible, and the pass cap remains\n\t// the terminal safety belt.\n\tfor (let pass = 0; pass < CONTEXT_COMPACTION_MAX_EVICTION_PASSES; pass++) {\n\t\tif (hasFitBudget(lastValidated, tokenBudget)) return lastValidated;\n\t\tlet changed = false;\n\t\tfor (const entryId of candidates) {\n\t\t\tif (hasFitBudget(lastValidated, tokenBudget)) return lastValidated;\n\t\t\tif (planned.some((target) => target.entryId === entryId) || excluded.has(entryId)) continue;\n\t\t\tconst nextPlanned: ContextDeletionTarget[] = [...planned, { kind: \"entry\", entryId }];\n\t\t\ttry {\n\t\t\t\tlastValidated = validateContextDeletionRequest(deletionRequestFromTargets(nextPlanned), relaxed);\n\t\t\t\tplanned.splice(0, planned.length, ...lastValidated.deletedTargets);\n\t\t\t\tchanged = true;\n\t\t\t} catch {\n\t\t\t\tconst exchanged = validatedExchange(relaxed, planned, entryId, lastValidated);\n\t\t\t\tif (exchanged !== undefined) {\n\t\t\t\t\tlastValidated = exchanged;\n\t\t\t\t\tplanned.splice(0, planned.length, ...lastValidated.deletedTargets);\n\t\t\t\t\tchanged = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\texcluded.add(entryId);\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tif (!changed) {\n\t\t\tthrow terminalDeterministicEvictionError(\"candidate sweep exhausted\", lastValidated, tokenBudget);\n\t\t}\n\t}\n\tthrow terminalDeterministicEvictionError(\n\t\t`reached ${CONTEXT_COMPACTION_MAX_EVICTION_PASSES} eviction pass cap`,\n\t\tlastValidated,\n\t\ttokenBudget,\n\t);\n}\n"]}
@@ -0,0 +1,172 @@
1
+ import { validateContextDeletionRequest } from "./context-deletion-application.js";
2
+ import { canDeleteTarget, deletionRequestFromTargets, isTaskBearingEntry } from "./context-deletion-targets.js";
3
+ import { relaxTranscriptForCriticalEviction } from "./context-compaction-critical.js";
4
+ import { assistantEntryHasThinkingContentBlock } from "./context-transcript-analysis.js";
5
+ export const CONTEXT_COMPACTION_MAX_EVICTION_PASSES = 50;
6
+ function terminalDeterministicEvictionError(reason, lastValidated, tokenBudget) {
7
+ const statsText = lastValidated
8
+ ? `achieved tokensAfter=${lastValidated.stats.tokensAfter}, reduction=${lastValidated.stats.percentReduction}%, deletionTargets=${lastValidated.deletedTargets.length}`
9
+ : "achieved no validated deletion targets";
10
+ return new Error(`Context deterministic overflow eviction failed: ${reason}; ${statsText}; budget=${tokenBudget}; nothing more was safely deletable`);
11
+ }
12
+ function hasFitBudget(result, tokenBudget) {
13
+ return result !== undefined && result.deletedTargets.length > 0 && result.stats.tokensAfter <= tokenBudget;
14
+ }
15
+ function isThinkingAssistantEntryTarget(transcript, target) {
16
+ if (target.kind !== "entry")
17
+ return false;
18
+ const entry = transcript.entries.find((candidate) => candidate.entryId === target.entryId);
19
+ return entry?.role === "assistant" && assistantEntryHasThinkingContentBlock(entry);
20
+ }
21
+ function isTaskBearingEntryTarget(transcript, target) {
22
+ if (target.kind !== "entry")
23
+ return false;
24
+ const entry = transcript.entries.find((candidate) => candidate.entryId === target.entryId);
25
+ return entry !== undefined && isTaskBearingEntry(entry);
26
+ }
27
+ function targetSignature(targets) {
28
+ return targets
29
+ .map((target) => `${target.kind}:${target.entryId}:${target.kind === "content_block" ? target.blockIndex : ""}`)
30
+ .join("|");
31
+ }
32
+ function entryOrder(transcript, entryId) {
33
+ const index = transcript.entries.findIndex((entry) => entry.entryId === entryId);
34
+ return index < 0 ? Number.MAX_SAFE_INTEGER : index;
35
+ }
36
+ function entryTokenEstimate(transcript, entryId) {
37
+ return transcript.entries.find((entry) => entry.entryId === entryId)?.tokenEstimate ?? 0;
38
+ }
39
+ function smallestTaskTarget(transcript, targets) {
40
+ return [...targets]
41
+ .filter((target) => isTaskBearingEntryTarget(transcript, target))
42
+ .sort((left, right) => {
43
+ const tokenDelta = entryTokenEstimate(transcript, left.entryId) - entryTokenEstimate(transcript, right.entryId);
44
+ if (tokenDelta !== 0)
45
+ return tokenDelta;
46
+ return entryOrder(transcript, left.entryId) - entryOrder(transcript, right.entryId);
47
+ })[0];
48
+ }
49
+ function newestThinkingTarget(transcript, targets) {
50
+ return [...targets]
51
+ .filter((target) => isThinkingAssistantEntryTarget(transcript, target))
52
+ .sort((left, right) => entryOrder(transcript, right.entryId) - entryOrder(transcript, left.entryId))[0];
53
+ }
54
+ const THINKING_EXCHANGES = ["keep-all", "drop-all", "retain-newest"];
55
+ const TASK_EXCHANGES = ["keep-all", "drop-all", "retain-smallest"];
56
+ function shouldDeleteTarget(transcript, target, thinkingExchange, taskExchange, retainedThinking, retainedTask) {
57
+ if (thinkingExchange === "drop-all" && isThinkingAssistantEntryTarget(transcript, target))
58
+ return false;
59
+ if (thinkingExchange === "retain-newest" && retainedThinking !== undefined && targetSignature([target]) === targetSignature([retainedThinking]))
60
+ return false;
61
+ if (taskExchange === "drop-all" && isTaskBearingEntryTarget(transcript, target))
62
+ return false;
63
+ if (taskExchange === "retain-smallest" && retainedTask !== undefined && targetSignature([target]) === targetSignature([retainedTask]))
64
+ return false;
65
+ return true;
66
+ }
67
+ function buildExchangePlan(transcript, withCandidate, thinkingExchange, taskExchange) {
68
+ const retainedThinking = thinkingExchange === "retain-newest" ? newestThinkingTarget(transcript, withCandidate) : undefined;
69
+ const retainedTask = taskExchange === "retain-smallest" ? smallestTaskTarget(transcript, withCandidate) : undefined;
70
+ return withCandidate.filter((target) => shouldDeleteTarget(transcript, target, thinkingExchange, taskExchange, retainedThinking, retainedTask));
71
+ }
72
+ function shouldBuildExchangePlan(withCandidate, thinkingExchange, taskExchange) {
73
+ if (thinkingExchange === "keep-all" && taskExchange === "keep-all")
74
+ return false;
75
+ if (thinkingExchange === "retain-newest" && withCandidate.filter((target) => target.kind === "entry").length === 0)
76
+ return false;
77
+ return true;
78
+ }
79
+ function addExchangePlan(plans, seen, originalTargets, targets) {
80
+ if (targetSignature(targets) === targetSignature(originalTargets))
81
+ return;
82
+ const signature = targetSignature(targets);
83
+ if (seen.has(signature))
84
+ return;
85
+ seen.add(signature);
86
+ plans.push([...targets]);
87
+ }
88
+ function exchangePlans(transcript, planned, entryId) {
89
+ const currentTarget = { kind: "entry", entryId };
90
+ const withCandidate = [...planned, currentTarget];
91
+ const plans = [];
92
+ const seen = new Set();
93
+ // Deterministic cross-product order: thinking handling outermost, then task handling.
94
+ // Variants are keep/drop-all/retain-one for each non-monotone category, minus the
95
+ // keep-all/keep-all no-op; signature dedup collapses degenerate variants.
96
+ for (const thinkingExchange of THINKING_EXCHANGES) {
97
+ for (const taskExchange of TASK_EXCHANGES) {
98
+ if (!shouldBuildExchangePlan(withCandidate, thinkingExchange, taskExchange))
99
+ continue;
100
+ addExchangePlan(plans, seen, withCandidate, buildExchangePlan(transcript, withCandidate, thinkingExchange, taskExchange));
101
+ }
102
+ }
103
+ return plans;
104
+ }
105
+ function validatedExchange(transcript, planned, entryId, lastValidated) {
106
+ let best;
107
+ for (const retryTargets of exchangePlans(transcript, planned, entryId)) {
108
+ try {
109
+ const result = validateContextDeletionRequest(deletionRequestFromTargets(retryTargets), transcript);
110
+ if (lastValidated !== undefined && result.stats.tokensAfter >= lastValidated.stats.tokensAfter)
111
+ continue;
112
+ if (best === undefined || result.stats.tokensAfter < best.stats.tokensAfter)
113
+ best = result;
114
+ }
115
+ catch {
116
+ // Try the next deterministic exchange variant.
117
+ }
118
+ }
119
+ return best;
120
+ }
121
+ export function runDeterministicContextEviction(transcript, tokenBudget) {
122
+ const relaxed = relaxTranscriptForCriticalEviction(transcript);
123
+ const latestAssistant = [...relaxed.entries].reverse().find((entry) => entry.role === "assistant");
124
+ const protectedThinkingAssistant = latestAssistant && assistantEntryHasThinkingContentBlock(latestAssistant) ? latestAssistant.entryId : undefined;
125
+ const candidates = relaxed.entries
126
+ .filter((entry) => entry.entryId !== protectedThinkingAssistant)
127
+ .filter((entry) => canDeleteTarget(relaxed, { kind: "entry", entryId: entry.entryId }))
128
+ .map((entry) => entry.entryId);
129
+ const planned = [];
130
+ const excluded = new Set();
131
+ let lastValidated;
132
+ // Hard iteration cap required by the compaction-ladder design: every compaction loop must be
133
+ // bounded (max 50) so no tier can spin indefinitely. Non-monotone validation rules can require
134
+ // local exchange: a prior thinking-bearing or task-bearing deletion can make a newer candidate
135
+ // invalid, even though replacing the prior deletion with that candidate is safe and fits better.
136
+ // Exchanges are deterministic and adopted only when validation succeeds and tokensAfter strictly
137
+ // decreases; exclusions grow monotonically, so no oscillation is possible, and the pass cap remains
138
+ // the terminal safety belt.
139
+ for (let pass = 0; pass < CONTEXT_COMPACTION_MAX_EVICTION_PASSES; pass++) {
140
+ if (hasFitBudget(lastValidated, tokenBudget))
141
+ return lastValidated;
142
+ let changed = false;
143
+ for (const entryId of candidates) {
144
+ if (hasFitBudget(lastValidated, tokenBudget))
145
+ return lastValidated;
146
+ if (planned.some((target) => target.entryId === entryId) || excluded.has(entryId))
147
+ continue;
148
+ const nextPlanned = [...planned, { kind: "entry", entryId }];
149
+ try {
150
+ lastValidated = validateContextDeletionRequest(deletionRequestFromTargets(nextPlanned), relaxed);
151
+ planned.splice(0, planned.length, ...lastValidated.deletedTargets);
152
+ changed = true;
153
+ }
154
+ catch {
155
+ const exchanged = validatedExchange(relaxed, planned, entryId, lastValidated);
156
+ if (exchanged !== undefined) {
157
+ lastValidated = exchanged;
158
+ planned.splice(0, planned.length, ...lastValidated.deletedTargets);
159
+ changed = true;
160
+ continue;
161
+ }
162
+ excluded.add(entryId);
163
+ changed = true;
164
+ }
165
+ }
166
+ if (!changed) {
167
+ throw terminalDeterministicEvictionError("candidate sweep exhausted", lastValidated, tokenBudget);
168
+ }
169
+ }
170
+ throw terminalDeterministicEvictionError(`reached ${CONTEXT_COMPACTION_MAX_EVICTION_PASSES} eviction pass cap`, lastValidated, tokenBudget);
171
+ }
172
+ //# sourceMappingURL=context-compaction-eviction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-compaction-eviction.js","sourceRoot":"","sources":["../../../src/core/compaction/context-compaction-eviction.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContextDeletionTarget } from \"../session-manager.ts\";\nimport type { CompactableTranscript, ValidatedContextDeletionResult } from \"./context-compaction-types.ts\";\nimport { validateContextDeletionRequest } from \"./context-deletion-application.ts\";\nimport { canDeleteTarget, deletionRequestFromTargets, isTaskBearingEntry } from \"./context-deletion-targets.ts\";\nimport { relaxTranscriptForCriticalEviction } from \"./context-compaction-critical.ts\";\nimport { assistantEntryHasThinkingContentBlock } from \"./context-transcript-analysis.ts\";\n\nexport const CONTEXT_COMPACTION_MAX_EVICTION_PASSES = 50;\n\nfunction terminalDeterministicEvictionError(\n\treason: string,\n\tlastValidated: ValidatedContextDeletionResult | undefined,\n\ttokenBudget: number,\n): Error {\n\tconst statsText = lastValidated\n\t\t? `achieved tokensAfter=${lastValidated.stats.tokensAfter}, reduction=${lastValidated.stats.percentReduction}%, deletionTargets=${lastValidated.deletedTargets.length}`\n\t\t: \"achieved no validated deletion targets\";\n\treturn new Error(\n\t\t`Context deterministic overflow eviction failed: ${reason}; ${statsText}; budget=${tokenBudget}; nothing more was safely deletable`,\n\t);\n}\n\n\nfunction hasFitBudget(result: ValidatedContextDeletionResult | undefined, tokenBudget: number): result is ValidatedContextDeletionResult {\n\treturn result !== undefined && result.deletedTargets.length > 0 && result.stats.tokensAfter <= tokenBudget;\n}\n\nfunction isThinkingAssistantEntryTarget(transcript: CompactableTranscript, target: ContextDeletionTarget): boolean {\n\tif (target.kind !== \"entry\") return false;\n\tconst entry = transcript.entries.find((candidate) => candidate.entryId === target.entryId);\n\treturn entry?.role === \"assistant\" && assistantEntryHasThinkingContentBlock(entry);\n}\n\nfunction isTaskBearingEntryTarget(transcript: CompactableTranscript, target: ContextDeletionTarget): boolean {\n\tif (target.kind !== \"entry\") return false;\n\tconst entry = transcript.entries.find((candidate) => candidate.entryId === target.entryId);\n\treturn entry !== undefined && isTaskBearingEntry(entry);\n}\n\nfunction targetSignature(targets: readonly ContextDeletionTarget[]): string {\n\treturn targets\n\t\t.map((target) => `${target.kind}:${target.entryId}:${target.kind === \"content_block\" ? target.blockIndex : \"\"}`)\n\t\t.join(\"|\");\n}\n\nfunction entryOrder(transcript: CompactableTranscript, entryId: string): number {\n\tconst index = transcript.entries.findIndex((entry) => entry.entryId === entryId);\n\treturn index < 0 ? Number.MAX_SAFE_INTEGER : index;\n}\n\nfunction entryTokenEstimate(transcript: CompactableTranscript, entryId: string): number {\n\treturn transcript.entries.find((entry) => entry.entryId === entryId)?.tokenEstimate ?? 0;\n}\n\nfunction smallestTaskTarget(transcript: CompactableTranscript, targets: readonly ContextDeletionTarget[]): ContextDeletionTarget | undefined {\n\treturn [...targets]\n\t\t.filter((target) => isTaskBearingEntryTarget(transcript, target))\n\t\t.sort((left: ContextDeletionTarget, right: ContextDeletionTarget) => {\n\t\t\tconst tokenDelta = entryTokenEstimate(transcript, left.entryId) - entryTokenEstimate(transcript, right.entryId);\n\t\t\tif (tokenDelta !== 0) return tokenDelta;\n\t\t\treturn entryOrder(transcript, left.entryId) - entryOrder(transcript, right.entryId);\n\t\t})[0];\n}\n\nfunction newestThinkingTarget(transcript: CompactableTranscript, targets: readonly ContextDeletionTarget[]): ContextDeletionTarget | undefined {\n\treturn [...targets]\n\t\t.filter((target) => isThinkingAssistantEntryTarget(transcript, target))\n\t\t.sort((left: ContextDeletionTarget, right: ContextDeletionTarget) => entryOrder(transcript, right.entryId) - entryOrder(transcript, left.entryId))[0];\n}\n\ntype ThinkingExchange = \"keep-all\" | \"drop-all\" | \"retain-newest\";\ntype TaskExchange = \"keep-all\" | \"drop-all\" | \"retain-smallest\";\n\nconst THINKING_EXCHANGES: readonly ThinkingExchange[] = [\"keep-all\", \"drop-all\", \"retain-newest\"];\nconst TASK_EXCHANGES: readonly TaskExchange[] = [\"keep-all\", \"drop-all\", \"retain-smallest\"];\n\nfunction shouldDeleteTarget(\n\ttranscript: CompactableTranscript,\n\ttarget: ContextDeletionTarget,\n\tthinkingExchange: ThinkingExchange,\n\ttaskExchange: TaskExchange,\n\tretainedThinking: ContextDeletionTarget | undefined,\n\tretainedTask: ContextDeletionTarget | undefined,\n): boolean {\n\tif (thinkingExchange === \"drop-all\" && isThinkingAssistantEntryTarget(transcript, target)) return false;\n\tif (thinkingExchange === \"retain-newest\" && retainedThinking !== undefined && targetSignature([target]) === targetSignature([retainedThinking])) return false;\n\tif (taskExchange === \"drop-all\" && isTaskBearingEntryTarget(transcript, target)) return false;\n\tif (taskExchange === \"retain-smallest\" && retainedTask !== undefined && targetSignature([target]) === targetSignature([retainedTask])) return false;\n\treturn true;\n}\n\nfunction buildExchangePlan(\n\ttranscript: CompactableTranscript,\n\twithCandidate: readonly ContextDeletionTarget[],\n\tthinkingExchange: ThinkingExchange,\n\ttaskExchange: TaskExchange,\n): ContextDeletionTarget[] {\n\tconst retainedThinking = thinkingExchange === \"retain-newest\" ? newestThinkingTarget(transcript, withCandidate) : undefined;\n\tconst retainedTask = taskExchange === \"retain-smallest\" ? smallestTaskTarget(transcript, withCandidate) : undefined;\n\treturn withCandidate.filter((target) =>\n\t\tshouldDeleteTarget(transcript, target, thinkingExchange, taskExchange, retainedThinking, retainedTask),\n\t);\n}\n\nfunction shouldBuildExchangePlan(\n\twithCandidate: readonly ContextDeletionTarget[],\n\tthinkingExchange: ThinkingExchange,\n\ttaskExchange: TaskExchange,\n): boolean {\n\tif (thinkingExchange === \"keep-all\" && taskExchange === \"keep-all\") return false;\n\tif (thinkingExchange === \"retain-newest\" && withCandidate.filter((target) => target.kind === \"entry\").length === 0) return false;\n\treturn true;\n}\n\nfunction addExchangePlan(\n\tplans: ContextDeletionTarget[][],\n\tseen: Set<string>,\n\toriginalTargets: readonly ContextDeletionTarget[],\n\ttargets: readonly ContextDeletionTarget[],\n): void {\n\tif (targetSignature(targets) === targetSignature(originalTargets)) return;\n\tconst signature = targetSignature(targets);\n\tif (seen.has(signature)) return;\n\tseen.add(signature);\n\tplans.push([...targets]);\n}\n\nfunction exchangePlans(\n\ttranscript: CompactableTranscript,\n\tplanned: readonly ContextDeletionTarget[],\n\tentryId: string,\n): ContextDeletionTarget[][] {\n\tconst currentTarget: ContextDeletionTarget = { kind: \"entry\", entryId };\n\tconst withCandidate = [...planned, currentTarget];\n\tconst plans: ContextDeletionTarget[][] = [];\n\tconst seen = new Set<string>();\n\t// Deterministic cross-product order: thinking handling outermost, then task handling.\n\t// Variants are keep/drop-all/retain-one for each non-monotone category, minus the\n\t// keep-all/keep-all no-op; signature dedup collapses degenerate variants.\n\tfor (const thinkingExchange of THINKING_EXCHANGES) {\n\t\tfor (const taskExchange of TASK_EXCHANGES) {\n\t\t\tif (!shouldBuildExchangePlan(withCandidate, thinkingExchange, taskExchange)) continue;\n\t\t\taddExchangePlan(plans, seen, withCandidate, buildExchangePlan(transcript, withCandidate, thinkingExchange, taskExchange));\n\t\t}\n\t}\n\treturn plans;\n}\n\nfunction validatedExchange(\n\ttranscript: CompactableTranscript,\n\tplanned: readonly ContextDeletionTarget[],\n\tentryId: string,\n\tlastValidated: ValidatedContextDeletionResult | undefined,\n): ValidatedContextDeletionResult | undefined {\n\tlet best: ValidatedContextDeletionResult | undefined;\n\tfor (const retryTargets of exchangePlans(transcript, planned, entryId)) {\n\t\ttry {\n\t\t\tconst result = validateContextDeletionRequest(deletionRequestFromTargets(retryTargets), transcript);\n\t\t\tif (lastValidated !== undefined && result.stats.tokensAfter >= lastValidated.stats.tokensAfter) continue;\n\t\t\tif (best === undefined || result.stats.tokensAfter < best.stats.tokensAfter) best = result;\n\t\t} catch {\n\t\t\t// Try the next deterministic exchange variant.\n\t\t}\n\t}\n\treturn best;\n}\n\nexport function runDeterministicContextEviction(\n\ttranscript: CompactableTranscript,\n\ttokenBudget: number,\n): ValidatedContextDeletionResult {\n\tconst relaxed = relaxTranscriptForCriticalEviction(transcript);\n\tconst latestAssistant = [...relaxed.entries].reverse().find((entry) => entry.role === \"assistant\");\n\tconst protectedThinkingAssistant = latestAssistant && assistantEntryHasThinkingContentBlock(latestAssistant) ? latestAssistant.entryId : undefined;\n\tconst candidates = relaxed.entries\n\t\t.filter((entry) => entry.entryId !== protectedThinkingAssistant)\n\t\t.filter((entry) => canDeleteTarget(relaxed, { kind: \"entry\", entryId: entry.entryId }))\n\t\t.map((entry) => entry.entryId);\n\tconst planned: ContextDeletionTarget[] = [];\n\tconst excluded = new Set<string>();\n\tlet lastValidated: ValidatedContextDeletionResult | undefined;\n\n\t// Hard iteration cap required by the compaction-ladder design: every compaction loop must be\n\t// bounded (max 50) so no tier can spin indefinitely. Non-monotone validation rules can require\n\t// local exchange: a prior thinking-bearing or task-bearing deletion can make a newer candidate\n\t// invalid, even though replacing the prior deletion with that candidate is safe and fits better.\n\t// Exchanges are deterministic and adopted only when validation succeeds and tokensAfter strictly\n\t// decreases; exclusions grow monotonically, so no oscillation is possible, and the pass cap remains\n\t// the terminal safety belt.\n\tfor (let pass = 0; pass < CONTEXT_COMPACTION_MAX_EVICTION_PASSES; pass++) {\n\t\tif (hasFitBudget(lastValidated, tokenBudget)) return lastValidated;\n\t\tlet changed = false;\n\t\tfor (const entryId of candidates) {\n\t\t\tif (hasFitBudget(lastValidated, tokenBudget)) return lastValidated;\n\t\t\tif (planned.some((target) => target.entryId === entryId) || excluded.has(entryId)) continue;\n\t\t\tconst nextPlanned: ContextDeletionTarget[] = [...planned, { kind: \"entry\", entryId }];\n\t\t\ttry {\n\t\t\t\tlastValidated = validateContextDeletionRequest(deletionRequestFromTargets(nextPlanned), relaxed);\n\t\t\t\tplanned.splice(0, planned.length, ...lastValidated.deletedTargets);\n\t\t\t\tchanged = true;\n\t\t\t} catch {\n\t\t\t\tconst exchanged = validatedExchange(relaxed, planned, entryId, lastValidated);\n\t\t\t\tif (exchanged !== undefined) {\n\t\t\t\t\tlastValidated = exchanged;\n\t\t\t\t\tplanned.splice(0, planned.length, ...lastValidated.deletedTargets);\n\t\t\t\t\tchanged = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\texcluded.add(entryId);\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tif (!changed) {\n\t\t\tthrow terminalDeterministicEvictionError(\"candidate sweep exhausted\", lastValidated, tokenBudget);\n\t\t}\n\t}\n\tthrow terminalDeterministicEvictionError(\n\t\t`reached ${CONTEXT_COMPACTION_MAX_EVICTION_PASSES} eviction pass cap`,\n\t\tlastValidated,\n\t\ttokenBudget,\n\t);\n}\n"]}
@@ -1,5 +1,11 @@
1
1
  import { type ThinkingLevel } from "@earendil-works/pi-agent-core";
2
2
  import type { Api, Model } from "@earendil-works/pi-ai/compat";
3
3
  import type { ContextCompactionPreparation, ValidatedContextDeletionResult } from "./context-compaction-types.ts";
4
- export declare function contextCompact(preparation: ContextCompactionPreparation, model: Model<Api>, apiKey: string, headers?: Record<string, string>, signal?: AbortSignal, thinkingLevel?: ThinkingLevel): Promise<ValidatedContextDeletionResult>;
4
+ export declare const CONTEXT_COMPACTION_MAX_TURNS = 50;
5
+ export declare const CONTEXT_COMPACTION_MAX_PLANNER_NUDGES = 50;
6
+ export interface ContextCompactionLadderOptions {
7
+ acceptanceTokenBudget?: number;
8
+ criticalEvictionTokenBudget?: number;
9
+ }
10
+ export declare function contextCompact(preparation: ContextCompactionPreparation, model: Model<Api>, apiKey: string, headers?: Record<string, string>, signal?: AbortSignal, thinkingLevel?: ThinkingLevel, ladder?: ContextCompactionLadderOptions): Promise<ValidatedContextDeletionResult>;
5
11
  //# sourceMappingURL=context-compaction-runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context-compaction-runner.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/context-compaction-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC7F,OAAO,KAAK,EAAE,GAAG,EAAoB,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAGjF,OAAO,KAAK,EAGX,4BAA4B,EAC5B,8BAA8B,EAC9B,MAAM,+BAA+B,CAAC;AAoOvC,wBAAsB,cAAc,CACnC,WAAW,EAAE,4BAA4B,EACzC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,MAAM,CAAC,EAAE,WAAW,EACpB,aAAa,GAAE,aAAqB,GAClC,OAAO,CAAC,8BAA8B,CAAC,CAoBzC","sourcesContent":["import { Agent, type AgentMessage, type ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, AssistantMessage, Model } from \"@earendil-works/pi-ai/compat\";\nimport { createAssistantMessageEventStream, isContextOverflow, streamSimple } from \"@earendil-works/pi-ai/compat\";\nimport { formatCopilotProviderError } from \"../copilot-errors.ts\";\nimport type {\n\tCompactableTranscript,\n\tContextCompactionParameters,\n\tContextCompactionPreparation,\n\tValidatedContextDeletionResult,\n} from \"./context-compaction-types.ts\";\nimport { CONTEXT_COMPACTION_AUTO_QUERY } from \"./context-compaction-types.ts\";\nimport {\n\tcreateContextCompactionBudgetDetails,\n\tcontextCompactionProgressKey,\n\tcontextCompactionProgressPercent,\n\tcontextCompactionTargetLabel,\n\tcontextCompactionTargetMet,\n\tformatErrorMessage,\n} from \"./context-compaction-metrics.ts\";\nimport {\n\tgetTranscriptCompactionParameters,\n\tnormalizeContextCompactionParameters,\n} from \"./context-compaction-strategy.ts\";\nimport {\n\tCONTEXT_COMPACTION_BUDGET_TOOL_NAME,\n\tCONTEXT_DELETE_TOOL_NAME,\n\tCONTEXT_GREP_DELETE_TOOL_NAME,\n\tCONTEXT_READ_ENTRY_TOOL_NAME,\n\tCONTEXT_SEARCH_TRANSCRIPT_TOOL_NAME,\n} from \"./context-deletion-tool-definitions.ts\";\nimport { createContextDeletionTool } from \"./context-deletion-tools.ts\";\nimport {\n\tbuildContextCompactionPrompt,\n\tCONTEXT_COMPACTION_SYSTEM_PROMPT,\n\twriteContextCompactionTranscriptFile,\n} from \"./context-compaction-prompt.ts\";\n\nfunction createContextCompactionTargetNudgeMessage(\n\tresult: ValidatedContextDeletionResult | undefined,\n\tparameters: ContextCompactionParameters,\n): AgentMessage {\n\tconst currentReductionPercent = contextCompactionProgressPercent(result);\n\tconst targetLabel = contextCompactionTargetLabel(parameters);\n\tconst tokensToDelete = result\n\t\t? createContextCompactionBudgetDetails(result.stats, 0, undefined, parameters, 0, 0).tokensToDeleteForTarget\n\t\t: undefined;\n\tconst remainingText = tokensToDelete !== undefined ? ` Delete about ${tokensToDelete} more token(s) if safe candidates exist.` : \"\";\n\treturn {\n\t\trole: \"user\",\n\t\tcontent: [\n\t\t\t{\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: `The strict ${targetLabel} context-reduction requirement is not met yet; current validated reduction is ${currentReductionPercent}%.${remainingText} Continue removing low-value message entries or message content blocks using ${CONTEXT_DELETE_TOOL_NAME} or ${CONTEXT_GREP_DELETE_TOOL_NAME}. Use the focus query ${JSON.stringify(parameters.query)} to preserve relevant context. Call ${CONTEXT_COMPACTION_BUDGET_TOOL_NAME} to verify progress, and do not provide a final answer until the validated reduction is at least ${targetLabel}.`,\n\t\t\t},\n\t\t],\n\t\ttimestamp: Date.now(),\n\t};\n}\nfunction createContextCompactionAssistantMessage(\n\tmodel: Model<Api>,\n\tcontent: AssistantMessage[\"content\"],\n\tstopReason: AssistantMessage[\"stopReason\"],\n\terrorMessage?: string,\n): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent,\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason,\n\t\t...(errorMessage !== undefined ? { errorMessage } : {}),\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction createContextCompactionStopStream(model: Model<Api>, text: string) {\n\tconst stream = createAssistantMessageEventStream();\n\tqueueMicrotask(() => {\n\t\tconst message = createContextCompactionAssistantMessage(model, [{ type: \"text\", text }], \"stop\");\n\t\tstream.push({ type: \"done\", reason: \"stop\", message });\n\t\tstream.end(message);\n\t});\n\treturn stream;\n}\n\nfunction isContextCompactionOverflowError(model: Model<Api>, errorMessage: string): boolean {\n\treturn isContextOverflow(\n\t\tcreateContextCompactionAssistantMessage(model, [], \"error\", errorMessage),\n\t\tmodel.contextWindow,\n\t);\n}\n\ninterface ContextDeletionRun {\n\tvalidatedResult: ValidatedContextDeletionResult | undefined;\n\tlastToolError: string | undefined;\n\tproviderError: string | undefined;\n}\n\nasync function runContextDeletionAssistant(\n\tinputTranscript: CompactableTranscript,\n\tmodel: Model<Api>,\n\tapiKey: string,\n\theaders?: Record<string, string>,\n\tsignal?: AbortSignal,\n\tthinkingLevel: ThinkingLevel = \"off\",\n\tparameters: ContextCompactionParameters = getTranscriptCompactionParameters(inputTranscript),\n): Promise<ContextDeletionRun> {\n\tconst transcript: CompactableTranscript = { ...inputTranscript, parameters };\n\tconst maxTokens = model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY;\n\tif (signal?.aborted) {\n\t\tthrow new Error(\"Context compaction failed: Request was aborted\");\n\t}\n\tconst transcriptFile = writeContextCompactionTranscriptFile(transcript);\n\tconst promptMessage: AgentMessage = {\n\t\trole: \"user\",\n\t\tcontent: [{ type: \"text\", text: buildContextCompactionPrompt(transcript, transcriptFile.path, parameters) }],\n\t\ttimestamp: Date.now(),\n\t};\n\tconst deletionTool = createContextDeletionTool(transcript, { contextWindow: model.contextWindow, ...parameters });\n\tconst agent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt: CONTEXT_COMPACTION_SYSTEM_PROMPT,\n\t\t\tmodel,\n\t\t\tthinkingLevel,\n\t\t\ttools: deletionTool.tools,\n\t\t},\n\t\ttoolExecution: \"parallel\",\n\t\tstreamFn: async (requestModel, context, streamOptions) => {\n\t\t\tconst currentResult = deletionTool.getValidatedResult();\n\t\t\tif (contextCompactionTargetMet(currentResult, parameters)) {\n\t\t\t\treturn createContextCompactionStopStream(\n\t\t\t\t\trequestModel,\n\t\t\t\t\t`Reached the strict ${contextCompactionTargetLabel(parameters)} context-reduction requirement (${currentResult.stats.percentReduction}%); using the validated deletions recorded so far.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn streamSimple(requestModel, context, {\n\t\t\t\t...streamOptions,\n\t\t\t\tmaxTokens,\n\t\t\t\tapiKey,\n\t\t\t\theaders: headers ?? streamOptions?.headers,\n\t\t\t});\n\t\t},\n\t});\n\n\tlet lastNudgedProgressKey: string | undefined;\n\tconst unsubscribeNudge = agent.subscribe((event, eventSignal) => {\n\t\tif (event.type !== \"turn_end\" || signal?.aborted || eventSignal.aborted) return;\n\t\tif (event.message.role !== \"assistant\") return;\n\t\tif (event.message.stopReason === \"error\" || event.message.stopReason === \"aborted\") return;\n\t\tif (event.message.content.some((content) => content.type === \"toolCall\")) return;\n\t\tconst currentResult = deletionTool.getValidatedResult();\n\t\tif (contextCompactionTargetMet(currentResult, parameters)) return;\n\t\tconst progressKey = contextCompactionProgressKey(currentResult);\n\t\tif (progressKey === lastNudgedProgressKey) return;\n\t\tlastNudgedProgressKey = progressKey;\n\t\tagent.followUp(createContextCompactionTargetNudgeMessage(currentResult, parameters));\n\t});\n\n\tconst abortOnSignal = () => agent.abort();\n\tsignal?.addEventListener(\"abort\", abortOnSignal, { once: true });\n\ttry {\n\t\tawait agent.prompt(promptMessage);\n\t} catch (error) {\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error(\"Context compaction failed: Request was aborted\");\n\t\t}\n\t\tthrow new Error(`Context compaction failed: ${formatCopilotProviderError(model.provider, formatErrorMessage(error))}`);\n\t} finally {\n\t\tsignal?.removeEventListener(\"abort\", abortOnSignal);\n\t\tunsubscribeNudge();\n\t\ttranscriptFile.cleanup();\n\t}\n\n\tif (signal?.aborted) {\n\t\tthrow new Error(\"Context compaction failed: Request was aborted\");\n\t}\n\tif (agent.state.errorMessage) {\n\t\tconst formattedErrorMessage = formatCopilotProviderError(model.provider, agent.state.errorMessage);\n\t\tif (isContextCompactionOverflowError(model, agent.state.errorMessage)) {\n\t\t\treturn {\n\t\t\t\tvalidatedResult: deletionTool.getValidatedResult(),\n\t\t\t\tlastToolError: deletionTool.getLastError(),\n\t\t\t\tproviderError: formattedErrorMessage === agent.state.errorMessage ? undefined : formattedErrorMessage,\n\t\t\t};\n\t\t}\n\t\tthrow new Error(`Context compaction failed: ${formattedErrorMessage}`);\n\t}\n\tif (deletionTool.getCallCount() === 0) {\n\t\tthrow new Error(\n\t\t\t`Context compaction did not call any transcript inspection, budget, or deletion tools (${CONTEXT_SEARCH_TRANSCRIPT_TOOL_NAME}, ${CONTEXT_READ_ENTRY_TOOL_NAME}, ${CONTEXT_COMPACTION_BUDGET_TOOL_NAME}, ${CONTEXT_DELETE_TOOL_NAME}, or ${CONTEXT_GREP_DELETE_TOOL_NAME})`,\n\t\t);\n\t}\n\treturn {\n\t\tvalidatedResult: deletionTool.getValidatedResult(),\n\t\tlastToolError: deletionTool.getLastError(),\n\t\tproviderError: undefined,\n\t};\n}\n\nfunction hasMetContextCompactionTarget(\n\trun: ContextDeletionRun,\n\tparameters: ContextCompactionParameters,\n): run is ContextDeletionRun & { validatedResult: ValidatedContextDeletionResult } {\n\treturn contextCompactionTargetMet(run.validatedResult, parameters);\n}\n\nfunction formatContextCompactionTargetFailureMessage(\n\tattempts: readonly ContextDeletionRunAttempt[],\n\tparameters: ContextCompactionParameters,\n): string {\n\tconst targetLabel = contextCompactionTargetLabel(parameters);\n\tif (attempts.length === 0) {\n\t\treturn `Context compaction did not meet the strict ${targetLabel} reduction requirement`;\n\t}\n\tconst attemptDetails = attempts\n\t\t.map((attempt) => {\n\t\t\tconst reduction = contextCompactionProgressPercent(attempt.validatedResult);\n\t\t\tconst deletionCount = attempt.validatedResult?.deletedTargets.length ?? 0;\n\t\t\tconst toolErrorText = attempt.lastToolError ? `; last deletion tool error: ${attempt.lastToolError}` : \"\";\n\t\t\tconst providerErrorText = attempt.providerError ? `; provider error: ${attempt.providerError}` : \"\";\n\t\t\treturn `attempt reached ${reduction}% with ${deletionCount} validated deletion target(s)${toolErrorText}${providerErrorText}`;\n\t\t})\n\t\t.join(\"; \");\n\treturn `Context compaction did not meet the strict ${targetLabel} reduction requirement; ${attemptDetails}`;\n}\n\ninterface ContextDeletionRunAttempt extends ContextDeletionRun {}\n\nexport async function contextCompact(\n\tpreparation: ContextCompactionPreparation,\n\tmodel: Model<Api>,\n\tapiKey: string,\n\theaders?: Record<string, string>,\n\tsignal?: AbortSignal,\n\tthinkingLevel: ThinkingLevel = \"off\",\n): Promise<ValidatedContextDeletionResult> {\n\tconst parameters = normalizeContextCompactionParameters(\n\t\tpreparation.parameters ?? preparation.transcript.parameters,\n\t\tpreparation.parameters?.query ?? preparation.transcript.parameters?.query ?? CONTEXT_COMPACTION_AUTO_QUERY,\n\t);\n\tconst transcript: CompactableTranscript = { ...preparation.transcript, parameters };\n\tconst attempts: ContextDeletionRunAttempt[] = [];\n\tconst standardRun = await runContextDeletionAssistant(\n\t\ttranscript,\n\t\tmodel,\n\t\tapiKey,\n\t\theaders,\n\t\tsignal,\n\t\tthinkingLevel,\n\t\tparameters,\n\t);\n\tif (hasMetContextCompactionTarget(standardRun, parameters)) return standardRun.validatedResult;\n\tattempts.push({ ...standardRun });\n\n\tthrow new Error(formatContextCompactionTargetFailureMessage(attempts, parameters));\n}\n"]}
1
+ {"version":3,"file":"context-compaction-runner.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/context-compaction-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC7F,OAAO,KAAK,EAAE,GAAG,EAAoB,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAGjF,OAAO,KAAK,EAGX,4BAA4B,EAC5B,8BAA8B,EAC9B,MAAM,+BAA+B,CAAC;AA+BvC,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,qCAAqC,KAAK,CAAC;AAExD,MAAM,WAAW,8BAA8B;IAC9C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACrC;AA4PD,wBAAsB,cAAc,CACnC,WAAW,EAAE,4BAA4B,EACzC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,MAAM,CAAC,EAAE,WAAW,EACpB,aAAa,GAAE,aAAqB,EACpC,MAAM,CAAC,EAAE,8BAA8B,GACrC,OAAO,CAAC,8BAA8B,CAAC,CAqDzC","sourcesContent":["import { Agent, type AgentMessage, type ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, AssistantMessage, Model } from \"@earendil-works/pi-ai/compat\";\nimport { createAssistantMessageEventStream, isContextOverflow, streamSimple } from \"@earendil-works/pi-ai/compat\";\nimport { formatCopilotProviderError } from \"../copilot-errors.ts\";\nimport type {\n\tCompactableTranscript,\n\tContextCompactionParameters,\n\tContextCompactionPreparation,\n\tValidatedContextDeletionResult,\n} from \"./context-compaction-types.ts\";\nimport { CONTEXT_COMPACTION_AUTO_QUERY } from \"./context-compaction-types.ts\";\nimport {\n\tcreateContextCompactionBudgetDetails,\n\tcontextCompactionProgressKey,\n\tcontextCompactionProgressPercent,\n\tcontextCompactionTargetLabel,\n\tcontextCompactionTargetMet,\n\tformatErrorMessage,\n} from \"./context-compaction-metrics.ts\";\nimport { getTranscriptCompactionParameters, normalizeContextCompactionParameters } from \"./context-compaction-strategy.ts\";\nimport {\n\tCONTEXT_COMPACTION_BUDGET_TOOL_NAME,\n\tCONTEXT_DELETE_TOOL_NAME,\n\tCONTEXT_GREP_DELETE_TOOL_NAME,\n\tCONTEXT_READ_ENTRY_TOOL_NAME,\n\tCONTEXT_SEARCH_TRANSCRIPT_TOOL_NAME,\n} from \"./context-deletion-tool-definitions.ts\";\nimport { createContextDeletionTool } from \"./context-deletion-tools.ts\";\nimport {\n\tbuildContextCompactionPrompt,\n\tCONTEXT_COMPACTION_SYSTEM_PROMPT,\n\twriteContextCompactionTranscriptFile,\n} from \"./context-compaction-prompt.ts\";\nimport {\n\tCONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT,\n\tcriticalCompactionParameters,\n\trelaxTranscriptForCriticalEviction,\n} from \"./context-compaction-critical.ts\";\nimport { runDeterministicContextEviction } from \"./context-compaction-eviction.ts\";\n\nexport const CONTEXT_COMPACTION_MAX_TURNS = 50;\nexport const CONTEXT_COMPACTION_MAX_PLANNER_NUDGES = 50;\n\nexport interface ContextCompactionLadderOptions {\n\tacceptanceTokenBudget?: number;\n\tcriticalEvictionTokenBudget?: number;\n}\n\ninterface ContextDeletionAssistantOptions {\n\tpromptSuffix?: string;\n\tacceptanceTokenBudget?: number;\n}\n\nfunction createContextCompactionTargetNudgeMessage(\n\tresult: ValidatedContextDeletionResult | undefined,\n\tparameters: ContextCompactionParameters,\n): AgentMessage {\n\tconst currentReductionPercent = contextCompactionProgressPercent(result);\n\tconst targetLabel = contextCompactionTargetLabel(parameters);\n\tconst tokensToDelete = result\n\t\t? createContextCompactionBudgetDetails(result.stats, 0, undefined, parameters, 0, 0).tokensToDeleteForTarget\n\t\t: undefined;\n\tconst remainingText = tokensToDelete !== undefined ? ` Delete about ${tokensToDelete} more token(s) if safe candidates exist.` : \"\";\n\treturn {\n\t\trole: \"user\",\n\t\tcontent: [\n\t\t\t{\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: `The strict ${targetLabel} context-reduction requirement is not met yet; current validated reduction is ${currentReductionPercent}%.${remainingText} Continue removing low-value message entries or message content blocks using ${CONTEXT_DELETE_TOOL_NAME} or ${CONTEXT_GREP_DELETE_TOOL_NAME}. Use the focus query ${JSON.stringify(parameters.query)} to preserve relevant context. Call ${CONTEXT_COMPACTION_BUDGET_TOOL_NAME} to verify progress, and do not provide a final answer until the validated reduction is at least ${targetLabel}.`,\n\t\t\t},\n\t\t],\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction createContextCompactionAssistantMessage(\n\tmodel: Model<Api>,\n\tcontent: AssistantMessage[\"content\"],\n\tstopReason: AssistantMessage[\"stopReason\"],\n\terrorMessage?: string,\n): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent,\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason,\n\t\t...(errorMessage !== undefined ? { errorMessage } : {}),\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction createContextCompactionStopStream(model: Model<Api>, text: string) {\n\tconst stream = createAssistantMessageEventStream();\n\tqueueMicrotask(() => {\n\t\tconst message = createContextCompactionAssistantMessage(model, [{ type: \"text\", text }], \"stop\");\n\t\tstream.push({ type: \"done\", reason: \"stop\", message });\n\t\tstream.end(message);\n\t});\n\treturn stream;\n}\n\nfunction isContextCompactionOverflowError(model: Model<Api>, errorMessage: string): boolean {\n\treturn isContextOverflow(createContextCompactionAssistantMessage(model, [], \"error\", errorMessage), model.contextWindow);\n}\n\nfunction feasibleAccepted(\n\tresult: ValidatedContextDeletionResult | undefined,\n\tacceptanceTokenBudget: number | undefined,\n): result is ValidatedContextDeletionResult {\n\treturn (\n\t\tacceptanceTokenBudget !== undefined &&\n\t\tresult !== undefined &&\n\t\tresult.deletedTargets.length > 0 &&\n\t\tresult.stats.tokensAfter <= acceptanceTokenBudget\n\t);\n}\n\nfunction acceptedWithinRun(\n\tresult: ValidatedContextDeletionResult | undefined,\n\tparameters: ContextCompactionParameters,\n\tacceptanceTokenBudget: number | undefined,\n): result is ValidatedContextDeletionResult {\n\tif (acceptanceTokenBudget !== undefined) return feasibleAccepted(result, acceptanceTokenBudget);\n\treturn contextCompactionTargetMet(result, parameters);\n}\n\nfunction targetMetAcceptedForLadder(\n\trun: ContextDeletionRun,\n\tparameters: ContextCompactionParameters,\n\tladder: ContextCompactionLadderOptions | undefined,\n): run is ContextDeletionRun & { validatedResult: ValidatedContextDeletionResult } {\n\tif (!hasMetContextCompactionTarget(run, parameters)) return false;\n\tconst overflowBudget = ladder?.criticalEvictionTokenBudget;\n\treturn overflowBudget === undefined || run.validatedResult.stats.tokensAfter <= overflowBudget;\n}\n\ninterface ContextDeletionRun {\n\tvalidatedResult: ValidatedContextDeletionResult | undefined;\n\tlastToolError: string | undefined;\n\tproviderError: string | undefined;\n}\n\nasync function runContextDeletionAssistant(\n\tinputTranscript: CompactableTranscript,\n\tmodel: Model<Api>,\n\tapiKey: string,\n\theaders?: Record<string, string>,\n\tsignal?: AbortSignal,\n\tthinkingLevel: ThinkingLevel = \"off\",\n\tparameters: ContextCompactionParameters = getTranscriptCompactionParameters(inputTranscript),\n\toptions: ContextDeletionAssistantOptions = {},\n): Promise<ContextDeletionRun> {\n\tconst transcript: CompactableTranscript = { ...inputTranscript, parameters };\n\tconst maxTokens = model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY;\n\tif (signal?.aborted) throw new Error(\"Context compaction failed: Request was aborted\");\n\tconst transcriptFile = writeContextCompactionTranscriptFile(transcript);\n\tconst promptText = buildContextCompactionPrompt(transcript, transcriptFile.path, parameters) + (options.promptSuffix ?? \"\");\n\tconst promptMessage: AgentMessage = {\n\t\trole: \"user\",\n\t\tcontent: [{ type: \"text\", text: promptText }],\n\t\ttimestamp: Date.now(),\n\t};\n\tconst deletionTool = createContextDeletionTool(transcript, { contextWindow: model.contextWindow, ...parameters });\n\tlet providerTurnCount = 0;\n\tconst agent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt: CONTEXT_COMPACTION_SYSTEM_PROMPT,\n\t\t\tmodel,\n\t\t\tthinkingLevel,\n\t\t\ttools: deletionTool.tools,\n\t\t},\n\t\ttoolExecution: \"parallel\",\n\t\tstreamFn: async (requestModel, context, streamOptions) => {\n\t\t\tconst currentResult = deletionTool.getValidatedResult();\n\t\t\tif (acceptedWithinRun(currentResult, parameters, options.acceptanceTokenBudget)) {\n\t\t\t\treturn createContextCompactionStopStream(\n\t\t\t\t\trequestModel,\n\t\t\t\t\t`Reached an acceptable context-reduction result (${currentResult.stats.percentReduction}%, ${currentResult.stats.tokensAfter} token(s) after deletion); using the validated deletions recorded so far.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (providerTurnCount >= CONTEXT_COMPACTION_MAX_TURNS) {\n\t\t\t\treturn createContextCompactionStopStream(\n\t\t\t\t\trequestModel,\n\t\t\t\t\t`Reached the context compaction turn cap (${CONTEXT_COMPACTION_MAX_TURNS}); using the validated deletions recorded so far.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tproviderTurnCount += 1;\n\t\t\treturn streamSimple(requestModel, context, {\n\t\t\t\t...streamOptions,\n\t\t\t\tmaxTokens,\n\t\t\t\tapiKey,\n\t\t\t\theaders: headers ?? streamOptions?.headers,\n\t\t\t});\n\t\t},\n\t});\n\n\tlet lastNudgedProgressKey: string | undefined;\n\tlet nudgeCount = 0;\n\tconst unsubscribeNudge = agent.subscribe((event, eventSignal) => {\n\t\tif (event.type !== \"turn_end\" || signal?.aborted || eventSignal.aborted) return;\n\t\tif (event.message.role !== \"assistant\") return;\n\t\tif (event.message.stopReason === \"error\" || event.message.stopReason === \"aborted\") return;\n\t\tif (event.message.content.some((content) => content.type === \"toolCall\")) return;\n\t\tif (nudgeCount >= CONTEXT_COMPACTION_MAX_PLANNER_NUDGES) return;\n\t\tconst currentResult = deletionTool.getValidatedResult();\n\t\tif (acceptedWithinRun(currentResult, parameters, options.acceptanceTokenBudget)) return;\n\t\tconst progressKey = contextCompactionProgressKey(currentResult);\n\t\tif (progressKey === lastNudgedProgressKey) return;\n\t\tlastNudgedProgressKey = progressKey;\n\t\tnudgeCount += 1;\n\t\tagent.followUp(createContextCompactionTargetNudgeMessage(currentResult, parameters));\n\t});\n\n\tconst abortOnSignal = () => agent.abort();\n\tsignal?.addEventListener(\"abort\", abortOnSignal, { once: true });\n\ttry {\n\t\tawait agent.prompt(promptMessage);\n\t} catch (error) {\n\t\tif (signal?.aborted) throw new Error(\"Context compaction failed: Request was aborted\");\n\t\tthrow new Error(`Context compaction failed: ${formatCopilotProviderError(model.provider, formatErrorMessage(error))}`);\n\t} finally {\n\t\tsignal?.removeEventListener(\"abort\", abortOnSignal);\n\t\tunsubscribeNudge();\n\t\ttranscriptFile.cleanup();\n\t}\n\n\tif (signal?.aborted) throw new Error(\"Context compaction failed: Request was aborted\");\n\tif (agent.state.errorMessage) {\n\t\tconst formattedErrorMessage = formatCopilotProviderError(model.provider, agent.state.errorMessage);\n\t\tif (isContextCompactionOverflowError(model, agent.state.errorMessage)) {\n\t\t\treturn {\n\t\t\t\tvalidatedResult: deletionTool.getValidatedResult(),\n\t\t\t\tlastToolError: deletionTool.getLastError(),\n\t\t\t\tproviderError: formattedErrorMessage === agent.state.errorMessage ? undefined : formattedErrorMessage,\n\t\t\t};\n\t\t}\n\t\tthrow new Error(`Context compaction failed: ${formattedErrorMessage}`);\n\t}\n\tif (deletionTool.getCallCount() === 0) {\n\t\tthrow new Error(\n\t\t\t`Context compaction did not call any transcript inspection, budget, or deletion tools (${CONTEXT_SEARCH_TRANSCRIPT_TOOL_NAME}, ${CONTEXT_READ_ENTRY_TOOL_NAME}, ${CONTEXT_COMPACTION_BUDGET_TOOL_NAME}, ${CONTEXT_DELETE_TOOL_NAME}, or ${CONTEXT_GREP_DELETE_TOOL_NAME})`,\n\t\t);\n\t}\n\treturn {\n\t\tvalidatedResult: deletionTool.getValidatedResult(),\n\t\tlastToolError: deletionTool.getLastError(),\n\t\tproviderError: undefined,\n\t};\n}\n\nfunction hasMetContextCompactionTarget(\n\trun: ContextDeletionRun,\n\tparameters: ContextCompactionParameters,\n): run is ContextDeletionRun & { validatedResult: ValidatedContextDeletionResult } {\n\treturn contextCompactionTargetMet(run.validatedResult, parameters);\n}\n\nfunction formatContextCompactionTargetFailureMessage(\n\tattempts: readonly ContextDeletionRunAttempt[],\n\tparameters: ContextCompactionParameters,\n): string {\n\tconst targetLabel = contextCompactionTargetLabel(parameters);\n\tif (attempts.length === 0) return `Context compaction did not meet the strict ${targetLabel} reduction requirement`;\n\tconst attemptDetails = attempts\n\t\t.map((attempt) => {\n\t\t\tconst reduction = contextCompactionProgressPercent(attempt.validatedResult);\n\t\t\tconst deletionCount = attempt.validatedResult?.deletedTargets.length ?? 0;\n\t\t\tconst tokensAfterText = attempt.validatedResult ? `; tokens after: ${attempt.validatedResult.stats.tokensAfter}` : \"\";\n\t\t\tconst toolErrorText = attempt.lastToolError ? `; last deletion tool error: ${attempt.lastToolError}` : \"\";\n\t\t\tconst providerErrorText = attempt.providerError ? `; provider error: ${attempt.providerError}` : \"\";\n\t\t\treturn `attempt reached ${reduction}% with ${deletionCount} validated deletion target(s)${tokensAfterText}${toolErrorText}${providerErrorText}`;\n\t\t})\n\t\t.join(\"; \");\n\treturn `Context compaction did not meet the strict ${targetLabel} reduction requirement; ${attemptDetails}`;\n}\n\nfunction runFailedAttempt(error: Error): ContextDeletionRunAttempt {\n\treturn { validatedResult: undefined, lastToolError: undefined, providerError: error.message };\n}\n\nfunction shouldRethrowPlannerError(error: Error, signal: AbortSignal | undefined, ladder: ContextCompactionLadderOptions | undefined): boolean {\n\tif (signal?.aborted) return true;\n\tif (error.message === \"Context compaction failed: Request was aborted\") return true;\n\treturn ladder?.criticalEvictionTokenBudget === undefined;\n}\n\ninterface ContextDeletionRunAttempt extends ContextDeletionRun {}\n\nexport async function contextCompact(\n\tpreparation: ContextCompactionPreparation,\n\tmodel: Model<Api>,\n\tapiKey: string,\n\theaders?: Record<string, string>,\n\tsignal?: AbortSignal,\n\tthinkingLevel: ThinkingLevel = \"off\",\n\tladder?: ContextCompactionLadderOptions,\n): Promise<ValidatedContextDeletionResult> {\n\tconst parameters = normalizeContextCompactionParameters(\n\t\tpreparation.parameters ?? preparation.transcript.parameters,\n\t\tpreparation.parameters?.query ?? preparation.transcript.parameters?.query ?? CONTEXT_COMPACTION_AUTO_QUERY,\n\t);\n\tconst transcript: CompactableTranscript = { ...preparation.transcript, parameters };\n\tconst attempts: ContextDeletionRunAttempt[] = [];\n\ttry {\n\t\tconst standardRun = await runContextDeletionAssistant(transcript, model, apiKey, headers, signal, thinkingLevel, parameters);\n\t\tif (targetMetAcceptedForLadder(standardRun, parameters, ladder)) return standardRun.validatedResult;\n\t\tif (feasibleAccepted(standardRun.validatedResult, ladder?.acceptanceTokenBudget)) return standardRun.validatedResult;\n\t\tattempts.push({ ...standardRun });\n\t} catch (error) {\n\t\tconst formattedError = error instanceof Error ? error : new Error(formatErrorMessage(error));\n\t\tif (shouldRethrowPlannerError(formattedError, signal, ladder)) throw formattedError;\n\t\tattempts.push(runFailedAttempt(formattedError));\n\t}\n\n\tif (ladder?.criticalEvictionTokenBudget === undefined) {\n\t\tthrow new Error(formatContextCompactionTargetFailureMessage(attempts, parameters));\n\t}\n\n\ttry {\n\t\tconst criticalParameters = criticalCompactionParameters(parameters);\n\t\tconst criticalTranscript = relaxTranscriptForCriticalEviction(transcript);\n\t\tconst criticalRun = await runContextDeletionAssistant(\n\t\t\tcriticalTranscript,\n\t\t\tmodel,\n\t\t\tapiKey,\n\t\t\theaders,\n\t\t\tsignal,\n\t\t\tthinkingLevel,\n\t\t\tcriticalParameters,\n\t\t\t{\n\t\t\t\tpromptSuffix: CONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT,\n\t\t\t\tacceptanceTokenBudget: ladder.criticalEvictionTokenBudget,\n\t\t\t},\n\t\t);\n\t\tif (targetMetAcceptedForLadder(criticalRun, parameters, ladder)) return criticalRun.validatedResult;\n\t\tif (feasibleAccepted(criticalRun.validatedResult, ladder.criticalEvictionTokenBudget)) return criticalRun.validatedResult;\n\t\tattempts.push({ ...criticalRun });\n\t} catch (error) {\n\t\tconst formattedError = error instanceof Error ? error : new Error(formatErrorMessage(error));\n\t\tif (signal?.aborted || formattedError.message === \"Context compaction failed: Request was aborted\") throw formattedError;\n\t\tattempts.push(runFailedAttempt(formattedError));\n\t}\n\n\ttry {\n\t\treturn runDeterministicContextEviction(transcript, ladder.criticalEvictionTokenBudget);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : formatErrorMessage(error);\n\t\tthrow new Error(`${formatContextCompactionTargetFailureMessage(attempts, parameters)}; ${message}`);\n\t}\n}\n"]}