@dreb/telegram 2.4.0 → 2.4.2
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-bridge.d.ts +7 -0
- package/dist/agent-bridge.d.ts.map +1 -1
- package/dist/agent-bridge.js +14 -0
- package/dist/agent-bridge.js.map +1 -1
- package/dist/bot.d.ts.map +1 -1
- package/dist/bot.js +9 -0
- package/dist/bot.js.map +1 -1
- package/dist/bridge-lifecycle.d.ts.map +1 -1
- package/dist/bridge-lifecycle.js +14 -0
- package/dist/bridge-lifecycle.js.map +1 -1
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +10 -22
- package/dist/commands/agent.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/agent-bridge.d.ts
CHANGED
|
@@ -104,6 +104,13 @@ export declare class AgentBridge {
|
|
|
104
104
|
* Set model.
|
|
105
105
|
*/
|
|
106
106
|
setModel(provider: string, modelId: string): Promise<any>;
|
|
107
|
+
/**
|
|
108
|
+
* Resolve a model pattern using the same logic as CLI/TUI.
|
|
109
|
+
*/
|
|
110
|
+
resolveModel(pattern: string): Promise<{
|
|
111
|
+
model: any;
|
|
112
|
+
warning?: string;
|
|
113
|
+
} | null>;
|
|
107
114
|
/**
|
|
108
115
|
* Set thinking level.
|
|
109
116
|
*/
|
|
@@ -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 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 * 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\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 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
|
@@ -297,6 +297,20 @@ export class AgentBridge {
|
|
|
297
297
|
throw e;
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* Resolve a model pattern using the same logic as CLI/TUI.
|
|
302
|
+
*/
|
|
303
|
+
async resolveModel(pattern) {
|
|
304
|
+
if (!this.client)
|
|
305
|
+
return null;
|
|
306
|
+
try {
|
|
307
|
+
return await this.client.resolveModel(pattern);
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
this.handleProcessError(e);
|
|
311
|
+
throw e;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
300
314
|
/**
|
|
301
315
|
* Set thinking level.
|
|
302
316
|
*/
|
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 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 * 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\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 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/bot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAE7B,OAAO,EAAoB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAE7B,OAAO,EAAoB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA6B5C,iBAAS,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAQhE;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAqF7C;AAED,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC","sourcesContent":["/**\n * Bot setup — creates the grammy Bot, wires up auth, commands, and message handlers.\n */\n\nimport { Bot } from \"grammy\";\nimport { ensureBridgeWithSession } from \"./bridge-lifecycle.js\";\nimport { registerCommands, setMyCommands } from \"./commands/index.js\";\nimport type { Config } from \"./config.js\";\nimport { handleFile } from \"./handlers/file.js\";\nimport { sendPrompt } from \"./handlers/message.js\";\nimport type { UserState } from \"./types.js\";\nimport { log, safeDelete, safeSend } from \"./util/telegram.js\";\n\n/** Flush any pending model fallback warning to the user (shown once after bridge restart) */\nasync function flushPendingWarning(api: Bot[\"api\"], chatId: number, userState: UserState): Promise<void> {\n\tif (userState.pendingModelFallbackWarning) {\n\t\tawait safeSend(api, chatId, `⚠️ _${userState.pendingModelFallbackWarning}_`);\n\t\tuserState.pendingModelFallbackWarning = undefined;\n\t}\n}\n\n/** Per-user state store */\nconst userStates = new Map<number, UserState>();\n\nfunction createUserState(config: Config): UserState {\n\treturn {\n\t\tbridge: null,\n\t\tconfig,\n\t\tpromptInFlight: false,\n\t\tnewSessionFlag: false,\n\t\tnewSessionCwd: null,\n\t\teffectiveCwd: null,\n\t\tbackgroundAgents: new Map(),\n\t\tstopRequested: false,\n\t\toutbox: [],\n\t\tbuddyController: null,\n\t};\n}\n\nfunction getUserState(userId: number, config?: Config): UserState {\n\tlet state = userStates.get(userId);\n\tif (!state) {\n\t\tif (!config) throw new Error(\"Config required for new user state creation\");\n\t\tstate = createUserState(config);\n\t\tuserStates.set(userId, state);\n\t}\n\treturn state;\n}\n\nexport function createBot(config: Config): Bot {\n\tconst bot = new Bot(config.botToken);\n\n\t// Auth middleware — check allowed user IDs\n\tif (config.allowedUserIds.length > 0) {\n\t\tbot.use(async (ctx, next) => {\n\t\t\tconst userId = ctx.from?.id;\n\t\t\tif (!userId || !config.allowedUserIds.includes(userId)) {\n\t\t\t\tlog(`[AUTH] Rejected user ${userId}`);\n\t\t\t\tawait ctx.reply(\"⛔ Not authorized\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait next();\n\t\t});\n\t}\n\n\t// Register slash commands — bind config so callers see (userId) => UserState\n\tconst boundGetUserState = (userId: number) => getUserState(userId, config);\n\tregisterCommands(bot, config, boundGetUserState);\n\n\t// Text message handler\n\tbot.on(\"message:text\", async (ctx) => {\n\t\t// Skip commands (already handled above)\n\t\tif (ctx.message.text.startsWith(\"/\")) return;\n\n\t\tconst userId = ctx.from!.id;\n\t\tconst userState = getUserState(userId, config);\n\t\tconst isBusy = userState.bridge?.isStreaming || userState.promptInFlight;\n\n\t\t// Show status immediately\n\t\tconst statusText = isBusy ? \"↩️ _Steering..._\" : \"🧠 _Thinking..._\";\n\t\tlet statusMessageId: number | null = null;\n\t\ttry {\n\t\t\tconst sent = await ctx.reply(statusText, { parse_mode: \"Markdown\" });\n\t\t\tstatusMessageId = sent.message_id;\n\t\t} catch (e) {\n\t\t\tlog(`[MSG] Failed to send status: ${e}`);\n\t\t}\n\n\t\t// Ensure bridge is alive and session is set up\n\t\ttry {\n\t\t\tawait ensureBridgeWithSession(config, userState);\n\t\t} catch (e) {\n\t\t\tlog(`[MSG] Bridge setup failed: ${e}`);\n\t\t\tif (statusMessageId) void safeDelete(ctx.api, ctx.chat!.id, statusMessageId);\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Failed to start agent: ${e}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait flushPendingWarning(ctx.api, ctx.chat!.id, userState);\n\n\t\tsendPrompt(ctx.api, userState, {\n\t\t\tchatId: ctx.chat!.id,\n\t\t\treplyToId: ctx.message.message_id,\n\t\t\tuserId,\n\t\t\tprompt: ctx.message.text,\n\t\t\tstatusMessageId,\n\t\t});\n\t});\n\n\t// File handler (documents, photos, voice, audio, video)\n\tbot.on([\"message:document\", \"message:photo\", \"message:voice\", \"message:audio\", \"message:video\"], async (ctx) => {\n\t\tconst userId = ctx.from!.id;\n\t\tconst userState = getUserState(userId, config);\n\n\t\t// Ensure bridge is alive\n\t\ttry {\n\t\t\tawait ensureBridgeWithSession(config, userState);\n\t\t} catch (e) {\n\t\t\tlog(`[FILE] Bridge setup failed: ${e}`);\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Failed to start agent: ${e}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait flushPendingWarning(ctx.api, ctx.chat!.id, userState);\n\n\t\tawait handleFile(ctx, ctx.api, boundGetUserState);\n\t});\n\n\t// Error handler\n\tbot.catch((err) => {\n\t\tlog(`[ERROR] ${err.error}`);\n\t});\n\n\treturn bot;\n}\n\nexport { getUserState, setMyCommands };\n"]}
|
package/dist/bot.js
CHANGED
|
@@ -7,6 +7,13 @@ import { registerCommands, setMyCommands } from "./commands/index.js";
|
|
|
7
7
|
import { handleFile } from "./handlers/file.js";
|
|
8
8
|
import { sendPrompt } from "./handlers/message.js";
|
|
9
9
|
import { log, safeDelete, safeSend } from "./util/telegram.js";
|
|
10
|
+
/** Flush any pending model fallback warning to the user (shown once after bridge restart) */
|
|
11
|
+
async function flushPendingWarning(api, chatId, userState) {
|
|
12
|
+
if (userState.pendingModelFallbackWarning) {
|
|
13
|
+
await safeSend(api, chatId, `⚠️ _${userState.pendingModelFallbackWarning}_`);
|
|
14
|
+
userState.pendingModelFallbackWarning = undefined;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
10
17
|
/** Per-user state store */
|
|
11
18
|
const userStates = new Map();
|
|
12
19
|
function createUserState(config) {
|
|
@@ -79,6 +86,7 @@ export function createBot(config) {
|
|
|
79
86
|
await safeSend(ctx.api, ctx.chat.id, `❌ Failed to start agent: ${e}`);
|
|
80
87
|
return;
|
|
81
88
|
}
|
|
89
|
+
await flushPendingWarning(ctx.api, ctx.chat.id, userState);
|
|
82
90
|
sendPrompt(ctx.api, userState, {
|
|
83
91
|
chatId: ctx.chat.id,
|
|
84
92
|
replyToId: ctx.message.message_id,
|
|
@@ -100,6 +108,7 @@ export function createBot(config) {
|
|
|
100
108
|
await safeSend(ctx.api, ctx.chat.id, `❌ Failed to start agent: ${e}`);
|
|
101
109
|
return;
|
|
102
110
|
}
|
|
111
|
+
await flushPendingWarning(ctx.api, ctx.chat.id, userState);
|
|
103
112
|
await handleFile(ctx, ctx.api, boundGetUserState);
|
|
104
113
|
});
|
|
105
114
|
// Error handler
|
package/dist/bot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bot.js","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bot setup — creates the grammy Bot, wires up auth, commands, and message handlers.\n */\n\nimport { Bot } from \"grammy\";\nimport { ensureBridgeWithSession } from \"./bridge-lifecycle.js\";\nimport { registerCommands, setMyCommands } from \"./commands/index.js\";\nimport type { Config } from \"./config.js\";\nimport { handleFile } from \"./handlers/file.js\";\nimport { sendPrompt } from \"./handlers/message.js\";\nimport type { UserState } from \"./types.js\";\nimport { log, safeDelete, safeSend } from \"./util/telegram.js\";\n\n/** Per-user state store */\nconst userStates = new Map<number, UserState>();\n\nfunction createUserState(config: Config): UserState {\n\treturn {\n\t\tbridge: null,\n\t\tconfig,\n\t\tpromptInFlight: false,\n\t\tnewSessionFlag: false,\n\t\tnewSessionCwd: null,\n\t\teffectiveCwd: null,\n\t\tbackgroundAgents: new Map(),\n\t\tstopRequested: false,\n\t\toutbox: [],\n\t\tbuddyController: null,\n\t};\n}\n\nfunction getUserState(userId: number, config?: Config): UserState {\n\tlet state = userStates.get(userId);\n\tif (!state) {\n\t\tif (!config) throw new Error(\"Config required for new user state creation\");\n\t\tstate = createUserState(config);\n\t\tuserStates.set(userId, state);\n\t}\n\treturn state;\n}\n\nexport function createBot(config: Config): Bot {\n\tconst bot = new Bot(config.botToken);\n\n\t// Auth middleware — check allowed user IDs\n\tif (config.allowedUserIds.length > 0) {\n\t\tbot.use(async (ctx, next) => {\n\t\t\tconst userId = ctx.from?.id;\n\t\t\tif (!userId || !config.allowedUserIds.includes(userId)) {\n\t\t\t\tlog(`[AUTH] Rejected user ${userId}`);\n\t\t\t\tawait ctx.reply(\"⛔ Not authorized\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait next();\n\t\t});\n\t}\n\n\t// Register slash commands — bind config so callers see (userId) => UserState\n\tconst boundGetUserState = (userId: number) => getUserState(userId, config);\n\tregisterCommands(bot, config, boundGetUserState);\n\n\t// Text message handler\n\tbot.on(\"message:text\", async (ctx) => {\n\t\t// Skip commands (already handled above)\n\t\tif (ctx.message.text.startsWith(\"/\")) return;\n\n\t\tconst userId = ctx.from!.id;\n\t\tconst userState = getUserState(userId, config);\n\t\tconst isBusy = userState.bridge?.isStreaming || userState.promptInFlight;\n\n\t\t// Show status immediately\n\t\tconst statusText = isBusy ? \"↩️ _Steering..._\" : \"🧠 _Thinking..._\";\n\t\tlet statusMessageId: number | null = null;\n\t\ttry {\n\t\t\tconst sent = await ctx.reply(statusText, { parse_mode: \"Markdown\" });\n\t\t\tstatusMessageId = sent.message_id;\n\t\t} catch (e) {\n\t\t\tlog(`[MSG] Failed to send status: ${e}`);\n\t\t}\n\n\t\t// Ensure bridge is alive and session is set up\n\t\ttry {\n\t\t\tawait ensureBridgeWithSession(config, userState);\n\t\t} catch (e) {\n\t\t\tlog(`[MSG] Bridge setup failed: ${e}`);\n\t\t\tif (statusMessageId) void safeDelete(ctx.api, ctx.chat!.id, statusMessageId);\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Failed to start agent: ${e}`);\n\t\t\treturn;\n\t\t}\n\n\t\tsendPrompt(ctx.api, userState, {\n\t\t\tchatId: ctx.chat!.id,\n\t\t\treplyToId: ctx.message.message_id,\n\t\t\tuserId,\n\t\t\tprompt: ctx.message.text,\n\t\t\tstatusMessageId,\n\t\t});\n\t});\n\n\t// File handler (documents, photos, voice, audio, video)\n\tbot.on([\"message:document\", \"message:photo\", \"message:voice\", \"message:audio\", \"message:video\"], async (ctx) => {\n\t\tconst userId = ctx.from!.id;\n\t\tconst userState = getUserState(userId, config);\n\n\t\t// Ensure bridge is alive\n\t\ttry {\n\t\t\tawait ensureBridgeWithSession(config, userState);\n\t\t} catch (e) {\n\t\t\tlog(`[FILE] Bridge setup failed: ${e}`);\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Failed to start agent: ${e}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait handleFile(ctx, ctx.api, boundGetUserState);\n\t});\n\n\t// Error handler\n\tbot.catch((err) => {\n\t\tlog(`[ERROR] ${err.error}`);\n\t});\n\n\treturn bot;\n}\n\nexport { getUserState, setMyCommands };\n"]}
|
|
1
|
+
{"version":3,"file":"bot.js","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bot setup — creates the grammy Bot, wires up auth, commands, and message handlers.\n */\n\nimport { Bot } from \"grammy\";\nimport { ensureBridgeWithSession } from \"./bridge-lifecycle.js\";\nimport { registerCommands, setMyCommands } from \"./commands/index.js\";\nimport type { Config } from \"./config.js\";\nimport { handleFile } from \"./handlers/file.js\";\nimport { sendPrompt } from \"./handlers/message.js\";\nimport type { UserState } from \"./types.js\";\nimport { log, safeDelete, safeSend } from \"./util/telegram.js\";\n\n/** Flush any pending model fallback warning to the user (shown once after bridge restart) */\nasync function flushPendingWarning(api: Bot[\"api\"], chatId: number, userState: UserState): Promise<void> {\n\tif (userState.pendingModelFallbackWarning) {\n\t\tawait safeSend(api, chatId, `⚠️ _${userState.pendingModelFallbackWarning}_`);\n\t\tuserState.pendingModelFallbackWarning = undefined;\n\t}\n}\n\n/** Per-user state store */\nconst userStates = new Map<number, UserState>();\n\nfunction createUserState(config: Config): UserState {\n\treturn {\n\t\tbridge: null,\n\t\tconfig,\n\t\tpromptInFlight: false,\n\t\tnewSessionFlag: false,\n\t\tnewSessionCwd: null,\n\t\teffectiveCwd: null,\n\t\tbackgroundAgents: new Map(),\n\t\tstopRequested: false,\n\t\toutbox: [],\n\t\tbuddyController: null,\n\t};\n}\n\nfunction getUserState(userId: number, config?: Config): UserState {\n\tlet state = userStates.get(userId);\n\tif (!state) {\n\t\tif (!config) throw new Error(\"Config required for new user state creation\");\n\t\tstate = createUserState(config);\n\t\tuserStates.set(userId, state);\n\t}\n\treturn state;\n}\n\nexport function createBot(config: Config): Bot {\n\tconst bot = new Bot(config.botToken);\n\n\t// Auth middleware — check allowed user IDs\n\tif (config.allowedUserIds.length > 0) {\n\t\tbot.use(async (ctx, next) => {\n\t\t\tconst userId = ctx.from?.id;\n\t\t\tif (!userId || !config.allowedUserIds.includes(userId)) {\n\t\t\t\tlog(`[AUTH] Rejected user ${userId}`);\n\t\t\t\tawait ctx.reply(\"⛔ Not authorized\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait next();\n\t\t});\n\t}\n\n\t// Register slash commands — bind config so callers see (userId) => UserState\n\tconst boundGetUserState = (userId: number) => getUserState(userId, config);\n\tregisterCommands(bot, config, boundGetUserState);\n\n\t// Text message handler\n\tbot.on(\"message:text\", async (ctx) => {\n\t\t// Skip commands (already handled above)\n\t\tif (ctx.message.text.startsWith(\"/\")) return;\n\n\t\tconst userId = ctx.from!.id;\n\t\tconst userState = getUserState(userId, config);\n\t\tconst isBusy = userState.bridge?.isStreaming || userState.promptInFlight;\n\n\t\t// Show status immediately\n\t\tconst statusText = isBusy ? \"↩️ _Steering..._\" : \"🧠 _Thinking..._\";\n\t\tlet statusMessageId: number | null = null;\n\t\ttry {\n\t\t\tconst sent = await ctx.reply(statusText, { parse_mode: \"Markdown\" });\n\t\t\tstatusMessageId = sent.message_id;\n\t\t} catch (e) {\n\t\t\tlog(`[MSG] Failed to send status: ${e}`);\n\t\t}\n\n\t\t// Ensure bridge is alive and session is set up\n\t\ttry {\n\t\t\tawait ensureBridgeWithSession(config, userState);\n\t\t} catch (e) {\n\t\t\tlog(`[MSG] Bridge setup failed: ${e}`);\n\t\t\tif (statusMessageId) void safeDelete(ctx.api, ctx.chat!.id, statusMessageId);\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Failed to start agent: ${e}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait flushPendingWarning(ctx.api, ctx.chat!.id, userState);\n\n\t\tsendPrompt(ctx.api, userState, {\n\t\t\tchatId: ctx.chat!.id,\n\t\t\treplyToId: ctx.message.message_id,\n\t\t\tuserId,\n\t\t\tprompt: ctx.message.text,\n\t\t\tstatusMessageId,\n\t\t});\n\t});\n\n\t// File handler (documents, photos, voice, audio, video)\n\tbot.on([\"message:document\", \"message:photo\", \"message:voice\", \"message:audio\", \"message:video\"], async (ctx) => {\n\t\tconst userId = ctx.from!.id;\n\t\tconst userState = getUserState(userId, config);\n\n\t\t// Ensure bridge is alive\n\t\ttry {\n\t\t\tawait ensureBridgeWithSession(config, userState);\n\t\t} catch (e) {\n\t\t\tlog(`[FILE] Bridge setup failed: ${e}`);\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Failed to start agent: ${e}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait flushPendingWarning(ctx.api, ctx.chat!.id, userState);\n\n\t\tawait handleFile(ctx, ctx.api, boundGetUserState);\n\t});\n\n\t// Error handler\n\tbot.catch((err) => {\n\t\tlog(`[ERROR] ${err.error}`);\n\t});\n\n\treturn bot;\n}\n\nexport { getUserState, setMyCommands };\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-lifecycle.d.ts","sourceRoot":"","sources":["../src/bridge-lifecycle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CA2B7F;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"bridge-lifecycle.d.ts","sourceRoot":"","sources":["../src/bridge-lifecycle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CA2B7F;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAmDxG","sourcesContent":["/**\n * Bridge lifecycle helpers — extracted to avoid circular imports between bot.ts and commands.\n */\n\nimport { AgentBridge } from \"./agent-bridge.js\";\nimport type { Config } from \"./config.js\";\nimport type { UserState } from \"./types.js\";\n\n/**\n * Ensure the user has an active agent bridge, starting one if needed.\n * Does NOT handle session selection — that's up to the caller.\n */\nexport async function ensureBridge(config: Config, userState: UserState): Promise<AgentBridge> {\n\tif (!userState.bridge || !userState.bridge.isAlive) {\n\t\t// Use effectiveCwd if set — preserves custom cwd across bridge crashes\n\t\tconst effectiveConfig =\n\t\t\tuserState.effectiveCwd && userState.effectiveCwd !== config.workingDir\n\t\t\t\t? { ...config, workingDir: userState.effectiveCwd }\n\t\t\t\t: config;\n\t\tconst bridge = new AgentBridge(effectiveConfig);\n\t\tawait bridge.start();\n\t\tuserState.bridge = bridge;\n\n\t\t// Wire up background agent tracking\n\t\tbridge.onEvent((event: any) => {\n\t\t\tif (event.type === \"background_agent_start\") {\n\t\t\t\tuserState.backgroundAgents.set(event.agentId, {\n\t\t\t\t\tagentId: event.agentId,\n\t\t\t\t\tagentType: event.agentType,\n\t\t\t\t\ttaskSummary: event.taskSummary,\n\t\t\t\t\tstartTime: Date.now(),\n\t\t\t\t});\n\t\t\t} else if (event.type === \"background_agent_end\") {\n\t\t\t\tuserState.backgroundAgents.delete(event.agentId);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn userState.bridge;\n}\n\n/**\n * Ensure bridge is alive AND a session is selected.\n * Used by message/file handlers and skill commands before prompting.\n */\nexport async function ensureBridgeWithSession(config: Config, userState: UserState): Promise<AgentBridge> {\n\t// Handle new session — always kill and recreate the bridge for clean state.\n\t// For /new <path>: uses the user-specified directory.\n\t// For /new (bare): preserves the current effectiveCwd.\n\tif (userState.newSessionFlag) {\n\t\tconst cwd = userState.newSessionCwd ?? userState.effectiveCwd ?? config.workingDir;\n\t\tuserState.newSessionFlag = false;\n\t\tuserState.newSessionCwd = null;\n\n\t\t// Kill existing bridge and start a new one with the resolved cwd\n\t\tif (userState.bridge?.isAlive) {\n\t\t\tawait userState.bridge.stop();\n\t\t}\n\t\tuserState.bridge = null;\n\n\t\tconst customConfig = { ...config, workingDir: cwd };\n\t\t// Set effectiveCwd BEFORE ensureBridge so the stale-cwd override\n\t\t// in ensureBridge doesn't clobber the resolved directory\n\t\tuserState.effectiveCwd = cwd;\n\t\tconst bridge = await ensureBridge(customConfig, userState);\n\t\tawait bridge.newSession();\n\t\treturn bridge;\n\t}\n\n\tconst hadBridge = !!userState.bridge?.isAlive;\n\tconst bridge = await ensureBridge(config, userState);\n\tconst freshBridge = !hadBridge;\n\n\t// Track effective cwd (default from config on first bridge creation)\n\tif (!userState.effectiveCwd) {\n\t\tuserState.effectiveCwd = config.workingDir;\n\t}\n\n\t// No session yet — try to resume latest\n\tif (!bridge.sessionId) {\n\t\tawait bridge.resumeLatest();\n\t}\n\n\t// Check for model fallback warning on fresh bridge (e.g. after crash)\n\tif (freshBridge) {\n\t\ttry {\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.modelFallbackMessage) {\n\t\t\t\tuserState.pendingModelFallbackWarning = state.modelFallbackMessage;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Non-critical — the warning is best-effort\n\t\t}\n\t}\n\n\treturn bridge;\n}\n"]}
|
package/dist/bridge-lifecycle.js
CHANGED
|
@@ -57,7 +57,9 @@ export async function ensureBridgeWithSession(config, userState) {
|
|
|
57
57
|
await bridge.newSession();
|
|
58
58
|
return bridge;
|
|
59
59
|
}
|
|
60
|
+
const hadBridge = !!userState.bridge?.isAlive;
|
|
60
61
|
const bridge = await ensureBridge(config, userState);
|
|
62
|
+
const freshBridge = !hadBridge;
|
|
61
63
|
// Track effective cwd (default from config on first bridge creation)
|
|
62
64
|
if (!userState.effectiveCwd) {
|
|
63
65
|
userState.effectiveCwd = config.workingDir;
|
|
@@ -66,6 +68,18 @@ export async function ensureBridgeWithSession(config, userState) {
|
|
|
66
68
|
if (!bridge.sessionId) {
|
|
67
69
|
await bridge.resumeLatest();
|
|
68
70
|
}
|
|
71
|
+
// Check for model fallback warning on fresh bridge (e.g. after crash)
|
|
72
|
+
if (freshBridge) {
|
|
73
|
+
try {
|
|
74
|
+
const state = await bridge.getState();
|
|
75
|
+
if (state?.modelFallbackMessage) {
|
|
76
|
+
userState.pendingModelFallbackWarning = state.modelFallbackMessage;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Non-critical — the warning is best-effort
|
|
81
|
+
}
|
|
82
|
+
}
|
|
69
83
|
return bridge;
|
|
70
84
|
}
|
|
71
85
|
//# sourceMappingURL=bridge-lifecycle.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-lifecycle.js","sourceRoot":"","sources":["../src/bridge-lifecycle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIhD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,SAAoB,EAAwB;IAC9F,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpD,yEAAuE;QACvE,MAAM,eAAe,GACpB,SAAS,CAAC,YAAY,IAAI,SAAS,CAAC,YAAY,KAAK,MAAM,CAAC,UAAU;YACrE,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,YAAY,EAAE;YACnD,CAAC,CAAC,MAAM,CAAC;QACX,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,eAAe,CAAC,CAAC;QAChD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC;QAE1B,oCAAoC;QACpC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAU,EAAE,EAAE,CAAC;YAC9B,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;gBAC7C,SAAS,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE;oBAC7C,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;oBAC9B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACrB,CAAC,CAAC;YACJ,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,sBAAsB,EAAE,CAAC;gBAClD,SAAS,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAClD,CAAC;QAAA,CACD,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,SAAS,CAAC,MAAM,CAAC;AAAA,CACxB;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,MAAc,EAAE,SAAoB,EAAwB;IACzG,8EAA4E;IAC5E,sDAAsD;IACtD,uDAAuD;IACvD,IAAI,SAAS,CAAC,cAAc,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,SAAS,CAAC,aAAa,IAAI,SAAS,CAAC,YAAY,IAAI,MAAM,CAAC,UAAU,CAAC;QACnF,SAAS,CAAC,cAAc,GAAG,KAAK,CAAC;QACjC,SAAS,CAAC,aAAa,GAAG,IAAI,CAAC;QAE/B,iEAAiE;QACjE,IAAI,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC/B,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC/B,CAAC;QACD,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC;QAExB,MAAM,YAAY,GAAG,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;QACpD,iEAAiE;QACjE,yDAAyD;QACzD,SAAS,CAAC,YAAY,GAAG,GAAG,CAAC;QAC7B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAC3D,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;QAC1B,OAAO,MAAM,CAAC;IACf,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"bridge-lifecycle.js","sourceRoot":"","sources":["../src/bridge-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bridge lifecycle helpers — extracted to avoid circular imports between bot.ts and commands.\n */\n\nimport { AgentBridge } from \"./agent-bridge.js\";\nimport type { Config } from \"./config.js\";\nimport type { UserState } from \"./types.js\";\n\n/**\n * Ensure the user has an active agent bridge, starting one if needed.\n * Does NOT handle session selection — that's up to the caller.\n */\nexport async function ensureBridge(config: Config, userState: UserState): Promise<AgentBridge> {\n\tif (!userState.bridge || !userState.bridge.isAlive) {\n\t\t// Use effectiveCwd if set — preserves custom cwd across bridge crashes\n\t\tconst effectiveConfig =\n\t\t\tuserState.effectiveCwd && userState.effectiveCwd !== config.workingDir\n\t\t\t\t? { ...config, workingDir: userState.effectiveCwd }\n\t\t\t\t: config;\n\t\tconst bridge = new AgentBridge(effectiveConfig);\n\t\tawait bridge.start();\n\t\tuserState.bridge = bridge;\n\n\t\t// Wire up background agent tracking\n\t\tbridge.onEvent((event: any) => {\n\t\t\tif (event.type === \"background_agent_start\") {\n\t\t\t\tuserState.backgroundAgents.set(event.agentId, {\n\t\t\t\t\tagentId: event.agentId,\n\t\t\t\t\tagentType: event.agentType,\n\t\t\t\t\ttaskSummary: event.taskSummary,\n\t\t\t\t\tstartTime: Date.now(),\n\t\t\t\t});\n\t\t\t} else if (event.type === \"background_agent_end\") {\n\t\t\t\tuserState.backgroundAgents.delete(event.agentId);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn userState.bridge;\n}\n\n/**\n * Ensure bridge is alive AND a session is selected.\n * Used by message/file handlers and skill commands before prompting.\n */\nexport async function ensureBridgeWithSession(config: Config, userState: UserState): Promise<AgentBridge> {\n\t// Handle new session — always kill and recreate the bridge for clean state.\n\t// For /new <path>: uses the user-specified directory.\n\t// For /new (bare): preserves the current effectiveCwd.\n\tif (userState.newSessionFlag) {\n\t\tconst cwd = userState.newSessionCwd ?? userState.effectiveCwd ?? config.workingDir;\n\t\tuserState.newSessionFlag = false;\n\t\tuserState.newSessionCwd = null;\n\n\t\t// Kill existing bridge and start a new one with the resolved cwd\n\t\tif (userState.bridge?.isAlive) {\n\t\t\tawait userState.bridge.stop();\n\t\t}\n\t\tuserState.bridge = null;\n\n\t\tconst customConfig = { ...config, workingDir: cwd };\n\t\t// Set effectiveCwd BEFORE ensureBridge so the stale-cwd override\n\t\t// in ensureBridge doesn't clobber the resolved directory\n\t\tuserState.effectiveCwd = cwd;\n\t\tconst bridge = await ensureBridge(customConfig, userState);\n\t\tawait bridge.newSession();\n\t\treturn bridge;\n\t}\n\n\tconst hadBridge = !!userState.bridge?.isAlive;\n\tconst bridge = await ensureBridge(config, userState);\n\tconst freshBridge = !hadBridge;\n\n\t// Track effective cwd (default from config on first bridge creation)\n\tif (!userState.effectiveCwd) {\n\t\tuserState.effectiveCwd = config.workingDir;\n\t}\n\n\t// No session yet — try to resume latest\n\tif (!bridge.sessionId) {\n\t\tawait bridge.resumeLatest();\n\t}\n\n\t// Check for model fallback warning on fresh bridge (e.g. after crash)\n\tif (freshBridge) {\n\t\ttry {\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.modelFallbackMessage) {\n\t\t\t\tuserState.pendingModelFallbackWarning = state.modelFallbackMessage;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Non-critical — the warning is best-effort\n\t\t}\n\t}\n\n\treturn bridge;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBlF;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAcjF;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ChF;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBlF;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAcjF;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ChF;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqD9F;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BjG","sourcesContent":["/**\n * Agent slash commands: /compact, /agents, /stats, /model, /thinking\n */\n\nimport type { Context } from \"grammy\";\nimport type { UserState } from \"../types.js\";\nimport { log, safeSend } from \"../util/telegram.js\";\n\nexport async function cmdCompact(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\tawait safeSend(ctx.api, chatId, \"🗜 _Compacting context..._\");\n\ttry {\n\t\tconst result = await bridge.compact();\n\t\tif (result) {\n\t\t\tconst before = (result as any).tokensBefore || 0;\n\t\t\tawait safeSend(ctx.api, chatId, `✅ Compacted (was ${Math.round(before / 1000)}k tokens)`);\n\t\t} else {\n\t\t\tawait safeSend(ctx.api, chatId, \"✅ Compacted.\");\n\t\t}\n\t} catch (e) {\n\t\tlog(`[CMD] /compact error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ Compaction failed: ${e}`);\n\t}\n}\n\nexport async function cmdAgents(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\n\tif (userState.backgroundAgents.size === 0) {\n\t\tawait safeSend(ctx.api, chatId, \"No background agents running.\");\n\t\treturn;\n\t}\n\n\tconst lines = [\"🤖 *Background Agents*:\\n\"];\n\tfor (const agent of userState.backgroundAgents.values()) {\n\t\tconst elapsed = Math.round((Date.now() - agent.startTime) / 1000);\n\t\tlines.push(`• *${agent.agentType}* (${elapsed}s)\\n ${agent.taskSummary.slice(0, 200)}`);\n\t}\n\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n}\n\nexport async function cmdStats(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst stats = await bridge.getSessionStats();\n\t\tif (!stats) {\n\t\t\tawait safeSend(ctx.api, chatId, \"No stats available.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = [\"📊 *Session Stats*:\\n\"];\n\t\tlines.push(`Messages: ${stats.userMessages || 0} user, ${stats.assistantMessages || 0} assistant`);\n\t\tlines.push(`Tool calls: ${stats.toolCalls || 0}`);\n\n\t\tif (stats.tokens) {\n\t\t\tconst t = stats.tokens;\n\t\t\tlines.push(`\\nTokens: ${Math.round((t.total || 0) / 1000)}k total`);\n\t\t\tlines.push(` Input: ${Math.round((t.input || 0) / 1000)}k`);\n\t\t\tlines.push(` Output: ${Math.round((t.output || 0) / 1000)}k`);\n\t\t\tif (t.cacheRead) lines.push(` Cache read: ${Math.round(t.cacheRead / 1000)}k`);\n\t\t}\n\n\t\tif (stats.cost != null) {\n\t\t\tlines.push(`\\n💰 Cost: $${stats.cost.toFixed(4)}`);\n\t\t}\n\n\t\tif (stats.contextUsage) {\n\t\t\tconst cu = stats.contextUsage;\n\t\t\tif (cu.percent != null) {\n\t\t\t\tlines.push(\n\t\t\t\t\t`\\n📏 Context: ${cu.percent}% (${Math.round((cu.tokens || 0) / 1000)}k / ${Math.round((cu.contextWindow || 0) / 1000)}k)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n\t} catch (e) {\n\t\tlog(`[CMD] /stats error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ Failed to get stats: ${e}`);\n\t}\n}\n\nexport async function cmdModel(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tif (!args.trim()) {\n\t\t\t// Show current model\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.model) {\n\t\t\t\tawait safeSend(ctx.api, chatId, `🧠 Current model: \\`${state.model.provider}/${state.model.id}\\``);\n\t\t\t} else {\n\t\t\t\tawait safeSend(ctx.api, chatId, \"🧠 No model set.\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Resolve pattern using the same logic as CLI/TUI\n\t\tconst pattern = args.trim();\n\t\tconst result = await bridge.resolveModel(pattern);\n\n\t\tif (!result) {\n\t\t\t// No match — list available models grouped by provider\n\t\t\tconst models = await bridge.getAvailableModels();\n\t\t\tconst byProvider = new Map<string, string[]>();\n\t\t\tfor (const m of models as any[]) {\n\t\t\t\tconst list = byProvider.get(m.provider) || [];\n\t\t\t\tlist.push(m.id);\n\t\t\t\tbyProvider.set(m.provider, list);\n\t\t\t}\n\t\t\tconst lines = [`No model matching \"${pattern}\". Available:`];\n\t\t\tfor (const [provider, ids] of byProvider) {\n\t\t\t\tlines.push(`\\n*${provider}*:`);\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tlines.push(` \\`${id}\\``);\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\").slice(0, 4000));\n\t\t\treturn;\n\t\t}\n\n\t\tconst model = result.model as any;\n\t\tawait bridge.setModel(model.provider, model.id);\n\t\tconst warning = result.warning ? ` ⚠️ ${result.warning}` : \"\";\n\t\tawait safeSend(ctx.api, chatId, `🧠 Switched to \\`${model.provider}/${model.id}\\`${warning}`);\n\t} catch (e) {\n\t\tlog(`[CMD] /model error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ ${e}`);\n\t}\n}\n\nexport async function cmdThinking(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tif (!args.trim()) {\n\t\t\tconst state = await bridge.getState();\n\t\t\tawait safeSend(ctx.api, chatId, `💭 Thinking level: \\`${state?.thinkingLevel || \"unknown\"}\\``);\n\t\t\treturn;\n\t\t}\n\n\t\tconst level = args.trim().toLowerCase();\n\t\tconst valid = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\t\tif (!valid.includes(level)) {\n\t\t\tawait safeSend(ctx.api, chatId, `Invalid level. Options: ${valid.join(\", \")}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait bridge.setThinkingLevel(level);\n\t\tawait safeSend(ctx.api, chatId, `💭 Thinking level set to \\`${level}\\``);\n\t} catch (e) {\n\t\tlog(`[CMD] /thinking error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ ${e}`);\n\t}\n}\n"]}
|
package/dist/commands/agent.js
CHANGED
|
@@ -97,24 +97,12 @@ export async function cmdModel(ctx, userState, args) {
|
|
|
97
97
|
}
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
|
-
//
|
|
101
|
-
const pattern = args.trim()
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
.
|
|
106
|
-
const id = m.id.toLowerCase();
|
|
107
|
-
const full = `${m.provider}/${m.id}`.toLowerCase();
|
|
108
|
-
if (id === pattern || full === pattern)
|
|
109
|
-
return { model: m, score: 0 };
|
|
110
|
-
if (id.includes(pattern) || full.includes(pattern))
|
|
111
|
-
return { model: m, score: 1 };
|
|
112
|
-
return { model: m, score: -1 };
|
|
113
|
-
})
|
|
114
|
-
.filter((s) => s.score >= 0)
|
|
115
|
-
.sort((a, b) => a.score - b.score);
|
|
116
|
-
if (scored.length === 0) {
|
|
117
|
-
// Group models by provider for readable display
|
|
100
|
+
// Resolve pattern using the same logic as CLI/TUI
|
|
101
|
+
const pattern = args.trim();
|
|
102
|
+
const result = await bridge.resolveModel(pattern);
|
|
103
|
+
if (!result) {
|
|
104
|
+
// No match — list available models grouped by provider
|
|
105
|
+
const models = await bridge.getAvailableModels();
|
|
118
106
|
const byProvider = new Map();
|
|
119
107
|
for (const m of models) {
|
|
120
108
|
const list = byProvider.get(m.provider) || [];
|
|
@@ -131,10 +119,10 @@ export async function cmdModel(ctx, userState, args) {
|
|
|
131
119
|
await safeSend(ctx.api, chatId, lines.join("\n").slice(0, 4000));
|
|
132
120
|
return;
|
|
133
121
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
await safeSend(ctx.api, chatId, `🧠 Switched to \`${
|
|
122
|
+
const model = result.model;
|
|
123
|
+
await bridge.setModel(model.provider, model.id);
|
|
124
|
+
const warning = result.warning ? ` ⚠️ ${result.warning}` : "";
|
|
125
|
+
await safeSend(ctx.api, chatId, `🧠 Switched to \`${model.provider}/${model.id}\`${warning}`);
|
|
138
126
|
}
|
|
139
127
|
catch (e) {
|
|
140
128
|
log(`[CMD] /model error: ${e}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent slash commands: /compact, /agents, /stats, /model, /thinking\n */\n\nimport type { Context } from \"grammy\";\nimport type { UserState } from \"../types.js\";\nimport { log, safeSend } from \"../util/telegram.js\";\n\nexport async function cmdCompact(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\tawait safeSend(ctx.api, chatId, \"🗜 _Compacting context..._\");\n\ttry {\n\t\tconst result = await bridge.compact();\n\t\tif (result) {\n\t\t\tconst before = (result as any).tokensBefore || 0;\n\t\t\tawait safeSend(ctx.api, chatId, `✅ Compacted (was ${Math.round(before / 1000)}k tokens)`);\n\t\t} else {\n\t\t\tawait safeSend(ctx.api, chatId, \"✅ Compacted.\");\n\t\t}\n\t} catch (e) {\n\t\tlog(`[CMD] /compact error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ Compaction failed: ${e}`);\n\t}\n}\n\nexport async function cmdAgents(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\n\tif (userState.backgroundAgents.size === 0) {\n\t\tawait safeSend(ctx.api, chatId, \"No background agents running.\");\n\t\treturn;\n\t}\n\n\tconst lines = [\"🤖 *Background Agents*:\\n\"];\n\tfor (const agent of userState.backgroundAgents.values()) {\n\t\tconst elapsed = Math.round((Date.now() - agent.startTime) / 1000);\n\t\tlines.push(`• *${agent.agentType}* (${elapsed}s)\\n ${agent.taskSummary.slice(0, 200)}`);\n\t}\n\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n}\n\nexport async function cmdStats(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst stats = await bridge.getSessionStats();\n\t\tif (!stats) {\n\t\t\tawait safeSend(ctx.api, chatId, \"No stats available.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = [\"📊 *Session Stats*:\\n\"];\n\t\tlines.push(`Messages: ${stats.userMessages || 0} user, ${stats.assistantMessages || 0} assistant`);\n\t\tlines.push(`Tool calls: ${stats.toolCalls || 0}`);\n\n\t\tif (stats.tokens) {\n\t\t\tconst t = stats.tokens;\n\t\t\tlines.push(`\\nTokens: ${Math.round((t.total || 0) / 1000)}k total`);\n\t\t\tlines.push(` Input: ${Math.round((t.input || 0) / 1000)}k`);\n\t\t\tlines.push(` Output: ${Math.round((t.output || 0) / 1000)}k`);\n\t\t\tif (t.cacheRead) lines.push(` Cache read: ${Math.round(t.cacheRead / 1000)}k`);\n\t\t}\n\n\t\tif (stats.cost != null) {\n\t\t\tlines.push(`\\n💰 Cost: $${stats.cost.toFixed(4)}`);\n\t\t}\n\n\t\tif (stats.contextUsage) {\n\t\t\tconst cu = stats.contextUsage;\n\t\t\tif (cu.percent != null) {\n\t\t\t\tlines.push(\n\t\t\t\t\t`\\n📏 Context: ${cu.percent}% (${Math.round((cu.tokens || 0) / 1000)}k / ${Math.round((cu.contextWindow || 0) / 1000)}k)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n\t} catch (e) {\n\t\tlog(`[CMD] /stats error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ Failed to get stats: ${e}`);\n\t}\n}\n\nexport async function cmdModel(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tif (!args.trim()) {\n\t\t\t// Show current model\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.model) {\n\t\t\t\tawait safeSend(ctx.api, chatId, `🧠 Current model: \\`${state.model.provider}/${state.model.id}\\``);\n\t\t\t} else {\n\t\t\t\tawait safeSend(ctx.api, chatId, \"🧠 No model set.\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Try to match and switch\n\t\tconst pattern = args.trim().toLowerCase();\n\t\tconst models = await bridge.getAvailableModels();\n\n\t\t// Score matches: exact id > exact provider/id > substring\n\t\tconst scored = models\n\t\t\t.map((m: any) => {\n\t\t\t\tconst id = m.id.toLowerCase();\n\t\t\t\tconst full = `${m.provider}/${m.id}`.toLowerCase();\n\t\t\t\tif (id === pattern || full === pattern) return { model: m, score: 0 };\n\t\t\t\tif (id.includes(pattern) || full.includes(pattern)) return { model: m, score: 1 };\n\t\t\t\treturn { model: m, score: -1 };\n\t\t\t})\n\t\t\t.filter((s) => s.score >= 0)\n\t\t\t.sort((a, b) => a.score - b.score);\n\n\t\tif (scored.length === 0) {\n\t\t\t// Group models by provider for readable display\n\t\t\tconst byProvider = new Map<string, string[]>();\n\t\t\tfor (const m of models as any[]) {\n\t\t\t\tconst list = byProvider.get(m.provider) || [];\n\t\t\t\tlist.push(m.id);\n\t\t\t\tbyProvider.set(m.provider, list);\n\t\t\t}\n\t\t\tconst lines = [`No model matching \"${pattern}\". Available:`];\n\t\t\tfor (const [provider, ids] of byProvider) {\n\t\t\t\tlines.push(`\\n*${provider}*:`);\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tlines.push(` \\`${id}\\``);\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\").slice(0, 4000));\n\t\t\treturn;\n\t\t}\n\n\t\t// If multiple matches, prefer exact over substring\n\t\tconst match = scored[0].model;\n\t\tawait bridge.setModel((match as any).provider, (match as any).id);\n\t\tawait safeSend(ctx.api, chatId, `🧠 Switched to \\`${(match as any).provider}/${(match as any).id}\\``);\n\t} catch (e) {\n\t\tlog(`[CMD] /model error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ ${e}`);\n\t}\n}\n\nexport async function cmdThinking(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tif (!args.trim()) {\n\t\t\tconst state = await bridge.getState();\n\t\t\tawait safeSend(ctx.api, chatId, `💭 Thinking level: \\`${state?.thinkingLevel || \"unknown\"}\\``);\n\t\t\treturn;\n\t\t}\n\n\t\tconst level = args.trim().toLowerCase();\n\t\tconst valid = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\t\tif (!valid.includes(level)) {\n\t\t\tawait safeSend(ctx.api, chatId, `Invalid level. Options: ${valid.join(\", \")}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait bridge.setThinkingLevel(level);\n\t\tawait safeSend(ctx.api, chatId, `💭 Thinking level set to \\`${level}\\``);\n\t} catch (e) {\n\t\tlog(`[CMD] /thinking error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ ${e}`);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent slash commands: /compact, /agents, /stats, /model, /thinking\n */\n\nimport type { Context } from \"grammy\";\nimport type { UserState } from \"../types.js\";\nimport { log, safeSend } from \"../util/telegram.js\";\n\nexport async function cmdCompact(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\tawait safeSend(ctx.api, chatId, \"🗜 _Compacting context..._\");\n\ttry {\n\t\tconst result = await bridge.compact();\n\t\tif (result) {\n\t\t\tconst before = (result as any).tokensBefore || 0;\n\t\t\tawait safeSend(ctx.api, chatId, `✅ Compacted (was ${Math.round(before / 1000)}k tokens)`);\n\t\t} else {\n\t\t\tawait safeSend(ctx.api, chatId, \"✅ Compacted.\");\n\t\t}\n\t} catch (e) {\n\t\tlog(`[CMD] /compact error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ Compaction failed: ${e}`);\n\t}\n}\n\nexport async function cmdAgents(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\n\tif (userState.backgroundAgents.size === 0) {\n\t\tawait safeSend(ctx.api, chatId, \"No background agents running.\");\n\t\treturn;\n\t}\n\n\tconst lines = [\"🤖 *Background Agents*:\\n\"];\n\tfor (const agent of userState.backgroundAgents.values()) {\n\t\tconst elapsed = Math.round((Date.now() - agent.startTime) / 1000);\n\t\tlines.push(`• *${agent.agentType}* (${elapsed}s)\\n ${agent.taskSummary.slice(0, 200)}`);\n\t}\n\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n}\n\nexport async function cmdStats(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst stats = await bridge.getSessionStats();\n\t\tif (!stats) {\n\t\t\tawait safeSend(ctx.api, chatId, \"No stats available.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = [\"📊 *Session Stats*:\\n\"];\n\t\tlines.push(`Messages: ${stats.userMessages || 0} user, ${stats.assistantMessages || 0} assistant`);\n\t\tlines.push(`Tool calls: ${stats.toolCalls || 0}`);\n\n\t\tif (stats.tokens) {\n\t\t\tconst t = stats.tokens;\n\t\t\tlines.push(`\\nTokens: ${Math.round((t.total || 0) / 1000)}k total`);\n\t\t\tlines.push(` Input: ${Math.round((t.input || 0) / 1000)}k`);\n\t\t\tlines.push(` Output: ${Math.round((t.output || 0) / 1000)}k`);\n\t\t\tif (t.cacheRead) lines.push(` Cache read: ${Math.round(t.cacheRead / 1000)}k`);\n\t\t}\n\n\t\tif (stats.cost != null) {\n\t\t\tlines.push(`\\n💰 Cost: $${stats.cost.toFixed(4)}`);\n\t\t}\n\n\t\tif (stats.contextUsage) {\n\t\t\tconst cu = stats.contextUsage;\n\t\t\tif (cu.percent != null) {\n\t\t\t\tlines.push(\n\t\t\t\t\t`\\n📏 Context: ${cu.percent}% (${Math.round((cu.tokens || 0) / 1000)}k / ${Math.round((cu.contextWindow || 0) / 1000)}k)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n\t} catch (e) {\n\t\tlog(`[CMD] /stats error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ Failed to get stats: ${e}`);\n\t}\n}\n\nexport async function cmdModel(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tif (!args.trim()) {\n\t\t\t// Show current model\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.model) {\n\t\t\t\tawait safeSend(ctx.api, chatId, `🧠 Current model: \\`${state.model.provider}/${state.model.id}\\``);\n\t\t\t} else {\n\t\t\t\tawait safeSend(ctx.api, chatId, \"🧠 No model set.\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Resolve pattern using the same logic as CLI/TUI\n\t\tconst pattern = args.trim();\n\t\tconst result = await bridge.resolveModel(pattern);\n\n\t\tif (!result) {\n\t\t\t// No match — list available models grouped by provider\n\t\t\tconst models = await bridge.getAvailableModels();\n\t\t\tconst byProvider = new Map<string, string[]>();\n\t\t\tfor (const m of models as any[]) {\n\t\t\t\tconst list = byProvider.get(m.provider) || [];\n\t\t\t\tlist.push(m.id);\n\t\t\t\tbyProvider.set(m.provider, list);\n\t\t\t}\n\t\t\tconst lines = [`No model matching \"${pattern}\". Available:`];\n\t\t\tfor (const [provider, ids] of byProvider) {\n\t\t\t\tlines.push(`\\n*${provider}*:`);\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tlines.push(` \\`${id}\\``);\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\").slice(0, 4000));\n\t\t\treturn;\n\t\t}\n\n\t\tconst model = result.model as any;\n\t\tawait bridge.setModel(model.provider, model.id);\n\t\tconst warning = result.warning ? ` ⚠️ ${result.warning}` : \"\";\n\t\tawait safeSend(ctx.api, chatId, `🧠 Switched to \\`${model.provider}/${model.id}\\`${warning}`);\n\t} catch (e) {\n\t\tlog(`[CMD] /model error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ ${e}`);\n\t}\n}\n\nexport async function cmdThinking(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tif (!args.trim()) {\n\t\t\tconst state = await bridge.getState();\n\t\t\tawait safeSend(ctx.api, chatId, `💭 Thinking level: \\`${state?.thinkingLevel || \"unknown\"}\\``);\n\t\t\treturn;\n\t\t}\n\n\t\tconst level = args.trim().toLowerCase();\n\t\tconst valid = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\t\tif (!valid.includes(level)) {\n\t\t\tawait safeSend(ctx.api, chatId, `Invalid level. Options: ${valid.join(\", \")}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait bridge.setThinkingLevel(level);\n\t\tawait safeSend(ctx.api, chatId, `💭 Thinking level set to \\`${level}\\``);\n\t} catch (e) {\n\t\tlog(`[CMD] /thinking error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ ${e}`);\n\t}\n}\n"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -37,6 +37,8 @@ export interface UserState {
|
|
|
37
37
|
}>;
|
|
38
38
|
/** Buddy controller — any to avoid import of @dreb/coding-agent/buddy */
|
|
39
39
|
buddyController: any;
|
|
40
|
+
/** Pending model fallback warning to show the user (set once after bridge start) */
|
|
41
|
+
pendingModelFallbackWarning?: string;
|
|
40
42
|
}
|
|
41
43
|
/** Session info for persistence across bot restarts */
|
|
42
44
|
export interface SavedSessions {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,+BAA+B;AAC/B,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,wCAAwC;AACxC,MAAM,WAAW,SAAS;IACzB,wCAAwC;IACxC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3B,qEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;IACxB,oDAAoD;IACpD,cAAc,EAAE,OAAO,CAAC;IACxB,mEAAmE;IACnE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0FAA0F;IAC1F,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,0CAA0C;IAC1C,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC5C,oDAAoD;IACpD,aAAa,EAAE,OAAO,CAAC;IACvB,kFAAgF;IAChF,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClF,2EAAyE;IACzE,eAAe,EAAE,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,+BAA+B;AAC/B,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,wCAAwC;AACxC,MAAM,WAAW,SAAS;IACzB,wCAAwC;IACxC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3B,qEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;IACxB,oDAAoD;IACpD,cAAc,EAAE,OAAO,CAAC;IACxB,mEAAmE;IACnE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0FAA0F;IAC1F,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,0CAA0C;IAC1C,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC5C,oDAAoD;IACpD,aAAa,EAAE,OAAO,CAAC;IACvB,kFAAgF;IAChF,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClF,2EAAyE;IACzE,eAAe,EAAE,GAAG,CAAC;IACrB,oFAAoF;IACpF,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,uDAAuD;AACvD,MAAM,WAAW,aAAa;IAC7B,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACH","sourcesContent":["/**\n * Bot-specific types for the Telegram frontend.\n */\n\nimport type { AgentBridge } from \"./agent-bridge.js\";\nimport type { Config } from \"./config.js\";\n\n/** Tracked background agent */\nexport interface TrackedAgent {\n\tagentId: string;\n\tagentType: string;\n\ttaskSummary: string;\n\tstartTime: number;\n}\n\n/** Per-user state managed by the bot */\nexport interface UserState {\n\t/** Active agent bridge (RPC process) */\n\tbridge: AgentBridge | null;\n\t/** Bot config — needed by buddy controller for RPC hatch/reroll */\n\tconfig: Config;\n\t/** Covers the race window between prompt() call and agent_start event */\n\tpromptInFlight: boolean;\n\t/** Flag to start a fresh session on next message */\n\tnewSessionFlag: boolean;\n\t/** Optional working directory override for the next new session */\n\tnewSessionCwd: string | null;\n\t/** The actual working directory of the current bridge (may differ from config default) */\n\teffectiveCwd: string | null;\n\t/** Currently running background agents */\n\tbackgroundAgents: Map<string, TrackedAgent>;\n\t/** Whether /stop was used (suppress DONE marker) */\n\tstopRequested: boolean;\n\t/** Messages waiting to be delivered to Telegram — drained by a delivery loop */\n\toutbox: Array<{ chatId: number; text: string; long?: boolean; retries?: number }>;\n\t/** Buddy controller — any to avoid import of @dreb/coding-agent/buddy */\n\tbuddyController: any;\n\t/** Pending model fallback warning to show the user (set once after bridge start) */\n\tpendingModelFallbackWarning?: string;\n}\n\n/** Session info for persistence across bot restarts */\nexport interface SavedSessions {\n\t[userId: string]: Array<{\n\t\tsessionPath: string;\n\t\tsessionId: string;\n\t\ttimestamp: number;\n\t\tpreview: string;\n\t}>;\n}\n"]}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * Bot-specific types for the Telegram frontend.\n */\n\nimport type { AgentBridge } from \"./agent-bridge.js\";\nimport type { Config } from \"./config.js\";\n\n/** Tracked background agent */\nexport interface TrackedAgent {\n\tagentId: string;\n\tagentType: string;\n\ttaskSummary: string;\n\tstartTime: number;\n}\n\n/** Per-user state managed by the bot */\nexport interface UserState {\n\t/** Active agent bridge (RPC process) */\n\tbridge: AgentBridge | null;\n\t/** Bot config — needed by buddy controller for RPC hatch/reroll */\n\tconfig: Config;\n\t/** Covers the race window between prompt() call and agent_start event */\n\tpromptInFlight: boolean;\n\t/** Flag to start a fresh session on next message */\n\tnewSessionFlag: boolean;\n\t/** Optional working directory override for the next new session */\n\tnewSessionCwd: string | null;\n\t/** The actual working directory of the current bridge (may differ from config default) */\n\teffectiveCwd: string | null;\n\t/** Currently running background agents */\n\tbackgroundAgents: Map<string, TrackedAgent>;\n\t/** Whether /stop was used (suppress DONE marker) */\n\tstopRequested: boolean;\n\t/** Messages waiting to be delivered to Telegram — drained by a delivery loop */\n\toutbox: Array<{ chatId: number; text: string; long?: boolean; retries?: number }>;\n\t/** Buddy controller — any to avoid import of @dreb/coding-agent/buddy */\n\tbuddyController: any;\n}\n\n/** Session info for persistence across bot restarts */\nexport interface SavedSessions {\n\t[userId: string]: Array<{\n\t\tsessionPath: string;\n\t\tsessionId: string;\n\t\ttimestamp: number;\n\t\tpreview: string;\n\t}>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * Bot-specific types for the Telegram frontend.\n */\n\nimport type { AgentBridge } from \"./agent-bridge.js\";\nimport type { Config } from \"./config.js\";\n\n/** Tracked background agent */\nexport interface TrackedAgent {\n\tagentId: string;\n\tagentType: string;\n\ttaskSummary: string;\n\tstartTime: number;\n}\n\n/** Per-user state managed by the bot */\nexport interface UserState {\n\t/** Active agent bridge (RPC process) */\n\tbridge: AgentBridge | null;\n\t/** Bot config — needed by buddy controller for RPC hatch/reroll */\n\tconfig: Config;\n\t/** Covers the race window between prompt() call and agent_start event */\n\tpromptInFlight: boolean;\n\t/** Flag to start a fresh session on next message */\n\tnewSessionFlag: boolean;\n\t/** Optional working directory override for the next new session */\n\tnewSessionCwd: string | null;\n\t/** The actual working directory of the current bridge (may differ from config default) */\n\teffectiveCwd: string | null;\n\t/** Currently running background agents */\n\tbackgroundAgents: Map<string, TrackedAgent>;\n\t/** Whether /stop was used (suppress DONE marker) */\n\tstopRequested: boolean;\n\t/** Messages waiting to be delivered to Telegram — drained by a delivery loop */\n\toutbox: Array<{ chatId: number; text: string; long?: boolean; retries?: number }>;\n\t/** Buddy controller — any to avoid import of @dreb/coding-agent/buddy */\n\tbuddyController: any;\n\t/** Pending model fallback warning to show the user (set once after bridge start) */\n\tpendingModelFallbackWarning?: string;\n}\n\n/** Session info for persistence across bot restarts */\nexport interface SavedSessions {\n\t[userId: string]: Array<{\n\t\tsessionPath: string;\n\t\tsessionId: string;\n\t\ttimestamp: number;\n\t\tpreview: string;\n\t}>;\n}\n"]}
|