byterover-cli 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -5
- package/dist/agent/core/domain/tools/constants.d.ts +0 -15
- package/dist/agent/core/domain/tools/constants.js +0 -15
- package/dist/agent/core/interfaces/i-cipher-agent.d.ts +6 -0
- package/dist/agent/core/interfaces/i-curate-service.d.ts +12 -0
- package/dist/agent/infra/llm/internal-llm-service.d.ts +13 -0
- package/dist/agent/infra/llm/internal-llm-service.js +61 -21
- package/dist/agent/infra/sandbox/local-sandbox.js +9 -2
- package/dist/agent/infra/tools/implementations/curate-tool.d.ts +133 -0
- package/dist/agent/infra/tools/implementations/curate-tool.js +14 -0
- package/dist/agent/infra/tools/implementations/search-knowledge-service.js +91 -14
- package/dist/agent/infra/tools/index.d.ts +0 -4
- package/dist/agent/infra/tools/index.js +0 -4
- package/dist/agent/infra/tools/tool-registry.js +0 -113
- package/dist/agent/resources/prompts/curate-detail-preservation.yml +73 -0
- package/dist/agent/resources/prompts/system-prompt.yml +69 -3
- package/dist/server/core/domain/knowledge/markdown-writer.d.ts +13 -0
- package/dist/server/core/domain/knowledge/markdown-writer.js +116 -8
- package/dist/server/infra/executor/curate-executor.js +1 -1
- package/dist/server/infra/executor/direct-search-responder.d.ts +45 -0
- package/dist/server/infra/executor/direct-search-responder.js +86 -0
- package/dist/server/infra/executor/folder-pack-executor.d.ts +13 -5
- package/dist/server/infra/executor/folder-pack-executor.js +739 -39
- package/dist/server/infra/executor/query-executor.d.ts +49 -3
- package/dist/server/infra/executor/query-executor.js +194 -9
- package/dist/server/infra/executor/query-result-cache.d.ts +87 -0
- package/dist/server/infra/executor/query-result-cache.js +127 -0
- package/dist/server/infra/executor/query-similarity.d.ts +28 -0
- package/dist/server/infra/executor/query-similarity.js +41 -0
- package/dist/server/infra/process/agent-worker.js +9 -2
- package/dist/server/infra/process/inline-agent-executor.js +16 -5
- package/dist/server/infra/usecase/curate-use-case.js +6 -1
- package/dist/server/infra/usecase/query-use-case.js +10 -0
- package/dist/server/utils/file-validator.js +78 -1
- package/dist/tui/hooks/use-slash-completion.js +25 -4
- package/oclif.manifest.json +1 -1
- package/package.json +2 -1
- package/dist/agent/infra/tools/implementations/bash-exec-tool.d.ts +0 -13
- package/dist/agent/infra/tools/implementations/bash-exec-tool.js +0 -110
- package/dist/agent/infra/tools/implementations/bash-output-tool.d.ts +0 -12
- package/dist/agent/infra/tools/implementations/bash-output-tool.js +0 -43
- package/dist/agent/infra/tools/implementations/batch-tool.d.ts +0 -12
- package/dist/agent/infra/tools/implementations/batch-tool.js +0 -142
- package/dist/agent/infra/tools/implementations/create-knowledge-topic-tool.d.ts +0 -11
- package/dist/agent/infra/tools/implementations/create-knowledge-topic-tool.js +0 -149
- package/dist/agent/infra/tools/implementations/delete-memory-tool.d.ts +0 -12
- package/dist/agent/infra/tools/implementations/delete-memory-tool.js +0 -37
- package/dist/agent/infra/tools/implementations/edit-file-tool.d.ts +0 -13
- package/dist/agent/infra/tools/implementations/edit-file-tool.js +0 -50
- package/dist/agent/infra/tools/implementations/edit-memory-tool.d.ts +0 -13
- package/dist/agent/infra/tools/implementations/edit-memory-tool.js +0 -53
- package/dist/agent/infra/tools/implementations/kill-process-tool.d.ts +0 -12
- package/dist/agent/infra/tools/implementations/kill-process-tool.js +0 -55
- package/dist/agent/infra/tools/implementations/list-memories-tool.d.ts +0 -12
- package/dist/agent/infra/tools/implementations/list-memories-tool.js +0 -63
- package/dist/agent/infra/tools/implementations/read-memory-tool.d.ts +0 -12
- package/dist/agent/infra/tools/implementations/read-memory-tool.js +0 -39
- package/dist/agent/infra/tools/implementations/read-todos-tool.d.ts +0 -11
- package/dist/agent/infra/tools/implementations/read-todos-tool.js +0 -39
- package/dist/agent/infra/tools/implementations/search-history-tool.d.ts +0 -10
- package/dist/agent/infra/tools/implementations/search-history-tool.js +0 -36
- package/dist/agent/infra/tools/implementations/spec-analyze-tool.d.ts +0 -7
- package/dist/agent/infra/tools/implementations/spec-analyze-tool.js +0 -78
- package/dist/agent/infra/tools/implementations/write-memory-tool.d.ts +0 -13
- package/dist/agent/infra/tools/implementations/write-memory-tool.js +0 -52
- package/dist/agent/infra/tools/implementations/write-todos-tool.d.ts +0 -13
- package/dist/agent/infra/tools/implementations/write-todos-tool.js +0 -121
|
@@ -1,23 +1,37 @@
|
|
|
1
1
|
import { generateRelationsSection, parseRelations } from './relation-parser.js';
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes newline characters in text.
|
|
4
|
+
* Converts literal \n strings to actual newlines.
|
|
5
|
+
*/
|
|
6
|
+
function normalizeNewlines(text) {
|
|
7
|
+
return text.replaceAll(String.raw `\n`, '\n');
|
|
8
|
+
}
|
|
2
9
|
function generateRawConceptSection(rawConcept) {
|
|
3
10
|
if (!rawConcept) {
|
|
4
11
|
return '';
|
|
5
12
|
}
|
|
6
13
|
const parts = [];
|
|
7
14
|
if (rawConcept.task) {
|
|
8
|
-
parts.push(`**Task:**\n${rawConcept.task}`);
|
|
15
|
+
parts.push(`**Task:**\n${normalizeNewlines(rawConcept.task)}`);
|
|
9
16
|
}
|
|
10
17
|
if (rawConcept.changes && rawConcept.changes.length > 0) {
|
|
11
|
-
parts.push(`**Changes:**\n${rawConcept.changes.map(c => `- ${c}`).join('\n')}`);
|
|
18
|
+
parts.push(`**Changes:**\n${rawConcept.changes.map(c => `- ${normalizeNewlines(c)}`).join('\n')}`);
|
|
12
19
|
}
|
|
13
20
|
if (rawConcept.files && rawConcept.files.length > 0) {
|
|
14
|
-
parts.push(`**Files:**\n${rawConcept.files.map(f => `- ${f}`).join('\n')}`);
|
|
21
|
+
parts.push(`**Files:**\n${rawConcept.files.map(f => `- ${normalizeNewlines(f)}`).join('\n')}`);
|
|
15
22
|
}
|
|
16
23
|
if (rawConcept.flow) {
|
|
17
|
-
parts.push(`**Flow:**\n${rawConcept.flow}`);
|
|
24
|
+
parts.push(`**Flow:**\n${normalizeNewlines(rawConcept.flow)}`);
|
|
18
25
|
}
|
|
19
26
|
if (rawConcept.timestamp) {
|
|
20
|
-
parts.push(`**Timestamp:** ${rawConcept.timestamp}`);
|
|
27
|
+
parts.push(`**Timestamp:** ${normalizeNewlines(rawConcept.timestamp)}`);
|
|
28
|
+
}
|
|
29
|
+
if (rawConcept.author) {
|
|
30
|
+
parts.push(`**Author:** ${rawConcept.author}`);
|
|
31
|
+
}
|
|
32
|
+
if (rawConcept.patterns && rawConcept.patterns.length > 0) {
|
|
33
|
+
const patternsText = rawConcept.patterns.map(p => `- \`${p.pattern}\`${p.flags ? ` (flags: ${p.flags})` : ''} - ${p.description}`).join('\n');
|
|
34
|
+
parts.push(`**Patterns:**\n${patternsText}`);
|
|
21
35
|
}
|
|
22
36
|
if (parts.length === 0) {
|
|
23
37
|
return '';
|
|
@@ -30,13 +44,27 @@ function generateNarrativeSection(narrative) {
|
|
|
30
44
|
}
|
|
31
45
|
const parts = [];
|
|
32
46
|
if (narrative.structure) {
|
|
33
|
-
parts.push(`### Structure\n${narrative.structure}`);
|
|
47
|
+
parts.push(`### Structure\n${normalizeNewlines(narrative.structure)}`);
|
|
34
48
|
}
|
|
35
49
|
if (narrative.dependencies) {
|
|
36
|
-
parts.push(`### Dependencies\n${narrative.dependencies}`);
|
|
50
|
+
parts.push(`### Dependencies\n${normalizeNewlines(narrative.dependencies)}`);
|
|
37
51
|
}
|
|
38
52
|
if (narrative.features) {
|
|
39
|
-
parts.push(`### Features\n${narrative.features}`);
|
|
53
|
+
parts.push(`### Features\n${normalizeNewlines(narrative.features)}`);
|
|
54
|
+
}
|
|
55
|
+
if (narrative.rules) {
|
|
56
|
+
parts.push(`### Rules\n${narrative.rules}`);
|
|
57
|
+
}
|
|
58
|
+
if (narrative.examples) {
|
|
59
|
+
parts.push(`### Examples\n${narrative.examples}`);
|
|
60
|
+
}
|
|
61
|
+
if (narrative.diagrams && narrative.diagrams.length > 0) {
|
|
62
|
+
const diagramParts = narrative.diagrams.map(d => {
|
|
63
|
+
const lang = d.type === 'ascii' ? '' : d.type;
|
|
64
|
+
const titleLine = d.title ? `**${d.title}**\n` : '';
|
|
65
|
+
return `${titleLine}\`\`\`${lang}\n${d.content}\n\`\`\``;
|
|
66
|
+
});
|
|
67
|
+
parts.push(`### Diagrams\n${diagramParts.join('\n\n')}`);
|
|
40
68
|
}
|
|
41
69
|
if (parts.length === 0) {
|
|
42
70
|
return '';
|
|
@@ -79,6 +107,31 @@ function parseRawConceptSection(content) {
|
|
|
79
107
|
if (timestampMatch) {
|
|
80
108
|
rawConcept.timestamp = timestampMatch[1].trim();
|
|
81
109
|
}
|
|
110
|
+
// Author can be inline
|
|
111
|
+
const authorMatch = sectionContent.match(/\*\*\s*Author\s*:\s*\*\*\s*(.+)/i);
|
|
112
|
+
if (authorMatch) {
|
|
113
|
+
rawConcept.author = authorMatch[1].trim();
|
|
114
|
+
}
|
|
115
|
+
// Patterns is multi-line with list items
|
|
116
|
+
const patternsMatch = sectionContent.match(/\*\*\s*Patterns\s*:\s*\*\*\s*\n([\s\S]*?)(?=\n\*\*|\n##|$)/i);
|
|
117
|
+
if (patternsMatch) {
|
|
118
|
+
const patterns = [];
|
|
119
|
+
for (const line of patternsMatch[1]
|
|
120
|
+
.split('\n')
|
|
121
|
+
.filter(line => line.trim().startsWith('- `'))) {
|
|
122
|
+
const match = line.match(/- `(.+?)`(?:\s*\(flags:\s*(.+?)\))?\s*-\s*(.+)/);
|
|
123
|
+
if (match) {
|
|
124
|
+
patterns.push({
|
|
125
|
+
description: match[3].trim(),
|
|
126
|
+
pattern: match[1],
|
|
127
|
+
...(match[2] ? { flags: match[2] } : {})
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (patterns.length > 0) {
|
|
132
|
+
rawConcept.patterns = patterns;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
82
135
|
if (Object.keys(rawConcept).length === 0) {
|
|
83
136
|
return undefined;
|
|
84
137
|
}
|
|
@@ -105,6 +158,30 @@ function parseNarrativeSection(content) {
|
|
|
105
158
|
if (featuresMatch) {
|
|
106
159
|
narrative.features = featuresMatch[1].trim();
|
|
107
160
|
}
|
|
161
|
+
const rulesMatch = sectionContent.match(/###\s*Rules\s*\n([\s\S]*?)(?=\n###\s|\n##\s|$)/i);
|
|
162
|
+
if (rulesMatch) {
|
|
163
|
+
narrative.rules = rulesMatch[1].trim();
|
|
164
|
+
}
|
|
165
|
+
const examplesMatch = sectionContent.match(/###\s*Examples\s*\n([\s\S]*?)(?=\n###\s|\n##\s|$)/i);
|
|
166
|
+
if (examplesMatch) {
|
|
167
|
+
narrative.examples = examplesMatch[1].trim();
|
|
168
|
+
}
|
|
169
|
+
const diagramsMatch = sectionContent.match(/###\s*Diagrams\s*\n([\s\S]*?)(?=\n###\s|\n##\s|$)/i);
|
|
170
|
+
if (diagramsMatch) {
|
|
171
|
+
const diagrams = [];
|
|
172
|
+
const blockRegex = /(?:\*\*(.+?)\*\*\n)?```(\w*)\n([\s\S]*?)```/g;
|
|
173
|
+
let match;
|
|
174
|
+
while ((match = blockRegex.exec(diagramsMatch[1])) !== null) {
|
|
175
|
+
diagrams.push({
|
|
176
|
+
content: match[3].trimEnd(),
|
|
177
|
+
...(match[1] ? { title: match[1] } : {}),
|
|
178
|
+
type: match[2] || 'ascii',
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
if (diagrams.length > 0) {
|
|
182
|
+
narrative.diagrams = diagrams;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
108
185
|
if (Object.keys(narrative).length === 0) {
|
|
109
186
|
return undefined;
|
|
110
187
|
}
|
|
@@ -159,6 +236,7 @@ function mergeRawConcepts(source, target) {
|
|
|
159
236
|
merged.task = source.task || target.task;
|
|
160
237
|
merged.flow = source.flow || target.flow;
|
|
161
238
|
merged.timestamp = source.timestamp || target.timestamp;
|
|
239
|
+
merged.author = source.author || target.author;
|
|
162
240
|
// Arrays: concatenate and deduplicate (target first, then source)
|
|
163
241
|
const allChanges = [...(target.changes || []), ...(source.changes || [])];
|
|
164
242
|
if (allChanges.length > 0) {
|
|
@@ -168,6 +246,18 @@ function mergeRawConcepts(source, target) {
|
|
|
168
246
|
if (allFiles.length > 0) {
|
|
169
247
|
merged.files = [...new Set(allFiles)];
|
|
170
248
|
}
|
|
249
|
+
// Patterns: concatenate and deduplicate by pattern+flags
|
|
250
|
+
const allPatterns = [...(target.patterns || []), ...(source.patterns || [])];
|
|
251
|
+
if (allPatterns.length > 0) {
|
|
252
|
+
const seen = new Set();
|
|
253
|
+
merged.patterns = allPatterns.filter(p => {
|
|
254
|
+
const key = p.pattern + (p.flags || '');
|
|
255
|
+
if (seen.has(key))
|
|
256
|
+
return false;
|
|
257
|
+
seen.add(key);
|
|
258
|
+
return true;
|
|
259
|
+
});
|
|
260
|
+
}
|
|
171
261
|
if (Object.keys(merged).length === 0) {
|
|
172
262
|
return undefined;
|
|
173
263
|
}
|
|
@@ -194,6 +284,24 @@ function mergeNarratives(source, target) {
|
|
|
194
284
|
const parts = [target.features, source.features].filter(Boolean);
|
|
195
285
|
merged.features = parts.join('\n\n');
|
|
196
286
|
}
|
|
287
|
+
if (source.rules || target.rules) {
|
|
288
|
+
const parts = [target.rules, source.rules].filter(Boolean);
|
|
289
|
+
merged.rules = parts.join('\n\n');
|
|
290
|
+
}
|
|
291
|
+
if (source.examples || target.examples) {
|
|
292
|
+
const parts = [target.examples, source.examples].filter(Boolean);
|
|
293
|
+
merged.examples = parts.join('\n\n');
|
|
294
|
+
}
|
|
295
|
+
if (source.diagrams || target.diagrams) {
|
|
296
|
+
const allDiagrams = [...(target.diagrams || []), ...(source.diagrams || [])];
|
|
297
|
+
const seen = new Set();
|
|
298
|
+
merged.diagrams = allDiagrams.filter(d => {
|
|
299
|
+
if (seen.has(d.content))
|
|
300
|
+
return false;
|
|
301
|
+
seen.add(d.content);
|
|
302
|
+
return true;
|
|
303
|
+
});
|
|
304
|
+
}
|
|
197
305
|
if (Object.keys(merged).length === 0) {
|
|
198
306
|
return undefined;
|
|
199
307
|
}
|
|
@@ -61,7 +61,7 @@ export class CurateExecutor {
|
|
|
61
61
|
if (failedReads.length > 0 || skippedFiles.length > 0) {
|
|
62
62
|
instructions.push('### Files that could not be read:', ...failedReads.map((r) => `- ${path.relative(projectRoot, r.filePath)}: ${r.error}`), ...skippedFiles.map((f) => `- ${f.path}: ${f.reason}`), '', '**Note:** You may use `read_file` or `grep_content` tools to find additional context if needed.', '');
|
|
63
63
|
}
|
|
64
|
-
instructions.push('**INSTRUCTIONS:**', '- The file contents above have been pre-loaded for you', '- Use this content to understand the context and create comprehensive knowledge topics', '- DO NOT use read_file tool for the files above - the content is already provided', '- Proceed with the normal workflow: detect domains, find existing knowledge, create/update topics', '');
|
|
64
|
+
instructions.push('**INSTRUCTIONS:**', '- The file contents above have been pre-loaded for you', '- Use this content to understand the context and create comprehensive knowledge topics', '- DO NOT use read_file tool for the files above - the content is already provided', '- Proceed with the normal workflow: detect domains, find existing knowledge, create/update topics', '- PRESERVE all diagrams (Mermaid, PlantUML, ASCII art) verbatim using narrative.diagrams array', '- PRESERVE all tables with every row - do not summarize table data', '- PRESERVE exact code examples, API signatures, and interface definitions', '- PRESERVE step-by-step procedures and numbered instructions in narrative.rules', '');
|
|
65
65
|
return instructions.join('\n');
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Score at which the result is so strong that dominance check is skipped */
|
|
2
|
+
export declare const DIRECT_RESPONSE_HIGH_CONFIDENCE_THRESHOLD = 15;
|
|
3
|
+
/** Minimum score for the top result to qualify for a direct (no-LLM) response */
|
|
4
|
+
export declare const DIRECT_RESPONSE_SCORE_THRESHOLD = 8;
|
|
5
|
+
/** Top result must be N times the second result's score to be considered dominant */
|
|
6
|
+
export declare const DIRECT_RESPONSE_DOMINANCE_RATIO = 2;
|
|
7
|
+
/**
|
|
8
|
+
* A search result with full document content for direct response formatting.
|
|
9
|
+
*/
|
|
10
|
+
export interface DirectSearchResult {
|
|
11
|
+
content: string;
|
|
12
|
+
path: string;
|
|
13
|
+
score: number;
|
|
14
|
+
title: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Determines if search results are confident enough for a direct response
|
|
18
|
+
* without involving the LLM.
|
|
19
|
+
*
|
|
20
|
+
* Requires:
|
|
21
|
+
* 1. Top result score >= DIRECT_RESPONSE_SCORE_THRESHOLD (minimum confidence)
|
|
22
|
+
* 2. Either: top score >= HIGH_CONFIDENCE_THRESHOLD (strong enough to skip dominance check)
|
|
23
|
+
* Or: top result dominates other results (score >= 2x the second result)
|
|
24
|
+
*
|
|
25
|
+
* @param results - Sorted search results (highest score first)
|
|
26
|
+
* @returns true if a direct response can be served
|
|
27
|
+
*/
|
|
28
|
+
export declare function canRespondDirectly(results: DirectSearchResult[]): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Format a direct response from search results (no LLM involved).
|
|
31
|
+
* Uses a structured template matching the existing query response format.
|
|
32
|
+
*
|
|
33
|
+
* @param query - Original user query
|
|
34
|
+
* @param results - Search results with full content
|
|
35
|
+
* @returns Formatted response string
|
|
36
|
+
*/
|
|
37
|
+
export declare function formatDirectResponse(query: string, results: DirectSearchResult[]): string;
|
|
38
|
+
/**
|
|
39
|
+
* Format a "not found" response when OOD detection determines
|
|
40
|
+
* the query topic is not covered in the knowledge base.
|
|
41
|
+
*
|
|
42
|
+
* @param query - Original user query
|
|
43
|
+
* @returns Formatted not-found response string
|
|
44
|
+
*/
|
|
45
|
+
export declare function formatNotFoundResponse(query: string): string;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/** Score at which the result is so strong that dominance check is skipped */
|
|
2
|
+
export const DIRECT_RESPONSE_HIGH_CONFIDENCE_THRESHOLD = 15;
|
|
3
|
+
/** Minimum score for the top result to qualify for a direct (no-LLM) response */
|
|
4
|
+
export const DIRECT_RESPONSE_SCORE_THRESHOLD = 8;
|
|
5
|
+
/** Top result must be N times the second result's score to be considered dominant */
|
|
6
|
+
export const DIRECT_RESPONSE_DOMINANCE_RATIO = 2;
|
|
7
|
+
/** Maximum content length per document in the direct response */
|
|
8
|
+
const MAX_CONTENT_LENGTH = 1500;
|
|
9
|
+
/** Maximum number of documents to include in the direct response */
|
|
10
|
+
const MAX_DOCS = 3;
|
|
11
|
+
/**
|
|
12
|
+
* Determines if search results are confident enough for a direct response
|
|
13
|
+
* without involving the LLM.
|
|
14
|
+
*
|
|
15
|
+
* Requires:
|
|
16
|
+
* 1. Top result score >= DIRECT_RESPONSE_SCORE_THRESHOLD (minimum confidence)
|
|
17
|
+
* 2. Either: top score >= HIGH_CONFIDENCE_THRESHOLD (strong enough to skip dominance check)
|
|
18
|
+
* Or: top result dominates other results (score >= 2x the second result)
|
|
19
|
+
*
|
|
20
|
+
* @param results - Sorted search results (highest score first)
|
|
21
|
+
* @returns true if a direct response can be served
|
|
22
|
+
*/
|
|
23
|
+
export function canRespondDirectly(results) {
|
|
24
|
+
if (results.length === 0)
|
|
25
|
+
return false;
|
|
26
|
+
const topResult = results[0];
|
|
27
|
+
if (topResult.score < DIRECT_RESPONSE_SCORE_THRESHOLD)
|
|
28
|
+
return false;
|
|
29
|
+
// Single result that passes threshold
|
|
30
|
+
if (results.length === 1)
|
|
31
|
+
return true;
|
|
32
|
+
// High-confidence path: score so strong that dominance is irrelevant
|
|
33
|
+
if (topResult.score >= DIRECT_RESPONSE_HIGH_CONFIDENCE_THRESHOLD)
|
|
34
|
+
return true;
|
|
35
|
+
const secondScore = results[1].score;
|
|
36
|
+
if (secondScore === 0)
|
|
37
|
+
return true;
|
|
38
|
+
return topResult.score / secondScore >= DIRECT_RESPONSE_DOMINANCE_RATIO;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Format a direct response from search results (no LLM involved).
|
|
42
|
+
* Uses a structured template matching the existing query response format.
|
|
43
|
+
*
|
|
44
|
+
* @param query - Original user query
|
|
45
|
+
* @param results - Search results with full content
|
|
46
|
+
* @returns Formatted response string
|
|
47
|
+
*/
|
|
48
|
+
export function formatDirectResponse(query, results) {
|
|
49
|
+
const topResults = results.slice(0, MAX_DOCS);
|
|
50
|
+
const summary = topResults.length === 1
|
|
51
|
+
? `Based on the curated knowledge, here is information about "${query}":`
|
|
52
|
+
: `Found ${topResults.length} relevant topics for "${query}":`;
|
|
53
|
+
const details = topResults
|
|
54
|
+
.map((r) => {
|
|
55
|
+
const truncatedContent = r.content.length > MAX_CONTENT_LENGTH ? `${r.content.slice(0, MAX_CONTENT_LENGTH).trim()}...` : r.content;
|
|
56
|
+
return `### ${r.title}\n\n${truncatedContent}`;
|
|
57
|
+
})
|
|
58
|
+
.join('\n\n---\n\n');
|
|
59
|
+
const sources = topResults.map((r) => `- \`.brv/context-tree/${r.path}\``).join('\n');
|
|
60
|
+
return `**Summary**: ${summary}
|
|
61
|
+
|
|
62
|
+
**Details**:
|
|
63
|
+
|
|
64
|
+
${details}
|
|
65
|
+
|
|
66
|
+
**Sources**:
|
|
67
|
+
${sources}
|
|
68
|
+
|
|
69
|
+
**Gaps**: This is a direct match from the context tree. For deeper analysis or cross-topic synthesis, try a more specific question.`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Format a "not found" response when OOD detection determines
|
|
73
|
+
* the query topic is not covered in the knowledge base.
|
|
74
|
+
*
|
|
75
|
+
* @param query - Original user query
|
|
76
|
+
* @returns Formatted not-found response string
|
|
77
|
+
*/
|
|
78
|
+
export function formatNotFoundResponse(query) {
|
|
79
|
+
return `**Summary**: No matching knowledge found for "${query}".
|
|
80
|
+
|
|
81
|
+
**Details**: The topic does not appear to be covered in the context tree. This could mean the topic hasn't been curated yet.
|
|
82
|
+
|
|
83
|
+
**Sources**: None
|
|
84
|
+
|
|
85
|
+
**Gaps**: Try rephrasing your query with different terms, or use /curate to add knowledge about this topic.`;
|
|
86
|
+
}
|
|
@@ -6,22 +6,30 @@ import type { FolderPackExecuteOptions, IFolderPackExecutor } from '../../core/i
|
|
|
6
6
|
*
|
|
7
7
|
* This executor:
|
|
8
8
|
* 1. Packs the folder using FolderPackService
|
|
9
|
-
* 2.
|
|
10
|
-
* 3.
|
|
11
|
-
* 4.
|
|
9
|
+
* 2. Stores packed data in sandbox environment as context variable
|
|
10
|
+
* 3. Guides agent to iteratively query and extract knowledge
|
|
11
|
+
* 4. Agent curates extracted pieces using tools.curate()
|
|
12
12
|
*
|
|
13
13
|
* Architecture:
|
|
14
14
|
* - TaskProcessor injects the long-lived CipherAgent
|
|
15
15
|
* - Event streaming is handled by agent-worker (subscribes to agentEventBus)
|
|
16
16
|
* - Transport handles task lifecycle (task:started, task:completed, task:error)
|
|
17
17
|
* - Executor focuses solely on folder pack + curate execution
|
|
18
|
+
* - Uses iterative extraction strategy (inspired by rlm) to avoid token limits
|
|
18
19
|
*/
|
|
19
20
|
export declare class FolderPackExecutor implements IFolderPackExecutor {
|
|
20
21
|
private readonly folderPackService;
|
|
21
22
|
constructor(folderPackService: IFolderPackService);
|
|
22
23
|
executeWithAgent(agent: ICipherAgent, options: FolderPackExecuteOptions): Promise<string>;
|
|
23
24
|
/**
|
|
24
|
-
* Build
|
|
25
|
+
* Build iterative extraction prompt with file-based access.
|
|
26
|
+
* Folder data is stored in a temporary file to avoid token limits.
|
|
25
27
|
*/
|
|
26
|
-
private
|
|
28
|
+
private buildIterativePromptWithFileAccess;
|
|
29
|
+
/**
|
|
30
|
+
* Execute folder curation using iterative extraction strategy.
|
|
31
|
+
* Pre-loads folder data into REPL environment, then guides agent to iterate and curate.
|
|
32
|
+
* This avoids token limits entirely - data is stored in REPL, not in prompt.
|
|
33
|
+
*/
|
|
34
|
+
private executeIterative;
|
|
27
35
|
}
|