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,6 +1,12 @@
1
+ import { randomUUID } from 'node:crypto';
1
2
  import { setMaxListeners } from 'node:events';
3
+ import { getEffectiveMaxInputTokens, resolveRegistryProvider } from '../../core/domain/llm/index.js';
2
4
  import { STREAMING_EVENT_NAMES } from '../../core/domain/streaming/types.js';
5
+ import { ToolName } from '../../core/domain/tools/constants.js';
3
6
  import { AgentEventBus } from '../events/event-emitter.js';
7
+ import { createGeneratorForProvider } from '../llm/providers/index.js';
8
+ import { EventBasedLogger } from '../logger/event-based-logger.js';
9
+ import { deregisterRootEligibleSession, registerRootEligibleSession } from '../map/agentic-map-service.js';
4
10
  import { SessionManager } from '../session/session-manager.js';
5
11
  import { TransportEventBridge } from '../transport/transport-event-bridge.js';
6
12
  import { AgentError } from './agent-error.js';
@@ -30,6 +36,8 @@ export class CipherAgent extends BaseAgent {
30
36
  // Private state (must come before methods)
31
37
  _agentEventBus;
32
38
  _brvConfig;
39
+ /** Unique ID for this agent instance — scopes nesting registry ownership. */
40
+ _instanceId = randomUUID();
33
41
  _projectIdProvider;
34
42
  /**
35
43
  * Session ID - created once during start().
@@ -42,6 +50,11 @@ export class CipherAgent extends BaseAgent {
42
50
  _transportClient;
43
51
  activeStreamControllers = new Map();
44
52
  eventBridge;
53
+ /**
54
+ * Tracks session IDs this agent instance registered as root-eligible.
55
+ * Used for bulk deregistration on agent teardown (cleanupServices).
56
+ */
57
+ rootEligibleSessions = new Set();
45
58
  sessionManager;
46
59
  /**
47
60
  * Creates a new CipherAgent instance.
@@ -143,6 +156,13 @@ export class CipherAgent extends BaseAgent {
143
156
  this.eventBridge.dispose();
144
157
  this.eventBridge = undefined;
145
158
  }
159
+ // Deregister all root-eligible sessions before session manager disposal.
160
+ // stop()/restart() disposes without calling deleteSession() per session,
161
+ // so this bulk cleanup prevents registry accumulation across restarts.
162
+ for (const sessionId of this.rootEligibleSessions) {
163
+ deregisterRootEligibleSession(sessionId, this._instanceId);
164
+ }
165
+ this.rootEligibleSessions.clear();
146
166
  // Dispose session manager
147
167
  if (this.sessionManager) {
148
168
  this.sessionManager.dispose();
@@ -166,17 +186,27 @@ export class CipherAgent extends BaseAgent {
166
186
  */
167
187
  async createSession(sessionId) {
168
188
  this.ensureStarted();
169
- return this.getSessionManagerInternal().createSession(sessionId);
189
+ const session = await this.getSessionManagerInternal().createSession(sessionId);
190
+ this.registerSessionInternal(session.id);
191
+ return session;
170
192
  }
171
193
  /**
172
194
  * Create a task-scoped child session for parallel execution.
173
195
  * The session gets its own sandbox, context manager, and LLM service.
174
196
  */
175
- async createTaskSession(taskId, commandType) {
197
+ async createTaskSession(taskId, commandType, options) {
176
198
  this.ensureStarted();
177
199
  const sessionMgr = this.getSessionManagerInternal();
178
200
  const parentSessionId = this.getSessionIdInternal();
179
201
  const childSession = await sessionMgr.createChildSession(parentSessionId, commandType, `task-${commandType}-${taskId}`);
202
+ // Only register as root-eligible when explicitly opted in.
203
+ // Top-level executors that may call agentic_map (curate-executor,
204
+ // folder-pack-executor) pass mapRootEligible: true.
205
+ // For agentic_map sub-sessions (created by processItem), registration
206
+ // is skipped here — processItem sets its own isRootCaller: false record.
207
+ if (options?.mapRootEligible) {
208
+ this.registerSessionInternal(childSession.id);
209
+ }
180
210
  return childSession.id;
181
211
  }
182
212
  /**
@@ -203,7 +233,15 @@ export class CipherAgent extends BaseAgent {
203
233
  if (this.stateManager) {
204
234
  this.stateManager.clearSessionOverride(sessionId);
205
235
  }
206
- return this.getSessionManagerInternal().deleteSession(sessionId);
236
+ const deleted = await this.getSessionManagerInternal().deleteSession(sessionId);
237
+ if (deleted && this.rootEligibleSessions.has(sessionId)) {
238
+ // Deregister only if this agent instance owns the session.
239
+ // Scoping prevents one agent's teardown from removing another
240
+ // agent's live record when they share a session ID.
241
+ deregisterRootEligibleSession(sessionId, this._instanceId);
242
+ this.rootEligibleSessions.delete(sessionId);
243
+ }
244
+ return deleted;
207
245
  }
208
246
  /**
209
247
  * Delete a task session and all its resources (sandbox + history).
@@ -212,6 +250,13 @@ export class CipherAgent extends BaseAgent {
212
250
  this.ensureStarted();
213
251
  await this.services.sandboxService.clearSession(sessionId);
214
252
  await this.getSessionManagerInternal().deleteSession(sessionId);
253
+ // Deregister root-eligible record if this agent owns it.
254
+ // For agentic_map sub-sessions, the record was overwritten to isRootCaller: false
255
+ // by processItem, so deregisterRootEligibleSession is a no-op (correct).
256
+ if (this.rootEligibleSessions.has(sessionId)) {
257
+ deregisterRootEligibleSession(sessionId, this._instanceId);
258
+ this.rootEligibleSessions.delete(sessionId);
259
+ }
215
260
  }
216
261
  /**
217
262
  * Execute the agent with user input.
@@ -299,7 +344,11 @@ export class CipherAgent extends BaseAgent {
299
344
  this.ensureStarted();
300
345
  const sessionMgr = this.getSessionManagerInternal();
301
346
  const existingSession = sessionMgr.getSession(sessionId);
302
- return existingSession ?? sessionMgr.createSession(sessionId);
347
+ if (existingSession)
348
+ return existingSession;
349
+ const newSession = await sessionMgr.createSession(sessionId);
350
+ this.registerSessionInternal(newSession.id); // only on actual creation
351
+ return newSession;
303
352
  }
304
353
  /**
305
354
  * Get a session by ID.
@@ -382,8 +431,6 @@ export class CipherAgent extends BaseAgent {
382
431
  providerApiKey: providerUpdate.providerApiKey,
383
432
  providerBaseUrl: providerUpdate.providerBaseUrl,
384
433
  providerHeaders: providerUpdate.providerHeaders,
385
- providerLocation: providerUpdate.providerLocation,
386
- providerProject: providerUpdate.providerProject,
387
434
  siteName: this.config.siteName,
388
435
  temperature: this.config.llm.temperature,
389
436
  verbose: this.config.llm.verbose,
@@ -394,16 +441,27 @@ export class CipherAgent extends BaseAgent {
394
441
  maxSessions: this.config.sessions.maxSessions,
395
442
  sessionTTL: this.config.sessions.sessionTTL,
396
443
  },
444
+ onSessionRemoved: (sessionId) => {
445
+ this.handleSessionRemoved(sessionId);
446
+ },
397
447
  });
398
- // Success — dispose old and swap
448
+ // Success — dispose old and swap.
449
+ // Deregister all root-eligible sessions from the old SM first —
450
+ // they no longer exist after dispose(). The next stream()/execute()
451
+ // will recreate sessions via getOrCreateSession and re-register them.
399
452
  if (this.sessionManager) {
453
+ for (const sid of this.rootEligibleSessions) {
454
+ deregisterRootEligibleSession(sid, this._instanceId);
455
+ }
456
+ this.rootEligibleSessions.clear();
400
457
  this.sessionManager.dispose();
401
458
  }
402
459
  this.sessionManager = newSessionManager;
403
460
  // Re-wire SessionManager into sandbox for tools.agentQuery()
404
461
  this.services.sandboxService.setSessionManager?.(this.sessionManager);
462
+ // Rebind map tools with fresh generator/tokenizer/maxContextTokens
463
+ this.rebindMapTools(services, httpConfig, sessionLLMConfig);
405
464
  }
406
- // === Protected Methods (implement abstract from BaseAgent) ===
407
465
  /**
408
466
  * Reset the agent to initial state.
409
467
  * Resets execution state only. To reset sessions, use resetSession(sessionId).
@@ -414,6 +472,7 @@ export class CipherAgent extends BaseAgent {
414
472
  this.stateManager.reset();
415
473
  }
416
474
  }
475
+ // === Protected Methods (implement abstract from BaseAgent) ===
417
476
  /**
418
477
  * Reset a specific session's conversation history.
419
478
  * @param sessionId - The session ID to reset
@@ -478,8 +537,6 @@ export class CipherAgent extends BaseAgent {
478
537
  providerApiKey: this.config.providerApiKey,
479
538
  providerBaseUrl: this.config.providerBaseUrl,
480
539
  providerHeaders: this.config.providerHeaders,
481
- providerLocation: this.config.providerLocation,
482
- providerProject: this.config.providerProject,
483
540
  siteName: this.config.siteName,
484
541
  temperature: this.config.llm.temperature,
485
542
  verbose: this.config.llm.verbose,
@@ -490,6 +547,9 @@ export class CipherAgent extends BaseAgent {
490
547
  maxSessions: this.config.sessions.maxSessions,
491
548
  sessionTTL: this.config.sessions.sessionTTL,
492
549
  },
550
+ onSessionRemoved: (sessionId) => {
551
+ this.handleSessionRemoved(sessionId);
552
+ },
493
553
  });
494
554
  // Wire SessionManager into sandbox for tools.agentQuery() sub-agent delegation
495
555
  this.services.sandboxService.setSessionManager?.(this.sessionManager);
@@ -497,6 +557,11 @@ export class CipherAgent extends BaseAgent {
497
557
  // Each agent has exactly 1 session created at start time
498
558
  const defaultSession = await this.sessionManager.createSession();
499
559
  this._sessionId = defaultSession.id;
560
+ this.registerSessionInternal(defaultSession.id);
561
+ // Inject agent instance and content generator into ToolProvider for map tools.
562
+ // Uses rebindMapTools() which atomically replaces map tools with fresh deps
563
+ // (generator, tokenizer, maxContextTokens, logger).
564
+ this.rebindMapTools(services, httpConfig, sessionLLMConfig);
500
565
  // Create event bridge if transport client is injected (child process mode).
501
566
  // The bridge forwards AgentEventBus llmservice:* events to the transport server.
502
567
  if (this._transportClient) {
@@ -581,6 +646,9 @@ export class CipherAgent extends BaseAgent {
581
646
  const sessionMgr = this.getSessionManagerInternal();
582
647
  const existingSession = sessionMgr.getSession(sessionId);
583
648
  const session = existingSession ?? (await sessionMgr.createSession(sessionId));
649
+ if (!existingSession) {
650
+ this.registerSessionInternal(session.id); // only on actual creation
651
+ }
584
652
  // Increment iteration counter
585
653
  this.getStateManager().incrementIteration();
586
654
  // Call session.streamRun() which emits events and run:complete
@@ -695,7 +763,6 @@ export class CipherAgent extends BaseAgent {
695
763
  }
696
764
  this._sessionId = sessionId;
697
765
  }
698
- // === Private Helpers (alphabetical order) ===
699
766
  /**
700
767
  * Build HTTP config for ByteRover API calls.
701
768
  * Uses lazy providers when injected (child process mode), otherwise static config.
@@ -710,6 +777,7 @@ export class CipherAgent extends BaseAgent {
710
777
  teamId: this._teamIdProvider ?? this.config.teamId ?? this._brvConfig?.teamId ?? '',
711
778
  };
712
779
  }
780
+ // === Private Helpers (alphabetical order) ===
713
781
  getHistoryStorageInternal() {
714
782
  const storage = this.services?.historyStorage;
715
783
  if (!storage) {
@@ -736,4 +804,63 @@ export class CipherAgent extends BaseAgent {
736
804
  }
737
805
  return manager;
738
806
  }
807
+ /**
808
+ * Handle SessionManager lifecycle removals (delete/end/TTL-expire) and keep
809
+ * root-eligible tracking synchronized with the global nesting registry.
810
+ */
811
+ handleSessionRemoved(sessionId) {
812
+ if (!this.rootEligibleSessions.has(sessionId))
813
+ return;
814
+ deregisterRootEligibleSession(sessionId, this._instanceId);
815
+ this.rootEligibleSessions.delete(sessionId);
816
+ }
817
+ /**
818
+ * Rebuild map tool dependencies and update ToolProvider + SandboxService.
819
+ * Called from both start() (initial setup) and refreshProviderConfig() (hot-swap).
820
+ */
821
+ rebindMapTools(services, httpConfig, sessionLLMConfig) {
822
+ const mapProvider = sessionLLMConfig.provider
823
+ ?? (sessionLLMConfig.openRouterApiKey ? 'openrouter' : 'byterover');
824
+ const mapGenerator = createGeneratorForProvider(mapProvider, {
825
+ apiKey: mapProvider === 'openrouter'
826
+ ? (sessionLLMConfig.openRouterApiKey ?? sessionLLMConfig.providerApiKey)
827
+ : sessionLLMConfig.providerApiKey,
828
+ baseUrl: sessionLLMConfig.providerBaseUrl,
829
+ headers: sessionLLMConfig.providerHeaders,
830
+ httpConfig: httpConfig,
831
+ httpReferer: sessionLLMConfig.httpReferer,
832
+ maxTokens: 4096,
833
+ model: sessionLLMConfig.model,
834
+ siteName: sessionLLMConfig.siteName,
835
+ temperature: 0,
836
+ });
837
+ // Adapter pattern: wrap mapGenerator.estimateTokensSync() as ITokenizer
838
+ const mapTokenizer = {
839
+ countTokens: (text) => mapGenerator.estimateTokensSync(text),
840
+ };
841
+ // Compute registry-clamped maxContextTokens
842
+ const mapModel = sessionLLMConfig.model ?? 'gemini-3-flash-preview';
843
+ const mapRegistryProvider = resolveRegistryProvider(mapModel, mapProvider);
844
+ const effectiveMaxContextTokens = getEffectiveMaxInputTokens(mapRegistryProvider, mapModel, sessionLLMConfig.maxInputTokens);
845
+ const mapLogger = new EventBasedLogger(this._agentEventBus, 'MapTools');
846
+ // Atomically replace map tools with fresh generator/tokenizer/maxContextTokens.
847
+ // replaceTools() is build-then-swap — if build fails, old tools remain intact.
848
+ services.toolProvider.replaceTools([ToolName.LLM_MAP, ToolName.AGENTIC_MAP], {
849
+ agentInstance: this,
850
+ contentGenerator: mapGenerator,
851
+ logger: mapLogger,
852
+ maxContextTokens: effectiveMaxContextTokens,
853
+ tokenizer: mapTokenizer,
854
+ });
855
+ // Update sandbox for tools.curation.mapExtract()
856
+ services.sandboxService.setContentGenerator?.(mapGenerator);
857
+ }
858
+ /**
859
+ * Register a session as root-eligible and track it for lifecycle cleanup.
860
+ * Routes all root-eligible registrations through a single point.
861
+ */
862
+ registerSessionInternal(sessionId) {
863
+ registerRootEligibleSession(sessionId, this._instanceId); // throws if id is a live sub-session record
864
+ this.rootEligibleSessions.add(sessionId);
865
+ }
739
866
  }
@@ -14,6 +14,4 @@ export interface ProviderUpdateConfig {
14
14
  providerApiKey?: string;
15
15
  providerBaseUrl?: string;
16
16
  providerHeaders?: Record<string, string>;
17
- providerLocation?: string;
18
- providerProject?: string;
19
17
  }
@@ -44,7 +44,7 @@ export interface SessionLLMConfig {
44
44
  maxTokens?: number;
45
45
  model: string;
46
46
  openRouterApiKey?: string;
47
- /** Provider ID (anthropic, openai, google, google-vertex, xai, groq, mistral, openrouter, byterover) */
47
+ /** Provider ID (anthropic, openai, google, xai, groq, mistral, openrouter, byterover) */
48
48
  provider?: string;
49
49
  /** API key for the direct provider */
50
50
  providerApiKey?: string;
@@ -52,10 +52,6 @@ export interface SessionLLMConfig {
52
52
  providerBaseUrl?: string;
53
53
  /** Custom headers for the provider */
54
54
  providerHeaders?: Record<string, string>;
55
- /** GCP location for Vertex AI (default: us-central1) */
56
- providerLocation?: string;
57
- /** GCP project ID for Vertex AI */
58
- providerProject?: string;
59
55
  siteName?: string;
60
56
  temperature?: number;
61
57
  verbose?: boolean;
@@ -76,7 +72,7 @@ export type { CipherAgentServices, SessionManagerConfig, SessionServices } from
76
72
  * 8. Policy engine (no dependencies)
77
73
  * 9. Tool scheduler (depends on ToolProvider, PolicyEngine)
78
74
  * 10. Tool manager (depends on ToolProvider, ToolScheduler)
79
- * 11. History storage (depends on BlobStorage)
75
+ * 11. History storage (file-based granular storage)
80
76
  * 12. Return all services
81
77
  *
82
78
  * @param config - Validated agent configuration (Zod-validated)
@@ -18,6 +18,9 @@ import { SessionEventBus } from '../events/event-emitter.js';
18
18
  import { FileSystemService } from '../file-system/file-system-service.js';
19
19
  import { AgentLLMService } from '../llm/agent-llm-service.js';
20
20
  import { CompactionService } from '../llm/context/compaction/compaction-service.js';
21
+ import { EscalatedCompressionStrategy } from '../llm/context/compression/escalated-compression.js';
22
+ import { MiddleRemovalStrategy } from '../llm/context/compression/middle-removal.js';
23
+ import { OldestRemovalStrategy } from '../llm/context/compression/oldest-removal.js';
21
24
  import { LoggingContentGenerator, RetryableContentGenerator, } from '../llm/generators/index.js';
22
25
  import { createGeneratorForProvider } from '../llm/providers/index.js';
23
26
  import { DEFAULT_RETRY_POLICY } from '../llm/retry/retry-policy.js';
@@ -26,12 +29,11 @@ import { EventBasedLogger } from '../logger/event-based-logger.js';
26
29
  import { MemoryManager } from '../memory/memory-manager.js';
27
30
  import { ProcessService } from '../process/process-service.js';
28
31
  import { SandboxService } from '../sandbox/sandbox-service.js';
29
- import { BlobHistoryStorage } from '../storage/blob-history-storage.js';
30
- import { DualFormatHistoryStorage } from '../storage/dual-format-history-storage.js';
32
+ import { FileKeyStorage } from '../storage/file-key-storage.js';
31
33
  import { GranularHistoryStorage } from '../storage/granular-history-storage.js';
32
34
  import { MessageStorageService } from '../storage/message-storage-service.js';
33
- import { SqliteKeyStorage } from '../storage/sqlite-key-storage.js';
34
35
  import { ContextTreeStructureContributor } from '../system-prompt/contributors/context-tree-structure-contributor.js';
36
+ import { MapSelectionContributor } from '../system-prompt/contributors/map-selection-contributor.js';
35
37
  import { SystemPromptManager } from '../system-prompt/system-prompt-manager.js';
36
38
  import { CoreToolScheduler } from '../tools/core-tool-scheduler.js';
37
39
  import { DEFAULT_POLICY_RULES } from '../tools/default-policy-rules.js';
@@ -54,7 +56,7 @@ import { ToolProvider } from '../tools/tool-provider.js';
54
56
  * 8. Policy engine (no dependencies)
55
57
  * 9. Tool scheduler (depends on ToolProvider, PolicyEngine)
56
58
  * 10. Tool manager (depends on ToolProvider, ToolScheduler)
57
- * 11. History storage (depends on BlobStorage)
59
+ * 11. History storage (file-based granular storage)
58
60
  * 12. Return all services
59
61
  *
60
62
  * @param config - Validated agent configuration (Zod-validated)
@@ -126,6 +128,10 @@ export async function createCipherAgentServices(config, agentEventBus) {
126
128
  workingDirectory,
127
129
  });
128
130
  systemPromptManager.registerContributor(contextTreeContributor);
131
+ // Register map selection contributor for curate commands
132
+ // Priority 16 — right after context tree structure, before memories
133
+ const mapSelectionContributor = new MapSelectionContributor('mapSelection', 16);
134
+ systemPromptManager.registerContributor(mapSelectionContributor);
129
135
  // 7. Tool provider (depends on FileSystemService, ProcessService, MemoryManager, SystemPromptManager)
130
136
  const verbose = config.llm.verbose ?? false;
131
137
  const descriptionLoader = new ToolDescriptionLoader();
@@ -148,38 +154,22 @@ export async function createCipherAgentServices(config, agentEventBus) {
148
154
  // 10. Tool manager (with scheduler for policy-based execution)
149
155
  const toolManager = new ToolManager(toolProvider, toolScheduler);
150
156
  await toolManager.initialize();
151
- // 11. History storage (depends on BlobStorage) - SHARED across sessions
152
- let historyStorage;
153
- let compactionService;
154
- let messageStorageService;
155
- if (config.useGranularStorage) {
156
- // Create granular storage infrastructure
157
- const keyStorage = new SqliteKeyStorage({
158
- storageDir: storageBasePath,
159
- });
160
- await keyStorage.initialize();
161
- const messageStorage = new MessageStorageService(keyStorage);
162
- messageStorageService = messageStorage;
163
- const granularStorage = new GranularHistoryStorage(messageStorage);
164
- const blobHistoryStorage = new BlobHistoryStorage(blobStorage);
165
- // DualFormatHistoryStorage routes between formats:
166
- // - New sessions → GranularHistoryStorage
167
- // - Existing sessions → BlobHistoryStorage (no migration)
168
- historyStorage = new DualFormatHistoryStorage(blobHistoryStorage, granularStorage);
169
- // Create CompactionService for context overflow management
170
- const tokenizer = new GeminiTokenizer(config.model ?? 'gemini-3-flash-preview');
171
- compactionService = new CompactionService(messageStorage, tokenizer, {
172
- overflowThreshold: 0.85, // 85% triggers compaction check
173
- protectedTurns: 2, // Protect last 2 user turns from pruning
174
- pruneKeepPercent: 0.2, // Keep 20% of context window in tool outputs
175
- pruneMinimumPercent: 0.1, // Only prune if 10%+ of context window can be saved
176
- });
177
- logger.info('Granular history storage enabled for new sessions');
178
- }
179
- else {
180
- // Default: use blob storage for all sessions
181
- historyStorage = new BlobHistoryStorage(blobStorage);
182
- }
157
+ // 11. History storage - granular file-based storage
158
+ const keyStorage = new FileKeyStorage({
159
+ storageDir: storageBasePath,
160
+ });
161
+ await keyStorage.initialize();
162
+ const messageStorage = new MessageStorageService(keyStorage);
163
+ const messageStorageService = messageStorage;
164
+ const historyStorage = new GranularHistoryStorage(messageStorage);
165
+ // CompactionService for context overflow management
166
+ const tokenizer = new GeminiTokenizer(config.model ?? 'gemini-3-flash-preview');
167
+ const compactionService = new CompactionService(messageStorage, tokenizer, {
168
+ overflowThreshold: 0.85, // 85% triggers compaction check
169
+ protectedTurns: 2, // Protect last 2 user turns from pruning
170
+ pruneKeepPercent: 0.2, // Keep 20% of context window in tool outputs
171
+ pruneMinimumPercent: 0.1, // Only prune if 10%+ of context window can be saved
172
+ });
183
173
  // 12. Log successful initialization
184
174
  logger.info('CipherAgent services initialized successfully', {
185
175
  model: config.model,
@@ -239,6 +229,20 @@ export function createSessionServices(sessionId, sharedServices, httpConfig, llm
239
229
  logResponses: llmConfig.verbose,
240
230
  verbose: llmConfig.verbose,
241
231
  });
232
+ // Create escalated compression strategy with retry-only generator (no UI noise).
233
+ // Skip LoggingContentGenerator: avoids llmservice:thinking spinner events.
234
+ // Use a silenced SessionEventBus: RetryableContentGenerator emits
235
+ // llmservice:warning/error via eventBus on retries. Using a detached
236
+ // event bus with no listeners ensures these fire into void.
237
+ const compactionEventBus = new SessionEventBus();
238
+ const compactionGenerator = new RetryableContentGenerator(baseGenerator, {
239
+ eventBus: compactionEventBus,
240
+ policy: DEFAULT_RETRY_POLICY,
241
+ });
242
+ const escalatedStrategy = new EscalatedCompressionStrategy({
243
+ generator: compactionGenerator,
244
+ model: llmConfig.model ?? 'gemini-3-flash-preview',
245
+ });
242
246
  return new AgentLLMService(sessionId, generator, {
243
247
  maxInputTokens: llmConfig.maxInputTokens,
244
248
  maxIterations: llmConfig.maxIterations ?? 50,
@@ -249,6 +253,11 @@ export function createSessionServices(sessionId, sharedServices, httpConfig, llm
249
253
  verbose: llmConfig.verbose ?? false,
250
254
  }, {
251
255
  compactionService: sharedServices.compactionService,
256
+ compressionStrategies: [
257
+ escalatedStrategy,
258
+ new MiddleRemovalStrategy({ preserveEnd: 5, preserveStart: 4 }),
259
+ new OldestRemovalStrategy({ minMessagesToKeep: 4 }),
260
+ ],
252
261
  historyStorage: sharedServices.historyStorage,
253
262
  logger: sessionLogger,
254
263
  memoryManager: sharedServices.memoryManager,
@@ -267,10 +276,8 @@ export function createSessionServices(sessionId, sharedServices, httpConfig, llm
267
276
  headers: llmConfig.providerHeaders,
268
277
  httpConfig: httpConfig,
269
278
  httpReferer: llmConfig.httpReferer,
270
- location: llmConfig.providerLocation,
271
279
  maxTokens: llmConfig.maxTokens ?? 8192,
272
280
  model: llmConfig.model,
273
- project: llmConfig.providerProject,
274
281
  siteName: llmConfig.siteName,
275
282
  temperature: llmConfig.temperature ?? 0.7,
276
283
  });
@@ -2,10 +2,10 @@ import type { BlobStorageConfig } from '../../core/domain/blob/types.js';
2
2
  import type { IBlobStorage } from '../../core/interfaces/i-blob-storage.js';
3
3
  /**
4
4
  * Factory function to create blob storage backend.
5
- * Always uses SQLite for better performance, single file, and ACID transactions.
5
+ * Uses file-based storage with one directory per blob.
6
6
  *
7
7
  * @param config - Blob storage configuration
8
- * @returns SQLite blob storage implementation
8
+ * @returns File-based blob storage implementation
9
9
  *
10
10
  * @example
11
11
  * const storage = createBlobStorage({ storageDir: '/path/to/xdg/storage' });
@@ -1,14 +1,14 @@
1
- import { SqliteBlobStorage } from './sqlite-blob-storage.js';
1
+ import { FileBlobStorage } from './file-blob-storage.js';
2
2
  /**
3
3
  * Factory function to create blob storage backend.
4
- * Always uses SQLite for better performance, single file, and ACID transactions.
4
+ * Uses file-based storage with one directory per blob.
5
5
  *
6
6
  * @param config - Blob storage configuration
7
- * @returns SQLite blob storage implementation
7
+ * @returns File-based blob storage implementation
8
8
  *
9
9
  * @example
10
10
  * const storage = createBlobStorage({ storageDir: '/path/to/xdg/storage' });
11
11
  */
12
12
  export function createBlobStorage(config) {
13
- return new SqliteBlobStorage(config);
13
+ return new FileBlobStorage(config);
14
14
  }
@@ -0,0 +1,96 @@
1
+ import type { BlobMetadata, BlobStats, BlobStorageConfig, StoredBlob } from '../../core/domain/blob/types.js';
2
+ import type { IBlobStorage } from '../../core/interfaces/i-blob-storage.js';
3
+ /**
4
+ * File-based blob storage implementation.
5
+ *
6
+ * Stores blobs as individual files on the filesystem:
7
+ * {storageDir}/blobs/{key}/content.bin — binary content
8
+ * {storageDir}/blobs/{key}/metadata.json — JSON metadata
9
+ *
10
+ * Features:
11
+ * - One directory per blob (O(1) read/write/delete)
12
+ * - Atomic writes via write-to-temp + rename
13
+ * - Size limit enforcement (per-blob and total)
14
+ * - In-memory mode for fast unit tests
15
+ * - Prefix-based listing via readdir
16
+ */
17
+ export declare class FileBlobStorage implements IBlobStorage {
18
+ private readonly baseDir;
19
+ private initialized;
20
+ private readonly inMemory;
21
+ private readonly maxBlobSize;
22
+ private readonly maxTotalSize;
23
+ private memoryStore;
24
+ private readonly storageDir;
25
+ constructor(config?: Partial<BlobStorageConfig>);
26
+ /**
27
+ * Clear all blobs from storage.
28
+ */
29
+ clear(): Promise<void>;
30
+ /**
31
+ * Close the storage. Releases in-memory data.
32
+ */
33
+ close(): void;
34
+ /**
35
+ * Delete a blob by its key.
36
+ */
37
+ delete(key: string): Promise<void>;
38
+ /**
39
+ * Check if a blob exists.
40
+ */
41
+ exists(key: string): Promise<boolean>;
42
+ /**
43
+ * Get metadata for a blob without retrieving its content.
44
+ */
45
+ getMetadata(key: string): Promise<BlobMetadata | undefined>;
46
+ /**
47
+ * Get storage statistics.
48
+ */
49
+ getStats(): Promise<BlobStats>;
50
+ /**
51
+ * Initialize the storage backend.
52
+ */
53
+ initialize(): Promise<void>;
54
+ /**
55
+ * List all blob keys, optionally filtered by prefix.
56
+ */
57
+ list(prefix?: string): Promise<string[]>;
58
+ /**
59
+ * Retrieve a blob by its key.
60
+ */
61
+ retrieve(key: string): Promise<StoredBlob | undefined>;
62
+ /**
63
+ * Store a blob with optional metadata.
64
+ */
65
+ store(key: string, content: Buffer | string, metadata?: Partial<BlobMetadata>): Promise<StoredBlob>;
66
+ private ensureInitialized;
67
+ /**
68
+ * Get stats by scanning the blobs directory.
69
+ */
70
+ private getStatsFromDisk;
71
+ /**
72
+ * List keys from disk, optionally filtered by prefix.
73
+ */
74
+ private listFromDisk;
75
+ /**
76
+ * Read metadata from disk for a given key.
77
+ */
78
+ private readMetadataFromDisk;
79
+ /**
80
+ * Retrieve a blob from disk.
81
+ */
82
+ private retrieveFromDisk;
83
+ /**
84
+ * Convert StoredMetadata to public BlobMetadata.
85
+ */
86
+ private toPublicMetadata;
87
+ /**
88
+ * Validate blob key.
89
+ * Keys must be alphanumeric with hyphens and underscores only.
90
+ */
91
+ private validateKey;
92
+ /**
93
+ * Write blob content and metadata to disk atomically.
94
+ */
95
+ private writeToDisk;
96
+ }