@animus-labs/cortex 0.2.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/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/budget-guard.d.ts +75 -0
- package/dist/budget-guard.d.ts.map +1 -0
- package/dist/budget-guard.js +142 -0
- package/dist/budget-guard.js.map +1 -0
- package/dist/compaction/compaction.d.ts +99 -0
- package/dist/compaction/compaction.d.ts.map +1 -0
- package/dist/compaction/compaction.js +302 -0
- package/dist/compaction/compaction.js.map +1 -0
- package/dist/compaction/failsafe.d.ts +57 -0
- package/dist/compaction/failsafe.d.ts.map +1 -0
- package/dist/compaction/failsafe.js +135 -0
- package/dist/compaction/failsafe.js.map +1 -0
- package/dist/compaction/index.d.ts +381 -0
- package/dist/compaction/index.d.ts.map +1 -0
- package/dist/compaction/index.js +979 -0
- package/dist/compaction/index.js.map +1 -0
- package/dist/compaction/microcompaction.d.ts +219 -0
- package/dist/compaction/microcompaction.d.ts.map +1 -0
- package/dist/compaction/microcompaction.js +536 -0
- package/dist/compaction/microcompaction.js.map +1 -0
- package/dist/compaction/observational/buffering.d.ts +225 -0
- package/dist/compaction/observational/buffering.d.ts.map +1 -0
- package/dist/compaction/observational/buffering.js +354 -0
- package/dist/compaction/observational/buffering.js.map +1 -0
- package/dist/compaction/observational/constants.d.ts +70 -0
- package/dist/compaction/observational/constants.d.ts.map +1 -0
- package/dist/compaction/observational/constants.js +507 -0
- package/dist/compaction/observational/constants.js.map +1 -0
- package/dist/compaction/observational/index.d.ts +219 -0
- package/dist/compaction/observational/index.d.ts.map +1 -0
- package/dist/compaction/observational/index.js +641 -0
- package/dist/compaction/observational/index.js.map +1 -0
- package/dist/compaction/observational/observer.d.ts +97 -0
- package/dist/compaction/observational/observer.d.ts.map +1 -0
- package/dist/compaction/observational/observer.js +424 -0
- package/dist/compaction/observational/observer.js.map +1 -0
- package/dist/compaction/observational/recall-tool.d.ts +27 -0
- package/dist/compaction/observational/recall-tool.d.ts.map +1 -0
- package/dist/compaction/observational/recall-tool.js +93 -0
- package/dist/compaction/observational/recall-tool.js.map +1 -0
- package/dist/compaction/observational/reflector.d.ts +94 -0
- package/dist/compaction/observational/reflector.d.ts.map +1 -0
- package/dist/compaction/observational/reflector.js +167 -0
- package/dist/compaction/observational/reflector.js.map +1 -0
- package/dist/compaction/observational/types.d.ts +271 -0
- package/dist/compaction/observational/types.d.ts.map +1 -0
- package/dist/compaction/observational/types.js +15 -0
- package/dist/compaction/observational/types.js.map +1 -0
- package/dist/context-manager.d.ts +134 -0
- package/dist/context-manager.d.ts.map +1 -0
- package/dist/context-manager.js +170 -0
- package/dist/context-manager.js.map +1 -0
- package/dist/cortex-agent.d.ts +1020 -0
- package/dist/cortex-agent.d.ts.map +1 -0
- package/dist/cortex-agent.js +3589 -0
- package/dist/cortex-agent.js.map +1 -0
- package/dist/error-classifier.d.ts +48 -0
- package/dist/error-classifier.d.ts.map +1 -0
- package/dist/error-classifier.js +152 -0
- package/dist/error-classifier.js.map +1 -0
- package/dist/event-bridge.d.ts +166 -0
- package/dist/event-bridge.d.ts.map +1 -0
- package/dist/event-bridge.js +381 -0
- package/dist/event-bridge.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-client.d.ts +119 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +474 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/model-wrapper.d.ts +58 -0
- package/dist/model-wrapper.d.ts.map +1 -0
- package/dist/model-wrapper.js +86 -0
- package/dist/model-wrapper.js.map +1 -0
- package/dist/noop-logger.d.ts +4 -0
- package/dist/noop-logger.d.ts.map +1 -0
- package/dist/noop-logger.js +8 -0
- package/dist/noop-logger.js.map +1 -0
- package/dist/prompt-diagnostics.d.ts +47 -0
- package/dist/prompt-diagnostics.d.ts.map +1 -0
- package/dist/prompt-diagnostics.js +230 -0
- package/dist/prompt-diagnostics.js.map +1 -0
- package/dist/provider-manager.d.ts +224 -0
- package/dist/provider-manager.d.ts.map +1 -0
- package/dist/provider-manager.js +563 -0
- package/dist/provider-manager.js.map +1 -0
- package/dist/provider-registry.d.ts +115 -0
- package/dist/provider-registry.d.ts.map +1 -0
- package/dist/provider-registry.js +305 -0
- package/dist/provider-registry.js.map +1 -0
- package/dist/schema-converter.d.ts +20 -0
- package/dist/schema-converter.d.ts.map +1 -0
- package/dist/schema-converter.js +48 -0
- package/dist/schema-converter.js.map +1 -0
- package/dist/skill-preprocessor.d.ts +46 -0
- package/dist/skill-preprocessor.d.ts.map +1 -0
- package/dist/skill-preprocessor.js +237 -0
- package/dist/skill-preprocessor.js.map +1 -0
- package/dist/skill-registry.d.ts +107 -0
- package/dist/skill-registry.d.ts.map +1 -0
- package/dist/skill-registry.js +330 -0
- package/dist/skill-registry.js.map +1 -0
- package/dist/skill-tool.d.ts +54 -0
- package/dist/skill-tool.d.ts.map +1 -0
- package/dist/skill-tool.js +88 -0
- package/dist/skill-tool.js.map +1 -0
- package/dist/sub-agent-manager.d.ts +90 -0
- package/dist/sub-agent-manager.d.ts.map +1 -0
- package/dist/sub-agent-manager.js +192 -0
- package/dist/sub-agent-manager.js.map +1 -0
- package/dist/token-estimator.d.ts +23 -0
- package/dist/token-estimator.d.ts.map +1 -0
- package/dist/token-estimator.js +27 -0
- package/dist/token-estimator.js.map +1 -0
- package/dist/tool-contract.d.ts +68 -0
- package/dist/tool-contract.d.ts.map +1 -0
- package/dist/tool-contract.js +35 -0
- package/dist/tool-contract.js.map +1 -0
- package/dist/tool-result-persistence.d.ts +89 -0
- package/dist/tool-result-persistence.d.ts.map +1 -0
- package/dist/tool-result-persistence.js +152 -0
- package/dist/tool-result-persistence.js.map +1 -0
- package/dist/tools/bash/index.d.ts +71 -0
- package/dist/tools/bash/index.d.ts.map +1 -0
- package/dist/tools/bash/index.js +485 -0
- package/dist/tools/bash/index.js.map +1 -0
- package/dist/tools/bash/interactive.d.ts +47 -0
- package/dist/tools/bash/interactive.d.ts.map +1 -0
- package/dist/tools/bash/interactive.js +262 -0
- package/dist/tools/bash/interactive.js.map +1 -0
- package/dist/tools/bash/safety.d.ts +149 -0
- package/dist/tools/bash/safety.d.ts.map +1 -0
- package/dist/tools/bash/safety.js +1116 -0
- package/dist/tools/bash/safety.js.map +1 -0
- package/dist/tools/edit.d.ts +57 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +310 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/glob.d.ts +34 -0
- package/dist/tools/glob.d.ts.map +1 -0
- package/dist/tools/glob.js +268 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/grep.d.ts +53 -0
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +673 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/index.d.ts +62 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +52 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/read.d.ts +43 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +459 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/runtime.d.ts +62 -0
- package/dist/tools/runtime.d.ts.map +1 -0
- package/dist/tools/runtime.js +116 -0
- package/dist/tools/runtime.js.map +1 -0
- package/dist/tools/shared/cwd-tracker.d.ts +32 -0
- package/dist/tools/shared/cwd-tracker.d.ts.map +1 -0
- package/dist/tools/shared/cwd-tracker.js +44 -0
- package/dist/tools/shared/cwd-tracker.js.map +1 -0
- package/dist/tools/shared/edit-history.d.ts +55 -0
- package/dist/tools/shared/edit-history.d.ts.map +1 -0
- package/dist/tools/shared/edit-history.js +72 -0
- package/dist/tools/shared/edit-history.js.map +1 -0
- package/dist/tools/shared/edit-matcher.d.ts +83 -0
- package/dist/tools/shared/edit-matcher.d.ts.map +1 -0
- package/dist/tools/shared/edit-matcher.js +359 -0
- package/dist/tools/shared/edit-matcher.js.map +1 -0
- package/dist/tools/shared/file-mutation-lock.d.ts +22 -0
- package/dist/tools/shared/file-mutation-lock.d.ts.map +1 -0
- package/dist/tools/shared/file-mutation-lock.js +35 -0
- package/dist/tools/shared/file-mutation-lock.js.map +1 -0
- package/dist/tools/shared/gitignore.d.ts +17 -0
- package/dist/tools/shared/gitignore.d.ts.map +1 -0
- package/dist/tools/shared/gitignore.js +59 -0
- package/dist/tools/shared/gitignore.js.map +1 -0
- package/dist/tools/shared/pdf-extractor.d.ts +96 -0
- package/dist/tools/shared/pdf-extractor.d.ts.map +1 -0
- package/dist/tools/shared/pdf-extractor.js +196 -0
- package/dist/tools/shared/pdf-extractor.js.map +1 -0
- package/dist/tools/shared/read-registry.d.ts +66 -0
- package/dist/tools/shared/read-registry.d.ts.map +1 -0
- package/dist/tools/shared/read-registry.js +65 -0
- package/dist/tools/shared/read-registry.js.map +1 -0
- package/dist/tools/shared/safe-env.d.ts +18 -0
- package/dist/tools/shared/safe-env.d.ts.map +1 -0
- package/dist/tools/shared/safe-env.js +70 -0
- package/dist/tools/shared/safe-env.js.map +1 -0
- package/dist/tools/sub-agent.d.ts +91 -0
- package/dist/tools/sub-agent.d.ts.map +1 -0
- package/dist/tools/sub-agent.js +89 -0
- package/dist/tools/sub-agent.js.map +1 -0
- package/dist/tools/task-output.d.ts +38 -0
- package/dist/tools/task-output.d.ts.map +1 -0
- package/dist/tools/task-output.js +186 -0
- package/dist/tools/task-output.js.map +1 -0
- package/dist/tools/tool-search/index.d.ts +40 -0
- package/dist/tools/tool-search/index.d.ts.map +1 -0
- package/dist/tools/tool-search/index.js +110 -0
- package/dist/tools/tool-search/index.js.map +1 -0
- package/dist/tools/tool-search/registry.d.ts +82 -0
- package/dist/tools/tool-search/registry.d.ts.map +1 -0
- package/dist/tools/tool-search/registry.js +238 -0
- package/dist/tools/tool-search/registry.js.map +1 -0
- package/dist/tools/undo-edit.d.ts +51 -0
- package/dist/tools/undo-edit.d.ts.map +1 -0
- package/dist/tools/undo-edit.js +231 -0
- package/dist/tools/undo-edit.js.map +1 -0
- package/dist/tools/web-fetch/cache.d.ts +49 -0
- package/dist/tools/web-fetch/cache.d.ts.map +1 -0
- package/dist/tools/web-fetch/cache.js +89 -0
- package/dist/tools/web-fetch/cache.js.map +1 -0
- package/dist/tools/web-fetch/index.d.ts +53 -0
- package/dist/tools/web-fetch/index.d.ts.map +1 -0
- package/dist/tools/web-fetch/index.js +513 -0
- package/dist/tools/web-fetch/index.js.map +1 -0
- package/dist/tools/write.d.ts +59 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +316 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/types.d.ts +881 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/working-tags.d.ts +44 -0
- package/dist/working-tags.d.ts.map +1 -0
- package/dist/working-tags.js +103 -0
- package/dist/working-tags.js.map +1 -0
- package/package.json +87 -0
- package/src/budget-guard.ts +170 -0
- package/src/compaction/compaction.ts +386 -0
- package/src/compaction/failsafe.ts +185 -0
- package/src/compaction/index.ts +1199 -0
- package/src/compaction/microcompaction.ts +709 -0
- package/src/compaction/observational/buffering.ts +430 -0
- package/src/compaction/observational/constants.ts +532 -0
- package/src/compaction/observational/index.ts +837 -0
- package/src/compaction/observational/observer.ts +510 -0
- package/src/compaction/observational/recall-tool.ts +130 -0
- package/src/compaction/observational/reflector.ts +221 -0
- package/src/compaction/observational/types.ts +343 -0
- package/src/context-manager.ts +237 -0
- package/src/cortex-agent.ts +4297 -0
- package/src/error-classifier.ts +199 -0
- package/src/event-bridge.ts +508 -0
- package/src/index.ts +292 -0
- package/src/mcp-client.ts +582 -0
- package/src/model-wrapper.ts +128 -0
- package/src/noop-logger.ts +9 -0
- package/src/prompt-diagnostics.ts +296 -0
- package/src/provider-manager.ts +823 -0
- package/src/provider-registry.ts +386 -0
- package/src/schema-converter.ts +51 -0
- package/src/skill-preprocessor.ts +314 -0
- package/src/skill-registry.ts +378 -0
- package/src/skill-tool.ts +130 -0
- package/src/sub-agent-manager.ts +236 -0
- package/src/token-estimator.ts +26 -0
- package/src/tool-contract.ts +113 -0
- package/src/tool-result-persistence.ts +197 -0
- package/src/tools/bash/index.ts +633 -0
- package/src/tools/bash/interactive.ts +302 -0
- package/src/tools/bash/safety.ts +1297 -0
- package/src/tools/edit.ts +422 -0
- package/src/tools/glob.ts +330 -0
- package/src/tools/grep.ts +819 -0
- package/src/tools/index.ts +110 -0
- package/src/tools/read.ts +580 -0
- package/src/tools/runtime.ts +173 -0
- package/src/tools/shared/cwd-tracker.ts +50 -0
- package/src/tools/shared/edit-history.ts +96 -0
- package/src/tools/shared/edit-matcher.ts +457 -0
- package/src/tools/shared/file-mutation-lock.ts +40 -0
- package/src/tools/shared/gitignore.ts +61 -0
- package/src/tools/shared/pdf-extractor.ts +290 -0
- package/src/tools/shared/read-registry.ts +93 -0
- package/src/tools/shared/safe-env.ts +82 -0
- package/src/tools/sub-agent.ts +171 -0
- package/src/tools/task-output.ts +236 -0
- package/src/tools/tool-search/index.ts +167 -0
- package/src/tools/tool-search/registry.ts +278 -0
- package/src/tools/undo-edit.ts +314 -0
- package/src/tools/web-fetch/cache.ts +112 -0
- package/src/tools/web-fetch/index.ts +604 -0
- package/src/tools/write.ts +385 -0
- package/src/types.ts +1057 -0
- package/src/working-tags.ts +118 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buffering coordinator for the observational memory system.
|
|
3
|
+
*
|
|
4
|
+
* Manages the async lifecycle of observer and reflector operations,
|
|
5
|
+
* ensuring at-most-one-in-flight per operation type, computing dynamic
|
|
6
|
+
* buffer intervals, and handling abort/cleanup.
|
|
7
|
+
*
|
|
8
|
+
* The coordinator does not own the observation slot or conversation
|
|
9
|
+
* history. It produces observation chunks and buffered reflections that
|
|
10
|
+
* the ObservationalMemoryEngine consumes during activation.
|
|
11
|
+
*
|
|
12
|
+
* References:
|
|
13
|
+
* - observational-memory-architecture.md (Observer System, Reflector System)
|
|
14
|
+
* - observer.ts (runObserver)
|
|
15
|
+
* - reflector.ts (runReflector)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { CompleteFn } from '../compaction.js';
|
|
19
|
+
import type { AgentMessage } from '../../context-manager.js';
|
|
20
|
+
import type { ObservationChunk, ObserverOutput, ReflectorOutput } from './types.js';
|
|
21
|
+
import { runObserver } from './observer.js';
|
|
22
|
+
import { runReflector } from './reflector.js';
|
|
23
|
+
import { estimateTokens } from '../../token-estimator.js';
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// BufferingCoordinator
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Coordinates async observer and reflector operations for the
|
|
31
|
+
* observational memory system.
|
|
32
|
+
*
|
|
33
|
+
* Ensures at-most-one observer and at-most-one reflector call are
|
|
34
|
+
* in-flight at any time. Completed observer results are stored as
|
|
35
|
+
* {@link ObservationChunk}s until the engine activates them. Completed
|
|
36
|
+
* reflector results are stored until the engine swaps them in.
|
|
37
|
+
*
|
|
38
|
+
* All in-flight operations are fire-and-forget from the caller's
|
|
39
|
+
* perspective. The coordinator attaches `.then()` / `.catch()` handlers
|
|
40
|
+
* internally and never surfaces unhandled rejections.
|
|
41
|
+
*/
|
|
42
|
+
export class BufferingCoordinator {
|
|
43
|
+
// --- Internal state ---
|
|
44
|
+
|
|
45
|
+
private chunks: ObservationChunk[] = [];
|
|
46
|
+
private bufferWatermark: number = 0;
|
|
47
|
+
private inFlightObserver: Promise<ObserverOutput> | null = null;
|
|
48
|
+
private inFlightObserverEndIndex: number | null = null;
|
|
49
|
+
private inFlightReflector: Promise<ReflectorOutput> | null = null;
|
|
50
|
+
private bufferedReflection: string | null = null;
|
|
51
|
+
private bufferedReflectionCompressionLevel: number = 0;
|
|
52
|
+
private aborted: boolean = false;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Activation epoch. Incremented each time activation consumes chunks or
|
|
56
|
+
* a sync observer trims messages. In-flight observers capture the epoch
|
|
57
|
+
* at launch and discard their result if the epoch has changed by the time
|
|
58
|
+
* they complete. This prevents stale chunks from landing after sync
|
|
59
|
+
* activation has already processed those messages.
|
|
60
|
+
*/
|
|
61
|
+
private activationEpoch: number = 0;
|
|
62
|
+
|
|
63
|
+
// -------------------------------------------------------------------------
|
|
64
|
+
// Buffer Interval Calculation
|
|
65
|
+
// -------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Compute the dynamic buffer interval based on current context state.
|
|
69
|
+
*
|
|
70
|
+
* The interval targets `bufferTargetCycles` observer calls between the
|
|
71
|
+
* current utilization and the activation threshold. It is clamped between
|
|
72
|
+
* `bufferMinTokens` and `effectiveBufferCap` (the lesser of
|
|
73
|
+
* `bufferTokenCap` and 60% of the utility model's context window).
|
|
74
|
+
*
|
|
75
|
+
* @param tokensUntilActivation - tokens remaining before activation threshold
|
|
76
|
+
* @param config - buffer interval configuration
|
|
77
|
+
* @returns the buffer interval in tokens
|
|
78
|
+
*/
|
|
79
|
+
computeBufferInterval(
|
|
80
|
+
tokensUntilActivation: number,
|
|
81
|
+
config: {
|
|
82
|
+
bufferTargetCycles: number;
|
|
83
|
+
bufferTokenCap: number;
|
|
84
|
+
bufferMinTokens: number;
|
|
85
|
+
utilityModelContextWindow: number;
|
|
86
|
+
},
|
|
87
|
+
): number {
|
|
88
|
+
const effectiveBufferCap = Math.min(
|
|
89
|
+
config.bufferTokenCap,
|
|
90
|
+
config.utilityModelContextWindow * 0.6,
|
|
91
|
+
);
|
|
92
|
+
const dynamicInterval = tokensUntilActivation / config.bufferTargetCycles;
|
|
93
|
+
return Math.max(
|
|
94
|
+
Math.min(dynamicInterval, effectiveBufferCap),
|
|
95
|
+
config.bufferMinTokens,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// -------------------------------------------------------------------------
|
|
100
|
+
// Observer Buffering
|
|
101
|
+
// -------------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Check if a buffer observation should be triggered based on
|
|
105
|
+
* unobserved tokens.
|
|
106
|
+
*
|
|
107
|
+
* Returns true when the unobserved token count meets or exceeds the
|
|
108
|
+
* buffer interval, no observer call is currently in flight, and the
|
|
109
|
+
* coordinator has not been aborted.
|
|
110
|
+
*
|
|
111
|
+
* @param unobservedTokens - estimated tokens of messages after the buffer watermark
|
|
112
|
+
* @param bufferInterval - computed from {@link computeBufferInterval}
|
|
113
|
+
* @returns true if a buffer observation should launch
|
|
114
|
+
*/
|
|
115
|
+
shouldBuffer(unobservedTokens: number, bufferInterval: number): boolean {
|
|
116
|
+
return (
|
|
117
|
+
unobservedTokens >= bufferInterval &&
|
|
118
|
+
!this.isObserverInFlight() &&
|
|
119
|
+
!this.aborted
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Launch an async observer call. Does NOT await it.
|
|
125
|
+
*
|
|
126
|
+
* Stores the in-flight promise and tracks the end index of messages
|
|
127
|
+
* being processed. When the observer completes, its output is converted
|
|
128
|
+
* to an {@link ObservationChunk} and appended to the internal chunk
|
|
129
|
+
* list. If the coordinator has been aborted before the observer
|
|
130
|
+
* completes, the result is discarded.
|
|
131
|
+
*
|
|
132
|
+
* @param complete - the LLM completion function
|
|
133
|
+
* @param messages - the unobserved messages to process (snapshot)
|
|
134
|
+
* @param endIndex - the index in conversation history where these messages end
|
|
135
|
+
* @param previousObservations - current observation text for context
|
|
136
|
+
* @param config - observer config
|
|
137
|
+
* @param logger - optional logger for error reporting
|
|
138
|
+
*/
|
|
139
|
+
launchObserver(
|
|
140
|
+
complete: CompleteFn,
|
|
141
|
+
messages: AgentMessage[],
|
|
142
|
+
endIndex: number,
|
|
143
|
+
previousObservations: string | null,
|
|
144
|
+
config: { previousObserverTokens: number; observerInstruction?: string },
|
|
145
|
+
logger?: { warn: (msg: string) => void },
|
|
146
|
+
): void {
|
|
147
|
+
if (this.aborted) return;
|
|
148
|
+
|
|
149
|
+
// Estimate tokens from the message snapshot for the chunk metadata
|
|
150
|
+
const messageTokensObserved = messages.reduce((sum, msg) => {
|
|
151
|
+
if (typeof msg.content === 'string') {
|
|
152
|
+
return sum + estimateTokens(msg.content);
|
|
153
|
+
}
|
|
154
|
+
if (Array.isArray(msg.content)) {
|
|
155
|
+
const text = msg.content
|
|
156
|
+
.map((part) => {
|
|
157
|
+
if (typeof part.text === 'string') return part.text;
|
|
158
|
+
return JSON.stringify(part);
|
|
159
|
+
})
|
|
160
|
+
.join(' ');
|
|
161
|
+
return sum + estimateTokens(text);
|
|
162
|
+
}
|
|
163
|
+
return sum;
|
|
164
|
+
}, 0);
|
|
165
|
+
|
|
166
|
+
const promise = runObserver(complete, messages, previousObservations, config);
|
|
167
|
+
this.inFlightObserver = promise;
|
|
168
|
+
this.inFlightObserverEndIndex = endIndex;
|
|
169
|
+
|
|
170
|
+
// Capture the activation epoch at launch. If activation fires (sync or
|
|
171
|
+
// chunk-based) before this observer completes, the epoch will have
|
|
172
|
+
// advanced and the result is stale (those messages were already observed).
|
|
173
|
+
const launchEpoch = this.activationEpoch;
|
|
174
|
+
|
|
175
|
+
promise
|
|
176
|
+
.then((output: ObserverOutput) => {
|
|
177
|
+
if (this.aborted) return;
|
|
178
|
+
|
|
179
|
+
// Discard if activation already processed these messages
|
|
180
|
+
if (this.activationEpoch !== launchEpoch) {
|
|
181
|
+
this.inFlightObserver = null;
|
|
182
|
+
this.inFlightObserverEndIndex = null;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const chunk: ObservationChunk = {
|
|
187
|
+
observations: output.observations,
|
|
188
|
+
messageTokensObserved,
|
|
189
|
+
createdAt: new Date(),
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
if (output.currentTask) {
|
|
193
|
+
chunk.currentTask = output.currentTask;
|
|
194
|
+
}
|
|
195
|
+
if (output.suggestedResponse) {
|
|
196
|
+
chunk.suggestedResponse = output.suggestedResponse;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
this.chunks.push(chunk);
|
|
200
|
+
this.bufferWatermark = this.inFlightObserverEndIndex ?? endIndex;
|
|
201
|
+
this.inFlightObserver = null;
|
|
202
|
+
this.inFlightObserverEndIndex = null;
|
|
203
|
+
})
|
|
204
|
+
.catch((err: unknown) => {
|
|
205
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
206
|
+
if (logger) {
|
|
207
|
+
logger.warn(`Observer buffer call failed: ${message}`);
|
|
208
|
+
}
|
|
209
|
+
this.inFlightObserver = null;
|
|
210
|
+
this.inFlightObserverEndIndex = null;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Check if there are completed buffer chunks ready for activation.
|
|
216
|
+
*/
|
|
217
|
+
hasCompletedChunks(): boolean {
|
|
218
|
+
return this.chunks.length > 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Get all completed chunks and the watermark up to which messages
|
|
223
|
+
* are covered.
|
|
224
|
+
*
|
|
225
|
+
* Does NOT clear state. Call {@link commitActivation} after
|
|
226
|
+
* successfully activating.
|
|
227
|
+
*/
|
|
228
|
+
getCompletedChunks(): { chunks: ObservationChunk[]; watermark: number } {
|
|
229
|
+
return { chunks: [...this.chunks], watermark: this.bufferWatermark };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Called after successful activation to reset buffer state.
|
|
234
|
+
*
|
|
235
|
+
* Clears accumulated chunks and resets the watermark to 0 since the
|
|
236
|
+
* messages it pointed to have been removed from the conversation
|
|
237
|
+
* history.
|
|
238
|
+
*/
|
|
239
|
+
commitActivation(): void {
|
|
240
|
+
this.chunks = [];
|
|
241
|
+
this.bufferWatermark = 0;
|
|
242
|
+
this.activationEpoch++;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// -------------------------------------------------------------------------
|
|
246
|
+
// Reflector Buffering
|
|
247
|
+
// -------------------------------------------------------------------------
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Check if reflection should be triggered based on observation token count.
|
|
251
|
+
*
|
|
252
|
+
* Returns:
|
|
253
|
+
* - `'sync'` when observation tokens are at or above the effective threshold
|
|
254
|
+
* (the caller decides whether to use a buffered reflection or force a sync call)
|
|
255
|
+
* - `'async'` when observation tokens are between the buffer activation point
|
|
256
|
+
* and the threshold, and no reflector is currently in flight
|
|
257
|
+
* - `'none'` otherwise
|
|
258
|
+
*
|
|
259
|
+
* @param observationTokens - current observation slot token count
|
|
260
|
+
* @param effectiveThreshold - from computeEffectiveReflectionThreshold
|
|
261
|
+
* @param reflectionBufferActivation - fraction at which to start async reflection
|
|
262
|
+
* @returns action indicator
|
|
263
|
+
*/
|
|
264
|
+
shouldReflect(
|
|
265
|
+
observationTokens: number,
|
|
266
|
+
effectiveThreshold: number,
|
|
267
|
+
reflectionBufferActivation: number,
|
|
268
|
+
): 'none' | 'async' | 'sync' {
|
|
269
|
+
if (observationTokens >= effectiveThreshold) {
|
|
270
|
+
return 'sync';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const asyncTrigger = effectiveThreshold * reflectionBufferActivation;
|
|
274
|
+
if (
|
|
275
|
+
observationTokens >= asyncTrigger &&
|
|
276
|
+
!this.isReflectorInFlight() &&
|
|
277
|
+
!this.aborted
|
|
278
|
+
) {
|
|
279
|
+
return 'async';
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return 'none';
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Launch an async reflector call. Does NOT await it.
|
|
287
|
+
*
|
|
288
|
+
* When the reflector completes, its result is stored in
|
|
289
|
+
* `bufferedReflection` for later consumption via
|
|
290
|
+
* {@link consumeBufferedReflection}. If the coordinator has been
|
|
291
|
+
* aborted before the reflector completes, the result is discarded.
|
|
292
|
+
*
|
|
293
|
+
* @param complete - the LLM completion function
|
|
294
|
+
* @param observations - the current observation text to consolidate
|
|
295
|
+
* @param config - reflector config
|
|
296
|
+
* @param logger - optional logger for error reporting
|
|
297
|
+
*/
|
|
298
|
+
launchReflector(
|
|
299
|
+
complete: CompleteFn,
|
|
300
|
+
observations: string,
|
|
301
|
+
config: { reflectionThreshold: number; reflectorInstruction?: string },
|
|
302
|
+
logger?: { warn: (msg: string) => void },
|
|
303
|
+
): void {
|
|
304
|
+
if (this.aborted) return;
|
|
305
|
+
|
|
306
|
+
const promise = runReflector(complete, observations, config);
|
|
307
|
+
this.inFlightReflector = promise;
|
|
308
|
+
|
|
309
|
+
promise
|
|
310
|
+
.then((output: ReflectorOutput) => {
|
|
311
|
+
if (this.aborted) return;
|
|
312
|
+
|
|
313
|
+
this.bufferedReflection = output.observations;
|
|
314
|
+
this.bufferedReflectionCompressionLevel = output.compressionLevel;
|
|
315
|
+
this.inFlightReflector = null;
|
|
316
|
+
})
|
|
317
|
+
.catch((err: unknown) => {
|
|
318
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
319
|
+
if (logger) {
|
|
320
|
+
logger.warn(`Reflector buffer call failed: ${message}`);
|
|
321
|
+
}
|
|
322
|
+
this.inFlightReflector = null;
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Check if a buffered reflection is ready to swap in.
|
|
328
|
+
*/
|
|
329
|
+
hasBufferedReflection(): boolean {
|
|
330
|
+
return this.bufferedReflection !== null;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Get the buffered reflection and clear it.
|
|
335
|
+
*
|
|
336
|
+
* Returns the consolidated observations and the compression level that
|
|
337
|
+
* was applied, or null if no buffered reflection is available.
|
|
338
|
+
*/
|
|
339
|
+
consumeBufferedReflection(): {
|
|
340
|
+
observations: string;
|
|
341
|
+
compressionLevel: number;
|
|
342
|
+
} | null {
|
|
343
|
+
if (this.bufferedReflection === null) return null;
|
|
344
|
+
|
|
345
|
+
const result = {
|
|
346
|
+
observations: this.bufferedReflection,
|
|
347
|
+
compressionLevel: this.bufferedReflectionCompressionLevel,
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
this.bufferedReflection = null;
|
|
351
|
+
this.bufferedReflectionCompressionLevel = 0;
|
|
352
|
+
|
|
353
|
+
return result;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// -------------------------------------------------------------------------
|
|
357
|
+
// Lifecycle
|
|
358
|
+
// -------------------------------------------------------------------------
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Get the current state for session persistence.
|
|
362
|
+
*
|
|
363
|
+
* In-flight operations are NOT included (they are lost on session
|
|
364
|
+
* save). Only completed chunks and the watermark are persisted.
|
|
365
|
+
*/
|
|
366
|
+
getState(): { chunks: ObservationChunk[]; watermark: number } {
|
|
367
|
+
return { chunks: [...this.chunks], watermark: this.bufferWatermark };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Restore state from a previous session.
|
|
372
|
+
*/
|
|
373
|
+
restoreState(state: { chunks: ObservationChunk[]; watermark: number }): void {
|
|
374
|
+
this.chunks = [...state.chunks];
|
|
375
|
+
this.bufferWatermark = state.watermark;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Abort all in-flight operations. Called on agent destruction.
|
|
380
|
+
*
|
|
381
|
+
* Sets the aborted flag so that any in-flight promise handlers
|
|
382
|
+
* discard their results when they eventually resolve.
|
|
383
|
+
*/
|
|
384
|
+
abort(): void {
|
|
385
|
+
this.aborted = true;
|
|
386
|
+
this.inFlightObserver = null;
|
|
387
|
+
this.inFlightObserverEndIndex = null;
|
|
388
|
+
this.inFlightReflector = null;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Whether an observer call is currently in flight.
|
|
393
|
+
*/
|
|
394
|
+
isObserverInFlight(): boolean {
|
|
395
|
+
return this.inFlightObserver !== null;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Whether a reflector call is currently in flight.
|
|
400
|
+
*/
|
|
401
|
+
isReflectorInFlight(): boolean {
|
|
402
|
+
return this.inFlightReflector !== null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Get the buffer watermark (index into conversation history marking
|
|
407
|
+
* where the last completed observation ended).
|
|
408
|
+
*/
|
|
409
|
+
getWatermark(): number {
|
|
410
|
+
return this.bufferWatermark;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Set the watermark. Used during initialization or after manual
|
|
415
|
+
* adjustments to the conversation history.
|
|
416
|
+
*/
|
|
417
|
+
setWatermark(index: number): void {
|
|
418
|
+
this.bufferWatermark = index;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Advance the activation epoch. Called when a sync activation trims
|
|
423
|
+
* messages outside of the normal commitActivation() flow (e.g., the
|
|
424
|
+
* engine's Step 2 sync observer path). This invalidates any in-flight
|
|
425
|
+
* observers that were launched before the sync activation.
|
|
426
|
+
*/
|
|
427
|
+
advanceEpoch(): void {
|
|
428
|
+
this.activationEpoch++;
|
|
429
|
+
}
|
|
430
|
+
}
|