[pull] main from danny-avila:main - #216
Merged
Merged
Conversation
* 🗜️ feat: Summarize-Only Runs for Manual Compaction Adds `summarizeOnly` to the agent inputs. A summarize-only run requests summarization outright on its first model step instead of consulting the configured trigger, and the step after the summary dispatches the post-summary context usage snapshot and ends the run without ever calling the model. Hosts use it for user-initiated compaction, where the summary is the whole response and the snapshot is what the host persists as the branch's new context baseline. The summarize node learns a `manual` reason for the request: the default recency window does not apply to a summary the user asked for, so the whole history becomes the checkpoint unless the host configured `retainRecent` explicitly. Summarization that is not enabled ends the run immediately with no model work, and the request claim is reset per run so a graph can be reused. * 🗜️ fix: Fail Closed and Report Correctly on Summarize-Only Runs Review round 1. A summarize-only run has no reply to fall back on, so every way it could end without a summary now either preserves history and reports the failure, or rejects: - A provider failure that yields the metadata stub keeps the history and completes the summary step with an error, as overflow recovery already did; a manual compaction must never replace the conversation with a message count. - A summary the graph cannot attempt — summarization disabled, the summarizer exhausted, instructions exceeding the budget, or an explicit recency window that already covers the whole conversation — rejects with `ManualSummarizationSkippedError` instead of ending quietly. - A `retainRecent` that sets only a token cap keeps the ordinary turn default; only an absent `retainRecent` means the whole history. - The summary rides in state as `manualSummary`, so the Langfuse root output reports it rather than the retained tail or the raw graph state. - In a multi-agent graph, successors of the summarizing agent are no-ops and the workflow drains to END, so no chained agent spends a model call on the compacted history. * 🗜️ fix: Route and Report Summarize-Only Runs Through Every Graph Shape Review round 2. - The standard workflow's outer state now declares `manualSummary`, as the agent subgraph and the multi-agent outer state already did, so a single-agent summarize-only run reaches the Langfuse root with its summary rather than serialized state. - Root-output shaping prefers the summary over any assistant message left in state: with an explicit recency window that message is an older reply the run retained, never its result. - A multi-agent run also starts at the agent that opted in, so a summarizer reachable only through a handoff runs; the workflow's own entry points stay reachable and end without a model call. - The primary model is constructed only once a model call is certain, so a summarize-only step neither fails on a primary configuration it never invokes nor pays for its tool binding. - The test seam types the recording summarizer against `initializeModel`'s return type directly, without a double assertion. * 🗜️ fix: Compile a Summarize-Only Run to Its Agent Alone Review round 3. - A multi-agent summarize-only run now compiles to the opted-in agent alone: no other node, no handoff or direct edge, START → agent → END. Keeping the ordinary routing ran a direct-edge target twice (from START and again after its predecessor) and could append routing prompts to the checkpoint it had just produced. The model-node no-op for successors is gone with it. - `manualSummary` never outlives its run on a checkpointed thread: the summarize-only request clears the previous value before the node writes the new one, and an ordinary run's first model step clears whatever it inherited, so a later run or a failed compaction cannot report an old checkpoint as its output. - PreCompact hooks receive `trigger: 'manual'` for a manual compaction instead of the configured token-pressure trigger that was never evaluated. * 📝 docs: Describe the Summarize-Only Workflow as Compiled to Its Agent * 🗜️ fix: Attribute, Persist, and Terminate a Summarize-Only Run Correctly Review round 4. - The run's agent follows the summarizer: `defaultAgentId` resolves to the agent that opted in, so root trace identity and per-agent Langfuse routing name the agent that actually ran, not the workflow's first. - The compaction's remove-all now crosses the agent-subgraph boundary. The subgraph applies it internally and returns only the retained tail, which the outer reducer merged back into the history it already held, leaving a checkpointed thread uncompacted. The outer node re-issues the remove-all when the run produced a summary; a run that produced none passes through. - A summarize-only run admits no stop continuation. Its one summarization claim is spent, so a blocking Stop hook would start segments that end without a model call until the continuation budget turned a successful compaction into an error. Stop and StopFinalize hooks still fire.
* refactor: classify the retained tool share with the provider tool taxonomy `budget.ts` hand-rolled its own answer to "is this message a tool invocation, a tool result, or mixed content" while `toolResultTypes.ts` already holds the canonical one, consumed by `format.ts`, `alternation.ts`, `recency.ts` and `handlers.ts`. Every shape the local classifier lagged behind became a review finding: v1 standard `tool_call` blocks, `HumanMessage(tool_result)` history, Anthropic server tools, generic assistant messages. This removes the fifth classifier instead of patching it. Lift the message-level walker recency.ts kept private into the taxonomy as exports: `appendProviderMessageToolCalls` (now reporting how many calls it recognized), `getProviderToolMessageResultDescriptor`, and a single `getProviderMessageRole` that normalizes `ChatMessage` roles the converters accept. recency.ts imports them back unchanged in behaviour. Redefine tool-only on the taxonomy: every part is blank text, a call descriptor, an inline provider tool result, or a reasoning block. That closes the provider asymmetry where Anthropic thinking turns were excluded while OpenAI reasoning in `additional_kwargs` was counted, admits `server_tool_use`, `mcp_tool_use` and their results, counts a user turn made only of paired `tool_result` parts, and counts generic tool-role results. A call id with conflicting names is ambiguous and attributed to `unknown_tool` rather than guessed, matching the index's own conflict rule. The lifted helpers stay internal: `toolResultTypes.ts` is not re-exported. * fix: pair results the way the wire walkers do and accept approximate counters Results now consume their call through `consumeProviderToolResultPair`, so a provider that reuses a call id in a later turn is attributed to the call it currently answers instead of poisoning the id, and answered calls no longer accumulate toward the bounded index cap in long tool-heavy contexts. Pending calls are forgotten at an ordinary user turn, as the recency walker does; a user turn made only of paired tool results is committed from a candidate copy. The `TokenCounter` contract promises a `number`, and the pruning path accepts approximate counters such as `length / 4`. The share now rounds a finite, non-negative count rather than dropping the whole gauge on the first fraction; NaN, infinities, negatives and values past the safe range still degrade. `getProviderMessageRole` learns the aliases the Google converter reads as the model turn (`ai`, `model`, `supervisor`) and as the user (`human`). * fix: consume inline provider results and count folded tool history An assistant turn carrying a provider call and its inline result, such as `server_tool_use` followed by `web_search_tool_result`, recognized the result but left the call pending, so a reused id was marked ambiguous and answered inline exchanges still accumulated toward the bounded index cap. The scan now pairs and consumes inline results the same way whole-message results are. A tool-less destination inheriting tool history has each call and its results folded into one synthetic user turn, and compaction of that fold keeps the lineage. The role-based scan read those as ordinary conversation, reporting a zero tool share when folded tool output dominated the prompt. The fold is what the provenance stamp records, so a user turn whose provenance carries a `tool` part is counted as retained tool output; per-tool attribution is lost with the fold. Synthetic turns without tool lineage, such as handoff cues, stay in the conversation share. * fix: count Google code-execution exchanges as tool-only invocations Google's built-in code execution emits an `executableCode` part with no call id, and its `codeExecutionResult` pairs by adjacency. The scan recognized the result but treated the code part as visible content, so the whole exchange reported zero. `isExecutableCodePart` is now exported from the taxonomy and counts as an invocation; the adjacent result then pairs as before. The fold JSDoc now states the bounded over-count a folded turn carries when visible model text was folded alongside its calls. * fix: round the tool share once after aggregation Rounding each message's count before summing threw away sub-token amounts the pruning path keeps: four quarter-token results summed to one token there and to zero here. Per-message counts now stay raw through aggregation, guarded only for finiteness, sign and the safe range, and the aggregate and per-tool totals round once at the end. `apportionTokenCounts` already distributes fractional per-tool totals to integers against the rounded result share.
* feat(langfuse): promote safe tool observation metadata * fix(langfuse): preserve observation identity during metadata promotion * fix(langfuse): record safe metadata from host tool results * fix(langfuse): mark failed host tool observations as errors * perf(langfuse): index host tool calls by ID
* perf: consolidate retained tool accounting * fix: preserve provider-aware tool accounting * fix: preserve provider roles in recency fallback * perf: lazily count retained tool messages
* fix: count raw OpenAI tool calls * fix: traverse raw tool metadata safely * fix: align raw tool-call measurement * fix: project descriptor-read tool calls * fix: bound provider raw tool metadata
* perf: cache active AgentSession log projections * test: cover cached compaction and continuation transitions
* 🧠 refactor: Centralize Reasoning Block Classification * fix: Preserve Provider Reasoning Replay Policy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )