@librechat/agents 3.7.15 → 3.7.16
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 +91 -0
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/agents/projection.cjs +2 -1
- package/dist/cjs/agents/projection.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +47 -6
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +20 -7
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/preempt.cjs +1 -10
- package/dist/cjs/llm/preempt.cjs.map +1 -1
- package/dist/cjs/main.cjs +23 -3
- package/dist/cjs/messages/contextPruning.cjs +1 -1
- package/dist/cjs/messages/contextPruning.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +29 -1
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/fading.cjs +122 -0
- package/dist/cjs/messages/fading.cjs.map +1 -0
- package/dist/cjs/messages/format.cjs +1 -1
- package/dist/cjs/messages/index.cjs +1 -0
- package/dist/cjs/messages/prune.cjs +364 -236
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/run.cjs +27 -0
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +213 -8
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/session/JsonlSessionStore.cjs +41 -0
- package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
- package/dist/cjs/session/index.cjs +1 -1
- package/dist/cjs/stream.cjs +1 -1
- package/dist/cjs/summarization/node.cjs +1 -1
- package/dist/cjs/tools/ToolNode.cjs +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +2 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentReplay.cjs +2 -1
- package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
- package/dist/cjs/utils/index.cjs +1 -1
- package/dist/cjs/utils/truncation.cjs +9 -0
- package/dist/cjs/utils/truncation.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +92 -1
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/agents/projection.mjs +2 -1
- package/dist/esm/agents/projection.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +47 -6
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +20 -7
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +2 -2
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/preempt.mjs +1 -10
- package/dist/esm/llm/preempt.mjs.map +1 -1
- package/dist/esm/main.mjs +6 -5
- package/dist/esm/messages/contextPruning.mjs +1 -1
- package/dist/esm/messages/contextPruning.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +29 -2
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/fading.mjs +108 -0
- package/dist/esm/messages/fading.mjs.map +1 -0
- package/dist/esm/messages/format.mjs +1 -1
- package/dist/esm/messages/index.mjs +1 -0
- package/dist/esm/messages/prune.mjs +363 -235
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/run.mjs +27 -0
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +213 -8
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/session/JsonlSessionStore.mjs +41 -0
- package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
- package/dist/esm/session/index.mjs +1 -1
- package/dist/esm/stream.mjs +1 -1
- package/dist/esm/summarization/node.mjs +1 -1
- package/dist/esm/tools/ToolNode.mjs +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +2 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentReplay.mjs +2 -1
- package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +1 -1
- package/dist/esm/utils/truncation.mjs +7 -1
- package/dist/esm/utils/truncation.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +32 -1
- package/dist/types/agents/projection.d.ts +3 -1
- package/dist/types/graphs/Graph.d.ts +14 -1
- package/dist/types/graphs/MultiAgentGraph.d.ts +7 -0
- package/dist/types/messages/contextPruning.d.ts +2 -0
- package/dist/types/messages/core.d.ts +2 -0
- package/dist/types/messages/fading.d.ts +81 -0
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/messages/prune.d.ts +71 -24
- package/dist/types/run.d.ts +16 -0
- package/dist/types/session/AgentSession.d.ts +15 -0
- package/dist/types/session/JsonlSessionStore.d.ts +4 -1
- package/dist/types/session/types.d.ts +9 -0
- package/dist/types/tools/subagent/SubagentReplay.d.ts +3 -1
- package/dist/types/types/graph.d.ts +23 -0
- package/dist/types/types/run.d.ts +10 -0
- package/dist/types/utils/truncation.d.ts +9 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +175 -1
- package/src/agents/projection.ts +4 -0
- package/src/graphs/Graph.ts +97 -11
- package/src/graphs/MultiAgentGraph.ts +54 -6
- package/src/llm/openai/utils/index.ts +3 -1
- package/src/llm/preempt.ts +4 -17
- package/src/messages/contextPruning.ts +7 -1
- package/src/messages/core.ts +52 -0
- package/src/messages/fading.ts +301 -0
- package/src/messages/index.ts +1 -0
- package/src/messages/prune.ts +719 -507
- package/src/run.ts +52 -0
- package/src/session/AgentSession.ts +397 -8
- package/src/session/JsonlSessionStore.ts +71 -0
- package/src/session/types.ts +10 -0
- package/src/tools/subagent/SubagentExecutor.ts +6 -0
- package/src/tools/subagent/SubagentReplay.ts +15 -2
- package/src/types/graph.ts +25 -0
- package/src/types/run.ts +10 -0
- package/src/utils/truncation.ts +25 -0
package/src/run.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
} from '@langchain/core/messages';
|
|
23
23
|
import type { StringPromptValue } from '@langchain/core/prompt_values';
|
|
24
24
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
25
|
+
import { isFadingTier } from '@/messages/fading';
|
|
25
26
|
import type { AggregatedHookResult, HookRegistry } from '@/hooks';
|
|
26
27
|
import type { MultiAgentGraph } from '@/graphs/MultiAgentGraph';
|
|
27
28
|
import type { StandardGraph } from '@/graphs/Graph';
|
|
@@ -472,6 +473,10 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
472
473
|
private subagentTasks?: t.SubagentTaskConfig;
|
|
473
474
|
private indexTokenCountMap?: Record<string, number>;
|
|
474
475
|
calibrationRatio: number = 1;
|
|
476
|
+
fadingTier?: t.FadingTier;
|
|
477
|
+
fadingTiers: t.FadingTiers = {};
|
|
478
|
+
private fadingTierReset: boolean = false;
|
|
479
|
+
private fadingTierResetAgentIds: string[] = [];
|
|
475
480
|
graphRunnable?: t.CompiledStateWorkflow;
|
|
476
481
|
Graph: StandardGraph | MultiAgentGraph | undefined;
|
|
477
482
|
returnContent: boolean = false;
|
|
@@ -516,6 +521,16 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
516
521
|
if (config.calibrationRatio != null && config.calibrationRatio > 0) {
|
|
517
522
|
this.calibrationRatio = config.calibrationRatio;
|
|
518
523
|
}
|
|
524
|
+
if (isFadingTier(config.fadingTier)) {
|
|
525
|
+
this.fadingTier = { ...config.fadingTier };
|
|
526
|
+
}
|
|
527
|
+
if (config.fadingTiers != null) {
|
|
528
|
+
this.fadingTiers = Object.fromEntries(
|
|
529
|
+
Object.entries(config.fadingTiers).flatMap(([agentId, tier]) =>
|
|
530
|
+
isFadingTier(tier) ? [[agentId, { ...tier }] as const] : []
|
|
531
|
+
)
|
|
532
|
+
);
|
|
533
|
+
}
|
|
519
534
|
|
|
520
535
|
const handlerRegistry = new HandlerRegistry();
|
|
521
536
|
|
|
@@ -632,6 +647,8 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
632
647
|
tokenCounter: this.tokenCounter,
|
|
633
648
|
indexTokenCountMap: this.indexTokenCountMap,
|
|
634
649
|
calibrationRatio: this.calibrationRatio,
|
|
650
|
+
fadingTier: this.fadingTier,
|
|
651
|
+
fadingTiers: this.fadingTiers,
|
|
635
652
|
subagentUsageSink: this.subagentUsageSink,
|
|
636
653
|
subagentTasks: this.subagentTasks,
|
|
637
654
|
preemption: this.preemption,
|
|
@@ -672,6 +689,8 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
672
689
|
tokenCounter: this.tokenCounter,
|
|
673
690
|
indexTokenCountMap: this.indexTokenCountMap,
|
|
674
691
|
calibrationRatio: this.calibrationRatio,
|
|
692
|
+
fadingTier: this.fadingTier,
|
|
693
|
+
fadingTiers: this.fadingTiers,
|
|
675
694
|
subagentUsageSink: this.subagentUsageSink,
|
|
676
695
|
subagentTasks: this.subagentTasks,
|
|
677
696
|
preemption: this.preemption,
|
|
@@ -937,6 +956,35 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
937
956
|
return this.calibrationRatio;
|
|
938
957
|
}
|
|
939
958
|
|
|
959
|
+
/**
|
|
960
|
+
* Returns the default agent's latched context-fading tier. Single-agent hosts
|
|
961
|
+
* may persist it beside the calibration ratio; multi-agent hosts should use
|
|
962
|
+
* `getFadingTiers()` so independent agent tiers are retained.
|
|
963
|
+
*/
|
|
964
|
+
getFadingTier(): t.FadingTier | undefined {
|
|
965
|
+
return this.fadingTier == null ? undefined : { ...this.fadingTier };
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/** Returns a defensive snapshot of latched tiers keyed by agent ID. */
|
|
969
|
+
getFadingTiers(): t.FadingTiers {
|
|
970
|
+
return Object.fromEntries(
|
|
971
|
+
Object.entries(this.fadingTiers).map(([agentId, tier]) => [
|
|
972
|
+
agentId,
|
|
973
|
+
{ ...tier },
|
|
974
|
+
])
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/** Agent IDs whose canonical history was compacted during this run. */
|
|
979
|
+
getFadingTierResetAgentIds(): string[] {
|
|
980
|
+
return [...this.fadingTierResetAgentIds];
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/** Whether the default agent compacted canonical history during this run. */
|
|
984
|
+
didResetFadingTier(): boolean {
|
|
985
|
+
return this.fadingTierReset;
|
|
986
|
+
}
|
|
987
|
+
|
|
940
988
|
getResolvedInstructionOverhead(): number | undefined {
|
|
941
989
|
return this.Graph?.getResolvedInstructionOverhead();
|
|
942
990
|
}
|
|
@@ -1761,6 +1809,10 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1761
1809
|
: undefined;
|
|
1762
1810
|
|
|
1763
1811
|
this.calibrationRatio = this.Graph.getCalibrationRatio();
|
|
1812
|
+
this.fadingTier = this.Graph.getFadingTier();
|
|
1813
|
+
this.fadingTiers = this.Graph.getFadingTiers();
|
|
1814
|
+
this.fadingTierReset = this.Graph.didResetFadingTier();
|
|
1815
|
+
this.fadingTierResetAgentIds = this.Graph.getFadingTierResetAgentIds();
|
|
1764
1816
|
|
|
1765
1817
|
/**
|
|
1766
1818
|
* Skip `clearHeavyState()` when the run paused on a clean HITL
|
|
@@ -21,18 +21,22 @@ import type {
|
|
|
21
21
|
SessionEntry,
|
|
22
22
|
SessionForkOptions,
|
|
23
23
|
} from './types';
|
|
24
|
+
import { isFadingTier } from '@/messages/fading';
|
|
24
25
|
import type { HookRegistry } from '@/hooks';
|
|
25
26
|
import type * as t from '@/types';
|
|
26
|
-
import { deriveMessages } from './deriveMessages';
|
|
27
27
|
import { createSummarizeNode } from '@/summarization/node';
|
|
28
28
|
import { resolveStreamLimits } from '@/llm/streamLimits';
|
|
29
29
|
import { JsonlSessionStore } from './JsonlSessionStore';
|
|
30
30
|
import { AgentContext } from '@/agents/AgentContext';
|
|
31
31
|
import { ContentTypes, GraphEvents } from '@/common';
|
|
32
32
|
import { createRunId, createSessionId } from './ids';
|
|
33
|
+
import { deriveMessages } from './deriveMessages';
|
|
33
34
|
import { createRunHandlers } from './handlers';
|
|
34
35
|
import { Run } from '@/run';
|
|
35
36
|
|
|
37
|
+
/** Compact per-thread fading metadata retained for recently used checkpoints. */
|
|
38
|
+
const MAX_ALTERNATE_THREAD_FADING_STATES = 64;
|
|
39
|
+
|
|
36
40
|
function isBaseMessage(value: unknown): value is BaseMessage {
|
|
37
41
|
return (
|
|
38
42
|
value instanceof BaseMessage ||
|
|
@@ -366,6 +370,90 @@ function createCallerConfig(
|
|
|
366
370
|
};
|
|
367
371
|
}
|
|
368
372
|
|
|
373
|
+
/** Generation key for one tier within a scope; `''` names the default tier. */
|
|
374
|
+
function fadingGenerationKey(scopeKey: string, agentKey: string): string {
|
|
375
|
+
return JSON.stringify([scopeKey, agentKey]);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function fadingGenerationScope(generationKey: string): string {
|
|
379
|
+
const parsed: unknown = JSON.parse(generationKey);
|
|
380
|
+
return Array.isArray(parsed) && typeof parsed[0] === 'string' ? parsed[0] : '';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** What a run saw when it started, so its capture can tell stale from current. */
|
|
384
|
+
type FadingCaptureSnapshot = {
|
|
385
|
+
epoch: number;
|
|
386
|
+
generations: Map<string, number>;
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
function fadingScopeKey(threadId: string, checkpointNs = ''): string {
|
|
390
|
+
return JSON.stringify([threadId, checkpointNs]);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** Merges two tiers monotonically. A malformed side (a corrupt store line or
|
|
394
|
+
* host input) is ignored rather than poisoning the budget with `NaN`. */
|
|
395
|
+
function mergeFadingTier(
|
|
396
|
+
currentTier: t.FadingTier | undefined,
|
|
397
|
+
incomingTier: t.FadingTier | undefined
|
|
398
|
+
): t.FadingTier | undefined {
|
|
399
|
+
const current = isFadingTier(currentTier) ? currentTier : undefined;
|
|
400
|
+
const incoming = isFadingTier(incomingTier) ? incomingTier : undefined;
|
|
401
|
+
if (current == null) {
|
|
402
|
+
return incoming == null ? undefined : { ...incoming };
|
|
403
|
+
}
|
|
404
|
+
if (incoming == null) {
|
|
405
|
+
return { ...current };
|
|
406
|
+
}
|
|
407
|
+
const budgetTokens = Math.min(
|
|
408
|
+
current.budgetTokens,
|
|
409
|
+
incoming.budgetTokens
|
|
410
|
+
);
|
|
411
|
+
const masked = current.masked || incoming.masked;
|
|
412
|
+
const latched =
|
|
413
|
+
current.latched === true ||
|
|
414
|
+
incoming.latched === true ||
|
|
415
|
+
budgetTokens !== current.budgetTokens ||
|
|
416
|
+
budgetTokens !== incoming.budgetTokens ||
|
|
417
|
+
masked !== current.masked ||
|
|
418
|
+
masked !== incoming.masked;
|
|
419
|
+
return {
|
|
420
|
+
v: 1,
|
|
421
|
+
budgetTokens,
|
|
422
|
+
masked,
|
|
423
|
+
...(latched ? { latched: true } : {}),
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function mergeFadingTiers(
|
|
428
|
+
current: t.FadingTiers | undefined,
|
|
429
|
+
incoming: t.FadingTiers | undefined
|
|
430
|
+
): t.FadingTiers {
|
|
431
|
+
const agentIds = new Set([
|
|
432
|
+
...Object.keys(current ?? {}),
|
|
433
|
+
...Object.keys(incoming ?? {}),
|
|
434
|
+
]);
|
|
435
|
+
return Object.fromEntries(
|
|
436
|
+
[...agentIds].flatMap((agentId) => {
|
|
437
|
+
const tier = mergeFadingTier(
|
|
438
|
+
ownFadingTier(current, agentId),
|
|
439
|
+
ownFadingTier(incoming, agentId)
|
|
440
|
+
);
|
|
441
|
+
return tier == null ? [] : [[agentId, tier]];
|
|
442
|
+
})
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** Reads an agent's tier as an own property, so an ID such as `constructor`
|
|
447
|
+
* never resolves through the prototype chain. */
|
|
448
|
+
function ownFadingTier(
|
|
449
|
+
tiers: t.FadingTiers | undefined,
|
|
450
|
+
agentId: string
|
|
451
|
+
): t.FadingTier | undefined {
|
|
452
|
+
return tiers != null && Object.prototype.hasOwnProperty.call(tiers, agentId)
|
|
453
|
+
? tiers[agentId]
|
|
454
|
+
: undefined;
|
|
455
|
+
}
|
|
456
|
+
|
|
369
457
|
function createCheckpointLookupConfig(config: RunnableConfig): RunnableConfig {
|
|
370
458
|
const threadId = getConfigString(config, 'thread_id');
|
|
371
459
|
if (threadId == null) {
|
|
@@ -850,6 +938,16 @@ export class AgentSession {
|
|
|
850
938
|
private runConfig: t.RunConfig;
|
|
851
939
|
private store: JsonlSessionStore | undefined;
|
|
852
940
|
private calibrationRatio: number | undefined;
|
|
941
|
+
private fadingTier: t.FadingTier | undefined;
|
|
942
|
+
private fadingTiers: t.FadingTiers | undefined;
|
|
943
|
+
/** Reset generations keyed per scope and per tier (`''` for the default). */
|
|
944
|
+
private fadingGenerations = new Map<string, number>();
|
|
945
|
+
/** Advances on every explicit history rewrite (branch, compact, restore). */
|
|
946
|
+
private fadingRewriteEpoch = 0;
|
|
947
|
+
private alternateThreadFadingState = new Map<
|
|
948
|
+
string,
|
|
949
|
+
{ fadingTier?: t.FadingTier; fadingTiers?: t.FadingTiers }
|
|
950
|
+
>();
|
|
853
951
|
private checkpointing: SessionCheckpointingState;
|
|
854
952
|
cwd: string;
|
|
855
953
|
threadId: string;
|
|
@@ -866,6 +964,18 @@ export class AgentSession {
|
|
|
866
964
|
this.threadId = params.threadId;
|
|
867
965
|
this.store = params.store;
|
|
868
966
|
this.checkpointing = params.checkpointing;
|
|
967
|
+
this.fadingTier =
|
|
968
|
+
params.runConfig.fadingTier == null
|
|
969
|
+
? undefined
|
|
970
|
+
: { ...params.runConfig.fadingTier };
|
|
971
|
+
this.fadingTiers =
|
|
972
|
+
params.runConfig.fadingTiers == null
|
|
973
|
+
? undefined
|
|
974
|
+
: Object.fromEntries(
|
|
975
|
+
Object.entries(params.runConfig.fadingTiers).map(
|
|
976
|
+
([agentId, tier]) => [agentId, { ...tier }]
|
|
977
|
+
)
|
|
978
|
+
);
|
|
869
979
|
}
|
|
870
980
|
|
|
871
981
|
static async create(config: AgentSessionConfig): Promise<AgentSession> {
|
|
@@ -881,7 +991,7 @@ export class AgentSession {
|
|
|
881
991
|
sessionId: explicitSessionId,
|
|
882
992
|
ephemeral: normalized.ephemeral,
|
|
883
993
|
});
|
|
884
|
-
|
|
994
|
+
const session = new AgentSession({
|
|
885
995
|
runConfig: normalized.runConfig,
|
|
886
996
|
cwd: normalized.cwd,
|
|
887
997
|
threadId: store?.header.id ?? explicitSessionId ?? createSessionId(),
|
|
@@ -891,6 +1001,8 @@ export class AgentSession {
|
|
|
891
1001
|
),
|
|
892
1002
|
store,
|
|
893
1003
|
});
|
|
1004
|
+
session.restoreFadingStateFromStore();
|
|
1005
|
+
return session;
|
|
894
1006
|
}
|
|
895
1007
|
|
|
896
1008
|
get sessionPath(): string | undefined {
|
|
@@ -905,6 +1017,223 @@ export class AgentSession {
|
|
|
905
1017
|
return this.checkpointing.checkpointer;
|
|
906
1018
|
}
|
|
907
1019
|
|
|
1020
|
+
private getFadingState(threadId: string, checkpointNs = ''): {
|
|
1021
|
+
fadingTier?: t.FadingTier;
|
|
1022
|
+
fadingTiers?: t.FadingTiers;
|
|
1023
|
+
} {
|
|
1024
|
+
if (threadId === this.threadId && checkpointNs === '') {
|
|
1025
|
+
return { fadingTier: this.fadingTier, fadingTiers: this.fadingTiers };
|
|
1026
|
+
}
|
|
1027
|
+
const scopeKey = fadingScopeKey(threadId, checkpointNs);
|
|
1028
|
+
const state = this.alternateThreadFadingState.get(scopeKey);
|
|
1029
|
+
if (state == null) {
|
|
1030
|
+
return {};
|
|
1031
|
+
}
|
|
1032
|
+
/** Refresh insertion order so the bounded map acts as an LRU. */
|
|
1033
|
+
this.alternateThreadFadingState.delete(scopeKey);
|
|
1034
|
+
this.alternateThreadFadingState.set(scopeKey, state);
|
|
1035
|
+
return state;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
private setFadingState(
|
|
1039
|
+
threadId: string,
|
|
1040
|
+
checkpointNs: string,
|
|
1041
|
+
fadingTier: t.FadingTier | undefined,
|
|
1042
|
+
fadingTiers: t.FadingTiers
|
|
1043
|
+
): void {
|
|
1044
|
+
if (threadId === this.threadId && checkpointNs === '') {
|
|
1045
|
+
this.fadingTier = mergeFadingTier(this.fadingTier, fadingTier);
|
|
1046
|
+
this.fadingTiers = mergeFadingTiers(this.fadingTiers, fadingTiers);
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const scopeKey = fadingScopeKey(threadId, checkpointNs);
|
|
1050
|
+
const current = this.alternateThreadFadingState.get(scopeKey);
|
|
1051
|
+
this.alternateThreadFadingState.delete(scopeKey);
|
|
1052
|
+
this.alternateThreadFadingState.set(scopeKey, {
|
|
1053
|
+
fadingTier: mergeFadingTier(current?.fadingTier, fadingTier),
|
|
1054
|
+
fadingTiers: mergeFadingTiers(current?.fadingTiers, fadingTiers),
|
|
1055
|
+
});
|
|
1056
|
+
this.trimAlternateFadingStates();
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
private trimAlternateFadingStates(): void {
|
|
1060
|
+
while (
|
|
1061
|
+
this.alternateThreadFadingState.size >
|
|
1062
|
+
MAX_ALTERNATE_THREAD_FADING_STATES
|
|
1063
|
+
) {
|
|
1064
|
+
const oldestThreadId = this.alternateThreadFadingState.keys().next();
|
|
1065
|
+
if (oldestThreadId.done === true) {
|
|
1066
|
+
break;
|
|
1067
|
+
}
|
|
1068
|
+
this.alternateThreadFadingState.delete(oldestThreadId.value);
|
|
1069
|
+
for (const key of [...this.fadingGenerations.keys()]) {
|
|
1070
|
+
if (fadingGenerationScope(key) === oldestThreadId.value) {
|
|
1071
|
+
this.fadingGenerations.delete(key);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
private clearFadingState(): void {
|
|
1078
|
+
this.fadingTier = undefined;
|
|
1079
|
+
this.fadingTiers = undefined;
|
|
1080
|
+
this.alternateThreadFadingState.clear();
|
|
1081
|
+
this.fadingGenerations.clear();
|
|
1082
|
+
/** A run that started before the rewrite would otherwise re-persist tiers
|
|
1083
|
+
* learned on a history that no longer exists once it completes. */
|
|
1084
|
+
this.fadingRewriteEpoch += 1;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
private snapshotFadingGenerations(): FadingCaptureSnapshot {
|
|
1088
|
+
return {
|
|
1089
|
+
epoch: this.fadingRewriteEpoch,
|
|
1090
|
+
generations: new Map(this.fadingGenerations),
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
private restoreFadingStateFromStore(clearWhenMissing = false): void {
|
|
1095
|
+
const states =
|
|
1096
|
+
this.store?.getFadingStates(
|
|
1097
|
+
this.threadId,
|
|
1098
|
+
MAX_ALTERNATE_THREAD_FADING_STATES
|
|
1099
|
+
) ?? [];
|
|
1100
|
+
if (states.length === 0) {
|
|
1101
|
+
if (clearWhenMissing || this.store?.hasFadingState() === true) {
|
|
1102
|
+
this.clearFadingState();
|
|
1103
|
+
}
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
this.clearFadingState();
|
|
1107
|
+
/** Store returns newest first; replay oldest first to retain LRU order. */
|
|
1108
|
+
for (let i = states.length - 1; i >= 0; i--) {
|
|
1109
|
+
const state = states[i];
|
|
1110
|
+
this.setFadingState(
|
|
1111
|
+
state.threadId,
|
|
1112
|
+
state.checkpointNs ?? '',
|
|
1113
|
+
isFadingTier(state.fadingTier) ? { ...state.fadingTier } : undefined,
|
|
1114
|
+
state.fadingTiers == null
|
|
1115
|
+
? {}
|
|
1116
|
+
: Object.fromEntries(
|
|
1117
|
+
Object.entries(state.fadingTiers).flatMap(([agentId, tier]) =>
|
|
1118
|
+
isFadingTier(tier) ? [[agentId, { ...tier }] as const] : []
|
|
1119
|
+
)
|
|
1120
|
+
)
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
private async persistFadingState(
|
|
1126
|
+
threadId: string,
|
|
1127
|
+
checkpointNs = ''
|
|
1128
|
+
): Promise<void> {
|
|
1129
|
+
const state = this.getFadingState(threadId, checkpointNs);
|
|
1130
|
+
await this.store?.appendFadingState({
|
|
1131
|
+
threadId,
|
|
1132
|
+
...(checkpointNs === '' ? {} : { checkpointNs }),
|
|
1133
|
+
...(state.fadingTier == null
|
|
1134
|
+
? {}
|
|
1135
|
+
: { fadingTier: { ...state.fadingTier } }),
|
|
1136
|
+
...(state.fadingTiers == null
|
|
1137
|
+
? {}
|
|
1138
|
+
: {
|
|
1139
|
+
fadingTiers: Object.fromEntries(
|
|
1140
|
+
Object.entries(state.fadingTiers).map(([agentId, tier]) => [
|
|
1141
|
+
agentId,
|
|
1142
|
+
{ ...tier },
|
|
1143
|
+
])
|
|
1144
|
+
),
|
|
1145
|
+
}),
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
private async captureRunContextState(
|
|
1150
|
+
threadId: string,
|
|
1151
|
+
checkpointNs: string,
|
|
1152
|
+
run: Run<t.IState>,
|
|
1153
|
+
snapshot: FadingCaptureSnapshot
|
|
1154
|
+
): Promise<void> {
|
|
1155
|
+
this.calibrationRatio = run.getCalibrationRatio();
|
|
1156
|
+
if (snapshot.epoch < this.fadingRewriteEpoch) {
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
const current = this.getFadingState(threadId, checkpointNs);
|
|
1160
|
+
const incomingTier = run.getFadingTier();
|
|
1161
|
+
const incomingTiers = run.getFadingTiers();
|
|
1162
|
+
const scopeKey = fadingScopeKey(threadId, checkpointNs);
|
|
1163
|
+
/** A tier whose generation moved after this run started was reset by a
|
|
1164
|
+
* concurrent run; this run's view of it is obsolete, while its other
|
|
1165
|
+
* agents' tiers remain valid escalations. */
|
|
1166
|
+
const isStale = (agentKey: string): boolean => {
|
|
1167
|
+
const key = fadingGenerationKey(scopeKey, agentKey);
|
|
1168
|
+
return (
|
|
1169
|
+
(snapshot.generations.get(key) ?? 0) <
|
|
1170
|
+
(this.fadingGenerations.get(key) ?? 0)
|
|
1171
|
+
);
|
|
1172
|
+
};
|
|
1173
|
+
const advanceGeneration = (agentKey: string): void => {
|
|
1174
|
+
const key = fadingGenerationKey(scopeKey, agentKey);
|
|
1175
|
+
this.fadingGenerations.set(key, (this.fadingGenerations.get(key) ?? 0) + 1);
|
|
1176
|
+
};
|
|
1177
|
+
const mergeCapturedTier = (
|
|
1178
|
+
existing: t.FadingTier | undefined,
|
|
1179
|
+
incoming: t.FadingTier | undefined,
|
|
1180
|
+
reset: boolean
|
|
1181
|
+
): t.FadingTier | undefined => {
|
|
1182
|
+
if (reset) {
|
|
1183
|
+
return incoming == null ? undefined : { ...incoming };
|
|
1184
|
+
}
|
|
1185
|
+
return mergeFadingTier(existing, incoming);
|
|
1186
|
+
};
|
|
1187
|
+
const resetAgentIds = new Set(run.getFadingTierResetAgentIds());
|
|
1188
|
+
const nextTiers: t.FadingTiers = Object.create(null) as t.FadingTiers;
|
|
1189
|
+
const allAgentIds = new Set([
|
|
1190
|
+
...Object.keys(current.fadingTiers ?? {}),
|
|
1191
|
+
...Object.keys(incomingTiers),
|
|
1192
|
+
...resetAgentIds,
|
|
1193
|
+
]);
|
|
1194
|
+
for (const agentId of allAgentIds) {
|
|
1195
|
+
const existing = ownFadingTier(current.fadingTiers, agentId);
|
|
1196
|
+
if (isStale(agentId)) {
|
|
1197
|
+
if (existing != null) {
|
|
1198
|
+
nextTiers[agentId] = existing;
|
|
1199
|
+
}
|
|
1200
|
+
continue;
|
|
1201
|
+
}
|
|
1202
|
+
const reset = resetAgentIds.has(agentId);
|
|
1203
|
+
if (reset) {
|
|
1204
|
+
advanceGeneration(agentId);
|
|
1205
|
+
}
|
|
1206
|
+
const tier = mergeCapturedTier(
|
|
1207
|
+
existing,
|
|
1208
|
+
ownFadingTier(incomingTiers, agentId),
|
|
1209
|
+
reset
|
|
1210
|
+
);
|
|
1211
|
+
if (tier != null) {
|
|
1212
|
+
nextTiers[agentId] = tier;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
let nextTier = current.fadingTier;
|
|
1216
|
+
if (!isStale('')) {
|
|
1217
|
+
const didResetTier = run.didResetFadingTier();
|
|
1218
|
+
if (didResetTier) {
|
|
1219
|
+
advanceGeneration('');
|
|
1220
|
+
}
|
|
1221
|
+
nextTier = mergeCapturedTier(current.fadingTier, incomingTier, didResetTier);
|
|
1222
|
+
}
|
|
1223
|
+
if (threadId === this.threadId && checkpointNs === '') {
|
|
1224
|
+
this.fadingTier = nextTier;
|
|
1225
|
+
this.fadingTiers = nextTiers;
|
|
1226
|
+
} else {
|
|
1227
|
+
this.alternateThreadFadingState.delete(scopeKey);
|
|
1228
|
+
this.alternateThreadFadingState.set(scopeKey, {
|
|
1229
|
+
fadingTier: nextTier,
|
|
1230
|
+
fadingTiers: nextTiers,
|
|
1231
|
+
});
|
|
1232
|
+
this.trimAlternateFadingStates();
|
|
1233
|
+
}
|
|
1234
|
+
await this.persistFadingState(threadId, checkpointNs);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
908
1237
|
async getLatestCheckpoint(
|
|
909
1238
|
options: AgentSessionCheckpointLookupOptions = {}
|
|
910
1239
|
): Promise<AgentSessionCheckpointReference | undefined> {
|
|
@@ -1042,6 +1371,8 @@ export class AgentSession {
|
|
|
1042
1371
|
const normalizedInput = normalizeInput(input);
|
|
1043
1372
|
const inputMessages = normalizedInput.messages;
|
|
1044
1373
|
const callerConfig = createCallerConfig(threadId, options);
|
|
1374
|
+
const checkpointNs = getConfigString(callerConfig, 'checkpoint_ns') ?? '';
|
|
1375
|
+
const fadingSnapshot = this.snapshotFadingGenerations();
|
|
1045
1376
|
const useCheckpointState = await this.hasCheckpointState(callerConfig);
|
|
1046
1377
|
let parentId = this.store?.getLeafEntry()?.id ?? null;
|
|
1047
1378
|
if (isSessionThread) {
|
|
@@ -1080,7 +1411,9 @@ export class AgentSession {
|
|
|
1080
1411
|
const sessionState = deriveMessages(
|
|
1081
1412
|
isSessionThread ? (this.store?.getPath() ?? []) : []
|
|
1082
1413
|
);
|
|
1414
|
+
const fadingState = this.getFadingState(threadId, checkpointNs);
|
|
1083
1415
|
let run: Run<t.IState> | undefined;
|
|
1416
|
+
let runContextCaptured = false;
|
|
1084
1417
|
try {
|
|
1085
1418
|
const runConfig: t.RunConfig = {
|
|
1086
1419
|
...this.runConfig,
|
|
@@ -1094,6 +1427,8 @@ export class AgentSession {
|
|
|
1094
1427
|
),
|
|
1095
1428
|
returnContent: true,
|
|
1096
1429
|
calibrationRatio: this.calibrationRatio,
|
|
1430
|
+
fadingTier: fadingState.fadingTier,
|
|
1431
|
+
fadingTiers: fadingState.fadingTiers,
|
|
1097
1432
|
customHandlers: {
|
|
1098
1433
|
...(this.runConfig.customHandlers ?? {}),
|
|
1099
1434
|
...handlerResult.handlers,
|
|
@@ -1115,8 +1450,14 @@ export class AgentSession {
|
|
|
1115
1450
|
await this.store?.appendMessage(message);
|
|
1116
1451
|
}
|
|
1117
1452
|
}
|
|
1118
|
-
this.calibrationRatio = run.getCalibrationRatio();
|
|
1119
1453
|
const interrupt = run.getInterrupt();
|
|
1454
|
+
await this.captureRunContextState(
|
|
1455
|
+
threadId,
|
|
1456
|
+
interrupt?.checkpointNs ?? checkpointNs,
|
|
1457
|
+
run,
|
|
1458
|
+
fadingSnapshot
|
|
1459
|
+
);
|
|
1460
|
+
runContextCaptured = true;
|
|
1120
1461
|
const haltedReason = run.getHaltReason();
|
|
1121
1462
|
if (interrupt) {
|
|
1122
1463
|
emitTerminalEvent({ type: 'run.interrupted' });
|
|
@@ -1165,6 +1506,14 @@ export class AgentSession {
|
|
|
1165
1506
|
threadId,
|
|
1166
1507
|
};
|
|
1167
1508
|
} catch (error) {
|
|
1509
|
+
if (run != null && !runContextCaptured) {
|
|
1510
|
+
await this.captureRunContextState(
|
|
1511
|
+
threadId,
|
|
1512
|
+
checkpointNs,
|
|
1513
|
+
run,
|
|
1514
|
+
fadingSnapshot
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1168
1517
|
emitTerminalEvent({
|
|
1169
1518
|
type: 'run.failed',
|
|
1170
1519
|
data: error instanceof Error ? error.message : String(error),
|
|
@@ -1227,11 +1576,13 @@ export class AgentSession {
|
|
|
1227
1576
|
this.store = await JsonlSessionStore.open(sessions[0].path);
|
|
1228
1577
|
this.cwd = this.store.header.cwd;
|
|
1229
1578
|
this.threadId = this.store.header.id;
|
|
1579
|
+
this.restoreFadingStateFromStore(true);
|
|
1230
1580
|
return this;
|
|
1231
1581
|
}
|
|
1232
1582
|
this.store = await JsonlSessionStore.open(pathOrId);
|
|
1233
1583
|
this.cwd = this.store.header.cwd;
|
|
1234
1584
|
this.threadId = this.store.header.id;
|
|
1585
|
+
this.restoreFadingStateFromStore(true);
|
|
1235
1586
|
return this;
|
|
1236
1587
|
}
|
|
1237
1588
|
|
|
@@ -1240,13 +1591,19 @@ export class AgentSession {
|
|
|
1240
1591
|
throw new Error('Cannot clone an ephemeral session');
|
|
1241
1592
|
}
|
|
1242
1593
|
const store = await this.store.clone(options);
|
|
1243
|
-
|
|
1244
|
-
runConfig:
|
|
1594
|
+
const session = new AgentSession({
|
|
1595
|
+
runConfig: {
|
|
1596
|
+
...this.runConfig,
|
|
1597
|
+
fadingTier: this.fadingTier,
|
|
1598
|
+
fadingTiers: this.fadingTiers,
|
|
1599
|
+
},
|
|
1245
1600
|
cwd: store.header.cwd,
|
|
1246
1601
|
threadId: store.header.id,
|
|
1247
1602
|
checkpointing: this.checkpointing,
|
|
1248
1603
|
store,
|
|
1249
1604
|
});
|
|
1605
|
+
await session.persistFadingState(session.threadId);
|
|
1606
|
+
return session;
|
|
1250
1607
|
}
|
|
1251
1608
|
|
|
1252
1609
|
async fork(
|
|
@@ -1256,14 +1613,22 @@ export class AgentSession {
|
|
|
1256
1613
|
if (!this.store) {
|
|
1257
1614
|
throw new Error('Cannot fork an ephemeral session');
|
|
1258
1615
|
}
|
|
1616
|
+
const retainsActiveLeaf =
|
|
1617
|
+
options.position === 'at' && this.store.getLeafEntry()?.id === entryId;
|
|
1259
1618
|
const store = await this.store.fork(entryId, options);
|
|
1260
|
-
|
|
1261
|
-
runConfig:
|
|
1619
|
+
const session = new AgentSession({
|
|
1620
|
+
runConfig: {
|
|
1621
|
+
...this.runConfig,
|
|
1622
|
+
fadingTier: retainsActiveLeaf ? this.fadingTier : undefined,
|
|
1623
|
+
fadingTiers: retainsActiveLeaf ? this.fadingTiers : undefined,
|
|
1624
|
+
},
|
|
1262
1625
|
cwd: store.header.cwd,
|
|
1263
1626
|
threadId: store.header.id,
|
|
1264
1627
|
checkpointing: this.checkpointing,
|
|
1265
1628
|
store,
|
|
1266
1629
|
});
|
|
1630
|
+
await session.persistFadingState(session.threadId);
|
|
1631
|
+
return session;
|
|
1267
1632
|
}
|
|
1268
1633
|
|
|
1269
1634
|
async branch(
|
|
@@ -1303,6 +1668,8 @@ export class AgentSession {
|
|
|
1303
1668
|
return;
|
|
1304
1669
|
}
|
|
1305
1670
|
await store.setLeaf(leafId);
|
|
1671
|
+
this.clearFadingState();
|
|
1672
|
+
await store.appendFadingState(null);
|
|
1306
1673
|
await this.resetCheckpointThreads('branch');
|
|
1307
1674
|
}
|
|
1308
1675
|
|
|
@@ -1424,6 +1791,8 @@ export class AgentSession {
|
|
|
1424
1791
|
retainedEntryIds,
|
|
1425
1792
|
summarizedEntryIds: summarized.map((entry) => entry.id),
|
|
1426
1793
|
});
|
|
1794
|
+
this.clearFadingState();
|
|
1795
|
+
await store.appendFadingState(null);
|
|
1427
1796
|
return summary;
|
|
1428
1797
|
}
|
|
1429
1798
|
|
|
@@ -1439,6 +1808,8 @@ export class AgentSession {
|
|
|
1439
1808
|
baseCallerConfig,
|
|
1440
1809
|
getStoredCheckpointForConfig(this.store, threadId, baseCallerConfig)?.data
|
|
1441
1810
|
);
|
|
1811
|
+
const checkpointNs = getConfigString(callerConfig, 'checkpoint_ns') ?? '';
|
|
1812
|
+
const fadingSnapshot = this.snapshotFadingGenerations();
|
|
1442
1813
|
await this.store?.appendRunEvent('run.started', undefined, {
|
|
1443
1814
|
runId,
|
|
1444
1815
|
threadId,
|
|
@@ -1451,7 +1822,9 @@ export class AgentSession {
|
|
|
1451
1822
|
const sessionState = deriveMessages(
|
|
1452
1823
|
isSessionThread ? (this.store?.getPath() ?? []) : []
|
|
1453
1824
|
);
|
|
1825
|
+
const fadingState = this.getFadingState(threadId, checkpointNs);
|
|
1454
1826
|
let run: Run<t.IState> | undefined;
|
|
1827
|
+
let runContextCaptured = false;
|
|
1455
1828
|
try {
|
|
1456
1829
|
run = await Run.create<t.IState>({
|
|
1457
1830
|
...this.runConfig,
|
|
@@ -1465,6 +1838,8 @@ export class AgentSession {
|
|
|
1465
1838
|
),
|
|
1466
1839
|
returnContent: true,
|
|
1467
1840
|
calibrationRatio: this.calibrationRatio,
|
|
1841
|
+
fadingTier: fadingState.fadingTier,
|
|
1842
|
+
fadingTiers: fadingState.fadingTiers,
|
|
1468
1843
|
customHandlers: {
|
|
1469
1844
|
...(this.runConfig.customHandlers ?? {}),
|
|
1470
1845
|
...handlerResult.handlers,
|
|
@@ -1477,8 +1852,14 @@ export class AgentSession {
|
|
|
1477
1852
|
await this.store?.appendMessage(message);
|
|
1478
1853
|
}
|
|
1479
1854
|
}
|
|
1480
|
-
this.calibrationRatio = run.getCalibrationRatio();
|
|
1481
1855
|
const interrupt = run.getInterrupt();
|
|
1856
|
+
await this.captureRunContextState(
|
|
1857
|
+
threadId,
|
|
1858
|
+
interrupt?.checkpointNs ?? checkpointNs,
|
|
1859
|
+
run,
|
|
1860
|
+
fadingSnapshot
|
|
1861
|
+
);
|
|
1862
|
+
runContextCaptured = true;
|
|
1482
1863
|
const haltedReason = run.getHaltReason();
|
|
1483
1864
|
if (interrupt) {
|
|
1484
1865
|
await this.store?.appendRunEvent('run.interrupted', interrupt, {
|
|
@@ -1524,6 +1905,14 @@ export class AgentSession {
|
|
|
1524
1905
|
threadId,
|
|
1525
1906
|
};
|
|
1526
1907
|
} catch (error) {
|
|
1908
|
+
if (run != null && !runContextCaptured) {
|
|
1909
|
+
await this.captureRunContextState(
|
|
1910
|
+
threadId,
|
|
1911
|
+
checkpointNs,
|
|
1912
|
+
run,
|
|
1913
|
+
fadingSnapshot
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1527
1916
|
await this.store?.appendRunEvent('run.failed', error, {
|
|
1528
1917
|
runId,
|
|
1529
1918
|
threadId,
|