@dreb/telegram 2.34.3 → 2.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../src/agent-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent bridge — manages the RPC connection to a dreb agent process.\n * One bridge per user, handles lifecycle, event subscription, and session management.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { RpcClient, type RpcSessionInfo } from \"@dreb/coding-agent/rpc\";\nimport type { Config } from \"./config.js\";\nimport { log } from \"./util/telegram.js\";\n\n/**\n * Resolve the absolute path to the dreb CLI entry point.\n * RpcClient defaults to \"dist/cli.js\" (relative to cwd), but we need\n * the absolute path since the bot's working dir differs from the dreb repo.\n */\nfunction resolveDrebCliPath(): string {\n\t// import.meta.resolve finds @dreb/coding-agent/dist/index.js\n\tconst resolved = import.meta.resolve(\"@dreb/coding-agent\");\n\tconst distDir = dirname(fileURLToPath(resolved));\n\treturn join(distDir, \"cli.js\");\n}\n\n/** RPC events include both AgentEvent and session-specific events */\ntype RpcEvent = { type: string; [key: string]: any };\n\nexport type AgentEventListener = (event: RpcEvent) => void;\n\nexport class AgentBridge {\n\tprivate client: RpcClient | null = null;\n\tprivate eventListeners: AgentEventListener[] = [];\n\tprivate _isStreaming = false;\n\tprivate _sessionFile: string | undefined;\n\tprivate _sessionId: string | undefined;\n\tprivate exited = false;\n\n\tconstructor(private config: Config) {}\n\n\t/** Whether the RPC process is alive */\n\tget isAlive(): boolean {\n\t\treturn this.client !== null && !this.exited;\n\t}\n\n\t/** Whether the agent is currently streaming a response */\n\tget isStreaming(): boolean {\n\t\treturn this._isStreaming;\n\t}\n\n\t/** Current session file path */\n\tget sessionFile(): string | undefined {\n\t\treturn this._sessionFile;\n\t}\n\n\t/** Current session ID */\n\tget sessionId(): string | undefined {\n\t\treturn this._sessionId;\n\t}\n\n\t/**\n\t * Start the RPC process. Does NOT resume a session — call resumeLatest() or newSession() after.\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this.client) return;\n\n\t\tthis.client = new RpcClient({\n\t\t\tcliPath: resolveDrebCliPath(),\n\t\t\tcwd: this.config.workingDir,\n\t\t\tprovider: this.config.provider,\n\t\t\tmodel: this.config.model,\n\t\t\targs: [\"--ui\", \"telegram\"],\n\t\t});\n\n\t\tthis.exited = false;\n\t\tawait this.client.start();\n\n\t\t// Subscribe to events and forward to listeners\n\t\t// Cast: RpcClient types events as AgentEvent but actually forwards all AgentSessionEvent types\n\t\tthis.client.onEvent((event) => {\n\t\t\tthis.handleEvent(event as RpcEvent);\n\t\t});\n\n\t\t// Detect process exit\n\t\t// RpcClient doesn't expose a direct \"on exit\" — we detect it when send() fails\n\t\tlog(\"[BRIDGE] RPC process started\");\n\t}\n\n\t/**\n\t * Resume the most recent session, or do nothing if no sessions exist.\n\t */\n\tasync resumeLatest(): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst sessions = await this.client.listSessions();\n\t\t\tif (sessions.length === 0) return false;\n\n\t\t\tconst latest = sessions[0]; // Already sorted by modified desc\n\t\t\tconst result = await this.client.switchSession(latest.path);\n\t\t\tif (!result.cancelled) {\n\t\t\t\tthis._sessionFile = latest.path;\n\t\t\t\tthis._sessionId = latest.id;\n\t\t\t\tlog(`[BRIDGE] Resumed session ${latest.id.slice(0, 8)}`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to resume latest session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * List available sessions.\n\t */\n\tasync listSessions(): Promise<RpcSessionInfo[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.listSessions();\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to list sessions: ${e}`);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Switch to a specific session by path.\n\t */\n\tasync switchSession(sessionPath: string): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst result = await this.client.switchSession(sessionPath);\n\t\t\tif (!result.cancelled) {\n\t\t\t\tthis._sessionFile = sessionPath;\n\t\t\t\tconst state = await this.client.getState();\n\t\t\t\tthis._sessionId = state.sessionId;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to switch session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Create a new session.\n\t */\n\tasync newSession(): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst result = await this.client.newSession();\n\t\t\tif (!result.cancelled) {\n\t\t\t\tconst state = await this.client.getState();\n\t\t\t\tthis._sessionFile = state.sessionFile;\n\t\t\t\tthis._sessionId = state.sessionId;\n\t\t\t\tlog(`[BRIDGE] New session ${state.sessionId.slice(0, 8)}`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to create new session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Send a prompt to the agent.\n\t */\n\tasync prompt(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.prompt(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a steering message to interrupt the agent mid-run.\n\t * The agent injects it after the current tool-call batch finishes.\n\t */\n\tasync steer(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.steer(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a follow-up message for after the agent finishes its current run.\n\t */\n\tasync followUp(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.followUp(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Abort the current operation.\n\t */\n\tasync abort(): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tawait this.client.abort();\n\t\t} catch {\n\t\t\t// Process may have already exited\n\t\t}\n\t}\n\n\t/**\n\t * Get the dreb version.\n\t */\n\tasync getVersion(): Promise<string> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\treturn await this.client!.getVersion();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get session statistics.\n\t */\n\tasync getSessionStats(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getSessionStats();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get performance statistics.\n\t */\n\tasync getPerformanceStats(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getPerformanceStats();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get current state.\n\t */\n\tasync getState(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getState();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get available commands (skills, extensions, prompt templates).\n\t */\n\tasync getCommands(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getCommands();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Compact context.\n\t */\n\tasync compact(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.compact();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get available models.\n\t */\n\tasync getAvailableModels(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getAvailableModels();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Set model.\n\t */\n\tasync setModel(provider: string, modelId: string): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.setModel(provider, modelId);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Resolve a model pattern using the same logic as CLI/TUI.\n\t */\n\tasync resolveModel(pattern: string): Promise<{ model: any; warning?: string } | null> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.resolveModel(pattern);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Set thinking level.\n\t */\n\tasync setThinkingLevel(level: string): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tawait this.client.setThinkingLevel(level as any);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get all messages.\n\t */\n\tasync getMessages(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getMessages();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Hatch a new buddy companion. Runs inside the agent process\n\t * so API keys never cross the process boundary.\n\t */\n\tasync buddyHatch(): Promise<any> {\n\t\tif (!this.client) throw new Error(\"Agent not connected.\");\n\t\ttry {\n\t\t\treturn await this.client.buddyHatch();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Reroll the buddy companion. Runs inside the agent process\n\t * so API keys never cross the process boundary.\n\t */\n\tasync buddyReroll(): Promise<any> {\n\t\tif (!this.client) throw new Error(\"Agent not connected.\");\n\t\ttry {\n\t\t\treturn await this.client.buddyReroll();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get last assistant text.\n\t */\n\tasync getLastAssistantText(): Promise<string | null> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getLastAssistantText();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Refresh session info from the RPC process state.\n\t */\n\tasync refreshSessionInfo(): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tconst state = await this.client.getState();\n\t\t\tthis._sessionFile = state.sessionFile;\n\t\t\tthis._sessionId = state.sessionId;\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\t// Non-critical — don't re-throw\n\t\t}\n\t}\n\n\t/**\n\t * Subscribe to agent events.\n\t */\n\tonEvent(listener: AgentEventListener): () => void {\n\t\tthis.eventListeners.push(listener);\n\t\treturn () => {\n\t\t\tconst idx = this.eventListeners.indexOf(listener);\n\t\t\tif (idx !== -1) this.eventListeners.splice(idx, 1);\n\t\t};\n\t}\n\n\t/**\n\t * Stop the RPC process.\n\t */\n\tasync stop(): Promise<void> {\n\t\tif (this.client) {\n\t\t\ttry {\n\t\t\t\tawait this.client.stop();\n\t\t\t} catch {\n\t\t\t\t// Ignore\n\t\t\t}\n\t\t\tthis.client = null;\n\t\t\tthis.exited = true;\n\t\t\tthis.eventListeners = [];\n\t\t\tlog(\"[BRIDGE] RPC process stopped\");\n\t\t}\n\t}\n\n\t// =========================================================================\n\t// Internal\n\t// =========================================================================\n\n\tprivate handleEvent(event: RpcEvent): void {\n\t\t// Track streaming state\n\t\tif (event.type === \"agent_start\") this._isStreaming = true;\n\t\tif (event.type === \"agent_end\") {\n\t\t\tthis._isStreaming = false;\n\t\t\t// Capture session info from agent_end messages\n\t\t\t// Session file/id updates happen via getState after prompt\n\t\t}\n\n\t\tfor (const listener of this.eventListeners) {\n\t\t\ttry {\n\t\t\t\tlistener(event);\n\t\t\t} catch (e) {\n\t\t\t\tlog(`[BRIDGE] Event listener error: ${e}`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleProcessError(e: unknown): void {\n\t\tconst msg = e instanceof Error ? e.message : String(e);\n\t\tif (\n\t\t\tmsg.includes(\"not started\") ||\n\t\t\tmsg.includes(\"not running\") ||\n\t\t\tmsg.includes(\"EPIPE\") ||\n\t\t\tmsg.includes(\"write after end\") ||\n\t\t\tmsg.includes(\"Timeout waiting for response\") ||\n\t\t\tmsg.includes(\"RPC process exited\")\n\t\t) {\n\t\t\tlog(`[BRIDGE] RPC process exited or hung: ${msg.slice(0, 100)}`);\n\t\t\t// Kill the child process before dropping the reference to prevent orphans\n\t\t\tif (this.client) {\n\t\t\t\tthis.client.stop().catch(() => {});\n\t\t\t}\n\t\t\tthis.exited = true;\n\t\t\tthis.client = null;\n\t\t}\n\t}\n\n\tprivate async ensureAlive(): Promise<void> {\n\t\tif (!this.client || this.exited) {\n\t\t\tlog(\"[BRIDGE] Restarting dead RPC process\");\n\t\t\tthis.client = null;\n\t\t\tthis.exited = false;\n\t\t\tawait this.start();\n\t\t\t// Session selection is handled by ensureBridgeWithSession — not here.\n\t\t\t// The previous session is still the latest and will be picked up by\n\t\t\t// resumeLatest() on the next message.\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../src/agent-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent bridge — manages the RPC connection to a dreb agent process.\n * One bridge per user, handles lifecycle, event subscription, and session management.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { RpcClient, type RpcSessionInfo } from \"@dreb/coding-agent/rpc\";\nimport type { Config } from \"./config.js\";\nimport { log } from \"./util/telegram.js\";\n\n/**\n * Resolve the absolute path to the dreb CLI entry point.\n * RpcClient defaults to \"dist/cli.js\" (relative to cwd), but we need\n * the absolute path since the bot's working dir differs from the dreb repo.\n */\nfunction resolveDrebCliPath(): string {\n\t// import.meta.resolve finds @dreb/coding-agent/dist/index.js\n\tconst resolved = import.meta.resolve(\"@dreb/coding-agent\");\n\tconst distDir = dirname(fileURLToPath(resolved));\n\treturn join(distDir, \"cli.js\");\n}\n\n/** RPC events include both AgentEvent and session-specific events */\ntype RpcEvent = { type: string; [key: string]: any };\n\nexport type AgentEventListener = (event: RpcEvent) => void;\n\nexport class AgentBridge {\n\tprivate client: RpcClient | null = null;\n\tprivate eventListeners: AgentEventListener[] = [];\n\tprivate _isStreaming = false;\n\tprivate _sessionFile: string | undefined;\n\tprivate _sessionId: string | undefined;\n\tprivate exited = false;\n\n\tconstructor(private config: Config) {}\n\n\t/** Whether the RPC process is alive */\n\tget isAlive(): boolean {\n\t\treturn this.client !== null && !this.exited;\n\t}\n\n\t/** Whether the agent is currently streaming a response */\n\tget isStreaming(): boolean {\n\t\treturn this._isStreaming;\n\t}\n\n\t/** Current session file path */\n\tget sessionFile(): string | undefined {\n\t\treturn this._sessionFile;\n\t}\n\n\t/** Current session ID */\n\tget sessionId(): string | undefined {\n\t\treturn this._sessionId;\n\t}\n\n\t/**\n\t * Start the RPC process. Does NOT resume a session — call resumeLatest() or newSession() after.\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this.client) return;\n\n\t\tthis.client = new RpcClient({\n\t\t\tcliPath: resolveDrebCliPath(),\n\t\t\tcwd: this.config.workingDir,\n\t\t\tprovider: this.config.provider,\n\t\t\tmodel: this.config.model,\n\t\t\targs: [\"--ui\", \"telegram\"],\n\t\t});\n\n\t\tthis.exited = false;\n\t\tawait this.client.start();\n\n\t\t// Subscribe to events and forward to listeners\n\t\tthis.client.onEvent((event) => {\n\t\t\tthis.handleEvent(event as RpcEvent);\n\t\t});\n\n\t\t// Detect process exit\n\t\t// RpcClient doesn't expose a direct \"on exit\" — we detect it when send() fails\n\t\tlog(\"[BRIDGE] RPC process started\");\n\t}\n\n\t/**\n\t * Resume the most recent session, or do nothing if no sessions exist.\n\t */\n\tasync resumeLatest(): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst sessions = await this.client.listSessions();\n\t\t\tif (sessions.length === 0) return false;\n\n\t\t\tconst latest = sessions[0]; // Already sorted by modified desc\n\t\t\tconst result = await this.client.switchSession(latest.path);\n\t\t\tif (!result.cancelled) {\n\t\t\t\tthis._sessionFile = latest.path;\n\t\t\t\tthis._sessionId = latest.id;\n\t\t\t\tlog(`[BRIDGE] Resumed session ${latest.id.slice(0, 8)}`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to resume latest session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * List available sessions.\n\t */\n\tasync listSessions(): Promise<RpcSessionInfo[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.listSessions();\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to list sessions: ${e}`);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Switch to a specific session by path.\n\t */\n\tasync switchSession(sessionPath: string): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst result = await this.client.switchSession(sessionPath);\n\t\t\tif (!result.cancelled) {\n\t\t\t\tthis._sessionFile = sessionPath;\n\t\t\t\tconst state = await this.client.getState();\n\t\t\t\tthis._sessionId = state.sessionId;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to switch session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Create a new session.\n\t */\n\tasync newSession(): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst result = await this.client.newSession();\n\t\t\tif (!result.cancelled) {\n\t\t\t\tconst state = await this.client.getState();\n\t\t\t\tthis._sessionFile = state.sessionFile;\n\t\t\t\tthis._sessionId = state.sessionId;\n\t\t\t\tlog(`[BRIDGE] New session ${state.sessionId.slice(0, 8)}`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to create new session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Send a prompt to the agent.\n\t */\n\tasync prompt(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.prompt(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a steering message to interrupt the agent mid-run.\n\t * The agent injects it after the current tool-call batch finishes.\n\t */\n\tasync steer(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.steer(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a follow-up message for after the agent finishes its current run.\n\t */\n\tasync followUp(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.followUp(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Abort the current operation.\n\t */\n\tasync abort(): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tawait this.client.abort();\n\t\t} catch {\n\t\t\t// Process may have already exited\n\t\t}\n\t}\n\n\t/**\n\t * Get the dreb version.\n\t */\n\tasync getVersion(): Promise<string> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\treturn await this.client!.getVersion();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get session statistics.\n\t */\n\tasync getSessionStats(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getSessionStats();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get performance statistics.\n\t */\n\tasync getPerformanceStats(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getPerformanceStats();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get current state.\n\t */\n\tasync getState(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getState();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get available commands (skills, extensions, prompt templates).\n\t */\n\tasync getCommands(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getCommands();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Compact context.\n\t */\n\tasync compact(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.compact();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get available models.\n\t */\n\tasync getAvailableModels(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getAvailableModels();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Set model.\n\t */\n\tasync setModel(provider: string, modelId: string): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.setModel(provider, modelId);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Resolve a model pattern using the same logic as CLI/TUI.\n\t */\n\tasync resolveModel(pattern: string): Promise<{ model: any; warning?: string } | null> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.resolveModel(pattern);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Set thinking level.\n\t */\n\tasync setThinkingLevel(level: string): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tawait this.client.setThinkingLevel(level as any);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get all messages.\n\t */\n\tasync getMessages(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getMessages();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Hatch a new buddy companion. Runs inside the agent process\n\t * so API keys never cross the process boundary.\n\t */\n\tasync buddyHatch(): Promise<any> {\n\t\tif (!this.client) throw new Error(\"Agent not connected.\");\n\t\ttry {\n\t\t\treturn await this.client.buddyHatch();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Reroll the buddy companion. Runs inside the agent process\n\t * so API keys never cross the process boundary.\n\t */\n\tasync buddyReroll(): Promise<any> {\n\t\tif (!this.client) throw new Error(\"Agent not connected.\");\n\t\ttry {\n\t\t\treturn await this.client.buddyReroll();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get last assistant text.\n\t */\n\tasync getLastAssistantText(): Promise<string | null> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getLastAssistantText();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Refresh session info from the RPC process state.\n\t */\n\tasync refreshSessionInfo(): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tconst state = await this.client.getState();\n\t\t\tthis._sessionFile = state.sessionFile;\n\t\t\tthis._sessionId = state.sessionId;\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\t// Non-critical — don't re-throw\n\t\t}\n\t}\n\n\t/**\n\t * Subscribe to agent events.\n\t */\n\tonEvent(listener: AgentEventListener): () => void {\n\t\tthis.eventListeners.push(listener);\n\t\treturn () => {\n\t\t\tconst idx = this.eventListeners.indexOf(listener);\n\t\t\tif (idx !== -1) this.eventListeners.splice(idx, 1);\n\t\t};\n\t}\n\n\t/**\n\t * Stop the RPC process.\n\t */\n\tasync stop(): Promise<void> {\n\t\tif (this.client) {\n\t\t\ttry {\n\t\t\t\tawait this.client.stop();\n\t\t\t} catch {\n\t\t\t\t// Ignore\n\t\t\t}\n\t\t\tthis.client = null;\n\t\t\tthis.exited = true;\n\t\t\tthis.eventListeners = [];\n\t\t\tlog(\"[BRIDGE] RPC process stopped\");\n\t\t}\n\t}\n\n\t// =========================================================================\n\t// Internal\n\t// =========================================================================\n\n\tprivate handleEvent(event: RpcEvent): void {\n\t\t// Track streaming state\n\t\tif (event.type === \"agent_start\") this._isStreaming = true;\n\t\tif (event.type === \"agent_end\") {\n\t\t\tthis._isStreaming = false;\n\t\t\t// Capture session info from agent_end messages\n\t\t\t// Session file/id updates happen via getState after prompt\n\t\t}\n\n\t\tfor (const listener of this.eventListeners) {\n\t\t\ttry {\n\t\t\t\tlistener(event);\n\t\t\t} catch (e) {\n\t\t\t\tlog(`[BRIDGE] Event listener error: ${e}`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleProcessError(e: unknown): void {\n\t\tconst msg = e instanceof Error ? e.message : String(e);\n\t\tif (\n\t\t\tmsg.includes(\"not started\") ||\n\t\t\tmsg.includes(\"not running\") ||\n\t\t\tmsg.includes(\"EPIPE\") ||\n\t\t\tmsg.includes(\"write after end\") ||\n\t\t\tmsg.includes(\"Timeout waiting for response\") ||\n\t\t\tmsg.includes(\"RPC process exited\")\n\t\t) {\n\t\t\tlog(`[BRIDGE] RPC process exited or hung: ${msg.slice(0, 100)}`);\n\t\t\t// Kill the child process before dropping the reference to prevent orphans\n\t\t\tif (this.client) {\n\t\t\t\tthis.client.stop().catch(() => {});\n\t\t\t}\n\t\t\tthis.exited = true;\n\t\t\tthis.client = null;\n\t\t}\n\t}\n\n\tprivate async ensureAlive(): Promise<void> {\n\t\tif (!this.client || this.exited) {\n\t\t\tlog(\"[BRIDGE] Restarting dead RPC process\");\n\t\t\tthis.client = null;\n\t\t\tthis.exited = false;\n\t\t\tawait this.start();\n\t\t\t// Session selection is handled by ensureBridgeWithSession — not here.\n\t\t\t// The previous session is still the latest and will be picked up by\n\t\t\t// resumeLatest() on the next message.\n\t\t}\n\t}\n}\n"]}
|
package/dist/agent-bridge.js
CHANGED
|
@@ -60,7 +60,6 @@ export class AgentBridge {
|
|
|
60
60
|
this.exited = false;
|
|
61
61
|
await this.client.start();
|
|
62
62
|
// Subscribe to events and forward to listeners
|
|
63
|
-
// Cast: RpcClient types events as AgentEvent but actually forwards all AgentSessionEvent types
|
|
64
63
|
this.client.onEvent((event) => {
|
|
65
64
|
this.handleEvent(event);
|
|
66
65
|
});
|
package/dist/agent-bridge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bridge.js","sourceRoot":"","sources":["../src/agent-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent bridge — manages the RPC connection to a dreb agent process.\n * One bridge per user, handles lifecycle, event subscription, and session management.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { RpcClient, type RpcSessionInfo } from \"@dreb/coding-agent/rpc\";\nimport type { Config } from \"./config.js\";\nimport { log } from \"./util/telegram.js\";\n\n/**\n * Resolve the absolute path to the dreb CLI entry point.\n * RpcClient defaults to \"dist/cli.js\" (relative to cwd), but we need\n * the absolute path since the bot's working dir differs from the dreb repo.\n */\nfunction resolveDrebCliPath(): string {\n\t// import.meta.resolve finds @dreb/coding-agent/dist/index.js\n\tconst resolved = import.meta.resolve(\"@dreb/coding-agent\");\n\tconst distDir = dirname(fileURLToPath(resolved));\n\treturn join(distDir, \"cli.js\");\n}\n\n/** RPC events include both AgentEvent and session-specific events */\ntype RpcEvent = { type: string; [key: string]: any };\n\nexport type AgentEventListener = (event: RpcEvent) => void;\n\nexport class AgentBridge {\n\tprivate client: RpcClient | null = null;\n\tprivate eventListeners: AgentEventListener[] = [];\n\tprivate _isStreaming = false;\n\tprivate _sessionFile: string | undefined;\n\tprivate _sessionId: string | undefined;\n\tprivate exited = false;\n\n\tconstructor(private config: Config) {}\n\n\t/** Whether the RPC process is alive */\n\tget isAlive(): boolean {\n\t\treturn this.client !== null && !this.exited;\n\t}\n\n\t/** Whether the agent is currently streaming a response */\n\tget isStreaming(): boolean {\n\t\treturn this._isStreaming;\n\t}\n\n\t/** Current session file path */\n\tget sessionFile(): string | undefined {\n\t\treturn this._sessionFile;\n\t}\n\n\t/** Current session ID */\n\tget sessionId(): string | undefined {\n\t\treturn this._sessionId;\n\t}\n\n\t/**\n\t * Start the RPC process. Does NOT resume a session — call resumeLatest() or newSession() after.\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this.client) return;\n\n\t\tthis.client = new RpcClient({\n\t\t\tcliPath: resolveDrebCliPath(),\n\t\t\tcwd: this.config.workingDir,\n\t\t\tprovider: this.config.provider,\n\t\t\tmodel: this.config.model,\n\t\t\targs: [\"--ui\", \"telegram\"],\n\t\t});\n\n\t\tthis.exited = false;\n\t\tawait this.client.start();\n\n\t\t// Subscribe to events and forward to listeners\n\t\t// Cast: RpcClient types events as AgentEvent but actually forwards all AgentSessionEvent types\n\t\tthis.client.onEvent((event) => {\n\t\t\tthis.handleEvent(event as RpcEvent);\n\t\t});\n\n\t\t// Detect process exit\n\t\t// RpcClient doesn't expose a direct \"on exit\" — we detect it when send() fails\n\t\tlog(\"[BRIDGE] RPC process started\");\n\t}\n\n\t/**\n\t * Resume the most recent session, or do nothing if no sessions exist.\n\t */\n\tasync resumeLatest(): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst sessions = await this.client.listSessions();\n\t\t\tif (sessions.length === 0) return false;\n\n\t\t\tconst latest = sessions[0]; // Already sorted by modified desc\n\t\t\tconst result = await this.client.switchSession(latest.path);\n\t\t\tif (!result.cancelled) {\n\t\t\t\tthis._sessionFile = latest.path;\n\t\t\t\tthis._sessionId = latest.id;\n\t\t\t\tlog(`[BRIDGE] Resumed session ${latest.id.slice(0, 8)}`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to resume latest session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * List available sessions.\n\t */\n\tasync listSessions(): Promise<RpcSessionInfo[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.listSessions();\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to list sessions: ${e}`);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Switch to a specific session by path.\n\t */\n\tasync switchSession(sessionPath: string): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst result = await this.client.switchSession(sessionPath);\n\t\t\tif (!result.cancelled) {\n\t\t\t\tthis._sessionFile = sessionPath;\n\t\t\t\tconst state = await this.client.getState();\n\t\t\t\tthis._sessionId = state.sessionId;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to switch session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Create a new session.\n\t */\n\tasync newSession(): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst result = await this.client.newSession();\n\t\t\tif (!result.cancelled) {\n\t\t\t\tconst state = await this.client.getState();\n\t\t\t\tthis._sessionFile = state.sessionFile;\n\t\t\t\tthis._sessionId = state.sessionId;\n\t\t\t\tlog(`[BRIDGE] New session ${state.sessionId.slice(0, 8)}`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to create new session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Send a prompt to the agent.\n\t */\n\tasync prompt(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.prompt(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a steering message to interrupt the agent mid-run.\n\t * The agent injects it after the current tool-call batch finishes.\n\t */\n\tasync steer(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.steer(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a follow-up message for after the agent finishes its current run.\n\t */\n\tasync followUp(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.followUp(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Abort the current operation.\n\t */\n\tasync abort(): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tawait this.client.abort();\n\t\t} catch {\n\t\t\t// Process may have already exited\n\t\t}\n\t}\n\n\t/**\n\t * Get the dreb version.\n\t */\n\tasync getVersion(): Promise<string> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\treturn await this.client!.getVersion();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get session statistics.\n\t */\n\tasync getSessionStats(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getSessionStats();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get performance statistics.\n\t */\n\tasync getPerformanceStats(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getPerformanceStats();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get current state.\n\t */\n\tasync getState(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getState();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get available commands (skills, extensions, prompt templates).\n\t */\n\tasync getCommands(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getCommands();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Compact context.\n\t */\n\tasync compact(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.compact();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get available models.\n\t */\n\tasync getAvailableModels(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getAvailableModels();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Set model.\n\t */\n\tasync setModel(provider: string, modelId: string): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.setModel(provider, modelId);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Resolve a model pattern using the same logic as CLI/TUI.\n\t */\n\tasync resolveModel(pattern: string): Promise<{ model: any; warning?: string } | null> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.resolveModel(pattern);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Set thinking level.\n\t */\n\tasync setThinkingLevel(level: string): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tawait this.client.setThinkingLevel(level as any);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get all messages.\n\t */\n\tasync getMessages(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getMessages();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Hatch a new buddy companion. Runs inside the agent process\n\t * so API keys never cross the process boundary.\n\t */\n\tasync buddyHatch(): Promise<any> {\n\t\tif (!this.client) throw new Error(\"Agent not connected.\");\n\t\ttry {\n\t\t\treturn await this.client.buddyHatch();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Reroll the buddy companion. Runs inside the agent process\n\t * so API keys never cross the process boundary.\n\t */\n\tasync buddyReroll(): Promise<any> {\n\t\tif (!this.client) throw new Error(\"Agent not connected.\");\n\t\ttry {\n\t\t\treturn await this.client.buddyReroll();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get last assistant text.\n\t */\n\tasync getLastAssistantText(): Promise<string | null> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getLastAssistantText();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Refresh session info from the RPC process state.\n\t */\n\tasync refreshSessionInfo(): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tconst state = await this.client.getState();\n\t\t\tthis._sessionFile = state.sessionFile;\n\t\t\tthis._sessionId = state.sessionId;\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\t// Non-critical — don't re-throw\n\t\t}\n\t}\n\n\t/**\n\t * Subscribe to agent events.\n\t */\n\tonEvent(listener: AgentEventListener): () => void {\n\t\tthis.eventListeners.push(listener);\n\t\treturn () => {\n\t\t\tconst idx = this.eventListeners.indexOf(listener);\n\t\t\tif (idx !== -1) this.eventListeners.splice(idx, 1);\n\t\t};\n\t}\n\n\t/**\n\t * Stop the RPC process.\n\t */\n\tasync stop(): Promise<void> {\n\t\tif (this.client) {\n\t\t\ttry {\n\t\t\t\tawait this.client.stop();\n\t\t\t} catch {\n\t\t\t\t// Ignore\n\t\t\t}\n\t\t\tthis.client = null;\n\t\t\tthis.exited = true;\n\t\t\tthis.eventListeners = [];\n\t\t\tlog(\"[BRIDGE] RPC process stopped\");\n\t\t}\n\t}\n\n\t// =========================================================================\n\t// Internal\n\t// =========================================================================\n\n\tprivate handleEvent(event: RpcEvent): void {\n\t\t// Track streaming state\n\t\tif (event.type === \"agent_start\") this._isStreaming = true;\n\t\tif (event.type === \"agent_end\") {\n\t\t\tthis._isStreaming = false;\n\t\t\t// Capture session info from agent_end messages\n\t\t\t// Session file/id updates happen via getState after prompt\n\t\t}\n\n\t\tfor (const listener of this.eventListeners) {\n\t\t\ttry {\n\t\t\t\tlistener(event);\n\t\t\t} catch (e) {\n\t\t\t\tlog(`[BRIDGE] Event listener error: ${e}`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleProcessError(e: unknown): void {\n\t\tconst msg = e instanceof Error ? e.message : String(e);\n\t\tif (\n\t\t\tmsg.includes(\"not started\") ||\n\t\t\tmsg.includes(\"not running\") ||\n\t\t\tmsg.includes(\"EPIPE\") ||\n\t\t\tmsg.includes(\"write after end\") ||\n\t\t\tmsg.includes(\"Timeout waiting for response\") ||\n\t\t\tmsg.includes(\"RPC process exited\")\n\t\t) {\n\t\t\tlog(`[BRIDGE] RPC process exited or hung: ${msg.slice(0, 100)}`);\n\t\t\t// Kill the child process before dropping the reference to prevent orphans\n\t\t\tif (this.client) {\n\t\t\t\tthis.client.stop().catch(() => {});\n\t\t\t}\n\t\t\tthis.exited = true;\n\t\t\tthis.client = null;\n\t\t}\n\t}\n\n\tprivate async ensureAlive(): Promise<void> {\n\t\tif (!this.client || this.exited) {\n\t\t\tlog(\"[BRIDGE] Restarting dead RPC process\");\n\t\t\tthis.client = null;\n\t\t\tthis.exited = false;\n\t\t\tawait this.start();\n\t\t\t// Session selection is handled by ensureBridgeWithSession — not here.\n\t\t\t// The previous session is still the latest and will be picked up by\n\t\t\t// resumeLatest() on the next message.\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent-bridge.js","sourceRoot":"","sources":["../src/agent-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent bridge — manages the RPC connection to a dreb agent process.\n * One bridge per user, handles lifecycle, event subscription, and session management.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { RpcClient, type RpcSessionInfo } from \"@dreb/coding-agent/rpc\";\nimport type { Config } from \"./config.js\";\nimport { log } from \"./util/telegram.js\";\n\n/**\n * Resolve the absolute path to the dreb CLI entry point.\n * RpcClient defaults to \"dist/cli.js\" (relative to cwd), but we need\n * the absolute path since the bot's working dir differs from the dreb repo.\n */\nfunction resolveDrebCliPath(): string {\n\t// import.meta.resolve finds @dreb/coding-agent/dist/index.js\n\tconst resolved = import.meta.resolve(\"@dreb/coding-agent\");\n\tconst distDir = dirname(fileURLToPath(resolved));\n\treturn join(distDir, \"cli.js\");\n}\n\n/** RPC events include both AgentEvent and session-specific events */\ntype RpcEvent = { type: string; [key: string]: any };\n\nexport type AgentEventListener = (event: RpcEvent) => void;\n\nexport class AgentBridge {\n\tprivate client: RpcClient | null = null;\n\tprivate eventListeners: AgentEventListener[] = [];\n\tprivate _isStreaming = false;\n\tprivate _sessionFile: string | undefined;\n\tprivate _sessionId: string | undefined;\n\tprivate exited = false;\n\n\tconstructor(private config: Config) {}\n\n\t/** Whether the RPC process is alive */\n\tget isAlive(): boolean {\n\t\treturn this.client !== null && !this.exited;\n\t}\n\n\t/** Whether the agent is currently streaming a response */\n\tget isStreaming(): boolean {\n\t\treturn this._isStreaming;\n\t}\n\n\t/** Current session file path */\n\tget sessionFile(): string | undefined {\n\t\treturn this._sessionFile;\n\t}\n\n\t/** Current session ID */\n\tget sessionId(): string | undefined {\n\t\treturn this._sessionId;\n\t}\n\n\t/**\n\t * Start the RPC process. Does NOT resume a session — call resumeLatest() or newSession() after.\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this.client) return;\n\n\t\tthis.client = new RpcClient({\n\t\t\tcliPath: resolveDrebCliPath(),\n\t\t\tcwd: this.config.workingDir,\n\t\t\tprovider: this.config.provider,\n\t\t\tmodel: this.config.model,\n\t\t\targs: [\"--ui\", \"telegram\"],\n\t\t});\n\n\t\tthis.exited = false;\n\t\tawait this.client.start();\n\n\t\t// Subscribe to events and forward to listeners\n\t\tthis.client.onEvent((event) => {\n\t\t\tthis.handleEvent(event as RpcEvent);\n\t\t});\n\n\t\t// Detect process exit\n\t\t// RpcClient doesn't expose a direct \"on exit\" — we detect it when send() fails\n\t\tlog(\"[BRIDGE] RPC process started\");\n\t}\n\n\t/**\n\t * Resume the most recent session, or do nothing if no sessions exist.\n\t */\n\tasync resumeLatest(): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst sessions = await this.client.listSessions();\n\t\t\tif (sessions.length === 0) return false;\n\n\t\t\tconst latest = sessions[0]; // Already sorted by modified desc\n\t\t\tconst result = await this.client.switchSession(latest.path);\n\t\t\tif (!result.cancelled) {\n\t\t\t\tthis._sessionFile = latest.path;\n\t\t\t\tthis._sessionId = latest.id;\n\t\t\t\tlog(`[BRIDGE] Resumed session ${latest.id.slice(0, 8)}`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to resume latest session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * List available sessions.\n\t */\n\tasync listSessions(): Promise<RpcSessionInfo[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.listSessions();\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to list sessions: ${e}`);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Switch to a specific session by path.\n\t */\n\tasync switchSession(sessionPath: string): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst result = await this.client.switchSession(sessionPath);\n\t\t\tif (!result.cancelled) {\n\t\t\t\tthis._sessionFile = sessionPath;\n\t\t\t\tconst state = await this.client.getState();\n\t\t\t\tthis._sessionId = state.sessionId;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to switch session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Create a new session.\n\t */\n\tasync newSession(): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst result = await this.client.newSession();\n\t\t\tif (!result.cancelled) {\n\t\t\t\tconst state = await this.client.getState();\n\t\t\t\tthis._sessionFile = state.sessionFile;\n\t\t\t\tthis._sessionId = state.sessionId;\n\t\t\t\tlog(`[BRIDGE] New session ${state.sessionId.slice(0, 8)}`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to create new session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Send a prompt to the agent.\n\t */\n\tasync prompt(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.prompt(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a steering message to interrupt the agent mid-run.\n\t * The agent injects it after the current tool-call batch finishes.\n\t */\n\tasync steer(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.steer(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a follow-up message for after the agent finishes its current run.\n\t */\n\tasync followUp(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.followUp(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Abort the current operation.\n\t */\n\tasync abort(): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tawait this.client.abort();\n\t\t} catch {\n\t\t\t// Process may have already exited\n\t\t}\n\t}\n\n\t/**\n\t * Get the dreb version.\n\t */\n\tasync getVersion(): Promise<string> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\treturn await this.client!.getVersion();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get session statistics.\n\t */\n\tasync getSessionStats(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getSessionStats();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get performance statistics.\n\t */\n\tasync getPerformanceStats(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getPerformanceStats();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get current state.\n\t */\n\tasync getState(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getState();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get available commands (skills, extensions, prompt templates).\n\t */\n\tasync getCommands(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getCommands();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Compact context.\n\t */\n\tasync compact(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.compact();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get available models.\n\t */\n\tasync getAvailableModels(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getAvailableModels();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Set model.\n\t */\n\tasync setModel(provider: string, modelId: string): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.setModel(provider, modelId);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Resolve a model pattern using the same logic as CLI/TUI.\n\t */\n\tasync resolveModel(pattern: string): Promise<{ model: any; warning?: string } | null> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.resolveModel(pattern);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Set thinking level.\n\t */\n\tasync setThinkingLevel(level: string): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tawait this.client.setThinkingLevel(level as any);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get all messages.\n\t */\n\tasync getMessages(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getMessages();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Hatch a new buddy companion. Runs inside the agent process\n\t * so API keys never cross the process boundary.\n\t */\n\tasync buddyHatch(): Promise<any> {\n\t\tif (!this.client) throw new Error(\"Agent not connected.\");\n\t\ttry {\n\t\t\treturn await this.client.buddyHatch();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Reroll the buddy companion. Runs inside the agent process\n\t * so API keys never cross the process boundary.\n\t */\n\tasync buddyReroll(): Promise<any> {\n\t\tif (!this.client) throw new Error(\"Agent not connected.\");\n\t\ttry {\n\t\t\treturn await this.client.buddyReroll();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get last assistant text.\n\t */\n\tasync getLastAssistantText(): Promise<string | null> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getLastAssistantText();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Refresh session info from the RPC process state.\n\t */\n\tasync refreshSessionInfo(): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tconst state = await this.client.getState();\n\t\t\tthis._sessionFile = state.sessionFile;\n\t\t\tthis._sessionId = state.sessionId;\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\t// Non-critical — don't re-throw\n\t\t}\n\t}\n\n\t/**\n\t * Subscribe to agent events.\n\t */\n\tonEvent(listener: AgentEventListener): () => void {\n\t\tthis.eventListeners.push(listener);\n\t\treturn () => {\n\t\t\tconst idx = this.eventListeners.indexOf(listener);\n\t\t\tif (idx !== -1) this.eventListeners.splice(idx, 1);\n\t\t};\n\t}\n\n\t/**\n\t * Stop the RPC process.\n\t */\n\tasync stop(): Promise<void> {\n\t\tif (this.client) {\n\t\t\ttry {\n\t\t\t\tawait this.client.stop();\n\t\t\t} catch {\n\t\t\t\t// Ignore\n\t\t\t}\n\t\t\tthis.client = null;\n\t\t\tthis.exited = true;\n\t\t\tthis.eventListeners = [];\n\t\t\tlog(\"[BRIDGE] RPC process stopped\");\n\t\t}\n\t}\n\n\t// =========================================================================\n\t// Internal\n\t// =========================================================================\n\n\tprivate handleEvent(event: RpcEvent): void {\n\t\t// Track streaming state\n\t\tif (event.type === \"agent_start\") this._isStreaming = true;\n\t\tif (event.type === \"agent_end\") {\n\t\t\tthis._isStreaming = false;\n\t\t\t// Capture session info from agent_end messages\n\t\t\t// Session file/id updates happen via getState after prompt\n\t\t}\n\n\t\tfor (const listener of this.eventListeners) {\n\t\t\ttry {\n\t\t\t\tlistener(event);\n\t\t\t} catch (e) {\n\t\t\t\tlog(`[BRIDGE] Event listener error: ${e}`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleProcessError(e: unknown): void {\n\t\tconst msg = e instanceof Error ? e.message : String(e);\n\t\tif (\n\t\t\tmsg.includes(\"not started\") ||\n\t\t\tmsg.includes(\"not running\") ||\n\t\t\tmsg.includes(\"EPIPE\") ||\n\t\t\tmsg.includes(\"write after end\") ||\n\t\t\tmsg.includes(\"Timeout waiting for response\") ||\n\t\t\tmsg.includes(\"RPC process exited\")\n\t\t) {\n\t\t\tlog(`[BRIDGE] RPC process exited or hung: ${msg.slice(0, 100)}`);\n\t\t\t// Kill the child process before dropping the reference to prevent orphans\n\t\t\tif (this.client) {\n\t\t\t\tthis.client.stop().catch(() => {});\n\t\t\t}\n\t\t\tthis.exited = true;\n\t\t\tthis.client = null;\n\t\t}\n\t}\n\n\tprivate async ensureAlive(): Promise<void> {\n\t\tif (!this.client || this.exited) {\n\t\t\tlog(\"[BRIDGE] Restarting dead RPC process\");\n\t\t\tthis.client = null;\n\t\t\tthis.exited = false;\n\t\t\tawait this.start();\n\t\t\t// Session selection is handled by ensureBridgeWithSession — not here.\n\t\t\t// The previous session is still the latest and will be picked up by\n\t\t\t// resumeLatest() on the next message.\n\t\t}\n\t}\n}\n"]}
|