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.
- package/README.md +6 -81
- package/dist/agent/core/domain/llm/index.d.ts +1 -1
- package/dist/agent/core/domain/llm/index.js +1 -1
- package/dist/agent/core/domain/llm/registry.d.ts +8 -0
- package/dist/agent/core/domain/llm/registry.js +34 -0
- package/dist/agent/core/domain/sandbox/types.d.ts +2 -0
- package/dist/agent/core/domain/tools/constants.d.ts +3 -0
- package/dist/agent/core/domain/tools/constants.js +3 -0
- package/dist/agent/core/interfaces/cipher-services.d.ts +2 -4
- package/dist/agent/core/interfaces/i-cipher-agent.d.ts +9 -1
- package/dist/agent/core/interfaces/i-sandbox-service.d.ts +8 -0
- package/dist/agent/core/interfaces/i-tool-provider.d.ts +10 -0
- package/dist/agent/core/interfaces/i-tool-scheduler.d.ts +9 -0
- package/dist/agent/infra/agent/agent-schemas.d.ts +0 -9
- package/dist/agent/infra/agent/agent-schemas.js +0 -3
- package/dist/agent/infra/agent/cipher-agent.d.ts +25 -1
- package/dist/agent/infra/agent/cipher-agent.js +138 -11
- package/dist/agent/infra/agent/provider-update-config.d.ts +0 -2
- package/dist/agent/infra/agent/service-initializer.d.ts +2 -6
- package/dist/agent/infra/agent/service-initializer.js +45 -38
- package/dist/agent/infra/blob/blob-storage-factory.d.ts +2 -2
- package/dist/agent/infra/blob/blob-storage-factory.js +4 -4
- package/dist/agent/infra/blob/file-blob-storage.d.ts +96 -0
- package/dist/agent/infra/blob/file-blob-storage.js +454 -0
- package/dist/agent/infra/blob/index.d.ts +2 -3
- package/dist/agent/infra/blob/index.js +4 -6
- package/dist/agent/infra/llm/agent-llm-service.d.ts +3 -0
- package/dist/agent/infra/llm/agent-llm-service.js +34 -52
- package/dist/agent/infra/llm/context/compression/compression-helpers.d.ts +35 -0
- package/dist/agent/infra/llm/context/compression/compression-helpers.js +124 -0
- package/dist/agent/infra/llm/context/compression/escalated-compression.d.ts +62 -0
- package/dist/agent/infra/llm/context/compression/escalated-compression.js +144 -0
- package/dist/agent/infra/llm/context/compression/index.d.ts +3 -0
- package/dist/agent/infra/llm/context/compression/index.js +3 -0
- package/dist/agent/infra/llm/context/compression/reactive-overflow.d.ts +0 -27
- package/dist/agent/infra/llm/context/compression/reactive-overflow.js +5 -122
- package/dist/agent/infra/llm/context/context-manager.d.ts +20 -1
- package/dist/agent/infra/llm/context/context-manager.js +37 -7
- package/dist/agent/infra/llm/providers/index.js +0 -2
- package/dist/agent/infra/llm/providers/types.d.ts +1 -5
- package/dist/agent/infra/map/agentic-map-service.d.ts +97 -0
- package/dist/agent/infra/map/agentic-map-service.js +309 -0
- package/dist/agent/infra/map/context-tree-store.d.ts +94 -0
- package/dist/agent/infra/map/context-tree-store.js +278 -0
- package/dist/agent/infra/map/index.d.ts +4 -0
- package/dist/agent/infra/map/index.js +4 -0
- package/dist/agent/infra/map/llm-map-memory.d.ts +59 -0
- package/dist/agent/infra/map/llm-map-memory.js +187 -0
- package/dist/agent/infra/map/llm-map-service.d.ts +36 -0
- package/dist/agent/infra/map/llm-map-service.js +118 -0
- package/dist/agent/infra/map/map-shared.d.ts +140 -0
- package/dist/agent/infra/map/map-shared.js +325 -0
- package/dist/agent/infra/map/worker-pool.d.ts +45 -0
- package/dist/agent/infra/map/worker-pool.js +73 -0
- package/dist/agent/infra/sandbox/curation-helpers.d.ts +62 -0
- package/dist/agent/infra/sandbox/curation-helpers.js +219 -0
- package/dist/agent/infra/sandbox/sandbox-service.d.ts +12 -0
- package/dist/agent/infra/sandbox/sandbox-service.js +39 -7
- package/dist/agent/infra/sandbox/tools-sdk.d.ts +48 -1
- package/dist/agent/infra/sandbox/tools-sdk.js +52 -1
- package/dist/agent/infra/session/session-manager.d.ts +8 -1
- package/dist/agent/infra/session/session-manager.js +24 -4
- package/dist/agent/infra/storage/file-key-storage.d.ts +142 -0
- package/dist/agent/infra/storage/file-key-storage.js +572 -0
- package/dist/agent/infra/storage/granular-history-storage.d.ts +1 -1
- package/dist/agent/infra/storage/granular-history-storage.js +1 -1
- package/dist/agent/infra/system-prompt/contributors/context-tree-structure-contributor.d.ts +4 -0
- package/dist/agent/infra/system-prompt/contributors/context-tree-structure-contributor.js +42 -14
- package/dist/agent/infra/system-prompt/contributors/map-selection-contributor.d.ts +16 -0
- package/dist/agent/infra/system-prompt/contributors/map-selection-contributor.js +47 -0
- package/dist/agent/infra/tools/core-tool-scheduler.js +3 -1
- package/dist/agent/infra/tools/implementations/agentic-map-tool.d.ts +35 -0
- package/dist/agent/infra/tools/implementations/agentic-map-tool.js +156 -0
- package/dist/agent/infra/tools/implementations/code-exec-tool.js +1 -0
- package/dist/agent/infra/tools/implementations/curate-tool.d.ts +9 -9
- package/dist/agent/infra/tools/implementations/expand-knowledge-tool.d.ts +18 -0
- package/dist/agent/infra/tools/implementations/expand-knowledge-tool.js +43 -0
- package/dist/agent/infra/tools/implementations/llm-map-tool.d.ts +24 -0
- package/dist/agent/infra/tools/implementations/llm-map-tool.js +87 -0
- package/dist/agent/infra/tools/implementations/memory-symbol-tree.d.ts +28 -1
- package/dist/agent/infra/tools/implementations/memory-symbol-tree.js +27 -3
- package/dist/agent/infra/tools/implementations/search-knowledge-service.d.ts +1 -0
- package/dist/agent/infra/tools/implementations/search-knowledge-service.js +83 -12
- package/dist/agent/infra/tools/implementations/search-knowledge-tool.js +2 -2
- package/dist/agent/infra/tools/tool-manager.js +6 -0
- package/dist/agent/infra/tools/tool-provider.d.ts +12 -0
- package/dist/agent/infra/tools/tool-provider.js +78 -0
- package/dist/agent/infra/tools/tool-registry.d.ts +14 -0
- package/dist/agent/infra/tools/tool-registry.js +32 -0
- package/dist/agent/resources/prompts/system-prompt.yml +48 -74
- package/dist/agent/resources/tools/expand_knowledge.txt +20 -0
- package/dist/oclif/commands/curate/index.js +1 -2
- package/dist/oclif/commands/main.js +1 -0
- package/dist/oclif/commands/providers/connect.d.ts +1 -3
- package/dist/oclif/commands/providers/connect.js +7 -29
- package/dist/oclif/commands/query.js +1 -2
- package/dist/server/constants.d.ts +7 -0
- package/dist/server/constants.js +8 -0
- package/dist/server/core/domain/entities/provider-registry.js +1 -15
- package/dist/server/core/domain/knowledge/memory-scoring.js +1 -1
- package/dist/server/core/domain/knowledge/summary-types.d.ts +126 -0
- package/dist/server/core/domain/knowledge/summary-types.js +7 -0
- package/dist/server/core/domain/transport/schemas.d.ts +0 -4
- package/dist/server/core/interfaces/context-tree/i-context-tree-archive-service.d.ts +30 -0
- package/dist/server/core/interfaces/context-tree/i-context-tree-archive-service.js +1 -0
- package/dist/server/core/interfaces/context-tree/i-context-tree-manifest-service.d.ts +30 -0
- package/dist/server/core/interfaces/context-tree/i-context-tree-manifest-service.js +1 -0
- package/dist/server/core/interfaces/context-tree/i-context-tree-summary-service.d.ts +29 -0
- package/dist/server/core/interfaces/context-tree/i-context-tree-summary-service.js +1 -0
- package/dist/server/infra/cogit/context-tree-to-push-context-mapper.js +10 -3
- package/dist/server/infra/connectors/skill/skill-connector.d.ts +4 -0
- package/dist/server/infra/connectors/skill/skill-connector.js +4 -0
- package/dist/server/infra/context-tree/children-hash.d.ts +20 -0
- package/dist/server/infra/context-tree/children-hash.js +22 -0
- package/dist/server/infra/context-tree/derived-artifact.d.ts +28 -0
- package/dist/server/infra/context-tree/derived-artifact.js +48 -0
- package/dist/server/infra/context-tree/file-context-tree-archive-service.d.ts +37 -0
- package/dist/server/infra/context-tree/file-context-tree-archive-service.js +219 -0
- package/dist/server/infra/context-tree/file-context-tree-manifest-service.d.ts +50 -0
- package/dist/server/infra/context-tree/file-context-tree-manifest-service.js +278 -0
- package/dist/server/infra/context-tree/file-context-tree-merger.js +4 -0
- package/dist/server/infra/context-tree/file-context-tree-snapshot-service.js +12 -4
- package/dist/server/infra/context-tree/file-context-tree-summary-service.d.ts +44 -0
- package/dist/server/infra/context-tree/file-context-tree-summary-service.js +313 -0
- package/dist/server/infra/context-tree/file-context-tree-writer-service.js +5 -0
- package/dist/server/infra/context-tree/prompts/summary-generation.d.ts +22 -0
- package/dist/server/infra/context-tree/prompts/summary-generation.js +45 -0
- package/dist/server/infra/context-tree/snapshot-diff.d.ts +19 -0
- package/dist/server/infra/context-tree/snapshot-diff.js +39 -0
- package/dist/server/infra/context-tree/summary-frontmatter.d.ts +24 -0
- package/dist/server/infra/context-tree/summary-frontmatter.js +111 -0
- package/dist/server/infra/daemon/agent-process.js +2 -14
- package/dist/server/infra/executor/curate-executor.d.ts +1 -0
- package/dist/server/infra/executor/curate-executor.js +82 -34
- package/dist/server/infra/executor/folder-pack-executor.js +1 -1
- package/dist/server/infra/executor/pre-compaction/compaction-escalation.d.ts +6 -0
- package/dist/server/infra/executor/pre-compaction/compaction-escalation.js +6 -0
- package/dist/server/infra/executor/pre-compaction/index.d.ts +3 -0
- package/dist/server/infra/executor/pre-compaction/index.js +1 -0
- package/dist/server/infra/executor/pre-compaction/pre-compaction-service.d.ts +59 -0
- package/dist/server/infra/executor/pre-compaction/pre-compaction-service.js +124 -0
- package/dist/server/infra/executor/pre-compaction/prompts.d.ts +24 -0
- package/dist/server/infra/executor/pre-compaction/prompts.js +47 -0
- package/dist/server/infra/executor/query-executor.d.ts +3 -0
- package/dist/server/infra/executor/query-executor.js +39 -4
- package/dist/server/infra/http/authenticated-http-client.js +4 -0
- package/dist/server/infra/http/provider-model-fetcher-registry.js +1 -5
- package/dist/server/infra/http/provider-model-fetchers.d.ts +0 -14
- package/dist/server/infra/http/provider-model-fetchers.js +0 -132
- package/dist/server/infra/provider/provider-config-resolver.js +0 -55
- package/dist/server/utils/curate-result-parser.d.ts +4 -4
- package/dist/shared/constants/curation.d.ts +6 -0
- package/dist/shared/constants/curation.js +6 -0
- package/dist/shared/utils/escalation-utils.d.ts +59 -0
- package/dist/shared/utils/escalation-utils.js +141 -0
- package/dist/tui/components/command-input.js +1 -1
- package/dist/tui/components/inline-prompts/inline-confirm.js +6 -1
- package/dist/tui/features/commands/definitions/exit.d.ts +2 -0
- package/dist/tui/features/commands/definitions/exit.js +9 -0
- package/dist/tui/features/commands/definitions/index.js +3 -0
- package/dist/tui/features/exit/components/exit-flow.d.ts +10 -0
- package/dist/tui/features/exit/components/exit-flow.js +19 -0
- package/dist/tui/features/provider/components/provider-flow.js +1 -21
- package/oclif.manifest.json +100 -109
- package/package.json +11 -4
- package/dist/agent/infra/blob/migrations.d.ts +0 -63
- package/dist/agent/infra/blob/migrations.js +0 -148
- package/dist/agent/infra/blob/sqlite-blob-storage.d.ts +0 -82
- package/dist/agent/infra/blob/sqlite-blob-storage.js +0 -307
- package/dist/agent/infra/llm/providers/google-vertex.d.ts +0 -15
- package/dist/agent/infra/llm/providers/google-vertex.js +0 -36
- package/dist/agent/infra/storage/blob-history-storage.d.ts +0 -81
- package/dist/agent/infra/storage/blob-history-storage.js +0 -193
- package/dist/agent/infra/storage/dual-format-history-storage.d.ts +0 -83
- package/dist/agent/infra/storage/dual-format-history-storage.js +0 -165
- package/dist/agent/infra/storage/sqlite-key-storage.d.ts +0 -113
- package/dist/agent/infra/storage/sqlite-key-storage.js +0 -438
- package/dist/server/infra/provider/vertex-ai-utils.d.ts +0 -10
- package/dist/server/infra/provider/vertex-ai-utils.js +0 -28
- package/dist/tui/features/provider/components/credential-path-dialog.d.ts +0 -30
- package/dist/tui/features/provider/components/credential-path-dialog.js +0 -85
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
/**
|
|
3
|
+
* File-based implementation of IContextTreeManifestService.
|
|
4
|
+
*
|
|
5
|
+
* Builds and reads the context manifest (_manifest.json) which allocates
|
|
6
|
+
* context tree entries into three lanes (summaries, contexts, stubs)
|
|
7
|
+
* with token budgets for efficient query context injection.
|
|
8
|
+
*
|
|
9
|
+
* Freshness check uses source_fingerprint (hash of sorted path:mtime:size)
|
|
10
|
+
* to detect additions, modifications, and deletions. Stat-only, no file reads.
|
|
11
|
+
*
|
|
12
|
+
* Tradeoff: rare false-fresh cases where content changes but mtime+size
|
|
13
|
+
* are preserved. Acceptable because writeFile() updates mtime and the next
|
|
14
|
+
* curate run will rebuild the manifest anyway.
|
|
15
|
+
*/
|
|
16
|
+
import { readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
17
|
+
import { join, relative } from 'node:path';
|
|
18
|
+
import { ARCHIVE_DIR, BRV_DIR, CONTEXT_FILE_EXTENSION, CONTEXT_TREE_DIR, MANIFEST_FILE, STUB_EXTENSION, SUMMARY_INDEX_FILE, } from '../../constants.js';
|
|
19
|
+
import { parseFrontmatterScoring } from '../../core/domain/knowledge/markdown-writer.js';
|
|
20
|
+
import { DEFAULT_LANE_BUDGETS } from '../../core/domain/knowledge/summary-types.js';
|
|
21
|
+
import { estimateTokens } from '../executor/pre-compaction/compaction-escalation.js';
|
|
22
|
+
import { isArchiveStub, isDerivedArtifact } from './derived-artifact.js';
|
|
23
|
+
import { computeContentHash } from './hash-utils.js';
|
|
24
|
+
import { toUnixPath } from './path-utils.js';
|
|
25
|
+
import { parseSummaryFrontmatter } from './summary-frontmatter.js';
|
|
26
|
+
export class FileContextTreeManifestService {
|
|
27
|
+
config;
|
|
28
|
+
constructor(config = {}) {
|
|
29
|
+
this.config = config;
|
|
30
|
+
}
|
|
31
|
+
async buildManifest(directory, laneBudgets) {
|
|
32
|
+
const baseDir = directory ?? this.config.baseDirectory ?? process.cwd();
|
|
33
|
+
const contextTreeDir = join(baseDir, BRV_DIR, CONTEXT_TREE_DIR);
|
|
34
|
+
const budgets = laneBudgets ?? DEFAULT_LANE_BUDGETS;
|
|
35
|
+
// Scan all entries
|
|
36
|
+
const summaries = [];
|
|
37
|
+
const contexts = [];
|
|
38
|
+
const stubs = [];
|
|
39
|
+
await this.scanForManifest(contextTreeDir, contextTreeDir, summaries, contexts, stubs);
|
|
40
|
+
// Lane allocation with prioritized fill
|
|
41
|
+
const activeSummaries = this.allocateLane(summaries.sort((a, b) => (b.order ?? 0) - (a.order ?? 0)), budgets.summaries);
|
|
42
|
+
const activeContexts = this.allocateLane(contexts.sort((a, b) => (b.importance ?? 50) - (a.importance ?? 50)), budgets.contexts);
|
|
43
|
+
const activeStubs = this.allocateLane(stubs, budgets.stubs);
|
|
44
|
+
const activeContext = [...activeSummaries, ...activeContexts, ...activeStubs];
|
|
45
|
+
const totalTokens = activeContext.reduce((sum, e) => sum + e.tokens, 0);
|
|
46
|
+
// Compute source fingerprint (stat-only, no file reads)
|
|
47
|
+
const sourceFingerprint = await this.computeSourceFingerprint(contextTreeDir);
|
|
48
|
+
const manifest = {
|
|
49
|
+
active_context: activeContext,
|
|
50
|
+
generated_at: new Date().toISOString(),
|
|
51
|
+
lane_tokens: {
|
|
52
|
+
contexts: activeContexts.reduce((sum, e) => sum + e.tokens, 0),
|
|
53
|
+
stubs: activeStubs.reduce((sum, e) => sum + e.tokens, 0),
|
|
54
|
+
summaries: activeSummaries.reduce((sum, e) => sum + e.tokens, 0),
|
|
55
|
+
},
|
|
56
|
+
source_fingerprint: sourceFingerprint,
|
|
57
|
+
total_tokens: totalTokens,
|
|
58
|
+
version: 1,
|
|
59
|
+
};
|
|
60
|
+
// Write _manifest.json
|
|
61
|
+
const manifestPath = join(contextTreeDir, MANIFEST_FILE);
|
|
62
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
63
|
+
return manifest;
|
|
64
|
+
}
|
|
65
|
+
async readManifest(directory) {
|
|
66
|
+
const baseDir = directory ?? this.config.baseDirectory ?? process.cwd();
|
|
67
|
+
const manifestPath = join(baseDir, BRV_DIR, CONTEXT_TREE_DIR, MANIFEST_FILE);
|
|
68
|
+
try {
|
|
69
|
+
const content = await readFile(manifestPath, 'utf8');
|
|
70
|
+
return JSON.parse(content);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async readManifestIfFresh(directory) {
|
|
77
|
+
const manifest = await this.readManifest(directory);
|
|
78
|
+
if (!manifest)
|
|
79
|
+
return null;
|
|
80
|
+
const baseDir = directory ?? this.config.baseDirectory ?? process.cwd();
|
|
81
|
+
const contextTreeDir = join(baseDir, BRV_DIR, CONTEXT_TREE_DIR);
|
|
82
|
+
// Compare stored fingerprint against current state
|
|
83
|
+
const currentFingerprint = await this.computeSourceFingerprint(contextTreeDir);
|
|
84
|
+
if (currentFingerprint !== manifest.source_fingerprint)
|
|
85
|
+
return null;
|
|
86
|
+
return manifest;
|
|
87
|
+
}
|
|
88
|
+
async resolveForInjection(manifest, _query, directory) {
|
|
89
|
+
const baseDir = directory ?? this.config.baseDirectory ?? process.cwd();
|
|
90
|
+
const contextTreeDir = join(baseDir, BRV_DIR, CONTEXT_TREE_DIR);
|
|
91
|
+
const resolved = [];
|
|
92
|
+
// Order: summaries (broadest first) → contexts → stubs
|
|
93
|
+
const ordered = [
|
|
94
|
+
...manifest.active_context.filter((e) => e.type === 'summary'),
|
|
95
|
+
...manifest.active_context.filter((e) => e.type === 'context'),
|
|
96
|
+
...manifest.active_context.filter((e) => e.type === 'stub'),
|
|
97
|
+
];
|
|
98
|
+
/* eslint-disable no-await-in-loop */
|
|
99
|
+
for (const entry of ordered) {
|
|
100
|
+
try {
|
|
101
|
+
const fullPath = join(contextTreeDir, entry.path);
|
|
102
|
+
const content = await readFile(fullPath, 'utf8');
|
|
103
|
+
resolved.push({
|
|
104
|
+
content,
|
|
105
|
+
path: entry.path,
|
|
106
|
+
tokens: entry.tokens,
|
|
107
|
+
type: entry.type,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Skip unreadable files
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/* eslint-enable no-await-in-loop */
|
|
115
|
+
return resolved;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Allocate entries into a lane respecting the token budget.
|
|
119
|
+
* Entries are already sorted by priority (caller responsibility).
|
|
120
|
+
*/
|
|
121
|
+
allocateLane(entries, budget) {
|
|
122
|
+
const allocated = [];
|
|
123
|
+
let remaining = budget;
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
if (entry.tokens <= remaining) {
|
|
126
|
+
allocated.push(entry);
|
|
127
|
+
remaining -= entry.tokens;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return allocated;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Compute source fingerprint from stat data (path:mtime:size).
|
|
134
|
+
* Stat-only — no file reads required.
|
|
135
|
+
*/
|
|
136
|
+
async computeSourceFingerprint(contextTreeDir) {
|
|
137
|
+
const entries = [];
|
|
138
|
+
await this.scanSourceStats(contextTreeDir, contextTreeDir, entries);
|
|
139
|
+
const input = entries
|
|
140
|
+
.sort((a, b) => a.path.localeCompare(b.path))
|
|
141
|
+
.map((e) => `${e.path}:${e.mtime}:${e.size}`)
|
|
142
|
+
.join('\n');
|
|
143
|
+
return computeContentHash(input);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Recursively scan _archived/ directory for .stub.md files.
|
|
147
|
+
*/
|
|
148
|
+
async scanArchivedStubs(currentDir, contextTreeDir, stubs) {
|
|
149
|
+
let entries;
|
|
150
|
+
try {
|
|
151
|
+
entries = await readdir(currentDir, { withFileTypes: true });
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
/* eslint-disable no-await-in-loop */
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
const entryName = entry.name;
|
|
159
|
+
const fullPath = join(currentDir, entryName);
|
|
160
|
+
if (entry.isDirectory()) {
|
|
161
|
+
await this.scanArchivedStubs(fullPath, contextTreeDir, stubs);
|
|
162
|
+
}
|
|
163
|
+
else if (entry.isFile() && entryName.endsWith(STUB_EXTENSION)) {
|
|
164
|
+
const relativePath = toUnixPath(relative(contextTreeDir, fullPath));
|
|
165
|
+
try {
|
|
166
|
+
const content = await readFile(fullPath, 'utf8');
|
|
167
|
+
stubs.push({
|
|
168
|
+
path: relativePath,
|
|
169
|
+
tokens: estimateTokens(content),
|
|
170
|
+
type: 'stub',
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Skip unreadable stubs
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/* eslint-enable no-await-in-loop */
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Recursively scan context tree, collecting entries for manifest building.
|
|
182
|
+
*/
|
|
183
|
+
async scanForManifest(currentDir, contextTreeDir, summaries, contexts, stubs) {
|
|
184
|
+
let entries;
|
|
185
|
+
try {
|
|
186
|
+
entries = await readdir(currentDir, { withFileTypes: true });
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
/* eslint-disable no-await-in-loop */
|
|
192
|
+
for (const entry of entries) {
|
|
193
|
+
const entryName = entry.name;
|
|
194
|
+
const fullPath = join(currentDir, entryName);
|
|
195
|
+
if (entry.isDirectory()) {
|
|
196
|
+
// Scan _archived/ for .stub.md files only; recurse otherwise
|
|
197
|
+
await (entryName === ARCHIVE_DIR
|
|
198
|
+
? this.scanArchivedStubs(fullPath, contextTreeDir, stubs)
|
|
199
|
+
: this.scanForManifest(fullPath, contextTreeDir, summaries, contexts, stubs));
|
|
200
|
+
}
|
|
201
|
+
else if (entry.isFile() && entryName.endsWith(CONTEXT_FILE_EXTENSION)) {
|
|
202
|
+
const relativePath = toUnixPath(relative(contextTreeDir, fullPath));
|
|
203
|
+
if (entryName === SUMMARY_INDEX_FILE) {
|
|
204
|
+
// Summary entry — read frontmatter for condensation_order and token_count
|
|
205
|
+
try {
|
|
206
|
+
const content = await readFile(fullPath, 'utf8');
|
|
207
|
+
const fm = parseSummaryFrontmatter(content);
|
|
208
|
+
summaries.push({
|
|
209
|
+
order: fm?.condensation_order,
|
|
210
|
+
path: relativePath,
|
|
211
|
+
tokens: fm?.token_count ?? estimateTokens(content),
|
|
212
|
+
type: 'summary',
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// Skip unreadable summaries
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
else if (!isDerivedArtifact(relativePath) && !isArchiveStub(relativePath)) {
|
|
220
|
+
// Regular context entry — extract importance from frontmatter
|
|
221
|
+
try {
|
|
222
|
+
const content = await readFile(fullPath, 'utf8');
|
|
223
|
+
const scoring = parseFrontmatterScoring(content);
|
|
224
|
+
contexts.push({
|
|
225
|
+
importance: scoring?.importance ?? 50,
|
|
226
|
+
path: relativePath,
|
|
227
|
+
tokens: estimateTokens(content),
|
|
228
|
+
type: 'context',
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// Skip unreadable files
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/* eslint-enable no-await-in-loop */
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Recursively collect stat data for all source files (for fingerprint).
|
|
241
|
+
* Excludes derived artifacts.
|
|
242
|
+
*/
|
|
243
|
+
async scanSourceStats(currentDir, contextTreeDir, entries) {
|
|
244
|
+
let dirEntries;
|
|
245
|
+
try {
|
|
246
|
+
dirEntries = await readdir(currentDir, { withFileTypes: true });
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
/* eslint-disable no-await-in-loop */
|
|
252
|
+
for (const entry of dirEntries) {
|
|
253
|
+
const entryName = entry.name;
|
|
254
|
+
const fullPath = join(currentDir, entryName);
|
|
255
|
+
if (entry.isDirectory()) {
|
|
256
|
+
await this.scanSourceStats(fullPath, contextTreeDir, entries);
|
|
257
|
+
}
|
|
258
|
+
else if (entry.isFile() && entryName.endsWith(CONTEXT_FILE_EXTENSION)) {
|
|
259
|
+
const relativePath = toUnixPath(relative(contextTreeDir, fullPath));
|
|
260
|
+
// Include all non-derived files (contexts + stubs) in fingerprint
|
|
261
|
+
if (isDerivedArtifact(relativePath))
|
|
262
|
+
continue;
|
|
263
|
+
try {
|
|
264
|
+
const fileStat = await stat(fullPath);
|
|
265
|
+
entries.push({
|
|
266
|
+
mtime: fileStat.mtimeMs,
|
|
267
|
+
path: relativePath,
|
|
268
|
+
size: fileStat.size,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
// Skip unreadable files
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/* eslint-enable no-await-in-loop */
|
|
277
|
+
}
|
|
278
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { copyFile, mkdir, opendir, readFile, rename, rm, stat, unlink, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname, extname, join } from 'node:path';
|
|
3
3
|
import { BRV_DIR, CONTEXT_TREE_BACKUP_DIR, CONTEXT_TREE_CONFLICT_DIR, CONTEXT_TREE_DIR } from '../../constants.js';
|
|
4
|
+
import { isExcludedFromSync } from './derived-artifact.js';
|
|
4
5
|
import { computeContentHash } from './hash-utils.js';
|
|
5
6
|
import { toUnixPath } from './path-utils.js';
|
|
6
7
|
/**
|
|
@@ -148,6 +149,9 @@ export class FileContextTreeMerger {
|
|
|
148
149
|
const remoteFileStates = new Map();
|
|
149
150
|
/* eslint-disable no-await-in-loop */
|
|
150
151
|
for (const [normalPath, file] of remoteFilesMap) {
|
|
152
|
+
// Skip derived artifacts (_index.md, _archived/*, _manifest.json)
|
|
153
|
+
if (isExcludedFromSync(normalPath))
|
|
154
|
+
continue;
|
|
151
155
|
const targetPath = join(contextTreeDir, normalPath);
|
|
152
156
|
const remoteHash = computeContentHash(file.decodedContent);
|
|
153
157
|
const snapshotHash = snapshotState.get(normalPath)?.hash;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname, join, relative } from 'node:path';
|
|
3
|
-
import { BRV_DIR, CONTEXT_FILE_EXTENSION, CONTEXT_TREE_DIR, README_FILE, SNAPSHOT_FILE } from '../../constants.js';
|
|
3
|
+
import { ARCHIVE_DIR, BRV_DIR, CONTEXT_FILE_EXTENSION, CONTEXT_TREE_DIR, README_FILE, SNAPSHOT_FILE } from '../../constants.js';
|
|
4
4
|
import { ContextTreeSnapshot, } from '../../core/domain/entities/context-tree-snapshot.js';
|
|
5
|
+
import { isExcludedFromSync } from './derived-artifact.js';
|
|
5
6
|
import { computeContentHash } from './hash-utils.js';
|
|
6
7
|
import { toUnixPath } from './path-utils.js';
|
|
7
8
|
/**
|
|
@@ -117,15 +118,22 @@ export class FileContextTreeSnapshotService {
|
|
|
117
118
|
continue;
|
|
118
119
|
}
|
|
119
120
|
if (entry.isDirectory()) {
|
|
121
|
+
// Skip _archived/ directory (contains derived artifacts)
|
|
122
|
+
if (entry.name === ARCHIVE_DIR)
|
|
123
|
+
continue;
|
|
120
124
|
tasks.push(this.scanDirectory(fullPath, rootDir, files));
|
|
121
125
|
}
|
|
122
126
|
else if (entry.isFile() && entry.name.endsWith(CONTEXT_FILE_EXTENSION)) {
|
|
123
127
|
// Only ignore README.md at root level, track it in subdirectories
|
|
124
128
|
const isRoot = currentDir === rootDir;
|
|
125
129
|
const isReadmeAtRoot = entry.name === README_FILE && isRoot;
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
if (isReadmeAtRoot)
|
|
131
|
+
continue;
|
|
132
|
+
// Skip derived artifacts (_index.md, _manifest.json, .stub.md, .full.md)
|
|
133
|
+
const relativePath = toUnixPath(relative(rootDir, fullPath));
|
|
134
|
+
if (isExcludedFromSync(relativePath))
|
|
135
|
+
continue;
|
|
136
|
+
tasks.push(this.processFile(fullPath, rootDir, files));
|
|
129
137
|
}
|
|
130
138
|
}
|
|
131
139
|
await Promise.all(tasks);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based implementation of IContextTreeSummaryService.
|
|
3
|
+
*
|
|
4
|
+
* Manages hierarchical summary nodes (_index.md) in the context tree.
|
|
5
|
+
* Uses three-tier escalation (normal → aggressive → deterministic fallback)
|
|
6
|
+
* following the same pattern as PreCompactionService.
|
|
7
|
+
*
|
|
8
|
+
* Fail-open: any error returns { actionTaken: false } — never blocks curation.
|
|
9
|
+
*/
|
|
10
|
+
import type { ICipherAgent } from '../../../agent/core/interfaces/i-cipher-agent.js';
|
|
11
|
+
import type { StalenessCheckResult, SummaryGenerationResult } from '../../core/domain/knowledge/summary-types.js';
|
|
12
|
+
import type { IContextTreeSummaryService } from '../../core/interfaces/context-tree/i-context-tree-summary-service.js';
|
|
13
|
+
export declare class FileContextTreeSummaryService implements IContextTreeSummaryService {
|
|
14
|
+
checkStaleness(directoryPath: string, directory?: string): Promise<StalenessCheckResult>;
|
|
15
|
+
generateSummary(directoryPath: string, agent: ICipherAgent, directory?: string): Promise<SummaryGenerationResult>;
|
|
16
|
+
hasSummary(directoryPath: string, directory?: string): Promise<boolean>;
|
|
17
|
+
propagateStaleness(changedPaths: string[], agent: ICipherAgent, directory?: string): Promise<SummaryGenerationResult[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Collect inputs for a summary (the summary input set invariant):
|
|
20
|
+
* - Leaf .md files in the directory (excluding _index.md, _archived/)
|
|
21
|
+
* - Child directory _index.md files (summaries of subdirectories)
|
|
22
|
+
*/
|
|
23
|
+
private collectInputs;
|
|
24
|
+
/**
|
|
25
|
+
* Determine condensation order from directory depth relative to context tree root.
|
|
26
|
+
* Root = d3, domain = d2, topic = d1, subtopic = d0
|
|
27
|
+
*/
|
|
28
|
+
private depthToCondensationOrder;
|
|
29
|
+
/**
|
|
30
|
+
* Execute a single summary generation pass via the agent.
|
|
31
|
+
*/
|
|
32
|
+
private executeSummaryPass;
|
|
33
|
+
/**
|
|
34
|
+
* Three-tier escalation for summary generation.
|
|
35
|
+
* Follows the same pattern as PreCompactionService.compact().
|
|
36
|
+
*/
|
|
37
|
+
private generateWithEscalation;
|
|
38
|
+
/**
|
|
39
|
+
* Check if any descendant of dirPath was stopped due to LLM/IO error.
|
|
40
|
+
* Since we process bottom-up (deepest first), a stopped child means
|
|
41
|
+
* its parent should also stop to prevent regenerating from stale state.
|
|
42
|
+
*/
|
|
43
|
+
private hasStoppedDescendant;
|
|
44
|
+
}
|