@memberjunction/ai-agents 5.48.0 → 5.49.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 +14 -0
- package/dist/AgentRunner.d.ts.map +1 -1
- package/dist/AgentRunner.js +5 -0
- package/dist/AgentRunner.js.map +1 -1
- package/dist/ConversationCompactionManager.d.ts +198 -0
- package/dist/ConversationCompactionManager.d.ts.map +1 -0
- package/dist/ConversationCompactionManager.js +384 -0
- package/dist/ConversationCompactionManager.js.map +1 -0
- package/dist/ConversationToolManager.d.ts +154 -0
- package/dist/ConversationToolManager.d.ts.map +1 -0
- package/dist/ConversationToolManager.js +336 -0
- package/dist/ConversationToolManager.js.map +1 -0
- package/dist/agent-types/loop-agent-prompt-params.d.ts +8 -0
- package/dist/agent-types/loop-agent-prompt-params.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-prompt-params.js +1 -0
- package/dist/agent-types/loop-agent-prompt-params.js.map +1 -1
- package/dist/agent-types/loop-agent-response-type.d.ts +10 -1
- package/dist/agent-types/loop-agent-response-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-response-type.js.map +1 -1
- package/dist/agent-types/loop-agent-type.d.ts +23 -34
- package/dist/agent-types/loop-agent-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-type.js +75 -2
- package/dist/agent-types/loop-agent-type.js.map +1 -1
- package/dist/base-agent.d.ts +314 -4
- package/dist/base-agent.d.ts.map +1 -1
- package/dist/base-agent.js +869 -92
- package/dist/base-agent.js.map +1 -1
- package/dist/conversation-history-format.d.ts +19 -0
- package/dist/conversation-history-format.d.ts.map +1 -0
- package/dist/conversation-history-format.js +40 -0
- package/dist/conversation-history-format.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/prior-turn-tool-result-cache.d.ts +71 -0
- package/dist/prior-turn-tool-result-cache.d.ts.map +1 -0
- package/dist/prior-turn-tool-result-cache.js +85 -0
- package/dist/prior-turn-tool-result-cache.js.map +1 -0
- package/dist/realtime/realtime-client-session-service.d.ts.map +1 -1
- package/dist/realtime/realtime-client-session-service.js +7 -3
- package/dist/realtime/realtime-client-session-service.js.map +1 -1
- package/dist/realtime/realtime-coagent-config.d.ts +41 -0
- package/dist/realtime/realtime-coagent-config.d.ts.map +1 -1
- package/dist/realtime/realtime-coagent-config.js +69 -0
- package/dist/realtime/realtime-coagent-config.js.map +1 -1
- package/dist/realtime/realtime-session-runner.d.ts +72 -1
- package/dist/realtime/realtime-session-runner.d.ts.map +1 -1
- package/dist/realtime/realtime-session-runner.js +215 -13
- package/dist/realtime/realtime-session-runner.js.map +1 -1
- package/dist/realtime/realtime-tool-broker.d.ts +7 -2
- package/dist/realtime/realtime-tool-broker.d.ts.map +1 -1
- package/dist/realtime/realtime-tool-broker.js +13 -10
- package/dist/realtime/realtime-tool-broker.js.map +1 -1
- package/dist/tool-result-format.d.ts +78 -0
- package/dist/tool-result-format.d.ts.map +1 -0
- package/dist/tool-result-format.js +46 -0
- package/dist/tool-result-format.js.map +1 -0
- package/package.json +18 -18
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Cross-turn conversation compaction manager.
|
|
3
|
+
*
|
|
4
|
+
* Implements the durable summary layer from plans/agent-conversation-compaction.md:
|
|
5
|
+
* when a conversation's assembled context window crosses the effective trigger budget,
|
|
6
|
+
* a single summary prompt folds the prior running summary plus the raw message delta
|
|
7
|
+
* into a new summary, persisted on the boundary row's
|
|
8
|
+
* `ConversationDetail.SummaryOfEarlierConversation` (with `SummaryPromptRunID` linking
|
|
9
|
+
* the producing `AIPromptRun` for cost/model audit). Subsequent runs assemble
|
|
10
|
+
* `[summary, boundary row, ...tail]` via `ConversationEngine.AssembleContextWindow`
|
|
11
|
+
* over rows from the single-sourced `ConversationEngine.LoadWindowRowsFresh` loader.
|
|
12
|
+
*
|
|
13
|
+
* This class owns the trigger math, boundary selection, prompt invocation, and the
|
|
14
|
+
* boundary-row write. BaseAgent owns run-step recording and when the check fires
|
|
15
|
+
* (pre-turn fallback / post-turn fire-and-forget).
|
|
16
|
+
*
|
|
17
|
+
* @module @memberjunction/ai-agents
|
|
18
|
+
*/
|
|
19
|
+
import { LogError, LogStatusEx, Metadata } from '@memberjunction/core';
|
|
20
|
+
import { NormalizeUUID, UUIDsEqual } from '@memberjunction/global';
|
|
21
|
+
import { ConversationEngine } from '@memberjunction/core-entities';
|
|
22
|
+
import { AIPromptParams, ExtractPromptResultText } from '@memberjunction/ai-core-plus';
|
|
23
|
+
import { FormatSequencedHistoryLine } from './conversation-history-format.js';
|
|
24
|
+
import { AIPromptRunner } from '@memberjunction/ai-prompts';
|
|
25
|
+
import { AIEngine } from '@memberjunction/aiengine';
|
|
26
|
+
/** Name of the system-default cross-turn summary prompt (seeded metadata). */
|
|
27
|
+
const CONVERSATION_SUMMARY_PROMPT_NAME = 'Conversation Summary';
|
|
28
|
+
/** Conservative context default, mirroring BaseAgent.getModelContextLimit's fallback. */
|
|
29
|
+
const DEFAULT_CONTEXT_TOKENS = 8000;
|
|
30
|
+
/**
|
|
31
|
+
* Safety values for the two percents. The AIAgentType columns are NOT NULL with DB
|
|
32
|
+
* defaults (75/30), so these only apply on typeless programmatic runs — keep in sync
|
|
33
|
+
* with the migration defaults.
|
|
34
|
+
*/
|
|
35
|
+
const DEFAULT_TRIGGER_PERCENT = 75;
|
|
36
|
+
const DEFAULT_TARGET_PERCENT = 30;
|
|
37
|
+
/** Tokens reserved for the yet-unwritten new summary when choosing the boundary. */
|
|
38
|
+
const SUMMARY_RESERVE_TOKENS = 1500;
|
|
39
|
+
/**
|
|
40
|
+
* Minimum projected token relief for a compaction pass to be worth its summary-prompt
|
|
41
|
+
* LLM call. Also exceeds a typical turn's context growth, so a degenerate budget (a
|
|
42
|
+
* trigger at or below the steady-state summary size) stops churning — without this,
|
|
43
|
+
* such a config re-fires a wasted summary call on EVERY turn while advancing the
|
|
44
|
+
* boundary one message at a time (observed live during contour testing).
|
|
45
|
+
*/
|
|
46
|
+
const MIN_COMPACTION_GAIN_TOKENS = 500;
|
|
47
|
+
/** Below this many raw messages a compaction pass is not worth a prompt run. */
|
|
48
|
+
const MIN_MESSAGES_TO_COMPACT = 4;
|
|
49
|
+
/** Per-message character cap when rendering the delta for the summary prompt. */
|
|
50
|
+
const MAX_DELTA_MESSAGE_CHARS = 10_000;
|
|
51
|
+
/**
|
|
52
|
+
* Owns cross-turn (Tier A) conversation compaction: budget resolution, trigger math,
|
|
53
|
+
* boundary selection, the summary prompt run, and the durable boundary-row write.
|
|
54
|
+
* Stateless aside from an in-process re-entrancy guard; all entry points are static.
|
|
55
|
+
*/
|
|
56
|
+
export class ConversationCompactionManager {
|
|
57
|
+
/** Conversations with a compaction currently in flight in this process (re-entrancy guard). */
|
|
58
|
+
static { this.inFlightConversations = new Set(); }
|
|
59
|
+
/** Conversations already warned (once per process) about an unsatisfiable trigger budget. */
|
|
60
|
+
static { this.warnedConversationBudgets = new Set(); }
|
|
61
|
+
/** Agents already warned (once per process) about an inverted target/trigger percent pair. */
|
|
62
|
+
static { this.warnedAgentPercentConfigs = new Set(); }
|
|
63
|
+
/**
|
|
64
|
+
* Resolves the effective context budget for a run.
|
|
65
|
+
*
|
|
66
|
+
* MaxTokens: `Agent.ContextWindowMaxTokens || AgentType.ContextWindowMaxTokens ||
|
|
67
|
+
* modelMaxInputTokens || 8000`, then clamped to `modelMaxInputTokens` when the
|
|
68
|
+
* configured value exceeds it (never silently exceed the model — the clamp is
|
|
69
|
+
* surfaced via `ClampedToModel` for the caller to log). Percents:
|
|
70
|
+
* `Agent || AgentType || framework default` (`||` by design — zero is not a valid
|
|
71
|
+
* value for any of these knobs, and NULL means inherit).
|
|
72
|
+
*
|
|
73
|
+
* @param agent - The executing agent
|
|
74
|
+
* @param agentType - The agent's type (null for typeless programmatic runs)
|
|
75
|
+
* @param modelMaxInputTokens - The selected model's MaxInputTokens when known, else null
|
|
76
|
+
*/
|
|
77
|
+
static ResolveEffectiveBudget(agent, agentType, modelMaxInputTokens) {
|
|
78
|
+
let maxTokens;
|
|
79
|
+
let boundedBy;
|
|
80
|
+
if (agent.ContextWindowMaxTokens) {
|
|
81
|
+
maxTokens = agent.ContextWindowMaxTokens;
|
|
82
|
+
boundedBy = 'Agent';
|
|
83
|
+
}
|
|
84
|
+
else if (agentType?.ContextWindowMaxTokens) {
|
|
85
|
+
maxTokens = agentType.ContextWindowMaxTokens;
|
|
86
|
+
boundedBy = 'AgentType';
|
|
87
|
+
}
|
|
88
|
+
else if (modelMaxInputTokens) {
|
|
89
|
+
maxTokens = modelMaxInputTokens;
|
|
90
|
+
boundedBy = 'Model';
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
maxTokens = DEFAULT_CONTEXT_TOKENS;
|
|
94
|
+
boundedBy = 'Default';
|
|
95
|
+
}
|
|
96
|
+
let clampedToModel = false;
|
|
97
|
+
if (modelMaxInputTokens && maxTokens > modelMaxInputTokens) {
|
|
98
|
+
maxTokens = modelMaxInputTokens;
|
|
99
|
+
boundedBy = 'Model';
|
|
100
|
+
clampedToModel = true;
|
|
101
|
+
}
|
|
102
|
+
// Defense-in-depth clamps mirroring the DB CHECK constraints (percents 1-100,
|
|
103
|
+
// target strictly under trigger). The DB enforces these on AIAgentType (both
|
|
104
|
+
// NOT NULL there); AIAgent's nullable inherit semantics mean the EFFECTIVE pair
|
|
105
|
+
// can only be validated here, after resolution — an inverted pair would make
|
|
106
|
+
// every pass select a no-progress boundary and churn.
|
|
107
|
+
const rawTriggerPercent = agent.CompactionTriggerPercent || agentType?.CompactionTriggerPercent || DEFAULT_TRIGGER_PERCENT;
|
|
108
|
+
const rawTargetPercent = agent.CompactionTargetPercent || agentType?.CompactionTargetPercent || DEFAULT_TARGET_PERCENT;
|
|
109
|
+
const triggerPercent = Math.min(Math.max(rawTriggerPercent, 1), 100);
|
|
110
|
+
let targetPercent = Math.min(Math.max(rawTargetPercent, 1), 100);
|
|
111
|
+
if (targetPercent >= triggerPercent) {
|
|
112
|
+
targetPercent = Math.max(1, triggerPercent - 1);
|
|
113
|
+
// Warn once per agent per process — this re-resolves every turn while the
|
|
114
|
+
// config stays wrong, and the operator only needs telling once (§8: don't
|
|
115
|
+
// spam logs, don't lose the diagnostic either).
|
|
116
|
+
const agentKey = NormalizeUUID(agent.ID);
|
|
117
|
+
if (!this.warnedAgentPercentConfigs.has(agentKey)) {
|
|
118
|
+
this.warnedAgentPercentConfigs.add(agentKey);
|
|
119
|
+
LogStatusEx({ message: `⚠️ [CrossTurnCompaction] CompactionTargetPercent (${rawTargetPercent}) >= CompactionTriggerPercent (${rawTriggerPercent}) for agent '${agent.Name}' — clamped target to ${targetPercent}. Fix the agent/type configuration.` });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
MaxTokens: maxTokens,
|
|
124
|
+
TriggerTokens: Math.floor(maxTokens * triggerPercent / 100),
|
|
125
|
+
TargetTokens: Math.floor(maxTokens * targetPercent / 100),
|
|
126
|
+
ClampedToModel: clampedToModel,
|
|
127
|
+
BoundedBy: boundedBy
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Checks the conversation's assembled window against the trigger budget and, when
|
|
132
|
+
* crossed, runs the summary prompt (input = prior summary + raw delta only — the
|
|
133
|
+
* recursive pattern) and persists the result on the boundary row. Never throws for
|
|
134
|
+
* business failures — returns an outcome with `ErrorMessage` and leaves the
|
|
135
|
+
* conversation untouched, so a failed compaction simply re-triggers next turn.
|
|
136
|
+
*/
|
|
137
|
+
static async CompactIfNeeded(input) {
|
|
138
|
+
const conversationKey = NormalizeUUID(input.ConversationId);
|
|
139
|
+
if (this.inFlightConversations.has(conversationKey)) {
|
|
140
|
+
return { Fired: false, SkippedReason: 'Compaction already in flight for this conversation', TokensBefore: 0, Warnings: [] };
|
|
141
|
+
}
|
|
142
|
+
this.inFlightConversations.add(conversationKey);
|
|
143
|
+
try {
|
|
144
|
+
return await this.compactIfNeededInternal(input);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
148
|
+
LogError(`Cross-turn compaction failed for conversation ${input.ConversationId}: ${message}`);
|
|
149
|
+
return { Fired: false, TokensBefore: 0, Warnings: [], ErrorMessage: message };
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
this.inFlightConversations.delete(conversationKey);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/** Core pass: window → trigger check → boundary + worth-it guards → prompt → boundary-row write. */
|
|
156
|
+
static async compactIfNeededInternal(input) {
|
|
157
|
+
const warnings = [];
|
|
158
|
+
if (input.Budget.ClampedToModel) {
|
|
159
|
+
warnings.push(`Configured ContextWindowMaxTokens exceeded the model's MaxInputTokens — clamped to ${input.Budget.MaxTokens}`);
|
|
160
|
+
}
|
|
161
|
+
const window = await this.loadWindowFresh(input);
|
|
162
|
+
const tokensBefore = input.EstimateTokens(window);
|
|
163
|
+
if (tokensBefore < input.Budget.TriggerTokens) {
|
|
164
|
+
this.logVerbose(input, `No compaction needed: ~${tokensBefore} tokens < trigger ${input.Budget.TriggerTokens}`);
|
|
165
|
+
return { Fired: false, SkippedReason: `Window ~${tokensBefore} tokens is under the ${input.Budget.TriggerTokens}-token trigger`, TokensBefore: tokensBefore, Warnings: warnings };
|
|
166
|
+
}
|
|
167
|
+
const { priorSummary, rawMessages } = this.splitWindow(window);
|
|
168
|
+
this.warnOnceIfBudgetUnsatisfiable(input, window, priorSummary, warnings);
|
|
169
|
+
const boundary = this.chooseBoundary(rawMessages, tokensBefore, input);
|
|
170
|
+
if ('skippedReason' in boundary) {
|
|
171
|
+
return { Fired: false, SkippedReason: boundary.skippedReason, TokensBefore: tokensBefore, Warnings: warnings };
|
|
172
|
+
}
|
|
173
|
+
const promptResult = await this.runSummaryPrompt(input, priorSummary, rawMessages.slice(0, boundary.index));
|
|
174
|
+
if (!promptResult.success) {
|
|
175
|
+
return { Fired: false, TokensBefore: tokensBefore, Warnings: warnings, ErrorMessage: promptResult.errorMessage, PromptId: promptResult.promptId };
|
|
176
|
+
}
|
|
177
|
+
const boundaryMessage = rawMessages[boundary.index];
|
|
178
|
+
await this.writeBoundaryRow(input, boundaryMessage.metadata.conversationDetailId, promptResult.summaryText, promptResult.promptRunId);
|
|
179
|
+
const tokensAfter = input.EstimateTokens([
|
|
180
|
+
{ role: 'user', content: promptResult.summaryText },
|
|
181
|
+
...rawMessages.slice(boundary.index)
|
|
182
|
+
]);
|
|
183
|
+
this.logVerbose(input, `Compacted at sequence ${boundaryMessage.metadata.sequence}: ~${tokensBefore} → ~${tokensAfter} tokens`);
|
|
184
|
+
return {
|
|
185
|
+
Fired: true,
|
|
186
|
+
BoundarySequence: boundaryMessage.metadata.sequence,
|
|
187
|
+
PromptId: promptResult.promptId,
|
|
188
|
+
PromptRunId: promptResult.promptRunId,
|
|
189
|
+
PromptRun: promptResult.promptRun,
|
|
190
|
+
TokensBefore: tokensBefore,
|
|
191
|
+
TokensAfter: tokensAfter,
|
|
192
|
+
SummaryText: promptResult.summaryText,
|
|
193
|
+
Warnings: warnings
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Loads the conversation window FRESH for this pass — via the single-sourced
|
|
198
|
+
* {@link ConversationEngine.LoadWindowRowsFresh} query (entity RLS via ContextUser,
|
|
199
|
+
* provider-scoped) assembled through the engine's pure fold — never the engine's
|
|
200
|
+
* process-global detail cache. Server-side compaction must see rows written by
|
|
201
|
+
* other servers (a warm cache can be stale across nodes) and must not grow an
|
|
202
|
+
* unbounded per-conversation cache in MJAPI. Honors `input.ExcludeDetailIds` (the
|
|
203
|
+
* in-flight placeholder row). Load failures throw — caught by
|
|
204
|
+
* {@link CompactIfNeeded}'s wrapper → ErrorMessage outcome, so the conversation is
|
|
205
|
+
* left untouched and the pass re-triggers later.
|
|
206
|
+
*/
|
|
207
|
+
static async loadWindowFresh(input) {
|
|
208
|
+
const rows = await ConversationEngine.LoadWindowRowsFresh(input.ConversationId, input.ContextUser, input.Provider || Metadata.Provider // global-provider-ok: caller-supplied provider preferred; global is the documented last-resort fallback
|
|
209
|
+
);
|
|
210
|
+
return ConversationEngine.AssembleContextWindow(rows, {
|
|
211
|
+
excludeDetailIds: input.ExcludeDetailIds
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Splits the assembled window into the leading running summary (when the engine
|
|
216
|
+
* placed one at index 0) and the raw sequence-addressable messages eligible for
|
|
217
|
+
* folding (summary rows and non-addressable placeholders are excluded).
|
|
218
|
+
*/
|
|
219
|
+
static splitWindow(window) {
|
|
220
|
+
const priorSummary = window.length > 0 && window[0].metadata?.isConversationSummary
|
|
221
|
+
? (typeof window[0].content === 'string' ? window[0].content : '')
|
|
222
|
+
: '';
|
|
223
|
+
const rawMessages = window.filter(m => !m.metadata?.isConversationSummary
|
|
224
|
+
&& m.metadata?.sequence !== undefined
|
|
225
|
+
&& !!m.metadata?.conversationDetailId);
|
|
226
|
+
return { priorSummary, rawMessages };
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Unsatisfiable-budget warning: when the prior summary ALONE meets the trigger, no
|
|
230
|
+
* amount of folding can bring the window back under it — the operator needs a bigger
|
|
231
|
+
* ContextWindowMaxTokens or a higher CompactionTriggerPercent. Warns once per
|
|
232
|
+
* conversation per process; the guard skip paths in {@link chooseBoundary} stop the
|
|
233
|
+
* per-turn churn.
|
|
234
|
+
*/
|
|
235
|
+
static warnOnceIfBudgetUnsatisfiable(input, window, priorSummary, warnings) {
|
|
236
|
+
const conversationKey = NormalizeUUID(input.ConversationId);
|
|
237
|
+
if (priorSummary.length === 0
|
|
238
|
+
|| input.EstimateTokens([window[0]]) < input.Budget.TriggerTokens
|
|
239
|
+
|| this.warnedConversationBudgets.has(conversationKey)) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
this.warnedConversationBudgets.add(conversationKey);
|
|
243
|
+
const budgetWarning = `Compaction trigger (~${input.Budget.TriggerTokens} tokens) is at or below the running summary's size — the window can never get under the trigger. Raise ContextWindowMaxTokens or CompactionTriggerPercent for this agent/type.`;
|
|
244
|
+
warnings.push(budgetWarning);
|
|
245
|
+
LogStatusEx({ message: `⚠️ [CrossTurnCompaction] Conversation ${input.ConversationId}: ${budgetWarning}` });
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Selects the fold boundary and applies the worth-it guards. Returns the boundary
|
|
249
|
+
* index into `rawMessages`, or a skip reason when there is no foldable boundary
|
|
250
|
+
* (too few addressable messages, or the window already fits the target tail —
|
|
251
|
+
* possible only with inverted trigger/target configs) or when the projected token
|
|
252
|
+
* relief is under {@link MIN_COMPACTION_GAIN_TOKENS} (not worth a summary-prompt
|
|
253
|
+
* LLM call).
|
|
254
|
+
*/
|
|
255
|
+
static chooseBoundary(rawMessages, tokensBefore, input) {
|
|
256
|
+
const boundaryIndex = this.selectBoundaryIndex(rawMessages, input);
|
|
257
|
+
if (boundaryIndex === null) {
|
|
258
|
+
this.logVerbose(input, `Over trigger (~${tokensBefore} tokens) but no foldable boundary — ${rawMessages.length} addressable messages, target tail ${input.Budget.TargetTokens - SUMMARY_RESERVE_TOKENS} tokens`);
|
|
259
|
+
return { skippedReason: 'No foldable boundary (too few addressable messages, or the window already fits the target tail)' };
|
|
260
|
+
}
|
|
261
|
+
// Minimum-gain guard: projected post-compaction size = summary reserve + retained tail.
|
|
262
|
+
const projectedTokensAfter = SUMMARY_RESERVE_TOKENS + input.EstimateTokens(rawMessages.slice(boundaryIndex));
|
|
263
|
+
const projectedGain = tokensBefore - projectedTokensAfter;
|
|
264
|
+
if (projectedGain < MIN_COMPACTION_GAIN_TOKENS) {
|
|
265
|
+
this.logVerbose(input, `Skipping: projected gain ~${projectedGain} tokens < minimum ${MIN_COMPACTION_GAIN_TOKENS}`);
|
|
266
|
+
return { skippedReason: `Projected gain (~${projectedGain} tokens) is under the ${MIN_COMPACTION_GAIN_TOKENS}-token minimum — not worth a summary prompt run` };
|
|
267
|
+
}
|
|
268
|
+
return { index: boundaryIndex };
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Picks the boundary index within the raw (non-summary, sequence-addressable)
|
|
272
|
+
* messages: the earliest message whose tail fits inside
|
|
273
|
+
* `TargetTokens - SUMMARY_RESERVE_TOKENS`, walking backward from the newest.
|
|
274
|
+
* Returns null when there is nothing worthwhile to fold (window too small or the
|
|
275
|
+
* boundary would sit at the first raw message, folding nothing).
|
|
276
|
+
*/
|
|
277
|
+
static selectBoundaryIndex(rawMessages, input) {
|
|
278
|
+
if (rawMessages.length < MIN_MESSAGES_TO_COMPACT) {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
const tailBudget = input.Budget.TargetTokens - SUMMARY_RESERVE_TOKENS;
|
|
282
|
+
let accumulated = 0;
|
|
283
|
+
let boundaryIndex = rawMessages.length - 1;
|
|
284
|
+
for (let i = rawMessages.length - 1; i >= 0; i--) {
|
|
285
|
+
const messageTokens = input.EstimateTokens([rawMessages[i]]);
|
|
286
|
+
if (accumulated + messageTokens > tailBudget) {
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
accumulated += messageTokens;
|
|
290
|
+
boundaryIndex = i;
|
|
291
|
+
}
|
|
292
|
+
// The newest raw row must never be the boundary (possible when it alone exceeds
|
|
293
|
+
// the tail budget — the walk breaks on iteration one leaving boundaryIndex at
|
|
294
|
+
// length-1): it may be an in-flight row whose Message a resolver is still
|
|
295
|
+
// writing, and the boundary write's Load→Save would race it. Defense-in-depth
|
|
296
|
+
// alongside ExcludeDetailIds; the retained tail may then exceed TargetTokens
|
|
297
|
+
// (correctness over compression — the next turn simply re-triggers).
|
|
298
|
+
// MIN_MESSAGES_TO_COMPACT >= 4 guarantees length-2 >= 2, so the clamp never
|
|
299
|
+
// nulls a previously valid selection.
|
|
300
|
+
const clamped = Math.min(boundaryIndex, rawMessages.length - 2);
|
|
301
|
+
// boundaryIndex 0 would fold nothing (delta empty) — no progress, skip.
|
|
302
|
+
return clamped > 0 ? clamped : null;
|
|
303
|
+
}
|
|
304
|
+
/** Renders the delta and runs the summary prompt. */
|
|
305
|
+
static async runSummaryPrompt(input, priorSummary, delta) {
|
|
306
|
+
const prompt = this.resolveSummaryPrompt(input);
|
|
307
|
+
if (!prompt) {
|
|
308
|
+
return { success: false, summaryText: '', errorMessage: `No conversation summary prompt found (agent/type ConversationSummaryPromptID unset and no '${CONVERSATION_SUMMARY_PROMPT_NAME}' system prompt)` };
|
|
309
|
+
}
|
|
310
|
+
const promptParams = new AIPromptParams();
|
|
311
|
+
promptParams.prompt = prompt;
|
|
312
|
+
// Keys are the conversation-summary.template.md contract ({{ priorSummary }}, {{ deltaMessages }})
|
|
313
|
+
promptParams.data = {
|
|
314
|
+
priorSummary: priorSummary || '(none — this is the first compaction of this conversation)',
|
|
315
|
+
deltaMessages: delta.map(m => this.renderDeltaMessage(m)).join('\n')
|
|
316
|
+
};
|
|
317
|
+
promptParams.contextUser = input.ContextUser;
|
|
318
|
+
if (input.AgentRunId) {
|
|
319
|
+
// Links the summary AIPromptRun to the agent run (AgentRunID) so per-run
|
|
320
|
+
// cost rollups include the recursive summary sub-call.
|
|
321
|
+
promptParams.agentRunId = input.AgentRunId;
|
|
322
|
+
}
|
|
323
|
+
if (input.OnPromptRunCreated) {
|
|
324
|
+
promptParams.onPromptRunCreated = input.OnPromptRunCreated;
|
|
325
|
+
}
|
|
326
|
+
const runner = new AIPromptRunner();
|
|
327
|
+
const result = await runner.ExecutePrompt(promptParams);
|
|
328
|
+
const summaryText = ExtractPromptResultText(result);
|
|
329
|
+
if (!result.success || summaryText.length === 0) {
|
|
330
|
+
return { success: false, summaryText: '', promptId: prompt.ID, promptRunId: result.promptRun?.ID, errorMessage: result.errorMessage || 'Summary prompt returned no content' };
|
|
331
|
+
}
|
|
332
|
+
return { success: true, summaryText, promptId: prompt.ID, promptRunId: result.promptRun?.ID, promptRun: result.promptRun };
|
|
333
|
+
}
|
|
334
|
+
/** Prompt resolution: agent override, else type override, else the seeded system prompt by name. */
|
|
335
|
+
static resolveSummaryPrompt(input) {
|
|
336
|
+
const overrideId = input.Agent.ConversationSummaryPromptID || input.AgentType?.ConversationSummaryPromptID;
|
|
337
|
+
if (overrideId) {
|
|
338
|
+
const overridePrompt = AIEngine.Instance.Prompts.find(p => UUIDsEqual(p.ID, overrideId));
|
|
339
|
+
if (overridePrompt) {
|
|
340
|
+
return overridePrompt;
|
|
341
|
+
}
|
|
342
|
+
LogError(`ConversationSummaryPromptID ${overrideId} not found in AIEngine prompt cache — falling back to the system prompt`);
|
|
343
|
+
}
|
|
344
|
+
// Trimmed, case-insensitive compare (the AIPromptRunner 'Repair JSON' lookup style)
|
|
345
|
+
// so cosmetic re-casing of the seeded prompt's display name can't disable compaction.
|
|
346
|
+
const targetName = CONVERSATION_SUMMARY_PROMPT_NAME.toLowerCase();
|
|
347
|
+
return AIEngine.Instance.Prompts.find(p => p.Name.trim().toLowerCase() === targetName);
|
|
348
|
+
}
|
|
349
|
+
/** One delta line (shared `[seq N] Role: text` shape) with a per-message size cap. */
|
|
350
|
+
static renderDeltaMessage(message) {
|
|
351
|
+
const text = typeof message.content === 'string' ? message.content : JSON.stringify(message.content);
|
|
352
|
+
const capped = text.length > MAX_DELTA_MESSAGE_CHARS
|
|
353
|
+
? `${text.slice(0, MAX_DELTA_MESSAGE_CHARS)}\n[truncated — ${text.length.toLocaleString()} chars total]`
|
|
354
|
+
: text;
|
|
355
|
+
return FormatSequencedHistoryLine(message.metadata?.sequence, message.role, capped);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Persists the summary on the boundary row. Written through a plain entity Save (the
|
|
359
|
+
* same external-save path everything else uses) so ConversationEngine's entity-event
|
|
360
|
+
* handler merges it into any warm cache in place — never through engine mutation
|
|
361
|
+
* helpers, whose self-mutation guard would skip the merge.
|
|
362
|
+
*/
|
|
363
|
+
static async writeBoundaryRow(input, boundaryDetailId, summaryText, promptRunId) {
|
|
364
|
+
// Favor the caller-supplied provider; the global is the explicit last resort
|
|
365
|
+
const provider = input.Provider || Metadata.Provider; // global-provider-ok: caller-supplied provider preferred; global is the documented last-resort fallback
|
|
366
|
+
const detail = await provider.GetEntityObject('MJ: Conversation Details', input.ContextUser);
|
|
367
|
+
if (!(await detail.Load(boundaryDetailId))) {
|
|
368
|
+
throw new Error(`Failed to load boundary ConversationDetail ${boundaryDetailId}`);
|
|
369
|
+
}
|
|
370
|
+
detail.SummaryOfEarlierConversation = summaryText;
|
|
371
|
+
detail.SummaryPromptRunID = promptRunId || null;
|
|
372
|
+
if (!(await detail.Save())) {
|
|
373
|
+
throw new Error(`Boundary-row save failed: ${detail.LatestResult?.CompleteMessage || 'unknown error'}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
static logVerbose(input, message) {
|
|
377
|
+
LogStatusEx({
|
|
378
|
+
message: `[CrossTurnCompaction] ${message}`,
|
|
379
|
+
verboseOnly: true,
|
|
380
|
+
isVerboseEnabled: () => input.Verbose === true
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
//# sourceMappingURL=ConversationCompactionManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConversationCompactionManager.js","sourceRoot":"","sources":["../src/ConversationCompactionManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Conversation-history retrieval tools (RLM-style addressable paging).
|
|
3
|
+
*
|
|
4
|
+
* The cross-turn summary (see ConversationCompactionManager) is deliberately lossy — a
|
|
5
|
+
* compact handle over history that has left the context window. These tools are the
|
|
6
|
+
* other half of the design (plans/agent-conversation-compaction.md §5): the full
|
|
7
|
+
* history stays in an addressable external store (ConversationDetail rows keyed by the
|
|
8
|
+
* persisted per-conversation `Sequence`), and the agent pages exact slices back in on
|
|
9
|
+
* demand instead of trusting the summary. Zero turn cost — calls ride the
|
|
10
|
+
* `conversationToolCalls` inline response field, mirroring `artifactToolCalls`.
|
|
11
|
+
*
|
|
12
|
+
* All reads are served from `ConversationEngine`'s per-conversation cache (one query on
|
|
13
|
+
* a cold miss, in-memory afterward).
|
|
14
|
+
*
|
|
15
|
+
* @module @memberjunction/ai-agents
|
|
16
|
+
*/
|
|
17
|
+
import { UserInfo } from '@memberjunction/core';
|
|
18
|
+
/**
|
|
19
|
+
* The conversation-tool name vocabulary — single source of truth. The LLM is taught these
|
|
20
|
+
* exact strings (GetToolDocumentation + the loop-agent system template's response-type
|
|
21
|
+
* union), the dispatch switch matches them, and framework guidance messages enumerate them.
|
|
22
|
+
* Add/rename tools HERE; everything else derives from this array.
|
|
23
|
+
*/
|
|
24
|
+
export declare const ConversationToolNames: readonly ["getMessageBySequence", "getMessagesByRange", "searchConversation", "summarizeRange"];
|
|
25
|
+
/** Tool names available on `conversationToolCalls` (derived from {@link ConversationToolNames}). */
|
|
26
|
+
export type ConversationToolName = typeof ConversationToolNames[number];
|
|
27
|
+
/**
|
|
28
|
+
* The seam through which `summarizeRange` runs its recursive LLM sub-call. Implemented by
|
|
29
|
+
* BaseAgent (which owns prompt execution and run-step observability) so this manager stays
|
|
30
|
+
* free of prompt-runner dependencies and trivially testable.
|
|
31
|
+
*/
|
|
32
|
+
export interface ConversationToolSummaryHost {
|
|
33
|
+
/**
|
|
34
|
+
* Runs the range-summary prompt over the rendered slice.
|
|
35
|
+
* @param rangeText - The `[seq N] Role: text` rendering of the requested range
|
|
36
|
+
* @param lens - The task-specific focus the summary must serve
|
|
37
|
+
* @returns The summary text plus the AIPromptRun ID of the sub-call (for lineage)
|
|
38
|
+
*/
|
|
39
|
+
RunSummaryPrompt(rangeText: string, lens: string): Promise<{
|
|
40
|
+
text: string;
|
|
41
|
+
promptRunId?: string;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* One conversation-tool invocation from the LLM. A single input shape with per-tool
|
|
46
|
+
* validation (mirrors the artifact tools' generic `input` while staying typed).
|
|
47
|
+
*/
|
|
48
|
+
export interface ConversationToolCall {
|
|
49
|
+
tool: ConversationToolName;
|
|
50
|
+
input: {
|
|
51
|
+
/** getMessageBySequence: the exact Sequence to fetch */
|
|
52
|
+
sequence?: number;
|
|
53
|
+
/** getMessagesByRange / searchConversation: inclusive range start */
|
|
54
|
+
startSequence?: number;
|
|
55
|
+
/** getMessagesByRange / searchConversation: inclusive range end */
|
|
56
|
+
endSequence?: number;
|
|
57
|
+
/** searchConversation: keyword (default) or regex pattern */
|
|
58
|
+
query?: string;
|
|
59
|
+
/** searchConversation: treat `query` as a regular expression */
|
|
60
|
+
isRegex?: boolean;
|
|
61
|
+
/** searchConversation: restrict to a role ('User' | 'AI') */
|
|
62
|
+
role?: string;
|
|
63
|
+
/** searchConversation: result cap (default 20, max 50) */
|
|
64
|
+
maxResults?: number;
|
|
65
|
+
/** summarizeRange: the task-specific focus the summary must serve */
|
|
66
|
+
lens?: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** A message as returned by the paging tools. */
|
|
70
|
+
export interface ConversationToolMessage {
|
|
71
|
+
sequence: number;
|
|
72
|
+
role: string;
|
|
73
|
+
/** Responding agent's name when the row was produced by an agent */
|
|
74
|
+
agent: string | null;
|
|
75
|
+
message: string;
|
|
76
|
+
}
|
|
77
|
+
/** One search hit: enough to decide whether to page the full message in. */
|
|
78
|
+
export interface ConversationSearchHit {
|
|
79
|
+
sequence: number;
|
|
80
|
+
role: string;
|
|
81
|
+
agent: string | null;
|
|
82
|
+
snippet: string;
|
|
83
|
+
matchType: 'keyword' | 'regex';
|
|
84
|
+
}
|
|
85
|
+
/** Execution record for one tool call (step OutputData + next-turn result message). */
|
|
86
|
+
export interface ConversationToolExecutionResult {
|
|
87
|
+
tool: ConversationToolName;
|
|
88
|
+
input: ConversationToolCall['input'];
|
|
89
|
+
result: {
|
|
90
|
+
success: boolean;
|
|
91
|
+
data?: unknown;
|
|
92
|
+
errorMessage?: string;
|
|
93
|
+
};
|
|
94
|
+
durationMs: number;
|
|
95
|
+
/** summarizeRange only: the AIPromptRun of the recursive sub-call (step TargetLogID lineage) */
|
|
96
|
+
promptRunId?: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Max conversationToolCalls executed per LLM response. Each call is a run step (and
|
|
100
|
+
* summarizeRange is a full LLM sub-call) — an unbounded Promise.all would let a single
|
|
101
|
+
* response fan out arbitrary work/cost. Excess calls are reported back to the model as
|
|
102
|
+
* skipped so it can re-request them next turn. Consumed by BaseAgent's executor.
|
|
103
|
+
*/
|
|
104
|
+
export declare const MAX_CONVERSATION_TOOL_CALLS_PER_TURN = 8;
|
|
105
|
+
/**
|
|
106
|
+
* Per-run manager for conversation-history retrieval tools. Initialized with the run's
|
|
107
|
+
* conversationId (the availability gate — no conversation, no tools) and serving all
|
|
108
|
+
* reads from the ConversationEngine cache.
|
|
109
|
+
*/
|
|
110
|
+
export declare class ConversationToolManager {
|
|
111
|
+
private conversationId;
|
|
112
|
+
private contextUser;
|
|
113
|
+
private summaryHost;
|
|
114
|
+
/** Arms the manager for a run. Pass null conversationId to disable (programmatic runs). */
|
|
115
|
+
Initialize(conversationId: string | null, contextUser: UserInfo): void;
|
|
116
|
+
/** Wires the recursive-sub-call seam (BaseAgent owns prompt execution). */
|
|
117
|
+
SetSummaryHost(host: ConversationToolSummaryHost | null): void;
|
|
118
|
+
/** Disarms the manager (per-run reset). */
|
|
119
|
+
Clear(): void;
|
|
120
|
+
/** True when the run has a conversation to page against. */
|
|
121
|
+
get IsAvailable(): boolean;
|
|
122
|
+
/** Executes one tool call with timing + full error containment. */
|
|
123
|
+
ExecuteSingleToolCall(call: ConversationToolCall): Promise<ConversationToolExecutionResult>;
|
|
124
|
+
/**
|
|
125
|
+
* Markdown tool documentation injected into the system prompt (the
|
|
126
|
+
* `{{ _CONVERSATION_TOOLS }}` template block), rendered only when the run has a
|
|
127
|
+
* conversation.
|
|
128
|
+
*/
|
|
129
|
+
GetToolDocumentation(): string;
|
|
130
|
+
/** Routes a call to its tool implementation. */
|
|
131
|
+
private dispatch;
|
|
132
|
+
/**
|
|
133
|
+
* The recursive sub-call (RLM pattern): renders the requested slice and hands it to
|
|
134
|
+
* the host's summary prompt with the caller's lens. The agent is not limited to the
|
|
135
|
+
* one baseline summary's framing — any window can be re-summarized however the
|
|
136
|
+
* current task needs, on a cheap sub-call model.
|
|
137
|
+
*/
|
|
138
|
+
private summarizeRange;
|
|
139
|
+
/** `[seq N] Role: text` rendering (shared shape) with a total input budget for the sub-call model. */
|
|
140
|
+
private renderRangeForSummary;
|
|
141
|
+
/** Full conversation history, ordered by Sequence, from the engine cache. */
|
|
142
|
+
private loadOrderedDetails;
|
|
143
|
+
private getMessageBySequence;
|
|
144
|
+
private getMessagesByRange;
|
|
145
|
+
private searchConversation;
|
|
146
|
+
/** Compiles the query as a case-insensitive regex in regex mode; null in keyword mode. */
|
|
147
|
+
private compileSearchRegex;
|
|
148
|
+
/** Role and sequence-range filters applied to every candidate message in a search. */
|
|
149
|
+
private passesSearchFilters;
|
|
150
|
+
/** A ~300-char window centered on the match, with ellipses at cut edges. */
|
|
151
|
+
private buildSnippet;
|
|
152
|
+
private toToolMessage;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=ConversationToolManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConversationToolManager.d.ts","sourceRoot":"","sources":["../src/ConversationToolManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAIhD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,iGAAkG,CAAC;AAErI,oGAAoG;AACpG,MAAM,MAAM,oBAAoB,GAAG,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAExE;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IACxC;;;;;OAKG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtG;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE;QACH,wDAAwD;QACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,qEAAqE;QACrE,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,mEAAmE;QACnE,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,6DAA6D;QAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,gEAAgE;QAChE,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,6DAA6D;QAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,0DAA0D;QAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,qEAAqE;QACrE,IAAI,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACL;AAED,iDAAiD;AACjD,MAAM,WAAW,uBAAuB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,4EAA4E;AAC5E,MAAM,WAAW,qBAAqB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC;CAClC;AAED,uFAAuF;AACvF,MAAM,WAAW,+BAA+B;IAC5C,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,EAAE;QACJ,OAAO,EAAE,OAAO,CAAC;QACjB,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAmCD;;;;;GAKG;AACH,eAAO,MAAM,oCAAoC,IAAI,CAAC;AAEtD;;;;GAIG;AACH,qBAAa,uBAAuB;IAChC,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,WAAW,CAA4C;IAE/D,2FAA2F;IACpF,UAAU,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,QAAQ,GAAG,IAAI;IAK7E,2EAA2E;IACpE,cAAc,CAAC,IAAI,EAAE,2BAA2B,GAAG,IAAI,GAAG,IAAI;IAIrE,2CAA2C;IACpC,KAAK,IAAI,IAAI;IAMpB,4DAA4D;IAC5D,IAAW,WAAW,IAAI,OAAO,CAEhC;IAED,mEAAmE;IACtD,qBAAqB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,+BAA+B,CAAC;IAWxG;;;;OAIG;IACI,oBAAoB,IAAI,MAAM;IAmBrC,gDAAgD;YAClC,QAAQ;IAgBtB;;;;;OAKG;YACW,cAAc;IAoC5B,sGAAsG;IACtG,OAAO,CAAC,qBAAqB;IAe7B,6EAA6E;YAC/D,kBAAkB;IAQhC,OAAO,CAAC,oBAAoB;IAW5B,OAAO,CAAC,kBAAkB;IA2B1B,OAAO,CAAC,kBAAkB;IAqD1B,0FAA0F;IAC1F,OAAO,CAAC,kBAAkB;IAc1B,sFAAsF;IACtF,OAAO,CAAC,mBAAmB;IAQ3B,4EAA4E;IAC5E,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,aAAa;CAQxB"}
|