agents 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -1
- package/dist/{agent-routing-CnkaHb-v.d.ts → agent-routing-DE5zmCQ8.d.ts} +1038 -394
- package/dist/agent-routing.d.ts +1 -1
- package/dist/agent-tool-types.d.ts +26 -26
- package/dist/{agent-tools-C0XJqWzB.d.ts → agent-tools-DtXMTDGM.d.ts} +7 -7
- package/dist/agent-tools.d.ts +21 -21
- package/dist/browser/ai.js +4 -1
- package/dist/browser/ai.js.map +1 -1
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/callable-decorator-DP__HhBA.d.ts +72 -0
- package/dist/callable-decorator.d.ts +18 -0
- package/dist/callable-decorator.js +71 -0
- package/dist/callable-decorator.js.map +1 -0
- package/dist/capability-BjSKYpzg.js.map +1 -1
- package/dist/capability-runner-Be_-PLR1.d.ts +459 -0
- package/dist/channel-Bnm4S7T2.d.ts +491 -0
- package/dist/channels/ai-sdk.d.ts +46 -0
- package/dist/channels/ai-sdk.js +120 -0
- package/dist/channels/ai-sdk.js.map +1 -0
- package/dist/channels/email.d.ts +95 -0
- package/dist/channels/email.js +323 -0
- package/dist/channels/email.js.map +1 -0
- package/dist/channels/index.d.ts +233 -0
- package/dist/channels/index.js +608 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/slack.d.ts +140 -0
- package/dist/channels/slack.js +614 -0
- package/dist/channels/slack.js.map +1 -0
- package/dist/channels/tanstack-ai.d.ts +39 -0
- package/dist/channels/tanstack-ai.js +17 -0
- package/dist/channels/tanstack-ai.js.map +1 -0
- package/dist/channels/telegram.d.ts +106 -0
- package/dist/channels/telegram.js +427 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/voice.d.ts +45 -0
- package/dist/channels/voice.js +122 -0
- package/dist/channels/voice.js.map +1 -0
- package/dist/chat/index.d.ts +2325 -2019
- package/dist/chat/index.js +888 -518
- package/dist/chat/index.js.map +1 -1
- package/dist/chat-sdk/index.d.ts +7 -7
- package/dist/chat-sdk/index.js +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/context/index.d.ts +216 -0
- package/dist/context/index.js +454 -0
- package/dist/context/index.js.map +1 -0
- package/dist/{current-agent-CuMErtly.d.ts → current-agent-Da_C9a3b.d.ts} +90 -107
- package/dist/current-agent-DhoDkSnH.js.map +1 -1
- package/dist/{diagnostics-CaBjfz4J.js → diagnostics-BzvaX2UT.js} +5 -1
- package/dist/diagnostics-BzvaX2UT.js.map +1 -0
- package/dist/diagnostics-C4jcz3VK.js +360 -0
- package/dist/diagnostics-C4jcz3VK.js.map +1 -0
- package/dist/index-BB0kqhIz.d.ts +101 -0
- package/dist/index-BVVgDSdq.d.ts +1 -0
- package/dist/index-XDkuQ7zm.d.ts +89 -0
- package/dist/{index-DcSAZKsB.d.ts → index-YSKgfgg9.d.ts} +3 -1
- package/dist/index.d.ts +91 -82
- package/dist/index.js +3 -2
- package/dist/ingress-BfetZbMO.js +83 -0
- package/dist/ingress-BfetZbMO.js.map +1 -0
- package/dist/internal-CYlgHl1l.js +59 -0
- package/dist/internal-CYlgHl1l.js.map +1 -0
- package/dist/internal_context.d.ts +1 -1
- package/dist/lifecycle/index.d.ts +35 -17
- package/dist/lifecycle/index.js +1 -1
- package/dist/lifecycle-CMRGjZdw.js +1299 -0
- package/dist/lifecycle-CMRGjZdw.js.map +1 -0
- package/dist/mcp/client/index.d.ts +20 -20
- package/dist/mcp/index.d.ts +35 -35
- package/dist/mcp/index.js +1 -1
- package/dist/observability/index.d.ts +1 -1
- package/dist/observability/index.js +1 -1
- package/dist/react.d.ts +4 -4
- package/dist/{retries-CAvxtG9d.d.ts → retries-D9Ds-1lz.d.ts} +17 -6
- package/dist/retries.d.ts +8 -6
- package/dist/retries.js +13 -1
- package/dist/retries.js.map +1 -1
- package/dist/routing/index.d.ts +137 -0
- package/dist/routing/index.js +244 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/sanitize-D9TujEK8.js +79 -0
- package/dist/sanitize-D9TujEK8.js.map +1 -0
- package/dist/scheduler-DD9NdYbF.js +665 -0
- package/dist/scheduler-DD9NdYbF.js.map +1 -0
- package/dist/{scheduler-DQoTGoAW.d.ts → scheduler-Dwh85ZGl.d.ts} +21 -22
- package/dist/schedules/index.d.ts +1 -1
- package/dist/schedules/index.js +1 -1
- package/dist/sentence-chunker-BAidJ4DA.d.ts +68 -0
- package/dist/serializable.d.ts +1 -1
- package/dist/sessions/index.d.ts +441 -0
- package/dist/sessions/index.js +2063 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/skills/index.d.ts +99 -0
- package/dist/skills/index.js +254 -5
- package/dist/skills/index.js.map +1 -1
- package/dist/{src-5W6JNKVb.js → src-DlSHshb2.js} +1460 -1110
- package/dist/src-DlSHshb2.js.map +1 -0
- package/dist/streams/index.d.ts +120 -0
- package/dist/streams/index.js +107 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams-D6tJ0NN9.d.ts +370 -0
- package/dist/streams-DZKgAj9b.js +709 -0
- package/dist/streams-DZKgAj9b.js.map +1 -0
- package/dist/sub-routing.d.ts +12 -12
- package/dist/surface-bZZJqBka.js +17 -0
- package/dist/surface-bZZJqBka.js.map +1 -0
- package/dist/tasks/index.d.ts +64 -0
- package/dist/tasks/index.js +2 -0
- package/dist/tasks-BRJ5zgya.d.ts +517 -0
- package/dist/tasks-ylZgBjhj.js +1656 -0
- package/dist/tasks-ylZgBjhj.js.map +1 -0
- package/dist/text-segment-joiner-BtAFQSA_.js +57 -0
- package/dist/text-segment-joiner-BtAFQSA_.js.map +1 -0
- package/dist/text-stream-CpdiKrJB.js +272 -0
- package/dist/text-stream-CpdiKrJB.js.map +1 -0
- package/dist/tokens-nHAKcN6M.js +52 -0
- package/dist/tokens-nHAKcN6M.js.map +1 -0
- package/dist/tool-schema-CBjGPrsQ.js +31 -0
- package/dist/tool-schema-CBjGPrsQ.js.map +1 -0
- package/dist/types-B7LojTe4.d.ts +202 -0
- package/dist/types-_Faxb570.d.ts +439 -0
- package/dist/voice/client.d.ts +226 -0
- package/dist/voice/client.js +932 -0
- package/dist/voice/client.js.map +1 -0
- package/dist/voice/errors.d.ts +43 -0
- package/dist/voice/errors.js +41 -0
- package/dist/voice/errors.js.map +1 -0
- package/dist/voice/index.d.ts +271 -0
- package/dist/voice/index.js +1812 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/react.d.ts +167 -0
- package/dist/voice/react.js +234 -0
- package/dist/voice/react.js.map +1 -0
- package/dist/voice/sfu.d.ts +71 -0
- package/dist/voice/sfu.js +157 -0
- package/dist/voice/sfu.js.map +1 -0
- package/dist/voice/text.d.ts +6 -0
- package/dist/voice/text.js +2 -0
- package/dist/voice/types.d.ts +58 -0
- package/dist/voice/types.js +18 -0
- package/dist/voice/types.js.map +1 -0
- package/dist/voice/workers-ai.d.ts +136 -0
- package/dist/voice/workers-ai.js +568 -0
- package/dist/voice/workers-ai.js.map +1 -0
- package/dist/websockets/index.d.ts +192 -0
- package/dist/websockets/index.js +2 -0
- package/dist/websockets-DUfRHPRq.js +502 -0
- package/dist/websockets-DUfRHPRq.js.map +1 -0
- package/dist/workflow-types.d.ts +25 -25
- package/dist/workflows.d.ts +21 -21
- package/dist/workflows.js +1 -1
- package/docs/agent-class.md +2 -2
- package/docs/agent-tools.md +2 -1
- package/docs/channels.md +323 -0
- package/docs/chat-agents.md +6 -13
- package/docs/context.md +131 -0
- package/docs/index.md +15 -12
- package/docs/lifecycle.md +102 -55
- package/docs/long-running-agents.md +2 -2
- package/docs/mcp-servers.md +5 -1
- package/docs/resumable-streaming.md +1 -1
- package/docs/routing.md +105 -0
- package/docs/sessions.md +237 -871
- package/docs/streams.md +213 -0
- package/docs/sub-agents.md +184 -124
- package/docs/tasks.md +246 -0
- package/docs/voice.md +745 -0
- package/package.json +115 -13
- package/dist/capability-runner-CvHGZqUu.d.ts +0 -150
- package/dist/compaction-helpers-iiKMr2TQ.js +0 -340
- package/dist/compaction-helpers-iiKMr2TQ.js.map +0 -1
- package/dist/compaction-helpers-wUz6M3us.d.ts +0 -621
- package/dist/diagnostics-CaBjfz4J.js.map +0 -1
- package/dist/durable-object-lifecycle-D6nNQJJd.js +0 -862
- package/dist/durable-object-lifecycle-D6nNQJJd.js.map +0 -1
- package/dist/experimental/memory/session/index.d.ts +0 -671
- package/dist/experimental/memory/session/index.js +0 -2379
- package/dist/experimental/memory/session/index.js.map +0 -1
- package/dist/experimental/memory/utils/index.d.ts +0 -96
- package/dist/experimental/memory/utils/index.js +0 -79
- package/dist/experimental/memory/utils/index.js.map +0 -1
- package/dist/scheduler-CR9RHGos.js +0 -857
- package/dist/scheduler-CR9RHGos.js.map +0 -1
- package/dist/src-5W6JNKVb.js.map +0 -1
- package/dist/tool-output-truncation-CNnnGZQ3.js +0 -98
- package/dist/tool-output-truncation-CNnnGZQ3.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"compaction-helpers-iiKMr2TQ.js","names":[],"sources":["../src/experimental/memory/utils/tokens.ts","../src/experimental/memory/utils/compaction-helpers.ts"],"sourcesContent":["/**\n * Token Estimation Utilities\n *\n * IMPORTANT: These are heuristic estimates, not actual tokenizer counts.\n *\n * We intentionally avoid real tokenizers (e.g. tiktoken, sentencepiece) because:\n * - A single tiktoken instance costs ~80-120MB of heap\n * - Cloudflare Workers have tight memory limits (128MB)\n * - For compaction thresholds, a conservative estimate is sufficient\n *\n * The hybrid approach (max of character-based and word-based estimates) handles\n * both dense token content (JSON, code) and natural language reasonably well.\n *\n * Calibration notes:\n * - Character-based: ~4 chars per token (conservative, from OpenAI guidance)\n * - Word-based: ~1.3 tokens per word (empirical, from Mastra's memory system)\n * - Per-message overhead: ~4 tokens for role/framing (empirical)\n *\n * These ratios are tuned for English. CJK, emoji-heavy, or highly technical\n * content may have different ratios. The conservative estimates help ensure\n * compaction triggers before context windows are actually exceeded.\n */\n\nimport type { SessionMessage } from \"../session/types\";\n\n/** Approximate characters per token for English text */\nexport const CHARS_PER_TOKEN = 4;\n\n/** Approximate token multiplier per whitespace-separated word */\nexport const WORDS_TOKEN_MULTIPLIER = 1.3;\n\n/** Approximate overhead tokens per message (role, framing) */\nexport const TOKENS_PER_MESSAGE = 4;\n\n/**\n * Estimate token count for a string using a hybrid heuristic.\n *\n * Takes the max of two estimates:\n * - Character-based: `length / 4` — better for dense content (JSON, code, URLs)\n * - Word-based: `words * 1.3` — better for natural language prose\n *\n * This is a heuristic. Do not use where exact counts are required.\n */\nexport function estimateStringTokens(text: string): number {\n if (!text) return 0;\n const charEstimate = text.length / CHARS_PER_TOKEN;\n const wordEstimate =\n text.split(/\\s+/).filter(Boolean).length * WORDS_TOKEN_MULTIPLIER;\n return Math.ceil(Math.max(charEstimate, wordEstimate));\n}\n\nfunction estimateUnknownTokens(value: unknown): number {\n if (value === null || value === undefined) return 0;\n if (typeof value === \"string\") return estimateStringTokens(value);\n\n try {\n return estimateStringTokens(JSON.stringify(value));\n } catch {\n return estimateStringTokens(String(value));\n }\n}\n\n/**\n * Estimate total token count for an array of UIMessages.\n *\n * Walks each message's parts (text, reasoning, tool invocations, tool results)\n * and applies per-message overhead.\n *\n * This is a heuristic. Do not use where exact counts are required.\n */\nexport function estimateMessageTokens(messages: SessionMessage[]): number {\n let tokens = 0;\n for (const msg of messages) {\n tokens += TOKENS_PER_MESSAGE;\n for (const part of msg.parts) {\n if (part.type === \"text\" || part.type === \"reasoning\") {\n tokens += estimateUnknownTokens(part.text ?? part.reasoning);\n } else if (\n part.type.startsWith(\"tool-\") ||\n part.type === \"dynamic-tool\"\n ) {\n tokens += estimateUnknownTokens(part.input);\n tokens += estimateUnknownTokens(part.output ?? part.result);\n } else if (part.text !== undefined) {\n tokens += estimateUnknownTokens(part.text);\n } else if (part.result !== undefined) {\n tokens += estimateUnknownTokens(part.result);\n }\n }\n }\n return tokens;\n}\n","/**\n * Compaction Helpers\n *\n * Utilities for full compaction (LLM-based summarization).\n * Used by the reference compaction implementation and available\n * for custom CompactFunction implementations.\n */\n\nimport type { CompactContext, SessionMessage } from \"../session/types\";\nimport { estimateMessageTokens } from \"./tokens\";\n\nexport type CompactTokenCounter = (\n messages: SessionMessage[]\n) => number | Promise<number>;\n\n// ── Compaction ID constants ─────────────────────────────────────────\n\n/** Prefix for all compaction messages (overlays and summaries) */\nexport const COMPACTION_PREFIX = \"compaction_\";\n\n/** Check if a message is a compaction message */\nexport function isCompactionMessage(msg: SessionMessage): boolean {\n return msg.id.startsWith(COMPACTION_PREFIX);\n}\n\n// ── Tool Pair Alignment ──────────────────────────────────────────────\n\n/**\n * Check if a message contains tool invocations.\n */\nfunction hasToolCalls(msg: SessionMessage): boolean {\n return msg.parts.some(\n (p) => p.type.startsWith(\"tool-\") || p.type === \"dynamic-tool\"\n );\n}\n\n/**\n * Get tool call IDs from a message's parts.\n */\nfunction getToolCallIds(msg: SessionMessage): Set<string> {\n const ids = new Set<string>();\n for (const part of msg.parts) {\n if (\n (part.type.startsWith(\"tool-\") || part.type === \"dynamic-tool\") &&\n \"toolCallId\" in part\n ) {\n ids.add((part as { toolCallId: string }).toolCallId);\n }\n }\n return ids;\n}\n\n/**\n * Check if a message is a tool result referencing a specific call ID.\n */\nfunction isToolResultFor(msg: SessionMessage, callIds: Set<string>): boolean {\n return msg.parts.some(\n (p) =>\n (p.type.startsWith(\"tool-\") || p.type === \"dynamic-tool\") &&\n \"toolCallId\" in p &&\n callIds.has((p as { toolCallId: string }).toolCallId)\n );\n}\n\n/**\n * Align a boundary index forward to avoid splitting tool call/result groups.\n * If the boundary falls between an assistant message with tool calls and its\n * tool results, move it forward past the results.\n */\nexport function alignBoundaryForward(\n messages: SessionMessage[],\n idx: number\n): number {\n if (idx <= 0 || idx >= messages.length) return idx;\n\n // Check if the message before the boundary has tool calls\n const prev = messages[idx - 1];\n if (prev.role === \"assistant\" && hasToolCalls(prev)) {\n const callIds = getToolCallIds(prev);\n // Skip forward past any tool results for these calls\n while (idx < messages.length && isToolResultFor(messages[idx], callIds)) {\n idx++;\n }\n }\n\n return idx;\n}\n\n/**\n * Align a boundary index backward to avoid splitting tool call/result groups.\n * If the boundary falls in the middle of tool results, move it backward to\n * include the assistant message that made the calls.\n */\nexport function alignBoundaryBackward(\n messages: SessionMessage[],\n idx: number\n): number {\n if (idx <= 0 || idx >= messages.length) return idx;\n\n // If the message at idx is a tool result, walk backward to find the call\n while (idx > 0) {\n const msg = messages[idx];\n if (msg.role === \"assistant\" && hasToolCalls(msg)) {\n break; // This is a tool call message — include it\n }\n // Check if this looks like a tool result (assistant message following another)\n const prev = messages[idx - 1];\n if (prev.role === \"assistant\" && hasToolCalls(prev)) {\n const callIds = getToolCallIds(prev);\n if (isToolResultFor(msg, callIds)) {\n idx--; // Move back to include the call\n continue;\n }\n }\n break;\n }\n\n return idx;\n}\n\n// ── Token-Budget Tail Protection ─────────────────────────────────────\n\n/**\n * Find the compression end boundary using a token budget for the tail.\n * Walks backward from the end, accumulating tokens until budget is reached.\n * Returns the index where compression should stop (everything from this\n * index onward is protected).\n *\n * @param messages All messages\n * @param headEnd Index where the protected head ends (compression starts here)\n * @param tailTokenBudget Maximum tokens to keep in the tail\n * @param minTailMessages Minimum messages to protect in the tail (fallback)\n */\nexport function findTailCutByTokens(\n messages: SessionMessage[],\n headEnd: number,\n tailTokenBudget = 20000,\n minTailMessages = 2\n): number {\n const n = messages.length;\n let accumulated = 0;\n let tokenCut = n;\n\n for (let i = n - 1; i >= headEnd; i--) {\n const msgTokens = estimateMessageTokens([messages[i]]);\n\n if (accumulated + msgTokens > tailTokenBudget && tokenCut < n) {\n // Budget exceeded and we already have at least one tail message\n break;\n }\n accumulated += msgTokens;\n tokenCut = i;\n }\n\n // Protect whichever is larger: token-based tail or minTailMessages\n const minCut = n - minTailMessages;\n const cutIdx = minCut >= headEnd ? Math.min(tokenCut, minCut) : tokenCut;\n\n // Align to avoid splitting tool groups\n return alignBoundaryBackward(messages, cutIdx);\n}\n\nasync function findTailCutByTokensWithCounter(\n messages: SessionMessage[],\n headEnd: number,\n tokenCounter: CompactTokenCounter,\n tailTokenBudget = 20000,\n minTailMessages = 2\n): Promise<number> {\n const n = messages.length;\n let accumulated = 0;\n let tokenCut = n;\n\n for (let i = n - 1; i >= headEnd; i--) {\n const msgTokens = await tokenCounter([messages[i]]);\n\n if (accumulated + msgTokens > tailTokenBudget && tokenCut < n) {\n break;\n }\n accumulated += msgTokens;\n tokenCut = i;\n }\n\n const minCut = n - minTailMessages;\n const cutIdx = minCut >= headEnd ? Math.min(tokenCut, minCut) : tokenCut;\n return alignBoundaryBackward(messages, cutIdx);\n}\n\n// ── Tool Pair Sanitization ───────────────────────────────────────────\n\n/**\n * Fix orphaned tool call/result pairs after compaction.\n *\n * Two failure modes:\n * 1. Tool result references a call_id whose assistant tool_call was removed\n * → Remove the orphaned result\n * 2. Assistant has tool_calls whose results were dropped\n * → Add stub results so the API doesn't error\n *\n * @param messages Messages after compaction\n * @returns Sanitized messages with no orphaned pairs\n */\nexport function sanitizeToolPairs(\n messages: SessionMessage[]\n): SessionMessage[] {\n // Build set of surviving tool call IDs (from assistant messages)\n const survivingCallIds = new Set<string>();\n for (const msg of messages) {\n if (msg.role === \"assistant\") {\n for (const id of getToolCallIds(msg)) {\n survivingCallIds.add(id);\n }\n }\n }\n\n // Build set of tool result IDs\n const resultCallIds = new Set<string>();\n for (const msg of messages) {\n for (const part of msg.parts) {\n if (\n (part.type.startsWith(\"tool-\") || part.type === \"dynamic-tool\") &&\n \"toolCallId\" in part &&\n \"output\" in part\n ) {\n resultCallIds.add((part as { toolCallId: string }).toolCallId);\n }\n }\n }\n\n // Remove orphaned results (results whose calls were dropped)\n const orphanedResults = new Set<string>();\n for (const id of resultCallIds) {\n if (!survivingCallIds.has(id)) {\n orphanedResults.add(id);\n }\n }\n\n let result = messages;\n if (orphanedResults.size > 0) {\n result = result.map((msg) => {\n const filteredParts = msg.parts.filter((part) => {\n if (\n (part.type.startsWith(\"tool-\") || part.type === \"dynamic-tool\") &&\n \"toolCallId\" in part &&\n \"output\" in part\n ) {\n return !orphanedResults.has(\n (part as { toolCallId: string }).toolCallId\n );\n }\n return true;\n });\n if (filteredParts.length !== msg.parts.length) {\n return { ...msg, parts: filteredParts } as SessionMessage;\n }\n return msg;\n });\n }\n\n // Add stub results for calls whose results were dropped\n const missingResults = new Set<string>();\n for (const id of survivingCallIds) {\n if (!resultCallIds.has(id) && !orphanedResults.has(id)) {\n missingResults.add(id);\n }\n }\n\n if (missingResults.size > 0) {\n const patched: SessionMessage[] = [];\n for (const msg of result) {\n patched.push(msg);\n if (msg.role === \"assistant\") {\n for (const id of getToolCallIds(msg)) {\n if (missingResults.has(id)) {\n // Find the tool name from the call\n const callPart = msg.parts.find(\n (p) =>\n \"toolCallId\" in p &&\n (p as { toolCallId: string }).toolCallId === id\n ) as { toolName?: string } | undefined;\n\n patched.push({\n id: `stub-${id}`,\n role: \"assistant\",\n parts: [\n {\n type: \"tool-result\" as const,\n toolCallId: id,\n toolName: callPart?.toolName ?? \"unknown\",\n result:\n \"[Result from earlier conversation — see context summary above]\"\n } as unknown as SessionMessage[\"parts\"][number]\n ],\n createdAt: new Date()\n } as SessionMessage);\n }\n }\n }\n }\n result = patched;\n }\n\n // Remove empty messages (all parts filtered out)\n return result.filter((msg) => msg.parts.length > 0);\n}\n\n// ── Summary Budget ───────────────────────────────────────────────────\n\n/**\n * Compute a summary token budget based on the content being compressed.\n * 20% of the compressed content, clamped to 2K-8K tokens.\n */\nexport function computeSummaryBudget(messages: SessionMessage[]): number {\n const contentTokens = estimateMessageTokens(messages);\n // Summary is ~20% of the content being compressed.\n // The summary replaces the compressed middle, so it's sized relative\n // to what it's replacing — not the tail budget (they occupy different\n // slots in the context window).\n const budget = Math.floor(contentTokens * 0.2);\n return Math.max(100, budget);\n}\n\n// ── Structured Summary Prompt ────────────────────────────────────────\n\n/**\n * Build a prompt for LLM summarization of compressed messages.\n *\n * @param messages Messages to summarize\n * @param previousSummary Previous summary for iterative updates (or null for first compaction)\n * @param budget Target token count for the summary\n */\nexport function buildSummaryPrompt(\n messages: SessionMessage[],\n previousSummary: string | null,\n budget: number\n): string {\n const content = messages\n .map((msg) => {\n const textParts = msg.parts\n .filter((p) => p.type === \"text\")\n .map((p) => (p as { text: string }).text)\n .join(\"\\n\");\n\n const toolParts = msg.parts\n .filter((p) => p.type.startsWith(\"tool-\") || p.type === \"dynamic-tool\")\n .map((p) => {\n const tp = p as {\n toolName?: string;\n input?: unknown;\n output?: unknown;\n };\n const parts = [`[Tool: ${tp.toolName ?? \"unknown\"}]`];\n if (tp.input)\n parts.push(`Input: ${JSON.stringify(tp.input).slice(0, 500)}`);\n if (tp.output)\n parts.push(`Output: ${String(tp.output).slice(0, 500)}`);\n return parts.join(\"\\n\");\n })\n .join(\"\\n\");\n\n return `[${msg.role}]\\n${textParts}${toolParts ? \"\\n\" + toolParts : \"\"}`;\n })\n .join(\"\\n\\n---\\n\\n\");\n\n if (previousSummary) {\n return `You are updating a conversation summary. A previous summary exists below. New conversation turns have occurred since then and need to be incorporated.\n\nPREVIOUS SUMMARY:\n${previousSummary}\n\nNEW TURNS TO INCORPORATE:\n${content}\n\nUpdate the summary. PRESERVE existing information that is still relevant. ADD new information. Remove information only if it is clearly obsolete.\n\n## Topic\n[What the conversation is about]\n\n## Key Points\n[Important information, decisions, and conclusions from the conversation]\n\n## Current State\n[Where things stand now — what has been done, what is in progress]\n\n## Open Items\n[Unresolved questions, pending tasks, or next steps discussed]\n\nTarget ~${budget} tokens. Be factual — only include information that was explicitly discussed in the conversation. Do NOT invent file paths, commands, or details that were not mentioned. Write only the summary body.`;\n }\n\n return `Create a concise summary of this conversation that preserves the important information for future context.\n\nCONVERSATION TO SUMMARIZE:\n${content}\n\nUse this structure:\n\n## Topic\n[What the conversation is about]\n\n## Key Points\n[Important information, decisions, and conclusions from the conversation]\n\n## Current State\n[Where things stand now — what has been done, what is in progress]\n\n## Open Items\n[Unresolved questions, pending tasks, or next steps discussed]\n\nTarget ~${budget} tokens. Be factual — only include information that was explicitly discussed in the conversation. Do NOT invent file paths, commands, or details that were not mentioned. Write only the summary body.`;\n}\n\n// ── Reference Compaction Implementation ──────────────────────────────\n\n/**\n * Result of a compaction function — describes the overlay to store.\n */\nexport interface CompactResult {\n /** First message ID in the compacted range */\n fromMessageId: string;\n /** Last message ID in the compacted range */\n toMessageId: string;\n /** Summary text to store as the overlay */\n summary: string;\n}\n\nexport interface CompactOptions {\n /**\n * Function to call the LLM for summarization.\n * Takes a user prompt string, returns the LLM's text response.\n */\n summarize: (prompt: string) => Promise<string>;\n\n /** Number of head messages to protect (default: 2) */\n protectHead?: number;\n\n /** Token budget for tail protection (default: 20000) */\n tailTokenBudget?: number;\n\n /** Minimum tail messages to protect (default: 2) */\n minTailMessages?: number;\n\n /**\n * Optional counter for tail-budget decisions. Use this when a tokenizer or\n * model-reported accounting is available; otherwise the Workers-safe\n * heuristic is used.\n */\n tokenCounter?: CompactTokenCounter;\n}\n\n/**\n * Reference compaction implementation.\n *\n * Implements the full hermes-style compaction algorithm:\n * 1. Protect head messages (first N)\n * 2. Protect tail by token budget (walk backward)\n * 3. Align boundaries to tool call groups\n * 4. Summarize middle section with LLM (structured format)\n * 5. Sanitize orphaned tool pairs\n * 6. Iterative summary updates on subsequent compactions\n *\n * @example\n * ```typescript\n * import { createCompactFunction } from \"agents/experimental/memory/utils\";\n *\n * const session = new Session(provider, {\n * compaction: {\n * tokenThreshold: 100000,\n * fn: createCompactFunction({\n * summarize: (prompt) => generateText({ model, prompt }).then(r => r.text)\n * })\n * }\n * });\n * ```\n */\nexport function createCompactFunction(opts: CompactOptions) {\n const protectHead = opts.protectHead ?? 3;\n const tailTokenBudget = opts.tailTokenBudget ?? 20000;\n const minTailMessages = opts.minTailMessages ?? 2;\n\n return async (\n messages: SessionMessage[],\n context?: CompactContext\n ): Promise<CompactResult | null> => {\n if (messages.length <= protectHead + minTailMessages) {\n return null;\n }\n\n // Prefer an explicit counter; otherwise adapt the Session's counter (flowed\n // via CompactContext) so a single `tokenCounter` on `compactAfter` drives\n // the boundary cut too — without it, a fire counter + the default heuristic\n // under-counting a tool-heavy history makes compaction fire every turn but\n // never shorten anything. The session counter is whole-prompt shaped; for\n // the tail walk we feed it individual messages with empty system/context.\n //\n // Caveat: this counter is invoked once PER MESSAGE. A tokenizer-style\n // counter yields accurate per-message tokens; a counter that returns a\n // fixed whole-prompt total (e.g. `usage.inputTokens`) returns the same\n // value for every message, which degrades `tailTokenBudget` to\n // `minTailMessages` — compaction still runs and context stays bounded, but\n // the byte budget is effectively ignored. It is also called O(n) times per\n // compaction, so an async/remote counter (e.g. a `count_tokens` API) will\n // be slow. For precise tail budgeting with such counters, pass an explicit\n // per-message `CompactOptions.tokenCounter` instead.\n const sessionCounter = context?.tokenCounter;\n const tailCounter: CompactTokenCounter | undefined =\n opts.tokenCounter ??\n (sessionCounter\n ? (msgs) =>\n sessionCounter({\n messages: msgs,\n systemPrompt: \"\",\n contextBlocks: []\n })\n : undefined);\n\n // 1. Find compression boundaries\n let compressStart = protectHead;\n compressStart = alignBoundaryForward(messages, compressStart);\n\n let compressEnd = tailCounter\n ? await findTailCutByTokensWithCounter(\n messages,\n compressStart,\n tailCounter,\n tailTokenBudget,\n minTailMessages\n )\n : findTailCutByTokens(\n messages,\n compressStart,\n tailTokenBudget,\n minTailMessages\n );\n\n if (compressEnd <= compressStart) {\n return null;\n }\n\n // Filter out compaction overlay messages — they have virtual IDs\n // and should not be included in the summary prompt or used as range IDs\n const middleMessages = messages\n .slice(compressStart, compressEnd)\n .filter((m) => !isCompactionMessage(m));\n\n if (middleMessages.length === 0) return null;\n\n // 2. Generate summary — extract previous summary from compaction overlays\n const existingCompaction = messages.find(isCompactionMessage);\n const previousSummary = existingCompaction\n ? existingCompaction.parts\n .filter((p) => p.type === \"text\")\n .map((p) => (p as { text: string }).text)\n .join(\"\\n\")\n : null;\n\n const budget = computeSummaryBudget(middleMessages);\n const prompt = buildSummaryPrompt(middleMessages, previousSummary, budget);\n const summary = await opts.summarize(prompt);\n\n if (!summary.trim()) return null;\n\n return {\n fromMessageId: middleMessages[0].id,\n toMessageId: middleMessages[middleMessages.length - 1].id,\n summary\n };\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,621 +0,0 @@
|
|
|
1
|
-
import { ToolSet } from "ai";
|
|
2
|
-
|
|
3
|
-
//#region src/experimental/memory/session/types.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* Minimal message part shape used by Session internals.
|
|
6
|
-
* Vercel AI SDK's `UIMessagePart` is structurally compatible.
|
|
7
|
-
*/
|
|
8
|
-
interface SessionMessagePart {
|
|
9
|
-
type: string;
|
|
10
|
-
text?: string;
|
|
11
|
-
reasoning?: string;
|
|
12
|
-
toolCallId?: string;
|
|
13
|
-
toolName?: string;
|
|
14
|
-
input?: unknown;
|
|
15
|
-
output?: unknown;
|
|
16
|
-
state?: string;
|
|
17
|
-
result?: unknown;
|
|
18
|
-
}
|
|
19
|
-
interface SessionTokenCounterInput {
|
|
20
|
-
/** Messages returned by `session.getHistory()` for the active branch. */
|
|
21
|
-
messages: SessionMessage[];
|
|
22
|
-
/** Frozen system prompt managed by the Session context system. */
|
|
23
|
-
systemPrompt: string;
|
|
24
|
-
/** Loaded context blocks that were used to build `systemPrompt`. */
|
|
25
|
-
contextBlocks: ContextBlock[];
|
|
26
|
-
}
|
|
27
|
-
type SessionTokenCounter = (
|
|
28
|
-
input: SessionTokenCounterInput
|
|
29
|
-
) => number | Promise<number>;
|
|
30
|
-
interface CompactAfterOptions {
|
|
31
|
-
/**
|
|
32
|
-
* Override the token estimate used by auto-compaction and status broadcasts.
|
|
33
|
-
*
|
|
34
|
-
* The default is a Workers-safe heuristic over message parts plus the
|
|
35
|
-
* Session-managed frozen system prompt. Callers that have model-reported
|
|
36
|
-
* usage or a tokenizer can provide a more precise counter here.
|
|
37
|
-
*/
|
|
38
|
-
tokenCounter?: SessionTokenCounter;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Context the Session passes to the registered compaction function. Lets the
|
|
42
|
-
* same authoritative token accounting drive BOTH the "should we compact?"
|
|
43
|
-
* (`compactAfter`) and "what should we compact?" (boundary) decisions, so a
|
|
44
|
-
* consumer that wires a `tokenCounter` once doesn't hit the failure mode where
|
|
45
|
-
* compaction fires every turn but silently no-ops because the boundary logic
|
|
46
|
-
* used a different (under-counting) estimate.
|
|
47
|
-
*/
|
|
48
|
-
interface CompactContext {
|
|
49
|
-
/** The Session's token counter (from `compactAfter`/options), if configured. */
|
|
50
|
-
tokenCounter?: SessionTokenCounter;
|
|
51
|
-
}
|
|
52
|
-
type CompactionErrorHandler = (error: unknown) => void | Promise<void>;
|
|
53
|
-
/**
|
|
54
|
-
* Minimal message shape used by Session internals.
|
|
55
|
-
* Vercel AI SDK's `UIMessage` is structurally compatible — you can pass
|
|
56
|
-
* `UIMessage` objects directly without conversion.
|
|
57
|
-
*/
|
|
58
|
-
interface SessionMessage {
|
|
59
|
-
id: string;
|
|
60
|
-
role: string;
|
|
61
|
-
parts: SessionMessagePart[];
|
|
62
|
-
createdAt?: Date;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Options for creating a Session.
|
|
66
|
-
*/
|
|
67
|
-
interface SessionOptions {
|
|
68
|
-
/** Context blocks for the system prompt. */
|
|
69
|
-
context?: ContextConfig[];
|
|
70
|
-
/** Provider for persisting the frozen system prompt. */
|
|
71
|
-
promptStore?: WritableContextProvider;
|
|
72
|
-
/** Custom token counter for auto-compaction/status estimates. */
|
|
73
|
-
tokenCounter?: SessionTokenCounter;
|
|
74
|
-
/** Called when automatic compaction fails after a threshold trigger. */
|
|
75
|
-
onCompactionError?: CompactionErrorHandler;
|
|
76
|
-
}
|
|
77
|
-
//#endregion
|
|
78
|
-
//#region src/experimental/memory/session/provider.d.ts
|
|
79
|
-
interface SearchResult {
|
|
80
|
-
id: string;
|
|
81
|
-
role: string;
|
|
82
|
-
content: string;
|
|
83
|
-
createdAt?: string;
|
|
84
|
-
sessionId?: string;
|
|
85
|
-
}
|
|
86
|
-
interface StoredCompaction {
|
|
87
|
-
id: string;
|
|
88
|
-
summary: string;
|
|
89
|
-
fromMessageId: string;
|
|
90
|
-
toMessageId: string;
|
|
91
|
-
createdAt: string;
|
|
92
|
-
}
|
|
93
|
-
/** Per-row info for the active branch path, root → leaf order. */
|
|
94
|
-
interface HistoryRowStat {
|
|
95
|
-
id: string;
|
|
96
|
-
/** Stored message role (e.g. "user" / "assistant"). */
|
|
97
|
-
role: string;
|
|
98
|
-
/** Serialized content size of the stored row in bytes. */
|
|
99
|
-
bytes: number;
|
|
100
|
-
}
|
|
101
|
-
/** Result of a byte-budgeted history read. */
|
|
102
|
-
interface RecentHistoryResult {
|
|
103
|
-
/**
|
|
104
|
-
* The most recent messages on the active branch path whose summed stored
|
|
105
|
-
* content size fits `maxContentBytes`, root → leaf order, with compaction
|
|
106
|
-
* overlays applied within the window. The window always covers at least
|
|
107
|
-
* the leaf row (and `minRecentMessages` rows when requested), but rows
|
|
108
|
-
* whose stored content fails to parse are skipped — so a corrupt leaf can
|
|
109
|
-
* yield fewer messages than the window covers.
|
|
110
|
-
*/
|
|
111
|
-
messages: SessionMessage[];
|
|
112
|
-
/** True when older messages were left out to satisfy the byte budget. */
|
|
113
|
-
truncated: boolean;
|
|
114
|
-
/** Summed stored content size of the FULL path, in bytes. */
|
|
115
|
-
totalContentBytes: number;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Session storage provider.
|
|
119
|
-
* Messages are tree-structured via parentId for branching.
|
|
120
|
-
*/
|
|
121
|
-
interface SessionProvider {
|
|
122
|
-
getMessage(
|
|
123
|
-
id: string
|
|
124
|
-
): SessionMessage | null | Promise<SessionMessage | null>;
|
|
125
|
-
/**
|
|
126
|
-
* Get conversation as a path from root to leaf.
|
|
127
|
-
* Applies compaction overlays. If leafId is null, uses the latest leaf.
|
|
128
|
-
*/
|
|
129
|
-
getHistory(
|
|
130
|
-
leafId?: string | null
|
|
131
|
-
): SessionMessage[] | Promise<SessionMessage[]>;
|
|
132
|
-
getLatestLeaf(): SessionMessage | null | Promise<SessionMessage | null>;
|
|
133
|
-
getBranches(messageId: string): SessionMessage[] | Promise<SessionMessage[]>;
|
|
134
|
-
getPathLength(leafId?: string | null): number | Promise<number>;
|
|
135
|
-
/**
|
|
136
|
-
* Optional: byte-budgeted read of the most recent messages on the active
|
|
137
|
-
* branch path. Lets hosts hydrate a bounded window instead of the full
|
|
138
|
-
* transcript, so wake-time memory scales with the budget rather than total
|
|
139
|
-
* session history (#1710). Providers that don't implement it fall back to
|
|
140
|
-
* a full `getHistory()` read in `Session.getRecentHistory()`.
|
|
141
|
-
*
|
|
142
|
-
* `minRecentMessages` (default 1) is a floor on the window size: the most
|
|
143
|
-
* recent N rows are always included even when they exceed the byte budget.
|
|
144
|
-
* Hosts use this to guarantee the window never shrinks below the recent
|
|
145
|
-
* span their model context assembly expects (rows are individually capped
|
|
146
|
-
* at write time, so the floor keeps memory bounded).
|
|
147
|
-
*/
|
|
148
|
-
getRecentHistory?(
|
|
149
|
-
leafId: string | null | undefined,
|
|
150
|
-
maxContentBytes: number,
|
|
151
|
-
minRecentMessages?: number
|
|
152
|
-
): RecentHistoryResult | Promise<RecentHistoryResult>;
|
|
153
|
-
/**
|
|
154
|
-
* Optional: per-row stored sizes for the active branch path (root → leaf),
|
|
155
|
-
* WITHOUT loading message content. Lets hosts find oversized rows (e.g.
|
|
156
|
-
* inline base64 media) and process them one at a time with bounded memory.
|
|
157
|
-
*/
|
|
158
|
-
getHistoryRowStats?(
|
|
159
|
-
leafId?: string | null
|
|
160
|
-
): HistoryRowStat[] | Promise<HistoryRowStat[]>;
|
|
161
|
-
/**
|
|
162
|
-
* Append a message.
|
|
163
|
-
*
|
|
164
|
-
* `parentId` semantics:
|
|
165
|
-
* - `undefined` / omitted → auto-detect: attach to the current latest leaf.
|
|
166
|
-
* - `null` → create a root message with no parent.
|
|
167
|
-
* - string → attach to the given parent id (provider may
|
|
168
|
-
* fall back to root if the parent doesn't
|
|
169
|
-
* belong to this session).
|
|
170
|
-
*
|
|
171
|
-
* Idempotent — appending the same `message.id` twice is a no-op.
|
|
172
|
-
*/
|
|
173
|
-
appendMessage(
|
|
174
|
-
message: SessionMessage,
|
|
175
|
-
parentId?: string | null
|
|
176
|
-
): void | Promise<void>;
|
|
177
|
-
updateMessage(message: SessionMessage): void | Promise<void>;
|
|
178
|
-
deleteMessages(messageIds: string[]): void | Promise<void>;
|
|
179
|
-
clearMessages(): void | Promise<void>;
|
|
180
|
-
addCompaction(
|
|
181
|
-
summary: string,
|
|
182
|
-
fromMessageId: string,
|
|
183
|
-
toMessageId: string
|
|
184
|
-
): StoredCompaction | Promise<StoredCompaction>;
|
|
185
|
-
getCompactions(): StoredCompaction[] | Promise<StoredCompaction[]>;
|
|
186
|
-
searchMessages?(
|
|
187
|
-
query: string,
|
|
188
|
-
limit?: number
|
|
189
|
-
): SearchResult[] | Promise<SearchResult[]>;
|
|
190
|
-
}
|
|
191
|
-
//#endregion
|
|
192
|
-
//#region src/experimental/memory/session/providers/agent.d.ts
|
|
193
|
-
interface SqlProvider {
|
|
194
|
-
sql<T = Record<string, string | number | boolean | null>>(
|
|
195
|
-
strings: TemplateStringsArray,
|
|
196
|
-
...values: (string | number | boolean | null)[]
|
|
197
|
-
): T[];
|
|
198
|
-
}
|
|
199
|
-
declare class AgentSessionProvider implements SessionProvider {
|
|
200
|
-
private agent;
|
|
201
|
-
private initialized;
|
|
202
|
-
private sessionId;
|
|
203
|
-
/**
|
|
204
|
-
* Cached id of the active branch tip (latest leaf). `undefined` means "not
|
|
205
|
-
* cached" (cold, or last lookup found the session empty).
|
|
206
|
-
*
|
|
207
|
-
* Finding the tip from scratch is an anti-join over every row in the
|
|
208
|
-
* session (`latestLeafRow`), which is O(rows). It runs on every hydration
|
|
209
|
-
* AND every auto-parent append, so on a long transcript it dominates the
|
|
210
|
-
* read cost of a wake. The tip is maintained in place on append/delete/
|
|
211
|
-
* clear, and a cached id is re-validated on read with an O(1) existence +
|
|
212
|
-
* still-childless check before it's trusted — so the cache self-heals if
|
|
213
|
-
* something else mutates the cached tip: a deleted tip or a tip that gained
|
|
214
|
-
* a child fails the check and triggers a single recompute. Direct SQL or a
|
|
215
|
-
* second provider instance that creates a newer leaf without touching the
|
|
216
|
-
* cached tip is outside the supported writer model and will be observed on
|
|
217
|
-
* the next cold lookup. The full scan therefore never runs more often than
|
|
218
|
-
* the original unconditional version did. Reads are synchronous and the DO
|
|
219
|
-
* is single-threaded, so no locking is needed.
|
|
220
|
-
*/
|
|
221
|
-
private activeLeafId;
|
|
222
|
-
/**
|
|
223
|
-
* @param agent - Agent or any object with a `sql` tagged template method
|
|
224
|
-
* @param sessionId - Optional session ID to isolate multiple sessions in the same DO.
|
|
225
|
-
* Messages are filtered by session_id within shared tables.
|
|
226
|
-
*/
|
|
227
|
-
constructor(agent: SqlProvider, sessionId?: string);
|
|
228
|
-
private ensureTable;
|
|
229
|
-
getMessage(id: string): SessionMessage | null;
|
|
230
|
-
getHistory(leafId?: string | null): SessionMessage[];
|
|
231
|
-
getRecentHistory(
|
|
232
|
-
leafId: string | null | undefined,
|
|
233
|
-
maxContentBytes: number,
|
|
234
|
-
minRecentMessages?: number
|
|
235
|
-
): RecentHistoryResult;
|
|
236
|
-
getHistoryRowStats(leafId?: string | null): HistoryRowStat[];
|
|
237
|
-
getLatestLeaf(): SessionMessage | null;
|
|
238
|
-
getBranches(messageId: string): SessionMessage[];
|
|
239
|
-
getPathLength(leafId?: string | null): number;
|
|
240
|
-
appendMessage(message: SessionMessage, parentId?: string | null): void;
|
|
241
|
-
updateMessage(message: SessionMessage): void;
|
|
242
|
-
deleteMessages(messageIds: string[]): void;
|
|
243
|
-
clearMessages(): void;
|
|
244
|
-
addCompaction(
|
|
245
|
-
summary: string,
|
|
246
|
-
fromMessageId: string,
|
|
247
|
-
toMessageId: string
|
|
248
|
-
): StoredCompaction;
|
|
249
|
-
getCompactions(): StoredCompaction[];
|
|
250
|
-
searchMessages(query: string, limit?: number): SearchResult[];
|
|
251
|
-
private latestLeafRow;
|
|
252
|
-
private leafRowById;
|
|
253
|
-
/**
|
|
254
|
-
* The active branch path as (id, role, content size) rows, root → leaf.
|
|
255
|
-
*
|
|
256
|
-
* Recurses over (id, parent_id) only. Carrying `content` through the
|
|
257
|
-
* recursive queue AND the ORDER BY sorter materializes the entire
|
|
258
|
-
* transcript several times over inside SQLite's allocator, which in
|
|
259
|
-
* workerd shares the isolate's memory budget with the JS heap — large
|
|
260
|
-
* media-heavy sessions then fail with SQLITE_NOMEM on wake (#1710).
|
|
261
|
-
* Content is fetched separately in bounded chunks (`messagesByPathStats`).
|
|
262
|
-
*/
|
|
263
|
-
private pathRowStats;
|
|
264
|
-
/**
|
|
265
|
-
* Fetch and parse message content for an ordered list of path rows.
|
|
266
|
-
*
|
|
267
|
-
* Content is read in chunks bounded by both row count and cumulative
|
|
268
|
-
* stored bytes (no ORDER BY — SQLite streams rows without materializing
|
|
269
|
-
* the result set) and reassembled in path order. Rows that fail to parse
|
|
270
|
-
* are skipped, matching previous behavior.
|
|
271
|
-
*/
|
|
272
|
-
private messagesByPathStats;
|
|
273
|
-
private indexFTS;
|
|
274
|
-
private deleteFTS;
|
|
275
|
-
private applyCompactions;
|
|
276
|
-
private parse;
|
|
277
|
-
private parseRows;
|
|
278
|
-
}
|
|
279
|
-
//#endregion
|
|
280
|
-
//#region src/experimental/memory/session/search.d.ts
|
|
281
|
-
/**
|
|
282
|
-
* Storage interface for searchable context.
|
|
283
|
-
*
|
|
284
|
-
* - `get()` returns a summary of indexed content (rendered into system prompt)
|
|
285
|
-
* - `search(query)` full-text search (via search_context tool)
|
|
286
|
-
* - `set(key, content)` indexes content under a key (via set_context tool)
|
|
287
|
-
*/
|
|
288
|
-
interface SearchProvider extends ContextProvider {
|
|
289
|
-
search(query: string): Promise<string | null>;
|
|
290
|
-
set?(key: string, content: string): Promise<void>;
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Check if a provider is a SearchProvider (has a `search` method).
|
|
294
|
-
*/
|
|
295
|
-
declare function isSearchProvider(
|
|
296
|
-
provider: unknown
|
|
297
|
-
): provider is SearchProvider;
|
|
298
|
-
/**
|
|
299
|
-
* SearchProvider backed by Durable Object SQLite with FTS5.
|
|
300
|
-
*
|
|
301
|
-
* - `get()` returns a count of indexed entries
|
|
302
|
-
* - `search(query)` full-text search using FTS5
|
|
303
|
-
* - `set(key, content)` indexes or replaces content under a key
|
|
304
|
-
*
|
|
305
|
-
* Each instance uses a namespaced FTS5 table to avoid collisions
|
|
306
|
-
* with the session message search.
|
|
307
|
-
*
|
|
308
|
-
* @example
|
|
309
|
-
* ```ts
|
|
310
|
-
* Session.create(this)
|
|
311
|
-
* .withContext("knowledge", {
|
|
312
|
-
* provider: new AgentSearchProvider(this)
|
|
313
|
-
* })
|
|
314
|
-
* ```
|
|
315
|
-
*/
|
|
316
|
-
declare class AgentSearchProvider implements SearchProvider {
|
|
317
|
-
private agent;
|
|
318
|
-
private label;
|
|
319
|
-
private initialized;
|
|
320
|
-
constructor(agent: SqlProvider);
|
|
321
|
-
init(label: string): void;
|
|
322
|
-
private ensureTable;
|
|
323
|
-
get(): Promise<string | null>;
|
|
324
|
-
search(query: string): Promise<string | null>;
|
|
325
|
-
set(key: string, content: string): Promise<void>;
|
|
326
|
-
private deleteFTS;
|
|
327
|
-
}
|
|
328
|
-
//#endregion
|
|
329
|
-
//#region src/experimental/memory/session/skills.d.ts
|
|
330
|
-
/**
|
|
331
|
-
* Storage interface for skill collections.
|
|
332
|
-
*
|
|
333
|
-
* - `get()` returns metadata listing (rendered into system prompt)
|
|
334
|
-
* - `load(key)` fetches full content (via load_context tool)
|
|
335
|
-
* - `set(key, content, description?)` writes an entry (via set_context tool)
|
|
336
|
-
*/
|
|
337
|
-
interface SkillProvider extends ContextProvider {
|
|
338
|
-
load(key: string): Promise<string | null>;
|
|
339
|
-
set?(key: string, content: string, description?: string): Promise<void>;
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Check if a provider is a SkillProvider (has a `load` method).
|
|
343
|
-
*/
|
|
344
|
-
declare function isSkillProvider(provider: unknown): provider is SkillProvider;
|
|
345
|
-
/**
|
|
346
|
-
* SkillProvider backed by an R2 bucket.
|
|
347
|
-
*
|
|
348
|
-
* - `get()` returns a metadata listing of all skills (key + description)
|
|
349
|
-
* - `load(key)` fetches a skill's full content
|
|
350
|
-
* - `set(key, content, description?)` writes a skill
|
|
351
|
-
*
|
|
352
|
-
* Descriptions are pulled from R2 custom metadata (`description` key).
|
|
353
|
-
* If a prefix is provided, it is prepended on storage operations and
|
|
354
|
-
* stripped from keys in metadata. `keys`, when provided, is matched against
|
|
355
|
-
* these prefix-relative keys.
|
|
356
|
-
*
|
|
357
|
-
* @example
|
|
358
|
-
* ```ts
|
|
359
|
-
* const skills = new R2SkillProvider(env.SKILLS_BUCKET, {
|
|
360
|
-
* prefix: "skills/",
|
|
361
|
-
* keys: ["code-review", "debugging"]
|
|
362
|
-
* });
|
|
363
|
-
* ```
|
|
364
|
-
*/
|
|
365
|
-
declare class R2SkillProvider implements SkillProvider {
|
|
366
|
-
private bucket;
|
|
367
|
-
private prefix;
|
|
368
|
-
private keys;
|
|
369
|
-
constructor(
|
|
370
|
-
bucket: R2Bucket,
|
|
371
|
-
options?: {
|
|
372
|
-
prefix?: string;
|
|
373
|
-
keys?: string[];
|
|
374
|
-
}
|
|
375
|
-
);
|
|
376
|
-
get(): Promise<string | null>;
|
|
377
|
-
load(key: string): Promise<string | null>;
|
|
378
|
-
set(key: string, content: string, description?: string): Promise<void>;
|
|
379
|
-
private allowsKey;
|
|
380
|
-
}
|
|
381
|
-
//#endregion
|
|
382
|
-
//#region src/experimental/memory/session/context.d.ts
|
|
383
|
-
/**
|
|
384
|
-
* Base storage interface for a context block.
|
|
385
|
-
* A provider with only `get()` is readonly.
|
|
386
|
-
*/
|
|
387
|
-
interface ContextProvider {
|
|
388
|
-
get(): Promise<string | null>;
|
|
389
|
-
/** Called by the context system to provide the block label before first use. */
|
|
390
|
-
init?(label: string): void;
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Writable context provider — extends ContextProvider with `set()`.
|
|
394
|
-
* Blocks backed by this provider are writable via the `set_context` tool.
|
|
395
|
-
*/
|
|
396
|
-
interface WritableContextProvider extends ContextProvider {
|
|
397
|
-
set(content: string): Promise<void>;
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Check if a provider is writable (has a `set` method).
|
|
401
|
-
*/
|
|
402
|
-
declare function isWritableProvider(
|
|
403
|
-
provider: unknown
|
|
404
|
-
): provider is WritableContextProvider;
|
|
405
|
-
/**
|
|
406
|
-
* Configuration for a context block.
|
|
407
|
-
*/
|
|
408
|
-
interface ContextConfig {
|
|
409
|
-
/** Block label — used as key and in tool descriptions */
|
|
410
|
-
label: string;
|
|
411
|
-
/** Human-readable description (shown to AI in tool) */
|
|
412
|
-
description?: string;
|
|
413
|
-
/** Maximum tokens allowed. Enforced on set. */
|
|
414
|
-
maxTokens?: number;
|
|
415
|
-
/** Storage provider. Determines block behavior:
|
|
416
|
-
* - ContextProvider (get only) → readonly
|
|
417
|
-
* - WritableContextProvider (get+set) → writable via set_context
|
|
418
|
-
* - SkillProvider (get+load+set?) → on-demand via load_context
|
|
419
|
-
* - SearchProvider (get+search+set?) → searchable via search_context
|
|
420
|
-
* If omitted, auto-wired to writable SQLite when using builder. */
|
|
421
|
-
provider?:
|
|
422
|
-
| ContextProvider
|
|
423
|
-
| WritableContextProvider
|
|
424
|
-
| SkillProvider
|
|
425
|
-
| SearchProvider;
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* A loaded context block with computed token count.
|
|
429
|
-
*/
|
|
430
|
-
interface ContextBlock {
|
|
431
|
-
label: string;
|
|
432
|
-
description?: string;
|
|
433
|
-
content: string;
|
|
434
|
-
tokens: number;
|
|
435
|
-
maxTokens?: number;
|
|
436
|
-
/** True if provider is writable (has set) */
|
|
437
|
-
writable: boolean;
|
|
438
|
-
/** True if backed by a SkillProvider */
|
|
439
|
-
isSkill: boolean;
|
|
440
|
-
/** True if backed by a SearchProvider */
|
|
441
|
-
isSearchable: boolean;
|
|
442
|
-
}
|
|
443
|
-
//#endregion
|
|
444
|
-
//#region src/experimental/memory/utils/compaction-helpers.d.ts
|
|
445
|
-
type CompactTokenCounter = (
|
|
446
|
-
messages: SessionMessage[]
|
|
447
|
-
) => number | Promise<number>;
|
|
448
|
-
/** Prefix for all compaction messages (overlays and summaries) */
|
|
449
|
-
declare const COMPACTION_PREFIX = "compaction_";
|
|
450
|
-
/** Check if a message is a compaction message */
|
|
451
|
-
declare function isCompactionMessage(msg: SessionMessage): boolean;
|
|
452
|
-
/**
|
|
453
|
-
* Align a boundary index forward to avoid splitting tool call/result groups.
|
|
454
|
-
* If the boundary falls between an assistant message with tool calls and its
|
|
455
|
-
* tool results, move it forward past the results.
|
|
456
|
-
*/
|
|
457
|
-
declare function alignBoundaryForward(
|
|
458
|
-
messages: SessionMessage[],
|
|
459
|
-
idx: number
|
|
460
|
-
): number;
|
|
461
|
-
/**
|
|
462
|
-
* Align a boundary index backward to avoid splitting tool call/result groups.
|
|
463
|
-
* If the boundary falls in the middle of tool results, move it backward to
|
|
464
|
-
* include the assistant message that made the calls.
|
|
465
|
-
*/
|
|
466
|
-
declare function alignBoundaryBackward(
|
|
467
|
-
messages: SessionMessage[],
|
|
468
|
-
idx: number
|
|
469
|
-
): number;
|
|
470
|
-
/**
|
|
471
|
-
* Find the compression end boundary using a token budget for the tail.
|
|
472
|
-
* Walks backward from the end, accumulating tokens until budget is reached.
|
|
473
|
-
* Returns the index where compression should stop (everything from this
|
|
474
|
-
* index onward is protected).
|
|
475
|
-
*
|
|
476
|
-
* @param messages All messages
|
|
477
|
-
* @param headEnd Index where the protected head ends (compression starts here)
|
|
478
|
-
* @param tailTokenBudget Maximum tokens to keep in the tail
|
|
479
|
-
* @param minTailMessages Minimum messages to protect in the tail (fallback)
|
|
480
|
-
*/
|
|
481
|
-
declare function findTailCutByTokens(
|
|
482
|
-
messages: SessionMessage[],
|
|
483
|
-
headEnd: number,
|
|
484
|
-
tailTokenBudget?: number,
|
|
485
|
-
minTailMessages?: number
|
|
486
|
-
): number;
|
|
487
|
-
/**
|
|
488
|
-
* Fix orphaned tool call/result pairs after compaction.
|
|
489
|
-
*
|
|
490
|
-
* Two failure modes:
|
|
491
|
-
* 1. Tool result references a call_id whose assistant tool_call was removed
|
|
492
|
-
* → Remove the orphaned result
|
|
493
|
-
* 2. Assistant has tool_calls whose results were dropped
|
|
494
|
-
* → Add stub results so the API doesn't error
|
|
495
|
-
*
|
|
496
|
-
* @param messages Messages after compaction
|
|
497
|
-
* @returns Sanitized messages with no orphaned pairs
|
|
498
|
-
*/
|
|
499
|
-
declare function sanitizeToolPairs(
|
|
500
|
-
messages: SessionMessage[]
|
|
501
|
-
): SessionMessage[];
|
|
502
|
-
/**
|
|
503
|
-
* Compute a summary token budget based on the content being compressed.
|
|
504
|
-
* 20% of the compressed content, clamped to 2K-8K tokens.
|
|
505
|
-
*/
|
|
506
|
-
declare function computeSummaryBudget(messages: SessionMessage[]): number;
|
|
507
|
-
/**
|
|
508
|
-
* Build a prompt for LLM summarization of compressed messages.
|
|
509
|
-
*
|
|
510
|
-
* @param messages Messages to summarize
|
|
511
|
-
* @param previousSummary Previous summary for iterative updates (or null for first compaction)
|
|
512
|
-
* @param budget Target token count for the summary
|
|
513
|
-
*/
|
|
514
|
-
declare function buildSummaryPrompt(
|
|
515
|
-
messages: SessionMessage[],
|
|
516
|
-
previousSummary: string | null,
|
|
517
|
-
budget: number
|
|
518
|
-
): string;
|
|
519
|
-
/**
|
|
520
|
-
* Result of a compaction function — describes the overlay to store.
|
|
521
|
-
*/
|
|
522
|
-
interface CompactResult {
|
|
523
|
-
/** First message ID in the compacted range */
|
|
524
|
-
fromMessageId: string;
|
|
525
|
-
/** Last message ID in the compacted range */
|
|
526
|
-
toMessageId: string;
|
|
527
|
-
/** Summary text to store as the overlay */
|
|
528
|
-
summary: string;
|
|
529
|
-
}
|
|
530
|
-
interface CompactOptions {
|
|
531
|
-
/**
|
|
532
|
-
* Function to call the LLM for summarization.
|
|
533
|
-
* Takes a user prompt string, returns the LLM's text response.
|
|
534
|
-
*/
|
|
535
|
-
summarize: (prompt: string) => Promise<string>;
|
|
536
|
-
/** Number of head messages to protect (default: 2) */
|
|
537
|
-
protectHead?: number;
|
|
538
|
-
/** Token budget for tail protection (default: 20000) */
|
|
539
|
-
tailTokenBudget?: number;
|
|
540
|
-
/** Minimum tail messages to protect (default: 2) */
|
|
541
|
-
minTailMessages?: number;
|
|
542
|
-
/**
|
|
543
|
-
* Optional counter for tail-budget decisions. Use this when a tokenizer or
|
|
544
|
-
* model-reported accounting is available; otherwise the Workers-safe
|
|
545
|
-
* heuristic is used.
|
|
546
|
-
*/
|
|
547
|
-
tokenCounter?: CompactTokenCounter;
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* Reference compaction implementation.
|
|
551
|
-
*
|
|
552
|
-
* Implements the full hermes-style compaction algorithm:
|
|
553
|
-
* 1. Protect head messages (first N)
|
|
554
|
-
* 2. Protect tail by token budget (walk backward)
|
|
555
|
-
* 3. Align boundaries to tool call groups
|
|
556
|
-
* 4. Summarize middle section with LLM (structured format)
|
|
557
|
-
* 5. Sanitize orphaned tool pairs
|
|
558
|
-
* 6. Iterative summary updates on subsequent compactions
|
|
559
|
-
*
|
|
560
|
-
* @example
|
|
561
|
-
* ```typescript
|
|
562
|
-
* import { createCompactFunction } from "agents/experimental/memory/utils";
|
|
563
|
-
*
|
|
564
|
-
* const session = new Session(provider, {
|
|
565
|
-
* compaction: {
|
|
566
|
-
* tokenThreshold: 100000,
|
|
567
|
-
* fn: createCompactFunction({
|
|
568
|
-
* summarize: (prompt) => generateText({ model, prompt }).then(r => r.text)
|
|
569
|
-
* })
|
|
570
|
-
* }
|
|
571
|
-
* });
|
|
572
|
-
* ```
|
|
573
|
-
*/
|
|
574
|
-
declare function createCompactFunction(
|
|
575
|
-
opts: CompactOptions
|
|
576
|
-
): (
|
|
577
|
-
messages: SessionMessage[],
|
|
578
|
-
context?: CompactContext
|
|
579
|
-
) => Promise<CompactResult | null>;
|
|
580
|
-
//#endregion
|
|
581
|
-
export {
|
|
582
|
-
StoredCompaction as A,
|
|
583
|
-
isSearchProvider as C,
|
|
584
|
-
RecentHistoryResult as D,
|
|
585
|
-
HistoryRowStat as E,
|
|
586
|
-
SessionMessagePart as F,
|
|
587
|
-
SessionOptions as I,
|
|
588
|
-
SessionTokenCounter as L,
|
|
589
|
-
CompactContext as M,
|
|
590
|
-
CompactionErrorHandler as N,
|
|
591
|
-
SearchResult as O,
|
|
592
|
-
SessionMessage as P,
|
|
593
|
-
SessionTokenCounterInput as R,
|
|
594
|
-
SearchProvider as S,
|
|
595
|
-
SqlProvider as T,
|
|
596
|
-
isWritableProvider as _,
|
|
597
|
-
alignBoundaryBackward as a,
|
|
598
|
-
isSkillProvider as b,
|
|
599
|
-
computeSummaryBudget as c,
|
|
600
|
-
isCompactionMessage as d,
|
|
601
|
-
sanitizeToolPairs as f,
|
|
602
|
-
WritableContextProvider as g,
|
|
603
|
-
ContextProvider as h,
|
|
604
|
-
CompactTokenCounter as i,
|
|
605
|
-
CompactAfterOptions as j,
|
|
606
|
-
SessionProvider as k,
|
|
607
|
-
createCompactFunction as l,
|
|
608
|
-
ContextConfig as m,
|
|
609
|
-
CompactOptions as n,
|
|
610
|
-
alignBoundaryForward as o,
|
|
611
|
-
ContextBlock as p,
|
|
612
|
-
CompactResult as r,
|
|
613
|
-
buildSummaryPrompt as s,
|
|
614
|
-
COMPACTION_PREFIX as t,
|
|
615
|
-
findTailCutByTokens as u,
|
|
616
|
-
R2SkillProvider as v,
|
|
617
|
-
AgentSessionProvider as w,
|
|
618
|
-
AgentSearchProvider as x,
|
|
619
|
-
SkillProvider as y
|
|
620
|
-
};
|
|
621
|
-
//# sourceMappingURL=compaction-helpers-wUz6M3us.d.ts.map
|