@openacp/cli 0.2.22 → 0.2.24

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.
Files changed (44) hide show
  1. package/dist/autostart-YBYXQA77.js +18 -0
  2. package/dist/autostart-YBYXQA77.js.map +1 -0
  3. package/dist/{setup-XQBEZZQB.js → chunk-4BN7NSKB.js} +140 -9
  4. package/dist/chunk-4BN7NSKB.js.map +1 -0
  5. package/dist/chunk-CQMS5U7Z.js +63 -0
  6. package/dist/chunk-CQMS5U7Z.js.map +1 -0
  7. package/dist/{chunk-XOVJLTEC.js → chunk-FGXG3H3F.js} +14 -58
  8. package/dist/chunk-FGXG3H3F.js.map +1 -0
  9. package/dist/{chunk-HTXK4NLG.js → chunk-IX63F4JG.js} +463 -44
  10. package/dist/chunk-IX63F4JG.js.map +1 -0
  11. package/dist/{chunk-ZATQZUJT.js → chunk-MNJDYDGH.js} +9 -1
  12. package/dist/{chunk-ZATQZUJT.js.map → chunk-MNJDYDGH.js.map} +1 -1
  13. package/dist/chunk-PQRVTUNH.js +145 -0
  14. package/dist/chunk-PQRVTUNH.js.map +1 -0
  15. package/dist/chunk-QWUJIKTX.js +527 -0
  16. package/dist/chunk-QWUJIKTX.js.map +1 -0
  17. package/dist/chunk-S6O7SM6A.js +129 -0
  18. package/dist/chunk-S6O7SM6A.js.map +1 -0
  19. package/dist/chunk-WXS6ONOD.js +103 -0
  20. package/dist/chunk-WXS6ONOD.js.map +1 -0
  21. package/dist/cli.js +354 -4
  22. package/dist/cli.js.map +1 -1
  23. package/dist/config-2XALNLAA.js +14 -0
  24. package/dist/config-2XALNLAA.js.map +1 -0
  25. package/dist/config-editor-56B6YU7B.js +11 -0
  26. package/dist/config-editor-56B6YU7B.js.map +1 -0
  27. package/dist/daemon-3E5OMLT3.js +29 -0
  28. package/dist/daemon-3E5OMLT3.js.map +1 -0
  29. package/dist/index.d.ts +96 -18
  30. package/dist/index.js +35 -6
  31. package/dist/install-cloudflared-57NRTI4E.js +8 -0
  32. package/dist/install-cloudflared-57NRTI4E.js.map +1 -0
  33. package/dist/{main-GC6JY7DS.js → main-YNCSLYVV.js} +43 -11
  34. package/dist/main-YNCSLYVV.js.map +1 -0
  35. package/dist/setup-FTNJACSC.js +27 -0
  36. package/dist/setup-FTNJACSC.js.map +1 -0
  37. package/dist/{tunnel-service-I6NUMBT4.js → tunnel-service-I6WM6USB.js} +10 -10
  38. package/dist/tunnel-service-I6WM6USB.js.map +1 -0
  39. package/package.json +2 -2
  40. package/dist/chunk-HTXK4NLG.js.map +0 -1
  41. package/dist/chunk-XOVJLTEC.js.map +0 -1
  42. package/dist/main-GC6JY7DS.js.map +0 -1
  43. package/dist/setup-XQBEZZQB.js.map +0 -1
  44. package/dist/tunnel-service-I6NUMBT4.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openacp/cli",
3
- "version": "0.2.22",
3
+ "version": "0.2.24",
4
4
  "description": "Self-hosted bridge for AI coding agents via ACP protocol",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "repository": {
40
40
  "type": "git",
41
- "url": "https://github.com/nicepkg/OpenACP"
41
+ "url": "https://github.com/Open-ACP/OpenACP"
42
42
  },
43
43
  "license": "AGPL-3.0",
44
44
  "keywords": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/core/streams.ts","../../src/core/stderr-capture.ts","../../src/core/agent-instance.ts","../../src/core/agent-manager.ts","../../src/core/session.ts","../../src/core/session-manager.ts","../../src/core/notification.ts","../../src/core/core.ts","../../src/core/session-store.ts","../../src/tunnel/extract-file-info.ts","../../src/core/channel.ts","../../src/adapters/telegram/adapter.ts","../../src/adapters/telegram/formatting.ts","../../src/adapters/telegram/streaming.ts","../../src/adapters/telegram/topics.ts","../../src/adapters/telegram/commands.ts","../../src/adapters/telegram/permissions.ts","../../src/adapters/telegram/assistant.ts"],"sourcesContent":["import type { Writable, Readable } from 'node:stream'\n\nexport function nodeToWebWritable(nodeStream: Writable): WritableStream<Uint8Array> {\n return new WritableStream<Uint8Array>({\n write(chunk) {\n return new Promise<void>((resolve, reject) => {\n nodeStream.write(Buffer.from(chunk), (err) => {\n if (err) reject(err)\n else resolve()\n })\n })\n },\n })\n}\n\nexport function nodeToWebReadable(nodeStream: Readable): ReadableStream<Uint8Array> {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n nodeStream.on('data', (chunk: Buffer) => {\n controller.enqueue(new Uint8Array(chunk))\n })\n nodeStream.on('end', () => controller.close())\n nodeStream.on('error', (err) => controller.error(err))\n },\n })\n}\n","export class StderrCapture {\n private lines: string[] = []\n\n constructor(private maxLines: number = 50) {}\n\n append(chunk: string): void {\n this.lines.push(...chunk.split('\\n').filter(Boolean))\n if (this.lines.length > this.maxLines) {\n this.lines = this.lines.slice(-this.maxLines)\n }\n }\n\n getLastLines(): string {\n return this.lines.join('\\n')\n }\n}\n","import { spawn, execSync, type ChildProcess } from \"node:child_process\";\nimport { Transform } from \"node:stream\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { ClientSideConnection, ndJsonStream } from \"@agentclientprotocol/sdk\";\nimport type {\n Agent,\n Client,\n PromptResponse,\n PermissionOption as SdkPermissionOption,\n} from \"@agentclientprotocol/sdk\";\nimport { nodeToWebWritable, nodeToWebReadable } from \"./streams.js\";\nimport { StderrCapture } from \"./stderr-capture.js\";\nimport type {\n AgentDefinition,\n AgentEvent,\n PermissionRequest,\n} from \"./types.js\";\nimport { createChildLogger } from \"./log.js\";\nconst log = createChildLogger({ module: \"agent-instance\" });\n\n/** Resolve an agent command to a directly executable form (avoids shell wrappers) */\nfunction resolveAgentCommand(cmd: string): { command: string; args: string[] } {\n // 1. Check local node_modules for the package's actual JS entry point\n const packageDirs = [\n path.resolve(\n process.cwd(),\n \"node_modules\",\n \"@zed-industries\",\n cmd,\n \"dist\",\n \"index.js\",\n ),\n path.resolve(process.cwd(), \"node_modules\", cmd, \"dist\", \"index.js\"),\n ];\n for (const jsPath of packageDirs) {\n if (fs.existsSync(jsPath)) {\n return { command: process.execPath, args: [jsPath] };\n }\n }\n\n // 2. Check local .bin — if it's a JS file with shebang, run with node directly\n const localBin = path.resolve(process.cwd(), \"node_modules\", \".bin\", cmd);\n if (fs.existsSync(localBin)) {\n const content = fs.readFileSync(localBin, \"utf-8\");\n if (content.startsWith(\"#!/usr/bin/env node\")) {\n return { command: process.execPath, args: [localBin] };\n }\n // Shell wrapper — try to find the target JS file\n const match = content.match(/\"([^\"]+\\.js)\"/);\n if (match) {\n const target = path.resolve(path.dirname(localBin), match[1]);\n if (fs.existsSync(target)) {\n return { command: process.execPath, args: [target] };\n }\n }\n }\n\n // 3. Try resolving from PATH using which\n try {\n const fullPath = execSync(`which ${cmd}`, { encoding: \"utf-8\" }).trim();\n if (fullPath) {\n const content = fs.readFileSync(fullPath, \"utf-8\");\n if (content.startsWith(\"#!/usr/bin/env node\")) {\n return { command: process.execPath, args: [fullPath] };\n }\n }\n } catch {\n // which failed\n }\n\n // 4. Fallback: use command as-is\n return { command: cmd, args: [] };\n}\n\ninterface TerminalState {\n process: ChildProcess;\n output: string;\n exitStatus: { exitCode: number | null; signal: string | null } | null;\n}\n\nexport class AgentInstance {\n private connection!: ClientSideConnection;\n private child!: ChildProcess;\n private stderrCapture!: StderrCapture;\n private terminals: Map<string, TerminalState> = new Map();\n\n sessionId!: string;\n agentName: string;\n\n // Callbacks — set by core when wiring events\n onSessionUpdate: (event: AgentEvent) => void = () => {};\n onPermissionRequest: (request: PermissionRequest) => Promise<string> =\n async () => \"\";\n\n private constructor(agentName: string) {\n this.agentName = agentName;\n }\n\n private static async spawnSubprocess(\n agentDef: AgentDefinition,\n workingDirectory: string,\n ): Promise<AgentInstance> {\n const instance = new AgentInstance(agentDef.name);\n const resolved = resolveAgentCommand(agentDef.command);\n log.debug(\n {\n agentName: agentDef.name,\n command: resolved.command,\n args: resolved.args,\n },\n \"Resolved agent command\",\n );\n\n instance.child = spawn(\n resolved.command,\n [...resolved.args, ...agentDef.args],\n {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n cwd: workingDirectory,\n env: { ...process.env, ...agentDef.env },\n },\n );\n\n await new Promise<void>((resolve, reject) => {\n instance.child.on(\"error\", (err) => {\n reject(\n new Error(\n `Failed to spawn agent \"${agentDef.name}\": ${err.message}. Is \"${agentDef.command}\" installed?`,\n ),\n );\n });\n instance.child.on(\"spawn\", () => resolve());\n });\n\n instance.stderrCapture = new StderrCapture(50);\n instance.child.stderr!.on(\"data\", (chunk: Buffer) => {\n instance.stderrCapture.append(chunk.toString());\n });\n\n const stdinLogger = new Transform({\n transform(chunk, _enc, cb) {\n log.debug(\n { direction: \"send\", raw: chunk.toString().trimEnd() },\n \"ACP raw\",\n );\n cb(null, chunk);\n },\n });\n stdinLogger.pipe(instance.child.stdin!);\n\n const stdoutLogger = new Transform({\n transform(chunk, _enc, cb) {\n log.debug(\n { direction: \"recv\", raw: chunk.toString().trimEnd() },\n \"ACP raw\",\n );\n cb(null, chunk);\n },\n });\n instance.child.stdout!.pipe(stdoutLogger);\n\n const toAgent = nodeToWebWritable(stdinLogger);\n const fromAgent = nodeToWebReadable(stdoutLogger);\n const stream = ndJsonStream(toAgent, fromAgent);\n\n instance.connection = new ClientSideConnection(\n (_agent: Agent): Client => instance.createClient(_agent),\n stream,\n );\n\n await instance.connection.initialize({\n protocolVersion: 1,\n clientCapabilities: {\n fs: { readTextFile: true, writeTextFile: true },\n terminal: true,\n },\n });\n\n return instance;\n }\n\n private setupCrashDetection(): void {\n this.child.on(\"exit\", (code, signal) => {\n log.info(\n { sessionId: this.sessionId, exitCode: code, signal },\n \"Agent process exited\",\n );\n if (code !== 0 && code !== null) {\n const stderr = this.stderrCapture.getLastLines();\n this.onSessionUpdate({\n type: \"error\",\n message: `Agent crashed (exit code ${code})\\n${stderr}`,\n });\n }\n });\n\n this.connection.closed.then(() => {\n log.debug({ sessionId: this.sessionId }, \"ACP connection closed\");\n });\n }\n\n static async spawn(\n agentDef: AgentDefinition,\n workingDirectory: string,\n ): Promise<AgentInstance> {\n log.debug(\n { agentName: agentDef.name, command: agentDef.command },\n \"Spawning agent\",\n );\n const spawnStart = Date.now();\n\n const instance = await AgentInstance.spawnSubprocess(\n agentDef,\n workingDirectory,\n );\n\n const response = await instance.connection.newSession({\n cwd: workingDirectory,\n mcpServers: [],\n });\n instance.sessionId = response.sessionId;\n instance.setupCrashDetection();\n\n log.info(\n { sessionId: response.sessionId, durationMs: Date.now() - spawnStart },\n \"Agent spawn complete\",\n );\n return instance;\n }\n\n static async resume(\n agentDef: AgentDefinition,\n workingDirectory: string,\n agentSessionId: string,\n ): Promise<AgentInstance> {\n log.debug({ agentName: agentDef.name, agentSessionId }, \"Resuming agent\");\n const spawnStart = Date.now();\n\n const instance = await AgentInstance.spawnSubprocess(\n agentDef,\n workingDirectory,\n );\n\n try {\n const response = await instance.connection.unstable_resumeSession({\n sessionId: agentSessionId,\n cwd: workingDirectory,\n });\n instance.sessionId = response.sessionId;\n log.info(\n { sessionId: response.sessionId, durationMs: Date.now() - spawnStart },\n \"Agent resume complete\",\n );\n } catch (err) {\n log.warn(\n { err, agentSessionId },\n \"Resume failed, falling back to new session\",\n );\n const response = await instance.connection.newSession({\n cwd: workingDirectory,\n mcpServers: [],\n });\n instance.sessionId = response.sessionId;\n log.info(\n { sessionId: response.sessionId, durationMs: Date.now() - spawnStart },\n \"Agent fallback spawn complete\",\n );\n }\n\n instance.setupCrashDetection();\n return instance;\n }\n\n // createClient — implemented in Task 6b\n private createClient(_agent: Agent): Client {\n const self = this;\n const MAX_OUTPUT_BYTES = 1024 * 1024; // 1MB cap\n\n return {\n // ── Session updates ──────────────────────────────────────────────────\n async sessionUpdate(params) {\n const update = params.update;\n let event: AgentEvent | null = null;\n\n switch (update.sessionUpdate) {\n case \"agent_message_chunk\":\n if (update.content.type === \"text\") {\n event = { type: \"text\", content: update.content.text };\n }\n break;\n case \"agent_thought_chunk\":\n if (update.content.type === \"text\") {\n event = { type: \"thought\", content: update.content.text };\n }\n break;\n case \"tool_call\":\n event = {\n type: \"tool_call\",\n id: update.toolCallId,\n name: update.title,\n kind: update.kind ?? undefined,\n status: update.status ?? \"pending\",\n content: update.content ?? undefined,\n };\n break;\n case \"tool_call_update\":\n event = {\n type: \"tool_update\",\n id: update.toolCallId,\n status: update.status ?? \"pending\",\n content: update.content ?? undefined,\n };\n break;\n case \"plan\":\n event = { type: \"plan\", entries: update.entries };\n break;\n case \"usage_update\":\n event = {\n type: \"usage\",\n tokensUsed: update.used,\n contextSize: update.size,\n cost: update.cost ?? undefined,\n };\n break;\n case \"available_commands_update\":\n event = {\n type: \"commands_update\",\n commands: update.availableCommands,\n };\n break;\n default:\n // Unknown update type — ignore\n return;\n }\n\n if (event !== null) {\n self.onSessionUpdate(event);\n }\n },\n\n // ── Permission requests ──────────────────────────────────────────────\n async requestPermission(params) {\n const permissionRequest: PermissionRequest = {\n id: params.toolCall.toolCallId,\n description: params.toolCall.title ?? params.toolCall.toolCallId,\n options: params.options.map((opt: SdkPermissionOption) => ({\n id: opt.optionId,\n label: opt.name,\n isAllow: opt.kind === \"allow_once\" || opt.kind === \"allow_always\",\n })),\n };\n\n const selectedOptionId =\n await self.onPermissionRequest(permissionRequest);\n return {\n outcome: { outcome: \"selected\" as const, optionId: selectedOptionId },\n };\n },\n\n // ── File operations ──────────────────────────────────────────────────\n async readTextFile(params) {\n const content = await fs.promises.readFile(params.path, \"utf-8\");\n return { content };\n },\n\n async writeTextFile(params) {\n await fs.promises.mkdir(path.dirname(params.path), { recursive: true });\n await fs.promises.writeFile(params.path, params.content, \"utf-8\");\n return {};\n },\n\n // ── Terminal operations ──────────────────────────────────────────────\n async createTerminal(params) {\n const terminalId = randomUUID();\n const args = params.args ?? [];\n const env: Record<string, string> = {};\n for (const ev of params.env ?? []) {\n env[ev.name] = ev.value;\n }\n\n const childProcess = spawn(params.command, args, {\n cwd: params.cwd ?? undefined,\n env: { ...process.env, ...env },\n shell: false,\n });\n\n const state: TerminalState = {\n process: childProcess,\n output: \"\",\n exitStatus: null,\n };\n self.terminals.set(terminalId, state);\n\n const outputByteLimit = params.outputByteLimit ?? MAX_OUTPUT_BYTES;\n\n const appendOutput = (chunk: string) => {\n state.output += chunk;\n // Truncate from the beginning if over limit\n const bytes = Buffer.byteLength(state.output, \"utf-8\");\n if (bytes > outputByteLimit) {\n // Find truncation point at character boundary\n const excess = bytes - outputByteLimit;\n state.output = state.output.slice(excess);\n }\n };\n\n childProcess.stdout?.on(\"data\", (chunk: Buffer) =>\n appendOutput(chunk.toString()),\n );\n childProcess.stderr?.on(\"data\", (chunk: Buffer) =>\n appendOutput(chunk.toString()),\n );\n\n childProcess.on(\"exit\", (code, signal) => {\n state.exitStatus = { exitCode: code, signal };\n });\n\n return { terminalId };\n },\n\n async terminalOutput(params) {\n const state = self.terminals.get(params.terminalId);\n if (!state) {\n throw new Error(`Terminal not found: ${params.terminalId}`);\n }\n return {\n output: state.output,\n truncated: false,\n exitStatus: state.exitStatus\n ? {\n exitCode: state.exitStatus.exitCode,\n signal: state.exitStatus.signal,\n }\n : undefined,\n };\n },\n\n async waitForTerminalExit(params) {\n const state = self.terminals.get(params.terminalId);\n if (!state) {\n throw new Error(`Terminal not found: ${params.terminalId}`);\n }\n if (state.exitStatus !== null) {\n return {\n exitCode: state.exitStatus.exitCode,\n signal: state.exitStatus.signal,\n };\n }\n return new Promise((resolve) => {\n state.process.on(\"exit\", (code, signal) => {\n resolve({ exitCode: code, signal });\n });\n });\n },\n\n async killTerminal(params) {\n const state = self.terminals.get(params.terminalId);\n if (!state) {\n throw new Error(`Terminal not found: ${params.terminalId}`);\n }\n state.process.kill(\"SIGTERM\");\n return {};\n },\n\n async releaseTerminal(params) {\n const state = self.terminals.get(params.terminalId);\n if (!state) {\n return;\n }\n state.process.kill(\"SIGKILL\");\n self.terminals.delete(params.terminalId);\n },\n };\n }\n\n async prompt(text: string): Promise<PromptResponse> {\n return this.connection.prompt({\n sessionId: this.sessionId,\n prompt: [{ type: \"text\", text }],\n });\n }\n\n async cancel(): Promise<void> {\n await this.connection.cancel({ sessionId: this.sessionId });\n }\n\n async destroy(): Promise<void> {\n // Cleanup terminals\n for (const [, t] of this.terminals) {\n t.process.kill(\"SIGKILL\");\n }\n this.terminals.clear();\n\n // Kill agent subprocess\n this.child.kill(\"SIGTERM\");\n setTimeout(() => {\n if (!this.child.killed) this.child.kill(\"SIGKILL\");\n }, 10_000);\n }\n}\n","import type { Config } from \"./config.js\";\nimport type { AgentDefinition } from \"./types.js\";\nimport { AgentInstance } from \"./agent-instance.js\";\n\nexport class AgentManager {\n constructor(private config: Config) {}\n\n getAvailableAgents(): AgentDefinition[] {\n return Object.entries(this.config.agents).map(([name, cfg]) => ({\n name,\n command: cfg.command,\n args: cfg.args,\n workingDirectory: cfg.workingDirectory,\n env: cfg.env,\n }));\n }\n\n getAgent(name: string): AgentDefinition | undefined {\n const cfg = this.config.agents[name];\n if (!cfg) return undefined;\n return { name, ...cfg };\n }\n\n async spawn(\n agentName: string,\n workingDirectory: string,\n ): Promise<AgentInstance> {\n const agentDef = this.getAgent(agentName);\n if (!agentDef) throw new Error(`Agent \"${agentName}\" not found in config`);\n return AgentInstance.spawn(agentDef, workingDirectory);\n }\n\n async resume(\n agentName: string,\n workingDirectory: string,\n agentSessionId: string,\n ): Promise<AgentInstance> {\n const agentDef = this.getAgent(agentName);\n if (!agentDef) throw new Error(`Agent \"${agentName}\" not found in config`);\n return AgentInstance.resume(agentDef, workingDirectory, agentSessionId);\n }\n}\n","import { nanoid } from \"nanoid\";\nimport type { AgentInstance } from \"./agent-instance.js\";\nimport type { ChannelAdapter } from \"./channel.js\";\nimport type { SessionStatus } from \"./types.js\";\nimport { createChildLogger, createSessionLogger, type Logger } from \"./log.js\";\nconst moduleLog = createChildLogger({ module: \"session\" });\n\nexport class Session {\n id: string;\n channelId: string;\n threadId: string = \"\";\n agentName: string;\n workingDirectory: string;\n agentInstance: AgentInstance;\n agentSessionId: string = \"\";\n status: SessionStatus = \"initializing\";\n name?: string;\n promptQueue: string[] = [];\n promptRunning: boolean = false;\n createdAt: Date = new Date();\n adapter?: ChannelAdapter; // Set by wireSessionEvents for renaming\n pendingPermission?: {\n requestId: string;\n resolve: (optionId: string) => void;\n };\n log: Logger;\n\n constructor(opts: {\n id?: string;\n channelId: string;\n agentName: string;\n workingDirectory: string;\n agentInstance: AgentInstance;\n }) {\n this.id = opts.id || nanoid(12);\n this.channelId = opts.channelId;\n this.agentName = opts.agentName;\n this.workingDirectory = opts.workingDirectory;\n this.agentInstance = opts.agentInstance;\n this.log = createSessionLogger(this.id, moduleLog);\n this.log.info({ agentName: this.agentName }, \"Session created\");\n }\n\n async enqueuePrompt(text: string): Promise<void> {\n if (this.promptRunning) {\n this.promptQueue.push(text);\n this.log.debug({ queueDepth: this.promptQueue.length }, \"Prompt queued\");\n return;\n }\n await this.runPrompt(text);\n }\n\n private async runPrompt(text: string): Promise<void> {\n this.promptRunning = true;\n this.status = \"active\";\n const promptStart = Date.now();\n this.log.debug(\"Prompt execution started\");\n\n try {\n await this.agentInstance.prompt(text);\n this.log.info(\n { durationMs: Date.now() - promptStart },\n \"Prompt execution completed\",\n );\n\n // Auto-name after first user prompt\n if (!this.name) {\n await this.autoName();\n }\n } catch (err) {\n this.status = \"error\";\n this.log.error({ err }, \"Prompt execution failed\");\n } finally {\n this.promptRunning = false;\n\n // Process next queued prompt\n if (this.promptQueue.length > 0) {\n const next = this.promptQueue.shift()!;\n await this.runPrompt(next);\n }\n }\n }\n\n // NOTE: This injects a summary prompt into the agent's conversation history.\n // Known Phase 1 limitation — the agent sees this prompt in its context.\n private async autoName(): Promise<void> {\n let title = \"\";\n const prevHandler = this.agentInstance.onSessionUpdate;\n this.agentInstance.onSessionUpdate = (event) => {\n if (event.type === \"text\") title += event.content;\n };\n\n try {\n await this.agentInstance.prompt(\n \"Summarize this conversation in max 5 words for a topic title. Reply ONLY with the title, nothing else.\",\n );\n this.name = title.trim().slice(0, 50);\n this.log.info({ name: this.name }, \"Session auto-named\");\n\n // Rename the topic on the channel\n if (this.adapter && this.name) {\n await this.adapter.renameSessionThread(this.id, this.name);\n }\n } catch {\n this.name = `Session ${this.id.slice(0, 6)}`;\n } finally {\n this.agentInstance.onSessionUpdate = prevHandler;\n }\n }\n\n /** Fire-and-forget warm-up: primes model cache while user types their first message */\n async warmup(): Promise<void> {\n this.promptRunning = true;\n const prevHandler = this.agentInstance.onSessionUpdate;\n this.agentInstance.onSessionUpdate = () => {}; // suppress warm-up output\n\n try {\n const start = Date.now();\n await this.agentInstance.prompt('Reply with only \"ready\".');\n this.log.info({ durationMs: Date.now() - start }, \"Warm-up complete\");\n } catch (err) {\n this.log.error({ err }, \"Warm-up failed\");\n } finally {\n this.agentInstance.onSessionUpdate = prevHandler;\n this.promptRunning = false;\n\n // Drain any prompts queued while warming up\n if (this.promptQueue.length > 0) {\n const next = this.promptQueue.shift()!;\n await this.runPrompt(next);\n }\n }\n }\n\n async cancel(): Promise<void> {\n this.status = \"cancelled\";\n this.log.info(\"Session cancelled\");\n await this.agentInstance.cancel();\n }\n\n async destroy(): Promise<void> {\n this.log.info(\"Session destroyed\");\n await this.agentInstance.destroy();\n }\n}\n","import type { AgentManager } from \"./agent-manager.js\";\nimport { Session } from \"./session.js\";\nimport type { SessionStore } from \"./session-store.js\";\nimport type { SessionStatus } from \"./types.js\";\n\nexport class SessionManager {\n private sessions: Map<string, Session> = new Map();\n private store: SessionStore | null;\n\n constructor(store: SessionStore | null = null) {\n this.store = store;\n }\n\n async createSession(\n channelId: string,\n agentName: string,\n workingDirectory: string,\n agentManager: AgentManager,\n ): Promise<Session> {\n const agentInstance = await agentManager.spawn(agentName, workingDirectory);\n const session = new Session({\n channelId,\n agentName,\n workingDirectory,\n agentInstance,\n });\n this.sessions.set(session.id, session);\n session.agentSessionId = session.agentInstance.sessionId;\n\n if (this.store) {\n await this.store.save({\n sessionId: session.id,\n agentSessionId: session.agentInstance.sessionId,\n agentName: session.agentName,\n workingDir: session.workingDirectory,\n channelId,\n status: session.status,\n createdAt: session.createdAt.toISOString(),\n lastActiveAt: new Date().toISOString(),\n name: session.name,\n platform: {},\n });\n }\n\n return session;\n }\n\n getSession(sessionId: string): Session | undefined {\n return this.sessions.get(sessionId);\n }\n\n getSessionByThread(channelId: string, threadId: string): Session | undefined {\n for (const session of this.sessions.values()) {\n if (session.channelId === channelId && session.threadId === threadId) {\n return session;\n }\n }\n return undefined;\n }\n\n registerSession(session: Session): void {\n this.sessions.set(session.id, session);\n }\n\n async updateSessionPlatform(\n sessionId: string,\n platform: Record<string, unknown>,\n ): Promise<void> {\n if (!this.store) return;\n const record = this.store.get(sessionId);\n if (record) {\n await this.store.save({ ...record, platform });\n }\n }\n\n async updateSessionActivity(sessionId: string): Promise<void> {\n if (!this.store) return;\n const record = this.store.get(sessionId);\n if (record) {\n await this.store.save({\n ...record,\n lastActiveAt: new Date().toISOString(),\n });\n }\n }\n\n async updateSessionStatus(\n sessionId: string,\n status: SessionStatus,\n ): Promise<void> {\n if (!this.store) return;\n const record = this.store.get(sessionId);\n if (record) {\n await this.store.save({ ...record, status });\n }\n }\n\n async cancelSession(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (session) {\n await session.cancel();\n if (this.store) {\n const record = this.store.get(sessionId);\n if (record) {\n await this.store.save({ ...record, status: \"cancelled\" });\n }\n }\n }\n }\n\n listSessions(channelId?: string): Session[] {\n const all = Array.from(this.sessions.values());\n if (channelId) return all.filter((s) => s.channelId === channelId);\n return all;\n }\n\n async destroyAll(): Promise<void> {\n if (this.store) {\n for (const session of this.sessions.values()) {\n const record = this.store.get(session.id);\n if (record) {\n await this.store.save({ ...record, status: \"finished\" });\n }\n }\n }\n for (const session of this.sessions.values()) {\n await session.destroy();\n }\n this.sessions.clear();\n }\n}\n","import type { ChannelAdapter } from './channel.js'\nimport type { NotificationMessage } from './types.js'\n\nexport class NotificationManager {\n constructor(private adapters: Map<string, ChannelAdapter>) {}\n\n async notify(channelId: string, notification: NotificationMessage): Promise<void> {\n const adapter = this.adapters.get(channelId)\n if (adapter) {\n await adapter.sendNotification(notification)\n }\n }\n\n async notifyAll(notification: NotificationMessage): Promise<void> {\n for (const adapter of this.adapters.values()) {\n await adapter.sendNotification(notification)\n }\n }\n}\n","import path from \"node:path\";\nimport os from \"node:os\";\nimport { ConfigManager } from \"./config.js\";\nimport { AgentManager } from \"./agent-manager.js\";\nimport { SessionManager } from \"./session-manager.js\";\nimport { NotificationManager } from \"./notification.js\";\nimport { ChannelAdapter } from \"./channel.js\";\nimport { Session } from \"./session.js\";\nimport { JsonFileSessionStore, type SessionStore } from \"./session-store.js\";\nimport type {\n IncomingMessage,\n AgentEvent,\n OutgoingMessage,\n PermissionRequest,\n} from \"./types.js\";\nimport type { TunnelService } from \"../tunnel/tunnel-service.js\";\nimport { extractFileInfo } from \"../tunnel/extract-file-info.js\";\nimport { createChildLogger } from \"./log.js\";\nconst log = createChildLogger({ module: \"core\" });\n\nexport class OpenACPCore {\n configManager: ConfigManager;\n agentManager: AgentManager;\n sessionManager: SessionManager;\n notificationManager: NotificationManager;\n adapters: Map<string, ChannelAdapter> = new Map();\n tunnelService?: TunnelService;\n private sessionStore: SessionStore | null = null;\n private resumeLocks: Map<string, Promise<Session | null>> = new Map();\n\n constructor(configManager: ConfigManager) {\n this.configManager = configManager;\n const config = configManager.get();\n this.agentManager = new AgentManager(config);\n const storePath = path.join(os.homedir(), \".openacp\", \"sessions.json\");\n this.sessionStore = new JsonFileSessionStore(\n storePath,\n config.sessionStore.ttlDays,\n );\n this.sessionManager = new SessionManager(this.sessionStore);\n this.notificationManager = new NotificationManager(this.adapters);\n }\n\n registerAdapter(name: string, adapter: ChannelAdapter): void {\n this.adapters.set(name, adapter);\n }\n\n async start(): Promise<void> {\n for (const adapter of this.adapters.values()) {\n await adapter.start();\n }\n }\n\n async stop(): Promise<void> {\n // 1. Notify users\n try {\n await this.notificationManager.notifyAll({\n sessionId: \"system\",\n type: \"error\",\n summary: \"OpenACP is shutting down\",\n });\n } catch {\n /* best effort */\n }\n\n // 2. Destroy all sessions\n await this.sessionManager.destroyAll();\n\n // 3. Stop adapters\n for (const adapter of this.adapters.values()) {\n await adapter.stop();\n }\n }\n\n // --- Message Routing ---\n\n async handleMessage(message: IncomingMessage): Promise<void> {\n const config = this.configManager.get();\n log.debug(\n {\n channelId: message.channelId,\n threadId: message.threadId,\n userId: message.userId,\n },\n \"Incoming message\",\n );\n\n // Security: check allowed user IDs\n if (config.security.allowedUserIds.length > 0) {\n if (!config.security.allowedUserIds.includes(message.userId)) {\n log.warn(\n { userId: message.userId },\n \"Rejected message from unauthorized user\",\n );\n return;\n }\n }\n\n // Check concurrent session limit\n const activeSessions = this.sessionManager\n .listSessions()\n .filter((s) => s.status === \"active\" || s.status === \"initializing\");\n if (activeSessions.length >= config.security.maxConcurrentSessions) {\n log.warn(\n {\n userId: message.userId,\n currentCount: activeSessions.length,\n max: config.security.maxConcurrentSessions,\n },\n \"Session limit reached\",\n );\n const adapter = this.adapters.get(message.channelId);\n if (adapter) {\n await adapter.sendMessage(\"system\", {\n type: \"error\",\n text: `Max concurrent sessions (${config.security.maxConcurrentSessions}) reached. Cancel a session first.`,\n });\n }\n return;\n }\n\n // Find session by thread\n let session = this.sessionManager.getSessionByThread(\n message.channelId,\n message.threadId,\n );\n\n // Lazy resume: try to restore session from store\n if (!session) {\n session = (await this.lazyResume(message)) ?? undefined;\n }\n\n if (!session) return;\n\n // Update activity timestamp\n this.sessionManager.updateSessionActivity(session.id);\n\n // Forward to session\n await session.enqueuePrompt(message.text);\n }\n\n async handleNewSession(\n channelId: string,\n agentName?: string,\n workspacePath?: string,\n ): Promise<Session> {\n const config = this.configManager.get();\n const resolvedAgent = agentName || config.defaultAgent;\n log.info({ channelId, agentName: resolvedAgent }, \"New session request\");\n const resolvedWorkspace = this.configManager.resolveWorkspace(\n workspacePath || config.agents[resolvedAgent]?.workingDirectory,\n );\n\n const session = await this.sessionManager.createSession(\n channelId,\n resolvedAgent,\n resolvedWorkspace,\n this.agentManager,\n );\n\n // Wire events\n const adapter = this.adapters.get(channelId);\n if (adapter) {\n this.wireSessionEvents(session, adapter);\n }\n\n return session;\n }\n\n async handleNewChat(\n channelId: string,\n currentThreadId: string,\n ): Promise<Session | null> {\n const currentSession = this.sessionManager.getSessionByThread(\n channelId,\n currentThreadId,\n );\n if (!currentSession) return null;\n\n return this.handleNewSession(\n channelId,\n currentSession.agentName,\n currentSession.workingDirectory,\n );\n }\n\n // --- Lazy Resume ---\n\n private async lazyResume(message: IncomingMessage): Promise<Session | null> {\n const store = this.sessionStore;\n if (!store) return null;\n\n const lockKey = `${message.channelId}:${message.threadId}`;\n\n // Check for existing resume in progress\n const existing = this.resumeLocks.get(lockKey);\n if (existing) return existing;\n\n const record = store.findByPlatform(\n message.channelId,\n (p) => String(p.topicId) === message.threadId,\n );\n if (!record) return null;\n\n // Don't resume cancelled/error sessions\n if (record.status === \"cancelled\" || record.status === \"error\") return null;\n\n const resumePromise = (async (): Promise<Session | null> => {\n try {\n const agentInstance = await this.agentManager.resume(\n record.agentName,\n record.workingDir,\n record.agentSessionId,\n );\n\n const session = new Session({\n id: record.sessionId,\n channelId: record.channelId,\n agentName: record.agentName,\n workingDirectory: record.workingDir,\n agentInstance,\n });\n session.threadId = message.threadId;\n session.agentSessionId = agentInstance.sessionId;\n session.status = \"active\";\n session.name = record.name;\n\n this.sessionManager.registerSession(session);\n\n const adapter = this.adapters.get(message.channelId);\n if (adapter) {\n this.wireSessionEvents(session, adapter);\n }\n\n // Update store with new agentSessionId (may differ after resume)\n await store.save({\n ...record,\n agentSessionId: agentInstance.sessionId,\n status: \"active\",\n lastActiveAt: new Date().toISOString(),\n });\n\n log.info(\n { sessionId: session.id, threadId: message.threadId },\n \"Lazy resume successful\",\n );\n return session;\n } catch (err) {\n log.error({ err, record }, \"Lazy resume failed\");\n return null;\n } finally {\n this.resumeLocks.delete(lockKey);\n }\n })();\n\n this.resumeLocks.set(lockKey, resumePromise);\n return resumePromise;\n }\n\n // --- Event Wiring ---\n\n private toOutgoingMessage(\n event: AgentEvent,\n session?: Session,\n ): OutgoingMessage {\n switch (event.type) {\n case \"text\":\n return { type: \"text\", text: event.content };\n case \"thought\":\n return { type: \"thought\", text: event.content };\n case \"tool_call\": {\n const metadata: Record<string, unknown> = {\n id: event.id,\n kind: event.kind,\n status: event.status,\n content: event.content,\n locations: event.locations,\n };\n this.enrichWithViewerLinks(event, metadata, session);\n return { type: \"tool_call\", text: event.name, metadata };\n }\n case \"tool_update\": {\n const metadata: Record<string, unknown> = {\n id: event.id,\n status: event.status,\n content: event.content,\n };\n this.enrichWithViewerLinks(event, metadata, session);\n return { type: \"tool_update\", text: \"\", metadata };\n }\n case \"plan\":\n return { type: \"plan\", text: \"\", metadata: { entries: event.entries } };\n case \"usage\":\n return {\n type: \"usage\",\n text: \"\",\n metadata: {\n tokensUsed: event.tokensUsed,\n contextSize: event.contextSize,\n cost: event.cost,\n },\n };\n default:\n return { type: \"text\", text: \"\" };\n }\n }\n\n private enrichWithViewerLinks(\n event: AgentEvent & { type: \"tool_call\" | \"tool_update\" },\n metadata: Record<string, unknown>,\n session?: Session,\n ): void {\n if (!this.tunnelService || !session) return;\n\n const name = \"name\" in event ? event.name || \"\" : \"\";\n const kind = \"kind\" in event ? event.kind : undefined;\n\n log.debug(\n { name, kind, status: event.status, hasContent: !!event.content },\n \"enrichWithViewerLinks: inspecting event\",\n );\n\n const fileInfo = extractFileInfo(name, kind, event.content);\n if (!fileInfo) return;\n\n log.info(\n {\n name,\n kind,\n filePath: fileInfo.filePath,\n hasOldContent: !!fileInfo.oldContent,\n },\n \"enrichWithViewerLinks: extracted file info\",\n );\n\n const store = this.tunnelService.getStore();\n const viewerLinks: Record<string, string> = {};\n\n // For edits/writes with diff data (oldText + newText)\n if (fileInfo.oldContent) {\n const id = store.storeDiff(\n session.id,\n fileInfo.filePath,\n fileInfo.oldContent,\n fileInfo.content,\n session.workingDirectory,\n );\n if (id) viewerLinks.diff = this.tunnelService.diffUrl(id);\n }\n\n // Always store as file view (new file creation or read)\n const id = store.storeFile(\n session.id,\n fileInfo.filePath,\n fileInfo.content,\n session.workingDirectory,\n );\n if (id) viewerLinks.file = this.tunnelService.fileUrl(id);\n\n if (Object.keys(viewerLinks).length > 0) {\n metadata.viewerLinks = viewerLinks;\n }\n }\n\n // Public — adapters call this for assistant session wiring\n wireSessionEvents(session: Session, adapter: ChannelAdapter): void {\n // Set adapter reference for autoName → renameSessionThread\n session.adapter = adapter;\n\n session.agentInstance.onSessionUpdate = (event: AgentEvent) => {\n switch (event.type) {\n case \"text\":\n case \"thought\":\n case \"tool_call\":\n case \"tool_update\":\n case \"plan\":\n case \"usage\":\n adapter.sendMessage(\n session.id,\n this.toOutgoingMessage(event, session),\n );\n break;\n\n case \"session_end\":\n session.status = \"finished\";\n this.sessionManager.updateSessionStatus(session.id, \"finished\");\n adapter.cleanupSkillCommands(session.id);\n adapter.sendMessage(session.id, {\n type: \"session_end\",\n text: `Done (${event.reason})`,\n });\n this.notificationManager.notify(session.channelId, {\n sessionId: session.id,\n sessionName: session.name,\n type: \"completed\",\n summary: `Session \"${session.name || session.id}\" completed`,\n });\n break;\n\n case \"error\":\n this.sessionManager.updateSessionStatus(session.id, \"error\");\n adapter.cleanupSkillCommands(session.id);\n adapter.sendMessage(session.id, {\n type: \"error\",\n text: event.message,\n });\n this.notificationManager.notify(session.channelId, {\n sessionId: session.id,\n sessionName: session.name,\n type: \"error\",\n summary: event.message,\n });\n break;\n\n case \"commands_update\":\n log.debug({ commands: event.commands }, \"Commands available\");\n adapter.sendSkillCommands(session.id, event.commands);\n break;\n }\n };\n\n session.agentInstance.onPermissionRequest = async (\n request: PermissionRequest,\n ) => {\n // Set pending BEFORE sending UI to avoid race condition\n const promise = new Promise<string>((resolve) => {\n session.pendingPermission = { requestId: request.id, resolve };\n });\n\n // Send permission UI to session topic (notification is sent by adapter)\n await adapter.sendPermissionRequest(session.id, request);\n\n // Wait for user response — adapter resolves this promise\n return promise;\n };\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { SessionRecord } from \"./types.js\";\nimport { createChildLogger } from \"./log.js\";\n\nconst log = createChildLogger({ module: \"session-store\" });\n\nexport interface SessionStore {\n save(record: SessionRecord): Promise<void>;\n get(sessionId: string): SessionRecord | undefined;\n findByPlatform(\n channelId: string,\n predicate: (platform: Record<string, unknown>) => boolean,\n ): SessionRecord | undefined;\n list(channelId?: string): SessionRecord[];\n remove(sessionId: string): Promise<void>;\n}\n\ninterface StoreFile {\n version: number;\n sessions: Record<string, SessionRecord>;\n}\n\nconst DEBOUNCE_MS = 2000;\n\nexport class JsonFileSessionStore implements SessionStore {\n private records: Map<string, SessionRecord> = new Map();\n private filePath: string;\n private ttlDays: number;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private cleanupInterval: ReturnType<typeof setInterval> | null = null;\n private flushHandler: (() => void) | null = null;\n\n constructor(filePath: string, ttlDays: number) {\n this.filePath = filePath;\n this.ttlDays = ttlDays;\n this.load();\n this.cleanup();\n\n // Daily cleanup for long-running instances\n this.cleanupInterval = setInterval(\n () => this.cleanup(),\n 24 * 60 * 60 * 1000,\n );\n\n // Force flush on shutdown\n this.flushHandler = () => this.flushSync();\n process.on(\"SIGTERM\", this.flushHandler);\n process.on(\"SIGINT\", this.flushHandler);\n process.on(\"exit\", this.flushHandler);\n }\n\n async save(record: SessionRecord): Promise<void> {\n this.records.set(record.sessionId, { ...record });\n this.scheduleDiskWrite();\n }\n\n get(sessionId: string): SessionRecord | undefined {\n return this.records.get(sessionId);\n }\n\n findByPlatform(\n channelId: string,\n predicate: (platform: Record<string, unknown>) => boolean,\n ): SessionRecord | undefined {\n for (const record of this.records.values()) {\n if (record.channelId === channelId && predicate(record.platform)) {\n return record;\n }\n }\n return undefined;\n }\n\n list(channelId?: string): SessionRecord[] {\n const all = [...this.records.values()];\n if (channelId) return all.filter((r) => r.channelId === channelId);\n return all;\n }\n\n async remove(sessionId: string): Promise<void> {\n this.records.delete(sessionId);\n this.scheduleDiskWrite();\n }\n\n flushSync(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n const data: StoreFile = {\n version: 1,\n sessions: Object.fromEntries(this.records),\n };\n const dir = path.dirname(this.filePath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(this.filePath, JSON.stringify(data, null, 2));\n }\n\n destroy(): void {\n if (this.debounceTimer) clearTimeout(this.debounceTimer);\n if (this.cleanupInterval) clearInterval(this.cleanupInterval);\n if (this.flushHandler) {\n process.removeListener(\"SIGTERM\", this.flushHandler);\n process.removeListener(\"SIGINT\", this.flushHandler);\n process.removeListener(\"exit\", this.flushHandler);\n this.flushHandler = null;\n }\n }\n\n private load(): void {\n if (!fs.existsSync(this.filePath)) return;\n try {\n const raw = JSON.parse(\n fs.readFileSync(this.filePath, \"utf-8\"),\n ) as StoreFile;\n if (raw.version !== 1) {\n log.warn(\n { version: raw.version },\n \"Unknown session store version, skipping load\",\n );\n return;\n }\n for (const [id, record] of Object.entries(raw.sessions)) {\n this.records.set(id, record);\n }\n log.info({ count: this.records.size }, \"Loaded session records\");\n } catch (err) {\n log.error({ err }, \"Failed to load session store\");\n }\n }\n\n private cleanup(): void {\n const cutoff = Date.now() - this.ttlDays * 24 * 60 * 60 * 1000;\n let removed = 0;\n for (const [id, record] of this.records) {\n if (record.status === \"active\" || record.status === \"initializing\")\n continue;\n const lastActive = new Date(record.lastActiveAt).getTime();\n if (lastActive < cutoff) {\n this.records.delete(id);\n removed++;\n }\n }\n if (removed > 0) {\n log.info({ removed }, \"Cleaned up expired session records\");\n this.scheduleDiskWrite();\n }\n }\n\n private scheduleDiskWrite(): void {\n if (this.debounceTimer) clearTimeout(this.debounceTimer);\n this.debounceTimer = setTimeout(() => {\n this.flushSync();\n }, DEBOUNCE_MS);\n }\n}\n","export interface FileInfo {\n filePath: string\n content: string\n oldContent?: string\n}\n\n/**\n * Extract file path and content from ACP tool_call/tool_update content.\n *\n * ACP content formats observed:\n * - Diff block: [{ type: \"diff\", path: \"...\", oldText: \"...\", newText: \"...\" }]\n * - Content wrapper: [{ type: \"content\", content: { type: \"text\", text: \"...\" } }]\n * - Text block: { type: \"text\", text: \"...\" }\n * - rawInput: { file_path: \"...\", content: \"...\" }\n */\nexport function extractFileInfo(name: string, kind: string | undefined, content: unknown): FileInfo | null {\n if (!content) return null\n\n // Only process file-related tool kinds\n if (kind && !['read', 'edit', 'write'].includes(kind)) return null\n\n // Try to extract from known ACP content patterns\n const info = parseContent(content)\n if (!info) return null\n\n // Infer file path from tool name if not in content\n if (!info.filePath) {\n // Some agents put the path in the tool name (e.g., \"Read src/main.ts\" or \"Write index.html\")\n const pathMatch = name.match(/(?:Read|Edit|Write|View)\\s+(.+)/i)\n if (pathMatch) info.filePath = pathMatch[1].trim()\n }\n\n if (!info.filePath || !info.content) return null\n return info as FileInfo\n}\n\nfunction parseContent(content: unknown): Partial<FileInfo> | null {\n if (typeof content === 'string') {\n return { content }\n }\n\n if (Array.isArray(content)) {\n // Array of content blocks — try each\n for (const block of content) {\n const result = parseContent(block)\n if (result?.content || result?.filePath) return result\n }\n return null\n }\n\n if (typeof content === 'object' && content !== null) {\n const c = content as Record<string, unknown>\n\n // ACP diff block: { type: 'diff', path: '...', oldText: '...', newText: '...' }\n if (c.type === 'diff' && typeof c.path === 'string') {\n const newText = c.newText as string | null | undefined\n const oldText = c.oldText as string | null | undefined\n if (newText) {\n return {\n filePath: c.path as string,\n content: newText,\n oldContent: oldText ?? undefined,\n }\n }\n }\n\n // ACP content wrapper: { type: 'content', content: { type: 'text', text: '...' } }\n if (c.type === 'content' && c.content) {\n return parseContent(c.content)\n }\n\n // ACP text block: { type: 'text', text: '...' }\n if (c.type === 'text' && typeof c.text === 'string') {\n return { content: c.text, filePath: c.filePath as string | undefined }\n }\n\n // Direct fields\n if (typeof c.text === 'string') {\n return { content: c.text, filePath: c.filePath as string | undefined }\n }\n\n // Tool input with file path: { file_path: '...', content: '...' }\n if (typeof c.file_path === 'string' || typeof c.filePath === 'string' || typeof c.path === 'string') {\n const filePath = (c.file_path || c.filePath || c.path) as string\n const fileContent = (c.content || c.text || c.output || c.newText) as string | undefined\n if (typeof fileContent === 'string') {\n return {\n filePath,\n content: fileContent,\n oldContent: (c.old_content || c.oldText) as string | undefined,\n }\n }\n }\n\n // Nested input/output\n if (c.input) {\n const result = parseContent(c.input)\n if (result) return result\n }\n if (c.output) {\n const result = parseContent(c.output)\n if (result) return result\n }\n }\n\n return null\n}\n","import type { OutgoingMessage, PermissionRequest, NotificationMessage, AgentCommand } from './types.js'\n\nexport interface ChannelConfig {\n enabled: boolean\n [key: string]: unknown\n}\n\nexport abstract class ChannelAdapter {\n constructor(protected core: any, protected config: ChannelConfig) {}\n\n abstract start(): Promise<void>\n abstract stop(): Promise<void>\n\n // Outgoing: core → channel\n abstract sendMessage(sessionId: string, content: OutgoingMessage): Promise<void>\n abstract sendPermissionRequest(sessionId: string, request: PermissionRequest): Promise<void>\n abstract sendNotification(notification: NotificationMessage): Promise<void>\n\n // Session lifecycle on channel side\n abstract createSessionThread(sessionId: string, name: string): Promise<string> // returns threadId\n abstract renameSessionThread(sessionId: string, newName: string): Promise<void>\n\n // Skill commands — override in adapters that support dynamic commands\n async sendSkillCommands(_sessionId: string, _commands: AgentCommand[]): Promise<void> {}\n async cleanupSkillCommands(_sessionId: string): Promise<void> {}\n}\n","import { Bot } from \"grammy\";\nimport {\n ChannelAdapter,\n type OpenACPCore,\n type OutgoingMessage,\n type PermissionRequest,\n type NotificationMessage,\n type Session,\n type AgentCommand,\n} from \"../../core/index.js\";\nimport { createChildLogger } from \"../../core/log.js\";\nconst log = createChildLogger({ module: \"telegram\" });\nimport type { TelegramChannelConfig } from \"./types.js\";\nimport { MessageDraft } from \"./streaming.js\";\nimport {\n ensureTopics,\n createSessionTopic,\n renameSessionTopic,\n} from \"./topics.js\";\nimport {\n setupCommands,\n setupMenuCallbacks,\n setupSkillCallbacks,\n buildMenuKeyboard,\n buildSkillKeyboard,\n clearSkillCallbacks,\n STATIC_COMMANDS,\n} from \"./commands.js\";\nimport { PermissionHandler } from \"./permissions.js\";\nimport {\n spawnAssistant,\n handleAssistantMessage,\n redirectToAssistant,\n} from \"./assistant.js\";\nimport {\n escapeHtml,\n formatToolCall,\n formatToolUpdate,\n formatPlan,\n formatUsage,\n} from \"./formatting.js\";\n\nexport class TelegramAdapter extends ChannelAdapter {\n private bot!: Bot;\n private telegramConfig: TelegramChannelConfig;\n private sessionDrafts: Map<string, MessageDraft> = new Map();\n private toolCallMessages: Map<\n string,\n Map<\n string,\n {\n msgId: number;\n name: string;\n kind?: string;\n viewerLinks?: { file?: string; diff?: string };\n }\n >\n > = new Map(); // sessionId → (toolCallId → state)\n private permissionHandler!: PermissionHandler;\n private assistantSession: Session | null = null;\n private notificationTopicId!: number;\n private assistantTopicId!: number;\n private skillMessages: Map<string, number> = new Map(); // sessionId → pinned messageId\n\n constructor(core: OpenACPCore, config: TelegramChannelConfig) {\n super(core, config as never);\n this.telegramConfig = config;\n }\n\n async start(): Promise<void> {\n this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch } });\n\n // Global error handler — prevent unhandled errors from crashing the bot\n this.bot.catch((err) => {\n const rootCause = err.error instanceof Error ? err.error : err;\n log.error({ err: rootCause }, \"Telegram bot error\");\n });\n\n // Ensure allowed_updates includes callback_query on every poll.\n // bot.start() passes allowed_updates, but grammY only sends it on the first\n // getUpdates call. Subsequent polls may omit the parameter, causing Telegram\n // to fall back to its default (which excludes callback_query). This transformer\n // guarantees callback_query is always requested.\n this.bot.api.config.use((prev, method, payload, signal) => {\n if (method === \"getUpdates\") {\n const p = payload as never as Record<string, unknown>;\n p.allowed_updates = (p.allowed_updates as string[] | undefined) ?? [\n \"message\",\n \"callback_query\",\n ];\n }\n return prev(method, payload, signal);\n });\n\n // Register static commands for Telegram autocomplete (scoped to this chat)\n await this.bot.api.setMyCommands(STATIC_COMMANDS, {\n scope: { type: \"chat\", chat_id: this.telegramConfig.chatId },\n });\n\n // Middleware: only accept updates from configured chatId\n this.bot.use((ctx, next) => {\n const chatId = ctx.chat?.id ?? ctx.callbackQuery?.message?.chat?.id;\n if (chatId !== this.telegramConfig.chatId) return;\n return next();\n });\n\n // Ensure system topics exist\n const topics = await ensureTopics(\n this.bot,\n this.telegramConfig.chatId,\n this.telegramConfig,\n async (updates) => {\n // Save topic IDs to config\n await (this.core as OpenACPCore).configManager.save({\n channels: { telegram: updates },\n });\n },\n );\n this.notificationTopicId = topics.notificationTopicId;\n this.assistantTopicId = topics.assistantTopicId;\n\n // Setup permission handler (instance only, callback registered later)\n this.permissionHandler = new PermissionHandler(\n this.bot,\n this.telegramConfig.chatId,\n (sessionId) =>\n (this.core as OpenACPCore).sessionManager.getSession(sessionId),\n (notification) => this.sendNotification(notification),\n );\n\n // Callback registration order matters!\n // Specific regex handlers first, catch-all last.\n setupSkillCallbacks(this.bot, this.core as OpenACPCore);\n setupMenuCallbacks(\n this.bot,\n this.core as OpenACPCore,\n this.telegramConfig.chatId,\n );\n setupCommands(\n this.bot,\n this.core as OpenACPCore,\n this.telegramConfig.chatId,\n );\n this.permissionHandler.setupCallbackHandler();\n\n // Setup message routing\n this.setupRoutes();\n\n // Start bot polling\n this.bot.start({\n allowed_updates: [\"message\", \"callback_query\"],\n onStart: () =>\n log.info(\n { chatId: this.telegramConfig.chatId },\n \"Telegram bot started\",\n ),\n });\n\n // Spawn assistant (after bot is started so it can send messages)\n try {\n this.assistantSession = await spawnAssistant(\n this.core as OpenACPCore,\n this,\n this.assistantTopicId,\n );\n } catch (err) {\n log.error({ err }, \"Failed to spawn assistant\");\n }\n\n // Send welcome message with menu to assistant topic\n try {\n const config = (this.core as OpenACPCore).configManager.get();\n const agents = (\n this.core as OpenACPCore\n ).agentManager.getAvailableAgents();\n const agentList = agents\n .map(\n (a) =>\n `${escapeHtml(a.name)}${a.name === config.defaultAgent ? \" (default)\" : \"\"}`,\n )\n .join(\", \");\n const workspace = escapeHtml(config.workspace.baseDir);\n\n const welcomeText =\n `👋 <b>OpenACP Assistant</b> is online.\\n\\n` +\n `Available agents: ${agentList}\\n` +\n `Workspace: <code>${workspace}</code>\\n\\n` +\n `<b>Select an action:</b>`;\n\n await this.bot.api.sendMessage(this.telegramConfig.chatId, welcomeText, {\n message_thread_id: this.assistantTopicId,\n parse_mode: \"HTML\",\n reply_markup: buildMenuKeyboard(),\n });\n } catch (err) {\n log.warn({ err }, \"Failed to send welcome message\");\n }\n }\n\n async stop(): Promise<void> {\n if (this.assistantSession) {\n await this.assistantSession.destroy();\n }\n await this.bot.stop();\n log.info(\"Telegram bot stopped\");\n }\n\n private setupRoutes(): void {\n this.bot.on(\"message:text\", async (ctx) => {\n const threadId = ctx.message.message_thread_id;\n\n // General topic or no thread → redirect to assistant\n if (!threadId) {\n const html = redirectToAssistant(\n this.telegramConfig.chatId,\n this.assistantTopicId,\n );\n await ctx.reply(html, { parse_mode: \"HTML\" });\n return;\n }\n\n // Notification topic → ignore\n if (threadId === this.notificationTopicId) return;\n\n // Assistant topic → send typing indicator and forward to assistant session\n if (threadId === this.assistantTopicId) {\n ctx.replyWithChatAction(\"typing\").catch(() => {});\n handleAssistantMessage(this.assistantSession, ctx.message.text).catch(\n (err) => log.error({ err }, \"Assistant error\"),\n );\n return;\n }\n\n // Session topic → send typing indicator and forward to core\n ctx.replyWithChatAction(\"typing\").catch(() => {});\n (this.core as OpenACPCore)\n .handleMessage({\n channelId: \"telegram\",\n threadId: String(threadId),\n userId: String(ctx.from.id),\n text: ctx.message.text,\n })\n .catch((err) => log.error({ err }, \"handleMessage error\"));\n });\n }\n\n // --- ChannelAdapter implementations ---\n\n async sendMessage(\n sessionId: string,\n content: OutgoingMessage,\n ): Promise<void> {\n // log.debug({ sessionId, type: content.type }, \"Sending message to Telegram\");\n const session = (this.core as OpenACPCore).sessionManager.getSession(\n sessionId,\n );\n if (!session) return;\n const threadId = Number(session.threadId);\n\n switch (content.type) {\n case \"thought\": {\n // Skip thought/thinking content — it's internal agent reasoning\n // Users don't need to see it\n break;\n }\n\n case \"text\": {\n let draft = this.sessionDrafts.get(sessionId);\n if (!draft) {\n draft = new MessageDraft(\n this.bot,\n this.telegramConfig.chatId,\n threadId,\n );\n this.sessionDrafts.set(sessionId, draft);\n }\n draft.append(content.text);\n break;\n }\n\n case \"tool_call\": {\n await this.finalizeDraft(sessionId);\n const meta = content.metadata as never as {\n id: string;\n name: string;\n kind?: string;\n status?: string;\n content?: unknown;\n viewerLinks?: { file?: string; diff?: string };\n };\n const msg = await this.bot.api.sendMessage(\n this.telegramConfig.chatId,\n formatToolCall(meta),\n {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n },\n );\n if (!this.toolCallMessages.has(sessionId)) {\n this.toolCallMessages.set(sessionId, new Map());\n }\n this.toolCallMessages.get(sessionId)!.set(meta.id, {\n msgId: msg.message_id,\n name: meta.name,\n kind: meta.kind,\n viewerLinks: meta.viewerLinks,\n });\n break;\n }\n\n case \"tool_update\": {\n const meta = content.metadata as never as {\n id: string;\n name: string;\n kind?: string;\n status: string;\n content?: unknown;\n viewerLinks?: { file?: string; diff?: string };\n };\n const toolState = this.toolCallMessages.get(sessionId)?.get(meta.id);\n if (toolState) {\n // Carry forward viewerLinks from previous updates if not present in current\n const viewerLinks = meta.viewerLinks || toolState.viewerLinks;\n if (meta.viewerLinks) toolState.viewerLinks = meta.viewerLinks;\n // Merge name/kind from original tool_call\n const merged = {\n ...meta,\n name: meta.name || toolState.name,\n kind: meta.kind || toolState.kind,\n viewerLinks,\n };\n try {\n await this.bot.api.editMessageText(\n this.telegramConfig.chatId,\n toolState.msgId,\n formatToolUpdate(merged),\n { parse_mode: \"HTML\" },\n );\n } catch {\n /* edit failed */\n }\n }\n break;\n }\n\n case \"plan\": {\n await this.finalizeDraft(sessionId);\n await this.bot.api.sendMessage(\n this.telegramConfig.chatId,\n formatPlan(\n content.metadata as never as {\n entries: Array<{ content: string; status: string }>;\n },\n ),\n {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n },\n );\n break;\n }\n\n case \"usage\": {\n // Show usage stats\n await this.bot.api.sendMessage(\n this.telegramConfig.chatId,\n formatUsage(\n content.metadata as never as {\n tokensUsed?: number;\n contextSize?: number;\n cost?: { amount: number; currency: string };\n },\n ),\n {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n },\n );\n break;\n }\n\n case \"session_end\": {\n await this.finalizeDraft(sessionId);\n this.sessionDrafts.delete(sessionId);\n this.toolCallMessages.delete(sessionId);\n await this.cleanupSkillCommands(sessionId);\n await this.bot.api.sendMessage(\n this.telegramConfig.chatId,\n `✅ <b>Done</b>`,\n {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n },\n );\n break;\n }\n\n case \"error\": {\n await this.finalizeDraft(sessionId);\n await this.bot.api.sendMessage(\n this.telegramConfig.chatId,\n `❌ <b>Error:</b> ${escapeHtml(content.text)}`,\n {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n },\n );\n break;\n }\n }\n }\n\n async sendPermissionRequest(\n sessionId: string,\n request: PermissionRequest,\n ): Promise<void> {\n log.info({ sessionId, requestId: request.id }, \"Permission request sent\");\n const session = (this.core as OpenACPCore).sessionManager.getSession(\n sessionId,\n );\n if (!session) return;\n await this.permissionHandler.sendPermissionRequest(session, request);\n }\n\n async sendNotification(notification: NotificationMessage): Promise<void> {\n log.info(\n { sessionId: notification.sessionId, type: notification.type },\n \"Notification sent\",\n );\n if (!this.notificationTopicId) return;\n const emoji: Record<string, string> = {\n completed: \"✅\",\n error: \"❌\",\n permission: \"🔐\",\n input_required: \"💬\",\n };\n let text = `${emoji[notification.type] || \"ℹ️\"} <b>${escapeHtml(notification.sessionName || notification.sessionId)}</b>\\n`;\n text += escapeHtml(notification.summary);\n if (notification.deepLink) {\n text += `\\n\\n<a href=\"${notification.deepLink}\">→ Go to message</a>`;\n }\n await this.bot.api.sendMessage(this.telegramConfig.chatId, text, {\n message_thread_id: this.notificationTopicId,\n parse_mode: \"HTML\",\n disable_notification: false,\n });\n }\n\n async createSessionThread(sessionId: string, name: string): Promise<string> {\n log.info({ sessionId, name }, \"Session topic created\");\n return String(\n await createSessionTopic(this.bot, this.telegramConfig.chatId, name),\n );\n }\n\n async renameSessionThread(sessionId: string, newName: string): Promise<void> {\n const session = (this.core as OpenACPCore).sessionManager.getSession(\n sessionId,\n );\n if (!session) return;\n await renameSessionTopic(\n this.bot,\n this.telegramConfig.chatId,\n Number(session.threadId),\n newName,\n );\n }\n\n async sendSkillCommands(\n sessionId: string,\n commands: AgentCommand[],\n ): Promise<void> {\n const session = (this.core as OpenACPCore).sessionManager.getSession(\n sessionId,\n );\n if (!session) return;\n const threadId = Number(session.threadId);\n if (!threadId) return;\n\n // Empty commands → remove pinned message\n if (commands.length === 0) {\n await this.cleanupSkillCommands(sessionId);\n return;\n }\n\n // Clear old callback entries before building new keyboard\n clearSkillCallbacks(sessionId);\n\n const keyboard = buildSkillKeyboard(sessionId, commands);\n const text = \"🛠 <b>Available commands:</b>\";\n const existingMsgId = this.skillMessages.get(sessionId);\n\n if (existingMsgId) {\n // Update existing pinned message\n try {\n await this.bot.api.editMessageText(\n this.telegramConfig.chatId,\n existingMsgId,\n text,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n return;\n } catch {\n // Message may have been deleted — fall through to create new\n }\n }\n\n // Create and pin new message\n try {\n const msg = await this.bot.api.sendMessage(\n this.telegramConfig.chatId,\n text,\n {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n reply_markup: keyboard,\n disable_notification: true,\n },\n );\n this.skillMessages.set(sessionId, msg.message_id);\n\n await this.bot.api.pinChatMessage(\n this.telegramConfig.chatId,\n msg.message_id,\n {\n disable_notification: true,\n },\n );\n } catch (err) {\n log.error({ err, sessionId }, \"Failed to send skill commands\");\n }\n\n // Update Telegram autocomplete with skill commands\n await this.updateCommandAutocomplete(session.agentName, commands);\n }\n\n async cleanupSkillCommands(sessionId: string): Promise<void> {\n const msgId = this.skillMessages.get(sessionId);\n if (!msgId) return;\n\n try {\n await this.bot.api.editMessageText(\n this.telegramConfig.chatId,\n msgId,\n \"🛠 <i>Session ended</i>\",\n { parse_mode: \"HTML\" },\n );\n await this.bot.api.unpinChatMessage(this.telegramConfig.chatId, msgId);\n } catch {\n /* message may already be deleted */\n }\n\n this.skillMessages.delete(sessionId);\n clearSkillCallbacks(sessionId);\n }\n\n private async updateCommandAutocomplete(\n agentName: string,\n skillCommands: AgentCommand[],\n ): Promise<void> {\n // Telegram requires: 1-32 chars, lowercase a-z, 0-9, underscores only\n const prefix = `[${agentName}] `;\n const validSkills = skillCommands\n .map((c) => ({\n command: c.name\n .toLowerCase()\n .replace(/[^a-z0-9_]/g, \"_\")\n .slice(0, 32),\n description: (\n prefix + (c.description || c.name).replace(/\\n/g, \" \")\n ).slice(0, 256),\n }))\n .filter((c) => c.command.length > 0);\n const all = [...STATIC_COMMANDS, ...validSkills];\n try {\n await this.bot.api.setMyCommands(all, {\n scope: { type: \"chat\", chat_id: this.telegramConfig.chatId },\n });\n log.info(\n { count: all.length, skills: validSkills.length },\n \"Updated command autocomplete\",\n );\n } catch (err) {\n log.error(\n { err, commands: all },\n \"Failed to update command autocomplete\",\n );\n }\n }\n\n private async finalizeDraft(sessionId: string): Promise<void> {\n const draft = this.sessionDrafts.get(sessionId);\n if (draft) {\n await draft.finalize();\n this.sessionDrafts.delete(sessionId);\n }\n }\n}\n","export function escapeHtml(text: string | undefined | null): string {\n if (!text) return ''\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\nexport function markdownToTelegramHtml(md: string): string {\n // Step 1: Extract code blocks and inline code into placeholders\n const codeBlocks: string[] = []\n const inlineCodes: string[] = []\n\n // Extract fenced code blocks (```lang\\n...\\n```)\n let text = md.replace(/```(\\w*)\\n?([\\s\\S]*?)```/g, (_match, lang: string, code: string) => {\n const index = codeBlocks.length\n const escapedCode = escapeHtml(code)\n const langAttr = lang ? ` class=\"language-${escapeHtml(lang)}\"` : ''\n codeBlocks.push(`<pre><code${langAttr}>${escapedCode}</code></pre>`)\n return `\\x00CODE_BLOCK_${index}\\x00`\n })\n\n // Extract inline code (`...`)\n text = text.replace(/`([^`]+)`/g, (_match, code: string) => {\n const index = inlineCodes.length\n inlineCodes.push(`<code>${escapeHtml(code)}</code>`)\n return `\\x00INLINE_CODE_${index}\\x00`\n })\n\n // Step 2: Escape HTML in remaining text\n text = escapeHtml(text)\n\n // Step 3: Apply markdown transformations\n // Bold: **text** → <b>text</b>\n text = text.replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>')\n\n // Italic: *text* → <i>text</i> (but not the ** used for bold)\n text = text.replace(/(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)/g, '<i>$1</i>')\n\n // Links: [text](url) → <a href=\"url\">text</a>\n // Note: after escapeHtml, parentheses are not affected, but we need to handle\n // the escaped brackets properly. Since [ ] and ( ) are not escaped, this works directly.\n text = text.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href=\"$2\">$1</a>')\n\n // Step 4: Restore fenced code blocks\n text = text.replace(/\\x00CODE_BLOCK_(\\d+)\\x00/g, (_match, idx: string) => {\n return codeBlocks[parseInt(idx, 10)]\n })\n\n // Step 5: Restore inline code\n text = text.replace(/\\x00INLINE_CODE_(\\d+)\\x00/g, (_match, idx: string) => {\n return inlineCodes[parseInt(idx, 10)]\n })\n\n return text\n}\n\nconst STATUS_ICON: Record<string, string> = {\n pending: '⏳',\n in_progress: '🔄',\n completed: '✅',\n failed: '❌',\n}\n\nconst KIND_ICON: Record<string, string> = {\n read: '📖', edit: '✏️', delete: '🗑️', execute: '▶️',\n search: '🔍', fetch: '🌐', think: '🧠', move: '📦', other: '🛠️',\n}\n\nfunction extractContentText(content: unknown, depth = 0): string {\n if (!content || depth > 5) return ''\n if (typeof content === 'string') return content\n if (Array.isArray(content)) {\n return content\n .map((c: unknown) => extractContentText(c, depth + 1))\n .filter(Boolean)\n .join('\\n')\n }\n if (typeof content === 'object' && content !== null) {\n const c = content as Record<string, unknown>\n // ACP content blocks: {type: ..., text: ...} or {type: ..., content: ...}\n if (c.type === 'text' && typeof c.text === 'string') return c.text\n if (typeof c.text === 'string') return c.text\n if (typeof c.content === 'string') return c.content\n // Tool input/output objects\n if (c.input) return extractContentText(c.input, depth + 1)\n if (c.output) return extractContentText(c.output, depth + 1)\n // Fallback: pretty-print JSON (but skip type-only objects)\n const keys = Object.keys(c).filter(k => k !== 'type')\n if (keys.length === 0) return ''\n return JSON.stringify(c, null, 2)\n }\n return String(content)\n}\n\nfunction truncateContent(text: string, maxLen = 3800): string {\n if (text.length <= maxLen) return text\n return text.slice(0, maxLen) + '\\n… (truncated)'\n}\n\nexport function formatToolCall(tool: { id: string; name?: string; kind?: string; status?: string; content?: unknown; viewerLinks?: { file?: string; diff?: string } }): string {\n const si = STATUS_ICON[tool.status || ''] || '🔧'\n const ki = KIND_ICON[tool.kind || ''] || '🛠️'\n let text = `${si} ${ki} <b>${escapeHtml(tool.name || 'Tool')}</b>`\n const details = extractContentText(tool.content)\n if (details) {\n text += `\\n<pre>${escapeHtml(truncateContent(details))}</pre>`\n }\n text += formatViewerLinks(tool.viewerLinks)\n return text\n}\n\nexport function formatToolUpdate(update: { id: string; name?: string; kind?: string; status: string; content?: unknown; viewerLinks?: { file?: string; diff?: string } }): string {\n const si = STATUS_ICON[update.status] || '🔧'\n const ki = KIND_ICON[update.kind || ''] || '🛠️'\n const name = update.name || 'Tool'\n let text = `${si} ${ki} <b>${escapeHtml(name)}</b>`\n const details = extractContentText(update.content)\n if (details) {\n text += `\\n<pre>${escapeHtml(truncateContent(details))}</pre>`\n }\n text += formatViewerLinks(update.viewerLinks)\n return text\n}\n\nfunction formatViewerLinks(links?: { file?: string; diff?: string }): string {\n if (!links) return ''\n let text = ''\n if (links.file) text += `\\n📄 <a href=\"${escapeHtml(links.file)}\">View file</a>`\n if (links.diff) text += `\\n📝 <a href=\"${escapeHtml(links.diff)}\">View diff</a>`\n return text\n}\n\nexport function formatPlan(plan: { entries: Array<{ content: string; status: string }> }): string {\n const statusIcon: Record<string, string> = { pending: '⬜', in_progress: '🔄', completed: '✅' }\n const lines = plan.entries.map((e, i) =>\n `${statusIcon[e.status] || '⬜'} ${i + 1}. ${escapeHtml(e.content)}`\n )\n return `<b>Plan:</b>\\n${lines.join('\\n')}`\n}\n\nexport function formatUsage(usage: { tokensUsed?: number; contextSize?: number; cost?: { amount: number; currency: string } }): string {\n const parts: string[] = []\n if (usage.tokensUsed != null) parts.push(`Tokens: ${usage.tokensUsed.toLocaleString()}`)\n if (usage.contextSize != null) parts.push(`Context: ${usage.contextSize.toLocaleString()}`)\n if (usage.cost) parts.push(`Cost: $${usage.cost.amount.toFixed(4)}`)\n return `📊 ${parts.join(' | ')}`\n}\n\nexport function splitMessage(text: string, maxLength = 4096): string[] {\n if (text.length <= maxLength) return [text]\n const chunks: string[] = []\n let remaining = text\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining)\n break\n }\n let splitAt = remaining.lastIndexOf('\\n\\n', maxLength)\n if (splitAt === -1 || splitAt < maxLength * 0.5) {\n splitAt = remaining.lastIndexOf('\\n', maxLength)\n }\n if (splitAt === -1 || splitAt < maxLength * 0.5) {\n splitAt = maxLength\n }\n chunks.push(remaining.slice(0, splitAt))\n remaining = remaining.slice(splitAt).trimStart()\n }\n return chunks\n}\n","import type { Bot } from 'grammy'\nimport { markdownToTelegramHtml, splitMessage } from './formatting.js'\n\nexport class MessageDraft {\n private messageId?: number\n private buffer: string = ''\n private lastFlush: number = 0\n private flushTimer?: ReturnType<typeof setTimeout>\n private flushPromise: Promise<void> = Promise.resolve() // serialize flushes\n private minInterval = 1000 // 1 second throttle\n\n constructor(\n private bot: Bot,\n private chatId: number,\n private threadId: number,\n ) {}\n\n append(text: string): void {\n this.buffer += text\n this.scheduleFlush()\n }\n\n private scheduleFlush(): void {\n const now = Date.now()\n const elapsed = now - this.lastFlush\n\n if (elapsed >= this.minInterval) {\n // Chain flush to prevent concurrent sends\n this.flushPromise = this.flushPromise.then(() => this.flush()).catch(() => {})\n } else if (!this.flushTimer) {\n this.flushTimer = setTimeout(() => {\n this.flushTimer = undefined\n this.flushPromise = this.flushPromise.then(() => this.flush()).catch(() => {})\n }, this.minInterval - elapsed)\n }\n }\n\n private async flush(): Promise<void> {\n if (!this.buffer) return\n this.lastFlush = Date.now()\n\n const html = markdownToTelegramHtml(this.buffer)\n // Truncate for streaming (will send full on finalize)\n const truncated = html.length > 4096 ? html.slice(0, 4090) + '\\n...' : html\n if (!truncated) return\n\n try {\n if (!this.messageId) {\n const msg = await this.bot.api.sendMessage(this.chatId, truncated, {\n message_thread_id: this.threadId,\n parse_mode: 'HTML',\n disable_notification: true,\n })\n this.messageId = msg.message_id\n } else {\n await this.bot.api.editMessageText(this.chatId, this.messageId, truncated, {\n parse_mode: 'HTML',\n })\n }\n } catch {\n // Edit failed — try plain text without HTML parse mode\n try {\n if (!this.messageId) {\n const msg = await this.bot.api.sendMessage(this.chatId, this.buffer.slice(0, 4096), {\n message_thread_id: this.threadId,\n disable_notification: true,\n })\n this.messageId = msg.message_id\n }\n } catch {\n // Give up on this flush\n }\n }\n }\n\n async finalize(): Promise<number | undefined> {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer)\n this.flushTimer = undefined\n }\n\n // Wait for any in-flight flush to complete\n await this.flushPromise\n\n if (!this.buffer) return this.messageId\n\n // Final send with full content + splitting\n const html = markdownToTelegramHtml(this.buffer)\n const chunks = splitMessage(html)\n\n try {\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i]\n if (i === 0 && this.messageId) {\n // Edit existing message with first chunk\n await this.bot.api.editMessageText(this.chatId, this.messageId, chunk, {\n parse_mode: 'HTML',\n })\n } else {\n // Send new message\n const msg = await this.bot.api.sendMessage(this.chatId, chunk, {\n message_thread_id: this.threadId,\n parse_mode: 'HTML',\n disable_notification: true,\n })\n this.messageId = msg.message_id\n }\n }\n } catch {\n // Best effort — try plain text\n try {\n await this.bot.api.sendMessage(this.chatId, this.buffer.slice(0, 4096), {\n message_thread_id: this.threadId,\n disable_notification: true,\n })\n } catch {\n // Give up\n }\n }\n\n return this.messageId\n }\n\n getMessageId(): number | undefined {\n return this.messageId\n }\n}\n","import type { Bot } from 'grammy'\n\n// Ensure notification and assistant topics exist, create if needed\n// Returns updated topic IDs\nexport async function ensureTopics(\n bot: Bot,\n chatId: number,\n config: { notificationTopicId: number | null; assistantTopicId: number | null },\n saveConfig: (updates: { notificationTopicId?: number; assistantTopicId?: number }) => Promise<void>,\n): Promise<{ notificationTopicId: number; assistantTopicId: number }> {\n let notificationTopicId = config.notificationTopicId\n let assistantTopicId = config.assistantTopicId\n\n if (notificationTopicId === null) {\n const topic = await bot.api.createForumTopic(chatId, '📋 Notifications')\n notificationTopicId = topic.message_thread_id\n await saveConfig({ notificationTopicId })\n }\n\n if (assistantTopicId === null) {\n const topic = await bot.api.createForumTopic(chatId, '🤖 Assistant')\n assistantTopicId = topic.message_thread_id\n await saveConfig({ assistantTopicId })\n }\n\n return { notificationTopicId, assistantTopicId }\n}\n\n// Create a new forum topic for a session\nexport async function createSessionTopic(\n bot: Bot,\n chatId: number,\n name: string,\n): Promise<number> {\n const topic = await bot.api.createForumTopic(chatId, name)\n return topic.message_thread_id\n}\n\n// Rename an existing forum topic\nexport async function renameSessionTopic(\n bot: Bot,\n chatId: number,\n threadId: number,\n name: string,\n): Promise<void> {\n try {\n await bot.api.editForumTopic(chatId, threadId, { name })\n } catch {\n // Ignore rename failures (topic may be closed/deleted)\n }\n}\n\n// Build a Telegram deep link to a specific message\nexport function buildDeepLink(chatId: number, messageId: number): string {\n // chatId for supergroups starts with -100, need to strip it for the link\n const cleanId = String(chatId).replace('-100', '')\n return `https://t.me/c/${cleanId}/${messageId}`\n}\n","import type { Bot, Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { OpenACPCore } from \"../../core/index.js\";\nimport { escapeHtml } from \"./formatting.js\";\nimport { createSessionTopic } from \"./topics.js\";\nimport { createChildLogger } from \"../../core/log.js\";\nimport { nanoid } from \"nanoid\";\nimport type { AgentCommand } from \"../../core/index.js\";\nconst log = createChildLogger({ module: \"telegram-commands\" });\n\nexport function setupCommands(\n bot: Bot,\n core: OpenACPCore,\n chatId: number,\n): void {\n bot.command(\"new\", (ctx) => handleNew(ctx, core, chatId));\n bot.command(\"new_chat\", (ctx) => handleNewChat(ctx, core, chatId));\n bot.command(\"cancel\", (ctx) => handleCancel(ctx, core));\n bot.command(\"status\", (ctx) => handleStatus(ctx, core));\n bot.command(\"agents\", (ctx) => handleAgents(ctx, core));\n bot.command(\"help\", (ctx) => handleHelp(ctx));\n bot.command(\"menu\", (ctx) => handleMenu(ctx));\n}\n\nexport function buildMenuKeyboard(): InlineKeyboard {\n return new InlineKeyboard()\n .text(\"🆕 New Session\", \"m:new\")\n .text(\"💬 New Chat\", \"m:new_chat\")\n .row()\n .text(\"⛔ Cancel\", \"m:cancel\")\n .text(\"📊 Status\", \"m:status\")\n .row()\n .text(\"🤖 Agents\", \"m:agents\")\n .text(\"❓ Help\", \"m:help\");\n}\n\nexport function setupMenuCallbacks(\n bot: Bot,\n core: OpenACPCore,\n chatId: number,\n): void {\n bot.callbackQuery(/^m:/, async (ctx) => {\n const data = ctx.callbackQuery.data;\n try {\n await ctx.answerCallbackQuery();\n } catch {\n /* expired or network — ignore */\n }\n\n switch (data) {\n case \"m:new\":\n await handleNew(ctx, core, chatId);\n break;\n case \"m:new_chat\":\n await handleNewChat(ctx, core, chatId);\n break;\n case \"m:cancel\":\n await handleCancel(ctx, core);\n break;\n case \"m:status\":\n await handleStatus(ctx, core);\n break;\n case \"m:agents\":\n await handleAgents(ctx, core);\n break;\n case \"m:help\":\n await handleHelp(ctx);\n break;\n }\n });\n}\n\nasync function handleMenu(ctx: Context): Promise<void> {\n await ctx.reply(`<b>OpenACP Menu</b>\\nChoose an action:`, {\n parse_mode: \"HTML\",\n reply_markup: buildMenuKeyboard(),\n });\n}\n\nasync function handleNew(\n ctx: Context,\n core: OpenACPCore,\n chatId: number,\n): Promise<void> {\n const rawMatch = (ctx as Context & { match: unknown }).match;\n const matchStr = typeof rawMatch === \"string\" ? rawMatch : \"\";\n const args = matchStr.split(\" \").filter(Boolean);\n const agentName = args[0];\n const workspace = args[1];\n\n log.info({ userId: ctx.from?.id, agentName }, \"New session command\");\n\n // Create topic first so threadId is ready before session events fire\n let threadId: number | undefined;\n try {\n const topicName = `🔄 New Session`;\n threadId = await createSessionTopic(botFromCtx(ctx), chatId, topicName);\n\n // Let user know we're setting up (spawn + warm-up can take a while)\n await ctx.api.sendMessage(chatId, `⏳ Setting up session, please wait...`, {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n });\n\n const session = await core.handleNewSession(\n \"telegram\",\n agentName,\n workspace,\n );\n session.threadId = String(threadId);\n\n // Persist platform mapping\n await core.sessionManager.updateSessionPlatform(session.id, {\n topicId: threadId,\n });\n\n // Rename topic with actual agent name\n const finalName = `🔄 ${session.agentName} — New Session`;\n try {\n await ctx.api.editForumTopic(chatId, threadId, { name: finalName });\n } catch {\n /* ignore rename failures */\n }\n\n await ctx.api.sendMessage(\n chatId,\n `✅ Session started\\n` +\n `<b>Agent:</b> ${escapeHtml(session.agentName)}\\n` +\n `<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,\n {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n },\n );\n\n // Warm up model cache in background while user types\n session.warmup().catch((err) => log.error({ err }, \"Warm-up error\"));\n } catch (err) {\n // Clean up orphaned topic if session creation failed\n if (threadId) {\n try {\n await ctx.api.deleteForumTopic(chatId, threadId);\n } catch {\n /* ignore cleanup failures */\n }\n }\n const message = err instanceof Error ? err.message : String(err);\n await ctx.reply(`❌ ${escapeHtml(message)}`, { parse_mode: \"HTML\" });\n }\n}\n\nasync function handleNewChat(\n ctx: Context,\n core: OpenACPCore,\n chatId: number,\n): Promise<void> {\n const threadId = ctx.message?.message_thread_id;\n if (!threadId) {\n await ctx.reply(\n \"Use /new_chat inside a session topic to inherit its config.\",\n { parse_mode: \"HTML\" },\n );\n return;\n }\n\n try {\n const session = await core.handleNewChat(\"telegram\", String(threadId));\n if (!session) {\n await ctx.reply(\"No active session in this topic.\", {\n parse_mode: \"HTML\",\n });\n return;\n }\n\n const topicName = `🔄 ${session.agentName} — New Chat`;\n const newThreadId = await createSessionTopic(\n botFromCtx(ctx),\n chatId,\n topicName,\n );\n\n await ctx.api.sendMessage(chatId, `⏳ Setting up session, please wait...`, {\n message_thread_id: newThreadId,\n parse_mode: \"HTML\",\n });\n\n session.threadId = String(newThreadId);\n\n // Persist platform mapping for new chat\n await core.sessionManager.updateSessionPlatform(session.id, {\n topicId: newThreadId,\n });\n\n await ctx.api.sendMessage(\n chatId,\n `✅ New chat (same agent &amp; workspace)\\n` +\n `<b>Agent:</b> ${escapeHtml(session.agentName)}\\n` +\n `<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,\n {\n message_thread_id: newThreadId,\n parse_mode: \"HTML\",\n },\n );\n\n // Warm up model cache in background while user types\n session.warmup().catch((err) => log.error({ err }, \"Warm-up error\"));\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await ctx.reply(`❌ ${escapeHtml(message)}`, { parse_mode: \"HTML\" });\n }\n}\n\nasync function handleCancel(ctx: Context, core: OpenACPCore): Promise<void> {\n const threadId = ctx.message?.message_thread_id;\n if (!threadId) return;\n\n const session = core.sessionManager.getSessionByThread(\n \"telegram\",\n String(threadId),\n );\n if (session) {\n log.info({ sessionId: session.id }, \"Cancel session command\");\n await session.cancel();\n await ctx.reply(\"⛔ Session cancelled.\", { parse_mode: \"HTML\" });\n }\n}\n\nasync function handleStatus(ctx: Context, core: OpenACPCore): Promise<void> {\n const threadId = ctx.message?.message_thread_id;\n if (threadId) {\n const session = core.sessionManager.getSessionByThread(\n \"telegram\",\n String(threadId),\n );\n if (session) {\n await ctx.reply(\n `<b>Session:</b> ${escapeHtml(session.name || session.id)}\\n` +\n `<b>Agent:</b> ${escapeHtml(session.agentName)}\\n` +\n `<b>Status:</b> ${escapeHtml(session.status)}\\n` +\n `<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>\\n` +\n `<b>Queue:</b> ${session.promptQueue.length} pending`,\n { parse_mode: \"HTML\" },\n );\n } else {\n await ctx.reply(\"No active session in this topic.\", {\n parse_mode: \"HTML\",\n });\n }\n } else {\n const sessions = core.sessionManager.listSessions(\"telegram\");\n const active = sessions.filter(\n (s) => s.status === \"active\" || s.status === \"initializing\",\n );\n await ctx.reply(\n `<b>OpenACP Status</b>\\n` +\n `Active sessions: ${active.length}\\n` +\n `Total sessions: ${sessions.length}`,\n { parse_mode: \"HTML\" },\n );\n }\n}\n\nasync function handleAgents(ctx: Context, core: OpenACPCore): Promise<void> {\n const agents = core.agentManager.getAvailableAgents();\n const defaultAgent = core.configManager.get().defaultAgent;\n const lines = agents.map(\n (a) =>\n `• <b>${escapeHtml(a.name)}</b>${a.name === defaultAgent ? \" (default)\" : \"\"}\\n` +\n ` <code>${escapeHtml(a.command)} ${a.args.map((arg) => escapeHtml(arg)).join(\" \")}</code>`,\n );\n const text =\n lines.length > 0\n ? `<b>Available Agents:</b>\\n\\n${lines.join(\"\\n\")}`\n : `<b>Available Agents:</b>\\n\\nNo agents configured.`;\n await ctx.reply(text, { parse_mode: \"HTML\" });\n}\n\nasync function handleHelp(ctx: Context): Promise<void> {\n await ctx.reply(\n `<b>OpenACP Commands:</b>\\n\\n` +\n `/new [agent] [workspace] — Create new session\\n` +\n `/new_chat — New chat, same agent &amp; workspace\\n` +\n `/cancel — Cancel current session\\n` +\n `/status — Show session/system status\\n` +\n `/agents — List available agents\\n` +\n `/menu — Show interactive menu\\n` +\n `/help — Show this help\\n\\n` +\n `Or just chat in the 🤖 Assistant topic for help!`,\n { parse_mode: \"HTML\" },\n );\n}\n\n// grammy's Context exposes .api (the bot's Api instance) and internally the bot\n// We need access to the bot instance for createSessionTopic (which uses bot.api.createForumTopic).\n// ctx.api is the same Api object as bot.api, so we can pass a minimal shim.\nfunction botFromCtx(ctx: Context): Bot {\n // createSessionTopic only uses bot.api.createForumTopic\n return { api: ctx.api } as unknown as Bot;\n}\n\n// Skill command callback lookup map (short key → session + command)\ninterface SkillCallbackEntry {\n sessionId: string;\n commandName: string;\n}\n\nconst skillCallbackMap = new Map<string, SkillCallbackEntry>();\n\nexport function buildSkillKeyboard(\n sessionId: string,\n commands: AgentCommand[],\n): InlineKeyboard {\n const keyboard = new InlineKeyboard();\n const sorted = [...commands].sort((a, b) => a.name.localeCompare(b.name));\n for (let i = 0; i < sorted.length; i++) {\n const cmd = sorted[i];\n const key = nanoid(8);\n skillCallbackMap.set(key, { sessionId, commandName: cmd.name });\n keyboard.text(`/${cmd.name}`, `s:${key}`);\n if (i % 2 === 1 && i < sorted.length - 1) {\n keyboard.row();\n }\n }\n return keyboard;\n}\n\nexport function clearSkillCallbacks(sessionId: string): void {\n for (const [key, entry] of skillCallbackMap) {\n if (entry.sessionId === sessionId) {\n skillCallbackMap.delete(key);\n }\n }\n}\n\nexport function setupSkillCallbacks(bot: Bot, core: OpenACPCore): void {\n bot.callbackQuery(/^s:/, async (ctx) => {\n try {\n await ctx.answerCallbackQuery();\n } catch {\n /* expired */\n }\n\n const key = ctx.callbackQuery.data.slice(2);\n const entry = skillCallbackMap.get(key);\n if (!entry) return;\n\n const session = core.sessionManager.getSession(entry.sessionId);\n if (!session || session.status !== \"active\") return;\n\n await session.enqueuePrompt(`/${entry.commandName}`);\n });\n}\n\nexport const STATIC_COMMANDS = [\n { command: \"new\", description: \"Create new session\" },\n { command: \"new_chat\", description: \"New chat, same agent & workspace\" },\n { command: \"cancel\", description: \"Cancel current session\" },\n { command: \"status\", description: \"Show status\" },\n { command: \"agents\", description: \"List available agents\" },\n { command: \"help\", description: \"Help\" },\n { command: \"menu\", description: \"Show menu\" },\n];\n","import type { Bot } from 'grammy'\nimport { InlineKeyboard } from 'grammy'\nimport { nanoid } from 'nanoid'\nimport type { PermissionRequest, NotificationMessage, Session } from '../../core/index.js'\nimport { escapeHtml } from './formatting.js'\nimport { buildDeepLink } from './topics.js'\nimport { createChildLogger } from '../../core/log.js'\nconst log = createChildLogger({ module: 'telegram-permissions' })\n\n// Stored pending permission callbacks: callbackKey → { sessionId, requestId, options }\ninterface PendingPermission {\n sessionId: string\n requestId: string\n options: { id: string; isAllow: boolean }[]\n}\n\nexport class PermissionHandler {\n private pending: Map<string, PendingPermission> = new Map()\n\n constructor(\n private bot: Bot,\n private chatId: number,\n private getSession: (sessionId: string) => Session | undefined,\n private sendNotification: (notification: NotificationMessage) => Promise<void>,\n ) {}\n\n async sendPermissionRequest(session: Session, request: PermissionRequest): Promise<void> {\n const threadId = Number(session.threadId)\n\n // Short callback key (Telegram 64-byte limit on callback_data)\n const callbackKey = nanoid(8)\n this.pending.set(callbackKey, {\n sessionId: session.id,\n requestId: request.id,\n options: request.options.map(o => ({ id: o.id, isAllow: o.isAllow })),\n })\n\n // Build inline keyboard\n const keyboard = new InlineKeyboard()\n for (const option of request.options) {\n const emoji = option.isAllow ? '✅' : '❌'\n keyboard.text(`${emoji} ${option.label}`, `p:${callbackKey}:${option.id}`)\n }\n\n // Send in session topic WITH notification\n const msg = await this.bot.api.sendMessage(this.chatId,\n `🔐 <b>Permission request:</b>\\n\\n${escapeHtml(request.description)}`,\n {\n message_thread_id: threadId,\n parse_mode: 'HTML',\n reply_markup: keyboard,\n disable_notification: false,\n }\n )\n\n // Deep link for notification\n const deepLink = buildDeepLink(this.chatId, msg.message_id)\n\n // Notify in notification topic\n await this.sendNotification({\n sessionId: session.id,\n sessionName: session.name,\n type: 'permission',\n summary: request.description,\n deepLink,\n })\n }\n\n setupCallbackHandler(): void {\n this.bot.on('callback_query:data', async (ctx) => {\n const data = ctx.callbackQuery.data\n if (!data.startsWith('p:')) return\n\n const parts = data.split(':')\n if (parts.length < 3) return\n const [, callbackKey, optionId] = parts\n\n const pending = this.pending.get(callbackKey)\n if (!pending) {\n try { await ctx.answerCallbackQuery({ text: '❌ Expired' }) } catch { /* old query */ }\n return\n }\n\n const session = this.getSession(pending.sessionId)\n const isAllow = pending.options.find(o => o.id === optionId)?.isAllow ?? false\n log.info({ requestId: pending.requestId, optionId, isAllow }, 'Permission responded')\n if (session?.pendingPermission?.requestId === pending.requestId) {\n session.pendingPermission.resolve(optionId)\n session.pendingPermission = undefined\n }\n this.pending.delete(callbackKey)\n\n try { await ctx.answerCallbackQuery({ text: '✅ Responded' }) } catch { /* old query */ }\n\n // Remove buttons\n try {\n await ctx.editMessageReplyMarkup({ reply_markup: undefined })\n } catch { /* ignore */ }\n })\n }\n}\n","import type { OpenACPCore, ChannelAdapter, Config, Session } from \"../../core/index.js\";\n\nexport async function spawnAssistant(\n core: OpenACPCore,\n adapter: ChannelAdapter,\n assistantTopicId: number,\n): Promise<Session> {\n const config = core.configManager.get();\n\n // Create session with default agent\n const session = await core.sessionManager.createSession(\n \"telegram\",\n config.defaultAgent,\n core.configManager.resolveWorkspace(),\n core.agentManager,\n );\n session.threadId = String(assistantTopicId);\n\n // Send system prompt BEFORE wiring events. enqueuePrompt awaits the full\n // agent response, so by the time it returns the system prompt conversation\n // is complete. Since no event handler is wired yet, the response is\n // intentionally discarded — users only see responses to their own messages.\n const systemPrompt = buildAssistantSystemPrompt(config);\n await session.enqueuePrompt(systemPrompt);\n\n // Wire events to adapter — only messages after this point reach the user\n core.wireSessionEvents(session, adapter);\n\n return session;\n}\n\nexport function buildAssistantSystemPrompt(config: Config): string {\n const agentNames = Object.keys(config.agents).join(\", \");\n return `You are the OpenACP Assistant. Help users manage their AI coding sessions.\n\nAvailable agents: ${agentNames}\nDefault agent: ${config.defaultAgent}\nWorkspace base: ${config.workspace.baseDir}\n\nWhen a user wants to create a session, guide them through:\n1. Which agent to use\n2. Which workspace/project\n3. Confirm and create\n\nCommands reference:\n- /new [agent] [workspace] — Create new session\n- /new_chat — New chat with same agent & workspace\n- /cancel — Cancel current session\n- /status — Show status\n- /agents — List agents\n- /help — Show help\n\nBe concise and helpful. When the user confirms session creation, tell them you'll create it now.`;\n}\n\nexport async function handleAssistantMessage(\n session: Session | null,\n text: string,\n): Promise<void> {\n if (!session) return;\n await session.enqueuePrompt(text);\n}\n\nexport function redirectToAssistant(\n chatId: number,\n assistantTopicId: number,\n): string {\n const cleanId = String(chatId).replace(\"-100\", \"\");\n const link = `https://t.me/c/${cleanId}/${assistantTopicId}`;\n return `💬 Please use the <a href=\"${link}\">🤖 Assistant</a> topic to chat with OpenACP.`;\n}\n"],"mappings":"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","names":["path","fs","path","log","path","fs","log","path","id","nanoid","log","InlineKeyboard","nanoid","log","nanoid","InlineKeyboard","log"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/core/config.ts","../../src/core/plugin-manager.ts"],"sourcesContent":["import { z } from \"zod\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { createChildLogger } from \"./log.js\";\nconst log = createChildLogger({ module: \"config\" });\n\nconst BaseChannelSchema = z\n .object({\n enabled: z.boolean().default(false),\n adapter: z.string().optional(), // package name for plugin adapters\n })\n .passthrough();\n\nexport const PLUGINS_DIR = path.join(os.homedir(), \".openacp\", \"plugins\");\n\nconst AgentSchema = z.object({\n command: z.string(),\n args: z.array(z.string()).default([]),\n workingDirectory: z.string().optional(),\n env: z.record(z.string(), z.string()).default({}),\n});\n\nconst LoggingSchema = z\n .object({\n level: z\n .enum([\"silent\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"])\n .default(\"info\"),\n logDir: z.string().default(\"~/.openacp/logs\"),\n maxFileSize: z.union([z.string(), z.number()]).default(\"10m\"),\n maxFiles: z.number().default(7),\n sessionLogRetentionDays: z.number().default(30),\n })\n .default({});\n\nexport type LoggingConfig = z.infer<typeof LoggingSchema>;\n\nconst TunnelAuthSchema = z\n .object({\n enabled: z.boolean().default(false),\n token: z.string().optional(),\n })\n .default({});\n\nconst TunnelSchema = z\n .object({\n enabled: z.boolean().default(false),\n port: z.number().default(3100),\n provider: z.enum([\"cloudflare\", \"ngrok\", \"bore\", \"tailscale\"]).default(\"cloudflare\"),\n options: z.record(z.string(), z.unknown()).default({}),\n storeTtlMinutes: z.number().default(60),\n auth: TunnelAuthSchema,\n })\n .default({});\n\nexport type TunnelConfig = z.infer<typeof TunnelSchema>;\n\nexport const ConfigSchema = z.object({\n channels: z.record(z.string(), BaseChannelSchema),\n agents: z.record(z.string(), AgentSchema),\n defaultAgent: z.string(),\n workspace: z\n .object({\n baseDir: z.string().default(\"~/openacp-workspace\"),\n })\n .default({}),\n security: z\n .object({\n allowedUserIds: z.array(z.string()).default([]),\n maxConcurrentSessions: z.number().default(5),\n sessionTimeoutMinutes: z.number().default(60),\n })\n .default({}),\n logging: LoggingSchema,\n sessionStore: z\n .object({\n ttlDays: z.number().default(30),\n })\n .default({}),\n tunnel: TunnelSchema,\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\nexport function expandHome(p: string): string {\n if (p.startsWith(\"~\")) {\n return path.join(os.homedir(), p.slice(1));\n }\n return p;\n}\n\nconst DEFAULT_CONFIG = {\n channels: {\n telegram: {\n enabled: false,\n botToken: \"YOUR_BOT_TOKEN_HERE\",\n chatId: 0,\n notificationTopicId: null,\n assistantTopicId: null,\n },\n },\n agents: {\n claude: { command: \"claude-agent-acp\", args: [], env: {} },\n codex: { command: \"codex\", args: [\"--acp\"], env: {} },\n },\n defaultAgent: \"claude\",\n workspace: { baseDir: \"~/openacp-workspace\" },\n security: {\n allowedUserIds: [],\n maxConcurrentSessions: 5,\n sessionTimeoutMinutes: 60,\n },\n sessionStore: { ttlDays: 30 },\n tunnel: {\n enabled: true,\n port: 3100,\n provider: \"cloudflare\",\n options: {},\n storeTtlMinutes: 60,\n auth: { enabled: false },\n },\n};\n\nexport class ConfigManager {\n private config!: Config;\n private configPath: string;\n\n constructor() {\n this.configPath =\n process.env.OPENACP_CONFIG_PATH || expandHome(\"~/.openacp/config.json\");\n }\n\n async load(): Promise<void> {\n // 1. Ensure directory exists\n const dir = path.dirname(this.configPath);\n fs.mkdirSync(dir, { recursive: true });\n\n // 2. If config file doesn't exist, create default\n if (!fs.existsSync(this.configPath)) {\n fs.writeFileSync(\n this.configPath,\n JSON.stringify(DEFAULT_CONFIG, null, 2),\n );\n log.info({ configPath: this.configPath }, \"Config created\");\n log.info(\n \"Please edit it with your Telegram bot token and chat ID, then restart.\",\n );\n process.exit(1);\n }\n\n // 3. Read and parse\n const raw = JSON.parse(fs.readFileSync(this.configPath, \"utf-8\"));\n\n // 3.5. Auto-migrate: add missing sections with defaults\n let configUpdated = false;\n if (!raw.tunnel) {\n raw.tunnel = {\n enabled: true,\n port: 3100,\n provider: \"cloudflare\",\n options: {},\n storeTtlMinutes: 60,\n auth: { enabled: false },\n };\n configUpdated = true;\n log.info(\"Added tunnel section to config (enabled by default with cloudflare)\");\n }\n if (configUpdated) {\n fs.writeFileSync(this.configPath, JSON.stringify(raw, null, 2));\n }\n\n // 4. Apply env var overrides\n this.applyEnvOverrides(raw);\n\n // 5. Validate with Zod\n const result = ConfigSchema.safeParse(raw);\n if (!result.success) {\n log.error(\"Config validation failed\");\n for (const issue of result.error.issues) {\n log.error(\n { path: issue.path.join(\".\"), message: issue.message },\n \"Validation error\",\n );\n }\n process.exit(1);\n }\n this.config = result.data;\n }\n\n get(): Config {\n return this.config;\n }\n\n async save(updates: Record<string, unknown>): Promise<void> {\n // Read current file, merge updates, write back\n const raw = JSON.parse(fs.readFileSync(this.configPath, \"utf-8\"));\n this.deepMerge(raw, updates);\n fs.writeFileSync(this.configPath, JSON.stringify(raw, null, 2));\n // Re-validate and update in-memory config\n const result = ConfigSchema.safeParse(raw);\n if (result.success) {\n this.config = result.data;\n }\n }\n\n resolveWorkspace(input?: string): string {\n if (!input) {\n const resolved = expandHome(this.config.workspace.baseDir);\n fs.mkdirSync(resolved, { recursive: true });\n return resolved;\n }\n if (input.startsWith(\"/\") || input.startsWith(\"~\")) {\n const resolved = expandHome(input);\n fs.mkdirSync(resolved, { recursive: true });\n return resolved;\n }\n // Named workspace → lowercase, under baseDir\n const name = input.toLowerCase();\n const resolved = path.join(expandHome(this.config.workspace.baseDir), name);\n fs.mkdirSync(resolved, { recursive: true });\n return resolved;\n }\n\n async exists(): Promise<boolean> {\n return fs.existsSync(this.configPath);\n }\n\n getConfigPath(): string {\n return this.configPath;\n }\n\n async writeNew(config: Config): Promise<void> {\n const dir = path.dirname(this.configPath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2));\n }\n\n private applyEnvOverrides(raw: Record<string, unknown>): void {\n const overrides: [string, string[]][] = [\n [\"OPENACP_TELEGRAM_BOT_TOKEN\", [\"channels\", \"telegram\", \"botToken\"]],\n [\"OPENACP_TELEGRAM_CHAT_ID\", [\"channels\", \"telegram\", \"chatId\"]],\n [\"OPENACP_DEFAULT_AGENT\", [\"defaultAgent\"]],\n ];\n for (const [envVar, configPath] of overrides) {\n const value = process.env[envVar];\n if (value !== undefined) {\n let target = raw as Record<string, any>;\n for (let i = 0; i < configPath.length - 1; i++) {\n if (!target[configPath[i]]) target[configPath[i]] = {};\n target = target[configPath[i]];\n }\n const key = configPath[configPath.length - 1];\n // Convert chatId to number\n target[key] = key === \"chatId\" ? Number(value) : value;\n }\n }\n\n // Logging env var overrides\n if (process.env.OPENACP_LOG_LEVEL) {\n raw.logging = raw.logging || {};\n (raw.logging as Record<string, unknown>).level =\n process.env.OPENACP_LOG_LEVEL;\n }\n if (process.env.OPENACP_LOG_DIR) {\n raw.logging = raw.logging || {};\n (raw.logging as Record<string, unknown>).logDir =\n process.env.OPENACP_LOG_DIR;\n }\n if (process.env.OPENACP_DEBUG && !process.env.OPENACP_LOG_LEVEL) {\n raw.logging = raw.logging || {};\n (raw.logging as Record<string, unknown>).level = \"debug\";\n }\n\n // Tunnel env var overrides\n if (process.env.OPENACP_TUNNEL_ENABLED) {\n raw.tunnel = raw.tunnel || {};\n (raw.tunnel as Record<string, unknown>).enabled =\n process.env.OPENACP_TUNNEL_ENABLED === \"true\";\n }\n if (process.env.OPENACP_TUNNEL_PORT) {\n raw.tunnel = raw.tunnel || {};\n (raw.tunnel as Record<string, unknown>).port = Number(\n process.env.OPENACP_TUNNEL_PORT,\n );\n }\n if (process.env.OPENACP_TUNNEL_PROVIDER) {\n raw.tunnel = raw.tunnel || {};\n (raw.tunnel as Record<string, unknown>).provider =\n process.env.OPENACP_TUNNEL_PROVIDER;\n }\n }\n\n private deepMerge(\n target: Record<string, any>,\n source: Record<string, any>,\n ): void {\n for (const key of Object.keys(source)) {\n if (\n source[key] &&\n typeof source[key] === \"object\" &&\n !Array.isArray(source[key])\n ) {\n if (!target[key]) target[key] = {};\n this.deepMerge(target[key], source[key]);\n } else {\n target[key] = source[key];\n }\n }\n }\n}\n","import { execSync } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { createRequire } from 'node:module'\nimport { PLUGINS_DIR } from './config.js'\nimport { createChildLogger } from './log.js'\nconst log = createChildLogger({ module: 'plugin-manager' })\nimport type { ChannelAdapter } from './channel.js'\n\nexport interface AdapterFactory {\n name: string\n createAdapter(core: any, config: any): ChannelAdapter\n}\n\nfunction ensurePluginsDir(): void {\n fs.mkdirSync(PLUGINS_DIR, { recursive: true })\n const pkgPath = path.join(PLUGINS_DIR, 'package.json')\n if (!fs.existsSync(pkgPath)) {\n fs.writeFileSync(pkgPath, JSON.stringify({ name: 'openacp-plugins', private: true, dependencies: {} }, null, 2))\n }\n}\n\nexport function installPlugin(packageName: string): void {\n ensurePluginsDir()\n log.info({ packageName }, 'Installing plugin')\n execSync(`npm install ${packageName} --prefix \"${PLUGINS_DIR}\"`, { stdio: 'inherit' })\n log.info({ packageName }, 'Plugin installed successfully')\n}\n\nexport function uninstallPlugin(packageName: string): void {\n ensurePluginsDir()\n log.info({ packageName }, 'Uninstalling plugin')\n execSync(`npm uninstall ${packageName} --prefix \"${PLUGINS_DIR}\"`, { stdio: 'inherit' })\n log.info({ packageName }, 'Plugin uninstalled')\n}\n\nexport function listPlugins(): Record<string, string> {\n const pkgPath = path.join(PLUGINS_DIR, 'package.json')\n if (!fs.existsSync(pkgPath)) return {}\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n return pkg.dependencies || {}\n}\n\nexport async function loadAdapterFactory(packageName: string): Promise<AdapterFactory | null> {\n try {\n const require = createRequire(path.join(PLUGINS_DIR, 'package.json'))\n const resolved = require.resolve(packageName)\n const mod = await import(resolved)\n\n // Plugin must export `adapterFactory` or default export conforming to AdapterFactory\n const factory: AdapterFactory | undefined = mod.adapterFactory || mod.default\n if (!factory || typeof factory.createAdapter !== 'function') {\n log.error({ packageName }, 'Plugin does not export a valid AdapterFactory (needs .createAdapter())')\n return null\n }\n return factory\n } catch (err) {\n log.error({ packageName, err }, 'Failed to load plugin')\n log.error({ packageName }, 'Run: npx openacp install <packageName>')\n return null\n }\n}\n"],"mappings":"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","names":["resolved","fs","path","log","require"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/main.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { ConfigManager } from './core/config.js'\nimport { OpenACPCore } from './core/core.js'\nimport { loadAdapterFactory } from './core/plugin-manager.js'\nimport { initLogger, shutdownLogger, cleanupOldSessionLogs, log } from './core/log.js'\nimport { TelegramAdapter } from './adapters/telegram/index.js'\n\nlet shuttingDown = false\n\nexport async function startServer() {\n // 1. Check config exists, run setup if not\n const configManager = new ConfigManager()\n const configExists = await configManager.exists()\n\n if (!configExists) {\n const { runSetup } = await import('./core/setup.js')\n const shouldStart = await runSetup(configManager)\n if (!shouldStart) process.exit(0)\n }\n\n // 2. Load config (validates with Zod)\n await configManager.load()\n const config = configManager.get()\n initLogger(config.logging)\n log.info({ configPath: configManager.getConfigPath() }, 'Config loaded')\n\n // Async cleanup of old session logs (non-blocking)\n cleanupOldSessionLogs(config.logging.sessionLogRetentionDays).catch(err =>\n log.warn({ err }, 'Session log cleanup failed')\n )\n\n // 3. Create core\n const core = new OpenACPCore(configManager)\n\n // 3.5 Start tunnel if configured\n let tunnelService: import('./tunnel/tunnel-service.js').TunnelService | undefined\n if (config.tunnel.enabled) {\n const { TunnelService } = await import('./tunnel/tunnel-service.js')\n tunnelService = new TunnelService(config.tunnel)\n const publicUrl = await tunnelService.start()\n core.tunnelService = tunnelService\n log.info({ publicUrl }, 'Tunnel started')\n }\n\n // 4. Register adapters from config\n for (const [channelName, channelConfig] of Object.entries(config.channels)) {\n if (!channelConfig.enabled) continue\n\n if (channelName === 'telegram') {\n core.registerAdapter('telegram', new TelegramAdapter(core, channelConfig as any))\n log.info({ adapter: 'telegram' }, 'Adapter registered')\n } else if (channelConfig.adapter) {\n // Plugin adapter\n const factory = await loadAdapterFactory(channelConfig.adapter)\n if (factory) {\n const adapter = factory.createAdapter(core, channelConfig)\n core.registerAdapter(channelName, adapter)\n log.info({ adapter: channelName, plugin: channelConfig.adapter }, 'Adapter registered')\n } else {\n const name = channelName\n const err = channelConfig.adapter\n log.error({ adapter: name, err }, 'Failed to load adapter')\n }\n } else {\n log.error({ adapter: channelName }, 'Channel has no built-in adapter; set \"adapter\" field to a plugin package')\n }\n }\n\n if (core.adapters.size === 0) {\n log.error('No channels enabled. Enable at least one channel in config.')\n process.exit(1)\n }\n\n // 5. Start\n await core.start()\n\n // 6. Log ready\n const agents = Object.keys(config.agents)\n log.info({ agents }, 'OpenACP started')\n log.info('Press Ctrl+C to stop')\n\n // 7. Graceful shutdown\n const shutdown = async (signal: string) => {\n if (shuttingDown) return\n shuttingDown = true\n log.info({ signal }, 'Signal received, shutting down')\n\n try {\n await core.stop()\n if (tunnelService) await tunnelService.stop()\n } catch (err) {\n log.error({ err }, 'Error during shutdown')\n }\n\n await shutdownLogger()\n process.exit(0)\n }\n\n process.on('SIGINT', () => shutdown('SIGINT'))\n process.on('SIGTERM', () => shutdown('SIGTERM'))\n\n process.on('uncaughtException', (err) => {\n log.error({ err }, 'Uncaught exception')\n })\n\n process.on('unhandledRejection', (err) => {\n log.error({ err }, 'Unhandled rejection')\n })\n}\n\n// Direct execution for dev (node dist/main.js)\nconst isDirectExecution = process.argv[1]?.endsWith('main.js')\nif (isDirectExecution) {\n startServer().catch((err) => {\n log.error({ err }, 'Fatal error')\n process.exit(1)\n })\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/core/setup.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { input, select } from \"@inquirer/prompts\";\nimport type { Config, ConfigManager } from \"./config.js\";\n\n// --- ANSI colors ---\n\nconst c = {\n reset: \"\\x1b[0m\",\n bold: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n red: \"\\x1b[31m\",\n cyan: \"\\x1b[36m\",\n white: \"\\x1b[37m\",\n};\n\nconst ok = (msg: string) =>\n `${c.green}${c.bold}✓${c.reset} ${c.green}${msg}${c.reset}`;\nconst warn = (msg: string) => `${c.yellow}⚠ ${msg}${c.reset}`;\nconst fail = (msg: string) => `${c.red}✗ ${msg}${c.reset}`;\nconst step = (n: number, title: string) =>\n `\\n${c.cyan}${c.bold}[${n}/3]${c.reset} ${c.bold}${title}${c.reset}\\n`;\nconst dim = (msg: string) => `${c.dim}${msg}${c.reset}`;\n\n// --- Telegram validation ---\n\nexport async function validateBotToken(\n token: string,\n): Promise<\n | { ok: true; botName: string; botUsername: string }\n | { ok: false; error: string }\n> {\n try {\n const res = await fetch(`https://api.telegram.org/bot${token}/getMe`);\n const data = (await res.json()) as {\n ok: boolean;\n result?: { first_name: string; username: string };\n description?: string;\n };\n if (data.ok && data.result) {\n return {\n ok: true,\n botName: data.result.first_name,\n botUsername: data.result.username,\n };\n }\n return { ok: false, error: data.description || \"Invalid token\" };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n}\n\nexport async function validateChatId(\n token: string,\n chatId: number,\n): Promise<\n { ok: true; title: string; isForum: boolean } | { ok: false; error: string }\n> {\n try {\n const res = await fetch(`https://api.telegram.org/bot${token}/getChat`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ chat_id: chatId }),\n });\n const data = (await res.json()) as {\n ok: boolean;\n result?: { title: string; type: string; is_forum?: boolean };\n description?: string;\n };\n if (!data.ok || !data.result) {\n return { ok: false, error: data.description || \"Invalid chat ID\" };\n }\n if (data.result.type !== \"supergroup\") {\n return {\n ok: false,\n error: `Chat is \"${data.result.type}\", must be a supergroup`,\n };\n }\n return {\n ok: true,\n title: data.result.title,\n isForum: data.result.is_forum === true,\n };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n}\n\n// --- Chat ID auto-detection ---\n\nfunction promptManualChatId(): Promise<number> {\n return input({\n message: \"Supergroup chat ID (e.g. -1001234567890):\",\n validate: (val) => {\n const n = Number(val.trim());\n if (isNaN(n) || !Number.isInteger(n)) return \"Chat ID must be an integer\";\n return true;\n },\n }).then((val) => Number(val.trim()));\n}\n\nasync function detectChatId(token: string): Promise<number> {\n // Clear old updates\n let lastUpdateId = 0;\n try {\n const clearRes = await fetch(\n `https://api.telegram.org/bot${token}/getUpdates?offset=-1`,\n );\n const clearData = (await clearRes.json()) as {\n ok: boolean;\n result?: Array<{ update_id: number }>;\n };\n if (clearData.ok && clearData.result?.length) {\n lastUpdateId = clearData.result[clearData.result.length - 1].update_id;\n }\n } catch {\n // ignore\n }\n\n console.log(\"\");\n console.log(` ${c.bold}If you don't have a supergroup yet:${c.reset}`);\n console.log(dim(\" 1. Open Telegram → New Group → add your bot\"));\n console.log(dim(\" 2. Group Settings → convert to Supergroup\"));\n console.log(dim(\" 3. Enable Topics in group settings\"));\n console.log(\"\");\n console.log(` ${c.bold}Then send \"hi\" in the group.${c.reset}`);\n console.log(\n dim(\n ` Listening... press ${c.reset}${c.yellow}m${c.reset}${c.dim} to enter ID manually`,\n ),\n );\n console.log(\"\");\n\n const MAX_ATTEMPTS = 120;\n const POLL_INTERVAL = 1000;\n\n // Listen for 'm' keypress to switch to manual\n let cancelled = false;\n const onKeypress = (data: Buffer) => {\n const key = data.toString();\n if (key === \"m\" || key === \"M\") {\n cancelled = true;\n }\n };\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n process.stdin.resume();\n process.stdin.on(\"data\", onKeypress);\n }\n\n const cleanup = () => {\n if (process.stdin.isTTY) {\n process.stdin.removeListener(\"data\", onKeypress);\n process.stdin.setRawMode(false);\n process.stdin.pause();\n }\n };\n\n try {\n for (let i = 0; i < MAX_ATTEMPTS; i++) {\n if (cancelled) {\n cleanup();\n return promptManualChatId();\n }\n\n try {\n const offset = lastUpdateId ? lastUpdateId + 1 : 0;\n const res = await fetch(\n `https://api.telegram.org/bot${token}/getUpdates?offset=${offset}&timeout=1`,\n );\n const data = (await res.json()) as {\n ok: boolean;\n result?: Array<{\n update_id: number;\n message?: {\n chat: { id: number; title?: string; type: string };\n };\n my_chat_member?: {\n chat: { id: number; title?: string; type: string };\n };\n }>;\n };\n\n if (!data.ok || !data.result?.length) {\n await new Promise((r) => setTimeout(r, POLL_INTERVAL));\n continue;\n }\n\n const groups = new Map<number, string>();\n for (const update of data.result) {\n lastUpdateId = update.update_id;\n const chat = update.message?.chat ?? update.my_chat_member?.chat;\n if (chat && (chat.type === \"supergroup\" || chat.type === \"group\")) {\n groups.set(chat.id, chat.title ?? String(chat.id));\n }\n }\n\n if (groups.size === 1) {\n const [id, title] = [...groups.entries()][0];\n console.log(\n ok(`Group detected: ${c.bold}${title}${c.reset}${c.green} (${id})`),\n );\n cleanup();\n return id;\n }\n\n if (groups.size > 1) {\n cleanup();\n const choices = [...groups.entries()].map(([id, title]) => ({\n name: `${title} (${id})`,\n value: id,\n }));\n return select({\n message: \"Multiple groups found. Pick one:\",\n choices,\n });\n }\n } catch {\n // Network error, retry\n }\n await new Promise((r) => setTimeout(r, POLL_INTERVAL));\n }\n\n console.log(warn(\"Timed out waiting for messages.\"));\n cleanup();\n return promptManualChatId();\n } catch (err) {\n cleanup();\n throw err;\n }\n}\n\n// --- Agent detection ---\n\nconst KNOWN_AGENTS: Array<{ name: string; commands: string[] }> = [\n { name: \"claude\", commands: [\"claude-agent-acp\", \"claude-code\", \"claude\"] },\n { name: \"codex\", commands: [\"codex\"] },\n];\n\nfunction commandExists(cmd: string): boolean {\n try {\n execFileSync(\"which\", [cmd], { stdio: \"pipe\" });\n return true;\n } catch {\n // not in PATH\n }\n // Check node_modules/.bin (walks up from cwd)\n let dir = process.cwd();\n while (true) {\n const binPath = path.join(dir, \"node_modules\", \".bin\", cmd);\n if (fs.existsSync(binPath)) return true;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return false;\n}\n\nexport async function detectAgents(): Promise<\n Array<{ name: string; command: string }>\n> {\n const found: Array<{ name: string; command: string }> = [];\n for (const agent of KNOWN_AGENTS) {\n // Find all available commands for this agent (PATH + node_modules/.bin)\n const available: string[] = [];\n for (const cmd of agent.commands) {\n if (commandExists(cmd)) {\n available.push(cmd);\n }\n }\n if (available.length > 0) {\n // Prefer claude-agent-acp over claude/claude-code (priority order)\n found.push({ name: agent.name, command: available[0] });\n }\n }\n return found;\n}\n\nexport async function validateAgentCommand(command: string): Promise<boolean> {\n try {\n execFileSync(\"which\", [command], { stdio: \"pipe\" });\n return true;\n } catch {\n return false;\n }\n}\n\n// --- Setup steps ---\n\nexport async function setupTelegram(): Promise<Config[\"channels\"][string]> {\n console.log(step(1, \"Telegram Bot\"));\n\n let botToken = \"\";\n\n while (true) {\n botToken = await input({\n message: \"Bot token (from @BotFather):\",\n validate: (val) => val.trim().length > 0 || \"Token cannot be empty\",\n });\n botToken = botToken.trim();\n\n const result = await validateBotToken(botToken);\n if (result.ok) {\n console.log(ok(`Connected to @${result.botUsername}`));\n break;\n }\n console.log(fail(result.error));\n const action = await select({\n message: \"What to do?\",\n choices: [\n { name: \"Re-enter token\", value: \"retry\" },\n { name: \"Use as-is (skip validation)\", value: \"skip\" },\n ],\n });\n if (action === \"skip\") break;\n }\n\n console.log(step(2, \"Group Chat\"));\n\n const chatId = await detectChatId(botToken);\n\n return {\n enabled: true,\n botToken,\n chatId,\n notificationTopicId: null,\n assistantTopicId: null,\n };\n}\n\nexport async function setupAgents(): Promise<{\n agents: Config[\"agents\"];\n defaultAgent: string;\n}> {\n const detected = await detectAgents();\n const agents: Config[\"agents\"] = {};\n\n if (detected.length > 0) {\n for (const agent of detected) {\n agents[agent.name] = { command: agent.command, args: [], env: {} };\n }\n } else {\n agents[\"claude\"] = { command: \"claude-agent-acp\", args: [], env: {} };\n }\n\n const defaultAgent = Object.keys(agents)[0];\n const agentCmd = agents[defaultAgent].command;\n console.log(\n ok(`Agent: ${c.bold}${defaultAgent}${c.reset}${c.green} (${agentCmd})`),\n );\n\n return { agents, defaultAgent };\n}\n\nexport async function setupWorkspace(): Promise<{ baseDir: string }> {\n console.log(step(3, \"Workspace\"));\n\n const baseDir = await input({\n message: \"Base directory for workspaces:\",\n default: \"~/openacp-workspace\",\n validate: (val) => val.trim().length > 0 || \"Path cannot be empty\",\n });\n\n return { baseDir: baseDir.trim().replace(/^['\"]|['\"]$/g, \"\") };\n}\n\n// --- Orchestrator ---\n\nfunction printWelcomeBanner(): void {\n console.log(`\n${c.cyan}${c.bold} ╔══════════════════════════════╗\n ║ Welcome to OpenACP ║\n ╚══════════════════════════════╝${c.reset}\n`);\n}\n\nexport async function runSetup(configManager: ConfigManager): Promise<boolean> {\n printWelcomeBanner();\n\n try {\n const telegram = await setupTelegram();\n const { agents, defaultAgent } = await setupAgents();\n const workspace = await setupWorkspace();\n const security = {\n allowedUserIds: [] as string[],\n maxConcurrentSessions: 5,\n sessionTimeoutMinutes: 60,\n };\n\n const config: Config = {\n channels: { telegram },\n agents,\n defaultAgent,\n workspace,\n security,\n logging: {\n level: \"info\",\n logDir: \"~/.openacp/logs\",\n maxFileSize: \"10m\",\n maxFiles: 7,\n sessionLogRetentionDays: 30,\n },\n sessionStore: { ttlDays: 30 },\n tunnel: {\n enabled: true,\n port: 3100,\n provider: \"cloudflare\",\n options: {},\n storeTtlMinutes: 60,\n auth: { enabled: false },\n },\n };\n\n try {\n await configManager.writeNew(config);\n } catch (writeErr) {\n console.log(\n fail(`Could not save config: ${(writeErr as Error).message}`),\n );\n return false;\n }\n\n console.log(\"\");\n console.log(\n ok(`Config saved to ${c.bold}${configManager.getConfigPath()}`),\n );\n console.log(ok(\"Starting OpenACP...\"));\n console.log(\"\");\n\n return true;\n } catch (err) {\n if ((err as Error).name === \"ExitPromptError\") {\n console.log(dim(\"\\nSetup cancelled.\"));\n return false;\n }\n throw err;\n }\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AAC7B,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,OAAO,cAAc;AAK9B,IAAM,IAAI;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,KAAK;AAAA,EACL,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,MAAM;AAAA,EACN,OAAO;AACT;AAEA,IAAM,KAAK,CAAC,QACV,GAAG,EAAE,KAAK,GAAG,EAAE,IAAI,SAAI,EAAE,KAAK,IAAI,EAAE,KAAK,GAAG,GAAG,GAAG,EAAE,KAAK;AAC3D,IAAM,OAAO,CAAC,QAAgB,GAAG,EAAE,MAAM,UAAK,GAAG,GAAG,EAAE,KAAK;AAC3D,IAAM,OAAO,CAAC,QAAgB,GAAG,EAAE,GAAG,UAAK,GAAG,GAAG,EAAE,KAAK;AACxD,IAAM,OAAO,CAAC,GAAW,UACvB;AAAA,EAAK,EAAE,IAAI,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,IAAI,GAAG,KAAK,GAAG,EAAE,KAAK;AAAA;AACpE,IAAM,MAAM,CAAC,QAAgB,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,EAAE,KAAK;AAIrD,eAAsB,iBACpB,OAIA;AACA,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,+BAA+B,KAAK,QAAQ;AACpE,UAAM,OAAQ,MAAM,IAAI,KAAK;AAK7B,QAAI,KAAK,MAAM,KAAK,QAAQ;AAC1B,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,SAAS,KAAK,OAAO;AAAA,QACrB,aAAa,KAAK,OAAO;AAAA,MAC3B;AAAA,IACF;AACA,WAAO,EAAE,IAAI,OAAO,OAAO,KAAK,eAAe,gBAAgB;AAAA,EACjE,SAAS,KAAK;AACZ,WAAO,EAAE,IAAI,OAAO,OAAQ,IAAc,QAAQ;AAAA,EACpD;AACF;AAEA,eAAsB,eACpB,OACA,QAGA;AACA,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,+BAA+B,KAAK,YAAY;AAAA,MACtE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,SAAS,OAAO,CAAC;AAAA,IAC1C,CAAC;AACD,UAAM,OAAQ,MAAM,IAAI,KAAK;AAK7B,QAAI,CAAC,KAAK,MAAM,CAAC,KAAK,QAAQ;AAC5B,aAAO,EAAE,IAAI,OAAO,OAAO,KAAK,eAAe,kBAAkB;AAAA,IACnE;AACA,QAAI,KAAK,OAAO,SAAS,cAAc;AACrC,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,OAAO,YAAY,KAAK,OAAO,IAAI;AAAA,MACrC;AAAA,IACF;AACA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,OAAO,KAAK,OAAO;AAAA,MACnB,SAAS,KAAK,OAAO,aAAa;AAAA,IACpC;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,EAAE,IAAI,OAAO,OAAQ,IAAc,QAAQ;AAAA,EACpD;AACF;AAIA,SAAS,qBAAsC;AAC7C,SAAO,MAAM;AAAA,IACX,SAAS;AAAA,IACT,UAAU,CAAC,QAAQ;AACjB,YAAM,IAAI,OAAO,IAAI,KAAK,CAAC;AAC3B,UAAI,MAAM,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,EAAG,QAAO;AAC7C,aAAO;AAAA,IACT;AAAA,EACF,CAAC,EAAE,KAAK,CAAC,QAAQ,OAAO,IAAI,KAAK,CAAC,CAAC;AACrC;AAEA,eAAe,aAAa,OAAgC;AAE1D,MAAI,eAAe;AACnB,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB,+BAA+B,KAAK;AAAA,IACtC;AACA,UAAM,YAAa,MAAM,SAAS,KAAK;AAIvC,QAAI,UAAU,MAAM,UAAU,QAAQ,QAAQ;AAC5C,qBAAe,UAAU,OAAO,UAAU,OAAO,SAAS,CAAC,EAAE;AAAA,IAC/D;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,KAAK,EAAE,IAAI,sCAAsC,EAAE,KAAK,EAAE;AACtE,UAAQ,IAAI,IAAI,yDAA+C,CAAC;AAChE,UAAQ,IAAI,IAAI,kDAA6C,CAAC;AAC9D,UAAQ,IAAI,IAAI,sCAAsC,CAAC;AACvD,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,KAAK,EAAE,IAAI,+BAA+B,EAAE,KAAK,EAAE;AAC/D,UAAQ;AAAA,IACN;AAAA,MACE,wBAAwB,EAAE,KAAK,GAAG,EAAE,MAAM,IAAI,EAAE,KAAK,GAAG,EAAE,GAAG;AAAA,IAC/D;AAAA,EACF;AACA,UAAQ,IAAI,EAAE;AAEd,QAAM,eAAe;AACrB,QAAM,gBAAgB;AAGtB,MAAI,YAAY;AAChB,QAAM,aAAa,CAAC,SAAiB;AACnC,UAAM,MAAM,KAAK,SAAS;AAC1B,QAAI,QAAQ,OAAO,QAAQ,KAAK;AAC9B,kBAAY;AAAA,IACd;AAAA,EACF;AACA,MAAI,QAAQ,MAAM,OAAO;AACvB,YAAQ,MAAM,WAAW,IAAI;AAC7B,YAAQ,MAAM,OAAO;AACrB,YAAQ,MAAM,GAAG,QAAQ,UAAU;AAAA,EACrC;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,QAAQ,MAAM,OAAO;AACvB,cAAQ,MAAM,eAAe,QAAQ,UAAU;AAC/C,cAAQ,MAAM,WAAW,KAAK;AAC9B,cAAQ,MAAM,MAAM;AAAA,IACtB;AAAA,EACF;AAEA,MAAI;AACF,aAAS,IAAI,GAAG,IAAI,cAAc,KAAK;AACrC,UAAI,WAAW;AACb,gBAAQ;AACR,eAAO,mBAAmB;AAAA,MAC5B;AAEA,UAAI;AACF,cAAM,SAAS,eAAe,eAAe,IAAI;AACjD,cAAM,MAAM,MAAM;AAAA,UAChB,+BAA+B,KAAK,sBAAsB,MAAM;AAAA,QAClE;AACA,cAAM,OAAQ,MAAM,IAAI,KAAK;AAa7B,YAAI,CAAC,KAAK,MAAM,CAAC,KAAK,QAAQ,QAAQ;AACpC,gBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;AACrD;AAAA,QACF;AAEA,cAAM,SAAS,oBAAI,IAAoB;AACvC,mBAAW,UAAU,KAAK,QAAQ;AAChC,yBAAe,OAAO;AACtB,gBAAM,OAAO,OAAO,SAAS,QAAQ,OAAO,gBAAgB;AAC5D,cAAI,SAAS,KAAK,SAAS,gBAAgB,KAAK,SAAS,UAAU;AACjE,mBAAO,IAAI,KAAK,IAAI,KAAK,SAAS,OAAO,KAAK,EAAE,CAAC;AAAA,UACnD;AAAA,QACF;AAEA,YAAI,OAAO,SAAS,GAAG;AACrB,gBAAM,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,OAAO,QAAQ,CAAC,EAAE,CAAC;AAC3C,kBAAQ;AAAA,YACN,GAAG,mBAAmB,EAAE,IAAI,GAAG,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,KAAK,KAAK,EAAE,GAAG;AAAA,UACpE;AACA,kBAAQ;AACR,iBAAO;AAAA,QACT;AAEA,YAAI,OAAO,OAAO,GAAG;AACnB,kBAAQ;AACR,gBAAM,UAAU,CAAC,GAAG,OAAO,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;AAAA,YAC1D,MAAM,GAAG,KAAK,KAAK,EAAE;AAAA,YACrB,OAAO;AAAA,UACT,EAAE;AACF,iBAAO,OAAO;AAAA,YACZ,SAAS;AAAA,YACT;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AACA,YAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;AAAA,IACvD;AAEA,YAAQ,IAAI,KAAK,iCAAiC,CAAC;AACnD,YAAQ;AACR,WAAO,mBAAmB;AAAA,EAC5B,SAAS,KAAK;AACZ,YAAQ;AACR,UAAM;AAAA,EACR;AACF;AAIA,IAAM,eAA4D;AAAA,EAChE,EAAE,MAAM,UAAU,UAAU,CAAC,oBAAoB,eAAe,QAAQ,EAAE;AAAA,EAC1E,EAAE,MAAM,SAAS,UAAU,CAAC,OAAO,EAAE;AACvC;AAEA,SAAS,cAAc,KAAsB;AAC3C,MAAI;AACF,iBAAa,SAAS,CAAC,GAAG,GAAG,EAAE,OAAO,OAAO,CAAC;AAC9C,WAAO;AAAA,EACT,QAAQ;AAAA,EAER;AAEA,MAAI,MAAM,QAAQ,IAAI;AACtB,SAAO,MAAM;AACX,UAAM,UAAe,UAAK,KAAK,gBAAgB,QAAQ,GAAG;AAC1D,QAAO,cAAW,OAAO,EAAG,QAAO;AACnC,UAAM,SAAc,aAAQ,GAAG;AAC/B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,eAAsB,eAEpB;AACA,QAAM,QAAkD,CAAC;AACzD,aAAW,SAAS,cAAc;AAEhC,UAAM,YAAsB,CAAC;AAC7B,eAAW,OAAO,MAAM,UAAU;AAChC,UAAI,cAAc,GAAG,GAAG;AACtB,kBAAU,KAAK,GAAG;AAAA,MACpB;AAAA,IACF;AACA,QAAI,UAAU,SAAS,GAAG;AAExB,YAAM,KAAK,EAAE,MAAM,MAAM,MAAM,SAAS,UAAU,CAAC,EAAE,CAAC;AAAA,IACxD;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,qBAAqB,SAAmC;AAC5E,MAAI;AACF,iBAAa,SAAS,CAAC,OAAO,GAAG,EAAE,OAAO,OAAO,CAAC;AAClD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIA,eAAsB,gBAAqD;AACzE,UAAQ,IAAI,KAAK,GAAG,cAAc,CAAC;AAEnC,MAAI,WAAW;AAEf,SAAO,MAAM;AACX,eAAW,MAAM,MAAM;AAAA,MACrB,SAAS;AAAA,MACT,UAAU,CAAC,QAAQ,IAAI,KAAK,EAAE,SAAS,KAAK;AAAA,IAC9C,CAAC;AACD,eAAW,SAAS,KAAK;AAEzB,UAAM,SAAS,MAAM,iBAAiB,QAAQ;AAC9C,QAAI,OAAO,IAAI;AACb,cAAQ,IAAI,GAAG,iBAAiB,OAAO,WAAW,EAAE,CAAC;AACrD;AAAA,IACF;AACA,YAAQ,IAAI,KAAK,OAAO,KAAK,CAAC;AAC9B,UAAM,SAAS,MAAM,OAAO;AAAA,MAC1B,SAAS;AAAA,MACT,SAAS;AAAA,QACP,EAAE,MAAM,kBAAkB,OAAO,QAAQ;AAAA,QACzC,EAAE,MAAM,+BAA+B,OAAO,OAAO;AAAA,MACvD;AAAA,IACF,CAAC;AACD,QAAI,WAAW,OAAQ;AAAA,EACzB;AAEA,UAAQ,IAAI,KAAK,GAAG,YAAY,CAAC;AAEjC,QAAM,SAAS,MAAM,aAAa,QAAQ;AAE1C,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,qBAAqB;AAAA,IACrB,kBAAkB;AAAA,EACpB;AACF;AAEA,eAAsB,cAGnB;AACD,QAAM,WAAW,MAAM,aAAa;AACpC,QAAM,SAA2B,CAAC;AAElC,MAAI,SAAS,SAAS,GAAG;AACvB,eAAW,SAAS,UAAU;AAC5B,aAAO,MAAM,IAAI,IAAI,EAAE,SAAS,MAAM,SAAS,MAAM,CAAC,GAAG,KAAK,CAAC,EAAE;AAAA,IACnE;AAAA,EACF,OAAO;AACL,WAAO,QAAQ,IAAI,EAAE,SAAS,oBAAoB,MAAM,CAAC,GAAG,KAAK,CAAC,EAAE;AAAA,EACtE;AAEA,QAAM,eAAe,OAAO,KAAK,MAAM,EAAE,CAAC;AAC1C,QAAM,WAAW,OAAO,YAAY,EAAE;AACtC,UAAQ;AAAA,IACN,GAAG,UAAU,EAAE,IAAI,GAAG,YAAY,GAAG,EAAE,KAAK,GAAG,EAAE,KAAK,KAAK,QAAQ,GAAG;AAAA,EACxE;AAEA,SAAO,EAAE,QAAQ,aAAa;AAChC;AAEA,eAAsB,iBAA+C;AACnE,UAAQ,IAAI,KAAK,GAAG,WAAW,CAAC;AAEhC,QAAM,UAAU,MAAM,MAAM;AAAA,IAC1B,SAAS;AAAA,IACT,SAAS;AAAA,IACT,UAAU,CAAC,QAAQ,IAAI,KAAK,EAAE,SAAS,KAAK;AAAA,EAC9C,CAAC;AAED,SAAO,EAAE,SAAS,QAAQ,KAAK,EAAE,QAAQ,gBAAgB,EAAE,EAAE;AAC/D;AAIA,SAAS,qBAA2B;AAClC,UAAQ,IAAI;AAAA,EACZ,EAAE,IAAI,GAAG,EAAE,IAAI;AAAA;AAAA,oMAEmB,EAAE,KAAK;AAAA,CAC1C;AACD;AAEA,eAAsB,SAAS,eAAgD;AAC7E,qBAAmB;AAEnB,MAAI;AACF,UAAM,WAAW,MAAM,cAAc;AACrC,UAAM,EAAE,QAAQ,aAAa,IAAI,MAAM,YAAY;AACnD,UAAM,YAAY,MAAM,eAAe;AACvC,UAAM,WAAW;AAAA,MACf,gBAAgB,CAAC;AAAA,MACjB,uBAAuB;AAAA,MACvB,uBAAuB;AAAA,IACzB;AAEA,UAAM,SAAiB;AAAA,MACrB,UAAU,EAAE,SAAS;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,UAAU;AAAA,QACV,yBAAyB;AAAA,MAC3B;AAAA,MACA,cAAc,EAAE,SAAS,GAAG;AAAA,MAC5B,QAAQ;AAAA,QACN,SAAS;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS,CAAC;AAAA,QACV,iBAAiB;AAAA,QACjB,MAAM,EAAE,SAAS,MAAM;AAAA,MACzB;AAAA,IACF;AAEA,QAAI;AACF,YAAM,cAAc,SAAS,MAAM;AAAA,IACrC,SAAS,UAAU;AACjB,cAAQ;AAAA,QACN,KAAK,0BAA2B,SAAmB,OAAO,EAAE;AAAA,MAC9D;AACA,aAAO;AAAA,IACT;AAEA,YAAQ,IAAI,EAAE;AACd,YAAQ;AAAA,MACN,GAAG,mBAAmB,EAAE,IAAI,GAAG,cAAc,cAAc,CAAC,EAAE;AAAA,IAChE;AACA,YAAQ,IAAI,GAAG,qBAAqB,CAAC;AACrC,YAAQ,IAAI,EAAE;AAEd,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAK,IAAc,SAAS,mBAAmB;AAC7C,cAAQ,IAAI,IAAI,oBAAoB,CAAC;AACrC,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;","names":[]}