@librechat/agents 3.8.0 → 3.8.1
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/dist/cjs/agents/AgentContext.cjs +13 -3
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +174 -53
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +15 -5
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +42 -0
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +42 -0
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/langfuseTraceShaping.cjs +7 -1
- package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +5 -9
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +2 -7
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/contextPressureMeter.cjs +51 -10
- package/dist/cjs/llm/contextPressureMeter.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -1
- package/dist/cjs/llm/invoke.cjs +2 -2
- package/dist/cjs/llm/openai/index.cjs +4 -3
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +5 -4
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/preempt.cjs +3 -2
- package/dist/cjs/llm/preempt.cjs.map +1 -1
- package/dist/cjs/llm/prepareProviderRequest.cjs +9 -7
- package/dist/cjs/llm/prepareProviderRequest.cjs.map +1 -1
- package/dist/cjs/llm/providers.cjs +1 -1
- package/dist/cjs/main.cjs +11 -5
- package/dist/cjs/messages/alternation.cjs +1 -5
- package/dist/cjs/messages/alternation.cjs.map +1 -1
- package/dist/cjs/messages/budget.cjs +206 -7
- package/dist/cjs/messages/budget.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +3 -9
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +25 -37
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +131 -42
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/index.cjs +2 -0
- package/dist/cjs/messages/prune.cjs +12 -7
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/reasoningTypes.cjs +21 -0
- package/dist/cjs/messages/reasoningTypes.cjs.map +1 -0
- package/dist/cjs/messages/recency.cjs +15 -94
- package/dist/cjs/messages/recency.cjs.map +1 -1
- package/dist/cjs/messages/toolHistoryProjection.cjs +243 -0
- package/dist/cjs/messages/toolHistoryProjection.cjs.map +1 -0
- package/dist/cjs/messages/toolResultTypes.cjs +145 -4
- package/dist/cjs/messages/toolResultTypes.cjs.map +1 -1
- package/dist/cjs/run.cjs +5 -4
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +6 -4
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/session/JsonlSessionStore.cjs +3 -0
- package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
- package/dist/cjs/session/index.cjs +1 -1
- package/dist/cjs/session/sessionProjection.cjs +75 -0
- package/dist/cjs/session/sessionProjection.cjs.map +1 -0
- package/dist/cjs/stream.cjs +18 -17
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/index.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +18 -8
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/summarization/shared.cjs +9 -0
- package/dist/cjs/summarization/shared.cjs.map +1 -1
- package/dist/cjs/tools/local/CompileCheckTool.cjs +1 -1
- package/dist/cjs/tools/local/LocalCodingTools.cjs +1 -1
- package/dist/cjs/utils/events.cjs +13 -0
- package/dist/cjs/utils/events.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +105 -0
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +13 -3
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +176 -55
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +15 -5
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +43 -2
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +41 -1
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/langfuseTraceShaping.mjs +7 -1
- package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +5 -9
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +2 -7
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/contextPressureMeter.mjs +52 -11
- package/dist/esm/llm/contextPressureMeter.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -1
- package/dist/esm/llm/invoke.mjs +2 -2
- package/dist/esm/llm/openai/index.mjs +2 -1
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +5 -4
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/preempt.mjs +2 -1
- package/dist/esm/llm/preempt.mjs.map +1 -1
- package/dist/esm/llm/prepareProviderRequest.mjs +9 -7
- package/dist/esm/llm/prepareProviderRequest.mjs.map +1 -1
- package/dist/esm/llm/providers.mjs +1 -1
- package/dist/esm/main.mjs +10 -7
- package/dist/esm/messages/alternation.mjs +2 -6
- package/dist/esm/messages/alternation.mjs.map +1 -1
- package/dist/esm/messages/budget.mjs +206 -8
- package/dist/esm/messages/budget.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +3 -9
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +23 -33
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +132 -43
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/index.mjs +2 -0
- package/dist/esm/messages/prune.mjs +12 -7
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/reasoningTypes.mjs +20 -0
- package/dist/esm/messages/reasoningTypes.mjs.map +1 -0
- package/dist/esm/messages/recency.mjs +16 -95
- package/dist/esm/messages/recency.mjs.map +1 -1
- package/dist/esm/messages/toolHistoryProjection.mjs +237 -0
- package/dist/esm/messages/toolHistoryProjection.mjs.map +1 -0
- package/dist/esm/messages/toolResultTypes.mjs +141 -4
- package/dist/esm/messages/toolResultTypes.mjs.map +1 -1
- package/dist/esm/run.mjs +5 -4
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +6 -4
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/session/JsonlSessionStore.mjs +3 -0
- package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
- package/dist/esm/session/index.mjs +1 -1
- package/dist/esm/session/sessionProjection.mjs +72 -0
- package/dist/esm/session/sessionProjection.mjs.map +1 -0
- package/dist/esm/stream.mjs +15 -14
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/index.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +19 -9
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/summarization/shared.mjs +9 -1
- package/dist/esm/summarization/shared.mjs.map +1 -1
- package/dist/esm/tools/local/CompileCheckTool.mjs +1 -1
- package/dist/esm/tools/local/LocalCodingTools.mjs +1 -1
- package/dist/esm/utils/events.mjs +13 -0
- package/dist/esm/utils/events.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +105 -1
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +13 -1
- package/dist/types/graphs/Graph.d.ts +27 -0
- package/dist/types/hooks/types.d.ts +4 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/langfuse.d.ts +5 -0
- package/dist/types/langfuseToolOutputTracing.d.ts +2 -0
- package/dist/types/llm/contextPressureMeter.d.ts +2 -1
- package/dist/types/llm/prepareProviderRequest.d.ts +7 -1
- package/dist/types/messages/budget.d.ts +15 -10
- package/dist/types/messages/core.d.ts +8 -17
- package/dist/types/messages/format.d.ts +3 -2
- package/dist/types/messages/index.d.ts +1 -1
- package/dist/types/messages/prune.d.ts +1 -1
- package/dist/types/messages/reasoningTypes.d.ts +7 -0
- package/dist/types/messages/recency.d.ts +3 -0
- package/dist/types/messages/toolHistoryProjection.d.ts +65 -0
- package/dist/types/messages/toolResultTypes.d.ts +31 -1
- package/dist/types/session/sessionProjection.d.ts +10 -0
- package/dist/types/summarization/index.d.ts +2 -1
- package/dist/types/summarization/node.d.ts +1 -0
- package/dist/types/summarization/shared.d.ts +12 -0
- package/dist/types/types/graph.d.ts +20 -0
- package/dist/types/types/run.d.ts +4 -0
- package/dist/types/types/summarize.d.ts +4 -1
- package/dist/types/utils/tokens.d.ts +2 -1
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +34 -3
- package/src/graphs/Graph.ts +465 -147
- package/src/graphs/MultiAgentGraph.ts +28 -6
- package/src/hooks/types.ts +4 -1
- package/src/index.ts +1 -0
- package/src/langfuse.ts +67 -9
- package/src/langfuseToolOutputTracing.ts +91 -0
- package/src/langfuseTraceShaping.ts +17 -4
- package/src/llm/anthropic/utils/message_inputs.ts +5 -10
- package/src/llm/bedrock/utils/message_inputs.ts +2 -8
- package/src/llm/contextPressureMeter.ts +80 -12
- package/src/llm/openai/utils/index.ts +5 -4
- package/src/llm/prepareProviderRequest.ts +21 -16
- package/src/messages/alternation.ts +2 -15
- package/src/messages/budget.ts +439 -23
- package/src/messages/cache.ts +8 -9
- package/src/messages/core.ts +65 -95
- package/src/messages/format.ts +262 -78
- package/src/messages/index.ts +1 -1
- package/src/messages/prune.ts +31 -12
- package/src/messages/reasoningTypes.ts +23 -0
- package/src/messages/recency.ts +35 -179
- package/src/messages/toolHistoryProjection.ts +460 -0
- package/src/messages/toolResultTypes.ts +331 -100
- package/src/run.ts +10 -2
- package/src/session/AgentSession.ts +33 -16
- package/src/session/JsonlSessionStore.ts +6 -0
- package/src/session/sessionProjection.ts +126 -0
- package/src/stream.ts +14 -19
- package/src/summarization/index.ts +2 -0
- package/src/summarization/node.ts +62 -13
- package/src/summarization/shared.ts +23 -0
- package/src/types/graph.ts +20 -0
- package/src/types/run.ts +4 -0
- package/src/types/summarize.ts +4 -1
- package/src/utils/events.ts +19 -0
- package/src/utils/tokens.ts +183 -0
|
@@ -21,9 +21,12 @@ import type {
|
|
|
21
21
|
SessionEntry,
|
|
22
22
|
SessionForkOptions,
|
|
23
23
|
} from './types';
|
|
24
|
-
import { isFadingTier } from '@/messages/fading';
|
|
25
24
|
import type { HookRegistry } from '@/hooks';
|
|
26
25
|
import type * as t from '@/types';
|
|
26
|
+
import {
|
|
27
|
+
deriveSessionMessages,
|
|
28
|
+
releaseSessionProjection,
|
|
29
|
+
} from './sessionProjection';
|
|
27
30
|
import { createSummarizeNode } from '@/summarization/node';
|
|
28
31
|
import { resolveStreamLimits } from '@/llm/streamLimits';
|
|
29
32
|
import { JsonlSessionStore } from './JsonlSessionStore';
|
|
@@ -31,6 +34,7 @@ import { AgentContext } from '@/agents/AgentContext';
|
|
|
31
34
|
import { ContentTypes, GraphEvents } from '@/common';
|
|
32
35
|
import { createRunId, createSessionId } from './ids';
|
|
33
36
|
import { deriveMessages } from './deriveMessages';
|
|
37
|
+
import { isFadingTier } from '@/messages/fading';
|
|
34
38
|
import { createRunHandlers } from './handlers';
|
|
35
39
|
import { Run } from '@/run';
|
|
36
40
|
|
|
@@ -377,7 +381,9 @@ function fadingGenerationKey(scopeKey: string, agentKey: string): string {
|
|
|
377
381
|
|
|
378
382
|
function fadingGenerationScope(generationKey: string): string {
|
|
379
383
|
const parsed: unknown = JSON.parse(generationKey);
|
|
380
|
-
return Array.isArray(parsed) && typeof parsed[0] === 'string'
|
|
384
|
+
return Array.isArray(parsed) && typeof parsed[0] === 'string'
|
|
385
|
+
? parsed[0]
|
|
386
|
+
: '';
|
|
381
387
|
}
|
|
382
388
|
|
|
383
389
|
/** What a run saw when it started, so its capture can tell stale from current. */
|
|
@@ -404,10 +410,7 @@ function mergeFadingTier(
|
|
|
404
410
|
if (incoming == null) {
|
|
405
411
|
return { ...current };
|
|
406
412
|
}
|
|
407
|
-
const budgetTokens = Math.min(
|
|
408
|
-
current.budgetTokens,
|
|
409
|
-
incoming.budgetTokens
|
|
410
|
-
);
|
|
413
|
+
const budgetTokens = Math.min(current.budgetTokens, incoming.budgetTokens);
|
|
411
414
|
const masked = current.masked || incoming.masked;
|
|
412
415
|
const latched =
|
|
413
416
|
current.latched === true ||
|
|
@@ -1010,6 +1013,9 @@ export class AgentSession {
|
|
|
1010
1013
|
}
|
|
1011
1014
|
|
|
1012
1015
|
getSessionStore(): JsonlSessionStore | undefined {
|
|
1016
|
+
if (this.store) {
|
|
1017
|
+
releaseSessionProjection(this.store);
|
|
1018
|
+
}
|
|
1013
1019
|
return this.store;
|
|
1014
1020
|
}
|
|
1015
1021
|
|
|
@@ -1017,7 +1023,10 @@ export class AgentSession {
|
|
|
1017
1023
|
return this.checkpointing.checkpointer;
|
|
1018
1024
|
}
|
|
1019
1025
|
|
|
1020
|
-
private getFadingState(
|
|
1026
|
+
private getFadingState(
|
|
1027
|
+
threadId: string,
|
|
1028
|
+
checkpointNs = ''
|
|
1029
|
+
): {
|
|
1021
1030
|
fadingTier?: t.FadingTier;
|
|
1022
1031
|
fadingTiers?: t.FadingTiers;
|
|
1023
1032
|
} {
|
|
@@ -1058,8 +1067,7 @@ export class AgentSession {
|
|
|
1058
1067
|
|
|
1059
1068
|
private trimAlternateFadingStates(): void {
|
|
1060
1069
|
while (
|
|
1061
|
-
this.alternateThreadFadingState.size >
|
|
1062
|
-
MAX_ALTERNATE_THREAD_FADING_STATES
|
|
1070
|
+
this.alternateThreadFadingState.size > MAX_ALTERNATE_THREAD_FADING_STATES
|
|
1063
1071
|
) {
|
|
1064
1072
|
const oldestThreadId = this.alternateThreadFadingState.keys().next();
|
|
1065
1073
|
if (oldestThreadId.done === true) {
|
|
@@ -1172,7 +1180,10 @@ export class AgentSession {
|
|
|
1172
1180
|
};
|
|
1173
1181
|
const advanceGeneration = (agentKey: string): void => {
|
|
1174
1182
|
const key = fadingGenerationKey(scopeKey, agentKey);
|
|
1175
|
-
this.fadingGenerations.set(
|
|
1183
|
+
this.fadingGenerations.set(
|
|
1184
|
+
key,
|
|
1185
|
+
(this.fadingGenerations.get(key) ?? 0) + 1
|
|
1186
|
+
);
|
|
1176
1187
|
};
|
|
1177
1188
|
const mergeCapturedTier = (
|
|
1178
1189
|
existing: t.FadingTier | undefined,
|
|
@@ -1218,7 +1229,11 @@ export class AgentSession {
|
|
|
1218
1229
|
if (didResetTier) {
|
|
1219
1230
|
advanceGeneration('');
|
|
1220
1231
|
}
|
|
1221
|
-
nextTier = mergeCapturedTier(
|
|
1232
|
+
nextTier = mergeCapturedTier(
|
|
1233
|
+
current.fadingTier,
|
|
1234
|
+
incomingTier,
|
|
1235
|
+
didResetTier
|
|
1236
|
+
);
|
|
1222
1237
|
}
|
|
1223
1238
|
if (threadId === this.threadId && checkpointNs === '') {
|
|
1224
1239
|
this.fadingTier = nextTier;
|
|
@@ -1408,8 +1423,8 @@ export class AgentSession {
|
|
|
1408
1423
|
handlerResult.events.push(streamEvent);
|
|
1409
1424
|
onEvent?.(streamEvent);
|
|
1410
1425
|
};
|
|
1411
|
-
const sessionState =
|
|
1412
|
-
isSessionThread ?
|
|
1426
|
+
const sessionState = deriveSessionMessages(
|
|
1427
|
+
isSessionThread ? this.store : undefined
|
|
1413
1428
|
);
|
|
1414
1429
|
const fadingState = this.getFadingState(threadId, checkpointNs);
|
|
1415
1430
|
let run: Run<t.IState> | undefined;
|
|
@@ -1690,7 +1705,9 @@ export class AgentSession {
|
|
|
1690
1705
|
throw new Error('Cannot compact an ephemeral session');
|
|
1691
1706
|
}
|
|
1692
1707
|
const activePath = path ?? store.getPath();
|
|
1693
|
-
const sessionState =
|
|
1708
|
+
const sessionState = path
|
|
1709
|
+
? deriveMessages(path)
|
|
1710
|
+
: deriveSessionMessages(store);
|
|
1694
1711
|
const messageEntries = activePath.filter(isMessageEntry);
|
|
1695
1712
|
if (sessionState.messages.length === 0) {
|
|
1696
1713
|
return undefined;
|
|
@@ -1819,8 +1836,8 @@ export class AgentSession {
|
|
|
1819
1836
|
threadId,
|
|
1820
1837
|
userHandlers: this.runConfig.customHandlers,
|
|
1821
1838
|
});
|
|
1822
|
-
const sessionState =
|
|
1823
|
-
isSessionThread ?
|
|
1839
|
+
const sessionState = deriveSessionMessages(
|
|
1840
|
+
isSessionThread ? this.store : undefined
|
|
1824
1841
|
);
|
|
1825
1842
|
const fadingState = this.getFadingState(threadId, checkpointNs);
|
|
1826
1843
|
let run: Run<t.IState> | undefined;
|
|
@@ -35,6 +35,10 @@ import {
|
|
|
35
35
|
serializeMessage,
|
|
36
36
|
toJsonValue,
|
|
37
37
|
} from './messageSerialization';
|
|
38
|
+
import {
|
|
39
|
+
initializeSessionProjection,
|
|
40
|
+
shareSessionProjectionOwnership,
|
|
41
|
+
} from './sessionProjection';
|
|
38
42
|
import { createEntryId, createSessionId, createTimestamp } from './ids';
|
|
39
43
|
|
|
40
44
|
const SESSION_VERSION = 1;
|
|
@@ -126,6 +130,7 @@ export class JsonlSessionStore {
|
|
|
126
130
|
this.path = params.path;
|
|
127
131
|
this.header = params.header;
|
|
128
132
|
this.entries = sortEntries(params.entries);
|
|
133
|
+
initializeSessionProjection(this, this.entries);
|
|
129
134
|
}
|
|
130
135
|
|
|
131
136
|
static getDefaultRoot(): string {
|
|
@@ -573,6 +578,7 @@ export class JsonlSessionStore {
|
|
|
573
578
|
name: options.name ?? this.header.name,
|
|
574
579
|
parentSession: this.path,
|
|
575
580
|
});
|
|
581
|
+
shareSessionProjectionOwnership(this, newStore);
|
|
576
582
|
const pathEntries = target ? this.getPath(target.id) : [];
|
|
577
583
|
for (const entry of pathEntries) {
|
|
578
584
|
await newStore.appendExistingEntry(entry);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { DerivedSessionMessages } from './deriveMessages';
|
|
2
|
+
import type { SessionEntry, SessionStateEntry } from './types';
|
|
3
|
+
import type { JsonlSessionStore } from './JsonlSessionStore';
|
|
4
|
+
import { deriveMessages } from './deriveMessages';
|
|
5
|
+
|
|
6
|
+
interface SessionProjection {
|
|
7
|
+
entries: readonly SessionEntry[];
|
|
8
|
+
ownership: { exposed: boolean };
|
|
9
|
+
indexedCount: number;
|
|
10
|
+
byId: Map<string, SessionEntry>;
|
|
11
|
+
lastState?: SessionStateEntry;
|
|
12
|
+
lastMessage?: SessionEntry;
|
|
13
|
+
leaf?: SessionEntry;
|
|
14
|
+
projected: SessionEntry[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const projections = new WeakMap<JsonlSessionStore, SessionProjection>();
|
|
18
|
+
|
|
19
|
+
/** Internal: only AgentSession's unexposed stores may reuse log topology. */
|
|
20
|
+
export function initializeSessionProjection(
|
|
21
|
+
store: JsonlSessionStore,
|
|
22
|
+
entries: readonly SessionEntry[]
|
|
23
|
+
): void {
|
|
24
|
+
projections.set(store, {
|
|
25
|
+
entries,
|
|
26
|
+
ownership: { exposed: false },
|
|
27
|
+
indexedCount: 0,
|
|
28
|
+
byId: new Map(),
|
|
29
|
+
projected: [],
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Forks share entry references, so exposing either disables the whole family. */
|
|
34
|
+
export function shareSessionProjectionOwnership(
|
|
35
|
+
source: JsonlSessionStore,
|
|
36
|
+
target: JsonlSessionStore
|
|
37
|
+
): void {
|
|
38
|
+
const sourceProjection = projections.get(source);
|
|
39
|
+
const targetProjection = projections.get(target);
|
|
40
|
+
if (sourceProjection && targetProjection) {
|
|
41
|
+
targetProjection.ownership = sourceProjection.ownership;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function releaseSessionProjection(store: JsonlSessionStore): void {
|
|
46
|
+
const projection = projections.get(store);
|
|
47
|
+
if (projection) {
|
|
48
|
+
projection.ownership.exposed = true;
|
|
49
|
+
projection.byId.clear();
|
|
50
|
+
projection.projected = [];
|
|
51
|
+
projection.leaf = undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function indexAppends(projection: SessionProjection): boolean {
|
|
56
|
+
for (
|
|
57
|
+
;
|
|
58
|
+
projection.indexedCount < projection.entries.length;
|
|
59
|
+
projection.indexedCount++
|
|
60
|
+
) {
|
|
61
|
+
const entry = projection.entries[projection.indexedCount];
|
|
62
|
+
if (projection.byId.has(entry.id)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
projection.byId.set(entry.id, entry);
|
|
66
|
+
if (entry.type === 'session_state') {
|
|
67
|
+
projection.lastState = entry;
|
|
68
|
+
} else if (entry.type === 'message' || entry.type === 'summary') {
|
|
69
|
+
projection.lastMessage = entry;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function projectLeaf(projection: SessionProjection): void {
|
|
76
|
+
const leafId = projection.lastState
|
|
77
|
+
? projection.lastState.data.leafId
|
|
78
|
+
: projection.lastMessage?.id;
|
|
79
|
+
const leaf =
|
|
80
|
+
leafId != null && leafId !== '' ? projection.byId.get(leafId) : undefined;
|
|
81
|
+
if (leaf === projection.leaf) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const delta: SessionEntry[] = [];
|
|
85
|
+
let current = leaf;
|
|
86
|
+
while (current && current !== projection.leaf) {
|
|
87
|
+
if (current.type === 'message' || current.type === 'summary') {
|
|
88
|
+
delta.push(current);
|
|
89
|
+
}
|
|
90
|
+
if (current.type === 'summary') {
|
|
91
|
+
current = undefined;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
current =
|
|
95
|
+
current.parentId == null
|
|
96
|
+
? undefined
|
|
97
|
+
: projection.byId.get(current.parentId);
|
|
98
|
+
}
|
|
99
|
+
if (!current) {
|
|
100
|
+
projection.projected = [];
|
|
101
|
+
}
|
|
102
|
+
for (let i = delta.length - 1; i >= 0; i--) {
|
|
103
|
+
projection.projected.push(delta[i]);
|
|
104
|
+
}
|
|
105
|
+
projection.leaf = leaf;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Reuses log topology, but materializes fresh mutable messages for every run. */
|
|
109
|
+
export function deriveSessionMessages(
|
|
110
|
+
store: JsonlSessionStore | undefined
|
|
111
|
+
): DerivedSessionMessages {
|
|
112
|
+
if (!store) {
|
|
113
|
+
return { messages: [] };
|
|
114
|
+
}
|
|
115
|
+
const projection = projections.get(store);
|
|
116
|
+
if (!projection || projection.ownership.exposed) {
|
|
117
|
+
releaseSessionProjection(store);
|
|
118
|
+
return deriveMessages(store.getPath());
|
|
119
|
+
}
|
|
120
|
+
if (!indexAppends(projection)) {
|
|
121
|
+
releaseSessionProjection(store);
|
|
122
|
+
return deriveMessages(store.getPath());
|
|
123
|
+
}
|
|
124
|
+
projectLeaf(projection);
|
|
125
|
+
return deriveMessages(projection.projected);
|
|
126
|
+
}
|
package/src/stream.ts
CHANGED
|
@@ -1014,18 +1014,6 @@ function pruneEagerToolCallChunkStates(args: {
|
|
|
1014
1014
|
}
|
|
1015
1015
|
}
|
|
1016
1016
|
|
|
1017
|
-
function isEagerToolChunkStateComplete(
|
|
1018
|
-
state: t.EagerEventToolCallChunkState
|
|
1019
|
-
): boolean {
|
|
1020
|
-
return (
|
|
1021
|
-
state.id != null &&
|
|
1022
|
-
state.id !== '' &&
|
|
1023
|
-
state.name != null &&
|
|
1024
|
-
state.name !== '' &&
|
|
1025
|
-
coerceRecordArgs(state.argsText) != null
|
|
1026
|
-
);
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
1017
|
function mergeToolCallArgsText(existing: string, incoming: string): string {
|
|
1030
1018
|
if (incoming === '') {
|
|
1031
1019
|
return existing;
|
|
@@ -1180,6 +1168,7 @@ function getStreamedReadyToolCalls(args: {
|
|
|
1180
1168
|
key: string;
|
|
1181
1169
|
state: t.EagerEventToolCallChunkState;
|
|
1182
1170
|
sealedByAdapter: boolean;
|
|
1171
|
+
parsedArgs: ToolCall['args'];
|
|
1183
1172
|
}> = [];
|
|
1184
1173
|
|
|
1185
1174
|
for (const [key, state] of graph.eagerEventToolCallChunks) {
|
|
@@ -1190,7 +1179,12 @@ function getStreamedReadyToolCalls(args: {
|
|
|
1190
1179
|
graph.eagerEventToolCallChunks.delete(key);
|
|
1191
1180
|
continue;
|
|
1192
1181
|
}
|
|
1193
|
-
if (
|
|
1182
|
+
if (
|
|
1183
|
+
state.id == null ||
|
|
1184
|
+
state.id === '' ||
|
|
1185
|
+
state.name == null ||
|
|
1186
|
+
state.name === ''
|
|
1187
|
+
) {
|
|
1194
1188
|
continue;
|
|
1195
1189
|
}
|
|
1196
1190
|
const isSealedByLaterChunk =
|
|
@@ -1209,10 +1203,15 @@ function getStreamedReadyToolCalls(args: {
|
|
|
1209
1203
|
isSealedByLaterChunk ||
|
|
1210
1204
|
isSealedExplicitly
|
|
1211
1205
|
) {
|
|
1206
|
+
const parsedArgs = coerceRecordArgs(state.argsText);
|
|
1207
|
+
if (parsedArgs == null) {
|
|
1208
|
+
continue;
|
|
1209
|
+
}
|
|
1212
1210
|
readyEntries.push({
|
|
1213
1211
|
key,
|
|
1214
1212
|
state,
|
|
1215
1213
|
sealedByAdapter: isSealedExplicitly || seal?.kind === 'all',
|
|
1214
|
+
parsedArgs,
|
|
1216
1215
|
});
|
|
1217
1216
|
}
|
|
1218
1217
|
}
|
|
@@ -1232,11 +1231,7 @@ function getStreamedReadyToolCalls(args: {
|
|
|
1232
1231
|
|
|
1233
1232
|
return readyEntries
|
|
1234
1233
|
.sort((left, right) => (left.state.index ?? 0) - (right.state.index ?? 0))
|
|
1235
|
-
.flatMap(({ state, sealedByAdapter }) => {
|
|
1236
|
-
const args = coerceRecordArgs(state.argsText);
|
|
1237
|
-
if (args == null) {
|
|
1238
|
-
return [];
|
|
1239
|
-
}
|
|
1234
|
+
.flatMap(({ state, sealedByAdapter, parsedArgs }) => {
|
|
1240
1235
|
// The final request's args come from LangChain's canonical verbatim
|
|
1241
1236
|
// concatenation of fragments, while `argsText` reconciles provider
|
|
1242
1237
|
// quirks with lossy heuristics that can also swallow legitimately
|
|
@@ -1267,7 +1262,7 @@ function getStreamedReadyToolCalls(args: {
|
|
|
1267
1262
|
{
|
|
1268
1263
|
id: state.id,
|
|
1269
1264
|
name: state.name ?? '',
|
|
1270
|
-
args,
|
|
1265
|
+
args: parsedArgs,
|
|
1271
1266
|
},
|
|
1272
1267
|
];
|
|
1273
1268
|
});
|
|
@@ -9,7 +9,9 @@ export {
|
|
|
9
9
|
buildSummarizationInstruction,
|
|
10
10
|
buildSummaryCarrierText,
|
|
11
11
|
separateSummarizationParameters,
|
|
12
|
+
ManualSummarizationSkippedError,
|
|
12
13
|
} from './shared';
|
|
14
|
+
export type { ManualSummarizationSkipReason } from './shared';
|
|
13
15
|
|
|
14
16
|
const VALID_TRIGGER_TYPES = [
|
|
15
17
|
'token_ratio',
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
buildSummaryCarrierText,
|
|
15
15
|
separateSummarizationParameters,
|
|
16
16
|
buildSummarizationInstruction,
|
|
17
|
+
ManualSummarizationSkippedError,
|
|
17
18
|
} from './shared';
|
|
18
19
|
import {
|
|
19
20
|
addTailCacheControl,
|
|
@@ -1026,6 +1027,14 @@ function findStreamLimitAbortReason(
|
|
|
1026
1027
|
return undefined;
|
|
1027
1028
|
}
|
|
1028
1029
|
|
|
1030
|
+
/** Wording for the history-preserved log and completion, by request reason. */
|
|
1031
|
+
const PRESERVATION_REASONS: Partial<
|
|
1032
|
+
Record<NonNullable<t.SummarizationNodeInput['reason']>, string>
|
|
1033
|
+
> = {
|
|
1034
|
+
overflow: 'overflow recovery',
|
|
1035
|
+
manual: 'manual compaction',
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1029
1038
|
export function createSummarizeNode({
|
|
1030
1039
|
agentContext,
|
|
1031
1040
|
graph: adapterGraph,
|
|
@@ -1037,7 +1046,11 @@ export function createSummarizeNode({
|
|
|
1037
1046
|
summarizationRequest?: t.SummarizationNodeInput;
|
|
1038
1047
|
},
|
|
1039
1048
|
config?: RunnableConfig
|
|
1040
|
-
): Promise<{
|
|
1049
|
+
): Promise<{
|
|
1050
|
+
summarizationRequest: undefined;
|
|
1051
|
+
messages?: BaseMessage[];
|
|
1052
|
+
manualSummary?: string;
|
|
1053
|
+
}> => {
|
|
1041
1054
|
/** The breaker signal is captured at node ENTRY, before dispatchRunStep,
|
|
1042
1055
|
* ON_SUMMARIZE_START, or PreCompact awaits: a sibling's trip plus a
|
|
1043
1056
|
* prompt next run can reset the controller during one of those awaits,
|
|
@@ -1114,6 +1127,12 @@ export function createSummarizeNode({
|
|
|
1114
1127
|
* its recursion budget dispatching empty summary steps.
|
|
1115
1128
|
*/
|
|
1116
1129
|
if (agentContext.summarizationExhausted) {
|
|
1130
|
+
if (request.reason === 'manual') {
|
|
1131
|
+
throw new ManualSummarizationSkippedError(
|
|
1132
|
+
'exhausted',
|
|
1133
|
+
'Compaction skipped: consecutive summarization attempts produced no usable summary'
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1117
1136
|
emitAgentLog(
|
|
1118
1137
|
config,
|
|
1119
1138
|
'warn',
|
|
@@ -1131,6 +1150,12 @@ export function createSummarizeNode({
|
|
|
1131
1150
|
|
|
1132
1151
|
const maxCtx = agentContext.maxContextTokens ?? 0;
|
|
1133
1152
|
if (maxCtx > 0 && agentContext.instructionTokens >= maxCtx) {
|
|
1153
|
+
if (request.reason === 'manual') {
|
|
1154
|
+
throw new ManualSummarizationSkippedError(
|
|
1155
|
+
'instructions_exceed_budget',
|
|
1156
|
+
'Compaction skipped: instructions and tool definitions exceed the context budget'
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1134
1159
|
emitAgentLog(
|
|
1135
1160
|
config,
|
|
1136
1161
|
'warn',
|
|
@@ -1167,6 +1192,16 @@ export function createSummarizeNode({
|
|
|
1167
1192
|
const runnableConfig = config ?? graph.config;
|
|
1168
1193
|
|
|
1169
1194
|
const retainRecent = agentContext.summarizationConfig?.retainRecent;
|
|
1195
|
+
/**
|
|
1196
|
+
* A manual summary is the user asking for the whole history to become a
|
|
1197
|
+
* checkpoint, so the default recency window does not apply to it. A
|
|
1198
|
+
* `retainRecent` the host configured explicitly still keeps its tail,
|
|
1199
|
+
* with the ordinary turn default when it only sets a token cap.
|
|
1200
|
+
*/
|
|
1201
|
+
const retainTurns =
|
|
1202
|
+
request.reason === 'manual' && retainRecent == null
|
|
1203
|
+
? 0
|
|
1204
|
+
: (retainRecent?.turns ?? DEFAULT_RETAIN_RECENT_TURNS);
|
|
1170
1205
|
const recencyTokenCounter =
|
|
1171
1206
|
agentContext.contextPressureTokenCounts?.count ??
|
|
1172
1207
|
agentContext.tokenCounter;
|
|
@@ -1175,7 +1210,8 @@ export function createSummarizeNode({
|
|
|
1175
1210
|
tailStartIndex,
|
|
1176
1211
|
usedIntraTurnFallback,
|
|
1177
1212
|
} = splitAtRecencyBoundary(restoredMessages, {
|
|
1178
|
-
|
|
1213
|
+
provider: agentContext.provider,
|
|
1214
|
+
turns: retainTurns,
|
|
1179
1215
|
tokens: retainRecent?.tokens,
|
|
1180
1216
|
tokenCounter: recencyTokenCounter,
|
|
1181
1217
|
intraTurnTokens: resolveIntraTurnRetainTokens({
|
|
@@ -1198,6 +1234,12 @@ export function createSummarizeNode({
|
|
|
1198
1234
|
const messagesToRetain = state.messages.slice(tailStartIndex);
|
|
1199
1235
|
|
|
1200
1236
|
if (messagesToRefine.length === 0) {
|
|
1237
|
+
if (request.reason === 'manual') {
|
|
1238
|
+
throw new ManualSummarizationSkippedError(
|
|
1239
|
+
'nothing_to_summarize',
|
|
1240
|
+
'Nothing to compact: the retained recent turns cover the whole conversation'
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1201
1243
|
/**
|
|
1202
1244
|
* Recency window covers the entire conversation — there is no
|
|
1203
1245
|
* older content to summarize. Skipping prevents the model from
|
|
@@ -1213,7 +1255,7 @@ export function createSummarizeNode({
|
|
|
1213
1255
|
'Summarization skipped — recency window retains all messages',
|
|
1214
1256
|
{
|
|
1215
1257
|
messagesRetained: messagesToRetain.length,
|
|
1216
|
-
retainTurns
|
|
1258
|
+
retainTurns,
|
|
1217
1259
|
},
|
|
1218
1260
|
{ runId: graph.runId, agentId: request.agentId }
|
|
1219
1261
|
);
|
|
@@ -1290,7 +1332,10 @@ export function createSummarizeNode({
|
|
|
1290
1332
|
threadId,
|
|
1291
1333
|
agentId: request.agentId,
|
|
1292
1334
|
messagesBeforeCount: messagesToRefine.length,
|
|
1293
|
-
trigger:
|
|
1335
|
+
trigger:
|
|
1336
|
+
request.reason === 'manual'
|
|
1337
|
+
? 'manual'
|
|
1338
|
+
: (agentContext.summarizationConfig?.trigger?.type ?? 'default'),
|
|
1294
1339
|
},
|
|
1295
1340
|
sessionId,
|
|
1296
1341
|
}).catch(() => {
|
|
@@ -1340,7 +1385,7 @@ export function createSummarizeNode({
|
|
|
1340
1385
|
compaction_semantic_index_entries: semanticIndex.entryCount,
|
|
1341
1386
|
compaction_semantic_index_chars: semanticIndex.charCount,
|
|
1342
1387
|
compaction_semantic_index_omitted_entries:
|
|
1343
|
-
|
|
1388
|
+
semanticIndex.omittedEntryCount,
|
|
1344
1389
|
/**
|
|
1345
1390
|
* Per-call model attribution for usage consumers (the subagent
|
|
1346
1391
|
* usage-capture handler): the summarizer's model can differ from
|
|
@@ -1398,19 +1443,21 @@ export function createSummarizeNode({
|
|
|
1398
1443
|
/**
|
|
1399
1444
|
* The metadata stub describes the history rather than summarizing it, so
|
|
1400
1445
|
* committing it means removing the head and keeping nothing of what it
|
|
1401
|
-
* said. That trade is never worth making to paper over an overflow
|
|
1402
|
-
*
|
|
1403
|
-
*
|
|
1404
|
-
*
|
|
1446
|
+
* said. That trade is never worth making to paper over an overflow, and
|
|
1447
|
+
* never for a manual compaction, which would replace the whole history
|
|
1448
|
+
* with a message count: the recovery would "succeed" only by destroying
|
|
1449
|
+
* the conversation it was supposed to preserve. Leave state untouched
|
|
1450
|
+
* and let the provider error surface instead.
|
|
1405
1451
|
*/
|
|
1406
1452
|
if (
|
|
1407
1453
|
usedMetadataStub === true &&
|
|
1408
|
-
(request.reason === 'overflow' ||
|
|
1454
|
+
(request.reason === 'overflow' ||
|
|
1455
|
+
request.reason === 'manual' ||
|
|
1456
|
+
usedIntraTurnFallback)
|
|
1409
1457
|
) {
|
|
1410
1458
|
const preservationReason =
|
|
1411
|
-
request.reason
|
|
1412
|
-
|
|
1413
|
-
: 'intra-turn compaction';
|
|
1459
|
+
PRESERVATION_REASONS[request.reason ?? 'trigger'] ??
|
|
1460
|
+
'intra-turn compaction';
|
|
1414
1461
|
log(
|
|
1415
1462
|
'warn',
|
|
1416
1463
|
`Summarization failed during ${preservationReason}; keeping history rather than replacing it with a metadata stub`
|
|
@@ -1570,6 +1617,8 @@ export function createSummarizeNode({
|
|
|
1570
1617
|
messagesToRetain.length > 0
|
|
1571
1618
|
? [createRemoveAllMessage(), ...messagesToRetain]
|
|
1572
1619
|
: [createRemoveAllMessage()],
|
|
1620
|
+
/** The run's result, for trace roots that otherwise see no reply. */
|
|
1621
|
+
...(request.reason === 'manual' ? { manualSummary: summaryText } : {}),
|
|
1573
1622
|
};
|
|
1574
1623
|
};
|
|
1575
1624
|
}
|
|
@@ -128,3 +128,26 @@ export function buildSummarizationInstruction(
|
|
|
128
128
|
}
|
|
129
129
|
return parts.join('');
|
|
130
130
|
}
|
|
131
|
+
|
|
132
|
+
/** Why a summarize-only run could not attempt its summary. */
|
|
133
|
+
export type ManualSummarizationSkipReason =
|
|
134
|
+
| 'disabled'
|
|
135
|
+
| 'exhausted'
|
|
136
|
+
| 'instructions_exceed_budget'
|
|
137
|
+
| 'nothing_to_summarize';
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* A summarize-only run asked for a summary the graph could not attempt.
|
|
141
|
+
* Thrown rather than ending the run quietly: the summary is the whole result
|
|
142
|
+
* of such a run, so a host left with neither a summary nor an error would
|
|
143
|
+
* have nothing to show the user.
|
|
144
|
+
*/
|
|
145
|
+
export class ManualSummarizationSkippedError extends Error {
|
|
146
|
+
readonly reason: ManualSummarizationSkipReason;
|
|
147
|
+
|
|
148
|
+
constructor(reason: ManualSummarizationSkipReason, message: string) {
|
|
149
|
+
super(message);
|
|
150
|
+
this.name = 'ManualSummarizationSkippedError';
|
|
151
|
+
this.reason = reason;
|
|
152
|
+
}
|
|
153
|
+
}
|
package/src/types/graph.ts
CHANGED
|
@@ -81,6 +81,13 @@ export type SystemCallbacks = {
|
|
|
81
81
|
export type BaseGraphState = {
|
|
82
82
|
messages: BaseMessage[];
|
|
83
83
|
runStepState?: RunStepResumeState;
|
|
84
|
+
/**
|
|
85
|
+
* The summary a summarize-only run produced. Kept in state because such a
|
|
86
|
+
* run has no assistant reply: trace roots report it as the run's output
|
|
87
|
+
* instead of the retained tail or the raw state. Empty once any later run
|
|
88
|
+
* on the same checkpointed thread starts, so it never outlives its run.
|
|
89
|
+
*/
|
|
90
|
+
manualSummary?: string;
|
|
84
91
|
};
|
|
85
92
|
|
|
86
93
|
export type AgentSubgraphState = BaseGraphState & {
|
|
@@ -906,6 +913,19 @@ interface AgentInputFields {
|
|
|
906
913
|
*/
|
|
907
914
|
discoveredTools?: string[];
|
|
908
915
|
summarizationEnabled?: boolean;
|
|
916
|
+
/**
|
|
917
|
+
* Runs this agent as a summarize-only pass: the first model step requests
|
|
918
|
+
* summarization outright instead of consulting the trigger, and the step
|
|
919
|
+
* after the summary emits its context snapshot and ends the run without a
|
|
920
|
+
* model call. Hosts use it for user-initiated compaction, where the summary
|
|
921
|
+
* is the whole response. Requires `summarizationEnabled`: a run that cannot
|
|
922
|
+
* attempt the summary rejects with `ManualSummarizationSkippedError`
|
|
923
|
+
* rather than ending with nothing, and one whose provider calls all fail
|
|
924
|
+
* keeps the history and reports the failure on the summary step. A
|
|
925
|
+
* multi-agent workflow compiles to the agent that opted in alone; no other
|
|
926
|
+
* agent runs.
|
|
927
|
+
*/
|
|
928
|
+
summarizeOnly?: boolean;
|
|
909
929
|
summarizationConfig?: SummarizationConfig;
|
|
910
930
|
/**
|
|
911
931
|
* Optional host-supplied, user-visible guidance for compaction. The SDK
|
package/src/types/run.ts
CHANGED
|
@@ -475,6 +475,10 @@ export type TokenBudgetBreakdown = {
|
|
|
475
475
|
toolTokenCounts?: Record<string, number>;
|
|
476
476
|
/** Names of counted tools that are deferred (`defer_loading`) and discovered. */
|
|
477
477
|
deferredToolNames?: string[];
|
|
478
|
+
/** Calibrated retained tool traffic: tool results plus assistant turns made only of tool calls, inline provider tool results and reasoning. A subset of messageTokens, not an additional budget category. */
|
|
479
|
+
toolMessageTokens?: number;
|
|
480
|
+
/** Per-tool result-message share; excludes assistant invocation overhead. */
|
|
481
|
+
toolMessageTokenCounts?: Record<string, number>;
|
|
478
482
|
};
|
|
479
483
|
|
|
480
484
|
export type EventStreamOptions = {
|
package/src/types/summarize.ts
CHANGED
|
@@ -127,8 +127,11 @@ export interface SummarizationNodeInput {
|
|
|
127
127
|
* is compacting to recover. When summarization is not enabled, this
|
|
128
128
|
* variant performs no model call — the corrected budget alone is what the
|
|
129
129
|
* retry needs.
|
|
130
|
+
* - `manual`: the host asked for a summary outright (a summarize-only run).
|
|
131
|
+
* The recency window is not applied unless the host configured
|
|
132
|
+
* `retainRecent` explicitly, so the summary replaces the whole history.
|
|
130
133
|
*/
|
|
131
|
-
reason?: 'trigger' | 'overflow';
|
|
134
|
+
reason?: 'trigger' | 'overflow' | 'manual';
|
|
132
135
|
/**
|
|
133
136
|
* Whether an overflow recovery may spend a summarization model call.
|
|
134
137
|
*
|
package/src/utils/events.ts
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
// src/utils/events.ts
|
|
3
3
|
import { dispatchCustomEvent } from '@langchain/core/callbacks/dispatch';
|
|
4
4
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
5
|
+
import type { ToolExecuteBatchRequest } from '@/types/tools';
|
|
5
6
|
import type { AgentLogEvent } from '@/types/graph';
|
|
7
|
+
import { traceHostToolResults } from '@/langfuse';
|
|
6
8
|
import { GraphEvents } from '@/common';
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -15,6 +17,23 @@ export async function safeDispatchCustomEvent(
|
|
|
15
17
|
config?: RunnableConfig
|
|
16
18
|
): Promise<boolean | void> {
|
|
17
19
|
try {
|
|
20
|
+
if (event === GraphEvents.ON_TOOL_EXECUTE) {
|
|
21
|
+
const request = payload as ToolExecuteBatchRequest;
|
|
22
|
+
payload = {
|
|
23
|
+
...request,
|
|
24
|
+
resolve: (
|
|
25
|
+
results: Parameters<ToolExecuteBatchRequest['resolve']>[0]
|
|
26
|
+
): void => {
|
|
27
|
+
void traceHostToolResults(request, results, config).then(
|
|
28
|
+
() => request.resolve(results),
|
|
29
|
+
() => {
|
|
30
|
+
console.warn('Failed to record host tool execution metadata');
|
|
31
|
+
request.resolve(results);
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
} satisfies ToolExecuteBatchRequest;
|
|
36
|
+
}
|
|
18
37
|
await dispatchCustomEvent(event, payload, config);
|
|
19
38
|
return true;
|
|
20
39
|
} catch (e) {
|