@agentscope-ai/agentscope 0.0.5 → 0.0.6
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/agent/index.d.mts +4 -4
- package/dist/agent/index.d.ts +4 -4
- package/dist/agent/index.js +34 -7
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +34 -7
- package/dist/agent/index.mjs.map +1 -1
- package/dist/{base-qmU135_k.d.ts → base-13VLaOvY.d.ts} +2 -2
- package/dist/{base-BI5s2ksj.d.mts → base-Bc3GkNS7.d.mts} +1 -1
- package/dist/{base-DHtZCg94.d.ts → base-CJkm56kB.d.ts} +1 -1
- package/dist/{base-CFDeoJRe.d.ts → base-Dfizi3RB.d.ts} +1 -1
- package/dist/{base-BDyDUIhj.d.mts → base-L72wZVx8.d.mts} +2 -2
- package/dist/{base-BB9eTlit.d.mts → base-Ps8E0j1_.d.mts} +1 -1
- package/dist/event/index.d.mts +2 -2
- package/dist/event/index.d.ts +2 -2
- package/dist/event/index.js.map +1 -1
- package/dist/event/index.mjs.map +1 -1
- package/dist/formatter/index.d.mts +2 -2
- package/dist/formatter/index.d.ts +2 -2
- package/dist/formatter/index.js +16 -0
- package/dist/formatter/index.js.map +1 -1
- package/dist/formatter/index.mjs +16 -0
- package/dist/formatter/index.mjs.map +1 -1
- package/dist/message/index.d.mts +1 -1
- package/dist/message/index.d.ts +1 -1
- package/dist/message/index.js +50 -14
- package/dist/message/index.js.map +1 -1
- package/dist/message/index.mjs +50 -14
- package/dist/message/index.mjs.map +1 -1
- package/dist/{message-D-LObC06.d.mts → message-COpNEf0G.d.mts} +10 -7
- package/dist/{message-DU0_qm3u.d.ts → message-DbCMy5tM.d.ts} +10 -7
- package/dist/model/index.d.mts +4 -4
- package/dist/model/index.d.ts +4 -4
- package/dist/model/index.js +16 -0
- package/dist/model/index.js.map +1 -1
- package/dist/model/index.mjs +16 -0
- package/dist/model/index.mjs.map +1 -1
- package/dist/storage/index.d.mts +2 -2
- package/dist/storage/index.d.ts +2 -2
- package/package.json +1 -1
- package/src/agent/agent.ts +20 -7
- package/src/event/index.ts +2 -2
- package/src/message/append-event.test.ts +17 -11
- package/src/message/message.test.ts +2 -2
- package/src/message/message.ts +77 -14
package/dist/agent/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/agent/index.ts","../../src/agent/agent.ts","../../src/message/message.ts","../../src/tool/response.ts","../../src/tool/toolkit.ts","../../src/_utils/common.ts","../../src/tool/bash.ts","../../src/tool/read.ts","../../src/tool/write.ts","../../src/tool/edit.ts","../../src/tool/glob.ts","../../src/tool/grep.ts","../../src/tool/task.ts"],"sourcesContent":["export { Agent, AgentOptions, CompressionConfig } from './agent';\nexport { ReplyOptions, ActingOptions, ReasoningOptions } from './interfaces';\n","import { z } from 'zod';\n\nimport {\n ContentBlock,\n createMsg,\n getContentBlocks,\n Msg,\n ToolCallBlock,\n ToolResultBlock,\n} from '../message';\nimport { ChatModelBase, ChatResponse, ChatUsage } from '../model';\nimport { Toolkit, ToolResponse } from '../tool';\nimport { ActingOptions, ObserveOptions, ReasoningOptions, ReplyOptions } from './interfaces';\nimport {\n AgentEvent,\n EventType,\n ModelCallEndEvent,\n ModelCallStartEvent,\n ReplyEndEvent,\n ReplyStartEvent,\n TextBlockDeltaEvent,\n TextBlockEndEvent,\n TextBlockStartEvent,\n ThinkingBlockDeltaEvent,\n ThinkingBlockEndEvent,\n ThinkingBlockStartEvent,\n ToolCallDeltaEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n ToolResultDataDeltaEvent,\n ToolResultEndEvent,\n ToolResultStartEvent,\n ToolResultTextDeltaEvent,\n} from '../event';\nimport { StorageBase } from '../storage';\n\nconst DEFAULT_COMPRESSION_PROMPT =\n '<system-hint>You have been working on the task described above but have not yet completed it. ' +\n 'Now write a continuation summary that will allow you to resume work efficiently in a future context window ' +\n 'where the conversation history will be replaced with this summary. ' +\n 'Your summary should be structured, concise, and actionable.</system-hint>';\n\nconst DEFAULT_SUMMARY_SCHEMA = z.object({\n task_overview: z\n .string()\n .max(300)\n .describe(\n \"The user\\'s core request and success criteria. Any clarifications or constraints they specified\"\n ),\n current_state: z\n .string()\n .max(300)\n .describe(\n 'What has been completed so far. File created, modified, or analyzed (with paths if relevant). Key outputs or artifacts produced.'\n ),\n important_discoveries: z\n .string()\n .max(300)\n .describe(\n \"Technical constraints or requirements uncovered. Decisions made and their rationale. Errors encountered and how they were resolved. What approaches were tried that didn\\'t work (and why)\"\n ),\n next_steps: z\n .string()\n .max(200)\n .describe(\n 'Specific actions needed to complete the task. Any blockers or open questions to resolve. Priority order if multiple steps remain'\n ),\n context_to_preserve: z\n .string()\n .max(300)\n .describe(\n \"User preferences or style requirements. Domain-specific details that aren\\'t obvious. Any promises made to the user\"\n ),\n});\n\nexport interface CompressionConfig {\n /**\n * Whether to enable memory compression.\n */\n enabled: boolean;\n /**\n * The token count threshold to trigger memory compression.\n */\n triggerThreshold: number;\n /**\n * The function to count the tokens of the messages in memory. If not provided, a heuristic token counting method will be used by default.\n */\n tokenCountFunc?: (msgs: Msg[]) => number;\n /**\n * The chat model used for compression. If not provided, the same model as the agent will be used by default.\n */\n compressionModel?: ChatModelBase;\n /**\n * The prompt template for memory compression. It should be designed to instruct the model to compress the input messages into a concise summary while preserving important information. If not provided, a default prompt will be used.\n */\n compressionPrompt?: string;\n /**\n * The JSON schema for the compressed summary. The model will be guided to compress the memory into a structured summary following this schema. If not provided, a default schema with a single text field will be used.\n */\n summarySchema?: z.ZodObject;\n /**\n * The number of recent messages to keep in the context without compression.\n */\n keepRecent?: number;\n}\n\nexport interface AgentOptions {\n name: string;\n sysPrompt: string;\n model: ChatModelBase;\n maxIters?: number;\n toolkit?: Toolkit;\n storage?: StorageBase;\n compressionConfig?: CompressionConfig;\n}\n\n/**\n * The unified agent class in AgentScope library.\n */\nexport class Agent {\n // Agent configuration\n name: string;\n model: ChatModelBase;\n maxIters: number;\n toolkit: Toolkit;\n storage?: StorageBase;\n context: Msg[];\n private _loaded: boolean;\n private _sysPrompt: string;\n compressionConfig?: CompressionConfig;\n\n // Agent state\n replyId: string;\n curIter: number;\n confirmedToolCallIds: string[];\n curSummary: string;\n\n /**\n * Initialize an agent instance with the given parameters.\n *\n * @param options - The agent configuration options.\n * @param options.name - The name of the agent.\n * @param options.sysPrompt - The system prompt for the agent.\n * @param options.model - The chat model to use.\n * @param options.maxIters - Maximum iterations (default: 5).\n * @param options.memory - Memory storage (default: InMemoryMemory).\n * @param options.toolkit - Toolkit for tools (default: Toolkit).\n */\n constructor(options: AgentOptions) {\n // Check maxIters mast be greater than 0\n if (options.maxIters !== undefined && options.maxIters <= 0) {\n throw new Error('maxIters must be greater than 0');\n }\n\n this.name = options.name;\n this._sysPrompt = options.sysPrompt;\n this.model = options.model;\n this.maxIters = options.maxIters ?? 20;\n this.context = [];\n this.toolkit = options.toolkit ?? new Toolkit();\n this.storage = options.storage;\n this.compressionConfig = options.compressionConfig;\n\n // Record if the agent state has been loaded from storage to avoid repeat loading\n this._loaded = false;\n\n // The states that tracks the current reply session\n this.replyId = '';\n this.curIter = 0;\n this.confirmedToolCallIds = [];\n this.curSummary = '';\n }\n\n /**\n * Load the state from the storage if storage is provided and not loaded yet.\n */\n async loadState() {\n if (this._loaded || !this.storage) return;\n const { context, metadata } = await this.storage.loadAgentState({ agentId: this.name });\n console.log(`Load state for agent \"${this.name}\" from storage:`, { context, metadata });\n this.context = context;\n this.replyId = (metadata.replyId as string) || '';\n this.curIter = (metadata.curIter as number) || 0;\n this.curSummary = (metadata.curSummary as string) || '';\n this._loaded = true;\n }\n\n /**\n * Save the state of the current reply session to storage if storage is provided.\n */\n async saveState() {\n if (!this.storage) return;\n await this.storage.saveAgentState({\n agentId: this.name,\n context: this.context,\n metadata: {\n replyId: this.replyId,\n curIter: this.curIter,\n curSummary: this.curSummary,\n },\n });\n }\n\n /**\n * Get the system prompt of the agent.\n *\n * @returns The system prompt string.\n */\n public get sysPrompt() {\n const skillsPrompt = this.toolkit.getSkillsPrompt();\n if (skillsPrompt.length > 0) {\n return this._sysPrompt + '\\n\\n' + skillsPrompt;\n }\n return this._sysPrompt;\n }\n\n /**\n * Reply to the given message and stream agent events as they are generated.\n *\n * @param options - The reply options containing the incoming message.\n * @returns An async generator that yields agent events and resolves to the final reply message.\n */\n public async *replyStream(options: ReplyOptions): AsyncGenerator<AgentEvent, Msg> {\n // Load the agent state from storage if not loaded yet\n await this.loadState();\n try {\n // Yield the reply stream\n return yield* this._reply(options);\n } finally {\n await this.saveState();\n }\n }\n\n /**\n * Reply to the given message, consuming all streamed events internally.\n *\n * @param options - The reply options containing the incoming message.\n * @param options.msgs - The incoming message(s) to reply to.\n * @returns A promise that resolves to the final reply message.\n */\n public async reply(options: ReplyOptions): Promise<Msg> {\n // Load the agent state from storage if not loaded yet\n await this.loadState();\n try {\n const res = this._reply(options);\n while (true) {\n const { value, done } = await res.next();\n if (done) {\n return value as Msg;\n }\n }\n } finally {\n await this.saveState();\n }\n }\n\n /**\n * Save the given content blocks into the context as a new block in the last assistant message,\n * or create a new assistant message if the last message is not from the assistant or has a different name.\n * @param blocks\n * @param usage\n */\n protected _saveToContext(blocks: ContentBlock[], usage?: ChatUsage): void {\n const lastMsg = this.context.at(-1);\n if (this.context.length === 0) {\n this.context.push(\n createMsg({ name: this.name, content: blocks, role: 'assistant', usage })\n );\n } else if (lastMsg && lastMsg.role === 'assistant' && lastMsg.name === this.name) {\n lastMsg.content.push(...blocks);\n if (usage) {\n if (!lastMsg.usage) {\n lastMsg.usage = {\n inputTokens: 0,\n outputTokens: 0,\n };\n }\n lastMsg.usage.inputTokens = lastMsg.usage.inputTokens + usage.inputTokens;\n lastMsg.usage.outputTokens = lastMsg.usage.outputTokens + usage.outputTokens;\n }\n } else {\n this.context.push(\n createMsg({ name: this.name, content: blocks, role: 'assistant', usage })\n );\n }\n }\n\n /**\n * Get the pending tool calls that have no results yet in the context.\n * @returns An array of pending `ToolCallBlock`s that are waiting for execution results.\n */\n protected _getPendingToolCalls(): ToolCallBlock[] {\n if (this.context.length === 0) return [];\n\n const lastMsg = this.context.at(-1);\n if (!lastMsg) return [];\n if (lastMsg.role === 'assistant') {\n const toolCalls = getContentBlocks(lastMsg, 'tool_call');\n const toolResults = getContentBlocks(lastMsg, 'tool_result');\n return toolCalls.filter(toolCall => !toolResults.some(tr => tr.id === toolCall.id));\n }\n return [];\n }\n\n /**\n * Get the awaiting tool calls that require user confirmation or external execution.\n * @returns An array of `ToolCallBlock`s that are waiting for user confirmation or external execution.\n */\n protected _getAwaitingToolCalls(): {\n awaitingType?: EventType.REQUIRE_USER_CONFIRM | EventType.REQUIRE_EXTERNAL_EXECUTION;\n expectedEventType?: EventType.USER_CONFIRM_RESULT | EventType.EXTERNAL_EXECUTION_RESULT;\n awaitingToolCalls: ToolCallBlock[];\n preToolCalls: ToolCallBlock[];\n } {\n // If there is awaiting tool calls within the last assistant message in the context\n const pendingToolCalls = this._getPendingToolCalls();\n\n // The tool calls that should be executed before yield the (maybe have) user-confirm or external-execution event\n const preToolCalls: ToolCallBlock[] = [];\n for (const [index, toolCall] of pendingToolCalls.entries()) {\n if (\n this.toolkit.requireUserConfirm(toolCall.name) &&\n !this.confirmedToolCallIds.includes(toolCall.id)\n ) {\n toolCall.state = 'asking';\n // Find the continuous tool calls that require user confirmation\n let i = index + 1;\n for (; i < pendingToolCalls.length; i++) {\n const nextToolCall = pendingToolCalls[i];\n if (\n !this.toolkit.requireUserConfirm(nextToolCall.name) ||\n this.confirmedToolCallIds.includes(nextToolCall.id)\n )\n break;\n nextToolCall.state = 'asking';\n }\n return {\n awaitingType: EventType.REQUIRE_USER_CONFIRM,\n expectedEventType: EventType.USER_CONFIRM_RESULT,\n awaitingToolCalls: pendingToolCalls.slice(index, i),\n preToolCalls,\n };\n }\n\n if (this.toolkit.requireExternalExecution(toolCall.name)) {\n // Find the continuous tool calls that require external execution\n let i = index + 1;\n for (; i < pendingToolCalls.length; i++) {\n const nextToolCall = pendingToolCalls[i];\n if (!this.toolkit.requireExternalExecution(nextToolCall.name)) break;\n }\n return {\n awaitingType: EventType.REQUIRE_EXTERNAL_EXECUTION,\n expectedEventType: EventType.EXTERNAL_EXECUTION_RESULT,\n awaitingToolCalls: pendingToolCalls.slice(index, i),\n preToolCalls,\n };\n }\n\n preToolCalls.push(toolCall);\n }\n return { awaitingToolCalls: [], preToolCalls };\n }\n\n /**\n * Core reply logic without middlewares. Observes the incoming message, runs\n * reasoning/acting iterations up to `maxIters`, and returns the final response.\n *\n * @param options - The reply options containing the incoming message.\n * @returns An async generator that yields agent events and resolves to the final reply message.\n */\n protected async *_reply(options?: ReplyOptions): AsyncGenerator<AgentEvent, Msg> {\n const { expectedEventType } = this._getAwaitingToolCalls();\n if (expectedEventType) {\n // Checking\n if (!options || !options.event || options.event.type !== expectedEventType) {\n throw new Error(\n `Agent is awaiting for '${expectedEventType}' confirmation, but received event of type '${options?.event?.type ?? 'none'}'.`\n );\n }\n\n // handle the external execution result event\n const event = options.event;\n if (event.type === EventType.EXTERNAL_EXECUTION_RESULT) {\n // Record the tool results into context and go on acting\n this._saveToContext(event.execution_results);\n } else if (event.type === EventType.USER_CONFIRM_RESULT) {\n for (const result of event.confirm_results) {\n if (result.confirmed) {\n this.confirmedToolCallIds.push(result.tool_call.id);\n } else {\n // If user rejected, add a rejection result and handle the pending tool calls\n const rejectionRes = `<system-info>**Note** the user rejected the execution of tool \"${result.tool_call.name}\"!</system-info>`;\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_START,\n reply_id: this.replyId,\n tool_call_id: result.tool_call.id,\n } as ToolResultStartEvent;\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_TEXT_DELTA,\n reply_id: this.replyId,\n tool_call_id: result.tool_call.id,\n delta: rejectionRes,\n } as ToolResultTextDeltaEvent;\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_END,\n reply_id: this.replyId,\n tool_call_id: result.tool_call.id,\n state: 'interrupted',\n } as ToolResultEndEvent;\n this._saveToContext([\n {\n type: 'tool_result',\n id: result.tool_call.id,\n name: result.tool_call.name,\n output: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `<system-info>**Note** the user rejected the execution of tool \"${result.tool_call.name}\"!</system-info>`,\n },\n ],\n state: 'interrupted',\n },\n ]);\n }\n }\n // Update tool call states based on confirm results\n const confirmedIds = new Set(\n event.confirm_results.filter(r => r.confirmed).map(r => r.tool_call.id)\n );\n const deniedIds = new Set(\n event.confirm_results.filter(r => !r.confirmed).map(r => r.tool_call.id)\n );\n this.context.at(-1)?.content.forEach(content => {\n if (content.type === 'tool_call') {\n if (confirmedIds.has(content.id)) {\n content.state = 'allowed';\n } else if (deniedIds.has(content.id)) {\n content.state = 'finished';\n }\n }\n });\n }\n } else {\n // The normal reply flow starts without any external event\n this.curIter = 0;\n this.replyId = crypto.randomUUID();\n this.confirmedToolCallIds = [];\n\n // Yield the run started event\n yield {\n id: crypto.randomUUID(),\n type: EventType.REPLY_START,\n created_at: new Date().toISOString(),\n session_id: '',\n reply_id: this.replyId,\n name: this.name,\n role: 'assistant',\n } as ReplyStartEvent;\n }\n\n // Store the incoming message into memory\n if (Array.isArray(options?.msgs)) {\n // await this.memory.add(options.msg);\n this.context.push(...options.msgs);\n } else if (options?.msgs) {\n this.context.push(options.msgs);\n }\n\n while (this.curIter < this.maxIters) {\n const pendingToolCalls = this._getPendingToolCalls();\n if (pendingToolCalls.length === 0) {\n await this.compressMemoryIfNeeded();\n const reasoningResponse = yield* this._reasoning({ toolChoice: 'auto' });\n this._saveToContext(reasoningResponse.content, reasoningResponse.usage);\n }\n\n // Extract the awaiting tool calls and those should be executed before yielding human-in-the-loop events\n const { awaitingType, awaitingToolCalls, preToolCalls } = this._getAwaitingToolCalls();\n // Execute the pre-tool calls before yielding the user-confirm or external-execution event if there is any\n for (const toolCall of preToolCalls) {\n const actingContent = yield* this._acting({ toolCall });\n this._saveToContext([actingContent]);\n // Consume the confirmation after execution\n this.confirmedToolCallIds = this.confirmedToolCallIds.filter(\n id => id !== toolCall.id\n );\n }\n\n // yield the user-confirm or external-execution event if there is any awaiting tool calls\n if (awaitingType) {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: awaitingType,\n reply_id: this.replyId,\n tool_calls: awaitingToolCalls,\n };\n\n return createMsg({\n name: this.name,\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text:\n awaitingType === EventType.REQUIRE_USER_CONFIRM\n ? 'Waiting for user confirmation ...'\n : 'Waiting for external execution ...',\n },\n ],\n role: 'assistant',\n });\n }\n\n // Break the loop if there is no tool call in the reasoning message\n if (preToolCalls.length === 0) break;\n\n this.curIter += 1;\n }\n\n // If exceed max iterations without text output\n if (this.context.at(-1)?.content.at(-1)?.type !== 'text') {\n // Generate a final response\n const summaryResponse = yield* this._reasoning({ toolChoice: 'none' });\n this._saveToContext(summaryResponse.content, summaryResponse.usage);\n }\n\n // Yield the run finished event\n yield {\n id: crypto.randomUUID(),\n type: EventType.REPLY_END,\n created_at: new Date().toISOString(),\n session_id: '',\n reply_id: this.replyId,\n } as ReplyEndEvent;\n\n return createMsg({\n id: this.replyId,\n name: this.name,\n // Must be a string for the final output message\n content: [this.context.at(-1)!.content.at(-1)!],\n role: 'assistant',\n });\n }\n\n /**\n * Core reasoning logic without middlewares. Calls the model with the current\n * memory and system prompt, converts the response to agent events, and saves\n * the resulting message to memory.\n *\n * @param options - The reasoning options, including tool choice strategy.\n * @returns An async generator that yields agent events and resolves to the content blocks of the model response.\n */\n protected async *_reasoning(\n options: ReasoningOptions\n ): AsyncGenerator<AgentEvent, ChatResponse> {\n const tools = this.toolkit.getJSONSchemas();\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.MODEL_CALL_START,\n reply_id: this.replyId,\n model_name: this.model.modelName,\n } as ModelCallStartEvent;\n const res = await this.model.call({\n messages: [\n createMsg({\n name: 'system',\n content: [{ type: 'text', text: this.sysPrompt, id: crypto.randomUUID() }],\n role: 'system',\n }),\n ...(this.curSummary\n ? [\n createMsg({\n name: 'user',\n content: [\n { type: 'text', text: this.curSummary, id: crypto.randomUUID() },\n ],\n role: 'user',\n }),\n ]\n : []),\n ...this.context,\n ],\n tools,\n toolChoice: options.toolChoice,\n });\n\n let blockIds = {\n textBlockId: null,\n thinkingBlockId: null,\n toolCallIds: [],\n } as {\n textBlockId: string | null;\n thinkingBlockId: string | null;\n toolCallIds: string[];\n };\n let completedResponse: ChatResponse;\n if (this.model.stream) {\n // Handle streaming response\n while (true) {\n const { value, done } = await (\n res as AsyncGenerator<ChatResponse, ChatResponse>\n ).next();\n if (done) {\n // The complete response is returned in the `value` when `done` is true\n completedResponse = value as ChatResponse;\n break;\n }\n const chunk = value as ChatResponse;\n yield* this.convertChatResponseToEvent(blockIds, chunk);\n }\n } else {\n // Handle non-streaming response, the res is a ChatResponse instance\n completedResponse = res as ChatResponse;\n yield* this.convertChatResponseToEvent(blockIds, res as ChatResponse);\n }\n\n // Send end events for text message, thinking message and tool calls\n if (blockIds.textBlockId) {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TEXT_BLOCK_END,\n reply_id: this.replyId,\n block_id: blockIds.textBlockId,\n } as TextBlockEndEvent;\n }\n if (blockIds.thinkingBlockId) {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.THINKING_BLOCK_END,\n reply_id: this.replyId,\n block_id: blockIds.thinkingBlockId,\n } as ThinkingBlockEndEvent;\n }\n if (blockIds.toolCallIds.length > 0) {\n for (const tool_call_id of blockIds.toolCallIds) {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_CALL_END,\n reply_id: this.replyId,\n tool_call_id,\n } as ToolCallEndEvent;\n }\n }\n\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.MODEL_CALL_END,\n reply_id: this.replyId,\n input_tokens: completedResponse.usage?.inputTokens || 0,\n output_tokens: completedResponse.usage?.outputTokens || 0,\n } as ModelCallEndEvent;\n\n return completedResponse;\n }\n\n /**\n * Core acting logic without middlewares. Executes the given tool call, streams\n * intermediate tool result events, and saves the final tool response to memory.\n *\n * @param options - The acting options containing the tool call to execute.\n * @returns An async generator that yields tool result events.\n */\n protected async *_acting(options: ActingOptions): AsyncGenerator<AgentEvent, ToolResultBlock> {\n const res = this.toolkit.callToolFunction(options.toolCall);\n\n yield {\n type: EventType.TOOL_RESULT_START,\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n reply_id: this.replyId,\n tool_call_id: options.toolCall.id,\n tool_call_name: options.toolCall.name,\n } as ToolResultStartEvent;\n\n while (true) {\n const { value, done } = await res.next();\n if (done) {\n return {\n type: 'tool_result',\n id: options.toolCall.id,\n name: options.toolCall.name,\n output: value.content,\n state: value.state,\n } as ToolResultBlock;\n }\n yield* this.convertToolResponseToEvent(options.toolCall, value);\n }\n }\n\n /**\n * Receive external observation message(s) and save them into memory.\n *\n * @param options - The observe options containing the message to store.\n * @returns A promise that resolves when the message has been saved to memory.\n */\n protected async _observe(options: ObserveOptions): Promise<void> {\n // Load the agent state from storage if not loaded yet\n await this.loadState();\n\n if (Array.isArray(options.msg)) {\n // await this.memory.add(options.msg);\n this.context.push(...options.msg);\n } else if (options.msg) {\n this.context.push(options.msg);\n }\n }\n\n /**\n * Convert a `ChatResponse` chunk into a sequence of typed agent events.\n * Tracks message IDs across calls via the mutable `responseId` object so that\n * start/content/end events are correctly correlated.\n *\n * @param responseId - Mutable object tracking IDs for the current text, thinking, and tool-call messages.\n * @param responseId.textMessageId - ID of the in-progress text message, or `null` if not yet started.\n * @param responseId.thinkingMessageId - ID of the in-progress thinking message, or `null` if not yet started.\n * @param responseId.textBlockId\n * @param responseId.thinkingBlockId\n * @param responseId.toolCallIds - List of tool-call IDs seen so far in this response.\n * @param chunk - The chat response chunk to convert.\n * @returns An async generator that yields the corresponding agent events.\n */\n protected async *convertChatResponseToEvent(\n responseId: {\n textBlockId: string | null;\n thinkingBlockId: string | null;\n toolCallIds: string[];\n },\n chunk: ChatResponse\n ): AsyncGenerator<AgentEvent> {\n for (const block of chunk.content) {\n switch (block.type) {\n case 'text':\n if (responseId.textBlockId === null) {\n // A new uuid\n responseId.textBlockId = crypto.randomUUID();\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TEXT_BLOCK_START,\n reply_id: this.replyId,\n block_id: responseId.textBlockId,\n } as TextBlockStartEvent;\n }\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TEXT_BLOCK_DELTA,\n reply_id: this.replyId,\n block_id: responseId.textBlockId,\n delta: block.text,\n } as TextBlockDeltaEvent;\n break;\n\n case 'thinking':\n if (responseId.thinkingBlockId === null) {\n responseId.thinkingBlockId = crypto.randomUUID();\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.THINKING_BLOCK_START,\n reply_id: this.replyId,\n block_id: responseId.thinkingBlockId,\n } as ThinkingBlockStartEvent;\n }\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.THINKING_BLOCK_DELTA,\n reply_id: this.replyId,\n block_id: responseId.thinkingBlockId,\n delta: block.thinking,\n } as ThinkingBlockDeltaEvent;\n break;\n\n case 'tool_call':\n if (!responseId.toolCallIds.includes(block.id)) {\n responseId.toolCallIds.push(block.id);\n yield {\n id: crypto.randomUUID(),\n type: EventType.TOOL_CALL_START,\n created_at: new Date().toISOString(),\n reply_id: this.replyId,\n tool_call_id: block.id,\n tool_call_name: block.name,\n } as ToolCallStartEvent;\n }\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_CALL_DELTA,\n delta: block.input,\n reply_id: this.replyId,\n tool_call_id: block.id,\n } as ToolCallDeltaEvent;\n }\n }\n }\n\n /**\n * Convert a `ToolResponse` into a sequence of typed agent events, followed by\n * a final `TOOL_RESULT_END` event.\n *\n * @param toolCall - The original tool-use block that triggered this response.\n * @param toolRes - The tool response containing result content blocks.\n * @returns An async generator that yields tool result events.\n */\n protected async *convertToolResponseToEvent(toolCall: ToolCallBlock, toolRes: ToolResponse) {\n for (const block of toolRes.content) {\n switch (block.type) {\n case 'text':\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_TEXT_DELTA,\n reply_id: this.replyId,\n tool_call_id: toolCall.id,\n delta: block.text,\n } as ToolResultTextDeltaEvent;\n break;\n\n case 'data':\n if (block.source.type === 'base64') {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_DATA_DELTA,\n reply_id: this.replyId,\n tool_call_id: toolCall.id,\n media_type: block.source.media_type,\n data: block.source.data,\n } as ToolResultDataDeltaEvent;\n } else if (block.source.type === 'url') {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_DATA_DELTA,\n reply_id: this.replyId,\n tool_call_id: toolCall.id,\n media_type: block.source.media_type,\n url: block.source.url,\n } as ToolResultDataDeltaEvent;\n }\n break;\n }\n }\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_END,\n reply_id: this.replyId,\n tool_call_id: toolCall.id,\n state: toolRes.state,\n } as ToolResultEndEvent;\n }\n\n /**\n * Convert the agent instance to a JSON-serializable object.\n * @returns An object containing the agent's name and system prompt.\n */\n public async toJSON() {\n return {\n reply_id: this.replyId,\n confirmedToolCallIds: this.confirmedToolCallIds,\n curIter: this.curIter,\n };\n }\n\n /**\n * Split the current context into two parts: one part that needs to be compressed and another part that should be reserved based on the compression configuration. The method calculates how many recent \"units\" (blocks or tool call pairs) to keep uncompressed according to the `keepRecent` setting in the compression configuration, and ensures that tool calls and their corresponding results are not separated during the split.\n * @returns An object containing the `toCompressedContext` which includes the messages that need to be compressed, and the `reservedContext` which includes the recent messages that should be kept uncompressed.\n */\n protected _splitContextForCompression() {\n let toCompressedContext: Msg[] = [];\n let reservedContext: Msg[] = [];\n\n // Calculate which messages need to be compressed\n // keepRecent specifies the number of recent \"units\" to keep uncompressed\n // A unit is either: a single block (text/thinking), or a tool_call + tool_result pair\n const keepRecent = this.compressionConfig!.keepRecent ?? 0;\n\n const nBlocks = this.context.map(msg => msg.content.length).reduce((a, b) => a + b, 0);\n const toCompressedBlockNumber = nBlocks - keepRecent > 0 ? nBlocks - keepRecent : 0;\n\n let currentCompressedBlocks = 0;\n for (const [index, msg] of this.context.entries()) {\n if (currentCompressedBlocks + msg.content.length <= toCompressedBlockNumber) {\n toCompressedContext.push(msg);\n currentCompressedBlocks += msg.content.length;\n } else {\n // The blocks that should be reserved according to the keepRecent count\n const reservedBlocks = msg.content.slice(\n toCompressedBlockNumber - currentCompressedBlocks\n );\n // Check if the reserved blocks contain an unpaired tool_call or tool_result\n const unPairedToolResultIds = new Set<string>();\n for (const block of reservedBlocks) {\n if (block.type == 'tool_call') {\n unPairedToolResultIds.add(block.id);\n } else if (block.type == 'tool_result') {\n if (unPairedToolResultIds.has(block.id)) {\n unPairedToolResultIds.delete(block.id);\n }\n }\n }\n // If there are unpaired tool calls, we need to move them to the reserved blocks\n let i = toCompressedBlockNumber - currentCompressedBlocks - 1;\n for (; i >= 0; i--) {\n const block = msg.content[i];\n if (block.type === 'tool_call' && unPairedToolResultIds.has(block.id)) {\n unPairedToolResultIds.delete(block.id);\n }\n if (unPairedToolResultIds.size === 0) break;\n }\n // All contents in this message should be reserved if i\n if (i <= 0) {\n reservedContext.push(msg);\n break;\n }\n\n // Slice the message content and push the reserved part to the compressed context\n const lastMsg = { ...msg };\n lastMsg.content = msg.content.slice(0, i);\n toCompressedContext.push(lastMsg);\n\n const reservedMsg = { ...msg };\n reservedMsg.content = msg.content.slice(i);\n reservedContext.push(reservedMsg);\n\n // The rest messages should be reserved\n reservedContext.push(...this.context.slice(index + 1));\n break;\n }\n }\n return { toCompressedContext, reservedContext };\n }\n\n /**\n * Compress the agent's memory using the specified compression model (if provided) or the original model.\n */\n protected async compressMemoryIfNeeded() {\n // The tool call and result pair must be kept or removed together\n if (!this.compressionConfig || !this.compressionConfig.enabled) return;\n\n const { toCompressedContext, reservedContext } = this._splitContextForCompression();\n\n // Compress the toCompressedContext\n if (\n toCompressedContext.length <= 0 ||\n (toCompressedContext.length === 1 && toCompressedContext.at(0)?.content.length === 1)\n )\n return;\n\n // Compute if the context exceed the threshold\n const messages = [\n createMsg({\n name: 'system',\n content: [{ type: 'text', text: this.sysPrompt, id: crypto.randomUUID() }],\n role: 'system',\n }),\n ...toCompressedContext,\n // instructions to compress the context into a summary\n createMsg({\n name: 'user',\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text:\n this.compressionConfig.compressionPrompt || DEFAULT_COMPRESSION_PROMPT,\n },\n ],\n role: 'user',\n }),\n ];\n\n const nTokens = await this.model.countTokens({\n messages,\n tools: this.toolkit.getJSONSchemas(),\n });\n console.debug(`[AGENT ${this.name}] Current context token count: ${nTokens}.`);\n if (nTokens <= this.compressionConfig.triggerThreshold) return;\n\n console.log(\n `[AGENT ${this.name}] Compressing memory with ${toCompressedContext.length} messages.`\n );\n // Generate the summary structured content\n const res = await this.model.callStructured({\n messages: [\n createMsg({\n name: 'system',\n content: [{ type: 'text', text: this.sysPrompt, id: crypto.randomUUID() }],\n role: 'system',\n }),\n ...toCompressedContext,\n // instructions to compress the context into a summary\n createMsg({\n name: 'user',\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text:\n this.compressionConfig.compressionPrompt ||\n DEFAULT_COMPRESSION_PROMPT,\n },\n ],\n role: 'user',\n }),\n ],\n schema: this.compressionConfig.summarySchema || DEFAULT_SUMMARY_SCHEMA,\n });\n\n // Make the compression summary\n let summaryText = '<system-reminder>Here is a summary of your previous work\\n';\n for (const [key, value] of Object.entries(res.content)) {\n summaryText += `# ${key}\\n${value}\\n`;\n }\n summaryText += '</system-reminder>';\n\n console.debug(`[AGENT ${this.name}] Compression summary: ${summaryText}`);\n\n // Update the context with the compression summary and the reserved recent blocks\n this.context = reservedContext;\n this.curSummary = summaryText;\n }\n}\n","import { JSONSerializableObject } from '../type';\nimport {\n ContentBlock,\n TextBlock,\n ThinkingBlock,\n ToolResultBlock,\n ToolCallBlock,\n DataBlock,\n Base64Source,\n URLSource,\n} from './block';\nimport { AgentEvent, EventType } from '../event';\n\n/** A chat message exchanged between agents or between an agent and a model. */\nexport interface Msg {\n /** Unique identifier for the message. */\n id: string;\n /** Display name of the message sender. */\n name: string;\n /** Conversation role of the sender. */\n role: 'user' | 'assistant' | 'system';\n /** Message body. */\n content: ContentBlock[];\n /** Arbitrary key-value metadata attached to the message. */\n metadata: Record<string, JSONSerializableObject>;\n /** ISO-8601 creation timestamp. */\n created_at: string;\n /** ISO-8601 finished timestamp. */\n finished_at?: string | null;\n /** Usage information for the message, such as token counts. */\n usage?: {\n inputTokens: number;\n outputTokens: number;\n };\n}\n\n/**\n * Create a new {@link Msg} object, filling in `id` and `created_at` when omitted.\n * A plain string `content` is automatically wrapped in a single {@link TextBlock}.\n * @param root0\n * @param root0.name\n * @param root0.content\n * @param root0.role\n * @param root0.metadata\n * @param root0.id\n * @param root0.created_at\n * @param root0.finished_at\n * @param root0.usage\n * @returns A Msg object.\n */\nexport function createMsg({\n name,\n content,\n role,\n metadata = {},\n id = crypto.randomUUID(),\n created_at = new Date().toISOString(),\n finished_at,\n usage,\n}: Omit<Msg, 'id' | 'created_at' | 'metadata' | 'content'> &\n Partial<Pick<Msg, 'id' | 'created_at' | 'metadata'>> & {\n content: string | ContentBlock[];\n }): Msg {\n const contentBlocks: ContentBlock[] =\n typeof content === 'string'\n ? [{ id: crypto.randomUUID(), type: 'text', text: content } as TextBlock]\n : content;\n return { id, name, role, content: contentBlocks, metadata, created_at, finished_at, usage };\n}\n\n/**\n * Create a user {@link Msg}.\n * @param root0\n * @param root0.name\n * @param root0.content\n * @param root0.metadata\n * @param root0.id\n * @param root0.created_at\n * @returns A Msg object with role 'user'.\n */\nexport function UserMsg({\n name,\n content,\n metadata = {},\n id = crypto.randomUUID(),\n created_at = new Date().toISOString(),\n}: {\n name: string;\n content: string | ContentBlock[];\n metadata?: Record<string, JSONSerializableObject>;\n id?: string;\n created_at?: string;\n}): Msg {\n return createMsg({ name, content, role: 'user', metadata, id, created_at });\n}\n\n/**\n * Create an assistant {@link Msg}.\n * @param root0\n * @param root0.name\n * @param root0.content\n * @param root0.metadata\n * @param root0.id\n * @param root0.created_at\n * @param root0.usage\n * @returns A Msg object with role 'assistant'.\n */\nexport function AssistantMsg({\n name,\n content,\n metadata = {},\n id = crypto.randomUUID(),\n created_at = new Date().toISOString(),\n usage,\n}: {\n name: string;\n content: string | ContentBlock[];\n metadata?: Record<string, JSONSerializableObject>;\n id?: string;\n created_at?: string;\n usage?: Msg['usage'];\n}): Msg {\n return createMsg({ name, content, role: 'assistant', metadata, id, created_at, usage });\n}\n\n/**\n * Create a system {@link Msg}.\n * @param root0\n * @param root0.name\n * @param root0.content\n * @param root0.metadata\n * @param root0.id\n * @param root0.created_at\n * @returns A Msg object with role 'system'.\n */\nexport function SystemMsg({\n name,\n content,\n metadata = {},\n id = crypto.randomUUID(),\n created_at = new Date().toISOString(),\n}: {\n name: string;\n content: string | ContentBlock[];\n metadata?: Record<string, JSONSerializableObject>;\n id?: string;\n created_at?: string;\n}): Msg {\n return createMsg({ name, content, role: 'system', metadata, id, created_at });\n}\n\n/**\n * Extract the plain-text content from a message.\n *\n * When `content` is a string it is returned as-is. When it is an array of\n * content blocks, all {@link TextBlock} texts are joined with `separator`.\n *\n * @param msg - The message to read.\n * @param separator - String inserted between consecutive text blocks. Defaults to `'\\n'`.\n * @returns The concatenated text, or `null` when no text blocks are present.\n */\nexport function getTextContent(msg: Msg, separator: string = '\\n'): string | null {\n const textBlocks = msg.content.filter(block => block.type === 'text');\n if (textBlocks.length === 0) return null;\n return textBlocks.map(block => (block as TextBlock).text).join(separator);\n}\n\n/**\n * Return all content blocks from a message, regardless of type.\n *\n * When `content` is a plain string it is wrapped in a single {@link TextBlock}.\n *\n * @param msg - The message to read.\n * @returns An array of all {@link ContentBlock} objects.\n */\nexport function getContentBlocks(msg: Msg): ContentBlock[];\nexport function getContentBlocks(msg: Msg, blockType: 'text'): TextBlock[];\nexport function getContentBlocks(msg: Msg, blockType: 'thinking'): ThinkingBlock[];\nexport function getContentBlocks(msg: Msg, blockType: 'data'): DataBlock[];\nexport function getContentBlocks(msg: Msg, blockType: 'tool_call'): ToolCallBlock[];\nexport function getContentBlocks(msg: Msg, blockType: 'tool_result'): ToolResultBlock[];\nexport function getContentBlocks(\n msg: Msg,\n blockType?: 'text' | 'thinking' | 'data' | 'tool_call' | 'tool_result'\n): ContentBlock[] {\n if (!blockType) return msg.content;\n return msg.content.filter(block => block.type === blockType);\n}\n\n/**\n * Find a content block by type and id within a message.\n * @param msg\n * @param blockType\n * @param blockId\n * @returns The matching {@link ContentBlock}, or `undefined` if not found.\n */\nfunction findBlock(msg: Msg, blockType: string, blockId: string): ContentBlock | undefined {\n return msg.content.find(block => block.type === blockType && block.id === blockId);\n}\n\n/**\n * Apply a streaming {@link AgentEvent} to a {@link Msg}, mutating it in place.\n *\n * Only `content` and `finished_at` are ever modified. Events whose\n * `reply_id` does not match `msg.id` are skipped with a warning.\n * @param msg\n * @param event\n * @returns The mutated {@link Msg} object.\n */\nexport function appendEvent(msg: Msg, event: AgentEvent): Msg {\n if (!('reply_id' in event)) return msg;\n if (event.reply_id !== msg.id) {\n console.warn(\n `Event reply_id \"${event.reply_id}\" does not match message id \"${msg.id}\", skipping.`\n );\n return msg;\n }\n\n switch (event.type) {\n case EventType.REPLY_END:\n msg.finished_at = event.created_at;\n break;\n\n case EventType.TEXT_BLOCK_START:\n msg.content.push({ type: 'text', id: event.block_id, text: '' });\n break;\n\n case EventType.TEXT_BLOCK_DELTA: {\n const block = findBlock(msg, 'text', event.block_id);\n if (!block) {\n console.warn(`TextBlock \"${event.block_id}\" not found, skipping.`);\n } else {\n (block as TextBlock).text += event.delta;\n }\n break;\n }\n\n case EventType.TEXT_BLOCK_END:\n break;\n\n case EventType.THINKING_BLOCK_START:\n msg.content.push({ type: 'thinking', id: event.block_id, thinking: '' });\n break;\n\n case EventType.THINKING_BLOCK_DELTA: {\n const block = findBlock(msg, 'thinking', event.block_id);\n if (!block) {\n console.warn(`ThinkingBlock \"${event.block_id}\" not found, skipping.`);\n } else {\n (block as ThinkingBlock).thinking += event.delta;\n }\n break;\n }\n\n case EventType.THINKING_BLOCK_END:\n break;\n\n case EventType.DATA_BLOCK_START:\n msg.content.push({\n type: 'data',\n id: event.block_id,\n source: { type: 'base64', data: '', media_type: event.media_type },\n });\n break;\n\n case EventType.DATA_BLOCK_DELTA: {\n const block = findBlock(msg, 'data', event.block_id);\n if (!block) {\n console.warn(`DataBlock \"${event.block_id}\" not found, skipping.`);\n } else {\n ((block as DataBlock).source as Base64Source).data += event.data;\n }\n break;\n }\n\n case EventType.DATA_BLOCK_END:\n break;\n\n case EventType.TOOL_CALL_START:\n msg.content.push({\n type: 'tool_call',\n id: event.tool_call_id,\n name: event.tool_call_name,\n input: '',\n state: 'pending',\n });\n break;\n\n case EventType.TOOL_CALL_DELTA: {\n const block = findBlock(msg, 'tool_call', event.tool_call_id);\n if (!block) {\n console.warn(`ToolCallBlock \"${event.tool_call_id}\" not found, skipping.`);\n } else {\n (block as ToolCallBlock).input += event.delta;\n }\n break;\n }\n\n case EventType.TOOL_CALL_END:\n break;\n\n case EventType.TOOL_RESULT_START:\n msg.content.push({\n type: 'tool_result',\n id: event.tool_call_id,\n name: event.tool_call_name,\n output: [],\n state: 'running',\n });\n break;\n\n case EventType.TOOL_RESULT_TEXT_DELTA: {\n const block = findBlock(msg, 'tool_result', event.tool_call_id);\n if (!block) {\n console.warn(`ToolResultBlock \"${event.tool_call_id}\" not found, skipping.`);\n } else {\n const trb = block as ToolResultBlock;\n if (typeof trb.output === 'string') {\n trb.output = [{ type: 'text', id: crypto.randomUUID(), text: trb.output }];\n }\n const last = trb.output[trb.output.length - 1];\n if (!last || last.type !== 'text') {\n trb.output.push({\n type: 'text',\n id: event.block_id ?? crypto.randomUUID(),\n text: event.delta,\n });\n } else {\n (last as TextBlock).text += event.delta;\n }\n }\n break;\n }\n\n case EventType.TOOL_RESULT_DATA_DELTA: {\n const block = findBlock(msg, 'tool_result', event.tool_call_id);\n if (!block) {\n console.warn(`ToolResultBlock \"${event.tool_call_id}\" not found, skipping.`);\n } else {\n const trb = block as ToolResultBlock;\n if (typeof trb.output === 'string') {\n trb.output = [{ type: 'text', id: crypto.randomUUID(), text: trb.output }];\n }\n const source: Base64Source | URLSource =\n event.data != null\n ? { type: 'base64', data: event.data, media_type: event.media_type }\n : { type: 'url', url: event.url!, media_type: event.media_type };\n trb.output.push({ type: 'data', id: event.block_id, source });\n }\n break;\n }\n\n case EventType.TOOL_RESULT_END: {\n const block = findBlock(msg, 'tool_result', event.tool_call_id);\n if (!block) {\n console.warn(`ToolResultBlock \"${event.tool_call_id}\" not found, skipping.`);\n } else {\n (block as ToolResultBlock).state = event.state;\n }\n break;\n }\n\n case EventType.MODEL_CALL_END:\n // Accumulated the input and output tokens here.\n if (msg.usage) {\n msg.usage.inputTokens += event.input_tokens;\n msg.usage.outputTokens += event.output_tokens;\n } else {\n msg.usage = {\n inputTokens: event.input_tokens,\n outputTokens: event.output_tokens,\n };\n }\n break;\n\n case EventType.REQUIRE_USER_CONFIRM:\n for (const tc of event.tool_calls) {\n const b = findBlock(msg, 'tool_call', tc.id);\n if (b) {\n (b as ToolCallBlock).state = 'asking';\n if (tc.suggested_rules !== undefined) {\n (b as ToolCallBlock).suggested_rules = tc.suggested_rules;\n }\n }\n }\n break;\n\n case EventType.USER_CONFIRM_RESULT:\n for (const result of event.confirm_results) {\n const b = findBlock(msg, 'tool_call', result.tool_call.id);\n if (b) {\n (b as ToolCallBlock).state = result.confirmed ? 'allowed' : 'finished';\n }\n }\n break;\n\n case EventType.REQUIRE_EXTERNAL_EXECUTION:\n for (const tc of event.tool_calls) {\n const b = findBlock(msg, 'tool_call', tc.id);\n if (b) (b as ToolCallBlock).state = 'submitted';\n }\n break;\n\n case EventType.EXTERNAL_EXECUTION_RESULT:\n for (const result of event.execution_results) {\n msg.content.push(result);\n }\n break;\n }\n\n return msg;\n}\n","import { DataBlock, TextBlock } from '../message';\nimport { JSONSerializableObject } from '../type';\n\n/**\n * The tool response structure.\n */\nexport interface ToolResponse {\n content: Array<TextBlock | DataBlock>;\n id: string;\n createdAt: string;\n metadata: Record<string, JSONSerializableObject>;\n state: 'success' | 'error' | 'interrupted' | 'running';\n isLast: boolean;\n isInterrupted: boolean;\n}\n\n/**\n * Create a tool response object with the given parameters.\n *\n * @param root0\n * @param root0.content\n * @param root0.state\n * @param root0.id\n * @param root0.createdAt\n * @param root0.metadata\n * @param root0.stream\n * @param root0.isLast\n * @param root0.isInterrupted\n * @returns A ToolResponse object\n */\nexport function createToolResponse({\n content,\n state,\n id = crypto.randomUUID(),\n createdAt = new Date().toISOString(),\n metadata = {},\n stream = false,\n isLast = true,\n isInterrupted = false,\n}: {\n content: Array<TextBlock | DataBlock>;\n state: 'success' | 'error' | 'interrupted' | 'running';\n id?: string;\n createdAt?: string;\n metadata?: Record<string, JSONSerializableObject>;\n stream?: boolean;\n isLast?: boolean;\n isInterrupted?: boolean;\n}) {\n return {\n content,\n id,\n createdAt,\n metadata,\n state,\n stream,\n isLast,\n isInterrupted,\n } as ToolResponse;\n}\n\n/**\n * If the given object conforms to the ToolResponse structure.\n *\n * @param obj\n * @returns True if the object is a ToolResponse, false otherwise\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function isToolResponse(obj: any): boolean {\n return (\n obj &&\n typeof obj === 'object' &&\n typeof obj.id === 'string' &&\n typeof obj.createdAt === 'string' &&\n Array.isArray(obj.content) &&\n typeof obj.metadata === 'object' &&\n typeof obj.stream === 'boolean' &&\n typeof obj.isLast === 'boolean' &&\n typeof obj.isInterrupted === 'boolean' &&\n ['success', 'error', 'interrupted', 'running'].includes(obj.state)\n );\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { Validator } from '@cfworker/json-schema';\nimport matter from 'gray-matter';\nimport { z } from 'zod';\n\nimport { createToolResponse, isToolResponse, ToolResponse } from './response';\nimport { HTTPMCPClient, StdioMCPClient } from '../mcp';\nimport { ToolCallBlock } from '../message';\nimport { ToolInputSchema, ToolSchema } from '../type';\nimport { Tool } from './base';\nimport { _jsonLoadsWithRepair } from '../_utils';\n\ninterface RegisteredTool extends Tool {\n type: 'function' | 'mcp';\n mcpName?: string;\n}\n\n/**\n * The toolkit module in AgentScope, which is responsible for registering tool functions, MCP, and agent skills.\n * It also provides group-wise management of tools.\n */\nexport class Toolkit {\n tools: RegisteredTool[];\n skills: string[];\n skillDirs: string[];\n\n // The cache mapping from the skill name to its corresponding tool name in the toolkit.\n private _skillCache: { [name: string]: string };\n\n /**\n * Initializes a new instance of the Toolkit class.\n * @param config - The configuration object for initializing the toolkit, which can include an array of tools, an array of skill paths, an array of skill directory paths, and a boolean indicating whether to include the built-in skill tool for reading SKILL.md files.\n * @param config.tools - An array of tool definitions to register in the toolkit.\n * @param config.skills - An array of file paths pointing to individual skills.\n * @param config.skillDirs - An array of directory paths, where each directory can contain multiple skills in its subdirectories.\n * @param config.builtInSkillTool - A boolean flag indicating whether to include the built-in skill tool for reading SKILL.md files.\n */\n constructor(config?: {\n tools?: Tool[];\n skills?: string[];\n skillDirs?: string[];\n builtInSkillTool?: boolean;\n }) {\n const { tools = [], skills = [], skillDirs = [], builtInSkillTool = true } = config || {};\n\n this.tools = [];\n\n if (builtInSkillTool) {\n this.tools.push({\n type: 'function',\n name: 'Skill',\n description: `Retrieves the full content of a skill by reading its SKILL.md file. Skills are packages of domain expertise that extend agent capabilities. Use this tool to access detailed instructions, examples, and guidelines for a specific skill.\n\nUsage:\n- Provide the skill name as the input parameter\n- The tool will return the complete SKILL.md file content for that skill\n- If the skill is not found, an error message with available skills will be returned\n- Available skills are listed in the skills-system section of the agent prompt`,\n inputSchema: z.object({ name: z.string().describe('The name of the skill') }),\n call: this._skillTool.bind(this),\n requireUserConfirm: false,\n });\n }\n\n tools.map(tool => {\n this.tools.push({\n type: 'function',\n ...tool,\n });\n });\n\n this.skills = skills;\n this.skillDirs = skillDirs;\n\n this._skillCache = {};\n }\n\n /**\n * Registers a tool function to the toolkit. The function can be either a plain function that adheres to the ToolFunction type, or an instance of a class that extends ToolBase. When registering a plain function, the name, description, and input schema must be provided explicitly. When registering a ToolBase instance, these properties will be extracted from the instance itself.\n *\n * @params tool - The tool function to register, which can be either a plain function with explicit properties or an instance of a class that extends ToolBase.\n * @returns The Toolkit instance with the new tool function registered\n * @param tool\n */\n registerToolFunction(tool: Tool): Toolkit {\n this.tools.push({\n type: 'function',\n ...tool,\n });\n return this;\n }\n\n /**\n * Registers functions from a given MCP client.\n *\n * @param root0\n * @param root0.client\n * @param root0.enabledTools\n * @param root0.disabledTools\n * @param root0.requireUserConfirm\n * @returns The Toolkit instance with the new tools registered\n */\n async registerMCPClient({\n client,\n enabledTools,\n disabledTools = [],\n requireUserConfirm = false,\n }: {\n client: HTTPMCPClient | StdioMCPClient;\n enabledTools?: string[];\n disabledTools?: string[];\n requireUserConfirm?: boolean;\n }): Promise<Toolkit> {\n const tools = await client.listTools();\n\n const appendTools: string[] = [];\n tools\n .filter(\n tool =>\n !(enabledTools && !enabledTools.includes(tool.name)) &&\n !disabledTools.includes(tool.name)\n )\n .forEach(tool => {\n this.tools.push({\n type: 'mcp',\n mcpName: client.name,\n ...tool,\n requireUserConfirm,\n });\n appendTools.push(tool.name);\n });\n console.log(`Registered tools from MCP client '${client.name}': ${appendTools.join(', ')}`);\n return this;\n }\n\n /**\n * Executes a registered tool function based on the provided ToolUseBlock.\n * Note this method always returns an AsyncGenerator of ToolResponse, regardless of the tool function type.\n *\n * @param toolCall - The ToolUseBlock containing the tool name and input arguments\n * @yields Incremental ToolResponse objects as they are produced by the tool function\n * @returns The final complete ToolResponse after the tool function execution is finished\n */\n async *callToolFunction(toolCall: ToolCallBlock): AsyncGenerator<ToolResponse, ToolResponse> {\n // If the tool is registered\n const tool = this.tools.find(tool => tool.name === toolCall.name);\n\n if (!tool) {\n const notFoundRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `FunctionNotFoundError: Cannot find the function named ${toolCall.name}`,\n },\n ],\n state: 'error',\n });\n yield notFoundRes;\n return notFoundRes;\n }\n\n // Parse the input arguments using the tool's schema\n let parsedInput: Record<string, unknown>;\n try {\n parsedInput = _jsonLoadsWithRepair(toolCall.input);\n if (tool.inputSchema instanceof z.ZodObject) {\n tool.inputSchema.parse(parsedInput);\n } else {\n //\n const validator = new Validator(tool.inputSchema);\n const validation = validator.validate(parsedInput);\n if (!validation.valid) {\n throw new Error(`Invalid input arguments: ${validation.errors}`);\n }\n }\n } catch (error) {\n const parseErrorRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `InvalidArgumentError: ${String(error)}`,\n },\n ],\n state: 'error',\n });\n yield parseErrorRes;\n return parseErrorRes;\n }\n\n // Log the tool call with parsed input\n if (!tool.call) {\n throw new Error(\n `Cannot execute external tool '${toolCall.name}' because no call method is defined for it in the toolkit.`\n );\n }\n\n // Execute the tool function and await the result\n // Note: await on a non-Promise value returns the value itself\n let finalRes: ToolResponse | null = null;\n try {\n const res = await tool.call(parsedInput);\n\n // If res is a string\n if (typeof res === 'string') {\n const textRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: res,\n },\n ],\n state: 'success',\n });\n yield textRes;\n finalRes = textRes;\n } else if (isToolResponse(res)) {\n // If res is a ToolResponse\n yield res as ToolResponse;\n finalRes = res as ToolResponse;\n } else if (Symbol.asyncIterator in res) {\n // If res is an AsyncGenerator of string or ToolResponse\n const accContent: ToolResponse['content'] = [];\n let nextResult = await (res as AsyncGenerator<string | ToolResponse>).next();\n\n while (!nextResult.done) {\n const currentValue = nextResult.value;\n // Peek ahead to determine if this is the last value\n nextResult = await (res as AsyncGenerator<string | ToolResponse>).next();\n const isLastValue = nextResult.done;\n\n if (typeof currentValue === 'string') {\n const itemRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: currentValue,\n },\n ],\n isLast: isLastValue,\n state: 'running',\n });\n yield itemRes;\n\n // Accumulate the text content into finalRes\n accContent.push({\n id: crypto.randomUUID(),\n type: 'text',\n text: currentValue,\n });\n } else if (isToolResponse(currentValue)) {\n // Use the isLast from the ToolResponse if set, otherwise use our calculated value\n currentValue.isLast = currentValue.isLast ?? isLastValue;\n yield currentValue as ToolResponse;\n\n // Accumulate the content of the ToolResponse into finalRes\n accContent.push(...currentValue.content);\n }\n }\n finalRes = createToolResponse({\n content: accContent,\n state: 'success',\n });\n } else if (Symbol.iterator in res) {\n // If res is a Generator of string or ToolResponse\n const accContent: ToolResponse['content'] = [];\n let nextResult = (res as Generator<string | ToolResponse>).next();\n\n while (!nextResult.done) {\n const currentValue = nextResult.value;\n // Peek ahead to determine if this is the last value\n nextResult = (res as Generator<string | ToolResponse>).next();\n const isLastValue = nextResult.done;\n\n if (typeof currentValue === 'string') {\n const itemRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: currentValue,\n },\n ],\n isLast: isLastValue,\n state: 'running',\n });\n yield itemRes;\n // Accumulate the text content into finalRes\n accContent.push({\n id: crypto.randomUUID(),\n type: 'text',\n text: currentValue,\n });\n } else if (isToolResponse(currentValue)) {\n // Use the isLast from the ToolResponse if set, otherwise use our calculated value\n currentValue.isLast = currentValue.isLast ?? isLastValue;\n yield currentValue as ToolResponse;\n // Accumulate the content of the ToolResponse into finalRes\n accContent.push(...currentValue.content);\n }\n }\n finalRes = createToolResponse({\n content: accContent,\n state: 'success',\n });\n } else {\n const invalidRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: String(res),\n },\n ],\n state: 'running',\n });\n yield invalidRes;\n finalRes = invalidRes;\n }\n } catch (error) {\n const errorRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `ToolExecutionError: ${String(error)}`,\n },\n ],\n state: 'error',\n });\n yield errorRes;\n finalRes = errorRes;\n }\n\n if (!finalRes) {\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `Tool ${toolCall.name} executed successfully.`,\n },\n ],\n state: 'success',\n });\n }\n\n // Clean the finalRes by merging the adjacent text blocks into one block, leaving\n // multimodal content blocks (e.g. image, audio) unchanged\n const cleanedContent: ToolResponse['content'] = [];\n let textBuffer = '';\n for (const block of finalRes.content) {\n if (block.type === 'text') {\n textBuffer += block.text;\n } else {\n if (textBuffer) {\n cleanedContent.push({\n id: crypto.randomUUID(),\n type: 'text',\n text: textBuffer,\n });\n textBuffer = '';\n }\n cleanedContent.push(block);\n }\n }\n // The remaining text in the buffer, if any, should also be pushed to the cleanedContent\n if (textBuffer) {\n cleanedContent.push({\n id: crypto.randomUUID(),\n type: 'text',\n text: textBuffer,\n });\n }\n\n return {\n ...finalRes,\n content: cleanedContent,\n };\n }\n\n /**\n * Returns the JSON schemas for all registered tools in a format compatible with LLM APIs.\n *\n * @returns An array of ToolJSONSchema objects\n */\n getJSONSchemas(): ToolSchema[] {\n return this.tools.map(tool => {\n const inputSchema =\n tool.inputSchema instanceof z.ZodObject\n ? tool.inputSchema.toJSONSchema({ target: 'openapi-3.0' })\n : tool.inputSchema;\n\n return {\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: inputSchema as ToolInputSchema,\n },\n };\n });\n }\n\n /**\n * Get the instruction prompt for the agent to use the skills.\n *\n * @returns A string containing the instruction prompt of the available skills and how to use them.\n */\n getSkillsPrompt(): string {\n this._skillCache = {};\n if (this.skills.length === 0 && this.skillDirs.length === 0) return '';\n\n if (typeof process !== 'undefined' && process.versions && process.versions.node) {\n const skillsInfo: { name: string; description: string; location: string }[] = [];\n this.skills.forEach(skillPath => {\n // 首先获取绝对路径\n const absSkillPath = path.resolve(skillPath);\n\n // Check if directory exists\n if (!fs.existsSync(absSkillPath) || !fs.statSync(absSkillPath).isDirectory()) {\n return;\n }\n\n // First, check if SKILL.md exists directly in this directory\n const skillMdPath = path.join(absSkillPath, 'SKILL.md');\n if (!fs.existsSync(skillMdPath)) return;\n\n // Read the SKILL.md file and extract the name and description from the YAML front matter\n try {\n const content = fs.readFileSync(skillMdPath, 'utf-8');\n const { data } = matter(content);\n\n const name = data.name || path.basename(skillPath);\n const description = data.description || 'No description provided';\n\n skillsInfo.push({\n name,\n description,\n location: absSkillPath,\n });\n\n this._skillCache[name] = absSkillPath;\n } catch (e) {\n console.error(`Error reading SKILL.md for skill at ${skillPath}:`, e);\n }\n });\n\n this.skillDirs.forEach(skillDir => {\n const absSkillDir = path.resolve(skillDir);\n\n // Check if directory exists\n if (!fs.existsSync(absSkillDir) || !fs.statSync(absSkillDir).isDirectory()) {\n return;\n }\n\n // Read all subdirectories in the skillDir\n const subdirs = fs.readdirSync(absSkillDir).filter(subdir => {\n const subdirPath = path.join(absSkillDir, subdir);\n return fs.statSync(subdirPath).isDirectory();\n });\n\n subdirs.forEach(subdir => {\n const skillMdPath = path.join(absSkillDir, subdir, 'SKILL.md');\n if (!fs.existsSync(skillMdPath)) return;\n\n try {\n const content = fs.readFileSync(skillMdPath, 'utf-8');\n const { data } = matter(content);\n\n const name = data.name || subdir;\n const description = data.description || 'No description provided';\n\n skillsInfo.push({\n name,\n description,\n location: path.join(skillDir, subdir),\n });\n\n this._skillCache[name] = path.join(absSkillDir, subdir);\n } catch (e) {\n console.error(\n `Error reading SKILL.md for skill at ${path.join(skillDir, subdir)}:`,\n e\n );\n }\n });\n });\n\n if (skillsInfo.length === 0) return '';\n\n const skillsXml = skillsInfo\n .map(\n skill => `<skill>\n<name>${skill.name}</name>\n<description>${skill.description}</description>\n<location>${skill.location}</location>\n</skill>`\n )\n .reduce((acc, skillInfo) => acc + `\\n${skillInfo}\\n`, '');\n\n return `<skills-system>\n## What are Skills?\nSkills are packages of domain expertise that extend your capabilities.\n\n## Important: How to Use Skills\n**Skill names are NOT callable functions.** You cannot call a skill directly by its name.\n${skillsXml}\n</skills-system>`;\n }\n\n return '';\n }\n\n /**\n * The agent skill tool to read SKILL.md file content based on the skill name.\n * @param root0\n * @param root0.name\n * @returns The content of the SKILL.md file for the specified skill, or an error message if the skill is not\n * found or the SKILL.md file cannot be read.\n */\n private async _skillTool({ name }: { name: string }): Promise<ToolResponse> {\n if (this._skillCache[name]) {\n // Look up the skill name in the cache to get the corresponding directory path\n const skillDir = this._skillCache[name];\n // Read the SKILL.md file in the skill directory and return its content as the tool response\n const skillMdPath = path.join(skillDir, 'SKILL.md');\n if (!fs.existsSync(skillMdPath)) {\n try {\n const fileContent = fs.readFileSync(skillMdPath, 'utf-8');\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: fileContent,\n },\n ],\n state: 'success',\n });\n } catch {}\n }\n }\n\n // Scan the skills and skillDirs again to find the skill if it's not in the cache and refresh the cache at the same time\n this.getSkillsPrompt();\n const refreshedSkillDir = this._skillCache[name];\n if (refreshedSkillDir) {\n const skillMdPath = path.join(refreshedSkillDir, 'SKILL.md');\n try {\n const fileContent = fs.readFileSync(skillMdPath, 'utf-8');\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: fileContent,\n },\n ],\n state: 'success',\n });\n } catch {}\n }\n\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `SkillNotFoundError: Cannot find the skill named ${name}, current available skills are ${Object.keys(this._skillCache).join(', ')}`,\n },\n ],\n state: 'error',\n });\n }\n\n /**\n * Checks if a tool requires user confirmation before execution based on its name.\n * @param toolName The name of the tool to check for user confirmation requirement.\n * @returns A boolean indicating whether the specified tool requires user confirmation before execution. If the tool is not found, it returns false.\n */\n requireUserConfirm(toolName: string): boolean {\n const tool = this.tools.find(tool => tool.name === toolName);\n return tool ? (tool.requireUserConfirm ?? false) : false;\n }\n\n /**\n * Checks if a tool requires external execution (e.g., by an MCP client) based on its name.\n * @param toolName\n * @returns A boolean indicating whether the specified tool requires external execution. If the tool is not found, it returns false.\n */\n requireExternalExecution(toolName: string): boolean {\n const tool = this.tools.find(tool => tool.name === toolName);\n return tool ? !tool.call : false;\n }\n}\n","import { jsonrepair } from 'jsonrepair';\n\nimport { JSONSerializableObject } from '../type';\n\n/**\n * Creates a timestamp string in the format \"YYYY-MM-DD HH:mm:ss.sss\"\n * representing the current date and time.\n *\n * @returns {string} The formatted timestamp string.\n */\nexport function _createTimestamp(): string {\n return new Date().toISOString().replace('T', ' ').replace('Z', '').substring(0, 23);\n}\n\n/**\n * Attempts to parse a JSON string into a dictionary/Record.\n * This function is used to handle the streaming tool use block from the LLM API.\n *\n * @param input - The JSON string to parse.\n * @returns A dictionary/Record parsed from the JSON string. If parsing fails, returns an empty dictionary.\n */\nexport function _jsonLoadsWithRepair(input: string): Record<string, JSONSerializableObject> {\n try {\n const jsonObj = JSON.parse(input);\n // Check if the jsonObj is a dictionary/Record\n if (typeof jsonObj === 'object' && jsonObj !== null && !Array.isArray(jsonObj)) {\n return jsonObj as Record<string, JSONSerializableObject>;\n } else {\n // Return an empty dictionary\n return {};\n }\n } catch {\n try {\n const repairedString = jsonrepair(input);\n const jsonObj = JSON.parse(repairedString);\n // Check if the jsonObj is a dictionary/Record\n if (typeof jsonObj === 'object' && jsonObj !== null && !Array.isArray(jsonObj)) {\n return jsonObj as Record<string, JSONSerializableObject>;\n } else {\n // Return an empty dictionary\n return {};\n }\n } catch (e) {\n console.error(`Failed to parse JSON \"${input}\" with error:`, e);\n return {};\n }\n }\n}\n\n/**\n * Parses a streamed response from the post request.\n * An async generator that yields parsed JSON objects from the SSE stream.\n *\n * @param response - The fetch response object.\n * @returns An async generator yielding parsed JSON objects.\n */\nexport async function* _parseStreamedResponse<T>(response: Response): AsyncGenerator<T> {\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error('Failed to get reader from response body for streaming.');\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Handle the completed line\n const lines = buffer.split('\\n');\n buffer = lines.pop() || ''; // Keep the last uncompleted line\n\n for (const line of lines) {\n const trimmedLine = line.trim();\n if (!trimmedLine || trimmedLine.startsWith(':')) {\n continue; // Skip the empty line and comments\n }\n\n if (trimmedLine.startsWith('data:')) {\n const jsonStr = trimmedLine.slice(5).trim(); // Remove \"data:\" prefix\n\n if (jsonStr === '[DONE]') {\n break;\n }\n\n try {\n const json = JSON.parse(jsonStr);\n yield json;\n } catch (e) {\n console.error('Failed to parse JSON:', e);\n throw new Error(`Failed to parse JSON from stream: ${jsonStr}`);\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import { exec } from 'child_process';\nimport { promisify } from 'util';\n\nimport { z } from 'zod';\n\nimport { createToolResponse, ToolResponse } from './response';\n\nconst execAsync = promisify(exec);\n\n/**\n * Tool for executing bash commands in a shell environment.\n * Intended for terminal operations such as git, npm, and docker.\n * File operations should use the dedicated Read, Write, Edit, Glob, and Grep tools instead.\n *\n * @returns A Tool object for executing bash commands, with a call method that performs the execution and returns the output or error message.\n */\nexport function Bash() {\n return {\n name: 'Bash',\n description: `Executes a given bash command and returns its output.\n\nThe working directory persists between commands, but shell state does not. The shell environment is initialized from the user's profile (bash or zsh).\n\nIMPORTANT: Avoid using this tool to run \\`find\\`, \\`grep\\`, \\`cat\\`, \\`head\\`, \\`tail\\`, \\`sed\\`, \\`awk\\`, or \\`echo\\` commands, unless explicitly instructed or after you have verified that a dedicated tool cannot accomplish your task. Instead, use the appropriate dedicated tool as this will provide a much better experience for the user:\n\n - File search: Use Glob (NOT find or ls)\n - Content search: Use Grep (NOT grep or rg)\n - Read files: Use Read (NOT cat/head/tail)\n - Edit files: Use Edit (NOT sed/awk)\n - Write files: Use Write (NOT echo >/cat <<EOF)\n - Communication: Output text directly (NOT echo/printf)\n\nWhile the Bash tool can do similar things, it's better to use the built-in tools as they provide a better user experience and make it easier to review tool calls and give permission.\n\n# Instructions\n - If your command will create new directories or files, first use this tool to run \\`ls\\` to verify the parent directory exists and is the correct location.\n - Always quote file paths that contain spaces with double quotes in your command (e.g., cd \"path with spaces/file.txt\")\n - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of \\`cd\\`. You may use \\`cd\\` if the User explicitly requests it.\n - You may specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). By default, your command will timeout after 120000ms (2 minutes).\n - Write a clear, concise description of what your command does. For simple commands, keep it brief (5-10 words). For complex commands (piped commands, obscure flags, or anything hard to understand at a glance), include enough context so that the user can understand what your command will do.\n - When issuing multiple commands:\n - If the commands are independent and can run in parallel, make multiple Bash tool calls in a single message. Example: if you need to run \"git status\" and \"git diff\", send a single message with two Bash tool calls in parallel.\n - If the commands depend on each other and must run sequentially, use a single Bash call with '&&' to chain them together.\n - Use ';' only when you need to run commands sequentially but don't care if earlier commands fail.\n - DO NOT use newlines to separate commands (newlines are ok in quoted strings).\n - For git commands:\n - Prefer to create a new commit rather than amending an existing commit.\n - Before running destructive operations (e.g., git reset --hard, git push --force, git checkout --), consider whether there is a safer alternative that achieves the same goal. Only use destructive operations when they are truly the best approach.\n - Never skip hooks (--no-verify) or bypass signing (--no-gpg-sign, -c commit.gpgsign=false) unless the user has explicitly asked for it. If a hook fails, investigate and fix the underlying issue.\n - Avoid unnecessary \\`sleep\\` commands:\n - Do not sleep between commands that can run immediately — just run them.\n - Do not retry failing commands in a sleep loop — diagnose the root cause or consider an alternative approach.\n - If you must sleep, keep the duration short (1-5 seconds) to avoid blocking the user.`,\n inputSchema: z.object({\n command: z.string().describe('The bash command to execute'),\n description: z\n .string()\n .optional()\n .describe(\n 'Clear, concise description of what this command does. For simple commands, keep it brief (5-10 words). For complex commands, include enough context.'\n ),\n timeout: z\n .number()\n .int()\n .min(0)\n .max(600000)\n .optional()\n .describe('Optional timeout in milliseconds (default: 120000, max: 600000)'),\n }),\n requireUserConfirm: true,\n\n /**\n * Executes a bash command and returns its output.\n *\n * @param root0 - The parameters object\n * @param root0.command - The bash command to execute\n * @param root0.description - Optional description of what the command does\n * @param root0.timeout - Optional timeout in milliseconds (default: 120000, max: 600000)\n * @returns The stdout of the command, or an error message if the command fails\n */\n async call({\n command,\n description: _description,\n timeout = 120000,\n }: {\n command: string;\n description?: string;\n timeout?: number;\n }): Promise<ToolResponse> {\n try {\n const maxTimeout = 600000;\n const effectiveTimeout = Math.min(timeout, maxTimeout);\n\n // Determine the appropriate shell based on platform\n let shell: string;\n if (process.platform === 'win32') {\n // On Windows, use cmd.exe or PowerShell\n shell = process.env.COMSPEC || 'cmd.exe';\n } else {\n // On Unix-like systems, use the user's shell or default to bash\n shell = process.env.SHELL || '/bin/bash';\n }\n\n const { stdout } = await execAsync(command, {\n encoding: 'utf-8',\n timeout: effectiveTimeout,\n maxBuffer: 30000 * 1024,\n shell,\n });\n\n // Normalize line endings to LF for cross-platform consistency\n const normalizedOutput = stdout.replace(/\\r\\n/g, '\\n');\n\n const maxOutputLength = 30000;\n if (normalizedOutput.length > maxOutputLength) {\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text:\n normalizedOutput.substring(0, maxOutputLength) +\n '\\n\\n[Output truncated - exceeded 30000 characters]',\n },\n ],\n state: 'success',\n });\n }\n\n return createToolResponse({\n content: [{ id: crypto.randomUUID(), type: 'text', text: normalizedOutput }],\n state: 'success',\n });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n const errorMessage = error.message || 'Unknown error';\n const stderr = error.stderr?.toString().replace(/\\r\\n/g, '\\n') || '';\n const stdout = error.stdout?.toString().replace(/\\r\\n/g, '\\n') || '';\n\n let result = `Command failed: ${command}\\n`;\n if (stdout) result += `\\nStdout:\\n${stdout}`;\n if (stderr) result += `\\nStderr:\\n${stderr}`;\n if (errorMessage && !stderr) result += `\\nError: ${errorMessage}`;\n\n return createToolResponse({\n content: [{ id: crypto.randomUUID(), type: 'text', text: result }],\n state: 'error',\n });\n }\n },\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { z } from 'zod';\n\nimport { createToolResponse, ToolResponse } from './response';\n\n/**\n * Tool for reading files from the local filesystem.\n * Returns file contents with line numbers in cat -n format.\n *\n * @returns A Tool object for reading files, with a call method that performs the read operation and returns the formatted contents or a warning if the file is empty.\n */\nexport function Read() {\n return {\n name: 'Read',\n description: `Reads a file from the local filesystem. You can access any file directly by using this tool.\nAssume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.\n\nUsage:\n- The file_path parameter must be an absolute path, not a relative path\n- By default, it reads up to 2000 lines starting from the beginning of the file\n- You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters\n- Any lines longer than 2000 characters will be truncated\n- Results are returned using cat -n format, with line numbers starting at 1\n- This tool can only read files, not directories. To read a directory, use an ls command via the Bash tool.\n- You can call multiple tools in a single response. It is always better to speculatively read multiple potentially useful files in parallel.\n- If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.`,\n inputSchema: z.object({\n file_path: z.string().describe('The absolute path to the file to read'),\n offset: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\n 'The line number to start reading from. Only provide if the file is too large to read at once'\n ),\n limit: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\n 'The number of lines to read. Only provide if the file is too large to read at once'\n ),\n }),\n requireUserConfirm: true,\n\n /**\n * Reads a file and returns its contents with line numbers.\n *\n * @param root0 - The parameters object\n * @param root0.file_path - Absolute path to the file to read\n * @param root0.offset - Line number to start reading from (1-based)\n * @param root0.limit - Maximum number of lines to read (capped at 2000)\n * @returns The file contents formatted with line numbers, or a warning if the file is empty\n * @throws If the path is not absolute, the file does not exist, or the path is a directory\n */\n call({\n file_path,\n offset,\n limit,\n }: {\n file_path: string;\n offset?: number;\n limit?: number;\n }): ToolResponse {\n if (!path.isAbsolute(file_path)) {\n throw new Error(`file_path must be an absolute path, got: ${file_path}`);\n }\n\n if (!fs.existsSync(file_path)) {\n throw new Error(`File not found: ${file_path}`);\n }\n\n const stat = fs.statSync(file_path);\n if (stat.isDirectory()) {\n throw new Error(\n `${file_path} is a directory, not a file. Use Bash with ls to read directories.`\n );\n }\n\n const rawContent = fs.readFileSync(file_path, 'utf-8');\n\n if (rawContent.length === 0) {\n return createToolResponse({\n content: [{ id: crypto.randomUUID(), type: 'text', text: rawContent }],\n state: 'success',\n });\n }\n\n const allLines = rawContent.split('\\n');\n const startLine = offset !== undefined ? offset - 1 : 0;\n const maxLines = 2000;\n const effectiveLimit = limit !== undefined ? Math.min(limit, maxLines) : maxLines;\n const selectedLines = allLines.slice(startLine, startLine + effectiveLimit);\n\n const maxLineLength = 2000;\n const formatted = selectedLines\n .map((line, i) => {\n const lineNum = startLine + i + 1;\n const truncated =\n line.length > maxLineLength\n ? line.substring(0, maxLineLength) + '[truncated]'\n : line;\n return `${String(lineNum).padStart(6)}\\t${truncated}`;\n })\n .join('\\n');\n\n return createToolResponse({\n content: [{ id: crypto.randomUUID(), type: 'text', text: formatted }],\n state: 'success',\n });\n },\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { z } from 'zod';\n\n/**\n * Tool for writing files to the local filesystem.\n * Creates parent directories as needed and overwrites existing files.\n *\n * @returns A Tool object for writing files, with a call method that performs the write operation.\n */\nexport function Write() {\n return {\n name: 'Write',\n description: `Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the Read tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.`,\n inputSchema: z.object({\n file_path: z\n .string()\n .describe(\n 'The absolute path to the file to write (must be absolute, not relative)'\n ),\n content: z.string().describe('The content to write to the file'),\n }),\n requireUserConfirm: true,\n\n /**\n * Writes content to a file, creating parent directories if necessary.\n *\n * @param root0 - The parameters object\n * @param root0.file_path - Absolute path to the file to write\n * @param root0.content - The content to write to the file\n * @returns A success message including the number of lines written\n * @throws If the path is not absolute\n */\n call({ file_path, content }: { file_path: string; content: string }): string {\n if (!path.isAbsolute(file_path)) {\n throw new Error(`file_path must be an absolute path, got: ${file_path}`);\n }\n\n const dir = path.dirname(file_path);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n fs.writeFileSync(file_path, content, 'utf-8');\n const lineCount = content.split('\\n').length;\n return `The file ${file_path} has been written successfully (${lineCount} lines).`;\n },\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { z } from 'zod';\n\n/**\n * Tool for performing exact string replacements in files.\n * Requires the file to have been read at least once before editing.\n *\n * @returns A Tool object with a call method that performs the edit operation based on the provided parameters, ensuring uniqueness of the old_string unless replace_all is true.\n */\nexport function Edit() {\n return {\n name: 'Edit',\n description: `Performs exact string replacements in files.\n\nUsage:\n- You must use your Read tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file.\n- When editing text from Read tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: spaces + line number + tab. Everything after that tab is the actual file content to match. Never include any part of the line number prefix in the old_string or new_string.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.\n- The edit will FAIL if \\`old_string\\` is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use \\`replace_all\\` to change every instance of \\`old_string\\`.\n- Use \\`replace_all\\` for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.`,\n inputSchema: z.object({\n file_path: z.string().describe('The absolute path to the file to modify'),\n old_string: z.string().describe('The text to replace'),\n new_string: z\n .string()\n .describe('The text to replace it with (must be different from old_string)'),\n replace_all: z\n .boolean()\n .optional()\n .default(false)\n .describe('Replace all occurrences of old_string (default false)'),\n }),\n requireUserConfirm: true,\n\n /**\n * Performs an exact string replacement in the specified file.\n *\n * @param root0 - The parameters object\n * @param root0.file_path - Absolute path to the file to modify\n * @param root0.old_string - The exact text to find and replace\n * @param root0.new_string - The text to replace old_string with\n * @param root0.replace_all - If true, replaces all occurrences; otherwise only the first\n * @returns A success message indicating the file was updated\n * @throws If the path is not absolute, file does not exist, strings are identical,\n * old_string is not found, or old_string is not unique and replace_all is false\n */\n call({\n file_path,\n old_string,\n new_string,\n replace_all = false,\n }: {\n file_path: string;\n old_string: string;\n new_string: string;\n replace_all?: boolean;\n }): string {\n if (!path.isAbsolute(file_path)) {\n throw new Error(`file_path must be an absolute path, got: ${file_path}`);\n }\n\n if (!fs.existsSync(file_path)) {\n throw new Error(`File not found: ${file_path}`);\n }\n\n if (old_string === new_string) {\n throw new Error('old_string and new_string must be different');\n }\n\n const content = fs.readFileSync(file_path, 'utf-8');\n const occurrences = content.split(old_string).length - 1;\n\n if (occurrences === 0) {\n throw new Error(`old_string not found in file: ${file_path}`);\n }\n\n if (!replace_all && occurrences > 1) {\n throw new Error(\n `old_string is not unique in the file (found ${occurrences} occurrences). ` +\n `Provide more surrounding context to make it unique, or use replace_all=true.`\n );\n }\n\n const newContent = replace_all\n ? content.split(old_string).join(new_string)\n : content.replace(old_string, new_string);\n\n fs.writeFileSync(file_path, newContent, 'utf-8');\n return `The file ${file_path} has been updated successfully.`;\n },\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { z } from 'zod';\n\n/**\n * Tool for fast file pattern matching across a codebase.\n * Supports glob patterns and returns results sorted by modification time.\n *\n * @returns A Tool object with a call method that performs glob matching based on the provided pattern and path.\n */\nexport function Glob() {\n /**\n * Matches files against a glob pattern starting from the given base directory.\n * @param pattern - The glob pattern to match against.\n * @param baseDir - The base directory to search from.\n * @returns An array of matched file paths.\n */\n const globMatch = (pattern: string, baseDir: string): string[] => {\n const results: string[] = [];\n const parts = pattern.split('/');\n matchParts(parts, 0, baseDir, results);\n return results;\n };\n\n /**\n * Recursively matches path parts against directory entries.\n * @param parts - The split glob pattern parts.\n * @param partIndex - The current index in the parts array.\n * @param currentDir - The current directory being traversed.\n * @param results - The accumulator array for matched file paths.\n */\n const matchParts = (\n parts: string[],\n partIndex: number,\n currentDir: string,\n results: string[]\n ): void => {\n if (partIndex >= parts.length) return;\n\n const part = parts[partIndex];\n const isLast = partIndex === parts.length - 1;\n\n if (part === '**') {\n if (isLast) {\n collectAll(currentDir, results);\n } else {\n matchParts(parts, partIndex + 1, currentDir, results);\n try {\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const subDir = path.join(currentDir, entry.name);\n matchParts(parts, partIndex, subDir, results);\n }\n }\n } catch {\n // skip unreadable dirs\n }\n }\n } else {\n const regex = globPartToRegex(part);\n try {\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n if (regex.test(entry.name)) {\n const fullPath = path.join(currentDir, entry.name);\n if (isLast) {\n if (entry.isFile()) results.push(fullPath);\n } else if (entry.isDirectory()) {\n matchParts(parts, partIndex + 1, fullPath, results);\n }\n }\n }\n } catch {\n // skip unreadable dirs\n }\n }\n };\n\n /**\n * Recursively collects all files under a directory.\n * @param dir - The directory to collect files from.\n * @param results - The accumulator array for collected file paths.\n */\n const collectAll = (dir: string, results: string[]): void => {\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isFile()) {\n results.push(fullPath);\n } else if (entry.isDirectory()) {\n collectAll(fullPath, results);\n }\n }\n } catch {\n // skip unreadable dirs\n }\n };\n\n /**\n * Converts a single glob pattern part to a RegExp.\n * @param part - The glob pattern part to convert.\n * @returns A RegExp that matches the pattern part.\n */\n const globPartToRegex = (part: string): RegExp => {\n const escaped = part\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.');\n return new RegExp(`^${escaped}$`);\n };\n\n return {\n name: 'Glob',\n description: `Fast file pattern matching tool that works with any codebase size.\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\"\n- Returns matching file paths sorted by modification time\n- Use this tool when you need to find files by name patterns\n- When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the Agent tool instead\n- You can call multiple tools in a single response. It is always better to speculatively perform multiple searches in parallel if they are potentially useful.`,\n inputSchema: z.object({\n pattern: z.string().describe('The glob pattern to match files against'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. If not specified, the current working directory will be used. IMPORTANT: Omit this field to use the default directory. DO NOT enter \"undefined\" or \"null\" - simply omit it for the default behavior. Must be a valid directory path if provided.'\n ),\n }),\n requireUserConfirm: true,\n\n /**\n * Finds files matching a glob pattern under the given directory.\n *\n * @param root0 - The parameters object\n * @param root0.pattern - The glob pattern to match files against\n * @param root0.path - The base directory to search in; defaults to cwd\n * @returns A newline-separated list of matching file paths sorted by modification time,\n * or a no-matches message if nothing is found\n * @throws If the base directory does not exist\n */\n call({ pattern, path: searchPath }: { pattern: string; path?: string }): string {\n const baseDir = searchPath ? searchPath : process.cwd();\n\n if (!fs.existsSync(baseDir)) {\n throw new Error(`Directory not found: ${baseDir}`);\n }\n\n const matches = globMatch(pattern, baseDir);\n\n matches.sort((a, b) => {\n const statA = fs.statSync(a);\n const statB = fs.statSync(b);\n return statB.mtimeMs - statA.mtimeMs;\n });\n\n if (matches.length === 0) {\n return `No files found matching pattern: ${pattern}`;\n }\n\n return matches.join('\\n');\n },\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { z } from 'zod';\n\ntype OutputMode = 'content' | 'files_with_matches' | 'count';\n\nconst TYPE_EXTENSIONS: Record<string, string[]> = {\n js: ['.js', '.mjs', '.cjs'],\n ts: ['.ts', '.mts', '.cts'],\n tsx: ['.tsx'],\n jsx: ['.jsx'],\n py: ['.py'],\n rust: ['.rs'],\n go: ['.go'],\n java: ['.java'],\n cpp: ['.cpp', '.cc', '.cxx', '.h', '.hpp'],\n c: ['.c', '.h'],\n css: ['.css'],\n html: ['.html', '.htm'],\n json: ['.json'],\n md: ['.md', '.markdown'],\n yaml: ['.yaml', '.yml'],\n toml: ['.toml'],\n sh: ['.sh', '.bash'],\n};\n\n/**\n * Tool for searching file contents using regular expressions.\n * Supports multiple output modes, file type filtering, and multiline matching.\n *\n * @returns A Tool object for performing regex searches across files, with a call method that executes the search and returns results based on the specified output mode.\n */\nexport function Grep() {\n /**\n * Collects all files under a base directory, optionally filtered by glob or type.\n * @param baseDir - The base directory to search from.\n * @param glob - Optional glob pattern to filter files by name.\n * @param type - Optional file type key to filter by extension.\n * @returns An array of matching file paths.\n */\n const collectFiles = (baseDir: string, glob?: string, type?: string): string[] => {\n const results: string[] = [];\n const extensions = type ? TYPE_EXTENSIONS[type] : undefined;\n\n const walk = (dir: string): void => {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;\n\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.isFile()) {\n if (extensions) {\n const ext = path.extname(entry.name);\n if (extensions.includes(ext)) results.push(fullPath);\n } else if (glob) {\n if (matchGlob(glob, entry.name)) results.push(fullPath);\n } else {\n results.push(fullPath);\n }\n }\n }\n };\n\n if (fs.existsSync(baseDir) && fs.statSync(baseDir).isFile()) {\n results.push(baseDir);\n } else {\n walk(baseDir);\n }\n\n return results;\n };\n\n /**\n * Tests whether a filename matches a glob pattern.\n * @param pattern - The glob pattern to match against.\n * @param filename - The filename to test.\n * @returns True if the filename matches the pattern, false otherwise.\n */\n const matchGlob = (pattern: string, filename: string): boolean => {\n const braceMatch = pattern.match(/\\*\\.\\\\{(.+)\\\\}/);\n if (braceMatch) {\n const exts = braceMatch[1].split(',').map(e => `.${e.trim()}`);\n return exts.includes(path.extname(filename));\n }\n const escaped = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.');\n return new RegExp(`^${escaped}$`).test(filename);\n };\n\n return {\n name: 'Grep',\n description: `A powerful search tool built on regular expressions.\n\nUsage:\n- ALWAYS use Grep for search tasks. NEVER invoke \\`grep\\` or \\`rg\\` as a Bash command. The Grep tool has been optimized for correct permissions and access.\n- Supports full regex syntax (e.g., \"log.*Error\", \"function\\\\s+\\\\w+\")\n- Filter files with glob parameter (e.g., \"*.js\", \"**/*.tsx\") or type parameter (e.g., \"js\", \"py\", \"rust\")\n- Output modes: \"content\" shows matching lines, \"files_with_matches\" shows only file paths (default), \"count\" shows match counts\n- Use Task tool for open-ended searches requiring multiple rounds\n- Pattern syntax: Uses ripgrep-style regex - literal braces need escaping (use \\`interface\\\\{\\\\}\\` to find \\`interface{}\\` in Go code)\n- Multiline matching: By default patterns match within single lines only. For cross-line patterns, use \\`multiline: true\\``,\n inputSchema: z.object({\n pattern: z\n .string()\n .describe('The regular expression pattern to search for in file contents'),\n path: z\n .string()\n .optional()\n .describe('File or directory to search in. Defaults to current working directory.'),\n glob: z\n .string()\n .optional()\n .describe('Glob pattern to filter files (e.g. \"*.js\", \"*.{ts,tsx}\")'),\n type: z\n .string()\n .optional()\n .describe(\n 'File type to search (e.g., \"js\", \"ts\", \"py\"). More efficient than glob for standard file types.'\n ),\n output_mode: z\n .enum(['content', 'files_with_matches', 'count'])\n .optional()\n .describe(\n 'Output mode: \"content\" | \"files_with_matches\" | \"count\". Defaults to \"files_with_matches\".'\n ),\n multiline: z\n .boolean()\n .optional()\n .describe(\n 'Enable multiline mode where . matches newlines and patterns can span lines. Default: false.'\n ),\n case_insensitive: z\n .boolean()\n .optional()\n .describe('Case insensitive search. Default: false.'),\n context: z\n .number()\n .int()\n .optional()\n .describe(\n 'Number of lines to show before and after each match. Requires output_mode: \"content\".'\n ),\n head_limit: z\n .number()\n .int()\n .optional()\n .describe('Limit output to first N lines/entries.'),\n }),\n requireUserConfirm: true,\n\n /**\n * Searches files for a regex pattern and returns results in the specified output mode.\n *\n * @param root0 - The parameters object\n * @param root0.pattern - The regular expression pattern to search for\n * @param root0.path - File or directory to search in; defaults to cwd\n * @param root0.glob - Glob pattern to filter which files are searched\n * @param root0.type - File type shorthand (e.g. \"ts\", \"py\") to filter files\n * @param root0.output_mode - How to format results: \"content\", \"files_with_matches\", or \"count\"\n * @param root0.multiline - Whether the pattern can span multiple lines\n * @param root0.case_insensitive - Whether the search is case-insensitive\n * @param root0.context - Number of surrounding lines to include with each match\n * @param root0.head_limit - Maximum number of result entries to return\n * @returns A newline-separated string of results, or a no-matches message\n */\n call({\n pattern,\n path: searchPath,\n glob,\n type,\n output_mode = 'files_with_matches',\n multiline = false,\n case_insensitive = false,\n context,\n head_limit,\n }: {\n pattern: string;\n path?: string;\n glob?: string;\n type?: string;\n output_mode?: OutputMode;\n multiline?: boolean;\n case_insensitive?: boolean;\n context?: number;\n head_limit?: number;\n }): string {\n const baseDir = searchPath ? searchPath : process.cwd();\n\n let flags = multiline ? 'gms' : 'gm';\n if (case_insensitive) flags += 'i';\n\n const regex = new RegExp(pattern, flags);\n const files = collectFiles(baseDir, glob, type);\n\n if (files.length === 0) {\n return 'No files found to search.';\n }\n\n const results: string[] = [];\n\n for (const file of files) {\n try {\n const content = fs.readFileSync(file, 'utf-8');\n\n if (output_mode === 'files_with_matches') {\n if (regex.test(content)) {\n results.push(file);\n }\n regex.lastIndex = 0;\n } else if (output_mode === 'count') {\n const matches = content.match(regex);\n if (matches) {\n results.push(`${file}: ${matches.length}`);\n }\n regex.lastIndex = 0;\n } else if (output_mode === 'content') {\n const lines = content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const lineRegex = new RegExp(pattern, case_insensitive ? 'i' : '');\n if (lineRegex.test(lines[i])) {\n const start = context !== undefined ? Math.max(0, i - context) : i;\n const end =\n context !== undefined\n ? Math.min(lines.length - 1, i + context)\n : i;\n for (let j = start; j <= end; j++) {\n results.push(`${file}:${j + 1}:${lines[j]}`);\n }\n }\n }\n }\n } catch {\n // skip unreadable files\n }\n }\n\n if (results.length === 0) {\n return `No matches found for pattern: ${pattern}`;\n }\n\n const output = head_limit !== undefined ? results.slice(0, head_limit) : results;\n return output.join('\\n');\n },\n };\n}\n","import { z } from 'zod';\n\nimport { createToolResponse, ToolResponse } from './response';\n\n// Task type definitions\nexport type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'deleted';\n\nexport interface Task {\n id: string;\n subject: string;\n description: string;\n status: TaskStatus;\n activeForm?: string;\n owner?: string;\n metadata?: Record<string, unknown>;\n blocks: string[];\n blockedBy: string[];\n createdAt: string;\n updatedAt: string;\n}\n\n// Module-level storage\nconst taskStore = new Map<string, Task>();\nlet nextId = 1;\n\n/**\n * Generate a unique task ID\n * @returns A unique task ID as a string\n */\nfunction generateId(): string {\n return String(nextId++);\n}\n\n/**\n * Reset task store for testing purposes\n * @internal\n */\nexport function _resetTaskStore(): void {\n taskStore.clear();\n nextId = 1;\n}\n\n/**\n * Tool for creating tasks\n * @returns A Tool object for creating tasks\n */\nexport function TaskCreate() {\n return {\n name: 'TaskCreate',\n description: `Use this tool to create a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.\nIt also helps the user understand the progress of the task and overall progress of their requests.\n\n## When to Use This Tool\n\nUse this tool proactively in these scenarios:\n\n- Complex multi-step tasks - When a task requires 3 or more distinct steps or actions\n- Non-trivial and complex tasks - Tasks that require careful planning or multiple operations\n- User explicitly requests todo list - When the user directly asks you to use the todo list\n- User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)\n\nAll tasks are created with status 'pending'.`,\n inputSchema: z.object({\n subject: z\n .string()\n .describe(\n 'A brief, actionable title in imperative form (e.g., \"Fix authentication bug in login flow\")'\n ),\n description: z\n .string()\n .describe(\n 'Detailed description of what needs to be done, including context and acceptance criteria'\n ),\n activeForm: z\n .string()\n .optional()\n .describe(\n 'Present continuous form shown in the spinner when the task is in_progress (e.g., \"Fixing authentication bug\"). If omitted, the spinner shows the subject instead.'\n ),\n metadata: z\n .record(z.string(), z.unknown())\n .optional()\n .describe('Arbitrary metadata to attach to the task'),\n }),\n requireUserConfirm: false,\n\n call({\n subject,\n description,\n activeForm,\n metadata,\n }: {\n subject: string;\n description: string;\n activeForm?: string;\n metadata?: Record<string, unknown>;\n }): ToolResponse {\n const id = generateId();\n const now = new Date().toISOString();\n\n const task: Task = {\n id,\n subject,\n description,\n status: 'pending',\n activeForm,\n metadata,\n blocks: [],\n blockedBy: [],\n createdAt: now,\n updatedAt: now,\n };\n\n taskStore.set(id, task);\n\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `Task #${id} created successfully: ${subject}`,\n },\n ],\n state: 'success',\n });\n },\n };\n}\n\n/**\n * Tool for updating tasks\n * @returns A Tool object for updating tasks\n */\nexport function TaskUpdate() {\n return {\n name: 'TaskUpdate',\n description: `Use this tool to update a task in the task list.\n\n## When to Use This Tool\n\n**Mark tasks as resolved:**\n- When you have completed the work described in a task\n- When a task is no longer needed or has been superseded\n- IMPORTANT: Always mark your assigned tasks as resolved when you finish them\n\n**Delete tasks:**\n- When a task is no longer relevant or was created in error\n- Setting status to 'deleted' permanently removes the task\n\n**Update task details:**\n- When requirements change or become clearer\n- When establishing dependencies between tasks`,\n inputSchema: z.object({\n taskId: z.string().describe('The ID of the task to update'),\n status: z\n .enum(['pending', 'in_progress', 'completed', 'deleted'])\n .optional()\n .describe('New status for the task'),\n subject: z.string().optional().describe('New subject for the task'),\n description: z.string().optional().describe('New description for the task'),\n activeForm: z\n .string()\n .optional()\n .describe(\n 'Present continuous form shown in spinner when in_progress (e.g., \"Running tests\")'\n ),\n owner: z.string().optional().describe('New owner for the task'),\n metadata: z\n .record(z.string(), z.unknown())\n .optional()\n .describe('Metadata keys to merge into the task. Set a key to null to delete it.'),\n addBlocks: z.array(z.string()).optional().describe('Task IDs that this task blocks'),\n addBlockedBy: z.array(z.string()).optional().describe('Task IDs that block this task'),\n }),\n requireUserConfirm: false,\n\n call({\n taskId,\n status,\n subject,\n description,\n activeForm,\n owner,\n metadata,\n addBlocks,\n addBlockedBy,\n }: {\n taskId: string;\n status?: TaskStatus;\n subject?: string;\n description?: string;\n activeForm?: string;\n owner?: string;\n metadata?: Record<string, unknown>;\n addBlocks?: string[];\n addBlockedBy?: string[];\n }): ToolResponse {\n const task = taskStore.get(taskId);\n if (!task) {\n throw new Error(`Task not found: ${taskId}`);\n }\n\n // Validate dependencies exist\n if (addBlocks) {\n for (const depId of addBlocks) {\n if (!taskStore.has(depId)) {\n throw new Error(`Cannot add dependency: task ${depId} does not exist`);\n }\n }\n }\n if (addBlockedBy) {\n for (const depId of addBlockedBy) {\n if (!taskStore.has(depId)) {\n throw new Error(`Cannot add dependency: task ${depId} does not exist`);\n }\n }\n }\n\n // Update fields\n if (subject !== undefined) task.subject = subject;\n if (description !== undefined) task.description = description;\n if (activeForm !== undefined) task.activeForm = activeForm;\n if (owner !== undefined) task.owner = owner;\n\n // Merge metadata\n if (metadata !== undefined) {\n if (!task.metadata) {\n task.metadata = {};\n }\n for (const [key, value] of Object.entries(metadata)) {\n if (value === null) {\n delete task.metadata[key];\n } else {\n task.metadata[key] = value;\n }\n }\n }\n\n // Add dependencies with deduplication\n if (addBlocks) {\n task.blocks = [...new Set([...task.blocks, ...addBlocks])];\n }\n if (addBlockedBy) {\n task.blockedBy = [...new Set([...task.blockedBy, ...addBlockedBy])];\n }\n\n task.updatedAt = new Date().toISOString();\n\n // Handle status change\n if (status !== undefined) {\n if (status === 'deleted') {\n taskStore.delete(taskId);\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `Task #${taskId} deleted successfully`,\n },\n ],\n state: 'success',\n });\n }\n task.status = status;\n }\n\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `Task #${taskId} updated successfully`,\n },\n ],\n state: 'success',\n });\n },\n };\n}\n\n/**\n * Tool for retrieving a single task\n * @returns A Tool object for retrieving a task by ID\n */\nexport function TaskGet() {\n return {\n name: 'TaskGet',\n description: `Use this tool to retrieve a task by its ID from the task list.\n\n## When to Use This Tool\n\n- When you need the full description and context before starting work on a task\n- To understand task dependencies (what it blocks, what blocks it)\n- After being assigned a task, to get complete requirements`,\n inputSchema: z.object({\n taskId: z.string().describe('The ID of the task to retrieve'),\n }),\n requireUserConfirm: false,\n\n call({ taskId }: { taskId: string }): ToolResponse {\n const task = taskStore.get(taskId);\n if (!task) {\n throw new Error(`Task not found: ${taskId}`);\n }\n\n let text = `Task #${task.id}: ${task.subject}\\n`;\n text += `Status: ${task.status}\\n`;\n text += `Description: ${task.description}\\n`;\n\n if (task.activeForm) {\n text += `Active Form: ${task.activeForm}\\n`;\n }\n if (task.owner) {\n text += `Owner: ${task.owner}\\n`;\n }\n if (task.blocks.length > 0) {\n text += `Blocks: ${task.blocks.join(', ')}\\n`;\n }\n if (task.blockedBy.length > 0) {\n text += `Blocked By: ${task.blockedBy.join(', ')}\\n`;\n }\n if (task.metadata && Object.keys(task.metadata).length > 0) {\n text += `Metadata: ${JSON.stringify(task.metadata, null, 2)}\\n`;\n }\n text += `Created: ${task.createdAt}\\n`;\n text += `Updated: ${task.updatedAt}`;\n\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text,\n },\n ],\n state: 'success',\n });\n },\n };\n}\n\n/**\n * Tool for listing all active tasks\n * @returns A Tool object for listing all active tasks\n */\nexport function TaskList() {\n return {\n name: 'TaskList',\n description: `Use this tool to list all tasks in the task list.\n\n## When to Use This Tool\n\n- To see what tasks are available to work on (status: 'pending', no owner, not blocked)\n- To check overall progress on the project\n- To find tasks that are blocked and need dependencies resolved\n- After completing a task, to check for newly unblocked work or claim the next available task`,\n inputSchema: z.object({}),\n requireUserConfirm: false,\n\n call(): ToolResponse {\n // Filter to only pending and in_progress tasks\n const activeTasks = Array.from(taskStore.values())\n .filter(task => task.status === 'pending' || task.status === 'in_progress')\n .sort((a, b) => Number(a.id) - Number(b.id));\n\n if (activeTasks.length === 0) {\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: 'No active tasks found',\n },\n ],\n state: 'success',\n });\n }\n\n const lines = activeTasks.map(task => {\n let line = `#${task.id} [${task.status}] ${task.subject}`;\n if (task.blockedBy.length > 0) {\n line += ` (blocked by: #${task.blockedBy.join(', #')})`;\n }\n return line;\n });\n\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: lines.join('\\n'),\n },\n ],\n state: 'success',\n });\n },\n };\n}\n"],"mappings":"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","names":["import_zod","tool","matter","import_zod","import_zod","import_zod","import_zod","import_zod","import_zod","import_zod"]}
|
|
1
|
+
{"version":3,"sources":["../../src/agent/index.ts","../../src/agent/agent.ts","../../src/message/message.ts","../../src/tool/response.ts","../../src/tool/toolkit.ts","../../src/_utils/common.ts","../../src/tool/bash.ts","../../src/tool/read.ts","../../src/tool/write.ts","../../src/tool/edit.ts","../../src/tool/glob.ts","../../src/tool/grep.ts","../../src/tool/task.ts"],"sourcesContent":["export { Agent, AgentOptions, CompressionConfig } from './agent';\nexport { ReplyOptions, ActingOptions, ReasoningOptions } from './interfaces';\n","import { z } from 'zod';\n\nimport {\n ContentBlock,\n createMsg,\n getContentBlocks,\n Msg,\n ToolCallBlock,\n ToolResultBlock,\n} from '../message';\nimport { ChatModelBase, ChatResponse, ChatUsage } from '../model';\nimport { Toolkit, ToolResponse } from '../tool';\nimport { ActingOptions, ObserveOptions, ReasoningOptions, ReplyOptions } from './interfaces';\nimport {\n AgentEvent,\n EventType,\n ModelCallEndEvent,\n ModelCallStartEvent,\n ReplyEndEvent,\n ReplyStartEvent,\n TextBlockDeltaEvent,\n TextBlockEndEvent,\n TextBlockStartEvent,\n ThinkingBlockDeltaEvent,\n ThinkingBlockEndEvent,\n ThinkingBlockStartEvent,\n ToolCallDeltaEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n ToolResultDataDeltaEvent,\n ToolResultEndEvent,\n ToolResultStartEvent,\n ToolResultTextDeltaEvent,\n} from '../event';\nimport { StorageBase } from '../storage';\n\nconst DEFAULT_COMPRESSION_PROMPT =\n '<system-hint>You have been working on the task described above but have not yet completed it. ' +\n 'Now write a continuation summary that will allow you to resume work efficiently in a future context window ' +\n 'where the conversation history will be replaced with this summary. ' +\n 'Your summary should be structured, concise, and actionable.</system-hint>';\n\nconst DEFAULT_SUMMARY_SCHEMA = z.object({\n task_overview: z\n .string()\n .max(300)\n .describe(\n \"The user\\'s core request and success criteria. Any clarifications or constraints they specified\"\n ),\n current_state: z\n .string()\n .max(300)\n .describe(\n 'What has been completed so far. File created, modified, or analyzed (with paths if relevant). Key outputs or artifacts produced.'\n ),\n important_discoveries: z\n .string()\n .max(300)\n .describe(\n \"Technical constraints or requirements uncovered. Decisions made and their rationale. Errors encountered and how they were resolved. What approaches were tried that didn\\'t work (and why)\"\n ),\n next_steps: z\n .string()\n .max(200)\n .describe(\n 'Specific actions needed to complete the task. Any blockers or open questions to resolve. Priority order if multiple steps remain'\n ),\n context_to_preserve: z\n .string()\n .max(300)\n .describe(\n \"User preferences or style requirements. Domain-specific details that aren\\'t obvious. Any promises made to the user\"\n ),\n});\n\nexport interface CompressionConfig {\n /**\n * Whether to enable memory compression.\n */\n enabled: boolean;\n /**\n * The token count threshold to trigger memory compression.\n */\n triggerThreshold: number;\n /**\n * The function to count the tokens of the messages in memory. If not provided, a heuristic token counting method will be used by default.\n */\n tokenCountFunc?: (msgs: Msg[]) => number;\n /**\n * The chat model used for compression. If not provided, the same model as the agent will be used by default.\n */\n compressionModel?: ChatModelBase;\n /**\n * The prompt template for memory compression. It should be designed to instruct the model to compress the input messages into a concise summary while preserving important information. If not provided, a default prompt will be used.\n */\n compressionPrompt?: string;\n /**\n * The JSON schema for the compressed summary. The model will be guided to compress the memory into a structured summary following this schema. If not provided, a default schema with a single text field will be used.\n */\n summarySchema?: z.ZodObject;\n /**\n * The number of recent messages to keep in the context without compression.\n */\n keepRecent?: number;\n}\n\nexport interface AgentOptions {\n name: string;\n sysPrompt: string;\n model: ChatModelBase;\n maxIters?: number;\n toolkit?: Toolkit;\n storage?: StorageBase;\n compressionConfig?: CompressionConfig;\n}\n\n/**\n * The unified agent class in AgentScope library.\n */\nexport class Agent {\n // Agent configuration\n name: string;\n model: ChatModelBase;\n maxIters: number;\n toolkit: Toolkit;\n storage?: StorageBase;\n context: Msg[];\n private _loaded: boolean;\n private _sysPrompt: string;\n compressionConfig?: CompressionConfig;\n\n // Agent state\n replyId: string;\n curIter: number;\n confirmedToolCallIds: string[];\n curSummary: string;\n\n /**\n * Initialize an agent instance with the given parameters.\n *\n * @param options - The agent configuration options.\n * @param options.name - The name of the agent.\n * @param options.sysPrompt - The system prompt for the agent.\n * @param options.model - The chat model to use.\n * @param options.maxIters - Maximum iterations (default: 5).\n * @param options.memory - Memory storage (default: InMemoryMemory).\n * @param options.toolkit - Toolkit for tools (default: Toolkit).\n */\n constructor(options: AgentOptions) {\n // Check maxIters mast be greater than 0\n if (options.maxIters !== undefined && options.maxIters <= 0) {\n throw new Error('maxIters must be greater than 0');\n }\n\n this.name = options.name;\n this._sysPrompt = options.sysPrompt;\n this.model = options.model;\n this.maxIters = options.maxIters ?? 20;\n this.context = [];\n this.toolkit = options.toolkit ?? new Toolkit();\n this.storage = options.storage;\n this.compressionConfig = options.compressionConfig;\n\n // Record if the agent state has been loaded from storage to avoid repeat loading\n this._loaded = false;\n\n // The states that tracks the current reply session\n this.replyId = '';\n this.curIter = 0;\n this.confirmedToolCallIds = [];\n this.curSummary = '';\n }\n\n /**\n * Load the state from the storage if storage is provided and not loaded yet.\n */\n async loadState() {\n if (this._loaded || !this.storage) return;\n const { context, metadata } = await this.storage.loadAgentState({ agentId: this.name });\n console.log(`Load state for agent \"${this.name}\" from storage:`, { context, metadata });\n this.context = context;\n this.replyId = (metadata.replyId as string) || '';\n this.curIter = (metadata.curIter as number) || 0;\n this.curSummary = (metadata.curSummary as string) || '';\n this._loaded = true;\n }\n\n /**\n * Save the state of the current reply session to storage if storage is provided.\n */\n async saveState() {\n if (!this.storage) return;\n await this.storage.saveAgentState({\n agentId: this.name,\n context: this.context,\n metadata: {\n replyId: this.replyId,\n curIter: this.curIter,\n curSummary: this.curSummary,\n },\n });\n }\n\n /**\n * Get the system prompt of the agent.\n *\n * @returns The system prompt string.\n */\n public get sysPrompt() {\n const skillsPrompt = this.toolkit.getSkillsPrompt();\n if (skillsPrompt.length > 0) {\n return this._sysPrompt + '\\n\\n' + skillsPrompt;\n }\n return this._sysPrompt;\n }\n\n /**\n * Reply to the given message and stream agent events as they are generated.\n *\n * @param options - The reply options containing the incoming message.\n * @returns An async generator that yields agent events and resolves to the final reply message.\n */\n public async *replyStream(options: ReplyOptions): AsyncGenerator<AgentEvent, Msg> {\n // Load the agent state from storage if not loaded yet\n await this.loadState();\n try {\n // Yield the reply stream\n return yield* this._reply(options);\n } finally {\n await this.saveState();\n }\n }\n\n /**\n * Reply to the given message, consuming all streamed events internally.\n *\n * @param options - The reply options containing the incoming message.\n * @param options.msgs - The incoming message(s) to reply to.\n * @returns A promise that resolves to the final reply message.\n */\n public async reply(options: ReplyOptions): Promise<Msg> {\n // Load the agent state from storage if not loaded yet\n await this.loadState();\n try {\n const res = this._reply(options);\n while (true) {\n const { value, done } = await res.next();\n if (done) {\n return value as Msg;\n }\n }\n } finally {\n await this.saveState();\n }\n }\n\n /**\n * Save the given content blocks into the context as a new block in the last assistant message,\n * or create a new assistant message if the last message is not from the assistant or has a different name.\n * @param blocks\n * @param usage\n */\n protected _saveToContext(blocks: ContentBlock[], usage?: ChatUsage): void {\n const msgUsage: Msg['usage'] = usage\n ? { input_tokens: usage.inputTokens, output_tokens: usage.outputTokens }\n : undefined;\n const lastMsg = this.context.at(-1);\n if (this.context.length === 0) {\n this.context.push(\n createMsg({\n name: this.name,\n content: blocks,\n role: 'assistant',\n usage: msgUsage,\n })\n );\n } else if (lastMsg && lastMsg.role === 'assistant' && lastMsg.name === this.name) {\n lastMsg.content.push(...blocks);\n if (msgUsage) {\n if (!lastMsg.usage) {\n lastMsg.usage = {\n input_tokens: 0,\n output_tokens: 0,\n };\n }\n lastMsg.usage.input_tokens = lastMsg.usage.input_tokens + msgUsage.input_tokens;\n lastMsg.usage.output_tokens = lastMsg.usage.output_tokens + msgUsage.output_tokens;\n }\n } else {\n this.context.push(\n createMsg({\n name: this.name,\n content: blocks,\n role: 'assistant',\n usage: msgUsage,\n })\n );\n }\n }\n\n /**\n * Get the pending tool calls that have no results yet in the context.\n * @returns An array of pending `ToolCallBlock`s that are waiting for execution results.\n */\n protected _getPendingToolCalls(): ToolCallBlock[] {\n if (this.context.length === 0) return [];\n\n const lastMsg = this.context.at(-1);\n if (!lastMsg) return [];\n if (lastMsg.role === 'assistant') {\n const toolCalls = getContentBlocks(lastMsg, 'tool_call');\n const toolResults = getContentBlocks(lastMsg, 'tool_result');\n return toolCalls.filter(toolCall => !toolResults.some(tr => tr.id === toolCall.id));\n }\n return [];\n }\n\n /**\n * Get the awaiting tool calls that require user confirmation or external execution.\n * @returns An array of `ToolCallBlock`s that are waiting for user confirmation or external execution.\n */\n protected _getAwaitingToolCalls(): {\n awaitingType?: EventType.REQUIRE_USER_CONFIRM | EventType.REQUIRE_EXTERNAL_EXECUTION;\n expectedEventType?: EventType.USER_CONFIRM_RESULT | EventType.EXTERNAL_EXECUTION_RESULT;\n awaitingToolCalls: ToolCallBlock[];\n preToolCalls: ToolCallBlock[];\n } {\n // If there is awaiting tool calls within the last assistant message in the context\n const pendingToolCalls = this._getPendingToolCalls();\n\n // The tool calls that should be executed before yield the (maybe have) user-confirm or external-execution event\n const preToolCalls: ToolCallBlock[] = [];\n for (const [index, toolCall] of pendingToolCalls.entries()) {\n if (\n this.toolkit.requireUserConfirm(toolCall.name) &&\n !this.confirmedToolCallIds.includes(toolCall.id)\n ) {\n toolCall.state = 'asking';\n // Find the continuous tool calls that require user confirmation\n let i = index + 1;\n for (; i < pendingToolCalls.length; i++) {\n const nextToolCall = pendingToolCalls[i];\n if (\n !this.toolkit.requireUserConfirm(nextToolCall.name) ||\n this.confirmedToolCallIds.includes(nextToolCall.id)\n )\n break;\n nextToolCall.state = 'asking';\n }\n return {\n awaitingType: EventType.REQUIRE_USER_CONFIRM,\n expectedEventType: EventType.USER_CONFIRM_RESULT,\n awaitingToolCalls: pendingToolCalls.slice(index, i),\n preToolCalls,\n };\n }\n\n if (this.toolkit.requireExternalExecution(toolCall.name)) {\n // Find the continuous tool calls that require external execution\n let i = index + 1;\n for (; i < pendingToolCalls.length; i++) {\n const nextToolCall = pendingToolCalls[i];\n if (!this.toolkit.requireExternalExecution(nextToolCall.name)) break;\n }\n return {\n awaitingType: EventType.REQUIRE_EXTERNAL_EXECUTION,\n expectedEventType: EventType.EXTERNAL_EXECUTION_RESULT,\n awaitingToolCalls: pendingToolCalls.slice(index, i),\n preToolCalls,\n };\n }\n\n preToolCalls.push(toolCall);\n }\n return { awaitingToolCalls: [], preToolCalls };\n }\n\n /**\n * Core reply logic without middlewares. Observes the incoming message, runs\n * reasoning/acting iterations up to `maxIters`, and returns the final response.\n *\n * @param options - The reply options containing the incoming message.\n * @returns An async generator that yields agent events and resolves to the final reply message.\n */\n protected async *_reply(options?: ReplyOptions): AsyncGenerator<AgentEvent, Msg> {\n const { expectedEventType } = this._getAwaitingToolCalls();\n if (expectedEventType) {\n // Checking\n if (!options || !options.event || options.event.type !== expectedEventType) {\n throw new Error(\n `Agent is awaiting for '${expectedEventType}' confirmation, but received event of type '${options?.event?.type ?? 'none'}'.`\n );\n }\n\n // handle the external execution result event\n const event = options.event;\n if (event.type === EventType.EXTERNAL_EXECUTION_RESULT) {\n // Record the tool results into context and go on acting\n this._saveToContext(event.execution_results);\n } else if (event.type === EventType.USER_CONFIRM_RESULT) {\n for (const result of event.confirm_results) {\n if (result.confirmed) {\n this.confirmedToolCallIds.push(result.tool_call.id);\n } else {\n // If user rejected, add a rejection result and handle the pending tool calls\n const rejectionRes = `<system-info>**Note** the user rejected the execution of tool \"${result.tool_call.name}\"!</system-info>`;\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_START,\n reply_id: this.replyId,\n tool_call_id: result.tool_call.id,\n } as ToolResultStartEvent;\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_TEXT_DELTA,\n reply_id: this.replyId,\n tool_call_id: result.tool_call.id,\n delta: rejectionRes,\n } as ToolResultTextDeltaEvent;\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_END,\n reply_id: this.replyId,\n tool_call_id: result.tool_call.id,\n state: 'interrupted',\n } as ToolResultEndEvent;\n this._saveToContext([\n {\n type: 'tool_result',\n id: result.tool_call.id,\n name: result.tool_call.name,\n output: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `<system-info>**Note** the user rejected the execution of tool \"${result.tool_call.name}\"!</system-info>`,\n },\n ],\n state: 'interrupted',\n },\n ]);\n }\n }\n // Update tool call states based on confirm results\n const confirmedIds = new Set(\n event.confirm_results.filter(r => r.confirmed).map(r => r.tool_call.id)\n );\n const deniedIds = new Set(\n event.confirm_results.filter(r => !r.confirmed).map(r => r.tool_call.id)\n );\n this.context.at(-1)?.content.forEach(content => {\n if (content.type === 'tool_call') {\n if (confirmedIds.has(content.id)) {\n content.state = 'allowed';\n } else if (deniedIds.has(content.id)) {\n content.state = 'finished';\n }\n }\n });\n }\n } else {\n // The normal reply flow starts without any external event\n this.curIter = 0;\n this.replyId = crypto.randomUUID();\n this.confirmedToolCallIds = [];\n\n // Yield the run started event\n yield {\n id: crypto.randomUUID(),\n type: EventType.REPLY_START,\n created_at: new Date().toISOString(),\n session_id: '',\n reply_id: this.replyId,\n name: this.name,\n role: 'assistant',\n } as ReplyStartEvent;\n }\n\n // Store the incoming message into memory\n if (Array.isArray(options?.msgs)) {\n // await this.memory.add(options.msg);\n this.context.push(...options.msgs);\n } else if (options?.msgs) {\n this.context.push(options.msgs);\n }\n\n while (this.curIter < this.maxIters) {\n const pendingToolCalls = this._getPendingToolCalls();\n if (pendingToolCalls.length === 0) {\n await this.compressMemoryIfNeeded();\n const reasoningResponse = yield* this._reasoning({ toolChoice: 'auto' });\n this._saveToContext(reasoningResponse.content, reasoningResponse.usage);\n }\n\n // Extract the awaiting tool calls and those should be executed before yielding human-in-the-loop events\n const { awaitingType, awaitingToolCalls, preToolCalls } = this._getAwaitingToolCalls();\n // Execute the pre-tool calls before yielding the user-confirm or external-execution event if there is any\n for (const toolCall of preToolCalls) {\n const actingContent = yield* this._acting({ toolCall });\n this._saveToContext([actingContent]);\n // Consume the confirmation after execution\n this.confirmedToolCallIds = this.confirmedToolCallIds.filter(\n id => id !== toolCall.id\n );\n }\n\n // yield the user-confirm or external-execution event if there is any awaiting tool calls\n if (awaitingType) {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: awaitingType,\n reply_id: this.replyId,\n tool_calls: awaitingToolCalls,\n };\n\n return createMsg({\n name: this.name,\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text:\n awaitingType === EventType.REQUIRE_USER_CONFIRM\n ? 'Waiting for user confirmation ...'\n : 'Waiting for external execution ...',\n },\n ],\n role: 'assistant',\n });\n }\n\n // Break the loop if there is no tool call in the reasoning message\n if (preToolCalls.length === 0) break;\n\n this.curIter += 1;\n }\n\n // If exceed max iterations without text output\n if (this.context.at(-1)?.content.at(-1)?.type !== 'text') {\n // Generate a final response\n const summaryResponse = yield* this._reasoning({ toolChoice: 'none' });\n this._saveToContext(summaryResponse.content, summaryResponse.usage);\n }\n\n // Yield the run finished event\n yield {\n id: crypto.randomUUID(),\n type: EventType.REPLY_END,\n created_at: new Date().toISOString(),\n session_id: '',\n reply_id: this.replyId,\n } as ReplyEndEvent;\n\n return createMsg({\n id: this.replyId,\n name: this.name,\n // Must be a string for the final output message\n content: [this.context.at(-1)!.content.at(-1)!],\n role: 'assistant',\n });\n }\n\n /**\n * Core reasoning logic without middlewares. Calls the model with the current\n * memory and system prompt, converts the response to agent events, and saves\n * the resulting message to memory.\n *\n * @param options - The reasoning options, including tool choice strategy.\n * @returns An async generator that yields agent events and resolves to the content blocks of the model response.\n */\n protected async *_reasoning(\n options: ReasoningOptions\n ): AsyncGenerator<AgentEvent, ChatResponse> {\n const tools = this.toolkit.getJSONSchemas();\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.MODEL_CALL_START,\n reply_id: this.replyId,\n model_name: this.model.modelName,\n } as ModelCallStartEvent;\n const res = await this.model.call({\n messages: [\n createMsg({\n name: 'system',\n content: [{ type: 'text', text: this.sysPrompt, id: crypto.randomUUID() }],\n role: 'system',\n }),\n ...(this.curSummary\n ? [\n createMsg({\n name: 'user',\n content: [\n { type: 'text', text: this.curSummary, id: crypto.randomUUID() },\n ],\n role: 'user',\n }),\n ]\n : []),\n ...this.context,\n ],\n tools,\n toolChoice: options.toolChoice,\n });\n\n let blockIds = {\n textBlockId: null,\n thinkingBlockId: null,\n toolCallIds: [],\n } as {\n textBlockId: string | null;\n thinkingBlockId: string | null;\n toolCallIds: string[];\n };\n let completedResponse: ChatResponse;\n if (this.model.stream) {\n // Handle streaming response\n while (true) {\n const { value, done } = await (\n res as AsyncGenerator<ChatResponse, ChatResponse>\n ).next();\n if (done) {\n // The complete response is returned in the `value` when `done` is true\n completedResponse = value as ChatResponse;\n break;\n }\n const chunk = value as ChatResponse;\n yield* this.convertChatResponseToEvent(blockIds, chunk);\n }\n } else {\n // Handle non-streaming response, the res is a ChatResponse instance\n completedResponse = res as ChatResponse;\n yield* this.convertChatResponseToEvent(blockIds, res as ChatResponse);\n }\n\n // Send end events for text message, thinking message and tool calls\n if (blockIds.textBlockId) {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TEXT_BLOCK_END,\n reply_id: this.replyId,\n block_id: blockIds.textBlockId,\n } as TextBlockEndEvent;\n }\n if (blockIds.thinkingBlockId) {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.THINKING_BLOCK_END,\n reply_id: this.replyId,\n block_id: blockIds.thinkingBlockId,\n } as ThinkingBlockEndEvent;\n }\n if (blockIds.toolCallIds.length > 0) {\n for (const tool_call_id of blockIds.toolCallIds) {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_CALL_END,\n reply_id: this.replyId,\n tool_call_id,\n } as ToolCallEndEvent;\n }\n }\n\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.MODEL_CALL_END,\n reply_id: this.replyId,\n input_tokens: completedResponse.usage?.inputTokens || 0,\n output_tokens: completedResponse.usage?.outputTokens || 0,\n } as ModelCallEndEvent;\n\n return completedResponse;\n }\n\n /**\n * Core acting logic without middlewares. Executes the given tool call, streams\n * intermediate tool result events, and saves the final tool response to memory.\n *\n * @param options - The acting options containing the tool call to execute.\n * @returns An async generator that yields tool result events.\n */\n protected async *_acting(options: ActingOptions): AsyncGenerator<AgentEvent, ToolResultBlock> {\n const res = this.toolkit.callToolFunction(options.toolCall);\n\n yield {\n type: EventType.TOOL_RESULT_START,\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n reply_id: this.replyId,\n tool_call_id: options.toolCall.id,\n tool_call_name: options.toolCall.name,\n } as ToolResultStartEvent;\n\n while (true) {\n const { value, done } = await res.next();\n if (done) {\n return {\n type: 'tool_result',\n id: options.toolCall.id,\n name: options.toolCall.name,\n output: value.content,\n state: value.state,\n } as ToolResultBlock;\n }\n yield* this.convertToolResponseToEvent(options.toolCall, value);\n }\n }\n\n /**\n * Receive external observation message(s) and save them into memory.\n *\n * @param options - The observe options containing the message to store.\n * @returns A promise that resolves when the message has been saved to memory.\n */\n protected async _observe(options: ObserveOptions): Promise<void> {\n // Load the agent state from storage if not loaded yet\n await this.loadState();\n\n if (Array.isArray(options.msg)) {\n // await this.memory.add(options.msg);\n this.context.push(...options.msg);\n } else if (options.msg) {\n this.context.push(options.msg);\n }\n }\n\n /**\n * Convert a `ChatResponse` chunk into a sequence of typed agent events.\n * Tracks message IDs across calls via the mutable `responseId` object so that\n * start/content/end events are correctly correlated.\n *\n * @param responseId - Mutable object tracking IDs for the current text, thinking, and tool-call messages.\n * @param responseId.textMessageId - ID of the in-progress text message, or `null` if not yet started.\n * @param responseId.thinkingMessageId - ID of the in-progress thinking message, or `null` if not yet started.\n * @param responseId.textBlockId\n * @param responseId.thinkingBlockId\n * @param responseId.toolCallIds - List of tool-call IDs seen so far in this response.\n * @param chunk - The chat response chunk to convert.\n * @returns An async generator that yields the corresponding agent events.\n */\n protected async *convertChatResponseToEvent(\n responseId: {\n textBlockId: string | null;\n thinkingBlockId: string | null;\n toolCallIds: string[];\n },\n chunk: ChatResponse\n ): AsyncGenerator<AgentEvent> {\n for (const block of chunk.content) {\n switch (block.type) {\n case 'text':\n if (responseId.textBlockId === null) {\n // A new uuid\n responseId.textBlockId = crypto.randomUUID();\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TEXT_BLOCK_START,\n reply_id: this.replyId,\n block_id: responseId.textBlockId,\n } as TextBlockStartEvent;\n }\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TEXT_BLOCK_DELTA,\n reply_id: this.replyId,\n block_id: responseId.textBlockId,\n delta: block.text,\n } as TextBlockDeltaEvent;\n break;\n\n case 'thinking':\n if (responseId.thinkingBlockId === null) {\n responseId.thinkingBlockId = crypto.randomUUID();\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.THINKING_BLOCK_START,\n reply_id: this.replyId,\n block_id: responseId.thinkingBlockId,\n } as ThinkingBlockStartEvent;\n }\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.THINKING_BLOCK_DELTA,\n reply_id: this.replyId,\n block_id: responseId.thinkingBlockId,\n delta: block.thinking,\n } as ThinkingBlockDeltaEvent;\n break;\n\n case 'tool_call':\n if (!responseId.toolCallIds.includes(block.id)) {\n responseId.toolCallIds.push(block.id);\n yield {\n id: crypto.randomUUID(),\n type: EventType.TOOL_CALL_START,\n created_at: new Date().toISOString(),\n reply_id: this.replyId,\n tool_call_id: block.id,\n tool_call_name: block.name,\n } as ToolCallStartEvent;\n }\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_CALL_DELTA,\n delta: block.input,\n reply_id: this.replyId,\n tool_call_id: block.id,\n } as ToolCallDeltaEvent;\n }\n }\n }\n\n /**\n * Convert a `ToolResponse` into a sequence of typed agent events, followed by\n * a final `TOOL_RESULT_END` event.\n *\n * @param toolCall - The original tool-use block that triggered this response.\n * @param toolRes - The tool response containing result content blocks.\n * @returns An async generator that yields tool result events.\n */\n protected async *convertToolResponseToEvent(toolCall: ToolCallBlock, toolRes: ToolResponse) {\n for (const block of toolRes.content) {\n switch (block.type) {\n case 'text':\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_TEXT_DELTA,\n reply_id: this.replyId,\n tool_call_id: toolCall.id,\n delta: block.text,\n } as ToolResultTextDeltaEvent;\n break;\n\n case 'data':\n if (block.source.type === 'base64') {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_DATA_DELTA,\n reply_id: this.replyId,\n tool_call_id: toolCall.id,\n media_type: block.source.media_type,\n data: block.source.data,\n } as ToolResultDataDeltaEvent;\n } else if (block.source.type === 'url') {\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_DATA_DELTA,\n reply_id: this.replyId,\n tool_call_id: toolCall.id,\n media_type: block.source.media_type,\n url: block.source.url,\n } as ToolResultDataDeltaEvent;\n }\n break;\n }\n }\n yield {\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n type: EventType.TOOL_RESULT_END,\n reply_id: this.replyId,\n tool_call_id: toolCall.id,\n state: toolRes.state,\n } as ToolResultEndEvent;\n }\n\n /**\n * Convert the agent instance to a JSON-serializable object.\n * @returns An object containing the agent's name and system prompt.\n */\n public async toJSON() {\n return {\n reply_id: this.replyId,\n confirmedToolCallIds: this.confirmedToolCallIds,\n curIter: this.curIter,\n };\n }\n\n /**\n * Split the current context into two parts: one part that needs to be compressed and another part that should be reserved based on the compression configuration. The method calculates how many recent \"units\" (blocks or tool call pairs) to keep uncompressed according to the `keepRecent` setting in the compression configuration, and ensures that tool calls and their corresponding results are not separated during the split.\n * @returns An object containing the `toCompressedContext` which includes the messages that need to be compressed, and the `reservedContext` which includes the recent messages that should be kept uncompressed.\n */\n protected _splitContextForCompression() {\n let toCompressedContext: Msg[] = [];\n let reservedContext: Msg[] = [];\n\n // Calculate which messages need to be compressed\n // keepRecent specifies the number of recent \"units\" to keep uncompressed\n // A unit is either: a single block (text/thinking), or a tool_call + tool_result pair\n const keepRecent = this.compressionConfig!.keepRecent ?? 0;\n\n const nBlocks = this.context.map(msg => msg.content.length).reduce((a, b) => a + b, 0);\n const toCompressedBlockNumber = nBlocks - keepRecent > 0 ? nBlocks - keepRecent : 0;\n\n let currentCompressedBlocks = 0;\n for (const [index, msg] of this.context.entries()) {\n if (currentCompressedBlocks + msg.content.length <= toCompressedBlockNumber) {\n toCompressedContext.push(msg);\n currentCompressedBlocks += msg.content.length;\n } else {\n // The blocks that should be reserved according to the keepRecent count\n const reservedBlocks = msg.content.slice(\n toCompressedBlockNumber - currentCompressedBlocks\n );\n // Check if the reserved blocks contain an unpaired tool_call or tool_result\n const unPairedToolResultIds = new Set<string>();\n for (const block of reservedBlocks) {\n if (block.type == 'tool_call') {\n unPairedToolResultIds.add(block.id);\n } else if (block.type == 'tool_result') {\n if (unPairedToolResultIds.has(block.id)) {\n unPairedToolResultIds.delete(block.id);\n }\n }\n }\n // If there are unpaired tool calls, we need to move them to the reserved blocks\n let i = toCompressedBlockNumber - currentCompressedBlocks - 1;\n for (; i >= 0; i--) {\n const block = msg.content[i];\n if (block.type === 'tool_call' && unPairedToolResultIds.has(block.id)) {\n unPairedToolResultIds.delete(block.id);\n }\n if (unPairedToolResultIds.size === 0) break;\n }\n // All contents in this message should be reserved if i\n if (i <= 0) {\n reservedContext.push(msg);\n break;\n }\n\n // Slice the message content and push the reserved part to the compressed context\n const lastMsg = { ...msg };\n lastMsg.content = msg.content.slice(0, i);\n toCompressedContext.push(lastMsg);\n\n const reservedMsg = { ...msg };\n reservedMsg.content = msg.content.slice(i);\n reservedContext.push(reservedMsg);\n\n // The rest messages should be reserved\n reservedContext.push(...this.context.slice(index + 1));\n break;\n }\n }\n return { toCompressedContext, reservedContext };\n }\n\n /**\n * Compress the agent's memory using the specified compression model (if provided) or the original model.\n */\n protected async compressMemoryIfNeeded() {\n // The tool call and result pair must be kept or removed together\n if (!this.compressionConfig || !this.compressionConfig.enabled) return;\n\n const { toCompressedContext, reservedContext } = this._splitContextForCompression();\n\n // Compress the toCompressedContext\n if (\n toCompressedContext.length <= 0 ||\n (toCompressedContext.length === 1 && toCompressedContext.at(0)?.content.length === 1)\n )\n return;\n\n // Compute if the context exceed the threshold\n const messages = [\n createMsg({\n name: 'system',\n content: [{ type: 'text', text: this.sysPrompt, id: crypto.randomUUID() }],\n role: 'system',\n }),\n ...toCompressedContext,\n // instructions to compress the context into a summary\n createMsg({\n name: 'user',\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text:\n this.compressionConfig.compressionPrompt || DEFAULT_COMPRESSION_PROMPT,\n },\n ],\n role: 'user',\n }),\n ];\n\n const nTokens = await this.model.countTokens({\n messages,\n tools: this.toolkit.getJSONSchemas(),\n });\n console.debug(`[AGENT ${this.name}] Current context token count: ${nTokens}.`);\n if (nTokens <= this.compressionConfig.triggerThreshold) return;\n\n console.log(\n `[AGENT ${this.name}] Compressing memory with ${toCompressedContext.length} messages.`\n );\n // Generate the summary structured content\n const res = await this.model.callStructured({\n messages: [\n createMsg({\n name: 'system',\n content: [{ type: 'text', text: this.sysPrompt, id: crypto.randomUUID() }],\n role: 'system',\n }),\n ...toCompressedContext,\n // instructions to compress the context into a summary\n createMsg({\n name: 'user',\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text:\n this.compressionConfig.compressionPrompt ||\n DEFAULT_COMPRESSION_PROMPT,\n },\n ],\n role: 'user',\n }),\n ],\n schema: this.compressionConfig.summarySchema || DEFAULT_SUMMARY_SCHEMA,\n });\n\n // Make the compression summary\n let summaryText = '<system-reminder>Here is a summary of your previous work\\n';\n for (const [key, value] of Object.entries(res.content)) {\n summaryText += `# ${key}\\n${value}\\n`;\n }\n summaryText += '</system-reminder>';\n\n console.debug(`[AGENT ${this.name}] Compression summary: ${summaryText}`);\n\n // Update the context with the compression summary and the reserved recent blocks\n this.context = reservedContext;\n this.curSummary = summaryText;\n }\n}\n","import { JSONSerializableObject } from '../type';\nimport {\n ContentBlock,\n TextBlock,\n ThinkingBlock,\n ToolResultBlock,\n ToolCallBlock,\n DataBlock,\n Base64Source,\n URLSource,\n} from './block';\nimport { AgentEvent, EventType } from '../event';\n\n/** A chat message exchanged between agents or between an agent and a model. */\nexport interface Msg {\n /** Unique identifier for the message. */\n id: string;\n /** Display name of the message sender. */\n name: string;\n /** Conversation role of the sender. */\n role: 'user' | 'assistant' | 'system';\n /** Message body. */\n content: ContentBlock[];\n /** Arbitrary key-value metadata attached to the message. */\n metadata: Record<string, JSONSerializableObject>;\n /** ISO-8601 creation timestamp. */\n created_at: string;\n /** ISO-8601 finished timestamp. */\n finished_at?: string | null;\n /** Usage information for the message, such as token counts. */\n usage?: {\n input_tokens: number;\n output_tokens: number;\n };\n}\n\n/**\n * Create a new {@link Msg} object, filling in `id` and `created_at` when omitted.\n * A plain string `content` is automatically wrapped in a single {@link TextBlock}.\n * @param root0\n * @param root0.name\n * @param root0.content\n * @param root0.role\n * @param root0.metadata\n * @param root0.id\n * @param root0.created_at\n * @param root0.finished_at\n * @param root0.usage\n * @returns A Msg object.\n */\n/**\n * Validate that content blocks are allowed for the given role.\n *\n * Mirrors the Python `_assert_user_content_blocks` / `_assert_system_content_blocks`\n * guards: user messages may only contain text or data blocks; system messages\n * may only contain text blocks; assistant messages accept any block type.\n * @param role\n * @param content\n */\nfunction assertContentBlocksForRole(role: Msg['role'], content: ContentBlock[]): void {\n if (role === 'user') {\n for (const block of content) {\n if (block.type !== 'text' && block.type !== 'data') {\n throw new Error('User message can only contain text blocks or data blocks.');\n }\n }\n } else if (role === 'system') {\n for (const block of content) {\n if (block.type !== 'text') {\n throw new Error('System message can only contain text blocks.');\n }\n }\n }\n}\n\n/**\n * createMsg is a low-level utility for constructing Msg objects with proper defaults and validation.\n * @param root0\n * @param root0.name\n * @param root0.content\n * @param root0.role\n * @param root0.metadata\n * @param root0.id\n * @param root0.created_at\n * @param root0.finished_at\n * @param root0.usage\n * @returns A Msg object with the specified properties, and defaults for any omitted fields.\n */\nexport function createMsg({\n name,\n content,\n role,\n metadata = {},\n id = crypto.randomUUID(),\n created_at = new Date().toISOString(),\n finished_at,\n usage,\n}: Omit<Msg, 'id' | 'created_at' | 'metadata' | 'content'> &\n Partial<Pick<Msg, 'id' | 'created_at' | 'metadata'>> & {\n content: string | ContentBlock[];\n }): Msg {\n const contentBlocks: ContentBlock[] =\n typeof content === 'string'\n ? [{ id: crypto.randomUUID(), type: 'text', text: content } as TextBlock]\n : content;\n assertContentBlocksForRole(role, contentBlocks);\n return { id, name, role, content: contentBlocks, metadata, created_at, finished_at, usage };\n}\n\n/**\n * Create a user {@link Msg}.\n * @param root0\n * @param root0.name\n * @param root0.content\n * @param root0.metadata\n * @param root0.id\n * @param root0.created_at\n * @param root0.finished_at\n * @returns A Msg object with role 'user'.\n */\nexport function UserMsg({\n name,\n content,\n metadata = {},\n id = crypto.randomUUID(),\n created_at = new Date().toISOString(),\n finished_at,\n}: {\n name: string;\n content: string | ContentBlock[];\n metadata?: Record<string, JSONSerializableObject>;\n id?: string;\n created_at?: string;\n finished_at?: string | null;\n}): Msg {\n return createMsg({\n name,\n content,\n role: 'user',\n metadata,\n id,\n created_at,\n finished_at: finished_at ?? created_at,\n });\n}\n\n/**\n * Create an assistant {@link Msg}.\n * @param root0\n * @param root0.name\n * @param root0.content\n * @param root0.metadata\n * @param root0.id\n * @param root0.created_at\n * @param root0.usage\n * @returns A Msg object with role 'assistant'.\n */\nexport function AssistantMsg({\n name,\n content,\n metadata = {},\n id = crypto.randomUUID(),\n created_at = new Date().toISOString(),\n usage,\n}: {\n name: string;\n content: string | ContentBlock[];\n metadata?: Record<string, JSONSerializableObject>;\n id?: string;\n created_at?: string;\n usage?: Msg['usage'];\n}): Msg {\n return createMsg({ name, content, role: 'assistant', metadata, id, created_at, usage });\n}\n\n/**\n * Create a system {@link Msg}.\n * @param root0\n * @param root0.name\n * @param root0.content\n * @param root0.metadata\n * @param root0.id\n * @param root0.created_at\n * @param root0.finished_at\n * @returns A Msg object with role 'system'.\n */\nexport function SystemMsg({\n name,\n content,\n metadata = {},\n id = crypto.randomUUID(),\n created_at = new Date().toISOString(),\n finished_at,\n}: {\n name: string;\n content: string | ContentBlock[];\n metadata?: Record<string, JSONSerializableObject>;\n id?: string;\n created_at?: string;\n finished_at?: string | null;\n}): Msg {\n return createMsg({\n name,\n content,\n role: 'system',\n metadata,\n id,\n created_at,\n finished_at: finished_at ?? created_at,\n });\n}\n\n/**\n * Extract the plain-text content from a message.\n *\n * When `content` is a string it is returned as-is. When it is an array of\n * content blocks, all {@link TextBlock} texts are joined with `separator`.\n *\n * @param msg - The message to read.\n * @param separator - String inserted between consecutive text blocks. Defaults to `'\\n'`.\n * @returns The concatenated text, or `null` when no text blocks are present.\n */\nexport function getTextContent(msg: Msg, separator: string = '\\n'): string | null {\n const textBlocks = msg.content.filter(block => block.type === 'text');\n if (textBlocks.length === 0) return null;\n return textBlocks.map(block => (block as TextBlock).text).join(separator);\n}\n\n/**\n * Return all content blocks from a message, regardless of type.\n *\n * When `content` is a plain string it is wrapped in a single {@link TextBlock}.\n *\n * @param msg - The message to read.\n * @returns An array of all {@link ContentBlock} objects.\n */\nexport function getContentBlocks(msg: Msg): ContentBlock[];\nexport function getContentBlocks(msg: Msg, blockType: 'text'): TextBlock[];\nexport function getContentBlocks(msg: Msg, blockType: 'thinking'): ThinkingBlock[];\nexport function getContentBlocks(msg: Msg, blockType: 'data'): DataBlock[];\nexport function getContentBlocks(msg: Msg, blockType: 'tool_call'): ToolCallBlock[];\nexport function getContentBlocks(msg: Msg, blockType: 'tool_result'): ToolResultBlock[];\nexport function getContentBlocks(\n msg: Msg,\n blockType?: 'text' | 'thinking' | 'data' | 'tool_call' | 'tool_result'\n): ContentBlock[] {\n if (!blockType) return msg.content;\n return msg.content.filter(block => block.type === blockType);\n}\n\n/**\n * Find a content block by type and id within a message.\n * @param msg\n * @param blockType\n * @param blockId\n * @returns The matching {@link ContentBlock}, or `undefined` if not found.\n */\nfunction findBlock(msg: Msg, blockType: string, blockId: string): ContentBlock | undefined {\n return msg.content.find(block => block.type === blockType && block.id === blockId);\n}\n\n/**\n * Apply a streaming {@link AgentEvent} to a {@link Msg}, mutating it in place.\n *\n * Only `content` and `finished_at` are ever modified. Events whose\n * `reply_id` does not match `msg.id` are skipped with a warning.\n * @param msg\n * @param event\n * @returns The mutated {@link Msg} object.\n */\nexport function appendEvent(msg: Msg, event: AgentEvent): Msg {\n if (!('reply_id' in event)) return msg;\n if (event.reply_id !== msg.id) {\n console.warn(\n `Event reply_id \"${event.reply_id}\" does not match message id \"${msg.id}\", skipping.`\n );\n return msg;\n }\n\n switch (event.type) {\n case EventType.REPLY_END:\n msg.finished_at = event.created_at;\n break;\n\n case EventType.TEXT_BLOCK_START:\n msg.content.push({ type: 'text', id: event.block_id, text: '' });\n break;\n\n case EventType.TEXT_BLOCK_DELTA: {\n const block = findBlock(msg, 'text', event.block_id);\n if (!block) {\n console.warn(`TextBlock \"${event.block_id}\" not found, skipping.`);\n } else {\n (block as TextBlock).text += event.delta;\n }\n break;\n }\n\n case EventType.TEXT_BLOCK_END:\n break;\n\n case EventType.THINKING_BLOCK_START:\n msg.content.push({ type: 'thinking', id: event.block_id, thinking: '' });\n break;\n\n case EventType.THINKING_BLOCK_DELTA: {\n const block = findBlock(msg, 'thinking', event.block_id);\n if (!block) {\n console.warn(`ThinkingBlock \"${event.block_id}\" not found, skipping.`);\n } else {\n (block as ThinkingBlock).thinking += event.delta;\n }\n break;\n }\n\n case EventType.THINKING_BLOCK_END:\n break;\n\n case EventType.DATA_BLOCK_START:\n msg.content.push({\n type: 'data',\n id: event.block_id,\n source: { type: 'base64', data: '', media_type: event.media_type },\n });\n break;\n\n case EventType.DATA_BLOCK_DELTA: {\n const block = findBlock(msg, 'data', event.block_id);\n if (!block) {\n console.warn(`DataBlock \"${event.block_id}\" not found, skipping.`);\n } else if (event.data) {\n ((block as DataBlock).source as Base64Source).data += event.data;\n }\n break;\n }\n\n case EventType.DATA_BLOCK_END:\n break;\n\n case EventType.TOOL_CALL_START:\n msg.content.push({\n type: 'tool_call',\n id: event.tool_call_id,\n name: event.tool_call_name,\n input: '',\n state: 'pending',\n });\n break;\n\n case EventType.TOOL_CALL_DELTA: {\n const block = findBlock(msg, 'tool_call', event.tool_call_id);\n if (!block) {\n console.warn(`ToolCallBlock \"${event.tool_call_id}\" not found, skipping.`);\n } else {\n (block as ToolCallBlock).input += event.delta;\n }\n break;\n }\n\n case EventType.TOOL_CALL_END:\n break;\n\n case EventType.TOOL_RESULT_START:\n msg.content.push({\n type: 'tool_result',\n id: event.tool_call_id,\n name: event.tool_call_name,\n output: [],\n state: 'running',\n });\n break;\n\n case EventType.TOOL_RESULT_TEXT_DELTA: {\n const block = findBlock(msg, 'tool_result', event.tool_call_id);\n if (!block) {\n console.warn(`ToolResultBlock \"${event.tool_call_id}\" not found, skipping.`);\n } else {\n const trb = block as ToolResultBlock;\n if (typeof trb.output === 'string') {\n trb.output = [{ type: 'text', id: crypto.randomUUID(), text: trb.output }];\n }\n const last = trb.output[trb.output.length - 1];\n if (!last || last.type !== 'text') {\n trb.output.push({\n type: 'text',\n id: crypto.randomUUID(),\n text: event.delta,\n });\n } else {\n (last as TextBlock).text += event.delta;\n }\n }\n break;\n }\n\n case EventType.TOOL_RESULT_DATA_DELTA: {\n const block = findBlock(msg, 'tool_result', event.tool_call_id);\n if (!block) {\n console.warn(`ToolResultBlock \"${event.tool_call_id}\" not found, skipping.`);\n } else {\n const trb = block as ToolResultBlock;\n if (typeof trb.output === 'string') {\n trb.output = [{ type: 'text', id: crypto.randomUUID(), text: trb.output }];\n }\n const source: Base64Source | URLSource =\n event.data != null\n ? { type: 'base64', data: event.data, media_type: event.media_type }\n : { type: 'url', url: event.url!, media_type: event.media_type };\n trb.output.push({\n type: 'data',\n id: event.block_id ?? crypto.randomUUID(),\n source,\n });\n }\n break;\n }\n\n case EventType.TOOL_RESULT_END: {\n const block = findBlock(msg, 'tool_result', event.tool_call_id);\n if (!block) {\n console.warn(`ToolResultBlock \"${event.tool_call_id}\" not found, skipping.`);\n } else {\n (block as ToolResultBlock).state = event.state;\n }\n break;\n }\n\n case EventType.MODEL_CALL_END:\n // Accumulated the input and output tokens here.\n if (msg.usage) {\n msg.usage.input_tokens += event.input_tokens;\n msg.usage.output_tokens += event.output_tokens;\n } else {\n msg.usage = {\n input_tokens: event.input_tokens,\n output_tokens: event.output_tokens,\n };\n }\n break;\n\n case EventType.REQUIRE_USER_CONFIRM:\n for (const tc of event.tool_calls) {\n const b = findBlock(msg, 'tool_call', tc.id);\n if (b) {\n (b as ToolCallBlock).state = 'asking';\n (b as ToolCallBlock).suggested_rules = tc.suggested_rules || [];\n }\n }\n break;\n\n case EventType.USER_CONFIRM_RESULT:\n for (const result of event.confirm_results) {\n const b = findBlock(msg, 'tool_call', result.tool_call.id);\n if (b) {\n (b as ToolCallBlock).state = result.confirmed ? 'allowed' : 'finished';\n }\n }\n break;\n\n case EventType.REQUIRE_EXTERNAL_EXECUTION:\n for (const tc of event.tool_calls) {\n const b = findBlock(msg, 'tool_call', tc.id);\n if (b) (b as ToolCallBlock).state = 'submitted';\n }\n break;\n\n case EventType.EXTERNAL_EXECUTION_RESULT:\n for (const result of event.execution_results) {\n msg.content.push(result);\n }\n break;\n }\n\n return msg;\n}\n","import { DataBlock, TextBlock } from '../message';\nimport { JSONSerializableObject } from '../type';\n\n/**\n * The tool response structure.\n */\nexport interface ToolResponse {\n content: Array<TextBlock | DataBlock>;\n id: string;\n createdAt: string;\n metadata: Record<string, JSONSerializableObject>;\n state: 'success' | 'error' | 'interrupted' | 'running';\n isLast: boolean;\n isInterrupted: boolean;\n}\n\n/**\n * Create a tool response object with the given parameters.\n *\n * @param root0\n * @param root0.content\n * @param root0.state\n * @param root0.id\n * @param root0.createdAt\n * @param root0.metadata\n * @param root0.stream\n * @param root0.isLast\n * @param root0.isInterrupted\n * @returns A ToolResponse object\n */\nexport function createToolResponse({\n content,\n state,\n id = crypto.randomUUID(),\n createdAt = new Date().toISOString(),\n metadata = {},\n stream = false,\n isLast = true,\n isInterrupted = false,\n}: {\n content: Array<TextBlock | DataBlock>;\n state: 'success' | 'error' | 'interrupted' | 'running';\n id?: string;\n createdAt?: string;\n metadata?: Record<string, JSONSerializableObject>;\n stream?: boolean;\n isLast?: boolean;\n isInterrupted?: boolean;\n}) {\n return {\n content,\n id,\n createdAt,\n metadata,\n state,\n stream,\n isLast,\n isInterrupted,\n } as ToolResponse;\n}\n\n/**\n * If the given object conforms to the ToolResponse structure.\n *\n * @param obj\n * @returns True if the object is a ToolResponse, false otherwise\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function isToolResponse(obj: any): boolean {\n return (\n obj &&\n typeof obj === 'object' &&\n typeof obj.id === 'string' &&\n typeof obj.createdAt === 'string' &&\n Array.isArray(obj.content) &&\n typeof obj.metadata === 'object' &&\n typeof obj.stream === 'boolean' &&\n typeof obj.isLast === 'boolean' &&\n typeof obj.isInterrupted === 'boolean' &&\n ['success', 'error', 'interrupted', 'running'].includes(obj.state)\n );\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { Validator } from '@cfworker/json-schema';\nimport matter from 'gray-matter';\nimport { z } from 'zod';\n\nimport { createToolResponse, isToolResponse, ToolResponse } from './response';\nimport { HTTPMCPClient, StdioMCPClient } from '../mcp';\nimport { ToolCallBlock } from '../message';\nimport { ToolInputSchema, ToolSchema } from '../type';\nimport { Tool } from './base';\nimport { _jsonLoadsWithRepair } from '../_utils';\n\ninterface RegisteredTool extends Tool {\n type: 'function' | 'mcp';\n mcpName?: string;\n}\n\n/**\n * The toolkit module in AgentScope, which is responsible for registering tool functions, MCP, and agent skills.\n * It also provides group-wise management of tools.\n */\nexport class Toolkit {\n tools: RegisteredTool[];\n skills: string[];\n skillDirs: string[];\n\n // The cache mapping from the skill name to its corresponding tool name in the toolkit.\n private _skillCache: { [name: string]: string };\n\n /**\n * Initializes a new instance of the Toolkit class.\n * @param config - The configuration object for initializing the toolkit, which can include an array of tools, an array of skill paths, an array of skill directory paths, and a boolean indicating whether to include the built-in skill tool for reading SKILL.md files.\n * @param config.tools - An array of tool definitions to register in the toolkit.\n * @param config.skills - An array of file paths pointing to individual skills.\n * @param config.skillDirs - An array of directory paths, where each directory can contain multiple skills in its subdirectories.\n * @param config.builtInSkillTool - A boolean flag indicating whether to include the built-in skill tool for reading SKILL.md files.\n */\n constructor(config?: {\n tools?: Tool[];\n skills?: string[];\n skillDirs?: string[];\n builtInSkillTool?: boolean;\n }) {\n const { tools = [], skills = [], skillDirs = [], builtInSkillTool = true } = config || {};\n\n this.tools = [];\n\n if (builtInSkillTool) {\n this.tools.push({\n type: 'function',\n name: 'Skill',\n description: `Retrieves the full content of a skill by reading its SKILL.md file. Skills are packages of domain expertise that extend agent capabilities. Use this tool to access detailed instructions, examples, and guidelines for a specific skill.\n\nUsage:\n- Provide the skill name as the input parameter\n- The tool will return the complete SKILL.md file content for that skill\n- If the skill is not found, an error message with available skills will be returned\n- Available skills are listed in the skills-system section of the agent prompt`,\n inputSchema: z.object({ name: z.string().describe('The name of the skill') }),\n call: this._skillTool.bind(this),\n requireUserConfirm: false,\n });\n }\n\n tools.map(tool => {\n this.tools.push({\n type: 'function',\n ...tool,\n });\n });\n\n this.skills = skills;\n this.skillDirs = skillDirs;\n\n this._skillCache = {};\n }\n\n /**\n * Registers a tool function to the toolkit. The function can be either a plain function that adheres to the ToolFunction type, or an instance of a class that extends ToolBase. When registering a plain function, the name, description, and input schema must be provided explicitly. When registering a ToolBase instance, these properties will be extracted from the instance itself.\n *\n * @params tool - The tool function to register, which can be either a plain function with explicit properties or an instance of a class that extends ToolBase.\n * @returns The Toolkit instance with the new tool function registered\n * @param tool\n */\n registerToolFunction(tool: Tool): Toolkit {\n this.tools.push({\n type: 'function',\n ...tool,\n });\n return this;\n }\n\n /**\n * Registers functions from a given MCP client.\n *\n * @param root0\n * @param root0.client\n * @param root0.enabledTools\n * @param root0.disabledTools\n * @param root0.requireUserConfirm\n * @returns The Toolkit instance with the new tools registered\n */\n async registerMCPClient({\n client,\n enabledTools,\n disabledTools = [],\n requireUserConfirm = false,\n }: {\n client: HTTPMCPClient | StdioMCPClient;\n enabledTools?: string[];\n disabledTools?: string[];\n requireUserConfirm?: boolean;\n }): Promise<Toolkit> {\n const tools = await client.listTools();\n\n const appendTools: string[] = [];\n tools\n .filter(\n tool =>\n !(enabledTools && !enabledTools.includes(tool.name)) &&\n !disabledTools.includes(tool.name)\n )\n .forEach(tool => {\n this.tools.push({\n type: 'mcp',\n mcpName: client.name,\n ...tool,\n requireUserConfirm,\n });\n appendTools.push(tool.name);\n });\n console.log(`Registered tools from MCP client '${client.name}': ${appendTools.join(', ')}`);\n return this;\n }\n\n /**\n * Executes a registered tool function based on the provided ToolUseBlock.\n * Note this method always returns an AsyncGenerator of ToolResponse, regardless of the tool function type.\n *\n * @param toolCall - The ToolUseBlock containing the tool name and input arguments\n * @yields Incremental ToolResponse objects as they are produced by the tool function\n * @returns The final complete ToolResponse after the tool function execution is finished\n */\n async *callToolFunction(toolCall: ToolCallBlock): AsyncGenerator<ToolResponse, ToolResponse> {\n // If the tool is registered\n const tool = this.tools.find(tool => tool.name === toolCall.name);\n\n if (!tool) {\n const notFoundRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `FunctionNotFoundError: Cannot find the function named ${toolCall.name}`,\n },\n ],\n state: 'error',\n });\n yield notFoundRes;\n return notFoundRes;\n }\n\n // Parse the input arguments using the tool's schema\n let parsedInput: Record<string, unknown>;\n try {\n parsedInput = _jsonLoadsWithRepair(toolCall.input);\n if (tool.inputSchema instanceof z.ZodObject) {\n tool.inputSchema.parse(parsedInput);\n } else {\n //\n const validator = new Validator(tool.inputSchema);\n const validation = validator.validate(parsedInput);\n if (!validation.valid) {\n throw new Error(`Invalid input arguments: ${validation.errors}`);\n }\n }\n } catch (error) {\n const parseErrorRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `InvalidArgumentError: ${String(error)}`,\n },\n ],\n state: 'error',\n });\n yield parseErrorRes;\n return parseErrorRes;\n }\n\n // Log the tool call with parsed input\n if (!tool.call) {\n throw new Error(\n `Cannot execute external tool '${toolCall.name}' because no call method is defined for it in the toolkit.`\n );\n }\n\n // Execute the tool function and await the result\n // Note: await on a non-Promise value returns the value itself\n let finalRes: ToolResponse | null = null;\n try {\n const res = await tool.call(parsedInput);\n\n // If res is a string\n if (typeof res === 'string') {\n const textRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: res,\n },\n ],\n state: 'success',\n });\n yield textRes;\n finalRes = textRes;\n } else if (isToolResponse(res)) {\n // If res is a ToolResponse\n yield res as ToolResponse;\n finalRes = res as ToolResponse;\n } else if (Symbol.asyncIterator in res) {\n // If res is an AsyncGenerator of string or ToolResponse\n const accContent: ToolResponse['content'] = [];\n let nextResult = await (res as AsyncGenerator<string | ToolResponse>).next();\n\n while (!nextResult.done) {\n const currentValue = nextResult.value;\n // Peek ahead to determine if this is the last value\n nextResult = await (res as AsyncGenerator<string | ToolResponse>).next();\n const isLastValue = nextResult.done;\n\n if (typeof currentValue === 'string') {\n const itemRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: currentValue,\n },\n ],\n isLast: isLastValue,\n state: 'running',\n });\n yield itemRes;\n\n // Accumulate the text content into finalRes\n accContent.push({\n id: crypto.randomUUID(),\n type: 'text',\n text: currentValue,\n });\n } else if (isToolResponse(currentValue)) {\n // Use the isLast from the ToolResponse if set, otherwise use our calculated value\n currentValue.isLast = currentValue.isLast ?? isLastValue;\n yield currentValue as ToolResponse;\n\n // Accumulate the content of the ToolResponse into finalRes\n accContent.push(...currentValue.content);\n }\n }\n finalRes = createToolResponse({\n content: accContent,\n state: 'success',\n });\n } else if (Symbol.iterator in res) {\n // If res is a Generator of string or ToolResponse\n const accContent: ToolResponse['content'] = [];\n let nextResult = (res as Generator<string | ToolResponse>).next();\n\n while (!nextResult.done) {\n const currentValue = nextResult.value;\n // Peek ahead to determine if this is the last value\n nextResult = (res as Generator<string | ToolResponse>).next();\n const isLastValue = nextResult.done;\n\n if (typeof currentValue === 'string') {\n const itemRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: currentValue,\n },\n ],\n isLast: isLastValue,\n state: 'running',\n });\n yield itemRes;\n // Accumulate the text content into finalRes\n accContent.push({\n id: crypto.randomUUID(),\n type: 'text',\n text: currentValue,\n });\n } else if (isToolResponse(currentValue)) {\n // Use the isLast from the ToolResponse if set, otherwise use our calculated value\n currentValue.isLast = currentValue.isLast ?? isLastValue;\n yield currentValue as ToolResponse;\n // Accumulate the content of the ToolResponse into finalRes\n accContent.push(...currentValue.content);\n }\n }\n finalRes = createToolResponse({\n content: accContent,\n state: 'success',\n });\n } else {\n const invalidRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: String(res),\n },\n ],\n state: 'running',\n });\n yield invalidRes;\n finalRes = invalidRes;\n }\n } catch (error) {\n const errorRes = createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `ToolExecutionError: ${String(error)}`,\n },\n ],\n state: 'error',\n });\n yield errorRes;\n finalRes = errorRes;\n }\n\n if (!finalRes) {\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `Tool ${toolCall.name} executed successfully.`,\n },\n ],\n state: 'success',\n });\n }\n\n // Clean the finalRes by merging the adjacent text blocks into one block, leaving\n // multimodal content blocks (e.g. image, audio) unchanged\n const cleanedContent: ToolResponse['content'] = [];\n let textBuffer = '';\n for (const block of finalRes.content) {\n if (block.type === 'text') {\n textBuffer += block.text;\n } else {\n if (textBuffer) {\n cleanedContent.push({\n id: crypto.randomUUID(),\n type: 'text',\n text: textBuffer,\n });\n textBuffer = '';\n }\n cleanedContent.push(block);\n }\n }\n // The remaining text in the buffer, if any, should also be pushed to the cleanedContent\n if (textBuffer) {\n cleanedContent.push({\n id: crypto.randomUUID(),\n type: 'text',\n text: textBuffer,\n });\n }\n\n return {\n ...finalRes,\n content: cleanedContent,\n };\n }\n\n /**\n * Returns the JSON schemas for all registered tools in a format compatible with LLM APIs.\n *\n * @returns An array of ToolJSONSchema objects\n */\n getJSONSchemas(): ToolSchema[] {\n return this.tools.map(tool => {\n const inputSchema =\n tool.inputSchema instanceof z.ZodObject\n ? tool.inputSchema.toJSONSchema({ target: 'openapi-3.0' })\n : tool.inputSchema;\n\n return {\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: inputSchema as ToolInputSchema,\n },\n };\n });\n }\n\n /**\n * Get the instruction prompt for the agent to use the skills.\n *\n * @returns A string containing the instruction prompt of the available skills and how to use them.\n */\n getSkillsPrompt(): string {\n this._skillCache = {};\n if (this.skills.length === 0 && this.skillDirs.length === 0) return '';\n\n if (typeof process !== 'undefined' && process.versions && process.versions.node) {\n const skillsInfo: { name: string; description: string; location: string }[] = [];\n this.skills.forEach(skillPath => {\n // 首先获取绝对路径\n const absSkillPath = path.resolve(skillPath);\n\n // Check if directory exists\n if (!fs.existsSync(absSkillPath) || !fs.statSync(absSkillPath).isDirectory()) {\n return;\n }\n\n // First, check if SKILL.md exists directly in this directory\n const skillMdPath = path.join(absSkillPath, 'SKILL.md');\n if (!fs.existsSync(skillMdPath)) return;\n\n // Read the SKILL.md file and extract the name and description from the YAML front matter\n try {\n const content = fs.readFileSync(skillMdPath, 'utf-8');\n const { data } = matter(content);\n\n const name = data.name || path.basename(skillPath);\n const description = data.description || 'No description provided';\n\n skillsInfo.push({\n name,\n description,\n location: absSkillPath,\n });\n\n this._skillCache[name] = absSkillPath;\n } catch (e) {\n console.error(`Error reading SKILL.md for skill at ${skillPath}:`, e);\n }\n });\n\n this.skillDirs.forEach(skillDir => {\n const absSkillDir = path.resolve(skillDir);\n\n // Check if directory exists\n if (!fs.existsSync(absSkillDir) || !fs.statSync(absSkillDir).isDirectory()) {\n return;\n }\n\n // Read all subdirectories in the skillDir\n const subdirs = fs.readdirSync(absSkillDir).filter(subdir => {\n const subdirPath = path.join(absSkillDir, subdir);\n return fs.statSync(subdirPath).isDirectory();\n });\n\n subdirs.forEach(subdir => {\n const skillMdPath = path.join(absSkillDir, subdir, 'SKILL.md');\n if (!fs.existsSync(skillMdPath)) return;\n\n try {\n const content = fs.readFileSync(skillMdPath, 'utf-8');\n const { data } = matter(content);\n\n const name = data.name || subdir;\n const description = data.description || 'No description provided';\n\n skillsInfo.push({\n name,\n description,\n location: path.join(skillDir, subdir),\n });\n\n this._skillCache[name] = path.join(absSkillDir, subdir);\n } catch (e) {\n console.error(\n `Error reading SKILL.md for skill at ${path.join(skillDir, subdir)}:`,\n e\n );\n }\n });\n });\n\n if (skillsInfo.length === 0) return '';\n\n const skillsXml = skillsInfo\n .map(\n skill => `<skill>\n<name>${skill.name}</name>\n<description>${skill.description}</description>\n<location>${skill.location}</location>\n</skill>`\n )\n .reduce((acc, skillInfo) => acc + `\\n${skillInfo}\\n`, '');\n\n return `<skills-system>\n## What are Skills?\nSkills are packages of domain expertise that extend your capabilities.\n\n## Important: How to Use Skills\n**Skill names are NOT callable functions.** You cannot call a skill directly by its name.\n${skillsXml}\n</skills-system>`;\n }\n\n return '';\n }\n\n /**\n * The agent skill tool to read SKILL.md file content based on the skill name.\n * @param root0\n * @param root0.name\n * @returns The content of the SKILL.md file for the specified skill, or an error message if the skill is not\n * found or the SKILL.md file cannot be read.\n */\n private async _skillTool({ name }: { name: string }): Promise<ToolResponse> {\n if (this._skillCache[name]) {\n // Look up the skill name in the cache to get the corresponding directory path\n const skillDir = this._skillCache[name];\n // Read the SKILL.md file in the skill directory and return its content as the tool response\n const skillMdPath = path.join(skillDir, 'SKILL.md');\n if (!fs.existsSync(skillMdPath)) {\n try {\n const fileContent = fs.readFileSync(skillMdPath, 'utf-8');\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: fileContent,\n },\n ],\n state: 'success',\n });\n } catch {}\n }\n }\n\n // Scan the skills and skillDirs again to find the skill if it's not in the cache and refresh the cache at the same time\n this.getSkillsPrompt();\n const refreshedSkillDir = this._skillCache[name];\n if (refreshedSkillDir) {\n const skillMdPath = path.join(refreshedSkillDir, 'SKILL.md');\n try {\n const fileContent = fs.readFileSync(skillMdPath, 'utf-8');\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: fileContent,\n },\n ],\n state: 'success',\n });\n } catch {}\n }\n\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `SkillNotFoundError: Cannot find the skill named ${name}, current available skills are ${Object.keys(this._skillCache).join(', ')}`,\n },\n ],\n state: 'error',\n });\n }\n\n /**\n * Checks if a tool requires user confirmation before execution based on its name.\n * @param toolName The name of the tool to check for user confirmation requirement.\n * @returns A boolean indicating whether the specified tool requires user confirmation before execution. If the tool is not found, it returns false.\n */\n requireUserConfirm(toolName: string): boolean {\n const tool = this.tools.find(tool => tool.name === toolName);\n return tool ? (tool.requireUserConfirm ?? false) : false;\n }\n\n /**\n * Checks if a tool requires external execution (e.g., by an MCP client) based on its name.\n * @param toolName\n * @returns A boolean indicating whether the specified tool requires external execution. If the tool is not found, it returns false.\n */\n requireExternalExecution(toolName: string): boolean {\n const tool = this.tools.find(tool => tool.name === toolName);\n return tool ? !tool.call : false;\n }\n}\n","import { jsonrepair } from 'jsonrepair';\n\nimport { JSONSerializableObject } from '../type';\n\n/**\n * Creates a timestamp string in the format \"YYYY-MM-DD HH:mm:ss.sss\"\n * representing the current date and time.\n *\n * @returns {string} The formatted timestamp string.\n */\nexport function _createTimestamp(): string {\n return new Date().toISOString().replace('T', ' ').replace('Z', '').substring(0, 23);\n}\n\n/**\n * Attempts to parse a JSON string into a dictionary/Record.\n * This function is used to handle the streaming tool use block from the LLM API.\n *\n * @param input - The JSON string to parse.\n * @returns A dictionary/Record parsed from the JSON string. If parsing fails, returns an empty dictionary.\n */\nexport function _jsonLoadsWithRepair(input: string): Record<string, JSONSerializableObject> {\n try {\n const jsonObj = JSON.parse(input);\n // Check if the jsonObj is a dictionary/Record\n if (typeof jsonObj === 'object' && jsonObj !== null && !Array.isArray(jsonObj)) {\n return jsonObj as Record<string, JSONSerializableObject>;\n } else {\n // Return an empty dictionary\n return {};\n }\n } catch {\n try {\n const repairedString = jsonrepair(input);\n const jsonObj = JSON.parse(repairedString);\n // Check if the jsonObj is a dictionary/Record\n if (typeof jsonObj === 'object' && jsonObj !== null && !Array.isArray(jsonObj)) {\n return jsonObj as Record<string, JSONSerializableObject>;\n } else {\n // Return an empty dictionary\n return {};\n }\n } catch (e) {\n console.error(`Failed to parse JSON \"${input}\" with error:`, e);\n return {};\n }\n }\n}\n\n/**\n * Parses a streamed response from the post request.\n * An async generator that yields parsed JSON objects from the SSE stream.\n *\n * @param response - The fetch response object.\n * @returns An async generator yielding parsed JSON objects.\n */\nexport async function* _parseStreamedResponse<T>(response: Response): AsyncGenerator<T> {\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error('Failed to get reader from response body for streaming.');\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Handle the completed line\n const lines = buffer.split('\\n');\n buffer = lines.pop() || ''; // Keep the last uncompleted line\n\n for (const line of lines) {\n const trimmedLine = line.trim();\n if (!trimmedLine || trimmedLine.startsWith(':')) {\n continue; // Skip the empty line and comments\n }\n\n if (trimmedLine.startsWith('data:')) {\n const jsonStr = trimmedLine.slice(5).trim(); // Remove \"data:\" prefix\n\n if (jsonStr === '[DONE]') {\n break;\n }\n\n try {\n const json = JSON.parse(jsonStr);\n yield json;\n } catch (e) {\n console.error('Failed to parse JSON:', e);\n throw new Error(`Failed to parse JSON from stream: ${jsonStr}`);\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import { exec } from 'child_process';\nimport { promisify } from 'util';\n\nimport { z } from 'zod';\n\nimport { createToolResponse, ToolResponse } from './response';\n\nconst execAsync = promisify(exec);\n\n/**\n * Tool for executing bash commands in a shell environment.\n * Intended for terminal operations such as git, npm, and docker.\n * File operations should use the dedicated Read, Write, Edit, Glob, and Grep tools instead.\n *\n * @returns A Tool object for executing bash commands, with a call method that performs the execution and returns the output or error message.\n */\nexport function Bash() {\n return {\n name: 'Bash',\n description: `Executes a given bash command and returns its output.\n\nThe working directory persists between commands, but shell state does not. The shell environment is initialized from the user's profile (bash or zsh).\n\nIMPORTANT: Avoid using this tool to run \\`find\\`, \\`grep\\`, \\`cat\\`, \\`head\\`, \\`tail\\`, \\`sed\\`, \\`awk\\`, or \\`echo\\` commands, unless explicitly instructed or after you have verified that a dedicated tool cannot accomplish your task. Instead, use the appropriate dedicated tool as this will provide a much better experience for the user:\n\n - File search: Use Glob (NOT find or ls)\n - Content search: Use Grep (NOT grep or rg)\n - Read files: Use Read (NOT cat/head/tail)\n - Edit files: Use Edit (NOT sed/awk)\n - Write files: Use Write (NOT echo >/cat <<EOF)\n - Communication: Output text directly (NOT echo/printf)\n\nWhile the Bash tool can do similar things, it's better to use the built-in tools as they provide a better user experience and make it easier to review tool calls and give permission.\n\n# Instructions\n - If your command will create new directories or files, first use this tool to run \\`ls\\` to verify the parent directory exists and is the correct location.\n - Always quote file paths that contain spaces with double quotes in your command (e.g., cd \"path with spaces/file.txt\")\n - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of \\`cd\\`. You may use \\`cd\\` if the User explicitly requests it.\n - You may specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). By default, your command will timeout after 120000ms (2 minutes).\n - Write a clear, concise description of what your command does. For simple commands, keep it brief (5-10 words). For complex commands (piped commands, obscure flags, or anything hard to understand at a glance), include enough context so that the user can understand what your command will do.\n - When issuing multiple commands:\n - If the commands are independent and can run in parallel, make multiple Bash tool calls in a single message. Example: if you need to run \"git status\" and \"git diff\", send a single message with two Bash tool calls in parallel.\n - If the commands depend on each other and must run sequentially, use a single Bash call with '&&' to chain them together.\n - Use ';' only when you need to run commands sequentially but don't care if earlier commands fail.\n - DO NOT use newlines to separate commands (newlines are ok in quoted strings).\n - For git commands:\n - Prefer to create a new commit rather than amending an existing commit.\n - Before running destructive operations (e.g., git reset --hard, git push --force, git checkout --), consider whether there is a safer alternative that achieves the same goal. Only use destructive operations when they are truly the best approach.\n - Never skip hooks (--no-verify) or bypass signing (--no-gpg-sign, -c commit.gpgsign=false) unless the user has explicitly asked for it. If a hook fails, investigate and fix the underlying issue.\n - Avoid unnecessary \\`sleep\\` commands:\n - Do not sleep between commands that can run immediately — just run them.\n - Do not retry failing commands in a sleep loop — diagnose the root cause or consider an alternative approach.\n - If you must sleep, keep the duration short (1-5 seconds) to avoid blocking the user.`,\n inputSchema: z.object({\n command: z.string().describe('The bash command to execute'),\n description: z\n .string()\n .optional()\n .describe(\n 'Clear, concise description of what this command does. For simple commands, keep it brief (5-10 words). For complex commands, include enough context.'\n ),\n timeout: z\n .number()\n .int()\n .min(0)\n .max(600000)\n .optional()\n .describe('Optional timeout in milliseconds (default: 120000, max: 600000)'),\n }),\n requireUserConfirm: true,\n\n /**\n * Executes a bash command and returns its output.\n *\n * @param root0 - The parameters object\n * @param root0.command - The bash command to execute\n * @param root0.description - Optional description of what the command does\n * @param root0.timeout - Optional timeout in milliseconds (default: 120000, max: 600000)\n * @returns The stdout of the command, or an error message if the command fails\n */\n async call({\n command,\n description: _description,\n timeout = 120000,\n }: {\n command: string;\n description?: string;\n timeout?: number;\n }): Promise<ToolResponse> {\n try {\n const maxTimeout = 600000;\n const effectiveTimeout = Math.min(timeout, maxTimeout);\n\n // Determine the appropriate shell based on platform\n let shell: string;\n if (process.platform === 'win32') {\n // On Windows, use cmd.exe or PowerShell\n shell = process.env.COMSPEC || 'cmd.exe';\n } else {\n // On Unix-like systems, use the user's shell or default to bash\n shell = process.env.SHELL || '/bin/bash';\n }\n\n const { stdout } = await execAsync(command, {\n encoding: 'utf-8',\n timeout: effectiveTimeout,\n maxBuffer: 30000 * 1024,\n shell,\n });\n\n // Normalize line endings to LF for cross-platform consistency\n const normalizedOutput = stdout.replace(/\\r\\n/g, '\\n');\n\n const maxOutputLength = 30000;\n if (normalizedOutput.length > maxOutputLength) {\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text:\n normalizedOutput.substring(0, maxOutputLength) +\n '\\n\\n[Output truncated - exceeded 30000 characters]',\n },\n ],\n state: 'success',\n });\n }\n\n return createToolResponse({\n content: [{ id: crypto.randomUUID(), type: 'text', text: normalizedOutput }],\n state: 'success',\n });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n const errorMessage = error.message || 'Unknown error';\n const stderr = error.stderr?.toString().replace(/\\r\\n/g, '\\n') || '';\n const stdout = error.stdout?.toString().replace(/\\r\\n/g, '\\n') || '';\n\n let result = `Command failed: ${command}\\n`;\n if (stdout) result += `\\nStdout:\\n${stdout}`;\n if (stderr) result += `\\nStderr:\\n${stderr}`;\n if (errorMessage && !stderr) result += `\\nError: ${errorMessage}`;\n\n return createToolResponse({\n content: [{ id: crypto.randomUUID(), type: 'text', text: result }],\n state: 'error',\n });\n }\n },\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { z } from 'zod';\n\nimport { createToolResponse, ToolResponse } from './response';\n\n/**\n * Tool for reading files from the local filesystem.\n * Returns file contents with line numbers in cat -n format.\n *\n * @returns A Tool object for reading files, with a call method that performs the read operation and returns the formatted contents or a warning if the file is empty.\n */\nexport function Read() {\n return {\n name: 'Read',\n description: `Reads a file from the local filesystem. You can access any file directly by using this tool.\nAssume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.\n\nUsage:\n- The file_path parameter must be an absolute path, not a relative path\n- By default, it reads up to 2000 lines starting from the beginning of the file\n- You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters\n- Any lines longer than 2000 characters will be truncated\n- Results are returned using cat -n format, with line numbers starting at 1\n- This tool can only read files, not directories. To read a directory, use an ls command via the Bash tool.\n- You can call multiple tools in a single response. It is always better to speculatively read multiple potentially useful files in parallel.\n- If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.`,\n inputSchema: z.object({\n file_path: z.string().describe('The absolute path to the file to read'),\n offset: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\n 'The line number to start reading from. Only provide if the file is too large to read at once'\n ),\n limit: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\n 'The number of lines to read. Only provide if the file is too large to read at once'\n ),\n }),\n requireUserConfirm: true,\n\n /**\n * Reads a file and returns its contents with line numbers.\n *\n * @param root0 - The parameters object\n * @param root0.file_path - Absolute path to the file to read\n * @param root0.offset - Line number to start reading from (1-based)\n * @param root0.limit - Maximum number of lines to read (capped at 2000)\n * @returns The file contents formatted with line numbers, or a warning if the file is empty\n * @throws If the path is not absolute, the file does not exist, or the path is a directory\n */\n call({\n file_path,\n offset,\n limit,\n }: {\n file_path: string;\n offset?: number;\n limit?: number;\n }): ToolResponse {\n if (!path.isAbsolute(file_path)) {\n throw new Error(`file_path must be an absolute path, got: ${file_path}`);\n }\n\n if (!fs.existsSync(file_path)) {\n throw new Error(`File not found: ${file_path}`);\n }\n\n const stat = fs.statSync(file_path);\n if (stat.isDirectory()) {\n throw new Error(\n `${file_path} is a directory, not a file. Use Bash with ls to read directories.`\n );\n }\n\n const rawContent = fs.readFileSync(file_path, 'utf-8');\n\n if (rawContent.length === 0) {\n return createToolResponse({\n content: [{ id: crypto.randomUUID(), type: 'text', text: rawContent }],\n state: 'success',\n });\n }\n\n const allLines = rawContent.split('\\n');\n const startLine = offset !== undefined ? offset - 1 : 0;\n const maxLines = 2000;\n const effectiveLimit = limit !== undefined ? Math.min(limit, maxLines) : maxLines;\n const selectedLines = allLines.slice(startLine, startLine + effectiveLimit);\n\n const maxLineLength = 2000;\n const formatted = selectedLines\n .map((line, i) => {\n const lineNum = startLine + i + 1;\n const truncated =\n line.length > maxLineLength\n ? line.substring(0, maxLineLength) + '[truncated]'\n : line;\n return `${String(lineNum).padStart(6)}\\t${truncated}`;\n })\n .join('\\n');\n\n return createToolResponse({\n content: [{ id: crypto.randomUUID(), type: 'text', text: formatted }],\n state: 'success',\n });\n },\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { z } from 'zod';\n\n/**\n * Tool for writing files to the local filesystem.\n * Creates parent directories as needed and overwrites existing files.\n *\n * @returns A Tool object for writing files, with a call method that performs the write operation.\n */\nexport function Write() {\n return {\n name: 'Write',\n description: `Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the Read tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.`,\n inputSchema: z.object({\n file_path: z\n .string()\n .describe(\n 'The absolute path to the file to write (must be absolute, not relative)'\n ),\n content: z.string().describe('The content to write to the file'),\n }),\n requireUserConfirm: true,\n\n /**\n * Writes content to a file, creating parent directories if necessary.\n *\n * @param root0 - The parameters object\n * @param root0.file_path - Absolute path to the file to write\n * @param root0.content - The content to write to the file\n * @returns A success message including the number of lines written\n * @throws If the path is not absolute\n */\n call({ file_path, content }: { file_path: string; content: string }): string {\n if (!path.isAbsolute(file_path)) {\n throw new Error(`file_path must be an absolute path, got: ${file_path}`);\n }\n\n const dir = path.dirname(file_path);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n fs.writeFileSync(file_path, content, 'utf-8');\n const lineCount = content.split('\\n').length;\n return `The file ${file_path} has been written successfully (${lineCount} lines).`;\n },\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { z } from 'zod';\n\n/**\n * Tool for performing exact string replacements in files.\n * Requires the file to have been read at least once before editing.\n *\n * @returns A Tool object with a call method that performs the edit operation based on the provided parameters, ensuring uniqueness of the old_string unless replace_all is true.\n */\nexport function Edit() {\n return {\n name: 'Edit',\n description: `Performs exact string replacements in files.\n\nUsage:\n- You must use your Read tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file.\n- When editing text from Read tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: spaces + line number + tab. Everything after that tab is the actual file content to match. Never include any part of the line number prefix in the old_string or new_string.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.\n- The edit will FAIL if \\`old_string\\` is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use \\`replace_all\\` to change every instance of \\`old_string\\`.\n- Use \\`replace_all\\` for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.`,\n inputSchema: z.object({\n file_path: z.string().describe('The absolute path to the file to modify'),\n old_string: z.string().describe('The text to replace'),\n new_string: z\n .string()\n .describe('The text to replace it with (must be different from old_string)'),\n replace_all: z\n .boolean()\n .optional()\n .default(false)\n .describe('Replace all occurrences of old_string (default false)'),\n }),\n requireUserConfirm: true,\n\n /**\n * Performs an exact string replacement in the specified file.\n *\n * @param root0 - The parameters object\n * @param root0.file_path - Absolute path to the file to modify\n * @param root0.old_string - The exact text to find and replace\n * @param root0.new_string - The text to replace old_string with\n * @param root0.replace_all - If true, replaces all occurrences; otherwise only the first\n * @returns A success message indicating the file was updated\n * @throws If the path is not absolute, file does not exist, strings are identical,\n * old_string is not found, or old_string is not unique and replace_all is false\n */\n call({\n file_path,\n old_string,\n new_string,\n replace_all = false,\n }: {\n file_path: string;\n old_string: string;\n new_string: string;\n replace_all?: boolean;\n }): string {\n if (!path.isAbsolute(file_path)) {\n throw new Error(`file_path must be an absolute path, got: ${file_path}`);\n }\n\n if (!fs.existsSync(file_path)) {\n throw new Error(`File not found: ${file_path}`);\n }\n\n if (old_string === new_string) {\n throw new Error('old_string and new_string must be different');\n }\n\n const content = fs.readFileSync(file_path, 'utf-8');\n const occurrences = content.split(old_string).length - 1;\n\n if (occurrences === 0) {\n throw new Error(`old_string not found in file: ${file_path}`);\n }\n\n if (!replace_all && occurrences > 1) {\n throw new Error(\n `old_string is not unique in the file (found ${occurrences} occurrences). ` +\n `Provide more surrounding context to make it unique, or use replace_all=true.`\n );\n }\n\n const newContent = replace_all\n ? content.split(old_string).join(new_string)\n : content.replace(old_string, new_string);\n\n fs.writeFileSync(file_path, newContent, 'utf-8');\n return `The file ${file_path} has been updated successfully.`;\n },\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { z } from 'zod';\n\n/**\n * Tool for fast file pattern matching across a codebase.\n * Supports glob patterns and returns results sorted by modification time.\n *\n * @returns A Tool object with a call method that performs glob matching based on the provided pattern and path.\n */\nexport function Glob() {\n /**\n * Matches files against a glob pattern starting from the given base directory.\n * @param pattern - The glob pattern to match against.\n * @param baseDir - The base directory to search from.\n * @returns An array of matched file paths.\n */\n const globMatch = (pattern: string, baseDir: string): string[] => {\n const results: string[] = [];\n const parts = pattern.split('/');\n matchParts(parts, 0, baseDir, results);\n return results;\n };\n\n /**\n * Recursively matches path parts against directory entries.\n * @param parts - The split glob pattern parts.\n * @param partIndex - The current index in the parts array.\n * @param currentDir - The current directory being traversed.\n * @param results - The accumulator array for matched file paths.\n */\n const matchParts = (\n parts: string[],\n partIndex: number,\n currentDir: string,\n results: string[]\n ): void => {\n if (partIndex >= parts.length) return;\n\n const part = parts[partIndex];\n const isLast = partIndex === parts.length - 1;\n\n if (part === '**') {\n if (isLast) {\n collectAll(currentDir, results);\n } else {\n matchParts(parts, partIndex + 1, currentDir, results);\n try {\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const subDir = path.join(currentDir, entry.name);\n matchParts(parts, partIndex, subDir, results);\n }\n }\n } catch {\n // skip unreadable dirs\n }\n }\n } else {\n const regex = globPartToRegex(part);\n try {\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n if (regex.test(entry.name)) {\n const fullPath = path.join(currentDir, entry.name);\n if (isLast) {\n if (entry.isFile()) results.push(fullPath);\n } else if (entry.isDirectory()) {\n matchParts(parts, partIndex + 1, fullPath, results);\n }\n }\n }\n } catch {\n // skip unreadable dirs\n }\n }\n };\n\n /**\n * Recursively collects all files under a directory.\n * @param dir - The directory to collect files from.\n * @param results - The accumulator array for collected file paths.\n */\n const collectAll = (dir: string, results: string[]): void => {\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isFile()) {\n results.push(fullPath);\n } else if (entry.isDirectory()) {\n collectAll(fullPath, results);\n }\n }\n } catch {\n // skip unreadable dirs\n }\n };\n\n /**\n * Converts a single glob pattern part to a RegExp.\n * @param part - The glob pattern part to convert.\n * @returns A RegExp that matches the pattern part.\n */\n const globPartToRegex = (part: string): RegExp => {\n const escaped = part\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.');\n return new RegExp(`^${escaped}$`);\n };\n\n return {\n name: 'Glob',\n description: `Fast file pattern matching tool that works with any codebase size.\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\"\n- Returns matching file paths sorted by modification time\n- Use this tool when you need to find files by name patterns\n- When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the Agent tool instead\n- You can call multiple tools in a single response. It is always better to speculatively perform multiple searches in parallel if they are potentially useful.`,\n inputSchema: z.object({\n pattern: z.string().describe('The glob pattern to match files against'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. If not specified, the current working directory will be used. IMPORTANT: Omit this field to use the default directory. DO NOT enter \"undefined\" or \"null\" - simply omit it for the default behavior. Must be a valid directory path if provided.'\n ),\n }),\n requireUserConfirm: true,\n\n /**\n * Finds files matching a glob pattern under the given directory.\n *\n * @param root0 - The parameters object\n * @param root0.pattern - The glob pattern to match files against\n * @param root0.path - The base directory to search in; defaults to cwd\n * @returns A newline-separated list of matching file paths sorted by modification time,\n * or a no-matches message if nothing is found\n * @throws If the base directory does not exist\n */\n call({ pattern, path: searchPath }: { pattern: string; path?: string }): string {\n const baseDir = searchPath ? searchPath : process.cwd();\n\n if (!fs.existsSync(baseDir)) {\n throw new Error(`Directory not found: ${baseDir}`);\n }\n\n const matches = globMatch(pattern, baseDir);\n\n matches.sort((a, b) => {\n const statA = fs.statSync(a);\n const statB = fs.statSync(b);\n return statB.mtimeMs - statA.mtimeMs;\n });\n\n if (matches.length === 0) {\n return `No files found matching pattern: ${pattern}`;\n }\n\n return matches.join('\\n');\n },\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { z } from 'zod';\n\ntype OutputMode = 'content' | 'files_with_matches' | 'count';\n\nconst TYPE_EXTENSIONS: Record<string, string[]> = {\n js: ['.js', '.mjs', '.cjs'],\n ts: ['.ts', '.mts', '.cts'],\n tsx: ['.tsx'],\n jsx: ['.jsx'],\n py: ['.py'],\n rust: ['.rs'],\n go: ['.go'],\n java: ['.java'],\n cpp: ['.cpp', '.cc', '.cxx', '.h', '.hpp'],\n c: ['.c', '.h'],\n css: ['.css'],\n html: ['.html', '.htm'],\n json: ['.json'],\n md: ['.md', '.markdown'],\n yaml: ['.yaml', '.yml'],\n toml: ['.toml'],\n sh: ['.sh', '.bash'],\n};\n\n/**\n * Tool for searching file contents using regular expressions.\n * Supports multiple output modes, file type filtering, and multiline matching.\n *\n * @returns A Tool object for performing regex searches across files, with a call method that executes the search and returns results based on the specified output mode.\n */\nexport function Grep() {\n /**\n * Collects all files under a base directory, optionally filtered by glob or type.\n * @param baseDir - The base directory to search from.\n * @param glob - Optional glob pattern to filter files by name.\n * @param type - Optional file type key to filter by extension.\n * @returns An array of matching file paths.\n */\n const collectFiles = (baseDir: string, glob?: string, type?: string): string[] => {\n const results: string[] = [];\n const extensions = type ? TYPE_EXTENSIONS[type] : undefined;\n\n const walk = (dir: string): void => {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;\n\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.isFile()) {\n if (extensions) {\n const ext = path.extname(entry.name);\n if (extensions.includes(ext)) results.push(fullPath);\n } else if (glob) {\n if (matchGlob(glob, entry.name)) results.push(fullPath);\n } else {\n results.push(fullPath);\n }\n }\n }\n };\n\n if (fs.existsSync(baseDir) && fs.statSync(baseDir).isFile()) {\n results.push(baseDir);\n } else {\n walk(baseDir);\n }\n\n return results;\n };\n\n /**\n * Tests whether a filename matches a glob pattern.\n * @param pattern - The glob pattern to match against.\n * @param filename - The filename to test.\n * @returns True if the filename matches the pattern, false otherwise.\n */\n const matchGlob = (pattern: string, filename: string): boolean => {\n const braceMatch = pattern.match(/\\*\\.\\\\{(.+)\\\\}/);\n if (braceMatch) {\n const exts = braceMatch[1].split(',').map(e => `.${e.trim()}`);\n return exts.includes(path.extname(filename));\n }\n const escaped = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.');\n return new RegExp(`^${escaped}$`).test(filename);\n };\n\n return {\n name: 'Grep',\n description: `A powerful search tool built on regular expressions.\n\nUsage:\n- ALWAYS use Grep for search tasks. NEVER invoke \\`grep\\` or \\`rg\\` as a Bash command. The Grep tool has been optimized for correct permissions and access.\n- Supports full regex syntax (e.g., \"log.*Error\", \"function\\\\s+\\\\w+\")\n- Filter files with glob parameter (e.g., \"*.js\", \"**/*.tsx\") or type parameter (e.g., \"js\", \"py\", \"rust\")\n- Output modes: \"content\" shows matching lines, \"files_with_matches\" shows only file paths (default), \"count\" shows match counts\n- Use Task tool for open-ended searches requiring multiple rounds\n- Pattern syntax: Uses ripgrep-style regex - literal braces need escaping (use \\`interface\\\\{\\\\}\\` to find \\`interface{}\\` in Go code)\n- Multiline matching: By default patterns match within single lines only. For cross-line patterns, use \\`multiline: true\\``,\n inputSchema: z.object({\n pattern: z\n .string()\n .describe('The regular expression pattern to search for in file contents'),\n path: z\n .string()\n .optional()\n .describe('File or directory to search in. Defaults to current working directory.'),\n glob: z\n .string()\n .optional()\n .describe('Glob pattern to filter files (e.g. \"*.js\", \"*.{ts,tsx}\")'),\n type: z\n .string()\n .optional()\n .describe(\n 'File type to search (e.g., \"js\", \"ts\", \"py\"). More efficient than glob for standard file types.'\n ),\n output_mode: z\n .enum(['content', 'files_with_matches', 'count'])\n .optional()\n .describe(\n 'Output mode: \"content\" | \"files_with_matches\" | \"count\". Defaults to \"files_with_matches\".'\n ),\n multiline: z\n .boolean()\n .optional()\n .describe(\n 'Enable multiline mode where . matches newlines and patterns can span lines. Default: false.'\n ),\n case_insensitive: z\n .boolean()\n .optional()\n .describe('Case insensitive search. Default: false.'),\n context: z\n .number()\n .int()\n .optional()\n .describe(\n 'Number of lines to show before and after each match. Requires output_mode: \"content\".'\n ),\n head_limit: z\n .number()\n .int()\n .optional()\n .describe('Limit output to first N lines/entries.'),\n }),\n requireUserConfirm: true,\n\n /**\n * Searches files for a regex pattern and returns results in the specified output mode.\n *\n * @param root0 - The parameters object\n * @param root0.pattern - The regular expression pattern to search for\n * @param root0.path - File or directory to search in; defaults to cwd\n * @param root0.glob - Glob pattern to filter which files are searched\n * @param root0.type - File type shorthand (e.g. \"ts\", \"py\") to filter files\n * @param root0.output_mode - How to format results: \"content\", \"files_with_matches\", or \"count\"\n * @param root0.multiline - Whether the pattern can span multiple lines\n * @param root0.case_insensitive - Whether the search is case-insensitive\n * @param root0.context - Number of surrounding lines to include with each match\n * @param root0.head_limit - Maximum number of result entries to return\n * @returns A newline-separated string of results, or a no-matches message\n */\n call({\n pattern,\n path: searchPath,\n glob,\n type,\n output_mode = 'files_with_matches',\n multiline = false,\n case_insensitive = false,\n context,\n head_limit,\n }: {\n pattern: string;\n path?: string;\n glob?: string;\n type?: string;\n output_mode?: OutputMode;\n multiline?: boolean;\n case_insensitive?: boolean;\n context?: number;\n head_limit?: number;\n }): string {\n const baseDir = searchPath ? searchPath : process.cwd();\n\n let flags = multiline ? 'gms' : 'gm';\n if (case_insensitive) flags += 'i';\n\n const regex = new RegExp(pattern, flags);\n const files = collectFiles(baseDir, glob, type);\n\n if (files.length === 0) {\n return 'No files found to search.';\n }\n\n const results: string[] = [];\n\n for (const file of files) {\n try {\n const content = fs.readFileSync(file, 'utf-8');\n\n if (output_mode === 'files_with_matches') {\n if (regex.test(content)) {\n results.push(file);\n }\n regex.lastIndex = 0;\n } else if (output_mode === 'count') {\n const matches = content.match(regex);\n if (matches) {\n results.push(`${file}: ${matches.length}`);\n }\n regex.lastIndex = 0;\n } else if (output_mode === 'content') {\n const lines = content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const lineRegex = new RegExp(pattern, case_insensitive ? 'i' : '');\n if (lineRegex.test(lines[i])) {\n const start = context !== undefined ? Math.max(0, i - context) : i;\n const end =\n context !== undefined\n ? Math.min(lines.length - 1, i + context)\n : i;\n for (let j = start; j <= end; j++) {\n results.push(`${file}:${j + 1}:${lines[j]}`);\n }\n }\n }\n }\n } catch {\n // skip unreadable files\n }\n }\n\n if (results.length === 0) {\n return `No matches found for pattern: ${pattern}`;\n }\n\n const output = head_limit !== undefined ? results.slice(0, head_limit) : results;\n return output.join('\\n');\n },\n };\n}\n","import { z } from 'zod';\n\nimport { createToolResponse, ToolResponse } from './response';\n\n// Task type definitions\nexport type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'deleted';\n\nexport interface Task {\n id: string;\n subject: string;\n description: string;\n status: TaskStatus;\n activeForm?: string;\n owner?: string;\n metadata?: Record<string, unknown>;\n blocks: string[];\n blockedBy: string[];\n createdAt: string;\n updatedAt: string;\n}\n\n// Module-level storage\nconst taskStore = new Map<string, Task>();\nlet nextId = 1;\n\n/**\n * Generate a unique task ID\n * @returns A unique task ID as a string\n */\nfunction generateId(): string {\n return String(nextId++);\n}\n\n/**\n * Reset task store for testing purposes\n * @internal\n */\nexport function _resetTaskStore(): void {\n taskStore.clear();\n nextId = 1;\n}\n\n/**\n * Tool for creating tasks\n * @returns A Tool object for creating tasks\n */\nexport function TaskCreate() {\n return {\n name: 'TaskCreate',\n description: `Use this tool to create a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.\nIt also helps the user understand the progress of the task and overall progress of their requests.\n\n## When to Use This Tool\n\nUse this tool proactively in these scenarios:\n\n- Complex multi-step tasks - When a task requires 3 or more distinct steps or actions\n- Non-trivial and complex tasks - Tasks that require careful planning or multiple operations\n- User explicitly requests todo list - When the user directly asks you to use the todo list\n- User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)\n\nAll tasks are created with status 'pending'.`,\n inputSchema: z.object({\n subject: z\n .string()\n .describe(\n 'A brief, actionable title in imperative form (e.g., \"Fix authentication bug in login flow\")'\n ),\n description: z\n .string()\n .describe(\n 'Detailed description of what needs to be done, including context and acceptance criteria'\n ),\n activeForm: z\n .string()\n .optional()\n .describe(\n 'Present continuous form shown in the spinner when the task is in_progress (e.g., \"Fixing authentication bug\"). If omitted, the spinner shows the subject instead.'\n ),\n metadata: z\n .record(z.string(), z.unknown())\n .optional()\n .describe('Arbitrary metadata to attach to the task'),\n }),\n requireUserConfirm: false,\n\n call({\n subject,\n description,\n activeForm,\n metadata,\n }: {\n subject: string;\n description: string;\n activeForm?: string;\n metadata?: Record<string, unknown>;\n }): ToolResponse {\n const id = generateId();\n const now = new Date().toISOString();\n\n const task: Task = {\n id,\n subject,\n description,\n status: 'pending',\n activeForm,\n metadata,\n blocks: [],\n blockedBy: [],\n createdAt: now,\n updatedAt: now,\n };\n\n taskStore.set(id, task);\n\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `Task #${id} created successfully: ${subject}`,\n },\n ],\n state: 'success',\n });\n },\n };\n}\n\n/**\n * Tool for updating tasks\n * @returns A Tool object for updating tasks\n */\nexport function TaskUpdate() {\n return {\n name: 'TaskUpdate',\n description: `Use this tool to update a task in the task list.\n\n## When to Use This Tool\n\n**Mark tasks as resolved:**\n- When you have completed the work described in a task\n- When a task is no longer needed or has been superseded\n- IMPORTANT: Always mark your assigned tasks as resolved when you finish them\n\n**Delete tasks:**\n- When a task is no longer relevant or was created in error\n- Setting status to 'deleted' permanently removes the task\n\n**Update task details:**\n- When requirements change or become clearer\n- When establishing dependencies between tasks`,\n inputSchema: z.object({\n taskId: z.string().describe('The ID of the task to update'),\n status: z\n .enum(['pending', 'in_progress', 'completed', 'deleted'])\n .optional()\n .describe('New status for the task'),\n subject: z.string().optional().describe('New subject for the task'),\n description: z.string().optional().describe('New description for the task'),\n activeForm: z\n .string()\n .optional()\n .describe(\n 'Present continuous form shown in spinner when in_progress (e.g., \"Running tests\")'\n ),\n owner: z.string().optional().describe('New owner for the task'),\n metadata: z\n .record(z.string(), z.unknown())\n .optional()\n .describe('Metadata keys to merge into the task. Set a key to null to delete it.'),\n addBlocks: z.array(z.string()).optional().describe('Task IDs that this task blocks'),\n addBlockedBy: z.array(z.string()).optional().describe('Task IDs that block this task'),\n }),\n requireUserConfirm: false,\n\n call({\n taskId,\n status,\n subject,\n description,\n activeForm,\n owner,\n metadata,\n addBlocks,\n addBlockedBy,\n }: {\n taskId: string;\n status?: TaskStatus;\n subject?: string;\n description?: string;\n activeForm?: string;\n owner?: string;\n metadata?: Record<string, unknown>;\n addBlocks?: string[];\n addBlockedBy?: string[];\n }): ToolResponse {\n const task = taskStore.get(taskId);\n if (!task) {\n throw new Error(`Task not found: ${taskId}`);\n }\n\n // Validate dependencies exist\n if (addBlocks) {\n for (const depId of addBlocks) {\n if (!taskStore.has(depId)) {\n throw new Error(`Cannot add dependency: task ${depId} does not exist`);\n }\n }\n }\n if (addBlockedBy) {\n for (const depId of addBlockedBy) {\n if (!taskStore.has(depId)) {\n throw new Error(`Cannot add dependency: task ${depId} does not exist`);\n }\n }\n }\n\n // Update fields\n if (subject !== undefined) task.subject = subject;\n if (description !== undefined) task.description = description;\n if (activeForm !== undefined) task.activeForm = activeForm;\n if (owner !== undefined) task.owner = owner;\n\n // Merge metadata\n if (metadata !== undefined) {\n if (!task.metadata) {\n task.metadata = {};\n }\n for (const [key, value] of Object.entries(metadata)) {\n if (value === null) {\n delete task.metadata[key];\n } else {\n task.metadata[key] = value;\n }\n }\n }\n\n // Add dependencies with deduplication\n if (addBlocks) {\n task.blocks = [...new Set([...task.blocks, ...addBlocks])];\n }\n if (addBlockedBy) {\n task.blockedBy = [...new Set([...task.blockedBy, ...addBlockedBy])];\n }\n\n task.updatedAt = new Date().toISOString();\n\n // Handle status change\n if (status !== undefined) {\n if (status === 'deleted') {\n taskStore.delete(taskId);\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `Task #${taskId} deleted successfully`,\n },\n ],\n state: 'success',\n });\n }\n task.status = status;\n }\n\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: `Task #${taskId} updated successfully`,\n },\n ],\n state: 'success',\n });\n },\n };\n}\n\n/**\n * Tool for retrieving a single task\n * @returns A Tool object for retrieving a task by ID\n */\nexport function TaskGet() {\n return {\n name: 'TaskGet',\n description: `Use this tool to retrieve a task by its ID from the task list.\n\n## When to Use This Tool\n\n- When you need the full description and context before starting work on a task\n- To understand task dependencies (what it blocks, what blocks it)\n- After being assigned a task, to get complete requirements`,\n inputSchema: z.object({\n taskId: z.string().describe('The ID of the task to retrieve'),\n }),\n requireUserConfirm: false,\n\n call({ taskId }: { taskId: string }): ToolResponse {\n const task = taskStore.get(taskId);\n if (!task) {\n throw new Error(`Task not found: ${taskId}`);\n }\n\n let text = `Task #${task.id}: ${task.subject}\\n`;\n text += `Status: ${task.status}\\n`;\n text += `Description: ${task.description}\\n`;\n\n if (task.activeForm) {\n text += `Active Form: ${task.activeForm}\\n`;\n }\n if (task.owner) {\n text += `Owner: ${task.owner}\\n`;\n }\n if (task.blocks.length > 0) {\n text += `Blocks: ${task.blocks.join(', ')}\\n`;\n }\n if (task.blockedBy.length > 0) {\n text += `Blocked By: ${task.blockedBy.join(', ')}\\n`;\n }\n if (task.metadata && Object.keys(task.metadata).length > 0) {\n text += `Metadata: ${JSON.stringify(task.metadata, null, 2)}\\n`;\n }\n text += `Created: ${task.createdAt}\\n`;\n text += `Updated: ${task.updatedAt}`;\n\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text,\n },\n ],\n state: 'success',\n });\n },\n };\n}\n\n/**\n * Tool for listing all active tasks\n * @returns A Tool object for listing all active tasks\n */\nexport function TaskList() {\n return {\n name: 'TaskList',\n description: `Use this tool to list all tasks in the task list.\n\n## When to Use This Tool\n\n- To see what tasks are available to work on (status: 'pending', no owner, not blocked)\n- To check overall progress on the project\n- To find tasks that are blocked and need dependencies resolved\n- After completing a task, to check for newly unblocked work or claim the next available task`,\n inputSchema: z.object({}),\n requireUserConfirm: false,\n\n call(): ToolResponse {\n // Filter to only pending and in_progress tasks\n const activeTasks = Array.from(taskStore.values())\n .filter(task => task.status === 'pending' || task.status === 'in_progress')\n .sort((a, b) => Number(a.id) - Number(b.id));\n\n if (activeTasks.length === 0) {\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: 'No active tasks found',\n },\n ],\n state: 'success',\n });\n }\n\n const lines = activeTasks.map(task => {\n let line = `#${task.id} [${task.status}] ${task.subject}`;\n if (task.blockedBy.length > 0) {\n line += ` (blocked by: #${task.blockedBy.join(', #')})`;\n }\n return line;\n });\n\n return createToolResponse({\n content: [\n {\n id: crypto.randomUUID(),\n type: 'text',\n text: lines.join('\\n'),\n },\n ],\n state: 'success',\n });\n },\n };\n}\n"],"mappings":"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","names":["import_zod","tool","matter","import_zod","import_zod","import_zod","import_zod","import_zod","import_zod","import_zod"]}
|