@jcheesepkg/nanobot 0.8.4 → 0.8.5
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/loop.d.mts.map +1 -1
- package/dist/agent/loop.mjs +4 -1
- package/dist/agent/loop.mjs.map +1 -1
- package/dist/agent/tools/flex.d.mts +162 -0
- package/dist/agent/tools/flex.d.mts.map +1 -0
- package/dist/agent/tools/flex.mjs +501 -0
- package/dist/agent/tools/flex.mjs.map +1 -0
- package/dist/config/schema.d.mts +36 -36
- package/package.json +1 -1
- package/skills/daily-summary/SKILL.md +17 -31
- package/skills/fortune/SKILL.md +23 -46
- package/skills/hydration/SKILL.md +19 -31
- package/skills/flex/SKILL.md +0 -280
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.d.mts","names":[],"sources":["../../src/agent/loop.ts"],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"loop.d.mts","names":[],"sources":["../../src/agent/loop.ts"],"mappings":";;;;;;;;;;;;AAqCA;;;;;;;cAAa,SAAA;EAAA,QACH,GAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAAA,QACA,KAAA;EAAA,QACA,kBAAA;EAAA,QACA,SAAA;EAAA,QACA,aAAA;EAAA,QACA,YAAA;EAAA,SAEC,OAAA,EAAS,cAAA;EAAA,SACT,QAAA,EAAU,cAAA;EAAA,SACV,KAAA,EAAO,YAAA;EAAA,SACP,SAAA,EAAW,eAAA;EAAA,QAEZ,QAAA;EARA;EAAA,QAWA,QAAA;cAEI,MAAA;IACV,GAAA,EAAK,UAAA;IACL,QAAA,EAAU,WAAA;IACV,SAAA;IACA,KAAA;IACA,kBAAA;IACA,SAAA;IACA,aAAA;IACA,YAAA;IACA,WAAA;IACA,UAAA,GAAa,cAAA;IACb,mBAAA;IACA,YAAA;IACA,aAAA;IACA,WAAA,GAAc,IAAA;EAAA;EAAA,QAqCR,oBAAA;EA9CN;EA+GI,GAAA,CAAA,GAAO,OAAA;EA7GX;EA8IF,IAAA,CAAA;EA5IE;EAAA,QAkJY,cAAA;EAAA,QA4FA,oBAAA;EAAA,QA4CA,YAAA;EAvRZ;EAAA,QAqXM,uBAAA;EAAA,QAkBA,kBAAA;EArYQ;EAuZV,aAAA,CACJ,OAAA,UACA,UAAA,WACA,OAAA,WACA,MAAA,YACC,OAAA;EAvXK;EAAA,QA+YM,iBAAA;AAAA"}
|
package/dist/agent/loop.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { WebFetchTool, WebSearchTool } from "./tools/web.mjs";
|
|
|
9
9
|
import { MessageTool } from "./tools/message.mjs";
|
|
10
10
|
import { SpawnTool } from "./tools/spawn.mjs";
|
|
11
11
|
import { CronTool } from "./tools/cron.mjs";
|
|
12
|
+
import { FlexTool } from "./tools/flex.mjs";
|
|
12
13
|
import { SubagentManager } from "./subagent.mjs";
|
|
13
14
|
import { SessionManager } from "../session/manager.mjs";
|
|
14
15
|
|
|
@@ -93,6 +94,7 @@ var AgentLoop = class {
|
|
|
93
94
|
registerIfEnabled(new MessageTool({ sendCallback: (msg) => this.bus.publishOutbound(msg) }));
|
|
94
95
|
registerIfEnabled(new SpawnTool(this.subagents));
|
|
95
96
|
registerIfEnabled(new CronTool());
|
|
97
|
+
registerIfEnabled(new FlexTool());
|
|
96
98
|
if (customTools && customTools.length > 0) for (const tool of customTools) registerIfEnabled(tool);
|
|
97
99
|
}
|
|
98
100
|
/** Run the agent loop, processing messages from the bus. */
|
|
@@ -280,7 +282,8 @@ var AgentLoop = class {
|
|
|
280
282
|
write_file: "ファイルを書き込み中...",
|
|
281
283
|
edit_file: "ファイルを編集中...",
|
|
282
284
|
exec: "コマンドを実行中...",
|
|
283
|
-
spawn: "サブエージェントを起動中..."
|
|
285
|
+
spawn: "サブエージェントを起動中...",
|
|
286
|
+
flex_message: "カードを作成中..."
|
|
284
287
|
};
|
|
285
288
|
for (const name of toolNames) if (fallbacks[name]) return fallbacks[name];
|
|
286
289
|
return "ちょっと待ってね...";
|
package/dist/agent/loop.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.mjs","names":[],"sources":["../../src/agent/loop.ts"],"sourcesContent":["import type { LLMProvider, ChatMessage, ToolCallRequest } from \"../providers/base.js\";\nimport type { MessageBus } from \"../bus/queue.js\";\nimport type {\n InboundMessage,\n OutboundMessage,\n} from \"../bus/events.js\";\nimport { createOutboundMessage } from \"../bus/events.js\";\nimport { ContextBuilder } from \"./context.js\";\nimport { ToolRegistry } from \"./tools/registry.js\";\nimport {\n ReadFileTool,\n WriteFileTool,\n EditFileTool,\n ListDirTool,\n} from \"./tools/filesystem.js\";\nimport { ExecTool } from \"./tools/shell.js\";\nimport { WebSearchTool, WebFetchTool } from \"./tools/web.js\";\nimport { MessageTool } from \"./tools/message.js\";\nimport { SpawnTool } from \"./tools/spawn.js\";\nimport { CronTool } from \"./tools/cron.js\";\nimport { SubagentManager } from \"./subagent.js\";\nimport { getBuiltinSkillsDir } from \"./skills.js\";\nimport { SessionManager, Session } from \"../session/manager.js\";\nimport { MemoryStore } from \"./memory.js\";\nimport type { ExecToolConfig } from \"../config/schema.js\";\nimport type { Tool } from \"./tools/base.js\";\n\n/**\n * The agent loop: core processing engine.\n *\n * 1. Receives messages from the bus\n * 2. Builds context with history, memory, skills\n * 3. Calls the LLM\n * 4. Executes tool calls\n * 5. Sends responses back\n */\nexport class AgentLoop {\n private bus: MessageBus;\n private provider: LLMProvider;\n private workspace: string;\n private model: string;\n private consolidationModel: string;\n private maxTokens: number;\n private maxIterations: number;\n private memoryWindow: number;\n\n readonly context: ContextBuilder;\n readonly sessions: SessionManager;\n readonly tools: ToolRegistry;\n readonly subagents: SubagentManager;\n\n private _running = false;\n\n /** In-flight AbortControllers keyed by session key. */\n private inflight = new Map<string, AbortController>();\n\n constructor(params: {\n bus: MessageBus;\n provider: LLMProvider;\n workspace: string;\n model?: string;\n consolidationModel?: string;\n maxTokens?: number;\n maxIterations?: number;\n memoryWindow?: number;\n braveApiKey?: string;\n execConfig?: ExecToolConfig;\n restrictToWorkspace?: boolean;\n toolsEnabled?: string[];\n toolsDisabled?: string[];\n customTools?: Tool[];\n }) {\n this.bus = params.bus;\n this.provider = params.provider;\n this.workspace = params.workspace;\n this.model = params.model ?? params.provider.getDefaultModel();\n this.consolidationModel = params.consolidationModel ?? this.model;\n this.maxTokens = params.maxTokens ?? 8192;\n this.maxIterations = params.maxIterations ?? 20;\n this.memoryWindow = params.memoryWindow ?? 50;\n\n const execConfig = params.execConfig ?? { timeout: 60 };\n const restrictToWorkspace = params.restrictToWorkspace ?? false;\n\n this.context = new ContextBuilder(params.workspace);\n this.sessions = new SessionManager(params.workspace);\n this.tools = new ToolRegistry();\n this.subagents = new SubagentManager({\n provider: params.provider,\n workspace: params.workspace,\n bus: params.bus,\n model: this.model,\n braveApiKey: params.braveApiKey,\n execConfig,\n restrictToWorkspace,\n });\n\n this.registerDefaultTools(\n execConfig,\n restrictToWorkspace,\n params.braveApiKey,\n params.toolsEnabled,\n params.toolsDisabled,\n params.customTools,\n );\n }\n\n private registerDefaultTools(\n execConfig: ExecToolConfig,\n restrictToWorkspace: boolean,\n braveApiKey?: string,\n toolsEnabled?: string[],\n toolsDisabled?: string[],\n customTools?: Tool[],\n ): void {\n const enabled = new Set(toolsEnabled ?? []);\n const disabled = new Set(toolsDisabled ?? []);\n const hasAllowlist = enabled.size > 0;\n const shouldRegister = (name: string): boolean =>\n (hasAllowlist ? enabled.has(name) : true) && !disabled.has(name);\n\n const registerIfEnabled = (tool: Tool): void => {\n if (shouldRegister(tool.name)) {\n this.tools.register(tool);\n }\n };\n\n // File tools — pass allowedDir when restrictToWorkspace is enabled\n const allowedDir = restrictToWorkspace ? this.workspace : undefined;\n const readOnlyPaths = restrictToWorkspace ? [getBuiltinSkillsDir()] : undefined;\n registerIfEnabled(new ReadFileTool({ allowedDir, readOnlyPaths }));\n registerIfEnabled(new WriteFileTool({ allowedDir }));\n registerIfEnabled(new EditFileTool({ allowedDir }));\n registerIfEnabled(new ListDirTool({ allowedDir, readOnlyPaths }));\n\n // Shell tool\n registerIfEnabled(\n new ExecTool({\n workingDir: this.workspace,\n timeout: execConfig.timeout,\n restrictToWorkspace,\n }),\n );\n\n // Web tools\n registerIfEnabled(new WebSearchTool({ apiKey: braveApiKey }));\n registerIfEnabled(new WebFetchTool());\n\n // Message tool\n const messageTool = new MessageTool({\n sendCallback: (msg) => this.bus.publishOutbound(msg),\n });\n registerIfEnabled(messageTool);\n\n // Spawn tool\n const spawnTool = new SpawnTool(this.subagents);\n registerIfEnabled(spawnTool);\n\n // Cron tool — always registered, uses DO scheduling via worker API\n registerIfEnabled(new CronTool());\n\n if (customTools && customTools.length > 0) {\n for (const tool of customTools) {\n registerIfEnabled(tool);\n }\n }\n }\n\n /** Run the agent loop, processing messages from the bus. */\n async run(): Promise<void> {\n this._running = true;\n console.log(\"Agent loop started\");\n\n while (this._running) {\n try {\n const msg = await this.bus.consumeInboundTimeout(1000);\n\n // Process concurrently so new messages can abort in-flight ones\n this.processMessage(msg)\n .then(async (response) => {\n if (response) {\n await this.bus.publishOutbound(response);\n }\n })\n .catch(async (err) => {\n if (isAbortError(err)) return; // Already handled\n console.error(\"Error processing message:\", err);\n await this.bus.publishOutbound(\n createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: `Sorry, I encountered an error: ${err instanceof Error ? err.message : err}`,\n }),\n );\n });\n } catch {\n // timeout, continue\n }\n }\n }\n\n /** Stop the agent loop. */\n stop(): void {\n this._running = false;\n console.log(\"Agent loop stopping\");\n }\n\n /** Process a single inbound message. */\n private async processMessage(\n msg: InboundMessage,\n ): Promise<OutboundMessage | null> {\n // Handle system messages (subagent announces)\n if (msg.channel === \"system\") {\n return this.processSystemMessage(msg);\n }\n\n console.log(`Processing message from ${msg.channel}:${msg.senderId}`);\n\n const sessionKey = `${msg.channel}:${msg.chatId}`;\n\n // Abort any in-flight request for this session\n const existing = this.inflight.get(sessionKey);\n if (existing) {\n console.log(`Aborting in-flight request for ${sessionKey}`);\n existing.abort();\n }\n\n // Create a new AbortController for this request\n const controller = new AbortController();\n this.inflight.set(sessionKey, controller);\n\n const session = this.sessions.getOrCreate(sessionKey);\n\n // Consolidate memory if session is too large\n if (session.history.length > this.memoryWindow) {\n await this.consolidateMemory(session);\n }\n\n // Update tool contexts\n this.updateToolContexts(msg.channel, msg.chatId);\n\n // Build initial messages\n const messages = this.context.buildMessages({\n history: session.getHistory(),\n currentMessage: msg.content,\n media: msg.media.length > 0 ? msg.media : undefined,\n channel: msg.channel,\n chatId: msg.chatId,\n });\n\n // The messages array is: [system, ...history, currentUser]\n // We want to save from the current user message onward (skip system + old history).\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen; // 1 for system prompt\n\n try {\n // Agent loop (mutates messages by appending assistant/tool messages)\n const result = await this.runAgentLoop(messages, controller.signal, (text) => {\n if (text && text.trim().length > 0) {\n this.bus.publishOutbound(\n createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: text,\n }),\n );\n }\n });\n\n // Save the new messages from this turn (user + all agent loop messages)\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: result.content,\n });\n } catch (err) {\n if (isAbortError(err)) {\n // Request was aborted because a new message arrived.\n // Save the user message to history so the next request has context,\n // but don't save any assistant response.\n const userMessages = messages.slice(newMsgStart).filter((m) => m.role === \"user\");\n if (userMessages.length > 0) {\n session.addTurnMessages(userMessages);\n this.sessions.save(session);\n }\n console.log(`Request aborted for ${sessionKey}, user message saved to history`);\n return null; // No response -- the new message will handle it\n }\n throw err; // Re-throw non-abort errors\n } finally {\n // Clean up if this is still our controller\n if (this.inflight.get(sessionKey) === controller) {\n this.inflight.delete(sessionKey);\n }\n }\n }\n\n private async processSystemMessage(\n msg: InboundMessage,\n ): Promise<OutboundMessage | null> {\n console.log(`Processing system message from ${msg.senderId}`);\n\n let originChannel: string;\n let originChatId: string;\n\n if (msg.chatId.includes(\":\")) {\n const [ch, id] = msg.chatId.split(\":\", 2);\n originChannel = ch;\n originChatId = id;\n } else {\n originChannel = \"cli\";\n originChatId = msg.chatId;\n }\n\n const sessionKey = `${originChannel}:${originChatId}`;\n const session = this.sessions.getOrCreate(sessionKey);\n\n this.updateToolContexts(originChannel, originChatId);\n\n const messages = this.context.buildMessages({\n history: session.getHistory(),\n currentMessage: msg.content,\n channel: originChannel,\n chatId: originChatId,\n });\n\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen;\n\n const result = await this.runAgentLoop(messages);\n\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return createOutboundMessage({\n channel: originChannel,\n chatId: originChatId,\n content: result.content,\n });\n }\n\n private async runAgentLoop(\n messages: ChatMessage[],\n signal?: AbortSignal,\n onToolCallText?: (text: string) => void,\n ): Promise<{ content: string; toolsUsed: string[] }> {\n let finalContent: string | null = null;\n let sentToolCallNotice = false;\n const toolsUsed: string[] = [];\n\n for (let i = 0; i < this.maxIterations; i++) {\n const response = await this.provider.chat({\n messages,\n tools: this.tools.getDefinitions(),\n model: this.model,\n maxTokens: this.maxTokens,\n signal,\n });\n\n if (response.hasToolCalls) {\n // Send an interim message so the user knows we're working\n if (!sentToolCallNotice && onToolCallText) {\n const interimText = response.content?.trim()\n || this.getToolCallFallbackText(response.toolCalls);\n if (interimText) {\n onToolCallText(interimText);\n }\n sentToolCallNotice = true;\n }\n const toolCallDicts = response.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n },\n }));\n\n this.context.addAssistantMessage(\n messages,\n response.content,\n toolCallDicts,\n );\n\n for (const tc of response.toolCalls) {\n toolsUsed.push(tc.name);\n console.log(`Tool: ${tc.name}(${JSON.stringify(tc.arguments)})`);\n\n // Detect skill reads\n if (tc.name === \"read_file\") {\n const path = String(tc.arguments?.path ?? \"\");\n const skillMatch = path.match(/skills\\/([^/]+)\\/SKILL\\.md$/);\n if (skillMatch) {\n console.log(`Skill activated: ${skillMatch[1]}`);\n }\n }\n\n const result = await this.tools.execute(tc.name, tc.arguments);\n this.context.addToolResult(messages, tc.id, tc.name, result);\n }\n } else {\n finalContent = response.content;\n\n // If the LLM returned empty content after tool use, nudge it to respond\n if ((!finalContent || finalContent.trim().length === 0) && i > 0) {\n messages.push({\n role: \"assistant\",\n content: \"\",\n });\n messages.push({\n role: \"user\",\n content: \"(You used tools but didn't respond to the user. Please provide a brief response summarizing what you did.)\",\n });\n continue;\n }\n\n // Push the final assistant message so it gets persisted with the turn\n messages.push({ role: \"assistant\", content: finalContent ?? \"\" });\n break;\n }\n }\n\n if (!finalContent || finalContent.trim().length === 0) {\n finalContent = \"I've completed processing but have no response to give.\";\n }\n\n // If we exhausted iterations without a non-tool-call response, still persist the final text\n if (messages[messages.length - 1]?.role !== \"assistant\" || messages[messages.length - 1]?.content !== finalContent) {\n messages.push({ role: \"assistant\", content: finalContent });\n }\n\n return { content: finalContent, toolsUsed };\n }\n\n /** Generate a fallback interim message based on which tools are being called. */\n private getToolCallFallbackText(toolCalls: ToolCallRequest[]): string {\n const toolNames = toolCalls.map((tc) => tc.name);\n const fallbacks: Record<string, string> = {\n web_search: \"検索中...\",\n web_fetch: \"ページを読み込み中...\",\n read_file: \"ファイルを確認中...\",\n write_file: \"ファイルを書き込み中...\",\n edit_file: \"ファイルを編集中...\",\n exec: \"コマンドを実行中...\",\n spawn: \"サブエージェントを起動中...\",\n };\n for (const name of toolNames) {\n if (fallbacks[name]) return fallbacks[name];\n }\n return \"ちょっと待ってね...\";\n }\n\n private updateToolContexts(channel: string, chatId: string): void {\n const messageTool = this.tools.get(\"message\");\n if (messageTool instanceof MessageTool) {\n messageTool.setContext(channel, chatId);\n }\n\n const spawnTool = this.tools.get(\"spawn\");\n if (spawnTool instanceof SpawnTool) {\n spawnTool.setContext(channel, chatId);\n }\n\n const cronTool = this.tools.get(\"cron\");\n if (cronTool instanceof CronTool) {\n cronTool.setContext(channel, chatId);\n }\n }\n\n /** Process a message directly (for CLI or cron usage). */\n async processDirect(\n content: string,\n sessionKey = \"cli:direct\",\n channel = \"cli\",\n chatId = \"direct\",\n ): Promise<string> {\n // Use inline version of processMessage for direct calls\n const session = this.sessions.getOrCreate(sessionKey);\n this.updateToolContexts(channel, chatId);\n\n const messages = this.context.buildMessages({\n history: session.getHistory(),\n currentMessage: content,\n channel,\n chatId,\n });\n\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen;\n\n const result = await this.runAgentLoop(messages);\n\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return result.content;\n }\n\n /** Consolidate old messages into MEMORY.md + HISTORY.md, then trim session. */\n private async consolidateMemory(session: Session): Promise<void> {\n const memory = new MemoryStore(this.workspace);\n const keepCount = Math.min(10, Math.max(2, Math.floor(this.memoryWindow / 2)));\n const oldMessages = session.history.slice(0, -keepCount);\n if (oldMessages.length === 0) return;\n\n console.log(`Memory consolidation: ${session.history.length} messages, archiving ${oldMessages.length}, keeping ${keepCount}`);\n\n // Format messages for LLM\n const lines: string[] = [];\n for (const m of oldMessages) {\n const content = typeof m.content === \"string\" ? m.content : \"\";\n if (!content) continue;\n const role = m.role.toUpperCase();\n const tools = m.toolsUsed ? ` [tools: ${m.toolsUsed.join(\", \")}]` : \"\";\n const ts = m.timestamp ? `[${m.timestamp.slice(0, 16)}] ` : \"\";\n lines.push(`${ts}${role}${tools}: ${content}`);\n }\n const conversation = lines.join(\"\\n\");\n const currentMemory = memory.readLongTerm();\n\n const prompt = `You are a memory consolidation agent. Process this conversation and return a JSON object with exactly two keys:\n\n1. \"history_entry\": A paragraph (2-5 sentences) summarizing the key events/decisions/topics. Start with a timestamp like [YYYY-MM-DD HH:MM]. Include enough detail to be useful when found by grep search later.\n\n2. \"memory_update\": The updated long-term memory content. Add any new facts: user location, preferences, personal info, habits, project context, technical decisions, tools/services used. If nothing new, return the existing content unchanged.\n\n## Current Long-term Memory\n${currentMemory || \"(empty)\"}\n\n## Conversation to Process\n${conversation}\n\nRespond with ONLY valid JSON, no markdown fences.`;\n\n try {\n const response = await this.provider.chat({\n messages: [\n { role: \"system\", content: \"You are a memory consolidation agent. Respond only with valid JSON.\" },\n { role: \"user\", content: prompt },\n ],\n model: this.consolidationModel,\n });\n\n let text = (response.content || \"\").trim();\n if (text.startsWith(\"```\")) {\n text = text.split(\"\\n\").slice(1).join(\"\\n\").split(\"```\")[0].trim();\n }\n\n const result = JSON.parse(text);\n\n if (result.history_entry) {\n const entry = typeof result.history_entry === \"string\"\n ? result.history_entry\n : JSON.stringify(result.history_entry, null, 2);\n memory.appendHistory(entry);\n }\n if (result.memory_update && result.memory_update !== currentMemory) {\n const update = typeof result.memory_update === \"string\"\n ? result.memory_update\n : JSON.stringify(result.memory_update, null, 2);\n memory.writeLongTerm(update);\n }\n } catch (err) {\n console.error(\"Memory consolidation failed:\", err);\n // Fallback: append raw conversation to history so it's not lost\n const fallbackEntry = `[${new Date().toISOString().slice(0, 16)}] Consolidation failed, archiving raw messages:\\n${conversation.slice(0, 2000)}`;\n memory.appendHistory(fallbackEntry);\n }\n\n // Always trim session to prevent unbounded growth\n session.trimHistory(keepCount);\n this.sessions.save(session);\n console.log(`Memory consolidation done, session trimmed to ${session.history.length} messages`);\n }\n}\n\n/** Check if an error is an abort/cancellation error. */\nfunction isAbortError(err: unknown): boolean {\n if (err instanceof DOMException && err.name === \"AbortError\") return true;\n if (err instanceof Error) {\n if (err.name === \"AbortError\") return true;\n // OpenAI SDK wraps abort as APIUserAbortError\n if (err.name === \"APIUserAbortError\") return true;\n if (err.message.includes(\"abort\")) return true;\n }\n return false;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loop.mjs","names":[],"sources":["../../src/agent/loop.ts"],"sourcesContent":["import type { LLMProvider, ChatMessage, ToolCallRequest } from \"../providers/base.js\";\nimport type { MessageBus } from \"../bus/queue.js\";\nimport type {\n InboundMessage,\n OutboundMessage,\n} from \"../bus/events.js\";\nimport { createOutboundMessage } from \"../bus/events.js\";\nimport { ContextBuilder } from \"./context.js\";\nimport { ToolRegistry } from \"./tools/registry.js\";\nimport {\n ReadFileTool,\n WriteFileTool,\n EditFileTool,\n ListDirTool,\n} from \"./tools/filesystem.js\";\nimport { ExecTool } from \"./tools/shell.js\";\nimport { WebSearchTool, WebFetchTool } from \"./tools/web.js\";\nimport { MessageTool } from \"./tools/message.js\";\nimport { SpawnTool } from \"./tools/spawn.js\";\nimport { CronTool } from \"./tools/cron.js\";\nimport { FlexTool } from \"./tools/flex.js\";\nimport { SubagentManager } from \"./subagent.js\";\nimport { getBuiltinSkillsDir } from \"./skills.js\";\nimport { SessionManager, Session } from \"../session/manager.js\";\nimport { MemoryStore } from \"./memory.js\";\nimport type { ExecToolConfig } from \"../config/schema.js\";\nimport type { Tool } from \"./tools/base.js\";\n\n/**\n * The agent loop: core processing engine.\n *\n * 1. Receives messages from the bus\n * 2. Builds context with history, memory, skills\n * 3. Calls the LLM\n * 4. Executes tool calls\n * 5. Sends responses back\n */\nexport class AgentLoop {\n private bus: MessageBus;\n private provider: LLMProvider;\n private workspace: string;\n private model: string;\n private consolidationModel: string;\n private maxTokens: number;\n private maxIterations: number;\n private memoryWindow: number;\n\n readonly context: ContextBuilder;\n readonly sessions: SessionManager;\n readonly tools: ToolRegistry;\n readonly subagents: SubagentManager;\n\n private _running = false;\n\n /** In-flight AbortControllers keyed by session key. */\n private inflight = new Map<string, AbortController>();\n\n constructor(params: {\n bus: MessageBus;\n provider: LLMProvider;\n workspace: string;\n model?: string;\n consolidationModel?: string;\n maxTokens?: number;\n maxIterations?: number;\n memoryWindow?: number;\n braveApiKey?: string;\n execConfig?: ExecToolConfig;\n restrictToWorkspace?: boolean;\n toolsEnabled?: string[];\n toolsDisabled?: string[];\n customTools?: Tool[];\n }) {\n this.bus = params.bus;\n this.provider = params.provider;\n this.workspace = params.workspace;\n this.model = params.model ?? params.provider.getDefaultModel();\n this.consolidationModel = params.consolidationModel ?? this.model;\n this.maxTokens = params.maxTokens ?? 8192;\n this.maxIterations = params.maxIterations ?? 20;\n this.memoryWindow = params.memoryWindow ?? 50;\n\n const execConfig = params.execConfig ?? { timeout: 60 };\n const restrictToWorkspace = params.restrictToWorkspace ?? false;\n\n this.context = new ContextBuilder(params.workspace);\n this.sessions = new SessionManager(params.workspace);\n this.tools = new ToolRegistry();\n this.subagents = new SubagentManager({\n provider: params.provider,\n workspace: params.workspace,\n bus: params.bus,\n model: this.model,\n braveApiKey: params.braveApiKey,\n execConfig,\n restrictToWorkspace,\n });\n\n this.registerDefaultTools(\n execConfig,\n restrictToWorkspace,\n params.braveApiKey,\n params.toolsEnabled,\n params.toolsDisabled,\n params.customTools,\n );\n }\n\n private registerDefaultTools(\n execConfig: ExecToolConfig,\n restrictToWorkspace: boolean,\n braveApiKey?: string,\n toolsEnabled?: string[],\n toolsDisabled?: string[],\n customTools?: Tool[],\n ): void {\n const enabled = new Set(toolsEnabled ?? []);\n const disabled = new Set(toolsDisabled ?? []);\n const hasAllowlist = enabled.size > 0;\n const shouldRegister = (name: string): boolean =>\n (hasAllowlist ? enabled.has(name) : true) && !disabled.has(name);\n\n const registerIfEnabled = (tool: Tool): void => {\n if (shouldRegister(tool.name)) {\n this.tools.register(tool);\n }\n };\n\n // File tools — pass allowedDir when restrictToWorkspace is enabled\n const allowedDir = restrictToWorkspace ? this.workspace : undefined;\n const readOnlyPaths = restrictToWorkspace ? [getBuiltinSkillsDir()] : undefined;\n registerIfEnabled(new ReadFileTool({ allowedDir, readOnlyPaths }));\n registerIfEnabled(new WriteFileTool({ allowedDir }));\n registerIfEnabled(new EditFileTool({ allowedDir }));\n registerIfEnabled(new ListDirTool({ allowedDir, readOnlyPaths }));\n\n // Shell tool\n registerIfEnabled(\n new ExecTool({\n workingDir: this.workspace,\n timeout: execConfig.timeout,\n restrictToWorkspace,\n }),\n );\n\n // Web tools\n registerIfEnabled(new WebSearchTool({ apiKey: braveApiKey }));\n registerIfEnabled(new WebFetchTool());\n\n // Message tool\n const messageTool = new MessageTool({\n sendCallback: (msg) => this.bus.publishOutbound(msg),\n });\n registerIfEnabled(messageTool);\n\n // Spawn tool\n const spawnTool = new SpawnTool(this.subagents);\n registerIfEnabled(spawnTool);\n\n // Cron tool — always registered, uses DO scheduling via worker API\n registerIfEnabled(new CronTool());\n\n // Flex Message tool — builds LINE Flex JSON from structured data\n registerIfEnabled(new FlexTool());\n\n if (customTools && customTools.length > 0) {\n for (const tool of customTools) {\n registerIfEnabled(tool);\n }\n }\n }\n\n /** Run the agent loop, processing messages from the bus. */\n async run(): Promise<void> {\n this._running = true;\n console.log(\"Agent loop started\");\n\n while (this._running) {\n try {\n const msg = await this.bus.consumeInboundTimeout(1000);\n\n // Process concurrently so new messages can abort in-flight ones\n this.processMessage(msg)\n .then(async (response) => {\n if (response) {\n await this.bus.publishOutbound(response);\n }\n })\n .catch(async (err) => {\n if (isAbortError(err)) return; // Already handled\n console.error(\"Error processing message:\", err);\n await this.bus.publishOutbound(\n createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: `Sorry, I encountered an error: ${err instanceof Error ? err.message : err}`,\n }),\n );\n });\n } catch {\n // timeout, continue\n }\n }\n }\n\n /** Stop the agent loop. */\n stop(): void {\n this._running = false;\n console.log(\"Agent loop stopping\");\n }\n\n /** Process a single inbound message. */\n private async processMessage(\n msg: InboundMessage,\n ): Promise<OutboundMessage | null> {\n // Handle system messages (subagent announces)\n if (msg.channel === \"system\") {\n return this.processSystemMessage(msg);\n }\n\n console.log(`Processing message from ${msg.channel}:${msg.senderId}`);\n\n const sessionKey = `${msg.channel}:${msg.chatId}`;\n\n // Abort any in-flight request for this session\n const existing = this.inflight.get(sessionKey);\n if (existing) {\n console.log(`Aborting in-flight request for ${sessionKey}`);\n existing.abort();\n }\n\n // Create a new AbortController for this request\n const controller = new AbortController();\n this.inflight.set(sessionKey, controller);\n\n const session = this.sessions.getOrCreate(sessionKey);\n\n // Consolidate memory if session is too large\n if (session.history.length > this.memoryWindow) {\n await this.consolidateMemory(session);\n }\n\n // Update tool contexts\n this.updateToolContexts(msg.channel, msg.chatId);\n\n // Build initial messages\n const messages = this.context.buildMessages({\n history: session.getHistory(),\n currentMessage: msg.content,\n media: msg.media.length > 0 ? msg.media : undefined,\n channel: msg.channel,\n chatId: msg.chatId,\n });\n\n // The messages array is: [system, ...history, currentUser]\n // We want to save from the current user message onward (skip system + old history).\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen; // 1 for system prompt\n\n try {\n // Agent loop (mutates messages by appending assistant/tool messages)\n const result = await this.runAgentLoop(messages, controller.signal, (text) => {\n if (text && text.trim().length > 0) {\n this.bus.publishOutbound(\n createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: text,\n }),\n );\n }\n });\n\n // Save the new messages from this turn (user + all agent loop messages)\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: result.content,\n });\n } catch (err) {\n if (isAbortError(err)) {\n // Request was aborted because a new message arrived.\n // Save the user message to history so the next request has context,\n // but don't save any assistant response.\n const userMessages = messages.slice(newMsgStart).filter((m) => m.role === \"user\");\n if (userMessages.length > 0) {\n session.addTurnMessages(userMessages);\n this.sessions.save(session);\n }\n console.log(`Request aborted for ${sessionKey}, user message saved to history`);\n return null; // No response -- the new message will handle it\n }\n throw err; // Re-throw non-abort errors\n } finally {\n // Clean up if this is still our controller\n if (this.inflight.get(sessionKey) === controller) {\n this.inflight.delete(sessionKey);\n }\n }\n }\n\n private async processSystemMessage(\n msg: InboundMessage,\n ): Promise<OutboundMessage | null> {\n console.log(`Processing system message from ${msg.senderId}`);\n\n let originChannel: string;\n let originChatId: string;\n\n if (msg.chatId.includes(\":\")) {\n const [ch, id] = msg.chatId.split(\":\", 2);\n originChannel = ch;\n originChatId = id;\n } else {\n originChannel = \"cli\";\n originChatId = msg.chatId;\n }\n\n const sessionKey = `${originChannel}:${originChatId}`;\n const session = this.sessions.getOrCreate(sessionKey);\n\n this.updateToolContexts(originChannel, originChatId);\n\n const messages = this.context.buildMessages({\n history: session.getHistory(),\n currentMessage: msg.content,\n channel: originChannel,\n chatId: originChatId,\n });\n\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen;\n\n const result = await this.runAgentLoop(messages);\n\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return createOutboundMessage({\n channel: originChannel,\n chatId: originChatId,\n content: result.content,\n });\n }\n\n private async runAgentLoop(\n messages: ChatMessage[],\n signal?: AbortSignal,\n onToolCallText?: (text: string) => void,\n ): Promise<{ content: string; toolsUsed: string[] }> {\n let finalContent: string | null = null;\n let sentToolCallNotice = false;\n const toolsUsed: string[] = [];\n\n for (let i = 0; i < this.maxIterations; i++) {\n const response = await this.provider.chat({\n messages,\n tools: this.tools.getDefinitions(),\n model: this.model,\n maxTokens: this.maxTokens,\n signal,\n });\n\n if (response.hasToolCalls) {\n // Send an interim message so the user knows we're working\n if (!sentToolCallNotice && onToolCallText) {\n const interimText = response.content?.trim()\n || this.getToolCallFallbackText(response.toolCalls);\n if (interimText) {\n onToolCallText(interimText);\n }\n sentToolCallNotice = true;\n }\n const toolCallDicts = response.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n },\n }));\n\n this.context.addAssistantMessage(\n messages,\n response.content,\n toolCallDicts,\n );\n\n for (const tc of response.toolCalls) {\n toolsUsed.push(tc.name);\n console.log(`Tool: ${tc.name}(${JSON.stringify(tc.arguments)})`);\n\n // Detect skill reads\n if (tc.name === \"read_file\") {\n const path = String(tc.arguments?.path ?? \"\");\n const skillMatch = path.match(/skills\\/([^/]+)\\/SKILL\\.md$/);\n if (skillMatch) {\n console.log(`Skill activated: ${skillMatch[1]}`);\n }\n }\n\n const result = await this.tools.execute(tc.name, tc.arguments);\n this.context.addToolResult(messages, tc.id, tc.name, result);\n }\n } else {\n finalContent = response.content;\n\n // If the LLM returned empty content after tool use, nudge it to respond\n if ((!finalContent || finalContent.trim().length === 0) && i > 0) {\n messages.push({\n role: \"assistant\",\n content: \"\",\n });\n messages.push({\n role: \"user\",\n content: \"(You used tools but didn't respond to the user. Please provide a brief response summarizing what you did.)\",\n });\n continue;\n }\n\n // Push the final assistant message so it gets persisted with the turn\n messages.push({ role: \"assistant\", content: finalContent ?? \"\" });\n break;\n }\n }\n\n if (!finalContent || finalContent.trim().length === 0) {\n finalContent = \"I've completed processing but have no response to give.\";\n }\n\n // If we exhausted iterations without a non-tool-call response, still persist the final text\n if (messages[messages.length - 1]?.role !== \"assistant\" || messages[messages.length - 1]?.content !== finalContent) {\n messages.push({ role: \"assistant\", content: finalContent });\n }\n\n return { content: finalContent, toolsUsed };\n }\n\n /** Generate a fallback interim message based on which tools are being called. */\n private getToolCallFallbackText(toolCalls: ToolCallRequest[]): string {\n const toolNames = toolCalls.map((tc) => tc.name);\n const fallbacks: Record<string, string> = {\n web_search: \"検索中...\",\n web_fetch: \"ページを読み込み中...\",\n read_file: \"ファイルを確認中...\",\n write_file: \"ファイルを書き込み中...\",\n edit_file: \"ファイルを編集中...\",\n exec: \"コマンドを実行中...\",\n spawn: \"サブエージェントを起動中...\",\n flex_message: \"カードを作成中...\",\n };\n for (const name of toolNames) {\n if (fallbacks[name]) return fallbacks[name];\n }\n return \"ちょっと待ってね...\";\n }\n\n private updateToolContexts(channel: string, chatId: string): void {\n const messageTool = this.tools.get(\"message\");\n if (messageTool instanceof MessageTool) {\n messageTool.setContext(channel, chatId);\n }\n\n const spawnTool = this.tools.get(\"spawn\");\n if (spawnTool instanceof SpawnTool) {\n spawnTool.setContext(channel, chatId);\n }\n\n const cronTool = this.tools.get(\"cron\");\n if (cronTool instanceof CronTool) {\n cronTool.setContext(channel, chatId);\n }\n }\n\n /** Process a message directly (for CLI or cron usage). */\n async processDirect(\n content: string,\n sessionKey = \"cli:direct\",\n channel = \"cli\",\n chatId = \"direct\",\n ): Promise<string> {\n // Use inline version of processMessage for direct calls\n const session = this.sessions.getOrCreate(sessionKey);\n this.updateToolContexts(channel, chatId);\n\n const messages = this.context.buildMessages({\n history: session.getHistory(),\n currentMessage: content,\n channel,\n chatId,\n });\n\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen;\n\n const result = await this.runAgentLoop(messages);\n\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return result.content;\n }\n\n /** Consolidate old messages into MEMORY.md + HISTORY.md, then trim session. */\n private async consolidateMemory(session: Session): Promise<void> {\n const memory = new MemoryStore(this.workspace);\n const keepCount = Math.min(10, Math.max(2, Math.floor(this.memoryWindow / 2)));\n const oldMessages = session.history.slice(0, -keepCount);\n if (oldMessages.length === 0) return;\n\n console.log(`Memory consolidation: ${session.history.length} messages, archiving ${oldMessages.length}, keeping ${keepCount}`);\n\n // Format messages for LLM\n const lines: string[] = [];\n for (const m of oldMessages) {\n const content = typeof m.content === \"string\" ? m.content : \"\";\n if (!content) continue;\n const role = m.role.toUpperCase();\n const tools = m.toolsUsed ? ` [tools: ${m.toolsUsed.join(\", \")}]` : \"\";\n const ts = m.timestamp ? `[${m.timestamp.slice(0, 16)}] ` : \"\";\n lines.push(`${ts}${role}${tools}: ${content}`);\n }\n const conversation = lines.join(\"\\n\");\n const currentMemory = memory.readLongTerm();\n\n const prompt = `You are a memory consolidation agent. Process this conversation and return a JSON object with exactly two keys:\n\n1. \"history_entry\": A paragraph (2-5 sentences) summarizing the key events/decisions/topics. Start with a timestamp like [YYYY-MM-DD HH:MM]. Include enough detail to be useful when found by grep search later.\n\n2. \"memory_update\": The updated long-term memory content. Add any new facts: user location, preferences, personal info, habits, project context, technical decisions, tools/services used. If nothing new, return the existing content unchanged.\n\n## Current Long-term Memory\n${currentMemory || \"(empty)\"}\n\n## Conversation to Process\n${conversation}\n\nRespond with ONLY valid JSON, no markdown fences.`;\n\n try {\n const response = await this.provider.chat({\n messages: [\n { role: \"system\", content: \"You are a memory consolidation agent. Respond only with valid JSON.\" },\n { role: \"user\", content: prompt },\n ],\n model: this.consolidationModel,\n });\n\n let text = (response.content || \"\").trim();\n if (text.startsWith(\"```\")) {\n text = text.split(\"\\n\").slice(1).join(\"\\n\").split(\"```\")[0].trim();\n }\n\n const result = JSON.parse(text);\n\n if (result.history_entry) {\n const entry = typeof result.history_entry === \"string\"\n ? result.history_entry\n : JSON.stringify(result.history_entry, null, 2);\n memory.appendHistory(entry);\n }\n if (result.memory_update && result.memory_update !== currentMemory) {\n const update = typeof result.memory_update === \"string\"\n ? result.memory_update\n : JSON.stringify(result.memory_update, null, 2);\n memory.writeLongTerm(update);\n }\n } catch (err) {\n console.error(\"Memory consolidation failed:\", err);\n // Fallback: append raw conversation to history so it's not lost\n const fallbackEntry = `[${new Date().toISOString().slice(0, 16)}] Consolidation failed, archiving raw messages:\\n${conversation.slice(0, 2000)}`;\n memory.appendHistory(fallbackEntry);\n }\n\n // Always trim session to prevent unbounded growth\n session.trimHistory(keepCount);\n this.sessions.save(session);\n console.log(`Memory consolidation done, session trimmed to ${session.history.length} messages`);\n }\n}\n\n/** Check if an error is an abort/cancellation error. */\nfunction isAbortError(err: unknown): boolean {\n if (err instanceof DOMException && err.name === \"AbortError\") return true;\n if (err instanceof Error) {\n if (err.name === \"AbortError\") return true;\n // OpenAI SDK wraps abort as APIUserAbortError\n if (err.name === \"APIUserAbortError\") return true;\n if (err.message.includes(\"abort\")) return true;\n }\n return false;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { Tool } from "./base.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/agent/tools/flex.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Tool for building LINE Flex Messages from structured data.
|
|
6
|
+
*
|
|
7
|
+
* The LLM calls this tool with a template name and data object,
|
|
8
|
+
* and the tool returns valid Flex JSON that line.ts parseMessage()
|
|
9
|
+
* will detect and send as a Flex Message.
|
|
10
|
+
*/
|
|
11
|
+
declare class FlexTool extends Tool {
|
|
12
|
+
readonly name = "flex_message";
|
|
13
|
+
readonly description = "Build a LINE Flex Message from a template. Returns JSON that will be rendered as a rich card in LINE. Use this instead of outputting raw JSON.";
|
|
14
|
+
readonly parameters: {
|
|
15
|
+
type: string;
|
|
16
|
+
properties: {
|
|
17
|
+
template: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
data: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
properties: {
|
|
25
|
+
sign: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
stars: {
|
|
30
|
+
type: string;
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
message: {
|
|
34
|
+
type: string;
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
lucky_color: {
|
|
38
|
+
type: string;
|
|
39
|
+
description: string;
|
|
40
|
+
};
|
|
41
|
+
lucky_item: {
|
|
42
|
+
type: string;
|
|
43
|
+
description: string;
|
|
44
|
+
};
|
|
45
|
+
title: {
|
|
46
|
+
type: string;
|
|
47
|
+
description: string;
|
|
48
|
+
};
|
|
49
|
+
body: {
|
|
50
|
+
type: string;
|
|
51
|
+
description: string;
|
|
52
|
+
};
|
|
53
|
+
prompt: {
|
|
54
|
+
type: string;
|
|
55
|
+
description: string;
|
|
56
|
+
};
|
|
57
|
+
buttons: {
|
|
58
|
+
type: string;
|
|
59
|
+
items: {
|
|
60
|
+
type: string;
|
|
61
|
+
properties: {
|
|
62
|
+
label: {
|
|
63
|
+
type: string;
|
|
64
|
+
};
|
|
65
|
+
text: {
|
|
66
|
+
type: string;
|
|
67
|
+
};
|
|
68
|
+
style: {
|
|
69
|
+
type: string;
|
|
70
|
+
enum: string[];
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
description: string;
|
|
75
|
+
};
|
|
76
|
+
items: {
|
|
77
|
+
type: string;
|
|
78
|
+
items: {
|
|
79
|
+
type: string;
|
|
80
|
+
properties: {
|
|
81
|
+
name: {
|
|
82
|
+
type: string;
|
|
83
|
+
};
|
|
84
|
+
value: {
|
|
85
|
+
type: string;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
description: string;
|
|
90
|
+
};
|
|
91
|
+
total: {
|
|
92
|
+
type: string;
|
|
93
|
+
description: string;
|
|
94
|
+
};
|
|
95
|
+
greeting: {
|
|
96
|
+
type: string;
|
|
97
|
+
description: string;
|
|
98
|
+
};
|
|
99
|
+
date: {
|
|
100
|
+
type: string;
|
|
101
|
+
description: string;
|
|
102
|
+
};
|
|
103
|
+
weather: {
|
|
104
|
+
type: string;
|
|
105
|
+
description: string;
|
|
106
|
+
};
|
|
107
|
+
advice: {
|
|
108
|
+
type: string;
|
|
109
|
+
description: string;
|
|
110
|
+
};
|
|
111
|
+
schedule: {
|
|
112
|
+
type: string;
|
|
113
|
+
items: {
|
|
114
|
+
type: string;
|
|
115
|
+
};
|
|
116
|
+
description: string;
|
|
117
|
+
};
|
|
118
|
+
header_color: {
|
|
119
|
+
type: string;
|
|
120
|
+
description: string;
|
|
121
|
+
};
|
|
122
|
+
current: {
|
|
123
|
+
type: string;
|
|
124
|
+
description: string;
|
|
125
|
+
};
|
|
126
|
+
goal: {
|
|
127
|
+
type: string;
|
|
128
|
+
description: string;
|
|
129
|
+
};
|
|
130
|
+
unit: {
|
|
131
|
+
type: string;
|
|
132
|
+
description: string;
|
|
133
|
+
};
|
|
134
|
+
button_label: {
|
|
135
|
+
type: string;
|
|
136
|
+
description: string;
|
|
137
|
+
};
|
|
138
|
+
button_text: {
|
|
139
|
+
type: string;
|
|
140
|
+
description: string;
|
|
141
|
+
};
|
|
142
|
+
contents: {
|
|
143
|
+
type: string;
|
|
144
|
+
description: string;
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
required: string[];
|
|
150
|
+
};
|
|
151
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
152
|
+
private buildFortune;
|
|
153
|
+
private buildInfoCard;
|
|
154
|
+
private buildActionButtons;
|
|
155
|
+
private buildReceipt;
|
|
156
|
+
private buildMorningSummary;
|
|
157
|
+
private buildHydration;
|
|
158
|
+
private buildCustom;
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
export { FlexTool };
|
|
162
|
+
//# sourceMappingURL=flex.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flex.d.mts","names":[],"sources":["../../../src/agent/tools/flex.ts"],"mappings":";;;;;AASA;;;;;cAAa,QAAA,SAAiB,IAAA;EAAA,SACnB,IAAA;EAAA,SACA,WAAA;EAAA,SAEA,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4EH,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;EAAA,QAuCtC,YAAA;EAAA,QAgEA,aAAA;EAAA,QA4BA,kBAAA;EAAA,QAwCA,YAAA;EAAA,QA+CA,mBAAA;EAAA,QAiDA,cAAA;EAAA,QAkCA,WAAA;AAAA"}
|