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,454 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import * as fs from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { BlobError } from '../../core/domain/errors/blob-error.js';
|
|
5
|
+
/**
|
|
6
|
+
* File-based blob storage implementation.
|
|
7
|
+
*
|
|
8
|
+
* Stores blobs as individual files on the filesystem:
|
|
9
|
+
* {storageDir}/blobs/{key}/content.bin — binary content
|
|
10
|
+
* {storageDir}/blobs/{key}/metadata.json — JSON metadata
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - One directory per blob (O(1) read/write/delete)
|
|
14
|
+
* - Atomic writes via write-to-temp + rename
|
|
15
|
+
* - Size limit enforcement (per-blob and total)
|
|
16
|
+
* - In-memory mode for fast unit tests
|
|
17
|
+
* - Prefix-based listing via readdir
|
|
18
|
+
*/
|
|
19
|
+
export class FileBlobStorage {
|
|
20
|
+
baseDir;
|
|
21
|
+
initialized = false;
|
|
22
|
+
inMemory;
|
|
23
|
+
maxBlobSize;
|
|
24
|
+
maxTotalSize;
|
|
25
|
+
memoryStore = null;
|
|
26
|
+
storageDir;
|
|
27
|
+
constructor(config) {
|
|
28
|
+
this.inMemory = config?.inMemory ?? false;
|
|
29
|
+
this.storageDir = config?.storageDir ?? '';
|
|
30
|
+
if (!this.inMemory && !this.storageDir) {
|
|
31
|
+
throw new Error('FileBlobStorage: storageDir is required when inMemory is false');
|
|
32
|
+
}
|
|
33
|
+
this.baseDir = join(this.storageDir, 'blobs');
|
|
34
|
+
this.maxBlobSize = config?.maxBlobSize ?? 100 * 1024 * 1024; // 100MB default
|
|
35
|
+
this.maxTotalSize = config?.maxTotalSize ?? 1024 * 1024 * 1024; // 1GB default
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Clear all blobs from storage.
|
|
39
|
+
*/
|
|
40
|
+
async clear() {
|
|
41
|
+
this.ensureInitialized();
|
|
42
|
+
try {
|
|
43
|
+
if (this.inMemory) {
|
|
44
|
+
this.memoryStore.clear();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Read all entries and remove each directory
|
|
48
|
+
let entries;
|
|
49
|
+
try {
|
|
50
|
+
entries = await fs.readdir(this.baseDir, { withFileTypes: true });
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return; // Nothing to clear
|
|
54
|
+
}
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
if (entry.isDirectory()) {
|
|
57
|
+
const dirPath = join(this.baseDir, entry.name);
|
|
58
|
+
// eslint-disable-next-line no-await-in-loop
|
|
59
|
+
await fs.rm(dirPath, { force: true, recursive: true });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (error instanceof BlobError) {
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
throw BlobError.deleteError(`Failed to clear storage: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Close the storage. Releases in-memory data.
|
|
72
|
+
*/
|
|
73
|
+
close() {
|
|
74
|
+
if (this.inMemory) {
|
|
75
|
+
this.memoryStore = null;
|
|
76
|
+
}
|
|
77
|
+
this.initialized = false;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Delete a blob by its key.
|
|
81
|
+
*/
|
|
82
|
+
async delete(key) {
|
|
83
|
+
this.ensureInitialized();
|
|
84
|
+
this.validateKey(key);
|
|
85
|
+
if (this.inMemory) {
|
|
86
|
+
if (!this.memoryStore.has(key)) {
|
|
87
|
+
throw BlobError.notFound(key);
|
|
88
|
+
}
|
|
89
|
+
this.memoryStore.delete(key);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const blobDir = join(this.baseDir, key);
|
|
93
|
+
try {
|
|
94
|
+
await fs.access(blobDir);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
throw BlobError.notFound(key);
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
await fs.rm(blobDir, { force: true, recursive: true });
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
throw BlobError.deleteError(`Failed to delete blob ${key}: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if a blob exists.
|
|
108
|
+
*/
|
|
109
|
+
async exists(key) {
|
|
110
|
+
this.ensureInitialized();
|
|
111
|
+
this.validateKey(key);
|
|
112
|
+
if (this.inMemory) {
|
|
113
|
+
return this.memoryStore.has(key);
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
await fs.access(join(this.baseDir, key, 'metadata.json'));
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get metadata for a blob without retrieving its content.
|
|
125
|
+
*/
|
|
126
|
+
async getMetadata(key) {
|
|
127
|
+
this.ensureInitialized();
|
|
128
|
+
this.validateKey(key);
|
|
129
|
+
try {
|
|
130
|
+
if (this.inMemory) {
|
|
131
|
+
const entry = this.memoryStore.get(key);
|
|
132
|
+
return entry ? this.toPublicMetadata(entry.metadata) : undefined;
|
|
133
|
+
}
|
|
134
|
+
return this.readMetadataFromDisk(key);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (error instanceof BlobError) {
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
throw BlobError.retrievalError(`Failed to read metadata for blob ${key}: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get storage statistics.
|
|
145
|
+
*/
|
|
146
|
+
async getStats() {
|
|
147
|
+
this.ensureInitialized();
|
|
148
|
+
try {
|
|
149
|
+
if (this.inMemory) {
|
|
150
|
+
let totalSize = 0;
|
|
151
|
+
for (const entry of this.memoryStore.values()) {
|
|
152
|
+
totalSize += entry.metadata.size;
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
lastUpdated: Date.now(),
|
|
156
|
+
totalBlobs: this.memoryStore.size,
|
|
157
|
+
totalSize,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return this.getStatsFromDisk();
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (error instanceof BlobError) {
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
throw BlobError.retrievalError(`Failed to get storage stats: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Initialize the storage backend.
|
|
171
|
+
*/
|
|
172
|
+
async initialize() {
|
|
173
|
+
if (this.initialized) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
if (this.inMemory) {
|
|
178
|
+
this.memoryStore = new Map();
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
await fs.mkdir(this.baseDir, { recursive: true });
|
|
182
|
+
}
|
|
183
|
+
this.initialized = true;
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
throw BlobError.initializationError(`Failed to initialize file blob storage: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* List all blob keys, optionally filtered by prefix.
|
|
191
|
+
*/
|
|
192
|
+
async list(prefix) {
|
|
193
|
+
this.ensureInitialized();
|
|
194
|
+
try {
|
|
195
|
+
if (this.inMemory) {
|
|
196
|
+
let keys = [...this.memoryStore.keys()];
|
|
197
|
+
if (prefix) {
|
|
198
|
+
keys = keys.filter((k) => k.startsWith(prefix));
|
|
199
|
+
}
|
|
200
|
+
keys.sort();
|
|
201
|
+
return keys;
|
|
202
|
+
}
|
|
203
|
+
return this.listFromDisk(prefix);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
if (error instanceof BlobError) {
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
throw BlobError.retrievalError(`Failed to list blobs: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Retrieve a blob by its key.
|
|
214
|
+
*/
|
|
215
|
+
async retrieve(key) {
|
|
216
|
+
this.ensureInitialized();
|
|
217
|
+
this.validateKey(key);
|
|
218
|
+
try {
|
|
219
|
+
if (this.inMemory) {
|
|
220
|
+
const entry = this.memoryStore.get(key);
|
|
221
|
+
if (!entry) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
content: entry.content,
|
|
226
|
+
key,
|
|
227
|
+
metadata: this.toPublicMetadata(entry.metadata),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return this.retrieveFromDisk(key);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
if (error instanceof BlobError) {
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
throw BlobError.retrievalError(`Failed to retrieve blob ${key}: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Store a blob with optional metadata.
|
|
241
|
+
*/
|
|
242
|
+
async store(key, content, metadata) {
|
|
243
|
+
this.ensureInitialized();
|
|
244
|
+
this.validateKey(key);
|
|
245
|
+
const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
|
|
246
|
+
// Check individual blob size
|
|
247
|
+
if (buffer.length > this.maxBlobSize) {
|
|
248
|
+
throw BlobError.tooLarge(buffer.length, this.maxBlobSize);
|
|
249
|
+
}
|
|
250
|
+
// Check total storage size
|
|
251
|
+
const stats = await this.getStats();
|
|
252
|
+
const existing = await this.retrieve(key);
|
|
253
|
+
const existingSize = existing?.metadata.size ?? 0;
|
|
254
|
+
const newTotalSize = stats.totalSize - existingSize + buffer.length;
|
|
255
|
+
if (newTotalSize > this.maxTotalSize) {
|
|
256
|
+
throw BlobError.totalSizeExceeded(stats.totalSize - existingSize, buffer.length, this.maxTotalSize);
|
|
257
|
+
}
|
|
258
|
+
const now = Date.now();
|
|
259
|
+
const fullMetadata = {
|
|
260
|
+
createdAt: existing?.metadata.createdAt ?? now,
|
|
261
|
+
size: buffer.length,
|
|
262
|
+
updatedAt: now,
|
|
263
|
+
...metadata,
|
|
264
|
+
};
|
|
265
|
+
const storedMeta = {
|
|
266
|
+
createdAt: fullMetadata.createdAt,
|
|
267
|
+
size: buffer.length,
|
|
268
|
+
updatedAt: fullMetadata.updatedAt,
|
|
269
|
+
};
|
|
270
|
+
if (fullMetadata.contentType) {
|
|
271
|
+
storedMeta.contentType = fullMetadata.contentType;
|
|
272
|
+
}
|
|
273
|
+
if (fullMetadata.originalName) {
|
|
274
|
+
storedMeta.originalName = fullMetadata.originalName;
|
|
275
|
+
}
|
|
276
|
+
if (fullMetadata.tags) {
|
|
277
|
+
storedMeta.tags = fullMetadata.tags;
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
if (this.inMemory) {
|
|
281
|
+
this.memoryStore.set(key, { content: buffer, metadata: storedMeta });
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
await this.writeToDisk(key, buffer, storedMeta);
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
content: buffer,
|
|
288
|
+
key,
|
|
289
|
+
metadata: fullMetadata,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
if (error instanceof BlobError) {
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
throw BlobError.storageError(`Failed to store blob ${key}: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
// Private helpers
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
ensureInitialized() {
|
|
303
|
+
if (!this.initialized) {
|
|
304
|
+
throw BlobError.notInitialized();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Get stats by scanning the blobs directory.
|
|
309
|
+
*/
|
|
310
|
+
async getStatsFromDisk() {
|
|
311
|
+
let totalBlobs = 0;
|
|
312
|
+
let totalSize = 0;
|
|
313
|
+
let entries;
|
|
314
|
+
try {
|
|
315
|
+
entries = await fs.readdir(this.baseDir, { withFileTypes: true });
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
return { lastUpdated: Date.now(), totalBlobs: 0, totalSize: 0 };
|
|
319
|
+
}
|
|
320
|
+
for (const entry of entries) {
|
|
321
|
+
if (entry.isDirectory()) {
|
|
322
|
+
try {
|
|
323
|
+
const metaPath = join(this.baseDir, entry.name, 'metadata.json');
|
|
324
|
+
// eslint-disable-next-line no-await-in-loop
|
|
325
|
+
const metaContent = await fs.readFile(metaPath, 'utf8');
|
|
326
|
+
const meta = JSON.parse(metaContent);
|
|
327
|
+
totalBlobs++;
|
|
328
|
+
totalSize += meta.size;
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
// Skip corrupt entries
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return { lastUpdated: Date.now(), totalBlobs, totalSize };
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* List keys from disk, optionally filtered by prefix.
|
|
339
|
+
*/
|
|
340
|
+
async listFromDisk(prefix) {
|
|
341
|
+
let entries;
|
|
342
|
+
try {
|
|
343
|
+
entries = await fs.readdir(this.baseDir, { withFileTypes: true });
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
let keys = entries
|
|
349
|
+
.filter((e) => e.isDirectory())
|
|
350
|
+
.map((e) => e.name);
|
|
351
|
+
if (prefix) {
|
|
352
|
+
keys = keys.filter((k) => k.startsWith(prefix));
|
|
353
|
+
}
|
|
354
|
+
keys.sort();
|
|
355
|
+
return keys;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Read metadata from disk for a given key.
|
|
359
|
+
*/
|
|
360
|
+
async readMetadataFromDisk(key) {
|
|
361
|
+
const metaPath = join(this.baseDir, key, 'metadata.json');
|
|
362
|
+
try {
|
|
363
|
+
const content = await fs.readFile(metaPath, 'utf8');
|
|
364
|
+
const stored = JSON.parse(content);
|
|
365
|
+
return this.toPublicMetadata(stored);
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
if (error.code === 'ENOENT') {
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
throw error;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Retrieve a blob from disk.
|
|
376
|
+
*/
|
|
377
|
+
async retrieveFromDisk(key) {
|
|
378
|
+
const blobDir = join(this.baseDir, key);
|
|
379
|
+
const contentPath = join(blobDir, 'content.bin');
|
|
380
|
+
const metaPath = join(blobDir, 'metadata.json');
|
|
381
|
+
try {
|
|
382
|
+
const [contentBuf, metaContent] = await Promise.all([
|
|
383
|
+
fs.readFile(contentPath),
|
|
384
|
+
fs.readFile(metaPath, 'utf8'),
|
|
385
|
+
]);
|
|
386
|
+
const stored = JSON.parse(metaContent);
|
|
387
|
+
return {
|
|
388
|
+
content: contentBuf,
|
|
389
|
+
key,
|
|
390
|
+
metadata: this.toPublicMetadata(stored),
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
catch (error) {
|
|
394
|
+
if (error.code === 'ENOENT') {
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Convert StoredMetadata to public BlobMetadata.
|
|
402
|
+
*/
|
|
403
|
+
toPublicMetadata(stored) {
|
|
404
|
+
const metadata = {
|
|
405
|
+
createdAt: stored.createdAt,
|
|
406
|
+
size: stored.size,
|
|
407
|
+
updatedAt: stored.updatedAt,
|
|
408
|
+
};
|
|
409
|
+
if (stored.contentType) {
|
|
410
|
+
metadata.contentType = stored.contentType;
|
|
411
|
+
}
|
|
412
|
+
if (stored.originalName) {
|
|
413
|
+
metadata.originalName = stored.originalName;
|
|
414
|
+
}
|
|
415
|
+
if (stored.tags) {
|
|
416
|
+
metadata.tags = stored.tags;
|
|
417
|
+
}
|
|
418
|
+
return metadata;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Validate blob key.
|
|
422
|
+
* Keys must be alphanumeric with hyphens and underscores only.
|
|
423
|
+
*/
|
|
424
|
+
validateKey(key) {
|
|
425
|
+
if (!key || typeof key !== 'string') {
|
|
426
|
+
throw BlobError.invalidKey(String(key), 'Key must be a non-empty string');
|
|
427
|
+
}
|
|
428
|
+
if (key.length === 0) {
|
|
429
|
+
throw BlobError.invalidKey(key, 'Key cannot be empty');
|
|
430
|
+
}
|
|
431
|
+
const validKeyRegex = /^[a-zA-Z0-9_-]+$/;
|
|
432
|
+
if (!validKeyRegex.test(key)) {
|
|
433
|
+
throw BlobError.invalidKey(key, 'Key must contain only alphanumeric characters, hyphens, and underscores');
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Write blob content and metadata to disk atomically.
|
|
438
|
+
*/
|
|
439
|
+
async writeToDisk(key, content, metadata) {
|
|
440
|
+
const blobDir = join(this.baseDir, key);
|
|
441
|
+
await fs.mkdir(blobDir, { recursive: true });
|
|
442
|
+
const contentPath = join(blobDir, 'content.bin');
|
|
443
|
+
const metaPath = join(blobDir, 'metadata.json');
|
|
444
|
+
const tmpSuffix = `.tmp.${randomUUID()}`;
|
|
445
|
+
// Write content atomically
|
|
446
|
+
const tmpContentPath = `${contentPath}${tmpSuffix}`;
|
|
447
|
+
await fs.writeFile(tmpContentPath, content);
|
|
448
|
+
await fs.rename(tmpContentPath, contentPath);
|
|
449
|
+
// Write metadata atomically
|
|
450
|
+
const tmpMetaPath = `${metaPath}${tmpSuffix}`;
|
|
451
|
+
await fs.writeFile(tmpMetaPath, JSON.stringify(metadata, null, 2), 'utf8');
|
|
452
|
+
await fs.rename(tmpMetaPath, metaPath);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Blob storage module
|
|
3
|
-
* Provides persistent storage for binary/large data blobs
|
|
3
|
+
* Provides persistent storage for binary/large data blobs
|
|
4
4
|
*/
|
|
5
5
|
export type { BlobMetadata, BlobStats, BlobStorageConfig, StoredBlob } from '../../core/domain/blob/types.js';
|
|
6
6
|
export { BlobError, BlobErrorCode } from '../../core/domain/errors/blob-error.js';
|
|
7
7
|
export type { IBlobStorage } from '../../core/interfaces/i-blob-storage.js';
|
|
8
8
|
export { createBlobStorage } from './blob-storage-factory.js';
|
|
9
|
-
export
|
|
10
|
-
export { SqliteBlobStorage } from './sqlite-blob-storage.js';
|
|
9
|
+
export { FileBlobStorage } from './file-blob-storage.js';
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Blob storage module
|
|
3
|
-
* Provides persistent storage for binary/large data blobs
|
|
3
|
+
* Provides persistent storage for binary/large data blobs
|
|
4
4
|
*/
|
|
5
5
|
// Re-export errors from core
|
|
6
6
|
export { BlobError, BlobErrorCode } from '../../core/domain/errors/blob-error.js';
|
|
7
|
-
// Factory (
|
|
7
|
+
// Factory (returns file-based implementation)
|
|
8
8
|
export { createBlobStorage } from './blob-storage-factory.js';
|
|
9
|
-
//
|
|
10
|
-
export
|
|
11
|
-
// SQLite storage implementation
|
|
12
|
-
export { SqliteBlobStorage } from './sqlite-blob-storage.js';
|
|
9
|
+
// File-based storage implementation
|
|
10
|
+
export { FileBlobStorage } from './file-blob-storage.js';
|
|
@@ -8,6 +8,7 @@ import type { MemoryManager } from '../memory/memory-manager.js';
|
|
|
8
8
|
import type { SystemPromptManager } from '../system-prompt/system-prompt-manager.js';
|
|
9
9
|
import type { ToolManager } from '../tools/tool-manager.js';
|
|
10
10
|
import type { CompactionService } from './context/compaction/compaction-service.js';
|
|
11
|
+
import type { ICompressionStrategy } from './context/compression/types.js';
|
|
11
12
|
import { type IContentGenerator } from '../../core/interfaces/i-content-generator.js';
|
|
12
13
|
import { SessionEventBus } from '../events/event-emitter.js';
|
|
13
14
|
import { ContextManager, type FileData, type ImageData } from './context/context-manager.js';
|
|
@@ -122,6 +123,8 @@ export declare class AgentLLMService implements ILLMService {
|
|
|
122
123
|
*/
|
|
123
124
|
constructor(sessionId: string, generator: IContentGenerator, config: AgentLLMServiceConfig, options: {
|
|
124
125
|
compactionService?: CompactionService;
|
|
126
|
+
/** Optional compression strategies for context overflow management */
|
|
127
|
+
compressionStrategies?: ICompressionStrategy[];
|
|
125
128
|
historyStorage?: IHistoryStorage;
|
|
126
129
|
logger?: ILogger;
|
|
127
130
|
memoryManager?: MemoryManager;
|
|
@@ -2,7 +2,7 @@ import { getErrorMessage } from '../../../server/utils/error-helpers.js';
|
|
|
2
2
|
import { AgentStateMachine } from '../../core/domain/agent/agent-state-machine.js';
|
|
3
3
|
import { AgentState, TerminationReason } from '../../core/domain/agent/agent-state.js';
|
|
4
4
|
import { LlmGenerationError, LlmMaxIterationsError, LlmResponseParsingError } from '../../core/domain/errors/llm-error.js';
|
|
5
|
-
import { getEffectiveMaxInputTokens, getMaxInputTokensForModel,
|
|
5
|
+
import { getEffectiveMaxInputTokens, getMaxInputTokensForModel, isValidProviderModel, resolveRegistryProvider, safeParseLLMConfig, } from '../../core/domain/llm/index.js';
|
|
6
6
|
import { StreamChunkType, } from '../../core/interfaces/i-content-generator.js';
|
|
7
7
|
import { NoOpLogger } from '../../core/interfaces/i-logger.js';
|
|
8
8
|
import { EnvironmentContextBuilder } from '../environment/environment-context-builder.js';
|
|
@@ -141,6 +141,7 @@ export class AgentLLMService {
|
|
|
141
141
|
}
|
|
142
142
|
// Initialize context manager with optional history storage
|
|
143
143
|
this.contextManager = new ContextManager({
|
|
144
|
+
compressionStrategies: options.compressionStrategies,
|
|
144
145
|
formatter: this.formatter,
|
|
145
146
|
historyStorage: options.historyStorage,
|
|
146
147
|
maxInputTokens: this.config.maxInputTokens,
|
|
@@ -558,33 +559,7 @@ export class AgentLLMService {
|
|
|
558
559
|
* @returns Provider type ('claude', 'gemini', or 'openai')
|
|
559
560
|
*/
|
|
560
561
|
detectProviderType(model, explicitProvider) {
|
|
561
|
-
|
|
562
|
-
if (explicitProvider) {
|
|
563
|
-
if (explicitProvider === 'anthropic')
|
|
564
|
-
return 'claude';
|
|
565
|
-
if (explicitProvider === 'google' || explicitProvider === 'google-vertex')
|
|
566
|
-
return 'gemini';
|
|
567
|
-
if (['groq', 'mistral', 'openai', 'openai-compatible', 'openrouter', 'xai'].includes(explicitProvider))
|
|
568
|
-
return 'openai';
|
|
569
|
-
}
|
|
570
|
-
// 2. Use registry to detect provider from model name
|
|
571
|
-
const registryProvider = getProviderFromModel(model);
|
|
572
|
-
if (registryProvider === 'claude')
|
|
573
|
-
return 'claude';
|
|
574
|
-
if (registryProvider === 'gemini')
|
|
575
|
-
return 'gemini';
|
|
576
|
-
if (registryProvider === 'openai')
|
|
577
|
-
return 'openai';
|
|
578
|
-
// 3. Fallback to string prefix matching for unknown models
|
|
579
|
-
const lowerModel = model.toLowerCase();
|
|
580
|
-
if (lowerModel.startsWith('claude'))
|
|
581
|
-
return 'claude';
|
|
582
|
-
if (lowerModel.startsWith('gpt') ||
|
|
583
|
-
lowerModel.startsWith('o1') ||
|
|
584
|
-
lowerModel.startsWith('o3') ||
|
|
585
|
-
lowerModel.startsWith('o4'))
|
|
586
|
-
return 'openai';
|
|
587
|
-
return 'gemini';
|
|
562
|
+
return resolveRegistryProvider(model, explicitProvider);
|
|
588
563
|
}
|
|
589
564
|
/**
|
|
590
565
|
* Determine which reflection prompt to add based on hierarchical priority.
|
|
@@ -728,31 +703,38 @@ export class AgentLLMService {
|
|
|
728
703
|
const maxMessageTokens = this.config.maxInputTokens - systemPromptTokens;
|
|
729
704
|
// Target utilization to leave headroom for response
|
|
730
705
|
const targetMessageTokens = Math.floor(maxMessageTokens * TARGET_MESSAGE_TOKEN_UTILIZATION);
|
|
731
|
-
//
|
|
732
|
-
this.contextManager.markToolOutputsCompacted(2);
|
|
733
|
-
// Get token counts (recalculate if pruning changed content)
|
|
706
|
+
// Count current token usage
|
|
734
707
|
const currentMessages = this.contextManager.getMessages();
|
|
735
|
-
const
|
|
736
|
-
//
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
const
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
if (
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
this.
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
708
|
+
const currentTokens = currentMessages.reduce((sum, msg) => sum + this.generator.estimateTokensSync(typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)), 0);
|
|
709
|
+
// Zero-cost continuity: skip all compression work if under threshold.
|
|
710
|
+
// Below 70% utilization, the agent pays zero overhead for context management.
|
|
711
|
+
if (currentTokens > targetMessageTokens) {
|
|
712
|
+
// Step 1: Non-destructive pruning — clear old tool outputs first
|
|
713
|
+
this.contextManager.markToolOutputsCompacted(2);
|
|
714
|
+
// Step 2: Recount after pruning
|
|
715
|
+
const afterPruningMessages = this.contextManager.getMessages();
|
|
716
|
+
const afterPruningTokens = afterPruningMessages.reduce((sum, msg) => sum + this.generator.estimateTokensSync(typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)), 0);
|
|
717
|
+
// Step 3: If still over, run escalated compression (L1→L2→L3) via strategy chain
|
|
718
|
+
if (afterPruningTokens > targetMessageTokens) {
|
|
719
|
+
await this.contextManager.compressAndReplace(systemPromptTokens, targetMessageTokens);
|
|
720
|
+
}
|
|
721
|
+
// Step 4: Emergency guard for curate/query commands.
|
|
722
|
+
// Critical because curate/query have only 1 user turn, making
|
|
723
|
+
// protectedTurns=2 in markToolOutputsCompacted() ineffective.
|
|
724
|
+
if (executionContext?.commandType === 'curate' || executionContext?.commandType === 'query') {
|
|
725
|
+
const postCompressionMessages = this.contextManager.getMessages();
|
|
726
|
+
const postCompressionTokens = postCompressionMessages.reduce((sum, msg) => sum + this.generator.estimateTokensSync(typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)), 0);
|
|
727
|
+
const totalWithSystem = postCompressionTokens + systemPromptTokens;
|
|
728
|
+
if (totalWithSystem > this.config.maxInputTokens * 0.9) {
|
|
729
|
+
// Aggressive: compact ALL tool outputs (protect 0 turns instead of 2)
|
|
730
|
+
this.contextManager.markToolOutputsCompacted(0);
|
|
731
|
+
// Re-run escalated compression with aggressively pruned context
|
|
732
|
+
await this.contextManager.compressAndReplace(systemPromptTokens, targetMessageTokens);
|
|
733
|
+
this.sessionEventBus.emit('llmservice:warning', {
|
|
734
|
+
message: `Emergency context compression triggered (${Math.round((totalWithSystem / this.config.maxInputTokens) * 100)}% utilization)`,
|
|
735
|
+
taskId,
|
|
736
|
+
});
|
|
737
|
+
}
|
|
756
738
|
}
|
|
757
739
|
}
|
|
758
740
|
// Build generation request
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared compression helper functions.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from ReactiveOverflowStrategy to enable reuse
|
|
5
|
+
* by EscalatedCompressionStrategy and other compression implementations.
|
|
6
|
+
*/
|
|
7
|
+
import type { ITokenizer } from '../../../../core/interfaces/i-tokenizer.js';
|
|
8
|
+
import type { InternalMessage } from '../../../../core/interfaces/message-types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Count tokens in message history.
|
|
11
|
+
*/
|
|
12
|
+
export declare function countHistoryTokens(history: InternalMessage[], tokenizer: ITokenizer): number;
|
|
13
|
+
/**
|
|
14
|
+
* Count tokens in a single message.
|
|
15
|
+
*/
|
|
16
|
+
export declare function countMessageTokens(message: InternalMessage, tokenizer: ITokenizer): number;
|
|
17
|
+
/**
|
|
18
|
+
* Extract text content from a message.
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractTextContent(message: InternalMessage): string;
|
|
21
|
+
/**
|
|
22
|
+
* Find turn boundaries in message history.
|
|
23
|
+
*
|
|
24
|
+
* A turn boundary is the index where a user message starts.
|
|
25
|
+
* Returns indices of all user messages.
|
|
26
|
+
*/
|
|
27
|
+
export declare function findTurnBoundaries(messages: InternalMessage[]): number[];
|
|
28
|
+
/**
|
|
29
|
+
* Format messages for the summary prompt.
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatMessagesForSummary(messages: InternalMessage[]): string;
|
|
32
|
+
/**
|
|
33
|
+
* Format role for display.
|
|
34
|
+
*/
|
|
35
|
+
export declare function formatRole(role: string): string;
|