repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
public Clawd ADK gateway launch mirror
stars
latest
clone command
git clone gitlawb://did:key:z6Mkq5mY...iFZ5/my-project-publ...git clone gitlawb://did:key:z6Mkq5mY.../my-project-publ...2fa351d6docs: add automaton and perps launch sources16d ago| #1 | /** |
| #2 | * Context Window Management |
| #3 | * |
| #4 | * Manages the conversation history for the agent loop. |
| #5 | * Handles summarization to keep within token limits. |
| #6 | */ |
| #7 | |
| #8 | import type { |
| #9 | ChatMessage, |
| #10 | AgentTurn, |
| #11 | AutomatonDatabase, |
| #12 | InferenceClient, |
| #13 | } from "../types.js"; |
| #14 | |
| #15 | const MAX_CONTEXT_TURNS = 20; |
| #16 | const SUMMARY_THRESHOLD = 15; |
| #17 | |
| #18 | /** |
| #19 | * Build the message array for the next inference call. |
| #20 | * Includes system prompt + recent conversation history. |
| #21 | */ |
| #22 | export function buildContextMessages( |
| #23 | systemPrompt: string, |
| #24 | recentTurns: AgentTurn[], |
| #25 | pendingInput?: { content: string; source: string }, |
| #26 | ): ChatMessage[] { |
| #27 | const messages: ChatMessage[] = [ |
| #28 | { role: "system", content: systemPrompt }, |
| #29 | ]; |
| #30 | |
| #31 | // Add recent turns as conversation history |
| #32 | for (const turn of recentTurns) { |
| #33 | // The turn's input (if any) as a user message |
| #34 | if (turn.input) { |
| #35 | messages.push({ |
| #36 | role: "user", |
| #37 | content: `[${turn.inputSource || "system"}] ${turn.input}`, |
| #38 | }); |
| #39 | } |
| #40 | |
| #41 | // The agent's thinking as assistant message |
| #42 | if (turn.thinking) { |
| #43 | const msg: ChatMessage = { |
| #44 | role: "assistant", |
| #45 | content: turn.thinking, |
| #46 | }; |
| #47 | |
| #48 | // If there were tool calls, include them |
| #49 | if (turn.toolCalls.length > 0) { |
| #50 | msg.tool_calls = turn.toolCalls.map((tc) => ({ |
| #51 | id: tc.id, |
| #52 | type: "function" as const, |
| #53 | function: { |
| #54 | name: tc.name, |
| #55 | arguments: JSON.stringify(tc.arguments), |
| #56 | }, |
| #57 | })); |
| #58 | } |
| #59 | messages.push(msg); |
| #60 | |
| #61 | // Add tool results |
| #62 | for (const tc of turn.toolCalls) { |
| #63 | messages.push({ |
| #64 | role: "tool", |
| #65 | content: tc.error |
| #66 | ? `Error: ${tc.error}` |
| #67 | : tc.result, |
| #68 | tool_call_id: tc.id, |
| #69 | }); |
| #70 | } |
| #71 | } |
| #72 | } |
| #73 | |
| #74 | // Add pending input if any |
| #75 | if (pendingInput) { |
| #76 | messages.push({ |
| #77 | role: "user", |
| #78 | content: `[${pendingInput.source}] ${pendingInput.content}`, |
| #79 | }); |
| #80 | } |
| #81 | |
| #82 | return messages; |
| #83 | } |
| #84 | |
| #85 | /** |
| #86 | * Trim context to fit within limits. |
| #87 | * Keeps the system prompt and most recent turns. |
| #88 | */ |
| #89 | export function trimContext( |
| #90 | turns: AgentTurn[], |
| #91 | maxTurns: number = MAX_CONTEXT_TURNS, |
| #92 | ): AgentTurn[] { |
| #93 | if (turns.length <= maxTurns) { |
| #94 | return turns; |
| #95 | } |
| #96 | |
| #97 | // Keep the most recent turns |
| #98 | return turns.slice(-maxTurns); |
| #99 | } |
| #100 | |
| #101 | /** |
| #102 | * Summarize old turns into a compact context entry. |
| #103 | * Used when context grows too large. |
| #104 | */ |
| #105 | export async function summarizeTurns( |
| #106 | turns: AgentTurn[], |
| #107 | inference: InferenceClient, |
| #108 | ): Promise<string> { |
| #109 | if (turns.length === 0) return "No previous activity."; |
| #110 | |
| #111 | const turnSummaries = turns.map((t) => { |
| #112 | const tools = t.toolCalls |
| #113 | .map((tc) => `${tc.name}(${tc.error ? "FAILED" : "ok"})`) |
| #114 | .join(", "); |
| #115 | return `[${t.timestamp}] ${t.inputSource || "self"}: ${t.thinking.slice(0, 100)}${tools ? ` | tools: ${tools}` : ""}`; |
| #116 | }); |
| #117 | |
| #118 | // If few enough turns, just return the summaries directly |
| #119 | if (turns.length <= 5) { |
| #120 | return `Previous activity summary:\n${turnSummaries.join("\n")}`; |
| #121 | } |
| #122 | |
| #123 | // For many turns, use inference to create a summary |
| #124 | try { |
| #125 | const response = await inference.chat([ |
| #126 | { |
| #127 | role: "system", |
| #128 | content: |
| #129 | "Summarize the following agent activity log into a concise paragraph. Focus on: what was accomplished, what failed, current goals, and important context for the next turn.", |
| #130 | }, |
| #131 | { |
| #132 | role: "user", |
| #133 | content: turnSummaries.join("\n"), |
| #134 | }, |
| #135 | ], { |
| #136 | maxTokens: 500, |
| #137 | temperature: 0, |
| #138 | }); |
| #139 | |
| #140 | return `Previous activity summary:\n${response.message.content}`; |
| #141 | } catch { |
| #142 | // Fallback: just use the raw summaries |
| #143 | return `Previous activity summary:\n${turnSummaries.slice(-5).join("\n")}`; |
| #144 | } |
| #145 | } |
| #146 |