byterover-cli 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.md +6 -81
  2. package/dist/agent/core/domain/llm/index.d.ts +1 -1
  3. package/dist/agent/core/domain/llm/index.js +1 -1
  4. package/dist/agent/core/domain/llm/registry.d.ts +8 -0
  5. package/dist/agent/core/domain/llm/registry.js +34 -0
  6. package/dist/agent/core/domain/sandbox/types.d.ts +2 -0
  7. package/dist/agent/core/domain/tools/constants.d.ts +3 -0
  8. package/dist/agent/core/domain/tools/constants.js +3 -0
  9. package/dist/agent/core/interfaces/cipher-services.d.ts +2 -4
  10. package/dist/agent/core/interfaces/i-cipher-agent.d.ts +9 -1
  11. package/dist/agent/core/interfaces/i-sandbox-service.d.ts +8 -0
  12. package/dist/agent/core/interfaces/i-tool-provider.d.ts +10 -0
  13. package/dist/agent/core/interfaces/i-tool-scheduler.d.ts +9 -0
  14. package/dist/agent/infra/agent/agent-schemas.d.ts +0 -9
  15. package/dist/agent/infra/agent/agent-schemas.js +0 -3
  16. package/dist/agent/infra/agent/cipher-agent.d.ts +25 -1
  17. package/dist/agent/infra/agent/cipher-agent.js +138 -11
  18. package/dist/agent/infra/agent/provider-update-config.d.ts +0 -2
  19. package/dist/agent/infra/agent/service-initializer.d.ts +2 -6
  20. package/dist/agent/infra/agent/service-initializer.js +45 -38
  21. package/dist/agent/infra/blob/blob-storage-factory.d.ts +2 -2
  22. package/dist/agent/infra/blob/blob-storage-factory.js +4 -4
  23. package/dist/agent/infra/blob/file-blob-storage.d.ts +96 -0
  24. package/dist/agent/infra/blob/file-blob-storage.js +454 -0
  25. package/dist/agent/infra/blob/index.d.ts +2 -3
  26. package/dist/agent/infra/blob/index.js +4 -6
  27. package/dist/agent/infra/llm/agent-llm-service.d.ts +3 -0
  28. package/dist/agent/infra/llm/agent-llm-service.js +34 -52
  29. package/dist/agent/infra/llm/context/compression/compression-helpers.d.ts +35 -0
  30. package/dist/agent/infra/llm/context/compression/compression-helpers.js +124 -0
  31. package/dist/agent/infra/llm/context/compression/escalated-compression.d.ts +62 -0
  32. package/dist/agent/infra/llm/context/compression/escalated-compression.js +144 -0
  33. package/dist/agent/infra/llm/context/compression/index.d.ts +3 -0
  34. package/dist/agent/infra/llm/context/compression/index.js +3 -0
  35. package/dist/agent/infra/llm/context/compression/reactive-overflow.d.ts +0 -27
  36. package/dist/agent/infra/llm/context/compression/reactive-overflow.js +5 -122
  37. package/dist/agent/infra/llm/context/context-manager.d.ts +20 -1
  38. package/dist/agent/infra/llm/context/context-manager.js +37 -7
  39. package/dist/agent/infra/llm/providers/index.js +0 -2
  40. package/dist/agent/infra/llm/providers/types.d.ts +1 -5
  41. package/dist/agent/infra/map/agentic-map-service.d.ts +97 -0
  42. package/dist/agent/infra/map/agentic-map-service.js +309 -0
  43. package/dist/agent/infra/map/context-tree-store.d.ts +94 -0
  44. package/dist/agent/infra/map/context-tree-store.js +278 -0
  45. package/dist/agent/infra/map/index.d.ts +4 -0
  46. package/dist/agent/infra/map/index.js +4 -0
  47. package/dist/agent/infra/map/llm-map-memory.d.ts +59 -0
  48. package/dist/agent/infra/map/llm-map-memory.js +187 -0
  49. package/dist/agent/infra/map/llm-map-service.d.ts +36 -0
  50. package/dist/agent/infra/map/llm-map-service.js +118 -0
  51. package/dist/agent/infra/map/map-shared.d.ts +140 -0
  52. package/dist/agent/infra/map/map-shared.js +325 -0
  53. package/dist/agent/infra/map/worker-pool.d.ts +45 -0
  54. package/dist/agent/infra/map/worker-pool.js +73 -0
  55. package/dist/agent/infra/sandbox/curation-helpers.d.ts +62 -0
  56. package/dist/agent/infra/sandbox/curation-helpers.js +219 -0
  57. package/dist/agent/infra/sandbox/sandbox-service.d.ts +12 -0
  58. package/dist/agent/infra/sandbox/sandbox-service.js +39 -7
  59. package/dist/agent/infra/sandbox/tools-sdk.d.ts +48 -1
  60. package/dist/agent/infra/sandbox/tools-sdk.js +52 -1
  61. package/dist/agent/infra/session/session-manager.d.ts +8 -1
  62. package/dist/agent/infra/session/session-manager.js +24 -4
  63. package/dist/agent/infra/storage/file-key-storage.d.ts +142 -0
  64. package/dist/agent/infra/storage/file-key-storage.js +572 -0
  65. package/dist/agent/infra/storage/granular-history-storage.d.ts +1 -1
  66. package/dist/agent/infra/storage/granular-history-storage.js +1 -1
  67. package/dist/agent/infra/system-prompt/contributors/context-tree-structure-contributor.d.ts +4 -0
  68. package/dist/agent/infra/system-prompt/contributors/context-tree-structure-contributor.js +42 -14
  69. package/dist/agent/infra/system-prompt/contributors/map-selection-contributor.d.ts +16 -0
  70. package/dist/agent/infra/system-prompt/contributors/map-selection-contributor.js +47 -0
  71. package/dist/agent/infra/tools/core-tool-scheduler.js +3 -1
  72. package/dist/agent/infra/tools/implementations/agentic-map-tool.d.ts +35 -0
  73. package/dist/agent/infra/tools/implementations/agentic-map-tool.js +156 -0
  74. package/dist/agent/infra/tools/implementations/code-exec-tool.js +1 -0
  75. package/dist/agent/infra/tools/implementations/curate-tool.d.ts +9 -9
  76. package/dist/agent/infra/tools/implementations/expand-knowledge-tool.d.ts +18 -0
  77. package/dist/agent/infra/tools/implementations/expand-knowledge-tool.js +43 -0
  78. package/dist/agent/infra/tools/implementations/llm-map-tool.d.ts +24 -0
  79. package/dist/agent/infra/tools/implementations/llm-map-tool.js +87 -0
  80. package/dist/agent/infra/tools/implementations/memory-symbol-tree.d.ts +28 -1
  81. package/dist/agent/infra/tools/implementations/memory-symbol-tree.js +27 -3
  82. package/dist/agent/infra/tools/implementations/search-knowledge-service.d.ts +1 -0
  83. package/dist/agent/infra/tools/implementations/search-knowledge-service.js +83 -12
  84. package/dist/agent/infra/tools/implementations/search-knowledge-tool.js +2 -2
  85. package/dist/agent/infra/tools/tool-manager.js +6 -0
  86. package/dist/agent/infra/tools/tool-provider.d.ts +12 -0
  87. package/dist/agent/infra/tools/tool-provider.js +78 -0
  88. package/dist/agent/infra/tools/tool-registry.d.ts +14 -0
  89. package/dist/agent/infra/tools/tool-registry.js +32 -0
  90. package/dist/agent/resources/prompts/system-prompt.yml +48 -74
  91. package/dist/agent/resources/tools/expand_knowledge.txt +20 -0
  92. package/dist/oclif/commands/curate/index.js +1 -2
  93. package/dist/oclif/commands/main.js +1 -0
  94. package/dist/oclif/commands/providers/connect.d.ts +1 -3
  95. package/dist/oclif/commands/providers/connect.js +7 -29
  96. package/dist/oclif/commands/query.js +1 -2
  97. package/dist/server/constants.d.ts +7 -0
  98. package/dist/server/constants.js +8 -0
  99. package/dist/server/core/domain/entities/provider-registry.js +1 -15
  100. package/dist/server/core/domain/knowledge/memory-scoring.js +1 -1
  101. package/dist/server/core/domain/knowledge/summary-types.d.ts +126 -0
  102. package/dist/server/core/domain/knowledge/summary-types.js +7 -0
  103. package/dist/server/core/domain/transport/schemas.d.ts +0 -4
  104. package/dist/server/core/interfaces/context-tree/i-context-tree-archive-service.d.ts +30 -0
  105. package/dist/server/core/interfaces/context-tree/i-context-tree-archive-service.js +1 -0
  106. package/dist/server/core/interfaces/context-tree/i-context-tree-manifest-service.d.ts +30 -0
  107. package/dist/server/core/interfaces/context-tree/i-context-tree-manifest-service.js +1 -0
  108. package/dist/server/core/interfaces/context-tree/i-context-tree-summary-service.d.ts +29 -0
  109. package/dist/server/core/interfaces/context-tree/i-context-tree-summary-service.js +1 -0
  110. package/dist/server/infra/cogit/context-tree-to-push-context-mapper.js +10 -3
  111. package/dist/server/infra/connectors/skill/skill-connector.d.ts +4 -0
  112. package/dist/server/infra/connectors/skill/skill-connector.js +4 -0
  113. package/dist/server/infra/context-tree/children-hash.d.ts +20 -0
  114. package/dist/server/infra/context-tree/children-hash.js +22 -0
  115. package/dist/server/infra/context-tree/derived-artifact.d.ts +28 -0
  116. package/dist/server/infra/context-tree/derived-artifact.js +48 -0
  117. package/dist/server/infra/context-tree/file-context-tree-archive-service.d.ts +37 -0
  118. package/dist/server/infra/context-tree/file-context-tree-archive-service.js +219 -0
  119. package/dist/server/infra/context-tree/file-context-tree-manifest-service.d.ts +50 -0
  120. package/dist/server/infra/context-tree/file-context-tree-manifest-service.js +278 -0
  121. package/dist/server/infra/context-tree/file-context-tree-merger.js +4 -0
  122. package/dist/server/infra/context-tree/file-context-tree-snapshot-service.js +12 -4
  123. package/dist/server/infra/context-tree/file-context-tree-summary-service.d.ts +44 -0
  124. package/dist/server/infra/context-tree/file-context-tree-summary-service.js +313 -0
  125. package/dist/server/infra/context-tree/file-context-tree-writer-service.js +5 -0
  126. package/dist/server/infra/context-tree/prompts/summary-generation.d.ts +22 -0
  127. package/dist/server/infra/context-tree/prompts/summary-generation.js +45 -0
  128. package/dist/server/infra/context-tree/snapshot-diff.d.ts +19 -0
  129. package/dist/server/infra/context-tree/snapshot-diff.js +39 -0
  130. package/dist/server/infra/context-tree/summary-frontmatter.d.ts +24 -0
  131. package/dist/server/infra/context-tree/summary-frontmatter.js +111 -0
  132. package/dist/server/infra/daemon/agent-process.js +2 -14
  133. package/dist/server/infra/executor/curate-executor.d.ts +1 -0
  134. package/dist/server/infra/executor/curate-executor.js +82 -34
  135. package/dist/server/infra/executor/folder-pack-executor.js +1 -1
  136. package/dist/server/infra/executor/pre-compaction/compaction-escalation.d.ts +6 -0
  137. package/dist/server/infra/executor/pre-compaction/compaction-escalation.js +6 -0
  138. package/dist/server/infra/executor/pre-compaction/index.d.ts +3 -0
  139. package/dist/server/infra/executor/pre-compaction/index.js +1 -0
  140. package/dist/server/infra/executor/pre-compaction/pre-compaction-service.d.ts +59 -0
  141. package/dist/server/infra/executor/pre-compaction/pre-compaction-service.js +124 -0
  142. package/dist/server/infra/executor/pre-compaction/prompts.d.ts +24 -0
  143. package/dist/server/infra/executor/pre-compaction/prompts.js +47 -0
  144. package/dist/server/infra/executor/query-executor.d.ts +3 -0
  145. package/dist/server/infra/executor/query-executor.js +39 -4
  146. package/dist/server/infra/http/authenticated-http-client.js +4 -0
  147. package/dist/server/infra/http/provider-model-fetcher-registry.js +1 -5
  148. package/dist/server/infra/http/provider-model-fetchers.d.ts +0 -14
  149. package/dist/server/infra/http/provider-model-fetchers.js +0 -132
  150. package/dist/server/infra/provider/provider-config-resolver.js +0 -55
  151. package/dist/server/utils/curate-result-parser.d.ts +4 -4
  152. package/dist/shared/constants/curation.d.ts +6 -0
  153. package/dist/shared/constants/curation.js +6 -0
  154. package/dist/shared/utils/escalation-utils.d.ts +59 -0
  155. package/dist/shared/utils/escalation-utils.js +141 -0
  156. package/dist/tui/components/command-input.js +1 -1
  157. package/dist/tui/components/inline-prompts/inline-confirm.js +6 -1
  158. package/dist/tui/features/commands/definitions/exit.d.ts +2 -0
  159. package/dist/tui/features/commands/definitions/exit.js +9 -0
  160. package/dist/tui/features/commands/definitions/index.js +3 -0
  161. package/dist/tui/features/exit/components/exit-flow.d.ts +10 -0
  162. package/dist/tui/features/exit/components/exit-flow.js +19 -0
  163. package/dist/tui/features/provider/components/provider-flow.js +1 -21
  164. package/oclif.manifest.json +100 -109
  165. package/package.json +11 -4
  166. package/dist/agent/infra/blob/migrations.d.ts +0 -63
  167. package/dist/agent/infra/blob/migrations.js +0 -148
  168. package/dist/agent/infra/blob/sqlite-blob-storage.d.ts +0 -82
  169. package/dist/agent/infra/blob/sqlite-blob-storage.js +0 -307
  170. package/dist/agent/infra/llm/providers/google-vertex.d.ts +0 -15
  171. package/dist/agent/infra/llm/providers/google-vertex.js +0 -36
  172. package/dist/agent/infra/storage/blob-history-storage.d.ts +0 -81
  173. package/dist/agent/infra/storage/blob-history-storage.js +0 -193
  174. package/dist/agent/infra/storage/dual-format-history-storage.d.ts +0 -83
  175. package/dist/agent/infra/storage/dual-format-history-storage.js +0 -165
  176. package/dist/agent/infra/storage/sqlite-key-storage.d.ts +0 -113
  177. package/dist/agent/infra/storage/sqlite-key-storage.js +0 -438
  178. package/dist/server/infra/provider/vertex-ai-utils.d.ts +0 -10
  179. package/dist/server/infra/provider/vertex-ai-utils.js +0 -28
  180. package/dist/tui/features/provider/components/credential-path-dialog.d.ts +0 -30
  181. package/dist/tui/features/provider/components/credential-path-dialog.js +0 -85
@@ -2,6 +2,11 @@ import path from 'node:path';
2
2
  import { FileValidationError } from '../../core/domain/errors/task-error.js';
3
3
  import { createFileContentReader, } from '../../utils/file-content-reader.js';
4
4
  import { validateFileForCurate } from '../../utils/file-validator.js';
5
+ import { FileContextTreeManifestService } from '../context-tree/file-context-tree-manifest-service.js';
6
+ import { FileContextTreeSnapshotService } from '../context-tree/file-context-tree-snapshot-service.js';
7
+ import { FileContextTreeSummaryService } from '../context-tree/file-context-tree-summary-service.js';
8
+ import { diffStates } from '../context-tree/snapshot-diff.js';
9
+ import { PreCompactionService } from './pre-compaction/pre-compaction-service.js';
5
10
  /**
6
11
  * CurateExecutor - Executes curate tasks with an injected CipherAgent.
7
12
  *
@@ -26,48 +31,72 @@ export class CurateExecutor {
26
31
  /** Last curation status — available for future status-check command */
27
32
  lastStatus;
28
33
  fileContentReader;
34
+ preCompactionService = new PreCompactionService();
29
35
  constructor(fileContentReader) {
30
36
  this.fileContentReader = fileContentReader ?? createFileContentReader();
31
37
  }
32
38
  async executeWithAgent(agent, options) {
33
39
  const { clientCwd, content, files, taskId } = options;
34
- // Create per-task session for parallel isolation (own sandbox + history + LLM service)
35
- const taskSessionId = await agent.createTaskSession(taskId, 'curate');
36
- // Process file references - reads file contents directly
40
+ // --- Phase 1: Preprocessing (no sessions created yet safe to throw) ---
37
41
  const fileReferenceInstructions = await this.processFileReferences(files ?? [], clientCwd);
38
- // Build full context (content + optional file references)
39
42
  const fullContext = fileReferenceInstructions ? `${content}\n${fileReferenceInstructions}` : content;
40
- // Task-scoped variable names for RLM pattern.
41
- // Replace hyphens with underscores: UUIDs have hyphens which are invalid in JS identifiers,
42
- // so the LLM would naturally use underscores when writing code-exec calls — causing a
43
- // ReferenceError if the variable was stored under the hyphen version.
44
- const taskIdSafe = taskId.replaceAll('-', '_');
45
- const ctxVar = `__curate_ctx_${taskIdSafe}`;
46
- const histVar = `__curate_hist_${taskIdSafe}`;
47
- const metaVar = `__curate_meta_${taskIdSafe}`;
48
- // Compute context metadata (RLM pattern — LM sees metadata, not raw content)
49
- const contextLines = fullContext.split('\n');
50
- const metadata = {
51
- charCount: fullContext.length,
52
- lineCount: contextLines.length,
53
- messageCount: (fullContext.match(/\n\n\[(USER|ASSISTANT)\]:/g) || []).length,
54
- preview: fullContext.slice(0, 500),
55
- type: 'string',
56
- };
57
- // Inject context, metadata, and empty history into the TASK session's sandbox
58
- agent.setSandboxVariableOnSession(taskSessionId, ctxVar, fullContext);
59
- agent.setSandboxVariableOnSession(taskSessionId, histVar, { entries: [], totalProcessed: 0 });
60
- agent.setSandboxVariableOnSession(taskSessionId, metaVar, metadata);
61
- // Prompt with metadata guidance (RLM pattern: LM sees metadata first, peeks via slicing)
62
- const prompt = [
63
- `Curate using RLM approach.`,
64
- `Context variable: ${ctxVar} (${metadata.charCount} chars, ${metadata.lineCount} lines, ${metadata.messageCount} messages)`,
65
- `History variable: ${histVar}`,
66
- `Metadata variable: ${metaVar}`,
67
- `IMPORTANT: Do NOT print raw context. Use slicing to peek at sections (e.g., ${ctxVar}.slice(0, 3000)).`,
68
- `Use silent mode (silent: true) for variable assignments. Use tools.agentQuery() for chunk processing.`,
69
- ].join('\n');
43
+ // --- Phase 2: Pre-compaction (fail-open, manages its own session lifecycle) ---
44
+ const compactionResult = await this.preCompactionService.compact(agent, fullContext, taskId);
45
+ const effectiveContext = compactionResult.context;
46
+ // --- Phase 3: Curation (session created AFTER preprocessing + compaction) ---
47
+ // Capture pre-curation state for snapshot diff (summary propagation)
48
+ const baseDir = clientCwd ?? process.cwd();
49
+ const snapshotService = new FileContextTreeSnapshotService({ baseDirectory: baseDir });
50
+ let preState;
51
+ try {
52
+ preState = await snapshotService.getCurrentState(baseDir);
53
+ }
54
+ catch {
55
+ // Fail-open: if snapshot fails, skip summary propagation
56
+ }
57
+ const taskSessionId = await agent.createTaskSession(taskId, 'curate', { mapRootEligible: true });
70
58
  try {
59
+ // Task-scoped variable names for RLM pattern.
60
+ // Replace hyphens with underscores: UUIDs have hyphens which are invalid in JS identifiers,
61
+ // so the LLM would naturally use underscores when writing code-exec calls — causing a
62
+ // ReferenceError if the variable was stored under the hyphen version.
63
+ const taskIdSafe = taskId.replaceAll('-', '_');
64
+ const ctxVar = `__curate_ctx_${taskIdSafe}`;
65
+ const histVar = `__curate_hist_${taskIdSafe}`;
66
+ const metaVar = `__curate_meta_${taskIdSafe}`;
67
+ // Compute context metadata (RLM pattern — LM sees metadata, not raw content)
68
+ const contextLines = effectiveContext.split('\n');
69
+ const metadata = {
70
+ charCount: effectiveContext.length,
71
+ lineCount: contextLines.length,
72
+ messageCount: (effectiveContext.match(/\n\n\[(USER|ASSISTANT)\]:/g) || []).length,
73
+ ...(compactionResult.preCompacted && {
74
+ originalCharCount: compactionResult.originalCharCount,
75
+ preCompacted: true,
76
+ preCompactionTier: compactionResult.preCompactionTier,
77
+ }),
78
+ preview: effectiveContext.slice(0, 500),
79
+ type: 'string',
80
+ };
81
+ // Inject context, metadata, empty history, and taskId into the TASK session's sandbox
82
+ const taskIdVar = `__taskId_${taskIdSafe}`;
83
+ agent.setSandboxVariableOnSession(taskSessionId, ctxVar, effectiveContext);
84
+ agent.setSandboxVariableOnSession(taskSessionId, histVar, { entries: [], totalProcessed: 0 });
85
+ agent.setSandboxVariableOnSession(taskSessionId, metaVar, metadata);
86
+ agent.setSandboxVariableOnSession(taskSessionId, taskIdVar, taskId);
87
+ // Prompt with curation helpers guidance (tools.curation.* replaces manual infrastructure code)
88
+ const prompt = [
89
+ `Curate using RLM approach.`,
90
+ `Context variable: ${ctxVar} (${metadata.charCount} chars, ${metadata.lineCount} lines, ${metadata.messageCount} messages)`,
91
+ `History variable: ${histVar}`,
92
+ `Metadata variable: ${metaVar}`,
93
+ `Task ID variable: ${taskIdVar} (pass as bare variable, not a string)`,
94
+ `IMPORTANT: Do NOT print raw context. Start with tools.curation.recon(${ctxVar}, ${metaVar}, ${histVar}) to assess.`,
95
+ `For chunked extraction use tools.curation.mapExtract(). Pass taskId: ${taskIdVar} (bare variable).`,
96
+ `IMPORTANT: Any code_exec call containing mapExtract MUST use timeout: 300000 on the code_exec tool call itself (not inside mapExtract options).`,
97
+ `Use tools.curation.groupBySubject() and tools.curation.dedup() to organize extractions.`,
98
+ `Verify via result.applied[].filePath — do NOT call readFile for verification.`,
99
+ ].join('\n');
71
100
  // Execute on the task session (isolated sandbox + history)
72
101
  // Task lifecycle is managed by Transport (task:started, task:completed, task:error)
73
102
  const response = await agent.executeOnSession(taskSessionId, prompt, {
@@ -76,6 +105,25 @@ export class CurateExecutor {
76
105
  });
77
106
  // Parse curation status from agent response for status tracking
78
107
  this.lastStatus = this.parseCurationStatus(taskId, response);
108
+ // --- Phase 4: Post-curation summary propagation (fail-open) ---
109
+ if (preState) {
110
+ try {
111
+ const postState = await snapshotService.getCurrentState(baseDir);
112
+ const changedPaths = diffStates(preState, postState);
113
+ if (changedPaths.length > 0) {
114
+ const summaryService = new FileContextTreeSummaryService();
115
+ const results = await summaryService.propagateStaleness(changedPaths, agent, baseDir);
116
+ // Opportunistic manifest rebuild (pre-warm for next query)
117
+ if (results.some((r) => r.actionTaken)) {
118
+ const manifestService = new FileContextTreeManifestService({ baseDirectory: baseDir });
119
+ await manifestService.buildManifest(baseDir);
120
+ }
121
+ }
122
+ }
123
+ catch {
124
+ // Fail-open: summary/manifest errors never block curation
125
+ }
126
+ }
79
127
  return response;
80
128
  }
81
129
  finally {
@@ -773,7 +773,7 @@ await tools.curate([{
773
773
  throw new Error(`Failed to write temp file: ${error instanceof Error ? error.message : String(error)}`);
774
774
  }
775
775
  // Create per-task session for parallel isolation (own sandbox + history + LLM service)
776
- const taskSessionId = await agent.createTaskSession(taskId, 'curate');
776
+ const taskSessionId = await agent.createTaskSession(taskId, 'curate', { mapRootEligible: true });
777
777
  // Step 3: Store full instructions as sandbox variable (lazy prompt loading).
778
778
  // This saves ~12-15K tokens by keeping the massive instruction set out of the prompt.
779
779
  // The LLM reads instructions on-demand via code_exec.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Re-export from shared utilities.
3
+ * Original implementation moved to src/shared/utils/escalation-utils.ts
4
+ * to allow cross-layer reuse (both server/ and agent/ can import).
5
+ */
6
+ export { buildDeterministicFallbackCompaction, type CompactionEscalationTier, estimateTokens, isCompactionOutputValid, shouldAcceptCompactionOutput, withAggressiveCompactionDirective, } from '../../../../shared/utils/escalation-utils.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Re-export from shared utilities.
3
+ * Original implementation moved to src/shared/utils/escalation-utils.ts
4
+ * to allow cross-layer reuse (both server/ and agent/ can import).
5
+ */
6
+ export { buildDeterministicFallbackCompaction, estimateTokens, isCompactionOutputValid, shouldAcceptCompactionOutput, withAggressiveCompactionDirective, } from '../../../../shared/utils/escalation-utils.js';
@@ -0,0 +1,3 @@
1
+ export type { CompactionEscalationTier } from './compaction-escalation.js';
2
+ export { PRE_COMPACTION_CHAR_THRESHOLD, PreCompactionService } from './pre-compaction-service.js';
3
+ export type { PreCompactionResult } from './pre-compaction-service.js';
@@ -0,0 +1 @@
1
+ export { PRE_COMPACTION_CHAR_THRESHOLD, PreCompactionService } from './pre-compaction-service.js';
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Pre-Compaction Service — Three-Level Escalation for curation context.
3
+ *
4
+ * Compacts large source context BEFORE the curation agent sees it,
5
+ * saving the agent from wasting iterations on chunking/extraction code.
6
+ *
7
+ * Fail-open: any error returns the original context unchanged.
8
+ */
9
+ import type { ICipherAgent } from '../../../../agent/core/interfaces/i-cipher-agent.js';
10
+ import { type CompactionEscalationTier } from './compaction-escalation.js';
11
+ /**
12
+ * Character threshold below which compaction is skipped.
13
+ * Re-exported from shared constant for backwards compatibility.
14
+ */
15
+ export declare const PRE_COMPACTION_CHAR_THRESHOLD = 20000;
16
+ /**
17
+ * Result of a pre-compaction operation.
18
+ */
19
+ export interface PreCompactionResult {
20
+ /** The (possibly compacted) context text */
21
+ context: string;
22
+ /** Original character count before compaction */
23
+ originalCharCount: number;
24
+ /** Whether compaction was actually applied */
25
+ preCompacted: boolean;
26
+ /** Which escalation tier succeeded (only set if preCompacted is true) */
27
+ preCompactionTier?: CompactionEscalationTier;
28
+ }
29
+ /**
30
+ * Service that pre-compacts curation context using three-level escalation.
31
+ *
32
+ * Level 1 (Normal): LLM compaction with standard prompt
33
+ * Level 2 (Aggressive): LLM compaction with aggressive prompt
34
+ * Level 3 (Fallback): Deterministic truncation (no LLM, always converges)
35
+ */
36
+ export declare class PreCompactionService {
37
+ /**
38
+ * Compact context if it exceeds the character threshold.
39
+ *
40
+ * Fail-open: any error during compaction returns the original context.
41
+ * Deterministic fallback is only used when the LLM responded but with
42
+ * unacceptable output — never when the LLM itself errored.
43
+ * Manages its own session lifecycle (creates + deletes a task session).
44
+ *
45
+ * @param agent - The CipherAgent to use for LLM calls
46
+ * @param context - The source context to compact
47
+ * @param taskId - Parent task ID (compaction uses `${taskId}__compact`)
48
+ * @returns PreCompactionResult with the (possibly compacted) context
49
+ */
50
+ compact(agent: ICipherAgent, context: string, taskId: string): Promise<PreCompactionResult>;
51
+ /**
52
+ * Execute a single compaction pass via the agent.
53
+ *
54
+ * Returns a discriminated result so the caller can distinguish
55
+ * "LLM responded with bad output" from "LLM errored" — the former
56
+ * should escalate to the next tier, the latter should fail-open.
57
+ */
58
+ private executeCompactionPass;
59
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Pre-Compaction Service — Three-Level Escalation for curation context.
3
+ *
4
+ * Compacts large source context BEFORE the curation agent sees it,
5
+ * saving the agent from wasting iterations on chunking/extraction code.
6
+ *
7
+ * Fail-open: any error returns the original context unchanged.
8
+ */
9
+ import { CURATION_CHAR_THRESHOLD } from '../../../../shared/constants/curation.js';
10
+ import { buildDeterministicFallbackCompaction, estimateTokens, isCompactionOutputValid, shouldAcceptCompactionOutput, } from './compaction-escalation.js';
11
+ import { buildCompactionSystemPrompt, buildCompactionUserMessage } from './prompts.js';
12
+ /**
13
+ * Character threshold below which compaction is skipped.
14
+ * Re-exported from shared constant for backwards compatibility.
15
+ */
16
+ export const PRE_COMPACTION_CHAR_THRESHOLD = CURATION_CHAR_THRESHOLD;
17
+ /**
18
+ * Service that pre-compacts curation context using three-level escalation.
19
+ *
20
+ * Level 1 (Normal): LLM compaction with standard prompt
21
+ * Level 2 (Aggressive): LLM compaction with aggressive prompt
22
+ * Level 3 (Fallback): Deterministic truncation (no LLM, always converges)
23
+ */
24
+ export class PreCompactionService {
25
+ /**
26
+ * Compact context if it exceeds the character threshold.
27
+ *
28
+ * Fail-open: any error during compaction returns the original context.
29
+ * Deterministic fallback is only used when the LLM responded but with
30
+ * unacceptable output — never when the LLM itself errored.
31
+ * Manages its own session lifecycle (creates + deletes a task session).
32
+ *
33
+ * @param agent - The CipherAgent to use for LLM calls
34
+ * @param context - The source context to compact
35
+ * @param taskId - Parent task ID (compaction uses `${taskId}__compact`)
36
+ * @returns PreCompactionResult with the (possibly compacted) context
37
+ */
38
+ async compact(agent, context, taskId) {
39
+ const originalCharCount = context.length;
40
+ const failOpen = { context, originalCharCount, preCompacted: false };
41
+ if (originalCharCount <= PRE_COMPACTION_CHAR_THRESHOLD) {
42
+ return failOpen;
43
+ }
44
+ const inputTokens = estimateTokens(context);
45
+ const compactionTaskId = `${taskId}__compact`;
46
+ try {
47
+ const sessionId = await agent.createTaskSession(compactionTaskId, 'query');
48
+ try {
49
+ // --- Pass 1: Normal compaction ---
50
+ const normalPass = await this.executeCompactionPass({ agent, aggressive: false, context, sessionId, taskId: compactionTaskId });
51
+ if (normalPass.errored)
52
+ return failOpen;
53
+ if (normalPass.output && shouldAcceptCompactionOutput(normalPass.output, inputTokens) && isCompactionOutputValid(normalPass.output)) {
54
+ return {
55
+ context: normalPass.output.trim(),
56
+ originalCharCount,
57
+ preCompacted: true,
58
+ preCompactionTier: 'normal',
59
+ };
60
+ }
61
+ // --- Pass 2: Aggressive compaction ---
62
+ const aggressivePass = await this.executeCompactionPass({ agent, aggressive: true, context, sessionId, taskId: compactionTaskId });
63
+ if (aggressivePass.errored)
64
+ return failOpen;
65
+ if (aggressivePass.output && shouldAcceptCompactionOutput(aggressivePass.output, inputTokens) && isCompactionOutputValid(aggressivePass.output)) {
66
+ return {
67
+ context: aggressivePass.output.trim(),
68
+ originalCharCount,
69
+ preCompacted: true,
70
+ preCompactionTier: 'aggressive',
71
+ };
72
+ }
73
+ // --- Pass 3: Deterministic fallback ---
74
+ // Only reached when both passes got LLM responses but output was unacceptable
75
+ const fallbackResult = buildDeterministicFallbackCompaction({
76
+ inputTokens,
77
+ sourceText: context,
78
+ suffixLabel: 'pre-curation compaction',
79
+ });
80
+ return {
81
+ context: fallbackResult,
82
+ originalCharCount,
83
+ preCompacted: true,
84
+ preCompactionTier: 'fallback',
85
+ };
86
+ }
87
+ finally {
88
+ await agent.deleteTaskSession(sessionId);
89
+ }
90
+ }
91
+ catch {
92
+ // Fail-open: return original context on ANY error
93
+ return failOpen;
94
+ }
95
+ }
96
+ /**
97
+ * Execute a single compaction pass via the agent.
98
+ *
99
+ * Returns a discriminated result so the caller can distinguish
100
+ * "LLM responded with bad output" from "LLM errored" — the former
101
+ * should escalate to the next tier, the latter should fail-open.
102
+ */
103
+ async executeCompactionPass(options) {
104
+ try {
105
+ const systemPrompt = buildCompactionSystemPrompt();
106
+ const userMessage = buildCompactionUserMessage(options.context, options.aggressive);
107
+ const prompt = `${systemPrompt}\n\n${userMessage}`;
108
+ const response = await options.agent.executeOnSession(options.sessionId, prompt, {
109
+ executionContext: {
110
+ clearHistory: true,
111
+ commandType: 'query',
112
+ maxIterations: 1,
113
+ maxTokens: 4096,
114
+ temperature: 0.3,
115
+ },
116
+ taskId: options.taskId,
117
+ });
118
+ return { errored: false, output: response || undefined };
119
+ }
120
+ catch {
121
+ return { errored: true, output: undefined };
122
+ }
123
+ }
124
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Compaction prompts for pre-curation context compaction.
3
+ *
4
+ * Self-contained prompts that steer the LLM away from query-mode policy
5
+ * (which would try to search the KB). The real guardrail against
6
+ * non-compaction responses is isCompactionOutputValid().
7
+ */
8
+ /**
9
+ * Build the system prompt for compaction.
10
+ *
11
+ * Opens with a clear, self-contained instruction that overrides
12
+ * query-mode's "answers from KB only" rules. Note: prompt-level override
13
+ * is best-effort; isCompactionOutputValid() is the actual guardrail.
14
+ */
15
+ export declare function buildCompactionSystemPrompt(): string;
16
+ /**
17
+ * Build the user message for a compaction pass.
18
+ *
19
+ * Wraps the context in <source_content> tags for clear delimitation.
20
+ *
21
+ * @param context - The source text to compact
22
+ * @param aggressive - Whether this is the aggressive (pass 2) attempt
23
+ */
24
+ export declare function buildCompactionUserMessage(context: string, aggressive: boolean): string;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Compaction prompts for pre-curation context compaction.
3
+ *
4
+ * Self-contained prompts that steer the LLM away from query-mode policy
5
+ * (which would try to search the KB). The real guardrail against
6
+ * non-compaction responses is isCompactionOutputValid().
7
+ */
8
+ /**
9
+ * Build the system prompt for compaction.
10
+ *
11
+ * Opens with a clear, self-contained instruction that overrides
12
+ * query-mode's "answers from KB only" rules. Note: prompt-level override
13
+ * is best-effort; isCompactionOutputValid() is the actual guardrail.
14
+ */
15
+ export function buildCompactionSystemPrompt() {
16
+ return `You are a knowledge extraction pre-processor. Your ONLY task is to compact the provided text.
17
+
18
+ ## Rules
19
+ - PRESERVE: facts, decisions, code examples, API signatures, diagrams (verbatim), tables (all rows), procedures, file paths, configurations, error patterns
20
+ - REMOVE: conversational filler, repeated explanations, verbose tool call descriptions, meta-commentary, acknowledgments
21
+ - Output clean structured markdown
22
+ - Do NOT wrap output in code blocks or XML tags
23
+ - Do NOT search any knowledge base
24
+ - Do NOT answer questions about the content
25
+ - Do NOT use any tools
26
+ - Output ONLY the compacted text`;
27
+ }
28
+ /**
29
+ * Build the user message for a compaction pass.
30
+ *
31
+ * Wraps the context in <source_content> tags for clear delimitation.
32
+ *
33
+ * @param context - The source text to compact
34
+ * @param aggressive - Whether this is the aggressive (pass 2) attempt
35
+ */
36
+ export function buildCompactionUserMessage(context, aggressive) {
37
+ const instruction = aggressive
38
+ ? 'Compact the following text MORE AGGRESSIVELY. A previous compaction attempt was not short enough. Remove all non-essential detail while keeping core facts, decisions, and code.'
39
+ : 'Compact the following text while preserving all knowledge-worthy information — facts, decisions, code, configurations, procedures.';
40
+ return `${instruction}
41
+
42
+ <source_content>
43
+ ${context}
44
+ </source_content>
45
+
46
+ Output ONLY the compacted text. Do NOT use any tools.`;
47
+ }
@@ -7,6 +7,8 @@ import type { IQueryExecutor, QueryExecuteOptions } from '../../core/interfaces/
7
7
  * All fields are optional — without them, the executor falls back to the original behavior.
8
8
  */
9
9
  export interface QueryExecutorDeps {
10
+ /** Base directory for manifest service (e.g., project path) */
11
+ baseDirectory?: string;
10
12
  /** Enable query result caching (default: false) */
11
13
  enableCache?: boolean;
12
14
  /** File system for reading full document content and computing fingerprints */
@@ -35,6 +37,7 @@ export interface QueryExecutorDeps {
35
37
  */
36
38
  export declare class QueryExecutor implements IQueryExecutor {
37
39
  private static readonly FINGERPRINT_CACHE_TTL_MS;
40
+ private readonly baseDirectory?;
38
41
  private readonly cache?;
39
42
  private cachedFingerprint?;
40
43
  private readonly fileSystem?;
@@ -1,5 +1,7 @@
1
1
  import { join } from 'node:path';
2
2
  import { BRV_DIR, CONTEXT_FILE_EXTENSION, CONTEXT_TREE_DIR } from '../../constants.js';
3
+ import { isDerivedArtifact } from '../context-tree/derived-artifact.js';
4
+ import { FileContextTreeManifestService } from '../context-tree/file-context-tree-manifest-service.js';
3
5
  import { canRespondDirectly, formatDirectResponse, formatNotFoundResponse, } from './direct-search-responder.js';
4
6
  import { QueryResultCache } from './query-result-cache.js';
5
7
  /** Attribution footer appended to all query responses */
@@ -29,11 +31,13 @@ const SMART_ROUTING_MAX_DOCS = 5;
29
31
  */
30
32
  export class QueryExecutor {
31
33
  static FINGERPRINT_CACHE_TTL_MS = 30_000;
34
+ baseDirectory;
32
35
  cache;
33
36
  cachedFingerprint;
34
37
  fileSystem;
35
38
  searchService;
36
39
  constructor(deps) {
40
+ this.baseDirectory = deps?.baseDirectory;
37
41
  this.fileSystem = deps?.fileSystem;
38
42
  this.searchService = deps?.searchService;
39
43
  if (deps?.enableCache) {
@@ -97,6 +101,28 @@ export class QueryExecutor {
97
101
  if (searchResult && this.fileSystem) {
98
102
  prefetchedContext = this.buildPrefetchedContext(searchResult);
99
103
  }
104
+ // Lazy manifest rebuild: provides broad structural awareness for LLM
105
+ let manifestContext;
106
+ if (this.baseDirectory) {
107
+ try {
108
+ const manifestService = new FileContextTreeManifestService({ baseDirectory: this.baseDirectory });
109
+ let manifest = await manifestService.readManifestIfFresh(this.baseDirectory);
110
+ if (!manifest) {
111
+ manifest = await manifestService.buildManifest(this.baseDirectory);
112
+ }
113
+ if (manifest) {
114
+ const resolved = await manifestService.resolveForInjection(manifest, query, this.baseDirectory);
115
+ if (resolved.length > 0) {
116
+ manifestContext = resolved
117
+ .map((e) => `[${e.type} ${e.path}]\n${e.content}`)
118
+ .join('\n\n---\n\n');
119
+ }
120
+ }
121
+ }
122
+ catch {
123
+ // Fail-open: proceed without manifest context
124
+ }
125
+ }
100
126
  // Create per-task session for parallel isolation (own sandbox + history + LLM service)
101
127
  const taskSessionId = await agent.createTaskSession(taskId, 'query');
102
128
  // Task-scoped variable names for sandbox injection (RLM pattern).
@@ -116,6 +142,7 @@ export class QueryExecutor {
116
142
  agent.setSandboxVariableOnSession(taskSessionId, resultsVar, searchResult?.results ?? []);
117
143
  agent.setSandboxVariableOnSession(taskSessionId, metaVar, metadata);
118
144
  const prompt = this.buildQueryPrompt(query, {
145
+ manifestContext,
119
146
  metadata,
120
147
  metaVar,
121
148
  prefetchedContext,
@@ -165,7 +192,7 @@ export class QueryExecutor {
165
192
  * @param options - Prompt options with variable names and metadata
166
193
  */
167
194
  buildQueryPrompt(query, options) {
168
- const { metadata, metaVar, prefetchedContext, resultsVar } = options;
195
+ const { manifestContext, metadata, metaVar, prefetchedContext, resultsVar } = options;
169
196
  const groundingRules = `### Grounding Rules (CRITICAL)
170
197
  - ONLY use information from the curated knowledge base (.brv/context-tree/)
171
198
  - If no relevant knowledge is found, respond: "This topic is not covered in the knowledge base."
@@ -177,6 +204,9 @@ export class QueryExecutor {
177
204
  - **Details**: Key findings with explanations
178
205
  - **Sources**: File paths from .brv/context-tree/
179
206
  - **Gaps**: Note any aspects not covered`;
207
+ const manifestSection = manifestContext
208
+ ? `\n## Structural Context (from manifest)\nThe following provides broad structural awareness of the knowledge base:\n\n${manifestContext}\n`
209
+ : '';
180
210
  if (prefetchedContext) {
181
211
  return `## User Query
182
212
  ${query}
@@ -185,7 +215,7 @@ ${query}
185
215
  The following relevant knowledge was found in the context tree:
186
216
 
187
217
  ${prefetchedContext}
188
-
218
+ ${manifestSection}
189
219
  ## Search Results Variable
190
220
  Additional search results: \`${resultsVar}\` (${metadata.resultCount} results, top score: ${metadata.topScore.toFixed(2)})
191
221
  Metadata: \`${metaVar}\`
@@ -202,7 +232,7 @@ ${responseFormat}`;
202
232
  }
203
233
  return `## User Query
204
234
  ${query}
205
-
235
+ ${manifestSection}
206
236
  ## Search Results Variable
207
237
  Search results: \`${resultsVar}\` (${metadata.resultCount} results, top score: ${metadata.topScore.toFixed(2)})
208
238
  Metadata: \`${metaVar}\`
@@ -234,7 +264,12 @@ ${responseFormat}`;
234
264
  maxResults: 10_000,
235
265
  respectGitignore: false,
236
266
  });
237
- const files = globResult.files.map((f) => ({
267
+ // Filter out non-searchable derived artifacts (_index.md, _manifest.json, .full.md).
268
+ // Stubs (.stub.md) are intentionally kept — archive/restore should invalidate cache.
269
+ // Summary-only churn does NOT invalidate cache (summaries are derivative content).
270
+ const files = globResult.files
271
+ .filter((f) => !isDerivedArtifact(f.path))
272
+ .map((f) => ({
238
273
  mtime: f.modified?.getTime() ?? 0,
239
274
  path: f.path,
240
275
  }));
@@ -153,6 +153,10 @@ export class AuthenticatedHttpClient {
153
153
  if ('message' in responseData && typeof responseData.message === 'string') {
154
154
  return responseData.message;
155
155
  }
156
+ // Some endpoints return 'error' instead of 'message'
157
+ if ('error' in responseData && typeof responseData.error === 'string') {
158
+ return responseData.error;
159
+ }
156
160
  // Fallback to HTTP status error
157
161
  return `HTTP ${error.response.status}: ${error.response.statusText}`;
158
162
  }
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { PROVIDER_REGISTRY } from '../../core/domain/entities/provider-registry.js';
8
8
  import { FileProviderConfigStore } from '../storage/file-provider-config-store.js';
9
- import { AnthropicModelFetcher, ChatBasedModelFetcher, GoogleModelFetcher, GoogleVertexModelFetcher, OpenAICompatibleModelFetcher, OpenAIModelFetcher, OpenRouterModelFetcher, } from './provider-model-fetchers.js';
9
+ import { AnthropicModelFetcher, ChatBasedModelFetcher, GoogleModelFetcher, OpenAICompatibleModelFetcher, OpenAIModelFetcher, OpenRouterModelFetcher, } from './provider-model-fetchers.js';
10
10
  /**
11
11
  * Singleton instances of model fetchers, lazily created.
12
12
  */
@@ -63,10 +63,6 @@ export async function getModelFetcher(providerId) {
63
63
  fetcher = new GoogleModelFetcher();
64
64
  break;
65
65
  }
66
- case 'google-vertex': {
67
- fetcher = new GoogleVertexModelFetcher();
68
- break;
69
- }
70
66
  case 'minimax': {
71
67
  fetcher = new ChatBasedModelFetcher('https://api.minimax.io/v1', 'MiniMax', ['MiniMax-M2', 'MiniMax-M2-Stable']);
72
68
  break;
@@ -50,20 +50,6 @@ export declare class GoogleModelFetcher implements IProviderModelFetcher {
50
50
  isValid: boolean;
51
51
  }>;
52
52
  }
53
- /**
54
- * Fetches models from Google Vertex AI using the @google/genai SDK with vertexai mode.
55
- * Uses Application Default Credentials (ADC) instead of API keys.
56
- */
57
- export declare class GoogleVertexModelFetcher implements IProviderModelFetcher {
58
- private cache;
59
- private readonly cacheTtlMs;
60
- constructor(cacheTtlMs?: number);
61
- fetchModels(apiKey: string, forceRefresh?: boolean): Promise<ProviderModelInfo[]>;
62
- validateApiKey(apiKey: string): Promise<{
63
- error?: string;
64
- isValid: boolean;
65
- }>;
66
- }
67
53
  /**
68
54
  * Generic model fetcher for OpenAI-compatible APIs.
69
55
  * Works with xAI (Grok), Groq, and Mistral.