From 60ba97e7e612017b6544f6bc8dc804d3e2781b03 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 8 Apr 2026 01:18:57 +0000 Subject: [PATCH] not building --- packages/core/src/context/ir/graphUtils.ts | 32 +++++++--- packages/core/src/context/pipeline.ts | 34 ++++++++--- .../emergencyTruncationProcessor.ts | 58 ++++++++++--------- .../core/src/context/sidecar/orchestrator.ts | 6 +- 4 files changed, 87 insertions(+), 43 deletions(-) diff --git a/packages/core/src/context/ir/graphUtils.ts b/packages/core/src/context/ir/graphUtils.ts index 8914ab177b..c6776f2cc6 100644 --- a/packages/core/src/context/ir/graphUtils.ts +++ b/packages/core/src/context/ir/graphUtils.ts @@ -4,35 +4,51 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { Episode, IrNode, AgentThought, ToolExecution, UserPrompt, AgentYield, SystemEvent } from './types.js'; +import type { Episode, Task, IrNode, AgentThought, ToolExecution, MaskedTool, UserPrompt, AgentYield, SystemEvent, Snapshot, RollingSummary } from './types.js'; import type { ContextTracer } from '../tracer.js'; import { debugLogger } from '../../utils/debugLogger.js'; import type { ContextEnvironment } from '../sidecar/environment.js'; -export function isEpisode(node: IrNode | Episode): node is Episode { +export function isEpisode(node: IrNode): node is Episode { return node.type === 'EPISODE'; } -export function isAgentThought(node: IrNode | Episode): node is AgentThought { +export function isTask(node: IrNode): node is Task { + return node.type === 'TASK'; +} + +export function isAgentThought(node: IrNode): node is AgentThought { return node.type === 'AGENT_THOUGHT'; } -export function isToolExecution(node: IrNode | Episode): node is ToolExecution { +export function isToolExecution(node: IrNode): node is ToolExecution { return node.type === 'TOOL_EXECUTION'; } -export function isUserPrompt(node: IrNode | Episode): node is UserPrompt { +export function isMaskedTool(node: IrNode): node is MaskedTool { + return node.type === 'MASKED_TOOL'; +} + +export function isUserPrompt(node: IrNode): node is UserPrompt { return node.type === 'USER_PROMPT'; } -export function isAgentYield(node: IrNode | Episode): node is AgentYield { +export function isAgentYield(node: IrNode): node is AgentYield { return node.type === 'AGENT_YIELD'; } -export function isSystemEvent(node: IrNode | Episode): node is SystemEvent { +export function isSystemEvent(node: IrNode): node is SystemEvent { return node.type === 'SYSTEM_EVENT'; } +export function isSnapshot(node: IrNode): node is Snapshot { + return node.type === 'SNAPSHOT'; +} + +export function isRollingSummary(node: IrNode): node is RollingSummary { + return node.type === 'ROLLING_SUMMARY'; +} + /** * Generates a computed view of the pristine log. * Sweeps backwards (newest to oldest), tracking rolling tokens. @@ -41,7 +57,7 @@ export function isSystemEvent(node: IrNode | Episode): node is SystemEvent { * Handles N-to-1 variant skipping automatically. */ -export function generateWorkingBufferView( +export function generateWorkingBufferView_OLD( pristineEpisodes: Episode[], retainedTokens: number, tracer: ContextTracer, diff --git a/packages/core/src/context/pipeline.ts b/packages/core/src/context/pipeline.ts index bd07dbc868..fefb347335 100644 --- a/packages/core/src/context/pipeline.ts +++ b/packages/core/src/context/pipeline.ts @@ -4,7 +4,27 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { ConcreteNode, IrMetadata } from './ir/types.js'; +import type { ConcreteNode, Snapshot, RollingSummary, IrMetadata } from './ir/types.js'; + +export type InboxMessage = + | { type: 'SNAPSHOT_READY'; snapshot: Snapshot; abstractsIds: string[] } + | { type: 'BACKGROUND_SUMMARY'; summary: RollingSummary; targetId: string }; + +export interface ContextInbox { + dispatch(message: InboxMessage): void; + peek(type: T): Extract | undefined; +} + +export interface ContextWorkingBuffer { + /** The current active (projected) flat list of ConcreteNodes. */ + readonly nodes: ReadonlyArray; + + /** Retrieves the historical, pristine version of a node (before any masks/summaries). */ + getPristineNode(id: string): ConcreteNode | undefined; + + /** Retrieves the full audit lineage of a specific node ID. */ + getLineage(id: string): ReadonlyArray; +} /** * State object passed through the processing pipeline. @@ -52,6 +72,9 @@ export interface ContextPatch { } export interface ProcessArgs { + /** The rich buffer containing current nodes and their history. */ + readonly buffer: ContextWorkingBuffer; + /** The flat, sequential array of current renderable nodes (The Ship). */ readonly ship: ReadonlyArray; @@ -64,11 +87,8 @@ export interface ProcessArgs { /** The token budget and accounting state. */ readonly state: ContextAccountingState; - /** - * An escape hatch allowing the processor to query the original, uncompressed - * state of a node from the Pristine Graph. - */ - readonly getPristineNode: (id: string) => ConcreteNode | undefined; + /** Type-safe messaging system for async/sync coordination. */ + readonly inbox: ContextInbox; } /** @@ -81,7 +101,7 @@ export interface ContextProcessor { /** Unique name for telemetry and logging. */ readonly name: string; - /** Returns an array of declarative patches to apply to the Ship. */ + /** Returns an array of declarative patches applied at this specific temporal phase. */ process(args: ProcessArgs): Promise; } diff --git a/packages/core/src/context/processors/emergencyTruncationProcessor.ts b/packages/core/src/context/processors/emergencyTruncationProcessor.ts index df154ee2a0..1ef4f55d59 100644 --- a/packages/core/src/context/processors/emergencyTruncationProcessor.ts +++ b/packages/core/src/context/processors/emergencyTruncationProcessor.ts @@ -6,11 +6,11 @@ import type { ContextProcessor, - ContextAccountingState, BackstopTargetOptions, + ProcessArgs, + ContextPatch, } from '../pipeline.js'; import type { ContextEnvironment } from '../sidecar/environment.js'; -import type { EpisodeEditor } from '../ir/episodeEditor.js'; export type EmergencyTruncationProcessorOptions = BackstopTargetOptions; @@ -47,10 +47,11 @@ export class EmergencyTruncationProcessor implements ContextProcessor { this.options = options; } - async process( - editor: EpisodeEditor, - state: ContextAccountingState, - ): Promise { + async process({ + ship, + triggerTargets, + state, + }: ProcessArgs): Promise { const toRemove: string[] = []; // Calculate how many tokens we need to remove based on the configured knob @@ -58,11 +59,11 @@ export class EmergencyTruncationProcessor implements ContextProcessor { const strategy = this.options.target ?? 'max'; if (strategy === 'incremental') { - if (state.currentTokens <= state.maxTokens) return; + if (state.currentTokens <= state.maxTokens) return []; targetTokensToRemove = state.currentTokens - state.maxTokens; } else if (strategy === 'freeNTokens') { targetTokensToRemove = this.options.freeTokensTarget ?? 0; - if (targetTokensToRemove <= 0) return; + if (targetTokensToRemove <= 0) return []; } else if (strategy === 'max') { // 'max' means we remove all targets without stopping early targetTokensToRemove = Infinity; @@ -70,28 +71,33 @@ export class EmergencyTruncationProcessor implements ContextProcessor { let removedTokens = 0; - // Iterate specifically over targets (which represent the aged-out delta). - // The editor returns targets from oldest to newest based on the working order. - // For truncation, we want to cut the oldest first. - for (const target of editor.targets) { - const ep = target.episode; - // We only truncate entire episodes here for safety and structural integrity - if (target.node !== ep) continue; + // The ship is sequentially ordered from oldest to newest. + // We want to delete the oldest targeted nodes first. + for (const node of ship) { + // Is this node part of the targeted delta (e.g. aged out of the budget)? + if (!triggerTargets.has(node.id)) continue; + // Is this node explicitly protected (e.g. part of an active Task)? + if (node.logicalParentId && state.protectedLogicalIds.has(node.logicalParentId)) continue; if (removedTokens >= targetTokensToRemove) break; - const epTokens = this._env.tokenCalculator.calculateEpisodeListTokens([ - ep, - ]); + removedTokens += node.metadata.currentTokens; + toRemove.push(node.id); + } - if (!state.protectedEpisodeIds.has(ep.id) && !toRemove.includes(ep.id)) { - removedTokens += epTokens; - toRemove.push(ep.id); + if (toRemove.length === 0) return []; + + return [{ + removedIds: toRemove, + metadata: { + originalTokens: removedTokens, + currentTokens: 0, + transformations: [{ + processorName: this.name, + action: 'TRUNCATED', + timestamp: Date.now(), + }], } - } - - if (toRemove.length > 0) { - editor.removeEpisodes(toRemove, 'TRUNCATED'); - } + }]; } } diff --git a/packages/core/src/context/sidecar/orchestrator.ts b/packages/core/src/context/sidecar/orchestrator.ts index faaf6e7596..dd4855ad24 100644 --- a/packages/core/src/context/sidecar/orchestrator.ts +++ b/packages/core/src/context/sidecar/orchestrator.ts @@ -155,7 +155,8 @@ export class PipelineOrchestrator { ship: currentShip, triggerTargets, state, - getPristineNode: () => undefined, + buffer: {} as any, // TODO: Implement ContextWorkingBuffer fully + inbox: {} as any, // TODO: Implement ContextInbox fully }); currentShip = this.reduceShip(currentShip, patches); @@ -200,7 +201,8 @@ export class PipelineOrchestrator { ship: currentShip, triggerTargets, state, - getPristineNode: () => undefined, + buffer: {} as any, // TODO: Implement ContextWorkingBuffer fully + inbox: {} as any, // TODO: Implement ContextInbox fully }); currentShip = this.reduceShip(currentShip, patches);