ai-memory-layer 2.0.1 → 3.0.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/CHANGELOG.md +19 -12
- package/README.md +435 -320
- package/bin/memory-server.mjs +0 -0
- package/dist/adapters/memory/embeddings.d.ts.map +1 -1
- package/dist/adapters/memory/embeddings.js +12 -1
- package/dist/adapters/memory/embeddings.js.map +1 -1
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +1281 -48
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/postgres/index.d.ts +1 -0
- package/dist/adapters/postgres/index.d.ts.map +1 -1
- package/dist/adapters/postgres/index.js +1770 -42
- package/dist/adapters/postgres/index.js.map +1 -1
- package/dist/adapters/sqlite/embeddings.d.ts.map +1 -1
- package/dist/adapters/sqlite/embeddings.js +49 -12
- package/dist/adapters/sqlite/embeddings.js.map +1 -1
- package/dist/adapters/sqlite/index.d.ts.map +1 -1
- package/dist/adapters/sqlite/index.js +1720 -38
- package/dist/adapters/sqlite/index.js.map +1 -1
- package/dist/adapters/sqlite/mappers.d.ts +39 -4
- package/dist/adapters/sqlite/mappers.d.ts.map +1 -1
- package/dist/adapters/sqlite/mappers.js +87 -0
- package/dist/adapters/sqlite/mappers.js.map +1 -1
- package/dist/adapters/sqlite/schema.d.ts +1 -1
- package/dist/adapters/sqlite/schema.d.ts.map +1 -1
- package/dist/adapters/sqlite/schema.js +297 -1
- package/dist/adapters/sqlite/schema.js.map +1 -1
- package/dist/adapters/sync-to-async.d.ts.map +1 -1
- package/dist/adapters/sync-to-async.js +54 -0
- package/dist/adapters/sync-to-async.js.map +1 -1
- package/dist/contracts/async-storage.d.ts +61 -1
- package/dist/contracts/async-storage.d.ts.map +1 -1
- package/dist/contracts/cognitive.d.ts +37 -0
- package/dist/contracts/cognitive.d.ts.map +1 -0
- package/dist/contracts/cognitive.js +24 -0
- package/dist/contracts/cognitive.js.map +1 -0
- package/dist/contracts/coordination.d.ts +101 -0
- package/dist/contracts/coordination.d.ts.map +1 -0
- package/dist/contracts/coordination.js +26 -0
- package/dist/contracts/coordination.js.map +1 -0
- package/dist/contracts/embedding.d.ts +1 -1
- package/dist/contracts/embedding.d.ts.map +1 -1
- package/dist/contracts/errors.d.ts +28 -0
- package/dist/contracts/errors.d.ts.map +1 -0
- package/dist/contracts/errors.js +41 -0
- package/dist/contracts/errors.js.map +1 -0
- package/dist/contracts/identity.d.ts +2 -0
- package/dist/contracts/identity.d.ts.map +1 -1
- package/dist/contracts/identity.js +26 -1
- package/dist/contracts/identity.js.map +1 -1
- package/dist/contracts/observability.d.ts +2 -1
- package/dist/contracts/observability.d.ts.map +1 -1
- package/dist/contracts/observability.js +11 -0
- package/dist/contracts/observability.js.map +1 -1
- package/dist/contracts/profile.d.ts +29 -0
- package/dist/contracts/profile.d.ts.map +1 -0
- package/dist/contracts/profile.js +2 -0
- package/dist/contracts/profile.js.map +1 -0
- package/dist/contracts/session-state.d.ts +10 -0
- package/dist/contracts/session-state.d.ts.map +1 -0
- package/dist/contracts/session-state.js +2 -0
- package/dist/contracts/session-state.js.map +1 -0
- package/dist/contracts/storage.d.ts +73 -1
- package/dist/contracts/storage.d.ts.map +1 -1
- package/dist/contracts/storage.js +16 -1
- package/dist/contracts/storage.js.map +1 -1
- package/dist/contracts/temporal.d.ts +112 -0
- package/dist/contracts/temporal.d.ts.map +1 -0
- package/dist/contracts/temporal.js +31 -0
- package/dist/contracts/temporal.js.map +1 -0
- package/dist/contracts/types.d.ts +135 -0
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/contracts/types.js +27 -0
- package/dist/contracts/types.js.map +1 -1
- package/dist/core/associations.d.ts +18 -0
- package/dist/core/associations.d.ts.map +1 -0
- package/dist/core/associations.js +185 -0
- package/dist/core/associations.js.map +1 -0
- package/dist/core/circuit-breaker.d.ts +9 -0
- package/dist/core/circuit-breaker.d.ts.map +1 -1
- package/dist/core/circuit-breaker.js +13 -1
- package/dist/core/circuit-breaker.js.map +1 -1
- package/dist/core/cognitive.d.ts +5 -0
- package/dist/core/cognitive.d.ts.map +1 -0
- package/dist/core/cognitive.js +120 -0
- package/dist/core/cognitive.js.map +1 -0
- package/dist/core/context.d.ts +72 -1
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +471 -45
- package/dist/core/context.js.map +1 -1
- package/dist/core/episodic.d.ts +28 -0
- package/dist/core/episodic.d.ts.map +1 -0
- package/dist/core/episodic.js +371 -0
- package/dist/core/episodic.js.map +1 -0
- package/dist/core/formatter.d.ts +4 -0
- package/dist/core/formatter.d.ts.map +1 -1
- package/dist/core/formatter.js +103 -0
- package/dist/core/formatter.js.map +1 -1
- package/dist/core/maintenance.d.ts +1 -0
- package/dist/core/maintenance.d.ts.map +1 -1
- package/dist/core/maintenance.js +75 -0
- package/dist/core/maintenance.js.map +1 -1
- package/dist/core/manager.d.ts +159 -7
- package/dist/core/manager.d.ts.map +1 -1
- package/dist/core/manager.js +740 -31
- package/dist/core/manager.js.map +1 -1
- package/dist/core/orchestrator.d.ts.map +1 -1
- package/dist/core/orchestrator.js +210 -178
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/playbook.d.ts +35 -0
- package/dist/core/playbook.d.ts.map +1 -0
- package/dist/core/playbook.js +184 -0
- package/dist/core/playbook.js.map +1 -0
- package/dist/core/profile.d.ts +8 -0
- package/dist/core/profile.d.ts.map +1 -0
- package/dist/core/profile.js +103 -0
- package/dist/core/profile.js.map +1 -0
- package/dist/core/quick.d.ts +5 -0
- package/dist/core/quick.d.ts.map +1 -1
- package/dist/core/quick.js +10 -1
- package/dist/core/quick.js.map +1 -1
- package/dist/core/runtime.d.ts +17 -1
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +88 -5
- package/dist/core/runtime.js.map +1 -1
- package/dist/core/streaming.d.ts +1 -1
- package/dist/core/streaming.d.ts.map +1 -1
- package/dist/core/temporal.d.ts +29 -0
- package/dist/core/temporal.d.ts.map +1 -0
- package/dist/core/temporal.js +447 -0
- package/dist/core/temporal.js.map +1 -0
- package/dist/core/validation.d.ts +3 -0
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +25 -10
- package/dist/core/validation.js.map +1 -1
- package/dist/core/workspace-detect.d.ts +17 -0
- package/dist/core/workspace-detect.d.ts.map +1 -0
- package/dist/core/workspace-detect.js +55 -0
- package/dist/core/workspace-detect.js.map +1 -0
- package/dist/embeddings/resilience.d.ts.map +1 -1
- package/dist/embeddings/resilience.js +19 -8
- package/dist/embeddings/resilience.js.map +1 -1
- package/dist/index.d.ts +21 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/integrations/claude-agent.d.ts +6 -0
- package/dist/integrations/claude-agent.d.ts.map +1 -1
- package/dist/integrations/claude-agent.js +5 -1
- package/dist/integrations/claude-agent.js.map +1 -1
- package/dist/integrations/claude-tools.d.ts +5 -4
- package/dist/integrations/claude-tools.d.ts.map +1 -1
- package/dist/integrations/claude-tools.js +155 -2
- package/dist/integrations/claude-tools.js.map +1 -1
- package/dist/integrations/middleware.d.ts +6 -0
- package/dist/integrations/middleware.d.ts.map +1 -1
- package/dist/integrations/middleware.js +11 -1
- package/dist/integrations/middleware.js.map +1 -1
- package/dist/integrations/openai-tools.d.ts +5 -4
- package/dist/integrations/openai-tools.d.ts.map +1 -1
- package/dist/integrations/openai-tools.js +170 -2
- package/dist/integrations/openai-tools.js.map +1 -1
- package/dist/integrations/vercel-ai.d.ts +6 -0
- package/dist/integrations/vercel-ai.d.ts.map +1 -1
- package/dist/integrations/vercel-ai.js +4 -0
- package/dist/integrations/vercel-ai.js.map +1 -1
- package/dist/server/http-server.d.ts +8 -0
- package/dist/server/http-server.d.ts.map +1 -1
- package/dist/server/http-server.js +976 -58
- package/dist/server/http-server.js.map +1 -1
- package/dist/server/mcp-server.d.ts +8 -0
- package/dist/server/mcp-server.d.ts.map +1 -1
- package/dist/server/mcp-server.js +1157 -37
- package/dist/server/mcp-server.js.map +1 -1
- package/dist/server/parsing.d.ts +12 -0
- package/dist/server/parsing.d.ts.map +1 -0
- package/dist/server/parsing.js +42 -0
- package/dist/server/parsing.js.map +1 -0
- package/dist/summarizers/prompts.d.ts +4 -0
- package/dist/summarizers/prompts.d.ts.map +1 -1
- package/dist/summarizers/prompts.js +42 -0
- package/dist/summarizers/prompts.js.map +1 -1
- package/docs/ULTIMATE_MEMORY_LAYER_ROADMAP.md +291 -0
- package/docs/prd.json +1498 -0
- package/openapi.yaml +1945 -112
- package/package.json +4 -2
package/dist/core/context.js
CHANGED
|
@@ -3,6 +3,70 @@ import { DEFAULT_CONTEXT_POLICY } from '../contracts/policy.js';
|
|
|
3
3
|
import { estimateTokens } from './tokens.js';
|
|
4
4
|
import { emitMemoryEvent } from './telemetry.js';
|
|
5
5
|
import { getLineageScore, rankKnowledge } from './retrieval.js';
|
|
6
|
+
const DEFAULT_MAX_ASSOCIATION_SEED_ITEMS = 8;
|
|
7
|
+
const DEFAULT_MAX_ASSOCIATED_KNOWLEDGE_ITEMS = 12;
|
|
8
|
+
const DEFAULT_RECENT_OUTPUT_LIMIT = 3;
|
|
9
|
+
const BLOCKER_PATTERN = /\b(blocked|blocker|waiting on|pending|stuck|cannot proceed|can't proceed)\b/i;
|
|
10
|
+
const ASSUMPTION_PATTERN = /\b(assume|assuming|assumption|likely|probably)\b/i;
|
|
11
|
+
const DECISION_PATTERN = /\b(decide|decision|choose|choice|whether|should we|need to determine)\b/i;
|
|
12
|
+
const TOOL_PATTERN = /\b(?:tool|command|script|query|search|fetch|deploy|build|test|lint|run)\b/i;
|
|
13
|
+
function matchesVisibility(visibilityClass, item, scope) {
|
|
14
|
+
const left = normalizeScope(item);
|
|
15
|
+
const right = normalizeScope(scope);
|
|
16
|
+
if (left.tenant_id !== right.tenant_id)
|
|
17
|
+
return false;
|
|
18
|
+
switch (visibilityClass) {
|
|
19
|
+
case 'tenant':
|
|
20
|
+
return true;
|
|
21
|
+
case 'workspace':
|
|
22
|
+
return left.workspace_id === right.workspace_id;
|
|
23
|
+
case 'shared_collaboration':
|
|
24
|
+
return (left.workspace_id === right.workspace_id &&
|
|
25
|
+
left.collaboration_id.length > 0 &&
|
|
26
|
+
left.collaboration_id === right.collaboration_id);
|
|
27
|
+
case 'private':
|
|
28
|
+
default:
|
|
29
|
+
return (left.system_id === right.system_id &&
|
|
30
|
+
left.workspace_id === right.workspace_id &&
|
|
31
|
+
left.collaboration_id === right.collaboration_id &&
|
|
32
|
+
left.scope_id === right.scope_id);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function isVisibilityAllowed(visibilityClass, item, scope, view) {
|
|
36
|
+
if (!matchesVisibility(visibilityClass, item, scope)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (view === 'local_only') {
|
|
40
|
+
return visibilityClass === 'private';
|
|
41
|
+
}
|
|
42
|
+
if (view === 'local_plus_shared_collaboration') {
|
|
43
|
+
return visibilityClass === 'private' || visibilityClass === 'shared_collaboration';
|
|
44
|
+
}
|
|
45
|
+
if (view === 'workspace_shared') {
|
|
46
|
+
return (visibilityClass === 'private' ||
|
|
47
|
+
visibilityClass === 'shared_collaboration' ||
|
|
48
|
+
visibilityClass === 'workspace');
|
|
49
|
+
}
|
|
50
|
+
return (visibilityClass === 'private' ||
|
|
51
|
+
visibilityClass === 'shared_collaboration' ||
|
|
52
|
+
visibilityClass === 'workspace' ||
|
|
53
|
+
visibilityClass === 'tenant');
|
|
54
|
+
}
|
|
55
|
+
export function resolveVisibleKnowledge(items, scope, view) {
|
|
56
|
+
return items.filter((item) => isVisibilityAllowed(item.visibility_class, item, scope, view));
|
|
57
|
+
}
|
|
58
|
+
export function resolveVisibleWorkItems(items, scope, view) {
|
|
59
|
+
return items.filter((item) => isVisibilityAllowed(item.visibility_class, item, scope, view));
|
|
60
|
+
}
|
|
61
|
+
export function resolveVisiblePlaybooks(items, scope, view) {
|
|
62
|
+
return items.filter((item) => isVisibilityAllowed(item.visibility_class, item, scope, view));
|
|
63
|
+
}
|
|
64
|
+
export function resolveVisibleWorkClaims(items, scope, view) {
|
|
65
|
+
return items.filter((item) => isVisibilityAllowed(item.visibility_class, item, scope, view));
|
|
66
|
+
}
|
|
67
|
+
export function resolveVisibleHandoffs(items, scope, view) {
|
|
68
|
+
return items.filter((item) => isVisibilityAllowed(item.visibility_class, item, scope, view));
|
|
69
|
+
}
|
|
6
70
|
function resolveContextPolicy(options) {
|
|
7
71
|
const base = {
|
|
8
72
|
...DEFAULT_CONTEXT_POLICY,
|
|
@@ -82,7 +146,111 @@ function deriveUnresolvedWork(workingMemory, activeTurns, relevantKnowledge, wor
|
|
|
82
146
|
}
|
|
83
147
|
return [...unresolved];
|
|
84
148
|
}
|
|
85
|
-
function
|
|
149
|
+
function compactSnippet(text, maxLength = 160) {
|
|
150
|
+
const normalized = text.replace(/\s+/g, ' ').trim();
|
|
151
|
+
if (normalized.length <= maxLength)
|
|
152
|
+
return normalized;
|
|
153
|
+
return `${normalized.slice(0, maxLength - 3).trimEnd()}...`;
|
|
154
|
+
}
|
|
155
|
+
function collectSignalSnippets(values, pattern, limit = 4) {
|
|
156
|
+
const matches = new Set();
|
|
157
|
+
for (const value of values) {
|
|
158
|
+
if (matches.size >= limit)
|
|
159
|
+
break;
|
|
160
|
+
if (pattern.test(value)) {
|
|
161
|
+
matches.add(compactSnippet(value));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return [...matches];
|
|
165
|
+
}
|
|
166
|
+
function deriveSessionState(workingMemory, activeTurns, activeObjectives, contextWorkItems, recentSummaries, currentObjective) {
|
|
167
|
+
const recentTexts = [
|
|
168
|
+
...activeTurns.slice(-6).map((turn) => turn.content),
|
|
169
|
+
workingMemory?.summary ?? '',
|
|
170
|
+
...recentSummaries.slice(0, 2).map((summary) => summary.summary),
|
|
171
|
+
].filter((value) => value.trim().length > 0);
|
|
172
|
+
const blockers = [
|
|
173
|
+
...contextWorkItems
|
|
174
|
+
.filter((item) => item.status === 'blocked')
|
|
175
|
+
.map((item) => compactSnippet(item.title)),
|
|
176
|
+
...collectSignalSnippets(recentTexts, BLOCKER_PATTERN),
|
|
177
|
+
];
|
|
178
|
+
const assumptions = collectSignalSnippets(recentTexts, ASSUMPTION_PATTERN);
|
|
179
|
+
const pendingDecisions = [
|
|
180
|
+
...collectSignalSnippets(recentTexts, DECISION_PATTERN),
|
|
181
|
+
...activeObjectives
|
|
182
|
+
.filter((item) => DECISION_PATTERN.test(item.title))
|
|
183
|
+
.map((item) => compactSnippet(item.title)),
|
|
184
|
+
];
|
|
185
|
+
const activeTools = [
|
|
186
|
+
...new Set([
|
|
187
|
+
...activeTurns
|
|
188
|
+
.map((turn) => turn.actor)
|
|
189
|
+
.filter((actor) => actor.length > 0 && !['user', 'assistant', 'system'].includes(actor)),
|
|
190
|
+
...activeTurns
|
|
191
|
+
.filter((turn) => TOOL_PATTERN.test(turn.content))
|
|
192
|
+
.map((turn) => compactSnippet(turn.content, 80)),
|
|
193
|
+
]),
|
|
194
|
+
].slice(0, 6);
|
|
195
|
+
const recentOutputs = activeTurns
|
|
196
|
+
.filter((turn) => turn.role !== 'user')
|
|
197
|
+
.slice(-DEFAULT_RECENT_OUTPUT_LIMIT)
|
|
198
|
+
.map((turn) => compactSnippet(turn.content));
|
|
199
|
+
const updatedAt = Math.max(0, ...activeTurns.map((turn) => turn.created_at), ...contextWorkItems.map((item) => item.updated_at), ...(workingMemory ? [workingMemory.created_at] : []), ...recentSummaries.map((summary) => summary.created_at));
|
|
200
|
+
return {
|
|
201
|
+
currentObjective,
|
|
202
|
+
blockers: [...new Set(blockers)].slice(0, 6),
|
|
203
|
+
assumptions: [...new Set(assumptions)].slice(0, 6),
|
|
204
|
+
pendingDecisions: [...new Set(pendingDecisions)].slice(0, 6),
|
|
205
|
+
activeTools,
|
|
206
|
+
recentOutputs,
|
|
207
|
+
updatedAt,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
export function buildDerivedShortTermState(input) {
|
|
211
|
+
const workingMemory = [...input.workingMemoryCandidates]
|
|
212
|
+
.sort((a, b) => b.id - a.id)[0] ?? null;
|
|
213
|
+
const recentSummaries = input.workingMemoryCandidates
|
|
214
|
+
.filter((summary) => summary.id !== workingMemory?.id)
|
|
215
|
+
.slice(0, input.maxRecentSummaries ?? DEFAULT_CONTEXT_POLICY.maxRecentSummaries);
|
|
216
|
+
const activeObjectives = input.contextWorkItems.filter((item) => item.kind === 'objective');
|
|
217
|
+
const currentObjective = deriveCurrentObjective(workingMemory, input.activeTurns);
|
|
218
|
+
return {
|
|
219
|
+
workingMemory,
|
|
220
|
+
recentSummaries,
|
|
221
|
+
activeObjectives,
|
|
222
|
+
currentObjective,
|
|
223
|
+
activeState: deriveActiveState(workingMemory, input.activeTurns),
|
|
224
|
+
// The session-state projection intentionally computes a fast-resume subset
|
|
225
|
+
// even when relevantKnowledge is omitted by projection refresh.
|
|
226
|
+
unresolvedWork: deriveUnresolvedWork(workingMemory, input.activeTurns, input.relevantKnowledge ?? [], input.contextWorkItems),
|
|
227
|
+
sessionState: deriveSessionState(workingMemory, input.activeTurns, activeObjectives, input.contextWorkItems, recentSummaries, currentObjective),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function isWorkItemActiveAt(item, asOf) {
|
|
231
|
+
return item.created_at <= asOf && !(item.status === 'done' && item.updated_at <= asOf);
|
|
232
|
+
}
|
|
233
|
+
export async function getContextWorkItems(adapter, scope, asOf, level) {
|
|
234
|
+
if (asOf == null) {
|
|
235
|
+
return level && level !== 'scope'
|
|
236
|
+
? adapter.getActiveWorkItemsCrossScope(scope, level)
|
|
237
|
+
: adapter.getActiveWorkItems(scope);
|
|
238
|
+
}
|
|
239
|
+
const workItems = level && level !== 'scope'
|
|
240
|
+
? await adapter.getWorkItemsByTimeRangeCrossScope(scope, level, { end_at: asOf })
|
|
241
|
+
: await adapter.getWorkItemsByTimeRange(scope, { end_at: asOf });
|
|
242
|
+
return workItems
|
|
243
|
+
.filter((item) => isWorkItemActiveAt(item, asOf))
|
|
244
|
+
.sort((a, b) => b.updated_at - a.updated_at || b.created_at - a.created_at || b.id - a.id);
|
|
245
|
+
}
|
|
246
|
+
export function resolveContextScopeLevel(crossScopeLevel, view) {
|
|
247
|
+
if (view === 'operator_supervisor')
|
|
248
|
+
return crossScopeLevel ?? 'tenant';
|
|
249
|
+
if (view && view !== 'local_only')
|
|
250
|
+
return crossScopeLevel ?? 'workspace';
|
|
251
|
+
return crossScopeLevel;
|
|
252
|
+
}
|
|
253
|
+
function computeContextTokenEstimate(activeTurns, workingMemory, relevantKnowledge, recentSummaries, tokenEstimator = estimateTokens, playbooks = [], associatedKnowledge = []) {
|
|
86
254
|
const turnTokens = activeTurns.reduce((acc, turn) => acc + turn.token_estimate, 0);
|
|
87
255
|
const workingTokens = workingMemory
|
|
88
256
|
? tokenEstimator(workingMemory.summary) +
|
|
@@ -91,7 +259,9 @@ function computeContextTokenEstimate(activeTurns, workingMemory, relevantKnowled
|
|
|
91
259
|
: 0;
|
|
92
260
|
const knowledgeTokens = relevantKnowledge.reduce((acc, knowledge) => acc + tokenEstimator(knowledge.fact), 0);
|
|
93
261
|
const summaryTokens = recentSummaries.reduce((acc, summary) => acc + tokenEstimator(summary.summary), 0);
|
|
94
|
-
|
|
262
|
+
const playbookTokens = playbooks.reduce((acc, pb) => acc + tokenEstimator(pb.title) + tokenEstimator(pb.description) + tokenEstimator(pb.instructions), 0);
|
|
263
|
+
const associatedTokens = associatedKnowledge.reduce((acc, knowledge) => acc + tokenEstimator(knowledge.fact), 0);
|
|
264
|
+
return turnTokens + workingTokens + knowledgeTokens + summaryTokens + playbookTokens + associatedTokens;
|
|
95
265
|
}
|
|
96
266
|
function dropLowestPriorityTurn(activeTurns) {
|
|
97
267
|
if (activeTurns.length === 0)
|
|
@@ -208,26 +378,33 @@ export async function buildMemoryContext(adapter, scope, options) {
|
|
|
208
378
|
const policy = resolveContextPolicy(options);
|
|
209
379
|
const tokenEstimator = options?.tokenEstimator ?? estimateTokens;
|
|
210
380
|
const asOf = options?.asOf;
|
|
381
|
+
const view = options?.view;
|
|
382
|
+
const effectiveScopeLevel = resolveContextScopeLevel(options?.crossScopeLevel, view);
|
|
211
383
|
let activeTurns = await adapter.getActiveTurns(normalizedScope, options?.sessionId);
|
|
212
384
|
if (asOf != null) {
|
|
213
385
|
activeTurns = activeTurns.filter((turn) => turn.created_at <= asOf);
|
|
214
386
|
}
|
|
215
|
-
const
|
|
387
|
+
const contextWorkItems = view
|
|
388
|
+
? resolveVisibleWorkItems(await getContextWorkItems(adapter, normalizedScope, asOf, effectiveScopeLevel), normalizedScope, view)
|
|
389
|
+
: await getContextWorkItems(adapter, normalizedScope, asOf, effectiveScopeLevel);
|
|
216
390
|
const workingMemoryCandidates = await adapter.getActiveWorkingMemory(normalizedScope, options?.sessionId);
|
|
217
|
-
const workingMemory = [...workingMemoryCandidates]
|
|
218
|
-
.filter((item) => asOf == null || item.created_at <= asOf)
|
|
219
|
-
.sort((a, b) => b.id - a.id)[0] ?? null;
|
|
220
391
|
const allWorkingMemory = workingMemoryCandidates.filter((item) => asOf == null || item.created_at <= asOf);
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
392
|
+
const initialShortTermState = buildDerivedShortTermState({
|
|
393
|
+
activeTurns,
|
|
394
|
+
workingMemoryCandidates: allWorkingMemory,
|
|
395
|
+
contextWorkItems,
|
|
396
|
+
maxRecentSummaries: policy.maxRecentSummaries,
|
|
397
|
+
});
|
|
398
|
+
const { activeObjectives, currentObjective, recentSummaries, workingMemory, } = initialShortTermState;
|
|
399
|
+
const activeKnowledge = effectiveScopeLevel && effectiveScopeLevel !== 'scope'
|
|
400
|
+
? await adapter.getActiveKnowledgeCrossScope(normalizedScope, effectiveScopeLevel)
|
|
226
401
|
: await adapter.getActiveKnowledgeMemory(normalizedScope);
|
|
227
|
-
const temporalKnowledge =
|
|
402
|
+
const temporalKnowledge = view
|
|
403
|
+
? resolveVisibleKnowledge(activeKnowledge.filter((item) => asOf == null || item.created_at <= asOf), normalizedScope, view)
|
|
404
|
+
: activeKnowledge.filter((item) => asOf == null || item.created_at <= asOf);
|
|
228
405
|
const lexicalRanks = options?.relevanceQuery
|
|
229
|
-
? normalizeLexicalRanks(
|
|
230
|
-
? await adapter.searchKnowledgeCrossScope(normalizedScope,
|
|
406
|
+
? normalizeLexicalRanks(effectiveScopeLevel && effectiveScopeLevel !== 'scope'
|
|
407
|
+
? await adapter.searchKnowledgeCrossScope(normalizedScope, effectiveScopeLevel, options.relevanceQuery, {
|
|
231
408
|
limit: policy.maxKnowledgeItems * 2,
|
|
232
409
|
activeOnly: true,
|
|
233
410
|
})
|
|
@@ -239,8 +416,8 @@ export async function buildMemoryContext(adapter, scope, options) {
|
|
|
239
416
|
let semanticRanks = new Map();
|
|
240
417
|
if (options?.embeddingAdapter && options.queryVector) {
|
|
241
418
|
try {
|
|
242
|
-
const semanticResults =
|
|
243
|
-
? await options.embeddingAdapter.findSimilarCrossScope(normalizedScope,
|
|
419
|
+
const semanticResults = effectiveScopeLevel && effectiveScopeLevel !== 'scope'
|
|
420
|
+
? await options.embeddingAdapter.findSimilarCrossScope(normalizedScope, effectiveScopeLevel, options.queryVector, {
|
|
244
421
|
limit: policy.maxKnowledgeItems * 2,
|
|
245
422
|
minSimilarity: policy.semanticMinSimilarity,
|
|
246
423
|
})
|
|
@@ -256,60 +433,300 @@ export async function buildMemoryContext(adapter, scope, options) {
|
|
|
256
433
|
});
|
|
257
434
|
}
|
|
258
435
|
}
|
|
259
|
-
const scopedKnowledge =
|
|
260
|
-
? temporalKnowledge
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
436
|
+
const scopedKnowledge = view && view !== 'local_only'
|
|
437
|
+
? temporalKnowledge
|
|
438
|
+
: effectiveScopeLevel && effectiveScopeLevel !== 'scope'
|
|
439
|
+
? temporalKnowledge.filter((item) => item.scope_id === normalizedScope.scope_id ||
|
|
440
|
+
(normalizedScope.collaboration_id.length > 0 &&
|
|
441
|
+
item.collaboration_id === normalizedScope.collaboration_id) ||
|
|
442
|
+
getLineageScore(normalizedScope.scope_id, item.scope_id) >= policy.minimumLineageScore)
|
|
443
|
+
: temporalKnowledge;
|
|
265
444
|
const relevanceTexts = [
|
|
266
445
|
options?.relevanceQuery ?? '',
|
|
267
446
|
workingMemory?.summary ?? '',
|
|
268
447
|
...activeObjectives.map((item) => item.title),
|
|
269
448
|
].filter((value) => value.trim().length > 0);
|
|
270
|
-
const candidates = buildCandidates(scopedKnowledge, lexicalRanks, semanticRanks, policy, normalizedScope, relevanceTexts,
|
|
449
|
+
const candidates = buildCandidates(scopedKnowledge, lexicalRanks, semanticRanks, policy, normalizedScope, relevanceTexts, effectiveScopeLevel != null && effectiveScopeLevel !== 'scope');
|
|
271
450
|
const trustedCoreClasses = new Set(['identity', 'constraint', 'preference']);
|
|
272
|
-
|
|
273
|
-
trustedCoreClasses.has(candidate.item.knowledge_class)), policy, 'trusted_core', Math.min(policy.trustedCoreLimit, Math.max(3, Math.floor(policy.maxKnowledgeItems / 2))))
|
|
451
|
+
const trustedCoreSelection = selectKnowledge(candidates.filter((candidate) => candidate.item.knowledge_state === 'trusted' &&
|
|
452
|
+
trustedCoreClasses.has(candidate.item.knowledge_class)), policy, 'trusted_core', Math.min(policy.trustedCoreLimit, Math.max(3, Math.floor(policy.maxKnowledgeItems / 2))));
|
|
453
|
+
let trustedCoreMemory = trustedCoreSelection.relevantKnowledge;
|
|
274
454
|
const trustedCoreIds = new Set(trustedCoreMemory.map((item) => item.id));
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const
|
|
455
|
+
const taskRelevantSelection = selectKnowledge(candidates.filter((candidate) => candidate.item.knowledge_state === 'trusted' && !trustedCoreIds.has(candidate.item.id)), policy, 'task_relevant', Math.min(policy.taskRelevantLimit, Math.max(0, policy.maxKnowledgeItems - trustedCoreMemory.length)));
|
|
456
|
+
let taskRelevantKnowledge = taskRelevantSelection.relevantKnowledge;
|
|
457
|
+
const provisionalSelection = selectKnowledge(candidates.filter((candidate) => candidate.item.knowledge_state === 'provisional'), policy, 'provisional', 4);
|
|
458
|
+
const provisionalKnowledge = provisionalSelection.relevantKnowledge;
|
|
459
|
+
const disputedSelection = selectKnowledge(candidates.filter((candidate) => candidate.item.knowledge_state === 'disputed'), policy, 'disputed', 4);
|
|
460
|
+
const disputedKnowledge = disputedSelection.relevantKnowledge;
|
|
278
461
|
let relevantKnowledge = [...trustedCoreMemory, ...taskRelevantKnowledge];
|
|
279
462
|
let knowledgeSelectionReasons = [
|
|
280
|
-
...
|
|
281
|
-
|
|
282
|
-
...
|
|
283
|
-
...
|
|
284
|
-
...selectKnowledge(candidates.filter((candidate) => candidate.item.knowledge_state === 'disputed'), policy, 'disputed', disputedKnowledge.length).knowledgeSelectionReasons,
|
|
463
|
+
...trustedCoreSelection.knowledgeSelectionReasons,
|
|
464
|
+
...taskRelevantSelection.knowledgeSelectionReasons,
|
|
465
|
+
...provisionalSelection.knowledgeSelectionReasons,
|
|
466
|
+
...disputedSelection.knowledgeSelectionReasons,
|
|
285
467
|
];
|
|
468
|
+
const initiallySelectedIds = new Set(relevantKnowledge.map((item) => item.id));
|
|
469
|
+
const excludedKnowledge = candidates
|
|
470
|
+
.filter((candidate) => !initiallySelectedIds.has(candidate.item.id))
|
|
471
|
+
.map((candidate) => ({
|
|
472
|
+
knowledgeMemoryId: candidate.item.id,
|
|
473
|
+
reason: 'not_selected',
|
|
474
|
+
detail: `excluded:${candidate.item.knowledge_state}:${candidate.item.knowledge_class}`,
|
|
475
|
+
}));
|
|
476
|
+
const scopedKnowledgeById = new Map(scopedKnowledge.map((item) => [item.id, item]));
|
|
477
|
+
let relevantPlaybooks = options?.relevanceQuery
|
|
478
|
+
? (effectiveScopeLevel && effectiveScopeLevel !== 'scope'
|
|
479
|
+
? await adapter.searchPlaybooksCrossScope(normalizedScope, effectiveScopeLevel, options.relevanceQuery, { limit: 3, activeOnly: true })
|
|
480
|
+
: await adapter.searchPlaybooks(normalizedScope, options.relevanceQuery, {
|
|
481
|
+
limit: 3,
|
|
482
|
+
activeOnly: true,
|
|
483
|
+
})).map((hit) => hit.item)
|
|
484
|
+
: [];
|
|
485
|
+
relevantPlaybooks = view
|
|
486
|
+
? resolveVisiblePlaybooks(relevantPlaybooks, normalizedScope, view)
|
|
487
|
+
: relevantPlaybooks;
|
|
488
|
+
// Single-hop association expansion via supports + related_to edges
|
|
489
|
+
const associationMinConfidence = options?.associationMinConfidence ?? 0.3;
|
|
490
|
+
const maxAssociationSeedItems = options?.maxAssociationSeedItems ?? DEFAULT_MAX_ASSOCIATION_SEED_ITEMS;
|
|
491
|
+
const maxAssociatedKnowledgeItems = options?.maxAssociatedKnowledgeItems ?? DEFAULT_MAX_ASSOCIATED_KNOWLEDGE_ITEMS;
|
|
492
|
+
const selectedIds = new Set(relevantKnowledge.map((k) => k.id));
|
|
493
|
+
const associatedKnowledge = [];
|
|
494
|
+
const associationTrace = {
|
|
495
|
+
seedKnowledgeIds: relevantKnowledge.slice(0, maxAssociationSeedItems).map((knowledge) => knowledge.id),
|
|
496
|
+
candidateKnowledgeIds: [],
|
|
497
|
+
includedKnowledgeIds: [],
|
|
498
|
+
truncatedKnowledgeIds: [],
|
|
499
|
+
maxSeedKnowledgeItems: maxAssociationSeedItems,
|
|
500
|
+
maxAssociatedKnowledgeItems,
|
|
501
|
+
};
|
|
502
|
+
if (relevantKnowledge.length > 0) {
|
|
503
|
+
const associationSeeds = relevantKnowledge.slice(0, maxAssociationSeedItems);
|
|
504
|
+
const associationResults = await Promise.all(associationSeeds.map(async (knowledge) => {
|
|
505
|
+
const associationScope = normalizeScope(knowledge);
|
|
506
|
+
const [from, to] = await Promise.all([
|
|
507
|
+
adapter.getAssociationsFrom('knowledge', knowledge.id, associationScope),
|
|
508
|
+
adapter.getAssociationsTo('knowledge', knowledge.id, associationScope),
|
|
509
|
+
]);
|
|
510
|
+
return { from, to };
|
|
511
|
+
}));
|
|
512
|
+
const expandScores = new Map();
|
|
513
|
+
// Outbound: current node is source, neighbor is target
|
|
514
|
+
for (const { from: assocs } of associationResults) {
|
|
515
|
+
for (const a of assocs) {
|
|
516
|
+
if ((a.association_type === 'supports' || a.association_type === 'related_to') &&
|
|
517
|
+
a.target_kind === 'knowledge' &&
|
|
518
|
+
a.confidence >= associationMinConfidence &&
|
|
519
|
+
!selectedIds.has(a.target_id)) {
|
|
520
|
+
expandScores.set(a.target_id, Math.max(expandScores.get(a.target_id) ?? 0, a.confidence));
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
// Inbound: current node is target, neighbor is source
|
|
525
|
+
for (const { to: assocs } of associationResults) {
|
|
526
|
+
for (const a of assocs) {
|
|
527
|
+
if ((a.association_type === 'supports' || a.association_type === 'related_to') &&
|
|
528
|
+
a.source_kind === 'knowledge' &&
|
|
529
|
+
a.confidence >= associationMinConfidence &&
|
|
530
|
+
!selectedIds.has(a.source_id)) {
|
|
531
|
+
expandScores.set(a.source_id, Math.max(expandScores.get(a.source_id) ?? 0, a.confidence));
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
const candidateAssociationIds = [...expandScores.entries()]
|
|
536
|
+
.sort((a, b) => b[1] - a[1] || a[0] - b[0])
|
|
537
|
+
.map(([id]) => id);
|
|
538
|
+
associationTrace.candidateKnowledgeIds = candidateAssociationIds;
|
|
539
|
+
if (candidateAssociationIds.length > maxAssociatedKnowledgeItems) {
|
|
540
|
+
associationTrace.truncatedKnowledgeIds = candidateAssociationIds.slice(maxAssociatedKnowledgeItems);
|
|
541
|
+
}
|
|
542
|
+
for (const targetId of candidateAssociationIds.slice(0, maxAssociatedKnowledgeItems)) {
|
|
543
|
+
const knowledge = scopedKnowledgeById.get(targetId);
|
|
544
|
+
if (knowledge && knowledge.knowledge_state !== 'retired' && knowledge.knowledge_state !== 'superseded') {
|
|
545
|
+
associatedKnowledge.push(knowledge);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
associationTrace.includedKnowledgeIds = associatedKnowledge.map((knowledge) => knowledge.id);
|
|
549
|
+
}
|
|
286
550
|
let trimmedSummaries = [...recentSummaries];
|
|
287
|
-
let
|
|
551
|
+
let trimmedAssociated = [...associatedKnowledge];
|
|
552
|
+
const tokenTrimTrace = {
|
|
553
|
+
initialTokenEstimate: 0,
|
|
554
|
+
finalTokenEstimate: 0,
|
|
555
|
+
droppedTurnIds: [],
|
|
556
|
+
droppedSummaryIds: [],
|
|
557
|
+
droppedPlaybookIds: [],
|
|
558
|
+
droppedAssociatedKnowledgeIds: [],
|
|
559
|
+
droppedKnowledgeIds: [],
|
|
560
|
+
};
|
|
561
|
+
function recomputeTokens() {
|
|
562
|
+
return computeContextTokenEstimate(activeTurns, workingMemory, relevantKnowledge, trimmedSummaries, tokenEstimator, relevantPlaybooks, trimmedAssociated);
|
|
563
|
+
}
|
|
564
|
+
let tokenEstimate = recomputeTokens();
|
|
565
|
+
tokenTrimTrace.initialTokenEstimate = tokenEstimate;
|
|
288
566
|
while (tokenEstimate > policy.tokenBudget && activeTurns.length > 0) {
|
|
567
|
+
const beforeIds = new Set(activeTurns.map((turn) => turn.id));
|
|
289
568
|
activeTurns = dropLowestPriorityTurn(activeTurns);
|
|
290
|
-
|
|
569
|
+
const afterIds = new Set(activeTurns.map((turn) => turn.id));
|
|
570
|
+
for (const id of beforeIds) {
|
|
571
|
+
if (!afterIds.has(id)) {
|
|
572
|
+
tokenTrimTrace.droppedTurnIds.push(id);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
tokenEstimate = recomputeTokens();
|
|
291
576
|
}
|
|
292
577
|
while (tokenEstimate > policy.tokenBudget && trimmedSummaries.length > 0) {
|
|
578
|
+
const removed = trimmedSummaries[trimmedSummaries.length - 1];
|
|
579
|
+
if (removed)
|
|
580
|
+
tokenTrimTrace.droppedSummaryIds.push(removed.id);
|
|
293
581
|
trimmedSummaries = trimmedSummaries.slice(0, -1);
|
|
294
|
-
tokenEstimate =
|
|
582
|
+
tokenEstimate = recomputeTokens();
|
|
583
|
+
}
|
|
584
|
+
// Trim playbooks (procedural guidance) before associated knowledge or core
|
|
585
|
+
// relevant knowledge. A few large playbooks can otherwise push the context
|
|
586
|
+
// over budget after every other lower-priority category has been trimmed.
|
|
587
|
+
// Drop the largest playbook first so we shed bytes aggressively rather
|
|
588
|
+
// than dropping smaller playbooks that may still be useful alongside a
|
|
589
|
+
// single outsized offender.
|
|
590
|
+
while (tokenEstimate > policy.tokenBudget && relevantPlaybooks.length > 0) {
|
|
591
|
+
const sizes = relevantPlaybooks.map((pb) => tokenEstimator(`${pb.title}\n${pb.description}\n${pb.instructions}`));
|
|
592
|
+
let worstIdx = 0;
|
|
593
|
+
for (let i = 1; i < sizes.length; i++) {
|
|
594
|
+
if (sizes[i] > sizes[worstIdx])
|
|
595
|
+
worstIdx = i;
|
|
596
|
+
}
|
|
597
|
+
const removed = relevantPlaybooks[worstIdx];
|
|
598
|
+
if (removed)
|
|
599
|
+
tokenTrimTrace.droppedPlaybookIds.push(removed.id);
|
|
600
|
+
relevantPlaybooks = relevantPlaybooks.filter((_, i) => i !== worstIdx);
|
|
601
|
+
tokenEstimate = recomputeTokens();
|
|
602
|
+
}
|
|
603
|
+
// Trim associated knowledge before core relevant knowledge
|
|
604
|
+
while (tokenEstimate > policy.tokenBudget && trimmedAssociated.length > 0) {
|
|
605
|
+
const removed = trimmedAssociated[trimmedAssociated.length - 1];
|
|
606
|
+
if (removed)
|
|
607
|
+
tokenTrimTrace.droppedAssociatedKnowledgeIds.push(removed.id);
|
|
608
|
+
trimmedAssociated = trimmedAssociated.slice(0, -1);
|
|
609
|
+
tokenEstimate = recomputeTokens();
|
|
295
610
|
}
|
|
296
611
|
while (tokenEstimate > policy.tokenBudget && relevantKnowledge.length > 0) {
|
|
612
|
+
const removed = relevantKnowledge[relevantKnowledge.length - 1];
|
|
613
|
+
if (removed) {
|
|
614
|
+
tokenTrimTrace.droppedKnowledgeIds.push(removed.id);
|
|
615
|
+
excludedKnowledge.push({
|
|
616
|
+
knowledgeMemoryId: removed.id,
|
|
617
|
+
reason: 'token_budget',
|
|
618
|
+
detail: 'trimmed:token_budget',
|
|
619
|
+
});
|
|
620
|
+
}
|
|
297
621
|
relevantKnowledge = relevantKnowledge.slice(0, -1);
|
|
298
622
|
trustedCoreMemory = trustedCoreMemory.filter((item) => relevantKnowledge.some((entry) => entry.id === item.id));
|
|
299
623
|
taskRelevantKnowledge = taskRelevantKnowledge.filter((item) => relevantKnowledge.some((entry) => entry.id === item.id));
|
|
300
624
|
const retainedIds = new Set(relevantKnowledge.map((item) => item.id));
|
|
301
625
|
knowledgeSelectionReasons = knowledgeSelectionReasons.filter((entry) => retainedIds.has(entry.knowledgeMemoryId));
|
|
302
|
-
tokenEstimate =
|
|
626
|
+
tokenEstimate = recomputeTokens();
|
|
303
627
|
}
|
|
628
|
+
tokenTrimTrace.finalTokenEstimate = tokenEstimate;
|
|
304
629
|
if (policy.touchSelectedKnowledge) {
|
|
305
|
-
|
|
306
|
-
await adapter.touchKnowledgeMemory(knowledge.id);
|
|
307
|
-
}
|
|
630
|
+
await adapter.touchKnowledgeMemories(relevantKnowledge.map((knowledge) => knowledge.id));
|
|
308
631
|
}
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
632
|
+
const rawWorkClaims = options?.includeCoordinationState || view
|
|
633
|
+
? await (effectiveScopeLevel && effectiveScopeLevel !== 'scope'
|
|
634
|
+
? adapter.listWorkClaimsCrossScope(normalizedScope, effectiveScopeLevel, {
|
|
635
|
+
actor: options?.viewer
|
|
636
|
+
? {
|
|
637
|
+
actor_kind: options.viewer.actor_kind,
|
|
638
|
+
actor_id: options.viewer.actor_id,
|
|
639
|
+
}
|
|
640
|
+
: undefined,
|
|
641
|
+
})
|
|
642
|
+
: adapter.listWorkClaims(normalizedScope, {
|
|
643
|
+
actor: options?.viewer
|
|
644
|
+
? {
|
|
645
|
+
actor_kind: options.viewer.actor_kind,
|
|
646
|
+
actor_id: options.viewer.actor_id,
|
|
647
|
+
}
|
|
648
|
+
: undefined,
|
|
649
|
+
}))
|
|
650
|
+
: [];
|
|
651
|
+
const workClaims = view
|
|
652
|
+
? resolveVisibleWorkClaims(rawWorkClaims, normalizedScope, view)
|
|
653
|
+
: rawWorkClaims;
|
|
654
|
+
const rawHandoffs = options?.includeCoordinationState || view
|
|
655
|
+
? await (effectiveScopeLevel && effectiveScopeLevel !== 'scope'
|
|
656
|
+
? adapter.listHandoffsCrossScope(normalizedScope, effectiveScopeLevel, {
|
|
657
|
+
actor: options?.viewer
|
|
658
|
+
? {
|
|
659
|
+
actor_kind: options.viewer.actor_kind,
|
|
660
|
+
actor_id: options.viewer.actor_id,
|
|
661
|
+
}
|
|
662
|
+
: undefined,
|
|
663
|
+
})
|
|
664
|
+
: adapter.listHandoffs(normalizedScope, {
|
|
665
|
+
actor: options?.viewer
|
|
666
|
+
? {
|
|
667
|
+
actor_kind: options.viewer.actor_kind,
|
|
668
|
+
actor_id: options.viewer.actor_id,
|
|
669
|
+
}
|
|
670
|
+
: undefined,
|
|
671
|
+
}))
|
|
672
|
+
: [];
|
|
673
|
+
const handoffs = view ? resolveVisibleHandoffs(rawHandoffs, normalizedScope, view) : rawHandoffs;
|
|
674
|
+
const coordinationState = options?.includeCoordinationState || view
|
|
675
|
+
? {
|
|
676
|
+
ownedClaims: options?.viewer
|
|
677
|
+
? workClaims.filter((claim) => claim.actor.actor_kind === options.viewer.actor_kind &&
|
|
678
|
+
claim.actor.actor_id === options.viewer.actor_id &&
|
|
679
|
+
claim.status === 'active')
|
|
680
|
+
: [],
|
|
681
|
+
pendingInboundHandoffs: options?.viewer
|
|
682
|
+
? handoffs.filter((handoff) => handoff.status === 'pending' &&
|
|
683
|
+
handoff.to_actor.actor_kind === options.viewer.actor_kind &&
|
|
684
|
+
handoff.to_actor.actor_id === options.viewer.actor_id)
|
|
685
|
+
: [],
|
|
686
|
+
pendingOutboundHandoffs: options?.viewer
|
|
687
|
+
? handoffs.filter((handoff) => handoff.status === 'pending' &&
|
|
688
|
+
handoff.from_actor.actor_kind === options.viewer.actor_kind &&
|
|
689
|
+
handoff.from_actor.actor_id === options.viewer.actor_id)
|
|
690
|
+
: [],
|
|
691
|
+
sharedWorkItems: contextWorkItems.filter((item) => item.visibility_class !== 'private'),
|
|
692
|
+
}
|
|
693
|
+
: null;
|
|
694
|
+
const shortTermState = buildDerivedShortTermState({
|
|
695
|
+
activeTurns,
|
|
696
|
+
workingMemoryCandidates: allWorkingMemory,
|
|
697
|
+
contextWorkItems,
|
|
698
|
+
relevantKnowledge,
|
|
699
|
+
maxRecentSummaries: policy.maxRecentSummaries,
|
|
700
|
+
});
|
|
701
|
+
const { activeState, sessionState: derivedSessionState, unresolvedWork, } = shortTermState;
|
|
702
|
+
const projectedSessionState = asOf == null && options?.sessionId
|
|
703
|
+
? await adapter.getSessionState(normalizedScope, options.sessionId)
|
|
704
|
+
: null;
|
|
705
|
+
const sessionState = projectedSessionState && projectedSessionState.updatedAt >= derivedSessionState.updatedAt
|
|
706
|
+
? {
|
|
707
|
+
currentObjective: projectedSessionState.currentObjective,
|
|
708
|
+
blockers: projectedSessionState.blockers,
|
|
709
|
+
assumptions: projectedSessionState.assumptions,
|
|
710
|
+
pendingDecisions: projectedSessionState.pendingDecisions,
|
|
711
|
+
activeTools: projectedSessionState.activeTools,
|
|
712
|
+
recentOutputs: projectedSessionState.recentOutputs,
|
|
713
|
+
updatedAt: projectedSessionState.updatedAt,
|
|
714
|
+
}
|
|
715
|
+
: derivedSessionState;
|
|
716
|
+
const debugTrace = {
|
|
717
|
+
scope: {
|
|
718
|
+
normalizedScope,
|
|
719
|
+
scopeSource: effectiveScopeLevel != null && effectiveScopeLevel !== 'scope'
|
|
720
|
+
? 'cross_scope'
|
|
721
|
+
: 'local',
|
|
722
|
+
scopeLevel: effectiveScopeLevel ?? 'scope',
|
|
723
|
+
asOf: asOf ?? null,
|
|
724
|
+
},
|
|
725
|
+
selectedKnowledge: knowledgeSelectionReasons,
|
|
726
|
+
excludedKnowledge,
|
|
727
|
+
associationExpansion: associationTrace,
|
|
728
|
+
tokenTrimming: tokenTrimTrace,
|
|
729
|
+
};
|
|
313
730
|
emitMemoryEvent('context_assembly', normalizedScope, options, Date.now() - startedAt, {
|
|
314
731
|
mode: policy.mode,
|
|
315
732
|
activeTurnCount: activeTurns.length,
|
|
@@ -318,10 +735,14 @@ export async function buildMemoryContext(adapter, scope, options) {
|
|
|
318
735
|
recentSummaryCount: trimmedSummaries.length,
|
|
319
736
|
tokenEstimate,
|
|
320
737
|
relevanceQuery: options?.relevanceQuery ?? null,
|
|
321
|
-
crossScopeLevel:
|
|
738
|
+
crossScopeLevel: effectiveScopeLevel ?? 'scope',
|
|
322
739
|
currentObjective,
|
|
323
740
|
unresolvedWorkCount: unresolvedWork.length,
|
|
741
|
+
sessionState,
|
|
324
742
|
selectionReasons: knowledgeSelectionReasons,
|
|
743
|
+
excludedKnowledgeCount: excludedKnowledge.length,
|
|
744
|
+
associationExpansion: associationTrace,
|
|
745
|
+
tokenTrimming: tokenTrimTrace,
|
|
325
746
|
});
|
|
326
747
|
return {
|
|
327
748
|
mode: policy.mode,
|
|
@@ -335,10 +756,15 @@ export async function buildMemoryContext(adapter, scope, options) {
|
|
|
335
756
|
durableKnowledge: trustedCoreMemory,
|
|
336
757
|
recentSummaries: trimmedSummaries,
|
|
337
758
|
currentObjective,
|
|
759
|
+
sessionState,
|
|
338
760
|
activeObjectives,
|
|
339
761
|
activeState,
|
|
762
|
+
coordinationState,
|
|
763
|
+
relevantPlaybooks,
|
|
764
|
+
associatedKnowledge: trimmedAssociated,
|
|
340
765
|
unresolvedWork,
|
|
341
766
|
knowledgeSelectionReasons,
|
|
767
|
+
debugTrace,
|
|
342
768
|
tokenEstimate,
|
|
343
769
|
};
|
|
344
770
|
}
|