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
@@ -1,4 +1,5 @@
1
1
  import { CogitPushContext } from '../../core/domain/entities/cogit-push-context.js';
2
+ import { isExcludedFromSync } from '../context-tree/derived-artifact.js';
2
3
  /**
3
4
  * Maps context file contents to CogitPushContext instances for the CoGit API.
4
5
  * Converts file reader output to the format expected by the push service.
@@ -7,21 +8,27 @@ import { CogitPushContext } from '../../core/domain/entities/cogit-push-context.
7
8
  * @returns Array of CogitPushContext instances ready for the push API
8
9
  */
9
10
  export const mapToPushContexts = (params) => {
10
- const addedContextFiles = params.addedFiles.map((file) => new CogitPushContext({
11
+ const addedContextFiles = params.addedFiles
12
+ .filter((file) => !isExcludedFromSync(file.path))
13
+ .map((file) => new CogitPushContext({
11
14
  content: file.content,
12
15
  operation: 'add',
13
16
  path: file.path,
14
17
  tags: file.tags,
15
18
  title: file.title,
16
19
  }));
17
- const editedContextFiles = params.modifiedFiles.map((file) => new CogitPushContext({
20
+ const editedContextFiles = params.modifiedFiles
21
+ .filter((file) => !isExcludedFromSync(file.path))
22
+ .map((file) => new CogitPushContext({
18
23
  content: file.content,
19
24
  operation: 'edit',
20
25
  path: file.path,
21
26
  tags: file.tags,
22
27
  title: file.title,
23
28
  }));
24
- const deletedContextFiles = params.deletedPaths.map((deletedPath) => new CogitPushContext({
29
+ const deletedContextFiles = params.deletedPaths
30
+ .filter((deletedPath) => !isExcludedFromSync(deletedPath))
31
+ .map((deletedPath) => new CogitPushContext({
25
32
  content: '',
26
33
  operation: 'delete',
27
34
  path: deletedPath,
@@ -39,6 +39,10 @@ export declare class SkillConnector implements IConnector {
39
39
  * Write files to a named skill subdirectory for the given agent.
40
40
  * Used by hub install to write downloaded skill files to e.g. `.claude/skills/{skillName}/`.
41
41
  *
42
+ * @param agent - Agent connector target
43
+ * @param skillName - Skill folder name to create under the connector path
44
+ * @param files - Skill files to write
45
+ * @param options - Optional install scope
42
46
  * @param options.scope - 'global' writes to home dir, 'project' (default) writes to project root
43
47
  */
44
48
  writeSkillFiles(agent: Agent, skillName: string, files: Array<{
@@ -216,6 +216,10 @@ export class SkillConnector {
216
216
  * Write files to a named skill subdirectory for the given agent.
217
217
  * Used by hub install to write downloaded skill files to e.g. `.claude/skills/{skillName}/`.
218
218
  *
219
+ * @param agent - Agent connector target
220
+ * @param skillName - Skill folder name to create under the connector path
221
+ * @param files - Skill files to write
222
+ * @param options - Optional install scope
219
223
  * @param options.scope - 'global' writes to home dir, 'project' (default) writes to project root
220
224
  */
221
225
  async writeSkillFiles(agent, skillName, files, options) {
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Children hash utility for summary staleness detection.
3
+ *
4
+ * Computes a deterministic hash from child paths and their content hashes.
5
+ * Including paths (not just content hashes) ensures that renames and moves
6
+ * are detected as structural changes.
7
+ */
8
+ /**
9
+ * Compute a deterministic hash of children for staleness detection.
10
+ *
11
+ * The hash includes both the relative path and content hash of each child,
12
+ * sorted by path for determinism. This detects:
13
+ * - Content changes (different contentHash)
14
+ * - Renames/moves (different path, same contentHash)
15
+ * - Additions/deletions (different set of paths)
16
+ */
17
+ export declare function computeChildrenHash(children: Array<{
18
+ contentHash: string;
19
+ path: string;
20
+ }>): string;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Children hash utility for summary staleness detection.
3
+ *
4
+ * Computes a deterministic hash from child paths and their content hashes.
5
+ * Including paths (not just content hashes) ensures that renames and moves
6
+ * are detected as structural changes.
7
+ */
8
+ import { computeContentHash } from './hash-utils.js';
9
+ /**
10
+ * Compute a deterministic hash of children for staleness detection.
11
+ *
12
+ * The hash includes both the relative path and content hash of each child,
13
+ * sorted by path for determinism. This detects:
14
+ * - Content changes (different contentHash)
15
+ * - Renames/moves (different path, same contentHash)
16
+ * - Additions/deletions (different set of paths)
17
+ */
18
+ export function computeChildrenHash(children) {
19
+ const sorted = [...children].sort((a, b) => a.path.localeCompare(b.path));
20
+ const input = sorted.map((c) => `${c.path}:${c.contentHash}`).join('\n');
21
+ return computeContentHash(input);
22
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Derived-artifact predicates for the Hierarchical DAG architecture.
3
+ *
4
+ * Three predicates with clear separation of concerns:
5
+ * - isDerivedArtifact() — non-searchable derived content (excluded from query fingerprint)
6
+ * - isArchiveStub() — searchable stubs (included in BM25 index and fingerprint)
7
+ * - isExcludedFromSync() — union of above (excluded from snapshot/sync/merge/push)
8
+ */
9
+ /**
10
+ * Returns true if the given relative path is a derived artifact
11
+ * that should be excluded from snapshot tracking, CoGit sync,
12
+ * and query cache fingerprinting.
13
+ *
14
+ * Derived artifacts: _index.md, _manifest.json, _archived/*.full.md
15
+ * NOTE: _archived/*.stub.md are NOT derived — they are searchable.
16
+ */
17
+ export declare function isDerivedArtifact(relativePath: string): boolean;
18
+ /**
19
+ * Returns true if the path is an archive stub (.stub.md inside _archived/).
20
+ * Stubs are searchable but excluded from snapshot/sync.
21
+ */
22
+ export declare function isArchiveStub(relativePath: string): boolean;
23
+ /**
24
+ * Returns true if the path should be excluded from snapshot tracking,
25
+ * CoGit sync (push/pull/merge), and writer operations.
26
+ * This includes ALL derived artifacts plus searchable stubs.
27
+ */
28
+ export declare function isExcludedFromSync(relativePath: string): boolean;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Derived-artifact predicates for the Hierarchical DAG architecture.
3
+ *
4
+ * Three predicates with clear separation of concerns:
5
+ * - isDerivedArtifact() — non-searchable derived content (excluded from query fingerprint)
6
+ * - isArchiveStub() — searchable stubs (included in BM25 index and fingerprint)
7
+ * - isExcludedFromSync() — union of above (excluded from snapshot/sync/merge/push)
8
+ */
9
+ import { ARCHIVE_DIR, FULL_ARCHIVE_EXTENSION, MANIFEST_FILE, STUB_EXTENSION, SUMMARY_INDEX_FILE } from '../../constants.js';
10
+ import { toUnixPath } from './path-utils.js';
11
+ /**
12
+ * Returns true if the given relative path is a derived artifact
13
+ * that should be excluded from snapshot tracking, CoGit sync,
14
+ * and query cache fingerprinting.
15
+ *
16
+ * Derived artifacts: _index.md, _manifest.json, _archived/*.full.md
17
+ * NOTE: _archived/*.stub.md are NOT derived — they are searchable.
18
+ */
19
+ export function isDerivedArtifact(relativePath) {
20
+ const normalized = toUnixPath(relativePath);
21
+ const segments = normalized.split('/');
22
+ const fileName = segments.at(-1) ?? '';
23
+ if (fileName === SUMMARY_INDEX_FILE)
24
+ return true;
25
+ if (fileName === MANIFEST_FILE)
26
+ return true;
27
+ if (segments.includes(ARCHIVE_DIR) && fileName.endsWith(FULL_ARCHIVE_EXTENSION))
28
+ return true;
29
+ return false;
30
+ }
31
+ /**
32
+ * Returns true if the path is an archive stub (.stub.md inside _archived/).
33
+ * Stubs are searchable but excluded from snapshot/sync.
34
+ */
35
+ export function isArchiveStub(relativePath) {
36
+ const normalized = toUnixPath(relativePath);
37
+ const segments = normalized.split('/');
38
+ const fileName = segments.at(-1) ?? '';
39
+ return segments.includes(ARCHIVE_DIR) && fileName.endsWith(STUB_EXTENSION);
40
+ }
41
+ /**
42
+ * Returns true if the path should be excluded from snapshot tracking,
43
+ * CoGit sync (push/pull/merge), and writer operations.
44
+ * This includes ALL derived artifacts plus searchable stubs.
45
+ */
46
+ export function isExcludedFromSync(relativePath) {
47
+ return isDerivedArtifact(relativePath) || isArchiveStub(relativePath);
48
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * File-based implementation of IContextTreeArchiveService.
3
+ *
4
+ * Archives low-importance context entries into _archived/ with:
5
+ * - .full.md: lossless preserved original content
6
+ * - .stub.md: searchable ghost cue (~220 tokens) with lineage pointers
7
+ *
8
+ * Archive naming preserves relative paths to avoid collisions:
9
+ * auth/jwt-tokens/refresh-flow.md → _archived/auth/jwt-tokens/refresh-flow.stub.md
10
+ *
11
+ * Fail-open: any error during ghost cue generation falls back to deterministic truncation.
12
+ */
13
+ import type { ICipherAgent } from '../../../agent/core/interfaces/i-cipher-agent.js';
14
+ import type { ArchiveResult, DrillDownResult } from '../../core/domain/knowledge/summary-types.js';
15
+ import type { IContextTreeArchiveService } from '../../core/interfaces/context-tree/i-context-tree-archive-service.js';
16
+ export declare class FileContextTreeArchiveService implements IContextTreeArchiveService {
17
+ archiveEntry(relativePath: string, agent: ICipherAgent, directory?: string): Promise<ArchiveResult>;
18
+ drillDown(stubPath: string, directory?: string): Promise<DrillDownResult>;
19
+ findArchiveCandidates(directory?: string): Promise<string[]>;
20
+ restoreEntry(stubPath: string, directory?: string): Promise<string>;
21
+ /**
22
+ * Extract importance score from frontmatter. Returns 50 if not found.
23
+ */
24
+ private extractImportance;
25
+ /**
26
+ * Generate a ghost cue using LLM with deterministic fallback.
27
+ */
28
+ private generateGhostCue;
29
+ /**
30
+ * Parse FrontmatterScoring fields from content frontmatter.
31
+ */
32
+ private parseScoring;
33
+ /**
34
+ * Recursively scan context tree for archive candidates.
35
+ */
36
+ private scanForCandidates;
37
+ }
@@ -0,0 +1,219 @@
1
+ /* eslint-disable camelcase */
2
+ /**
3
+ * File-based implementation of IContextTreeArchiveService.
4
+ *
5
+ * Archives low-importance context entries into _archived/ with:
6
+ * - .full.md: lossless preserved original content
7
+ * - .stub.md: searchable ghost cue (~220 tokens) with lineage pointers
8
+ *
9
+ * Archive naming preserves relative paths to avoid collisions:
10
+ * auth/jwt-tokens/refresh-flow.md → _archived/auth/jwt-tokens/refresh-flow.stub.md
11
+ *
12
+ * Fail-open: any error during ghost cue generation falls back to deterministic truncation.
13
+ */
14
+ import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
15
+ import { dirname, extname, join } from 'node:path';
16
+ import { ARCHIVE_DIR, ARCHIVE_IMPORTANCE_THRESHOLD, BRV_DIR, CONTEXT_FILE_EXTENSION, CONTEXT_TREE_DIR, DEFAULT_GHOST_CUE_MAX_TOKENS, FULL_ARCHIVE_EXTENSION, STUB_EXTENSION, } from '../../constants.js';
17
+ import { applyDecay } from '../../core/domain/knowledge/memory-scoring.js';
18
+ import { estimateTokens } from '../executor/pre-compaction/compaction-escalation.js';
19
+ import { isArchiveStub, isDerivedArtifact } from './derived-artifact.js';
20
+ import { toUnixPath } from './path-utils.js';
21
+ import { generateArchiveStubContent, parseArchiveStubFrontmatter } from './summary-frontmatter.js';
22
+ export class FileContextTreeArchiveService {
23
+ async archiveEntry(relativePath, agent, directory) {
24
+ const baseDir = directory ?? process.cwd();
25
+ const contextTreeDir = join(baseDir, BRV_DIR, CONTEXT_TREE_DIR);
26
+ const originalFullPath = join(contextTreeDir, relativePath);
27
+ // Read original content
28
+ const content = await readFile(originalFullPath, 'utf8');
29
+ const originalTokenCount = estimateTokens(content);
30
+ // Compute archive paths: replace extension with .stub.md / .full.md
31
+ const pathWithoutExt = relativePath.slice(0, -extname(relativePath).length);
32
+ const stubRelPath = join(ARCHIVE_DIR, `${pathWithoutExt}${STUB_EXTENSION}`);
33
+ const fullRelPath = join(ARCHIVE_DIR, `${pathWithoutExt}${FULL_ARCHIVE_EXTENSION}`);
34
+ const stubFullPath = join(contextTreeDir, stubRelPath);
35
+ const fullFullPath = join(contextTreeDir, fullRelPath);
36
+ // Create parent directories under _archived/
37
+ await mkdir(dirname(stubFullPath), { recursive: true });
38
+ // Write .full.md — verbatim original content (lossless)
39
+ await writeFile(fullFullPath, content, 'utf8');
40
+ // Generate ghost cue via LLM (fail-open to deterministic truncation)
41
+ const ghostCue = await this.generateGhostCue(agent, content);
42
+ const ghostCueTokenCount = estimateTokens(ghostCue);
43
+ // Parse frontmatter to get importance for eviction metadata
44
+ const importance = this.extractImportance(content);
45
+ // Write .stub.md with archive stub frontmatter
46
+ const stubContent = generateArchiveStubContent({
47
+ evicted_at: new Date().toISOString(),
48
+ evicted_importance: importance,
49
+ original_path: relativePath,
50
+ original_token_count: originalTokenCount,
51
+ points_to: toUnixPath(fullRelPath),
52
+ type: 'archive_stub',
53
+ }, ghostCue);
54
+ await writeFile(stubFullPath, stubContent, 'utf8');
55
+ // Delete original file
56
+ await unlink(originalFullPath);
57
+ return {
58
+ fullPath: toUnixPath(fullRelPath),
59
+ ghostCueTokenCount,
60
+ originalPath: relativePath,
61
+ stubPath: toUnixPath(stubRelPath),
62
+ };
63
+ }
64
+ async drillDown(stubPath, directory) {
65
+ const baseDir = directory ?? process.cwd();
66
+ const contextTreeDir = join(baseDir, BRV_DIR, CONTEXT_TREE_DIR);
67
+ const stubFullPath = join(contextTreeDir, stubPath);
68
+ // Parse stub to get points_to
69
+ const stubContent = await readFile(stubFullPath, 'utf8');
70
+ const fm = parseArchiveStubFrontmatter(stubContent);
71
+ if (!fm) {
72
+ throw new Error(`Invalid archive stub: ${stubPath}`);
73
+ }
74
+ // Read full content
75
+ const fullPath = join(contextTreeDir, fm.points_to);
76
+ const fullContent = await readFile(fullPath, 'utf8');
77
+ return {
78
+ fullContent,
79
+ originalPath: fm.original_path,
80
+ tokenCount: estimateTokens(fullContent),
81
+ };
82
+ }
83
+ async findArchiveCandidates(directory) {
84
+ const baseDir = directory ?? process.cwd();
85
+ const contextTreeDir = join(baseDir, BRV_DIR, CONTEXT_TREE_DIR);
86
+ const candidates = [];
87
+ await this.scanForCandidates(contextTreeDir, contextTreeDir, candidates);
88
+ return candidates;
89
+ }
90
+ async restoreEntry(stubPath, directory) {
91
+ const baseDir = directory ?? process.cwd();
92
+ const contextTreeDir = join(baseDir, BRV_DIR, CONTEXT_TREE_DIR);
93
+ const stubFullPath = join(contextTreeDir, stubPath);
94
+ // Parse stub to get original_path and points_to
95
+ const stubContent = await readFile(stubFullPath, 'utf8');
96
+ const fm = parseArchiveStubFrontmatter(stubContent);
97
+ if (!fm) {
98
+ throw new Error(`Invalid archive stub: ${stubPath}`);
99
+ }
100
+ // Read full content
101
+ const fullPath = join(contextTreeDir, fm.points_to);
102
+ const fullContent = await readFile(fullPath, 'utf8');
103
+ // Write to original path (restore)
104
+ const restoredPath = join(contextTreeDir, fm.original_path);
105
+ await mkdir(dirname(restoredPath), { recursive: true });
106
+ await writeFile(restoredPath, fullContent, 'utf8');
107
+ // Delete stub and full archive files
108
+ await unlink(stubFullPath);
109
+ await unlink(fullPath);
110
+ return fm.original_path;
111
+ }
112
+ /**
113
+ * Extract importance score from frontmatter. Returns 50 if not found.
114
+ */
115
+ extractImportance(content) {
116
+ const match = /^importance:\s*(\d+(?:\.\d+)?)/m.exec(content);
117
+ return match ? Number.parseFloat(match[1]) : 50;
118
+ }
119
+ /**
120
+ * Generate a ghost cue using LLM with deterministic fallback.
121
+ */
122
+ async generateGhostCue(agent, content) {
123
+ try {
124
+ const taskId = `ghost_cue_${Date.now()}`;
125
+ const sessionId = await agent.createTaskSession(taskId, 'query');
126
+ try {
127
+ const prompt = `Summarize the following knowledge entry in ~${DEFAULT_GHOST_CUE_MAX_TOKENS} tokens or less. Output ONLY the summary. Preserve key entity names and relationships.
128
+
129
+ <content>
130
+ ${content.slice(0, 8000)}
131
+ </content>`;
132
+ const response = await agent.executeOnSession(sessionId, prompt, {
133
+ executionContext: {
134
+ clearHistory: true,
135
+ commandType: 'query',
136
+ maxIterations: 1,
137
+ maxTokens: DEFAULT_GHOST_CUE_MAX_TOKENS * 4, // chars ≈ tokens * 4
138
+ temperature: 0.3,
139
+ },
140
+ taskId,
141
+ });
142
+ if (response && response.trim().length > 20) {
143
+ return response.trim();
144
+ }
145
+ }
146
+ finally {
147
+ await agent.deleteTaskSession(sessionId);
148
+ }
149
+ }
150
+ catch {
151
+ // Fall through to deterministic fallback
152
+ }
153
+ // Deterministic fallback: truncate content
154
+ return `${content.replaceAll(/\s+/g, ' ').trim().slice(0, 320)}...`;
155
+ }
156
+ /**
157
+ * Parse FrontmatterScoring fields from content frontmatter.
158
+ */
159
+ parseScoring(content) {
160
+ const scoring = {};
161
+ const importanceMatch = /^importance:\s*(\d+(?:\.\d+)?)/m.exec(content);
162
+ if (importanceMatch)
163
+ scoring.importance = Number.parseFloat(importanceMatch[1]);
164
+ const maturityMatch = /^maturity:\s*['"]?(core|draft|validated)['"]?/m.exec(content);
165
+ if (maturityMatch)
166
+ scoring.maturity = maturityMatch[1];
167
+ const updatedMatch = /^updatedAt:\s*['"]?(.+?)['"]?\s*$/m.exec(content);
168
+ if (updatedMatch)
169
+ scoring.updatedAt = updatedMatch[1];
170
+ return scoring;
171
+ }
172
+ /**
173
+ * Recursively scan context tree for archive candidates.
174
+ */
175
+ async scanForCandidates(currentDir, contextTreeDir, candidates) {
176
+ const { readdir: readdirFs } = await import('node:fs/promises');
177
+ let entries;
178
+ try {
179
+ entries = await readdirFs(currentDir, { withFileTypes: true });
180
+ }
181
+ catch {
182
+ return;
183
+ }
184
+ const now = Date.now();
185
+ /* eslint-disable no-await-in-loop */
186
+ for (const entry of entries) {
187
+ const entryName = entry.name;
188
+ const fullPath = join(currentDir, entryName);
189
+ if (entry.isDirectory()) {
190
+ if (entryName === ARCHIVE_DIR)
191
+ continue;
192
+ await this.scanForCandidates(fullPath, contextTreeDir, candidates);
193
+ }
194
+ else if (entry.isFile() && entryName.endsWith(CONTEXT_FILE_EXTENSION)) {
195
+ const relativePath = toUnixPath(fullPath.slice(contextTreeDir.length + 1));
196
+ if (isDerivedArtifact(relativePath) || isArchiveStub(relativePath))
197
+ continue;
198
+ try {
199
+ const content = await readFile(fullPath, 'utf8');
200
+ const scoring = this.parseScoring(content);
201
+ // Only archive draft entries below importance threshold
202
+ if (scoring.maturity !== 'draft')
203
+ continue;
204
+ const daysSinceUpdate = scoring.updatedAt
205
+ ? (now - new Date(scoring.updatedAt).getTime()) / (1000 * 60 * 60 * 24)
206
+ : 0;
207
+ const decayed = applyDecay(scoring, daysSinceUpdate);
208
+ if ((decayed.importance ?? 50) < ARCHIVE_IMPORTANCE_THRESHOLD) {
209
+ candidates.push(relativePath);
210
+ }
211
+ }
212
+ catch {
213
+ // Skip unreadable files
214
+ }
215
+ }
216
+ }
217
+ /* eslint-enable no-await-in-loop */
218
+ }
219
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * File-based implementation of IContextTreeManifestService.
3
+ *
4
+ * Builds and reads the context manifest (_manifest.json) which allocates
5
+ * context tree entries into three lanes (summaries, contexts, stubs)
6
+ * with token budgets for efficient query context injection.
7
+ *
8
+ * Freshness check uses source_fingerprint (hash of sorted path:mtime:size)
9
+ * to detect additions, modifications, and deletions. Stat-only, no file reads.
10
+ *
11
+ * Tradeoff: rare false-fresh cases where content changes but mtime+size
12
+ * are preserved. Acceptable because writeFile() updates mtime and the next
13
+ * curate run will rebuild the manifest anyway.
14
+ */
15
+ import type { ContextManifest, LaneTokens, ResolvedEntry } from '../../core/domain/knowledge/summary-types.js';
16
+ import type { IContextTreeManifestService } from '../../core/interfaces/context-tree/i-context-tree-manifest-service.js';
17
+ export interface ManifestServiceConfig {
18
+ baseDirectory?: string;
19
+ }
20
+ export declare class FileContextTreeManifestService implements IContextTreeManifestService {
21
+ private readonly config;
22
+ constructor(config?: ManifestServiceConfig);
23
+ buildManifest(directory?: string, laneBudgets?: LaneTokens): Promise<ContextManifest>;
24
+ readManifest(directory?: string): Promise<ContextManifest | null>;
25
+ readManifestIfFresh(directory?: string): Promise<ContextManifest | null>;
26
+ resolveForInjection(manifest: ContextManifest, _query?: string, directory?: string): Promise<ResolvedEntry[]>;
27
+ /**
28
+ * Allocate entries into a lane respecting the token budget.
29
+ * Entries are already sorted by priority (caller responsibility).
30
+ */
31
+ private allocateLane;
32
+ /**
33
+ * Compute source fingerprint from stat data (path:mtime:size).
34
+ * Stat-only — no file reads required.
35
+ */
36
+ private computeSourceFingerprint;
37
+ /**
38
+ * Recursively scan _archived/ directory for .stub.md files.
39
+ */
40
+ private scanArchivedStubs;
41
+ /**
42
+ * Recursively scan context tree, collecting entries for manifest building.
43
+ */
44
+ private scanForManifest;
45
+ /**
46
+ * Recursively collect stat data for all source files (for fingerprint).
47
+ * Excludes derived artifacts.
48
+ */
49
+ private scanSourceStats;
50
+ }