@n1creator/openacp-cli 2026.712.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +326 -0
- package/dist/channel-Cowirs76.d.ts +816 -0
- package/dist/cli.d.ts +32 -0
- package/dist/cli.js +36438 -0
- package/dist/cli.js.map +1 -0
- package/dist/data/registry-snapshot.json +851 -0
- package/dist/index.d.ts +5204 -0
- package/dist/index.js +21205 -0
- package/dist/index.js.map +1 -0
- package/dist/testing.d.ts +5 -0
- package/dist/testing.js +17749 -0
- package/dist/testing.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/utils/log.ts","../../src/core/config/config-migrations.ts","../../src/core/config/config-registry.ts","../../src/core/config/config.ts","../../src/core/utils/read-text-file.ts","../../src/core/events.ts","../../src/core/utils/apply-patch-detection.ts","../../src/core/utils/bypass-detection.ts","../../src/core/agents/agent-dependencies.ts","../../src/core/agents/agent-registry.ts","../../src/core/agents/agent-installer.ts","../../src/core/doctor/checks/config.ts","../../src/core/doctor/checks/agents.ts","../../src/core/plugin/settings-manager.ts","../../src/core/doctor/checks/telegram.ts","../../src/core/doctor/checks/storage.ts","../../src/core/doctor/checks/workspace.ts","../../src/core/doctor/checks/plugins.ts","../../src/core/doctor/checks/daemon.ts","../../src/core/utils/install-binary.ts","../../src/plugins/tunnel/providers/install-cloudflared.ts","../../src/core/doctor/checks/tunnel.ts","../../src/core/doctor/index.ts","../../src/core/instance/instance-context.ts","../../src/core/instance/instance-registry.ts","../../src/core/plugin/plugin-installer.ts","../../src/core/plugin/terminal-io.ts","../../src/core/plugin/install-context.ts","../../src/cli/api-client.ts","../../src/plugins/notifications/notification.ts","../../src/plugins/file-service/file-service.ts","../../src/plugins/security/security-guard.ts","../../src/plugins/api-server/middleware/error-handler.ts","../../src/plugins/api-server/auth/jwt.ts","../../src/plugins/api-server/auth/roles.ts","../../src/plugins/api-server/middleware/auth.ts","../../src/plugins/api-server/server.ts","../../src/plugins/api-server/service.ts","../../src/plugins/api-server/sse-manager.ts","../../src/plugins/api-server/static-server.ts","../../src/plugins/speech/speech-service.ts","../../src/plugins/speech/providers/groq.ts","../../src/plugins/speech/exports.ts","../../src/plugins/context/context-cache.ts","../../src/plugins/context/context-manager.ts","../../src/plugins/context/context-provider.ts","../../src/plugins/context/entire/checkpoint-reader.ts","../../src/plugins/context/entire/message-cleaner.ts","../../src/plugins/context/entire/conversation-builder.ts","../../src/plugins/context/entire/entire-provider.ts","../../src/core/adapter-primitives/format-types.ts","../../src/core/adapter-primitives/message-formatter.ts","../../src/core/adapter-primitives/messaging-adapter.ts","../../src/core/adapter-primitives/format-utils.ts","../../src/core/adapter-primitives/rendering/renderer.ts","../../src/core/adapter-primitives/primitives/send-queue.ts","../../src/core/adapter-primitives/primitives/tool-card-state.ts","../../src/core/adapter-primitives/stream-accumulator.ts","../../src/core/adapter-primitives/display-spec-builder.ts","../../src/core/adapter-primitives/output-mode-resolver.ts","../../src/plugins/telegram/topics.ts","../../src/plugins/telegram/formatting.ts","../../src/cli/version.ts","../../src/plugins/telegram/commands/admin.ts","../../src/plugins/telegram/commands/new-session.ts","../../src/plugins/telegram/commands/session.ts","../../src/cli/integrate.ts","../../src/plugins/telegram/commands/agents.ts","../../src/plugins/telegram/commands/model.ts","../../src/plugins/telegram/commands/resume.ts","../../src/plugins/telegram/commands/menu.ts","../../src/plugins/telegram/commands/settings.ts","../../src/plugins/telegram/commands/doctor.ts","../../src/plugins/telegram/commands/tunnel.ts","../../src/plugins/telegram/commands/switch.ts","../../src/plugins/telegram/commands/telegram-overrides.ts","../../src/plugins/telegram/commands/integrate.ts","../../src/plugins/telegram/assistant.ts","../../src/plugins/telegram/commands/index.ts","../../src/plugins/telegram/permissions.ts","../../src/plugins/telegram/activity.ts","../../src/plugins/telegram/streaming.ts","../../src/plugins/telegram/draft-manager.ts","../../src/plugins/telegram/skill-command-manager.ts","../../src/plugins/telegram/renderer.ts","../../src/plugins/telegram/validators.ts","../../src/plugins/telegram/adapter.ts","../../src/core/index.ts","../../src/core/channel.ts","../../src/core/utils/streams.ts","../../src/core/utils/stderr-capture.ts","../../src/core/agents/agent-instance.ts","../../src/core/security/path-guard.ts","../../src/core/security/env-filter.ts","../../src/core/utils/typed-emitter.ts","../../src/core/agents/attachment-blocks.ts","../../src/core/sessions/terminal-manager.ts","../../src/core/agents/mcp-manager.ts","../../src/core/utils/debug-tracer.ts","../../src/core/agents/agent-manager.ts","../../src/core/sessions/session.ts","../../src/core/sessions/prompt-queue.ts","../../src/core/sessions/permission-gate.ts","../../src/core/sessions/turn-context.ts","../../src/core/message-transformer.ts","../../src/core/utils/extract-file-info.ts","../../src/core/sessions/session-manager.ts","../../src/core/sessions/session-bridge.ts","../../src/core/sessions/session-factory.ts","../../src/core/core.ts","../../src/core/sessions/session-store.ts","../../src/core/agent-switch-handler.ts","../../src/core/agents/agent-catalog.ts","../../src/core/agents/agent-store.ts","../../src/core/event-bus.ts","../../src/core/plugin/plugin-loader.ts","../../src/core/plugin/service-registry.ts","../../src/core/plugin/middleware-chain.ts","../../src/core/plugin/error-tracker.ts","../../src/core/plugin/plugin-storage.ts","../../src/core/plugin/plugin-context.ts","../../src/core/plugin/lifecycle-manager.ts","../../src/core/menu-registry.ts","../../src/core/assistant/assistant-registry.ts","../../src/core/assistant/prompt-constants.ts","../../src/core/assistant/assistant-manager.ts","../../src/core/assistant/sections/sessions.ts","../../src/core/assistant/sections/agents.ts","../../src/core/assistant/sections/config.ts","../../src/core/assistant/sections/system.ts","../../src/core/assistant/sections/remote.ts","../../src/core/menu/core-items.ts","../../src/core/command-registry.ts","../../src/core/config/config-editor.ts","../../src/cli/autostart.ts","../../src/cli/resolve-instance-id.ts","../../src/cli/daemon.ts","../../src/plugins/telegram/topic-manager.ts","../../src/core/adapter-primitives/index.ts","../../src/core/adapter-primitives/stream-adapter.ts","../../src/core/adapter-primitives/rendering/index.ts","../../src/core/adapter-primitives/primitives/index.ts","../../src/core/adapter-primitives/primitives/draft-manager.ts","../../src/core/adapter-primitives/primitives/tool-call-tracker.ts","../../src/core/adapter-primitives/primitives/activity-tracker.ts","../../src/data/product-guide.ts","../../src/index.ts"],"sourcesContent":["import pino from 'pino'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport type { LoggingConfig } from '../config/config.js'\n\nexport type Logger = pino.Logger\n\n// --- Default console-only logger (pre-init) ---\n// Before initLogger() is called (e.g., during module-level imports),\n// a basic pino-pretty logger writes to stderr. initLogger() replaces it\n// with a multi-target logger (console + rolling file).\nlet rootLogger: pino.Logger = pino({\n level: 'debug',\n transport: { target: 'pino-pretty', options: { colorize: true, translateTime: 'SYS:standard', destination: 2 } },\n})\nlet initialized = false\nlet logDir: string | undefined\nlet currentTransport: ReturnType<typeof pino.transport> | undefined\n\nfunction expandHome(p: string): string {\n return p.startsWith('~') ? path.join(os.homedir(), p.slice(1)) : p\n}\n\n// --- Variadic wrapper for backward compatibility ---\n// Pino's API requires (obj, msg) or (msg) — this wrapper allows\n// callers to use log.info(obj, ...strings) or log.info(...strings),\n// matching the more permissive console.log-style API.\nfunction wrapVariadic(logger: pino.Logger) {\n return {\n info: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.info(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.info(args.map(String).join(' '))\n }\n },\n warn: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.warn(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.warn(args.map(String).join(' '))\n }\n },\n error: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.error(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.error(args.map(String).join(' '))\n }\n },\n debug: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.debug(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.debug(args.map(String).join(' '))\n }\n },\n fatal: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.fatal(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.fatal(args.map(String).join(' '))\n }\n },\n child: (bindings: pino.Bindings) => logger.child(bindings),\n }\n}\n\nexport const log = wrapVariadic(rootLogger)\n\n// --- Mute/unmute (suppress pino output during interactive prompts) ---\n\nlet muteCount = 0\nlet savedLevel = 'info'\n\n/**\n * Suppress all log output. Calls are ref-counted — each muteLogger()\n * must be balanced by an unmuteLogger(). Used during interactive CLI\n * prompts (e.g., setup wizard) to avoid log noise in the terminal.\n *\n * Must always be paired with a corresponding `unmuteLogger()` call;\n * otherwise the logger remains permanently silenced.\n */\nexport function muteLogger(): void {\n if (muteCount === 0) {\n savedLevel = rootLogger.level\n rootLogger.level = 'silent'\n }\n muteCount++\n}\n\n/** Decrement the mute ref count and restore log level when it reaches zero. */\nexport function unmuteLogger(): void {\n muteCount--\n if (muteCount <= 0) {\n muteCount = 0\n rootLogger.level = savedLevel\n }\n}\n\n// --- Public API ---\n\n/**\n * Initialize the root logger with file + console output.\n *\n * Sets up a multi-target pino transport: pino-pretty to stderr (for humans)\n * and pino-roll to a rotating log file (for persistence). Also creates\n * the sessions/ subdirectory for per-session log files.\n *\n * Safe to call multiple times — subsequent calls are no-ops.\n */\nexport function initLogger(config: LoggingConfig): Logger {\n if (initialized) return rootLogger\n\n const resolvedLogDir = expandHome(config.logDir)\n logDir = resolvedLogDir\n\n try {\n fs.mkdirSync(resolvedLogDir, { recursive: true })\n fs.mkdirSync(path.join(resolvedLogDir, 'sessions'), { recursive: true })\n } catch (err) {\n console.error(`[WARN] Failed to create log directory ${resolvedLogDir}, falling back to console-only:`, err)\n return rootLogger\n }\n\n const transports = pino.transport({\n targets: [\n {\n target: 'pino-pretty',\n options: {\n colorize: true,\n translateTime: 'SYS:HH:MM:ss',\n ignore: 'pid,hostname',\n singleLine: true,\n destination: 2,\n },\n level: config.level,\n },\n {\n target: 'pino-roll',\n options: {\n file: path.join(resolvedLogDir, 'openacp.log'),\n size: config.maxFileSize,\n limit: { count: config.maxFiles },\n },\n level: config.level,\n },\n ],\n })\n\n currentTransport = transports\n rootLogger = pino({ level: config.level }, transports)\n initialized = true\n\n // Update the default log wrapper to use the new root logger\n Object.assign(log, wrapVariadic(rootLogger))\n\n return rootLogger\n}\n\n/** Change log level at runtime. Pino transport targets respect parent level changes automatically. */\nexport function setLogLevel(level: string): void {\n rootLogger.level = level\n}\n\n/**\n * Create a child logger scoped to a module (e.g., \"core\", \"session\", \"telegram\").\n *\n * Returns a Proxy that always delegates to the current rootLogger — this\n * ensures child loggers created at module-level (before initLogger runs)\n * pick up the initialized logger with proper transports once it's ready.\n */\nexport function createChildLogger(context: { module: string; [key: string]: unknown }): Logger {\n return new Proxy({} as Logger, {\n get(_target, prop, receiver) {\n const child = rootLogger.child(context)\n const value = Reflect.get(child, prop, receiver)\n return typeof value === 'function' ? value.bind(child) : value\n },\n })\n}\n\n/**\n * Create a per-session logger that writes to both the combined log and\n * a session-specific file (`<logDir>/sessions/<sessionId>.log`).\n *\n * This dual-write pattern allows operators to view all logs in one place\n * (combined log) while also being able to inspect a single session's\n * activity in isolation (session file).\n *\n * Falls back to a simple child logger if the session log file can't be created.\n */\nexport function createSessionLogger(sessionId: string, parentLogger: Logger): Logger {\n const sessionLogDir = logDir ? path.join(logDir, 'sessions') : undefined\n if (!sessionLogDir) {\n return parentLogger.child({ sessionId })\n }\n\n try {\n const sessionLogPath = path.join(sessionLogDir, `${sessionId}.log`)\n const dest = pino.destination(sessionLogPath)\n const sessionFileLogger = pino({ level: parentLogger.level }, dest).child({ sessionId })\n\n // Create a logger that writes to both parent (combined) and session file\n const combinedChild = parentLogger.child({ sessionId })\n const originalInfo = combinedChild.info.bind(combinedChild)\n const originalWarn = combinedChild.warn.bind(combinedChild)\n const originalError = combinedChild.error.bind(combinedChild)\n const originalDebug = combinedChild.debug.bind(combinedChild)\n const originalFatal = combinedChild.fatal.bind(combinedChild)\n\n // Proxy log methods to write to both destinations\n combinedChild.info = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.info(objOrMsg, ...rest)\n return originalInfo(objOrMsg, ...rest)\n }) as any\n combinedChild.warn = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.warn(objOrMsg, ...rest)\n return originalWarn(objOrMsg, ...rest)\n }) as any\n combinedChild.error = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.error(objOrMsg, ...rest)\n return originalError(objOrMsg, ...rest)\n }) as any\n combinedChild.debug = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.debug(objOrMsg, ...rest)\n return originalDebug(objOrMsg, ...rest)\n }) as any\n combinedChild.fatal = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.fatal(objOrMsg, ...rest)\n return originalFatal(objOrMsg, ...rest)\n }) as any\n\n // Store dest for cleanup\n ;(combinedChild as any).__sessionDest = dest\n\n return combinedChild\n } catch (err) {\n // Graceful degradation: session file failed, just use combined log\n parentLogger.warn({ sessionId, err }, 'Failed to create session log file, using combined log only')\n return parentLogger.child({ sessionId })\n }\n}\n\n/** Close the per-session log file destination to release the file handle. */\nexport function closeSessionLogger(logger: Logger): void {\n const dest = (logger as any).__sessionDest\n if (dest && typeof dest.destroy === 'function') {\n dest.destroy()\n }\n}\n\n/**\n * Flush and close the root logger transport.\n *\n * Resets all state so the logger can be re-initialized (e.g., after restart).\n * Waits up to 3 seconds for the transport to close gracefully.\n */\nexport async function shutdownLogger(): Promise<void> {\n if (!initialized) return\n\n const transport = currentTransport\n\n // Reset state immediately so re-init is possible\n rootLogger = pino({ level: 'debug' })\n Object.assign(log, wrapVariadic(rootLogger))\n currentTransport = undefined\n logDir = undefined\n initialized = false\n\n if (transport) {\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(resolve, 3000)\n transport.on('close', () => {\n clearTimeout(timeout)\n resolve()\n })\n transport.end()\n })\n }\n}\n\n/**\n * Delete session log files older than the given retention period.\n *\n * Called during startup to prevent unbounded disk usage from accumulated\n * per-session log files.\n */\nexport async function cleanupOldSessionLogs(retentionDays: number): Promise<void> {\n if (!logDir) return\n\n const sessionsDir = path.join(logDir, 'sessions')\n try {\n const files = await fs.promises.readdir(sessionsDir)\n const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1000\n\n for (const file of files) {\n try {\n const filePath = path.join(sessionsDir, file)\n const stat = await fs.promises.stat(filePath)\n if (stat.mtimeMs < cutoff) {\n await fs.promises.unlink(filePath)\n rootLogger.debug({ file }, 'Deleted old session log')\n }\n } catch (err) {\n rootLogger.warn({ file, err }, 'Failed to delete old session log')\n }\n }\n } catch {\n // Sessions directory doesn't exist — no-op\n }\n}\n","/**\n * Config migrations transform old config.json shapes to the current schema.\n *\n * Migrations run automatically during ConfigManager.load(), before Zod validation.\n * Each migration mutates the raw JSON object in place and returns true if it\n * made changes. They run sequentially in array order — new migrations should\n * always be appended at the end.\n *\n * If any migration fires, the updated config is written back to disk immediately,\n * so the file stays in sync with the in-memory representation.\n */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { createChildLogger } from \"../utils/log.js\";\nconst log = createChildLogger({ module: \"config-migrations\" });\n\ntype RawConfig = Record<string, unknown>;\n\n/** Context passed to migrations that need filesystem access (e.g. looking up the instance registry). */\nexport interface MigrationContext {\n configDir: string;\n}\n\n/** A single config migration: a named transform applied to the raw config object. */\nexport interface Migration {\n name: string;\n /** Mutates `raw` in place. Returns `true` if the config was changed. */\n apply: (raw: RawConfig, ctx?: MigrationContext) => boolean;\n}\n\n/**\n * Ordered list of config migrations. Each runs once — idempotent guards\n * (checking if the field already exists) prevent re-application.\n */\nexport const migrations: Migration[] = [\n {\n // v2025.x: instanceName was added to support multi-instance setups.\n // Old configs lack this field — default to \"Main\" so the UI has a display name.\n name: \"add-instance-name\",\n apply(raw) {\n if (raw.instanceName) return false;\n raw.instanceName = \"Main\";\n log.info(\"Added instanceName to config\");\n return true;\n },\n },\n {\n // displayVerbosity was replaced by outputMode — remove the legacy key\n // so it doesn't confuse Zod strict parsing or the config editor.\n name: \"delete-display-verbosity\",\n apply(raw) {\n if (!(\"displayVerbosity\" in raw)) return false;\n delete raw.displayVerbosity;\n log.info(\"Removed legacy displayVerbosity key from config\");\n return true;\n },\n },\n {\n // Instance IDs were originally only in instances.json (the global registry).\n // This migration copies the ID into config.json so each instance is self-identifying\n // without needing to cross-reference the registry.\n name: \"add-instance-id\",\n apply(raw, ctx) {\n if (raw.id) return false;\n if (!ctx?.configDir) return false;\n\n const instanceRoot = ctx.configDir;\n\n try {\n // Look up this instance's ID from the global registry\n const registryPath = path.join(os.homedir(), \".openacp\", \"instances.json\");\n const data = JSON.parse(fs.readFileSync(registryPath, \"utf-8\"));\n const instances = data?.instances ?? {};\n const entry = Object.values(instances).find(\n (e: any) => e.root === instanceRoot,\n ) as { id?: string } | undefined;\n if (entry?.id) {\n raw.id = entry.id;\n log.info({ instanceRoot }, \"Migrated: added id to config from registry\");\n return true;\n }\n } catch {\n /* best-effort — registry may not exist on fresh installs */\n }\n\n return false;\n },\n },\n];\n\n/**\n * Applies all pending migrations to a raw config object (mutates in place).\n *\n * Called by ConfigManager.load() before Zod validation. Each migration is\n * idempotent — safe to run on configs that have already been migrated.\n *\n * @returns Whether any migration made changes (caller should persist if true)\n */\nexport function applyMigrations(\n raw: RawConfig,\n migrationList: Migration[] = migrations,\n ctx?: MigrationContext,\n): { changed: boolean } {\n let changed = false;\n for (const migration of migrationList) {\n if (migration.apply(raw, ctx)) {\n changed = true;\n }\n }\n return { changed };\n}\n","/**\n * Config registry — declarative metadata for config fields.\n *\n * Each registered field describes its UI type, whether it can be hot-reloaded,\n * and whether it's safe to expose via the API. This drives:\n * - The API server's PATCH /api/config endpoint (validates + applies changes)\n * - Hot-reload: fields marked `hotReload: true` take effect immediately via\n * ConfigManager's `config:changed` event, without requiring a restart\n * - Security: only `scope: \"safe\"` fields are exposed to the REST API\n */\nimport type { Config } from \"./config.js\";\n\n/**\n * Metadata for a single config field, describing how it should be\n * displayed, validated, and applied at runtime.\n */\nexport interface ConfigFieldDef {\n /** Dot-path into the Config object (e.g. \"logging.level\"). */\n path: string;\n /** Human-readable label for UI display. */\n displayName: string;\n /** Grouping key for organizing fields in the editor (e.g. \"agent\", \"logging\"). */\n group: string;\n /** UI control type — determines validation and rendering. */\n type: \"toggle\" | \"select\" | \"number\" | \"string\";\n /** For \"select\" type: allowed values, either static or derived from current config. */\n options?: string[] | ((config: Config) => string[]);\n /** \"safe\" fields can be exposed via the API; \"sensitive\" fields require direct file access. */\n scope: \"safe\" | \"sensitive\";\n /** Whether changes to this field take effect without restarting the server. */\n hotReload: boolean;\n}\n\n/**\n * Static registry of all config fields that support programmatic access.\n * Fields not listed here can still exist in config.json but won't be\n * editable via the API or shown in the config UI.\n */\nexport const CONFIG_REGISTRY: ConfigFieldDef[] = [\n {\n path: \"defaultAgent\",\n displayName: \"Default Agent\",\n group: \"agent\",\n type: \"select\",\n options: (config) => {\n // Full agent list is managed by AgentCatalog at runtime, not config registry\n const current = config.defaultAgent;\n return current ? [current] : [];\n },\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"logging.level\",\n displayName: \"Log Level\",\n group: \"logging\",\n type: \"select\",\n options: [\"silent\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"],\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"sessionStore.ttlDays\",\n displayName: \"Session Store TTL (days)\",\n group: \"storage\",\n type: \"number\",\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"agentSwitch.labelHistory\",\n displayName: \"Label Agent in History\",\n group: \"agent\",\n type: \"toggle\",\n scope: \"safe\",\n hotReload: true,\n },\n];\n\n/** Looks up a field definition by its dot-path. */\nexport function getFieldDef(path: string): ConfigFieldDef | undefined {\n return CONFIG_REGISTRY.find((f) => f.path === path);\n}\n\n/** Returns only fields safe to expose via the REST API. */\nexport function getSafeFields(): ConfigFieldDef[] {\n return CONFIG_REGISTRY.filter((f) => f.scope === \"safe\");\n}\n\n/** Checks whether a config field can be changed without restarting the server. */\nexport function isHotReloadable(path: string): boolean {\n const def = getFieldDef(path);\n return def?.hotReload ?? false;\n}\n\n/** Resolves select options — evaluates the function form against the current config if needed. */\nexport function resolveOptions(\n def: ConfigFieldDef,\n config: Config,\n): string[] | undefined {\n if (!def.options) return undefined;\n return typeof def.options === \"function\" ? def.options(config) : def.options;\n}\n\n/** Traverses a config object by dot-path (e.g. \"logging.level\") and returns the value. */\nexport function getConfigValue(config: Config, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = config;\n for (const part of parts) {\n if (current && typeof current === \"object\" && part in current) {\n current = (current as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n return current;\n}\n\n/** Thrown when a config field value fails type validation against its registry definition. */\nexport class ConfigValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigValidationError\";\n }\n}\n\n/** Validates that a value matches the expected type for a registry field. */\nfunction validateFieldValue(field: ConfigFieldDef, value: unknown): void {\n switch (field.type) {\n case \"number\":\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n throw new ConfigValidationError(`\"${field.path}\" expects a number, got ${typeof value}`);\n }\n break;\n case \"toggle\":\n if (typeof value !== \"boolean\") {\n throw new ConfigValidationError(`\"${field.path}\" expects a boolean, got ${typeof value}`);\n }\n break;\n case \"string\":\n if (typeof value !== \"string\") {\n throw new ConfigValidationError(`\"${field.path}\" expects a string, got ${typeof value}`);\n }\n break;\n case \"select\": {\n if (typeof value !== \"string\") {\n throw new ConfigValidationError(`\"${field.path}\" expects a string, got ${typeof value}`);\n }\n break;\n }\n }\n}\n\n/**\n * Validates and persists a config field value via the ConfigManager.\n *\n * @returns Whether the change requires a server restart to take effect\n */\nexport async function setFieldValueAsync(\n field: ConfigFieldDef,\n value: unknown,\n configManager: { setPath(path: string, value: unknown): Promise<void>; emit?(event: string, data: unknown): void },\n): Promise<{ needsRestart: boolean }> {\n validateFieldValue(field, value);\n await configManager.setPath(field.path, value);\n return { needsRestart: !field.hotReload };\n}\n","import { z } from \"zod\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { randomBytes } from \"node:crypto\";\nimport { EventEmitter } from \"node:events\";\nimport { applyMigrations } from \"./config-migrations.js\";\nimport { createChildLogger } from \"../utils/log.js\";\nimport type { SettingsManager } from \"../plugin/settings-manager.js\";\nconst log = createChildLogger({ module: \"config\" });\n\n/** Log rotation and verbosity settings. */\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 .prefault({});\n\n/** Runtime logging configuration. Controls per-module log levels and output destinations. */\nexport type LoggingConfig = z.infer<typeof LoggingSchema>;\n\n/**\n * Zod schema for the global OpenACP config file (`~/.openacp/config.json`).\n *\n * Every field uses `.default()` or `.optional()` so that config files from older\n * versions — which lack newly added fields — still pass validation without error.\n * This is critical for backward compatibility: users should never have to manually\n * edit their config after upgrading.\n *\n * Plugin-specific settings live separately in per-plugin settings files\n * (`~/.openacp/plugins/<name>/settings.json`), not here. This schema only\n * covers global, cross-cutting concerns.\n */\nexport const ConfigSchema = z.object({\n /** Instance UUID, written once at creation time. */\n id: z.string().optional(),\n instanceName: z.string().optional(),\n defaultAgent: z.string(),\n\n // --- Workspace security & path resolution ---\n workspace: z\n .object({\n allowExternalWorkspaces: z.boolean().default(true),\n security: z\n .object({\n allowedPaths: z.array(z.string()).default([]),\n envWhitelist: z.array(z.string()).default([]),\n })\n .prefault({}),\n })\n .prefault({}),\n\n // --- Logging ---\n logging: LoggingSchema,\n\n // --- Process lifecycle ---\n runMode: z.enum([\"foreground\", \"daemon\"]).default(\"foreground\"),\n autoStart: z.boolean().default(false),\n\n // --- Session persistence ---\n sessionStore: z\n .object({\n ttlDays: z.number().default(30),\n })\n .prefault({}),\n\n // --- Installed integration tracking (e.g. plugins installed via CLI) ---\n integrations: z\n .record(\n z.string(),\n z.object({\n installed: z.boolean(),\n installedAt: z.string().optional(),\n }),\n )\n .default({}),\n\n // --- Agent output verbosity control ---\n outputMode: z.enum([\"low\", \"medium\", \"high\"]).default(\"medium\").optional(),\n\n // --- Multi-agent switching behavior ---\n agentSwitch: z.object({\n labelHistory: z.boolean().default(true),\n }).prefault({}),\n});\n\n/** Validated config object used throughout the codebase. Always obtained via `ConfigManager.get()` to ensure it's up-to-date. */\nexport type Config = z.infer<typeof ConfigSchema>;\n\n/** Expands a leading `~` to the user's home directory. Returns the path unchanged if no `~` prefix. */\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 defaultAgent: \"claude\",\n sessionStore: { ttlDays: 30 },\n};\n\n/**\n * Manages loading, validating, and persisting the global config file.\n *\n * The load cycle is: read JSON -> apply migrations -> apply env overrides -> validate with Zod.\n * Emits `config:changed` events when individual fields are updated, enabling\n * hot-reload for fields marked as `hotReload` in the config registry.\n */\nexport class ConfigManager extends EventEmitter {\n private config!: Config;\n private configPath: string;\n\n constructor(configPath?: string) {\n super();\n this.configPath =\n process.env.OPENACP_CONFIG_PATH || configPath || expandHome(\"~/.openacp/config.json\");\n }\n\n /**\n * Loads config from disk through the full validation pipeline:\n * 1. Create default config if missing (first run)\n * 2. Apply migrations for older config formats\n * 3. Apply environment variable overrides\n * 4. Validate against Zod schema — exits on failure\n */\n async load(): Promise<void> {\n const dir = path.dirname(this.configPath);\n fs.mkdirSync(dir, { recursive: true });\n\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 \"Run 'openacp setup' to configure channels and agents, then restart.\",\n );\n process.exit(1);\n }\n\n const raw = JSON.parse(fs.readFileSync(this.configPath, \"utf-8\"));\n\n // Auto-migrate before validation — transforms old config shapes to current schema\n const { changed: configUpdated } = applyMigrations(raw, undefined, { configDir: path.dirname(this.configPath) });\n if (configUpdated) {\n fs.writeFileSync(this.configPath, JSON.stringify(raw, null, 2));\n }\n\n this.applyEnvOverrides(raw);\n\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 /** Returns a deep clone of the current config to prevent external mutation. */\n get(): Config {\n return structuredClone(this.config);\n }\n\n /**\n * Merges partial updates into the config file using atomic write (write tmp + rename).\n *\n * Validates the merged result before writing. If `changePath` is provided,\n * emits a `config:changed` event with old and new values for that path,\n * enabling hot-reload without restart.\n */\n async save(\n updates: Record<string, unknown>,\n changePath?: string,\n ): Promise<void> {\n const oldConfig = this.config ? structuredClone(this.config) : undefined;\n const raw = JSON.parse(fs.readFileSync(this.configPath, \"utf-8\"));\n this.deepMerge(raw, updates);\n // Validate BEFORE writing to disk\n const result = ConfigSchema.safeParse(raw);\n if (!result.success) {\n log.error({ errors: result.error.issues }, \"Config validation failed, not saving\");\n return;\n }\n // Atomic write: tmp file + rename prevents corruption if process crashes mid-write\n const tmpPath = this.configPath + `.tmp.${randomBytes(4).toString('hex')}`;\n fs.writeFileSync(tmpPath, JSON.stringify(raw, null, 2), \"utf-8\");\n fs.renameSync(tmpPath, this.configPath);\n this.config = result.data;\n if (changePath) {\n const { getConfigValue } = await import(\"./config-registry.js\");\n const value = getConfigValue(this.config, changePath);\n const oldValue = oldConfig\n ? getConfigValue(oldConfig, changePath)\n : undefined;\n this.emit(\"config:changed\", { path: changePath, value, oldValue });\n }\n }\n\n /**\n * Convenience wrapper for updating a single deeply-nested config field\n * without constructing the full update object manually.\n *\n * Accepts a dot-path (e.g. \"logging.level\") and builds the nested\n * update object internally before delegating to `save()`.\n * Throws if the path contains prototype-pollution keys.\n */\n async setPath(dotPath: string, value: unknown): Promise<void> {\n const BLOCKED_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n const parts = dotPath.split('.');\n if (parts.some((p) => BLOCKED_KEYS.has(p))) {\n throw new Error(`Invalid config path: ${dotPath}`);\n }\n\n // Build nested updates object from dot-path\n const updates: Record<string, unknown> = {};\n let target = updates;\n for (let i = 0; i < parts.length - 1; i++) {\n target[parts[i]!] = {};\n target = target[parts[i]!] as Record<string, unknown>;\n }\n target[parts[parts.length - 1]!] = value;\n\n await this.save(updates, dotPath);\n }\n\n /**\n * Resolves a workspace path from user input.\n *\n * Supports three forms: no input (returns base dir), absolute/tilde paths\n * (validated against allowExternalWorkspaces), and named workspaces\n * (alphanumeric subdirectories under the base).\n */\n resolveWorkspace(input?: string): string {\n // configPath = /x/y/.openacp/config.json → workspace = /x/y/\n const workspaceBase = path.dirname(path.dirname(this.configPath));\n\n if (!input) {\n fs.mkdirSync(workspaceBase, { recursive: true });\n return workspaceBase;\n }\n\n // Absolute or tilde path\n const expanded = input.startsWith(\"~\") ? expandHome(input) : input;\n if (path.isAbsolute(expanded)) {\n const resolved = path.resolve(expanded);\n const base = path.resolve(workspaceBase);\n const isInternal = resolved === base || resolved.startsWith(base + path.sep);\n\n if (!isInternal) {\n if (!this.config.workspace.allowExternalWorkspaces) {\n throw new Error(\n `Workspace path \"${input}\" is outside base directory \"${workspaceBase}\". Set allowExternalWorkspaces: true to allow this.`,\n );\n }\n if (!fs.existsSync(resolved)) {\n throw new Error(`Workspace path \"${resolved}\" does not exist.`);\n }\n return resolved;\n }\n\n fs.mkdirSync(resolved, { recursive: true });\n return resolved;\n }\n\n // Named workspace\n if (!/^[a-zA-Z0-9_-]+$/.test(input)) {\n throw new Error(\n `Invalid workspace name: \"${input}\". Only alphanumeric characters, hyphens, and underscores are allowed.`,\n );\n }\n const namedPath = path.join(workspaceBase, input.toLowerCase());\n fs.mkdirSync(namedPath, { recursive: true });\n return namedPath;\n }\n\n /** Checks whether the config file exists on disk. Wraps synchronous `fs.existsSync` behind an async interface for consistency with the rest of the ConfigManager API. */\n async exists(): Promise<boolean> {\n return fs.existsSync(this.configPath);\n }\n\n /** Returns the resolved path to the config JSON file. */\n getConfigPath(): string {\n return this.configPath;\n }\n\n /** Writes a complete config object to disk, creating the directory if needed. Used during initial setup. */\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 /**\n * Applies `OPENACP_*` environment variables as overrides to per-plugin settings.\n *\n * This lets users configure plugin values (bot tokens, ports, etc.) via env vars\n * without editing settings files — useful for Docker, CI, and headless setups.\n */\n async applyEnvToPluginSettings(settingsManager: SettingsManager): Promise<void> {\n const pluginOverrides: Array<{\n envVar: string;\n pluginName: string;\n key: string;\n transform?: (v: string) => unknown;\n }> = [\n { envVar: 'OPENACP_TUNNEL_ENABLED', pluginName: '@openacp/tunnel', key: 'enabled', transform: v => v === 'true' },\n { envVar: 'OPENACP_TUNNEL_PORT', pluginName: '@openacp/tunnel', key: 'port', transform: v => Number(v) },\n { envVar: 'OPENACP_TUNNEL_PROVIDER', pluginName: '@openacp/tunnel', key: 'provider' },\n { envVar: 'OPENACP_API_PORT', pluginName: '@openacp/api-server', key: 'port', transform: v => Number(v) },\n { envVar: 'OPENACP_SPEECH_STT_PROVIDER', pluginName: '@openacp/speech', key: 'sttProvider' },\n { envVar: 'OPENACP_SPEECH_GROQ_API_KEY', pluginName: '@openacp/speech', key: 'groqApiKey' },\n { envVar: 'OPENACP_TELEGRAM_BOT_TOKEN', pluginName: '@openacp/telegram', key: 'botToken' },\n { envVar: 'OPENACP_TELEGRAM_CHAT_ID', pluginName: '@openacp/telegram', key: 'chatId', transform: v => Number(v) },\n // Future adapters — no-ops if plugin settings don't exist\n { envVar: 'OPENACP_DISCORD_BOT_TOKEN', pluginName: '@openacp/discord-adapter', key: 'botToken' },\n { envVar: 'OPENACP_DISCORD_GUILD_ID', pluginName: '@openacp/discord-adapter', key: 'guildId' },\n { envVar: 'OPENACP_SLACK_BOT_TOKEN', pluginName: '@openacp/slack-adapter', key: 'botToken' },\n { envVar: 'OPENACP_SLACK_APP_TOKEN', pluginName: '@openacp/slack-adapter', key: 'appToken' },\n { envVar: 'OPENACP_SLACK_SIGNING_SECRET', pluginName: '@openacp/slack-adapter', key: 'signingSecret' },\n ];\n\n for (const { envVar, pluginName, key, transform } of pluginOverrides) {\n const value = process.env[envVar];\n if (value !== undefined) {\n const resolved = transform ? transform(value) : value;\n await settingsManager.updatePluginSettings(pluginName, { [key]: resolved });\n log.debug({ envVar, pluginName, key }, 'Env var override applied to plugin settings');\n }\n }\n }\n\n /** Applies env var overrides to the raw config object before Zod validation. */\n private applyEnvOverrides(raw: Record<string, unknown>): void {\n const overrides: [string, string[]][] = [\n [\"OPENACP_DEFAULT_AGENT\", [\"defaultAgent\"]],\n [\"OPENACP_RUN_MODE\", [\"runMode\"]],\n ];\n for (const [envVar, configPath] of overrides) {\n const value = process.env[envVar];\n if (value !== undefined) {\n let target: Record<string, unknown> = raw;\n for (let i = 0; i < configPath.length - 1; i++) {\n if (!target[configPath[i]]) target[configPath[i]] = {};\n target = target[configPath[i]] as Record<string, unknown>;\n }\n const key = configPath[configPath.length - 1];\n target[key] = 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\n /** Recursively merges source into target, skipping prototype-pollution keys. */\n private deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n ): void {\n // Prototype pollution guard — these keys must never be set via user input\n const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n for (const key of Object.keys(source)) {\n if (DANGEROUS_KEYS.has(key)) continue;\n const val = source[key];\n if (val && typeof val === \"object\" && !Array.isArray(val)) {\n if (!target[key]) target[key] = {};\n this.deepMerge(\n target[key] as Record<string, unknown>,\n val as Record<string, unknown>,\n );\n } else {\n target[key] = val;\n }\n }\n }\n}\n","import fs from \"node:fs\";\n\n/**\n * Read a text file, optionally returning only a range of lines.\n *\n * The `line` and `limit` options enable partial file reads — used by the\n * file-service plugin when agents request specific line ranges (e.g.,\n * \"read lines 50-100 of config.ts\"). This avoids sending entire large\n * files over the ACP protocol when the agent only needs a slice.\n */\nexport async function readTextFileWithRange(\n filePath: string,\n options?: { line?: number; limit?: number },\n): Promise<string> {\n const content = await fs.promises.readFile(filePath, \"utf-8\");\n if (!options?.line && !options?.limit) return content;\n const lines = content.split(\"\\n\");\n // ACP line numbers are 1-based; array indices are 0-based\n const start = Math.max(0, (options.line ?? 1) - 1);\n const end = options.limit ? start + options.limit : lines.length;\n return lines.slice(start, end).join(\"\\n\");\n}\n","/**\n * Centralized event name constants for all event systems in OpenACP.\n *\n * Three distinct event systems:\n * - Hook — Middleware pipeline hooks (middlewareChain.execute / ctx.registerMiddleware)\n * - BusEvent — EventBus events (eventBus.emit / eventBus.on), cross-cutting concerns\n * - SessionEv — Session TypedEmitter events (session.emit / session.on), per-session state\n */\n\nimport type { EventBusEvents } from './event-bus.js';\nimport type { SessionEvents } from './sessions/session.js';\n\n// ---------------------------------------------------------------------------\n// Middleware Hooks\n// ---------------------------------------------------------------------------\n\n/**\n * Names for all middleware pipeline hooks.\n *\n * Each hook is an intercept point in the request/event pipeline. Middleware\n * registered on a hook can inspect, modify, or block the payload before it\n * proceeds. \"Read-only, fire-and-forget\" hooks run after the fact and cannot\n * block.\n *\n * Used with `middlewareChain.execute(Hook.X, ...)` and\n * `ctx.registerMiddleware(Hook.X, ...)`.\n */\nexport const Hook = {\n // --- Message flow ---\n /** Incoming message from any adapter — modifiable, can block. */\n MESSAGE_INCOMING: 'message:incoming',\n /** Outgoing message before it reaches the adapter — modifiable, can block. */\n MESSAGE_OUTGOING: 'message:outgoing',\n\n // --- Agent / turn lifecycle ---\n /** Before a user prompt is sent to the agent — modifiable, can block. */\n AGENT_BEFORE_PROMPT: 'agent:beforePrompt',\n /** Before an agent event is dispatched — modifiable, can block. */\n AGENT_BEFORE_EVENT: 'agent:beforeEvent',\n /** After an agent event is dispatched — read-only, fire-and-forget. */\n AGENT_AFTER_EVENT: 'agent:afterEvent',\n /** Before the current prompt is cancelled — modifiable, can block. */\n AGENT_BEFORE_CANCEL: 'agent:beforeCancel',\n /** Before the agent is switched — modifiable, can block. */\n AGENT_BEFORE_SWITCH: 'agent:beforeSwitch',\n /** After the agent has been switched — read-only, fire-and-forget. */\n AGENT_AFTER_SWITCH: 'agent:afterSwitch',\n\n // --- Turn boundaries ---\n /** Turn started — read-only, fire-and-forget. */\n TURN_START: 'turn:start',\n /** Turn ended (always fires, even on error) — read-only, fire-and-forget. */\n TURN_END: 'turn:end',\n /** After a turn completes — full assembled agent text, read-only, fire-and-forget. */\n AGENT_AFTER_TURN: 'agent:afterTurn',\n\n // --- Session lifecycle ---\n /** Before a new session is created — modifiable, can block. */\n SESSION_BEFORE_CREATE: 'session:beforeCreate',\n /** After a session is destroyed — read-only, fire-and-forget. */\n SESSION_AFTER_DESTROY: 'session:afterDestroy',\n\n // --- Permissions ---\n /** Before a permission request is shown to the user — modifiable, can block. */\n PERMISSION_BEFORE_REQUEST: 'permission:beforeRequest',\n /** After a permission request is resolved — read-only, fire-and-forget. */\n PERMISSION_AFTER_RESOLVE: 'permission:afterResolve',\n\n // --- Config ---\n /** Before config options change — modifiable, can block. */\n CONFIG_BEFORE_CHANGE: 'config:beforeChange',\n\n // --- Filesystem (agent-level) ---\n /** Before a file read operation — modifiable. */\n FS_BEFORE_READ: 'fs:beforeRead',\n /** Before a file write operation — modifiable. */\n FS_BEFORE_WRITE: 'fs:beforeWrite',\n\n // --- Terminal ---\n /** Before a terminal session is created — modifiable, can block. */\n TERMINAL_BEFORE_CREATE: 'terminal:beforeCreate',\n /** After a terminal session exits — read-only, fire-and-forget. */\n TERMINAL_AFTER_EXIT: 'terminal:afterExit',\n} as const;\n\nexport type HookName = typeof Hook[keyof typeof Hook];\n\n// ---------------------------------------------------------------------------\n// EventBus Events\n// ---------------------------------------------------------------------------\n\n/**\n * Names for all EventBus events.\n *\n * EventBus is the global pub/sub bus for cross-cutting concerns — plugins\n * subscribe to these events without needing direct references to sessions\n * or adapters. Type-checked against the EventBusEvents interface.\n *\n * Used with `eventBus.emit(BusEvent.X, ...)` and `eventBus.on(BusEvent.X, ...)`.\n */\nexport const BusEvent = {\n // --- Session lifecycle ---\n /** Fired when a new session is created and ready. */\n SESSION_CREATED: 'session:created',\n /** Fired when session metadata changes (status, name, overrides). */\n SESSION_UPDATED: 'session:updated',\n /** Fired when a session record is deleted from the store. */\n SESSION_DELETED: 'session:deleted',\n /** Fired when a session ends (agent finished or error). */\n SESSION_ENDED: 'session:ended',\n /** Fired when a session receives its auto-generated name. */\n SESSION_NAMED: 'session:named',\n /** Fired after a new session thread is created and bridge connected. */\n SESSION_THREAD_READY: 'session:threadReady',\n /** Fired when an agent's config options change (adapters update control UIs). */\n SESSION_CONFIG_CHANGED: 'session:configChanged',\n /** Fired during agent switch lifecycle (starting/succeeded/failed). */\n SESSION_AGENT_SWITCH: 'session:agentSwitch',\n\n // --- Agent ---\n /** Fired for every agent event (text, tool_call, usage, etc.). */\n AGENT_EVENT: 'agent:event',\n /** Fired when a prompt is sent to the agent. */\n AGENT_PROMPT: 'agent:prompt',\n\n // --- Permissions ---\n /** Fired when the agent requests user permission (blocks until resolved). */\n PERMISSION_REQUEST: 'permission:request',\n /** Fired after a permission request is resolved (approved or denied). */\n PERMISSION_RESOLVED: 'permission:resolved',\n\n // --- Message visibility ---\n /** Fired when a user message is queued (for cross-adapter input visibility). */\n MESSAGE_QUEUED: 'message:queued',\n /** Fired when a queued message starts processing. */\n MESSAGE_PROCESSING: 'message:processing',\n /** Fired when a queued message is rejected (e.g. blocked by middleware). */\n MESSAGE_FAILED: 'message:failed',\n\n // --- System lifecycle ---\n /** Fired after kernel (core + plugin infrastructure) has booted. */\n KERNEL_BOOTED: 'kernel:booted',\n /** Fired when the system is fully ready (all adapters connected). */\n SYSTEM_READY: 'system:ready',\n /** Fired during graceful shutdown. */\n SYSTEM_SHUTDOWN: 'system:shutdown',\n /** Fired when all system commands are registered and available. */\n SYSTEM_COMMANDS_READY: 'system:commands-ready',\n\n // --- Plugin lifecycle ---\n /** Fired when a plugin loads successfully. */\n PLUGIN_LOADED: 'plugin:loaded',\n /** Fired when a plugin fails to load. */\n PLUGIN_FAILED: 'plugin:failed',\n /** Fired when a plugin is disabled (e.g., missing config). */\n PLUGIN_DISABLED: 'plugin:disabled',\n /** Fired when a plugin is unloaded during shutdown. */\n PLUGIN_UNLOADED: 'plugin:unloaded',\n\n // --- Message visibility ---\n /** Fired when a user message is actually placed in the pending queue behind a running prompt.\n * Emitted synchronously from inside PromptQueue.enqueue(), so state is accurate — no race condition. */\n PROMPT_WAITING: 'prompt:waiting',\n\n // --- Usage ---\n /** Fired when a token usage record is captured (consumed by usage plugin). */\n USAGE_RECORDED: 'usage:recorded',\n\n // --- Identity lifecycle ---\n /** Fired when a new user+identity record is created. */\n IDENTITY_CREATED: 'identity:created',\n /** Fired when user profile fields change. */\n IDENTITY_UPDATED: 'identity:updated',\n /** Fired when two identities are linked (same person). */\n IDENTITY_LINKED: 'identity:linked',\n /** Fired when an identity is unlinked into a new user. */\n IDENTITY_UNLINKED: 'identity:unlinked',\n /** Fired when two user records are merged during a link operation. */\n IDENTITY_USER_MERGED: 'identity:userMerged',\n /** Fired when a user's role changes. */\n IDENTITY_ROLE_CHANGED: 'identity:roleChanged',\n /** Fired when a user is seen (throttled). */\n IDENTITY_SEEN: 'identity:seen',\n\n // --- Notifications ---\n /** Fired when a user-targeted notification is dispatched (e.g. agent mention). */\n USER_NOTIFICATION: 'user:notification',\n} as const satisfies Record<string, keyof EventBusEvents>;\n\nexport type BusEventName = typeof BusEvent[keyof typeof BusEvent];\n\n// ---------------------------------------------------------------------------\n// Session TypedEmitter Events\n// ---------------------------------------------------------------------------\n\n/**\n * Names for all Session TypedEmitter events.\n *\n * These are per-session events emitted by the Session instance itself.\n * SessionBridge subscribes to these to relay agent output to adapters.\n *\n * Used with `session.on(SessionEv.X, ...)` and `session.emit(SessionEv.X, ...)`.\n * Type-checked against the SessionEvents interface.\n */\nexport const SessionEv = {\n /** Agent produced an event (text, tool_call, etc.) during a turn. */\n AGENT_EVENT: 'agent_event',\n /** Agent is requesting user permission — blocks until resolved. */\n PERMISSION_REQUEST: 'permission_request',\n /** Session ended (agent finished, cancelled, or errored). */\n SESSION_END: 'session_end',\n /** Session status changed (e.g., initializing → active). */\n STATUS_CHANGE: 'status_change',\n /** Session received an auto-generated name from the first response. */\n NAMED: 'named',\n /** An unrecoverable error occurred in the session. */\n ERROR: 'error',\n /** The session's prompt count changed (used for UI counters). */\n PROMPT_COUNT_CHANGED: 'prompt_count_changed',\n /** A new prompt turn started (provides TurnContext for middleware). */\n TURN_STARTED: 'turn_started',\n /** A prompt was placed behind a running prompt — fired synchronously from PromptQueue.enqueue(). */\n PROMPT_QUEUED: 'prompt_queued',\n} as const satisfies Record<string, keyof SessionEvents>;\n\nexport type SessionEvName = typeof SessionEv[keyof typeof SessionEv];\n","/**\n * Detection utilities for apply_patch tool calls.\n *\n * Some agents (e.g., OpenCode) use an \"apply_patch\" tool with kind \"other\"\n * to modify files via unified diff patches. These need special handling:\n * - Security: permission gating must treat them as write operations\n * - File preview: extractFileInfo must parse the patch output format\n * instead of the standard read/edit/write content format\n */\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\n/** Check if rawInput contains a patchText/patch_text field (indicates apply_patch semantics). */\nexport function hasApplyPatchPatchText(rawInput: unknown): boolean {\n const input = asRecord(rawInput);\n return !!input && (typeof input.patchText === \"string\" || typeof input.patch_text === \"string\");\n}\n\n/**\n * Determine if a tool call is an apply_patch operation.\n *\n * Matches either by explicit tool name (\"apply_patch\") or by the presence\n * of a patchText field in rawInput — both indicate file modification via\n * unified diff that requires write-level permission gating.\n */\nexport function isApplyPatchOtherTool(kind: string | undefined, name: string, rawInput: unknown): boolean {\n // Only \"other\" kind tools can be apply_patch — read/edit/write are handled normally\n if (kind !== \"other\") return false;\n if (name.toLowerCase() === \"apply_patch\") return true;\n return hasApplyPatchPatchText(rawInput);\n}\n","/**\n * Detection for permission-bypass configuration values.\n *\n * When a user sets an agent's permission mode to a bypass value (e.g., \"yolo\",\n * \"dangerous\"), ALL tool calls are auto-approved without prompting. This is\n * distinct from \"dontask\"/\"dont_ask\"/\"skip\" which DENY unknown permissions\n * rather than approving them.\n *\n * Used by PermissionGate to determine whether to show permission prompts\n * or auto-approve.\n */\n\n/** Keywords that indicate a permission-bypass (auto-approve) value.\n * NOTE: \"dontask\"/\"dont_ask\"/\"skip\" are NOT bypass — they DENY unknown permissions. */\nexport const BYPASS_KEYWORDS = ['bypass', 'dangerous', 'auto_accept', 'yolo']\n\n/** Returns true if the given value string contains a bypass keyword (case-insensitive). */\nexport function isPermissionBypass(value: string): boolean {\n const lower = value.toLowerCase()\n return BYPASS_KEYWORDS.some(kw => lower.includes(kw))\n}\n","/**\n * Agent dependency metadata, capabilities, and integration specs.\n *\n * This module contains static knowledge about each supported agent:\n * - **Dependencies** — external CLIs an agent requires (e.g., claude-acp needs the Claude CLI)\n * - **Setup info** — post-install instructions shown to the user\n * - **Capabilities** — whether an agent supports resume, and integration specs\n * for hooks/plugins that enable cross-agent coordination\n * - **Aliases** — maps registry IDs to short names (e.g., \"claude-acp\" → \"claude\")\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { AvailabilityResult } from \"../types.js\";\n\n/** An external CLI binary that an agent requires to function. */\nexport interface AgentDependency {\n command: string;\n label: string;\n installHint: string;\n}\n\n/** Post-install setup instructions displayed to the user after agent installation. */\nexport interface AgentSetupInfo {\n setupSteps: string[];\n loginCommand?: string;\n}\n\n/**\n * Integration spec for agents that support hooks-based coordination.\n *\n * Hooks let OpenACP inject behavior into the agent's lifecycle (e.g.,\n * intercepting prompts for multi-agent handoff). The spec describes\n * where the agent's settings and hooks directories live.\n */\nexport interface AgentHooksIntegrationSpec {\n strategy: \"hooks\";\n hookEvent: string;\n settingsPath: string;\n settingsFormat: \"settings_json\" | \"hooks_json\";\n hooksDirPath: string;\n outputFormat: \"plaintext\" | \"json\";\n sessionIdField: string;\n commandsPath?: string;\n handoffCommandName?: string;\n commandFormat?: \"markdown\" | \"skill\";\n sessionIdVar?: string;\n workingDirVar?: string;\n}\n\n/** Integration spec for agents that support plugin-based coordination (e.g., opencode). */\nexport interface AgentPluginIntegrationSpec {\n strategy: \"plugin\";\n pluginProvider: \"opencode\";\n commandsPath: string;\n pluginsPath: string;\n handoffCommandName: string;\n handoffCommandFile: string;\n pluginFileName: string;\n}\n\nexport type AgentIntegrationSpec = AgentHooksIntegrationSpec | AgentPluginIntegrationSpec;\n\n/** Static capability metadata for an agent (resume support, integration spec). */\nexport interface AgentCapability {\n supportsResume: boolean;\n resumeCommand?: (sessionId: string) => string;\n integration?: AgentIntegrationSpec;\n}\n\nconst AGENT_DEPENDENCIES: Record<string, AgentDependency[]> = {\n \"claude-acp\": [\n {\n command: \"claude\",\n label: \"Claude CLI\",\n installHint: \"npm install -g @anthropic-ai/claude-code\",\n },\n ],\n \"codex-acp\": [\n {\n command: \"codex\",\n label: \"Codex CLI\",\n installHint: \"npm install -g @openai/codex\",\n },\n ],\n};\n\nconst AGENT_SETUP: Record<string, AgentSetupInfo> = {\n // --- Agents requiring their own CLI installed first ---\n \"claude-acp\": {\n setupSteps: [\n \"Install Claude CLI: npm install -g @anthropic-ai/claude-code\",\n \"Login: claude login (opens browser for Anthropic account)\",\n ],\n loginCommand: \"claude login\",\n },\n \"codex-acp\": {\n setupSteps: [\n \"Install Codex CLI: npm install -g @openai/codex\",\n \"Login: codex (select 'Sign in with ChatGPT')\",\n \"Or set API key: export OPENAI_API_KEY=<your-key>\",\n ],\n loginCommand: \"codex\",\n },\n\n // --- Agents with built-in auth (npx handles download) ---\n \"gemini\": {\n setupSteps: [\n \"Login with Google: openacp agents run gemini (select 'Sign in with Google')\",\n \"Or set API key: export GEMINI_API_KEY=<key> (get from aistudio.google.com/apikey)\",\n \"Free tier: 60 requests/min, 1000 requests/day\",\n ],\n loginCommand: \"openacp agents run gemini\",\n },\n \"github-copilot-cli\": {\n setupSteps: [\n \"Requires active GitHub Copilot subscription\",\n \"Login: openacp agents run copilot (use /login command inside CLI)\",\n \"Or set token: export GITHUB_TOKEN=<personal-access-token>\",\n ],\n loginCommand: \"openacp agents run copilot\",\n },\n \"cline\": {\n setupSteps: [\n \"Setup: openacp agents run cline (guided API key setup on first run)\",\n \"Supports: Anthropic, OpenAI, Gemini, AWS Bedrock, Azure, Ollama, and more\",\n \"Or set env: export ANTHROPIC_API_KEY=<key> (or OPENAI_API_KEY, etc.)\",\n ],\n loginCommand: \"openacp agents run cline\",\n },\n \"auggie\": {\n setupSteps: [\n \"Login: openacp agents run auggie (opens browser for Augment account on first run)\",\n ],\n loginCommand: \"openacp agents run auggie\",\n },\n \"qwen-code\": {\n setupSteps: [\n \"Login: openacp agents run qwen (use /auth command, select 'Qwen OAuth')\",\n \"Free: 1000 requests/day with Qwen OAuth\",\n \"Or set API key: export OPENAI_API_KEY=<key> in ~/.qwen/settings.json\",\n ],\n loginCommand: \"openacp agents run qwen\",\n },\n\n // --- Agents requiring API keys via env vars ---\n \"kimi\": {\n setupSteps: [\n \"Login: openacp agents run kimi (use /login command inside CLI)\",\n \"Recommended: select 'Kimi Code' for browser-based OAuth\",\n \"Or select another provider and enter API key manually\",\n ],\n loginCommand: \"openacp agents run kimi\",\n },\n \"cursor\": {\n setupSteps: [\n \"Requires active Cursor subscription\",\n \"Login: openacp agents run cursor (opens browser for Cursor account)\",\n ],\n loginCommand: \"openacp agents run cursor\",\n },\n\n // --- Agents with provider selection on first run ---\n \"goose\": {\n setupSteps: [\n \"First run auto-enters setup mode — choose your LLM provider\",\n \"Options: OpenAI, Anthropic, Google Gemini, OpenRouter, or local models\",\n \"Set provider API key: export OPENAI_API_KEY=<key> (or other provider)\",\n \"Reconfigure anytime: goose configure\",\n ],\n },\n \"junie\": {\n setupSteps: [\n \"Bring Your Own Key (BYOK) — provide API key from any supported provider\",\n \"Supports: OpenAI, Anthropic, Gemini, xAI, OpenRouter\",\n \"Free tier: up to $50 with Gemini 3 Flash included\",\n \"Set key via env or first-run setup prompt\",\n ],\n },\n \"kilo\": {\n setupSteps: [\n \"Options: bring your own API keys (Anthropic, OpenAI, Google) or use Kilo Gateway\",\n \"Kilo Gateway: pay-as-you-go, includes free models — no API key needed\",\n \"BYOK: set provider key, e.g. export ANTHROPIC_API_KEY=<key>\",\n ],\n },\n \"mistral-vibe\": {\n setupSteps: [\n \"Get API key from console.mistral.ai/codestral/cli\",\n \"Or sign up for Free/Pro/Team plan at mistral.ai\",\n \"Set key when prompted on first run\",\n ],\n },\n \"deepagents\": {\n setupSteps: [\n \"Powered by LangChain — set your LLM provider API key\",\n \"Example: export OPENAI_API_KEY=<key> or export ANTHROPIC_API_KEY=<key>\",\n ],\n },\n\n // --- Agents that work out of the box (no setup / minimal setup) ---\n \"crow-cli\": {\n setupSteps: [\n \"Requires uvx (Python package runner): pip install uv\",\n \"Bring your own API key for your chosen LLM provider\",\n ],\n },\n \"fast-agent\": {\n setupSteps: [\n \"Requires uvx (Python package runner): pip install uv\",\n \"Configure LLM provider in agent config file\",\n ],\n },\n};\n\n/** Get post-install setup instructions for an agent, if any. */\nexport function getAgentSetup(registryId: string): AgentSetupInfo | undefined {\n return AGENT_SETUP[registryId];\n}\n\nconst AGENT_CAPABILITIES: Record<string, AgentCapability> = {\n claude: {\n supportsResume: true,\n resumeCommand: (sid) => `claude --resume ${sid}`,\n integration: {\n strategy: \"hooks\",\n hookEvent: \"UserPromptSubmit\",\n settingsPath: \"~/.claude/settings.json\",\n settingsFormat: \"settings_json\",\n hooksDirPath: \"~/.claude/hooks/\",\n outputFormat: \"plaintext\",\n sessionIdField: \".session_id\",\n commandsPath: \"~/.claude/commands/\",\n handoffCommandName: \"openacp:handoff\",\n commandFormat: \"markdown\",\n sessionIdVar: \"CLAUDE_SESSION_ID\",\n workingDirVar: \"CLAUDE_WORKING_DIR\",\n },\n },\n cursor: {\n supportsResume: true,\n resumeCommand: (sid) => `cursor --resume ${sid}`,\n integration: {\n strategy: \"hooks\",\n hookEvent: \"beforeSubmitPrompt\",\n settingsPath: \"~/.cursor/hooks.json\",\n settingsFormat: \"hooks_json\",\n hooksDirPath: \"~/.cursor/hooks/\",\n outputFormat: \"json\",\n sessionIdField: \".conversation_id\",\n commandsPath: \"~/.cursor/skills/\",\n handoffCommandName: \"openacp-handoff\",\n commandFormat: \"skill\",\n },\n },\n gemini: {\n supportsResume: true,\n resumeCommand: (sid) => `gemini --resume ${sid}`,\n integration: {\n strategy: \"hooks\",\n hookEvent: \"BeforeAgent\",\n settingsPath: \"~/.gemini/settings.json\",\n settingsFormat: \"settings_json\",\n hooksDirPath: \"~/.gemini/hooks/\",\n outputFormat: \"json\",\n sessionIdField: \".session_id\",\n },\n },\n cline: {\n supportsResume: true,\n resumeCommand: () => `cline --continue`,\n integration: {\n strategy: \"hooks\",\n hookEvent: \"TaskStart\",\n settingsPath: \"~/.cline/settings.json\",\n settingsFormat: \"settings_json\",\n hooksDirPath: \"~/.cline/hooks/\",\n outputFormat: \"json\",\n sessionIdField: \".session_id\",\n },\n },\n codex: {\n supportsResume: true,\n resumeCommand: (sid) => `codex resume ${sid}`,\n },\n kilo: {\n supportsResume: true,\n resumeCommand: () => `kilo --continue`,\n },\n amp: {\n supportsResume: true,\n resumeCommand: (sid) => `amp threads continue ${sid}`,\n },\n opencode: {\n supportsResume: true,\n resumeCommand: (sid) => `opencode --session ${sid}`,\n integration: {\n strategy: \"plugin\",\n pluginProvider: \"opencode\",\n commandsPath: \"~/.config/opencode/commands/\",\n pluginsPath: \"~/.config/opencode/plugins/\",\n handoffCommandName: \"openacp:handoff\",\n handoffCommandFile: \"openacp-handoff.md\",\n pluginFileName: \"openacp-handoff.js\",\n },\n },\n};\n\n/** Maps registry IDs to short user-facing names used as keys in agents.json. */\nexport const REGISTRY_AGENT_ALIASES: Record<string, string> = {\n \"claude-acp\": \"claude\",\n \"codex-acp\": \"codex\",\n \"gemini\": \"gemini\",\n \"cursor\": \"cursor\",\n \"github-copilot-cli\": \"copilot\",\n \"cline\": \"cline\",\n \"goose\": \"goose\",\n \"kilo\": \"kilo\",\n \"qwen-code\": \"qwen\",\n};\n\n/** Convert a registry ID to its short alias, or return the ID if no alias exists. */\nexport function getAgentAlias(registryId: string): string {\n return REGISTRY_AGENT_ALIASES[registryId] ?? registryId;\n}\n\n/** Get the external CLI dependencies for an agent (empty if none required). */\nexport function getAgentDependencies(registryId: string): AgentDependency[] {\n return AGENT_DEPENDENCIES[registryId] ?? [];\n}\n\n/** Look up static capabilities for an agent by its short name. */\nexport function getAgentCapabilities(agentName: string): AgentCapability {\n return AGENT_CAPABILITIES[agentName] ?? { supportsResume: false };\n}\n\n/** List agent names that have a hooks or plugin integration spec defined. */\nexport function listAgentsWithIntegration(): string[] {\n return Object.entries(AGENT_CAPABILITIES)\n .filter(([, cap]) => cap.integration != null)\n .map(([key]) => key);\n}\n\n/**\n * Check if a command is available on the system.\n *\n * Checks PATH via `which` first, then walks up from cwd looking for\n * the command in `node_modules/.bin/` directories.\n */\nexport function 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\n/** Check if all external dependencies for an agent are installed on this system. */\nexport function checkDependencies(registryId: string): AvailabilityResult {\n const deps = getAgentDependencies(registryId);\n if (deps.length === 0) return { available: true };\n\n const missing = deps.filter((d) => !commandExists(d.command));\n if (missing.length === 0) return { available: true };\n\n return {\n available: false,\n reason: `Requires: ${missing.map((m) => m.label).join(\", \")}`,\n missing: missing.map((m) => ({ label: m.label, installHint: m.installHint })),\n };\n}\n\n/** Check if a package runner (npx or uvx) is available on this system. */\nexport function checkRuntimeAvailable(runtime: \"npx\" | \"uvx\"): boolean {\n return commandExists(runtime);\n}\n","/** @deprecated Re-exports from agent-dependencies.ts for backward compatibility. */\nexport { getAgentCapabilities } from \"./agent-dependencies.js\";\nexport type { AgentCapability } from \"./agent-dependencies.js\";\n","/**\n * Agent installation and uninstallation logic.\n *\n * Handles three distribution types:\n * - **npx** — Node package runner (no download, resolved on first use)\n * - **uvx** — Python package runner (no download, resolved on first use)\n * - **binary** — pre-built archive downloaded and extracted to `~/.openacp/agents/`\n *\n * Binary archives are validated before and after extraction to prevent\n * path traversal attacks and symlink escapes.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport crypto from \"node:crypto\";\nimport { createChildLogger } from \"../utils/log.js\";\nimport type { InstalledAgent, RegistryAgent, InstallProgress, InstallResult } from \"../types.js\";\nimport { getAgentAlias, checkDependencies, checkRuntimeAvailable, getAgentSetup } from \"./agent-dependencies.js\";\nimport { AgentStore } from \"./agent-store.js\";\n\nconst log = createChildLogger({ module: \"agent-installer\" });\n\nconst DEFAULT_AGENTS_DIR = path.join(os.homedir(), \".openacp\", \"agents\");\n\nexport const MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024; // 500MB\n\n/** Verify SHA-256 checksum of a downloaded binary archive. */\nexport function verifyChecksum(buffer: Buffer, expectedHash: string): void {\n const actualHash = crypto.createHash(\"sha256\").update(buffer).digest(\"hex\");\n if (actualHash !== expectedHash) {\n throw new Error(\n `Integrity check failed: expected ${expectedHash}, got ${actualHash}`,\n );\n }\n}\n\n/**\n * Validate archive entries before extraction to prevent path traversal attacks.\n *\n * Rejects entries containing `..` path segments or absolute paths that could\n * write files outside the destination directory.\n */\nexport function validateArchiveContents(entries: string[], destDir: string): void {\n for (const entry of entries) {\n // Check for path traversal segments, not just substring — avoids false positives\n // on filenames like \"setup..sh\" or \"..config\" that are not traversal attacks.\n const segments = entry.split(\"/\");\n if (segments.includes(\"..\")) {\n throw new Error(`Archive contains unsafe path traversal: ${entry}`);\n }\n if (entry.startsWith(\"/\")) {\n throw new Error(`Archive contains unsafe absolute path: ${entry}`);\n }\n }\n}\n\n/** @deprecated Use validateArchiveContents instead */\nexport const validateTarContents = validateArchiveContents;\n\n/** Safety check: refuse to delete paths outside the agents directory during uninstall. */\nexport function validateUninstallPath(binaryPath: string, agentsDir: string): void {\n const realPath = path.resolve(binaryPath);\n const realAgentsDir = path.resolve(agentsDir);\n if (!realPath.startsWith(realAgentsDir + path.sep) && realPath !== realAgentsDir) {\n throw new Error(`Refusing to delete path outside agents directory: ${realPath}`);\n }\n}\n\n// Map Node's os.arch/platform values to the naming convention used by the\n// ACP agent registry's binary distribution targets.\nconst ARCH_MAP: Record<string, string> = {\n arm64: \"aarch64\",\n x64: \"x86_64\",\n};\n\nconst PLATFORM_MAP: Record<string, string> = {\n darwin: \"darwin\",\n linux: \"linux\",\n win32: \"windows\",\n};\n\n/** Build a platform-arch key (e.g., \"darwin-aarch64\") for binary distribution lookup. */\nexport function getPlatformKey(): string {\n const platform = PLATFORM_MAP[process.platform] ?? process.platform;\n const arch = ARCH_MAP[process.arch] ?? process.arch;\n return `${platform}-${arch}`;\n}\n\nexport type ResolvedDistribution =\n | { type: \"npx\"; package: string; args: string[]; env?: Record<string, string> }\n | { type: \"uvx\"; package: string; args: string[]; env?: Record<string, string> }\n | { type: \"binary\"; archive: string; cmd: string; args: string[]; env?: Record<string, string> };\n\n/**\n * Determine how to run an agent on this platform.\n *\n * Returns the distribution type (npx/uvx/binary) with all necessary\n * metadata, or null if no distribution exists for the current platform.\n */\nexport function resolveDistribution(agent: RegistryAgent): ResolvedDistribution | null {\n const dist = agent.distribution;\n\n if (dist.npx) {\n return { type: \"npx\", package: dist.npx.package, args: dist.npx.args ?? [], env: dist.npx.env };\n }\n if (dist.uvx) {\n return { type: \"uvx\", package: dist.uvx.package, args: dist.uvx.args ?? [], env: dist.uvx.env };\n }\n if (dist.binary) {\n const platformKey = getPlatformKey();\n const target = dist.binary[platformKey];\n if (!target) return null;\n return { type: \"binary\", archive: target.archive, cmd: target.cmd, args: target.args ?? [], env: target.env };\n }\n return null;\n}\n\n/**\n * Build an InstalledAgent record from a resolved distribution.\n *\n * For npx/uvx agents, version pins are stripped from the package name so\n * the runner always resolves to the latest version at runtime.\n */\nexport function buildInstalledAgent(\n registryId: string,\n name: string,\n version: string,\n dist: ResolvedDistribution,\n binaryPath?: string,\n): InstalledAgent {\n if (dist.type === \"npx\") {\n // Use latest version: strip pinned version from package name (e.g. @google/gemini-cli@0.34.0 → @google/gemini-cli)\n const npxPackage = stripPackageVersion(dist.package);\n return {\n registryId, name, version, distribution: \"npx\",\n command: \"npx\", args: [npxPackage, ...dist.args],\n env: dist.env ?? {}, installedAt: new Date().toISOString(), binaryPath: null,\n };\n }\n if (dist.type === \"uvx\") {\n // Strip pinned version: \"fast-agent-acp==0.6.6\" → \"fast-agent-acp\", \"minion-code@0.1.44\" → \"minion-code\"\n const uvxPackage = stripPythonPackageVersion(dist.package);\n return {\n registryId, name, version, distribution: \"uvx\",\n command: \"uvx\", args: [uvxPackage, ...dist.args],\n env: dist.env ?? {}, installedAt: new Date().toISOString(), binaryPath: null,\n };\n }\n // binary\n const absCmd = path.resolve(binaryPath!, dist.cmd);\n return {\n registryId, name, version, distribution: \"binary\",\n command: absCmd, args: dist.args,\n env: dist.env ?? {}, installedAt: new Date().toISOString(), binaryPath: binaryPath!,\n };\n}\n\n/**\n * Strip pinned version from npm package name so npx always uses latest.\n * e.g. \"@google/gemini-cli@0.34.0\" → \"@google/gemini-cli\"\n * \"cline@2.9.0\" → \"cline\"\n * \"@scope/pkg\" → \"@scope/pkg\" (no version, unchanged)\n */\nfunction stripPackageVersion(pkg: string): string {\n // Scoped: @scope/name@version → find the second @\n if (pkg.startsWith(\"@\")) {\n const afterScope = pkg.indexOf(\"/\");\n if (afterScope === -1) return pkg;\n const versionAt = pkg.indexOf(\"@\", afterScope + 1);\n return versionAt === -1 ? pkg : pkg.slice(0, versionAt);\n }\n // Unscoped: name@version\n const at = pkg.indexOf(\"@\");\n return at === -1 ? pkg : pkg.slice(0, at);\n}\n\n/**\n * Strip pinned version from Python package name so uvx always uses latest.\n * e.g. \"fast-agent-acp==0.6.6\" → \"fast-agent-acp\"\n * \"minion-code@0.1.44\" → \"minion-code\"\n * \"crow-cli\" → \"crow-cli\" (no version, unchanged)\n */\nfunction stripPythonPackageVersion(pkg: string): string {\n // Python-style: name==version or name>=version\n const pyMatch = pkg.match(/^([^=@><!]+)/);\n if (pyMatch && pkg.includes(\"==\")) return pyMatch[1]!;\n // npm-style @ used in some uvx packages\n const at = pkg.indexOf(\"@\");\n return at === -1 ? pkg : pkg.slice(0, at);\n}\n\n/**\n * Install an agent from the registry.\n *\n * Steps:\n * 1. Resolve distribution type for the current platform\n * 2. Check runtime availability (uvx/npx must exist)\n * 3. Check external CLI dependencies (non-blocking — surfaced as setup steps)\n * 4. Download and extract binary archives (binary type only)\n * 5. Save to the agent store\n * 6. Return setup instructions for post-install configuration\n */\nexport async function installAgent(\n agent: RegistryAgent,\n store: AgentStore,\n progress?: InstallProgress,\n agentsDir?: string,\n): Promise<InstallResult> {\n const agentKey = getAgentAlias(agent.id);\n await progress?.onStart(agent.id, agent.name);\n\n // 1. Resolve distribution\n const dist = resolveDistribution(agent);\n if (!dist) {\n const platformKey = getPlatformKey();\n const msg = `${agent.name} is not available for your system (${platformKey}). Check their website for other install options.`;\n await progress?.onError(msg);\n return { ok: false, agentKey, error: msg };\n }\n\n // 2. Check runtime availability (hard requirement — uvx/npx must exist to run)\n if (dist.type === \"uvx\" && !checkRuntimeAvailable(\"uvx\")) {\n const msg = `${agent.name} requires Python's uvx tool.\\nInstall it with: pip install uv`;\n await progress?.onError(msg, \"pip install uv\");\n return { ok: false, agentKey, error: msg, hint: \"pip install uv\" };\n }\n\n // 3. Check external CLI dependencies (non-blocking — install proceeds, setup steps\n // guide the user to install required CLIs afterward)\n const depResult = checkDependencies(agent.id);\n\n // 4. Install based on distribution type\n let binaryPath: string | undefined;\n\n if (dist.type === \"binary\") {\n try {\n binaryPath = await downloadAndExtract(agent.id, dist.archive, progress, agentsDir);\n } catch (err) {\n const msg = `Failed to download ${agent.name}. Please try again or install manually.`;\n await progress?.onError(msg);\n return { ok: false, agentKey, error: msg };\n }\n } else {\n await progress?.onStep(\"Setting up... (will download on first use)\");\n }\n\n // 5. Save to store\n const installed = buildInstalledAgent(agent.id, agent.name, agent.version, dist, binaryPath);\n store.addAgent(agentKey, installed);\n\n // 6. Build setup steps: prefer agent-specific steps; fall back to dep install hints\n const setup = getAgentSetup(agent.id);\n const setupSteps = setup?.setupSteps ?? (\n depResult.missing?.map((m) => `${m.label}: ${m.installHint}`) ?? []\n );\n\n await progress?.onSuccess(agent.name);\n return { ok: true, agentKey, setupSteps: setupSteps.length > 0 ? setupSteps : undefined };\n}\n\nasync function downloadAndExtract(\n agentId: string,\n archiveUrl: string,\n progress?: InstallProgress,\n agentsDir?: string,\n): Promise<string> {\n const destDir = path.join(agentsDir ?? DEFAULT_AGENTS_DIR, agentId);\n fs.mkdirSync(destDir, { recursive: true });\n\n await progress?.onStep(\"Downloading...\");\n log.info({ agentId, url: archiveUrl }, \"Downloading agent binary\");\n\n const response = await fetch(archiveUrl);\n if (!response.ok) {\n throw new Error(`Download failed: ${response.status} ${response.statusText}`);\n }\n\n const contentLength = Number(response.headers.get(\"content-length\") || 0);\n const buffer = await readResponseWithProgress(response, contentLength, progress);\n\n await progress?.onStep(\"Extracting...\");\n\n if (archiveUrl.endsWith(\".zip\")) {\n await extractZip(buffer, destDir);\n } else {\n await extractTarGz(buffer, destDir);\n }\n\n await progress?.onStep(\"Ready!\");\n return destDir;\n}\n\n/**\n * Read a fetch response body with download progress reporting and size limit enforcement.\n *\n * Streams chunks incrementally to avoid buffering the entire response\n * in memory before checking the size.\n */\nexport async function readResponseWithProgress(\n response: Response,\n contentLength: number,\n progress?: InstallProgress,\n): Promise<Buffer> {\n if (!response.body) {\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n }\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let received = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n received += value.length;\n if (received > MAX_DOWNLOAD_SIZE) {\n throw new Error(`Download exceeds size limit of ${MAX_DOWNLOAD_SIZE} bytes`);\n }\n if (contentLength > 0) {\n await progress?.onDownloadProgress(Math.round((received / contentLength) * 100));\n }\n }\n\n return Buffer.concat(chunks);\n}\n\n/**\n * Post-extraction safety check: verify all extracted files resolve within\n * the destination directory. Catches symlink-based escapes that wouldn't\n * be detected by pre-extraction path validation alone.\n */\nfunction validateExtractedPaths(destDir: string): void {\n const realDest = fs.realpathSync(destDir);\n const entries = fs.readdirSync(destDir, { recursive: true, withFileTypes: true });\n for (const entry of entries) {\n // Node <20.12 uses `path`, >=20.12 uses `parentPath` on Dirent with recursive readdir\n const dirent = entry as fs.Dirent & { parentPath?: string; path?: string };\n const parentPath = dirent.parentPath ?? dirent.path ?? destDir;\n const fullPath = path.join(parentPath, entry.name);\n let realPath: string;\n try {\n realPath = fs.realpathSync(fullPath);\n } catch {\n // Broken symlink — check where it points\n const linkTarget = fs.readlinkSync(fullPath);\n realPath = path.resolve(path.dirname(fullPath), linkTarget);\n }\n if (!realPath.startsWith(realDest + path.sep) && realPath !== realDest) {\n fs.rmSync(destDir, { recursive: true, force: true });\n throw new Error(`Archive contains unsafe path: ${entry.name}`);\n }\n }\n}\n\nasync function extractTarGz(buffer: Buffer, destDir: string): Promise<void> {\n const { execFileSync } = await import(\"node:child_process\");\n const tmpFile = path.join(destDir, \"_archive.tar.gz\");\n fs.writeFileSync(tmpFile, buffer);\n try {\n // Validate contents BEFORE extraction\n const listing = execFileSync(\"tar\", [\"tf\", tmpFile], { stdio: \"pipe\" })\n .toString().trim().split(\"\\n\").filter(Boolean);\n validateArchiveContents(listing, destDir);\n // Safe to extract\n execFileSync(\"tar\", [\"xzf\", tmpFile, \"-C\", destDir], { stdio: \"pipe\" });\n } finally {\n fs.unlinkSync(tmpFile);\n }\n validateExtractedPaths(destDir);\n}\n\nasync function extractZip(buffer: Buffer, destDir: string): Promise<void> {\n const { execFileSync } = await import(\"node:child_process\");\n const tmpFile = path.join(destDir, \"_archive.zip\");\n fs.writeFileSync(tmpFile, buffer);\n try {\n // Validate contents BEFORE extraction\n const listing = execFileSync(\"unzip\", [\"-l\", tmpFile], { stdio: \"pipe\" })\n .toString().trim().split(\"\\n\").filter(Boolean);\n // unzip -l output has header/footer lines; file paths are in the 4th column\n const entries = listing\n .slice(3, -2) // skip header (3 lines) and footer (2 lines)\n .map((line) => line.trim().split(/\\s+/).slice(3).join(\" \"))\n .filter(Boolean);\n validateArchiveContents(entries, destDir);\n // Safe to extract\n execFileSync(\"unzip\", [\"-o\", tmpFile, \"-d\", destDir], { stdio: \"pipe\" });\n } finally {\n fs.unlinkSync(tmpFile);\n }\n validateExtractedPaths(destDir);\n}\n\n/** Remove an agent from the store and delete its binary directory if present. */\nexport async function uninstallAgent(\n agentKey: string,\n store: AgentStore,\n agentsDir?: string,\n): Promise<void> {\n const agent = store.getAgent(agentKey);\n if (!agent) return;\n\n if (agent.binaryPath && fs.existsSync(agent.binaryPath)) {\n validateUninstallPath(agent.binaryPath, agentsDir ?? DEFAULT_AGENTS_DIR);\n fs.rmSync(agent.binaryPath, { recursive: true, force: true });\n log.info({ agentKey, binaryPath: agent.binaryPath }, \"Deleted agent binary\");\n }\n\n store.removeAgent(agentKey);\n}\n","/**\n * Doctor check: Config — validates config.json exists, is parseable JSON,\n * passes Zod schema validation, and has no pending migrations.\n *\n * Pending migrations are auto-fixable (safe). This check runs first (order 1)\n * because most other checks depend on a valid config.\n */\n\nimport * as fs from \"node:fs\";\nimport { ConfigSchema } from \"../../config/config.js\";\nimport { applyMigrations } from \"../../config/config-migrations.js\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nexport const configCheck: DoctorCheck = {\n name: \"Config\",\n order: 1,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (!fs.existsSync(ctx.configPath)) {\n results.push({ status: \"fail\", message: \"Config file not found\" });\n return results;\n }\n results.push({ status: \"pass\", message: \"Config file exists\" });\n\n let raw: Record<string, unknown>;\n try {\n raw = JSON.parse(fs.readFileSync(ctx.configPath, \"utf-8\"));\n } catch (err) {\n results.push({\n status: \"fail\",\n message: `Config JSON invalid: ${err instanceof Error ? err.message : String(err)}`,\n });\n return results;\n }\n results.push({ status: \"pass\", message: \"JSON valid\" });\n\n const testRaw = structuredClone(raw);\n const { changed } = applyMigrations(testRaw);\n if (changed) {\n results.push({\n status: \"warn\",\n message: \"Pending config migrations\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n applyMigrations(raw);\n fs.writeFileSync(ctx.configPath, JSON.stringify(raw, null, 2));\n return { success: true, message: \"applied migrations\" };\n },\n });\n }\n\n const result = ConfigSchema.safeParse(raw);\n if (!result.success) {\n for (const issue of result.error.issues) {\n results.push({\n status: \"fail\",\n message: `Validation: ${issue.path.join(\".\")} — ${issue.message}`,\n });\n }\n } else {\n results.push({ status: \"pass\", message: \"Schema valid\" });\n }\n\n return results;\n },\n};\n","/**\n * Doctor check: Agents — verifies configured agents are installed and\n * their commands are accessible via PATH or local node_modules/.bin.\n * Missing the default agent is a \"fail\"; missing optional agents are \"warn\".\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\n/** Checks PATH first, then walks up the directory tree looking for node_modules/.bin. */\nfunction commandExists(cmd: string): boolean {\n try {\n execFileSync(\"which\", [cmd], { stdio: \"pipe\" });\n return true;\n } catch {\n // not in PATH — fall through to node_modules check\n }\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 const agentsCheck: DoctorCheck = {\n name: \"Agents\",\n order: 2,\n async run(ctx) {\n const results: CheckResult[] = [];\n if (!ctx.config) {\n results.push({ status: \"fail\", message: \"Cannot check agents — config not loaded\" });\n return results;\n }\n\n const defaultAgent = ctx.config.defaultAgent;\n\n // Read agents from agents.json (agents were migrated out of config.json)\n let agents: Record<string, { command: string }> = {};\n try {\n const agentsPath = path.join(ctx.dataDir, \"agents.json\");\n if (fs.existsSync(agentsPath)) {\n const data = JSON.parse(fs.readFileSync(agentsPath, \"utf-8\"));\n agents = data.installed ?? {};\n }\n } catch { /* proceed with empty agents */ }\n\n if (!agents[defaultAgent]) {\n results.push({\n status: \"fail\",\n message: `Default agent \"${defaultAgent}\" not found in agents config`,\n });\n }\n\n for (const [name, agent] of Object.entries(agents)) {\n const isDefault = name === defaultAgent;\n const agentEntry = agent as { command?: string };\n const agentCommand = agentEntry.command ?? name;\n if (commandExists(agentCommand)) {\n results.push({\n status: \"pass\",\n message: `${agentCommand} found${isDefault ? \" (default)\" : \"\"}`,\n });\n } else {\n results.push({\n status: isDefault ? \"fail\" : \"warn\",\n message: `${agentCommand} not found in PATH${isDefault ? \" (default agent!)\" : \"\"}`,\n });\n }\n }\n\n return results;\n },\n};\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { SettingsAPI } from './types.js'\nimport type { ZodSchema } from 'zod'\n\n/** Result of validating plugin settings against a Zod schema. */\nexport interface ValidationResult {\n valid: boolean\n errors?: string[]\n}\n\n/**\n * Manages per-plugin settings files.\n *\n * Each plugin's settings are stored at `<basePath>/<pluginName>/settings.json`.\n * The basePath is typically `~/.openacp/plugins/`.\n * Settings are distinct from plugin storage (kv.json) — settings are user-facing\n * configuration, while storage is internal plugin state.\n */\nexport class SettingsManager {\n constructor(private basePath: string) {}\n\n /** Returns the base path for all plugin settings directories. */\n getBasePath(): string {\n return this.basePath\n }\n\n /** Create a SettingsAPI instance scoped to a specific plugin. */\n createAPI(pluginName: string): SettingsAPI {\n const settingsPath = this.getSettingsPath(pluginName)\n return new SettingsAPIImpl(settingsPath)\n }\n\n /** Load a plugin's settings from disk. Returns empty object if file doesn't exist. */\n async loadSettings(pluginName: string): Promise<Record<string, unknown>> {\n const settingsPath = this.getSettingsPath(pluginName)\n try {\n const content = fs.readFileSync(settingsPath, 'utf-8')\n return JSON.parse(content)\n } catch {\n return {}\n }\n }\n\n /** Validate settings against a Zod schema. Returns valid if no schema is provided. */\n validateSettings(\n _pluginName: string,\n settings: unknown,\n schema?: ZodSchema,\n ): ValidationResult {\n if (!schema) return { valid: true }\n const result = schema.safeParse(settings)\n if (result.success) return { valid: true }\n return {\n valid: false,\n errors: result.error.issues.map(\n (e) => `${e.path.map(String).join('.')}: ${e.message}`,\n ),\n }\n }\n\n /** Resolve the absolute path to a plugin's settings.json file. */\n getSettingsPath(pluginName: string): string {\n return path.join(this.basePath, pluginName, 'settings.json')\n }\n\n async getPluginSettings(pluginName: string): Promise<Record<string, unknown>> {\n return this.loadSettings(pluginName)\n }\n\n /** Merge updates into existing settings (shallow merge). */\n async updatePluginSettings(pluginName: string, updates: Record<string, unknown>): Promise<void> {\n const api = this.createAPI(pluginName)\n const current = await api.getAll()\n await api.setAll({ ...current, ...updates })\n }\n}\n\n/**\n * File-backed implementation of SettingsAPI for a single plugin.\n * Reads/writes JSON synchronously with an in-memory cache to avoid\n * redundant disk reads within the same lifecycle.\n */\nclass SettingsAPIImpl implements SettingsAPI {\n private cache: Record<string, unknown> | null = null\n\n constructor(private settingsPath: string) {}\n\n private readFile(): Record<string, unknown> {\n if (this.cache !== null) return this.cache\n try {\n const content = fs.readFileSync(this.settingsPath, 'utf-8')\n this.cache = JSON.parse(content)\n return this.cache!\n } catch {\n this.cache = {}\n return this.cache\n }\n }\n\n private writeFile(data: Record<string, unknown>): void {\n const dir = path.dirname(this.settingsPath)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(this.settingsPath, JSON.stringify(data, null, 2))\n this.cache = data\n }\n\n async get<T = unknown>(key: string): Promise<T | undefined> {\n const data = this.readFile()\n return data[key] as T | undefined\n }\n\n async set<T = unknown>(key: string, value: T): Promise<void> {\n const data = this.readFile()\n data[key] = value\n this.writeFile(data)\n }\n\n async getAll(): Promise<Record<string, unknown>> {\n return { ...this.readFile() }\n }\n\n async setAll(settings: Record<string, unknown>): Promise<void> {\n this.writeFile({ ...settings })\n }\n\n async delete(key: string): Promise<void> {\n const data = this.readFile()\n delete data[key]\n this.writeFile(data)\n }\n\n async clear(): Promise<void> {\n this.writeFile({})\n }\n\n async has(key: string): Promise<boolean> {\n const data = this.readFile()\n return key in data\n }\n}\n","/**\n * Doctor check: Telegram — validates bot token, chat ID, and bot permissions.\n *\n * Performs live API calls to verify:\n * 1. Bot token format matches Telegram's pattern\n * 2. Bot token is accepted by the Telegram API (getMe)\n * 3. Chat ID points to a valid supergroup with topics enabled\n * 4. Bot has administrator privileges in the group\n *\n * Skipped if Telegram is not configured (no bot token or chat ID in settings).\n */\n\nimport * as path from \"node:path\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\n/** Telegram bot tokens follow the pattern: <bot_id>:<alphanumeric_secret> */\nconst BOT_TOKEN_REGEX = /^\\d+:[A-Za-z0-9_-]{35,}$/;\n\nexport const telegramCheck: DoctorCheck = {\n name: \"Telegram\",\n order: 3,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (!ctx.config) {\n results.push({ status: \"fail\", message: \"Cannot check Telegram — config not loaded\" });\n return results;\n }\n\n // Read Telegram settings from plugin settings (migrated out of config.json)\n const { SettingsManager } = await import(\"../../plugin/settings-manager.js\");\n const sm = new SettingsManager(path.join(ctx.pluginsDir, \"data\"));\n const ps = await sm.loadSettings(\"@openacp/telegram\");\n\n const botToken = ps.botToken as string | undefined;\n const chatId = ps.chatId as number | undefined;\n\n if (!botToken && !chatId) {\n results.push({ status: \"pass\", message: \"Telegram not configured (skipped)\" });\n return results;\n }\n\n if (!botToken || !BOT_TOKEN_REGEX.test(botToken)) {\n results.push({ status: \"fail\", message: \"Bot token format invalid\" });\n return results;\n }\n results.push({ status: \"pass\", message: \"Bot token format valid\" });\n\n let botId: number | undefined;\n try {\n const res = await fetch(`https://api.telegram.org/bot${botToken}/getMe`);\n const data = (await res.json()) as { ok: boolean; result?: { id: number; username: string }; description?: string };\n if (data.ok && data.result) {\n botId = data.result.id;\n results.push({ status: \"pass\", message: `Bot token valid (@${data.result.username})` });\n } else {\n results.push({ status: \"fail\", message: `Bot token rejected: ${data.description || \"unknown error\"}` });\n return results;\n }\n } catch (err) {\n results.push({ status: \"fail\", message: `Cannot reach Telegram API: ${err instanceof Error ? err.message : String(err)}` });\n return results;\n }\n\n if (!chatId || chatId === 0) {\n results.push({ status: \"fail\", message: \"Chat ID not configured\" });\n return results;\n }\n\n try {\n const res = await fetch(`https://api.telegram.org/bot${botToken}/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?: { type: string; is_forum?: boolean; title: string };\n description?: string;\n };\n if (!data.ok || !data.result) {\n results.push({ status: \"fail\", message: `Chat ID invalid: ${data.description || \"unknown error\"}` });\n return results;\n }\n if (data.result.type !== \"supergroup\") {\n results.push({ status: \"fail\", message: `Chat is \"${data.result.type}\", must be a supergroup` });\n return results;\n }\n if (!data.result.is_forum) {\n results.push({ status: \"warn\", message: \"Chat does not have topics enabled\" });\n } else {\n results.push({ status: \"pass\", message: `Chat is supergroup with topics (\"${data.result.title}\")` });\n }\n } catch (err) {\n results.push({ status: \"fail\", message: `Cannot validate chat: ${err instanceof Error ? err.message : String(err)}` });\n return results;\n }\n\n try {\n const res = await fetch(`https://api.telegram.org/bot${botToken}/getChatMember`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ chat_id: chatId, user_id: botId }),\n });\n const data = (await res.json()) as { ok: boolean; result?: { status: string }; description?: string };\n if (!data.ok || !data.result) {\n results.push({ status: \"fail\", message: `Cannot check bot membership: ${data.description || \"unknown\"}` });\n } else if (data.result.status === \"administrator\" || data.result.status === \"creator\") {\n results.push({ status: \"pass\", message: \"Bot is admin in group\" });\n } else {\n results.push({\n status: \"fail\",\n message: `Bot is \"${data.result.status}\" — must be admin. Promote bot in group settings.`,\n });\n }\n } catch (err) {\n results.push({ status: \"fail\", message: `Admin check failed: ${err instanceof Error ? err.message : String(err)}` });\n }\n\n return results;\n },\n};\n","/**\n * Doctor check: Storage — verifies the data directory (.openacp/), session\n * store, and log directory exist and are writable. Corrupt sessions.json\n * is a risky fix (resetting it drops session history).\n */\n\nimport * as fs from \"node:fs\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nexport const storageCheck: DoctorCheck = {\n name: \"Storage\",\n order: 4,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (!fs.existsSync(ctx.dataDir)) {\n results.push({\n status: \"fail\",\n message: \"Data directory ~/.openacp does not exist\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.mkdirSync(ctx.dataDir, { recursive: true });\n return { success: true, message: \"created directory\" };\n },\n });\n } else {\n try {\n fs.accessSync(ctx.dataDir, fs.constants.W_OK);\n results.push({ status: \"pass\", message: \"Data directory exists and writable\" });\n } catch {\n results.push({ status: \"fail\", message: \"Data directory not writable\" });\n }\n }\n\n if (fs.existsSync(ctx.sessionsPath)) {\n try {\n const content = fs.readFileSync(ctx.sessionsPath, \"utf-8\");\n const data = JSON.parse(content);\n if (typeof data === \"object\" && data !== null && \"sessions\" in data) {\n results.push({ status: \"pass\", message: \"Sessions file valid\" });\n } else {\n results.push({\n status: \"fail\",\n message: \"Sessions file has invalid structure\",\n fixable: true,\n fixRisk: \"risky\",\n fix: async () => {\n fs.writeFileSync(ctx.sessionsPath, JSON.stringify({ version: 1, sessions: {} }, null, 2));\n return { success: true, message: \"reset sessions file\" };\n },\n });\n }\n } catch {\n results.push({\n status: \"fail\",\n message: \"Sessions file corrupt (invalid JSON)\",\n fixable: true,\n fixRisk: \"risky\",\n fix: async () => {\n fs.writeFileSync(ctx.sessionsPath, JSON.stringify({ version: 1, sessions: {} }, null, 2));\n return { success: true, message: \"reset sessions file\" };\n },\n });\n }\n } else {\n results.push({ status: \"pass\", message: \"Sessions file not present yet (created on first session)\" });\n }\n\n if (!fs.existsSync(ctx.logsDir)) {\n results.push({\n status: \"warn\",\n message: \"Log directory does not exist\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.mkdirSync(ctx.logsDir, { recursive: true });\n return { success: true, message: \"created log directory\" };\n },\n });\n } else {\n try {\n fs.accessSync(ctx.logsDir, fs.constants.W_OK);\n results.push({ status: \"pass\", message: \"Log directory exists and writable\" });\n } catch {\n results.push({ status: \"fail\", message: \"Log directory not writable\" });\n }\n }\n\n return results;\n },\n};\n","/**\n * Doctor check: Workspace — verifies the workspace directory (parent of\n * .openacp/) exists and is writable. A missing workspace is auto-fixable.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nexport const workspaceCheck: DoctorCheck = {\n name: \"Workspace\",\n order: 5,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n // Workspace is the parent of the instance root (dataDir = .openacp/)\n const workspace = path.dirname(ctx.dataDir);\n\n if (!fs.existsSync(workspace)) {\n results.push({\n status: \"warn\",\n message: `Workspace directory does not exist: ${workspace}`,\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.mkdirSync(workspace, { recursive: true });\n return { success: true, message: \"created directory\" };\n },\n });\n } else {\n try {\n fs.accessSync(workspace, fs.constants.W_OK);\n results.push({ status: \"pass\", message: `Workspace directory exists: ${workspace}` });\n } catch {\n results.push({ status: \"fail\", message: `Workspace directory not writable: ${workspace}` });\n }\n }\n\n return results;\n },\n};\n","/**\n * Doctor check: Plugins — verifies the plugins directory exists and its\n * package.json is valid. Missing directory or package.json are auto-fixable.\n * Corrupt package.json is a risky fix (requires user confirmation since\n * resetting it loses the dependency list).\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nexport const pluginsCheck: DoctorCheck = {\n name: \"Plugins\",\n order: 6,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (!fs.existsSync(ctx.pluginsDir)) {\n results.push({\n status: \"warn\",\n message: \"Plugins directory does not exist\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.mkdirSync(ctx.pluginsDir, { recursive: true });\n fs.writeFileSync(\n path.join(ctx.pluginsDir, \"package.json\"),\n JSON.stringify({ name: \"openacp-plugins\", private: true, dependencies: {} }, null, 2),\n );\n return { success: true, message: \"initialized plugins directory\" };\n },\n });\n return results;\n }\n results.push({ status: \"pass\", message: \"Plugins directory exists\" });\n\n const pkgPath = path.join(ctx.pluginsDir, \"package.json\");\n if (!fs.existsSync(pkgPath)) {\n results.push({\n status: \"warn\",\n message: \"Plugins package.json missing\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.writeFileSync(\n pkgPath,\n JSON.stringify({ name: \"openacp-plugins\", private: true, dependencies: {} }, null, 2),\n );\n return { success: true, message: \"created package.json\" };\n },\n });\n return results;\n }\n\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf-8\"));\n const deps = pkg.dependencies || {};\n const count = Object.keys(deps).length;\n results.push({ status: \"pass\", message: `Plugins package.json valid (${count} plugins)` });\n } catch {\n results.push({\n status: \"fail\",\n message: \"Plugins package.json is invalid JSON\",\n fixable: true,\n fixRisk: \"risky\",\n fix: async () => {\n fs.writeFileSync(\n pkgPath,\n JSON.stringify({ name: \"openacp-plugins\", private: true, dependencies: {} }, null, 2),\n );\n return { success: true, message: \"reset package.json\" };\n },\n });\n }\n\n return results;\n },\n};\n","/**\n * Doctor check: Daemon — verifies daemon process state, PID file validity,\n * and API port availability. Stale PID files are auto-fixable.\n */\n\nimport * as fs from \"node:fs\";\nimport * as net from \"node:net\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\n/** Sends signal 0 to check if a process exists without killing it. */\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Attempts to bind the port — if binding fails, the port is in use. */\nfunction checkPortInUse(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = net.createServer();\n server.once(\"error\", () => resolve(true));\n server.once(\"listening\", () => {\n server.close();\n resolve(false);\n });\n server.listen(port, \"127.0.0.1\");\n });\n}\n\nexport const daemonCheck: DoctorCheck = {\n name: \"Daemon\",\n order: 7,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (fs.existsSync(ctx.pidPath)) {\n const content = fs.readFileSync(ctx.pidPath, \"utf-8\").trim();\n const pid = parseInt(content, 10);\n if (isNaN(pid)) {\n results.push({\n status: \"warn\",\n message: \"PID file contains invalid data\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.unlinkSync(ctx.pidPath);\n return { success: true, message: \"removed invalid PID file\" };\n },\n });\n } else if (!isProcessAlive(pid)) {\n results.push({\n status: \"warn\",\n message: `Stale PID file (PID ${pid} not running)`,\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.unlinkSync(ctx.pidPath);\n return { success: true, message: \"removed stale PID file\" };\n },\n });\n } else {\n results.push({ status: \"pass\", message: `Daemon running (PID ${pid})` });\n }\n }\n\n if (fs.existsSync(ctx.portFilePath)) {\n const content = fs.readFileSync(ctx.portFilePath, \"utf-8\").trim();\n const port = parseInt(content, 10);\n if (isNaN(port)) {\n results.push({\n status: \"warn\",\n message: \"Port file contains invalid data\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.unlinkSync(ctx.portFilePath);\n return { success: true, message: \"removed invalid port file\" };\n },\n });\n } else {\n results.push({ status: \"pass\", message: `Port file valid (port ${port})` });\n }\n }\n\n if (ctx.config) {\n const apiPort = 21420;\n const inUse = await checkPortInUse(apiPort);\n if (inUse) {\n if (fs.existsSync(ctx.pidPath)) {\n const pid = parseInt(fs.readFileSync(ctx.pidPath, \"utf-8\").trim(), 10);\n if (!isNaN(pid) && isProcessAlive(pid)) {\n results.push({ status: \"pass\", message: `API port ${apiPort} in use by OpenACP daemon` });\n } else {\n results.push({ status: \"warn\", message: `API port ${apiPort} in use by another process` });\n }\n } else {\n results.push({ status: \"warn\", message: `API port ${apiPort} in use by another process` });\n }\n } else {\n results.push({ status: \"pass\", message: `API port ${apiPort} available` });\n }\n }\n\n return results;\n },\n};\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport https from 'node:https'\nimport os from 'node:os'\nimport { execFileSync } from 'node:child_process'\nimport { createChildLogger } from './log.js'\nimport { commandExists } from '../agents/agent-dependencies.js'\nimport { MAX_DOWNLOAD_SIZE, validateTarContents } from '../agents/agent-installer.js'\n\nconst log = createChildLogger({ module: 'binary-installer' })\n\nconst DEFAULT_BIN_DIR = path.join(os.homedir(), '.openacp', 'bin')\nconst IS_WINDOWS = os.platform() === 'win32'\n\n/**\n * Specification for a binary that can be auto-downloaded from GitHub releases.\n *\n * Used by ensureBinary() to resolve the correct download URL for the\n * current platform/architecture and handle archive extraction if needed.\n */\nexport interface BinarySpec {\n name: string\n /** GitHub base URL for releases, e.g. \"https://github.com/jqlang/jq/releases/latest/download\" */\n githubBaseUrl: string\n /** Platform → arch → filename mapping */\n platforms: Record<string, Record<string, string>>\n /** If true, downloaded file is a .tgz archive that needs extraction */\n isArchive?: (url: string) => boolean\n}\n\n/**\n * Download a file via HTTPS with redirect following and size limit enforcement.\n *\n * Cleans up partial downloads on failure to avoid leaving corrupt files.\n */\nfunction downloadFile(url: string, dest: string, maxRedirects = 10): Promise<string> {\n return new Promise((resolve, reject) => {\n const file = fs.createWriteStream(dest)\n\n const cleanup = () => {\n try { if (fs.existsSync(dest)) fs.unlinkSync(dest) } catch { /* ignore */ }\n }\n\n https.get(url, (response) => {\n if (response.statusCode === 301 || response.statusCode === 302) {\n if (maxRedirects <= 0) {\n file.close(() => {\n cleanup()\n reject(new Error('Too many redirects'))\n })\n return\n }\n file.close(() => {\n cleanup()\n downloadFile(response.headers.location!, dest, maxRedirects - 1).then(resolve).catch(reject)\n })\n return\n }\n\n if (response.statusCode !== 200) {\n file.close(() => {\n cleanup()\n reject(new Error(`Download failed with status ${response.statusCode}`))\n })\n return\n }\n\n // Enforce size limit to prevent malicious or oversized downloads\n let totalBytes = 0\n const request = response\n\n response.on('data', (chunk: Buffer) => {\n totalBytes += chunk.length\n if (totalBytes > MAX_DOWNLOAD_SIZE) {\n request.destroy()\n file.close(() => {\n cleanup()\n reject(new Error(`Download exceeds size limit of ${MAX_DOWNLOAD_SIZE} bytes`))\n })\n }\n })\n\n response.pipe(file)\n file.on('finish', () => file.close(() => resolve(dest)))\n file.on('error', (err) => {\n file.close(() => {\n cleanup()\n reject(err)\n })\n })\n }).on('error', (err) => {\n file.close(() => {\n cleanup()\n reject(err)\n })\n })\n })\n}\n\n/** Resolve the download URL for the current platform and architecture. */\nfunction getDownloadUrl(spec: BinarySpec): string {\n const platform = os.platform()\n const arch = os.arch()\n const mapping = spec.platforms[platform]\n if (!mapping) throw new Error(`${spec.name}: unsupported platform ${platform}`)\n const binary = mapping[arch]\n if (!binary) throw new Error(`${spec.name}: unsupported architecture ${arch} for ${platform}`)\n return `${spec.githubBaseUrl}/${binary}`\n}\n\n/**\n * Ensure a binary is available, downloading it from GitHub if needed.\n *\n * Resolution order:\n * 1. System PATH — respects user's existing install\n * 2. ~/.openacp/bin/ — previously downloaded by OpenACP\n * 3. Download from GitHub releases — fetch, verify, extract if needed\n *\n * For archives (.tgz), tar contents are validated before extraction\n * to prevent path traversal attacks.\n */\nexport async function ensureBinary(spec: BinarySpec, binDir?: string): Promise<string> {\n const resolvedBinDir = binDir ?? DEFAULT_BIN_DIR\n const binName = IS_WINDOWS ? `${spec.name}.exe` : spec.name\n const binPath = path.join(resolvedBinDir, binName)\n\n // 1. Check PATH first\n if (commandExists(spec.name)) {\n log.debug({ name: spec.name }, 'Found in PATH')\n return spec.name\n }\n\n // 2. Check our bin directory\n if (fs.existsSync(binPath)) {\n if (!IS_WINDOWS) fs.chmodSync(binPath, '755')\n log.debug({ name: spec.name, path: binPath }, 'Found in ~/.openacp/bin')\n return binPath\n }\n\n // 3. Download\n log.info({ name: spec.name }, 'Not found, downloading from GitHub...')\n fs.mkdirSync(resolvedBinDir, { recursive: true })\n\n const url = getDownloadUrl(spec)\n const isArchive = spec.isArchive?.(url) ?? false\n const downloadDest = isArchive ? path.join(resolvedBinDir, `${spec.name}.tgz`) : binPath\n\n await downloadFile(url, downloadDest)\n\n if (isArchive) {\n // Validate tar contents before extraction to prevent path traversal\n const listing = execFileSync('tar', ['-tf', downloadDest], { stdio: 'pipe' })\n .toString().trim().split(\"\\n\").filter(Boolean);\n validateTarContents(listing, resolvedBinDir);\n execFileSync('tar', ['-xzf', downloadDest, '-C', resolvedBinDir], { stdio: 'pipe' })\n try { fs.unlinkSync(downloadDest) } catch { /* ignore */ }\n }\n\n // Validate the binary was actually produced\n if (!fs.existsSync(binPath)) {\n throw new Error(`${spec.name}: binary not found at ${binPath} after download/extraction. The archive structure may have changed.`)\n }\n\n if (!IS_WINDOWS) {\n fs.chmodSync(binPath, '755')\n }\n\n log.info({ name: spec.name, path: binPath }, 'Installed successfully')\n return binPath\n}\n","import { ensureBinary, type BinarySpec } from '../../../core/utils/install-binary.js'\n\n// cloudflared is not available on all systems via a package manager, and\n// users of the OpenACP managed tunnel provider don't need to install it themselves.\n// We download the official prebuilt binary from GitHub Releases on first use\n// and cache it in the plugin's bin directory.\nexport const CLOUDFLARED_SPEC: BinarySpec = {\n name: 'cloudflared',\n githubBaseUrl: 'https://github.com/cloudflare/cloudflared/releases/latest/download',\n platforms: {\n darwin: {\n x64: 'cloudflared-darwin-amd64.tgz',\n arm64: 'cloudflared-darwin-arm64.tgz',\n },\n win32: {\n x64: 'cloudflared-windows-amd64.exe',\n },\n linux: {\n x64: 'cloudflared-linux-amd64',\n arm64: 'cloudflared-linux-arm64',\n },\n },\n isArchive: (url) => url.endsWith('.tgz'),\n}\n\n/**\n * Ensure the cloudflared binary is available, downloading it if needed.\n * Returns the path to the binary.\n */\nexport async function ensureCloudflared(): Promise<string> {\n return ensureBinary(CLOUDFLARED_SPEC)\n}\n","/**\n * Doctor check: Tunnel — verifies tunnel provider binary availability and\n * port configuration when tunneling is enabled. Currently supports Cloudflare\n * tunnel (cloudflared). Missing binary is auto-fixable via download.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { execFileSync } from \"node:child_process\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nexport const tunnelCheck: DoctorCheck = {\n name: \"Tunnel\",\n order: 8,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (!ctx.config) {\n results.push({ status: \"fail\", message: \"Cannot check tunnel — config not loaded\" });\n return results;\n }\n\n // Read tunnel settings from plugin settings (tunnel migrated out of config.json)\n const { SettingsManager } = await import(\"../../plugin/settings-manager.js\");\n const sm = new SettingsManager(path.join(ctx.pluginsDir, \"data\"));\n const tunnelSettings = await sm.loadSettings(\"@openacp/tunnel\");\n const tunnelEnabled = (tunnelSettings.enabled as boolean) ?? false;\n const provider = (tunnelSettings.provider as string) ?? \"cloudflare\";\n const tunnelPort = (tunnelSettings.port as number) ?? 3100;\n\n if (!tunnelEnabled) {\n results.push({ status: \"pass\", message: \"Tunnel not enabled (skipped)\" });\n return results;\n }\n\n results.push({ status: \"pass\", message: `Tunnel provider: ${provider}` });\n\n if (provider === \"cloudflare\") {\n const binName = os.platform() === \"win32\" ? \"cloudflared.exe\" : \"cloudflared\";\n const binPath = path.join(ctx.dataDir, \"bin\", binName);\n let found = false;\n if (fs.existsSync(binPath)) {\n found = true;\n } else {\n try {\n execFileSync(\"which\", [\"cloudflared\"], { stdio: \"pipe\" });\n found = true;\n } catch {\n // not found\n }\n }\n\n if (found) {\n results.push({ status: \"pass\", message: \"cloudflared binary found\" });\n } else {\n results.push({\n status: \"warn\",\n message: \"cloudflared binary not found\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n try {\n const { ensureCloudflared } = await import(\"../../../plugins/tunnel/providers/install-cloudflared.js\");\n await ensureCloudflared();\n return { success: true, message: \"installed cloudflared\" };\n } catch (err) {\n return { success: false, message: err instanceof Error ? err.message : String(err) };\n }\n },\n });\n }\n }\n\n // tunnelPort already set from plugin settings above\n if (tunnelPort < 1 || tunnelPort > 65535) {\n results.push({ status: \"fail\", message: `Invalid tunnel port: ${tunnelPort}` });\n } else {\n results.push({ status: \"pass\", message: `Tunnel port: ${tunnelPort}` });\n }\n\n return results;\n },\n};\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { DoctorCheck, DoctorContext, DoctorReport, CategoryResult, PendingFix, CheckResult } from \"./types.js\";\nimport { ConfigManager, expandHome } from \"../config/config.js\";\n\nimport { configCheck } from \"./checks/config.js\";\nimport { agentsCheck } from \"./checks/agents.js\";\nimport { telegramCheck } from \"./checks/telegram.js\";\nimport { storageCheck } from \"./checks/storage.js\";\nimport { workspaceCheck } from \"./checks/workspace.js\";\nimport { pluginsCheck } from \"./checks/plugins.js\";\nimport { daemonCheck } from \"./checks/daemon.js\";\nimport { tunnelCheck } from \"./checks/tunnel.js\";\n\n/** All registered checks, sorted by order before execution. */\nconst ALL_CHECKS: DoctorCheck[] = [\n configCheck,\n agentsCheck,\n telegramCheck,\n storageCheck,\n workspaceCheck,\n pluginsCheck,\n daemonCheck,\n tunnelCheck,\n];\n\n/** Per-check timeout — prevents a single hanging check from blocking the entire run. */\nconst CHECK_TIMEOUT_MS = 10_000;\n\n/**\n * Orchestrates diagnostic checks for an OpenACP installation.\n *\n * Runs all registered checks in order, collects results, and automatically\n * applies safe fixes (unless in dry-run mode). Risky fixes are collected\n * as `pendingFixes` for the CLI to present interactively.\n */\nexport class DoctorEngine {\n private dryRun: boolean;\n private dataDir: string;\n\n constructor(options?: { dryRun?: boolean; dataDir?: string }) {\n this.dryRun = options?.dryRun ?? false;\n this.dataDir = options!.dataDir!;\n }\n\n /**\n * Executes all checks and returns an aggregated report.\n *\n * Safe fixes are applied inline (mutating CheckResult.message to show \"Fixed\").\n * Risky fixes are deferred to `report.pendingFixes` for user confirmation.\n */\n async runAll(): Promise<DoctorReport> {\n const ctx = await this.buildContext();\n const checks = [...ALL_CHECKS].sort((a, b) => a.order - b.order);\n\n const categories: CategoryResult[] = [];\n const pendingFixes: PendingFix[] = [];\n const summary = { passed: 0, warnings: 0, failed: 0, fixed: 0 };\n\n for (const check of checks) {\n let results: CheckResult[];\n try {\n results = await Promise.race([\n check.run(ctx),\n new Promise<CheckResult[]>((_, reject) =>\n setTimeout(() => reject(new Error(\"timeout\")), CHECK_TIMEOUT_MS),\n ),\n ]);\n } catch {\n results = [{ status: \"fail\", message: `${check.name} check timed out` }];\n }\n\n for (const result of results) {\n if (result.fixable && result.fix) {\n if (result.fixRisk === \"safe\" && !this.dryRun) {\n try {\n const fixResult = await result.fix();\n if (fixResult.success) {\n result.message += ` → Fixed (${fixResult.message})`;\n result.status = \"warn\";\n delete result.fix;\n summary.fixed++;\n }\n } catch {\n // Fix failed, leave as-is\n }\n } else if (result.fixRisk === \"risky\") {\n pendingFixes.push({\n category: check.name,\n message: result.message,\n fix: result.fix,\n });\n }\n }\n\n if (result.status === \"pass\") summary.passed++;\n else if (result.status === \"warn\") summary.warnings++;\n else if (result.status === \"fail\") summary.failed++;\n }\n\n categories.push({ name: check.name, results });\n }\n\n return { categories, summary, pendingFixes };\n }\n\n /** Constructs the shared context used by all checks — loads config if available. */\n private async buildContext(): Promise<DoctorContext> {\n const dataDir = this.dataDir;\n const configPath = process.env.OPENACP_CONFIG_PATH || path.join(dataDir, \"config.json\");\n\n let config = null;\n let rawConfig: unknown = null;\n\n try {\n const content = fs.readFileSync(configPath, \"utf-8\");\n rawConfig = JSON.parse(content);\n const cm = new ConfigManager(configPath);\n await cm.load();\n config = cm.get();\n } catch {\n // Config may not exist or may be invalid — checks will handle this\n }\n\n const logsDir = config\n ? expandHome(config.logging.logDir)\n : path.join(dataDir, \"logs\");\n\n return {\n config,\n rawConfig,\n configPath,\n dataDir,\n sessionsPath: path.join(dataDir, \"sessions.json\"),\n pidPath: path.join(dataDir, \"openacp.pid\"),\n portFilePath: path.join(dataDir, \"api.port\"),\n pluginsDir: path.join(dataDir, \"plugins\"),\n logsDir,\n };\n }\n}\n\nexport type { DoctorReport, CategoryResult, PendingFix, CheckResult, DoctorContext } from \"./types.js\";\n","import path from 'node:path'\nimport fs from 'node:fs'\nimport os from 'node:os'\n\n/**\n * Describes a single OpenACP instance and all its filesystem paths.\n *\n * An \"instance\" is one running OpenACP server process, identified by a\n * unique ID. Multiple instances can run on the same machine with different\n * configs, ports, and data directories. The instance root is typically\n * `<workspace>/.openacp/`.\n */\nexport interface InstanceContext {\n /** Unique identifier for this instance (UUID). */\n id: string\n /** Absolute path to the instance root directory (e.g. `~/my-project/.openacp/`). */\n root: string\n /** Pre-resolved paths to all instance files and directories. */\n paths: {\n config: string\n sessions: string\n agents: string\n /** Shared across all instances — lives under ~/.openacp/cache/. */\n registryCache: string\n plugins: string\n pluginsData: string\n pluginRegistry: string\n logs: string\n /** PID file written by the daemon process to track the running server. */\n pid: string\n /** Marker file that indicates the daemon was intentionally started. */\n running: string\n /** Written at startup with the API server's port number. */\n apiPort: string\n apiSecret: string\n /** Shared across all instances — lives under ~/.openacp/bin/. */\n bin: string\n cache: string\n tunnels: string\n /** Shared across all instances — lives under ~/.openacp/agents/. */\n agentsDir: string\n }\n}\n\nexport interface CreateInstanceContextOpts {\n id: string\n root: string\n}\n\n/**\n * Creates an InstanceContext with all filesystem paths pre-resolved.\n *\n * Some paths (registryCache, bin, agentsDir) point to the global root\n * (`~/.openacp/`) because they are shared across all instances.\n */\nexport function createInstanceContext(opts: CreateInstanceContextOpts): InstanceContext {\n const { id, root } = opts\n const globalRoot = getGlobalRoot()\n return {\n id, root,\n paths: {\n config: path.join(root, 'config.json'),\n sessions: path.join(root, 'sessions.json'),\n agents: path.join(root, 'agents.json'),\n registryCache: path.join(globalRoot, 'cache', 'registry-cache.json'),\n plugins: path.join(root, 'plugins'),\n pluginsData: path.join(root, 'plugins', 'data'),\n pluginRegistry: path.join(root, 'plugins.json'),\n logs: path.join(root, 'logs'),\n pid: path.join(root, 'openacp.pid'),\n running: path.join(root, 'running'),\n apiPort: path.join(root, 'api.port'),\n apiSecret: path.join(root, 'api-secret'),\n bin: path.join(globalRoot, 'bin'),\n cache: path.join(root, 'cache'),\n tunnels: path.join(root, 'tunnels.json'),\n agentsDir: path.join(globalRoot, 'agents'),\n },\n }\n}\n\n/** Converts a display name to a URL/filesystem-safe slug (lowercase, hyphens only). */\nexport function generateSlug(name: string): string {\n const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '')\n return slug || 'openacp'\n}\n\nfunction expandHome(p: string): string {\n if (p.startsWith('~')) return path.join(os.homedir(), p.slice(1))\n return p\n}\n\nexport interface ResolveOpts {\n /** Explicit directory flag (`--dir`). */\n dir?: string\n /** Use CWD as instance root (`--local`). */\n local?: boolean\n cwd?: string\n}\n\n/**\n * Resolves the instance root directory using a priority chain:\n *\n * 1. `--dir` flag (explicit)\n * 2. `--local` flag (CWD)\n * 3. CWD contains `.openacp/config.json`\n * 4. Walk up parent directories (stop at $HOME, skip ~/.openacp)\n * 5. Home directory fallback: `~/openacp-workspace/.openacp/`\n * 6. `OPENACP_INSTANCE_ROOT` env var\n *\n * Returns null if no instance root is found via any of the above steps.\n *\n * `~/.openacp` is always skipped because it's the shared global store\n * (registry, cache, bin), not an instance directory.\n */\nexport function resolveInstanceRoot(opts: ResolveOpts): string | null {\n const cwd = opts.cwd ?? process.cwd()\n const home = os.homedir()\n const globalRoot = getGlobalRoot()\n\n // 1. --dir flag → return <dir>/.openacp\n if (opts.dir) return path.join(expandHome(opts.dir), '.openacp')\n\n // 2. --local flag → return cwd/.openacp\n if (opts.local) return path.join(cwd, '.openacp')\n\n // 3. CWD has .openacp/config.json → return it\n const cwdRoot = path.join(cwd, '.openacp')\n if (fs.existsSync(path.join(cwdRoot, 'config.json'))) return cwdRoot\n\n // 4. Walk-up parent dirs (stop at $HOME inclusive)\n let dir = path.resolve(cwd)\n while (true) {\n const parent = path.dirname(dir)\n if (parent === dir) break // filesystem root\n dir = parent\n const candidate = path.join(dir, '.openacp')\n // Skip ~/.openacp (shared store, not an instance)\n if (candidate === globalRoot) {\n // If we've reached $HOME, stop after checking (skip it)\n if (dir === home) break\n continue\n }\n if (fs.existsSync(path.join(candidate, 'config.json'))) return candidate\n // Stop at $HOME (inclusive — we checked it above)\n if (dir === home) break\n }\n\n // 5. Home directory fallback: check ~/openacp-workspace/.openacp/config.json\n if (path.resolve(cwd) === path.resolve(home)) {\n const defaultWs = path.join(home, 'openacp-workspace', '.openacp')\n if (fs.existsSync(path.join(defaultWs, 'config.json'))) return defaultWs\n }\n\n // 6. Check OPENACP_INSTANCE_ROOT env\n if (process.env.OPENACP_INSTANCE_ROOT) return process.env.OPENACP_INSTANCE_ROOT\n\n // 7. return null\n return null\n}\n\n/** Returns the global shared root (`~/.openacp/`) used for cross-instance resources. */\nexport function getGlobalRoot(): string {\n return path.join(os.homedir(), '.openacp')\n}\n\n/**\n * Walk up directory tree from `cwd` looking for a running `.openacp/` instance.\n * Skips instances that exist but aren't running (dead daemon).\n * Skips `~/.openacp` (shared store, not an instance).\n * Stops at $HOME (inclusive). Returns null if nothing is running.\n */\nexport async function resolveRunningInstance(cwd: string): Promise<string | null> {\n const globalRoot = getGlobalRoot()\n const home = os.homedir()\n let dir = path.resolve(cwd)\n\n while (true) {\n const candidate = path.join(dir, '.openacp')\n // Skip ~/.openacp (shared store, not an instance)\n if (candidate !== globalRoot && fs.existsSync(candidate)) {\n if (await isInstanceRunning(candidate)) return candidate\n }\n const parent = path.dirname(dir)\n if (parent === dir) break // filesystem root\n // Stop at $HOME (inclusive — we already checked it)\n if (dir === home) break\n dir = parent\n }\n\n return null\n}\n\n/** Checks if an instance is running by reading its api.port file and hitting the health endpoint. */\nasync function isInstanceRunning(instanceRoot: string): Promise<boolean> {\n const portFile = path.join(instanceRoot, 'api.port')\n try {\n const content = fs.readFileSync(portFile, 'utf-8').trim()\n const port = parseInt(content, 10)\n if (isNaN(port)) return false\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 2000)\n const res = await fetch(`http://127.0.0.1:${port}/api/v1/system/health`, {\n signal: controller.signal,\n })\n clearTimeout(timeout)\n return res.ok\n } catch {\n return false\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { randomUUID } from 'node:crypto'\n\n/** An entry in the instance registry mapping an instance ID to its root directory. */\nexport interface InstanceRegistryEntry {\n id: string\n root: string\n}\n\ninterface RegistryData {\n version: 1\n instances: Record<string, InstanceRegistryEntry>\n}\n\n/**\n * File-based registry that tracks all known OpenACP instances on this machine.\n *\n * Stored at `~/.openacp/instances.json`. Each entry maps an instance ID to\n * its root directory. The registry is the source for instance discovery,\n * listing, and ID resolution.\n *\n * `config.json` inside each instance is the source of truth for the ID.\n * If the registry disagrees with config.json, the registry is updated to match.\n */\nexport class InstanceRegistry {\n private data: RegistryData = { version: 1, instances: {} }\n\n constructor(private registryPath: string) {}\n\n /** Load the registry from disk. If the file is missing or corrupt, starts fresh. */\n load(): void {\n try {\n const raw = fs.readFileSync(this.registryPath, 'utf-8')\n const parsed = JSON.parse(raw) as RegistryData\n if (parsed.version === 1 && parsed.instances) {\n this.data = parsed\n this.deduplicate()\n }\n } catch {\n // File doesn't exist or invalid — start fresh\n }\n }\n\n /** Remove duplicate entries that point to the same root, keeping the first one */\n private deduplicate(): void {\n const seen = new Set<string>()\n const toRemove: string[] = []\n for (const [id, entry] of Object.entries(this.data.instances)) {\n if (seen.has(entry.root)) {\n toRemove.push(id)\n } else {\n seen.add(entry.root)\n }\n }\n if (toRemove.length > 0) {\n for (const id of toRemove) delete this.data.instances[id]\n this.save() // auto-clean on load\n }\n }\n\n /** Persist the registry to disk, creating parent directories if needed. */\n save(): void {\n const dir = path.dirname(this.registryPath)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(this.registryPath, JSON.stringify(this.data, null, 2))\n }\n\n /** Add or update an instance entry in the registry. Does not persist — call save() after. */\n register(id: string, root: string): void {\n this.data.instances[id] = { id, root }\n }\n\n /** Remove an instance entry. Does not persist — call save() after. */\n remove(id: string): void {\n delete this.data.instances[id]\n }\n\n /** Look up an instance by its ID. */\n get(id: string): InstanceRegistryEntry | undefined {\n return this.data.instances[id]\n }\n\n /** Look up an instance by its root directory path. */\n getByRoot(root: string): InstanceRegistryEntry | undefined {\n return Object.values(this.data.instances).find((e) => e.root === root)\n }\n\n /** Returns all registered instances. */\n list(): InstanceRegistryEntry[] {\n return Object.values(this.data.instances)\n }\n\n /** Returns `baseId` if available, otherwise appends `-2`, `-3`, etc. until unique. */\n uniqueId(baseId: string): string {\n if (!this.data.instances[baseId]) return baseId\n let n = 2\n while (this.data.instances[`${baseId}-${n}`]) n++\n return `${baseId}-${n}`\n }\n\n /**\n * Resolve the authoritative id for an instance root.\n *\n * config.json is the source of truth. If the registry entry disagrees with\n * config.json, the registry is updated to match. If neither exists, a fresh\n * UUID is generated and registered (but NOT written to config.json — the\n * caller must call initInstanceFiles to persist it).\n *\n * Returns the resolved id and whether the registry was mutated (so callers\n * can decide whether to persist with save()).\n */\n resolveId(instanceRoot: string): { id: string; registryUpdated: boolean } {\n const configId = readIdFromConfig(instanceRoot)\n const entry = this.getByRoot(instanceRoot)\n\n // Mismatch: registry disagrees with config.json — trust config.json\n if (entry && configId && entry.id !== configId) {\n this.remove(entry.id)\n this.register(configId, instanceRoot)\n return { id: configId, registryUpdated: true }\n }\n\n const id = configId ?? entry?.id ?? randomUUID()\n\n if (!this.getByRoot(instanceRoot)) {\n this.register(id, instanceRoot)\n return { id, registryUpdated: true }\n }\n\n return { id, registryUpdated: false }\n }\n}\n\n/** Read the `id` field from an instance's config.json. Returns null if missing or invalid. */\nexport function readIdFromConfig(instanceRoot: string): string | null {\n try {\n const raw = JSON.parse(fs.readFileSync(path.join(instanceRoot, 'config.json'), 'utf-8'))\n return typeof raw.id === 'string' && raw.id ? raw.id : null\n } catch { return null }\n}\n","import { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nconst execFileAsync = promisify(execFile)\n\n/**\n * Import a package resolved from a specific directory (not the project root).\n *\n * We can't use bare `import('packageName')` because Node resolves from the\n * project root's node_modules. Plugins are installed to a separate directory\n * (~/.openacp/plugins/node_modules), so we manually resolve the ESM entry point\n * from the package's package.json and import by absolute file:// URL.\n */\nexport async function importFromDir(packageName: string, dir: string): Promise<any> {\n const pkgDir = path.join(dir, 'node_modules', ...packageName.split('/'))\n const pkgJsonPath = path.join(pkgDir, 'package.json')\n\n let pkgJson: Record<string, any>\n try {\n pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf-8'))\n } catch (err) {\n throw new Error(`Cannot read package.json for \"${packageName}\" at ${pkgJsonPath}: ${(err as Error).message}`)\n }\n\n // Resolve entry: exports[\".\"].import > main > index.js\n let entry: string\n const exportsMain = pkgJson.exports?.['.']\n if (typeof exportsMain === 'string') {\n entry = exportsMain\n } else if (exportsMain?.import) {\n entry = exportsMain.import\n } else {\n entry = pkgJson.main ?? 'index.js'\n }\n\n const entryPath = path.join(pkgDir, entry)\n try {\n await fs.access(entryPath)\n } catch {\n throw new Error(`Entry point \"${entry}\" not found for \"${packageName}\" at ${entryPath}`)\n }\n\n return import(pathToFileURL(entryPath).href)\n}\n\n/** Valid npm package name: optional @scope/, alphanumeric/hyphens/dots, optional @version */\nconst VALID_NPM_NAME = /^(@[a-z0-9][\\w.-]*\\/)?[a-z0-9][\\w.-]*(@[\\w.^~>=<|-]+)?$/i;\n\n/**\n * Install an npm package to the isolated plugins directory and return the loaded module.\n *\n * Plugins are installed to `~/.openacp/plugins/` (separate from the project's node_modules)\n * to avoid version conflicts with core dependencies. Uses `--ignore-scripts` for security.\n * Tries to import first (already installed case) before running npm install.\n */\nexport async function installNpmPlugin(packageName: string, pluginsDir?: string): Promise<any> {\n if (!VALID_NPM_NAME.test(packageName)) {\n throw new Error(`Invalid package name: \"${packageName}\". Must be a valid npm package name.`);\n }\n\n const dir = pluginsDir!\n\n // Try import from plugins dir first — already installed\n try {\n return await importFromDir(packageName, dir)\n } catch {\n // Not installed, proceed with install\n }\n\n await execFileAsync('npm', ['install', packageName, '--prefix', dir, '--save', '--ignore-scripts'], {\n timeout: 60000,\n })\n\n return await importFromDir(packageName, dir)\n}\n","import * as clack from '@clack/prompts'\nimport type { TerminalIO } from './types.js'\n\n// @clack/prompts returns a symbol when the user cancels (Ctrl+C).\n// We convert that to an Error so callers can handle cancellation uniformly.\n\nfunction isCancel(value: unknown): value is symbol {\n return typeof value === 'symbol'\n}\n\nfunction guardCancel<T>(value: T | symbol): T {\n if (isCancel(value)) {\n throw new Error('cancelled')\n }\n return value as T\n}\n\n/**\n * Create a TerminalIO implementation backed by @clack/prompts.\n *\n * Used during plugin install/configure flows — these are CLI-only operations\n * that require interactive terminal access. Not available during normal\n * runtime (headless server mode).\n */\nexport function createTerminalIO(): TerminalIO {\n return {\n async text(opts) {\n const result = await clack.text(opts as Parameters<typeof clack.text>[0])\n return guardCancel(result)\n },\n async select<T>(opts: {\n message: string\n options: { value: T; label: string; hint?: string }[]\n }): Promise<T> {\n const result = await clack.select(opts as Parameters<typeof clack.select>[0])\n return guardCancel(result) as T\n },\n async confirm(opts) {\n const result = await clack.confirm(opts as Parameters<typeof clack.confirm>[0])\n return guardCancel(result)\n },\n async password(opts) {\n const result = await clack.password(opts as Parameters<typeof clack.password>[0])\n return guardCancel(result)\n },\n async multiselect<T>(opts: {\n message: string\n options: { value: T; label: string; hint?: string }[]\n required?: boolean\n }): Promise<T[]> {\n const result = await clack.multiselect(opts as Parameters<typeof clack.multiselect>[0])\n return guardCancel(result) as T[]\n },\n log: {\n info: (msg) => clack.log.info(msg),\n success: (msg) => clack.log.success(msg),\n warning: (msg) => clack.log.warning(msg),\n error: (msg) => clack.log.error(msg),\n step: (msg) => clack.log.step(msg),\n },\n spinner() {\n const s = clack.spinner()\n return {\n start: (msg: string) => s.start(msg),\n stop: (msg?: string) => s.stop(msg),\n fail: (msg?: string) => s.stop(msg ?? 'Failed'),\n }\n },\n note: (msg, title) => clack.note(msg, title),\n cancel: (msg) => clack.cancel(msg),\n }\n}\n","import path from 'node:path'\nimport type { InstallContext } from './types.js'\nimport type { SettingsManager } from './settings-manager.js'\nimport { createTerminalIO } from './terminal-io.js'\nimport { log as rootLog } from '../utils/log.js'\n\n/** Options for creating an InstallContext. */\nexport interface CreateInstallContextOpts {\n pluginName: string\n settingsManager: SettingsManager\n /** Base path for plugin data (typically ~/.openacp/plugins/) */\n basePath: string\n instanceRoot?: string\n}\n\n/**\n * Factory for InstallContext — the limited context available to plugin\n * install/uninstall/configure hooks.\n *\n * Unlike PluginContext (runtime), InstallContext provides terminal I/O for\n * interactive setup and settings access, but no services, events, or middleware.\n * This keeps install-time logic decoupled from the running system.\n */\nexport function createInstallContext(opts: CreateInstallContextOpts): InstallContext {\n const { pluginName, settingsManager, basePath, instanceRoot } = opts\n const dataDir = path.join(basePath, pluginName, 'data')\n\n return {\n pluginName,\n terminal: createTerminalIO(),\n settings: settingsManager.createAPI(pluginName),\n dataDir,\n log: rootLog.child({ plugin: pluginName }),\n instanceRoot,\n }\n}\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { setTimeout as sleep } from 'node:timers/promises'\n\n// The daemon writes its bound port to api.port once it is ready to accept requests.\n// The CLI reads this file to discover how to talk to the daemon.\n\nfunction defaultPortFile(root: string): string {\n return path.join(root, 'api.port')\n}\n\nfunction defaultSecretFile(root: string): string {\n return path.join(root, 'api-secret')\n}\n\n/**\n * Poll the api.port file until it appears (daemon has bound its port) or timeout.\n * Returns the port number, or null if the daemon did not start within timeoutMs.\n *\n * Used after `openacp start` to wait for the daemon to become ready before\n * reporting the port in JSON output.\n */\nexport async function waitForPortFile(\n portFilePath: string,\n timeoutMs = 5000,\n intervalMs = 100,\n): Promise<number | null> {\n const deadline = Date.now() + timeoutMs\n while (Date.now() < deadline) {\n const port = readApiPort(portFilePath)\n if (port !== null) return port\n await sleep(intervalMs)\n }\n // One final attempt at deadline in case the write landed in the last interval\n return readApiPort(portFilePath)\n}\n\n/**\n * Read the daemon's API port from the port file.\n * Returns null if the file doesn't exist or contains an invalid value,\n * which indicates the daemon is not running.\n */\nexport function readApiPort(portFilePath?: string, instanceRoot?: string): number | null {\n const filePath = portFilePath ?? defaultPortFile(instanceRoot!)\n try {\n const content = fs.readFileSync(filePath, 'utf-8').trim()\n const port = parseInt(content, 10)\n return isNaN(port) ? null : port\n } catch {\n return null\n }\n}\n\n/**\n * Read the shared secret used to authenticate CLI requests to the daemon.\n * Returns null if the file doesn't exist (daemon not running or API auth disabled).\n */\nexport function readApiSecret(secretFilePath?: string, instanceRoot?: string): string | null {\n const filePath = secretFilePath ?? defaultSecretFile(instanceRoot!)\n try {\n const content = fs.readFileSync(filePath, 'utf-8').trim()\n return content || null\n } catch {\n return null\n }\n}\n\n/**\n * Remove a stale api.port file left behind by an unclean daemon shutdown.\n * Stale files cause the CLI to think the daemon is running when it isn't.\n */\nexport function removeStalePortFile(portFilePath?: string, instanceRoot?: string): void {\n const filePath = portFilePath ?? defaultPortFile(instanceRoot!)\n try {\n fs.unlinkSync(filePath)\n } catch {\n // ignore\n }\n}\n\n/**\n * Make an authenticated HTTP request to the running daemon.\n *\n * All daemon API calls go to 127.0.0.1 (loopback only — no external exposure).\n * The shared secret is injected as a Bearer token for each request.\n */\nexport async function apiCall(\n port: number,\n urlPath: string,\n options?: RequestInit,\n instanceRoot?: string,\n): Promise<Response> {\n const secret = readApiSecret(undefined, instanceRoot)\n const headers = new Headers(options?.headers)\n if (secret) {\n headers.set('Authorization', `Bearer ${secret}`)\n }\n return fetch(`http://127.0.0.1:${port}${urlPath}`, { ...options, headers })\n}\n","import type { IChannelAdapter } from '../../core/channel.js'\nimport type { NotificationMessage } from '../../core/types.js'\nimport type { EventBus } from '../../core/event-bus.js'\nimport { BusEvent } from '../../core/events.js'\n\n/** Target for user-directed notifications. */\nexport type NotificationTarget =\n | { identityId: string }\n | { userId: string }\n | { channelId: string; platformId: string }\n\nexport interface NotificationOptions {\n via?: 'dm' | 'thread' | 'topic'\n topicId?: string\n sessionId?: string\n onlyPlatforms?: string[]\n excludePlatforms?: string[]\n}\n\n/** User-facing notification content — distinct from system NotificationMessage. */\nexport interface UserNotificationContent {\n type: 'text'\n text: string\n}\n\n/**\n * Minimal identity service interface — avoids hard dependency on identity plugin types.\n * NotificationService only needs resolution capabilities, not full CRUD.\n */\ninterface IdentityResolver {\n getIdentity(identityId: string): Promise<{ userId: string; source: string; platformId: string; platformUsername?: string } | undefined>\n getUser(userId: string): Promise<{ userId: string; identities: string[] } | undefined>\n getIdentitiesFor(userId: string): Promise<Array<{ identityId: string; source: string; platformId: string; platformUsername?: string }>>\n}\n\n/**\n * Routes notifications to channel adapters. Extends the legacy NotificationManager\n * with user-targeted delivery via the identity system.\n *\n * Legacy API (notify/notifyAll) is preserved — existing callers in SessionBridge\n * continue to work without changes.\n */\nexport class NotificationService {\n private identityResolver?: IdentityResolver\n private eventBus?: EventBus\n\n constructor(private adapters: Map<string, IChannelAdapter>) {}\n\n /** Inject EventBus to broadcast user:notification events for SSE delivery to App clients. */\n setEventBus(bus: EventBus): void {\n this.eventBus = bus\n }\n\n /** Inject identity resolver for user-targeted notifications. */\n setIdentityResolver(resolver: IdentityResolver): void {\n this.identityResolver = resolver\n }\n\n // --- Legacy API (backward compat with NotificationManager) ---\n\n /**\n * Send a notification to a specific channel adapter.\n *\n * Failures are swallowed — notifications are best-effort and must not crash\n * the session or caller (e.g. on session completion).\n */\n async notify(channelId: string, notification: NotificationMessage): Promise<void> {\n const adapter = this.adapters.get(channelId)\n if (!adapter) return\n try {\n await adapter.sendNotification(notification)\n } catch {\n // Best effort\n }\n }\n\n /**\n * Broadcast a notification to every registered adapter.\n *\n * Used for system-wide alerts (e.g. global budget exhausted). Each adapter\n * failure is isolated so one broken adapter cannot block the rest.\n */\n async notifyAll(notification: NotificationMessage): Promise<void> {\n for (const adapter of this.adapters.values()) {\n try {\n await adapter.sendNotification(notification)\n } catch {\n // Continue\n }\n }\n }\n\n // --- New user-targeted API ---\n\n /**\n * Send a notification to a user across all their linked platforms.\n * Fire-and-forget — never throws, swallows all errors.\n */\n async notifyUser(\n target: NotificationTarget,\n message: UserNotificationContent,\n options?: NotificationOptions,\n ): Promise<void> {\n try {\n const userId = 'userId' in target ? target.userId : undefined\n await this._resolveAndDeliver(target, message, options)\n // Emit on EventBus so SSEManager can broadcast to App clients over /api/v1/events.\n // This is the only SSE stream the App subscribes to.\n if (this.eventBus && userId) {\n this.eventBus.emit(BusEvent.USER_NOTIFICATION, {\n userId,\n text: message.text,\n sessionId: options?.sessionId,\n })\n }\n } catch {\n // Fire-and-forget\n }\n }\n\n private async _resolveAndDeliver(\n target: NotificationTarget,\n message: UserNotificationContent,\n options?: NotificationOptions,\n ): Promise<void> {\n // Direct adapter call — bypass identity resolution.\n // sendUserNotification accepts NotificationMessage which covers UserNotificationContent shape.\n if ('channelId' in target && 'platformId' in target) {\n const adapter = this.adapters.get(target.channelId)\n if (!adapter?.sendUserNotification) return\n await adapter.sendUserNotification(target.platformId, message as unknown as NotificationMessage, {\n via: options?.via,\n topicId: options?.topicId,\n sessionId: options?.sessionId,\n })\n return\n }\n\n // Identity-based resolution\n if (!this.identityResolver) return\n\n let identities: Array<{ identityId: string; source: string; platformId: string; platformUsername?: string }> = []\n\n if ('identityId' in target) {\n const identity = await this.identityResolver.getIdentity(target.identityId)\n if (!identity) return\n const user = await this.identityResolver.getUser(identity.userId)\n if (!user) return\n identities = await this.identityResolver.getIdentitiesFor(user.userId)\n } else if ('userId' in target) {\n identities = await this.identityResolver.getIdentitiesFor(target.userId)\n }\n\n // Platform filters\n if (options?.onlyPlatforms) {\n identities = identities.filter(i => options.onlyPlatforms!.includes(i.source))\n }\n if (options?.excludePlatforms) {\n identities = identities.filter(i => !options.excludePlatforms!.includes(i.source))\n }\n\n // Deliver to each identity's adapter\n for (const identity of identities) {\n const adapter = this.adapters.get(identity.source)\n if (!adapter?.sendUserNotification) continue\n try {\n await adapter.sendUserNotification(identity.platformId, message as unknown as NotificationMessage, {\n via: options?.via,\n topicId: options?.topicId,\n sessionId: options?.sessionId,\n platformMention: {\n platformUsername: identity.platformUsername,\n platformId: identity.platformId,\n },\n })\n } catch {\n // Continue — best effort\n }\n }\n }\n}\n\n// Backward compat alias — existing imports use NotificationManager\nexport { NotificationService as NotificationManager }\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { OggOpusDecoder } from \"ogg-opus-decoder\";\nimport wav from \"node-wav\";\nimport type { Attachment } from \"../../core/types.js\";\n\nconst MIME_TO_EXT: Record<string, string> = {\n \"image/jpeg\": \".jpg\",\n \"image/png\": \".png\",\n \"image/gif\": \".gif\",\n \"image/webp\": \".webp\",\n \"image/svg+xml\": \".svg\",\n \"audio/ogg\": \".ogg\",\n \"audio/mpeg\": \".mp3\",\n \"audio/wav\": \".wav\",\n \"audio/webm\": \".webm\",\n \"audio/mp4\": \".m4a\",\n \"video/mp4\": \".mp4\",\n \"video/webm\": \".webm\",\n \"application/pdf\": \".pdf\",\n \"text/plain\": \".txt\",\n};\n\nconst EXT_TO_MIME: Record<string, string> = {\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".svg\": \"image/svg+xml\",\n \".ogg\": \"audio/ogg\",\n \".oga\": \"audio/ogg\",\n \".mp3\": \"audio/mpeg\",\n \".wav\": \"audio/wav\",\n \".m4a\": \"audio/mp4\",\n \".mp4\": \"video/mp4\",\n \".pdf\": \"application/pdf\",\n \".txt\": \"text/plain\",\n};\n\nfunction classifyMime(mimeType: string): Attachment[\"type\"] {\n if (mimeType.startsWith(\"image/\")) return \"image\";\n if (mimeType.startsWith(\"audio/\")) return \"audio\";\n return \"file\";\n}\n\n/**\n * Provides file I/O for session attachments and agent-generated files.\n *\n * All files are stored under `baseDir/<sessionId>/` so that access is naturally\n * scoped per session and cleanup is a single directory removal. File names are\n * sanitized and prefixed with a timestamp to prevent collisions and path traversal.\n *\n * This service acts as the security boundary between adapters/agents and the\n * filesystem — callers never write directly; they go through `saveFile` so that\n * naming, MIME classification, and path normalization are always applied.\n */\nexport class FileService {\n constructor(readonly baseDir: string) {}\n\n /**\n * Remove session file directories older than maxAgeDays.\n * Called on startup to prevent unbounded disk growth.\n */\n async cleanupOldFiles(maxAgeDays: number): Promise<number> {\n const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;\n let removed = 0;\n try {\n const entries = await fs.promises.readdir(this.baseDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const dirPath = path.join(this.baseDir, entry.name);\n try {\n const stat = await fs.promises.stat(dirPath);\n if (stat.mtimeMs < cutoff) {\n await fs.promises.rm(dirPath, { recursive: true, force: true });\n removed++;\n }\n } catch {\n // Skip inaccessible directories\n }\n }\n } catch {\n // Base dir doesn't exist yet — nothing to clean\n }\n return removed;\n }\n\n /**\n * Persist a file to the session's directory and return an `Attachment` descriptor.\n *\n * The file name is sanitized (non-safe characters replaced with `_`) and prefixed\n * with a millisecond timestamp to prevent name collisions across multiple saves in\n * the same session. The original `fileName` is preserved in the returned descriptor\n * so the user-facing name is not lost.\n */\n async saveFile(\n sessionId: string,\n fileName: string,\n data: Buffer,\n mimeType: string,\n ): Promise<Attachment> {\n const sessionDir = path.join(this.baseDir, sessionId);\n await fs.promises.mkdir(sessionDir, { recursive: true });\n\n // Sanitize to prevent path traversal and filesystem issues\n const safeName = `${Date.now()}-${fileName.replace(/[^a-zA-Z0-9._-]/g, \"_\")}`;\n const filePath = path.join(sessionDir, safeName);\n await fs.promises.writeFile(filePath, data);\n\n return {\n type: classifyMime(mimeType),\n filePath,\n fileName,\n mimeType,\n size: data.length,\n };\n }\n\n /**\n * Build an `Attachment` descriptor for a file that already exists on disk.\n * Returns `null` if the path does not exist or is not a regular file.\n */\n async resolveFile(filePath: string): Promise<Attachment | null> {\n try {\n const stat = await fs.promises.stat(filePath);\n if (!stat.isFile()) return null;\n\n const ext = path.extname(filePath).toLowerCase();\n const mimeType = EXT_TO_MIME[ext] || \"application/octet-stream\";\n\n return {\n type: classifyMime(mimeType),\n filePath,\n fileName: path.basename(filePath),\n mimeType,\n size: stat.size,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Convert OGG Opus audio to WAV format.\n * Telegram voice messages use OGG Opus which many AI agents can't read.\n */\n async convertOggToWav(oggData: Buffer): Promise<Buffer> {\n const decoder = new OggOpusDecoder();\n await decoder.ready;\n try {\n const { channelData, sampleRate } = await decoder.decode(new Uint8Array(oggData));\n const wavData = wav.encode(channelData, { sampleRate, float: true, bitDepth: 32 });\n return Buffer.from(wavData);\n } finally {\n decoder.free();\n }\n }\n\n /** Instance method — delegates to static for FileServiceInterface compliance */\n async readTextFileWithRange(\n filePath: string,\n options?: { line?: number; limit?: number },\n ): Promise<string> {\n return FileService.readTextFileWithRange(filePath, options);\n }\n\n static async readTextFileWithRange(\n filePath: string,\n options?: { line?: number; limit?: number },\n ): Promise<string> {\n // Delegate to core utility (canonical implementation)\n const { readTextFileWithRange } = await import(\"../../core/utils/read-text-file.js\");\n return readTextFileWithRange(filePath, options);\n }\n\n /** Instance method — delegates to static for FileServiceInterface compliance */\n extensionFromMime(mimeType: string): string {\n return FileService.extensionFromMime(mimeType);\n }\n\n /** Returns the canonical file extension for a given MIME type (e.g. `\"image/png\"` → `\".png\"`). */\n static extensionFromMime(mimeType: string): string {\n return MIME_TO_EXT[mimeType] || \".bin\";\n }\n}\n","/**\n * Configuration for the SecurityGuard access policy.\n *\n * `allowedUserIds`: if non-empty, only users whose string ID appears in this list\n * are permitted. An empty array means \"allow all\" (open access).\n * `maxConcurrentSessions`: caps how many active/initializing sessions may exist\n * at once across all users, preventing resource exhaustion.\n */\nexport interface SecurityConfig {\n allowedUserIds: string[];\n maxConcurrentSessions: number;\n}\n\n/**\n * Enforces user allowlist and global session-count limits on every incoming message.\n *\n * Implemented as a plugin service (rather than baked into core) so that the access\n * policy is swappable — deployments can replace or extend it without touching core.\n * The plugin registers this guard as a `message:incoming` middleware handler.\n */\nexport class SecurityGuard {\n constructor(\n private getSecurityConfig: () => Promise<SecurityConfig>,\n private sessionManager: { listSessions(): Array<{ status: string }> },\n ) {}\n\n /**\n * Returns `{ allowed: true }` when the message may proceed, or\n * `{ allowed: false, reason }` when it should be blocked.\n *\n * Two checks run in order:\n * 1. **Allowlist** — if `allowedUserIds` is non-empty, the user's ID (coerced to string)\n * must appear in the list. Telegram/Slack IDs are numbers, so coercion is required.\n * 2. **Session cap** — counts sessions in `active` or `initializing` state. `initializing`\n * is included because a session holds resources before it reaches `active`.\n */\n async checkAccess(message: { userId: string | number }):\n Promise<{ allowed: true } | { allowed: false; reason: string }>\n {\n const config = await this.getSecurityConfig();\n const allowedIds = config.allowedUserIds ?? [];\n const maxSessions = config.maxConcurrentSessions ?? 20;\n\n if (allowedIds.length > 0) {\n // Coerce to string: platform adapters may deliver userId as a number (e.g. Telegram)\n const userId = String(message.userId);\n if (!allowedIds.includes(userId)) {\n return { allowed: false, reason: \"Unauthorized user\" };\n }\n }\n const active = this.sessionManager.listSessions()\n .filter(s => s.status === \"active\" || s.status === \"initializing\");\n if (active.length >= maxSessions) {\n return { allowed: false, reason: `Session limit reached (${maxSessions})` };\n }\n return { allowed: true };\n }\n}\n","import type { FastifyError, FastifyReply, FastifyRequest } from 'fastify';\nimport { ZodError } from 'zod';\nimport { ConfigValidationError } from '../../../core/config/config-registry.js';\n\n/** Standard error envelope returned by all API error responses. */\nexport interface ApiErrorResponse {\n error: {\n code: string;\n message: string;\n statusCode: number;\n details?: unknown;\n };\n}\n\n/** Thrown by route handlers when a requested resource does not exist (→ 404). */\nexport class NotFoundError extends Error {\n constructor(\n public code: string,\n message: string,\n ) {\n super(message);\n this.name = 'NotFoundError';\n }\n}\n\n/** Thrown when the client sends a malformed or semantically invalid request (→ 400). */\nexport class BadRequestError extends Error {\n constructor(\n public code: string,\n message: string,\n ) {\n super(message);\n this.name = 'BadRequestError';\n }\n}\n\n/** Thrown when a required backing service (e.g. file-service) is not loaded (→ 503). */\nexport class ServiceUnavailableError extends Error {\n constructor(\n public code: string,\n message: string,\n ) {\n super(message);\n this.name = 'ServiceUnavailableError';\n }\n}\n\n/** Thrown by auth middleware and routes for authentication/authorization failures (→ 401 or 403). */\nexport class AuthError extends Error {\n constructor(\n public code: string,\n message: string,\n public statusCode: number = 401,\n ) {\n super(message);\n this.name = 'AuthError';\n }\n}\n\n/**\n * Fastify global error handler that normalizes all thrown errors into the `ApiErrorResponse`\n * envelope. Maps known error classes to their corresponding HTTP status codes and falls back\n * to 500 for anything unrecognized.\n */\nexport function globalErrorHandler(\n error: FastifyError | Error,\n _request: FastifyRequest,\n reply: FastifyReply,\n): void {\n if (error instanceof ZodError) {\n reply.status(400).send({\n error: {\n code: 'VALIDATION_ERROR',\n message: error.issues.map((e) => e.message).join(', '),\n statusCode: 400,\n details: error.issues,\n },\n });\n return;\n }\n\n if (error instanceof ConfigValidationError) {\n reply.status(400).send({\n error: {\n code: 'VALIDATION_ERROR',\n message: error.message,\n statusCode: 400,\n },\n });\n return;\n }\n\n if (error instanceof BadRequestError) {\n reply.status(400).send({\n error: {\n code: error.code,\n message: error.message,\n statusCode: 400,\n },\n });\n return;\n }\n\n if (error instanceof NotFoundError) {\n reply.status(404).send({\n error: {\n code: error.code,\n message: error.message,\n statusCode: 404,\n },\n });\n return;\n }\n\n if (error instanceof ServiceUnavailableError) {\n reply.status(503).send({\n error: {\n code: error.code,\n message: error.message,\n statusCode: 503,\n },\n });\n return;\n }\n\n if (error instanceof AuthError) {\n reply.status(error.statusCode).send({\n error: {\n code: error.code,\n message: error.message,\n statusCode: error.statusCode,\n },\n });\n return;\n }\n\n reply.status(500).send({\n error: {\n code: 'INTERNAL_ERROR',\n message: 'Internal server error',\n statusCode: 500,\n },\n });\n}\n","import jwt from 'jsonwebtoken';\nimport type { SignOptions } from 'jsonwebtoken';\nimport type { JwtPayload } from './types.js';\n\nexport interface SignPayload {\n sub: string;\n role: string;\n scopes?: string[];\n /** Refresh deadline as Unix timestamp (seconds). Embedded in the JWT so the refresh endpoint\n * can enforce it without a database lookup when the access token has already expired. */\n rfd: number;\n}\n\n/**\n * Signs a short-lived access token using HS256.\n *\n * HS256 is sufficient here because the secret never leaves the server process;\n * asymmetric keys would add complexity with no security benefit in this deployment model.\n */\nexport function signToken(payload: SignPayload, secret: string, expiresIn: string): string {\n const opts: SignOptions = { algorithm: 'HS256', expiresIn: expiresIn as SignOptions['expiresIn'] };\n return jwt.sign(payload, secret, opts);\n}\n\n/**\n * Verifies a JWT and returns its decoded payload.\n *\n * Throws if the token is invalid, has been tampered with, or is expired.\n */\nexport function verifyToken(token: string, secret: string): JwtPayload {\n return jwt.verify(token, secret, { algorithms: ['HS256'] }) as JwtPayload;\n}\n\n/**\n * Verifies a JWT signature but ignores expiration — used exclusively during token refresh.\n *\n * The caller must check `rfd` (refresh deadline) separately: once that deadline passes,\n * refreshing is no longer permitted regardless of the original expiry window.\n */\nexport function verifyForRefresh(token: string, secret: string): JwtPayload {\n return jwt.verify(token, secret, { algorithms: ['HS256'], ignoreExpiration: true }) as JwtPayload;\n}\n","// sessions:dangerous is intentionally not assigned to any named role.\n// Only admin (via wildcard '*') has it. Operators must be explicitly granted it\n// via custom scopes to prevent accidental bypass-permission escalation.\nexport const ROLES = {\n admin: ['*'],\n operator: [\n 'sessions:read', 'sessions:write', 'sessions:prompt', 'sessions:permission',\n 'agents:read', 'agents:write', 'commands:execute', 'system:health', 'config:read',\n ],\n viewer: ['sessions:read', 'agents:read', 'system:health'],\n} as const;\n\n// Known scopes — used for documentation and validation.\n// sessions:dangerous grants access to destructive session operations.\n// agents:write grants access to agent catalog management (e.g. reload).\nexport const KNOWN_SCOPES = [\n 'sessions:read', 'sessions:write', 'sessions:prompt', 'sessions:permission', 'sessions:dangerous',\n 'agents:read', 'agents:write', 'commands:execute', 'system:health', 'config:read',\n] as const;\n\nexport type RoleName = keyof typeof ROLES;\n\n/** Returns true if `role` is one of the known built-in role names. */\nexport function isValidRole(role: string): role is RoleName {\n return role in ROLES;\n}\n\n/** Returns the default scope list for a role, or an empty array for unknown roles. */\nexport function getRoleScopes(role: string): string[] {\n if (!isValidRole(role)) return [];\n return [...ROLES[role]];\n}\n\n/**\n * Returns true if `userScopes` contains `requiredScope`.\n *\n * The wildcard `'*'` short-circuits all checks — it is only granted to admin-type tokens.\n */\nexport function hasScope(userScopes: string[], requiredScope: string): boolean {\n if (userScopes.includes('*')) return true;\n return userScopes.includes(requiredScope);\n}\n","import type { FastifyReply, FastifyRequest, preHandlerHookHandler } from 'fastify';\nimport { createHmac, timingSafeEqual } from 'node:crypto';\nimport { AuthError } from './error-handler.js';\nimport { verifyToken } from '../auth/jwt.js';\nimport { getRoleScopes } from '../auth/roles.js';\nimport type { TokenStore } from '../auth/token-store.js';\nimport { createChildLogger } from '../../../core/utils/log.js';\n\nconst log = createChildLogger({ module: 'api-auth' });\n\n// Augment FastifyRequest so all route handlers have typed access to the authenticated identity.\ndeclare module 'fastify' {\n interface FastifyRequest {\n auth: {\n /** \"secret\" = raw API secret (full admin); \"jwt\" = scoped JWT token. */\n type: 'secret' | 'jwt';\n /** Present for JWT auth; used to look up the token record for revocation checks. */\n tokenId?: string;\n role: string;\n scopes: string[];\n };\n }\n}\n\nfunction constantTimeSecretCheck(token: string, secret: string): boolean {\n // Hash both to fixed-length to prevent length leaking\n const tokenHash = createHmac('sha256', 'openacp-auth').update(token).digest();\n const secretHash = createHmac('sha256', 'openacp-auth').update(secret).digest();\n return timingSafeEqual(tokenHash, secretHash);\n}\n\n/**\n * Creates a Fastify pre-handler that validates incoming auth tokens.\n *\n * Two credential types are accepted in priority order:\n * 1. Raw API secret — constant-time compared; grants full admin access (`scopes: ['*']`).\n * 2. JWT — verified with HS256, then checked against TokenStore for revocation.\n *\n * On success, `request.auth` is populated with role and scopes for downstream handlers.\n * Token may be provided via `Authorization: Bearer <token>` or `?token=` query param\n * (the latter is warned against because tunnel providers log query strings).\n */\nexport function createAuthPreHandler(\n getSecret: () => string,\n getJwtSecret: () => string,\n tokenStore: TokenStore,\n): preHandlerHookHandler {\n return async function authPreHandler(request: FastifyRequest, _reply: FastifyReply) {\n const authHeader = request.headers.authorization;\n const queryToken = (request.query as Record<string, string>)?.token;\n const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : queryToken;\n\n // Warn when token is passed via URL query param — tokens in URLs are logged by\n // tunnel providers (Cloudflare, ngrok), appear in browser history, and can leak\n // via Referer headers. Use the Authorization: Bearer header instead.\n if (queryToken && !authHeader) {\n log.warn({ url: request.url.replace(/([?&]token=)[^&]+/, '$1[REDACTED]') },\n 'Token passed via URL query param — use Authorization: Bearer header to avoid token leakage in tunnel/proxy logs')\n }\n\n if (!token) {\n throw new AuthError('UNAUTHORIZED', 'Missing authentication token');\n }\n\n // 1. Check secret token (constant-time compare) — full admin access\n const secret = getSecret();\n if (constantTimeSecretCheck(token, secret)) {\n request.auth = { type: 'secret', role: 'admin', scopes: ['*'] };\n return;\n }\n\n // 2. Try JWT verification\n let payload;\n try {\n const jwtSecret = getJwtSecret();\n payload = verifyToken(token, jwtSecret);\n } catch {\n throw new AuthError('UNAUTHORIZED', 'Invalid authentication token');\n }\n\n const stored = tokenStore.get(payload.sub);\n if (!stored || stored.revoked) {\n throw new AuthError('UNAUTHORIZED', 'Token has been revoked');\n }\n\n tokenStore.updateLastUsed(payload.sub);\n\n const scopes = payload.scopes ?? getRoleScopes(payload.role);\n\n request.auth = {\n type: 'jwt',\n tokenId: payload.sub,\n role: payload.role,\n scopes,\n };\n };\n}\n\n/**\n * Returns a Fastify pre-handler that enforces one or more required scopes.\n *\n * Wildcard `'*'` (admin secret auth) bypasses all scope checks.\n * Throws 403 listing the specific missing scopes for easier debugging.\n */\nexport function requireScopes(...scopes: string[]): preHandlerHookHandler {\n return async function scopeCheck(request: FastifyRequest, _reply: FastifyReply) {\n const { scopes: userScopes } = request.auth;\n if (userScopes.includes('*')) return;\n\n const missing = scopes.filter((s) => !userScopes.includes(s));\n if (missing.length > 0) {\n throw new AuthError('FORBIDDEN', `Missing scopes: ${missing.join(', ')}`, 403);\n }\n };\n}\n\n/**\n * Returns a Fastify pre-handler that enforces a minimum role level.\n *\n * Roles form a hierarchy: viewer < operator < admin. A user with a higher role\n * always passes a lower-role gate, so `requireRole('operator')` also admits admins.\n */\nexport function requireRole(role: string): preHandlerHookHandler {\n const roleHierarchy: Record<string, number> = { viewer: 0, operator: 1, admin: 2 };\n\n return async function roleCheck(request: FastifyRequest, _reply: FastifyReply) {\n const userLevel = roleHierarchy[request.auth.role] ?? -1;\n const requiredLevel = roleHierarchy[role] ?? 999;\n\n if (userLevel < requiredLevel) {\n throw new AuthError('FORBIDDEN', `Requires ${role} role`, 403);\n }\n };\n}\n","import Fastify, { type FastifyInstance, type FastifyPluginAsync } from 'fastify';\nimport fastifySwagger from '@fastify/swagger';\nimport fastifySwaggerUi from '@fastify/swagger-ui';\nimport fastifyRateLimit from '@fastify/rate-limit';\nimport { serializerCompiler, validatorCompiler } from 'fastify-type-provider-zod';\nimport { globalErrorHandler } from './middleware/error-handler.js';\nimport { createAuthPreHandler } from './middleware/auth.js';\n\nexport interface ApiServerOptions {\n port: number;\n host: string;\n /** Returns the raw API secret used for full-admin Bearer auth. Fetched lazily so rotation is safe. */\n getSecret: () => string;\n /** Returns the HMAC secret used to sign and verify JWTs. */\n getJwtSecret: () => string;\n tokenStore: import('./auth/token-store.js').TokenStore;\n logger?: boolean;\n}\n\n/**\n * The handle returned by `createApiServer`.\n *\n * Route plugins are registered before `start()` is called; Fastify's plugin\n * encapsulation keeps each prefix isolated with its own auth hooks.\n */\nexport interface ApiServerInstance {\n app: FastifyInstance;\n /** Binds to the configured host/port, auto-incrementing if EADDRINUSE. */\n start(): Promise<{ port: number; host: string }>;\n stop(): Promise<void>;\n /**\n * Registers a Fastify plugin scoped to `prefix`.\n *\n * Auth is applied by default. Pass `{ auth: false }` only for public endpoints\n * (e.g. `/api/v1/system` health, `/api/v1/auth/exchange`).\n */\n registerPlugin(prefix: string, plugin: FastifyPluginAsync, opts?: { auth?: boolean }): void;\n}\n\n/**\n * Creates and configures the Fastify HTTP server.\n *\n * Sets up in order: Zod validation, CORS, rate limiting, Swagger docs,\n * backward-compat redirects (`/api/*` → `/api/v1/*`), and the global error handler.\n * Route plugins are registered after this returns via `ApiServerInstance.registerPlugin`.\n */\nexport async function createApiServer(options: ApiServerOptions): Promise<ApiServerInstance> {\n const app = Fastify({ logger: options.logger ?? false, forceCloseConnections: true });\n\n // Zod validation\n app.setValidatorCompiler(validatorCompiler);\n app.setSerializerCompiler(serializerCompiler);\n\n // CORS — allow any origin (auth is Bearer token, not cookie-based).\n // Do NOT set Access-Control-Allow-Credentials: credentials mode is not needed\n // for Bearer token auth and reflecting the origin with credentials: true is a\n // well-known CORS misconfiguration that could allow cross-origin credential abuse.\n app.addHook('onRequest', async (request, reply) => {\n const origin = request.headers.origin;\n if (origin) {\n reply.header('Access-Control-Allow-Origin', origin);\n }\n if (request.method === 'OPTIONS') {\n reply.header('Access-Control-Allow-Methods', 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS');\n reply.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');\n reply.status(204).send();\n }\n });\n\n // Plugins\n await app.register(fastifyRateLimit, {\n max: 100,\n timeWindow: '1 minute',\n // When the server is reachable through a tunnel (Cloudflare, ngrok, etc.) all\n // requests arrive from the same tunnel-proxy IP address. Use the real client IP\n // from well-known forwarded headers so rate limits are enforced per-caller, not\n // per-tunnel. Only the first value in X-Forwarded-For is taken (the client); the\n // rest may be added by intermediate proxies and must not be trusted for limiting.\n keyGenerator: (request) => {\n // Only trust forwarded headers when binding to localhost (behind tunnel)\n const bindHost = options.host;\n const isBehindProxy = !bindHost || bindHost === '127.0.0.1' || bindHost === 'localhost' || bindHost === '::1';\n if (isBehindProxy) {\n const cfIp = request.headers['cf-connecting-ip'];\n if (cfIp && typeof cfIp === 'string') return cfIp;\n const xff = request.headers['x-forwarded-for'];\n if (xff) {\n const first = (Array.isArray(xff) ? xff[0] : xff).split(',')[0]?.trim();\n if (first) return first;\n }\n }\n return request.ip;\n },\n });\n await app.register(fastifySwagger, {\n openapi: {\n info: { title: 'OpenACP API', version: '1.0.0' },\n components: {\n securitySchemes: {\n bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },\n },\n },\n },\n });\n await app.register(fastifySwaggerUi, { routePrefix: '/api/docs' });\n\n // Global error handler\n app.setErrorHandler(globalErrorHandler);\n\n // Backward-compatible redirects: /api/* → /api/v1/*\n app.addHook('onRequest', async (request, reply) => {\n const url = request.url;\n if (url.startsWith('/api/') && !url.startsWith('/api/v1/') && !url.startsWith('/api/docs')) {\n const newUrl = url.replace('/api/', '/api/v1/');\n return reply.redirect(newUrl, 308);\n }\n });\n\n // Auth pre-handler\n const authPreHandler = createAuthPreHandler(options.getSecret, options.getJwtSecret, options.tokenStore);\n\n // Decorate request with auth object\n app.decorateRequest('auth', null, []);\n\n // Track whether Fastify has booted — after boot, new routes cannot be added.\n // On dev hot-reload, plugins re-run setup() but their routes from the first\n // load are still active, so duplicate registration is safely skipped.\n let booted = false\n app.addHook('onReady', async () => { booted = true })\n\n return {\n app,\n\n registerPlugin(prefix: string, plugin: FastifyPluginAsync, opts?: { auth?: boolean }) {\n if (booted) {\n // Fastify already booted — routes from first load still active, skip\n return\n }\n const wrappedPlugin: FastifyPluginAsync = async (pluginApp, pluginOpts) => {\n // Fastify encapsulation means this hook only applies to routes in this scope,\n // so unauthenticated prefixes (e.g. /exchange) don't accidentally inherit auth.\n if (opts?.auth !== false) {\n pluginApp.addHook('onRequest', authPreHandler);\n }\n await plugin(pluginApp, pluginOpts);\n };\n app.register(wrappedPlugin, { prefix });\n },\n\n async start() {\n await app.ready();\n\n // Auto-detect available port: retry +1 until a free port is found\n let port = options.port;\n\n while (port <= 65535) {\n try {\n const address = await app.listen({ port, host: options.host });\n const url = new URL(address);\n return { port: Number(url.port), host: url.hostname };\n } catch (err: any) {\n if (err?.code === 'EADDRINUSE' && port < 65535) {\n console.log(`[api-server] Port ${port} in use, trying ${port + 1}...`);\n port++;\n continue;\n }\n throw err;\n }\n }\n throw new Error('No available ports found');\n },\n\n async stop() {\n await app.close();\n },\n };\n}\n","import type { FastifyPluginAsync, preHandlerHookHandler } from 'fastify';\nimport type { ApiServerInstance } from './server.js';\nimport { requireScopes, requireRole } from './middleware/auth.js';\n\n/**\n * The `api-server` service interface exposed to other plugins via ServiceRegistry.\n *\n * Other plugins (e.g. SSE adapter, tunnel plugin) use this to register their own\n * Fastify route plugins under the shared HTTP server without needing a direct\n * reference to the Fastify instance.\n */\nexport interface ApiServerService {\n /** Register a Fastify plugin at the given prefix, with optional auth bypass. */\n registerPlugin(prefix: string, plugin: FastifyPluginAsync, opts?: { auth?: boolean }): void;\n /** Pre-handler that validates Bearer/JWT auth — attach to routes that need custom auth. */\n authPreHandler: preHandlerHookHandler;\n /** Creates a pre-handler that requires all listed scopes. */\n requireScopes(...scopes: string[]): preHandlerHookHandler;\n /** Creates a pre-handler that requires a minimum role level. */\n requireRole(role: string): preHandlerHookHandler;\n getPort(): number;\n getBaseUrl(): string;\n /** Returns the public tunnel URL, or null if no tunnel is active. */\n getTunnelUrl(): string | null;\n}\n\n/**\n * Wraps an `ApiServerInstance` as a service object registered in the ServiceRegistry.\n *\n * The getPort/getBaseUrl/getTunnelUrl callbacks are evaluated lazily so callers always\n * get the current values (port is only known after the server starts listening).\n */\nexport function createApiServerService(\n server: ApiServerInstance,\n getPort: () => number,\n getBaseUrl: () => string,\n getTunnelUrl: () => string | null,\n authPreHandler: preHandlerHookHandler,\n): ApiServerService {\n return {\n registerPlugin: server.registerPlugin.bind(server),\n authPreHandler,\n requireScopes,\n requireRole,\n getPort,\n getBaseUrl,\n getTunnelUrl,\n };\n}\n","import * as http from \"node:http\";\nimport type { FastifyRequest, FastifyReply } from 'fastify';\nimport type { EventBus, EventBusEvents } from \"../../core/event-bus.js\";\nimport { BusEvent } from \"../../core/events.js\";\n\ninterface SSEResponse extends http.ServerResponse {\n sessionFilter?: string;\n}\n\ninterface SessionStats {\n active: number;\n total: number;\n}\n\n// Maximum concurrent SSE connections. Beyond this the server returns 503 to\n// prevent resource exhaustion (file-descriptor / memory DoS) from a single\n// attacker opening many persistent connections via the public tunnel.\nconst MAX_SSE_CONNECTIONS = 50;\n\n/**\n * Manages Server-Sent Events (SSE) connections and broadcasts OpenACP bus events to clients.\n *\n * SSE is used instead of WebSockets because:\n * - It works natively through HTTP/1.1 proxies (Cloudflare, nginx) without upgrade negotiation.\n * - The communication is unidirectional (server → client); clients send commands via REST.\n * - It requires no additional dependencies and is built into all modern browsers.\n *\n * Clients can subscribe to a specific session by passing `?sessionId=` to filter the stream.\n * Health heartbeats are sent every 15 seconds to keep the connection alive through idle-timeout\n * proxies and to deliver periodic memory/session stats to the dashboard.\n */\nexport class SSEManager {\n private sseConnections = new Set<http.ServerResponse>();\n private sseCleanupHandlers = new Map<http.ServerResponse, () => void>();\n private healthInterval?: ReturnType<typeof setInterval>;\n private boundHandlers: Array<{\n event: keyof EventBusEvents;\n handler: (data: unknown) => void;\n }> = [];\n\n constructor(\n private eventBus: EventBus | undefined,\n private getSessionStats: () => SessionStats,\n private startedAt: number,\n ) {}\n\n /**\n * Subscribes to EventBus events and starts the health heartbeat interval.\n *\n * Must be called after the HTTP server is listening, not during plugin setup —\n * before `setup()` runs, no EventBus listeners are registered, so any events\n * emitted in the interim are silently missed.\n */\n setup(): void {\n if (!this.eventBus) return;\n\n const events = [\n BusEvent.SESSION_CREATED,\n BusEvent.SESSION_UPDATED,\n BusEvent.SESSION_DELETED,\n BusEvent.AGENT_EVENT,\n BusEvent.PERMISSION_REQUEST,\n BusEvent.PERMISSION_RESOLVED,\n BusEvent.MESSAGE_QUEUED,\n BusEvent.MESSAGE_PROCESSING,\n BusEvent.MESSAGE_FAILED,\n BusEvent.PROMPT_WAITING,\n ] as const;\n\n for (const eventName of events) {\n const handler = (data: unknown) => {\n this.broadcast(eventName, data);\n };\n this.eventBus.on(eventName, handler);\n this.boundHandlers.push({ event: eventName, handler });\n }\n\n // user:notification is broadcast as notification:text so the App's existing\n // SSE listener picks it up without any client-side changes.\n const userNotificationHandler = (data: unknown) => {\n const { text, sessionId } = data as { userId: string; text: string; sessionId?: string };\n this.broadcast('notification:text', { text, sessionId });\n };\n this.eventBus.on(BusEvent.USER_NOTIFICATION, userNotificationHandler);\n this.boundHandlers.push({ event: BusEvent.USER_NOTIFICATION, handler: userNotificationHandler });\n\n // Health heartbeat every 15s\n this.healthInterval = setInterval(() => {\n const mem = process.memoryUsage();\n const stats = this.getSessionStats();\n this.broadcast(\"health\", {\n uptime: Date.now() - this.startedAt,\n memory: {\n rss: mem.rss,\n heapUsed: mem.heapUsed,\n heapTotal: mem.heapTotal,\n },\n sessions: stats,\n });\n }, 15_000);\n }\n\n /**\n * Handles an incoming SSE request by upgrading the HTTP response to an event stream.\n *\n * The response is kept open indefinitely; cleanup runs when the client disconnects.\n * An initial `: connected` comment is written immediately so proxies and browsers\n * flush the response headers before the first real event arrives.\n */\n handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n if (this.sseConnections.size >= MAX_SSE_CONNECTIONS) {\n res.writeHead(503, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Too many SSE connections\" }));\n return;\n }\n\n const parsedUrl = new URL(req.url || \"\", \"http://localhost\");\n const sessionFilter = parsedUrl.searchParams.get(\"sessionId\");\n console.log(`[sse] +connection total=${this.sseConnections.size + 1}`);\n\n const origin = req.headers.origin;\n const corsHeaders: Record<string, string> = {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache, no-transform\",\n Connection: \"keep-alive\",\n // Disable buffering in Nginx/Cloudflare/other reverse proxies\n \"X-Accel-Buffering\": \"no\",\n };\n // SSE is authenticated via Bearer/query token — no credentials (cookies) involved,\n // so Access-Control-Allow-Credentials is not needed and must not be set alongside\n // a reflected origin (that combination is a known CORS misconfiguration).\n if (origin) {\n corsHeaders[\"Access-Control-Allow-Origin\"] = origin;\n }\n // Disable Nagle's algorithm so small SSE chunks are sent immediately\n res.socket?.setNoDelay(true);\n res.writeHead(200, corsHeaders);\n res.flushHeaders();\n // Send initial comment immediately so proxies (Cloudflare, nginx) flush headers to client\n res.write(': connected\\n\\n');\n\n // Store filter metadata on the response for broadcast\n (res as SSEResponse).sessionFilter = sessionFilter ?? undefined;\n\n this.sseConnections.add(res);\n\n const cleanup = () => {\n this.sseConnections.delete(res);\n this.sseCleanupHandlers.delete(res);\n console.log(`[sse] -connection remaining=${this.sseConnections.size}`);\n };\n this.sseCleanupHandlers.set(res, cleanup);\n req.on(\"close\", cleanup);\n }\n\n /**\n * Broadcasts an event to all connected SSE clients.\n *\n * Session-scoped events (agent_event, permission_request, etc.) are filtered per-connection:\n * a client that subscribed with `?sessionId=X` only receives events for session X.\n * Global events (session_created, health) are delivered to every client.\n */\n broadcast(event: string, data: unknown): void {\n const payload = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`;\n // Events that carry sessionId and should be filtered\n const sessionEvents: string[] = [\n BusEvent.AGENT_EVENT,\n BusEvent.PERMISSION_REQUEST,\n BusEvent.PERMISSION_RESOLVED,\n BusEvent.SESSION_UPDATED,\n BusEvent.MESSAGE_QUEUED,\n BusEvent.MESSAGE_PROCESSING,\n BusEvent.MESSAGE_FAILED,\n BusEvent.PROMPT_WAITING,\n ];\n for (const res of this.sseConnections) {\n const filter = (res as SSEResponse).sessionFilter;\n if (filter && sessionEvents.includes(event)) {\n const eventData = data as { sessionId: string };\n if (eventData.sessionId !== filter) continue;\n }\n try {\n if (res.writable) res.write(payload);\n } catch {\n /* connection closed */\n }\n }\n }\n\n /**\n * Returns a Fastify route handler that hijacks the response\n * and delegates to the raw http SSE handler.\n */\n createFastifyHandler() {\n return async (request: FastifyRequest, reply: FastifyReply) => {\n reply.hijack();\n this.handleRequest(request.raw, reply.raw);\n };\n }\n\n /**\n * Stops the heartbeat, removes all EventBus listeners, and closes all open SSE connections.\n *\n * Only listeners registered by this instance are removed — other consumers on the same\n * EventBus are unaffected.\n */\n stop(): void {\n if (this.healthInterval) clearInterval(this.healthInterval);\n\n // Remove only our own event bus listeners\n if (this.eventBus) {\n for (const { event, handler } of this.boundHandlers) {\n this.eventBus.off(event, handler);\n }\n }\n this.boundHandlers = [];\n\n // Copy to avoid modifying Map while iterating\n const entries = [...this.sseCleanupHandlers];\n for (const [res, cleanup] of entries) {\n res.end();\n cleanup();\n }\n }\n}\n\nexport type { SessionStats };\n","import * as http from \"node:http\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst MIME_TYPES: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"application/javascript; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".svg\": \"image/svg+xml\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n};\n\n/**\n * Serves the bundled OpenACP App (a Vite SPA) from the local filesystem.\n *\n * Two directory layouts are probed at startup to support both the development\n * build (`ui/dist/`) and the npm publish layout (`../ui/`). If neither exists,\n * `isAvailable()` returns false and the server skips static file serving.\n *\n * Path traversal is blocked in two stages:\n * 1. String prefix check before resolving symlinks (catches obvious `..` segments).\n * 2. `realpathSync` check after resolution (catches symlinks that escape the UI dir).\n *\n * Vite content-hashed assets (`*.{hash}.js/css`) receive a 1-year immutable cache.\n * All other files use `no-cache` so updates roll out on the next page refresh.\n *\n * Non-asset routes fall through to `index.html` (SPA client-side routing).\n */\nexport class StaticServer {\n private uiDir: string | undefined;\n\n constructor(uiDir?: string) {\n this.uiDir = uiDir;\n\n if (!this.uiDir) {\n const __filename = fileURLToPath(import.meta.url);\n const candidate = path.resolve(path.dirname(__filename), \"../../ui/dist\");\n if (fs.existsSync(path.join(candidate, \"index.html\"))) {\n this.uiDir = candidate;\n }\n // Also check dist-publish layout\n if (!this.uiDir) {\n const publishCandidate = path.resolve(\n path.dirname(__filename),\n \"../ui\",\n );\n if (fs.existsSync(path.join(publishCandidate, \"index.html\"))) {\n this.uiDir = publishCandidate;\n }\n }\n }\n }\n\n /** Returns true if a UI build was found and static serving is active. */\n isAvailable(): boolean {\n return this.uiDir !== undefined;\n }\n\n /**\n * Attempts to serve a static file or SPA fallback for the given request.\n *\n * @returns true if the response was handled, false if the caller should return a 404.\n */\n serve(req: http.IncomingMessage, res: http.ServerResponse): boolean {\n if (!this.uiDir) return false;\n\n const urlPath = (req.url || \"/\").split(\"?\")[0];\n const safePath = path.normalize(urlPath);\n\n // Try exact file match\n const filePath = path.join(this.uiDir, safePath);\n if (!filePath.startsWith(this.uiDir + path.sep) && filePath !== this.uiDir)\n return false; // path traversal guard (pre-symlink check)\n\n // Resolve symlinks to prevent traversal via symlinks pointing outside uiDir.\n // Only apply the symlink guard when the file actually exists — a non-existent\n // path cannot be a symlink pointing anywhere, so skipping the guard is safe\n // and avoids false negatives on systems where the temp directory itself is a\n // symlink (e.g. macOS where /var → /private/var).\n let realFilePath: string | null;\n try {\n realFilePath = fs.realpathSync(filePath);\n } catch {\n // File does not exist — fall through to SPA fallback without symlink check\n realFilePath = null;\n }\n\n if (realFilePath !== null) {\n const realUiDir = fs.realpathSync(this.uiDir);\n if (!realFilePath.startsWith(realUiDir + path.sep) && realFilePath !== realUiDir)\n return false; // symlink traversal guard\n }\n\n if (realFilePath !== null && fs.existsSync(realFilePath) && fs.statSync(realFilePath).isFile()) {\n const ext = path.extname(filePath);\n const contentType = MIME_TYPES[ext] ?? \"application/octet-stream\";\n // Vite-hashed assets get long cache, others get no-cache\n const isHashed = /\\.[a-zA-Z0-9]{8,}\\.(js|css)$/.test(filePath);\n const cacheControl = isHashed\n ? \"public, max-age=31536000, immutable\"\n : \"no-cache\";\n res.writeHead(200, {\n \"Content-Type\": contentType,\n \"Cache-Control\": cacheControl,\n });\n fs.createReadStream(realFilePath).pipe(res);\n return true;\n }\n\n // SPA fallback — serve index.html\n const indexPath = path.join(this.uiDir, \"index.html\");\n if (fs.existsSync(indexPath)) {\n res.writeHead(200, {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"Cache-Control\": \"no-cache\",\n });\n fs.createReadStream(indexPath).pipe(res);\n return true;\n }\n\n return false;\n }\n}\n","import type { STTProvider, TTSProvider, STTOptions, STTResult, TTSOptions, TTSResult, SpeechServiceConfig } from './speech-types.js';\n\n/**\n * A factory that recreates provider instances from a new config snapshot.\n * Used for hot-reload: when settings change, the plugin calls `refreshProviders`\n * which invokes this factory to build fresh provider objects.\n *\n * Returns separate Maps for STT and TTS so that externally-registered providers\n * (e.g. from `@openacp/msedge-tts-plugin`) are not discarded — only factory-owned\n * providers are overwritten.\n */\nexport type ProviderFactory = (config: SpeechServiceConfig) => { stt: Map<string, STTProvider>; tts: Map<string, TTSProvider> };\n\n/**\n * Central service for speech-to-text and text-to-speech operations.\n *\n * Providers are registered at setup time and may also be registered by external\n * plugins (e.g. `@openacp/msedge-tts-plugin` registers a TTS provider after boot).\n * The service itself is registered under the `\"speech\"` key in the ServiceRegistry\n * and accessed by `session.ts` to synthesize audio after agent responses when\n * `voiceMode` is active.\n */\nexport class SpeechService {\n private sttProviders = new Map<string, STTProvider>();\n private ttsProviders = new Map<string, TTSProvider>();\n private providerFactory?: ProviderFactory;\n\n constructor(private config: SpeechServiceConfig) {}\n\n /** Set a factory function that can recreate providers from config (for hot-reload) */\n setProviderFactory(factory: ProviderFactory): void {\n this.providerFactory = factory;\n }\n\n /** Register an STT provider by name. Overwrites any existing provider with the same name. */\n registerSTTProvider(name: string, provider: STTProvider): void {\n this.sttProviders.set(name, provider);\n }\n\n /** Register a TTS provider by name. Called by external TTS plugins (e.g. msedge-tts-plugin). */\n registerTTSProvider(name: string, provider: TTSProvider): void {\n this.ttsProviders.set(name, provider);\n }\n\n /** Remove a TTS provider — called by external plugins on teardown. */\n unregisterTTSProvider(name: string): void {\n this.ttsProviders.delete(name);\n }\n\n /** Returns true if an STT provider is configured and has credentials. */\n isSTTAvailable(): boolean {\n const { provider, providers } = this.config.stt;\n return provider !== null && providers[provider]?.apiKey !== undefined;\n }\n\n /**\n * Returns true if a TTS provider is configured and an implementation is registered.\n *\n * Config alone is not enough — the TTS provider plugin must have registered\n * its implementation via `registerTTSProvider` before this returns true.\n */\n isTTSAvailable(): boolean {\n const provider = this.config.tts.provider;\n return provider !== null && this.ttsProviders.has(provider);\n }\n\n /**\n * Transcribes audio using the configured STT provider.\n *\n * @throws if no STT provider is configured or if the named provider is not registered.\n */\n async transcribe(audioBuffer: Buffer, mimeType: string, options?: STTOptions): Promise<STTResult> {\n const providerName = this.config.stt.provider;\n if (!providerName || !this.config.stt.providers[providerName]?.apiKey) {\n throw new Error(\"STT not configured. Set speech.stt.provider and API key in config.\");\n }\n const provider = this.sttProviders.get(providerName);\n if (!provider) {\n throw new Error(`STT provider \"${providerName}\" not registered. Available: ${[...this.sttProviders.keys()].join(\", \") || \"none\"}`);\n }\n return provider.transcribe(audioBuffer, mimeType, options);\n }\n\n /**\n * Synthesizes speech using the configured TTS provider.\n *\n * @throws if no TTS provider is configured or if the named provider is not registered.\n */\n async synthesize(text: string, options?: TTSOptions): Promise<TTSResult> {\n const providerName = this.config.tts.provider;\n if (!providerName) {\n throw new Error(\"TTS not configured. Set speech.tts.provider in config.\");\n }\n const provider = this.ttsProviders.get(providerName);\n if (!provider) {\n throw new Error(`TTS provider \"${providerName}\" not registered. Available: ${[...this.ttsProviders.keys()].join(\", \") || \"none\"}`);\n }\n return provider.synthesize(text, options);\n }\n\n /** Replace the active config without rebuilding providers. Use `refreshProviders` to also rebuild. */\n updateConfig(config: SpeechServiceConfig): void {\n this.config = config;\n }\n\n /**\n * Reloads TTS and STT providers from a new config snapshot.\n *\n * Called after config changes or plugin hot-reload. Factory-managed providers are\n * rebuilt via the registered `ProviderFactory`; externally-registered providers\n * (e.g. from `@openacp/msedge-tts-plugin`) are preserved rather than discarded.\n */\n refreshProviders(newConfig: SpeechServiceConfig): void {\n this.config = newConfig;\n if (this.providerFactory) {\n const { stt, tts } = this.providerFactory(newConfig);\n // Merge: factory providers overwrite, but externally-registered providers are preserved\n for (const [name, provider] of stt) {\n this.sttProviders.set(name, provider);\n }\n for (const [name, provider] of tts) {\n this.ttsProviders.set(name, provider);\n }\n }\n }\n}\n","import type { STTProvider, STTOptions, STTResult } from '../speech-types.js';\n\n// Groq's Whisper-compatible transcription endpoint (OpenAI-compatible API)\nconst GROQ_API_URL = \"https://api.groq.com/openai/v1/audio/transcriptions\";\n\n/**\n * Speech-to-text provider backed by Groq's hosted Whisper API.\n *\n * Groq requires the audio to be submitted as a multipart form upload. The file\n * must have a valid extension matching its MIME type — Groq uses the extension\n * to determine the codec, so a mismatch causes a transcription error.\n *\n * Free tier limit: 28,800 seconds of audio per day. Max file size: 25 MB.\n */\nexport class GroqSTT implements STTProvider {\n readonly name = \"groq\";\n\n constructor(\n private apiKey: string,\n private defaultModel: string = \"whisper-large-v3-turbo\",\n ) {}\n\n /**\n * Transcribes audio using the Groq Whisper API.\n *\n * `verbose_json` response format is requested so the API returns language\n * detection and duration metadata alongside the transcript text.\n */\n async transcribe(audioBuffer: Buffer, mimeType: string, options?: STTOptions): Promise<STTResult> {\n const ext = mimeToExt(mimeType);\n const form = new FormData();\n // Groq uses the filename extension to identify the audio codec — must match mimeType\n form.append(\"file\", new Blob([new Uint8Array(audioBuffer)], { type: mimeType }), `audio${ext}`);\n form.append(\"model\", options?.model || this.defaultModel);\n form.append(\"response_format\", \"verbose_json\");\n if (options?.language) {\n form.append(\"language\", options.language);\n }\n\n const resp = await fetch(GROQ_API_URL, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n body: form,\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n if (resp.status === 401) {\n throw new Error(\"Invalid Groq API key. Check your key at console.groq.com.\");\n }\n if (resp.status === 413) {\n throw new Error(\"Audio file too large for Groq API (max 25MB).\");\n }\n if (resp.status === 429) {\n throw new Error(\"Groq rate limit exceeded. Free tier: 28,800 seconds/day. Try again later.\");\n }\n throw new Error(`Groq STT error (${resp.status}): ${body}`);\n }\n\n const data = await resp.json() as { text: string; language?: string; duration?: number };\n return {\n text: data.text,\n language: data.language,\n duration: data.duration,\n };\n }\n}\n\n/** Maps MIME types to file extensions required by the Groq upload API. */\nfunction mimeToExt(mimeType: string): string {\n const map: Record<string, string> = {\n \"audio/ogg\": \".ogg\",\n \"audio/wav\": \".wav\",\n \"audio/mpeg\": \".mp3\",\n \"audio/mp4\": \".m4a\",\n \"audio/webm\": \".webm\",\n \"audio/flac\": \".flac\",\n };\n return map[mimeType] || \".bin\";\n}\n","// Public surface of the speech plugin — re-exported for use by the plugin index\n// and by external TTS plugins that need to register against SpeechService.\nexport type { STTProvider, TTSProvider, STTOptions, STTResult, TTSOptions, TTSResult, SpeechServiceConfig, SpeechProviderConfig } from './speech-types.js';\nexport { SpeechService } from './speech-service.js';\nexport { GroqSTT } from './providers/groq.js';\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport type { ContextResult } from \"./context-provider.js\";\n\n// Building context from checkpoints or history involves disk reads and markdown rendering.\n// Cache results on disk for 1 hour so repeated agent switches to the same context are fast.\nconst DEFAULT_TTL_MS = 60 * 60 * 1000;\n\n/**\n * Disk-backed cache for built ContextResult objects.\n *\n * Cache keys are a hash of (repoPath + queryKey) so different repos and query\n * parameters never collide. TTL is enforced on read — stale files are deleted lazily.\n * Callers can bypass the cache via `ContextOptions.noCache` for live agent switches\n * where history was just written and would otherwise be served stale.\n */\nexport class ContextCache {\n constructor(private cacheDir: string, private ttlMs: number = DEFAULT_TTL_MS) {\n fs.mkdirSync(cacheDir, { recursive: true });\n }\n\n private keyHash(repoPath: string, queryKey: string): string {\n return crypto.createHash(\"sha256\").update(`${repoPath}:${queryKey}`).digest(\"hex\").slice(0, 16);\n }\n\n private filePath(repoPath: string, queryKey: string): string {\n return path.join(this.cacheDir, `${this.keyHash(repoPath, queryKey)}.json`);\n }\n\n get(repoPath: string, queryKey: string): ContextResult | null {\n const fp = this.filePath(repoPath, queryKey);\n try {\n const stat = fs.statSync(fp);\n if (Date.now() - stat.mtimeMs > this.ttlMs) { fs.unlinkSync(fp); return null; }\n return JSON.parse(fs.readFileSync(fp, \"utf-8\")) as ContextResult;\n } catch { return null; }\n }\n\n set(repoPath: string, queryKey: string, result: ContextResult): void {\n fs.writeFileSync(this.filePath(repoPath, queryKey), JSON.stringify(result));\n }\n}\n","import type { ContextProvider, ContextQuery, ContextOptions, ContextResult, SessionListResult } from \"./context-provider.js\";\nimport type { HistoryStore } from \"./history/history-store.js\";\nimport type { SessionHistory } from \"./history/types.js\";\nimport { ContextCache } from \"./context-cache.js\";\n\n/**\n * Orchestrates context providers and caching.\n *\n * Providers are tried in registration order — the first one that is available\n * and returns non-empty markdown wins. This lets the \"local\" history provider\n * take priority over the \"entire\" checkpoint provider for sessions that are\n * still in progress (not yet checkpointed).\n *\n * The context service is registered under the `\"context\"` key in the\n * ServiceRegistry so other plugins (e.g. the git-pilot plugin) can call\n * `buildContext()` before injecting context into a new agent.\n */\nexport class ContextManager {\n private providers: ContextProvider[] = [];\n private cache: ContextCache;\n private historyStore?: HistoryStore;\n private sessionFlusher?: (sessionId: string) => Promise<void>;\n\n constructor(cachePath: string) {\n this.cache = new ContextCache(cachePath);\n }\n\n /**\n * Wire in the history store after construction.\n *\n * Injected separately because the history store (backed by the context plugin's\n * recorder) may not be ready when ContextManager is first instantiated.\n */\n setHistoryStore(store: HistoryStore): void {\n this.historyStore = store;\n }\n\n /** Register a callback that flushes in-memory recorder state for a session to disk. */\n registerFlusher(fn: (sessionId: string) => Promise<void>): void {\n this.sessionFlusher = fn;\n }\n\n /**\n * Flush the recorder state for a session to disk before reading its context.\n * Call this before buildContext() when switching agents to avoid a race\n * where the last turn hasn't been persisted yet.\n */\n async flushSession(sessionId: string): Promise<void> {\n if (this.sessionFlusher) await this.sessionFlusher(sessionId);\n }\n\n /**\n * Read the raw history for a session directly from the history store.\n *\n * Returns null if no historyStore has been configured via `setHistoryStore()`,\n * or if the session has no recorded history.\n */\n async getHistory(sessionId: string): Promise<SessionHistory | null> {\n if (!this.historyStore) return null;\n return this.historyStore.read(sessionId);\n }\n\n /**\n * Register a provider. Providers are queried in insertion order.\n * Register higher-priority sources (e.g. local history) before lower-priority ones (e.g. entire).\n */\n register(provider: ContextProvider): void {\n this.providers.push(provider);\n }\n\n /**\n * Return the first provider that reports itself available for the given repo.\n *\n * This is a availability check — it returns the highest-priority available provider\n * (i.e. the first registered one that passes `isAvailable`), not necessarily the\n * one that would yield the richest context for a specific query.\n */\n async getProvider(repoPath: string): Promise<ContextProvider | null> {\n for (const provider of this.providers) {\n if (await provider.isAvailable(repoPath)) return provider;\n }\n return null;\n }\n\n /**\n * List sessions using the same provider-waterfall logic as `buildContext`.\n *\n * Tries each registered provider in order, returning the first non-empty result.\n * Unlike `buildContext`, results are not cached — callers should avoid calling\n * this in hot paths.\n */\n async listSessions(query: ContextQuery): Promise<SessionListResult | null> {\n for (const provider of this.providers) {\n if (!(await provider.isAvailable(query.repoPath))) continue;\n const result = await provider.listSessions(query);\n if (result.sessions.length > 0) return result;\n }\n return null;\n }\n\n /**\n * Build a context block for injection into an agent prompt.\n *\n * Tries each registered provider in order. Results are cached by (repoPath + queryKey)\n * to avoid redundant disk reads. Pass `options.noCache = true` when the caller knows\n * the history just changed (e.g. immediately after an agent switch + flush).\n */\n async buildContext(query: ContextQuery, options?: ContextOptions): Promise<ContextResult | null> {\n const queryKey = `${query.type}:${query.value}:${options?.limit ?? \"\"}:${options?.maxTokens ?? \"\"}:${options?.labelAgent ?? \"\"}`;\n\n if (!options?.noCache) {\n const cached = this.cache.get(query.repoPath, queryKey);\n if (cached) return cached;\n }\n\n for (const provider of this.providers) {\n if (!(await provider.isAvailable(query.repoPath))) continue;\n const result = await provider.buildContext(query, options);\n if (result && result.markdown) {\n if (!options?.noCache) {\n this.cache.set(query.repoPath, queryKey, result);\n }\n return result;\n }\n }\n return null;\n }\n}\n","// NOTE: This interface is designed around Entire as the first provider.\n// It may evolve when additional providers (Cursor history, Zed, etc.) are added.\n// Providers may only support a subset of query types and should return empty results\n// for unsupported types rather than throwing.\n\n/**\n * Abstract interface for conversation context sources.\n *\n * Two providers are built-in: \"local\" (history recorder) and \"entire\" (Claude Code checkpoints).\n * ContextManager iterates providers in priority order and returns the first non-empty result.\n */\nexport interface ContextProvider {\n readonly name: string;\n isAvailable(repoPath: string): Promise<boolean>;\n listSessions(query: ContextQuery): Promise<SessionListResult>;\n buildContext(query: ContextQuery, options?: ContextOptions): Promise<ContextResult>;\n}\n\n/**\n * Describes which sessions to include in a context build.\n *\n * - `type: \"latest\"` with `value: \"5\"` returns the 5 most recent sessions.\n * - `type: \"session\"` with a UUID returns exactly that session.\n * - `type: \"branch\"` / `\"commit\"` / `\"pr\"` are only supported by the \"entire\" provider\n * which reads Claude Code checkpoints stored in the git repo.\n */\nexport interface ContextQuery {\n repoPath: string;\n type: \"branch\" | \"commit\" | \"pr\" | \"latest\" | \"checkpoint\" | \"session\";\n value: string;\n}\n\nexport interface ContextOptions {\n maxTokens?: number;\n limit?: number;\n /** When true, insert `## [agentName]` headers at agent boundaries in merged history */\n labelAgent?: boolean;\n /** When true, skip the context cache (use for live switches where history just changed) */\n noCache?: boolean;\n}\n\n/**\n * Metadata for a single recorded session, used when listing available context.\n * Fields like `checkpointId` and `transcriptPath` are populated by the \"entire\" provider;\n * the \"local\" provider leaves them empty since it stores sessions in its own HistoryStore.\n */\nexport interface SessionInfo {\n checkpointId: string;\n sessionIndex: string;\n transcriptPath: string;\n createdAt: string;\n endedAt: string;\n branch: string;\n agent: string;\n turnCount: number;\n filesTouched: string[];\n sessionId: string;\n}\n\nexport interface SessionListResult {\n sessions: SessionInfo[];\n estimatedTokens: number;\n}\n\n/**\n * Controls how much detail is rendered per turn in the context markdown.\n * - `full`: full diffs, tool call outputs, thinking blocks, usage stats\n * - `balanced`: diffs truncated, thinking omitted\n * - `compact`: single-line summary per turn pair (user + tools used)\n */\nexport type ContextMode = \"full\" | \"balanced\" | \"compact\";\n\n/**\n * The built context block to be prepended to an agent prompt.\n * `markdown` is the formatted text; `truncated` is true when oldest sessions\n * were dropped to fit within `maxTokens`.\n */\nexport interface ContextResult {\n markdown: string;\n tokenEstimate: number;\n sessionCount: number;\n totalTurns: number;\n mode: ContextMode;\n truncated: boolean;\n timeRange: { start: string; end: string };\n}\n\nexport const DEFAULT_MAX_TOKENS = 30_000;\nexport const TOKENS_PER_TURN_ESTIMATE = 400;\n","import { execFileSync } from \"child_process\";\nimport type { SessionInfo } from \"../context-provider.js\";\n\n// ─── Internal types ────────────────────────────────────────────────────────────\n\ninterface CheckpointMeta {\n checkpoint_id?: string;\n branch?: string;\n files_touched?: string[];\n sessions: Array<{\n metadata: string;\n transcript: string;\n }>;\n}\n\ninterface SessionMeta {\n session_id?: string;\n created_at?: string;\n branch?: string;\n agent?: string;\n files_touched?: string[];\n session_metrics?: {\n turn_count?: number;\n };\n}\n\n// ─── CheckpointReader ─────────────────────────────────────────────────────────\n\n// The Entire Claude Code extension pushes session data to this branch.\n// All git show / ls-tree commands read from this remote branch so we never\n// need to check it out locally.\nconst ENTIRE_BRANCH = \"origin/entire/checkpoints/v1\";\nconst CHECKPOINT_ID_RE = /^[0-9a-f]{12}$/;\nconst SESSION_ID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\n/**\n * Reads Claude Code session data from the `origin/entire/checkpoints/v1` git branch.\n *\n * The branch layout is:\n * `<shard[0:2]>/<shard[2:12]>/metadata.json` — checkpoint-level metadata\n * `<shard[0:2]>/<shard[2:12]>/<session-idx>/metadata.json` — per-session metadata\n * `<shard[0:2]>/<shard[2:12]>/<session-idx>/transcript.jsonl` — raw JSONL conversation\n *\n * All git operations use `execFileSync` so errors are caught and returned as\n * empty strings rather than throwing — callers get null/[] on missing data.\n */\nexport class CheckpointReader {\n constructor(private readonly repoPath: string) {}\n\n // ─── Git execution ───────────────────────────────────────────────────────────\n\n /**\n * Run a git command in the repo directory.\n * Returns trimmed stdout on success, empty string on failure.\n */\n private git(...args: string[]): string {\n try {\n return execFileSync(\"git\", [\"-C\", this.repoPath, ...args], {\n encoding: \"utf-8\",\n }).trim();\n } catch {\n return \"\";\n }\n }\n\n // ─── Static helpers ──────────────────────────────────────────────────────────\n\n /**\n * Convert a 12-char checkpoint ID to its shard path: \"f634acf05138\" → \"f6/34acf05138\"\n */\n static shardPath(cpId: string): string {\n return `${cpId.slice(0, 2)}/${cpId.slice(2)}`;\n }\n\n /**\n * Returns true when value looks like a 12-char lowercase hex checkpoint ID.\n */\n static isCheckpointId(value: string): boolean {\n return CHECKPOINT_ID_RE.test(value);\n }\n\n /**\n * Returns true when value looks like a UUID (session ID).\n */\n static isSessionId(value: string): boolean {\n return SESSION_ID_RE.test(value);\n }\n\n /**\n * Parse checkpoint-level metadata JSON. Returns null on error.\n */\n static parseCheckpointMeta(json: string): CheckpointMeta | null {\n try {\n const parsed = JSON.parse(json) as CheckpointMeta;\n if (!parsed || typeof parsed !== \"object\") return null;\n if (!Array.isArray(parsed.sessions)) return null;\n return parsed;\n } catch {\n return null;\n }\n }\n\n /**\n * Extract Entire-Checkpoint trailer IDs from `git log --format=\"%H|%(trailers:...)\"` output.\n * Each line is: `<hash>|<trailer_value_or_empty>`. Returns only non-empty trailer values.\n * Uses the last pipe on each line to locate the trailer value, to be robust against\n * subject lines that contain pipes.\n */\n static parseCheckpointTrailers(output: string): string[] {\n const ids: string[] = [];\n for (const line of output.split(\"\\n\")) {\n const pipe = line.lastIndexOf(\"|\");\n if (pipe === -1) continue;\n const trailerId = line.slice(pipe + 1).trim();\n if (trailerId) ids.push(trailerId);\n }\n return ids;\n }\n\n // ─── Branch check ────────────────────────────────────────────────────────────\n\n async hasEntireBranch(): Promise<boolean> {\n const out = this.git(\"branch\", \"-r\");\n return out.includes(\"entire/checkpoints/v1\");\n }\n\n // ─── Core session fetching ───────────────────────────────────────────────────\n\n private listAllCheckpointIds(): string[] {\n const out = this.git(\n \"ls-tree\",\n \"-r\",\n ENTIRE_BRANCH,\n \"--name-only\"\n );\n if (!out) return [];\n\n const ids = new Set<string>();\n for (const file of out.split(\"\\n\")) {\n const parts = file.split(\"/\");\n // Checkpoint-level metadata lives at depth 3: XX/YYYYYYYYYY/metadata.json\n // Session-level files are deeper — skip them here.\n if (parts.length === 3 && parts[2] === \"metadata.json\") {\n ids.add(parts[0] + parts[1]);\n }\n }\n return [...ids];\n }\n\n private fetchCheckpointMeta(cpId: string): CheckpointMeta | null {\n const shard = CheckpointReader.shardPath(cpId);\n const raw = this.git(\"show\", `${ENTIRE_BRANCH}:${shard}/metadata.json`);\n if (!raw) return null;\n return CheckpointReader.parseCheckpointMeta(raw);\n }\n\n private fetchSessionMeta(metaPath: string): SessionMeta {\n const normalized = metaPath.startsWith(\"/\") ? metaPath.slice(1) : metaPath;\n const raw = this.git(\"show\", `${ENTIRE_BRANCH}:${normalized}`);\n if (!raw) return {};\n try {\n return JSON.parse(raw) as SessionMeta;\n } catch {\n return {};\n }\n }\n\n /**\n * Build SessionInfo[] from a single checkpoint's metadata.\n */\n private buildSessionsForCheckpoint(\n cpId: string,\n cpMeta: CheckpointMeta\n ): SessionInfo[] {\n const sessions: SessionInfo[] = [];\n\n for (let idx = 0; idx < cpMeta.sessions.length; idx++) {\n const sess = cpMeta.sessions[idx];\n const transcriptPath = (sess.transcript ?? \"\").replace(/^\\//, \"\");\n const metaPath = sess.metadata ?? \"\";\n\n const smeta = this.fetchSessionMeta(metaPath);\n const createdAt = smeta.created_at ?? \"\";\n\n sessions.push({\n checkpointId: cpId,\n sessionIndex: String(idx),\n transcriptPath,\n createdAt,\n // endedAt isn't stored in the checkpoint metadata; EntireProvider fills\n // it from the last turn timestamp when parsing the JSONL transcript.\n endedAt: createdAt,\n branch: smeta.branch ?? cpMeta.branch ?? \"\",\n agent: smeta.agent ?? \"\",\n turnCount: smeta.session_metrics?.turn_count ?? 0,\n filesTouched: smeta.files_touched ?? cpMeta.files_touched ?? [],\n sessionId: smeta.session_id ?? \"\",\n });\n }\n\n return sessions;\n }\n\n private getSessionsForCheckpoint(cpId: string): SessionInfo[] {\n const meta = this.fetchCheckpointMeta(cpId);\n if (!meta) return [];\n return this.buildSessionsForCheckpoint(cpId, meta);\n }\n\n // ─── Public resolvers ────────────────────────────────────────────────────────\n\n /**\n * All sessions recorded on a given branch, sorted by createdAt ascending.\n */\n async resolveByBranch(branchName: string): Promise<SessionInfo[]> {\n const cpIds = this.listAllCheckpointIds();\n const sessions: SessionInfo[] = [];\n\n for (const cpId of cpIds) {\n const meta = this.fetchCheckpointMeta(cpId);\n if (!meta) continue;\n if (meta.branch !== branchName) continue;\n sessions.push(...this.buildSessionsForCheckpoint(cpId, meta));\n }\n\n sessions.sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n return sessions;\n }\n\n /**\n * Sessions linked to a specific commit via the Entire-Checkpoint git trailer.\n */\n async resolveByCommit(commitHash: string): Promise<SessionInfo[]> {\n const fullHash = this.git(\"rev-parse\", commitHash);\n if (!fullHash) return [];\n\n const cpId = this.git(\n \"log\",\n \"-1\",\n \"--format=%(trailers:key=Entire-Checkpoint,valueonly)\",\n fullHash\n );\n if (!cpId) return [];\n\n return this.getSessionsForCheckpoint(cpId.trim());\n }\n\n /**\n * All sessions from a merged PR (by number or GitHub URL).\n */\n async resolveByPr(prInput: string): Promise<SessionInfo[]> {\n let prNumber: string;\n\n if (/^\\d+$/.test(prInput)) {\n prNumber = prInput;\n } else {\n const m = /\\/pull\\/(\\d+)/.exec(prInput);\n if (!m) return [];\n prNumber = m[1];\n }\n\n const mergeOut = this.git(\n \"log\",\n \"--all\",\n \"--oneline\",\n \"--grep\",\n `Merge pull request #${prNumber}`\n );\n if (!mergeOut) return [];\n\n const mergeCommit = mergeOut.split(\"\\n\")[0].split(\" \")[0];\n\n const logOut = this.git(\n \"log\",\n \"--format=%H|%(trailers:key=Entire-Checkpoint,valueonly)\",\n `${mergeCommit}^2`,\n \"--not\",\n `${mergeCommit}^1`\n );\n if (!logOut) return [];\n\n const cpIds = CheckpointReader.parseCheckpointTrailers(logOut);\n const sessions: SessionInfo[] = [];\n\n for (const cpId of cpIds) {\n sessions.push(...this.getSessionsForCheckpoint(cpId));\n }\n\n sessions.sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n return sessions;\n }\n\n /**\n * Sessions matching a specific checkpoint ID.\n */\n async resolveByCheckpoint(checkpointId: string): Promise<SessionInfo[]> {\n return this.getSessionsForCheckpoint(checkpointId);\n }\n\n /**\n * Find a session by its UUID.\n */\n async resolveBySessionId(sessionId: string): Promise<SessionInfo[]> {\n const cpIds = this.listAllCheckpointIds();\n\n for (const cpId of cpIds) {\n const sessions = this.getSessionsForCheckpoint(cpId);\n const match = sessions.find((s) => s.sessionId === sessionId);\n if (match) return [match];\n }\n\n return [];\n }\n\n /**\n * Latest N sessions across all checkpoints, sorted by createdAt descending.\n */\n async resolveLatest(count: number): Promise<SessionInfo[]> {\n const cpIds = this.listAllCheckpointIds();\n const all: SessionInfo[] = [];\n\n for (const cpId of cpIds) {\n all.push(...this.getSessionsForCheckpoint(cpId));\n }\n\n all.sort((a, b) => b.createdAt.localeCompare(a.createdAt));\n return all.slice(0, count);\n }\n\n /**\n * Read the full JSONL transcript content from the entire branch.\n */\n getTranscript(transcriptPath: string): string {\n const normalized = transcriptPath.startsWith(\"/\")\n ? transcriptPath.slice(1)\n : transcriptPath;\n return this.git(\"show\", `${ENTIRE_BRANCH}:${normalized}`);\n }\n}\n","// Claude Code injects XML tags into user prompts to pass context (IDE state, commands, etc.).\n// These tags are machine-generated noise that would pollute the conversation history shown\n// to the next agent. Strip them before storing or rendering turns.\nconst SYSTEM_TAG_PATTERNS: RegExp[] = [\n /<system-reminder>[\\s\\S]*?<\\/system-reminder>/g,\n /<local-command-caveat>[\\s\\S]*?<\\/local-command-caveat>/g,\n /<local-command-stdout>[\\s\\S]*?<\\/local-command-stdout>/g,\n /<command-name>[\\s\\S]*?<\\/command-name>/g,\n /<command-message>[\\s\\S]*?<\\/command-message>/g,\n /<user-prompt-submit-hook>[\\s\\S]*?<\\/user-prompt-submit-hook>/g,\n /<ide_selection>[\\s\\S]*?<\\/ide_selection>/g,\n /<ide_context>[\\s\\S]*?<\\/ide_context>/g,\n /<ide_opened_file>[\\s\\S]*?<\\/ide_opened_file>/g,\n /<cursor_context>[\\s\\S]*?<\\/cursor_context>/g,\n /<attached_files>[\\s\\S]*?<\\/attached_files>/g,\n /<repo_context>[\\s\\S]*?<\\/repo_context>/g,\n /<task-notification>[\\s\\S]*?<\\/task-notification>/g,\n];\n\n// `<command-args>` is special: its content is the user-visible portion of a slash command.\n// Preserve that text while stripping the wrapping tag.\nconst COMMAND_ARGS_RE = /<command-args>([\\s\\S]*?)<\\/command-args>/;\n\n/**\n * Remove Claude Code XML injection tags from a user message.\n *\n * `<command-args>` content is extracted and appended as plain text so the\n * human-readable portion of slash commands survives after tag removal.\n */\nexport function cleanSystemTags(text: string): string {\n const argsMatch = COMMAND_ARGS_RE.exec(text);\n const userArgs = argsMatch?.[1]?.trim() ?? \"\";\n text = text.replace(/<command-args>[\\s\\S]*?<\\/command-args>/g, \"\");\n for (const pat of SYSTEM_TAG_PATTERNS) {\n text = text.replace(new RegExp(pat.source, pat.flags), \"\");\n }\n text = text.trim();\n if (!text && userArgs) return userArgs;\n if (text && userArgs && text !== userArgs) return `${text}\\n${userArgs}`;\n return text || userArgs;\n}\n\n// Skill prompts are large structured instructions injected by the slash-command system.\n// They're not real user messages — including them in history would add thousands of tokens\n// of structured boilerplate that provides no useful context for subsequent agents.\nconst SKILL_INDICATORS = [\n \"Base directory for this skill:\",\n \"<HARD-GATE>\",\n \"## Checklist\",\n \"## Process Flow\",\n \"## Key Principles\",\n \"digraph brainstorming\",\n \"You MUST create a task for each\",\n];\n\n/**\n * Returns true for injected skill/command prompts that should be omitted from history.\n * Heuristic: known indicator strings OR very long prompts with many H2 headers.\n */\nexport function isSkillPrompt(text: string): boolean {\n for (const indicator of SKILL_INDICATORS) {\n if (text.includes(indicator)) return true;\n }\n // Long prompts with 3+ H2 headers are almost certainly structured skill instructions\n if (text.length > 2000) {\n const headerCount = (text.match(/## /g) || []).length;\n if (headerCount >= 3) return true;\n }\n return false;\n}\n\n/**\n * Returns true for short, meaningless messages that don't belong in history.\n * Covers: blank messages after tag removal, model-select commands, deprecated command stubs.\n */\nexport function isNoiseMessage(text: string): boolean {\n const cleaned = cleanSystemTags(text);\n if (!cleaned) return true;\n if (/^(ready|ready\\.)$/i.test(cleaned)) return true;\n if (cleaned.includes(\"Tell your human partner that this command is deprecated\")) return true;\n if (cleaned.startsWith(\"Read the output file to retrieve the result:\")) return true;\n // Model selection shortcuts (e.g. \"sonnet\", \"opus[1m]\") — not real user messages\n if (/^(opus|sonnet|haiku|claude)(\\[.*\\])?$/i.test(cleaned)) return true;\n return false;\n}\n","import type { ContextMode } from \"../context-provider.js\";\nimport { cleanSystemTags, isSkillPrompt, isNoiseMessage } from \"./message-cleaner.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface AssistantPart {\n type: \"text\" | \"edit\" | \"write\";\n content?: string;\n file?: string;\n old?: string;\n new?: string;\n fileContent?: string;\n}\n\nexport interface Turn {\n userText: string;\n userTimestamp: string;\n assistantParts: AssistantPart[];\n}\n\nexport interface ParseResult {\n turns: Turn[];\n branch: string;\n firstTimestamp: string;\n lastTimestamp: string;\n}\n\nexport interface SessionMarkdownInput {\n markdown: string;\n startTime: string;\n endTime: string;\n agent: string;\n turns: number;\n branch: string;\n files: string[];\n}\n\n// ─── Mode selection ────────────────────────────────────────────────────────────\n\n/**\n * Select rendering mode based on total turn count.\n * Mirrors the same thresholds used by HistoryProvider to keep context depth consistent.\n * ≤10 → full\n * 11-25 → balanced\n * >25 → compact\n */\nexport function selectMode(totalTurns: number): ContextMode {\n if (totalTurns <= 10) return \"full\";\n if (totalTurns <= 25) return \"balanced\";\n return \"compact\";\n}\n\n// ─── Token estimation ─────────────────────────────────────────────────────────\n\n// Rough heuristic: 1 token ≈ 4 chars for English/code mixed text.\nexport function estimateTokens(text: string): number {\n return Math.floor(text.length / 4);\n}\n\n// ─── Path helpers ─────────────────────────────────────────────────────────────\n\nfunction shortenPath(fp: string): string {\n const parts = fp.split(\"/\");\n if (parts.length >= 2) return parts.slice(-2).join(\"/\");\n return fp;\n}\n\nfunction countLines(s: string): number {\n const trimmed = s.trim();\n if (!trimmed) return 0;\n return trimmed.split(\"\\n\").length;\n}\n\n// ─── Content extraction ───────────────────────────────────────────────────────\n\ntype ContentBlock = { type: string; [key: string]: unknown };\n\nfunction extractText(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return content\n .filter((b): b is ContentBlock => typeof b === \"object\" && b !== null && (b as ContentBlock).type === \"text\")\n .map((b) => (b as unknown as { text: string }).text)\n .join(\"\\n\");\n }\n return \"\";\n}\n\nfunction extractContentBlocks(content: unknown): ContentBlock[] {\n if (typeof content === \"string\") return [{ type: \"text\", text: content }];\n if (Array.isArray(content)) {\n return content.filter((b): b is ContentBlock => typeof b === \"object\" && b !== null);\n }\n return [];\n}\n\nfunction isToolResultOnly(content: unknown): boolean {\n if (typeof content === \"string\") return false;\n if (!Array.isArray(content)) return true;\n for (const block of content) {\n if (typeof block === \"object\" && block !== null) {\n const b = block as ContentBlock;\n if (b.type === \"text\" && typeof b.text === \"string\" && (b.text as string).trim()) return false;\n if (b.type === \"image\") return false;\n }\n }\n return true;\n}\n\nfunction hasImage(content: unknown): boolean {\n if (!Array.isArray(content)) return false;\n return content.some((b) => typeof b === \"object\" && b !== null && (b as ContentBlock).type === \"image\");\n}\n\n// ─── Format functions ─────────────────────────────────────────────────────────\n\nfunction formatEditFull(filePath: string, oldStr: string, newStr: string): string {\n const lines: string[] = [];\n lines.push(`✏️ \\`${filePath}\\``);\n lines.push(\"```diff\");\n for (const line of oldStr.split(\"\\n\")) lines.push(`- ${line}`);\n for (const line of newStr.split(\"\\n\")) lines.push(`+ ${line}`);\n lines.push(\"```\");\n return lines.join(\"\\n\");\n}\n\nfunction formatEditBalanced(filePath: string, oldStr: string, newStr: string, maxDiffLines = 12): string {\n const oldLines = oldStr.split(\"\\n\");\n const newLines = newStr.split(\"\\n\");\n const total = oldLines.length + newLines.length;\n const lines: string[] = [];\n lines.push(`✏️ \\`${filePath}\\``);\n lines.push(\"```diff\");\n if (total <= maxDiffLines) {\n for (const line of oldLines) lines.push(`- ${line}`);\n for (const line of newLines) lines.push(`+ ${line}`);\n } else {\n const half = Math.floor(maxDiffLines / 2);\n for (const line of oldLines.slice(0, half)) lines.push(`- ${line}`);\n if (oldLines.length > half) lines.push(` ... (-${oldLines.length} lines total)`);\n for (const line of newLines.slice(0, half)) lines.push(`+ ${line}`);\n if (newLines.length > half) lines.push(` ... (+${newLines.length} lines total)`);\n }\n lines.push(\"```\");\n return lines.join(\"\\n\");\n}\n\nfunction formatEditCompact(filePath: string, oldStr: string, newStr: string): string {\n const oldLines = countLines(oldStr);\n const newLines = countLines(newStr);\n let firstNew = \"\";\n for (const line of newStr.split(\"\\n\")) {\n const stripped = line.trim();\n if (stripped && !stripped.startsWith(\"//\") && !stripped.startsWith(\"*\")) {\n firstNew = stripped.slice(0, 80);\n break;\n }\n }\n if (firstNew) {\n return `✏️ \\`${filePath}\\` (-${oldLines}/+${newLines} lines): \\`${firstNew}\\``;\n }\n return `✏️ \\`${filePath}\\` (-${oldLines}/+${newLines} lines)`;\n}\n\nfunction formatWriteFull(filePath: string, content: string): string {\n const lines: string[] = [];\n lines.push(`📝 \\`${filePath}\\``);\n lines.push(\"```\");\n lines.push(content);\n lines.push(\"```\");\n return lines.join(\"\\n\");\n}\n\nfunction formatWriteBalanced(filePath: string, content: string, maxLines = 15): string {\n const contentLines = content.split(\"\\n\");\n const lines: string[] = [];\n lines.push(`📝 \\`${filePath}\\` (${contentLines.length} lines)`);\n lines.push(\"```\");\n for (const line of contentLines.slice(0, maxLines)) lines.push(line);\n if (contentLines.length > maxLines) lines.push(`... (${contentLines.length - maxLines} more lines)`);\n lines.push(\"```\");\n return lines.join(\"\\n\");\n}\n\nfunction formatWriteCompact(filePath: string, content: string): string {\n const numLines = countLines(content);\n return `📝 \\`${filePath}\\` (${numLines} lines written)`;\n}\n\n// ─── Parser ───────────────────────────────────────────────────────────────────\n\ninterface RawEvent {\n type?: string;\n message?: { role?: string; content?: unknown };\n timestamp?: string;\n uuid?: string;\n parentUuid?: string | null;\n sessionId?: string;\n gitBranch?: string;\n}\n\n/**\n * Parse a Claude Code JSONL transcript into structured turns.\n *\n * The JSONL format emitted by Claude Code contains one JSON object per line,\n * with `type` being either \"user\" or \"assistant\". Tool results appear as\n * \"user\" messages with `tool_result` content blocks — these are filtered out\n * since they duplicate information already captured in the assistant's tool_use\n * blocks. Only Edit and Write tool calls are surfaced; read-only tools are skipped\n * because they don't contribute meaningful context about what the agent did.\n */\nexport function parseJsonlToTurns(jsonl: string): ParseResult {\n const events: RawEvent[] = [];\n for (const rawLine of jsonl.split(\"\\n\")) {\n const line = rawLine.trim();\n if (!line) continue;\n try {\n events.push(JSON.parse(line) as RawEvent);\n } catch {\n // skip invalid JSON lines\n }\n }\n\n // Extract gitBranch from first event that has it\n let branch = \"unknown\";\n for (const e of events) {\n if (e.gitBranch) {\n branch = e.gitBranch;\n break;\n }\n }\n\n const convEvents = events.filter((e) => e.type === \"user\" || e.type === \"assistant\");\n\n const turns: Turn[] = [];\n let currentTurn: Turn | null = null;\n\n for (const e of convEvents) {\n const etype = e.type;\n const content = e.message?.content ?? [];\n const ts = e.timestamp ?? \"\";\n\n if (etype === \"user\") {\n if (isToolResultOnly(content)) continue;\n\n const text = extractText(content);\n\n if (isSkillPrompt(text)) continue;\n if (isNoiseMessage(text)) continue;\n\n const cleaned = cleanSystemTags(text);\n if (!cleaned) continue;\n\n // Push previous turn if any\n if (currentTurn) turns.push(currentTurn);\n\n const imgSuffix = hasImage(content) ? \" [image]\" : \"\";\n currentTurn = {\n userText: cleaned + imgSuffix,\n userTimestamp: ts,\n assistantParts: [],\n };\n } else if (etype === \"assistant\" && currentTurn) {\n const blocks = extractContentBlocks(content);\n let pendingText: string | null = null;\n\n for (const block of blocks) {\n const btype = block.type;\n\n if (btype === \"text\") {\n const text = typeof block.text === \"string\" ? (block.text as string).trim() : \"\";\n if (text) pendingText = text;\n } else if (btype === \"tool_use\") {\n const name = typeof block.name === \"string\" ? block.name : \"\";\n const inp = (typeof block.input === \"object\" && block.input !== null ? block.input : {}) as Record<string, string>;\n\n if (name === \"Edit\") {\n if (pendingText) {\n currentTurn.assistantParts.push({ type: \"text\", content: pendingText });\n pendingText = null;\n }\n currentTurn.assistantParts.push({\n type: \"edit\",\n file: shortenPath(inp.file_path ?? \"\"),\n old: inp.old_string ?? \"\",\n new: inp.new_string ?? \"\",\n });\n } else if (name === \"Write\") {\n if (pendingText) {\n currentTurn.assistantParts.push({ type: \"text\", content: pendingText });\n pendingText = null;\n }\n currentTurn.assistantParts.push({\n type: \"write\",\n file: shortenPath(inp.file_path ?? \"\"),\n fileContent: inp.content ?? \"\",\n });\n }\n // Skip Read, Bash, Grep, Glob, etc. — these don't show what was *changed*\n }\n }\n\n if (pendingText) {\n currentTurn.assistantParts.push({ type: \"text\", content: pendingText });\n }\n }\n }\n\n if (currentTurn) turns.push(currentTurn);\n\n const firstTimestamp = turns[0]?.userTimestamp ?? \"\";\n const lastTimestamp = turns[turns.length - 1]?.userTimestamp ?? \"\";\n\n return { turns, branch, firstTimestamp, lastTimestamp };\n}\n\n// ─── Markdown builder ─────────────────────────────────────────────────────────\n\nexport function buildSessionMarkdown(turns: Turn[], mode: ContextMode): string {\n const out: string[] = [];\n\n for (let i = 0; i < turns.length; i++) {\n const turn = turns[i];\n const userText = turn.userText.trim();\n if (!userText) continue;\n\n out.push(`**User [${i + 1}]:**`);\n out.push(userText);\n out.push(\"\");\n\n let hasContent = false;\n\n for (const part of turn.assistantParts) {\n if (part.type === \"text\") {\n if (!hasContent) {\n out.push(\"**Assistant:**\");\n hasContent = true;\n }\n out.push(part.content ?? \"\");\n out.push(\"\");\n } else if (part.type === \"edit\") {\n if (!hasContent) {\n out.push(\"**Assistant:**\");\n hasContent = true;\n }\n const file = part.file ?? \"\";\n const oldStr = part.old ?? \"\";\n const newStr = part.new ?? \"\";\n if (mode === \"full\") {\n out.push(formatEditFull(file, oldStr, newStr));\n } else if (mode === \"balanced\") {\n out.push(formatEditBalanced(file, oldStr, newStr));\n } else {\n out.push(formatEditCompact(file, oldStr, newStr));\n }\n out.push(\"\");\n } else if (part.type === \"write\") {\n if (!hasContent) {\n out.push(\"**Assistant:**\");\n hasContent = true;\n }\n const file = part.file ?? \"\";\n const content = part.fileContent ?? \"\";\n if (mode === \"full\") {\n out.push(formatWriteFull(file, content));\n } else if (mode === \"balanced\") {\n out.push(formatWriteBalanced(file, content));\n } else {\n out.push(formatWriteCompact(file, content));\n }\n out.push(\"\");\n }\n }\n\n out.push(\"---\");\n out.push(\"\");\n }\n\n return out.join(\"\\n\");\n}\n\n// ─── Session merger ───────────────────────────────────────────────────────────\n\nconst DISCLAIMER = `> **Note:** This conversation history may contain outdated information. File contents, code, and project state may have changed since these sessions were recorded. Use this as context only — always verify against current files before acting.`;\n\nexport function mergeSessionsMarkdown(\n sessions: SessionMarkdownInput[],\n mode: ContextMode,\n title: string\n): string {\n // Sort sessions chronologically (oldest first)\n const sorted = [...sessions].sort((a, b) => a.startTime.localeCompare(b.startTime));\n\n const totalTurns = sorted.reduce((sum, s) => sum + s.turns, 0);\n const overallStart = sorted[0]?.startTime.slice(0, 16) ?? \"?\";\n const overallEnd = sorted[sorted.length - 1]?.endTime.slice(0, 16) ?? \"?\";\n\n const out: string[] = [];\n out.push(`# Conversation History from ${title}`);\n out.push(`${sorted.length} sessions | ${totalTurns} turns | ${overallStart} → ${overallEnd} | mode: ${mode}`);\n out.push(\"\");\n\n for (let i = 0; i < sorted.length; i++) {\n const s = sorted[i];\n const start = s.startTime.slice(0, 16);\n const end = s.endTime.slice(0, 16);\n out.push(`## Session Conversation History ${i + 1} — ${start} → ${end} (${s.agent}, ${s.turns} turns, branch: ${s.branch})`);\n out.push(\"\");\n out.push(s.markdown);\n }\n\n out.push(DISCLAIMER);\n out.push(\"\");\n\n return out.join(\"\\n\");\n}\n","import type { ContextProvider, ContextQuery, ContextOptions, ContextResult, SessionListResult, SessionInfo } from \"../context-provider.js\";\nimport type { ContextMode } from \"../context-provider.js\";\nimport { DEFAULT_MAX_TOKENS, TOKENS_PER_TURN_ESTIMATE } from \"../context-provider.js\";\nimport { CheckpointReader } from \"./checkpoint-reader.js\";\nimport { parseJsonlToTurns, buildSessionMarkdown, mergeSessionsMarkdown, selectMode, estimateTokens, type SessionMarkdownInput } from \"./conversation-builder.js\";\n\n/**\n * Context provider backed by Claude Code checkpoint data stored in the git repo.\n *\n * Only available when the repo has an `origin/entire/checkpoints/v1` branch,\n * which is pushed by the Entire Claude Code extension. Supports all query types\n * (branch, commit, PR, session, latest) by reading checkpoint metadata and\n * reconstructing conversation turns from JSONL transcripts.\n *\n * Used as a lower-priority fallback after HistoryProvider: the live history\n * recorder takes precedence for sessions still in memory, while this provider\n * covers older sessions or cross-branch queries.\n */\nexport class EntireProvider implements ContextProvider {\n readonly name = \"entire\";\n\n async isAvailable(repoPath: string): Promise<boolean> {\n return new CheckpointReader(repoPath).hasEntireBranch();\n }\n\n async listSessions(query: ContextQuery): Promise<SessionListResult> {\n const reader = new CheckpointReader(query.repoPath);\n const sessions = await this.resolveSessions(reader, query);\n const estimatedTokens = sessions.reduce((sum, s) => sum + s.turnCount * TOKENS_PER_TURN_ESTIMATE, 0);\n return { sessions, estimatedTokens };\n }\n\n async buildContext(query: ContextQuery, options?: ContextOptions): Promise<ContextResult> {\n const maxTokens = options?.maxTokens ?? DEFAULT_MAX_TOKENS;\n const reader = new CheckpointReader(query.repoPath);\n let sessions = await this.resolveSessions(reader, query);\n\n if (options?.limit && sessions.length > options.limit) {\n sessions = sessions.slice(-options.limit);\n }\n\n if (sessions.length === 0) {\n return { markdown: \"\", tokenEstimate: 0, sessionCount: 0, totalTurns: 0, mode: \"full\", truncated: false, timeRange: { start: \"\", end: \"\" } };\n }\n\n // Parse all transcripts upfront so we know total turns before selecting mode.\n // We may need to re-render with a different mode, so we keep the raw JSONL.\n const parsedSessions: { session: SessionInfo; jsonl: string; }[] = [];\n for (const sess of sessions) {\n const jsonl = reader.getTranscript(sess.transcriptPath);\n if (jsonl) parsedSessions.push({ session: sess, jsonl });\n }\n\n if (parsedSessions.length === 0) {\n return { markdown: \"\", tokenEstimate: 0, sessionCount: 0, totalTurns: 0, mode: \"full\", truncated: false, timeRange: { start: \"\", end: \"\" } };\n }\n\n const totalTurns = parsedSessions.reduce((sum, ps) => {\n const parsed = parseJsonlToTurns(ps.jsonl);\n return sum + parsed.turns.length;\n }, 0);\n\n let mode = selectMode(totalTurns);\n const title = this.buildTitle(query);\n\n // Build markdown for each session\n let sessionMarkdowns = this.buildSessionMarkdowns(parsedSessions, mode);\n let merged = mergeSessionsMarkdown(sessionMarkdowns, mode, title);\n let tokens = estimateTokens(merged);\n\n // Auto-downgrade to compact if over budget\n if (tokens > maxTokens && mode !== \"compact\") {\n mode = \"compact\";\n sessionMarkdowns = this.buildSessionMarkdowns(parsedSessions, \"compact\");\n merged = mergeSessionsMarkdown(sessionMarkdowns, \"compact\", title);\n tokens = estimateTokens(merged);\n }\n\n // Truncate oldest sessions if still over budget\n let truncated = false;\n while (tokens > maxTokens && sessionMarkdowns.length > 1) {\n sessionMarkdowns = sessionMarkdowns.slice(1);\n truncated = true;\n merged = mergeSessionsMarkdown(sessionMarkdowns, mode, title);\n tokens = estimateTokens(merged);\n }\n\n const allTimes = sessionMarkdowns.flatMap(s => [s.startTime, s.endTime]).filter(Boolean).sort();\n const finalTurns = sessionMarkdowns.reduce((sum, s) => sum + s.turns, 0);\n\n return {\n markdown: merged,\n tokenEstimate: tokens,\n sessionCount: sessionMarkdowns.length,\n totalTurns: finalTurns,\n mode,\n truncated,\n timeRange: { start: allTimes[0] ?? \"\", end: allTimes[allTimes.length - 1] ?? \"\" },\n };\n }\n\n private buildSessionMarkdowns(parsedSessions: { session: SessionInfo; jsonl: string }[], mode: ContextMode): SessionMarkdownInput[] {\n return parsedSessions.map(ps => {\n const parsed = parseJsonlToTurns(ps.jsonl);\n return {\n markdown: buildSessionMarkdown(parsed.turns, mode),\n startTime: parsed.firstTimestamp,\n endTime: parsed.lastTimestamp,\n agent: ps.session.agent,\n turns: parsed.turns.length,\n branch: ps.session.branch,\n files: ps.session.filesTouched.map(f => f.split(\"/\").pop() ?? f),\n };\n });\n }\n\n private async resolveSessions(reader: CheckpointReader, query: ContextQuery): Promise<SessionInfo[]> {\n switch (query.type) {\n case \"branch\": return reader.resolveByBranch(query.value);\n case \"commit\": return reader.resolveByCommit(query.value);\n case \"pr\": return reader.resolveByPr(query.value);\n case \"checkpoint\": return reader.resolveByCheckpoint(query.value);\n case \"session\": return reader.resolveBySessionId(query.value);\n case \"latest\": return reader.resolveLatest(parseInt(query.value) || 5);\n default: return [];\n }\n }\n\n private buildTitle(query: ContextQuery): string {\n switch (query.type) {\n case \"pr\": return `PR #${query.value.replace(/.*\\/pull\\//, \"\")}`;\n case \"branch\": return `branch \\`${query.value}\\``;\n case \"commit\": return `commit \\`${query.value.slice(0, 8)}\\``;\n case \"checkpoint\": return `checkpoint \\`${query.value}\\``;\n case \"session\": return `session \\`${query.value.slice(0, 8)}...\\``;\n case \"latest\": return `latest ${query.value} sessions`;\n default: return \"unknown\";\n }\n }\n}\n","// src/adapters/shared/format-types.ts\n//\n// Shared type definitions for message formatting, tool display, and\n// output verbosity across all adapters.\n\n/**\n * Controls how much detail is shown in agent output.\n * - `\"low\"` — minimal: hides thoughts, usage, noisy tool calls\n * - `\"medium\"` — balanced: shows tool summaries, hides noise\n * - `\"high\"` — full: shows everything including tool output and thoughts\n */\nexport type OutputMode = \"low\" | \"medium\" | \"high\";\n/** @deprecated Use OutputMode instead */\nexport type DisplayVerbosity = OutputMode;\n\n/**\n * Action to take for a tool call classified as noise.\n * - `\"hide\"` — suppress entirely (not shown at any verbosity except high)\n * - `\"collapse\"` — show minimally (hidden only at low verbosity)\n */\nexport type NoiseAction = \"hide\" | \"collapse\";\n\n/** Rule that classifies a tool call as noise based on its name, kind, and input. */\nexport interface NoiseRule {\n match: (name: string, kind: string, rawInput: unknown) => boolean;\n action: NoiseAction;\n}\n\n/** Visual style category for rendering a formatted message. */\nexport type MessageStyle =\n | \"text\"\n | \"thought\"\n | \"tool\"\n | \"plan\"\n | \"usage\"\n | \"system\"\n | \"error\"\n | \"attachment\";\n\n/** Structured metadata attached to a formatted message for rendering decisions. */\nexport interface MessageMetadata {\n toolName?: string;\n toolStatus?: string;\n toolKind?: string;\n filePath?: string;\n command?: string;\n planEntries?: { content: string; status: string }[];\n tokens?: number;\n contextSize?: number;\n cost?: number;\n viewerLinks?: ViewerLinks;\n viewerFilePath?: string;\n}\n\n/**\n * Platform-agnostic formatted message ready for rendering.\n * Summary and detail are always plain text — renderers handle\n * escaping for their target format (HTML, markdown, etc.).\n */\nexport interface FormattedMessage {\n summary: string;\n detail?: string;\n viewerLinks?: ViewerLinks;\n icon: string;\n originalType: string;\n style: MessageStyle;\n metadata?: MessageMetadata;\n}\n\n/** Maps tool call status strings to emoji icons for display. */\nexport const STATUS_ICONS: Record<string, string> = {\n pending: \"⏳\",\n in_progress: \"🔄\",\n completed: \"✅\",\n failed: \"❌\",\n cancelled: \"🚫\",\n running: \"🔄\",\n done: \"✅\",\n error: \"❌\",\n};\n\n/** Maps tool kind strings to emoji icons for display. */\nexport const KIND_ICONS: Record<string, string> = {\n read: \"📖\",\n edit: \"✏️\",\n write: \"✏️\",\n delete: \"🗑️\",\n execute: \"▶️\",\n command: \"▶️\",\n bash: \"▶️\",\n terminal: \"▶️\",\n search: \"🔍\",\n web: \"🌐\",\n fetch: \"🌐\",\n agent: \"🧠\",\n think: \"🧠\",\n install: \"📦\",\n move: \"📦\",\n other: \"🛠️\",\n};\n\n/** Maps tool kind strings to human-readable labels for UI display. */\nexport const KIND_LABELS: Record<string, string> = {\n read: \"Read\",\n edit: \"Edit\",\n write: \"Write\",\n delete: \"Delete\",\n execute: \"Run\",\n bash: \"Bash\",\n command: \"Run\",\n terminal: \"Terminal\",\n search: \"Search\",\n web: \"Web\",\n fetch: \"Fetch\",\n agent: \"Agent\",\n think: \"Agent\",\n install: \"Install\",\n move: \"Move\",\n};\n\n/** Links to the web viewer for file contents or diffs. */\nexport interface ViewerLinks {\n file?: string;\n diff?: string;\n}\n\n/**\n * Metadata extracted from a tool_call agent event.\n * Carried on OutgoingMessage.metadata for rendering by adapters.\n */\nexport interface ToolCallMeta {\n id: string;\n name: string;\n /** Semantic kind (read, edit, execute, search, etc.) used for icon/label selection. */\n kind?: string;\n status?: string;\n content?: unknown;\n rawInput?: unknown;\n viewerLinks?: ViewerLinks;\n viewerFilePath?: string;\n /** Agent-provided summary override (takes precedence over auto-generated summary). */\n displaySummary?: string;\n /** Agent-provided title override (takes precedence over auto-generated title). */\n displayTitle?: string;\n /** Agent-provided kind override (takes precedence over inferred kind). */\n displayKind?: string;\n}\n\n/** Metadata for a tool_update event — same as ToolCallMeta but status is required. */\nexport interface ToolUpdateMeta extends ToolCallMeta {\n status: string;\n}\n","import type { NoiseAction, NoiseRule } from \"./format-types.js\";\nimport { STATUS_ICONS, KIND_ICONS } from \"./format-types.js\";\n\n/**\n * Recursively extracts plain text from an agent's response content.\n *\n * Agent responses can be strings, arrays of content blocks, or nested\n * objects with `text`, `content`, `input`, or `output` fields. This\n * function normalizes all variants into a single string. Falls back\n * to JSON serialization for unrecognized structures to avoid silently\n * dropping edge-case responses.\n */\nexport function 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) => extractContentText(c, depth + 1))\n .filter(Boolean)\n .join(\"\\n\");\n }\n if (typeof content !== \"object\") return String(content);\n\n const obj = content as Record<string, unknown>;\n if (obj.text && typeof obj.text === \"string\") return obj.text;\n if (obj.content) {\n if (typeof obj.content === \"string\") return obj.content;\n if (Array.isArray(obj.content)) {\n return obj.content\n .map((c) => extractContentText(c, depth + 1))\n .filter(Boolean)\n .join(\"\\n\");\n }\n return extractContentText(obj.content, depth + 1);\n }\n if (obj.input) return extractContentText(obj.input, depth + 1);\n if (obj.output) return extractContentText(obj.output, depth + 1);\n\n // Skip objects with only a 'type' key and no content fields\n const keys = Object.keys(obj).filter((k) => k !== \"type\");\n if (keys.length === 0) return \"\";\n\n // Fallback: serialize unrecognized objects so edge-case agent responses are not silently dropped\n try {\n return JSON.stringify(obj, null, 2);\n } catch {\n return \"\";\n }\n}\n\nfunction parseRawInput(rawInput: unknown): Record<string, unknown> {\n try {\n if (typeof rawInput === \"string\") {\n return JSON.parse(rawInput) as Record<string, unknown>;\n }\n if (typeof rawInput === \"object\" && rawInput !== null) {\n return rawInput as Record<string, unknown>;\n }\n } catch {\n // fall through\n }\n return {};\n}\n\n/**\n * Builds a human-readable summary line for a tool call (used at medium/high verbosity).\n *\n * Includes an icon and key arguments (e.g., \"Read src/foo.ts (50 lines)\").\n * If the agent provides a `displaySummary` override, it takes precedence.\n */\nexport function formatToolSummary(\n name: string,\n rawInput: unknown,\n displaySummary?: string,\n): string {\n if (displaySummary && typeof displaySummary === \"string\") {\n return displaySummary;\n }\n\n const args = parseRawInput(rawInput);\n const lowerName = name.toLowerCase();\n\n if (lowerName === \"read\") {\n const fp = args.file_path ?? args.filePath ?? \"\";\n const limit = args.limit ? ` (${args.limit} lines)` : \"\";\n return fp ? `📖 Read ${fp}${limit}` : `🔧 ${name}`;\n }\n if (lowerName === \"edit\") {\n const fp = args.file_path ?? args.filePath ?? \"\";\n return fp ? `✏️ Edit ${fp}` : `🔧 ${name}`;\n }\n if (lowerName === \"write\") {\n const fp = args.file_path ?? args.filePath ?? \"\";\n return fp ? `📝 Write ${fp}` : `🔧 ${name}`;\n }\n if (lowerName === \"bash\" || lowerName === \"terminal\") {\n const cmd = String(args.command ?? args.cmd ?? \"\").slice(0, 60);\n return cmd ? `▶️ Run: ${cmd}` : `▶️ Terminal`;\n }\n if (lowerName === \"grep\") {\n const pattern = args.pattern ?? \"\";\n const path = args.path ?? \"\";\n return pattern\n ? `🔍 Grep \"${pattern}\"${path ? ` in ${path}` : \"\"}`\n : `🔧 ${name}`;\n }\n if (lowerName === \"glob\") {\n const pattern = args.pattern ?? \"\";\n return pattern ? `🔍 Glob ${pattern}` : `🔧 ${name}`;\n }\n if (lowerName === \"agent\") {\n const desc = String(args.description ?? \"\").slice(0, 60);\n return desc ? `🧠 Agent: ${desc}` : `🔧 ${name}`;\n }\n if (lowerName === \"webfetch\" || lowerName === \"web_fetch\") {\n const raw = args.url ?? \"\";\n const url = (raw !== \"undefined\" ? String(raw) : \"\").slice(0, 60);\n return url ? `🌐 Fetch ${url}` : `🔧 ${name}`;\n }\n if (lowerName === \"websearch\" || lowerName === \"web_search\") {\n const raw = args.query ?? \"\";\n const query = (raw !== \"undefined\" ? String(raw) : \"\").slice(0, 60);\n return query ? `🌐 Search \"${query}\"` : `🔧 ${name}`;\n }\n\n return `🔧 ${name}`;\n}\n\n/**\n * Builds a compact title for a tool call (used at low verbosity).\n *\n * Returns just the key identifier (file path, command, pattern) without\n * icons or extra decoration. If `displayTitle` is provided, it takes precedence.\n */\nexport function formatToolTitle(\n name: string,\n rawInput: unknown,\n displayTitle?: string,\n): string {\n if (displayTitle && typeof displayTitle === \"string\") {\n return displayTitle;\n }\n\n const args = parseRawInput(rawInput);\n const lowerName = name.toLowerCase();\n\n if ([\"read\", \"edit\", \"write\"].includes(lowerName)) {\n return String(args.file_path ?? args.filePath ?? name);\n }\n if (lowerName === \"bash\" || lowerName === \"terminal\") {\n return String(args.command ?? args.cmd ?? name).slice(0, 60);\n }\n if (lowerName === \"grep\") {\n const pattern = args.pattern ?? \"\";\n const path = args.path ?? \"\";\n return pattern ? `\"${pattern}\"${path ? ` in ${path}` : \"\"}` : name;\n }\n if (lowerName === \"glob\") {\n return String(args.pattern ?? name);\n }\n if (lowerName === \"agent\") {\n return String(args.description ?? name).slice(0, 60);\n }\n if ([\"webfetch\", \"web_fetch\"].includes(lowerName)) {\n const url = typeof args.url === \"string\" && args.url !== \"undefined\" ? args.url : null;\n return (url ?? name).slice(0, 60);\n }\n if ([\"websearch\", \"web_search\"].includes(lowerName)) {\n const query = typeof args.query === \"string\" && args.query !== \"undefined\" ? args.query : null;\n return (query ?? name).slice(0, 60);\n }\n\n return name;\n}\n\n/**\n * Selects the appropriate emoji icon for a tool call card.\n * Priority: status icon (e.g., running, done, error) > kind icon (e.g., read, execute) > default.\n */\nexport function resolveToolIcon(tool: {\n status?: string;\n displayKind?: string;\n kind?: string;\n}): string {\n const statusIcon = STATUS_ICONS[tool.status || \"\"];\n if (statusIcon) return statusIcon;\n const kind = tool.displayKind ?? tool.kind;\n if (kind && KIND_ICONS[kind]) return KIND_ICONS[kind];\n return \"🔧\";\n}\n\n// Noise filtering — determines which tool calls are low-signal and can be\n// hidden or collapsed at lower verbosity levels to reduce chat clutter.\n\nconst NOISE_RULES: NoiseRule[] = [\n {\n match: (name) => name.toLowerCase() === \"ls\",\n action: \"hide\",\n },\n {\n match: (_name, kind, rawInput) => {\n if (kind !== \"read\") return false;\n const args = parseRawInput(rawInput);\n const p = String(args.file_path ?? args.filePath ?? args.path ?? \"\");\n return p.endsWith(\"/\");\n },\n action: \"hide\",\n },\n {\n match: (name) => name.toLowerCase() === \"glob\",\n action: \"hide\",\n },\n {\n match: (name) => name.toLowerCase() === \"grep\",\n action: \"hide\",\n },\n];\n\n/**\n * Evaluates whether a tool call is considered \"noise\" based on its name, kind, and input.\n * Returns `\"hide\"` (suppress entirely) or `\"collapse\"` (show minimally), or `null` if not noise.\n */\nexport function evaluateNoise(\n name: string,\n kind: string,\n rawInput: unknown,\n): NoiseAction | null {\n for (const rule of NOISE_RULES) {\n if (rule.match(name, kind, rawInput)) return rule.action;\n }\n return null;\n}\n","import type {\n IChannelAdapter,\n ChannelConfig,\n AdapterCapabilities,\n} from \"../channel.js\";\nimport type {\n OutgoingMessage,\n PermissionRequest,\n NotificationMessage,\n} from \"../types.js\";\nimport type { DisplayVerbosity, ToolCallMeta } from \"./format-types.js\";\nimport type { IRenderer } from \"./rendering/renderer.js\";\nimport { evaluateNoise } from \"./message-formatter.js\";\n\n/** Runtime services available to adapters via dependency injection. */\nexport interface AdapterContext {\n configManager: { get(): Record<string, unknown> };\n fileService?: unknown;\n}\n\n/** Configuration for adapters that extend MessagingAdapter. */\nexport interface MessagingAdapterConfig extends ChannelConfig {\n /** Platform-imposed limit on a single message body (e.g., 4096 for Telegram). */\n maxMessageLength: number;\n /** How often (ms) to flush buffered streaming text to the platform. */\n flushInterval?: number;\n /** Minimum interval (ms) between consecutive send-queue operations. */\n sendInterval?: number;\n /** How often (ms) to refresh the typing indicator during agent thinking. */\n thinkingRefreshInterval?: number;\n /** Max duration (ms) to show the typing indicator before auto-dismissing. */\n thinkingDuration?: number;\n /** Default output verbosity for this adapter (can be overridden per-session). */\n displayVerbosity?: DisplayVerbosity;\n}\n\n/** Represents a message that was successfully sent to the platform. */\nexport interface SentMessage {\n messageId: string;\n}\n\n/** Message types that are hidden entirely at \"low\" verbosity. */\nconst HIDDEN_ON_LOW = new Set([\"thought\", \"usage\"]);\n\n/**\n * Abstract base class for platform-specific messaging adapters (Telegram, Slack, etc.).\n *\n * Provides a dispatch pipeline: incoming OutgoingMessage -> verbosity filter -> type-based\n * handler. Subclasses override the `handle*` methods to implement platform-specific rendering\n * and delivery. The base implementations are no-ops, so subclasses only override what they need.\n */\nexport abstract class MessagingAdapter implements IChannelAdapter {\n abstract readonly name: string;\n /** Platform-specific renderer that converts OutgoingMessage to formatted output. */\n abstract readonly renderer: IRenderer;\n /**\n * Declares what this adapter can do. The platform-specific subclass sets these flags,\n * and core/stream-adapter use them to decide how to route and format output — e.g.,\n * whether to stream text in-place (streaming), send to threads/topics (threads),\n * render markdown (richFormatting), upload files (fileUpload), or play audio (voice).\n */\n abstract readonly capabilities: AdapterCapabilities;\n\n constructor(\n protected context: AdapterContext,\n protected adapterConfig: MessagingAdapterConfig,\n ) {}\n\n // === Message dispatch flow ===\n\n /**\n * Entry point for all outbound messages from sessions to the platform.\n * Resolves the current verbosity, filters messages that should be hidden,\n * then dispatches to the appropriate type-specific handler.\n */\n async sendMessage(\n sessionId: string,\n content: OutgoingMessage,\n ): Promise<void> {\n const verbosity = this.getVerbosity();\n if (!this.shouldDisplay(content, verbosity)) return;\n await this.dispatchMessage(sessionId, content, verbosity);\n }\n\n /**\n * Routes a message to its type-specific handler.\n * Subclasses can override this for custom dispatch logic, but typically\n * override individual handle* methods instead.\n */\n protected async dispatchMessage(\n sessionId: string,\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): Promise<void> {\n switch (content.type) {\n case \"text\":\n return this.handleText(sessionId, content);\n case \"thought\":\n return this.handleThought(sessionId, content, verbosity);\n case \"tool_call\":\n return this.handleToolCall(sessionId, content, verbosity);\n case \"tool_update\":\n return this.handleToolUpdate(sessionId, content, verbosity);\n case \"plan\":\n return this.handlePlan(sessionId, content, verbosity);\n case \"usage\":\n return this.handleUsage(sessionId, content, verbosity);\n case \"error\":\n return this.handleError(sessionId, content);\n case \"attachment\":\n return this.handleAttachment(sessionId, content);\n case \"system_message\":\n return this.handleSystem(sessionId, content);\n case \"session_end\":\n return this.handleSessionEnd(sessionId, content);\n case \"mode_change\":\n return this.handleModeChange(sessionId, content);\n case \"config_update\":\n return this.handleConfigUpdate(sessionId, content);\n case \"model_update\":\n return this.handleModelUpdate(sessionId, content);\n case \"user_replay\":\n return this.handleUserReplay(sessionId, content);\n case \"resource\":\n return this.handleResource(sessionId, content);\n case \"resource_link\":\n return this.handleResourceLink(sessionId, content);\n }\n }\n\n // === Default handlers — all protected, all overridable ===\n // Each handler is a no-op by default. Subclasses override only the message\n // types they support (e.g., Telegram overrides handleText, handleToolCall, etc.).\n\n protected async handleText(\n _sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {}\n protected async handleThought(\n _sessionId: string,\n _content: OutgoingMessage,\n _verbosity: DisplayVerbosity,\n ): Promise<void> {}\n protected async handleToolCall(\n _sessionId: string,\n _content: OutgoingMessage,\n _verbosity: DisplayVerbosity,\n ): Promise<void> {}\n protected async handleToolUpdate(\n _sessionId: string,\n _content: OutgoingMessage,\n _verbosity: DisplayVerbosity,\n ): Promise<void> {}\n protected async handlePlan(\n _sessionId: string,\n _content: OutgoingMessage,\n _verbosity: DisplayVerbosity,\n ): Promise<void> {}\n protected async handleUsage(\n _sessionId: string,\n _content: OutgoingMessage,\n _verbosity: DisplayVerbosity,\n ): Promise<void> {}\n protected async handleError(\n _sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {}\n protected async handleAttachment(\n _sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {}\n protected async handleSystem(\n _sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {}\n protected async handleSessionEnd(\n _sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {}\n protected async handleModeChange(\n _sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {}\n protected async handleConfigUpdate(\n _sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {}\n protected async handleModelUpdate(\n _sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {}\n protected async handleUserReplay(\n _sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {}\n protected async handleResource(\n _sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {}\n protected async handleResourceLink(\n _sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {}\n\n // === Helpers ===\n\n /**\n * Resolves the current output verbosity by checking (in priority order):\n * per-channel config, global config, then adapter default. Falls back to \"medium\".\n */\n protected getVerbosity(): DisplayVerbosity {\n const config = this.context.configManager.get();\n const channelConfig = (config as Record<string, unknown>).channels as\n | Record<string, Record<string, unknown>>\n | undefined;\n const ch = channelConfig?.[this.name];\n const v =\n ch?.outputMode ??\n ch?.displayVerbosity ??\n (config as Record<string, unknown>).outputMode ??\n this.adapterConfig.displayVerbosity;\n if (v === \"low\" || v === \"high\") return v;\n return \"medium\";\n }\n\n /**\n * Determines whether a message should be displayed at the given verbosity.\n *\n * Noise filtering: tool calls matching noise rules (e.g., `ls`, `glob`, `grep`)\n * are hidden at medium/low verbosity to reduce clutter. Thoughts and usage\n * stats are hidden entirely at \"low\".\n */\n protected shouldDisplay(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): boolean {\n if (verbosity === \"low\" && HIDDEN_ON_LOW.has(content.type)) return false;\n\n if (content.type === \"tool_call\") {\n const meta = (content.metadata ?? {}) as Partial<ToolCallMeta>;\n const toolName = meta.name ?? content.text ?? \"\";\n const toolKind = String(meta.kind ?? \"other\");\n const noiseAction = evaluateNoise(toolName, toolKind, meta.rawInput);\n if (noiseAction === \"hide\" && verbosity !== \"high\") return false;\n if (noiseAction === \"collapse\" && verbosity === \"low\") return false;\n }\n\n return true;\n }\n\n // === Abstract — adapter MUST implement ===\n\n /** Initializes the adapter (e.g., connect to platform API, start polling). */\n abstract start(): Promise<void>;\n /** Gracefully shuts down the adapter and releases resources. */\n abstract stop(): Promise<void>;\n /** Creates a platform-specific thread/topic for a session. Returns the thread ID. */\n abstract createSessionThread(\n sessionId: string,\n name: string,\n ): Promise<string>;\n /** Renames an existing session thread/topic on the platform. */\n abstract renameSessionThread(\n sessionId: string,\n newName: string,\n ): Promise<void>;\n /** Sends a permission request to the user with approve/deny actions. */\n abstract sendPermissionRequest(\n sessionId: string,\n request: PermissionRequest,\n ): Promise<void>;\n /** Sends a cross-session notification (e.g., session completed, budget warning). */\n abstract sendNotification(notification: NotificationMessage): Promise<void>;\n}\n","/** Renders a text-based progress bar (e.g., \"▓▓▓▓░░░░░░\") for token usage display. */\nexport function progressBar(ratio: number, length = 10): string {\n const filled = Math.round(Math.min(ratio, 1) * length);\n return \"▓\".repeat(filled) + \"░\".repeat(length - filled);\n}\n\n/** Formats a token count with SI suffixes (e.g., 1500 -> \"1.5k\", 2000000 -> \"2M\"). */\nexport function formatTokens(n: number): string {\n if (n >= 1_000_000) {\n const m = n / 1_000_000;\n return m % 1 === 0 ? `${m}M` : `${parseFloat(m.toFixed(1))}M`;\n }\n if (n >= 1000) {\n const k = n / 1000;\n return k % 1 === 0 ? `${k}k` : `${parseFloat(k.toFixed(1))}k`;\n }\n return String(n);\n}\n\n/** Strips markdown code fences (```lang ... ```) from text, leaving only the content. */\nexport function stripCodeFences(text: string): string {\n return text\n .replace(/```\\w*\\n?/g, \"\")\n .replace(/```$/gm, \"\")\n .trim();\n}\n\n/** Truncates text to maxLen characters, appending a truncation marker if cut. */\nexport function truncateContent(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return text.slice(0, maxLen) + \"\\n… (truncated)\";\n}\n\n/**\n * Splits a long message into chunks that fit within maxLength.\n *\n * Splitting strategy:\n * 1. Prefer paragraph boundaries (\\n\\n), then line boundaries (\\n)\n * 2. If remaining text is only slightly over the limit (< 1.3x), split\n * near the midpoint to avoid leaving a tiny trailing chunk\n * 3. Avoid splitting inside markdown code fences — extend past the closing\n * fence if it doesn't exceed 2x maxLength\n */\nexport function splitMessage(text: string, maxLength: number): 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\n // If splitting would leave a very small trailing chunk, split closer\n // to the middle to produce two balanced chunks instead.\n const wouldLeaveSmall = remaining.length < maxLength * 1.3;\n const searchLimit = wouldLeaveSmall\n ? Math.floor(remaining.length / 2) + 300\n : maxLength;\n\n // Find the best split point: paragraph > line > hard limit\n const threshold = maxLength * 0.2;\n let splitAt = remaining.lastIndexOf(\"\\n\\n\", searchLimit);\n if (splitAt === -1 || splitAt < threshold) {\n splitAt = remaining.lastIndexOf(\"\\n\", searchLimit);\n }\n if (splitAt === -1 || splitAt < threshold) {\n splitAt = searchLimit;\n }\n\n // If we'd split inside an open code fence, extend to include the closing fence\n const candidate = remaining.slice(0, splitAt);\n const fences = candidate.match(/```/g);\n if (fences && fences.length % 2 !== 0) {\n const closingFence = remaining.indexOf(\"```\", splitAt);\n if (closingFence !== -1) {\n const afterFence = remaining.indexOf(\"\\n\", closingFence + 3);\n const fenceSplit =\n afterFence !== -1 ? afterFence + 1 : closingFence + 3;\n if (fenceSplit <= maxLength * 2) {\n splitAt = fenceSplit;\n }\n }\n }\n\n chunks.push(remaining.slice(0, splitAt));\n remaining = remaining.slice(splitAt).replace(/^\\n+/, \"\");\n }\n return chunks;\n}\n","import type {\n OutgoingMessage,\n PermissionRequest,\n NotificationMessage,\n} from \"../../types.js\";\nimport type {\n DisplayVerbosity,\n ToolCallMeta,\n ToolUpdateMeta,\n} from \"../format-types.js\";\nimport {\n formatToolSummary,\n formatToolTitle,\n resolveToolIcon,\n} from \"../message-formatter.js\";\nimport { progressBar, formatTokens } from \"../format-utils.js\";\n\n/**\n * A rendered message ready for platform delivery.\n * The `format` field tells the adapter how to interpret the body\n * (e.g., pass as HTML to Telegram, or as plain text to SSE).\n */\nexport interface RenderedMessage<TComponents = unknown> {\n body: string;\n format: \"html\" | \"markdown\" | \"plain\" | \"structured\";\n attachments?: RenderedAttachment[];\n /** Platform-specific UI components (e.g., Telegram inline keyboards). */\n components?: TComponents;\n}\n\n/** A rendered permission request with approve/deny action buttons. */\nexport interface RenderedPermission<\n TComponents = unknown,\n> extends RenderedMessage<TComponents> {\n actions: RenderedAction[];\n}\n\n/** A single action button for permission requests. */\nexport interface RenderedAction {\n id: string;\n label: string;\n isAllow?: boolean;\n}\n\n/** A file, image, or audio attachment to include with a rendered message. */\nexport interface RenderedAttachment {\n type: \"file\" | \"image\" | \"audio\";\n data: Buffer | string;\n mimeType?: string;\n filename?: string;\n}\n\n/**\n * Rendering contract for platform-specific message formatting.\n *\n * Each adapter provides its own IRenderer implementation (e.g., TelegramRenderer\n * outputs HTML, a CLI renderer might output ANSI). Required methods handle the\n * core message types; optional methods handle less common types and fall back\n * to no-ops if not implemented.\n */\nexport interface IRenderer {\n renderText(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage;\n renderToolCall(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage;\n renderToolUpdate(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage;\n renderPlan(content: OutgoingMessage): RenderedMessage;\n renderUsage(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage;\n renderPermission(request: PermissionRequest): RenderedPermission;\n renderError(content: OutgoingMessage): RenderedMessage;\n renderNotification(notification: NotificationMessage): RenderedMessage;\n renderThought?(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage;\n renderAttachment?(content: OutgoingMessage): RenderedMessage;\n renderSessionEnd?(content: OutgoingMessage): RenderedMessage;\n renderSystemMessage?(content: OutgoingMessage): RenderedMessage;\n renderModeChange?(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage;\n renderConfigUpdate?(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage;\n renderModelUpdate?(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage;\n renderResource?(content: OutgoingMessage): RenderedMessage;\n renderResourceLink?(content: OutgoingMessage): RenderedMessage;\n}\n\n/**\n * Default renderer producing plain-text output for all message types.\n *\n * Platform-specific renderers (e.g., TelegramRenderer) extend this class\n * and override methods to produce HTML, markdown, or structured output.\n * Methods not overridden fall back to these plain-text defaults.\n */\nexport class BaseRenderer implements IRenderer {\n renderText(content: OutgoingMessage): RenderedMessage {\n return { body: content.text, format: \"plain\" };\n }\n\n renderToolCall(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage {\n const meta = (content.metadata ?? {}) as Partial<ToolCallMeta>;\n const name = meta.name ?? content.text ?? \"Tool\";\n const icon = resolveToolIcon(meta);\n const label =\n verbosity === \"low\"\n ? formatToolTitle(\n name,\n meta.rawInput,\n meta.displayTitle as string | undefined,\n )\n : formatToolSummary(\n name,\n meta.rawInput,\n meta.displaySummary as string | undefined,\n );\n return { body: `${icon} ${label}`, format: \"plain\" };\n }\n\n renderToolUpdate(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage {\n const meta = (content.metadata ?? {}) as Partial<ToolUpdateMeta>;\n const name = meta.name ?? content.text ?? \"Tool\";\n const icon = resolveToolIcon(meta);\n const label =\n verbosity === \"low\"\n ? formatToolTitle(\n name,\n meta.rawInput,\n meta.displayTitle as string | undefined,\n )\n : formatToolSummary(\n name,\n meta.rawInput,\n meta.displaySummary as string | undefined,\n );\n return { body: `${icon} ${label}`, format: \"plain\" };\n }\n\n renderPlan(content: OutgoingMessage): RenderedMessage {\n const entries =\n (\n content.metadata as {\n entries?: Array<{ content: string; status: string }>;\n }\n )?.entries ?? [];\n const lines = entries.map((e, i) => {\n const icon =\n e.status === \"completed\"\n ? \"✅\"\n : e.status === \"in_progress\"\n ? \"🔄\"\n : \"⬜\";\n return `${icon} ${i + 1}. ${e.content}`;\n });\n return { body: `📋 Plan\\n${lines.join(\"\\n\")}`, format: \"plain\" };\n }\n\n renderUsage(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage {\n const meta = content.metadata as\n | { tokensUsed?: number; contextSize?: number; cost?: number }\n | undefined;\n if (!meta?.tokensUsed)\n return { body: \"📊 Usage data unavailable\", format: \"plain\" };\n const costStr = meta.cost != null ? ` · $${meta.cost.toFixed(2)}` : \"\";\n if (verbosity === \"medium\") {\n return {\n body: `📊 ${formatTokens(meta.tokensUsed)} tokens${costStr}`,\n format: \"plain\",\n };\n }\n if (!meta.contextSize)\n return {\n body: `📊 ${formatTokens(meta.tokensUsed)} tokens`,\n format: \"plain\",\n };\n const ratio = meta.tokensUsed / meta.contextSize;\n const pct = Math.round(ratio * 100);\n const bar = progressBar(ratio);\n let text = `📊 ${formatTokens(meta.tokensUsed)} / ${formatTokens(meta.contextSize)} tokens\\n${bar} ${pct}%`;\n if (meta.cost != null) text += `\\n💰 $${meta.cost.toFixed(2)}`;\n return { body: text, format: \"plain\" };\n }\n\n renderPermission(request: PermissionRequest): RenderedPermission {\n return {\n body: request.description,\n format: \"plain\",\n actions: request.options.map((o) => ({\n id: o.id,\n label: o.label,\n isAllow: o.isAllow,\n })),\n };\n }\n\n renderError(content: OutgoingMessage): RenderedMessage {\n return { body: `❌ Error: ${content.text}`, format: \"plain\" };\n }\n\n renderNotification(notification: NotificationMessage): RenderedMessage {\n const emoji: Record<string, string> = {\n completed: \"✅\",\n error: \"❌\",\n permission: \"🔐\",\n input_required: \"💬\",\n budget_warning: \"⚠️\",\n };\n return {\n body: `${emoji[notification.type] || \"ℹ️\"} ${notification.sessionName || \"Session\"}\\n${notification.summary}`,\n format: \"plain\",\n };\n }\n\n renderSystemMessage(content: OutgoingMessage): RenderedMessage {\n return { body: content.text, format: \"plain\" };\n }\n\n renderModeChange(content: OutgoingMessage): RenderedMessage {\n const modeId = (content.metadata as Record<string, unknown>)?.modeId ?? \"\";\n return { body: `🔄 Mode: ${modeId}`, format: \"plain\" };\n }\n\n renderConfigUpdate(): RenderedMessage {\n return { body: \"⚙️ Config updated\", format: \"plain\" };\n }\n\n renderModelUpdate(content: OutgoingMessage): RenderedMessage {\n const modelId =\n (content.metadata as Record<string, unknown>)?.modelId ?? \"\";\n return { body: `🤖 Model: ${modelId}`, format: \"plain\" };\n }\n\n renderResource(content: OutgoingMessage): RenderedMessage {\n const uri = (content.metadata as Record<string, unknown>)?.uri ?? \"\";\n return { body: `📄 Resource: ${content.text} (${uri})`, format: \"plain\" };\n }\n\n renderResourceLink(content: OutgoingMessage): RenderedMessage {\n const uri = (content.metadata as Record<string, unknown>)?.uri ?? \"\";\n return { body: `🔗 ${content.text}: ${uri}`, format: \"plain\" };\n }\n}\n","/**\n * Item type determines deduplication behavior:\n * - `\"text\"` items with the same key replace each other (only latest is sent)\n * - `\"other\"` items are always queued individually\n */\nexport type QueueItemType = 'text' | 'other'\n\n/** Configuration for the SendQueue rate limiter. */\nexport interface SendQueueConfig {\n /** Minimum interval (ms) between consecutive operations. */\n minInterval: number\n /** Per-category interval overrides (e.g., separate limits for edits vs. sends). */\n categoryIntervals?: Record<string, number>\n onRateLimited?: () => void\n onError?: (error: Error) => void\n}\n\n/** Options for enqueuing an operation. */\nexport interface EnqueueOptions {\n type?: QueueItemType\n /** Deduplication key — text items with the same key replace earlier ones. */\n key?: string\n /** Category for per-category rate limiting. */\n category?: string\n}\n\ninterface QueueItem<T = unknown> {\n fn: () => Promise<T>\n type: QueueItemType\n key?: string\n category?: string\n resolve: (value: T | undefined) => void\n reject: (err: unknown) => void\n promise: Promise<T | undefined>\n}\n\n/**\n * Serializes outbound platform API calls to respect rate limits and maintain ordering.\n *\n * Key behaviors:\n * - Enforces a minimum interval between consecutive operations\n * - Supports per-category intervals (e.g., different limits for message edits vs. sends)\n * - Deduplicates text-type items with the same key (only the latest update is sent)\n * - On rate limit, drops all pending text items to reduce backlog\n */\nexport class SendQueue {\n private items: QueueItem[] = []\n private processing = false\n private lastExec = 0\n private lastCategoryExec = new Map<string, number>()\n\n constructor(private config: SendQueueConfig) {}\n\n get pending(): number {\n return this.items.length\n }\n\n /**\n * Queues an async operation for rate-limited execution.\n *\n * For text-type items with a key, replaces any existing queued item with\n * the same key (deduplication). This is used for streaming draft updates\n * where only the latest content matters.\n */\n enqueue<T>(\n fn: () => Promise<T>,\n opts?: EnqueueOptions,\n ): Promise<T | undefined> {\n const type = opts?.type ?? 'other'\n const key = opts?.key\n const category = opts?.category\n\n let resolve!: (value: T | undefined) => void\n let reject!: (err: unknown) => void\n const promise = new Promise<T | undefined>((res, rej) => {\n resolve = res\n reject = rej\n })\n // Suppress unhandled rejection — callers are expected to handle via .catch or await\n promise.catch(() => {})\n\n // Deduplication: replace an existing text item with the same key\n if (type === 'text' && key) {\n const idx = this.items.findIndex(\n (item) => item.type === 'text' && item.key === key,\n )\n if (idx !== -1) {\n this.items[idx].resolve(undefined)\n this.items[idx] = { fn, type, key, category, resolve, reject, promise } as QueueItem\n this.scheduleProcess()\n return promise\n }\n }\n\n this.items.push({ fn, type, key, category, resolve, reject, promise } as QueueItem)\n this.scheduleProcess()\n return promise\n }\n\n /**\n * Called when a platform rate limit is hit. Drops all pending text items\n * (draft updates) to reduce backlog, keeping only non-text items that\n * represent important operations (e.g., permission requests).\n */\n onRateLimited(): void {\n this.config.onRateLimited?.()\n const remaining: QueueItem[] = []\n for (const item of this.items) {\n if (item.type === 'text') {\n item.resolve(undefined)\n } else {\n remaining.push(item)\n }\n }\n this.items = remaining\n }\n\n clear(): void {\n for (const item of this.items) {\n item.resolve(undefined)\n }\n this.items = []\n }\n\n /**\n * Schedules the next item for processing after the rate-limit delay.\n * Uses per-category timing when available, falling back to the global minInterval.\n */\n private scheduleProcess(): void {\n if (this.processing) return\n if (this.items.length === 0) return\n\n const item = this.items[0]\n const interval = this.getInterval(item.category)\n const lastExec = item.category\n ? this.lastCategoryExec.get(item.category) ?? 0\n : this.lastExec\n const elapsed = Date.now() - lastExec\n const delay = Math.max(0, interval - elapsed)\n\n this.processing = true\n setTimeout(() => void this.processNext(), delay)\n }\n\n private getInterval(category?: string): number {\n if (category && this.config.categoryIntervals?.[category] != null) {\n return this.config.categoryIntervals[category]\n }\n return this.config.minInterval\n }\n\n private async processNext(): Promise<void> {\n const item = this.items.shift()\n if (!item) {\n this.processing = false\n return\n }\n\n try {\n const result = await item.fn()\n item.resolve(result)\n } catch (err) {\n // Suppress \"message is not modified\" errors — harmless duplicate edits\n if (err instanceof Error && 'description' in err &&\n typeof (err as Record<string, unknown>).description === 'string' &&\n ((err as Record<string, unknown>).description as string).includes('message is not modified')) {\n item.resolve(undefined)\n } else {\n this.config.onError?.(err instanceof Error ? err : new Error(String(err)))\n item.reject(err)\n }\n } finally {\n const now = Date.now()\n this.lastExec = now\n if (item.category) {\n this.lastCategoryExec.set(item.category, now)\n }\n this.processing = false\n this.scheduleProcess()\n }\n }\n}\n","import type { ToolDisplaySpec } from \"../display-spec-builder.js\";\nimport type { PlanEntry } from \"../../types.js\";\n\n/** Debounce interval for batching rapid tool state updates into a single render. */\nconst DEBOUNCE_MS = 500;\n\nexport type { ToolDisplaySpec };\n\n/** Token usage and cost data appended to the tool card after the turn completes. */\nexport interface UsageData {\n tokensUsed?: number;\n contextSize?: number;\n cost?: number;\n}\n\n/**\n * A point-in-time snapshot of all tool cards in a turn, used for rendering.\n * Includes completion counts so adapters can show progress (e.g., \"3/5 tools done\").\n */\nexport interface ToolCardSnapshot {\n specs: ToolDisplaySpec[];\n planEntries?: PlanEntry[];\n usage?: UsageData;\n totalVisible: number;\n completedVisible: number;\n /** True when all visible tools have reached a terminal status. */\n allComplete: boolean;\n}\n\nexport interface ToolCardStateConfig {\n /** Called with a snapshot whenever the tool card content changes. */\n onFlush: (snapshot: ToolCardSnapshot) => void;\n}\n\nconst DONE_STATUSES = new Set([\"completed\", \"done\", \"failed\", \"error\"]);\n\n/**\n * Aggregates tool display specs, plan entries, and usage data for a single\n * turn, then flushes snapshots to the adapter for rendering.\n *\n * Uses debouncing to batch rapid updates (e.g., multiple tools starting\n * in quick succession) into a single render pass. The first update flushes\n * immediately for responsiveness; subsequent updates are debounced.\n */\nexport class ToolCardState {\n private specs: ToolDisplaySpec[] = [];\n private planEntries?: PlanEntry[];\n private usage?: UsageData;\n // Lifecycle: active (first flush pending) → active (subsequent updates debounced) → finalized.\n // Once finalized, all updateFromSpec/updatePlan/appendUsage/finalize() calls are no-ops —\n // guards against events arriving after the session has ended or the tool has already completed.\n private finalized = false;\n private isFirstFlush = true;\n private debounceTimer?: ReturnType<typeof setTimeout>;\n private onFlush: (snapshot: ToolCardSnapshot) => void;\n\n constructor(config: ToolCardStateConfig) {\n this.onFlush = config.onFlush;\n }\n\n /** Adds or updates a tool spec. First call flushes immediately; subsequent calls are debounced. */\n updateFromSpec(spec: ToolDisplaySpec): void {\n if (this.finalized) return;\n\n const existingIdx = this.specs.findIndex((s) => s.id === spec.id);\n if (existingIdx >= 0) {\n this.specs[existingIdx] = spec;\n } else {\n this.specs.push(spec);\n }\n\n if (this.isFirstFlush) {\n this.isFirstFlush = false;\n this.flush();\n } else {\n this.scheduleFlush();\n }\n }\n\n /** Updates the plan entries displayed alongside tool cards. */\n updatePlan(entries: PlanEntry[]): void {\n if (this.finalized) return;\n this.planEntries = entries;\n\n if (this.specs.length === 0 && this.isFirstFlush) {\n this.isFirstFlush = false;\n this.flush();\n } else {\n this.scheduleFlush();\n }\n }\n\n /** Appends token usage data to the tool card (typically at end of turn). */\n appendUsage(usage: UsageData): void {\n if (this.finalized) return;\n this.usage = usage;\n this.scheduleFlush();\n }\n\n /** Marks the turn as complete and flushes the final snapshot immediately. */\n finalize(): void {\n if (this.finalized) return;\n this.finalized = true;\n this.clearDebounce();\n this.flush();\n }\n\n /** Stops all pending flushes without emitting a final snapshot. */\n destroy(): void {\n this.finalized = true;\n this.clearDebounce();\n }\n\n hasContent(): boolean {\n return this.specs.length > 0 || this.planEntries !== undefined;\n }\n\n private snapshot(): ToolCardSnapshot {\n const visible = this.specs.filter((s) => !s.isHidden);\n const completedVisible = visible.filter((s) => DONE_STATUSES.has(s.status)).length;\n const allComplete = visible.length > 0 && completedVisible === visible.length;\n return {\n specs: this.specs,\n planEntries: this.planEntries,\n usage: this.usage,\n totalVisible: visible.length,\n completedVisible,\n allComplete,\n };\n }\n\n private flush(): void {\n this.clearDebounce();\n this.onFlush(this.snapshot());\n }\n\n private scheduleFlush(): void {\n this.clearDebounce();\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = undefined;\n this.flush();\n }, DEBOUNCE_MS);\n }\n\n private clearDebounce(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = undefined;\n }\n }\n}\n","// src/core/adapter-primitives/stream-accumulator.ts\n//\n// Buffers and accumulates streaming agent output (tool calls, thoughts)\n// into structured state that adapters can render at any point during a turn.\n\nimport type { ToolCallMeta, ViewerLinks } from \"./format-types.js\";\nimport { evaluateNoise } from \"./message-formatter.js\";\n\n/** Accumulated state for a single tool call during a streaming response. */\nexport interface ToolEntry {\n id: string;\n name: string;\n kind: string;\n rawInput: unknown;\n /** Tool output content, populated when the tool completes. */\n content: string | null;\n status: string;\n viewerLinks?: ViewerLinks;\n diffStats?: { added: number; removed: number };\n displaySummary?: string;\n displayTitle?: string;\n displayKind?: string;\n /** Whether this tool is considered noise (e.g., ls, glob) and should be hidden at lower verbosities. */\n isNoise: boolean;\n}\n\n/** Buffered update for a tool whose initial `tool_call` event has not arrived yet. */\ninterface PendingUpdate {\n status: string;\n rawInput?: unknown;\n content?: string | null;\n viewerLinks?: ViewerLinks;\n diffStats?: { added: number; removed: number };\n}\n\n/**\n * Tracks all tool calls within a single streaming turn.\n *\n * Tool call events and update events can arrive out of order (e.g., a\n * `tool_update` may arrive before the corresponding `tool_call`). This map\n * handles that by buffering updates until the initial call arrives.\n */\nexport class ToolStateMap {\n private entries: Map<string, ToolEntry> = new Map();\n private pendingUpdates: Map<string, PendingUpdate> = new Map();\n\n /**\n * Creates or updates an entry from a tool_call event.\n * If a pending update exists for this id, applies it immediately.\n */\n upsert(meta: ToolCallMeta, kind: string, rawInput: unknown): ToolEntry {\n const isNoise = evaluateNoise(meta.name, kind, rawInput) !== null;\n\n const entry: ToolEntry = {\n id: meta.id,\n name: meta.name,\n kind,\n rawInput,\n content: null,\n status: meta.status ?? \"running\",\n viewerLinks: meta.viewerLinks,\n displaySummary: meta.displaySummary,\n displayTitle: meta.displayTitle,\n displayKind: meta.displayKind,\n isNoise,\n };\n\n this.entries.set(meta.id, entry);\n\n // Apply any pending update that arrived before the initial call\n const pending = this.pendingUpdates.get(meta.id);\n if (pending) {\n this.pendingUpdates.delete(meta.id);\n this._applyUpdate(entry, pending);\n }\n\n return entry;\n }\n\n /**\n * Updates an existing entry from a tool_call_update event.\n * If the entry doesn't exist yet (out-of-order delivery), buffers the update.\n */\n merge(\n id: string,\n status: string,\n rawInput?: unknown,\n content?: string | null,\n viewerLinks?: ViewerLinks,\n diffStats?: { added: number; removed: number },\n ): ToolEntry | undefined {\n const entry = this.entries.get(id);\n\n if (!entry) {\n // Buffer the update for when upsert is called\n this.pendingUpdates.set(id, { status, rawInput, content, viewerLinks, diffStats });\n return undefined;\n }\n\n this._applyUpdate(entry, { status, rawInput, content, viewerLinks, diffStats });\n return entry;\n }\n\n private _applyUpdate(entry: ToolEntry, update: PendingUpdate): void {\n entry.status = update.status;\n if (update.rawInput !== undefined) {\n entry.rawInput = update.rawInput;\n }\n if (update.content !== undefined) {\n entry.content = update.content ?? null;\n }\n if (update.viewerLinks !== undefined) {\n entry.viewerLinks = update.viewerLinks;\n }\n if (update.diffStats !== undefined) {\n entry.diffStats = update.diffStats;\n }\n }\n\n /** Retrieves a tool entry by ID, or undefined if not yet tracked. */\n get(id: string): ToolEntry | undefined {\n return this.entries.get(id);\n }\n\n /** Resets all state between turns. */\n clear(): void {\n this.entries.clear();\n this.pendingUpdates.clear();\n }\n}\n\n/**\n * Buffers partial thought text chunks from the agent's extended thinking.\n *\n * Chunks are appended until `seal()` is called, which marks the thought\n * as complete and returns the full text. Once sealed, further appends\n * are ignored.\n */\nexport class ThoughtBuffer {\n private chunks: string[] = [];\n private sealed = false;\n\n /** Appends a thought text chunk. Ignored if already sealed. */\n append(chunk: string): void {\n if (this.sealed) return;\n this.chunks.push(chunk);\n }\n\n /** Marks the thought as complete and returns the full accumulated text. */\n seal(): string {\n this.sealed = true;\n return this.chunks.join(\"\");\n }\n\n /** Returns the text accumulated so far without sealing. */\n getText(): string {\n return this.chunks.join(\"\");\n }\n\n isSealed(): boolean {\n return this.sealed;\n }\n\n /** Resets the buffer for reuse in a new turn. */\n reset(): void {\n this.chunks = [];\n this.sealed = false;\n }\n}\n","// src/core/adapter-primitives/display-spec-builder.ts\n\nimport { KIND_ICONS } from \"./format-types.js\";\nimport type { OutputMode, ViewerLinks } from \"./format-types.js\";\nimport type { ToolEntry } from \"./stream-accumulator.js\";\nimport type { TunnelServiceInterface } from \"../plugin/types.js\";\nimport { isApplyPatchOtherTool } from \"../utils/apply-patch-detection.js\";\n\n// ─── Output spec interfaces ────────────────────────────────────────────────\n\n/**\n * A fully resolved, platform-agnostic description of how to display a tool call.\n * Built by DisplaySpecBuilder from a ToolEntry + output mode, then consumed\n * by adapters to render tool cards.\n */\nexport interface ToolDisplaySpec {\n id: string;\n kind: string;\n icon: string;\n title: string;\n description: string | null;\n command: string | null;\n inputContent: string | null;\n outputSummary: string | null;\n outputContent: string | null;\n diffStats: { added: number; removed: number } | null;\n viewerLinks?: ViewerLinks;\n outputViewerLink?: string;\n outputFallbackContent?: string;\n /** Working directory of the session that produced this tool call.\n * Adapters can use this to display relative paths instead of absolute ones. */\n workingDirectory?: string;\n status: string;\n isNoise: boolean;\n isHidden: boolean;\n}\n\n/** Display specification for an agent's extended thinking block. */\nexport interface ThoughtDisplaySpec {\n indicator: string;\n /** Full thought text, only populated at high verbosity. */\n content: string | null;\n}\n\n// ─── Constants ────────────────────────────────────────────────────────────\n\n/** Kinds that have a runnable command in rawInput.command */\nconst EXECUTE_KINDS = new Set([\"execute\", \"bash\", \"command\", \"terminal\"]);\n\nconst INLINE_MAX_LINES = 15;\nconst INLINE_MAX_CHARS = 800;\n\n// ─── Helpers ──────────────────────────────────────────────────────────────\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return {};\n}\n\nfunction capitalize(s: string): string {\n return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);\n}\n\n/**\n * Try multiple field name variants (snake_case and camelCase) and return the\n * first non-empty string value found. Needed because different agents use\n * different naming conventions (e.g. Claude Code uses `file_path`, OpenCode\n * uses `filePath`).\n */\nfunction getStringField(input: Record<string, unknown>, keys: string[]): string | null {\n for (const key of keys) {\n const value = input[key];\n if (typeof value === \"string\" && value.trim().length > 0) return value;\n }\n return null;\n}\n\n/**\n * Extract target file paths from an apply_patch patch text.\n * Handles \"Update File\", \"Add File\", and \"Delete File\" directives.\n */\nfunction parseApplyPatchTargets(patchText: string): string[] {\n const targets: string[] = [];\n const seen = new Set<string>();\n for (const line of patchText.split(\"\\n\")) {\n const match = line.match(/^\\*\\*\\*\\s+(?:Update|Add|Delete)\\s+File:\\s+(.+)$/);\n if (!match) continue;\n const p = match[1].trim();\n if (p && !seen.has(p)) { seen.add(p); targets.push(p); }\n }\n return targets;\n}\n\nfunction buildTitle(entry: ToolEntry, kind: string): string {\n // Explicit overrides take highest priority\n if (entry.displayTitle) return entry.displayTitle;\n if (entry.displaySummary) return entry.displaySummary;\n\n const input = asRecord(entry.rawInput);\n const nameLower = entry.name.toLowerCase();\n\n // apply_patch: keep title parsing based on patchText even when we map display\n // kind to \"edit\" for simpler icon/label compatibility.\n if (isApplyPatchOtherTool(entry.kind, entry.name, entry.rawInput)) {\n const patchText = getStringField(input, [\"patchText\", \"patch_text\"]);\n if (patchText) {\n const targets = parseApplyPatchTargets(patchText);\n if (targets.length === 1) return targets[0];\n if (targets.length > 1) {\n const shown = targets.slice(0, 2).join(\", \");\n const rest = targets.length - 2;\n return rest > 0 ? `${shown} (+${rest} more)` : shown;\n }\n }\n return \"apply_patch\";\n }\n\n if (kind === \"read\") {\n // Support both snake_case (Claude Code) and camelCase (OpenCode) field names\n const filePath = getStringField(input, [\"file_path\", \"filePath\", \"path\"]);\n if (filePath) {\n // start_line/end_line style\n const startLine = typeof input.start_line === \"number\" ? input.start_line : null;\n const endLine = typeof input.end_line === \"number\" ? input.end_line : null;\n if (startLine !== null && endLine !== null) return `${filePath} (lines ${startLine}–${endLine})`;\n if (startLine !== null) return `${filePath} (from line ${startLine})`;\n // offset/limit style (Claude Code Read tool)\n const offset = typeof input.offset === \"number\" ? input.offset : null;\n const limit = typeof input.limit === \"number\" ? input.limit : null;\n if (offset !== null && limit !== null) return `${filePath} (lines ${offset}–${offset + limit - 1})`;\n if (offset !== null) return `${filePath} (from line ${offset})`;\n return filePath;\n }\n return capitalize(entry.name);\n }\n\n if (kind === \"edit\" || kind === \"write\" || kind === \"delete\") {\n // Support both snake_case and camelCase field names\n const filePath = getStringField(input, [\"file_path\", \"filePath\", \"path\"]);\n if (filePath) return filePath;\n return capitalize(entry.name);\n }\n\n if (EXECUTE_KINDS.has(kind)) {\n const description = typeof input.description === \"string\" ? input.description : null;\n if (description) return description;\n const command = typeof input.command === \"string\" ? input.command : null;\n if (command) return command.length > 60 ? command.slice(0, 57) + \"...\" : command;\n return capitalize(entry.name);\n }\n\n if (kind === \"agent\") {\n const skill = typeof input.skill === \"string\" ? input.skill : null;\n const description = typeof input.description === \"string\" ? input.description : null;\n const subtype = typeof input.subagent_type === \"string\" ? input.subagent_type : null;\n if (skill) return skill;\n if (description) return description.length > 60 ? description.slice(0, 57) + \"...\" : description;\n if (subtype) return subtype;\n return capitalize(entry.name);\n }\n\n if (kind === \"search\") {\n const pattern =\n typeof input.pattern === \"string\"\n ? input.pattern\n : typeof input.query === \"string\"\n ? input.query\n : null;\n if (pattern) {\n let title = `${capitalize(entry.name)} \"${pattern}\"`;\n const glob = typeof input.glob === \"string\" ? input.glob : null;\n const type = typeof input.type === \"string\" ? input.type : null;\n if (glob) title += ` (glob: ${glob})`;\n else if (type) title += ` (type: ${type})`;\n return title;\n }\n return capitalize(entry.name);\n }\n\n if (kind === \"fetch\" || kind === \"web\") {\n const url = typeof input.url === \"string\" ? input.url : null;\n if (url && url !== \"undefined\") return url.length > 60 ? url.slice(0, 57) + \"...\" : url;\n const query = typeof input.query === \"string\" ? input.query : null;\n if (query && query !== \"undefined\") return query.length > 60 ? query.slice(0, 57) + \"...\" : query;\n return capitalize(entry.name);\n }\n\n // Show skill name for Skill tool calls (e.g. Claude Code's Skill tool)\n if (nameLower === \"skill\" && typeof input.skill === \"string\" && input.skill) {\n return input.skill;\n }\n\n // todowrite / TodoWrite: well-known todo list tool (Claude Code, others)\n // Summarise progress so the card shows \"Todo list (2/5 done, 1 active)\"\n // rather than just the bare tool name.\n if (nameLower === \"todowrite\") {\n const todos = Array.isArray(input.todos) ? input.todos : [];\n if (todos.length > 0) {\n const completed = todos.filter((t) => isRecord(t) && t.status === \"completed\").length;\n const active = todos.filter((t) => isRecord(t) && t.status === \"in_progress\").length;\n return `Todo list (${completed}/${todos.length} done${active > 0 ? `, ${active} active` : \"\"})`;\n }\n return \"Todo list\";\n }\n\n return entry.name;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction buildOutputSummary(content: string): string {\n const lines = content.split(\"\\n\").length;\n return `${lines} line${lines === 1 ? \"\" : \"s\"} of output`;\n}\n\nfunction isShortOutput(content: string): boolean {\n return content.split(\"\\n\").length <= INLINE_MAX_LINES && content.length <= INLINE_MAX_CHARS;\n}\n\n/** Check if title was derived from the command (exact match or truncated version) */\nfunction isTitleFromCommand(title: string, command: string): boolean {\n return title === command || (command.length > 60 && title === command.slice(0, 57) + \"...\");\n}\n\n// ─── DisplaySpecBuilder ───────────────────────────────────────────────────\n\n/**\n * Transforms raw ToolEntry state into display-ready ToolDisplaySpec objects.\n *\n * This is the central place where output mode (low/medium/high) controls what\n * information is included in tool cards. Low mode strips metadata; medium mode\n * includes summaries; high mode includes full output content and viewer links.\n */\nexport class DisplaySpecBuilder {\n constructor(private tunnelService?: TunnelServiceInterface) {}\n\n /**\n * Builds a display spec for a single tool call entry.\n *\n * Deduplicates fields to avoid repeating the same info (e.g., if the title\n * was derived from the command, the command field is omitted). For long\n * output, generates a viewer link via the tunnel service when available.\n */\n buildToolSpec(\n entry: ToolEntry,\n mode: OutputMode,\n sessionContext?: { id: string; workingDirectory: string },\n ): ToolDisplaySpec {\n const effectiveKind = entry.displayKind ?? (isApplyPatchOtherTool(entry.kind, entry.name, entry.rawInput) ? \"edit\" : entry.kind);\n const icon = KIND_ICONS[effectiveKind] ?? KIND_ICONS[\"other\"] ?? \"🛠️\";\n const title = buildTitle(entry, effectiveKind);\n const isHidden = entry.isNoise && mode !== \"high\";\n\n // Fields that are always null on low\n const includeMeta = mode !== \"low\";\n\n const input = asRecord(entry.rawInput);\n\n // Deduplicate: skip description if it matches title, kind label, or tool name\n const rawDescription = typeof input.description === \"string\" ? input.description : null;\n const descLower = rawDescription?.toLowerCase();\n const description =\n includeMeta && rawDescription && rawDescription !== title\n && descLower !== effectiveKind && descLower !== entry.name.toLowerCase()\n ? rawDescription : null;\n\n // Deduplicate: skip command if title was derived from it\n const rawCommand =\n EXECUTE_KINDS.has(effectiveKind) && typeof input.command === \"string\"\n ? input.command\n : null;\n const command =\n includeMeta && rawCommand && !isTitleFromCommand(title, rawCommand)\n ? rawCommand\n : null;\n\n const inputContent: string | null = null;\n\n const content = entry.content;\n\n let outputSummary: string | null = null;\n let outputContent: string | null = null;\n let outputViewerLink: string | undefined = undefined;\n let outputFallbackContent: string | undefined = undefined;\n\n if (content && content.trim().length > 0 && includeMeta) {\n outputSummary = buildOutputSummary(content);\n\n const isLong = !isShortOutput(content);\n\n if (isLong) {\n const publicUrl = this.tunnelService?.getPublicUrl();\n const hasPublicTunnel = !!publicUrl && !publicUrl.startsWith(\"http://localhost\") && !publicUrl.startsWith(\"http://127.0.0.1\");\n if (this.tunnelService && sessionContext && hasPublicTunnel) {\n const label =\n typeof input.command === \"string\" ? input.command : entry.name;\n const id = this.tunnelService.getStore().storeOutput(sessionContext.id, label, content);\n if (id !== null) {\n outputViewerLink = this.tunnelService.outputUrl(id);\n }\n } else if (mode === \"high\") {\n outputFallbackContent = content;\n }\n } else if (mode === \"high\") {\n outputContent = content;\n }\n }\n\n const diffStats = includeMeta ? (entry.diffStats ?? null) : null;\n\n return {\n id: entry.id,\n kind: effectiveKind,\n icon,\n title,\n description,\n command,\n inputContent,\n outputSummary,\n outputContent,\n diffStats,\n viewerLinks: entry.viewerLinks,\n outputViewerLink,\n outputFallbackContent,\n workingDirectory: sessionContext?.workingDirectory,\n status: entry.status,\n isNoise: entry.isNoise,\n isHidden,\n };\n }\n\n /** Builds a display spec for an agent thought. Content is only included at high verbosity. */\n buildThoughtSpec(content: string, mode: OutputMode): ThoughtDisplaySpec {\n const indicator = \"Thinking...\";\n return {\n indicator,\n content: mode === \"high\" ? content : null,\n };\n }\n}\n","// src/core/adapter-primitives/output-mode-resolver.ts\n//\n// Resolves the effective output mode for a given session by applying\n// a cascade of overrides: global -> per-adapter -> per-session.\n\nimport type { OutputMode } from \"./format-types.js\";\n\ninterface ConfigManagerLike {\n get(): Record<string, unknown>;\n}\n\ninterface SessionManagerLike {\n getSessionRecord(id: string): { outputMode?: OutputMode } | undefined;\n}\n\nconst VALID_MODES = new Set<string>([\"low\", \"medium\", \"high\"]);\nfunction toOutputMode(v: unknown): OutputMode | undefined {\n return typeof v === \"string\" && VALID_MODES.has(v) ? (v as OutputMode) : undefined;\n}\n\n/**\n * Resolves the effective output mode (low/medium/high) for a session.\n *\n * Override cascade (most specific wins):\n * 1. Global config `outputMode` (default: \"medium\")\n * 2. Per-adapter config `channels.<adapterName>.outputMode`\n * 3. Per-session override stored on the session record\n */\nexport class OutputModeResolver {\n /** Resolves the effective output mode by walking the override cascade. */\n resolve(\n configManager: ConfigManagerLike,\n adapterName: string,\n sessionId?: string,\n sessionManager?: SessionManagerLike,\n ): OutputMode {\n const config = configManager.get();\n let mode: OutputMode = toOutputMode(config.outputMode) ?? \"medium\";\n\n const channels = config.channels as Record<string, unknown> | undefined;\n const channelCfg = channels?.[adapterName] as Record<string, unknown> | undefined;\n const adapterMode = toOutputMode(channelCfg?.outputMode);\n if (adapterMode) mode = adapterMode;\n\n if (sessionId && sessionManager) {\n const record = sessionManager.getSessionRecord(sessionId);\n const sessionMode = toOutputMode(record?.outputMode);\n if (sessionMode) mode = sessionMode;\n }\n return mode;\n }\n}\n","import type { Bot } from 'grammy'\n\n/**\n * Ensure the two system topics (Notifications and Assistant) exist in the group.\n * Creates any missing topic and persists the resulting IDs via `saveConfig`.\n * Called once on startup; idempotent if both IDs are already present in config.\n */\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/**\n * Create a new forum topic for a session and return its thread ID.\n * Each session gets exactly one dedicated topic; the thread ID is used\n * to route all subsequent messages for that session.\n */\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. Failures are silently ignored (topic may be closed/deleted). */\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/** Delete a forum topic and all its messages permanently. */\nexport async function deleteSessionTopic(\n bot: Bot,\n chatId: number,\n threadId: number,\n): Promise<void> {\n await bot.api.deleteForumTopic(chatId, threadId);\n}\n\n/**\n * Build a Telegram deep link that navigates directly to a forum topic or message.\n *\n * When `messageId` is provided (and differs from `threadId`), the link points to\n * that specific message; otherwise it links to the topic root.\n */\nexport function buildDeepLink(chatId: number, threadId: number, messageId?: number): string {\n // Group chatId is prefixed with -100; strip it to form a valid t.me link\n const cleanId = String(chatId).replace('-100', '')\n // For forum groups: c/{chatId}/{threadId}/{messageId} links to a specific message\n // Without messageId: c/{chatId}/{threadId} links to the topic itself\n if (messageId && messageId !== threadId) {\n return `https://t.me/c/${cleanId}/${threadId}/${messageId}`\n }\n return `https://t.me/c/${cleanId}/${threadId}`\n}\n","import type {\n ToolCallMeta,\n ToolUpdateMeta,\n ViewerLinks,\n} from \"../../core/adapter-primitives/format-types.js\";\nimport type { ToolCardSnapshot } from \"../../core/adapter-primitives/primitives/tool-card-state.js\";\nimport type { ToolDisplaySpec } from \"../../core/adapter-primitives/display-spec-builder.js\";\nimport {\n STATUS_ICONS,\n KIND_ICONS,\n KIND_LABELS,\n} from \"../../core/adapter-primitives/format-types.js\";\nimport {\n progressBar,\n formatTokens,\n truncateContent,\n stripCodeFences,\n splitMessage as sharedSplitMessage,\n} from \"../../core/adapter-primitives/format-utils.js\";\nimport {\n extractContentText,\n formatToolSummary,\n formatToolTitle,\n resolveToolIcon,\n} from \"../../core/adapter-primitives/message-formatter.js\";\nimport type { DisplayVerbosity } from \"../../core/adapter-primitives/format-types.js\";\n\n/**\n * Escape characters that have special meaning in Telegram HTML parse mode.\n * Must be applied to all user/agent-provided strings before embedding in HTML messages.\n */\nexport function escapeHtml(text: string | undefined | null): string {\n if (!text) return \"\";\n return text\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\n/**\n * Convert Markdown to Telegram HTML parse mode.\n *\n * Telegram supports HTML rather than MarkdownV2 because MarkdownV2 requires\n * escaping almost every punctuation character, which breaks real-world agent\n * output (file paths, diffs, JSON, etc.). HTML mode only requires escaping\n * `&`, `<`, and `>`, making it far more reliable for agent-generated content.\n *\n * Strategy: code blocks/inline code are extracted into placeholders first so\n * that subsequent HTML escaping and markdown transformations don't corrupt them.\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(\n /```(\\w*)\\n?([\\s\\S]*?)```/g,\n (_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\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\n/**\n * Render a tool call event as Telegram HTML.\n * Higher verbosity levels show more input/output detail.\n */\nexport function formatToolCall(\n tool: ToolCallMeta,\n verbosity: DisplayVerbosity = \"medium\",\n): string {\n const si = resolveToolIcon(tool);\n const name = tool.name || \"Tool\";\n const label =\n verbosity === \"low\"\n ? formatToolTitle(name, tool.rawInput, tool.displayTitle)\n : formatToolSummary(name, tool.rawInput, tool.displaySummary);\n let text = `${si} <b>${escapeHtml(label)}</b>`;\n // viewer links always shown regardless of verbosity\n text += formatViewerLinks(tool.viewerLinks, tool.viewerFilePath);\n // high only: rawInput + content\n if (verbosity === \"high\") {\n text += formatHighDetails(tool.rawInput, tool.content, 3800);\n }\n return text;\n}\n\nexport function formatToolUpdate(\n update: ToolUpdateMeta,\n verbosity: DisplayVerbosity = \"medium\",\n): string {\n return formatToolCall(update, verbosity);\n}\n\nfunction formatHighDetails(\n rawInput: unknown,\n content: unknown,\n maxLen: number,\n): string {\n let text = \"\";\n if (rawInput) {\n const inputStr =\n typeof rawInput === \"string\"\n ? rawInput\n : JSON.stringify(rawInput, null, 2);\n if (inputStr && inputStr !== \"{}\") {\n text += `\\n<b>Input:</b>\\n<pre>${escapeHtml(truncateContent(inputStr, maxLen))}</pre>`;\n }\n }\n const details = stripCodeFences(extractContentText(content));\n if (details) {\n text += `\\n<b>Output:</b>\\n<pre>${escapeHtml(truncateContent(details, maxLen))}</pre>`;\n }\n return text;\n}\n\nfunction formatViewerLinks(links?: ViewerLinks, filePath?: string): string {\n if (!links) return \"\";\n const fileName = filePath ? filePath.split(\"/\").pop() || filePath : \"\";\n let text = \"\\n\";\n if (links.file)\n text += `\\n📄 <a href=\"${escapeHtml(links.file)}\">View ${escapeHtml(fileName || \"file\")}</a>`;\n if (links.diff)\n text += `\\n📝 <a href=\"${escapeHtml(links.diff)}\">View diff${fileName ? ` — ${escapeHtml(fileName)}` : \"\"}</a>`;\n return text;\n}\n\nexport function formatPlan(plan: {\n entries: Array<{ content: string; status: string }>;\n}): string {\n const { entries } = plan;\n const statusIcon: Record<string, string> = {\n pending: \"⬜\",\n in_progress: \"🔄\",\n completed: \"✅\",\n };\n const lines = entries.map(\n (e, i) =>\n `${statusIcon[e.status] || \"⬜\"} ${i + 1}. ${escapeHtml(e.content)}`,\n );\n return `<b>Plan:</b>\\n${lines.join(\"\\n\")}`;\n}\n\n/**\n * Render token usage as a visual bar + percentage.\n * Shows a warning emoji when context is ≥85% full to alert users before hitting limits.\n */\nexport function formatUsage(\n usage: { tokensUsed?: number; contextSize?: number; cost?: number },\n _verbosity: DisplayVerbosity = \"medium\",\n): string {\n const { tokensUsed, contextSize } = usage;\n if (tokensUsed == null) return \"📊 Usage data unavailable\";\n if (contextSize == null) return `📊 ${formatTokens(tokensUsed)} tokens`;\n\n const ratio = tokensUsed / contextSize;\n const pct = Math.round(ratio * 100);\n const bar = progressBar(ratio);\n const emoji = pct >= 85 ? \"⚠️\" : \"📊\";\n return `${emoji} ${formatTokens(tokensUsed)} / ${formatTokens(contextSize)} tokens\\n${bar} ${pct}%`;\n}\n\nexport function formatSummary(summary: string, sessionName?: string): string {\n const header = sessionName\n ? `📋 <b>Summary — ${escapeHtml(sessionName)}</b>`\n : \"📋 <b>Session Summary</b>\";\n return `${header}\\n\\n${escapeHtml(summary)}`;\n}\n\nexport function splitMessage(text: string, maxLength = 3800): string[] {\n return sharedSplitMessage(text, maxLength);\n}\n\nexport function renderToolCard(snap: ToolCardSnapshot): string {\n const sections: string[] = [];\n\n const { totalVisible, completedVisible, allComplete } = snap;\n const headerCheck = allComplete ? \" ✅\" : \"\";\n if (totalVisible > 0) {\n sections.push(`<b>📋 Tools (${completedVisible}/${totalVisible})</b>${headerCheck}`);\n }\n\n const DONE = new Set([\"completed\", \"done\", \"failed\", \"error\"]);\n const visible = snap.specs.filter((s) => !s.isHidden);\n const completed = visible.filter((s) => DONE.has(s.status));\n const running = visible.filter((s) => !DONE.has(s.status));\n\n for (const spec of completed) {\n sections.push(renderSpecSection(spec));\n }\n\n if (snap.planEntries && snap.planEntries.length > 0) {\n const planDone = snap.planEntries.filter((e) => e.status === \"completed\").length;\n const planTotal = snap.planEntries.length;\n sections.push(`── Plan: ${planDone}/${planTotal} ──`);\n const statusIcon: Record<string, string> = { completed: \"✅\", in_progress: \"🔄\", pending: \"⬜\" };\n for (let i = 0; i < snap.planEntries.length; i++) {\n const e = snap.planEntries[i];\n sections.push(`${statusIcon[e.status] ?? \"⬜\"} ${i + 1}. ${escapeHtml(e.content)}`);\n }\n sections.push(\"────\");\n }\n\n for (const spec of running) {\n sections.push(renderSpecSection(spec));\n }\n\n return sections.join(\"\\n\\n\");\n}\n\nconst FILE_KINDS = new Set([\"read\", \"edit\", \"write\", \"delete\"]);\n\nfunction normalizePathLike(pathLike: string): string {\n return pathLike.replace(/\\\\/g, \"/\");\n}\n\n/**\n * Shorten a file-path title for display in Telegram tool cards.\n *\n * Strategy (in priority order):\n * 1. If `workingDirectory` is provided and the path starts with it,\n * return the relative path → \"src/foo.ts (lines 1–10)\"\n * Also handles comma-separated multi-file titles (e.g. apply_patch).\n * 2. For file-kind titles, fall back to basename → \"foo.ts (lines 1–10)\"\n * 3. Non-file-kind titles are returned unchanged.\n */\nfunction shortenTitle(title: string, kind: string, workingDirectory?: string): string {\n if (!title.includes(\"/\")) return title;\n\n const parenIdx = title.indexOf(\" (\");\n const pathPart = parenIdx > 0 ? title.slice(0, parenIdx) : title;\n const rangePart = parenIdx > 0 ? title.slice(parenIdx) : \"\";\n\n if (workingDirectory) {\n const normalizedPathPart = normalizePathLike(pathPart);\n const normalizedCwd = normalizePathLike(workingDirectory).replace(/\\/+$/, \"\");\n const prefix = `${normalizedCwd}/`;\n const relativized = normalizedPathPart\n .split(\", \")\n .map((segment) => (segment.startsWith(prefix) ? segment.slice(prefix.length) : segment))\n .join(\", \");\n if (relativized !== normalizedPathPart) return relativized + rangePart;\n }\n\n if (FILE_KINDS.has(kind)) return basename(pathPart) + rangePart;\n return title;\n}\n\n/** Extract the last path segment for use in link labels. */\nfunction basename(pathLike: string): string {\n return pathLike.replace(/\\\\/g, \"/\").split(\"/\").pop() || pathLike;\n}\n\nfunction renderSpecSection(spec: ToolDisplaySpec): string {\n const lines: string[] = [];\n\n const DONE = new Set([\"completed\", \"done\", \"failed\", \"error\"]);\n // Status prefix at the start so text doesn't shift when status changes\n const statusPrefix =\n spec.status === \"error\" || spec.status === \"failed\"\n ? \"❌ \"\n : DONE.has(spec.status)\n ? \"✅ \"\n : \"🔄 \";\n\n // Build title line: \"✅ 📖 Read · filename.ts\"\n const kindLabel = KIND_LABELS[spec.kind];\n const displayTitle = shortenTitle(spec.title, spec.kind, spec.workingDirectory);\n // Suppress title when it duplicates the kind label (e.g. \"Edit · Edit\")\n const hasUniqueTitle = displayTitle && displayTitle.toLowerCase() !== kindLabel?.toLowerCase()\n && displayTitle.toLowerCase() !== spec.kind;\n let titleLine: string;\n if (kindLabel) {\n titleLine = hasUniqueTitle\n ? `${statusPrefix}${spec.icon} <b>${kindLabel}</b> · ${escapeHtml(displayTitle)}`\n : `${statusPrefix}${spec.icon} <b>${kindLabel}</b>`;\n } else {\n titleLine = `${statusPrefix}${spec.icon} ${escapeHtml(displayTitle)}`;\n }\n if (spec.diffStats) {\n const { added, removed } = spec.diffStats;\n if (added > 0 && removed > 0) titleLine += ` · <i>+${added}/-${removed} lines</i>`;\n else if (added > 0) titleLine += ` · <i>+${added} lines</i>`;\n else if (removed > 0) titleLine += ` · <i>-${removed} lines</i>`;\n }\n lines.push(titleLine);\n\n if (spec.description) lines.push(` <i>${escapeHtml(spec.description)}</i>`);\n if (spec.command) lines.push(` <code>${escapeHtml(spec.command)}</code>`);\n if (spec.inputContent) {\n const truncated = spec.inputContent.length > 800 ? spec.inputContent.slice(0, 797) + \"…\" : spec.inputContent;\n lines.push(` <pre><code>${escapeHtml(truncated)}</code></pre>`);\n }\n if (spec.outputSummary) lines.push(` · ${escapeHtml(spec.outputSummary)}`);\n if (spec.outputContent || spec.outputFallbackContent) {\n const raw = spec.outputContent ?? spec.outputFallbackContent!;\n const truncated =\n raw.length > 800\n ? raw.slice(0, 797) + \"…\"\n : raw;\n lines.push(` <pre><code>${escapeHtml(truncated)}</code></pre>`);\n }\n\n if (spec.viewerLinks?.file || spec.viewerLinks?.diff || spec.outputViewerLink) {\n const linkParts: string[] = [];\n // Use basename for link labels so they stay compact even with relative paths\n const linkName = basename(displayTitle || kindLabel || spec.kind);\n if (spec.viewerLinks?.file)\n linkParts.push(`<a href=\"${escapeHtml(spec.viewerLinks.file)}\">View ${escapeHtml(linkName)}</a>`);\n if (spec.viewerLinks?.diff)\n linkParts.push(`<a href=\"${escapeHtml(spec.viewerLinks.diff)}\">View diff</a>`);\n if (spec.outputViewerLink)\n linkParts.push(`<a href=\"${escapeHtml(spec.outputViewerLink)}\">View output</a>`);\n lines.push(` ${linkParts.join(\" · \")}`);\n }\n\n return lines.join(\"\\n\");\n}\n\nconst TELEGRAM_MAX_LENGTH = 4096;\n\n/**\n * Close any HTML tags that were opened but not closed in the given text.\n * Handles the Telegram HTML subset: b, i, u, s, code, pre, a, tg-spoiler.\n */\nfunction closeOpenHtmlTags(text: string): string {\n const openStack: string[] = [];\n const tagRe = /<\\/?([a-z][\\w-]*)(?:\\s[^>]*)?\\/?>/gi;\n let m: RegExpExecArray | null;\n while ((m = tagRe.exec(text)) !== null) {\n const full = m[0];\n const tag = m[1].toLowerCase();\n if (full.endsWith(\"/>\")) continue;\n if (full.startsWith(\"</\")) {\n const idx = openStack.lastIndexOf(tag);\n if (idx !== -1) openStack.splice(idx, 1);\n } else {\n openStack.push(tag);\n }\n }\n if (openStack.length === 0) return text;\n return text + openStack.reverse().map((t) => `</${t}>`).join(\"\");\n}\n\n/**\n * Split text on \"\\n\\n\" boundaries that are NOT inside <code> or <pre> blocks.\n * Naive splitting breaks HTML when commands contain blank lines.\n */\nfunction splitAtSafeBoundaries(text: string): string[] {\n const sections: string[] = [];\n let depth = 0;\n let start = 0;\n\n for (let i = 0; i < text.length; ) {\n if (text[i] === \"<\") {\n // Detect opening or closing <code>/<pre> tags to track depth\n if (text.startsWith(\"</code>\", i) || text.startsWith(\"</pre>\", i)) {\n depth = Math.max(0, depth - 1);\n i += text[i + 1] === \"/\" ? 7 : 6;\n continue;\n }\n if (text.startsWith(\"<code\", i) || text.startsWith(\"<pre\", i)) {\n depth++;\n }\n }\n\n if (depth === 0 && text[i] === \"\\n\" && text[i + 1] === \"\\n\") {\n sections.push(text.slice(start, i));\n start = i + 2;\n i += 2;\n continue;\n }\n\n i++;\n }\n\n sections.push(text.slice(start));\n return sections;\n}\n\n/**\n * Split a tool card into multiple chunks at entry boundaries.\n * Each chunk stays under the Telegram message length limit.\n */\nexport function splitToolCardText(text: string): string[] {\n if (text.length <= TELEGRAM_MAX_LENGTH) return [text];\n\n const sections = splitAtSafeBoundaries(text);\n const chunks: string[] = [];\n let current = \"\";\n\n for (const section of sections) {\n // Truncate oversized sections: slice, append ellipsis, then close open tags.\n // Order matters: ellipsis goes inside the code block, closing tag wraps it.\n // Buffer of 20 chars leaves room for closing tags after the ellipsis.\n const safeSection =\n section.length > TELEGRAM_MAX_LENGTH\n ? closeOpenHtmlTags(section.slice(0, TELEGRAM_MAX_LENGTH - 20) + \"...\")\n : section;\n\n const candidate = current ? `${current}\\n\\n${safeSection}` : safeSection;\n if (candidate.length > TELEGRAM_MAX_LENGTH && current) {\n chunks.push(closeOpenHtmlTags(current));\n current = safeSection;\n } else {\n current = candidate;\n }\n }\n if (current) chunks.push(closeOpenHtmlTags(current));\n return chunks;\n}\n","import { fileURLToPath } from 'node:url'\nimport { dirname, join, resolve } from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\n\nexport const NPM_PACKAGE = '@n1creator/openacp-cli'\n\n// Walk up from the current module's directory to find package.json.\n// Necessary because the compiled output lives in dist/ but package.json is at the root.\nfunction findPackageJson(): string | null {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (let i = 0; i < 5; i++) {\n const candidate = join(dir, 'package.json')\n if (existsSync(candidate)) return candidate\n const parent = resolve(dir, '..')\n if (parent === dir) break\n dir = parent\n }\n return null\n}\n\n/**\n * Return the installed CLI version from package.json.\n * Returns '0.0.0-dev' when running from source (no package.json found or parseable).\n */\nexport function getCurrentVersion(): string {\n try {\n const pkgPath = findPackageJson()\n if (!pkgPath) return '0.0.0-dev'\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n return pkg.version as string\n } catch {\n return '0.0.0-dev'\n }\n}\n\n/**\n * Fetch the latest published version from the npm registry.\n * Returns null on any error (network failure, rate limit, etc.) — callers treat null as \"unknown\".\n * Timeout is 5 seconds to avoid blocking the CLI on slow connections.\n */\nexport async function getLatestVersion(): Promise<string | null> {\n try {\n const res = await fetch(`https://registry.npmjs.org/${NPM_PACKAGE}/latest`, {\n signal: AbortSignal.timeout(5000),\n })\n if (!res.ok) return null\n const data = (await res.json()) as { version?: string }\n return data.version ?? null\n } catch {\n return null\n }\n}\n\n/**\n * Numerically compare two semver strings (major.minor.patch).\n * Returns -1 if current < latest, 0 if equal, 1 if current > latest.\n */\nexport function compareVersions(current: string, latest: string): -1 | 0 | 1 {\n const a = current.split('.').map(Number)\n const b = latest.split('.').map(Number)\n for (let i = 0; i < 3; i++) {\n if ((a[i] ?? 0) < (b[i] ?? 0)) return -1\n if ((a[i] ?? 0) > (b[i] ?? 0)) return 1\n }\n return 0\n}\n\n/**\n * Install the latest n1creator OpenACP release as a global package.\n * Forwards signals to the child so Ctrl+C during update cancels cleanly.\n */\nexport async function runUpdate(): Promise<boolean> {\n const { spawn } = await import('node:child_process')\n return new Promise((resolve) => {\n const child = spawn('npm', ['install', '-g', `${NPM_PACKAGE}@latest`], {\n stdio: 'inherit',\n shell: false,\n })\n const onSignal = () => {\n child.kill('SIGTERM')\n resolve(false)\n }\n process.on('SIGINT', onSignal)\n process.on('SIGTERM', onSignal)\n child.on('close', (code) => {\n process.off('SIGINT', onSignal)\n process.off('SIGTERM', onSignal)\n resolve(code === 0)\n })\n })\n}\n\n/**\n * Check for a newer CLI version and interactively offer to update before continuing.\n *\n * Skipped in dev mode, CI, non-TTY environments, and when OPENACP_SKIP_UPDATE_CHECK is set.\n * After a successful update the process exits with 0 — the user must re-run their command\n * against the newly installed binary.\n */\nexport async function checkAndPromptUpdate(): Promise<void> {\n if (process.env.OPENACP_DEV_LOOP || process.env.OPENACP_SKIP_UPDATE_CHECK || !process.stdin.isTTY) return\n\n const current = getCurrentVersion()\n if (current === '0.0.0-dev') return\n\n const latest = await getLatestVersion()\n if (!latest || compareVersions(current, latest) >= 0) return\n\n console.log(`\\x1b[33mUpdate available: v${current} → v${latest}\\x1b[0m`)\n const clack = await import('@clack/prompts')\n const yes = await clack.confirm({\n message: 'Update now before starting?',\n })\n if (clack.isCancel(yes) || !yes) {\n return\n }\n const ok = await runUpdate()\n if (ok) {\n console.log(`\\x1b[32m✓ Updated to v${latest}. Please re-run your command.\\x1b[0m`)\n process.exit(0)\n } else {\n console.error('\\x1b[31mUpdate failed. Continuing with current version.\\x1b[0m')\n }\n}\n","import type { Bot, Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { OpenACPCore } from \"../../../core/index.js\";\nimport type { Session } from \"../../../core/sessions/session.js\";\nimport { isPermissionBypass } from \"../../../core/utils/bypass-detection.js\";\nimport type { CommandRegistry } from \"../../../core/command-registry.js\";\nimport { escapeHtml } from \"../formatting.js\";\nimport { createChildLogger } from \"../../../core/utils/log.js\";\nconst log = createChildLogger({ module: \"telegram-cmd-admin\" });\n\n/**\n * Return true if the session currently has bypass permissions enabled.\n * Checks both the ACP-level mode config and the client-side override flag.\n */\nexport function isBypassActive(session: Session): boolean {\n const modeOpt = session.getConfigByCategory(\"mode\");\n return (modeOpt?.type === \"select\" && isPermissionBypass(String(modeOpt.currentValue)))\n || !!session.clientOverrides.bypassPermissions;\n}\n\nexport function buildDangerousModeKeyboard(\n sessionId: string,\n enabled: boolean,\n): InlineKeyboard {\n return new InlineKeyboard().text(\n enabled ? \"🔐 Disable Bypass Permissions\" : \"☠️ Enable Bypass Permissions\",\n `d:${sessionId}`,\n );\n}\n\n/**\n * Register the `d:` callback handler for bypass permissions toggle buttons.\n *\n * When the session is live in memory, the toggle is applied via the command registry\n * (/bypass_permissions) so it propagates through ACP. When the session is no longer\n * in memory (e.g. after restart), the toggle falls back to a direct store patch.\n */\nexport function setupDangerousModeCallbacks(bot: Bot, core: OpenACPCore): void {\n bot.callbackQuery(/^d:/, async (ctx) => {\n const sessionId = ctx.callbackQuery.data.slice(2);\n const session = core.sessionManager.getSession(sessionId);\n\n // Session live in memory — delegate to /bypass command (handles ACP + client-side fallback)\n if (session) {\n const wantOn = !isBypassActive(session);\n const toastText = wantOn\n ? \"☠️ Bypass Permissions enabled — permissions auto-approved\"\n : \"🔐 Bypass Permissions disabled — approvals required\";\n try {\n await ctx.answerCallbackQuery({ text: toastText });\n } catch {\n /* expired */\n }\n\n const registry = core.lifecycleManager?.serviceRegistry?.get<CommandRegistry>(\"command-registry\");\n if (registry) {\n await registry.execute(wantOn ? \"/bypass_permissions on\" : \"/bypass_permissions off\", {\n raw: wantOn ? \"on\" : \"off\",\n sessionId,\n channelId: \"telegram\",\n userId: String(ctx.from?.id ?? \"\"),\n reply: async () => {},\n }).catch(() => {});\n }\n log.info({ sessionId, wantOn }, \"Bypass permissions toggled via button\");\n\n try {\n await ctx.editMessageText(buildSessionStatusText(session), {\n parse_mode: \"HTML\",\n reply_markup: buildSessionControlKeyboard(sessionId, isBypassActive(session), session.voiceMode === \"on\"),\n });\n } catch {\n /* ignore */\n }\n return;\n }\n\n // Session not in memory (e.g. after restart) — toggle directly in store\n const record = core.sessionManager.getSessionRecord(sessionId);\n if (!record || record.status === \"cancelled\" || record.status === \"error\") {\n try {\n await ctx.answerCallbackQuery({\n text: \"⚠️ Session not found or already ended.\",\n });\n } catch {\n /* expired */\n }\n return;\n }\n\n const newDangerousMode = !(record.clientOverrides?.bypassPermissions ?? record.dangerousMode ?? false);\n core.sessionManager\n .patchRecord(sessionId, { clientOverrides: { bypassPermissions: newDangerousMode } })\n .catch(() => {});\n log.info(\n { sessionId, dangerousMode: newDangerousMode },\n \"Bypass permissions toggled via button (store-only, session not in memory)\",\n );\n\n const toastText = newDangerousMode\n ? \"☠️ Bypass Permissions enabled — permissions auto-approved\"\n : \"🔐 Bypass Permissions disabled — approvals required\";\n try {\n await ctx.answerCallbackQuery({ text: toastText });\n } catch {\n /* expired */\n }\n\n try {\n await ctx.editMessageReplyMarkup({\n reply_markup: buildSessionControlKeyboard(\n sessionId,\n newDangerousMode,\n false,\n ),\n });\n } catch {\n /* ignore */\n }\n });\n}\n\n\nexport function buildTTSKeyboard(\n sessionId: string,\n enabled: boolean,\n): InlineKeyboard {\n return new InlineKeyboard().text(\n enabled ? \"🔊 Text to Speech\" : \"🔇 Text to Speech\",\n `v:${sessionId}`,\n );\n}\n\nexport function buildSessionControlKeyboard(\n sessionId: string,\n dangerousMode: boolean,\n voiceMode: boolean,\n): InlineKeyboard {\n return new InlineKeyboard()\n .text(\n dangerousMode ? \"🔐 Disable Bypass Permissions\" : \"☠️ Enable Bypass Permissions\",\n `d:${sessionId}`,\n )\n .row()\n .text(\n voiceMode ? \"🔊 Text to Speech\" : \"🔇 Text to Speech\",\n `v:${sessionId}`,\n );\n}\n\n/**\n * Build the status text shown in the session control message.\n * Includes agent, workspace, and current config info (model, thought, mode).\n */\nexport function buildSessionStatusText(\n session: Session,\n heading: string = \"✅ New chat (same agent & workspace)\",\n): string {\n const lines: string[] = [heading];\n lines.push(`<b>Agent:</b> ${escapeHtml(session.agentName)}`);\n lines.push(`<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`);\n\n const modelOpt = session.getConfigByCategory(\"model\");\n if (modelOpt && modelOpt.type === \"select\") {\n const choice = modelOpt.options\n .flatMap((o) => \"group\" in o ? o.options : [o])\n .find((c) => c.value === modelOpt.currentValue);\n lines.push(`<b>Model:</b> ${escapeHtml(choice?.name ?? modelOpt.currentValue)}`);\n }\n\n const thoughtOpt = session.getConfigByCategory(\"thought_level\");\n if (thoughtOpt && thoughtOpt.type === \"select\") {\n const choice = thoughtOpt.options\n .flatMap((o) => \"group\" in o ? o.options : [o])\n .find((c) => c.value === thoughtOpt.currentValue);\n lines.push(`<b>Thinking:</b> ${escapeHtml(choice?.name ?? thoughtOpt.currentValue)}`);\n }\n\n const modeOpt = session.getConfigByCategory(\"mode\");\n if (isBypassActive(session)) {\n lines.push(`<b>Mode:</b> ☠️ Bypass Permissions enabled`);\n } else if (modeOpt && modeOpt.type === \"select\") {\n const choice = modeOpt.options\n .flatMap((o) => \"group\" in o ? o.options : [o])\n .find((c) => c.value === modeOpt.currentValue);\n lines.push(`<b>Mode:</b> ${escapeHtml(choice?.name ?? modeOpt.currentValue)}`);\n }\n\n return lines.join(\"\\n\");\n}\n\nexport function setupTTSCallbacks(bot: Bot, core: OpenACPCore): void {\n bot.callbackQuery(/^v:/, async (ctx) => {\n const sessionId = ctx.callbackQuery.data.slice(2);\n const session = core.sessionManager.getSession(sessionId);\n\n if (!session) {\n try {\n await ctx.answerCallbackQuery({\n text: \"⚠️ Session not found or not active.\",\n });\n } catch {}\n return;\n }\n\n // Check if TTS provider is available\n if (session.voiceMode !== \"on\" && !core.speechService?.isTTSAvailable()) {\n try {\n await ctx.answerCallbackQuery({\n text: \"⚠️ TTS provider not installed. Use /tts install to set up.\",\n });\n } catch {}\n return;\n }\n\n const newMode = session.voiceMode === \"on\" ? \"off\" : \"on\";\n session.setVoiceMode(newMode);\n\n const toastText =\n newMode === \"on\"\n ? \"🔊 Text to Speech enabled\"\n : \"🔇 Text to Speech disabled\";\n try {\n await ctx.answerCallbackQuery({ text: toastText });\n } catch {}\n\n try {\n const keyboard = buildSessionControlKeyboard(\n sessionId,\n isBypassActive(session),\n newMode === \"on\",\n );\n await ctx.editMessageText(buildSessionStatusText(session), {\n parse_mode: \"HTML\",\n reply_markup: keyboard,\n });\n } catch {\n /* ignore */\n }\n });\n}\n\n/**\n * Handle `/text_to_speech [on|off]` — toggle TTS for the current session.\n * Must be used inside a session topic. Without an argument, enables TTS for the\n * next message only. Checks that a TTS provider is installed before enabling.\n */\nexport async function handleTTS(\n ctx: Context,\n core: OpenACPCore,\n): Promise<void> {\n const threadId = ctx.message?.message_thread_id;\n if (!threadId) {\n await ctx.reply(\"⚠️ This command only works inside a session topic.\", {\n parse_mode: \"HTML\",\n });\n return;\n }\n const session = await core.getOrResumeSession(\"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 args = ctx.message?.text?.split(/\\s+/).slice(1) ?? [];\n const arg = args[0]?.toLowerCase();\n\n // Check if TTS provider is available before enabling\n if (arg === \"on\" || (!arg)) {\n if (!core.speechService?.isTTSAvailable()) {\n await ctx.reply(\n \"⚠️ TTS provider not installed.\\n\\nUse <code>/tts install</code> to install Edge TTS plugin.\",\n { parse_mode: \"HTML\" },\n );\n return;\n }\n }\n\n if (arg === \"on\") {\n session.setVoiceMode(\"on\");\n await ctx.reply(\"🔊 Text to Speech enabled for this session.\", {\n parse_mode: \"HTML\",\n });\n } else if (arg === \"off\") {\n session.setVoiceMode(\"off\");\n await ctx.reply(\"🔇 Text to Speech disabled.\", { parse_mode: \"HTML\" });\n } else {\n session.setVoiceMode(\"next\");\n await ctx.reply(\"🔊 Text to Speech enabled for the next message.\", {\n parse_mode: \"HTML\",\n });\n }\n}\n\n// ─── Verbosity (deprecated alias) ──────────────────────────────────────────\n\nexport async function handleVerbosity(\n ctx: Context,\n core: OpenACPCore,\n): Promise<void> {\n // Deprecated — alias for /outputmode\n await ctx.reply(\"⚠️ <code>/verbosity</code> is deprecated. Use <code>/outputmode</code> instead.\", { parse_mode: \"HTML\" });\n await handleOutputMode(ctx, core);\n}\n\n// ─── Output Mode ────────────────────────────────────────────────────────────\n\nconst OUTPUT_MODE_LABELS: Record<string, string> = {\n low: \"🔇 Low\",\n medium: \"📊 Medium\",\n high: \"🔍 High\",\n};\n\n/**\n * Handle `/outputmode [low|medium|high]` — set the adapter-level or session-level\n * output verbosity.\n *\n * - No args: show current mode and usage\n * - `low|medium|high`: set adapter default (persisted to config)\n * - `session low|medium|high|reset`: override for the current session only\n */\nexport async function handleOutputMode(\n ctx: Context,\n core: OpenACPCore,\n): Promise<void> {\n const args = ctx.message?.text?.split(/\\s+/).slice(1) ?? [];\n const arg0 = args[0]?.toLowerCase();\n const arg1 = args[1]?.toLowerCase();\n\n // /outputmode session [low|medium|high|reset]\n if (arg0 === \"session\") {\n const chatId = ctx.chat?.id;\n const threadId = ctx.message?.message_thread_id;\n if (!chatId || threadId === undefined) {\n await ctx.reply(\"⚠️ This command must be used in a session topic.\", { parse_mode: \"HTML\" });\n return;\n }\n\n const session = await core.getOrResumeSession(\n \"telegram\",\n String(threadId),\n );\n\n if (!session) {\n await ctx.reply(\"⚠️ No active session found for this topic.\", { parse_mode: \"HTML\" });\n return;\n }\n\n if (arg1 === \"reset\") {\n await core.sessionManager.patchRecord(session.id, { outputMode: undefined });\n await ctx.reply(\"🔄 Session output mode reset to adapter default.\", { parse_mode: \"HTML\" });\n } else if (arg1 === \"low\" || arg1 === \"medium\" || arg1 === \"high\") {\n await core.sessionManager.patchRecord(session.id, { outputMode: arg1 });\n await ctx.reply(\n `${OUTPUT_MODE_LABELS[arg1]} Session output mode set to <b>${arg1}</b>.`,\n { parse_mode: \"HTML\" },\n );\n } else {\n const record = core.sessionManager.getSessionRecord(session.id);\n const current = record?.outputMode ?? \"(adapter default)\";\n await ctx.reply(\n `📊 Session output mode: <b>${current}</b>\\n\\nUsage: <code>/outputmode session low|medium|high|reset</code>`,\n { parse_mode: \"HTML\" },\n );\n }\n return;\n }\n\n // /outputmode [low|medium|high] — adapter-level\n if (arg0 === \"low\" || arg0 === \"medium\" || arg0 === \"high\") {\n await core.configManager.save(\n { channels: { telegram: { outputMode: arg0 } } },\n \"channels.telegram.outputMode\",\n );\n await ctx.reply(\n `${OUTPUT_MODE_LABELS[arg0]} Output mode set to <b>${arg0}</b>.`,\n { parse_mode: \"HTML\" },\n );\n } else {\n // Read outputMode from plugin settings (migrated out of config.json)\n let current = \"medium\";\n if (core.settingsManager) {\n const tgSettings = await core.settingsManager.loadSettings(\"@openacp/telegram\");\n current = (tgSettings.outputMode as string) ?? \"medium\";\n }\n await ctx.reply(\n `📊 Current output mode: <b>${current}</b>\\n\\n` +\n `Usage: <code>/outputmode low|medium|high</code>\\n` +\n `Session override: <code>/outputmode session low|medium|high|reset</code>\\n\\n` +\n `• <b>low</b> — minimal: title only\\n` +\n `• <b>medium</b> — balanced: description + output summary (default)\\n` +\n `• <b>high</b> — full detail: inline output, IN/OUT blocks`,\n { parse_mode: \"HTML\" },\n );\n }\n}\n\nexport function setupVerbosityCallbacks(bot: Bot, core: OpenACPCore): void {\n bot.callbackQuery(/^vb:/, async (ctx) => {\n const level = ctx.callbackQuery.data.slice(3);\n if (level !== \"low\" && level !== \"medium\" && level !== \"high\") return;\n\n await core.configManager.save(\n { channels: { telegram: { outputMode: level } } },\n \"channels.telegram.outputMode\",\n );\n\n try {\n await ctx.answerCallbackQuery({\n text: `${OUTPUT_MODE_LABELS[level]} Output mode: ${level}`,\n });\n } catch {}\n });\n}\n\n/**\n * Handle `/update` — check npm for a newer n1creator OpenACP release, install it,\n * and trigger a process restart. Edits the status message in-place as each step completes.\n */\nexport async function handleUpdate(\n ctx: Context,\n core: OpenACPCore,\n): Promise<void> {\n if (!core.requestRestart) {\n await ctx.reply(\n \"⚠️ Update is not available (no restart handler registered).\",\n { parse_mode: \"HTML\" },\n );\n return;\n }\n\n const { getCurrentVersion, getLatestVersion, compareVersions, runUpdate } =\n await import(\"../../../cli/version.js\");\n const current = getCurrentVersion();\n const statusMsg = await ctx.reply(\n `🔍 Checking for updates... (current: v${escapeHtml(current)})`,\n { parse_mode: \"HTML\" },\n );\n\n const latest = await getLatestVersion();\n if (!latest) {\n await ctx.api.editMessageText(\n ctx.chat!.id,\n statusMsg.message_id,\n \"❌ Could not check for updates.\",\n { parse_mode: \"HTML\" },\n );\n return;\n }\n\n if (compareVersions(current, latest) >= 0) {\n await ctx.api.editMessageText(\n ctx.chat!.id,\n statusMsg.message_id,\n `✅ Already up to date (v${escapeHtml(current)}).`,\n { parse_mode: \"HTML\" },\n );\n return;\n }\n\n await ctx.api.editMessageText(\n ctx.chat!.id,\n statusMsg.message_id,\n `⬇️ Updating v${escapeHtml(current)} → v${escapeHtml(latest)}...`,\n { parse_mode: \"HTML\" },\n );\n\n const ok = await runUpdate();\n if (!ok) {\n await ctx.api.editMessageText(\n ctx.chat!.id,\n statusMsg.message_id,\n \"❌ Update failed. Try manually: <code>npm install -g @n1creator/openacp-cli@latest</code>\",\n { parse_mode: \"HTML\" },\n );\n return;\n }\n\n await ctx.api.editMessageText(\n ctx.chat!.id,\n statusMsg.message_id,\n `✅ Updated to v${escapeHtml(latest)}. Restarting...`,\n { parse_mode: \"HTML\" },\n );\n\n await new Promise((r) => setTimeout(r, 500));\n await core.requestRestart();\n}\n\n/**\n * Handle `/restart` — confirm and trigger a process restart via `core.requestRestart()`.\n * A short delay is added so Telegram can deliver the confirmation message before shutdown.\n */\nexport async function handleRestart(\n ctx: Context,\n core: OpenACPCore,\n): Promise<void> {\n if (!core.requestRestart) {\n await ctx.reply(\n \"⚠️ Restart is not available (no restart handler registered).\",\n { parse_mode: \"HTML\" },\n );\n return;\n }\n await ctx.reply(\n \"🔄 <b>Restarting OpenACP...</b>\\nRebuilding and restarting. Be back shortly.\",\n { parse_mode: \"HTML\" },\n );\n // Give Telegram a moment to deliver the message before shutting down\n await new Promise((r) => setTimeout(r, 500));\n await core.requestRestart();\n}\n","import type { Bot, Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { OpenACPCore } from \"../../../core/index.js\";\nimport type { Session } from \"../../../core/sessions/session.js\";\nimport { escapeHtml } from \"../formatting.js\";\nimport { createSessionTopic, renameSessionTopic, buildDeepLink } from \"../topics.js\";\nimport { createChildLogger } from \"../../../core/utils/log.js\";\nimport { buildSessionControlKeyboard, buildSessionStatusText } from \"./admin.js\";\nimport type { CommandsAssistantContext } from \"../types.js\";\nconst log = createChildLogger({ module: \"telegram-cmd-new-session\" });\n\nfunction botFromCtx(ctx: Context): Bot {\n // createSessionTopic only uses bot.api.createForumTopic\n return { api: ctx.api } as unknown as Bot;\n}\n\n/**\n * Handle `/new [agent] [workspace]` — create a new session.\n *\n * - With both args: creates the session directly via `createSessionDirect`.\n * - From the assistant topic without full args: delegates to the AI assistant\n * to guide the user through selecting an agent and workspace.\n * - Otherwise: shows a usage hint.\n */\nexport async function handleNew(\n ctx: Context,\n core: OpenACPCore,\n chatId: number,\n assistant?: CommandsAssistantContext,\n onControlMessage?: (sessionId: string, msgId: number) => void,\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 // Full args → create directly\n if (agentName && workspace) {\n await createSessionDirect(ctx, core, chatId, agentName, workspace, onControlMessage);\n return;\n }\n\n // In assistant topic → forward to assistant for conversational handling\n const currentThreadId = ctx.message?.message_thread_id;\n if (assistant && currentThreadId === assistant.topicId) {\n const assistantSession = assistant.getSession();\n if (assistantSession) {\n const prompt = agentName\n ? `User wants to create a new session with agent \"${agentName}\" but didn't specify a workspace. Ask them which project directory to use as workspace.`\n : `User wants to create a new session. Guide them through choosing an agent and workspace (project directory).`;\n await assistantSession.enqueuePrompt(prompt);\n return;\n }\n }\n\n // Outside assistant topic or no assistant — show usage hint\n await ctx.reply(\n `Usage: <code>/new <agent> <workspace></code>\\n\\n` +\n `Or ask the assistant to create a session for you.`,\n { parse_mode: \"HTML\" },\n );\n}\n\n/**\n * Create a session topic and start the agent immediately.\n *\n * Creates the forum topic first (before the session events fire) to ensure the\n * thread ID is available when `session_thread_ready` fires. Sends a control\n * message with session status and bypass/TTS buttons. Cleans up the orphaned\n * topic if session creation fails.\n *\n * Returns the Telegram thread ID, or null on failure.\n */\nexport async function createSessionDirect(\n ctx: Context,\n core: OpenACPCore,\n chatId: number,\n agentName: string,\n workspace: string,\n onControlMessage?: (sessionId: string, msgId: number) => void,\n): Promise<number | null> {\n log.info({ userId: ctx.from?.id, agentName, workspace }, \"New session command (direct)\");\n\n let threadId: number | undefined;\n try {\n const topicName = `🔄 New Session`;\n threadId = await createSessionTopic(botFromCtx(ctx), chatId, topicName);\n\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(\"telegram\", agentName, workspace, { threadId: String(threadId) });\n\n const finalName = `🔄 ${session.agentName} — New Session`;\n try {\n await ctx.api.editForumTopic(chatId, threadId, { name: finalName });\n } catch { /* ignore rename failures */ }\n\n const controlMsg = await ctx.api.sendMessage(\n chatId,\n buildSessionStatusText(session, `✅ <b>Session started</b>`),\n {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n reply_markup: buildSessionControlKeyboard(session.id, false, false),\n },\n );\n\n onControlMessage?.(session.id, controlMsg.message_id);\n\n return threadId ?? null;\n } catch (err) {\n log.error({ err }, \"Session creation failed\");\n if (threadId) {\n try { await ctx.api.deleteForumTopic(chatId, threadId); } catch { /* ignore */ }\n }\n const message = err instanceof Error ? err.message : (typeof err === 'object' ? JSON.stringify(err) : String(err));\n await ctx.reply(`❌ ${escapeHtml(message)}`, { parse_mode: \"HTML\" });\n return null;\n }\n}\n\n/**\n * Handle `/newchat` — start a fresh chat in a new topic, reusing the current\n * session's agent and workspace. Resolves agent/workspace from the in-memory session\n * first, falling back to the stored record for sessions not currently loaded.\n */\nexport async function handleNewChat(\n ctx: Context,\n core: OpenACPCore,\n chatId: number,\n onControlMessage?: (sessionId: string, msgId: number) => void,\n): Promise<void> {\n const threadId = ctx.message?.message_thread_id;\n if (!threadId) {\n await ctx.reply(\n \"Use /newchat inside a session topic to inherit its config.\",\n { parse_mode: \"HTML\" },\n );\n return;\n }\n\n // Read agent config directly from the stored record — no need to resume the old session\n // since /newchat only inherits agentName + workspace, not any live session state.\n const record = core.sessionManager.getRecordByThread(\"telegram\", String(threadId));\n if (!record || record.status === \"cancelled\" || record.status === \"error\") {\n await ctx.reply(\"No active session in this topic.\", {\n parse_mode: \"HTML\",\n });\n return;\n }\n\n const agentName = record.agentName;\n const workspace = record.workingDir;\n\n let newThreadId: number | undefined;\n try {\n // Create topic FIRST so threadId is ready before session events fire\n const topicName = `🔄 ${agentName} — New Chat`;\n newThreadId = await createSessionTopic(\n botFromCtx(ctx),\n chatId,\n topicName,\n );\n\n // Notify in the original topic immediately with a deep link to the new one\n const topicLink = buildDeepLink(chatId, newThreadId);\n await ctx.reply(\n `✅ New chat created → <a href=\"${topicLink}\">Open topic</a>`,\n { parse_mode: \"HTML\" },\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 const session = await core.handleNewSession(\n \"telegram\",\n agentName,\n workspace,\n { threadId: String(newThreadId) },\n );\n\n const controlMsg = await ctx.api.sendMessage(\n chatId,\n buildSessionStatusText(session, `✅ New chat (same agent & workspace)`),\n {\n message_thread_id: newThreadId,\n parse_mode: \"HTML\",\n reply_markup: buildSessionControlKeyboard(session.id, false, false),\n },\n );\n\n onControlMessage?.(session.id, controlMsg.message_id);\n\n } catch (err) {\n // Clean up orphaned topic if session creation failed\n if (newThreadId) {\n try {\n await ctx.api.deleteForumTopic(chatId, newThreadId);\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\n// --- New Session button flow (ns: prefix) ---\n\n/** Workspace cache for callback data — avoids Telegram's 64-byte callback limit */\nconst WS_CACHE_MAX = 50\nconst workspaceCache = new Map<number, { agentKey: string; workspace: string; ts: number }>()\nlet nextWsId = 0\n\n// --- Force Reply state for custom path input ---\n\ninterface ForceReplyEntry {\n agentKey: string;\n chatId: number;\n createdAt: number; // ms timestamp, for TTL\n}\n\nexport const _forceReplyMap = new Map<number, ForceReplyEntry>();\n\nexport function _pruneExpiredForceReplies(): void {\n const cutoff = Date.now() - 10 * 60 * 1000;\n for (const [msgId, entry] of _forceReplyMap) {\n if (entry.createdAt < cutoff) _forceReplyMap.delete(msgId);\n }\n}\n\nexport async function _sendCustomPathPrompt(\n ctx: Context,\n chatId: number,\n agentKey: string,\n): Promise<void> {\n const threadId =\n ctx.message?.message_thread_id ??\n (ctx as Context & { callbackQuery?: { message?: { message_thread_id?: number } } })\n .callbackQuery?.message?.message_thread_id;\n\n const sent = await ctx.api.sendMessage(\n chatId,\n `Please type the workspace path.\\n\\n` +\n `Examples:\\n` +\n `• <code>/absolute/path/to/project</code>\\n` +\n `• <code>~/my-project</code>\\n` +\n `• <code>project-name</code> (created under your base directory)\\n\\n` +\n `Reply to this message with your path.`,\n {\n parse_mode: 'HTML',\n reply_markup: { force_reply: true },\n ...(threadId !== undefined ? { message_thread_id: threadId } : {}),\n },\n );\n _forceReplyMap.set(sent.message_id, { agentKey, chatId, createdAt: Date.now() });\n}\n\nexport async function _handleCustomPathReply(\n ctx: Context,\n core: OpenACPCore,\n chatId: number,\n entry: ForceReplyEntry,\n): Promise<void> {\n const input = (ctx.message!.text ?? '').trim();\n\n let resolvedPath: string;\n try {\n resolvedPath = core.configManager.resolveWorkspace(input);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await ctx.reply(`❌ ${escapeHtml(message)}\\n\\nPlease try again:`, {\n parse_mode: 'HTML',\n }).catch(() => {});\n await _sendCustomPathPrompt(ctx, chatId, entry.agentKey);\n return;\n }\n\n await createSessionDirect(ctx, core, chatId, entry.agentKey, resolvedPath);\n}\n\nfunction cacheWorkspace(agentKey: string, workspace: string): number {\n // Evict stale entries (>5 min) and cap size\n const now = Date.now()\n for (const [id, entry] of workspaceCache) {\n if (now - entry.ts > 5 * 60_000) {\n workspaceCache.delete(id)\n }\n }\n if (workspaceCache.size > WS_CACHE_MAX) {\n const sorted = [...workspaceCache.entries()].sort((a, b) => a[1].ts - b[1].ts)\n const toDelete = sorted.slice(0, workspaceCache.size - WS_CACHE_MAX)\n for (const [id] of toDelete) {\n workspaceCache.delete(id)\n }\n }\n const id = nextWsId++\n workspaceCache.set(id, { agentKey, workspace, ts: now })\n return id\n}\n\nfunction shortenPath(ws: string): string {\n const home = process.env.HOME || ''\n return home && ws.startsWith(home) ? '~' + ws.slice(home.length) : ws\n}\n\n/**\n * Show the agent selection step of the multi-step new session wizard.\n * If only one agent is installed, skip directly to workspace selection.\n */\nexport async function showAgentPicker(ctx: Context, core: OpenACPCore, chatId: number): Promise<void> {\n const catalog = core.agentCatalog\n const installed = catalog.getAvailable().filter((i) => i.installed)\n\n if (installed.length === 0) {\n await ctx.reply('No agents installed. Use /install to add one.', { parse_mode: 'HTML' }).catch(() => {})\n return\n }\n\n // Single agent → skip picker, go straight to workspace\n if (installed.length === 1) {\n await showWorkspacePicker(ctx, core, chatId, installed[0].key, true)\n return\n }\n\n const kb = new InlineKeyboard()\n for (let i = 0; i < installed.length; i += 2) {\n const row = installed.slice(i, i + 2)\n for (const agent of row) {\n kb.text(agent.name, `ns:agent:${agent.key}`)\n }\n kb.row()\n }\n\n // Always new message — menu stays untouched\n await ctx.reply('<b>🆕 New Session</b>\\nSelect an agent:', {\n parse_mode: 'HTML',\n reply_markup: kb,\n }).catch(() => {})\n}\n\nasync function showWorkspacePicker(ctx: Context, core: OpenACPCore, chatId: number, agentKey: string, newMessage = false): Promise<void> {\n const records = core.sessionManager.listRecords()\n const recentWorkspaces = [...new Set(records.map((r) => r.workingDir).filter(Boolean))]\n .slice(0, 5)\n\n const resolvedBaseDir = core.configManager.resolveWorkspace()\n\n // Ensure workspace base is always an option\n const hasBaseDir = recentWorkspaces.some(ws => ws === resolvedBaseDir)\n const workspaces = hasBaseDir\n ? recentWorkspaces\n : [resolvedBaseDir, ...recentWorkspaces].slice(0, 5)\n\n const kb = new InlineKeyboard()\n for (const ws of workspaces) {\n const id = cacheWorkspace(agentKey, ws)\n kb.text(`📁 ${shortenPath(ws)}`, `ns:ws:${id}`).row()\n }\n // Custom path → delegate to AI\n kb.text('📁 Custom path...', `ns:custom:${agentKey}`).row()\n\n const agentLabel = escapeHtml(agentKey)\n const text = `<b>🆕 New Session</b>\\nAgent: <code>${agentLabel}</code>\\n\\nSelect workspace:`\n const opts = { parse_mode: 'HTML' as const, reply_markup: kb }\n\n if (newMessage) {\n // First message in flow (single agent skip) — new message, menu untouched\n await ctx.reply(text, opts).catch(() => {})\n } else {\n // Edit the agent picker message in-place\n try {\n await ctx.editMessageText(text, opts)\n } catch {\n await ctx.reply(text, opts).catch(() => {})\n }\n }\n}\n\n/**\n * Register all callback handlers for the multi-step new session wizard.\n *\n * Wizard flow:\n * 1. `ns:start` → agent picker\n * 2. `ns:agent:<key>` → workspace picker\n * 3. `ns:ws:<id>` → create session\n * 4. `ns:custom:<key>` → force-reply prompt for custom path input\n *\n * Custom path replies are intercepted from the `message:text` middleware by\n * checking against the `_forceReplyMap` before passing to the next handler.\n */\nexport function setupNewSessionCallbacks(\n bot: Bot,\n core: OpenACPCore,\n chatId: number,\n): void {\n // Intercept replies to force-reply messages (custom path input)\n bot.on(\"message:text\", async (ctx, next) => {\n _pruneExpiredForceReplies()\n const replyToId = ctx.message.reply_to_message?.message_id\n if (replyToId === undefined) return next()\n const entry = _forceReplyMap.get(replyToId)\n if (!entry || entry.chatId !== ctx.message.chat.id) return next()\n _forceReplyMap.delete(replyToId)\n await _handleCustomPathReply(ctx, core, chatId, entry)\n })\n\n // Agent picker (also triggered from m: handler callback case)\n bot.callbackQuery('ns:start', async (ctx) => {\n try { await ctx.answerCallbackQuery() } catch { /* expired */ }\n await showAgentPicker(ctx, core, chatId)\n })\n\n bot.callbackQuery(/^ns:agent:/, async (ctx) => {\n const agentKey = ctx.callbackQuery.data.replace('ns:agent:', '')\n try { await ctx.answerCallbackQuery() } catch { /* expired */ }\n await showWorkspacePicker(ctx, core, chatId, agentKey)\n })\n\n bot.callbackQuery(/^ns:ws:/, async (ctx) => {\n const id = parseInt(ctx.callbackQuery.data.replace('ns:ws:', ''), 10)\n try { await ctx.answerCallbackQuery() } catch { /* expired */ }\n\n const entry = workspaceCache.get(id)\n if (!entry) {\n try { await ctx.editMessageText('⚠️ Session expired. Please try again via /menu.') } catch { /* ignore */ }\n return\n }\n workspaceCache.delete(id)\n\n // Show creating state in same message\n try {\n await ctx.editMessageText(\n `<b>🆕 New Session</b>\\n` +\n `Agent: <code>${escapeHtml(entry.agentKey)}</code>\\n` +\n `Workspace: <code>${escapeHtml(shortenPath(entry.workspace))}</code>\\n\\n` +\n `⏳ Creating session...`,\n { parse_mode: 'HTML' },\n )\n } catch { /* ignore */ }\n\n const threadId = await createSessionDirect(ctx, core, chatId, entry.agentKey, entry.workspace)\n\n // Update message with result\n if (threadId) {\n const { buildDeepLink } = await import('../topics.js')\n const link = buildDeepLink(chatId, threadId)\n try {\n await ctx.editMessageText(\n `<b>✅ Session created</b>\\n` +\n `Agent: <code>${escapeHtml(entry.agentKey)}</code>\\n` +\n `Workspace: <code>${escapeHtml(shortenPath(entry.workspace))}</code>\\n\\n` +\n `<a href=\"${link}\">Open session →</a>`,\n { parse_mode: 'HTML' },\n )\n } catch { /* ignore */ }\n } else {\n try {\n await ctx.editMessageText(\n `<b>❌ Session creation failed</b>\\n` +\n `Agent: <code>${escapeHtml(entry.agentKey)}</code>\\n` +\n `Workspace: <code>${escapeHtml(shortenPath(entry.workspace))}</code>\\n\\n` +\n `Try again with /new or /menu`,\n { parse_mode: 'HTML' },\n )\n } catch { /* ignore */ }\n }\n })\n\n bot.callbackQuery(/^ns:custom:/, async (ctx) => {\n const agentKey = ctx.callbackQuery.data.replace('ns:custom:', '')\n try { await ctx.answerCallbackQuery() } catch { /* expired */ }\n\n // Remove inline keyboard from wizard message so user can't click stale buttons\n try {\n await ctx.editMessageText(\n `<b>🆕 New Session</b>\\n` +\n `Agent: <code>${escapeHtml(agentKey)}</code>\\n\\n` +\n `⌨️ Waiting for workspace path...`,\n { parse_mode: 'HTML' },\n )\n } catch { /* ignore */ }\n\n await _sendCustomPathPrompt(ctx, chatId, agentKey)\n })\n}\n\n/**\n * Create a new session programmatically (used by the API server and assistant).\n *\n * Creates the forum topic and sends the initial \"Setting up...\" message before\n * calling `core.handleNewSession()`, so the threadId is ready when session events\n * fire. Cleans up the orphaned topic on failure.\n */\nexport async function executeNewSession(\n bot: Bot,\n core: OpenACPCore,\n chatId: number,\n agentName?: string,\n workspace?: string,\n): Promise<{ session: Session; threadId: number; firstMsgId: number }> {\n // Create topic with generic name first (same as original handleNew)\n const threadId = await createSessionTopic(bot, chatId, \"🔄 New Session\");\n\n const setupMsg = await bot.api.sendMessage(chatId, \"⏳ Setting up session, please wait...\", {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n });\n const firstMsgId = setupMsg.message_id;\n\n try {\n // core.handleNewSession() already wires events internally — do NOT call wireSessionEvents again\n const session = await core.handleNewSession(\n \"telegram\",\n agentName,\n workspace,\n { threadId: String(threadId) },\n );\n\n // Rename topic with agent name after session is created\n const finalName = `🔄 ${session.agentName} — New Session`;\n await renameSessionTopic(bot, chatId, threadId, finalName);\n\n return { session, threadId, firstMsgId };\n } catch (err) {\n // Clean up orphaned topic on failure\n try {\n await bot.api.deleteForumTopic(chatId, threadId);\n } catch {\n /* best effort */\n }\n throw err;\n }\n}\n","import type { Bot, Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { OpenACPCore } from \"../../../core/index.js\";\nimport type { Session } from \"../../../core/sessions/session.js\";\nimport { escapeHtml } from \"../formatting.js\";\nimport { createChildLogger } from \"../../../core/utils/log.js\";\nimport type { CommandsAssistantContext } from \"../types.js\";\nconst log = createChildLogger({ module: \"telegram-cmd-session\" });\n\n/**\n * Handle `/cancel` — abort the current prompt in the active session.\n *\n * In the assistant topic, delegates to the AI to confirm which session to cancel.\n * In a session topic with an active prompt, calls `session.abortPrompt()`.\n * If there is no active prompt (session paused/waiting), informs the user.\n */\nexport async function handleCancel(\n ctx: Context,\n core: OpenACPCore,\n assistant?: CommandsAssistantContext,\n): Promise<void> {\n const threadId = ctx.message?.message_thread_id;\n if (!threadId) return;\n\n // In assistant topic: forward to assistant for confirmation\n if (assistant && threadId === assistant.topicId) {\n const assistantSession = assistant.getSession();\n if (assistantSession) {\n await assistantSession.enqueuePrompt(\n \"User wants to cancel a session. Confirm which session to cancel.\",\n );\n return;\n }\n }\n\n const session = core.sessionManager.getSessionByThread(\n \"telegram\",\n String(threadId),\n );\n if (session) {\n log.info({ sessionId: session.id }, \"Abort prompt command\");\n await session.abortPrompt();\n await ctx.reply(\"⛔ Prompt aborted. Session is still active — send a new message to continue.\", { parse_mode: \"HTML\" });\n return;\n }\n\n // Fallback: session not in memory — nothing to abort, but session can\n // still be resumed when the user sends a new message.\n const record = core.sessionManager.getRecordByThread(\"telegram\", String(threadId));\n if (record && record.status !== \"error\") {\n log.info({ sessionId: record.sessionId, status: record.status }, \"Cancel command — no active prompt to abort\");\n await ctx.reply(\"ℹ️ No active prompt to cancel. Send a new message to resume the session.\", { parse_mode: \"HTML\" });\n }\n}\n\n/**\n * Handle `/status` — show session info for the current topic, or overall system\n * stats when used outside a topic. Falls back to stored record when the session\n * is not currently loaded in memory.\n */\nexport async function handleStatus(ctx: Context, core: OpenACPCore): Promise<void> {\n const threadId = ctx.message?.message_thread_id;\n if (threadId) {\n const session = await core.getOrResumeSession(\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.queueDepth} pending`,\n { parse_mode: \"HTML\" },\n );\n } else {\n // Fallback: show stored session info when not loaded in memory (e.g. after restart)\n const record = core.sessionManager.getRecordByThread(\"telegram\", String(threadId));\n if (record) {\n await ctx.reply(\n `<b>Session:</b> ${escapeHtml(record.name || record.sessionId)}\\n` +\n `<b>Agent:</b> ${escapeHtml(record.agentName)}\\n` +\n `<b>Status:</b> ${escapeHtml(record.status)} (not loaded)\\n` +\n `<b>Workspace:</b> <code>${escapeHtml(record.workingDir)}</code>`,\n { parse_mode: \"HTML\" },\n );\n } else {\n await ctx.reply(\"No active session in this topic.\", {\n parse_mode: \"HTML\",\n });\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\n/**\n * Handle `/sessions` — list all sessions with a status overview and cleanup buttons.\n * Headless sessions (no Telegram topic) are filtered from the display but counted.\n */\nexport async function handleTopics(ctx: Context, core: OpenACPCore): Promise<void> {\n try {\n const allRecords = core.sessionManager.listRecords();\n\n // Only show sessions that have a Telegram topic (skip headless/CLI-only)\n const records = allRecords.filter((r) => {\n const platform = r.platform as { topicId?: number };\n return !!platform?.topicId;\n });\n\n const headlessCount = allRecords.length - records.length;\n\n if (records.length === 0) {\n const extra = headlessCount > 0 ? ` (${headlessCount} headless hidden)` : \"\";\n await ctx.reply(`No sessions with topics found.${extra}`, { parse_mode: \"HTML\" });\n return;\n }\n\n const statusEmoji: Record<string, string> = {\n active: \"🟢\",\n initializing: \"🟡\",\n finished: \"✅\",\n error: \"❌\",\n cancelled: \"⛔\",\n };\n\n // Sort: active/initializing first, then by lastActiveAt desc\n const statusOrder: Record<string, number> = { active: 0, initializing: 1, error: 2, finished: 3, cancelled: 4 };\n records.sort((a, b) => (statusOrder[a.status] ?? 5) - (statusOrder[b.status] ?? 5));\n\n const MAX_DISPLAY = 30;\n const displayed = records.slice(0, MAX_DISPLAY);\n\n const lines = displayed.map((r) => {\n const emoji = statusEmoji[r.status] || \"⚪\";\n const name = r.name?.trim();\n const label = name ? escapeHtml(name) : `<i>${escapeHtml(r.agentName)} session</i>`;\n return `${emoji} ${label} <code>[${r.status}]</code>`;\n });\n\n const header = `<b>Sessions: ${records.length}</b>` +\n (headlessCount > 0 ? ` (${headlessCount} headless hidden)` : \"\");\n const truncated = records.length > MAX_DISPLAY ? `\\n\\n<i>...and ${records.length - MAX_DISPLAY} more</i>` : \"\";\n\n // Count by status for cleanup buttons (include headless sessions)\n const finishedCount = allRecords.filter((r) => r.status === \"finished\").length;\n const errorCount = allRecords.filter((r) => r.status === \"error\" || r.status === \"cancelled\").length;\n\n const keyboard = new InlineKeyboard();\n if (finishedCount > 0) {\n keyboard.text(`Cleanup finished (${finishedCount})`, \"m:cleanup:finished\").row();\n }\n if (errorCount > 0) {\n keyboard.text(`Cleanup errors (${errorCount})`, \"m:cleanup:errors\").row();\n }\n if (finishedCount + errorCount > 0) {\n keyboard.text(`Cleanup all non-active (${finishedCount + errorCount})`, \"m:cleanup:all\").row();\n }\n keyboard.text(`⚠️ Cleanup ALL (${allRecords.length})`, \"m:cleanup:everything\").row();\n keyboard.text(\"Refresh\", \"m:topics\");\n\n await ctx.reply(\n `${header}\\n\\n${lines.join(\"\\n\")}${truncated}`,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n } catch (err) {\n log.error({ err }, \"handleTopics error\");\n await ctx.reply(\"❌ Failed to list sessions.\", { parse_mode: \"HTML\" }).catch(() => {});\n }\n}\n\nexport async function handleCleanup(ctx: Context, core: OpenACPCore, chatId: number, statuses: string[]): Promise<void> {\n const allRecords = core.sessionManager.listRecords();\n const cleanable = allRecords.filter((r) => statuses.includes(r.status));\n\n if (cleanable.length === 0) {\n await ctx.reply(\"Nothing to clean up.\", { parse_mode: \"HTML\" });\n return;\n }\n\n let deleted = 0;\n let failed = 0;\n\n for (const record of cleanable) {\n try {\n const topicId = (record.platform as { topicId?: number })?.topicId;\n if (topicId) {\n try {\n await ctx.api.deleteForumTopic(chatId, topicId);\n } catch (err) {\n log.warn({ err, sessionId: record.sessionId, topicId }, \"Failed to delete forum topic during cleanup\");\n }\n }\n await core.sessionManager.removeRecord(record.sessionId);\n deleted++;\n } catch (err) {\n log.error({ err, sessionId: record.sessionId }, \"Failed to cleanup session\");\n failed++;\n }\n }\n\n await ctx.reply(\n `🗑 Cleaned up <b>${deleted}</b> sessions${failed > 0 ? ` (${failed} failed)` : \"\"}.`,\n { parse_mode: \"HTML\" },\n );\n}\n\nexport async function handleCleanupEverything(\n ctx: Context,\n core: OpenACPCore,\n chatId: number,\n systemTopicIds?: { notificationTopicId: number; assistantTopicId: number },\n): Promise<void> {\n const allRecords = core.sessionManager.listRecords();\n const cleanable = allRecords.filter((r) => {\n const platform = r.platform as { topicId?: number };\n if (systemTopicIds && platform?.topicId && (platform.topicId === systemTopicIds.notificationTopicId || platform.topicId === systemTopicIds.assistantTopicId)) return false;\n return true;\n });\n\n if (cleanable.length === 0) {\n await ctx.reply(\"Nothing to clean up.\", { parse_mode: \"HTML\" });\n return;\n }\n\n // Group by status for breakdown\n const statusCounts = new Map<string, number>();\n for (const r of cleanable) {\n statusCounts.set(r.status, (statusCounts.get(r.status) ?? 0) + 1);\n }\n\n const statusEmoji: Record<string, string> = {\n active: \"🟢\", initializing: \"🟡\", finished: \"✅\", error: \"❌\", cancelled: \"⛔\",\n };\n\n const breakdown = Array.from(statusCounts.entries())\n .map(([status, count]) => `${statusEmoji[status] ?? \"⚪\"} ${status}: ${count}`)\n .join(\"\\n\");\n\n const activeCount = (statusCounts.get(\"active\") ?? 0) + (statusCounts.get(\"initializing\") ?? 0);\n const activeWarning = activeCount > 0\n ? `\\n\\n⚠️ <b>${activeCount} active session(s) will be cancelled and their agents stopped!</b>`\n : \"\";\n\n const keyboard = new InlineKeyboard()\n .text(\"Yes, delete all\", \"m:cleanup:everything:confirm\")\n .text(\"Cancel\", \"m:topics\");\n\n await ctx.reply(\n `<b>Delete ${cleanable.length} topics?</b>\\n\\n` +\n `This will:\\n` +\n `• Delete all session topics from this group\\n` +\n `• Cancel any running agent sessions\\n` +\n `• Remove all session records\\n\\n` +\n `<b>Breakdown:</b>\\n${breakdown}${activeWarning}\\n\\n` +\n `<i>Notifications and Assistant topics will NOT be deleted.</i>`,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n}\n\nexport async function handleCleanupEverythingConfirmed(\n ctx: Context,\n core: OpenACPCore,\n chatId: number,\n systemTopicIds?: { notificationTopicId: number; assistantTopicId: number },\n): Promise<void> {\n const allRecords = core.sessionManager.listRecords();\n const cleanable = allRecords.filter((r) => {\n const platform = r.platform as { topicId?: number };\n if (systemTopicIds && platform?.topicId && (platform.topicId === systemTopicIds.notificationTopicId || platform.topicId === systemTopicIds.assistantTopicId)) return false;\n return true;\n });\n\n if (cleanable.length === 0) {\n await ctx.reply(\"Nothing to clean up.\", { parse_mode: \"HTML\" });\n return;\n }\n\n let deleted = 0;\n let failed = 0;\n\n for (const record of cleanable) {\n try {\n // Cancel active sessions first\n if (record.status === \"active\" || record.status === \"initializing\") {\n try {\n await core.sessionManager.cancelSession(record.sessionId);\n } catch (err) {\n log.warn({ err, sessionId: record.sessionId }, \"Failed to cancel session during cleanup\");\n }\n }\n\n const topicId = (record.platform as { topicId?: number })?.topicId;\n if (topicId) {\n try {\n await ctx.api.deleteForumTopic(chatId, topicId);\n } catch (err) {\n log.warn({ err, sessionId: record.sessionId, topicId }, \"Failed to delete forum topic during cleanup\");\n }\n }\n await core.sessionManager.removeRecord(record.sessionId);\n deleted++;\n } catch (err) {\n log.error({ err, sessionId: record.sessionId }, \"Failed to cleanup session\");\n failed++;\n }\n }\n\n await ctx.reply(\n `🗑 Cleaned up <b>${deleted}</b> sessions${failed > 0 ? ` (${failed} failed)` : \"\"}.`,\n { parse_mode: \"HTML\" },\n );\n}\n\n/**\n * Programmatically abort the most recently active Telegram session.\n * Used by the assistant when told to cancel a session via chat.\n * `excludeSessionId` prevents cancelling the assistant's own session.\n */\nexport async function executeCancelSession(\n core: OpenACPCore,\n excludeSessionId?: string,\n): Promise<Session | null> {\n const sessions = core.sessionManager\n .listSessions(\"telegram\")\n .filter((s) => s.status === \"active\" && s.id !== excludeSessionId)\n .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n\n const session = sessions[0];\n if (!session) return null;\n\n await session.abortPrompt();\n return session;\n}\n\nexport function setupSessionCallbacks(\n bot: Bot,\n core: OpenACPCore,\n chatId: number,\n systemTopicIds?: { notificationTopicId: number; assistantTopicId: number },\n): void {\n bot.callbackQuery(/^m:cleanup/, async (ctx) => {\n const data = ctx.callbackQuery.data;\n try {\n await ctx.answerCallbackQuery();\n } catch { /* expired */ }\n\n switch (data) {\n case \"m:cleanup:finished\":\n await handleCleanup(ctx, core, chatId, [\"finished\"]);\n break;\n case \"m:cleanup:errors\":\n await handleCleanup(ctx, core, chatId, [\"error\", \"cancelled\"]);\n break;\n case \"m:cleanup:all\":\n await handleCleanup(ctx, core, chatId, [\"finished\", \"error\", \"cancelled\"]);\n break;\n case \"m:cleanup:everything\":\n await handleCleanupEverything(ctx, core, chatId, systemTopicIds);\n break;\n case \"m:cleanup:everything:confirm\":\n await handleCleanupEverythingConfirmed(ctx, core, chatId, systemTopicIds);\n break;\n }\n });\n\n // Refresh sessions list (button created by handleTopics)\n bot.callbackQuery('m:topics', async (ctx) => {\n try { await ctx.answerCallbackQuery() } catch { /* expired */ }\n await handleTopics(ctx, core)\n })\n}\n\n/**\n * Handle `/archive` — show a confirmation prompt before deleting the current\n * session's topic. The archive destroys the Telegram topic (all messages lost)\n * and permanently removes the session record.\n */\nexport async function handleArchive(\n ctx: Context,\n core: OpenACPCore,\n): Promise<void> {\n const threadId = ctx.message?.message_thread_id;\n if (!threadId) return;\n\n // Check in-memory session first, then fall back to stored record\n const session = core.sessionManager.getSessionByThread(\"telegram\", String(threadId));\n const record = !session ? core.sessionManager.getRecordByThread(\"telegram\", String(threadId)) : undefined;\n\n // Must be used in a session topic (not orphan or non-session)\n if (!session && !record) {\n await ctx.reply(\"This topic is not linked to a session.\", { parse_mode: \"HTML\" });\n return;\n }\n\n const identifier = session?.id ?? record?.sessionId;\n if (!identifier) {\n await ctx.reply(\"Could not determine session for this topic.\", { parse_mode: \"HTML\" });\n return;\n }\n\n // Session must be active (not initializing)\n const status = session?.status ?? record?.status;\n if (status === \"initializing\") {\n await ctx.reply(\"Cannot archive a session that is still initializing. Wait for it to become active.\", { parse_mode: \"HTML\" });\n return;\n }\n\n await ctx.reply(\n \"⚠️ <b>Archive this session?</b>\\n\\n\" +\n \"This will:\\n\" +\n \"• Stop the agent session\\n\" +\n \"• Delete this topic permanently\\n\\n\" +\n \"<i>This cannot be undone.</i>\",\n {\n parse_mode: \"HTML\",\n reply_markup: new InlineKeyboard()\n .text(\"🗑 Yes, archive\", `ar:yes:${identifier}`)\n .text(\"❌ Cancel\", `ar:no:${identifier}`),\n },\n );\n}\n\nexport async function handleArchiveConfirm(\n ctx: Context,\n core: OpenACPCore,\n chatId: number,\n): Promise<void> {\n const data = ctx.callbackQuery?.data;\n if (!data) return;\n\n try {\n await ctx.answerCallbackQuery();\n } catch { /* expired */ }\n\n // Format: ar:<action>:<identifier> where identifier is sessionId\n const [, action, ...rest] = data.split(\":\");\n const identifier = rest.join(\":\");\n\n if (action === \"no\") {\n await ctx.editMessageText(\"Archive cancelled.\", { parse_mode: \"HTML\" });\n return;\n }\n\n // action === \"yes\"\n // Note: we don't editMessageText here because the topic is about to be deleted\n\n const result = await core.archiveSession(identifier);\n if (!result.ok) {\n try {\n await ctx.editMessageText(`Failed to archive: <code>${escapeHtml(result.error)}</code>`, { parse_mode: \"HTML\" });\n } catch {\n core.notificationManager.notifyAll({\n sessionId: identifier,\n type: \"error\",\n summary: `Failed to archive session \"${identifier}\": ${result.error}`,\n });\n }\n }\n}\n\n","import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, chmodSync, rmdirSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { getAgentCapabilities, commandExists, listAgentsWithIntegration } from \"../core/agents/agent-dependencies.js\";\nimport type {\n AgentIntegrationSpec,\n AgentHooksIntegrationSpec,\n AgentPluginIntegrationSpec,\n} from \"../core/agents/agent-dependencies.js\";\n\n/** Result of an install/uninstall operation with a human-readable log. */\nexport interface IntegrationResult {\n success: boolean;\n /** Log lines describing what was created, updated, or removed. */\n logs: string[];\n}\n\n/**\n * A single installable integration component (e.g., handoff scripts, tunnel skill).\n * Each integration consists of one or more items installed/uninstalled independently.\n */\nexport interface IntegrationItem {\n id: string;\n name: string;\n description: string;\n isInstalled(): boolean;\n install(): Promise<IntegrationResult>;\n uninstall(): Promise<IntegrationResult>;\n}\n\n/** All integration items for a specific agent. */\nexport interface AgentIntegration {\n items: IntegrationItem[];\n}\n\n// The filename used to detect whether the inject hook is already installed in an agent's\n// hook event. Checked before adding to prevent duplicate entries across re-runs.\nconst HOOK_MARKER = \"openacp-inject-session.sh\";\n\nfunction isPluginIntegrationSpec(spec: AgentIntegrationSpec): spec is AgentPluginIntegrationSpec {\n return spec.strategy === \"plugin\";\n}\n\nfunction isHooksIntegrationSpec(spec: AgentIntegrationSpec): spec is AgentHooksIntegrationSpec {\n return spec.strategy === \"hooks\";\n}\n\nfunction expandPath(p: string): string {\n return p.replace(/^~/, homedir());\n}\n\n// --- Script generators ---\n\n/**\n * Generate the session inject hook script for a hooks-based agent integration.\n *\n * The inject script runs before each agent prompt. It reads the ACP session ID and CWD\n * from the agent's hook input (JSON or plaintext), then outputs them as context variables\n * that the agent can pass to `openacp adopt` via the handoff command.\n *\n * Uses `jq` for JSON parsing; falls back to `~/.openacp/bin/jq` if not on PATH.\n */\nfunction generateInjectScript(_agentKey: string, spec: AgentHooksIntegrationSpec): string {\n const sidVar = spec.sessionIdVar ?? \"SESSION_ID\";\n const cwdVar = spec.workingDirVar ?? \"WORKING_DIR\";\n\n // Resolve jq: check ~/.openacp/bin first, then PATH\n const jqResolver = `JQ=$(command -v jq 2>/dev/null || echo \"$HOME/.openacp/bin/jq\")`;\n\n if (spec.outputFormat === \"plaintext\") {\n return `#!/bin/bash\n${jqResolver}\nINPUT=$(cat)\nSESSION_ID=$(echo \"$INPUT\" | \"$JQ\" -r '${spec.sessionIdField}')\nCWD=$(echo \"$INPUT\" | \"$JQ\" -r '.cwd')\n\necho \"${sidVar}: $SESSION_ID\"\necho \"${cwdVar}: $CWD\"\n\nexit 0\n`;\n }\n\n // JSON output (Gemini, Cline, Cursor)\n return `#!/bin/bash\n${jqResolver}\nINPUT=$(cat)\nSESSION_ID=$(echo \"$INPUT\" | \"$JQ\" -r '${spec.sessionIdField}')\nCWD=$(echo \"$INPUT\" | \"$JQ\" -r '.cwd')\n\n\"$JQ\" -n --arg sid \"$SESSION_ID\" --arg cwd \"$CWD\" \\\\\n '{\"additionalContext\":\"${sidVar}: \\\\($sid)\\\\n${cwdVar}: \\\\($cwd)\"}'\n\nexit 0\n`;\n}\n\n/**\n * Generate the handoff shell script that the agent calls to transfer the session.\n * Wraps `openacp adopt <agent> <session_id>` with optional --cwd and --channel flags.\n */\nfunction generateHandoffScript(agentKey: string): string {\n return `#!/bin/bash\nSESSION_ID=$1\nCWD=$2\nCHANNEL=$3\n\nif [ -z \"$SESSION_ID\" ]; then\n echo \"Usage: openacp-handoff.sh <session_id> [cwd] [channel]\"\n exit 1\nfi\n\nopenacp adopt ${agentKey} \"$SESSION_ID\" \\${CWD:+--cwd \"$CWD\"} \\${CHANNEL:+--channel \"$CHANNEL\"}\n`;\n}\n\n/**\n * Generate the tunnel skill markdown file.\n * This is a slash command / skill description that tells the AI agent when and how\n * to use `openacp tunnel` commands to expose local ports.\n */\nfunction generateTunnelCommand(): string {\n return `---\ndescription: Expose local ports to the internet. Use when user wants to share, preview, or access their local dev server remotely. Triggers on phrases like \"expose port\", \"map port\", \"share my app\", \"make it public\", \"open tunnel\", \"public URL\", \"share localhost\", \"preview on phone\", \"access from outside\", \"forward port\", \"ngrok\", \"cloudflare tunnel\", etc.\n---\n\nYou have access to OpenACP tunnel management via CLI. This creates a public URL for any local port (dev servers, APIs, static sites, etc.) using Cloudflare tunnel.\n\n## Commands\n\n\\`\\`\\`bash\n# Create a tunnel — exposes local port to the internet\nopenacp tunnel add <port> --label <name>\n\n# List all active tunnels with their public URLs\nopenacp tunnel list\n\n# Stop a specific tunnel\nopenacp tunnel stop <port>\n\n# Stop all tunnels\nopenacp tunnel stop-all\n\\`\\`\\`\n\n## When to use\n\nUser wants to:\n- **Share their local app** — \"share this on my phone\", \"let my friend see this\", \"preview on mobile\"\n- **Expose a port** — \"expose port 3000\", \"map port 5173\", \"make port 8080 public\"\n- **Get a public URL** — \"give me a public URL\", \"I need an external link\", \"make localhost accessible\"\n- **Open a tunnel** — \"open tunnel\", \"start tunnel\", \"tunnel this\"\n- **Forward/proxy a port** — \"forward port 3000\", \"proxy my server\"\n- **Deploy preview** — \"deploy preview\", \"share a preview link\"\n- **Access remotely** — \"access from my phone\", \"access from outside\"\n- **Manage tunnels** — \"show tunnels\", \"list tunnels\", \"stop tunnel\", \"close tunnel\", \"kill tunnel\"\n\n## How to respond\n\n1. Run the CLI command\n2. Share the public URL with the user\n3. Mention the URL works on any device (phone, tablet, other computer)\n4. If the user hasn't started a dev server yet, remind them to start one first\n\n## Example flow\n\nUser: \"I want to see this React app on my phone\"\n→ Check if dev server is running (e.g. port 5173 for Vite)\n→ Run: \\`openacp tunnel add 5173 --label react-app\\`\n→ Share the public URL\n`;\n}\n\nfunction generateHandoffCommand(_agentKey: string, spec: AgentHooksIntegrationSpec): string {\n const sidVar = spec.sessionIdVar ?? \"SESSION_ID\";\n const cwdVar = spec.workingDirVar ?? \"WORKING_DIR\";\n const hooksDir = expandPath(spec.hooksDirPath);\n\n return `---\ndescription: Transfer current session to OpenACP (Telegram/Discord)\n---\n\nLook at the context injected at the start of this message to find\n${sidVar} and ${cwdVar}, then run:\n\nbash ${hooksDir}openacp-handoff.sh <${sidVar}> <${cwdVar}> <args if any>\n\nUsage: /openacp:handoff [channel]\n channel: name of a registered adapter (e.g. telegram), or omit for default\n\nExamples:\n /openacp:handoff\n /openacp:handoff telegram\n`;\n}\n\nfunction generateOpencodeHandoffCommand(spec: AgentPluginIntegrationSpec): string {\n return `---\nname: ${spec.handoffCommandName}\ndescription: Transfer current OpenCode session to OpenACP (Telegram/Discord)\n---\n\nUse OPENCODE_SESSION_ID from injected context, then run:\n\nopenacp adopt opencode <OPENCODE_SESSION_ID>\n\nIf a channel argument is provided, append:\n\n--channel <channel_name>\n\nUsage:\n /${spec.handoffCommandName}\n /${spec.handoffCommandName} telegram\n`;\n}\n\nfunction generateOpencodePlugin(spec: AgentPluginIntegrationSpec): string {\n return `export const OpenACPHandoffPlugin = async () => {\n return {\n \"command.execute.before\": async (input, output) => {\n if (input.command !== ${JSON.stringify(spec.handoffCommandName)}) return\n output.parts.unshift({\n id: \"openacp-session-inject\",\n sessionID: input.sessionID,\n messageID: \"openacp-inject\",\n type: \"text\",\n text: \\`OPENCODE_SESSION_ID: \\${input.sessionID}\\\\n\\`,\n })\n },\n }\n}\n`;\n}\n\n// --- Settings mergers ---\n// These functions add the inject hook to an agent's settings file without clobbering\n// existing hooks. A backup (.bak) is written before any modification.\n\n/**\n * Add the inject hook to a Claude-style settings.json (hooks nested under groups).\n * Skips if the hook is already present (idempotent).\n */\nfunction mergeSettingsJson(settingsPath: string, hookEvent: string, hookScriptPath: string): void {\n const fullPath = expandPath(settingsPath);\n let settings: Record<string, unknown> = {};\n\n if (existsSync(fullPath)) {\n const raw = readFileSync(fullPath, \"utf-8\");\n writeFileSync(`${fullPath}.bak`, raw);\n settings = JSON.parse(raw);\n }\n\n const hooks = (settings.hooks ?? {}) as Record<string, unknown[]>;\n settings.hooks = hooks;\n\n const eventHooks = (hooks[hookEvent] ?? []) as Array<{ hooks?: Array<{ type?: string; command?: string }> }>;\n hooks[hookEvent] = eventHooks;\n\n const alreadyInstalled = eventHooks.some((group) =>\n group.hooks?.some((h) => h.command?.includes(HOOK_MARKER)),\n );\n\n if (!alreadyInstalled) {\n eventHooks.push({\n hooks: [{ type: \"command\", command: hookScriptPath }],\n });\n }\n\n mkdirSync(dirname(fullPath), { recursive: true });\n writeFileSync(fullPath, JSON.stringify(settings, null, 2) + \"\\n\");\n}\n\n/**\n * Add the inject hook to a flat hooks.json (hooks as direct array entries).\n * Used for agents that follow the hooks.json format (e.g. Cursor, Cline).\n * Skips if the hook is already present (idempotent).\n */\nfunction mergeHooksJson(settingsPath: string, hookEvent: string, hookScriptPath: string): void {\n const fullPath = expandPath(settingsPath);\n let config: Record<string, unknown> = { version: 1 };\n\n if (existsSync(fullPath)) {\n const raw = readFileSync(fullPath, \"utf-8\");\n writeFileSync(`${fullPath}.bak`, raw);\n config = JSON.parse(raw);\n }\n\n const hooks = (config.hooks ?? {}) as Record<string, unknown[]>;\n config.hooks = hooks;\n\n const eventHooks = (hooks[hookEvent] ?? []) as Array<{ command?: string }>;\n hooks[hookEvent] = eventHooks;\n\n const alreadyInstalled = eventHooks.some((h) => h.command?.includes(HOOK_MARKER));\n\n if (!alreadyInstalled) {\n eventHooks.push({ command: hookScriptPath });\n }\n\n mkdirSync(dirname(fullPath), { recursive: true });\n writeFileSync(fullPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\nfunction removeFromSettingsJson(settingsPath: string, hookEvent: string): void {\n const fullPath = expandPath(settingsPath);\n if (!existsSync(fullPath)) return;\n\n const raw = readFileSync(fullPath, \"utf-8\");\n const settings = JSON.parse(raw);\n const hooks = settings.hooks as Record<string, unknown[]> | undefined;\n if (!hooks?.[hookEvent]) return;\n\n hooks[hookEvent] = (hooks[hookEvent] as Array<{ hooks?: Array<{ command?: string }> }>).filter(\n (group) => !group.hooks?.some((h) => h.command?.includes(\"openacp-\")),\n );\n\n if ((hooks[hookEvent] as unknown[]).length === 0) {\n delete hooks[hookEvent];\n }\n\n writeFileSync(fullPath, JSON.stringify(settings, null, 2) + \"\\n\");\n}\n\nfunction removeFromHooksJson(settingsPath: string, hookEvent: string): void {\n const fullPath = expandPath(settingsPath);\n if (!existsSync(fullPath)) return;\n\n const raw = readFileSync(fullPath, \"utf-8\");\n const config = JSON.parse(raw);\n const hooks = config.hooks as Record<string, unknown[]> | undefined;\n if (!hooks?.[hookEvent]) return;\n\n hooks[hookEvent] = (hooks[hookEvent] as Array<{ command?: string }>).filter(\n (h) => !h.command?.includes(\"openacp-\"),\n );\n\n if ((hooks[hookEvent] as unknown[]).length === 0) {\n delete hooks[hookEvent];\n }\n\n writeFileSync(fullPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n// --- Core install/uninstall ---\n\nasync function installHooksIntegration(agentKey: string, spec: AgentHooksIntegrationSpec): Promise<IntegrationResult> {\n const logs: string[] = [];\n try {\n // Check jq\n if (!commandExists(\"jq\")) {\n return {\n success: false,\n logs: [\"jq is required for handoff hooks. Install: brew install jq (macOS) or apt install jq (Linux)\"],\n };\n }\n\n const hooksDir = expandPath(spec.hooksDirPath);\n mkdirSync(hooksDir, { recursive: true });\n\n // Inject script\n const injectPath = join(hooksDir, \"openacp-inject-session.sh\");\n writeFileSync(injectPath, generateInjectScript(agentKey, spec));\n chmodSync(injectPath, 0o755);\n logs.push(`Created ${injectPath}`);\n\n // Handoff script\n const handoffPath = join(hooksDir, \"openacp-handoff.sh\");\n writeFileSync(handoffPath, generateHandoffScript(agentKey));\n chmodSync(handoffPath, 0o755);\n logs.push(`Created ${handoffPath}`);\n\n // Slash command / skill\n if (spec.commandsPath && spec.handoffCommandName) {\n if (spec.commandFormat === \"skill\") {\n const skillDir = expandPath(join(spec.commandsPath, spec.handoffCommandName));\n mkdirSync(skillDir, { recursive: true });\n const skillPath = join(skillDir, \"SKILL.md\");\n writeFileSync(skillPath, generateHandoffCommand(agentKey, spec));\n logs.push(`Created ${skillPath}`);\n } else {\n const cmdsDir = expandPath(spec.commandsPath);\n mkdirSync(cmdsDir, { recursive: true });\n const cmdPath = join(cmdsDir, `${spec.handoffCommandName}.md`);\n writeFileSync(cmdPath, generateHandoffCommand(agentKey, spec));\n logs.push(`Created ${cmdPath}`);\n }\n }\n\n // Merge settings\n const injectFullPath = join(hooksDir, \"openacp-inject-session.sh\");\n if (spec.settingsFormat === \"hooks_json\") {\n mergeHooksJson(spec.settingsPath, spec.hookEvent, injectFullPath);\n } else {\n mergeSettingsJson(spec.settingsPath, spec.hookEvent, injectFullPath);\n }\n logs.push(`Updated ${expandPath(spec.settingsPath)}`);\n\n return { success: true, logs };\n } catch (err) {\n logs.push(`Error: ${err instanceof Error ? err.message : String(err)}`);\n return { success: false, logs };\n }\n}\n\nasync function uninstallHooksIntegration(agentKey: string, spec: AgentHooksIntegrationSpec): Promise<IntegrationResult> {\n const logs: string[] = [];\n try {\n const hooksDir = expandPath(spec.hooksDirPath);\n\n // Remove hook scripts\n for (const filename of [\"openacp-inject-session.sh\", \"openacp-handoff.sh\"]) {\n const filePath = join(hooksDir, filename);\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n logs.push(`Removed ${filePath}`);\n }\n }\n\n // Remove slash command / skill\n if (spec.commandsPath && spec.handoffCommandName) {\n if (spec.commandFormat === \"skill\") {\n const skillDir = expandPath(join(spec.commandsPath, spec.handoffCommandName));\n const skillPath = join(skillDir, \"SKILL.md\");\n if (existsSync(skillPath)) {\n unlinkSync(skillPath);\n try { rmdirSync(skillDir); } catch { /* not empty */ }\n logs.push(`Removed ${skillPath}`);\n }\n } else {\n const cmdPath = expandPath(join(spec.commandsPath, `${spec.handoffCommandName}.md`));\n if (existsSync(cmdPath)) {\n unlinkSync(cmdPath);\n logs.push(`Removed ${cmdPath}`);\n }\n }\n }\n\n // Clean settings\n if (spec.settingsFormat === \"hooks_json\") {\n removeFromHooksJson(spec.settingsPath, spec.hookEvent);\n } else {\n removeFromSettingsJson(spec.settingsPath, spec.hookEvent);\n }\n logs.push(`Updated ${expandPath(spec.settingsPath)}`);\n\n return { success: true, logs };\n } catch (err) {\n logs.push(`Error: ${err instanceof Error ? err.message : String(err)}`);\n return { success: false, logs };\n }\n}\n\nasync function installPluginIntegration(_agentKey: string, spec: AgentPluginIntegrationSpec): Promise<IntegrationResult> {\n const logs: string[] = [];\n try {\n const commandsDir = expandPath(spec.commandsPath);\n mkdirSync(commandsDir, { recursive: true });\n const commandPath = join(commandsDir, spec.handoffCommandFile);\n\n const pluginsDir = expandPath(spec.pluginsPath);\n mkdirSync(pluginsDir, { recursive: true });\n const pluginPath = join(pluginsDir, spec.pluginFileName);\n\n if (existsSync(commandPath) && existsSync(pluginPath)) {\n logs.push(\"Already installed, skipping.\");\n return { success: true, logs };\n }\n\n if (existsSync(commandPath) || existsSync(pluginPath)) {\n logs.push(\"Overwriting existing files.\");\n }\n\n writeFileSync(commandPath, generateOpencodeHandoffCommand(spec));\n logs.push(`Created ${commandPath}`);\n\n writeFileSync(pluginPath, generateOpencodePlugin(spec));\n logs.push(`Created ${pluginPath}`);\n\n return { success: true, logs };\n } catch (err) {\n logs.push(`Error: ${err instanceof Error ? err.message : String(err)}`);\n return { success: false, logs };\n }\n}\n\nasync function uninstallPluginIntegration(_agentKey: string, spec: AgentPluginIntegrationSpec): Promise<IntegrationResult> {\n const logs: string[] = [];\n try {\n const commandPath = join(expandPath(spec.commandsPath), spec.handoffCommandFile);\n let removedCount = 0;\n if (existsSync(commandPath)) {\n unlinkSync(commandPath);\n logs.push(`Removed ${commandPath}`);\n removedCount += 1;\n }\n\n const pluginPath = join(expandPath(spec.pluginsPath), spec.pluginFileName);\n if (existsSync(pluginPath)) {\n unlinkSync(pluginPath);\n logs.push(`Removed ${pluginPath}`);\n removedCount += 1;\n }\n\n if (removedCount === 0) {\n logs.push(\"Nothing to remove.\");\n }\n\n return { success: true, logs };\n } catch (err) {\n logs.push(`Error: ${err instanceof Error ? err.message : String(err)}`);\n return { success: false, logs };\n }\n}\n\n/**\n * Install the integration for an agent based on its spec strategy.\n * Routes to hooks-based or plugin-based installation depending on `spec.strategy`.\n */\nexport async function installIntegration(agentKey: string, spec: AgentIntegrationSpec): Promise<IntegrationResult> {\n if (isHooksIntegrationSpec(spec)) {\n return installHooksIntegration(agentKey, spec);\n }\n return installPluginIntegration(agentKey, spec);\n}\n\n/**\n * Uninstall the integration for an agent based on its spec strategy.\n * Routes to hooks-based or plugin-based removal depending on `spec.strategy`.\n */\nexport async function uninstallIntegration(agentKey: string, spec: AgentIntegrationSpec): Promise<IntegrationResult> {\n if (isHooksIntegrationSpec(spec)) {\n return uninstallHooksIntegration(agentKey, spec);\n }\n return uninstallPluginIntegration(agentKey, spec);\n}\n\n// --- Public API ---\n// These functions build IntegrationItem objects for display and management in cmdIntegrate.\n\nfunction buildHandoffItem(agentKey: string, spec: AgentIntegrationSpec): IntegrationItem {\n return {\n id: \"handoff\",\n name: \"Handoff\",\n description: \"Transfer sessions between terminal and messaging platforms\",\n isInstalled(): boolean {\n if (isHooksIntegrationSpec(spec)) {\n const hooksDir = expandPath(spec.hooksDirPath);\n return (\n existsSync(join(hooksDir, \"openacp-inject-session.sh\")) &&\n existsSync(join(hooksDir, \"openacp-handoff.sh\"))\n );\n }\n const commandPath = join(expandPath(spec.commandsPath), spec.handoffCommandFile);\n const pluginPath = join(expandPath(spec.pluginsPath), spec.pluginFileName);\n return existsSync(commandPath) && existsSync(pluginPath);\n },\n install: () => installIntegration(agentKey, spec),\n uninstall: () => uninstallIntegration(agentKey, spec),\n };\n}\n\nfunction getSkillBasePath(spec: AgentHooksIntegrationSpec): string {\n // Skills go into the agent's skills directory (sibling to commands/).\n // Claude: ~/.claude/skills/, Cursor: ~/.cursor/skills/\n const base = spec.commandsPath!;\n // If commandsPath ends with commands/, replace it with skills/ for skill-format agents\n const skillsBase = base.replace(/\\/commands\\/?$/, \"/skills/\");\n return expandPath(skillsBase);\n}\n\nfunction buildTunnelItem(spec: AgentIntegrationSpec): IntegrationItem | null {\n if (!isHooksIntegrationSpec(spec) || !spec.commandsPath) return null;\n const hooksSpec = spec;\n\n function getTunnelPath(): string {\n return join(getSkillBasePath(hooksSpec), \"openacp-tunnel\", \"SKILL.md\");\n }\n\n return {\n id: \"tunnel\",\n name: \"Tunnel\",\n description: \"Expose local ports to the internet via OpenACP tunnel\",\n isInstalled(): boolean {\n return existsSync(getTunnelPath());\n },\n async install(): Promise<IntegrationResult> {\n const logs: string[] = [];\n try {\n const skillPath = getTunnelPath();\n mkdirSync(dirname(skillPath), { recursive: true });\n writeFileSync(skillPath, generateTunnelCommand());\n logs.push(`Created ${skillPath}`);\n return { success: true, logs };\n } catch (err) {\n logs.push(`Error: ${err instanceof Error ? err.message : String(err)}`);\n return { success: false, logs };\n }\n },\n async uninstall(): Promise<IntegrationResult> {\n const logs: string[] = [];\n try {\n const skillPath = getTunnelPath();\n if (existsSync(skillPath)) {\n unlinkSync(skillPath);\n try { rmdirSync(dirname(skillPath)); } catch { /* not empty */ }\n logs.push(`Removed ${skillPath}`);\n }\n return { success: true, logs };\n } catch (err) {\n logs.push(`Error: ${err instanceof Error ? err.message : String(err)}`);\n return { success: false, logs };\n }\n },\n };\n}\n\n/**\n * Return all integration items for the given agent, or undefined if the agent\n * has no integration support. Includes both handoff and tunnel items where applicable.\n */\nexport function getIntegration(agentName: string): AgentIntegration | undefined {\n const caps = getAgentCapabilities(agentName);\n if (!caps.integration) return undefined;\n const items: IntegrationItem[] = [buildHandoffItem(agentName, caps.integration)];\n const tunnelItem = buildTunnelItem(caps.integration);\n if (tunnelItem) items.push(tunnelItem);\n return { items };\n}\n\n/** Return the list of agent keys that have integration support defined. */\nexport function listIntegrations(): string[] {\n return listAgentsWithIntegration();\n}\n","import type { Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { OpenACPCore } from \"../../../core/core.js\";\nimport type { InstallProgress } from \"../../../core/types.js\";\nimport { escapeHtml } from \"../formatting.js\";\n\nconst AGENTS_PER_PAGE = 6;\n\n/**\n * Handle `/agents` — show installed and available agents with install buttons.\n * Available agents are paginated (6 per page). Agents with unmet dependencies\n * are shown with a warning rather than an install button.\n */\nexport async function handleAgents(ctx: Context, core: OpenACPCore, page = 0): Promise<void> {\n const catalog = core.agentCatalog;\n const items = catalog.getAvailable();\n\n const installed = items.filter((i) => i.installed);\n const available = items.filter((i) => !i.installed);\n\n let text = \"<b>🤖 Agents</b>\\n\\n\";\n\n // Installed agents section\n if (installed.length > 0) {\n text += \"<b>Installed:</b>\\n\";\n for (const item of installed) {\n text += `✅ <b>${escapeHtml(item.name)}</b>`;\n if (item.description) {\n text += ` — <i>${escapeHtml(truncate(item.description, 50))}</i>`;\n }\n text += \"\\n\";\n }\n text += \"\\n\";\n }\n\n // Available agents section (paginated)\n if (available.length > 0) {\n const totalPages = Math.ceil(available.length / AGENTS_PER_PAGE);\n const safePage = Math.max(0, Math.min(page, totalPages - 1));\n const pageItems = available.slice(safePage * AGENTS_PER_PAGE, (safePage + 1) * AGENTS_PER_PAGE);\n\n text += `<b>Available to install:</b>`;\n if (totalPages > 1) {\n text += ` (${safePage + 1}/${totalPages})`;\n }\n text += \"\\n\";\n\n for (const item of pageItems) {\n if (item.available) {\n text += `⬇️ <b>${escapeHtml(item.name)}</b>`;\n } else {\n const deps = item.missingDeps?.join(\", \") ?? \"requirements not met\";\n text += `⚠️ <b>${escapeHtml(item.name)}</b> <i>(needs: ${escapeHtml(deps)})</i>`;\n }\n if (item.description) {\n text += `\\n <i>${escapeHtml(truncate(item.description, 60))}</i>`;\n }\n text += \"\\n\";\n }\n\n // Install buttons for current page\n const keyboard = new InlineKeyboard();\n const installable = pageItems.filter((i) => i.available);\n for (let i = 0; i < installable.length; i += 2) {\n const row = installable.slice(i, i + 2);\n for (const item of row) {\n keyboard.text(`⬇️ ${item.name}`, `ag:install:${item.key}`);\n }\n keyboard.row();\n }\n\n // Pagination buttons\n if (totalPages > 1) {\n if (safePage > 0) {\n keyboard.text(\"◀️ Prev\", `ag:page:${safePage - 1}`);\n }\n if (safePage < totalPages - 1) {\n keyboard.text(\"Next ▶️\", `ag:page:${safePage + 1}`);\n }\n keyboard.row();\n }\n\n // Tip for CLI install\n if (available.some((i) => !i.available)) {\n text += \"\\n💡 <i>Agents marked ⚠️ need additional setup. Use</i> <code>openacp agents info <name></code> <i>for details.</i>\\n\";\n }\n\n await ctx.reply(text, { parse_mode: \"HTML\", reply_markup: keyboard });\n } else {\n text += \"<i>All agents are already installed!</i>\";\n await ctx.reply(text, { parse_mode: \"HTML\" });\n }\n}\n\n/**\n * Handle `/install <agent>` — install an agent by name or ID with live progress updates.\n * After installation, auto-installs handoff integration if the agent supports it,\n * and shows required setup steps as copyable commands.\n */\nexport async function handleInstall(ctx: Context, core: OpenACPCore): Promise<void> {\n const text = (ctx.message?.text ?? \"\").trim();\n const parts = text.split(/\\s+/);\n const nameOrId = parts[1];\n\n if (!nameOrId) {\n await ctx.reply(\n \"📦 <b>Install an agent</b>\\n\\n\" +\n \"Usage: <code>/install <agent-name></code>\\n\" +\n \"Example: <code>/install gemini</code>\\n\\n\" +\n \"Use /agents to browse available agents.\",\n { parse_mode: \"HTML\" },\n );\n return;\n }\n\n await installAgentWithProgress(ctx, core, nameOrId);\n}\n\nexport async function handleAgentCallback(ctx: Context, core: OpenACPCore): Promise<void> {\n const data = ctx.callbackQuery?.data ?? \"\";\n await ctx.answerCallbackQuery();\n\n if (data.startsWith(\"ag:install:\")) {\n const nameOrId = data.replace(\"ag:install:\", \"\");\n await installAgentWithProgress(ctx, core, nameOrId);\n return;\n }\n\n if (data.startsWith(\"ag:page:\")) {\n const page = parseInt(data.replace(\"ag:page:\", \"\"), 10);\n // Edit the existing message with the new page\n try {\n const catalog = core.agentCatalog;\n const items = catalog.getAvailable();\n const installed = items.filter((i) => i.installed);\n const available = items.filter((i) => !i.installed);\n\n let text = \"<b>🤖 Agents</b>\\n\\n\";\n\n if (installed.length > 0) {\n text += \"<b>Installed:</b>\\n\";\n for (const item of installed) {\n text += `✅ <b>${escapeHtml(item.name)}</b>`;\n if (item.description) {\n text += ` — <i>${escapeHtml(truncate(item.description, 50))}</i>`;\n }\n text += \"\\n\";\n }\n text += \"\\n\";\n }\n\n const totalPages = Math.ceil(available.length / AGENTS_PER_PAGE);\n const safePage = Math.max(0, Math.min(page, totalPages - 1));\n const pageItems = available.slice(safePage * AGENTS_PER_PAGE, (safePage + 1) * AGENTS_PER_PAGE);\n\n text += `<b>Available to install:</b>`;\n if (totalPages > 1) {\n text += ` (${safePage + 1}/${totalPages})`;\n }\n text += \"\\n\";\n\n for (const item of pageItems) {\n if (item.available) {\n text += `⬇️ <b>${escapeHtml(item.name)}</b>`;\n } else {\n const deps = item.missingDeps?.join(\", \") ?? \"requirements not met\";\n text += `⚠️ <b>${escapeHtml(item.name)}</b> <i>(needs: ${escapeHtml(deps)})</i>`;\n }\n if (item.description) {\n text += `\\n <i>${escapeHtml(truncate(item.description, 60))}</i>`;\n }\n text += \"\\n\";\n }\n\n const keyboard = new InlineKeyboard();\n const installable = pageItems.filter((i) => i.available);\n for (let i = 0; i < installable.length; i += 2) {\n const row = installable.slice(i, i + 2);\n for (const item of row) {\n keyboard.text(`⬇️ ${item.name}`, `ag:install:${item.key}`);\n }\n keyboard.row();\n }\n\n if (totalPages > 1) {\n if (safePage > 0) {\n keyboard.text(\"◀️ Prev\", `ag:page:${safePage - 1}`);\n }\n if (safePage < totalPages - 1) {\n keyboard.text(\"Next ▶️\", `ag:page:${safePage + 1}`);\n }\n keyboard.row();\n }\n\n await ctx.editMessageText(text, { parse_mode: \"HTML\", reply_markup: keyboard });\n } catch { /* ignore edit failures */ }\n }\n}\n\nasync function installAgentWithProgress(ctx: Context, core: OpenACPCore, nameOrId: string): Promise<void> {\n const catalog = core.agentCatalog;\n const msg = await ctx.reply(`⏳ Installing <b>${escapeHtml(nameOrId)}</b>...`, { parse_mode: \"HTML\" });\n\n let lastEdit = 0;\n const EDIT_THROTTLE_MS = 1500;\n\n const progress: InstallProgress = {\n onStart(_id, _name) { /* initial message already sent */ },\n async onStep(step) {\n const now = Date.now();\n if (now - lastEdit > EDIT_THROTTLE_MS) {\n lastEdit = now;\n try {\n await ctx.api.editMessageText(msg.chat.id, msg.message_id, `⏳ <b>${escapeHtml(nameOrId)}</b>: ${escapeHtml(step)}`, { parse_mode: \"HTML\" });\n } catch { /* rate limit or unchanged */ }\n }\n },\n async onDownloadProgress(percent) {\n const now = Date.now();\n if (now - lastEdit > EDIT_THROTTLE_MS) {\n lastEdit = now;\n try {\n const bar = buildProgressBar(percent);\n await ctx.api.editMessageText(msg.chat.id, msg.message_id, `⏳ <b>${escapeHtml(nameOrId)}</b>\\nDownloading... ${bar} ${percent}%`, { parse_mode: \"HTML\" });\n } catch { /* rate limit */ }\n }\n },\n async onSuccess(name) {\n try {\n const keyboard = new InlineKeyboard().text(`🚀 Start session with ${name}`, `na:${nameOrId}`);\n await ctx.api.editMessageText(msg.chat.id, msg.message_id, `✅ <b>${escapeHtml(name)}</b> installed!`, { parse_mode: \"HTML\", reply_markup: keyboard });\n } catch { /* ignore */ }\n },\n async onError(error) {\n try {\n await ctx.api.editMessageText(msg.chat.id, msg.message_id, `❌ ${escapeHtml(error)}`, { parse_mode: \"HTML\" });\n } catch { /* ignore */ }\n },\n };\n\n const result = await catalog.install(nameOrId, progress);\n\n // Auto-integrate handoff if agent supports it\n if (result.ok) {\n const { getAgentCapabilities } = await import(\"../../../core/agents/agent-dependencies.js\");\n const caps = getAgentCapabilities(result.agentKey);\n if (caps.integration) {\n const { installIntegration } = await import(\"../../../cli/integrate.js\");\n const intResult = await installIntegration(result.agentKey, caps.integration);\n if (intResult.success) {\n try {\n await ctx.reply(`🔗 Handoff integration installed for <b>${escapeHtml(result.agentKey)}</b>`, { parse_mode: \"HTML\" });\n } catch { /* ignore */ }\n }\n }\n }\n\n // Show setup steps as a follow-up message with copyable commands\n if (result.ok && result.setupSteps?.length) {\n let setupText = `📋 <b>Setup for ${escapeHtml(result.agentKey)}:</b>\\n\\n`;\n for (const step of result.setupSteps) {\n setupText += `→ ${formatSetupStep(step)}\\n`;\n }\n setupText += `\\n💡 <i>Tap any command to copy it</i>`;\n try {\n await ctx.reply(setupText, { parse_mode: \"HTML\" });\n } catch { /* ignore */ }\n }\n}\n\n/**\n * Format a setup step for Telegram, wrapping commands in <code> for tap-to-copy.\n * Patterns handled:\n * \"Install Claude CLI: npm install -g @anthropic-ai/claude-code\"\n * \"Login: claude login (opens browser)\"\n * \"Or set API key: export OPENAI_API_KEY=<your-key>\"\n * \"Free tier: 60 requests/min\" (no command — plain text)\n */\nfunction formatSetupStep(step: string): string {\n const colonIdx = step.indexOf(\": \");\n if (colonIdx === -1) return escapeHtml(step);\n\n const label = step.slice(0, colonIdx);\n const rest = step.slice(colonIdx + 2);\n\n // Check if the part after \":\" looks like a command (starts with known command prefixes)\n const cmdPrefixes = [\n \"npm \", \"npx \", \"pip \", \"uvx \", \"export \", \"claude \", \"codex \",\n \"openacp \", \"goose \", \"gemini \", \"cursor \", \"gh \",\n ];\n const looksLikeCommand = cmdPrefixes.some((p) => rest.startsWith(p));\n\n if (looksLikeCommand) {\n // Split command from parenthetical explanation: \"claude login (opens browser)\"\n const parenIdx = rest.indexOf(\" (\");\n if (parenIdx !== -1) {\n const cmd = rest.slice(0, parenIdx);\n const explanation = rest.slice(parenIdx);\n return `${escapeHtml(label)}: <code>${escapeHtml(cmd)}</code> ${escapeHtml(explanation)}`;\n }\n return `${escapeHtml(label)}: <code>${escapeHtml(rest)}</code>`;\n }\n\n return escapeHtml(step);\n}\n\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return text.slice(0, maxLen - 1) + \"…\";\n}\n\nfunction buildProgressBar(percent: number): string {\n const filled = Math.round(percent / 10);\n const empty = 10 - filled;\n return \"█\".repeat(filled) + \"░\".repeat(empty);\n}\n","import type { Context } from 'grammy'\nimport { InlineKeyboard } from 'grammy'\nimport type { OpenACPCore } from '../../../core/index.js'\nimport type { ConfigSelectChoice, ConfigSelectGroup } from '../../../core/types.js'\n\nconst MODELS_PER_PAGE = 8\n\nfunction flattenChoices(options: (ConfigSelectChoice | ConfigSelectGroup)[]): ConfigSelectChoice[] {\n const result: ConfigSelectChoice[] = []\n for (const item of options) {\n if ('group' in item && 'options' in item) {\n result.push(...(item as ConfigSelectGroup).options)\n } else {\n result.push(item as ConfigSelectChoice)\n }\n }\n return result\n}\n\n/**\n * Entry point for /model — shows page 0 of the paginated model selection menu.\n * Registered in TELEGRAM_OVERRIDES to intercept both direct-command and menu-callback flows.\n */\nexport async function handleModel(ctx: Context, core: OpenACPCore): Promise<void> {\n await showModelPage(ctx, core, 0, 'send')\n}\n\n/**\n * Render a paginated model selection keyboard.\n *\n * Model buttons use `c//model <value>` callback data, handled by the existing\n * c/ dispatcher in adapter.ts. Navigation buttons use `mod:<page>`.\n */\nexport async function showModelPage(\n ctx: Context,\n core: OpenACPCore,\n page: number,\n action: 'send' | 'edit',\n): Promise<void> {\n const topicId = (ctx.message ?? ctx.callbackQuery?.message)?.message_thread_id\n\n const sessionId = topicId != null\n ? ((await core.getOrResumeSession('telegram', String(topicId)))?.id ?? null)\n : null\n\n if (!sessionId) {\n if (action === 'edit') {\n await ctx.answerCallbackQuery({ text: 'Session no longer active.' }).catch(() => {})\n } else {\n await ctx.reply('⚠️ No active session. Start a session first.').catch(() => {})\n }\n return\n }\n\n const session = core.sessionManager.getSession(sessionId)\n const configOption = session?.getConfigByCategory('model')\n\n if (!configOption || configOption.type !== 'select') {\n if (action === 'edit') {\n await ctx.answerCallbackQuery({ text: 'This agent does not support switching models.' }).catch(() => {})\n } else {\n await ctx.reply('⚠️ This agent does not support switching models.').catch(() => {})\n }\n return\n }\n\n const choices = flattenChoices(configOption.options)\n const totalPages = Math.ceil(choices.length / MODELS_PER_PAGE)\n const safePage = Math.max(0, Math.min(page, totalPages - 1))\n const pageChoices = choices.slice(safePage * MODELS_PER_PAGE, (safePage + 1) * MODELS_PER_PAGE)\n\n const currentChoice = choices.find(c => c.value === configOption.currentValue)\n const currentLabel = currentChoice?.name ?? String(configOption.currentValue)\n const pageInfo = totalPages > 1 ? ` — Page ${safePage + 1}/${totalPages}` : ''\n const title = `Choose a model (current: ${currentLabel})${pageInfo}`\n\n const kb = new InlineKeyboard()\n for (const choice of pageChoices) {\n const label = choice.value === configOption.currentValue ? `✅ ${choice.name}` : choice.name\n kb.text(label, `c//model ${choice.value}`).row()\n }\n\n // Navigation row — only shown when there is more than one page\n if (totalPages > 1) {\n if (safePage > 0) kb.text('◀️ Prev', `mod:${safePage - 1}`)\n if (safePage < totalPages - 1) kb.text('Next ▶️', `mod:${safePage + 1}`)\n kb.row()\n }\n\n if (action === 'edit') {\n await ctx.editMessageText(title, { reply_markup: kb }).catch(() => {})\n } else {\n await ctx.reply(title, { reply_markup: kb }).catch(() => {})\n }\n}\n","import type { Bot, Context } from \"grammy\";\nimport type { OpenACPCore } from \"../../../core/index.js\";\nimport type { ContextQuery } from \"../../../plugins/context/context-provider.js\";\nimport { DEFAULT_MAX_TOKENS } from \"../../../plugins/context/context-provider.js\";\nimport { CheckpointReader } from \"../../../plugins/context/entire/checkpoint-reader.js\";\nimport { escapeHtml } from \"../formatting.js\";\nimport { createSessionTopic, buildDeepLink } from \"../topics.js\";\nimport { buildSessionControlKeyboard, buildSessionStatusText } from \"./admin.js\";\nimport { createChildLogger } from \"../../../core/utils/log.js\";\nimport type { CommandsAssistantContext } from \"../types.js\";\n\nconst log = createChildLogger({ module: \"telegram-cmd-resume\" });\n\nfunction botFromCtx(ctx: Context): Bot {\n return { api: ctx.api } as unknown as Bot;\n}\n\n// --- Arg parsing ---\n\n/**\n * Parse `/resume` arguments into a ContextQuery.\n *\n * Supported forms:\n * - No args → latest 5 sessions\n * - `pr <number>`, `branch <name>`, `commit <hash>`\n * - Checkpoint ID (12-char hex) or session ID (auto-detected)\n * - GitHub/Entire.io URLs (PR, commit, branch, compare, checkpoint)\n */\nexport function parseResumeArgs(matchStr: string): { query: Omit<ContextQuery, \"repoPath\"> } | null {\n const args = matchStr.split(\" \").filter(Boolean);\n if (args.length === 0) return { query: { type: \"latest\", value: \"5\" } };\n\n const first = args[0];\n\n // Subcommands\n if (first === \"pr\") return args[1] ? { query: { type: \"pr\", value: args[1] } } : null;\n if (first === \"branch\") return args[1] ? { query: { type: \"branch\", value: args[1] } } : null;\n if (first === \"commit\") return args[1] ? { query: { type: \"commit\", value: args[1] } } : null;\n\n // Auto-detect ID format\n if (CheckpointReader.isCheckpointId(first)) return { query: { type: \"checkpoint\", value: first } };\n if (CheckpointReader.isSessionId(first)) return { query: { type: \"session\", value: first } };\n\n // GitHub PR URL: github.com/org/repo/pull/19\n if (first.includes(\"/pull/\")) {\n const prMatch = first.match(/\\/pull\\/(\\d+)/);\n return prMatch ? { query: { type: \"pr\", value: prMatch[1] } } : null;\n }\n\n // GitHub commit URL: github.com/org/repo/commit/e0dd2fa4...\n const ghCommitMatch = first.match(/github\\.com\\/[^/]+\\/[^/]+\\/commit\\/([0-9a-f]+)/);\n if (ghCommitMatch) return { query: { type: \"commit\", value: ghCommitMatch[1] } };\n\n // GitHub branch URL: github.com/org/repo/tree/branch-name\n const ghBranchMatch = first.match(/github\\.com\\/[^/]+\\/[^/]+\\/tree\\/(.+?)(?:\\?|#|$)/);\n if (ghBranchMatch) return { query: { type: \"branch\", value: ghBranchMatch[1] } };\n\n // GitHub compare URL: github.com/org/repo/compare/base...head\n const ghCompareMatch = first.match(/github\\.com\\/[^/]+\\/[^/]+\\/compare\\/(?:[^.]+\\.{2,3})(.+?)(?:\\?|#|$)/);\n if (ghCompareMatch) return { query: { type: \"branch\", value: ghCompareMatch[1] } };\n\n // GitHub bare repo URL: github.com/org/repo (no subpath) → latest\n if (first.match(/github\\.com\\/[^/]+\\/[^/]+\\/?$/) && !first.includes(\"/tree/\") && !first.includes(\"/pull/\") && !first.includes(\"/commit/\") && !first.includes(\"/compare/\")) {\n return { query: { type: \"latest\", value: \"5\" } };\n }\n\n // Entire.io checkpoint URL: entire.io/gh/{owner}/{repo}/checkpoints/{branch}/{checkpoint_id}\n const entireCheckpointMatch = first.match(/entire\\.io\\/gh\\/[^/]+\\/[^/]+\\/checkpoints\\/[^/]+\\/([0-9a-f]{12})/);\n if (entireCheckpointMatch) return { query: { type: \"checkpoint\", value: entireCheckpointMatch[1] } };\n\n // Entire.io commit URL: entire.io/gh/{owner}/{repo}/commit/{commit_hash}\n const entireCommitMatch = first.match(/entire\\.io\\/gh\\/[^/]+\\/[^/]+\\/commit\\/([0-9a-f]+)/);\n if (entireCommitMatch) return { query: { type: \"commit\", value: entireCommitMatch[1] } };\n\n // Unknown — default to latest\n return { query: { type: \"latest\", value: \"5\" } };\n}\n\n// --- Execute resume with resolved workspace ---\n\nasync function executeResume(\n ctx: Context,\n core: OpenACPCore,\n chatId: number,\n query: Omit<ContextQuery, \"repoPath\">,\n repoPath: string,\n onControlMessage?: (sessionId: string, msgId: number) => void,\n): Promise<void> {\n // Check provider availability\n const provider = await core.contextManager.getProvider(repoPath);\n if (!provider) {\n await ctx.reply(\n `⚠️ <b>Entire not enabled in <code>${escapeHtml(repoPath)}</code></b>\\n\\n` +\n `To enable conversation history tracking:\\n` +\n `<code>cd ${escapeHtml(repoPath)} && npx entire enable</code>\\n\\n` +\n `Learn more: https://docs.entire.io/getting-started`,\n { parse_mode: \"HTML\" },\n );\n return;\n }\n\n // Scan sessions\n const fullQuery: ContextQuery = { ...query, repoPath };\n\n await ctx.reply(`🔍 Scanning ${query.type === \"latest\" ? \"latest sessions\" : `${query.type}: ${escapeHtml(query.value)}`}...`, { parse_mode: \"HTML\" });\n\n const listResult = await core.contextManager.listSessions(fullQuery);\n if (!listResult || listResult.sessions.length === 0) {\n await ctx.reply(\n `🔍 <b>No sessions found</b>\\n\\n` +\n `Query: <code>${escapeHtml(query.type)}: ${escapeHtml(query.value)}</code>\\n` +\n `Repo: <code>${escapeHtml(repoPath)}</code>`,\n { parse_mode: \"HTML\" },\n );\n return;\n }\n\n const config = core.configManager.get();\n const agentName = config.defaultAgent;\n\n let threadId: number | undefined;\n try {\n // Create topic FIRST (before session events fire — prevent race condition)\n const queryLabel = query.type === \"latest\" ? \"latest\" : `${query.type}: ${query.value.slice(0, 20)}`;\n const topicName = `📜 Resume — ${queryLabel}`;\n threadId = await createSessionTopic(botFromCtx(ctx), chatId, topicName);\n\n await ctx.api.sendMessage(chatId, `⏳ Loading context and starting session...`, {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n });\n\n // TODO: Over-budget interactive selection not yet implemented (F7)\n // Spec says: show options when context > 30K tokens, auto-select after 60s\n\n const { session, contextResult } = await core.createSessionWithContext({\n channelId: \"telegram\",\n agentName,\n workingDirectory: repoPath,\n contextQuery: fullQuery,\n contextOptions: { maxTokens: DEFAULT_MAX_TOKENS },\n threadId: String(threadId),\n });\n\n // Build summary info\n const sessionCount = contextResult?.sessionCount ?? listResult.sessions.length;\n const mode = contextResult?.mode ?? \"full\";\n const tokens = contextResult?.tokenEstimate ?? listResult.estimatedTokens;\n\n const topicLink = buildDeepLink(chatId, threadId);\n const replyTarget = ctx.message?.message_thread_id;\n\n if (replyTarget !== threadId) {\n await ctx.reply(\n `✅ Session resumed → <a href=\"${topicLink}\">Open topic</a>`,\n { parse_mode: \"HTML\" },\n );\n }\n\n const resumeHeading =\n `✅ <b>Session resumed with context</b>\\n` +\n `<b>Sessions loaded:</b> ${sessionCount}\\n` +\n `<b>Mode:</b> ${escapeHtml(mode)}\\n` +\n `<b>~Tokens:</b> ${tokens.toLocaleString()}`;\n const controlMsg = await ctx.api.sendMessage(\n chatId,\n buildSessionStatusText(session, resumeHeading),\n {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n reply_markup: buildSessionControlKeyboard(session.id, false, false),\n },\n );\n\n onControlMessage?.(session.id, controlMsg.message_id);\n\n } catch (err) {\n log.error({ err }, \"Resume session creation failed\");\n if (threadId) {\n try {\n await ctx.api.deleteForumTopic(chatId, threadId);\n } catch { /* ignore cleanup failures */ }\n }\n const message = err instanceof Error ? err.message : (typeof err === \"object\" ? JSON.stringify(err) : String(err));\n await ctx.reply(`❌ ${escapeHtml(message)}`, { parse_mode: \"HTML\" });\n }\n}\n\n// --- Main handler ---\n\n/**\n * Handle `/resume [args]` — create a new session with Entire.io conversation context.\n *\n * Parses the args into a ContextQuery, resolves the default workspace, checks that\n * the Entire provider is enabled, then creates the session with historical context\n * loaded. Creates the forum topic before calling `createSessionWithContext` to\n * avoid a race where session events fire before the thread is ready.\n */\nexport async function handleResume(\n ctx: Context,\n core: OpenACPCore,\n chatId: number,\n assistant?: CommandsAssistantContext,\n onControlMessage?: (sessionId: string, msgId: number) => void,\n): Promise<void> {\n const rawMatch = (ctx as Context & { match: unknown }).match;\n const matchStr = typeof rawMatch === \"string\" ? rawMatch : \"\";\n\n const parsed = parseResumeArgs(matchStr);\n if (!parsed) {\n await ctx.reply(\n `❌ <b>Invalid arguments.</b>\\n\\n` +\n `Usage examples:\\n` +\n `• <code>/resume</code> — latest 5 sessions\\n` +\n `• <code>/resume pr 19</code>\\n` +\n `• <code>/resume branch main</code>\\n` +\n `• <code>/resume commit e0dd2fa4</code>\\n` +\n `• <code>/resume f634acf05138</code> — checkpoint ID\\n` +\n `• <code>/resume https://entire.io/gh/.../checkpoints/.../2e884e2c402a</code>\\n` +\n `• <code>/resume https://entire.io/gh/.../commit/e0dd2fa4...</code>`,\n { parse_mode: \"HTML\" },\n );\n return;\n }\n\n const { query } = parsed;\n\n // Use default workspace directory (derived from instance root)\n const resolved = core.configManager.resolveWorkspace();\n await executeResume(ctx, core, chatId, query, resolved, onControlMessage);\n}\n\n// setupResumeCallbacks is retained as a no-op for backward compatibility\n// (callers may still reference it; workspace picker was removed in favor of assistant-driven flow)\nexport function setupResumeCallbacks(\n _bot: Bot,\n _core: OpenACPCore,\n _chatId: number,\n _onControlMessage?: (sessionId: string, msgId: number) => void,\n): void {\n // No-op: workspace picker callbacks removed\n}\n","import type { Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { AgentCommand } from \"../../../core/index.js\";\nimport type { MenuRegistry } from \"../../../core/menu-registry.js\";\n\n/**\n * Build the main OpenACP menu keyboard from the MenuRegistry.\n * Falls back to a hardcoded keyboard when the registry is not available.\n * Items are grouped by their `group` property, with a row break between groups.\n */\nexport function buildMenuKeyboard(menuRegistry?: MenuRegistry): InlineKeyboard {\n if (!menuRegistry) {\n return new InlineKeyboard()\n .text('🆕 New Session', 'm:core:new')\n .text('📋 Sessions', 'm:core:sessions')\n .row()\n .text('📊 Status', 'm:core:status')\n .text('🤖 Agents', 'm:core:agents')\n .row()\n .text('❓ Help', 'm:core:help')\n }\n\n const items = menuRegistry.getItems()\n const kb = new InlineKeyboard()\n let currentGroup: string | undefined\n let rowCount = 0\n\n for (const item of items) {\n if (item.group !== currentGroup && rowCount > 0) {\n kb.row()\n rowCount = 0\n }\n currentGroup = item.group\n if (rowCount >= 2) {\n kb.row()\n rowCount = 0\n }\n kb.text(item.label, `m:${item.id}`)\n rowCount++\n }\n\n return kb\n}\n\nexport async function handleMenu(ctx: Context, menuRegistry?: MenuRegistry): Promise<void> {\n await ctx.reply(`<b>OpenACP Menu</b>\\nChoose an action:`, {\n parse_mode: \"HTML\",\n reply_markup: buildMenuKeyboard(menuRegistry),\n });\n}\n\nexport async function handleHelp(ctx: Context): Promise<void> {\n await ctx.reply(\n `📖 <b>OpenACP Help</b>\\n\\n` +\n `🚀 <b>Getting Started</b>\\n` +\n `Tap 🆕 New Session to start coding with AI.\\n` +\n `Each session gets its own topic — chat there to work with the agent.\\n\\n` +\n `💡 <b>Common Tasks</b>\\n` +\n `/new [agent] [workspace] — Create new session\\n` +\n `/cancel — Cancel session (in session topic)\\n` +\n `/status — Show session or system status\\n` +\n `/sessions — List all sessions\\n` +\n `/agents — Browse & install agents\\n` +\n `/install <name> — Install an agent\\n\\n` +\n `⚙️ <b>System</b>\\n` +\n `/restart — Restart OpenACP\\n` +\n `/update — Update to latest version\\n` +\n `/integrate — Manage agent integrations\\n` +\n `/menu — Show action menu\\n\\n` +\n `🔒 <b>Session Options</b>\\n` +\n `/bypass_permissions — Toggle bypass permissions\\n` +\n `/handoff — Continue session in terminal\\n` +\n `/archive — Archive session topic\\n\\n` +\n `💬 Need help? Just ask me in this topic!`,\n { parse_mode: \"HTML\" },\n );\n}\n\n\nconst TELEGRAM_MSG_LIMIT = 4096;\n\n/**\n * Build plain-text skill command messages. Each command is on its own line\n * wrapped in <code> for tap-to-copy. If the list exceeds Telegram's message\n * limit, it is split into multiple messages (cut at line boundaries).\n */\nexport function buildSkillMessages(commands: AgentCommand[]): string[] {\n const sorted = [...commands].sort((a, b) => a.name.localeCompare(b.name));\n const header = \"🛠 <b>Available Skills</b>\\n\";\n const lines = sorted.map((c) => `<code>/${c.name}</code>`);\n\n const messages: string[] = [];\n let current = header;\n\n for (const line of lines) {\n const candidate = current + \"\\n\" + line;\n if (candidate.length > TELEGRAM_MSG_LIMIT) {\n messages.push(current);\n current = line;\n } else {\n current = candidate;\n }\n }\n if (current) messages.push(current);\n return messages;\n}\n","import type { Bot, Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { OpenACPCore } from \"../../../core/index.js\";\nimport {\n getSafeFields,\n resolveOptions,\n getConfigValue,\n setFieldValueAsync,\n isHotReloadable,\n type ConfigFieldDef,\n} from \"../../../core/config/config-registry.js\";\nimport { createChildLogger } from \"../../../core/utils/log.js\";\n\nconst log = createChildLogger({ module: \"telegram-settings\" });\n\nasync function buildSettingsKeyboard(core: OpenACPCore): Promise<InlineKeyboard> {\n const fields = getSafeFields();\n const kb = new InlineKeyboard();\n\n for (const field of fields) {\n const value = getConfigValue(core.configManager.get() as any, field.path);\n const label = formatFieldLabel(field, value);\n\n if (field.type === 'toggle') {\n kb.text(`${label}`, `s:toggle:${field.path}`).row();\n } else if (field.type === 'select') {\n kb.text(`${label}`, `s:select:${field.path}`).row();\n } else {\n kb.text(`${label}`, `s:input:${field.path}`).row();\n }\n }\n\n kb.text(\"◀️ Back to Menu\", \"s:back\");\n return kb;\n}\n\nfunction formatFieldLabel(field: ConfigFieldDef, value: unknown): string {\n const icons: Record<string, string> = {\n agent: '🤖', logging: '📝', tunnel: '🔗',\n security: '🔒', workspace: '📁', storage: '💾', speech: '🎤',\n };\n const icon = icons[field.group] ?? '⚙️';\n\n if (field.type === 'toggle') {\n return `${icon} ${field.displayName}: ${value ? 'ON' : 'OFF'}`;\n }\n const displayValue = value === null || value === undefined ? 'Not set' : String(value);\n return `${icon} ${field.displayName}: ${displayValue}`;\n}\n\n/**\n * Handle `/settings` — display an inline keyboard of all safe config fields.\n * Each button triggers a toggle, select, or delegated-to-assistant input flow.\n */\nexport async function handleSettings(ctx: Context, core: OpenACPCore): Promise<void> {\n const kb = await buildSettingsKeyboard(core);\n await ctx.reply(`<b>⚙️ Settings</b>\\nTap to change:`, {\n parse_mode: \"HTML\",\n reply_markup: kb,\n });\n}\n\n/**\n * Register all `s:` settings callback handlers.\n *\n * For string/number fields that need free-form input (`s:input:`), the\n * handler delegates to the assistant session so the user can type a value\n * conversationally rather than using a keyboard.\n *\n * STT provider selection (`s:pick:speech.stt.provider`) checks whether an\n * API key is already configured; if not, it redirects to the assistant to\n * collect the key before applying the change.\n */\nexport function setupSettingsCallbacks(\n bot: Bot,\n core: OpenACPCore,\n getAssistantSession: () => { topicId: number; enqueuePrompt: (p: string) => Promise<string> } | undefined,\n): void {\n bot.callbackQuery(/^s:toggle:/, async (ctx) => {\n const fieldPath = ctx.callbackQuery.data.replace('s:toggle:', '');\n const fieldDef = getSafeFields().find(f => f.path === fieldPath);\n if (!fieldDef) return;\n\n const currentValue = getConfigValue(core.configManager.get() as any, fieldDef.path);\n const newValue = !currentValue;\n\n try {\n await setFieldValueAsync(fieldDef, newValue, core.configManager);\n const toast = isHotReloadable(fieldPath)\n ? `✅ ${fieldPath} = ${newValue}`\n : `✅ ${fieldPath} = ${newValue} (restart needed)`;\n try { await ctx.answerCallbackQuery({ text: toast }); } catch { /* expired */ }\n try {\n await ctx.editMessageReplyMarkup({ reply_markup: await buildSettingsKeyboard(core) });\n } catch { /* ignore */ }\n } catch (err) {\n log.error({ err, fieldPath }, 'Failed to toggle config');\n try { await ctx.answerCallbackQuery({ text: '❌ Failed to update' }); } catch { /* expired */ }\n }\n });\n\n bot.callbackQuery(/^s:select:/, async (ctx) => {\n const fieldPath = ctx.callbackQuery.data.replace('s:select:', '');\n const config = core.configManager.get();\n const fieldDef = getSafeFields().find(f => f.path === fieldPath);\n if (!fieldDef) return;\n\n const options = resolveOptions(fieldDef, config) ?? [];\n const currentValue = getConfigValue(core.configManager.get() as any, fieldDef.path);\n const kb = new InlineKeyboard();\n\n for (const opt of options) {\n const marker = opt === String(currentValue) ? ' ✓' : '';\n kb.text(`${opt}${marker}`, `s:pick:${fieldPath}:${opt}`).row();\n }\n kb.text(\"◀️ Back\", \"s:back:refresh\");\n\n try { await ctx.answerCallbackQuery(); } catch { /* expired */ }\n\n try {\n await ctx.editMessageText(`<b>⚙️ ${fieldDef.displayName}</b>\\nSelect a value:`, {\n parse_mode: \"HTML\",\n reply_markup: kb,\n });\n } catch { /* ignore */ }\n });\n\n bot.callbackQuery(/^s:pick:/, async (ctx) => {\n const parts = ctx.callbackQuery.data.replace('s:pick:', '').split(':');\n const fieldPath = parts.slice(0, -1).join(':');\n const newValue = parts[parts.length - 1];\n const fieldDef = getSafeFields().find(f => f.path === fieldPath);\n if (!fieldDef) return;\n\n try {\n // For speech.stt.provider: check if the selected provider has an API key configured\n if (fieldPath === 'speech.stt.provider') {\n const sm = core.settingsManager;\n let hasApiKey = false;\n if (sm) {\n const speechSettings = await sm.loadSettings('@openacp/speech');\n hasApiKey = !!(speechSettings.groqApiKey as string);\n } else {\n // speech config migrated to plugin settings; no API key available without settingsManager\n hasApiKey = false;\n }\n if (!hasApiKey) {\n // No API key — delegate to assistant to collect it\n const assistant = getAssistantSession();\n if (assistant) {\n try { await ctx.answerCallbackQuery({ text: `🔑 API key needed — check Assistant topic` }); } catch { /* expired */ }\n const prompt = `User wants to enable ${newValue} as Speech-to-Text provider, but no API key is configured yet. Guide them to get a ${newValue} API key and set it up. After they provide the key, run both commands: \\`openacp config set speech.stt.providers.${newValue}.apiKey <key>\\` and \\`openacp config set speech.stt.provider ${newValue}\\``;\n await assistant.enqueuePrompt(prompt);\n return;\n }\n // No assistant — just warn\n try { await ctx.answerCallbackQuery({ text: `⚠️ Set API key first: openacp config set speech.stt.providers.${newValue}.apiKey <key>` }); } catch { /* expired */ }\n return;\n }\n }\n\n await setFieldValueAsync(fieldDef, newValue, core.configManager);\n\n try { await ctx.answerCallbackQuery({ text: `✅ ${fieldPath} = ${newValue}` }); } catch { /* expired */ }\n try {\n await ctx.editMessageText(`<b>⚙️ Settings</b>\\nTap to change:`, {\n parse_mode: \"HTML\",\n reply_markup: await buildSettingsKeyboard(core),\n });\n } catch { /* ignore */ }\n } catch (err) {\n log.error({ err, fieldPath }, 'Failed to set config');\n try { await ctx.answerCallbackQuery({ text: '❌ Failed to update' }); } catch { /* expired */ }\n }\n });\n\n bot.callbackQuery(/^s:input:/, async (ctx) => {\n const fieldPath = ctx.callbackQuery.data.replace('s:input:', '');\n const fieldDef = getSafeFields().find(f => f.path === fieldPath);\n if (!fieldDef) return;\n\n const currentValue = getConfigValue(core.configManager.get() as any, fieldDef.path);\n const assistant = getAssistantSession();\n\n if (!assistant) {\n try { await ctx.answerCallbackQuery({ text: '⚠️ Start the assistant first (/assistant)' }); } catch { /* expired */ }\n return;\n }\n\n try { await ctx.answerCallbackQuery({ text: `Delegating to assistant...` }); } catch { /* expired */ }\n\n const prompt = `User wants to change ${fieldDef.displayName} (config path: ${fieldPath}). Current value: ${JSON.stringify(currentValue)}. Ask them for the new value and apply it using: openacp config set ${fieldPath} <value>`;\n await assistant.enqueuePrompt(prompt);\n });\n\n bot.callbackQuery(\"s:back\", async (ctx) => {\n try { await ctx.answerCallbackQuery(); } catch { /* expired */ }\n const { buildMenuKeyboard } = await import('./menu.js');\n const menuRegistry = core.lifecycleManager?.serviceRegistry?.get('menu-registry') as import('../../../core/menu-registry.js').MenuRegistry | undefined;\n try {\n await ctx.editMessageText(`<b>OpenACP Menu</b>\\nChoose an action:`, {\n parse_mode: \"HTML\",\n reply_markup: buildMenuKeyboard(menuRegistry),\n });\n } catch { /* ignore */ }\n });\n\n bot.callbackQuery(\"s:back:refresh\", async (ctx) => {\n try { await ctx.answerCallbackQuery(); } catch { /* expired */ }\n try {\n await ctx.editMessageText(`<b>⚙️ Settings</b>\\nTap to change:`, {\n parse_mode: \"HTML\",\n reply_markup: await buildSettingsKeyboard(core),\n });\n } catch { /* ignore */ }\n });\n}\n","import type { Bot, Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { OpenACPCore } from \"../../../core/index.js\";\nimport { DoctorEngine } from \"../../../core/doctor/index.js\";\nimport type { DoctorReport, PendingFix } from \"../../../core/doctor/types.js\";\nimport { createChildLogger } from \"../../../core/utils/log.js\";\n\nconst log = createChildLogger({ module: \"telegram-cmd-doctor\" });\n\nconst pendingFixesStore = new Map<string, PendingFix[]>();\n\nfunction renderReport(report: DoctorReport): { text: string; keyboard: InlineKeyboard | undefined } {\n const icons = { pass: \"✅\", warn: \"⚠️\", fail: \"❌\" };\n const lines: string[] = [\"🩺 <b>OpenACP Doctor</b>\\n\"];\n\n for (const category of report.categories) {\n lines.push(`<b>${category.name}</b>`);\n for (const result of category.results) {\n lines.push(` ${icons[result.status]} ${escapeHtml(result.message)}`);\n }\n lines.push(\"\");\n }\n\n const { passed, warnings, failed, fixed } = report.summary;\n const fixedStr = fixed > 0 ? `, ${fixed} fixed` : \"\";\n lines.push(`<b>Result:</b> ${passed} passed, ${warnings} warnings, ${failed} failed${fixedStr}`);\n\n let keyboard: InlineKeyboard | undefined;\n if (report.pendingFixes.length > 0) {\n keyboard = new InlineKeyboard();\n for (let i = 0; i < report.pendingFixes.length; i++) {\n const label = `🔧 Fix: ${report.pendingFixes[i].message.slice(0, 30)}`;\n keyboard.text(label, `m:doctor:fix:${i}`).row();\n }\n }\n\n return { text: lines.join(\"\\n\"), keyboard };\n}\n\nfunction escapeHtml(text: string): string {\n return text.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\n/**\n * Handle `/doctor` — run all system diagnostic checks and display the report.\n *\n * Sends an initial \"running…\" message, runs `DoctorEngine.runAll()`, then\n * edits the message with the report. Pending fixes are stored per-message so\n * the `m:doctor:fix:<index>` callbacks know which fixes to apply.\n */\nexport async function handleDoctor(ctx: Context, core: OpenACPCore): Promise<void> {\n const statusMsg = await ctx.reply(\"🩺 Running diagnostics...\", { parse_mode: \"HTML\" });\n\n try {\n const engine = new DoctorEngine({ dataDir: core.instanceContext.root });\n const report = await engine.runAll();\n const { text, keyboard } = renderReport(report);\n\n const storeKey = `${ctx.chat!.id}:${statusMsg.message_id}`;\n if (report.pendingFixes.length > 0) {\n pendingFixesStore.set(storeKey, report.pendingFixes);\n }\n\n await ctx.api.editMessageText(ctx.chat!.id, statusMsg.message_id, text, {\n parse_mode: \"HTML\",\n reply_markup: keyboard,\n });\n } catch (err) {\n log.error({ err }, \"Doctor command failed\");\n await ctx.api.editMessageText(\n ctx.chat!.id,\n statusMsg.message_id,\n `❌ Doctor failed: ${err instanceof Error ? err.message : String(err)}`,\n { parse_mode: \"HTML\" },\n );\n }\n}\n\nexport function setupDoctorCallbacks(bot: Bot, core: OpenACPCore): void {\n bot.callbackQuery(/^m:doctor:fix:/, async (ctx) => {\n const data = ctx.callbackQuery.data;\n const index = parseInt(data.replace(\"m:doctor:fix:\", \"\"), 10);\n const chatId = ctx.callbackQuery.message?.chat.id;\n const messageId = ctx.callbackQuery.message?.message_id;\n\n try {\n await ctx.answerCallbackQuery({ text: \"Applying fix...\" });\n } catch { /* expired */ }\n\n if (chatId === undefined || messageId === undefined) return;\n\n const storeKey = `${chatId}:${messageId}`;\n const fixes = pendingFixesStore.get(storeKey);\n if (!fixes || index < 0 || index >= fixes.length) {\n try { await ctx.answerCallbackQuery({ text: \"Fix no longer available\" }); } catch { /* */ }\n return;\n }\n\n const pending = fixes[index];\n try {\n const result = await pending.fix();\n if (result.success) {\n const engine = new DoctorEngine({ dataDir: core.instanceContext.root });\n const report = await engine.runAll();\n const { text, keyboard } = renderReport(report);\n\n if (report.pendingFixes.length > 0) {\n pendingFixesStore.set(storeKey, report.pendingFixes);\n } else {\n pendingFixesStore.delete(storeKey);\n }\n\n await ctx.editMessageText(text, { parse_mode: \"HTML\", reply_markup: keyboard });\n } else {\n try { await ctx.answerCallbackQuery({ text: `Fix failed: ${result.message}` }); } catch { /* */ }\n }\n } catch (err) {\n log.error({ err, index }, \"Doctor fix callback failed\");\n }\n });\n\n bot.callbackQuery(\"m:doctor\", async (ctx) => {\n try { await ctx.answerCallbackQuery(); } catch { /* */ }\n await handleDoctor(ctx, core);\n });\n}\n","import type { Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { OpenACPCore } from \"../../../core/index.js\";\nimport { escapeHtml } from \"../formatting.js\";\nimport { createChildLogger } from \"../../../core/utils/log.js\";\n\nconst log = createChildLogger({ module: \"telegram-cmd-tunnel\" });\n\n/**\n * Handle `/tunnel [port] [label]` or `/tunnel stop <port>`.\n *\n * Associates the tunnel with the current session (if in a session topic) so it\n * appears in `/tunnels` session-scoped view.\n */\nexport async function handleTunnel(\n ctx: Context,\n core: OpenACPCore,\n): Promise<void> {\n if (!core.tunnelService) {\n await ctx.reply(\"❌ Tunnel service is not enabled.\", { parse_mode: \"HTML\" });\n return;\n }\n\n const match = (ctx as Context & { match?: string }).match?.trim() ?? \"\";\n\n // /tunnel stop <port>\n if (match.startsWith(\"stop \")) {\n const portStr = match.slice(5).trim();\n const port = parseInt(portStr, 10);\n if (isNaN(port)) {\n await ctx.reply(\"❌ Invalid port number.\", { parse_mode: \"HTML\" });\n return;\n }\n\n try {\n await core.tunnelService.stopTunnel(port);\n await ctx.reply(`🔌 Tunnel stopped: port ${port}`, { parse_mode: \"HTML\" });\n } catch (err) {\n await ctx.reply(`❌ ${escapeHtml((err as Error).message)}`, { parse_mode: \"HTML\" });\n }\n return;\n }\n\n // /tunnel <port> [label]\n if (match) {\n const parts = match.split(/\\s+/);\n const port = parseInt(parts[0], 10);\n if (isNaN(port)) {\n await ctx.reply(\"❌ Invalid port number. Usage: <code>/tunnel 3000 [label]</code>\", { parse_mode: \"HTML\" });\n return;\n }\n const label = parts.slice(1).join(\" \") || undefined;\n\n // Find session for this thread\n const threadId = ctx.message?.message_thread_id;\n let sessionId: string | undefined;\n if (threadId) {\n const session = core.sessionManager.getSessionByThread(\"telegram\", String(threadId));\n if (session) sessionId = session.id;\n }\n\n try {\n await ctx.reply(`⏳ Starting tunnel for port ${port}...`, { parse_mode: \"HTML\" });\n const entry = await core.tunnelService.addTunnel(port, { label, sessionId });\n await ctx.reply(\n `🔗 <b>Tunnel active</b>\\n\\nPort ${port}${label ? ` (${escapeHtml(label)})` : \"\"}\\n→ <a href=\"${escapeHtml(entry.publicUrl || \"\")}\">${escapeHtml(entry.publicUrl || \"\")}</a>`,\n { parse_mode: \"HTML\" },\n );\n } catch (err) {\n await ctx.reply(`❌ ${escapeHtml((err as Error).message)}`, { parse_mode: \"HTML\" });\n }\n return;\n }\n\n // No args — show help\n await ctx.reply(\n `<b>Tunnel commands:</b>\\n\\n` +\n `<code>/tunnel <port> [label]</code> — Create tunnel\\n` +\n `<code>/tunnel stop <port></code> — Stop tunnel\\n` +\n `<code>/tunnels</code> — List active tunnels`,\n { parse_mode: \"HTML\" },\n );\n}\n\n/**\n * Handle `/tunnels` — list active tunnels with stop buttons.\n * In a session topic, shows only tunnels owned by that session.\n * In other topics (assistant, general), shows all tunnels.\n */\nexport async function handleTunnels(\n ctx: Context,\n core: OpenACPCore,\n): Promise<void> {\n if (!core.tunnelService) {\n await ctx.reply(\"❌ Tunnel service is not enabled.\", { parse_mode: \"HTML\" });\n return;\n }\n\n // In session topic: show only that session's tunnels. In assistant/other: show all.\n const threadId = ctx.message?.message_thread_id;\n let entries = core.tunnelService.listTunnels();\n let sessionScoped = false;\n\n if (threadId) {\n const session = core.sessionManager.getSessionByThread(\"telegram\", String(threadId));\n if (session) {\n entries = entries.filter(e => e.sessionId === session.id);\n sessionScoped = true;\n }\n }\n\n if (entries.length === 0) {\n const hint = sessionScoped\n ? \"No tunnels for this session.\\n\\nUse <code>/tunnel <port></code> to create one.\"\n : \"No active tunnels.\\n\\nUse <code>/tunnel <port></code> to create one.\";\n await ctx.reply(hint, { parse_mode: \"HTML\" });\n return;\n }\n\n const lines = entries.map((e) => {\n const status = e.status === \"active\" ? \"✅\" : e.status === \"starting\" ? \"⏳\" : \"❌\";\n const label = e.label ? ` (${escapeHtml(e.label)})` : \"\";\n const url = e.publicUrl ? `\\n → <a href=\"${escapeHtml(e.publicUrl)}\">${escapeHtml(e.publicUrl)}</a>` : \"\";\n return `${status} Port <b>${e.port}</b>${label}${url}`;\n });\n\n const keyboard = new InlineKeyboard();\n for (const e of entries) {\n keyboard.text(`🔌 Stop ${e.port}${e.label ? ` (${e.label})` : \"\"}`, `tn:stop:${e.port}`).row();\n }\n if (entries.length > 1) {\n keyboard.text(\"🔌 Stop all\", \"tn:stop-all\").row();\n }\n\n await ctx.reply(\n `<b>Active tunnels:</b>\\n\\n${lines.join(\"\\n\\n\")}`,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n}\n\nexport function setupTunnelCallbacks(\n bot: import(\"grammy\").Bot,\n core: OpenACPCore,\n): void {\n bot.callbackQuery(/^tn:/, async (ctx) => {\n const data = ctx.callbackQuery.data;\n if (!core.tunnelService) {\n await ctx.answerCallbackQuery({ text: \"Tunnel not enabled\" });\n return;\n }\n\n try {\n if (data === \"tn:stop-all\") {\n const entries = core.tunnelService.listTunnels();\n for (const e of entries) {\n try { await core.tunnelService.stopTunnel(e.port); } catch { /* ignore system */ }\n }\n await ctx.answerCallbackQuery({ text: \"All tunnels stopped\" });\n await ctx.editMessageText(\"🔌 All tunnels stopped.\", { parse_mode: \"HTML\" });\n } else if (data.startsWith(\"tn:stop:\")) {\n const port = parseInt(data.replace(\"tn:stop:\", \"\"), 10);\n await core.tunnelService.stopTunnel(port);\n await ctx.answerCallbackQuery({ text: `Port ${port} stopped` });\n // Refresh list with keyboard\n const remaining = core.tunnelService.listTunnels();\n if (remaining.length === 0) {\n await ctx.editMessageText(\"🔌 All tunnels stopped.\", { parse_mode: \"HTML\" });\n } else {\n const kb = new InlineKeyboard();\n for (const e of remaining) {\n kb.text(`🔌 Stop ${e.port}${e.label ? ` (${e.label})` : \"\"}`, `tn:stop:${e.port}`).row();\n }\n if (remaining.length > 1) {\n kb.text(\"🔌 Stop all\", \"tn:stop-all\").row();\n }\n await ctx.editMessageText(\n `<b>Active tunnels:</b>\\n\\n` +\n remaining.map(e => {\n const status = e.status === \"active\" ? \"✅\" : \"⏳\";\n const label = e.label ? ` (${escapeHtml(e.label)})` : \"\";\n const url = e.publicUrl ? `\\n → <a href=\"${escapeHtml(e.publicUrl)}\">${escapeHtml(e.publicUrl)}</a>` : \"\";\n return `${status} Port <b>${e.port}</b>${label}${url}`;\n }).join(\"\\n\\n\"),\n { parse_mode: \"HTML\", reply_markup: kb },\n );\n }\n }\n } catch (err) {\n await ctx.answerCallbackQuery({ text: (err as Error).message });\n }\n });\n}\n","import type { Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { OpenACPCore } from \"../../../core/index.js\";\nimport { escapeHtml } from \"../formatting.js\";\nimport { createChildLogger } from \"../../../core/utils/log.js\";\n\nconst log = createChildLogger({ module: \"telegram-cmd-switch\" });\n\n/**\n * Handle `/switch [agent]` — switch the agent for the current session.\n *\n * - No args: show inline keyboard of available agents.\n * - With agent name: switch directly; if a prompt is in-flight, show a confirmation\n * prompt before interrupting it.\n * - `/switch label on|off`: toggle whether the agent name is prefixed in session history.\n */\nexport async function handleSwitch(\n ctx: Context,\n core: OpenACPCore,\n): Promise<void> {\n const threadId = ctx.message?.message_thread_id;\n if (!threadId) return;\n\n const session = await core.getOrResumeSession(\"telegram\", String(threadId));\n if (!session) {\n await ctx.reply(\"No active session in this topic.\");\n return;\n }\n\n const rawMatch = (ctx as Context & { match: unknown }).match;\n const raw = (typeof rawMatch === \"string\" ? rawMatch : \"\").trim();\n\n // /switch label on|off\n if (raw.startsWith(\"label \")) {\n const value = raw.slice(6).trim().toLowerCase();\n if (value === \"on\" || value === \"off\") {\n await core.configManager.save(\n { agentSwitch: { labelHistory: value === \"on\" } },\n \"agentSwitch.labelHistory\",\n );\n await ctx.reply(`Agent label in history: ${value}`);\n } else {\n await ctx.reply(\"Usage: /switch label on|off\");\n }\n return;\n }\n\n // /switch (no args) → show menu\n if (!raw) {\n const agents = core.agentManager.getAvailableAgents();\n const currentAgent = session.agentName;\n const options = agents.filter((a) => a.name !== currentAgent);\n\n if (options.length === 0) {\n await ctx.reply(\"No other agents available.\");\n return;\n }\n\n const keyboard = new InlineKeyboard();\n for (const agent of options) {\n keyboard.text(agent.name, `sw:${agent.name}`).row();\n }\n\n await ctx.reply(\n `<b>Switch Agent</b>\\nCurrent: <code>${escapeHtml(currentAgent)}</code>\\n\\nSelect an agent:`,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n return;\n }\n\n // /switch <agentName> → direct switch\n // Check if a prompt is currently being processed\n if (session.promptRunning) {\n const keyboard = new InlineKeyboard();\n keyboard.text(\"Yes, switch now\", `swc:${raw}`).text(\"Cancel\", \"swc:cancel\");\n await ctx.reply(\n `A prompt is currently running. Switching will interrupt it.\\n\\nSwitch to <b>${escapeHtml(raw)}</b> anyway?`,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n return;\n }\n\n await executeSwitchAgent(ctx, core, session.id, raw);\n}\n\n/**\n * Execute an agent switch for a session. Called by both the `/switch` command\n * handler and the `swc:` confirmation callback (when a prompt was in-flight).\n */\nexport async function executeSwitchAgent(\n ctx: Context,\n core: OpenACPCore,\n sessionId: string,\n agentName: string,\n): Promise<void> {\n try {\n const { resumed } = await core.switchSessionAgent(sessionId, agentName);\n const status = resumed ? \"resumed\" : \"new session\";\n await ctx.reply(\n `Switched to <b>${escapeHtml(agentName)}</b> (${status})`,\n { parse_mode: \"HTML\" },\n );\n log.info({ sessionId, agentName, resumed }, \"Agent switched via /switch\");\n } catch (err: any) {\n await ctx.reply(`Failed to switch agent: ${escapeHtml(String(err.message || err))}`);\n log.warn({ sessionId, agentName, err: err.message }, \"Agent switch failed\");\n }\n}\n\nexport function setupSwitchCallbacks(\n bot: import(\"grammy\").Bot,\n core: OpenACPCore,\n): void {\n bot.callbackQuery(/^sw:/, async (ctx) => {\n const agentName = ctx.callbackQuery.data!.replace(\"sw:\", \"\");\n await ctx.answerCallbackQuery();\n\n const threadId = ctx.callbackQuery.message?.message_thread_id;\n if (!threadId) return;\n\n const session = await core.getOrResumeSession(\"telegram\", String(threadId));\n if (!session) {\n await ctx.reply(\"No active session in this topic.\");\n return;\n }\n\n // Check if a prompt is currently being processed\n if (session.promptRunning) {\n const keyboard = new InlineKeyboard();\n keyboard.text(\"Yes, switch now\", `swc:${agentName}`).text(\"Cancel\", \"swc:cancel\");\n await ctx.reply(\n `A prompt is currently running. Switching will interrupt it.\\n\\nSwitch to <b>${escapeHtml(agentName)}</b> anyway?`,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n return;\n }\n\n await executeSwitchAgent(ctx, core, session.id, agentName);\n });\n\n // Handle switch confirmation callbacks (when prompt was in-flight)\n bot.callbackQuery(/^swc:/, async (ctx) => {\n const data = ctx.callbackQuery.data!.replace(\"swc:\", \"\");\n await ctx.answerCallbackQuery();\n\n if (data === \"cancel\") {\n await ctx.editMessageText(\"Switch cancelled.\");\n return;\n }\n\n const threadId = ctx.callbackQuery.message?.message_thread_id;\n if (!threadId) return;\n\n const session = await core.getOrResumeSession(\"telegram\", String(threadId));\n if (!session) {\n await ctx.reply(\"No active session in this topic.\");\n return;\n }\n\n // Cancel in-flight prompt before switching\n await session.abortPrompt();\n await executeSwitchAgent(ctx, core, session.id, data);\n });\n}\n","import type { Context } from 'grammy'\nimport type { OpenACPCore } from '../../../core/index.js'\nimport type { MenuRegistry } from '../../../core/menu-registry.js'\nimport { handleAgents } from './agents.js'\nimport { handleTopics } from './session.js'\nimport { handleDoctor } from './doctor.js'\nimport { handleUpdate, handleRestart } from './admin.js'\nimport { handleHelp, handleMenu } from './menu.js'\nimport { handleModel } from './model.js'\n\n/**\n * Commands that should be intercepted and handled by Telegram-specific\n * handlers instead of going through CommandRegistry core handlers.\n *\n * These handlers use grammY Context for rich UI (inline keyboards,\n * message editing, pagination) that CommandResponse cannot express.\n */\nexport const TELEGRAM_OVERRIDES: Record<\n string,\n (ctx: Context, core: OpenACPCore) => Promise<void>\n> = {\n agents: (ctx, core) => handleAgents(ctx, core),\n model: (ctx, core) => handleModel(ctx, core),\n sessions: (ctx, core) => handleTopics(ctx, core),\n doctor: (ctx, core) => handleDoctor(ctx, core),\n update: (ctx, core) => handleUpdate(ctx, core),\n restart: (ctx, core) => handleRestart(ctx, core),\n help: (ctx) => handleHelp(ctx),\n menu: (ctx, core) => {\n const menuRegistry = core.lifecycleManager?.serviceRegistry?.get('menu-registry') as MenuRegistry | undefined\n return handleMenu(ctx, menuRegistry)\n },\n}\n","import type { Bot, Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { OpenACPCore } from \"../../../core/index.js\";\nimport { escapeHtml } from \"../formatting.js\";\n\n/**\n * Handle `/integrate` — show a list of agents with available integrations.\n * Tapping an agent opens the integration items (install/uninstall hooks).\n */\nexport async function handleIntegrate(ctx: Context, _core: OpenACPCore): Promise<void> {\n const { listIntegrations } = await import(\"../../../cli/integrate.js\");\n const agents = listIntegrations();\n\n const keyboard = new InlineKeyboard();\n for (const agent of agents) {\n keyboard.text(`🤖 ${agent}`, `i:agent:${agent}`).row();\n }\n\n await ctx.reply(\n `<b>🔗 Integrations</b>\\n\\nSelect an agent to manage its integrations.`,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n}\n\nfunction buildAgentItemsKeyboard(agentName: string, items: import(\"../../../cli/integrate.js\").IntegrationItem[]): InlineKeyboard {\n const keyboard = new InlineKeyboard();\n for (const item of items) {\n const installed = item.isInstalled();\n keyboard.text(\n installed ? `✅ ${item.name} — Uninstall` : `📦 ${item.name} — Install`,\n installed ? `i:uninstall:${agentName}:${item.id}` : `i:install:${agentName}:${item.id}`,\n ).row();\n }\n keyboard.text(\"← Back\", \"i:back\").row();\n return keyboard;\n}\n\nexport function setupIntegrateCallbacks(\n bot: Bot,\n core: OpenACPCore,\n): void {\n bot.callbackQuery(/^i:/, async (ctx) => {\n const data = ctx.callbackQuery.data;\n try {\n await ctx.answerCallbackQuery();\n } catch {\n /* expired */\n }\n\n // Back to agent list\n if (data === \"i:back\") {\n const { listIntegrations } = await import(\"../../../cli/integrate.js\");\n const agents = listIntegrations();\n const keyboard = new InlineKeyboard();\n for (const agent of agents) {\n keyboard.text(`🤖 ${agent}`, `i:agent:${agent}`).row();\n }\n try {\n await ctx.editMessageText(\n `<b>🔗 Integrations</b>\\n\\nSelect an agent to manage its integrations.`,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n } catch { /* message unchanged */ }\n return;\n }\n\n // Show agent items\n const agentMatch = data.match(/^i:agent:(.+)$/);\n if (agentMatch) {\n const agentName = agentMatch[1];\n const { getIntegration } = await import(\"../../../cli/integrate.js\");\n const integration = getIntegration(agentName);\n if (!integration) {\n await ctx.reply(`❌ No integration available for '${escapeHtml(agentName)}'.`, { parse_mode: \"HTML\" });\n return;\n }\n const keyboard = buildAgentItemsKeyboard(agentName, integration.items);\n try {\n await ctx.editMessageText(\n `<b>🔗 ${escapeHtml(agentName)} Integrations</b>\\n\\n${integration.items.map((i) => `• <b>${escapeHtml(i.name)}</b> — ${escapeHtml(i.description)}`).join(\"\\n\")}`,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n } catch {\n await ctx.reply(\n `<b>🔗 ${escapeHtml(agentName)} Integrations</b>`,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n }\n return;\n }\n\n // Install / uninstall item\n const actionMatch = data.match(/^i:(install|uninstall):([^:]+):(.+)$/);\n if (!actionMatch) return;\n\n const action = actionMatch[1] as \"install\" | \"uninstall\";\n const agentName = actionMatch[2];\n const itemId = actionMatch[3];\n\n const { getIntegration } = await import(\"../../../cli/integrate.js\");\n const integration = getIntegration(agentName);\n if (!integration) return;\n\n const item = integration.items.find((i) => i.id === itemId);\n if (!item) return;\n\n const result = action === \"install\"\n ? await item.install()\n : await item.uninstall();\n\n // Save state to config\n const installed = action === \"install\" && result.success;\n await core.configManager.save({\n integrations: {\n [agentName]: {\n installed,\n installedAt: installed ? new Date().toISOString() : undefined,\n },\n },\n });\n\n const statusEmoji = result.success ? \"✅\" : \"❌\";\n const actionLabel = action === \"install\" ? \"installed\" : \"uninstalled\";\n const logsText = result.logs.map((l) => `<code>${escapeHtml(l)}</code>`).join(\"\\n\");\n const resultText = `${statusEmoji} <b>${escapeHtml(item.name)}</b> ${actionLabel}.\\n\\n${logsText}`;\n\n const keyboard = buildAgentItemsKeyboard(agentName, integration.items);\n try {\n await ctx.editMessageText(\n `<b>🔗 ${escapeHtml(agentName)} Integrations</b>\\n\\n${resultText}`,\n { parse_mode: \"HTML\", reply_markup: keyboard },\n );\n } catch {\n await ctx.reply(resultText, { parse_mode: \"HTML\" });\n }\n });\n}\n","/** Data needed to render the startup welcome message in the Assistant topic. */\nexport interface WelcomeContext {\n activeCount: number;\n errorCount: number;\n totalCount: number;\n agents: string[];\n defaultAgent: string;\n workspace: string;\n}\n\n/**\n * Build the welcome message sent to the Assistant topic on startup.\n * The message variant adapts based on whether there are active sessions or errors.\n */\nexport function buildWelcomeMessage(ctx: WelcomeContext): string {\n const { activeCount, errorCount, totalCount, agents, defaultAgent, workspace } = ctx;\n\n const agentList = agents\n .map((a) => `${a}${a === defaultAgent ? \" (default)\" : \"\"}`)\n .join(\", \");\n\n // Variant 1: No sessions\n if (totalCount === 0) {\n return (\n `👋 <b>OpenACP is ready!</b>\\n\\n` +\n `📂 ${workspace}\\n\\n` +\n `No sessions yet. Tap 🆕 New Session to start, or ask me anything!`\n );\n }\n\n // Variant 2: Has errors\n if (errorCount > 0) {\n return (\n `👋 <b>OpenACP is ready!</b>\\n\\n` +\n `📂 ${workspace}\\n` +\n `📊 ${activeCount} active, ${errorCount} errors / ${totalCount} total\\n` +\n `⚠️ ${errorCount} session${errorCount > 1 ? \"s have\" : \" has\"} errors — ask me to check if you'd like.\\n\\n` +\n `Agents: ${agentList}`\n );\n }\n\n // Variant 3/4: Has active or fallback\n return (\n `👋 <b>OpenACP is ready!</b>\\n\\n` +\n `📂 ${workspace}\\n` +\n `📊 ${activeCount} active / ${totalCount} total\\n` +\n `Agents: ${agentList}`\n );\n}\n\n/**\n * Build a redirect message pointing to the Assistant topic.\n * Sent when a user sends a plain message in the General topic (threadId = 0),\n * which is not a session topic and cannot receive session messages.\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","import type { Bot, Context } from \"grammy\";\nimport type { OpenACPCore } from \"../../../core/index.js\";\n\n// Domain modules\nimport { handleNew, handleNewChat, createSessionDirect, showAgentPicker, setupNewSessionCallbacks } from './new-session.js'\nimport { handleCancel, handleStatus, handleTopics, handleArchive, handleArchiveConfirm, setupSessionCallbacks } from './session.js'\nimport { handleUpdate, handleRestart, handleTTS, handleVerbosity, handleOutputMode } from './admin.js'\nimport { handleMenu, handleHelp, buildMenuKeyboard } from './menu.js'\nimport { handleAgents, handleInstall, handleAgentCallback } from \"./agents.js\";\nimport { handleIntegrate } from \"./integrate.js\";\nimport { showModelPage } from './model.js'\nimport {\n handleResume,\n setupResumeCallbacks,\n} from \"./resume.js\";\nimport { handleSettings, setupSettingsCallbacks } from \"./settings.js\";\nimport { handleDoctor, setupDoctorCallbacks } from \"./doctor.js\";\nimport { handleTunnel, handleTunnels, setupTunnelCallbacks } from \"./tunnel.js\";\nimport { handleSwitch, setupSwitchCallbacks } from \"./switch.js\";\nimport type { CommandRegistry } from \"../../../core/command-registry.js\";\nimport type { MenuRegistry } from \"../../../core/menu-registry.js\";\nimport { TELEGRAM_OVERRIDES } from './telegram-overrides.js'\nimport { createChildLogger } from '../../../core/utils/log.js'\nconst log = createChildLogger({ module: 'telegram-menu-callbacks' })\n\n/**\n * Register all callback query handlers for the Telegram bot.\n *\n * Handler registration order is significant in grammY: more specific prefix\n * handlers (ns:, ar:, ag:) must be registered before the broad `m:` dispatcher,\n * otherwise the broad handler would consume callbacks intended for specific flows.\n */\nexport function setupAllCallbacks(\n bot: Bot,\n core: OpenACPCore,\n chatId: number,\n systemTopicIds?: { notificationTopicId: number; assistantTopicId: number },\n getAssistantSession?: () =>\n | { topicId: number; enqueuePrompt: (p: string) => Promise<string> }\n | undefined,\n onControlMessage?: (sessionId: string, msgId: number) => void,\n): void {\n // Register specific prefix handlers FIRST (grammY middleware order matters)\n setupResumeCallbacks(bot, core, chatId, onControlMessage);\n setupSessionCallbacks(bot, core, chatId, systemTopicIds);\n\n // Settings handlers — must be before broad m: handler\n setupSettingsCallbacks(bot, core, getAssistantSession ?? (() => undefined));\n\n // Doctor handlers — must be before broad m: handler\n setupDoctorCallbacks(bot, core);\n\n // Tunnel callbacks — must be before broad m: handler\n setupTunnelCallbacks(bot, core);\n\n // Switch agent callbacks — must be before broad m: handler\n setupSwitchCallbacks(bot, core);\n\n // Agent callbacks (install + pagination) — must be before broad m: handler\n bot.callbackQuery(/^ag:/, (ctx) => handleAgentCallback(ctx, core));\n\n // Model pagination callbacks — must be before broad m: handler\n bot.callbackQuery(/^mod:/, async (ctx) => {\n const page = parseInt(ctx.callbackQuery.data.replace('mod:', ''), 10)\n try { await ctx.answerCallbackQuery() } catch { /* expired */ }\n await showModelPage(ctx, core, page, 'edit')\n })\n\n // New session with specific agent callback — must be before broad m: handler\n bot.callbackQuery(/^na:/, async (ctx) => {\n const agentKey = ctx.callbackQuery.data!.replace(\"na:\", \"\");\n await ctx.answerCallbackQuery();\n await createSessionDirect(\n ctx,\n core,\n chatId,\n agentKey,\n core.configManager.resolveWorkspace(),\n );\n });\n\n // New Session button flow — must be before broad m: handler\n setupNewSessionCallbacks(bot, core, chatId);\n\n // Archive confirmation callbacks\n bot.callbackQuery(/^ar:/, (ctx) => handleArchiveConfirm(ctx, core, chatId));\n\n // Broad m: handler for MenuRegistry dispatch — LAST\n bot.callbackQuery(/^m:/, async (ctx) => {\n const itemId = ctx.callbackQuery.data.replace('m:', '')\n try { await ctx.answerCallbackQuery() } catch { /* expired */ }\n\n const menuRegistry = core.lifecycleManager?.serviceRegistry?.get('menu-registry') as MenuRegistry | undefined\n if (!menuRegistry) return\n\n const item = menuRegistry.getItem(itemId)\n if (!item) {\n log.warn({ itemId }, 'Menu item not found in registry')\n return\n }\n\n const topicId = ctx.callbackQuery.message?.message_thread_id\n const registry = core.lifecycleManager?.serviceRegistry?.get('command-registry') as CommandRegistry | undefined\n\n switch (item.action.type) {\n case 'command': {\n // Check Telegram-specific override first\n const cmdName = item.action.command.replace(/^\\//, '').split(' ')[0]\n const telegramOverride = TELEGRAM_OVERRIDES[cmdName]\n if (telegramOverride) {\n try {\n await telegramOverride(ctx, core)\n } catch (err) {\n await ctx.reply(`⚠️ Command failed: ${String(err)}`).catch(() => {})\n }\n break\n }\n\n // Fallback: dispatch through CommandRegistry for commands without overrides\n if (!registry) return\n const response = await registry.execute(item.action.command, {\n raw: '',\n channelId: 'telegram',\n userId: String(ctx.from.id),\n sessionId: null,\n reply: async () => {},\n })\n if (response.type !== 'delegated' && response.type !== 'silent') {\n if (response.type === 'text') {\n await ctx.reply(response.text, { parse_mode: 'HTML' }).catch(() => {})\n } else if (response.type === 'error') {\n await ctx.reply(`⚠️ ${response.message}`).catch(() => {})\n } else if (response.type === 'list') {\n const lines = response.items.map((i: { label: string; detail?: string }) => `• ${i.label}${i.detail ? ` — ${i.detail}` : ''}`).join('\\n')\n await ctx.reply(`${response.title}\\n${lines}`, { parse_mode: 'HTML' }).catch(() => {})\n } else if (response.type === 'menu') {\n const { InlineKeyboard } = await import('grammy')\n const kb = new InlineKeyboard()\n for (const opt of response.options) {\n kb.text(opt.label, `c/${opt.command}`).row()\n }\n await ctx.reply(response.title, { parse_mode: 'HTML', reply_markup: kb }).catch(() => {})\n } else if (response.type === 'confirm') {\n const { InlineKeyboard } = await import('grammy')\n const kb = new InlineKeyboard()\n .text('✅ Yes', `c/${response.onYes}`)\n .text('❌ No', `c/${response.onNo}`)\n await ctx.reply(response.question, { reply_markup: kb }).catch(() => {})\n }\n }\n break\n }\n case 'delegate': {\n // Telegram-specific override: New Session uses button flow instead of AI\n if (itemId === 'core:new') {\n await showAgentPicker(ctx, core, chatId)\n break\n }\n\n const assistant = core.assistantManager?.get('telegram')\n if (assistant) {\n if (topicId && systemTopicIds && topicId !== systemTopicIds.assistantTopicId) {\n const { redirectToAssistant } = await import('../assistant.js')\n await ctx.reply(redirectToAssistant(chatId, systemTopicIds.assistantTopicId), { parse_mode: 'HTML' }).catch(() => {})\n } else {\n await assistant.enqueuePrompt(item.action.prompt)\n }\n } else {\n await ctx.reply('⚠️ Assistant is not available.').catch(() => {})\n }\n break\n }\n case 'callback': {\n const cbData = item.action.callbackData\n if (cbData === 's:settings') {\n await handleSettings(ctx, core)\n }\n break\n }\n }\n })\n}\n\n// Backward compat alias\nexport { setupAllCallbacks as setupMenuCallbacks };\n\n// Re-exports for external consumers (adapter.ts)\nexport { buildMenuKeyboard } from \"./menu.js\";\nexport { buildSkillMessages } from \"./menu.js\";\nexport { TELEGRAM_OVERRIDES } from './telegram-overrides.js'\nexport {\n executeNewSession,\n} from \"./new-session.js\";\nexport { executeCancelSession } from \"./session.js\";\nexport {\n setupDangerousModeCallbacks,\n buildDangerousModeKeyboard,\n} from \"./admin.js\";\nexport {\n setupTTSCallbacks,\n setupVerbosityCallbacks,\n buildTTSKeyboard,\n buildSessionControlKeyboard,\n handleTTS,\n handleVerbosity,\n handleOutputMode,\n} from \"./admin.js\";\nexport { setupIntegrateCallbacks } from \"./integrate.js\";\nexport { setupSettingsCallbacks } from \"./settings.js\";\nexport { setupDoctorCallbacks } from \"./doctor.js\";\nexport { setupResumeCallbacks } from \"./resume.js\";\n\nexport const STATIC_COMMANDS = [\n { command: \"new\", description: \"Create new session\" },\n { command: \"newchat\", description: \"New chat, same agent & workspace\" },\n { command: \"cancel\", description: \"Cancel current session\" },\n { command: \"status\", description: \"Show status\" },\n { command: \"sessions\", description: \"List all sessions\" },\n { command: \"agents\", description: \"List available agents\" },\n { command: \"install\", description: \"Install a new agent\" },\n { command: \"help\", description: \"Help\" },\n { command: \"menu\", description: \"Show menu\" },\n { command: \"integrate\", description: \"Manage agent integrations\" },\n { command: \"handoff\", description: \"Continue this session in your terminal\" },\n { command: \"restart\", description: \"Restart OpenACP\" },\n { command: \"update\", description: \"Update to latest version and restart\" },\n { command: \"doctor\", description: \"Run system diagnostics\" },\n { command: \"retry\", description: \"Re-check setup prerequisites (use if bot is stuck on startup)\" },\n { command: \"tunnel\", description: \"Create/stop tunnel for a local port\" },\n { command: \"tunnels\", description: \"List active tunnels\" },\n { command: 'archive', description: 'Archive session topic (recreate with clean history)' },\n { command: 'text_to_speech', description: 'Toggle Text to Speech (/text_to_speech on, /text_to_speech off)' },\n { command: 'verbosity', description: 'Deprecated: use /outputmode instead' },\n { command: \"outputmode\", description: \"Control output display level (low/medium/high)\" },\n { command: 'resume', description: 'Resume with conversation history from Entire checkpoints' },\n { command: 'switch', description: 'Switch agent in current session' },\n { command: 'mode', description: 'Change session mode' },\n { command: 'model', description: 'Change AI model' },\n { command: 'thought', description: 'Change thinking level' },\n { command: 'bypass_permissions', description: 'Toggle bypass permissions (on/off)' },\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/utils/log.js'\nconst log = createChildLogger({ module: 'telegram-permissions' })\n\n// In-memory map from short callback key → pending permission data.\n// Keys are 8-character nanoid strings to stay within Telegram's 64-byte callback_data limit.\ninterface PendingPermission {\n sessionId: string\n requestId: string\n options: { id: string; isAllow: boolean }[]\n}\n\n/**\n * Renders PermissionRequest objects as Telegram inline keyboard buttons and\n * routes the user's approval/denial back to the PermissionGate.\n *\n * Each request is assigned a short callback key (`p:<key>:<optionId>`) to avoid\n * Telegram's 64-byte limit on callback_data. The resolved option is passed to\n * `session.permissionGate.resolve()` to unblock the waiting agent prompt.\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 /**\n * Send a permission request to the session's topic as an inline keyboard message,\n * and fire a notification to the Notifications topic so the user is alerted.\n *\n * Each button encodes `p:<callbackKey>:<optionId>`. The callbackKey is a short\n * nanoid that maps to the full pending state stored in-memory, avoiding the\n * 64-byte Telegram callback_data limit.\n */\n async sendPermissionRequest(session: Session, request: PermissionRequest): Promise<void> {\n const threadId = Number(session.threadId)\n\n // nanoid(8) = 8-char key; well within the 64-byte callback_data budget\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 — include threadId so link navigates into the topic\n const deepLink = buildDeepLink(this.chatId, threadId, msg.message_id)\n\n // Notify in notification topic (fire-and-forget to avoid sendQueue deadlock:\n // this method runs INSIDE a sendQueue item, so awaiting another enqueue() deadlocks)\n void this.sendNotification({\n sessionId: session.id,\n sessionName: session.name,\n type: 'permission',\n summary: request.description,\n deepLink,\n })\n }\n\n /**\n * Register the `p:` callback handler in the bot's middleware chain.\n *\n * Must be called during setup so grammY processes permission responses\n * before other generic callback handlers.\n */\n setupCallbackHandler(): void {\n this.bot.on('callback_query:data', async (ctx, next) => {\n const data = ctx.callbackQuery.data\n // Only handle permission callbacks; pass everything else to the next handler\n if (!data.startsWith('p:')) return next()\n\n const parts = data.split(':')\n if (parts.length < 3) return next()\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?.permissionGate.requestId === pending.requestId) {\n session.permissionGate.resolve(optionId)\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 { Bot } from \"grammy\";\nimport { createChildLogger } from \"../../core/utils/log.js\";\nimport type { PlanEntry } from \"../../core/types.js\";\nimport type { ToolCallMeta, ViewerLinks } from \"../../core/adapter-primitives/format-types.js\";\nimport type { SendQueue } from \"../../core/adapter-primitives/primitives/send-queue.js\";\nimport type { DebugTracer } from \"../../core/utils/debug-tracer.js\";\nimport { ToolCardState } from \"../../core/adapter-primitives/primitives/tool-card-state.js\";\nimport type { ToolCardSnapshot } from \"../../core/adapter-primitives/primitives/tool-card-state.js\";\nimport { escapeHtml, renderToolCard, splitToolCardText } from \"./formatting.js\";\nimport { ToolStateMap } from \"../../core/adapter-primitives/stream-accumulator.js\";\nimport { ThoughtBuffer } from \"../../core/adapter-primitives/stream-accumulator.js\";\nimport { DisplaySpecBuilder } from \"../../core/adapter-primitives/display-spec-builder.js\";\nimport type { ToolDisplaySpec } from \"../../core/adapter-primitives/display-spec-builder.js\";\nimport type { OutputMode } from \"../../core/adapter-primitives/format-types.js\";\nimport type { TunnelServiceInterface } from \"../../core/plugin/types.js\";\n\nconst log = createChildLogger({ module: \"telegram:activity\" });\n\n// ─── ThinkingIndicator ────────────────────────────────────────────────────────\n\n// Refresh the \"thinking\" message every 15s to show elapsed time.\n// Auto-stop after 3 minutes to avoid leaving stale indicators.\nconst THINKING_REFRESH_MS = 15_000;\nconst THINKING_MAX_MS = 3 * 60 * 1000;\n\n/**\n * Manages the transient \"💭 Thinking...\" message shown while the agent is reasoning.\n *\n * Sends a message on `show()`, periodically edits it with elapsed time, and\n * dismisses it (leaving the message in chat) when text output begins. Leaving\n * the message avoids an extra API delete call; the indicator is just no longer updated.\n */\nexport class ThinkingIndicator {\n private msgId?: number;\n private sending = false;\n private dismissed = false;\n private refreshTimer?: ReturnType<typeof setInterval>;\n private showTime = 0;\n private tracer: DebugTracer | null;\n\n constructor(\n private api: Bot[\"api\"],\n private chatId: number,\n private threadId: number,\n private sendQueue: SendQueue,\n private sessionId: string = \"\",\n tracer: DebugTracer | null = null,\n ) {\n this.tracer = tracer;\n }\n\n async show(): Promise<void> {\n if (this.sending || this.dismissed) return;\n if (this.msgId) return;\n this.sending = true;\n this.showTime = Date.now();\n const text = \"💭 <i>Thinking...</i>\";\n try {\n const result = await this.sendQueue.enqueue(() =>\n this.api.sendMessage(this.chatId, text, {\n message_thread_id: this.threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n }),\n );\n if (result) {\n this.tracer?.log(\"telegram\", { action: \"thinking:show\", sessionId: this.sessionId, msgId: result.message_id });\n if (this.dismissed) {\n // dismissed during queue wait — message stays in chat (no delete to save API calls)\n } else {\n this.msgId = result.message_id;\n this.startRefreshTimer();\n }\n }\n } catch (err) {\n log.warn({ err }, \"ThinkingIndicator.show() failed\");\n } finally {\n this.sending = false;\n }\n }\n\n /** Edit the indicator message to append a viewer link, then dismiss. */\n async finalizeWithViewerLink(url: string): Promise<void> {\n this.stopRefreshTimer();\n if (this.msgId && !this.dismissed) {\n const text = `💭 <i>Thinking...</i> <a href=\"${escapeHtml(url)}\">View thinking</a>`;\n await this.sendQueue\n .enqueue(() => {\n if (!this.msgId) return Promise.resolve(undefined);\n return this.api.editMessageText(this.chatId, this.msgId, text, { parse_mode: \"HTML\" });\n })\n .catch(() => {});\n }\n this.dismissed = true;\n this.msgId = undefined;\n }\n\n /** Dismiss indicator: stops refresh timer. Message is left in chat to reduce API calls. */\n async dismiss(): Promise<void> {\n if (this.dismissed) return;\n this.dismissed = true;\n this.tracer?.log(\"telegram\", { action: \"thinking:dismiss\", sessionId: this.sessionId });\n this.stopRefreshTimer();\n this.msgId = undefined;\n }\n\n /** Reset for a new prompt cycle */\n reset(): void {\n this.dismissed = false;\n }\n\n private startRefreshTimer(): void {\n this.stopRefreshTimer();\n this.refreshTimer = setInterval(() => {\n if (\n this.dismissed ||\n !this.msgId ||\n Date.now() - this.showTime >= THINKING_MAX_MS\n ) {\n this.stopRefreshTimer();\n return;\n }\n const elapsed = Math.round((Date.now() - this.showTime) / 1000);\n const refreshText = `💭 <i>Still thinking... (${elapsed}s)</i>`;\n this.sendQueue\n .enqueue(() => {\n // Re-check after waiting in queue — dismiss may have been called\n if (this.dismissed || !this.msgId) return Promise.resolve(undefined);\n return this.api.editMessageText(\n this.chatId,\n this.msgId,\n refreshText,\n { parse_mode: \"HTML\" },\n );\n })\n .then(() => {})\n .catch(() => {});\n }, THINKING_REFRESH_MS);\n }\n\n private stopRefreshTimer(): void {\n if (this.refreshTimer) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = undefined;\n }\n }\n}\n\n// ─── ToolCard ─────────────────────────────────────────────────────────────────\n\n/**\n * Displays the live tool execution card for a prompt cycle.\n *\n * Receives tool state updates via `ToolCardState` (which batches rapid updates\n * and calls `onFlush` with a snapshot). Each flush either sends a new message or\n * edits the existing one. When the card exceeds Telegram's 4096-char limit, it is\n * split into overflow messages that are recycled on subsequent renders.\n */\nexport class ToolCard {\n private state: ToolCardState;\n private msgId?: number;\n private lastSentText?: string;\n private flushPromise: Promise<void> = Promise.resolve();\n private overflowMsgIds: number[] = [];\n private aborted = false;\n private tracer: DebugTracer | null;\n private sessionId: string;\n\n constructor(\n private api: Bot[\"api\"],\n private chatId: number,\n private threadId: number,\n private sendQueue: SendQueue,\n sessionId: string = \"\",\n tracer: DebugTracer | null = null,\n ) {\n this.tracer = tracer;\n this.sessionId = sessionId;\n this.state = new ToolCardState({\n onFlush: (snapshot) => {\n this.flushPromise = this.flushPromise\n .then(() => {\n if (this.aborted) return;\n return this._sendOrEdit(snapshot);\n })\n .catch(() => {});\n },\n });\n }\n\n updateFromSpec(spec: ToolDisplaySpec): void {\n this.state.updateFromSpec(spec);\n }\n\n updatePlan(entries: PlanEntry[]): void {\n this.state.updatePlan(entries);\n }\n\n appendUsage(usage: { tokensUsed?: number; contextSize?: number; cost?: number }): void {\n this.state.appendUsage(usage);\n }\n\n async finalize(): Promise<void> {\n this.state.finalize();\n await this.flushPromise;\n }\n\n destroy(): void {\n this.aborted = true;\n this.state.destroy();\n }\n\n hasContent(): boolean {\n return this.state.hasContent();\n }\n\n getMsgId(): number | undefined {\n return this.msgId;\n }\n\n private async _sendOrEdit(snapshot: ToolCardSnapshot): Promise<void> {\n // Overflow strip: if full render exceeds Telegram limit, strip inline outputContent\n let snapshotToRender = snapshot;\n let fullText = renderToolCard(snapshotToRender);\n if (fullText.length > 4096) {\n snapshotToRender = {\n ...snapshot,\n specs: snapshot.specs.map((s) =>\n s.outputContent ? { ...s, outputContent: null } : s\n ),\n };\n fullText = renderToolCard(snapshotToRender);\n }\n\n if (!fullText) return;\n if (this.msgId && fullText === this.lastSentText) return;\n this.lastSentText = fullText;\n\n const chunks = splitToolCardText(fullText);\n this.tracer?.log(\"telegram\", { action: \"toolCard:render\", sessionId: this.sessionId, chunks: chunks.length, total: snapshot.totalVisible, completed: snapshot.completedVisible, allComplete: snapshot.allComplete, msgId: this.msgId, entries: snapshot.specs.map(s => ({ id: s.id, kind: s.kind, icon: s.icon, title: s.title })), html: fullText });\n\n try {\n const firstChunk = chunks[0];\n if (this.msgId) {\n await this.sendQueue.enqueue(() =>\n this.api.editMessageText(this.chatId, this.msgId!, firstChunk, { parse_mode: \"HTML\" }),\n );\n this.tracer?.log(\"telegram\", { action: \"telegram:edit\", sessionId: this.sessionId, msgId: this.msgId, html: firstChunk });\n } else {\n const result = await this.sendQueue.enqueue(() =>\n this.api.sendMessage(this.chatId, firstChunk, {\n message_thread_id: this.threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n }),\n );\n if (result) this.msgId = result.message_id;\n this.tracer?.log(\"telegram\", { action: \"telegram:send\", sessionId: this.sessionId, msgId: result?.message_id, html: firstChunk });\n }\n\n for (let i = 1; i < chunks.length; i++) {\n const overflowIdx = i - 1;\n if (overflowIdx < this.overflowMsgIds.length) {\n await this.sendQueue.enqueue(() =>\n this.api.editMessageText(this.chatId, this.overflowMsgIds[overflowIdx], chunks[i], { parse_mode: \"HTML\" }),\n );\n this.tracer?.log(\"telegram\", { action: \"telegram:edit:overflow\", sessionId: this.sessionId, msgId: this.overflowMsgIds[overflowIdx] });\n } else {\n const result = await this.sendQueue.enqueue(() =>\n this.api.sendMessage(this.chatId, chunks[i], {\n message_thread_id: this.threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n }),\n );\n if (result) this.overflowMsgIds.push(result.message_id);\n this.tracer?.log(\"telegram\", { action: \"telegram:send:overflow\", sessionId: this.sessionId, msgId: result?.message_id });\n }\n }\n\n // Clean up stale overflow messages when chunk count decreases\n const neededOverflow = chunks.length - 1;\n while (this.overflowMsgIds.length > neededOverflow) {\n const staleId = this.overflowMsgIds.pop()!;\n await this.sendQueue.enqueue(() =>\n this.api.deleteMessage(this.chatId, staleId).catch(() => {}),\n );\n this.tracer?.log(\"telegram\", { action: \"telegram:delete:overflow\", sessionId: this.sessionId, msgId: staleId });\n }\n } catch (err) {\n log.warn({ err }, \"[ToolCard] send/edit failed\");\n }\n }\n}\n\n// ─── ActivityTracker ──────────────────────────────────────────────────────────\n\n/**\n * Per-session coordinator for all live activity indicators in a topic.\n *\n * Owns the ThinkingIndicator and the current ToolCard for a session. On each\n * new prompt cycle (`onNewPrompt`), the previous card is finalized (frozen) and\n * a fresh card and thinking indicator are created for the new turn.\n *\n * The \"seal\" mechanism (`sealToolCardIfNeeded`) finalizes the current tool card\n * when text output begins, preserving tool results as a historical record while\n * the new streaming draft message appears below them.\n */\nexport class ActivityTracker {\n private isFirstEvent = true;\n private thinking: ThinkingIndicator;\n private toolCard: ToolCard;\n private previousToolCard?: ToolCard;\n private toolStateMap: ToolStateMap;\n private previousToolStateMap?: ToolStateMap;\n private specBuilder: DisplaySpecBuilder;\n private thoughtBuffer: ThoughtBuffer;\n private _outputMode: OutputMode;\n private tracer: DebugTracer | null;\n private sessionId: string;\n private sessionContext?: { id: string; workingDirectory: string };\n private tunnelService?: TunnelServiceInterface;\n\n constructor(\n private api: Bot[\"api\"],\n private chatId: number,\n private threadId: number,\n private sendQueue: SendQueue,\n outputMode: OutputMode = \"medium\",\n sessionId: string = \"\",\n tracer: DebugTracer | null = null,\n tunnelService?: TunnelServiceInterface,\n sessionContext?: { id: string; workingDirectory: string },\n ) {\n this._outputMode = outputMode;\n this.tracer = tracer;\n this.sessionId = sessionId;\n this.sessionContext = sessionContext;\n this.tunnelService = tunnelService;\n this.specBuilder = new DisplaySpecBuilder(tunnelService);\n this.toolStateMap = new ToolStateMap();\n this.thoughtBuffer = new ThoughtBuffer();\n this.thinking = new ThinkingIndicator(api, chatId, threadId, sendQueue, sessionId, tracer);\n this.toolCard = new ToolCard(api, chatId, threadId, sendQueue, sessionId, tracer);\n }\n\n setOutputMode(mode: OutputMode): void {\n this._outputMode = mode;\n }\n\n async onNewPrompt(): Promise<void> {\n this.tracer?.log(\"telegram\", { action: \"tracker:newPrompt\", sessionId: this.sessionId });\n this.isFirstEvent = true;\n this.thoughtBuffer.reset();\n await this.thinking.dismiss();\n this.thinking.reset();\n await this.toolCard.finalize();\n // Clear previous card references so tool updates from new prompt don't leak into old messages\n this.previousToolCard = undefined;\n this.previousToolStateMap = undefined;\n this.toolStateMap.clear();\n this.toolCard = new ToolCard(this.api, this.chatId, this.threadId, this.sendQueue, this.sessionId, this.tracer);\n }\n\n async onThought(text: string): Promise<void> {\n this.tracer?.log(\"telegram\", { action: \"tracker:thought\", sessionId: this.sessionId });\n this.isFirstEvent = false;\n if (!this.thoughtBuffer.isSealed()) this.thoughtBuffer.append(text);\n await this.sealToolCardIfNeeded();\n await this.thinking.show();\n }\n\n async onTextStart(): Promise<void> {\n this.tracer?.log(\"telegram\", { action: \"tracker:textStart\", sessionId: this.sessionId });\n this.isFirstEvent = false;\n this.thoughtBuffer.seal();\n\n // In high mode with tunnel: store thought content and show a viewer link before dismissing\n if (this._outputMode === \"high\" && this.tunnelService && this.sessionContext) {\n const thoughtText = this.thoughtBuffer.getText();\n if (thoughtText.trim().length > 0) {\n const id = this.tunnelService.getStore().storeOutput(\n this.sessionContext.id,\n \"thinking\",\n thoughtText,\n );\n if (id !== null) {\n await this.thinking.finalizeWithViewerLink(this.tunnelService.outputUrl(id));\n } else {\n await this.thinking.dismiss();\n }\n } else {\n await this.thinking.dismiss();\n }\n } else {\n await this.thinking.dismiss();\n }\n\n await this.sealToolCardIfNeeded();\n }\n\n async onToolCall(\n meta: ToolCallMeta,\n kind: string,\n rawInput: unknown,\n ): Promise<void> {\n this.tracer?.log(\"telegram\", { action: \"tracker:toolCall\", sessionId: this.sessionId, meta, kind, rawInput });\n this.isFirstEvent = false;\n await this.thinking.dismiss();\n this.thinking.reset();\n\n const entry = this.toolStateMap.upsert(meta, kind, rawInput);\n const spec = this.specBuilder.buildToolSpec(entry, this._outputMode, this.sessionContext);\n this.toolCard.updateFromSpec(spec);\n }\n\n async onToolUpdate(\n id: string,\n status: string,\n viewerLinks?: ViewerLinks,\n viewerFilePath?: string,\n content?: string | null,\n rawInput?: unknown,\n diffStats?: { added: number; removed: number },\n ): Promise<void> {\n this.tracer?.log(\"telegram\", { action: \"tracker:toolUpdate\", sessionId: this.sessionId, toolId: id, status, viewerLinks, viewerFilePath, hasPrevCard: !!this.previousToolCard });\n\n // Forward to previous card first if the tool originated there (out-of-order update after seal)\n if (this.previousToolStateMap?.get(id)) {\n this.previousToolStateMap.merge(id, status, rawInput, content, viewerLinks, diffStats);\n const prevEntry = this.previousToolStateMap.get(id);\n if (prevEntry) {\n const prevSpec = this.specBuilder.buildToolSpec(prevEntry, this._outputMode, this.sessionContext);\n this.previousToolCard?.updateFromSpec(prevSpec);\n }\n }\n\n const existed = !!this.toolStateMap.get(id);\n const entry = this.toolStateMap.merge(id, status, rawInput, content, viewerLinks, diffStats);\n // Skip spec build for out-of-order updates — buffered in pendingUpdates\n if (!existed || !entry) return;\n\n if (viewerLinks || entry.viewerLinks) {\n log.debug({ toolId: id, status, hasIncomingLinks: !!viewerLinks, hasEntryLinks: !!entry.viewerLinks, entryLinks: entry.viewerLinks }, \"toolUpdate: viewer links trace\");\n }\n const spec = this.specBuilder.buildToolSpec(entry, this._outputMode, this.sessionContext);\n this.toolCard.updateFromSpec(spec);\n }\n\n async onPlan(entries: PlanEntry[]): Promise<void> {\n this.tracer?.log(\"telegram\", { action: \"tracker:plan\", sessionId: this.sessionId, entries });\n this.isFirstEvent = false;\n await this.thinking.dismiss();\n this.toolCard.updatePlan(entries);\n }\n\n /** @deprecated Usage is now sent as a separate message by the adapter */\n async sendUsage(_usage: {\n tokensUsed?: number;\n contextSize?: number;\n cost?: number;\n }): Promise<void> {\n // no-op — adapter sends usage as a standalone message\n }\n\n getToolCardMsgId(): number | undefined {\n return this.toolCard.getMsgId();\n }\n\n async cleanup(): Promise<void> {\n await this.thinking.dismiss();\n await this.toolCard.finalize();\n this.toolCard.destroy();\n }\n\n destroy(): void {\n void this.thinking.dismiss();\n this.toolCard.destroy();\n }\n\n private async sealToolCardIfNeeded(): Promise<void> {\n if (!this.toolCard.hasContent()) return;\n this.tracer?.log(\"telegram\", { action: \"tracker:seal\", sessionId: this.sessionId });\n await this.toolCard.finalize();\n this.previousToolCard = this.toolCard;\n this.previousToolStateMap = this.toolStateMap;\n this.toolStateMap = new ToolStateMap();\n this.toolCard = new ToolCard(this.api, this.chatId, this.threadId, this.sendQueue, this.sessionId, this.tracer);\n }\n}\n","import type { Bot } from 'grammy'\nimport { markdownToTelegramHtml, splitMessage } from './formatting.js'\nimport type { SendQueue } from '../../core/adapter-primitives/primitives/send-queue.js'\nimport type { DebugTracer } from '../../core/utils/debug-tracer.js'\n\n// Throttle interval for streaming edits: emit at most one edit every 5 seconds while\n// text chunks are arriving. This avoids hitting Telegram's per-message rate limit\n// (~1 edit/s) while still showing live progress for long-running agents.\nconst FLUSH_INTERVAL = 5000\n\n/**\n * In-flight streaming message for a single agent response.\n *\n * Text chunks arrive via `append()`. A debounced timer fires `flush()` every\n * FLUSH_INTERVAL ms, sending the buffered content as either a new message (first\n * flush) or an edit of the existing message. `finalize()` is called on\n * `text_done` to push the complete, untruncated content.\n *\n * The edit-in-place approach keeps each agent turn as a single Telegram message\n * rather than flooding the topic with many small messages.\n */\nexport class MessageDraft {\n private buffer: string = ''\n private messageId?: number\n private firstFlushPending = false\n private flushTimer?: ReturnType<typeof setTimeout>\n private flushPromise: Promise<void> = Promise.resolve()\n private lastSentBuffer: string = ''\n private lastSentHtml: string = ''\n private displayTruncated = false\n private tracer: DebugTracer | null\n\n constructor(\n private bot: Bot,\n private chatId: number,\n private threadId: number,\n private sendQueue: SendQueue,\n private sessionId: string,\n tracer: DebugTracer | null = null,\n ) {\n this.tracer = tracer\n }\n\n /** Append a text chunk to the buffer and schedule a throttled flush. */\n append(text: string): void {\n if (!text) return\n this.buffer += text\n this.scheduleFlush()\n }\n\n private scheduleFlush(): void {\n // One timer at a time — let the current interval elapse before re-arming\n if (this.flushTimer) return\n this.flushTimer = setTimeout(() => {\n this.flushTimer = undefined\n this.flushPromise = this.flushPromise\n .then(() => this.flush())\n .catch(() => {})\n }, FLUSH_INTERVAL)\n }\n\n private async flush(): Promise<void> {\n if (!this.buffer) return\n if (this.firstFlushPending) return\n\n // CRITICAL: Snapshot the buffer BEFORE any await.\n // append() can be called synchronously while we're awaiting sendQueue,\n // so this.buffer may change. We must track what was ACTUALLY sent.\n const snapshot = this.buffer\n\n let html = markdownToTelegramHtml(snapshot)\n if (!html) return\n let truncated = false\n if (html.length > 4096) {\n // Estimate markdown cut point proportionally, then find a line boundary\n const ratio = 4000 / html.length\n const targetLen = Math.floor(snapshot.length * ratio)\n let cutAt = snapshot.lastIndexOf('\\n', targetLen)\n if (cutAt < targetLen * 0.5) cutAt = targetLen\n html = markdownToTelegramHtml(snapshot.slice(0, cutAt) + '\\n…')\n truncated = true\n if (html.length > 4096) {\n html = html.slice(0, 4090) + '\\n…'\n }\n }\n\n if (!this.messageId) {\n this.firstFlushPending = true\n try {\n const result = await this.sendQueue.enqueue(\n () => this.bot.api.sendMessage(this.chatId, html, {\n message_thread_id: this.threadId,\n parse_mode: 'HTML',\n disable_notification: true,\n }),\n { type: 'other' },\n )\n if (result) {\n this.messageId = result.message_id\n this.tracer?.log(\"telegram\", { action: \"draft:send\", sessionId: this.sessionId, msgId: result.message_id, textLen: snapshot.length, truncated, text: snapshot })\n if (!truncated) {\n this.lastSentBuffer = snapshot\n this.displayTruncated = false\n } else {\n this.displayTruncated = true\n }\n }\n } catch {\n // sendMessage failed — next flush will retry\n } finally {\n this.firstFlushPending = false\n }\n } else {\n // Skip if the rendered HTML is identical to what was last sent/enqueued\n if (html === this.lastSentHtml) return\n this.lastSentHtml = html\n\n try {\n const result = await this.sendQueue.enqueue(\n () => this.bot.api.editMessageText(this.chatId, this.messageId!, html, {\n parse_mode: 'HTML',\n }),\n { type: 'text', key: this.sessionId },\n )\n // Only mark as sent if the edit was actually executed (not dropped by dedup/rate-limit)\n if (result !== undefined) {\n this.tracer?.log(\"telegram\", { action: \"draft:edit\", sessionId: this.sessionId, msgId: this.messageId, textLen: snapshot.length, truncated, text: snapshot })\n if (!truncated) {\n this.lastSentBuffer = snapshot\n this.displayTruncated = false\n } else {\n this.displayTruncated = true\n }\n }\n } catch {\n // Don't reset messageId — transient errors (rate limit, network) would cause\n // the next flush to sendMessage the full buffer as a NEW message, creating duplicates.\n // If the message was truly deleted, finalize() handles the fallback.\n }\n }\n }\n\n /**\n * Flush the complete buffer as the final message for this turn.\n *\n * Cancels any pending debounce timer, waits for in-flight flushes to settle,\n * then sends the full content. If the HTML exceeds 4096 bytes, the content is\n * split at markdown boundaries to avoid breaking HTML tags mid-tag.\n *\n * Returns the Telegram message ID of the sent/edited message, or undefined\n * if nothing was sent (empty buffer or all network calls failed).\n */\n async finalize(): Promise<number | undefined> {\n this.tracer?.log(\"telegram\", { action: \"draft:finalize\", sessionId: this.sessionId, bufferLen: this.buffer.length, msgId: this.messageId })\n if (this.flushTimer) {\n clearTimeout(this.flushTimer)\n this.flushTimer = undefined\n }\n\n await this.flushPromise\n\n if (!this.buffer) return this.messageId\n\n // Skip if buffer was already fully sent by flush() and nothing new was appended.\n // Do NOT skip if flush() truncated the display — finalize must send the full content.\n if (this.messageId && this.buffer === this.lastSentBuffer && !this.displayTruncated) {\n return this.messageId\n }\n\n // Try sending full buffer as a single message (most common case).\n // Only split if HTML exceeds Telegram's 4096 char limit.\n const fullHtml = markdownToTelegramHtml(this.buffer)\n if (fullHtml.length <= 4096) {\n // Single enqueue — no ordering issue possible\n try {\n if (this.messageId) {\n await this.sendQueue.enqueue(\n () => this.bot.api.editMessageText(this.chatId, this.messageId!, fullHtml, {\n parse_mode: 'HTML',\n }),\n { type: 'other' },\n )\n this.tracer?.log(\"telegram\", { action: \"draft:finalize:edit\", sessionId: this.sessionId, msgId: this.messageId })\n } else {\n const msg = await this.sendQueue.enqueue(\n () => this.bot.api.sendMessage(this.chatId, fullHtml, {\n message_thread_id: this.threadId,\n parse_mode: 'HTML',\n disable_notification: true,\n }),\n { type: 'other' },\n )\n if (msg) this.messageId = msg.message_id\n this.tracer?.log(\"telegram\", { action: \"draft:finalize:send\", sessionId: this.sessionId, msgId: msg?.message_id })\n }\n return this.messageId\n } catch {\n // HTML send failed — fall through to split/fallback below\n }\n }\n\n // HTML > 4096 or single send failed — split markdown, convert each chunk separately.\n // This prevents breaking HTML tags (e.g. <pre><code>) at split boundaries.\n //\n // CRITICAL: Enqueue ALL chunks in a tight synchronous loop (no await between\n // enqueues). This prevents concurrent event handlers (usage, session_end) from\n // slipping their messages between our chunks in the sendQueue.\n const mdChunks = splitMessage(this.buffer)\n const chunkPromises: Promise<void>[] = []\n\n for (let i = 0; i < mdChunks.length; i++) {\n const html = markdownToTelegramHtml(mdChunks[i])\n const isEdit = i === 0 && !!this.messageId\n const chunkMd = mdChunks[i]\n\n const fn = isEdit\n ? () => this.bot.api.editMessageText(this.chatId, this.messageId!, html, { parse_mode: 'HTML' }) as Promise<unknown>\n : () => this.bot.api.sendMessage(this.chatId, html, {\n message_thread_id: this.threadId,\n parse_mode: 'HTML',\n disable_notification: true,\n })\n const promise = this.sendQueue.enqueue(fn, { type: 'other' })\n .then((result) => {\n if (!isEdit && result && typeof result === 'object' && 'message_id' in (result as Record<string, unknown>)) {\n this.messageId = (result as { message_id: number }).message_id\n }\n })\n .catch(() => {\n // HTML failed — enqueue plain text fallback (goes after all chunks, acceptable)\n const fallbackFn = isEdit\n ? () => this.bot.api.editMessageText(this.chatId, this.messageId!, chunkMd.slice(0, 4096)) as Promise<unknown>\n : () => this.bot.api.sendMessage(this.chatId, chunkMd.slice(0, 4096), {\n message_thread_id: this.threadId,\n disable_notification: true,\n })\n return this.sendQueue.enqueue(fallbackFn, { type: 'other' })\n .then((result) => {\n if (!isEdit && result && typeof result === 'object' && 'message_id' in (result as Record<string, unknown>)) {\n this.messageId = (result as { message_id: number }).message_id\n }\n })\n .catch(() => {})\n })\n\n chunkPromises.push(promise)\n }\n\n // All chunks are now in the queue — any items enqueued by concurrent handlers\n // (usage, session_end) will go AFTER our chunks. Safe to await.\n await Promise.all(chunkPromises)\n this.tracer?.log(\"telegram\", { action: \"draft:finalize:split\", sessionId: this.sessionId, chunks: mdChunks.length })\n\n return this.messageId\n }\n\n /** Returns the Telegram message ID for this draft, or undefined if not yet sent. */\n getMessageId(): number | undefined {\n return this.messageId\n }\n\n /**\n * Strip occurrences of `pattern` from the buffer and edit the message in-place.\n * Used by the TTS plugin to remove [TTS]...[/TTS] blocks after audio is sent.\n */\n async stripPattern(pattern: RegExp): Promise<void> {\n if (!this.messageId || !this.buffer) return\n\n const stripped = this.buffer.replace(pattern, '').trim()\n if (stripped === this.buffer.trim()) return\n\n this.buffer = stripped\n this.lastSentBuffer = stripped\n\n const html = markdownToTelegramHtml(stripped)\n if (!html) return\n\n try {\n await this.sendQueue.enqueue(\n () => this.bot.api.editMessageText(this.chatId, this.messageId!, html, {\n parse_mode: 'HTML',\n }),\n { type: 'other' },\n )\n } catch {\n // Best effort — non-critical edit\n }\n }\n}\n","import type { Bot } from \"grammy\";\nimport { MessageDraft } from \"./streaming.js\";\nimport type { SendQueue } from \"../../core/adapter-primitives/primitives/send-queue.js\";\nimport type { DebugTracer } from \"../../core/utils/debug-tracer.js\";\n\n// Retains a finalized draft so tts_strip can edit the message after finalization.\n// The draft's stripPattern() still holds the message ID and can make a single edit.\ninterface FinalizedDraft {\n messageId: number;\n draft: MessageDraft;\n}\n\n/**\n * Per-session draft lifecycle manager.\n *\n * Owns the active `MessageDraft` for each session (the streaming message being\n * assembled from text_delta events) and keeps a short-lived reference after\n * finalization so post-send edits (e.g. TTS block removal) can still update\n * the message in-place.\n */\nexport class DraftManager {\n private drafts: Map<string, MessageDraft> = new Map();\n private textBuffers: Map<string, string> = new Map();\n private finalizedDrafts: Map<string, FinalizedDraft> = new Map();\n\n constructor(\n private bot: Bot,\n private chatId: number,\n private sendQueue: SendQueue,\n ) {}\n\n /**\n * Return the active draft for a session, creating one if it doesn't exist yet.\n * Only one draft per session exists at a time.\n */\n getOrCreate(sessionId: string, threadId: number, tracer: DebugTracer | null = null): MessageDraft {\n let draft = this.drafts.get(sessionId);\n if (!draft) {\n draft = new MessageDraft(\n this.bot,\n this.chatId,\n threadId,\n this.sendQueue,\n sessionId,\n tracer,\n );\n this.drafts.set(sessionId, draft);\n }\n return draft;\n }\n\n hasDraft(sessionId: string): boolean {\n return this.drafts.has(sessionId);\n }\n\n getDraft(sessionId: string): MessageDraft | undefined {\n return this.drafts.get(sessionId);\n }\n\n appendText(sessionId: string, text: string): void {\n this.textBuffers.set(\n sessionId,\n (this.textBuffers.get(sessionId) ?? \"\") + text,\n );\n }\n\n /**\n * Finalize the active draft for a session and retain a short-lived reference for post-send edits.\n *\n * Removes the draft from the active map before awaiting to prevent concurrent calls from\n * double-finalizing the same draft. If the draft produces a message ID, stores it as a\n * `FinalizedDraft` so `stripPattern` (e.g. TTS block removal) can still edit the message\n * after it has been sent.\n */\n async finalize(\n sessionId: string,\n _assistantSessionId?: string,\n ): Promise<void> {\n const draft = this.drafts.get(sessionId);\n if (!draft) return;\n\n // Delete BEFORE awaiting to prevent concurrent finalizeDraft() calls\n // from double-finalizing the same draft\n this.drafts.delete(sessionId);\n const finalMsgId = await draft.finalize();\n\n // Keep finalized draft reference so tts_strip can edit after finalization\n if (finalMsgId) {\n this.finalizedDrafts.set(sessionId, { messageId: finalMsgId, draft });\n }\n\n this.textBuffers.delete(sessionId);\n }\n\n /**\n * Strip a regex pattern from the active or finalized draft for a session.\n * Used by tts_strip to remove [TTS]...[/TTS] blocks after TTS audio is sent.\n */\n async stripPattern(sessionId: string, pattern: RegExp): Promise<void> {\n const draft = this.drafts.get(sessionId);\n if (draft) {\n await draft.stripPattern(pattern);\n return;\n }\n const finalized = this.finalizedDrafts.get(sessionId);\n if (finalized) {\n await finalized.draft.stripPattern(pattern);\n }\n // If no draft found (e.g., TTS synthesis slower than next prompt cycle), the\n // [TTS] block will remain visible. This is a rare edge case — log for debugging.\n }\n\n /**\n * Discard all draft state for a session without sending anything.\n *\n * Removes the active draft, text buffer, and finalized draft reference. Called when a\n * session ends or is reset and any unsent content should be silently dropped.\n */\n cleanup(sessionId: string): void {\n this.drafts.delete(sessionId);\n this.textBuffers.delete(sessionId);\n this.finalizedDrafts.delete(sessionId);\n }\n}\n","import type { Bot } from \"grammy\";\nimport type { SendQueue } from \"../../core/adapter-primitives/primitives/send-queue.js\";\nimport type { AgentCommand } from \"../../core/types.js\";\nimport type { SessionManager } from \"../../core/sessions/session-manager.js\";\nimport type { TelegramPlatformData } from \"../../core/types.js\";\nimport { buildSkillMessages } from \"./commands/index.js\";\nimport { createChildLogger } from \"../../core/utils/log.js\";\n\nconst log = createChildLogger({ module: \"skill-commands\" });\n\n/**\n * Manages the pinned \"Available Skills\" message in each session topic.\n *\n * When an agent registers skill commands, they are displayed as a pinned HTML\n * message with tap-to-copy slash commands. The message is edited in-place on\n * each update and unpinned when the session ends. Message IDs are persisted to\n * platform data so the correct message is found after a restart.\n */\nexport class SkillCommandManager {\n // sessionId → Telegram message ID of the pinned skills message\n private messages: Map<string, number> = new Map();\n\n constructor(\n private bot: Bot,\n private chatId: number,\n private sendQueue: SendQueue,\n private sessionManager: SessionManager,\n ) {}\n\n /**\n * Send or update the pinned skill commands message for a session.\n * Creates a new pinned message if none exists; edits the existing one otherwise.\n * Passing an empty `commands` array removes the pinned message.\n */\n async send(\n sessionId: string,\n threadId: number,\n commands: AgentCommand[],\n ): Promise<void> {\n // Restore skillMsgIds from persisted platform data if not in memory\n if (!this.messages.has(sessionId)) {\n const record = this.sessionManager.getSessionRecord(sessionId);\n const platform = record?.platform as TelegramPlatformData | undefined;\n if (platform?.skillMsgId) {\n this.messages.set(sessionId, platform.skillMsgId);\n }\n }\n\n // Empty commands → remove pinned message\n if (commands.length === 0) {\n await this.cleanup(sessionId);\n return;\n }\n\n const messages = buildSkillMessages(commands);\n const existingMsgId = this.messages.get(sessionId);\n\n if (existingMsgId) {\n try {\n await this.bot.api.editMessageText(\n this.chatId,\n existingMsgId,\n messages[0],\n { parse_mode: \"HTML\" },\n );\n return;\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : \"\";\n if (msg.includes(\"message is not modified\")) return;\n try {\n await this.bot.api.deleteMessage(this.chatId, existingMsgId);\n } catch { /* already gone */ }\n this.messages.delete(sessionId);\n }\n }\n\n // Send new messages and pin the first one\n try {\n let firstMsgId: number | undefined;\n for (const text of messages) {\n const msg = await this.sendQueue.enqueue(() =>\n this.bot.api.sendMessage(this.chatId, text, {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n }),\n );\n if (!firstMsgId) firstMsgId = msg!.message_id;\n }\n\n this.messages.set(sessionId, firstMsgId!);\n\n // Persist skillMsgId so it survives restarts\n const record = this.sessionManager.getSessionRecord(sessionId);\n if (record) {\n await this.sessionManager.patchRecord(sessionId, {\n platform: { ...record.platform, skillMsgId: firstMsgId },\n });\n }\n\n await this.bot.api.pinChatMessage(this.chatId, firstMsgId!, {\n disable_notification: true,\n });\n } catch (err) {\n log.error({ err, sessionId }, \"Failed to send skill commands\");\n }\n }\n\n async cleanup(sessionId: string): Promise<void> {\n const msgId = this.messages.get(sessionId);\n if (!msgId) return;\n\n try {\n await this.bot.api.editMessageText(\n this.chatId,\n msgId,\n \"🛠 <i>Session ended</i>\",\n { parse_mode: \"HTML\" },\n );\n await this.bot.api.unpinChatMessage(this.chatId, msgId);\n } catch { /* message may already be deleted */ }\n\n this.messages.delete(sessionId);\n\n // Clear persisted skillMsgId\n const record = this.sessionManager.getSessionRecord(sessionId);\n if (record) {\n const platform = record.platform;\n if (platform && typeof platform === 'object' && 'topicId' in platform) {\n const { skillMsgId: _removed, ...rest } = platform as unknown as TelegramPlatformData;\n await this.sessionManager.patchRecord(sessionId, { platform: rest });\n }\n }\n }\n}\n","import { BaseRenderer } from \"../../core/adapter-primitives/rendering/renderer.js\";\nimport type { RenderedMessage } from \"../../core/adapter-primitives/rendering/renderer.js\";\nimport type { OutgoingMessage, NotificationMessage } from \"../../core/types.js\";\nimport type {\n DisplayVerbosity,\n ToolCallMeta,\n ToolUpdateMeta,\n} from \"../../core/adapter-primitives/format-types.js\";\nimport {\n escapeHtml,\n formatToolCall,\n formatToolUpdate,\n formatPlan,\n formatUsage,\n} from \"./formatting.js\";\n\n/**\n * Telegram-specific renderer for structured `OutgoingMessage` types.\n *\n * Produces HTML strings (Telegram parse_mode: 'HTML') for each message kind.\n * Plain text rendering is not used because HTML mode handles agent output\n * (code fences, diffs, etc.) more reliably than MarkdownV2.\n */\nexport class TelegramRenderer extends BaseRenderer {\n renderToolCall(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage {\n const meta = (content.metadata ?? {}) as Partial<ToolCallMeta>;\n return {\n body: formatToolCall(meta as ToolCallMeta, verbosity),\n format: \"html\",\n };\n }\n\n renderToolUpdate(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage {\n const meta = (content.metadata ?? {}) as Partial<ToolUpdateMeta>;\n return {\n body: formatToolUpdate(meta as ToolUpdateMeta, verbosity),\n format: \"html\",\n };\n }\n\n renderPlan(content: OutgoingMessage): RenderedMessage {\n const meta = content.metadata as\n | { entries?: Array<{ content: string; status: string }> }\n | undefined;\n return {\n body: formatPlan({ entries: meta?.entries ?? [] }),\n format: \"html\",\n };\n }\n\n renderUsage(\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): RenderedMessage {\n const meta = content.metadata as\n | { tokensUsed?: number; contextSize?: number; cost?: number }\n | undefined;\n return { body: formatUsage(meta ?? {}, verbosity), format: \"html\" };\n }\n\n renderError(content: OutgoingMessage): RenderedMessage {\n return {\n body: `❌ <b>Error:</b> ${escapeHtml(content.text)}`,\n format: \"html\",\n };\n }\n\n renderNotification(notification: NotificationMessage): RenderedMessage {\n const emoji: Record<string, string> = {\n completed: \"✅\",\n error: \"❌\",\n permission: \"🔐\",\n input_required: \"💬\",\n budget_warning: \"⚠️\",\n };\n let text = `${emoji[notification.type] || \"ℹ️\"} <b>${escapeHtml(notification.sessionName || \"Session\")}</b>\\n`;\n text += escapeHtml(notification.summary);\n return { body: text, format: \"html\" };\n }\n\n renderSystemMessage(content: OutgoingMessage): RenderedMessage {\n return { body: escapeHtml(content.text), format: \"html\" };\n }\n\n renderModeChange(content: OutgoingMessage): RenderedMessage {\n const modeId = (content.metadata as Record<string, unknown>)?.modeId ?? \"\";\n return {\n body: `🔄 <b>Mode:</b> ${escapeHtml(String(modeId))}`,\n format: \"html\",\n };\n }\n\n renderConfigUpdate(): RenderedMessage {\n return { body: \"⚙️ <b>Config updated</b>\", format: \"html\" };\n }\n\n renderModelUpdate(content: OutgoingMessage): RenderedMessage {\n const modelId =\n (content.metadata as Record<string, unknown>)?.modelId ?? \"\";\n return {\n body: `🤖 <b>Model:</b> ${escapeHtml(String(modelId))}`,\n format: \"html\",\n };\n }\n}\n","import { createChildLogger } from '../../core/utils/log.js'\nconst log = createChildLogger({ module: 'telegram-validators' })\n\n/**\n * Validate a bot token by calling the Telegram `getMe` API.\n * Returns the bot name and username on success, or an error description on failure.\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\n/**\n * Validate that a chat ID refers to a supergroup (required for forum topics).\n * Returns whether the group has Topics enabled (`isForum`).\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 const typeHint =\n data.result.type === \"group\"\n ? 'This group does not have Topics enabled. Tap the group name → Edit (pencil icon) → enable \"Topics\" → tap the checkmark to save. Telegram will upgrade the group automatically.'\n : `Chat must be a group with Topics enabled (got: \"${data.result.type}\"). Channels and private chats are not supported.`;\n return { ok: false, error: typeHint };\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/**\n * Check that the bot is an admin in the group and has the `can_manage_topics` permission.\n * Group creators are always treated as having all permissions.\n */\nexport async function validateBotAdmin(\n token: string,\n chatId: number,\n): Promise<{ ok: true; canManageTopics: boolean } | { ok: false; error: string }> {\n try {\n // Get bot's own user ID\n const meRes = await fetch(`https://api.telegram.org/bot${token}/getMe`);\n const meData = (await meRes.json()) as {\n ok: boolean;\n result?: { id: number };\n };\n if (!meData.ok || !meData.result) {\n return { ok: false, error: \"Could not retrieve bot info\" };\n }\n\n const res = await fetch(\n `https://api.telegram.org/bot${token}/getChatMember`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ chat_id: chatId, user_id: meData.result.id }),\n },\n );\n const data = (await res.json()) as {\n ok: boolean;\n result?: { status: string; can_manage_topics?: boolean };\n description?: string;\n };\n if (!data.ok || !data.result) {\n return {\n ok: false,\n error: data.description || \"Could not check bot membership\",\n };\n }\n\n const { status } = data.result;\n log.info(\n { status, can_manage_topics: data.result.can_manage_topics, raw_result: data.result },\n 'validateBotAdmin: getChatMember raw result',\n )\n if (status === \"creator\") {\n // Group creator has all permissions\n return { ok: true, canManageTopics: true };\n }\n if (status === \"administrator\") {\n return { ok: true, canManageTopics: data.result.can_manage_topics === true };\n }\n return {\n ok: false,\n error: `Bot is \"${status}\" in this group — it must be an admin. Ask a group admin to go to Group Settings → Administrators → add the bot → tap Save/Done. Note: only existing admins can promote the bot.`,\n };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n}\n\n/**\n * Run all prerequisite checks for the topic-per-session model:\n * 1. Topics are enabled on the group (`is_forum: true`)\n * 2. Bot is an admin\n * 3. Bot has `can_manage_topics` permission\n *\n * Returns all failing issues so the user can fix them all at once.\n * Called on adapter startup; if checks fail, a background watcher retries periodically.\n */\nexport async function checkTopicsPrerequisites(\n token: string,\n chatId: number,\n): Promise<{ ok: true } | { ok: false; issues: string[] }> {\n const issues: string[] = [];\n\n log.info({ chatId }, 'checkTopicsPrerequisites: starting checks')\n\n // Check 1: Topics enabled\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?: { is_forum?: boolean; type?: string; title?: string };\n };\n log.info(\n { chatId, apiOk: data.ok, is_forum: data.result?.is_forum, type: data.result?.type, title: data.result?.title },\n 'checkTopicsPrerequisites: getChat result',\n )\n if (data.ok && data.result && !data.result.is_forum) {\n issues.push(\n '❌ Topics are not enabled on this group.\\n→ Go to Group Settings → Edit → enable \"Topics\" → tap Save/Done',\n );\n }\n } catch (err) {\n log.warn({ err, chatId }, 'checkTopicsPrerequisites: getChat failed (network error)')\n issues.push('❌ Could not check if Topics are enabled (network error).');\n }\n\n // Check 2 & 3: Bot is admin + can_manage_topics\n const adminResult = await validateBotAdmin(token, chatId);\n log.info(\n { chatId, adminOk: adminResult.ok, canManageTopics: adminResult.ok ? adminResult.canManageTopics : undefined, error: !adminResult.ok ? adminResult.error : undefined },\n 'checkTopicsPrerequisites: validateBotAdmin result',\n )\n if (!adminResult.ok) {\n issues.push(\n `❌ Bot is not an admin.\\n→ Go to Group Settings → Administrators → add the bot as admin → tap Save/Done\\n→ Note: only existing group admins can promote the bot`,\n );\n } else if (!adminResult.canManageTopics) {\n issues.push(\n '❌ Bot cannot manage topics.\\n→ Go to Group Settings → Administrators → tap the bot → enable \"Manage Topics\" → tap Save/Done (the checkmark)',\n );\n }\n\n log.info({ chatId, issueCount: issues.length, ok: issues.length === 0 }, 'checkTopicsPrerequisites: result')\n if (issues.length > 0) return { ok: false, issues };\n return { ok: true };\n}\n","import { Bot, InputFile, InlineKeyboard } from \"grammy\";\nimport path from \"node:path\";\nimport { BusEvent } from \"../../core/events.js\";\nimport type {\n OpenACPCore,\n OutgoingMessage,\n PermissionRequest,\n NotificationMessage,\n AgentCommand,\n FileServiceInterface,\n} from \"../../core/index.js\";\nimport { createChildLogger } from \"../../core/utils/log.js\";\nimport type { DebugTracer } from \"../../core/utils/debug-tracer.js\";\nconst log = createChildLogger({ module: \"telegram\" });\nimport type { TelegramChannelConfig } from \"./types.js\";\nimport type { CommandRegistry } from \"../../core/command-registry.js\";\nimport type { CommandResponse } from \"../../core/plugin/types.js\";\nimport {\n ensureTopics,\n createSessionTopic,\n renameSessionTopic,\n deleteSessionTopic,\n} from \"./topics.js\";\nimport {\n setupMenuCallbacks,\n setupDangerousModeCallbacks,\n setupTTSCallbacks,\n setupVerbosityCallbacks,\n setupIntegrateCallbacks,\n buildMenuKeyboard,\n STATIC_COMMANDS,\n} from \"./commands/index.js\";\nimport { TELEGRAM_OVERRIDES } from './commands/telegram-overrides.js'\nimport { buildSessionStatusText, buildSessionControlKeyboard, isBypassActive } from \"./commands/admin.js\";\nimport type { TelegramPlatformData } from \"../../core/types.js\";\nimport { PermissionHandler } from \"./permissions.js\";\nimport {\n redirectToAssistant,\n buildWelcomeMessage,\n} from \"./assistant.js\";\nimport { escapeHtml, formatUsage } from \"./formatting.js\";\nimport { ActivityTracker } from \"./activity.js\";\nimport { SendQueue } from \"../../core/adapter-primitives/primitives/send-queue.js\";\nimport { DraftManager } from \"./draft-manager.js\";\nimport { SkillCommandManager } from \"./skill-command-manager.js\";\nimport {\n MessagingAdapter,\n type MessagingAdapterConfig,\n} from \"../../core/adapter-primitives/messaging-adapter.js\";\nimport type { IRenderer } from \"../../core/adapter-primitives/rendering/renderer.js\";\nimport { TelegramRenderer } from \"./renderer.js\";\nimport type { AdapterCapabilities } from \"../../core/channel.js\";\nimport type {\n DisplayVerbosity,\n ToolCallMeta,\n ToolUpdateMeta,\n OutputMode,\n} from \"../../core/adapter-primitives/format-types.js\";\nimport { OutputModeResolver } from \"../../core/adapter-primitives/output-mode-resolver.js\";\nimport type { TunnelServiceInterface } from \"../../core/plugin/types.js\";\n// evaluateNoise is handled by MessagingAdapter.shouldDisplay()\n\ninterface PlanMetadata {\n entries: Array<{ content: string; status: string; priority: string }>;\n}\n\ninterface UsageMetadata {\n tokensUsed?: number;\n contextSize?: number;\n}\n\n/**\n * Wrap native fetch to work around grammY's polyfilled AbortController.\n *\n * grammY uses an abort-controller polyfill whose AbortSignal fails `instanceof`\n * checks in Node 24+ native fetch. This wrapper re-creates a native AbortSignal\n * from the polyfilled signal object before forwarding the request.\n */\nfunction patchedFetch(\n input: RequestInfo | URL,\n init?: RequestInit,\n): Promise<Response> {\n if (init?.signal && !(init.signal instanceof AbortSignal)) {\n const nativeController = new AbortController();\n const polyfillSignal = init.signal as unknown as {\n aborted: boolean;\n addEventListener: (event: string, fn: () => void) => void;\n };\n if (polyfillSignal.aborted) {\n nativeController.abort();\n } else {\n polyfillSignal.addEventListener(\"abort\", () => nativeController.abort());\n }\n init = { ...init, signal: nativeController.signal };\n }\n return fetch(input, init);\n}\n\n/**\n * Telegram adapter — bridges the OpenACP session system to a Telegram supergroup.\n *\n * Architecture overview:\n * - **Topic-per-session model**: each agent session lives in its own Telegram forum\n * topic. The topic ID is stored as `session.threadId` and used to route all\n * inbound and outbound messages.\n * - **Two system topics**: \"📋 Notifications\" (cross-session alerts) and\n * \"🤖 Assistant\" (conversational AI). Created once on first run, IDs persisted.\n * - **Streaming**: agent text arrives as `text_delta` chunks. A `MessageDraft`\n * accumulates chunks and edits the message in-place every 5 seconds, reducing\n * API calls while keeping the response live.\n * - **Callback routing**:\n * - `p:<key>:<optionId>` — permission approval buttons\n * - `c/<command>` (or `c/#<id>` for long commands) — command button actions\n * - `m:<itemId>` — MenuRegistry item dispatch\n * - Domain prefixes (`d:`, `v:`, `ns:`, `sw:`, etc.) — specific feature flows\n * - **Two-phase startup**: Phase 1 starts the bot and registers handlers.\n * Phase 2 checks group prerequisites (admin, topics enabled) and creates\n * system topics. If prerequisites are not met, a background watcher retries.\n */\nexport class TelegramAdapter extends MessagingAdapter {\n readonly name = \"telegram\";\n readonly renderer: IRenderer = new TelegramRenderer();\n readonly capabilities: AdapterCapabilities = {\n streaming: true,\n richFormatting: true,\n threads: true,\n reactions: true,\n fileUpload: true,\n voice: true,\n };\n\n private core: OpenACPCore;\n private bot!: Bot;\n private telegramConfig: TelegramChannelConfig;\n private saveTopicIds?: (updates: { notificationTopicId?: number; assistantTopicId?: number }) => Promise<void>;\n private permissionHandler!: PermissionHandler;\n private notificationTopicId!: number;\n private assistantTopicId!: number;\n private sendQueue = new SendQueue({ minInterval: 3000 });\n private _sessionThreadIds = new Map<string, number>();\n private outputModeResolver = new OutputModeResolver();\n\n // Extracted managers\n private draftManager!: DraftManager;\n private skillManager!: SkillCommandManager;\n private fileService!: FileServiceInterface;\n private sessionTrackers: Map<string, ActivityTracker> = new Map();\n private callbackCache = new Map<string, string>();\n private callbackCounter = 0;\n /** Pending skill commands queued when session.threadId was not yet set */\n private _pendingSkillCommands = new Map<string, AgentCommand[]>();\n /** Control message IDs per session (for updating status text/buttons) */\n private controlMsgIds = new Map<string, number>();\n /** Last usage message ID per session — replaced on each new prompt cycle */\n private usageMsgIds = new Map<string, number>();\n private _threadReadyHandler?: (data: { sessionId: string; channelId: string; threadId: string }) => void;\n private _configChangedHandler?: (data: { sessionId: string }) => void;\n /** Mutable ref passed to callbacks before topics are ready; updated in-place by initTopicDependentFeatures */\n private _systemTopicIds = { notificationTopicId: 0, assistantTopicId: 0 };\n /**\n * Tracks queue notification state per turnId:\n * - 'pending': notification is being sent (sendQueue delay), not yet assigned a msgId\n * - 'delete': MESSAGE_PROCESSING fired before the send completed — delete as soon as it's sent\n * - number: notification sent successfully, this is the Telegram message_id to delete\n */\n private _queueNotifications = new Map<string, number | 'pending' | 'delete'>();\n /** True once topics are initialized and Phase 2 is complete */\n private _topicsInitialized = false;\n /** Background watcher timer — cancelled on stop() or when topics succeed */\n private _prerequisiteWatcher: ReturnType<typeof setTimeout> | null = null;\n /** Set during normal shutdown so bot.stop() does not trigger a self-restart. */\n private _stopping = false;\n\n /** Returns the configured Telegram supergroup chat ID. */\n getChatId(): number {\n return this.telegramConfig.chatId\n }\n\n /**\n * Persist the control message ID both in-memory and to the session record.\n * The control message is the pinned status card with bypass/TTS buttons; its ID\n * is needed after a restart to edit it when config changes.\n */\n private storeControlMsgId(sessionId: string, msgId: number): void {\n this.controlMsgIds.set(sessionId, msgId);\n const record = this.core.sessionManager.getSessionRecord(sessionId);\n this.core.sessionManager.patchRecord(sessionId, {\n platform: { ...(record?.platform ?? {}), controlMsgId: msgId },\n }).catch(() => {});\n }\n\n /** Get control message ID (from Map, with fallback to session record) */\n private getControlMsgId(sessionId: string): number | undefined {\n let msgId = this.controlMsgIds.get(sessionId);\n if (!msgId) {\n const record = this.core.sessionManager.getSessionRecord(sessionId);\n const platform = record?.platform as TelegramPlatformData | undefined;\n if (platform?.controlMsgId) {\n msgId = platform.controlMsgId;\n this.storeControlMsgId(sessionId, msgId);\n }\n }\n return msgId;\n }\n\n private getThreadId(sessionId: string): number {\n const threadId = this._sessionThreadIds.get(sessionId);\n if (threadId === undefined) {\n throw new Error(`No threadId stored for session ${sessionId}`);\n }\n return threadId;\n }\n\n private getOrCreateTracker(\n sessionId: string,\n threadId: number,\n outputMode: OutputMode = \"medium\",\n ): ActivityTracker {\n let tracker = this.sessionTrackers.get(sessionId);\n if (!tracker) {\n const tunnelService = this.core.lifecycleManager?.serviceRegistry?.get(\"tunnel\") as TunnelServiceInterface | undefined;\n const session = this.core.sessionManager.getSession(sessionId);\n const sessionContext = session ? {\n id: sessionId,\n workingDirectory: session.workingDirectory,\n } : undefined;\n tracker = new ActivityTracker(\n this.bot.api,\n this.telegramConfig.chatId,\n threadId,\n this.sendQueue,\n outputMode,\n sessionId,\n this.getTracer(sessionId),\n tunnelService,\n sessionContext,\n );\n this.sessionTrackers.set(sessionId, tracker);\n } else {\n tracker.setOutputMode(outputMode);\n }\n return tracker;\n }\n\n constructor(\n core: OpenACPCore,\n config: TelegramChannelConfig,\n saveTopicIds?: (updates: { notificationTopicId?: number; assistantTopicId?: number }) => Promise<void>,\n ) {\n super({ configManager: core.configManager }, {\n ...(config as Record<string, unknown>),\n maxMessageLength: 4096,\n enabled: config.enabled ?? true,\n } as MessagingAdapterConfig);\n this.core = core;\n this.telegramConfig = config;\n this.saveTopicIds = saveTopicIds;\n }\n\n /**\n * Set up the grammY bot, register all callback and message handlers, then perform\n * two-phase startup: Phase 1 starts polling immediately; Phase 2 checks group\n * prerequisites (bot is admin, topics are enabled) and creates/restores system topics.\n * If prerequisites are not met, a background watcher retries until they are.\n */\n async start(): Promise<void> {\n this._stopping = false;\n this.bot = new Bot(this.telegramConfig.botToken, {\n client: {\n baseFetchConfig: { duplex: \"half\" } as RequestInit,\n fetch: patchedFetch,\n },\n });\n this.fileService = this.core.fileService;\n\n // Initialize extracted managers\n this.draftManager = new DraftManager(\n this.bot,\n this.telegramConfig.chatId,\n this.sendQueue,\n );\n this.skillManager = new SkillCommandManager(\n this.bot,\n this.telegramConfig.chatId,\n this.sendQueue,\n this.core.sessionManager,\n );\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 // Auto-retry on 429 (Too Many Requests)\n this.bot.api.config.use(async (prev, method, payload, signal) => {\n const maxRetries = 3;\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const result = await prev(method, payload, signal);\n if (\n result.ok ||\n (result as { error_code?: number }).error_code !== 429 ||\n attempt === maxRetries\n ) {\n return result;\n }\n const retryAfter =\n ((result as { parameters?: { retry_after?: number } }).parameters\n ?.retry_after ?? 5) + 1;\n const rateLimitedMethods = [\n \"sendMessage\",\n \"editMessageText\",\n \"editMessageReplyMarkup\",\n ];\n if (rateLimitedMethods.includes(method)) {\n this.sendQueue.onRateLimited();\n }\n log.warn(\n { method, retryAfter, attempt: attempt + 1 },\n \"Rate limited by Telegram, retrying\",\n );\n await new Promise((r) => setTimeout(r, retryAfter * 1000));\n }\n return prev(method, payload, signal);\n });\n\n // Ensure allowed_updates includes callback_query on every poll\n this.bot.api.config.use((prev, method, payload, signal) => {\n if (method === \"getUpdates\") {\n const p = payload 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 Telegram autocomplete commands after all plugins finish setup.\n // Keeps listening persistently so hot-reload (dev plugin) and community plugin\n // changes re-sync the command list without restarting the server.\n const onCommandsReady = ({ commands }: { commands: Array<{ name: string; description: string; category?: string }> }) => {\n this.syncCommandsWithRetry(commands);\n };\n this.core.eventBus.on(BusEvent.SYSTEM_COMMANDS_READY, onCommandsReady);\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 // Setup permission handler\n this.permissionHandler = new PermissionHandler(\n this.bot,\n this.telegramConfig.chatId,\n (sessionId) => this.core.sessionManager.getSession(sessionId),\n (notification) => this.sendNotification(notification),\n );\n\n // Generic CommandRegistry dispatch — handles any command registered via plugin system.\n // Must be early so registry commands run before legacy bot.command() handlers.\n // Command handler — guard when topics not yet initialized\n this.bot.on(\"message:text\", async (ctx, next) => {\n const text = ctx.message?.text;\n if (!text?.startsWith(\"/\")) return next();\n\n // /retry is always allowed — it is specifically for recovering from the not-initialized state\n const rawCmd = text.split(\" \")[0].slice(1).split(\"@\")[0].toLowerCase();\n if (rawCmd === \"retry\") {\n await this.handleRetryCommand(ctx);\n return;\n }\n\n if (!this._topicsInitialized) {\n await ctx.reply(\n \"⏳ OpenACP is still setting up. Open the Topics list of this group and check the 📌 General topic for setup instructions. Or send /retry to re-check now.\",\n ).catch(() => {});\n return;\n }\n\n const registry =\n this.core.lifecycleManager?.serviceRegistry?.get<CommandRegistry>(\n \"command-registry\",\n );\n if (!registry) return next();\n\n // Extract command name (remove / and @botname suffix)\n const rawCommand = text.split(\" \")[0].slice(1);\n const atIdx = rawCommand.indexOf(\"@\");\n // If command is directed at another bot, ignore it\n if (\n atIdx !== -1 &&\n rawCommand.slice(atIdx + 1).toLowerCase() !==\n ctx.me.username.toLowerCase()\n ) {\n return next();\n }\n const commandName =\n atIdx === -1 ? rawCommand : rawCommand.slice(0, atIdx);\n const def = registry.get(commandName);\n if (!def) return next(); // not in registry, fall through to existing handlers\n\n // Telegram-specific override — use rich handler instead of core CommandRegistry\n const telegramOverride = TELEGRAM_OVERRIDES[commandName]\n if (telegramOverride) {\n try {\n await telegramOverride(ctx, this.core as OpenACPCore)\n } catch (err) {\n await ctx.reply(`⚠️ Command failed: ${String(err)}`)\n }\n return\n }\n\n const chatId = ctx.chat.id;\n const topicId = ctx.message.message_thread_id;\n\n try {\n const sessionId =\n topicId != null\n ? ((await this.core.getOrResumeSession(\n \"telegram\",\n String(topicId),\n ))?.id ?? null)\n : null;\n\n const response = await registry.execute(text, {\n raw: \"\",\n sessionId,\n channelId: \"telegram\",\n userId: String(ctx.from?.id),\n reply: async (content) => {\n if (typeof content === \"string\") {\n await ctx.reply(content);\n } else if (\n typeof content === \"object\" &&\n content !== null &&\n \"type\" in content\n ) {\n await this.renderCommandResponse(\n content as CommandResponse,\n chatId,\n topicId,\n );\n }\n },\n });\n\n if (response.type === \"delegated\") {\n // Delegated means assistant will handle the response — don't render anything\n return;\n }\n if (response.type === \"silent\") {\n // Silent means fall through to message routing (backward compat for commands not yet migrated)\n return next();\n }\n await this.renderCommandResponse(response, chatId, topicId);\n } catch (err) {\n await ctx.reply(`\\u26a0\\ufe0f Command failed: ${String(err)}`);\n }\n });\n\n // Callback query handler for command buttons (c/ prefix)\n this.bot.callbackQuery(/^c\\//, async (ctx) => {\n if (!this._topicsInitialized) {\n await ctx.answerCallbackQuery().catch(() => {});\n return;\n }\n\n const data = ctx.callbackQuery.data;\n const command = this.fromCallbackData(data);\n\n const registry =\n this.core.lifecycleManager?.serviceRegistry?.get<CommandRegistry>(\n \"command-registry\",\n );\n if (!registry) return;\n\n const chatId = ctx.chat!.id;\n const topicId = ctx.callbackQuery.message?.message_thread_id;\n\n try {\n const sessionId =\n topicId != null\n ? ((await this.core.getOrResumeSession(\n \"telegram\",\n String(topicId),\n ))?.id ?? null)\n : null;\n\n const response = await registry.execute(command, {\n raw: \"\",\n sessionId,\n channelId: \"telegram\",\n userId: String(ctx.from?.id),\n reply: async (content) => {\n if (typeof content === \"string\") {\n await ctx.editMessageText(content).catch(() => {});\n }\n },\n });\n\n await ctx.answerCallbackQuery();\n if (response.type !== \"silent\") {\n // Always edit the callback message in-place (no new messages)\n if (response.type === \"menu\") {\n const keyboard = response.options.map((opt) => [\n {\n text: `${opt.label}${opt.hint ? ` \\u2014 ${opt.hint}` : \"\"}`,\n callback_data: this.toCallbackData(opt.command),\n },\n ]);\n try {\n await ctx.editMessageText(response.title, {\n reply_markup: { inline_keyboard: keyboard },\n });\n } catch {\n /* message unchanged or deleted */\n }\n } else if (response.type === \"text\" || response.type === \"error\" || response.type === \"adaptive\") {\n let text: string;\n let parseMode: 'HTML' | 'Markdown' | 'MarkdownV2' | undefined;\n if (response.type === \"adaptive\") {\n const variant = response.variants?.['telegram'] as\n | { text?: string; parse_mode?: 'HTML' | 'Markdown' | 'MarkdownV2' }\n | undefined;\n text = variant?.text ?? response.fallback;\n parseMode = variant?.parse_mode;\n } else {\n text = response.type === \"text\" ? response.text : `❌ ${response.message}`;\n parseMode = \"Markdown\";\n }\n try {\n await ctx.editMessageText(text, { ...(parseMode && { parse_mode: parseMode }) });\n } catch {\n /* message unchanged or deleted */\n }\n }\n }\n } catch {\n await ctx.answerCallbackQuery({ text: \"Command failed\" });\n }\n });\n\n // Callback query handler for queue management buttons (q: prefix).\n // Buttons are attached to queue notification messages sent by the MESSAGE_QUEUED listener.\n this.bot.callbackQuery(/^q:/, async (ctx) => {\n const data = ctx.callbackQuery.data!;\n const parts = data.split(':');\n const action = parts[1];\n const sessionId = parts[2];\n\n const session = this.core.sessionManager.getSession(sessionId);\n if (!session) {\n await ctx.answerCallbackQuery({ text: 'Session not found.' });\n return;\n }\n\n if (action === 'now') {\n const turnId = parts[3];\n const found = await session.prioritizePrompt(turnId);\n await ctx.answerCallbackQuery({ text: found ? '⏭ Processing now!' : 'Message already processed.' });\n } else if (action === 'clear') {\n session.clearQueue();\n await ctx.answerCallbackQuery({ text: '🗑 Queue cleared.' });\n } else if (action === 'cancel') {\n await session.abortPrompt();\n await ctx.answerCallbackQuery({ text: '⛔ Current prompt cancelled.' });\n } else if (action === 'flush') {\n await session.flushAll();\n session.markCancelled();\n await ctx.answerCallbackQuery({ text: '🔄 Session flushed.' });\n }\n\n // For clear/flush/now, all queued notifications are obsolete — dismiss them all.\n // For sent notifications (number), delete via API immediately.\n // For pending notifications (not yet sent due to sendQueue delay), mark as 'delete'\n // so the send handler deletes them right after sending.\n if (action === 'clear' || action === 'flush' || action === 'now') {\n for (const [turnId, entry] of this._queueNotifications) {\n if (typeof entry === 'number') {\n this.bot.api.deleteMessage(this.telegramConfig.chatId, entry).catch(() => {});\n this._queueNotifications.delete(turnId);\n } else if (entry === 'pending') {\n this._queueNotifications.set(turnId, 'delete');\n }\n // 'delete' entries are already handled\n }\n }\n\n // Delete this notification message\n try { await ctx.deleteMessage(); } catch { /* already deleted */ }\n });\n\n // Callback registration order matters!\n setupDangerousModeCallbacks(this.bot, this.core as OpenACPCore);\n setupTTSCallbacks(this.bot, this.core as OpenACPCore);\n setupVerbosityCallbacks(this.bot, this.core as OpenACPCore);\n setupIntegrateCallbacks(this.bot, this.core as OpenACPCore);\n this.permissionHandler.setupCallbackHandler();\n\n // Register topic-dependent callbacks using a mutable ref (_systemTopicIds).\n // The ref is updated in-place by initTopicDependentFeatures() once topics are ready.\n setupMenuCallbacks(\n this.bot,\n this.core as OpenACPCore,\n this.telegramConfig.chatId,\n this._systemTopicIds,\n () => {\n const assistant = this.core.assistantManager?.get('telegram');\n if (!assistant) return undefined;\n return {\n topicId: this.assistantTopicId,\n enqueuePrompt: (p: string) => {\n const pending = this.core.assistantManager?.consumePendingSystemPrompt('telegram');\n const text = pending ? `${pending}\\n\\n---\\n\\nUser message:\\n${p}` : p;\n return assistant.enqueuePrompt(text);\n },\n };\n },\n (sessionId: string, msgId: number) => {\n this.storeControlMsgId(sessionId, msgId);\n },\n );\n this.setupRoutes();\n\n // Start bot polling. Supervise the promise so a rejected poll triggers a restart\n // instead of silently leaving the process alive but no longer consuming messages.\n void this.bot.start({\n allowed_updates: [\"message\", \"callback_query\"],\n onStart: () => log.info({ chatId: this.telegramConfig.chatId }, \"Telegram bot started\"),\n }).catch((err: unknown) => {\n if (this._stopping) return;\n\n // grammY sometimes wraps the real error in an { error } envelope.\n const inner = (err as Record<string, unknown>)?.error;\n const rootCause = err instanceof Error ? err : inner instanceof Error ? inner : err;\n\n log.error({ err: rootCause }, \"Telegram polling stopped unexpectedly\");\n if (this.core.requestRestart) {\n const requestRestart = this.core.requestRestart;\n log.error(\"Restarting OpenACP because Telegram polling stopped\");\n setImmediate(() => {\n if (this._stopping) return;\n void requestRestart().catch((restartErr: unknown) => {\n log.error({ err: restartErr }, \"OpenACP restart request failed after Telegram polling stopped\");\n });\n });\n } else {\n log.error(\"Exiting because Telegram polling stopped and no restart hook is available\");\n setImmediate(() => {\n if (this._stopping) return;\n process.exit(1);\n });\n }\n });\n\n // Phase 2: check prerequisites and either initialize topics now or start watcher\n log.info(\n {\n chatId: this.telegramConfig.chatId,\n notificationTopicId: this.telegramConfig.notificationTopicId,\n assistantTopicId: this.telegramConfig.assistantTopicId,\n },\n 'Telegram adapter: starting prerequisite check (existing topic IDs shown)',\n );\n const { checkTopicsPrerequisites } = await import('./validators.js');\n const prereqResult = await checkTopicsPrerequisites(\n this.telegramConfig.botToken,\n this.telegramConfig.chatId,\n );\n\n if (prereqResult.ok) {\n log.info('Telegram adapter: prerequisites OK, initializing topic-dependent features');\n await this.initTopicDependentFeatures();\n } else {\n log.warn({ issues: prereqResult.issues }, 'Telegram adapter: prerequisites NOT met, starting watcher');\n for (const issue of prereqResult.issues) {\n log.warn({ issue }, 'Telegram prerequisite not met');\n }\n this.startPrerequisiteWatcher(prereqResult.issues);\n }\n }\n\n /**\n * Retry an async operation with exponential backoff.\n * Used for Telegram API calls that may fail due to transient network issues.\n */\n private async retryWithBackoff<T>(\n fn: () => Promise<T>,\n label: string,\n maxRetries = 5,\n baseDelayMs = 2000,\n ): Promise<T> {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n if (attempt === maxRetries) throw err;\n const delay = baseDelayMs * Math.pow(2, attempt - 1);\n log.warn(\n { err, attempt, maxRetries, delayMs: delay, operation: label },\n `${label} failed, retrying in ${delay}ms`,\n );\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n throw new Error(\"unreachable\");\n }\n\n /**\n * Sync Telegram autocomplete commands after all plugins are ready.\n * Merges STATIC_COMMANDS (hardcoded system commands) with plugin commands\n * from the registry, deduplicating by command name. Non-critical.\n */\n private syncCommandsWithRetry(registryCommands: Array<{ name: string; description: string; category?: string }>): void {\n const staticNames = new Set(STATIC_COMMANDS.map((c) => c.command));\n\n // Only add plugin commands not already in STATIC_COMMANDS.\n // Telegram command names must be lowercase alphanumeric + underscore only.\n const pluginCommands = registryCommands\n .filter((c) => c.category === 'plugin' && !staticNames.has(c.name) && /^[a-z0-9_]+$/.test(c.name))\n .map((c) => ({ command: c.name, description: c.description.slice(0, 256) }))\n\n const allCommands = [...STATIC_COMMANDS, ...pluginCommands].slice(0, 100)\n\n this.retryWithBackoff(\n () => this.bot.api.setMyCommands(allCommands, {\n scope: { type: \"chat\", chat_id: this.telegramConfig.chatId },\n }),\n \"setMyCommands\",\n ).catch((err) => {\n log.warn({ err }, \"Failed to register Telegram commands after retries (non-critical)\");\n });\n }\n\n private async initTopicDependentFeatures(): Promise<void> {\n if (this._topicsInitialized) return; // idempotent guard\n log.info(\n { notificationTopicId: this.telegramConfig.notificationTopicId, assistantTopicId: this.telegramConfig.assistantTopicId },\n 'initTopicDependentFeatures: starting (existing IDs in config)',\n );\n // Ensure system topics exist (retry on transient network failures)\n const topics = await this.retryWithBackoff(\n () => ensureTopics(\n this.bot,\n this.telegramConfig.chatId,\n this.telegramConfig,\n async (updates) => {\n if (this.saveTopicIds) {\n await this.saveTopicIds(updates);\n } else {\n // Fallback for legacy usage without plugin settings\n await this.core.configManager.save({\n channels: { telegram: updates },\n });\n }\n },\n ),\n \"ensureTopics\",\n );\n this.notificationTopicId = topics.notificationTopicId;\n this.assistantTopicId = topics.assistantTopicId;\n // Update the mutable ref so callbacks registered before bot.start() see the correct IDs\n this._systemTopicIds.notificationTopicId = topics.notificationTopicId;\n this._systemTopicIds.assistantTopicId = topics.assistantTopicId;\n log.info(\n { notificationTopicId: this.notificationTopicId, assistantTopicId: this.assistantTopicId },\n 'initTopicDependentFeatures: topics ready',\n );\n\n // Send initial messages when a new session thread is created via API/CLI\n this._threadReadyHandler = ({ sessionId, channelId, threadId }) => {\n if (channelId !== \"telegram\") return;\n const session = this.core.sessionManager.getSession(sessionId);\n if (!session) return;\n const numThreadId = Number(threadId);\n if (!numThreadId) return;\n // Skip assistant session — it manages its own welcome message\n if (sessionId === this.core.assistantManager?.get('telegram')?.id) return;\n\n // Send \"Setting up...\" then control message with real session state\n this.sendQueue.enqueue(() =>\n this.bot.api.sendMessage(this.telegramConfig.chatId, `⏳ Setting up session, please wait...`, {\n message_thread_id: numThreadId,\n parse_mode: 'HTML',\n }),\n ).then(() =>\n this.sendQueue.enqueue(() =>\n this.bot.api.sendMessage(\n this.telegramConfig.chatId,\n buildSessionStatusText(session, '✅ <b>Session started</b>'),\n {\n message_thread_id: numThreadId,\n parse_mode: 'HTML',\n reply_markup: buildSessionControlKeyboard(sessionId, isBypassActive(session), session.voiceMode === \"on\"),\n },\n ),\n ),\n ).then((msg) => {\n if (msg) this.storeControlMsgId(sessionId, msg.message_id);\n }).catch((err) => {\n log.warn({ err, sessionId }, 'Failed to send initial messages for new session');\n });\n };\n this.core.eventBus.on(BusEvent.SESSION_THREAD_READY, this._threadReadyHandler);\n\n // Update control message when config changes via commands (/model, /mode, /bypass, etc.)\n this._configChangedHandler = ({ sessionId }) => {\n this.updateControlMessage(sessionId).catch(() => {});\n };\n this.core.eventBus.on(\"session:configChanged\", this._configChangedHandler);\n\n // Show an inline notification when a message is actually placed in the pending queue\n // behind an active prompt. The PROMPT_WAITING event fires from inside PromptQueue.enqueue()\n // with accurate state, eliminating the race condition from checking promptRunning asynchronously.\n this.core.eventBus.on(BusEvent.PROMPT_WAITING, async (data) => {\n if (data.sourceAdapterId !== 'telegram') return;\n const session = this.core.sessionManager.getSession(data.sessionId);\n if (!session) return;\n const threadId = Number(session.threadId);\n if (!threadId) return;\n\n const position = data.queueDepth;\n const keyboard = new InlineKeyboard()\n .text('⏭ Process Now', `q:now:${data.sessionId}:${data.turnId}`)\n .text('🗑 Clear Queue', `q:clear:${data.sessionId}`)\n .row()\n .text('⛔ Cancel Current', `q:cancel:${data.sessionId}`)\n .text('🔄 Flush All', `q:flush:${data.sessionId}`);\n\n const text = [\n `📋 <b>Message queued</b> (#${position} in line)`,\n '',\n '<i>Agent is processing another prompt.</i>',\n '',\n '⏭ <b>Process Now</b> — Skip queue, process immediately',\n '🗑 <b>Clear Queue</b> — Remove queued messages',\n '⛔ <b>Cancel Current</b> — Stop current prompt',\n '🔄 <b>Flush All</b> — Cancel everything, start fresh',\n ].join('\\n');\n\n // Mark as 'pending' BEFORE awaiting sendQueue so MESSAGE_PROCESSING can observe this\n // turnId and mark it for deletion if the message is processed before the notification is sent.\n // Without this, MESSAGE_PROCESSING may fire during the 3s sendQueue delay and see no entry.\n this._queueNotifications.set(data.turnId, 'pending');\n try {\n const result = await this.sendQueue.enqueue(() =>\n this.bot.api.sendMessage(this.telegramConfig.chatId, text, {\n message_thread_id: threadId,\n parse_mode: 'HTML',\n reply_markup: keyboard,\n disable_notification: true,\n }),\n );\n const current = this._queueNotifications.get(data.turnId);\n if (current === 'delete') {\n // MESSAGE_PROCESSING fired while we were waiting — delete the notification immediately\n this._queueNotifications.delete(data.turnId);\n if (result) {\n this.bot.api.deleteMessage(this.telegramConfig.chatId, result.message_id).catch((err) => {\n log.warn({ err }, 'Failed to delete queue notification after race');\n });\n }\n } else if (result) {\n this._queueNotifications.set(data.turnId, result.message_id);\n } else {\n this._queueNotifications.delete(data.turnId);\n }\n } catch (err) {\n this._queueNotifications.delete(data.turnId);\n log.warn({ err }, 'Failed to send queue notification');\n }\n });\n\n // Dismiss the queue notification once the queued message starts processing.\n // Handles two cases: notification already sent (number) → delete via API immediately;\n // notification still in sendQueue ('pending') → mark as 'delete' so the send handler cleans up.\n this.core.eventBus.on(BusEvent.MESSAGE_PROCESSING, async (data) => {\n const entry = this._queueNotifications.get(data.turnId);\n if (entry === undefined) return;\n if (typeof entry === 'number') {\n this._queueNotifications.delete(data.turnId);\n this.bot.api.deleteMessage(this.telegramConfig.chatId, entry).catch((err) => {\n log.warn({ err }, 'Failed to delete queue notification on processing');\n });\n } else if (entry === 'pending') {\n // Notification not sent yet — mark for deletion when the sendQueue flush completes\n this._queueNotifications.set(data.turnId, 'delete');\n }\n // 'delete' state: already handled\n });\n\n // Send welcome message\n log.info({ assistantTopicId: this.assistantTopicId }, 'initTopicDependentFeatures: sending welcome message');\n try {\n const config = this.core.configManager.get();\n const agents = this.core.agentManager.getAvailableAgents();\n const allRecords = this.core.sessionManager.listRecords();\n\n const welcomeText = buildWelcomeMessage({\n activeCount: allRecords.filter(\n (r) => r.status === \"active\" || r.status === \"initializing\",\n ).length,\n errorCount: allRecords.filter((r) => r.status === \"error\").length,\n totalCount: allRecords.length,\n agents: agents.map((a) => a.name),\n defaultAgent: config.defaultAgent,\n workspace: this.core.configManager.resolveWorkspace(),\n });\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 this.core.lifecycleManager?.serviceRegistry?.get('menu-registry') as import('../../core/menu-registry.js').MenuRegistry | undefined,\n ),\n });\n log.info('initTopicDependentFeatures: welcome message sent');\n } catch (err) {\n log.warn({ err }, \"Failed to send welcome message\");\n }\n\n // Spawn assistant via AssistantManager\n try {\n await this.core.assistantManager.getOrSpawn(\"telegram\", String(this.assistantTopicId));\n } catch (err) {\n log.error({ err }, \"Failed to spawn assistant\");\n }\n\n this._topicsInitialized = true;\n log.info(\"Telegram adapter fully initialized\");\n }\n\n /**\n * Handle the /retry command — re-runs prerequisite checks immediately.\n *\n * This command is allowed even before topics are initialized so users can\n * trigger a recheck right after fixing permissions, without waiting for the\n * next automatic retry cycle.\n */\n private async handleRetryCommand(ctx: { reply: (text: string, opts?: Record<string, unknown>) => Promise<unknown> }): Promise<void> {\n if (this._topicsInitialized) {\n await ctx.reply(\"✅ OpenACP is already running.\").catch(() => {});\n return;\n }\n\n await ctx.reply(\"🔄 Re-checking prerequisites...\").catch(() => {});\n\n const { checkTopicsPrerequisites } = await import('./validators.js');\n const result = await checkTopicsPrerequisites(\n this.telegramConfig.botToken,\n this.telegramConfig.chatId,\n );\n\n if (result.ok) {\n try {\n // Cancel the background watcher since we're handling init inline\n if (this._prerequisiteWatcher !== null) {\n clearTimeout(this._prerequisiteWatcher);\n this._prerequisiteWatcher = null;\n }\n await this.initTopicDependentFeatures();\n const assistantTopicLink = this.assistantTopicId\n ? ` <a href=\"https://t.me/c/${String(this.telegramConfig.chatId).replace(/^-100/, '')}/${this.assistantTopicId}\">🤖 Assistant</a>`\n : ' 🤖 Assistant';\n await ctx.reply(\n `✅ <b>OpenACP is ready!</b> Tap${assistantTopicLink} to get started.`,\n { parse_mode: 'HTML' },\n ).catch(() => {});\n } catch (err) {\n log.error({ err }, 'handleRetryCommand: init failed after prerequisites met');\n await ctx.reply(\"⚠️ Prerequisites are met but initialization failed. Check server logs and try /retry again.\").catch(() => {});\n }\n } else {\n const issueText = result.issues.join('\\n\\n');\n await ctx.reply(\n `⚠️ <b>Still not ready:</b>\\n\\n${issueText}\\n\\nFix the issues above and send /retry again.`,\n { parse_mode: 'HTML' },\n ).catch(() => {});\n }\n }\n\n private startPrerequisiteWatcher(issues: string[]): void {\n const setupMessage =\n `⚠️ <b>OpenACP needs setup before it can start.</b>\\n\\n` +\n issues.join('\\n\\n') +\n `\\n\\nOpenACP will automatically retry every 30 seconds. After fixing the issues above, send /retry to re-check immediately.`;\n\n this.bot.api.sendMessage(this.telegramConfig.chatId, setupMessage, {\n parse_mode: 'HTML',\n }).catch((err) => {\n log.warn({ err }, 'Failed to send setup guidance to General topic');\n });\n\n const schedule = [5_000, 10_000, 30_000];\n let attempt = 1;\n\n const retry = async () => {\n if (this._prerequisiteWatcher === null) return; // cancelled by stop()\n\n const { checkTopicsPrerequisites } = await import('./validators.js');\n const result = await checkTopicsPrerequisites(\n this.telegramConfig.botToken,\n this.telegramConfig.chatId,\n );\n\n if (result.ok) {\n log.info('Prerequisites met — completing Telegram adapter initialization');\n try {\n await this.initTopicDependentFeatures();\n // Only cancel watcher after successful init — if init fails we keep retrying\n this._prerequisiteWatcher = null;\n const assistantTopicLink = this.assistantTopicId\n ? ` <a href=\"https://t.me/c/${String(this.telegramConfig.chatId).replace(/^-100/, '')}/${this.assistantTopicId}\">🤖 Assistant</a>`\n : ' 🤖 Assistant';\n await this.bot.api.sendMessage(\n this.telegramConfig.chatId,\n `✅ <b>OpenACP is ready!</b>\\n\\nSystem topics have been created. Tap${assistantTopicLink} to get started.`,\n { parse_mode: 'HTML' },\n );\n } catch (err) {\n log.error({ err }, 'Failed to complete initialization after prerequisites met — will retry');\n // Don't cancel watcher; schedule next retry so init can be reattempted\n const delay = schedule[Math.min(attempt, schedule.length - 1)];\n attempt++;\n this._prerequisiteWatcher = setTimeout(retry, delay);\n }\n return;\n }\n\n log.debug({ issues: result.issues }, 'Prerequisites not yet met, retrying');\n const delay = schedule[Math.min(attempt, schedule.length - 1)];\n attempt++;\n this._prerequisiteWatcher = setTimeout(retry, delay);\n };\n\n this._prerequisiteWatcher = setTimeout(retry, schedule[0]);\n }\n\n /**\n * Tear down the bot and release all associated resources.\n *\n * Cancels the background prerequisite watcher, destroys all per-session activity\n * trackers (which hold interval timers), removes eventBus listeners, clears the\n * send queue, and stops the grammY bot polling loop.\n */\n async stop(): Promise<void> {\n this._stopping = true;\n\n // Cancel background prerequisite watcher if running\n if (this._prerequisiteWatcher !== null) {\n clearTimeout(this._prerequisiteWatcher);\n this._prerequisiteWatcher = null;\n }\n\n // Cleanup activity trackers (interval timers)\n for (const tracker of this.sessionTrackers.values()) {\n tracker.destroy();\n }\n this.sessionTrackers.clear();\n\n // Remove direct eventBus listeners\n if (this._threadReadyHandler) {\n this.core.eventBus.off(BusEvent.SESSION_THREAD_READY, this._threadReadyHandler);\n this._threadReadyHandler = undefined;\n }\n if (this._configChangedHandler) {\n this.core.eventBus.off(\"session:configChanged\", this._configChangedHandler);\n this._configChangedHandler = undefined;\n }\n\n // Clear send queue\n this.sendQueue.clear();\n\n await this.bot.stop();\n log.info(\"Telegram bot stopped\");\n }\n\n // --- CommandRegistry response rendering ---\n\n private async renderCommandResponse(\n response: CommandResponse,\n chatId: number,\n topicId?: number,\n ): Promise<void> {\n switch (response.type) {\n case \"text\":\n await this.bot.api.sendMessage(chatId, response.text, {\n message_thread_id: topicId,\n });\n break;\n case \"adaptive\": {\n const variant = response.variants?.['telegram'] as\n | { text?: string; parse_mode?: 'HTML' | 'Markdown' | 'MarkdownV2' }\n | undefined;\n const text = variant?.text ?? response.fallback;\n await this.bot.api.sendMessage(chatId, text, {\n message_thread_id: topicId,\n ...(variant?.parse_mode && { parse_mode: variant.parse_mode }),\n });\n break;\n }\n case \"error\":\n await this.bot.api.sendMessage(\n chatId,\n `\\u26a0\\ufe0f ${response.message}`,\n { message_thread_id: topicId },\n );\n break;\n case \"menu\": {\n const keyboard = response.options.map((opt) => [\n {\n text: `${opt.label}${opt.hint ? ` \\u2014 ${opt.hint}` : \"\"}`,\n callback_data: this.toCallbackData(opt.command),\n },\n ]);\n await this.bot.api.sendMessage(chatId, response.title, {\n message_thread_id: topicId,\n reply_markup: { inline_keyboard: keyboard },\n });\n break;\n }\n case \"list\": {\n const lines = response.items.map(\n (i) => `\\u2022 ${i.label}${i.detail ? ` \\u2014 ${i.detail}` : \"\"}`,\n );\n const text = `${response.title}\\n${lines.join(\"\\n\")}`;\n await this.bot.api.sendMessage(chatId, text, {\n message_thread_id: topicId,\n });\n break;\n }\n case \"confirm\": {\n const buttons = [\n [\n {\n text: \"\\u2705 Yes\",\n callback_data: this.toCallbackData(response.onYes),\n },\n ],\n ];\n if (response.onNo) {\n buttons[0].push({\n text: \"\\u274c No\",\n callback_data: this.toCallbackData(response.onNo),\n });\n }\n await this.bot.api.sendMessage(chatId, response.question, {\n message_thread_id: topicId,\n reply_markup: { inline_keyboard: buttons },\n });\n break;\n }\n case \"silent\":\n break;\n }\n }\n\n private toCallbackData(command: string): string {\n const data = `c/${command}`;\n if (data.length <= 64) return data;\n const id = String(++this.callbackCounter);\n this.callbackCache.set(id, command);\n if (this.callbackCache.size > 1000) {\n const first = this.callbackCache.keys().next().value;\n if (first) this.callbackCache.delete(first);\n }\n return `c/#${id}`;\n }\n\n private fromCallbackData(data: string): string {\n if (data.startsWith(\"c/#\")) {\n return this.callbackCache.get(data.slice(3)) ?? data.slice(2);\n }\n return data.slice(2);\n }\n\n private setupRoutes(): void {\n this.bot.on(\"message:text\", async (ctx) => {\n // Guard: topics not yet initialized — non-command messages would use stale/zero topic IDs\n if (!this._topicsInitialized) {\n await ctx.reply(\n \"⏳ OpenACP is still setting up. Open the Topics list of this group and check the 📌 General topic for setup instructions. Or send /retry to re-check now.\",\n ).catch(() => {});\n return;\n }\n\n const threadId = ctx.message.message_thread_id;\n const text = ctx.message.text;\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 // Strip leading \"/\" from unrecognized commands — registered commands\n // are handled by the CommandRegistry dispatch middleware above.\n // Unrecognized slash commands are stripped to avoid agent subprocess hangs.\n const forwardText = text.startsWith(\"/\") ? text.slice(1) : text;\n\n // All topics (including assistant) → forward to core\n const sessionId = (await this.core.getOrResumeSession(\n \"telegram\",\n String(threadId),\n ))?.id;\n if (sessionId) {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"incoming:message\", sessionId, userId: String(ctx.from?.id), text: ctx.message?.text });\n const assistantSession = this.core.assistantManager?.get('telegram');\n await this.draftManager.finalize(sessionId, assistantSession?.id);\n }\n if (sessionId) {\n await this.drainAndResetTracker(sessionId);\n }\n ctx.replyWithChatAction(\"typing\").catch(() => {});\n const fromName = [ctx.from.first_name, ctx.from.last_name].filter(Boolean).join(' ') || undefined\n this.core\n .handleMessage(\n {\n channelId: \"telegram\",\n threadId: String(threadId),\n userId: String(ctx.from.id),\n text: forwardText,\n },\n // Inject structured channel user info into TurnMeta so plugins can identify\n // the sender by name without adapter-specific fields on IncomingMessage.\n {\n channelUser: {\n channelId: 'telegram',\n userId: String(ctx.from.id),\n displayName: fromName,\n username: ctx.from.username,\n },\n },\n )\n .catch((err) => log.error({ err }, \"handleMessage error\"));\n });\n\n // --- Incoming media handlers ---\n\n this.bot.on(\"message:photo\", async (ctx) => {\n const threadId = ctx.message.message_thread_id;\n if (!threadId || threadId === this.notificationTopicId) return;\n\n const photos = ctx.message.photo;\n const largest = photos[photos.length - 1];\n const ext = \".jpg\";\n await this.handleIncomingMedia(\n threadId,\n ctx.from.id,\n largest.file_id,\n `photo${ext}`,\n \"image/jpeg\",\n ctx.message.caption || undefined,\n );\n });\n\n this.bot.on(\"message:document\", async (ctx) => {\n const threadId = ctx.message.message_thread_id;\n if (!threadId || threadId === this.notificationTopicId) return;\n\n const doc = ctx.message.document;\n await this.handleIncomingMedia(\n threadId,\n ctx.from.id,\n doc.file_id,\n doc.file_name || \"document\",\n doc.mime_type || \"application/octet-stream\",\n ctx.message.caption || undefined,\n );\n });\n\n this.bot.on(\"message:voice\", async (ctx) => {\n const threadId = ctx.message.message_thread_id;\n if (!threadId || threadId === this.notificationTopicId) return;\n\n const voice = ctx.message.voice;\n await this.handleIncomingMedia(\n threadId,\n ctx.from.id,\n voice.file_id,\n \"voice.wav\",\n \"audio/wav\",\n undefined,\n true,\n );\n });\n\n this.bot.on(\"message:audio\", async (ctx) => {\n const threadId = ctx.message.message_thread_id;\n if (!threadId || threadId === this.notificationTopicId) return;\n\n const audio = ctx.message.audio;\n await this.handleIncomingMedia(\n threadId,\n ctx.from.id,\n audio.file_id,\n audio.file_name || \"audio.mp3\",\n audio.mime_type || \"audio/mpeg\",\n ctx.message.caption || undefined,\n );\n });\n\n this.bot.on(\"message:video_note\", async (ctx) => {\n const threadId = ctx.message.message_thread_id;\n if (!threadId || threadId === this.notificationTopicId) return;\n\n const videoNote = ctx.message.video_note;\n await this.handleIncomingMedia(\n threadId,\n ctx.from.id,\n videoNote.file_id,\n \"video_note.mp4\",\n \"video/mp4\",\n );\n });\n }\n\n // --- MessagingAdapter overrides ---\n\n /**\n * Per-session serial dispatch queues.\n * SessionBridge fires sendMessage() as fire-and-forget, so multiple events\n * (tool_call, tool_update, text) can arrive concurrently. Without serialization,\n * fast handlers (tool_update) overtake slow ones (tool_call with draftManager.finalize),\n * causing out-of-order processing where a tool's completion update is processed before\n * its creation event. This queue ensures events are processed in the order they arrive.\n */\n private _dispatchQueues = new Map<string, Promise<void>>();\n\n /**\n * Drain pending event dispatches from the previous prompt, then reset the\n * activity tracker so late tool_call events don't leak into the new card.\n */\n private async drainAndResetTracker(sessionId: string): Promise<void> {\n const pendingDispatch = this._dispatchQueues.get(sessionId);\n if (pendingDispatch) await pendingDispatch;\n\n const tracker = this.sessionTrackers.get(sessionId);\n if (tracker) await tracker.onNewPrompt();\n }\n\n private getTracer(sessionId: string): DebugTracer | null {\n return this.core.sessionManager.getSession(sessionId)?.agentInstance?.debugTracer ?? null;\n }\n\n /**\n * Primary outbound dispatch method — routes an agent message to the session's Telegram topic.\n *\n * Wraps the base class `sendMessage` in a per-session promise chain (`_dispatchQueues`)\n * so that concurrent events fired from SessionBridge are serialized and delivered in the\n * order they arrive, preventing fast handlers from overtaking slower ones.\n */\n async sendMessage(\n sessionId: string,\n content: OutgoingMessage,\n ): Promise<void> {\n const session = this.core.sessionManager.getSession(sessionId);\n if (!session) return;\n\n // Drop messages while topic is being recreated (archive in progress)\n if (session.archiving) return;\n const threadId = Number(session.threadId);\n if (!threadId || isNaN(threadId)) {\n log.warn(\n { sessionId, threadId: session.threadId },\n \"Session has no valid threadId, skipping message\",\n );\n return;\n }\n\n // Serialize dispatch per session to preserve event ordering\n const prev = this._dispatchQueues.get(sessionId) ?? Promise.resolve();\n const next = prev.then(async () => {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"dispatch:enter\", sessionId, message: content });\n this._sessionThreadIds.set(sessionId, threadId);\n try {\n await super.sendMessage(sessionId, content);\n } finally {\n this._sessionThreadIds.delete(sessionId);\n }\n }).catch((err) => {\n log.warn({ err, sessionId }, \"Dispatch queue error\");\n });\n this._dispatchQueues.set(sessionId, next);\n await next;\n }\n\n protected async handleThought(\n sessionId: string,\n content: OutgoingMessage,\n _verbosity: DisplayVerbosity,\n ): Promise<void> {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"handle:thought\", sessionId, text: content.text });\n const threadId = this.getThreadId(sessionId);\n const mode = this.outputModeResolver.resolve(\n this.context.configManager,\n this.name,\n sessionId,\n this.core.sessionManager,\n );\n const tracker = this.getOrCreateTracker(sessionId, threadId, mode);\n await tracker.onThought(content.text);\n }\n\n protected async handleText(\n sessionId: string,\n content: OutgoingMessage,\n ): Promise<void> {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"handle:text\", sessionId, text: content.text });\n const threadId = this.getThreadId(sessionId);\n // Per-session dispatch queue serializes all events, so we can safely await here.\n if (!this.draftManager.hasDraft(sessionId)) {\n const mode = this.outputModeResolver.resolve(\n this.context.configManager,\n this.name,\n sessionId,\n this.core.sessionManager,\n );\n const tracker = this.getOrCreateTracker(sessionId, threadId, mode);\n await tracker.onTextStart();\n }\n const draft = this.draftManager.getOrCreate(sessionId, threadId, this.getTracer(sessionId));\n draft.append(content.text);\n this.draftManager.appendText(sessionId, content.text);\n }\n\n protected async handleToolCall(\n sessionId: string,\n content: OutgoingMessage,\n _verbosity: DisplayVerbosity,\n ): Promise<void> {\n const threadId = this.getThreadId(sessionId);\n const meta = (content.metadata ?? {}) as Partial<ToolCallMeta>;\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"handle:toolCall\", sessionId, toolId: meta.id, toolName: meta.name, kind: meta.kind, status: meta.status, displaySummary: meta.displaySummary, rawInput: meta.rawInput });\n\n const mode = this.outputModeResolver.resolve(\n this.context.configManager,\n this.name,\n sessionId,\n this.core.sessionManager,\n );\n const tracker = this.getOrCreateTracker(sessionId, threadId, mode);\n await this.draftManager.finalize(sessionId, this.core.assistantManager?.get('telegram')?.id);\n await tracker.onToolCall(\n {\n id: meta.id ?? \"\",\n name: meta.name ?? content.text ?? \"Tool\",\n kind: meta.kind,\n status: meta.status,\n content: meta.content,\n rawInput: meta.rawInput,\n viewerLinks: meta.viewerLinks,\n viewerFilePath: meta.viewerFilePath,\n displaySummary: meta.displaySummary as string | undefined,\n displayTitle: meta.displayTitle as string | undefined,\n displayKind: meta.displayKind as string | undefined,\n },\n String(meta.kind ?? \"\"),\n meta.rawInput,\n );\n }\n\n protected async handleToolUpdate(\n sessionId: string,\n content: OutgoingMessage,\n _verbosity: DisplayVerbosity,\n ): Promise<void> {\n const threadId = this.getThreadId(sessionId);\n const meta = (content.metadata ?? {}) as Partial<ToolUpdateMeta>;\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"handle:toolUpdate\", sessionId, toolId: meta.id, name: meta.name, kind: meta.kind, status: meta.status, viewerLinks: meta.viewerLinks, viewerFilePath: meta.viewerFilePath });\n const mode = this.outputModeResolver.resolve(\n this.context.configManager,\n this.name,\n sessionId,\n this.core.sessionManager,\n );\n const tracker = this.getOrCreateTracker(sessionId, threadId, mode);\n await tracker.onToolUpdate(\n meta.id ?? \"\",\n meta.status ?? \"completed\",\n meta.viewerLinks as { file?: string; diff?: string } | undefined,\n meta.viewerFilePath as string | undefined,\n typeof meta.content === \"string\" ? meta.content : null,\n meta.rawInput ?? undefined,\n (meta as any).diffStats as { added: number; removed: number } | undefined,\n );\n }\n\n protected async handlePlan(\n sessionId: string,\n content: OutgoingMessage,\n _verbosity: DisplayVerbosity,\n ): Promise<void> {\n const threadId = this.getThreadId(sessionId);\n const meta = (content.metadata ?? {}) as Partial<PlanMetadata>;\n const entries = meta.entries ?? [];\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"handle:plan\", sessionId, entryCount: entries.length });\n const mode = this.outputModeResolver.resolve(\n this.context.configManager,\n this.name,\n sessionId,\n this.core.sessionManager,\n );\n const tracker = this.getOrCreateTracker(sessionId, threadId, mode);\n await tracker.onPlan(\n entries.map((e) => ({\n content: e.content,\n status: e.status as \"pending\" | \"in_progress\" | \"completed\",\n priority: (e.priority ?? \"medium\") as \"high\" | \"medium\" | \"low\",\n })),\n );\n }\n\n protected async handleUsage(\n sessionId: string,\n content: OutgoingMessage,\n verbosity: DisplayVerbosity,\n ): Promise<void> {\n const threadId = this.getThreadId(sessionId);\n const meta = content.metadata as UsageMetadata | undefined;\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"handle:usage\", sessionId, tokensUsed: meta?.tokensUsed, contextSize: meta?.contextSize, cost: (meta as Record<string, unknown>)?.cost });\n await this.draftManager.finalize(sessionId, this.core.assistantManager?.get('telegram')?.id);\n\n // Delete previous usage message for this session before sending the new one\n const prevUsageMsgId = this.usageMsgIds.get(sessionId);\n if (prevUsageMsgId) {\n this.sendQueue\n .enqueue(() => this.bot.api.deleteMessage(this.telegramConfig.chatId, prevUsageMsgId).catch(() => {}))\n .catch(() => {});\n this.usageMsgIds.delete(sessionId);\n }\n\n // Send usage as a separate message (not part of the tool card)\n const usageText = formatUsage(meta ?? {}, verbosity);\n let usageMsgId: number | undefined;\n try {\n const result = await this.sendQueue.enqueue(() =>\n this.bot.api.sendMessage(this.telegramConfig.chatId, usageText, {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n }),\n );\n usageMsgId = result?.message_id;\n if (usageMsgId) this.usageMsgIds.set(sessionId, usageMsgId);\n } catch (err) {\n log.warn({ err, sessionId }, \"Failed to send usage message\");\n }\n\n }\n\n protected async handleAttachment(\n sessionId: string,\n content: OutgoingMessage,\n ): Promise<void> {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"handle:attachment\", sessionId, type: content.attachment?.type, fileName: content.attachment?.fileName });\n const threadId = this.getThreadId(sessionId);\n if (!content.attachment) return;\n const { attachment } = content;\n\n // Telegram bot API upload limit: 50MB\n if (attachment.size > 50 * 1024 * 1024) {\n log.warn(\n {\n sessionId,\n fileName: attachment.fileName,\n size: attachment.size,\n },\n \"File too large for Telegram (>50MB)\",\n );\n await this.sendQueue.enqueue(() =>\n this.bot.api.sendMessage(\n this.telegramConfig.chatId,\n `⚠️ File too large to send (${Math.round(attachment.size / 1024 / 1024)}MB): ${escapeHtml(attachment.fileName)}`,\n { message_thread_id: threadId, parse_mode: \"HTML\" },\n ),\n );\n return;\n }\n\n try {\n const inputFile = new InputFile(attachment.filePath);\n if (attachment.type === \"image\") {\n await this.sendQueue.enqueue(() =>\n this.bot.api.sendPhoto(this.telegramConfig.chatId, inputFile, {\n message_thread_id: threadId,\n }),\n );\n } else if (attachment.type === \"audio\") {\n await this.sendQueue.enqueue(() =>\n this.bot.api.sendVoice(this.telegramConfig.chatId, inputFile, {\n message_thread_id: threadId,\n }),\n );\n // Strip [TTS]...[/TTS] block from the text message after voice is sent\n const draft = this.draftManager.getDraft(sessionId);\n if (draft) {\n draft.stripPattern(/\\[TTS\\][\\s\\S]*?\\[\\/TTS\\]/g).catch(() => {});\n }\n } else {\n await this.sendQueue.enqueue(() =>\n this.bot.api.sendDocument(this.telegramConfig.chatId, inputFile, {\n message_thread_id: threadId,\n }),\n );\n }\n } catch (err) {\n log.error(\n { err, sessionId, fileName: attachment.fileName },\n \"Failed to send attachment\",\n );\n }\n }\n\n protected async handleSessionEnd(\n sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"handle:sessionEnd\", sessionId });\n const threadId = this.getThreadId(sessionId);\n await this.draftManager.finalize(sessionId, this.core.assistantManager?.get('telegram')?.id);\n this.draftManager.cleanup(sessionId);\n await this.skillManager.cleanup(sessionId);\n\n // Notify the Notifications topic once the turn is fully done\n const lastUsageMsgId = this.usageMsgIds.get(sessionId);\n this.usageMsgIds.delete(sessionId);\n if (this.notificationTopicId && sessionId !== this.core.assistantManager?.get('telegram')?.id) {\n const sess = this.core.sessionManager.getSession(sessionId);\n const sessionName = sess?.name || \"Session\";\n const chatIdStr = String(this.telegramConfig.chatId);\n const numericId = chatIdStr.startsWith(\"-100\")\n ? chatIdStr.slice(4)\n : chatIdStr.replace(\"-\", \"\");\n const deepLink = lastUsageMsgId\n ? `https://t.me/c/${numericId}/${threadId}/${lastUsageMsgId}`\n : `https://t.me/c/${numericId}/${threadId}`;\n const text = `✅ <b>${escapeHtml(sessionName)}</b>\\nTask completed.\\n\\n<a href=\"${deepLink}\">→ Go to topic</a>`;\n this.sendQueue\n .enqueue(() =>\n 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 .catch(() => {});\n }\n const tracker = this.sessionTrackers.get(sessionId);\n if (tracker) {\n await tracker.cleanup();\n this.sessionTrackers.delete(sessionId);\n } else {\n await this.sendQueue.enqueue(() =>\n this.bot.api.sendMessage(this.telegramConfig.chatId, `✅ <b>Done</b>`, {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n }),\n );\n }\n }\n\n protected async handleConfigUpdate(\n sessionId: string,\n _content: OutgoingMessage,\n ): Promise<void> {\n await this.updateControlMessage(sessionId);\n }\n\n /**\n * Edit the pinned control message to reflect current session state\n * (model, thought level, mode, bypass status).\n */\n async updateControlMessage(sessionId: string): Promise<void> {\n const session = this.core.sessionManager.getSession(sessionId);\n if (!session) return;\n\n const controlMsgId = this.getControlMsgId(sessionId);\n if (!controlMsgId) return;\n\n const threadId = Number(session.threadId);\n if (!threadId || isNaN(threadId)) return;\n\n const text = buildSessionStatusText(session);\n const keyboard = buildSessionControlKeyboard(\n sessionId,\n isBypassActive(session),\n session.voiceMode === \"on\",\n );\n\n try {\n // Update text first\n await this.bot.api.editMessageText(\n this.telegramConfig.chatId,\n controlMsgId,\n text,\n { parse_mode: \"HTML\" },\n );\n // Then update keyboard separately\n await this.bot.api.editMessageReplyMarkup(\n this.telegramConfig.chatId,\n controlMsgId,\n { reply_markup: keyboard },\n );\n } catch {\n /* message unchanged or deleted — ignore */\n }\n }\n\n protected async handleError(\n sessionId: string,\n content: OutgoingMessage,\n ): Promise<void> {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"handle:error\", sessionId, text: content.text });\n const threadId = this.getThreadId(sessionId);\n await this.draftManager.finalize(sessionId, this.core.assistantManager?.get('telegram')?.id);\n const tracker = this.sessionTrackers.get(sessionId);\n if (tracker) {\n tracker.destroy();\n this.sessionTrackers.delete(sessionId);\n }\n await this.sendQueue.enqueue(() =>\n 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 );\n }\n\n protected async handleSystem(\n sessionId: string,\n content: OutgoingMessage,\n ): Promise<void> {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"handle:system\", sessionId, text: content.text });\n const threadId = this.getThreadId(sessionId);\n await this.sendQueue.enqueue(() =>\n this.bot.api.sendMessage(\n this.telegramConfig.chatId,\n escapeHtml(content.text),\n {\n message_thread_id: threadId,\n parse_mode: \"HTML\",\n disable_notification: true,\n },\n ),\n );\n }\n\n /**\n * Render a PermissionRequest as an inline keyboard in the session topic and\n * notify the Notifications topic. Runs inside a sendQueue item, so\n * notification is fire-and-forget to avoid deadlock.\n */\n async sendPermissionRequest(\n sessionId: string,\n request: PermissionRequest,\n ): Promise<void> {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"permission:send\", sessionId, requestId: request.id, description: request.description });\n log.info({ sessionId, requestId: request.id }, \"Permission request sent\");\n const session = this.core.sessionManager.getSession(sessionId);\n if (!session) return;\n\n await this.sendQueue.enqueue(() =>\n this.permissionHandler.sendPermissionRequest(session, request),\n );\n }\n\n /**\n * Post a notification to the Notifications topic.\n * Assistant session notifications are suppressed — the assistant topic is\n * the user's primary interface and does not need a separate alert.\n */\n async sendNotification(notification: NotificationMessage): Promise<void> {\n this.getTracer(notification.sessionId)?.log(\"telegram\", { action: \"notification:send\", sessionId: notification.sessionId, type: notification.type });\n if (notification.sessionId === this.core.assistantManager?.get('telegram')?.id) return;\n\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 || \"New session\")}</b>\\n`;\n text += escapeHtml(notification.summary);\n\n const deepLink =\n notification.deepLink ??\n (() => {\n const session = this.core.sessionManager.getSession(\n notification.sessionId,\n );\n const threadId = session?.threadId;\n if (!threadId) return undefined;\n const chatIdStr = String(this.telegramConfig.chatId);\n const numericId = chatIdStr.startsWith(\"-100\")\n ? chatIdStr.slice(4)\n : chatIdStr.replace(\"-\", \"\");\n return `https://t.me/c/${numericId}/${threadId}`;\n })();\n\n if (deepLink) {\n text += `\\n\\n<a href=\"${deepLink}\">→ Go to topic</a>`;\n }\n\n await this.sendQueue.enqueue(() =>\n 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\n /**\n * Create a new Telegram forum topic for a session and return its thread ID as a string.\n * Called by the core when a session is created via the API or CLI (not from the Telegram UI).\n *\n * Sends \"⏳ Setting up...\" immediately after topic creation so the user sees\n * activity while the agent subprocess initializes in the background.\n */\n async createSessionThread(sessionId: string, name: string): Promise<string> {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"thread:create\", sessionId, name });\n log.info({ sessionId, name }, \"Session topic created\");\n const threadId = await createSessionTopic(this.bot, this.telegramConfig.chatId, name);\n // Non-critical — fire and forget so topic creation still succeeds even if this fails\n this.bot.api.sendMessage(\n this.telegramConfig.chatId,\n \"⏳ Setting up session, please wait...\",\n { message_thread_id: threadId, parse_mode: \"HTML\" },\n ).catch((err) => {\n log.warn({ err, sessionId, threadId }, \"Failed to send setup message to new topic\");\n });\n return String(threadId);\n }\n\n /**\n * Rename the forum topic for a session and update the session record's display name.\n * No-ops silently if the session doesn't have a threadId yet (e.g. still initializing).\n */\n async renameSessionThread(sessionId: string, newName: string): Promise<void> {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"thread:rename\", sessionId, newName });\n const session = this.core.sessionManager.getSession(sessionId);\n if (!session) return;\n const threadId = Number(session.threadId);\n if (!threadId) {\n log.debug({ sessionId, newName }, \"Cannot rename thread — threadId not set yet\");\n return;\n }\n await renameSessionTopic(\n this.bot,\n this.telegramConfig.chatId,\n threadId,\n newName,\n );\n await this.core.sessionManager.patchRecord(sessionId, { name: newName });\n }\n\n /** Delete the forum topic associated with a session. */\n async deleteSessionThread(sessionId: string): Promise<void> {\n const record = this.core.sessionManager.getSessionRecord(sessionId);\n const platform = record?.platform as\n | import(\"../../core/types.js\").TelegramPlatformData\n | undefined;\n const topicId = platform?.topicId;\n if (!topicId) return;\n\n try {\n await this.bot.api.deleteForumTopic(this.telegramConfig.chatId, topicId);\n } catch (err) {\n log.warn(\n { err, sessionId, topicId },\n \"Failed to delete forum topic (may already be deleted)\",\n );\n }\n }\n\n /**\n * Display or update the pinned skill commands message for a session.\n * If the session's threadId is not yet set (e.g. session created from API),\n * the commands are queued and flushed once the thread becomes available.\n */\n async sendSkillCommands(\n sessionId: string,\n commands: AgentCommand[],\n ): Promise<void> {\n if (sessionId === this.core.assistantManager?.get('telegram')?.id) return;\n\n const session = this.core.sessionManager.getSession(sessionId);\n if (!session) return;\n const threadId = Number(session.threadId);\n if (!threadId) {\n // Queue for later — flushed when threadId is assigned via flushPendingSkillCommands()\n this._pendingSkillCommands.set(sessionId, commands);\n return;\n }\n\n await this.skillManager.send(sessionId, threadId, commands);\n }\n\n /** Flush any skill commands that were queued before threadId was available */\n async flushPendingSkillCommands(sessionId: string): Promise<void> {\n const commands = this._pendingSkillCommands.get(sessionId);\n if (!commands) return;\n this._pendingSkillCommands.delete(sessionId);\n const session = this.core.sessionManager.getSession(sessionId);\n if (!session) return;\n const threadId = Number(session.threadId);\n if (!threadId) return;\n await this.skillManager.send(sessionId, threadId, commands);\n }\n\n private async resolveSessionId(threadId: number): Promise<string | undefined> {\n return (await this.core.getOrResumeSession(\n \"telegram\",\n String(threadId),\n ))?.id;\n }\n\n private async downloadTelegramFile(\n fileId: string,\n ): Promise<{ buffer: Buffer; filePath: string } | null> {\n try {\n const file = await this.bot.api.getFile(fileId);\n if (!file.file_path) return null;\n const url = `https://api.telegram.org/file/bot${this.telegramConfig.botToken}/${file.file_path}`;\n const response = await fetch(url);\n if (!response.ok) return null;\n const buffer = Buffer.from(await response.arrayBuffer());\n return { buffer, filePath: file.file_path };\n } catch (err) {\n log.error({ err }, \"Failed to download file from Telegram\");\n return null;\n }\n }\n\n private async handleIncomingMedia(\n threadId: number,\n userId: number,\n fileId: string,\n fileName: string,\n mimeType: string,\n caption?: string,\n convertOggToWav?: boolean,\n ): Promise<void> {\n const downloaded = await this.downloadTelegramFile(fileId);\n if (!downloaded) return;\n\n let buffer = downloaded.buffer;\n let originalFilePath: string | undefined;\n const sessionId = (await this.resolveSessionId(threadId)) || \"unknown\";\n\n if (convertOggToWav) {\n // Save original OGG for STT (smaller, API-compatible)\n const oggAtt = await this.fileService.saveFile(\n sessionId,\n \"voice.ogg\",\n downloaded.buffer,\n \"audio/ogg\",\n );\n originalFilePath = oggAtt.filePath;\n\n try {\n buffer = await this.fileService.convertOggToWav(buffer);\n } catch (err) {\n log.warn({ err }, \"OGG→WAV conversion failed, saving original OGG\");\n fileName = \"voice.ogg\";\n mimeType = \"audio/ogg\";\n originalFilePath = undefined;\n }\n }\n\n const att = await this.fileService.saveFile(\n sessionId,\n fileName,\n buffer,\n mimeType,\n );\n if (originalFilePath) {\n att.originalFilePath = originalFilePath;\n }\n\n const rawText =\n caption ||\n `[${att.type === \"image\" ? \"Photo\" : att.type === \"audio\" ? \"Audio\" : \"File\"}: ${att.fileName}]`;\n const text = rawText.startsWith(\"/\") ? rawText.slice(1) : rawText;\n\n // Assistant topic\n if (threadId === this.assistantTopicId) {\n const assistantSession = this.core.assistantManager?.get('telegram');\n if (assistantSession) {\n await assistantSession.enqueuePrompt(text, [att]);\n }\n return;\n }\n\n // Session topic\n const sid = await this.resolveSessionId(threadId);\n if (sid) {\n await this.draftManager.finalize(sid, this.core.assistantManager?.get('telegram')?.id);\n await this.drainAndResetTracker(sid);\n }\n this.core\n .handleMessage({\n channelId: \"telegram\",\n threadId: String(threadId),\n userId: String(userId),\n text,\n attachments: [att],\n })\n .catch((err) => log.error({ err }, \"handleMessage error\"));\n }\n\n /**\n * Remove skill slash commands from the Telegram bot command list for a session.\n *\n * Clears any queued pending commands that hadn't been sent yet, then delegates\n * to `SkillCommandManager` to delete the commands from the Telegram API. Called\n * when a session with registered skill commands ends.\n */\n async cleanupSkillCommands(sessionId: string): Promise<void> {\n this._pendingSkillCommands.delete(sessionId);\n await this.skillManager.cleanup(sessionId);\n }\n\n /**\n * Clean up all adapter state associated with a session.\n *\n * Finalizes and discards any in-flight draft, destroys the activity tracker\n * (stopping ThinkingIndicator timers and finalizing any open ToolCard), and\n * clears pending skill commands. Called when a session ends or is reset.\n */\n async cleanupSessionState(sessionId: string): Promise<void> {\n this._pendingSkillCommands.delete(sessionId);\n // Finalize and clean up draft state\n await this.draftManager.finalize(sessionId, this.core.assistantManager?.get('telegram')?.id);\n this.draftManager.cleanup(sessionId);\n\n // Destroy activity tracker (stops ThinkingIndicator timers, finalizes ToolCard)\n const tracker = this.sessionTrackers.get(sessionId);\n if (tracker) {\n tracker.destroy();\n this.sessionTrackers.delete(sessionId);\n }\n }\n\n /**\n * Remove `[TTS]...[/TTS]` blocks from the active or finalized draft for a session.\n *\n * The agent embeds these blocks so the speech plugin can extract the TTS text, but\n * they should never appear in the chat message. Called after TTS audio has been sent.\n */\n async stripTTSBlock(sessionId: string): Promise<void> {\n await this.draftManager.stripPattern(sessionId, /\\[TTS\\][\\s\\S]*?\\[\\/TTS\\]/g);\n }\n\n /**\n * Archive a session by deleting its forum topic.\n *\n * Sets `session.archiving = true` to suppress any outgoing messages while the\n * topic is being torn down, finalizes pending drafts, cleans up all trackers,\n * then deletes the Telegram topic (which removes all messages).\n */\n async archiveSessionTopic(sessionId: string): Promise<void> {\n this.getTracer(sessionId)?.log(\"telegram\", { action: \"thread:archive\", sessionId });\n const core = this.core as OpenACPCore;\n const session = core.sessionManager.getSession(sessionId);\n\n const chatId = this.telegramConfig.chatId;\n let oldTopicId: number;\n\n if (session) {\n // Set archiving flag — sendMessage will skip while this is true.\n session.archiving = true;\n oldTopicId = Number(session.threadId);\n\n // Finalize any pending draft\n await this.draftManager.finalize(session.id, this.core.assistantManager?.get('telegram')?.id);\n\n // Cleanup all trackers\n this.draftManager.cleanup(session.id);\n await this.skillManager.cleanup(session.id);\n const tracker = this.sessionTrackers.get(session.id);\n if (tracker) {\n tracker.destroy();\n this.sessionTrackers.delete(session.id);\n }\n } else {\n // Session not in memory — get topicId from stored record\n const record = core.sessionManager.getSessionRecord(sessionId);\n const platform = record?.platform as TelegramPlatformData | undefined;\n if (!platform?.topicId) throw new Error(\"Session not found\");\n oldTopicId = platform.topicId;\n }\n\n // Delete topic (removes all messages) — no recreation\n await deleteSessionTopic(this.bot, chatId, oldTopicId);\n }\n}\n","// ─── Core modules ───────────────────────────────────────────────────────────\nexport * from \"./types.js\";\nexport {\n log,\n initLogger,\n createChildLogger,\n createSessionLogger,\n shutdownLogger,\n cleanupOldSessionLogs,\n setLogLevel,\n type Logger,\n} from \"./utils/log.js\";\nexport {\n ChannelAdapter,\n type IChannelAdapter,\n type AdapterCapabilities,\n type ChannelConfig,\n} from \"./channel.js\";\nexport { nodeToWebWritable, nodeToWebReadable } from \"./utils/streams.js\";\nexport { StderrCapture } from \"./utils/stderr-capture.js\";\nexport {\n ConfigManager,\n expandHome,\n type Config,\n type LoggingConfig,\n} from \"./config/config.js\";\nexport { AgentInstance } from \"./agents/agent-instance.js\";\nexport { AgentManager } from \"./agents/agent-manager.js\";\nexport { Session, type SessionEvents } from \"./sessions/session.js\";\nexport { TypedEmitter } from \"./utils/typed-emitter.js\";\nexport { PromptQueue } from \"./sessions/prompt-queue.js\";\nexport { PermissionGate } from \"./sessions/permission-gate.js\";\nexport { MessageTransformer } from \"./message-transformer.js\";\nexport type { FileServiceInterface } from \"./plugin/types.js\";\nexport { SessionManager, type SessionSummary } from \"./sessions/session-manager.js\";\nexport { SessionBridge, type BridgeDeps } from \"./sessions/session-bridge.js\";\nexport type { TurnContext, TurnRouting } from \"./sessions/turn-context.js\";\nexport { createTurnContext, getEffectiveTarget, isSystemEvent } from \"./sessions/turn-context.js\";\nexport {\n SessionFactory,\n type SessionCreateParams,\n type SideEffectDeps,\n} from \"./sessions/session-factory.js\";\nexport { OpenACPCore } from \"./core.js\";\nexport { EventBus, type EventBusEvents } from \"./event-bus.js\";\nexport { MenuRegistry, type MenuItem } from './menu-registry.js';\nexport { AssistantRegistry, AssistantManager, type AssistantSection, type AssistantCommand } from './assistant/index.js';\nexport { CommandRegistry } from \"./command-registry.js\";\nexport { DoctorEngine, type DoctorReport, type PendingFix } from \"./doctor/index.js\";\nexport {\n CONFIG_REGISTRY,\n getFieldDef,\n getSafeFields,\n isHotReloadable,\n resolveOptions,\n getConfigValue,\n type ConfigFieldDef,\n} from \"./config/config-registry.js\";\nexport { runConfigEditor } from \"./config/config-editor.js\";\nexport { startDaemon, stopDaemon, getStatus, getPidPath } from \"../cli/daemon.js\";\nexport {\n installAutoStart,\n uninstallAutoStart,\n isAutoStartInstalled,\n isAutoStartSupported,\n} from \"../cli/autostart.js\";\n\n// ─── Plugin re-exports (convenience for @n1creator/openacp-cli consumers) ──────────────\n// These are NOT core dependencies — they're re-exported for public API\n// convenience so consumers don't need to import from deep plugin paths.\nexport { NotificationManager } from \"../plugins/notifications/notification.js\";\nexport { FileService } from \"../plugins/file-service/file-service.js\";\nexport { SecurityGuard } from \"../plugins/security/security-guard.js\";\nexport { type ApiConfig } from \"../plugins/api-server/index.js\";\nexport { createApiServer, type ApiServerInstance, type ApiServerOptions } from \"../plugins/api-server/server.js\";\nexport { createApiServerService, type ApiServerService } from \"../plugins/api-server/service.js\";\nexport { SSEManager } from \"../plugins/api-server/sse-manager.js\";\nexport { StaticServer } from \"../plugins/api-server/static-server.js\";\nexport {\n TopicManager,\n type TopicInfo,\n type DeleteTopicResult,\n type CleanupResult,\n} from \"../plugins/telegram/topic-manager.js\";\nexport { SpeechService, GroqSTT } from \"../plugins/speech/exports.js\";\nexport type {\n STTProvider,\n TTSProvider,\n STTOptions,\n STTResult,\n TTSOptions,\n TTSResult,\n SpeechServiceConfig,\n SpeechProviderConfig,\n} from \"../plugins/speech/exports.js\";\nexport type {\n ContextProvider,\n ContextQuery,\n ContextOptions,\n ContextResult,\n SessionInfo as ContextSessionInfo,\n SessionListResult,\n} from \"../plugins/context/context-provider.js\";\nexport { ContextManager } from \"../plugins/context/context-manager.js\";\nexport { EntireProvider } from \"../plugins/context/entire/entire-provider.js\";\n\n// ─── Adapter primitives ─────────────────────────────────────────────────────\nexport { MessagingAdapter, StreamAdapter } from './adapter-primitives/index.js'\nexport type { MessagingAdapterConfig } from './adapter-primitives/index.js'\nexport { BaseRenderer } from './adapter-primitives/index.js'\nexport type { IRenderer, RenderedMessage } from './adapter-primitives/index.js'\nexport { SendQueue, DraftManager, ToolCallTracker, ActivityTracker } from './adapter-primitives/index.js'\nexport type { DisplayVerbosity, ToolCallMeta, ToolUpdateMeta, ViewerLinks } from './adapter-primitives/index.js'\nexport { STATUS_ICONS, KIND_ICONS } from './adapter-primitives/index.js'\nexport { progressBar, formatTokens, truncateContent, stripCodeFences, splitMessage } from './adapter-primitives/index.js'\nexport { extractContentText, formatToolSummary, formatToolTitle, resolveToolIcon } from './adapter-primitives/index.js'\nexport { ToolStateMap, ThoughtBuffer } from './adapter-primitives/index.js'\nexport type { ToolEntry } from './adapter-primitives/index.js'\nexport { DisplaySpecBuilder } from './adapter-primitives/index.js'\nexport type { ToolDisplaySpec, ThoughtDisplaySpec } from './adapter-primitives/index.js'\nexport { OutputModeResolver } from './adapter-primitives/index.js'\nexport type { OutputMode } from './adapter-primitives/index.js'\nexport { ToolCardState } from './adapter-primitives/index.js'\nexport type { ToolCardSnapshot, ToolCardStateConfig } from './adapter-primitives/index.js'\n\n// ─── Plugin types (for SDK re-exports) ──────────────────────────────────────\nexport type {\n OpenACPPlugin, PluginContext, PluginPermission, PluginStorage,\n InstallContext, MigrateContext, TerminalIO, SettingsAPI,\n CommandDef, CommandArgs, CommandResponse, MenuOption, ListItem,\n SecurityService, NotificationService, UsageService,\n SpeechServiceInterface, TunnelServiceInterface, ContextService,\n FieldDef, MiddlewarePayloadMap, MiddlewareHook,\n} from './plugin/types.js'\nexport { PRODUCT_GUIDE } from '../data/product-guide.js'\n","import type { OutgoingMessage, PermissionRequest, NotificationMessage, AgentCommand } from './types.js'\n\n/**\n * Configuration for an adapter channel (Telegram, Slack, etc.).\n * Each adapter defines its own fields beyond `enabled`.\n */\nexport interface ChannelConfig {\n enabled: boolean\n [key: string]: unknown\n}\n\n/**\n * Declares what a messaging platform supports. Core uses these to decide\n * whether to attempt features like streaming, file uploads, or voice.\n */\nexport interface AdapterCapabilities {\n streaming: boolean\n richFormatting: boolean\n threads: boolean\n reactions: boolean\n fileUpload: boolean\n voice: boolean\n}\n\n/**\n * Contract for a messaging platform adapter.\n *\n * A \"channel\" in OpenACP is identified by an adapter name (e.g. \"telegram\", \"slack\").\n * Each session binds to a channel + thread ID — together they form a unique conversation\n * location. The adapter is responsible for platform-specific I/O: sending messages,\n * creating threads/topics, handling permission buttons, etc.\n *\n * Core calls adapter methods via SessionBridge (for agent events) or directly\n * (for session lifecycle operations like thread creation and archiving).\n */\nexport interface IChannelAdapter {\n readonly name: string\n readonly capabilities: AdapterCapabilities\n\n start(): Promise<void>\n stop(): Promise<void>\n\n // --- Outgoing: core → platform ---\n sendMessage(sessionId: string, content: OutgoingMessage): Promise<void>\n sendPermissionRequest(sessionId: string, request: PermissionRequest): Promise<void>\n sendNotification(notification: NotificationMessage): Promise<void>\n\n // --- Session lifecycle on platform side ---\n /** Create a thread/topic for a session. Returns the platform-specific thread ID. */\n createSessionThread(sessionId: string, name: string): Promise<string>\n renameSessionThread(sessionId: string, newName: string): Promise<void>\n deleteSessionThread?(sessionId: string): Promise<void>\n archiveSessionTopic?(sessionId: string): Promise<void>\n\n // TTS strip — optional, called after TTS audio is synthesized to remove [TTS] block from text\n stripTTSBlock?(sessionId: string): Promise<void>\n\n // --- Skill commands — optional, for agents that expose interactive commands ---\n sendSkillCommands?(sessionId: string, commands: AgentCommand[]): Promise<void>\n cleanupSkillCommands?(sessionId: string): Promise<void>\n /** Flush skill commands that were queued before threadId was available. */\n flushPendingSkillCommands?(sessionId: string): Promise<void>\n\n // Agent switch cleanup — optional, called when switching agents to clear adapter-side per-session state\n cleanupSessionState?(sessionId: string): Promise<void>\n\n // --- User-targeted notifications (optional) ---\n /** Send a notification directly to a user by platform ID. Best-effort delivery. */\n sendUserNotification?(\n platformId: string,\n message: NotificationMessage,\n options?: {\n via?: 'dm' | 'thread' | 'topic'\n topicId?: string\n sessionId?: string\n platformMention?: { platformUsername?: string; platformId: string }\n }\n ): Promise<void>\n}\n\n/**\n * Original base class for channel adapters. Provides default no-op implementations\n * for optional IChannelAdapter methods so subclasses only need to implement the\n * methods they care about.\n *\n * This class predates the adapter-primitives package. It has since been superseded\n * by MessagingAdapter and StreamAdapter, which add structured send queuing, streaming\n * support, and platform-specific rendering out of the box.\n *\n * @deprecated Use MessagingAdapter or StreamAdapter instead. Kept for backward compat during migration.\n */\nexport abstract class ChannelAdapter<TCore = unknown> implements IChannelAdapter {\n abstract readonly name: string\n readonly capabilities: AdapterCapabilities = {\n streaming: false, richFormatting: false, threads: false,\n reactions: false, fileUpload: false, voice: false,\n }\n\n constructor(public readonly core: TCore, protected config: ChannelConfig) {}\n\n abstract start(): Promise<void>\n abstract stop(): Promise<void>\n\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 abstract createSessionThread(sessionId: string, name: string): Promise<string>\n abstract renameSessionThread(sessionId: string, newName: string): Promise<void>\n async deleteSessionThread(_sessionId: string): Promise<void> {}\n\n async sendSkillCommands(_sessionId: string, _commands: AgentCommand[]): Promise<void> {}\n async cleanupSkillCommands(_sessionId: string): Promise<void> {}\n async cleanupSessionState(_sessionId: string): Promise<void> {}\n async archiveSessionTopic(_sessionId: string): Promise<void> {}\n async sendUserNotification(_platformId: string, _message: NotificationMessage, _options?: any): Promise<void> {}\n}\n","// The ACP SDK uses the Web Streams API (ReadableStream / WritableStream),\n// but Node's child_process.spawn returns Node streams. These adapters bridge\n// the two so AgentInstance can pipe subprocess stdio through the ACP SDK.\n\n/**\n * Wrap a Node.js WritableStream as a Web API WritableStream.\n *\n * Handles backpressure by waiting for the 'drain' event when the Node\n * stream's internal buffer is full.\n */\nexport function nodeToWebWritable(nodeStream: NodeJS.WritableStream): WritableStream<Uint8Array> {\n return new WritableStream<Uint8Array>({\n write(chunk) {\n return new Promise<void>((resolve, reject) => {\n const ok = nodeStream.write(chunk);\n if (ok) { resolve(); return; }\n const onDrain = () => { (nodeStream as any).removeListener(\"error\", onError); resolve(); };\n const onError = (err: Error) => { (nodeStream as any).removeListener(\"drain\", onDrain); reject(err); };\n (nodeStream as any).once(\"drain\", onDrain);\n (nodeStream as any).once(\"error\", onError);\n });\n },\n close() {\n (nodeStream as any).end();\n },\n abort(reason) {\n (nodeStream as any).destroy(reason instanceof Error ? reason : new Error(String(reason)));\n },\n });\n}\n\n/**\n * Wrap a Node.js ReadableStream as a Web API ReadableStream.\n *\n * Converts Node Buffer chunks to Uint8Array for Web Streams compatibility.\n */\nexport function nodeToWebReadable(nodeStream: NodeJS.ReadableStream): ReadableStream<Uint8Array> {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n nodeStream.on(\"data\", (chunk: Buffer) => controller.enqueue(new Uint8Array(chunk)));\n nodeStream.on(\"end\", () => controller.close());\n nodeStream.on(\"error\", (err) => controller.error(err));\n },\n cancel() {\n (nodeStream as any).destroy();\n },\n });\n}\n","/**\n * Rolling buffer that captures the last N lines of an agent subprocess's stderr.\n *\n * Agent stdout carries the ACP protocol (JSON-RPC); stderr is used for\n * debug/diagnostic output. This capture provides context when the agent\n * crashes or errors — the last lines of stderr are included in error\n * messages shown to the user.\n */\nexport class StderrCapture {\n private lines: string[] = []\n\n constructor(private maxLines: number = 50) {}\n\n /** Append a chunk of stderr output, splitting on newlines and trimming to maxLines. */\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 /** Return all captured lines joined as a single string. */\n getLastLines(): string {\n return this.lines.join('\\n')\n }\n}\n","/**\n * AgentInstance — the ACP Client implementation that manages an agent subprocess.\n *\n * This is the lowest-level boundary between OpenACP and an external AI agent.\n * It spawns the agent as a child process, communicates over stdin/stdout using\n * the Agent Client Protocol (newline-delimited JSON), and translates ACP events\n * into the internal `AgentEvent` union consumed by Session and adapters.\n *\n * Lifecycle: spawn → ACP initialize → newSession/loadSession → prompt ↔ events → destroy\n *\n * Session wraps AgentInstance to add prompt queuing, auto-naming, and\n * permission gating. This module should NOT be used directly by adapters.\n */\n\nimport { spawn, execFileSync, type ChildProcess } from \"node:child_process\";\nimport { Transform } from \"node:stream\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { ClientSideConnection, ndJsonStream } from \"@agentclientprotocol/sdk\";\nimport { PathGuard } from \"../security/path-guard.js\";\nimport { filterEnv } from \"../security/env-filter.js\";\nimport type {\n Agent,\n Client,\n PromptResponse,\n PermissionOption as SdkPermissionOption,\n} from \"@agentclientprotocol/sdk\";\nimport { nodeToWebWritable, nodeToWebReadable } from \"../utils/streams.js\";\nimport { StderrCapture } from \"../utils/stderr-capture.js\";\nimport { TypedEmitter } from \"../utils/typed-emitter.js\";\nimport type {\n AgentDefinition,\n AgentEvent,\n Attachment,\n ConfigOption,\n McpServerConfig,\n PermissionRequest,\n SetConfigOptionValue,\n} from \"../types.js\";\nimport { readTextFileWithRange } from \"../utils/read-text-file.js\";\nimport { SUPPORTED_IMAGE_MIMES, buildAttachmentNote } from \"./attachment-blocks.js\";\nimport type { MiddlewareChain } from \"../plugin/middleware-chain.js\";\nimport { PROTOCOL_VERSION } from \"@agentclientprotocol/sdk\";\nimport { TerminalManager } from \"../sessions/terminal-manager.js\";\nimport { McpManager } from \"./mcp-manager.js\";\nimport type {\n ListSessionsResponse,\n LoadSessionResponse,\n ForkSessionResponse,\n SetSessionConfigOptionResponse,\n} from \"@agentclientprotocol/sdk\";\nimport { createDebugTracer, type DebugTracer } from \"../utils/debug-tracer.js\";\nimport { createChildLogger } from \"../utils/log.js\";\nimport { Hook, SessionEv } from \"../events.js\";\nconst log = createChildLogger({ module: \"agent-instance\" });\n\n/**\n * Find the nearest ancestor directory containing package.json.\n *\n * Used by `resolveAgentCommand` to locate node_modules from the package's\n * own install location, which differs between tsc output (`dist/core/`)\n * and tsup bundles (`dist/`).\n */\nfunction findPackageRoot(startDir: string): string {\n let dir = startDir;\n while (dir !== path.dirname(dir)) {\n if (fs.existsSync(path.join(dir, \"package.json\"))) {\n return dir;\n }\n dir = path.dirname(dir);\n }\n return startDir;\n}\n\nfunction commandForWindowsScript(filePath: string): { command: string; args: string[] } {\n const ext = path.extname(filePath).toLowerCase();\n if (process.platform === \"win32\" && (ext === \".cmd\" || ext === \".bat\")) {\n return { command: process.env.ComSpec ?? \"cmd.exe\", args: [\"/d\", \"/s\", \"/c\", `\"${filePath}\"`] };\n }\n return { command: filePath, args: [] };\n}\n\n/**\n * Resolve an agent command name to a directly executable form.\n *\n * Agent commands (e.g. \"claude-agent-acp\") are npm package names, not native\n * binaries. This function locates the actual JS entry point and runs it via\n * `node` directly, avoiding npm shell wrappers that break subprocess stdio\n * piping (the ACP protocol relies on clean stdin/stdout streams).\n *\n * Resolution order:\n * 1. node_modules/<package>/dist/index.js (direct entry point)\n * 2. node_modules/.bin/<cmd> (parse shebang or shell wrapper to find JS target)\n * 3. System PATH via `which`\n * 4. Special handling for npx/uvx: derive from the running Node's bin directory\n * 5. Fallback: use command as-is\n */\nfunction resolveAgentCommand(cmd: string): { command: string; args: string[] } {\n // Directories to search for node_modules: cwd AND the package's own directory\n const searchRoots = [process.cwd()];\n // Add the directory where this package is installed (for global installs)\n // Use findPackageRoot instead of hardcoded \"../..\" to handle both tsc (dist/core/)\n // and tsup bundle (dist/) directory structures correctly\n const ownDir = findPackageRoot(import.meta.dirname);\n if (ownDir !== process.cwd()) {\n searchRoots.push(ownDir);\n }\n\n // 1. Check node_modules for the package's actual JS entry point\n for (const root of searchRoots) {\n const packageDirs = [\n path.resolve(root, \"node_modules\", \"@zed-industries\", cmd, \"dist\", \"index.js\"),\n path.resolve(root, \"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\n // 2. Check .bin — if it's a JS file with shebang, run with node directly\n for (const root of searchRoots) {\n const localBin = path.resolve(root, \"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\n // 3. For npx/uvx: derive from the running Node's bin directory.\n // When openacp is installed globally (e.g. via Homebrew or nvm), npx lives\n // next to the same node binary that is executing this process. The user's\n // shell PATH may not include that directory (common with nvm in non-interactive\n // shells), so resolve it explicitly. On Windows, prefer .cmd/.exe wrappers;\n // extensionless shim files cannot be spawned reliably by CreateProcess.\n if (cmd === \"npx\" || cmd === \"uvx\") {\n const seen = new Set<string>();\n const candidates: string[] = [];\n const addCandidate = (dir: string) => {\n if (!seen.has(dir)) { seen.add(dir); candidates.push(dir); }\n };\n\n addCandidate(path.dirname(process.execPath));\n try { addCandidate(path.dirname(fs.realpathSync(process.execPath))); } catch { /* ignore */ }\n addCandidate(\"/opt/homebrew/bin\");\n addCandidate(\"/usr/local/bin\");\n\n const executableNames = process.platform === \"win32\" ? [`${cmd}.cmd`, `${cmd}.exe`, cmd] : [cmd];\n for (const dir of candidates) {\n if (cmd === \"npx\") {\n // npm layout differs by platform:\n // Windows (nvm-windows): {bindir}/node_modules/npm/bin/npx-cli.js\n // macOS/Linux: {noderoot}/lib/node_modules/npm/bin/npx-cli.js (bindir is {noderoot}/bin)\n const npxCliCandidates = [\n path.join(dir, \"node_modules\", \"npm\", \"bin\", \"npx-cli.js\"),\n path.join(path.dirname(dir), \"lib\", \"node_modules\", \"npm\", \"bin\", \"npx-cli.js\"),\n ];\n for (const npxCli of npxCliCandidates) {\n if (fs.existsSync(npxCli)) {\n log.info({ cmd, resolved: npxCli }, \"Resolved npx CLI from Node installation\");\n return { command: process.execPath, args: [npxCli] };\n }\n }\n }\n for (const name of executableNames) {\n const candidate = path.join(dir, name);\n if (fs.existsSync(candidate)) {\n const resolved = commandForWindowsScript(candidate);\n log.info({ cmd, resolved: candidate }, \"Resolved package runner from fallback search\");\n return resolved;\n }\n }\n }\n log.warn({ cmd, execPath: process.execPath, candidates }, \"Could not find package runner in candidate directories\");\n }\n\n // 4. Try resolving from PATH using which\n try {\n const fullPath = execFileSync(\"which\", [cmd], { encoding: \"utf-8\" }).trim();\n // On Windows, which may return an extensionless shim that CreateProcess cannot spawn directly.\n // Skip it and let the fallback handle it via commandForWindowsScript.\n const isUnspawnableWindowsShim =\n process.platform === \"win32\" && (cmd === \"npx\" || cmd === \"uvx\") && !path.extname(fullPath);\n if (fullPath && !isUnspawnableWindowsShim) {\n try {\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 } catch {\n // Binary file (not readable as utf-8) — use full path directly\n }\n // Found via PATH but not a node script — use the resolved full path\n return commandForWindowsScript(fullPath);\n }\n } catch {\n // which failed — command not on PATH\n }\n\n // 5. Fallback: use command as-is\n return { command: cmd, args: [] };\n}\n\n// TerminalState has been extracted to TerminalManager\n\n// Local types for ACP session update shapes not fully typed by the SDK.\n// The SDK uses a generic `update` object; these interfaces provide type safety\n// for fields we access when converting ACP events to internal AgentEvent types.\ninterface SdkToolCallFields {\n rawInput?: unknown;\n rawOutput?: unknown;\n _meta?: Record<string, unknown>;\n}\n\ninterface SdkSessionInfoUpdate {\n sessionUpdate: 'session_info_update';\n title?: string | null;\n updatedAt?: string | null;\n _meta?: Record<string, unknown>;\n}\n\ninterface SdkConfigOptionUpdate {\n sessionUpdate: 'config_option_update';\n configOptions: unknown[];\n _meta?: Record<string, unknown>;\n}\n\n\ninterface SdkUserMessageChunk {\n sessionUpdate: 'user_message_chunk';\n content: { type: string; text?: string };\n}\n\ninterface SdkReadTextFileParams {\n path: string;\n line?: number;\n limit?: number;\n}\n\n/** Events emitted by AgentInstance — consumed by Session to relay to adapters. */\nexport interface AgentInstanceEvents {\n agent_event: (event: AgentEvent) => void;\n}\n\n/**\n * Manages an ACP agent subprocess and implements the ACP Client interface.\n *\n * Each AgentInstance owns exactly one child process. It handles:\n * - Subprocess spawning with filtered environment and path guarding\n * - ACP protocol handshake (initialize → newSession/loadSession)\n * - Translating ACP session updates into internal AgentEvent types\n * - File I/O and terminal operations requested by the agent\n * - Permission request proxying (agent → Session → adapter → user → agent)\n * - Graceful shutdown (SIGTERM with SIGKILL fallback)\n *\n * Session wraps this class to add prompt queuing and lifecycle management.\n */\nexport class AgentInstance extends TypedEmitter<AgentInstanceEvents> {\n private connection!: ClientSideConnection;\n private child!: ChildProcess;\n private stderrCapture!: StderrCapture;\n /** Manages terminal subprocesses that agents can spawn for shell commands. */\n private terminalManager = new TerminalManager();\n /** Shared across all instances — resolves MCP server configs for ACP sessions. */\n private static mcpManager = new McpManager();\n /** Guards against emitting crash events during intentional shutdown. */\n private _destroying = false;\n /** Restricts agent file I/O to the workspace directory and explicitly allowed paths. */\n private pathGuard!: PathGuard;\n\n sessionId!: string;\n agentName: string;\n promptCapabilities?: { image?: boolean; audio?: boolean };\n agentCapabilities?: import(\"../types.js\").AgentCapabilities;\n /** Preserved from newSession/resumeSession response for ACP state propagation */\n initialSessionResponse?: { modes?: unknown; configOptions?: unknown; models?: unknown };\n middlewareChain?: MiddlewareChain;\n debugTracer: DebugTracer | null = null;\n\n /**\n * Whitelist an additional filesystem path for agent read access.\n *\n * Called by SessionFactory to allow agents to read files outside the\n * workspace (e.g., the file-service upload directory for attachments).\n */\n addAllowedPath(p: string): void {\n this.pathGuard.addAllowedPath(p);\n }\n\n // Callback — set by Session/Core when wiring events. Returns the selected\n // permission option ID. Default no-op auto-selects the first option.\n onPermissionRequest: (request: PermissionRequest) => Promise<string> =\n async () => \"\";\n\n private constructor(agentName: string) {\n super();\n this.agentName = agentName;\n }\n\n /**\n * Spawn the agent child process and complete the ACP protocol handshake.\n *\n * Steps:\n * 1. Resolve the agent command to a directly executable path\n * 2. Create a PathGuard scoped to the working directory\n * 3. Spawn the subprocess with a filtered environment (security: only whitelisted\n * env vars are passed to prevent leaking secrets like API keys)\n * 4. Wire stdin/stdout through debug-tracing Transform streams\n * 5. Convert Node streams → Web streams for the ACP SDK\n * 6. Perform the ACP `initialize` handshake and negotiate capabilities\n *\n * Does NOT create a session — callers must follow up with newSession or loadSession.\n */\n public static async spawnSubprocess(\n agentDef: AgentDefinition,\n workingDirectory: string,\n allowedPaths: 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 const ignorePatterns = PathGuard.loadIgnoreFile(workingDirectory);\n instance.pathGuard = new PathGuard({\n cwd: workingDirectory,\n allowedPaths,\n ignorePatterns,\n });\n\n instance.child = spawn(\n resolved.command,\n [...resolved.args, ...agentDef.args],\n {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n cwd: workingDirectory,\n // envWhitelist from workspace.security.envWhitelist config would extend DEFAULT_ENV_WHITELIST.\n // Tracked as follow-up: pass workspace security config through spawn/resume call chain.\n env: filterEnv(process.env as Record<string, string>, 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 // Capture last 50 stderr lines for crash diagnostics — stderr is NOT part of\n // the ACP protocol (which uses stdout), but agents log errors/warnings there.\n instance.stderrCapture = new StderrCapture(50);\n instance.child.stderr!.on(\"data\", (chunk: Buffer) => {\n instance.stderrCapture.append(chunk.toString());\n });\n\n // stdin/stdout pass-through transforms for ACP protocol tracing.\n // When debugTracer is active, each ndjson message is logged with direction\n // (send/recv) for protocol-level debugging. The transforms are transparent\n // when tracing is off — they pass chunks through without modification.\n const stdinLogger = new Transform({\n transform(chunk, _enc, cb) {\n if (instance.debugTracer) {\n const raw = chunk.toString().trimEnd();\n try {\n instance.debugTracer.log(\"acp\", { dir: \"send\", data: JSON.parse(raw) });\n } catch {\n instance.debugTracer.log(\"acp\", { dir: \"send\", data: raw });\n }\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 if (instance.debugTracer) {\n const raw = chunk.toString().trimEnd();\n try {\n instance.debugTracer.log(\"acp\", { dir: \"recv\", data: JSON.parse(raw) });\n } catch {\n instance.debugTracer.log(\"acp\", { dir: \"recv\", data: raw });\n }\n }\n cb(null, chunk);\n },\n });\n instance.child.stdout!.pipe(stdoutLogger);\n\n // Bridge Node streams → Web Streams API for the ACP SDK, which uses\n // ReadableStream/WritableStream internally for ndjson parsing.\n const toAgent = nodeToWebWritable(stdinLogger);\n const fromAgent = nodeToWebReadable(stdoutLogger);\n const stream = ndJsonStream(toAgent, fromAgent);\n\n // ClientSideConnection is the ACP SDK's client. It sends JSON-RPC requests\n // and routes incoming notifications to the Client callbacks (createClient).\n instance.connection = new ClientSideConnection(\n (_agent: Agent): Client => instance.createClient(_agent),\n stream,\n );\n\n // ACP handshake: negotiate protocol version and declare client capabilities.\n // The agent responds with its own capabilities (image/audio support, etc.).\n const initResponse = await instance.connection.initialize({\n protocolVersion: PROTOCOL_VERSION,\n clientCapabilities: {\n fs: { readTextFile: true, writeTextFile: true },\n terminal: true,\n },\n });\n\n if (initResponse.protocolVersion !== PROTOCOL_VERSION) {\n log.warn(\n { expected: PROTOCOL_VERSION, got: initResponse.protocolVersion },\n \"ACP protocol version mismatch — some features may not work correctly\",\n );\n }\n\n instance.promptCapabilities =\n initResponse.agentCapabilities?.promptCapabilities;\n\n // Store full agent capabilities for introspection (session list, fork, close, etc.)\n instance.agentCapabilities = initResponse.agentCapabilities as import(\"../types.js\").AgentCapabilities | undefined;\n\n log.info(\n { promptCapabilities: instance.promptCapabilities ?? {} },\n \"Agent prompt capabilities\",\n );\n\n return instance;\n }\n\n /**\n * Monitor the subprocess for unexpected exits and emit error events.\n *\n * Distinguishes intentional shutdown (SIGTERM/SIGINT during destroy) from\n * crashes (non-zero exit code or unexpected signal). Crash events include\n * captured stderr output for diagnostic context.\n */\n private setupCrashDetection(): void {\n this.child.on(\"exit\", (code, signal) => {\n if (this._destroying) return;\n log.info(\n { sessionId: this.sessionId, exitCode: code, signal },\n \"Agent process exited\",\n );\n // SIGINT/SIGTERM are graceful shutdown signals — not crashes\n if (signal === \"SIGINT\" || signal === \"SIGTERM\") return;\n if ((code !== 0 && code !== null) || signal) {\n const stderr = this.stderrCapture.getLastLines();\n this.emit(SessionEv.AGENT_EVENT, {\n type: \"error\",\n message: signal\n ? `Agent killed by signal ${signal}\\n${stderr}`\n : `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 /** True if the subprocess has exited or been killed. */\n get isDead(): boolean {\n return this.child.killed || this.child.exitCode !== null;\n }\n\n /**\n * Open a fresh ACP session on a pre-initialized AgentInstance.\n *\n * Pre-conditions:\n * - The instance has been through `spawnSubprocess` (subprocess is running,\n * ACP `initialize` handshake is done, `agentCapabilities` is set).\n * - `sessionId` is unset — calling twice is a programming error.\n *\n * Post-conditions on success:\n * - `sessionId`, `initialSessionResponse`, and `debugTracer` are populated.\n * - `setupCrashDetection` is wired so subsequent process exits emit\n * `AGENT_EVENT` with an error message.\n * - The instance is fully ready for `prompt()` calls.\n *\n * @throws Error(\"AgentInstance already has a session — cannot claim twice\")\n * if `sessionId` is already set.\n * @throws Whatever the agent's `newSession` RPC throws (timeout, ACP error,\n * subprocess exited mid-call). The instance is NOT destroyed in this\n * case — the caller is responsible for cleanup.\n *\n * Called by:\n * - `AgentManager.spawn()` when a warm instance is consumed (only path\n * where `spawnSubprocess` and `claimForSession` are split apart).\n * - `AgentInstance.spawn()` as the second half of the normal spawn path.\n */\n async claimForSession(workingDirectory: string, mcpServers?: McpServerConfig[], timeoutMs?: number): Promise<void> {\n if (this.sessionId) {\n throw new Error(\"AgentInstance already has a session — cannot claim twice\");\n }\n const resolvedMcp = AgentInstance.mcpManager.resolve(mcpServers);\n const response = await withAgentTimeout(\n this.connection.newSession({ cwd: workingDirectory, mcpServers: resolvedMcp as any }),\n this.agentName,\n 'newSession',\n timeoutMs,\n );\n this.sessionId = response.sessionId;\n this.initialSessionResponse = response;\n this.debugTracer = createDebugTracer(response.sessionId, workingDirectory);\n this.setupCrashDetection();\n }\n\n /**\n * Spawn a new agent subprocess and create a fresh ACP session.\n *\n * This is the primary entry point for starting an agent. It spawns the\n * subprocess, completes the ACP handshake, and calls `newSession` to\n * initialize the agent's working context (cwd, MCP servers).\n *\n * @param agentDef - Agent definition (command, args, env) from the catalog\n * @param workingDirectory - Workspace root the agent operates in\n * @param mcpServers - Optional MCP server configs to extend agent capabilities\n * @param allowedPaths - Extra filesystem paths the agent may access\n */\n static async spawn(\n agentDef: AgentDefinition,\n workingDirectory: string,\n mcpServers?: McpServerConfig[],\n allowedPaths?: string[],\n ): Promise<AgentInstance> {\n log.debug({ agentName: agentDef.name, command: agentDef.command }, \"Spawning agent\");\n const spawnStart = Date.now();\n\n const instance = await AgentInstance.spawnSubprocess(agentDef, workingDirectory, allowedPaths);\n await instance.claimForSession(workingDirectory, mcpServers, agentDef.initTimeoutMs);\n\n log.info(\n {\n sessionId: instance.sessionId,\n durationMs: Date.now() - spawnStart,\n configOptions: (instance.initialSessionResponse as any)?.configOptions ?? [],\n agentCapabilities: instance.agentCapabilities ?? null,\n },\n \"Agent spawn complete\",\n );\n return instance;\n }\n\n /**\n * Spawn a new subprocess and restore an existing agent session.\n *\n * Tries loadSession first (preferred, stable API), falls back to the\n * unstable resumeSession, and finally falls back to creating a brand-new\n * session if resume fails entirely (e.g., agent lost its state).\n *\n * @param agentSessionId - The agent-side session ID to restore\n */\n static async resume(\n agentDef: AgentDefinition,\n workingDirectory: string,\n agentSessionId: string,\n mcpServers?: McpServerConfig[],\n allowedPaths?: 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 allowedPaths,\n );\n\n const resolvedMcp = AgentInstance.mcpManager.resolve(mcpServers);\n\n try {\n if (instance.agentCapabilities?.loadSession) {\n // Agent supports session/load — preferred over unstable session/resume\n const response = await withAgentTimeout(\n instance.connection.loadSession({ sessionId: agentSessionId, cwd: workingDirectory, mcpServers: resolvedMcp as any }),\n agentDef.name,\n 'loadSession',\n agentDef.initTimeoutMs,\n );\n instance.sessionId = agentSessionId;\n instance.initialSessionResponse = response;\n instance.debugTracer = createDebugTracer(agentSessionId, workingDirectory);\n log.info(\n {\n sessionId: agentSessionId,\n durationMs: Date.now() - spawnStart,\n agentCapabilities: instance.agentCapabilities ?? null,\n },\n \"Agent load complete\",\n );\n } else {\n const response = await withAgentTimeout(\n instance.connection.resumeSession({ sessionId: agentSessionId, cwd: workingDirectory, mcpServers: resolvedMcp as any }),\n agentDef.name,\n 'resumeSession',\n agentDef.initTimeoutMs,\n );\n instance.sessionId = agentSessionId;\n instance.initialSessionResponse = response;\n instance.debugTracer = createDebugTracer(agentSessionId, workingDirectory);\n log.info(\n {\n sessionId: agentSessionId,\n durationMs: Date.now() - spawnStart,\n agentCapabilities: instance.agentCapabilities ?? null,\n },\n \"Agent resume complete\",\n );\n }\n } catch (err) {\n log.warn(\n { err, agentSessionId },\n \"Resume failed, falling back to new session\",\n );\n const response = await withAgentTimeout(\n instance.connection.newSession({ cwd: workingDirectory, mcpServers: resolvedMcp as any }),\n agentDef.name,\n 'newSession (fallback)',\n agentDef.initTimeoutMs,\n );\n instance.sessionId = response.sessionId;\n instance.initialSessionResponse = response;\n instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);\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 /**\n * Build the ACP Client callback object.\n *\n * The ACP SDK invokes these callbacks when the agent sends notifications\n * or requests. Each callback maps an ACP protocol message to either:\n * - An internal AgentEvent (emitted for Session/adapters to consume)\n * - A filesystem or terminal operation (executed on the agent's behalf)\n * - A permission request (proxied to the user via the adapter)\n */\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 // The agent streams its response as a series of session update events.\n // Each event type maps to an internal AgentEvent that Session relays\n // to adapters for rendering (text chunks, tool calls, usage stats, etc.).\n // Chunks are forwarded to Session individually as they arrive — no buffering\n // happens at this layer. If buffering is needed (e.g., to avoid rate limits),\n // it is the responsibility of the Session or adapter layer.\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 } else if (update.content.type === \"image\") {\n // ACP SDK types don't expose data/mimeType fields for image content\n const c = update.content as unknown as { data: string; mimeType: string };\n event = { type: \"image_content\", data: c.data, mimeType: c.mimeType };\n } else if (update.content.type === \"audio\") {\n // ACP SDK types don't expose data/mimeType fields for audio content\n const c = update.content as unknown as { data: string; mimeType: string };\n event = { type: \"audio_content\", data: c.data, mimeType: c.mimeType };\n } else if (update.content.type === \"resource\") {\n // EmbeddedResource: content.resource is TextResourceContents or BlobResourceContents\n // TextResourceContents has { uri, text, mimeType? }\n // BlobResourceContents has { uri, blob, mimeType? }\n const c = update.content as unknown as {\n resource: { uri: string; text?: string; blob?: string; mimeType?: string };\n annotations?: { audience?: string[]; priority?: number };\n };\n // The EmbeddedResource content block doesn't carry a top-level name field —\n // use the uri as a fallback name since AgentEvent.resource_content requires one.\n event = {\n type: \"resource_content\",\n uri: c.resource.uri,\n name: c.resource.uri,\n text: c.resource.text ?? undefined,\n blob: c.resource.blob ?? undefined,\n mimeType: c.resource.mimeType ?? undefined,\n };\n } else if (update.content.type === \"resource_link\") {\n // ResourceLink: { uri, name, mimeType?, title?, description?, size? }\n const c = update.content as unknown as {\n uri: string;\n name: string;\n mimeType?: string | null;\n title?: string | null;\n description?: string | null;\n size?: number | null;\n };\n event = {\n type: \"resource_link\",\n uri: c.uri,\n name: c.name,\n mimeType: c.mimeType ?? undefined,\n title: c.title ?? undefined,\n description: c.description ?? undefined,\n size: c.size ?? undefined,\n };\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 const tc = update as unknown as SdkToolCallFields;\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 rawInput: tc.rawInput ?? undefined,\n rawOutput: tc.rawOutput ?? undefined,\n meta: tc._meta ?? undefined,\n };\n break;\n }\n case \"tool_call_update\": {\n const tcu = update as unknown as SdkToolCallFields;\n event = {\n type: \"tool_update\",\n id: update.toolCallId,\n name: update.title ?? undefined,\n kind: update.kind ?? undefined,\n status: update.status ?? \"pending\",\n content: update.content ?? undefined,\n rawInput: tcu.rawInput ?? undefined,\n rawOutput: tcu.rawOutput ?? undefined,\n meta: tcu._meta ?? undefined,\n };\n break;\n }\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 case \"session_info_update\": {\n const si = update as unknown as SdkSessionInfoUpdate;\n event = {\n type: \"session_info_update\",\n title: si.title ?? undefined,\n updatedAt: si.updatedAt ?? undefined,\n _meta: si._meta ?? undefined,\n };\n break;\n }\n case \"config_option_update\": {\n const co = update as unknown as SdkConfigOptionUpdate;\n event = {\n type: \"config_option_update\",\n options: (co.configOptions ?? []) as ConfigOption[],\n };\n break;\n }\n case \"user_message_chunk\": {\n const um = update as unknown as SdkUserMessageChunk;\n event = {\n type: \"user_message_chunk\",\n content: um.content?.text ?? \"\",\n };\n break;\n }\n // NOTE: model_update is NOT a session update type in the ACP SDK schema.\n // Model changes are applied via setSessionConfigOption() and the response\n // is synchronous — the SDK does not push a model_update notification to\n // the client. Therefore AgentEvent \"model_update\" cannot originate from\n // sessionUpdate and must be emitted by callers of setConfigOption()\n // if they need to propagate the change downstream.\n default:\n // Unknown update type — ignore\n return;\n }\n\n if (event !== null) {\n self.emit(SessionEv.AGENT_EVENT, event);\n }\n },\n\n // ── Permission requests ──────────────────────────────────────────────\n // The agent needs user approval before performing sensitive operations\n // (e.g., file writes, shell commands). This proxies the request up\n // through Session → PermissionGate → adapter → user, then returns\n // the user's chosen option ID back to the agent.\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 // The agent reads/writes files through these callbacks rather than\n // accessing the filesystem directly. This allows PathGuard to enforce\n // workspace boundaries and middleware hooks to intercept I/O.\n async readTextFile(params) {\n const p = params as unknown as SdkReadTextFileParams;\n // Security: validate path against workspace boundary\n const pathCheck = self.pathGuard.validatePath(p.path, \"read\");\n if (!pathCheck.allowed) {\n throw new Error(`[Access denied] ${pathCheck.reason}`);\n }\n // Hook: fs:beforeRead — modifiable, can block\n if (self.middlewareChain) {\n const result = await self.middlewareChain.execute(Hook.FS_BEFORE_READ, { sessionId: self.sessionId, path: p.path, line: p.line, limit: p.limit }, async (r) => r);\n if (!result) return { content: \"\" }; // blocked by middleware\n p.path = result.path;\n }\n const content = await readTextFileWithRange(p.path, {\n line: p.line ?? undefined,\n limit: p.limit ?? undefined,\n });\n return { content };\n },\n\n async writeTextFile(params) {\n let writePath = params.path;\n let writeContent = params.content;\n // Security: validate path against workspace boundary\n const pathCheck = self.pathGuard.validatePath(writePath, \"write\");\n if (!pathCheck.allowed) {\n throw new Error(`[Access denied] ${pathCheck.reason}`);\n }\n // Hook: fs:beforeWrite — modifiable, can block\n if (self.middlewareChain) {\n const result = await self.middlewareChain.execute(Hook.FS_BEFORE_WRITE, { sessionId: self.sessionId, path: writePath, content: writeContent }, async (r) => r);\n if (!result) return {}; // blocked by middleware\n writePath = result.path;\n writeContent = result.content;\n }\n await fs.promises.mkdir(path.dirname(writePath), { recursive: true });\n await fs.promises.writeFile(writePath, writeContent, \"utf-8\");\n return {};\n },\n\n // ── Terminal operations (delegated to TerminalManager) ─────────────\n // Agents can spawn shell commands via terminal operations. TerminalManager\n // handles subprocess lifecycle, output capture, and byte-limit enforcement.\n async createTerminal(params) {\n return self.terminalManager.createTerminal(\n self.sessionId,\n {\n command: params.command,\n args: params.args,\n env: params.env,\n cwd: params.cwd ?? undefined,\n outputByteLimit: params.outputByteLimit ?? MAX_OUTPUT_BYTES,\n },\n self.middlewareChain,\n );\n },\n\n async terminalOutput(params) {\n return self.terminalManager.getOutput(params.terminalId);\n },\n\n async waitForTerminalExit(params) {\n return self.terminalManager.waitForExit(params.terminalId);\n },\n\n async killTerminal(params) {\n self.terminalManager.kill(params.terminalId);\n return {};\n },\n\n async releaseTerminal(params) {\n self.terminalManager.release(params.terminalId);\n },\n };\n }\n\n // ── New ACP methods ──────────────────────────────────────────────────\n\n /**\n * Update a session config option (mode, model, etc.) on the agent.\n *\n * Falls back to legacy `setSessionMode`/`unstable_setSessionModel` methods\n * for agents that haven't adopted the unified `session/set_config_option`\n * ACP method (detected via JSON-RPC -32601 \"Method Not Found\" error).\n */\n async setConfigOption(\n configId: string,\n value: SetConfigOptionValue,\n ): Promise<SetSessionConfigOptionResponse> {\n try {\n return await this.connection.setSessionConfigOption({\n sessionId: this.sessionId,\n configId,\n ...value,\n } as any);\n } catch (err) {\n // Fall back to legacy setSessionMode / unstable_setSessionModel for agents\n // (e.g. Gemini CLI) that implement the old separate methods instead of the\n // unified session/set_config_option method (ACP -32601 Method Not Found).\n if (typeof err === 'object' && err !== null && (err as any).code === -32601) {\n if (configId === 'mode' && value.type === 'select') {\n await this.connection.setSessionMode({\n sessionId: this.sessionId,\n modeId: value.value as string,\n });\n return { configOptions: [] };\n }\n if (configId === 'model' && value.type === 'select') {\n await this.connection.request(\"session/set_model\", {\n sessionId: this.sessionId,\n modelId: value.value as string,\n });\n return { configOptions: [] };\n }\n }\n throw err;\n }\n }\n\n /** List the agent's known sessions, optionally filtered by working directory. */\n async listSessions(\n cwd?: string,\n cursor?: string,\n ): Promise<ListSessionsResponse> {\n return await this.connection.listSessions({\n cwd: cwd ?? null,\n cursor: cursor ?? null,\n });\n }\n\n /** Load an existing agent session by ID into this subprocess. */\n async loadSession(\n sessionId: string,\n cwd: string,\n mcpServers?: McpServerConfig[],\n ): Promise<LoadSessionResponse> {\n const resolvedMcp = AgentInstance.mcpManager.resolve(mcpServers);\n return await this.connection.loadSession({\n sessionId,\n cwd,\n mcpServers: resolvedMcp as any,\n });\n }\n\n /** Trigger agent-managed authentication (e.g., OAuth flow). */\n async authenticate(methodId: string): Promise<void> {\n await this.connection.authenticate({ methodId });\n }\n\n /** Fork an existing session, creating a new branch with shared history. */\n async forkSession(\n sessionId: string,\n cwd: string,\n mcpServers?: McpServerConfig[],\n ): Promise<ForkSessionResponse> {\n const resolvedMcp = AgentInstance.mcpManager.resolve(mcpServers);\n return await this.connection.unstable_forkSession({\n sessionId,\n cwd,\n mcpServers: resolvedMcp as any,\n });\n }\n\n /** Close a session on the agent side (cleanup agent-internal state). */\n async closeSession(sessionId: string): Promise<void> {\n await this.connection.closeSession({ sessionId });\n }\n\n // ── Prompt & lifecycle ──────────────────────────────────────────────\n\n /**\n * Send a user prompt to the agent and wait for the complete response.\n *\n * Builds ACP content blocks from the text and any attachments (images\n * are base64-encoded if the agent supports them, otherwise appended as\n * file paths). The promise resolves when the agent finishes responding;\n * streaming events arrive via the `agent_event` emitter during execution.\n *\n * Attachments that exceed size limits or use unsupported formats are\n * skipped with a note appended to the prompt text.\n *\n * Call `cancel()` to interrupt a running prompt; the agent will stop and\n * the promise resolves with partial results.\n */\n async prompt(text: string, attachments?: Attachment[]): Promise<PromptResponse> {\n const contentBlocks: Array<Record<string, unknown>> = [{ type: \"text\", text }];\n const capabilities = this.promptCapabilities ?? {};\n\n for (const att of attachments ?? []) {\n // Check if this attachment should be skipped (too large, unsupported format)\n const skipNote = buildAttachmentNote(att, capabilities);\n if (skipNote !== null) {\n (contentBlocks[0] as { text: string }).text += `\\n\\n${skipNote}`;\n continue;\n }\n\n if (att.type === \"image\" && capabilities.image && SUPPORTED_IMAGE_MIMES.has(att.mimeType)) {\n const attCheck = this.pathGuard.validatePath(att.filePath, \"read\");\n if (!attCheck.allowed) {\n (contentBlocks[0] as { text: string }).text += `\\n\\n[Attachment access denied: ${attCheck.reason}]`;\n continue;\n }\n const data = await fs.promises.readFile(att.filePath);\n contentBlocks.push({ type: \"image\", data: data.toString(\"base64\"), mimeType: att.mimeType });\n } else if (att.type === \"audio\" && capabilities.audio) {\n const attCheck = this.pathGuard.validatePath(att.filePath, \"read\");\n if (!attCheck.allowed) {\n (contentBlocks[0] as { text: string }).text += `\\n\\n[Attachment access denied: ${attCheck.reason}]`;\n continue;\n }\n const data = await fs.promises.readFile(att.filePath);\n contentBlocks.push({ type: \"audio\", data: data.toString(\"base64\"), mimeType: att.mimeType });\n } else {\n // Fallback: append file path to text so agent can read from disk\n if (att.type === \"image\" || att.type === \"audio\") {\n log.debug(\n { type: att.type, capabilities },\n \"Agent does not support %s content, falling back to file path\",\n att.type,\n );\n }\n (contentBlocks[0] as { text: string }).text += `\\n\\n[Attached file: ${att.filePath}]`;\n }\n }\n\n return this.connection.prompt({\n sessionId: this.sessionId,\n prompt: contentBlocks as any,\n });\n }\n\n /** Cancel the currently running prompt. The agent should stop and return partial results. */\n async cancel(): Promise<void> {\n await this.connection.cancel({ sessionId: this.sessionId });\n }\n\n /**\n * Gracefully shut down the agent subprocess.\n *\n * Sends SIGTERM first, giving the agent up to 10 seconds to clean up.\n * If the process hasn't exited by then, SIGKILL forces termination.\n * The timer is unref'd so it doesn't keep the Node process alive\n * during shutdown.\n */\n async destroy(): Promise<void> {\n this._destroying = true;\n\n this.terminalManager.destroyAll();\n\n if (this.child.exitCode !== null) return;\n\n await new Promise<void>((resolve) => {\n // Register exit listener BEFORE sending signal to avoid race\n this.child.on(\"exit\", () => {\n clearTimeout(forceKillTimer);\n resolve();\n });\n\n this.child.kill(\"SIGTERM\");\n\n const forceKillTimer = setTimeout(() => {\n // Use exitCode check — child.killed is true after ANY kill() call,\n // even if the process hasn't actually exited yet\n if (this.child.exitCode === null) this.child.kill(\"SIGKILL\");\n resolve();\n }, 10_000);\n if (typeof forceKillTimer === 'object' && forceKillTimer !== null && 'unref' in forceKillTimer) {\n (forceKillTimer as NodeJS.Timeout).unref();\n }\n });\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nconst AGENT_INIT_TIMEOUT_MS = 30_000;\n\n/**\n * Wraps an ACP handshake call (newSession / loadSession / resumeSession) with\n * a timeout so a non-responsive agent process fails fast instead of hanging\n * the HTTP request and blocking a server thread indefinitely.\n */\nfunction withAgentTimeout<T>(promise: Promise<T>, agentName: string, op: string, timeoutMs?: number): Promise<T> {\n const timeout = timeoutMs ?? AGENT_INIT_TIMEOUT_MS;\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(\n `Agent \"${agentName}\" did not respond to ${op} within ${timeout / 1000}s. ` +\n `The agent process may have hung during initialization.`\n ));\n }, timeout);\n\n promise.then(\n (val) => { clearTimeout(timer); resolve(val); },\n (err) => { clearTimeout(timer); reject(err); },\n );\n });\n}\n","/**\n * PathGuard — filesystem access control for agent subprocesses.\n *\n * Agents run with the user's filesystem permissions but should NOT access:\n * - Files outside the workspace directory (prevents system-wide reads/writes)\n * - Sensitive files within the workspace (env files, keys, credentials)\n * - The .openacp/ directory itself (contains bot tokens and API keys)\n *\n * The guard uses a deny-by-default approach: paths must be within the cwd\n * or explicitly allowlisted, AND must not match any deny pattern. Deny\n * patterns use .gitignore syntax via the `ignore` library.\n *\n * Used by AgentInstance when processing file read/write tool calls from agents.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport ignore, { type Ignore } from \"ignore\";\n\n/**\n * Default deny patterns for sensitive files that agents must never access.\n * Uses .gitignore-style glob syntax.\n */\nconst DEFAULT_DENY_PATTERNS = [\n // Environment files — contain API keys, database URLs, etc.\n \".env\",\n \".env.*\",\n // Cryptographic keys\n \"*.key\",\n \"*.pem\",\n // SSH and cloud credentials\n \".ssh/\",\n \".aws/\",\n // OpenACP workspace — contains bot tokens and secrets\n \".openacp/\",\n // Generic credential/secret files\n \"**/credentials*\",\n \"**/secrets*\",\n \"**/*.secret\",\n];\n\n/** Configuration for PathGuard's access control rules. */\nexport interface PathGuardOptions {\n /** Workspace root — files must be within this directory (or allowedPaths). */\n cwd: string;\n /** Additional paths allowed outside cwd (e.g. file-service upload dir). */\n allowedPaths: string[];\n /** Additional deny patterns (merged with DEFAULT_DENY_PATTERNS). */\n ignorePatterns: string[];\n}\n\n/**\n * Enforces path restrictions on agent file operations.\n *\n * Validates that a target path is within the workspace boundary and does\n * not match any deny pattern. Resolves symlinks to prevent traversal attacks\n * (e.g. symlink pointing outside the workspace).\n */\nexport class PathGuard {\n private readonly cwd: string;\n private readonly allowedPaths: string[];\n private readonly ig: Ignore;\n\n constructor(options: PathGuardOptions) {\n // Use realpathSync to resolve symlinks (e.g., /var -> /private/var on macOS)\n try {\n this.cwd = fs.realpathSync(path.resolve(options.cwd));\n } catch {\n this.cwd = path.resolve(options.cwd);\n }\n this.allowedPaths = options.allowedPaths.map((p) => {\n try {\n return fs.realpathSync(path.resolve(p));\n } catch {\n return path.resolve(p);\n }\n });\n this.ig = ignore();\n this.ig.add(DEFAULT_DENY_PATTERNS);\n if (options.ignorePatterns.length > 0) {\n this.ig.add(options.ignorePatterns);\n }\n }\n\n /**\n * Checks whether an agent is allowed to access the given path.\n *\n * Validation order:\n * 1. Write to .openacpignore is always blocked (prevents agents from weakening their own restrictions)\n * 2. Path must be within cwd or an explicitly allowlisted path\n * 3. If within cwd but not allowlisted, path must not match any deny pattern\n *\n * @param targetPath - The path the agent is attempting to access.\n * @param operation - The operation type. Write operations are subject to stricter\n * restrictions than reads — specifically, writing to `.openacpignore` is blocked\n * (to prevent agents from weakening their own restrictions), while reading it is allowed.\n * @returns `{ allowed: true }` or `{ allowed: false, reason: \"...\" }`\n */\n validatePath(\n targetPath: string,\n operation: \"read\" | \"write\",\n ): { allowed: boolean; reason: string } {\n const resolved = path.resolve(targetPath);\n\n let realPath: string;\n try {\n realPath = fs.realpathSync(resolved);\n } catch {\n realPath = resolved;\n }\n\n if (operation === \"write\" && path.basename(realPath) === \".openacpignore\") {\n return { allowed: false, reason: \"Cannot write to .openacpignore\" };\n }\n\n const isWithinCwd =\n realPath === this.cwd || realPath.startsWith(this.cwd + path.sep);\n const isWithinAllowed = this.allowedPaths.some(\n (ap) => realPath === ap || realPath.startsWith(ap + path.sep),\n );\n\n if (!isWithinCwd && !isWithinAllowed) {\n return {\n allowed: false,\n reason: `Path is outside workspace boundary: ${realPath}`,\n };\n }\n\n // allowedPaths explicitly whitelists paths — they override ignore patterns.\n // This lets file-service uploads (e.g. .openacp/files/) be readable even\n // though .openacp/ is in the default deny list.\n if (isWithinCwd && !isWithinAllowed) {\n const relativePath = path.relative(this.cwd, realPath);\n // Allow .openacpignore reads — it is a config file, not sensitive\n if (relativePath === \".openacpignore\") {\n return { allowed: true, reason: \"\" };\n }\n if (relativePath && this.ig.ignores(relativePath)) {\n return {\n allowed: false,\n reason: `Path matches ignore pattern: ${relativePath}`,\n };\n }\n }\n\n return { allowed: true, reason: \"\" };\n }\n\n /** Adds an additional allowed path at runtime (e.g. for file-service uploads). */\n addAllowedPath(p: string): void {\n try {\n this.allowedPaths.push(fs.realpathSync(path.resolve(p)));\n } catch {\n this.allowedPaths.push(path.resolve(p));\n }\n }\n\n /**\n * Loads additional deny patterns from .openacpignore in the workspace root.\n * Follows .gitignore syntax — blank lines and lines starting with # are skipped.\n */\n static loadIgnoreFile(cwd: string): string[] {\n const ignorePath = path.join(cwd, \".openacpignore\");\n try {\n const content = fs.readFileSync(ignorePath, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l && !l.startsWith(\"#\"));\n } catch {\n return [];\n }\n }\n}\n","/**\n * Allowlisted environment variable patterns for agent subprocesses.\n *\n * Agent processes inherit a filtered subset of the parent environment.\n * This prevents agents from accessing secrets (API keys, tokens, cloud\n * credentials) that happen to be in the OpenACP process environment.\n *\n * Only shell basics (PATH, HOME, SHELL), locale settings, git/SSH config,\n * and terminal rendering vars are passed through. Patterns ending with \"*\"\n * match any variable starting with that prefix (e.g. \"GIT_*\" matches \"GIT_AUTHOR_NAME\").\n */\nexport const DEFAULT_ENV_WHITELIST = [\n // Shell basics — agents need these to resolve commands and write temp files\n \"PATH\", \"HOME\", \"SHELL\", \"LANG\", \"LC_*\", \"TERM\", \"USER\", \"LOGNAME\",\n \"TMPDIR\", \"XDG_*\", \"NODE_ENV\", \"EDITOR\",\n // Git — agents need git config and SSH access for code operations\n \"GIT_*\", \"SSH_AUTH_SOCK\", \"SSH_AGENT_PID\",\n // Network proxies — ACP adapters and their CLI children may require the host proxy.\n // Include both cases because different runtimes and libraries inspect different names.\n \"HTTP_PROXY\", \"HTTPS_PROXY\", \"ALL_PROXY\", \"NO_PROXY\",\n \"http_proxy\", \"https_proxy\", \"all_proxy\", \"no_proxy\",\n \"NODE_USE_ENV_PROXY\",\n // Terminal rendering — ensures correct color output in agent responses\n \"COLORTERM\", \"FORCE_COLOR\", \"NO_COLOR\", \"TERM_PROGRAM\", \"HOSTNAME\",\n];\n\n/** Supports exact match and trailing wildcard (e.g. \"GIT_*\" matches \"GIT_AUTHOR_NAME\"). */\nfunction matchesPattern(key: string, pattern: string): boolean {\n if (pattern.endsWith(\"*\")) {\n return key.startsWith(pattern.slice(0, -1));\n }\n return key === pattern;\n}\n\n/**\n * Filters environment variables for an agent subprocess.\n *\n * Only variables matching the whitelist patterns are passed through.\n * Agent-specific env vars (from agent config) are merged on top,\n * allowing agents to override allowlisted values.\n *\n * @param processEnv - The parent process environment (typically `process.env`)\n * @param agentEnv - Additional env vars defined in the agent's configuration\n * @param whitelist - Custom whitelist patterns (defaults to DEFAULT_ENV_WHITELIST)\n */\nexport function filterEnv(\n processEnv: Record<string, string | undefined>,\n agentEnv?: Record<string, string>,\n whitelist?: string[],\n): Record<string, string> {\n const patterns = whitelist ?? DEFAULT_ENV_WHITELIST;\n const result: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(processEnv)) {\n if (value === undefined) continue;\n if (patterns.some((p) => matchesPattern(key, p))) {\n result[key] = value;\n }\n }\n\n if (agentEnv) {\n Object.assign(result, agentEnv);\n }\n\n return result;\n}\n","/**\n * Type-safe event emitter where the event map is enforced at compile time.\n *\n * Unlike Node's EventEmitter, event names and listener signatures are\n * validated by TypeScript — no stringly-typed events. Supports pause/resume\n * with buffering, used by Session and EventBus to defer events during\n * initialization or agent switches.\n *\n * Usage:\n * interface MyEvents {\n * data: (payload: string) => void\n * error: (err: Error) => void\n * }\n * const emitter = new TypedEmitter<MyEvents>()\n * emitter.on('data', (payload) => { ... })\n * emitter.emit('data', 'hello')\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class TypedEmitter<T extends Record<string & keyof T, (...args: any[]) => void>> {\n private static readonly MAX_BUFFER_SIZE = 10000\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private listeners = new Map<keyof T, Set<(...args: any[]) => void>>()\n private paused = false\n private buffer: Array<{ event: keyof T; args: unknown[] }> = []\n\n /** Register a listener for the given event. Returns `this` for chaining. */\n on<K extends keyof T>(event: K, listener: T[K]): this {\n let set = this.listeners.get(event)\n if (!set) {\n set = new Set()\n this.listeners.set(event, set)\n }\n set.add(listener)\n return this\n }\n\n /** Remove a specific listener for the given event. */\n off<K extends keyof T>(event: K, listener: T[K]): this {\n this.listeners.get(event)?.delete(listener)\n return this\n }\n\n /**\n * Emit an event to all registered listeners.\n *\n * When paused, events are buffered (up to MAX_BUFFER_SIZE) unless\n * the passthrough filter allows them through immediately.\n */\n emit<K extends keyof T>(event: K, ...args: Parameters<T[K]>): void {\n if (this.paused) {\n // Check passthrough filter — some events may bypass the pause\n if (this.passthroughFn?.(event, args)) {\n this.deliver(event, args)\n } else {\n this.buffer.push({ event, args })\n if (this.buffer.length > TypedEmitter.MAX_BUFFER_SIZE) {\n console.warn(`[TypedEmitter] Buffer exceeded ${TypedEmitter.MAX_BUFFER_SIZE} events, dropping oldest`)\n this.buffer.shift()\n }\n }\n return\n }\n this.deliver(event, args)\n }\n\n /**\n * Pause event delivery. Events emitted while paused are buffered.\n * Optionally pass a filter to allow specific events through even while paused.\n */\n pause(passthrough?: (event: keyof T, args: unknown[]) => boolean): void {\n this.paused = true\n this.passthroughFn = passthrough\n }\n private passthroughFn?: (event: keyof T, args: unknown[]) => boolean\n\n /** Resume event delivery and replay buffered events in order. */\n resume(): void {\n this.paused = false\n this.passthroughFn = undefined\n const buffered = this.buffer.splice(0)\n for (const { event, args } of buffered) {\n this.deliver(event, args)\n }\n }\n\n /** Discard all buffered events without delivering them. */\n clearBuffer(): void {\n this.buffer.length = 0\n }\n\n get isPaused(): boolean {\n return this.paused\n }\n\n get bufferSize(): number {\n return this.buffer.length\n }\n\n /** Remove all listeners for a specific event, or all events if none specified. */\n removeAllListeners(event?: keyof T): void {\n if (event) {\n this.listeners.delete(event)\n } else {\n this.listeners.clear()\n }\n }\n\n /** Deliver an event to listeners, isolating errors so one broken listener doesn't break others. */\n private deliver(event: keyof T, args: unknown[]): void {\n const set = this.listeners.get(event)\n if (!set) return\n for (const listener of set) {\n try {\n (listener as (...a: unknown[]) => void)(...args)\n } catch (err) {\n // Don't let one listener break others\n console.error(`[EventBus] Listener error on \"${String(event)}\":`, err)\n }\n }\n }\n}\n","/**\n * Attachment handling utilities for building ACP prompt content blocks.\n *\n * Extracted as a pure module so the logic can be unit-tested independently\n * of AgentInstance (which requires a subprocess to instantiate).\n */\n\nimport type { Attachment } from \"../types.js\";\n\n/** Image MIME types supported by the Claude API for base64 inline content. */\nexport const SUPPORTED_IMAGE_MIMES = new Set([\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n \"image/avif\",\n \"image/heic\",\n \"image/heif\",\n]);\n\nconst MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024; // 10 MB\n\nexport interface PromptCapabilities {\n image?: boolean;\n audio?: boolean;\n}\n\n/**\n * Determines if an attachment should be skipped (not included as base64 content block).\n *\n * Returns a human-readable note to append to the prompt text when the attachment\n * cannot be embedded, or null when the attachment should be included normally.\n *\n * This function does NOT read the file — it only classifies the attachment based\n * on its metadata. Path validation and file reading remain in AgentInstance.prompt().\n */\nexport function buildAttachmentNote(\n att: Attachment,\n capabilities: PromptCapabilities,\n): string | null {\n const tooLarge = att.size > MAX_ATTACHMENT_SIZE;\n\n if (tooLarge) {\n const sizeMB = Math.round(att.size / 1024 / 1024);\n return `[Attachment skipped: \"${att.fileName}\" is too large (${sizeMB}MB > 10MB limit)]`;\n }\n\n if (att.type === \"image\") {\n if (!capabilities.image) {\n // Agent doesn't support image content — will fall back to file path\n return null;\n }\n if (!SUPPORTED_IMAGE_MIMES.has(att.mimeType)) {\n return `[Attachment skipped: image format not supported (${att.mimeType})]`;\n }\n // Supported — include as base64 block\n return null;\n }\n\n if (att.type === \"audio\") {\n if (!capabilities.audio) {\n // Agent doesn't support audio content — will fall back to file path\n return null;\n }\n // Supported — include as base64 block\n return null;\n }\n\n // Generic file attachment — always falls back to file path, no note needed\n return null;\n}\n","import { spawn, type ChildProcess } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport type { MiddlewareChain } from \"../plugin/middleware-chain.js\";\nimport { filterEnv } from \"../security/env-filter.js\";\nimport { Hook } from \"../events.js\";\n\ninterface TerminalState {\n process: ChildProcess;\n output: string;\n exitStatus: { exitCode: number | null; signal: string | null } | null;\n command: string;\n startTime: number;\n}\n\ninterface CreateTerminalParams {\n command: string;\n args?: string[] | Array<{ name: string; value: string }>;\n env?: Array<{ name: string; value: string }>;\n cwd?: string;\n outputByteLimit?: number;\n}\n\ninterface TerminalOutputResult {\n output: string;\n truncated: boolean;\n exitStatus?: { exitCode: number | null; signal: string | null };\n}\n\ninterface WaitForExitResult {\n exitCode: number | null;\n signal: string | null;\n}\n\n/**\n * Manages child-process terminals for agents that need interactive command execution.\n *\n * Agents request terminals via ACP's terminal_create method. Each terminal is a\n * spawned child process whose stdout/stderr is captured into a ring buffer\n * (capped at maxOutputBytes). The agent can poll output, wait for exit, or kill\n * the process. Terminals are auto-cleaned up 30s after the process exits to allow\n * final output retrieval.\n */\nexport class TerminalManager {\n private terminals: Map<string, TerminalState> = new Map();\n private maxOutputBytes: number;\n\n constructor(maxOutputBytes = 1024 * 1024) {\n this.maxOutputBytes = maxOutputBytes;\n }\n\n /**\n * Spawn a new terminal process. Runs terminal:beforeCreate middleware first\n * (which can modify command/args/env or block creation entirely).\n * Returns a terminalId for subsequent output/wait/kill operations.\n */\n async createTerminal(\n sessionId: string,\n params: CreateTerminalParams,\n middlewareChain?: MiddlewareChain,\n ): Promise<{ terminalId: string }> {\n let termCommand = params.command;\n let termArgs = params.args ?? [];\n let termEnvArr = params.env ?? [];\n let termCwd = params.cwd ?? undefined;\n\n if (middlewareChain) {\n const envRecord: Record<string, string> = {};\n for (const ev of termEnvArr) { envRecord[ev.name] = ev.value; }\n const result = await middlewareChain.execute(Hook.TERMINAL_BEFORE_CREATE, {\n sessionId,\n command: termCommand,\n args: termArgs as string[],\n env: envRecord,\n cwd: termCwd,\n }, async (p) => p);\n if (!result) return { terminalId: \"\" }; // blocked by middleware\n termCommand = result.command;\n termArgs = result.args ?? termArgs;\n termCwd = result.cwd ?? termCwd;\n if (result.env) {\n termEnvArr = Object.entries(result.env).map(([name, value]) => ({ name, value }));\n }\n }\n\n const terminalId = randomUUID();\n const args = termArgs as string[];\n const env: Record<string, string> = {};\n for (const ev of termEnvArr) {\n env[ev.name] = ev.value;\n }\n\n const childProcess = spawn(termCommand, args, {\n cwd: termCwd,\n env: filterEnv(process.env as Record<string, string>, env),\n shell: false,\n });\n\n const state: TerminalState = {\n process: childProcess,\n output: \"\",\n exitStatus: null,\n command: termCommand,\n startTime: Date.now(),\n };\n this.terminals.set(terminalId, state);\n\n const outputByteLimit = params.outputByteLimit ?? this.maxOutputBytes;\n\n // Ring buffer: keep only the latest outputByteLimit bytes — oldest output\n // is trimmed from the front when the buffer exceeds the limit.\n const appendOutput = (chunk: string) => {\n state.output += chunk;\n const bytes = Buffer.byteLength(state.output, \"utf-8\");\n if (bytes > outputByteLimit) {\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 if (middlewareChain) {\n middlewareChain.execute(Hook.TERMINAL_AFTER_EXIT, {\n sessionId,\n terminalId,\n command: state.command,\n exitCode: code ?? -1,\n durationMs: Date.now() - state.startTime,\n }, async (p) => p).catch(() => {});\n }\n // Schedule cleanup of the terminal entry after a grace period to allow\n // output retrieval by the agent after the process has exited.\n setTimeout(() => {\n if (this.terminals.has(terminalId)) {\n this.terminals.delete(terminalId);\n }\n }, 30_000).unref();\n });\n\n return { terminalId };\n }\n\n /** Retrieve accumulated stdout/stderr output for a terminal. */\n getOutput(terminalId: string): TerminalOutputResult {\n const state = this.terminals.get(terminalId);\n if (!state) {\n throw new Error(`Terminal not found: ${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 /** Block until the terminal process exits, returning exit code and signal. */\n async waitForExit(terminalId: string): Promise<WaitForExitResult> {\n const state = this.terminals.get(terminalId);\n if (!state) {\n throw new Error(`Terminal not found: ${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 // Re-check after attaching the listener to close the TOCTOU race:\n // the process may have exited between the null check above and the\n // listener registration.\n if (state.exitStatus !== null) {\n resolve({\n exitCode: state.exitStatus.exitCode,\n signal: state.exitStatus.signal,\n });\n }\n });\n }\n\n /** Send SIGTERM to a terminal process (graceful shutdown). */\n kill(terminalId: string): void {\n const state = this.terminals.get(terminalId);\n if (!state) {\n throw new Error(`Terminal not found: ${terminalId}`);\n }\n state.process.kill(\"SIGTERM\");\n }\n\n /** Force-kill (SIGKILL) and immediately remove a terminal from the registry. */\n release(terminalId: string): void {\n const state = this.terminals.get(terminalId);\n if (!state) {\n return;\n }\n state.process.kill(\"SIGKILL\");\n this.terminals.delete(terminalId);\n }\n\n /** Force-kill all terminals. Used during session/system teardown. */\n destroyAll(): void {\n for (const [, t] of this.terminals) {\n t.process.kill(\"SIGKILL\");\n }\n this.terminals.clear();\n }\n}\n","import type { McpServerConfig } from \"../types.js\";\n\n/**\n * Resolves MCP server configuration for agent sessions.\n * Centralizes the logic for providing MCP servers to newSession/resumeSession/loadSession/forkSession.\n */\nexport class McpManager {\n /**\n * Resolve the MCP server config to pass to ACP session methods.\n * Returns the provided config array or an empty array if none given.\n */\n resolve(sessionConfig?: McpServerConfig[]): McpServerConfig[] {\n return sessionConfig ?? [];\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport type TraceLayer = \"acp\" | \"core\" | \"telegram\";\n\n// Evaluated once at module load — zero overhead when disabled\nconst DEBUG_ENABLED = process.env.OPENACP_DEBUG === \"true\" || process.env.OPENACP_DEBUG === \"1\";\n\n/**\n * Per-session debug trace logger that writes JSONL files to `<workingDirectory>/.log/`.\n *\n * Only active when `OPENACP_DEBUG=true` is set in the environment.\n * Each trace layer (acp, core, telegram) gets its own file, making it easy\n * to inspect protocol-level, core-level, or adapter-level events separately.\n *\n * Uses appendFileSync for simplicity — this blocks the event loop briefly per write,\n * which is acceptable for a debug-only tool. The DEBUG_ENABLED guard ensures zero\n * overhead in production.\n */\nexport class DebugTracer {\n private dirCreated = false;\n private logDir: string;\n\n constructor(\n private sessionId: string,\n private workingDirectory: string,\n ) {\n this.logDir = path.join(workingDirectory, \".log\");\n }\n\n /**\n * Write a timestamped JSONL entry to the trace file for the given layer.\n *\n * Handles circular references gracefully and silently swallows errors —\n * debug logging must never crash the application.\n */\n log(layer: TraceLayer, data: Record<string, unknown>): void {\n try {\n if (!this.dirCreated) {\n fs.mkdirSync(this.logDir, { recursive: true });\n this.dirCreated = true;\n }\n const filePath = path.join(this.logDir, `${this.sessionId}_${layer}.jsonl`);\n // WeakSet tracks seen objects to replace circular references with \"[Circular]\"\n const seen = new WeakSet();\n const line =\n JSON.stringify({ ts: Date.now(), ...data }, (_key, value) => {\n if (typeof value === \"object\" && value !== null) {\n if (seen.has(value)) return \"[Circular]\";\n seen.add(value);\n }\n return value;\n }) + \"\\n\";\n fs.appendFileSync(filePath, line);\n } catch {\n // Debug logging must never crash the app\n }\n }\n\n /** No-op cleanup — establishes the pattern for future async implementations */\n destroy(): void {\n // No file handles to close with appendFileSync\n }\n}\n\n/**\n * Create a DebugTracer if debug mode is enabled, otherwise return null.\n *\n * The DEBUG_ENABLED const is evaluated once at module load time from\n * `process.env.OPENACP_DEBUG`, so there is no per-call overhead when disabled.\n */\nexport function createDebugTracer(sessionId: string, workingDirectory: string): DebugTracer | null {\n if (!DEBUG_ENABLED) return null;\n return new DebugTracer(sessionId, workingDirectory);\n}\n","import type { AgentDefinition } from \"../types.js\";\nimport { AgentInstance } from \"./agent-instance.js\";\nimport type { AgentCatalog } from \"./agent-catalog.js\";\nimport { createChildLogger } from \"../utils/log.js\";\n\nconst log = createChildLogger({ module: \"agent-manager\" });\n\nconst WARM_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\ninterface WarmEntry {\n agentName: string;\n workingDir: string;\n /**\n * The allowedPaths set baked into the warm instance's PathGuard at spawn time.\n * Must match the requested set on takeWarm — different paths produce a\n * different security boundary, so a mismatched warm is not safe to claim.\n */\n allowedPaths: readonly string[];\n instance: AgentInstance;\n createdAt: number;\n}\n\n/**\n * Order-insensitive equality on two path lists. Used to decide whether a warm\n * instance's PathGuard configuration matches a fresh request.\n */\nfunction pathListsEqual(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false;\n const sa = [...a].sort();\n const sb = [...b].sort();\n for (let i = 0; i < sa.length; i++) {\n if (sa[i] !== sb[i]) return false;\n }\n return true;\n}\n\n/**\n * High-level facade for spawning and resuming agent instances.\n *\n * Resolves agent names to definitions via AgentCatalog, then delegates\n * to AgentInstance for subprocess management. Used by SessionFactory\n * to create the agent backing a session.\n *\n * Maintains a single-slot warm pool: one pre-initialized AgentInstance\n * (subprocess spawned + ACP initialize done) is kept ready so the next\n * createSession only pays for the newSession RPC (~300ms) instead of a\n * full subprocess spawn (~2–3s).\n *\n * Agent switching (swapping the agent mid-session) is coordinated at the\n * Session layer — AgentManager only handles individual spawn/resume calls.\n */\nexport class AgentManager {\n private warmEntry: WarmEntry | null = null;\n /** In-flight prewarm promise — guards against concurrent prewarm calls. */\n private warming: Promise<void> | null = null;\n\n constructor(private catalog: AgentCatalog) {}\n\n /** Return definitions for all installed agents. */\n getAvailableAgents(): AgentDefinition[] {\n const installed = this.catalog.getInstalledEntries();\n return Object.entries(installed).map(([key, agent]) => ({\n name: key,\n command: agent.command,\n args: agent.args,\n env: agent.env,\n }));\n }\n\n /** Look up a single agent definition by its short name (e.g., \"claude\", \"gemini\"). */\n getAgent(name: string): AgentDefinition | undefined {\n return this.catalog.resolve(name);\n }\n\n /**\n * Spawn-and-initialize one AgentInstance in the background for the given\n * agent/workingDir/allowedPaths. Safe to call repeatedly — a second call\n * while warming is in flight is a no-op (logged at debug), and a call while\n * a valid warm entry with matching params already exists is a no-op.\n *\n * If a warm entry exists with mismatched params, it is evicted and destroyed\n * before the new warm is started — otherwise the async spawn would complete\n * only to find the slot still occupied and destroy itself, wasting a full\n * subprocess spawn + ACP handshake and leaving the wrong entry in place.\n */\n prewarm(agentName: string, workingDir: string, allowedPaths: readonly string[] = []): void {\n if (this.warming) {\n log.debug(\n { requestedAgent: agentName, requestedWorkingDir: workingDir },\n \"prewarm: another warm spawn already in flight; request dropped\",\n );\n return;\n }\n if (this.warmEntry) {\n const e = this.warmEntry;\n if (\n e.agentName === agentName &&\n e.workingDir === workingDir &&\n pathListsEqual(e.allowedPaths, allowedPaths)\n ) {\n return; // exact match — no-op\n }\n // Mismatched entry — evict before warming with new params.\n this.warmEntry = null;\n e.instance.destroy().catch(() => {});\n }\n const agentDef = this.catalog.resolve(agentName);\n if (!agentDef) {\n log.debug({ agentName }, \"prewarm: agent not installed, skipping\");\n return;\n }\n this.warming = (async () => {\n try {\n const instance = await AgentInstance.spawnSubprocess(agentDef, workingDir, [...allowedPaths]);\n // If someone else set warmEntry while we were warming (unlikely), destroy ours.\n if (this.warmEntry) {\n await instance.destroy().catch(() => {});\n return;\n }\n this.warmEntry = {\n agentName,\n workingDir,\n allowedPaths: [...allowedPaths],\n instance,\n createdAt: Date.now(),\n };\n log.info({ agentName, workingDir }, \"Agent warm-pool: instance ready\");\n } catch (err) {\n log.warn({ err, agentName }, \"Agent warm-pool: prewarm failed\");\n } finally {\n this.warming = null;\n }\n })();\n }\n\n /**\n * Destroy the warm instance (if any) and clear the slot. Called from the\n * server shutdown path so the warm subprocess does not outlive its parent.\n * Best-effort — errors are swallowed since shutdown should not fail.\n */\n async destroyWarm(): Promise<void> {\n const entry = this.warmEntry;\n this.warmEntry = null;\n if (entry) {\n try { await entry.instance.destroy(); } catch { /* best effort */ }\n }\n }\n\n /**\n * Take the warm instance if it matches the given agent/workingDir/allowedPaths\n * AND is alive AND has not exceeded its TTL. Clears the slot in every\n * mismatch/discard branch — the caller is responsible for claiming the\n * returned instance (or discarding it if claim fails).\n *\n * `allowedPaths` is part of the match key because it is baked into the\n * subprocess's PathGuard at spawn time and cannot be safely re-applied\n * post-hoc on a warm instance.\n */\n private takeWarm(\n agentName: string,\n workingDir: string,\n allowedPaths: readonly string[],\n ): AgentInstance | null {\n const entry = this.warmEntry;\n if (!entry) return null;\n if (entry.agentName !== agentName || entry.workingDir !== workingDir) return null;\n if (!pathListsEqual(entry.allowedPaths, allowedPaths)) {\n // Security-relevant mismatch: PathGuard differs. Discard and clear.\n log.debug(\n { agentName, workingDir },\n \"Warm-pool: allowedPaths mismatch on takeWarm — discarding warm\",\n );\n this.warmEntry = null;\n entry.instance.destroy().catch(() => {});\n return null;\n }\n if (Date.now() - entry.createdAt > WARM_TTL_MS) {\n log.debug({ agentName, workingDir }, \"Warm-pool: TTL expired — discarding warm\");\n this.warmEntry = null;\n entry.instance.destroy().catch(() => {});\n return null;\n }\n if (entry.instance.isDead) {\n log.warn(\n { agentName, workingDir },\n \"Warm-pool: instance died before claim — discarding\",\n );\n this.warmEntry = null;\n // Subprocess is gone but listeners and StderrCapture are still referenced.\n entry.instance.destroy().catch(() => {});\n return null;\n }\n this.warmEntry = null;\n return entry.instance;\n }\n\n /**\n * Spawn a new agent subprocess with a fresh session.\n *\n * When a warm instance is available for the requested agent/workingDir, it is\n * claimed (only the newSession RPC is paid) instead of a full subprocess spawn.\n * After a successful warm claim, a background refill is kicked off so the next\n * caller also benefits.\n *\n * @throws If the agent is not installed — includes install instructions in the error message.\n */\n async spawn(\n agentName: string,\n workingDirectory: string,\n allowedPaths?: string[],\n ): Promise<AgentInstance> {\n const agentDef = this.getAgent(agentName);\n if (!agentDef) {\n throw new Error(\n `Agent \"${agentName}\" is not installed. Run \"openacp agents install ${agentName}\" to add it.`,\n );\n }\n\n // Fast path: claim the warm instance if it matches (agent + workingDir + allowedPaths).\n const warm = this.takeWarm(agentName, workingDirectory, allowedPaths ?? []);\n if (warm) {\n try {\n await warm.claimForSession(workingDirectory);\n // Refill in background for the next caller.\n this.prewarm(agentName, workingDirectory, allowedPaths ?? []);\n return warm;\n } catch (err) {\n log.warn({ err, agentName }, \"Warm claim failed — falling back to fresh spawn\");\n warm.destroy().catch(() => {});\n // fall through to regular spawn\n }\n }\n\n return AgentInstance.spawn(agentDef, workingDirectory, undefined, allowedPaths);\n }\n\n /**\n * Spawn a subprocess and resume an existing agent session.\n *\n * Falls back to a new session if the agent cannot restore the given session ID.\n * Resume does not use the warm pool — it requires a specific existing session.\n */\n async resume(\n agentName: string,\n workingDirectory: string,\n agentSessionId: string,\n allowedPaths?: string[],\n ): Promise<AgentInstance> {\n const agentDef = this.getAgent(agentName);\n if (!agentDef) {\n throw new Error(\n `Agent \"${agentName}\" is not installed. Run \"openacp agents install ${agentName}\" to add it.`,\n );\n }\n return AgentInstance.resume(agentDef, workingDirectory, agentSessionId, undefined, allowedPaths);\n }\n}\n","import { nanoid } from \"nanoid\";\nimport type { AgentInstance } from \"../agents/agent-instance.js\";\nimport type { AgentCapabilities, AgentCommand, AgentEvent, AgentSwitchEntry, Attachment, PermissionRequest, SessionStatus, ConfigOption, SessionModeState, SessionModelState, TurnMeta } from \"../types.js\";\nimport { TypedEmitter } from \"../utils/typed-emitter.js\";\nimport { PromptQueue } from \"./prompt-queue.js\";\nimport { PermissionGate } from \"./permission-gate.js\";\nimport { createChildLogger, createSessionLogger, closeSessionLogger, type Logger } from \"../utils/log.js\";\nimport type { SpeechService } from \"../../plugins/speech/exports.js\";\nimport type { MiddlewareChain } from \"../plugin/middleware-chain.js\";\nimport * as fs from \"node:fs\";\nimport type { TurnRouting } from \"./turn-context.js\";\nimport { createTurnContext, type TurnContext } from \"./turn-context.js\";\nimport { Hook, SessionEv } from \"../events.js\";\nconst moduleLog = createChildLogger({ module: \"session\" });\n\nfunction formatPromptError(err: unknown): string {\n const message = extractErrorMessage(err);\n return message ?? \"Unknown error\";\n}\n\nfunction extractErrorMessage(value: unknown, seen = new WeakSet<object>()): string | null {\n if (typeof value === \"string\") return normalizeErrorMessage(value);\n if (typeof value === \"number\" || typeof value === \"boolean\" || typeof value === \"bigint\") {\n return String(value);\n }\n if (value == null || typeof value !== \"object\") return null;\n if (seen.has(value)) return null;\n seen.add(value);\n\n if (value instanceof Error) {\n const direct = normalizeErrorMessage(value.message);\n if (direct && !isGenericPromptErrorMessage(direct)) return direct;\n\n const cause = extractErrorMessage((value as { cause?: unknown }).cause, seen);\n if (cause) return cause;\n\n return direct ?? normalizeErrorMessage(value.name);\n }\n\n const record = value as Record<string, unknown>;\n const direct = typeof record.message === \"string\" ? normalizeErrorMessage(record.message) : null;\n if (direct && !isGenericPromptErrorMessage(direct)) return direct;\n\n for (const key of [\"error\", \"reason\", \"details\", \"detail\", \"data\", \"cause\"]) {\n const nested = extractErrorMessage(record[key], seen);\n if (nested) return nested;\n }\n // \"message\" is already captured in `direct` above; use it as last resort before JSON fallback.\n if (direct) return direct;\n\n try {\n const json = JSON.stringify(value);\n const jsonMessage = json ? normalizeErrorMessage(json) : null;\n if (jsonMessage && jsonMessage !== \"{}\") return jsonMessage;\n } catch {\n // Fall through to a stable fallback below.\n }\n\n const name = (value as { constructor?: { name?: string } }).constructor?.name;\n return name && name !== \"Object\" ? name : null;\n}\n\nfunction normalizeErrorMessage(message: string | undefined): string | null {\n const trimmed = message?.trim();\n if (!trimmed || trimmed === \"[object Object]\" || trimmed === \"undefined\" || trimmed === \"null\") {\n return null;\n }\n return trimmed;\n}\n\n// JSON-RPC -32603 surfaces \"Internal error\" as its top-level message, which is not\n// meaningful to users. Skip it and look for a more specific cause in nested fields.\nfunction isGenericPromptErrorMessage(message: string): boolean {\n return message === \"Internal error\";\n}\n\n// TTS injection pattern: we append TTS_PROMPT_INSTRUCTION to the user's prompt so the\n// agent includes a [TTS]...[/TTS] block in its response. After the response completes,\n// we extract that block via TTS_BLOCK_REGEX and synthesize speech from it. The TTS block\n// is then stripped from the text shown to the user (via tts_strip event).\nexport const TTS_PROMPT_INSTRUCTION = `\\n\\nAdditionally, include a [TTS]...[/TTS] block with a spoken-friendly summary of your response. Focus on key information, decisions the user needs to make, or actions required. The agent decides what to say and how long. Respond in the same language the user is using. This instruction applies to this message only.`;\nexport const TTS_BLOCK_REGEX = /\\[TTS\\]([\\s\\S]*?)\\[\\/TTS\\]/;\nexport const TTS_MAX_LENGTH = 5000;\nexport const TTS_TIMEOUT_MS = 30_000;\n\n// Session state machine — valid transitions: from → Set<to>\n//\n// initializing → active (first prompt received)\n// → error (agent spawn failed)\n// active → error (unrecoverable prompt failure)\n// → finished (agent signaled session_end)\n// → cancelled (user cancelled the session)\n// error → active (retry: user sends a new prompt)\n// → cancelled (user gives up)\n// cancelled → active (resume: user sends a new prompt)\n// finished → (terminal — no further transitions)\nconst VALID_TRANSITIONS: Record<SessionStatus, Set<SessionStatus>> = {\n initializing: new Set([\"active\", \"error\"]),\n active: new Set([\"error\", \"finished\", \"cancelled\"]),\n error: new Set([\"active\", \"cancelled\"]),\n cancelled: new Set([\"active\"]),\n finished: new Set(),\n};\n\n/** Events emitted by a Session instance — SessionBridge subscribes to relay them to adapters. */\nexport interface SessionEvents {\n agent_event: (event: AgentEvent) => void;\n permission_request: (request: PermissionRequest) => void;\n session_end: (reason: string) => void;\n status_change: (from: SessionStatus, to: SessionStatus) => void;\n named: (name: string) => void;\n error: (error: Error) => void;\n prompt_count_changed: (count: number) => void;\n turn_started: (ctx: TurnContext) => void;\n /** Fired synchronously when a prompt is placed behind a running prompt in the queue. */\n prompt_queued: (data: { turnId: string | undefined; position: number; routing: TurnRouting | undefined }) => void;\n}\n\n/**\n * Manages a single conversation between a user and an AI agent.\n *\n * Wraps an AgentInstance with serial prompt queuing (via PromptQueue), permission\n * gating (via PermissionGate), TTS/STT integration, auto-naming, and a state\n * machine tracking the session lifecycle. SessionBridge subscribes to this\n * emitter to forward agent output to channel adapters.\n *\n * A session can be attached to multiple adapters simultaneously (e.g., Telegram\n * and SSE). The `threadIds` map tracks which thread each adapter uses.\n */\nexport class Session extends TypedEmitter<SessionEvents> {\n id: string;\n channelId: string;\n /** @deprecated Use threadIds map directly. Getter returns primary adapter's threadId. */\n get threadId(): string {\n return this.threadIds.get(this.channelId) ?? \"\";\n }\n set threadId(value: string) {\n if (value) {\n this.threadIds.set(this.channelId, value);\n }\n }\n agentName: string;\n workingDirectory: string;\n private _agentInstance!: AgentInstance;\n get agentInstance(): AgentInstance { return this._agentInstance; }\n /** Setting agentInstance wires the agent→session event relay and commands buffer.\n * This happens both at construction and on agent switch (switchAgent). */\n set agentInstance(agent: AgentInstance) {\n this._agentInstance = agent;\n this.wireAgentRelay();\n this.wireCommandsBuffer();\n }\n agentSessionId: string = \"\";\n private _status: SessionStatus = \"initializing\";\n name?: string;\n createdAt: Date = new Date();\n voiceMode: \"off\" | \"next\" | \"on\" = \"off\";\n configOptions: ConfigOption[] = [];\n clientOverrides: { bypassPermissions?: boolean } = {};\n agentCapabilities?: AgentCapabilities;\n archiving: boolean = false;\n promptCount: number = 0;\n firstAgent: string;\n agentSwitchHistory: AgentSwitchEntry[] = [];\n isAssistant: boolean = false;\n log: Logger;\n middlewareChain?: MiddlewareChain;\n /** Latest commands emitted by the agent — buffered before bridge connects so they're not lost */\n latestCommands: AgentCommand[] | null = null;\n /** Adapters currently attached to this session (including primary) */\n attachedAdapters: string[] = [];\n /** Per-adapter thread IDs: adapterId → threadId */\n threadIds: Map<string, string> = new Map();\n /** Active turn context — sealed on prompt dequeue, cleared on turn end */\n activeTurnContext: TurnContext | null = null;\n\n readonly permissionGate = new PermissionGate();\n private readonly queue: PromptQueue;\n private speechService?: SpeechService;\n private pendingContext: string | null = null;\n /** Per-turn abort tracking — avoids race when next turn resets before current turn reads */\n private _abortedTurnIds = new Set<string>();\n /** Last completed turnId — used by abortPrompt() to retroactively mark a just-finished turn as interrupted */\n private _lastCompletedTurnId: string | null = null;\n\n private _autoApprovedCommands: string[] = []\n\n get autoApprovedCommands(): string[] {\n return this._autoApprovedCommands\n }\n\n constructor(opts: {\n id?: string;\n channelId: string;\n agentName: string;\n workingDirectory: string;\n agentInstance: AgentInstance;\n speechService?: SpeechService;\n isAssistant?: boolean;\n autoApprovedCommands?: string[];\n }) {\n super();\n this.id = opts.id || nanoid(12);\n this.channelId = opts.channelId;\n this.attachedAdapters = [opts.channelId];\n this.agentName = opts.agentName;\n this.firstAgent = opts.agentName;\n this.workingDirectory = opts.workingDirectory;\n this.agentInstance = opts.agentInstance;\n this.speechService = opts.speechService;\n this.isAssistant = opts.isAssistant ?? false;\n this._autoApprovedCommands = opts.autoApprovedCommands ?? [];\n this.log = createSessionLogger(this.id, moduleLog);\n this.log.info({ agentName: this.agentName }, \"Session created\");\n\n this.queue = new PromptQueue(\n (text, userPrompt, attachments, routing, turnId, meta) => this.processPrompt(text, userPrompt, attachments, routing, turnId, meta),\n (err) => {\n this.log.error({ err }, \"Prompt execution failed\");\n const message = formatPromptError(err);\n this.fail(message);\n this.emit(SessionEv.AGENT_EVENT, { type: \"error\", message: `Prompt execution failed: ${message}` });\n },\n (turnId, position, routing) => {\n this.emit(SessionEv.PROMPT_QUEUED, { turnId, position, routing });\n },\n );\n\n }\n\n /** Wire the agent→session event relay on the current agentInstance.\n * Removes any previous relay first to avoid duplicates on agent switch.\n * This relay ensures session.emit(\"agent_event\") fires for ALL sessions,\n * including headless API sessions that have no SessionBridge attached. */\n private agentRelayCleanup?: () => void;\n private wireAgentRelay(): void {\n this.agentRelayCleanup?.();\n const instance = this._agentInstance;\n const handler = (event: AgentEvent) => {\n this.emit(SessionEv.AGENT_EVENT, event);\n };\n instance.on(SessionEv.AGENT_EVENT, handler);\n this.agentRelayCleanup = () => instance.off(SessionEv.AGENT_EVENT, handler);\n }\n\n /** Wire a listener on the current agentInstance to buffer commands_update events.\n * Must be called after every agentInstance replacement (constructor + switchAgent). */\n private commandsBufferCleanup?: () => void;\n private wireCommandsBuffer(): void {\n // Remove previous listener (if switching agents) to avoid leaks\n this.commandsBufferCleanup?.();\n const instance = this._agentInstance;\n const handler = (event: AgentEvent) => {\n if (event.type === \"commands_update\") {\n this.latestCommands = event.commands;\n }\n };\n instance.on(SessionEv.AGENT_EVENT, handler);\n this.commandsBufferCleanup = () => instance.off(SessionEv.AGENT_EVENT, handler);\n }\n\n // --- State Machine ---\n\n get status(): SessionStatus {\n return this._status;\n }\n\n\n /** Transition to active — from initializing, error, or cancelled */\n activate(): void {\n this.transition(\"active\");\n }\n\n /** Transition to error — from initializing or active. Idempotent if already in error. */\n fail(reason: string): void {\n if (this._status === \"error\") return;\n this.transition(\"error\");\n this.emit(SessionEv.ERROR, new Error(reason));\n }\n\n /** Transition to finished — from active only. Emits session_end for backward compat. */\n finish(reason?: string): void {\n this.transition(\"finished\");\n this.emit(SessionEv.SESSION_END, reason ?? \"completed\");\n }\n\n /** Transition to cancelled — from active or error (terminal session cancel) */\n markCancelled(): void {\n this.transition(\"cancelled\");\n }\n\n private transition(to: SessionStatus): void {\n const from = this._status;\n const allowed = VALID_TRANSITIONS[from];\n if (!allowed?.has(to)) {\n throw new Error(\n `Invalid session transition: ${from} → ${to}`,\n );\n }\n this._status = to;\n this.log.debug({ from, to }, \"Session status transition\");\n this.emit(SessionEv.STATUS_CHANGE, from, to);\n }\n\n /** Number of prompts waiting in queue */\n get queueDepth(): number {\n return this.queue.pending;\n }\n\n /** Whether a prompt is currently being processed by the agent */\n get promptRunning(): boolean {\n return this.queue.isProcessing;\n }\n\n /** Snapshot of queued (not yet processing) items — for inspection by API consumers. */\n get queueItems() {\n return this.queue.pendingItems;\n }\n\n // --- Context Injection ---\n\n /** Store context markdown to be prepended to the next prompt (used for session resume with history). */\n setContext(markdown: string): void {\n this.pendingContext = markdown;\n }\n\n // --- Voice Mode ---\n\n /** Set TTS mode: \"off\" = disabled, \"next\" = one-shot (auto-resets after prompt), \"on\" = persistent. */\n setVoiceMode(mode: \"off\" | \"next\" | \"on\"): void {\n this.voiceMode = mode;\n this.log.info({ voiceMode: mode }, \"TTS mode changed\");\n }\n\n // --- Public API ---\n\n /**\n * Enqueue a user prompt for serial processing.\n *\n * Runs the prompt through agent:beforePrompt middleware (which can modify or block),\n * then adds it to the PromptQueue. Returns a turnId that callers can use to correlate\n * queued/processing events before the prompt actually runs.\n */\n async enqueuePrompt(text: string, attachments?: Attachment[], routing?: TurnRouting, externalTurnId?: string, meta?: TurnMeta): Promise<string> {\n // Use pre-generated turnId if provided (so callers can emit events before awaiting the queue)\n const turnId = externalTurnId ?? nanoid(8);\n const turnMeta: TurnMeta = meta ?? { turnId };\n // Capture raw user prompt before middleware can modify it\n const userPrompt = text;\n // Hook: agent:beforePrompt — modifiable, can block\n if (this.middlewareChain) {\n const payload = { text, attachments, sessionId: this.id, sourceAdapterId: routing?.sourceAdapterId, meta: turnMeta };\n const result = await this.middlewareChain.execute(Hook.AGENT_BEFORE_PROMPT, payload, async (p) => p);\n if (!result) throw new Error('PROMPT_BLOCKED'); // blocked by middleware — caller must emit message:failed\n text = result.text;\n attachments = result.attachments;\n }\n await this.queue.enqueue(text, userPrompt, attachments, routing, turnId, turnMeta);\n return turnId;\n }\n\n private async processPrompt(text: string, userPrompt: string, attachments?: Attachment[], routing?: TurnRouting, turnId?: string, meta?: TurnMeta): Promise<void> {\n // Don't process prompts for finished sessions (queue may still drain)\n if (this._status === \"finished\") return;\n\n // Seal turn context — bridges use this to decide routing for every emitted event\n // Pass the pre-generated turnId so message:queued and message:processing share the same ID\n this.activeTurnContext = createTurnContext(\n routing?.sourceAdapterId ?? this.channelId,\n routing?.responseAdapterId,\n turnId,\n userPrompt,\n text, // finalPrompt (after middleware transformations)\n attachments,\n meta,\n );\n\n // Emit turn_started so SessionBridge can emit message:processing on EventBus\n this.emit(SessionEv.TURN_STARTED, this.activeTurnContext);\n\n this.promptCount++;\n this.emit(SessionEv.PROMPT_COUNT_CHANGED, this.promptCount);\n\n if (this._status === \"initializing\" || this._status === \"cancelled\" || this._status === \"error\") {\n this.activate();\n }\n const promptStart = Date.now();\n this.log.debug(\"Prompt execution started\");\n\n // Context injection: prepend on first real prompt only\n const contextUsed = this.pendingContext;\n if (contextUsed) {\n text = `[CONVERSATION HISTORY - This is context from previous sessions, not current conversation]\\n\\n${contextUsed}\\n\\n[END CONVERSATION HISTORY]\\n\\n${text}`;\n this.log.debug(\"Context injected into prompt\");\n }\n\n // STT: transcribe audio attachments if agent doesn't support audio\n const processed = await this.maybeTranscribeAudio(text, attachments);\n\n // TTS: determine if TTS is active for this prompt\n const ttsActive =\n this.voiceMode !== \"off\" &&\n !!this.speechService?.isTTSAvailable();\n\n // TTS: inject prompt instruction\n if (ttsActive) {\n processed.text += TTS_PROMPT_INSTRUCTION;\n }\n\n // TTS: set up text accumulator before prompting\n let accumulatedText = \"\";\n const accumulatorListener = ttsActive\n ? (event: AgentEvent) => {\n if (event.type === \"text\") {\n accumulatedText += event.content;\n }\n }\n : null;\n\n if (accumulatorListener) {\n this.on(SessionEv.AGENT_EVENT, accumulatorListener);\n }\n\n // Buffer text events for agent:afterTurn hook — accumulates full response text\n const turnTextBuffer: string[] = [];\n const turnTextListener = (event: AgentEvent) => {\n if (event.type === 'text' && typeof (event as any).content === 'string') {\n turnTextBuffer.push((event as any).content);\n }\n };\n this.on(SessionEv.AGENT_EVENT, turnTextListener);\n\n // Hook: agent:afterEvent — fire for every agent event, including headless API sessions.\n // Listens directly on agentInstance so it works regardless of whether a SessionBridge is connected.\n // The bridge previously fired this hook from dispatchAgentEvent, but that path is skipped when\n // no adapter is attached (headless), causing AI response steps to be missing from history.\n const mw = this.middlewareChain;\n const afterEventListener = mw\n ? (event: AgentEvent) => {\n mw.execute(Hook.AGENT_AFTER_EVENT, { sessionId: this.id, event, outgoingMessage: { type: 'text' as const, text: '' } }, async (e) => e).catch(() => {});\n }\n : null;\n\n if (afterEventListener) {\n this.agentInstance.on(SessionEv.AGENT_EVENT, afterEventListener);\n }\n\n // Hook: turn:start — read-only, fire-and-forget\n if (this.middlewareChain) {\n this.middlewareChain.execute(Hook.TURN_START, {\n sessionId: this.id,\n promptText: processed.text,\n promptNumber: this.promptCount,\n turnId: this.activeTurnContext?.turnId ?? turnId ?? '',\n meta,\n userPrompt: this.activeTurnContext?.userPrompt,\n sourceAdapterId: this.activeTurnContext?.sourceAdapterId,\n responseAdapterId: this.activeTurnContext?.responseAdapterId,\n }, async (p) => p).catch(() => {});\n }\n\n let stopReason: string = 'end_turn';\n let promptError: unknown;\n try {\n const response = await this.agentInstance.prompt(processed.text, processed.attachments);\n if (response && typeof response === 'object' && 'stopReason' in response) {\n stopReason = (response as { stopReason?: string }).stopReason ?? 'end_turn';\n }\n // Clear context only after successful prompt — if prompt fails, context is preserved for retry\n if (contextUsed) {\n this.pendingContext = null;\n }\n // Reset \"next\" voice mode only after successful prompt\n if (ttsActive && this.voiceMode === \"next\") {\n this.voiceMode = \"off\";\n }\n } catch (err) {\n stopReason = 'error';\n promptError = err;\n } finally {\n if (accumulatorListener) {\n this.off(SessionEv.AGENT_EVENT, accumulatorListener);\n }\n if (afterEventListener) {\n this.agentInstance.off(SessionEv.AGENT_EVENT, afterEventListener);\n }\n this.off(SessionEv.AGENT_EVENT, turnTextListener);\n\n const finalTurnId = this.activeTurnContext?.turnId ?? turnId ?? '';\n const wasAborted = this._abortedTurnIds.has(finalTurnId);\n if (wasAborted) this._abortedTurnIds.delete(finalTurnId);\n const finalStopReason = wasAborted ? 'interrupted' : stopReason;\n\n // Hook: turn:end — always fires, even on error\n if (this.middlewareChain) {\n this.middlewareChain.execute(Hook.TURN_END, { sessionId: this.id, stopReason: finalStopReason as import('../types.js').StopReason, durationMs: Date.now() - promptStart, turnId: finalTurnId, meta }, async (p) => p).catch(() => {});\n }\n\n // Hook: agent:afterTurn — full assembled text, read-only, fire-and-forget\n if (this.middlewareChain) {\n this.middlewareChain.execute(Hook.AGENT_AFTER_TURN, {\n sessionId: this.id,\n turnId: finalTurnId,\n fullText: turnTextBuffer.join(''),\n stopReason: finalStopReason as import('../types.js').StopReason,\n meta,\n }, async (p) => p).catch(() => {});\n }\n\n // Track the completed turnId so late abortPrompt() calls can retroactively mark it\n this._lastCompletedTurnId = finalTurnId || null;\n // Always clear turn context so routing state is never stale after a failed turn\n this.activeTurnContext = null;\n }\n\n // Re-throw so PromptQueue error handler can call this.fail()\n if (promptError !== undefined) {\n throw promptError;\n }\n\n this.log.info(\n { durationMs: Date.now() - promptStart },\n \"Prompt execution completed\",\n );\n\n // TTS: fire-and-forget post-response synthesis\n if (ttsActive && accumulatedText) {\n this.processTTSResponse(accumulatedText).catch((err) => {\n this.log.warn({ err }, \"TTS post-processing failed\");\n });\n }\n\n // Also trigger auto-naming when name is still the adopted session placeholder.\n if (!this.name || this.name === \"Adopted session\") {\n await this.autoName();\n }\n }\n\n /**\n * Transcribe audio attachments to text if the agent doesn't support audio natively.\n * Audio attachments are removed and their transcriptions are appended to the prompt text.\n */\n private async maybeTranscribeAudio(\n text: string,\n attachments?: Attachment[],\n ): Promise<{ text: string; attachments?: Attachment[] }> {\n if (!attachments?.length || !this.speechService) {\n return { text, attachments };\n }\n\n const hasAudioCapability = this.agentInstance.promptCapabilities?.audio === true;\n if (hasAudioCapability) {\n return { text, attachments };\n }\n\n if (!this.speechService.isSTTAvailable()) {\n return { text, attachments };\n }\n\n let transcribedText = text;\n const remainingAttachments: Attachment[] = [];\n\n for (const att of attachments) {\n if (att.type !== \"audio\") {\n remainingAttachments.push(att);\n continue;\n }\n\n try {\n const audioPath = att.originalFilePath || att.filePath;\n const audioMime = att.originalFilePath ? \"audio/ogg\" : att.mimeType;\n const audioBuffer = await fs.promises.readFile(audioPath);\n const result = await this.speechService.transcribe(audioBuffer, audioMime);\n this.log.info({ provider: \"stt\", duration: result.duration }, \"Voice transcribed\");\n // Notify user of transcription result\n this.emit(SessionEv.AGENT_EVENT, {\n type: \"system_message\",\n message: `🎤 You said: ${result.text}`,\n });\n // Strip [Audio: ...] placeholder since we have the transcription\n transcribedText = transcribedText.replace(/\\[Audio:\\s*[^\\]]*\\]\\s*/g, \"\").trim();\n transcribedText = transcribedText\n ? `${transcribedText}\\n${result.text}`\n : result.text;\n } catch (err) {\n this.log.warn({ err }, \"STT transcription failed, keeping audio attachment\");\n this.emit(SessionEv.AGENT_EVENT, {\n type: \"error\",\n message: `Voice transcription failed: ${(err as Error).message}`,\n });\n remainingAttachments.push(att);\n }\n }\n\n return {\n text: transcribedText,\n attachments: remainingAttachments.length > 0 ? remainingAttachments : undefined,\n };\n }\n\n /** Extract [TTS] block from agent response, synthesize speech, and emit audio_content event. */\n private async processTTSResponse(responseText: string): Promise<void> {\n const match = TTS_BLOCK_REGEX.exec(responseText);\n if (!match?.[1]) {\n this.log.debug(\"No [TTS] block found in response, skipping synthesis\");\n return;\n }\n\n let ttsText = match[1].trim();\n if (!ttsText) return;\n\n if (ttsText.length > TTS_MAX_LENGTH) {\n ttsText = ttsText.slice(0, TTS_MAX_LENGTH);\n }\n\n try {\n let ttsTimer: ReturnType<typeof setTimeout>;\n const timeoutPromise = new Promise<never>((_, reject) => {\n ttsTimer = setTimeout(() => reject(new Error(\"TTS synthesis timed out\")), TTS_TIMEOUT_MS);\n });\n try {\n const result = await Promise.race([\n this.speechService!.synthesize(ttsText),\n timeoutPromise,\n ]);\n const base64 = result.audioBuffer.toString(\"base64\");\n this.emit(SessionEv.AGENT_EVENT, {\n type: \"audio_content\",\n data: base64,\n mimeType: result.mimeType,\n });\n this.emit(SessionEv.AGENT_EVENT, { type: \"tts_strip\" });\n this.log.info(\"TTS synthesis completed\");\n } finally {\n clearTimeout(ttsTimer!);\n }\n } catch (err) {\n this.log.warn({ err }, \"TTS synthesis failed, skipping\");\n }\n }\n\n // Sends a special prompt to the agent to generate a short session title.\n // The session emitter is paused (excluding non-agent_event emissions) so the naming\n // prompt's output is intercepted by a capture handler instead of being forwarded to adapters.\n private async autoName(): Promise<void> {\n let title = \"\";\n\n // Temporarily remove all agent_event listeners so auto-name output\n // is not forwarded to the adapter. Add a capture-only listener instead.\n const captureHandler = (event: AgentEvent) => {\n if (event.type === \"text\") title += event.content;\n // Swallow all other events from auto-name prompt\n };\n\n // Pause the session emitter so agent_event emissions from SessionBridge\n // don't reach the adapter during auto-name. The AgentInstance emitter\n // stays active — we just intercept with our capture handler.\n this.pause((event) => event !== SessionEv.AGENT_EVENT);\n this.agentInstance.on(SessionEv.AGENT_EVENT, captureHandler);\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) || `Session ${this.id.slice(0, 6)}`;\n this.log.info({ name: this.name }, \"Session auto-named\");\n\n // Emit named event — SessionBridge listens to rename the thread\n this.emit(SessionEv.NAMED, this.name);\n } catch {\n this.name = `Session ${this.id.slice(0, 6)}`;\n } finally {\n this.agentInstance.off(SessionEv.AGENT_EVENT, captureHandler);\n // Discard buffered auto-name agent_events, then resume normal delivery\n this.clearBuffer();\n this.resume();\n }\n }\n\n\n // --- ACP Mode / Config / Model State ---\n\n setInitialConfigOptions(options: ConfigOption[]): void {\n this.configOptions = options ?? [];\n }\n\n setAgentCapabilities(caps: AgentCapabilities | undefined): void {\n this.agentCapabilities = caps;\n }\n\n /**\n * Hydrate configOptions and agentCapabilities from a spawn response.\n * Handles both the native configOptions format and legacy modes/models fields.\n */\n applySpawnResponse(resp: { modes?: unknown; configOptions?: unknown; models?: unknown } | undefined, caps: AgentCapabilities | undefined): void {\n if (caps) this.agentCapabilities = caps;\n if (!resp) return;\n\n if (resp.configOptions) {\n this.configOptions = resp.configOptions as ConfigOption[];\n return;\n }\n\n // Convert legacy modes/models fields (old ACP format) to configOptions\n const legacyOptions: ConfigOption[] = [];\n if (resp.modes) {\n const m = resp.modes as SessionModeState;\n legacyOptions.push({\n id: 'mode', name: 'Mode', category: 'mode', type: 'select',\n currentValue: m.currentModeId,\n options: m.availableModes.map(x => ({ value: x.id, name: x.name, description: x.description })),\n });\n }\n if (resp.models) {\n const m = resp.models as SessionModelState;\n legacyOptions.push({\n id: 'model', name: 'Model', category: 'model', type: 'select',\n currentValue: m.currentModelId,\n options: m.availableModels.map(x => ({ value: (x as any).modelId ?? x.id, name: x.name, description: x.description })),\n });\n }\n if (legacyOptions.length > 0) this.configOptions = legacyOptions;\n }\n\n getConfigOption(id: string): ConfigOption | undefined {\n return this.configOptions.find(o => o.id === id);\n }\n\n getConfigByCategory(category: string): ConfigOption | undefined {\n return this.configOptions.find(o => o.category === category);\n }\n\n getConfigValue(id: string): string | undefined {\n const option = this.getConfigOption(id);\n if (!option) return undefined;\n return String(option.currentValue);\n }\n\n /** Set session name explicitly and emit 'named' event */\n setName(name: string): void {\n this.name = name;\n this.emit(SessionEv.NAMED, name);\n }\n\n /** Send a config option change to the agent and update local state from the response. */\n async setConfigOption(configId: string, value: import(\"../types.js\").SetConfigOptionValue): Promise<void> {\n const response = await this.agentInstance.setConfigOption(configId, value);\n if (response.configOptions && response.configOptions.length > 0) {\n await this.updateConfigOptions(response.configOptions as ConfigOption[]);\n } else if (value.type === 'select') {\n // Legacy agents return empty configOptions — update currentValue optimistically.\n const updated = this.configOptions.map((o): ConfigOption =>\n o.id === configId && o.type === 'select' ? { ...o, currentValue: value.value as string } : o\n );\n await this.updateConfigOptions(updated);\n }\n }\n\n async updateConfigOptions(options: ConfigOption[]): Promise<void> {\n // Hook: config:beforeChange — await-able, can block\n if (this.middlewareChain) {\n const result = await this.middlewareChain.execute(Hook.CONFIG_BEFORE_CHANGE, { sessionId: this.id, configId: 'options', oldValue: this.configOptions, newValue: options }, async (p) => p);\n if (!result) return; // blocked by middleware\n }\n this.configOptions = options;\n }\n\n /** Snapshot of current ACP state for persistence */\n toAcpStateSnapshot(): NonNullable<import(\"../types.js\").SessionRecord[\"acpState\"]> {\n return {\n configOptions: this.configOptions.length > 0 ? this.configOptions : undefined,\n agentCapabilities: this.agentCapabilities,\n };\n }\n\n /** Check if the agent supports a specific session capability */\n supportsCapability(cap: 'list' | 'fork' | 'close' | 'loadSession'): boolean {\n if (cap === 'loadSession') return this.agentCapabilities?.loadSession === true;\n return this.agentCapabilities?.sessionCapabilities?.[cap] === true;\n }\n\n /** Cancel the current prompt. Queued prompts continue processing. Stays in active state. */\n async abortPrompt(): Promise<void> {\n // Hook: agent:beforeCancel — modifiable, can block\n if (this.middlewareChain) {\n const result = await this.middlewareChain.execute(Hook.AGENT_BEFORE_CANCEL, { sessionId: this.id }, async (p) => p);\n if (!result) return; // blocked by middleware\n }\n const turnId = this.activeTurnContext?.turnId;\n\n if (turnId) {\n // Turn is still in-flight — mark for interrupted stopReason in the finally block\n this._abortedTurnIds.add(turnId);\n\n // Cancel agent FIRST so the orphaned processPrompt resolves before\n // drainNext starts the next item. Timeout prevents hanging if agent\n // is unresponsive — queue abort proceeds regardless after 5 seconds.\n await Promise.race([\n this.agentInstance.cancel().catch(() => {}),\n new Promise<void>((r) => setTimeout(r, 5000)),\n ]);\n\n this.queue.abortCurrent();\n } else if (this._lastCompletedTurnId && !this.queue.isProcessing) {\n // Turn already completed before cancel arrived — retroactively update\n // the history record so the client sees stopReason: \"interrupted\" on reload.\n const retroTurnId = this._lastCompletedTurnId;\n this._lastCompletedTurnId = null;\n if (this.middlewareChain) {\n await this.middlewareChain.execute(Hook.TURN_END, {\n sessionId: this.id,\n stopReason: 'interrupted' as import('../types.js').StopReason,\n durationMs: 0,\n turnId: retroTurnId,\n }, async (p) => p).catch(() => {});\n }\n }\n\n this.log.info(\"Prompt aborted (queue preserved, %d pending)\", this.queue.pending);\n }\n\n /** Discard all queued prompts without interrupting the in-flight prompt. */\n clearQueue(): void {\n const dropped = this.queue.pending;\n this.queue.clearPending();\n this.log.info(\"Queue cleared (%d pending prompts discarded)\", dropped);\n }\n\n /**\n * Cancel the in-flight prompt AND discard all queued prompts.\n * Full reset — nothing remains in the pipeline after this call.\n */\n async flushAll(): Promise<void> {\n // Hook: agent:beforeCancel — modifiable, can block\n if (this.middlewareChain) {\n const result = await this.middlewareChain.execute(Hook.AGENT_BEFORE_CANCEL, { sessionId: this.id }, async (p) => p);\n if (!result) return; // blocked by middleware\n }\n const turnId = this.activeTurnContext?.turnId;\n if (turnId) this._abortedTurnIds.add(turnId);\n\n // Cancel agent first, then clear everything\n await Promise.race([\n this.agentInstance.cancel().catch(() => {}),\n new Promise<void>((r) => setTimeout(r, 5000)),\n ]);\n\n this.queue.clear();\n this.log.info(\"Session flushed (prompt cancelled, queue cleared)\");\n }\n\n /**\n * Skip the queue: cancel the in-flight prompt, discard all other queued\n * items, and promote the specified turn to process next.\n *\n * Used when the user clicks \"Process Now\" on a queued message — they want\n * THIS message handled immediately, discarding everything else in the pipeline.\n *\n * @returns true if the turn was found and promoted, false if already processed\n */\n async prioritizePrompt(turnId: string): Promise<boolean> {\n // Rearrange queue: keep only the target item\n const found = this.queue.prioritize(turnId);\n if (!found) return false;\n\n // Cancel agent so the current processPrompt resolves and drainNext\n // picks up the promoted item. Timeout fallback if agent is unresponsive.\n await Promise.race([\n this.agentInstance.cancel().catch(() => {}),\n new Promise<void>((r) => setTimeout(r, 5000)),\n ]);\n\n // If the process didn't settle naturally (timeout), force abort\n if (this.queue.isProcessing) {\n this.queue.abortCurrent();\n }\n\n this.log.info({ turnId }, \"Prompt prioritized\");\n return true;\n }\n\n /** Search backward through agentSwitchHistory for the last entry matching agentName */\n findLastSwitchEntry(agentName: string): AgentSwitchEntry | undefined {\n for (let i = this.agentSwitchHistory.length - 1; i >= 0; i--) {\n if (this.agentSwitchHistory[i].agentName === agentName) {\n return this.agentSwitchHistory[i];\n }\n }\n return undefined;\n }\n\n /** Switch the agent instance in-place, preserving session identity */\n async switchAgent(agentName: string, createAgent: () => Promise<AgentInstance>): Promise<void> {\n if (agentName === this.agentName) {\n throw new Error(`Already using ${agentName}`);\n }\n\n // Record current agent in history\n this.agentSwitchHistory.push({\n agentName: this.agentName,\n agentSessionId: this.agentSessionId,\n switchedAt: new Date().toISOString(),\n promptCount: this.promptCount,\n });\n\n // Clear queued prompts and abort in-flight prompt before destroying old agent\n this.queue.clear();\n\n // Reject any pending permission request before destroying old agent\n if (this.permissionGate.isPending) {\n this.permissionGate.reject(\"Agent switched\");\n }\n\n // Destroy old agent\n await this.agentInstance.destroy();\n\n // Create and wire new agent\n const newAgent = await createAgent();\n this.agentInstance = newAgent;\n this.agentName = agentName;\n this.agentSessionId = newAgent.sessionId;\n this.promptCount = 0;\n\n // Hydrate ACP state from the new agent's spawn response\n this.agentCapabilities = undefined;\n this.configOptions = [];\n this.latestCommands = null;\n this.applySpawnResponse(newAgent.initialSessionResponse, newAgent.agentCapabilities);\n\n this.log.info({ from: this.agentSwitchHistory.at(-1)!.agentName, to: agentName }, \"Agent switched\");\n }\n\n /** Tear down the session: reject pending permissions, clear queue, destroy agent subprocess. */\n async destroy(): Promise<void> {\n this.log.info(\"Session destroyed\");\n // Reject any pending permission promise so callers don't hang\n if (this.permissionGate.isPending) {\n this.permissionGate.reject(\"Session destroyed\");\n }\n // Clear queued prompts\n this.queue.clear();\n await this.agentInstance.destroy();\n closeSessionLogger(this.log);\n }\n}\n","import type { Attachment, TurnMeta } from '../types.js'\nimport type { TurnRouting } from './turn-context.js'\n\n/**\n * Serial prompt queue — ensures prompts are processed one at a time.\n *\n * Agents are stateful (each prompt builds on prior context), so concurrent\n * prompts would corrupt the conversation. This queue guarantees that only\n * one prompt is processed at a time; additional prompts are buffered and\n * drained sequentially after the current one completes.\n */\nexport class PromptQueue {\n private queue: Array<{ text: string; userPrompt: string; attachments?: Attachment[]; routing?: TurnRouting; turnId?: string; meta?: TurnMeta; resolve: () => void }> = []\n private processing = false\n private abortController: AbortController | null = null\n /** Set when abort is triggered; drainNext waits for the current processor to settle before starting the next item. */\n private processorSettled: Promise<void> | null = null\n\n constructor(\n private processor: (text: string, userPrompt: string, attachments?: Attachment[], routing?: TurnRouting, turnId?: string, meta?: TurnMeta) => Promise<void>,\n private onError?: (err: unknown) => void,\n // Fires synchronously when an item is placed behind a running prompt — before it's pushed\n // to the pending list. Called with accurate queue depth so callers can emit notifications\n // without a race condition on promptRunning state.\n private onActuallyQueued?: (turnId: string | undefined, position: number, routing: TurnRouting | undefined) => void,\n ) {}\n\n /**\n * Add a prompt to the queue. If no prompt is currently processing, it runs\n * immediately. Otherwise, it's buffered and the returned promise resolves\n * only after the prompt finishes processing.\n */\n async enqueue(text: string, userPrompt: string, attachments?: Attachment[], routing?: TurnRouting, turnId?: string, meta?: TurnMeta): Promise<void> {\n if (this.processing) {\n // Fire synchronously BEFORE pushing so the caller sees accurate position and promptRunning state.\n // This eliminates the race condition where multiple concurrent enqueue() calls all observe\n // processing=false before any of them sets it to true.\n const position = this.queue.length + 1;\n this.onActuallyQueued?.(turnId, position, routing);\n return new Promise<void>((resolve) => {\n this.queue.push({ text, userPrompt, attachments, routing, turnId, meta, resolve })\n })\n }\n await this.process(text, userPrompt, attachments, routing, turnId, meta)\n }\n\n /** Run a single prompt through the processor, then drain the next queued item. */\n private async process(text: string, userPrompt: string, attachments?: Attachment[], routing?: TurnRouting, turnId?: string, meta?: TurnMeta): Promise<void> {\n this.processing = true\n this.abortController = new AbortController()\n const { signal } = this.abortController\n let settledResolve: () => void\n this.processorSettled = new Promise<void>((r) => { settledResolve = r })\n try {\n await Promise.race([\n this.processor(text, userPrompt, attachments, routing, turnId, meta),\n new Promise<never>((_, reject) => {\n signal.addEventListener('abort', () => reject(new Error('Prompt aborted')), { once: true })\n }),\n ])\n } catch (err) {\n // Only forward non-abort errors to onError handler\n if (!(err instanceof Error && err.message === 'Prompt aborted')) {\n this.onError?.(err)\n }\n } finally {\n this.abortController = null\n this.processing = false\n settledResolve!()\n this.processorSettled = null\n this.drainNext()\n }\n }\n\n /** Dequeue and process the next pending prompt, if any. Called after each prompt completes. */\n private drainNext(): void {\n const next = this.queue.shift()\n if (next) {\n this.process(next.text, next.userPrompt, next.attachments, next.routing, next.turnId, next.meta).then(next.resolve)\n }\n }\n\n /**\n * Abort only the in-flight prompt, keeping queued prompts intact.\n * The queue will automatically drain to the next item via `drainNext()`\n * in the `process()` finally block.\n */\n abortCurrent(): void {\n if (this.abortController) {\n this.abortController.abort()\n }\n }\n\n /**\n * Abort the in-flight prompt and discard all queued prompts.\n * Pending promises are resolved (not rejected) so callers don't see unhandled rejections.\n */\n clear(): void {\n this.abortCurrent()\n // Resolve pending promises so callers don't hang\n for (const item of this.queue) {\n item.resolve()\n }\n this.queue = []\n }\n\n /**\n * Discard all queued prompts without aborting the in-flight prompt.\n * The currently processing prompt continues to completion; only pending\n * (not-yet-started) items are removed. Their promises are resolved\n * (not rejected) so callers don't see unhandled rejections.\n */\n clearPending(): void {\n for (const item of this.queue) {\n item.resolve()\n }\n this.queue = []\n }\n\n /**\n * Promote a specific queued item to the front and discard all others.\n *\n * Finds the item with the matching turnId, removes every other pending item\n * (resolving their promises), and leaves only the target in the queue.\n * Does NOT abort the in-flight prompt — caller handles that separately.\n *\n * @returns true if the item was found and promoted, false if not in queue\n */\n prioritize(turnId: string): boolean {\n const idx = this.queue.findIndex(item => item.turnId === turnId)\n if (idx === -1) return false\n const target = this.queue[idx]\n for (let i = 0; i < this.queue.length; i++) {\n if (i !== idx) this.queue[i].resolve()\n }\n this.queue = [target]\n return true\n }\n\n get pending(): number {\n return this.queue.length\n }\n\n get isProcessing(): boolean {\n return this.processing\n }\n\n /** Snapshot of queued (not yet processing) items — used for queue inspection by callers. */\n get pendingItems(): Array<{ userPrompt: string; turnId?: string }> {\n return this.queue.map(item => ({\n userPrompt: item.userPrompt,\n turnId: item.turnId,\n }))\n }\n}\n","import type { PermissionRequest } from \"../types.js\";\n\nconst DEFAULT_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes\n\n/**\n * Blocks the prompt pipeline until the user approves or denies a permission request.\n *\n * When an agent requests permission (e.g., to run a shell command), AgentInstance\n * calls its `onPermissionRequest` callback. SessionBridge handles this by calling\n * `setPending()`, which returns a promise that blocks the ACP prompt/response cycle\n * until `resolve()` or `reject()` is called. If the user doesn't respond within\n * the timeout, the request is automatically rejected.\n *\n * Only one permission request can be pending at a time — setting a new one\n * supersedes (rejects) the previous.\n *\n * When `bypassPermissions` is enabled on the session, SessionBridge short-circuits\n * this gate entirely: `setPending()` is never called, and permissions are auto-approved\n * upstream before the request reaches this class.\n */\nexport class PermissionGate {\n private request?: PermissionRequest;\n private resolveFn?: (optionId: string) => void;\n private rejectFn?: (reason: Error) => void;\n private settled = false;\n private timeoutTimer?: ReturnType<typeof setTimeout>;\n private timeoutMs: number;\n\n constructor(timeoutMs?: number) {\n this.timeoutMs = timeoutMs ?? DEFAULT_TIMEOUT_MS;\n }\n\n /**\n * Register a new permission request and return a promise that resolves with the\n * chosen option ID when the user responds, or rejects on timeout / supersession.\n */\n setPending(request: PermissionRequest): Promise<string> {\n // Reject any existing pending promise so callers don't hang forever\n if (!this.settled && this.rejectFn) {\n this.rejectFn(new Error(\"Superseded by new permission request\"));\n }\n this.request = request;\n this.settled = false;\n this.clearTimeout();\n\n return new Promise<string>((resolve, reject) => {\n this.resolveFn = resolve;\n this.rejectFn = reject;\n\n this.timeoutTimer = setTimeout(() => {\n this.reject(\"Permission request timed out (no response received)\");\n }, this.timeoutMs);\n // unref() prevents the timeout from keeping the process alive during shutdown\n if (typeof this.timeoutTimer === 'object' && 'unref' in this.timeoutTimer) {\n (this.timeoutTimer as NodeJS.Timeout).unref();\n }\n });\n }\n\n /** Approve the pending request with the given option ID. No-op if already settled. */\n resolve(optionId: string): void {\n if (this.settled || !this.resolveFn) return;\n this.settled = true;\n this.clearTimeout();\n this.resolveFn(optionId);\n this.cleanup();\n }\n\n /** Deny the pending request. No-op if already settled. */\n reject(reason?: string): void {\n if (this.settled || !this.rejectFn) return;\n this.settled = true;\n this.clearTimeout();\n this.rejectFn(new Error(reason ?? \"Permission rejected\"));\n this.cleanup();\n }\n\n get isPending(): boolean {\n return !!this.request && !this.settled;\n }\n\n get currentRequest(): PermissionRequest | undefined {\n return this.isPending ? this.request : undefined;\n }\n\n /** The request ID of the current pending request, undefined after settlement */\n get requestId(): string | undefined {\n return this.request?.id;\n }\n\n private clearTimeout(): void {\n if (this.timeoutTimer) {\n clearTimeout(this.timeoutTimer);\n this.timeoutTimer = undefined;\n }\n }\n\n private cleanup(): void {\n this.request = undefined;\n this.resolveFn = undefined;\n this.rejectFn = undefined;\n }\n}\n","import { nanoid } from \"nanoid\";\nimport type { AgentEvent } from \"../types.js\";\nimport type { Attachment, TurnMeta } from \"../types.js\";\n\n/**\n * Immutable context for a single user-prompt → agent-response cycle (\"turn\").\n *\n * Sealed when a prompt is dequeued from the PromptQueue and cleared after the turn\n * completes. Bridges use this to route agent events to the correct adapter when\n * multiple adapters are attached to the same session.\n */\nexport interface TurnContext {\n /** Unique identifier for this turn — shared between message:queued and message:processing events. */\n turnId: string;\n /** The adapter that originated this prompt. */\n sourceAdapterId: string;\n /** Where to send the response: null = silent (suppress), undefined = same as source, string = explicit target. */\n responseAdapterId?: string | null;\n /** The raw user prompt before any middleware modifications. */\n userPrompt: string;\n /** The final prompt text sent to the agent after all middleware transformations. */\n finalPrompt: string;\n /** File or media attachments included with the prompt. */\n attachments?: Attachment[];\n /** Caller-supplied metadata for this turn (e.g., identity, source info). */\n meta?: TurnMeta;\n}\n\n/**\n * Routing hints attached to an incoming prompt — carried through the queue\n * and used to construct TurnContext when the prompt is dequeued.\n */\nexport interface TurnRouting {\n sourceAdapterId: string;\n responseAdapterId?: string | null;\n}\n\n/** Identity and display info for the user who sent the turn. */\nexport interface TurnSender {\n userId: string;\n identityId: string;\n displayName?: string;\n username?: string;\n}\n\n/**\n * Extract sender identity from turn metadata.\n * Returns null if identity fields are missing or incomplete.\n */\nexport function extractSender(meta?: TurnMeta): TurnSender | null {\n const identity = (meta as any)?.identity;\n if (!identity || !identity.userId || !identity.identityId) return null;\n return {\n userId: identity.userId,\n identityId: identity.identityId,\n displayName: identity.displayName,\n username: identity.username,\n };\n}\n\n/**\n * Get the effective response adapter for a turn.\n * - null → silent (no adapter renders)\n * - undefined → fallback to sourceAdapterId\n * - string → explicit target\n */\nexport function getEffectiveTarget(ctx: TurnContext): string | null {\n if (ctx.responseAdapterId === null) return null;\n return ctx.responseAdapterId ?? ctx.sourceAdapterId;\n}\n\n/**\n * Create a new TurnContext. Called when a prompt is dequeued from the queue.\n * If a pre-generated turnId is provided (from enqueuePrompt), it is used; otherwise a new one is generated.\n */\nexport function createTurnContext(\n sourceAdapterId: string,\n responseAdapterId: string | null | undefined,\n turnId: string | undefined,\n userPrompt: string,\n finalPrompt: string,\n attachments?: Attachment[],\n meta?: TurnMeta,\n): TurnContext {\n return {\n turnId: turnId ?? nanoid(8),\n sourceAdapterId,\n responseAdapterId,\n userPrompt,\n finalPrompt,\n attachments,\n meta,\n };\n}\n\n/**\n * System events are broadcast to ALL attached adapters.\n * Turn events are routed only to the response adapter.\n */\nconst SYSTEM_EVENT_TYPES = new Set([\n \"session_end\",\n \"system_message\",\n \"session_info_update\",\n \"config_option_update\",\n \"commands_update\",\n \"tts_strip\",\n]);\n\nexport function isSystemEvent(event: AgentEvent): boolean {\n return SYSTEM_EVENT_TYPES.has(event.type);\n}\n","import * as path from \"node:path\";\nimport type { AgentEvent, OutgoingMessage } from \"./types.js\";\nimport type { TunnelServiceInterface } from \"./plugin/types.js\";\nimport { extractFileInfo } from \"./utils/extract-file-info.js\";\nimport { isApplyPatchOtherTool } from \"./utils/apply-patch-detection.js\";\nimport { createChildLogger } from \"./utils/log.js\";\n\nconst log = createChildLogger({ module: \"message-transformer\" });\n\n// Binary file extensions that cannot be displayed in the code viewer\nconst BINARY_VIEWER_EXTENSIONS = new Set([\n '.wav', '.ogg', '.mp3', '.m4a', '.aac', '.flac', '.opus', '.weba',\n '.mp4', '.avi', '.mov', '.webm', '.mkv',\n '.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.ico', '.svg',\n '.pdf', '.zip', '.gz', '.tar', '.7z', '.rar',\n '.bin', '.exe', '.dll', '.so', '.dylib', '.wasm',\n]);\n\n/**\n * Compute actual line-level diff by stripping common prefix/suffix lines.\n * This avoids counting unchanged context lines as added/removed.\n */\nfunction computeLineDiff(oldStr: string, newStr: string): { added: number; removed: number } {\n const oldLines = oldStr ? oldStr.split(\"\\n\") : [];\n const newLines = newStr ? newStr.split(\"\\n\") : [];\n\n // Find common prefix\n let prefixLen = 0;\n const minLen = Math.min(oldLines.length, newLines.length);\n while (prefixLen < minLen && oldLines[prefixLen] === newLines[prefixLen]) {\n prefixLen++;\n }\n\n // Find common suffix (not overlapping with prefix)\n let suffixLen = 0;\n const maxSuffix = minLen - prefixLen;\n while (\n suffixLen < maxSuffix &&\n oldLines[oldLines.length - 1 - suffixLen] === newLines[newLines.length - 1 - suffixLen]\n ) {\n suffixLen++;\n }\n\n return {\n added: Math.max(0, newLines.length - prefixLen - suffixLen),\n removed: Math.max(0, oldLines.length - prefixLen - suffixLen),\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction asNonNegativeNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : null;\n}\n\n/**\n * OpenCode ACP apply_patch may include diff counts in rawOutput.metadata:\n * - metadata.files[].additions/deletions\n * - metadata.additions/deletions\n */\nfunction extractDiffStatsFromRawOutput(rawOutput: unknown): { added: number; removed: number } | null {\n const output = asRecord(rawOutput);\n const metadata = asRecord(output?.metadata);\n if (!metadata) return null;\n\n const files = Array.isArray(metadata.files) ? metadata.files : null;\n if (files && files.length > 0) {\n let added = 0;\n let removed = 0;\n let hasAny = false;\n\n for (const file of files) {\n const fileMeta = asRecord(file);\n if (!fileMeta) continue;\n\n const fileAdded = asNonNegativeNumber(fileMeta.additions);\n const fileRemoved = asNonNegativeNumber(fileMeta.deletions);\n if (fileAdded === null && fileRemoved === null) continue;\n\n added += fileAdded ?? 0;\n removed += fileRemoved ?? 0;\n hasAny = true;\n }\n\n if (hasAny && (added > 0 || removed > 0)) return { added, removed };\n }\n\n const added = asNonNegativeNumber(metadata.additions);\n const removed = asNonNegativeNumber(metadata.deletions);\n if (added === null && removed === null) return null;\n if ((added ?? 0) === 0 && (removed ?? 0) === 0) return null;\n\n return {\n added: added ?? 0,\n removed: removed ?? 0,\n };\n}\n\nfunction extractDiffStatsFromToolPayload(\n name: string,\n kind: string | undefined,\n rawInput: unknown,\n rawOutput: unknown,\n): { added: number; removed: number } | null {\n if (kind === \"edit\" || kind === \"write\") {\n const ri = asRecord(rawInput);\n if (!ri) return null;\n\n const oldStr = typeof ri.old_string === \"string\" ? ri.old_string : typeof ri.oldText === \"string\" ? ri.oldText : null;\n const newStr = typeof ri.new_string === \"string\" ? ri.new_string : typeof ri.newText === \"string\" ? ri.newText : typeof ri.content === \"string\" ? ri.content : null;\n\n if (oldStr !== null && newStr !== null) {\n const stats = computeLineDiff(oldStr, newStr);\n return stats.added > 0 || stats.removed > 0 ? stats : null;\n }\n\n if (oldStr === null && newStr !== null && kind === \"write\") {\n // New file creation — no old content, count all new lines as added\n const added = newStr.split(\"\\n\").length;\n return added > 0 ? { added, removed: 0 } : null;\n }\n\n return null;\n }\n\n if (isApplyPatchOtherTool(kind, name, rawInput)) {\n return extractDiffStatsFromRawOutput(rawOutput);\n }\n\n return null;\n}\n\n/**\n * Transforms AgentEvents into OutgoingMessages suitable for adapter delivery.\n *\n * Handles two key concerns beyond simple type mapping:\n * 1. **Tool input caching** — `tool_call` events carry `rawInput`, but subsequent\n * `tool_update` events for the same tool often omit it. The transformer caches\n * rawInput so updates can inherit it for viewer link generation.\n * 2. **Viewer link enrichment** — when a tunnel service is available, tool events\n * for file reads/edits are enriched with public URLs to the code viewer and diff viewer.\n * Intermediate updates (with raw content) are preferred over completion events\n * (which have formatted content with line numbers).\n */\nexport class MessageTransformer {\n tunnelService?: TunnelServiceInterface;\n /** Cache rawInput from tool_call so it's available in tool_update (which often lacks it) */\n private toolRawInputCache: Map<string, unknown> = new Map();\n /** Cache viewer links generated from intermediate updates so completion events carry them */\n private toolViewerCache: Map<string, { viewerLinks: Record<string, string>; viewerFilePath: string }> = new Map();\n\n constructor(tunnelService?: TunnelServiceInterface) {\n this.tunnelService = tunnelService;\n }\n\n /**\n * Convert an agent event to an outgoing message for adapter delivery.\n *\n * For tool events, enriches the metadata with diff stats and viewer links\n * when a tunnel service is available.\n */\n transform(\n event: AgentEvent,\n sessionContext?: { id: string; workingDirectory: string },\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 // Cache rawInput (only if non-empty) for later tool_update events\n if (event.id && this.isNonEmptyInput(event.rawInput)) {\n this.toolRawInputCache.set(event.id, event.rawInput);\n }\n const meta = event.meta as Record<string, unknown> | undefined;\n const metadata: Record<string, unknown> = {\n id: event.id,\n name: event.name,\n kind: event.kind,\n status: event.status,\n content: event.content,\n locations: event.locations,\n rawInput: event.rawInput,\n displaySummary: meta?.displaySummary,\n displayTitle: meta?.displayTitle,\n displayKind: meta?.displayKind,\n };\n this.enrichWithViewerLinks(event, metadata, sessionContext);\n return { type: \"tool_call\", text: event.name, metadata };\n }\n case \"tool_update\": {\n // Update cache if this update carries non-empty rawInput (some agents send it on first update)\n if (event.id && this.isNonEmptyInput(event.rawInput)) {\n this.toolRawInputCache.set(event.id, event.rawInput);\n }\n // Merge cached rawInput when tool_update doesn't carry it\n const cachedRawInput = event.id ? this.toolRawInputCache.get(event.id) : undefined;\n const effectiveRawInput = this.isNonEmptyInput(event.rawInput) ? event.rawInput : cachedRawInput;\n // Clean up cache on terminal status\n if (event.id && (event.status === \"completed\" || event.status === \"done\" || event.status === \"failed\" || event.status === \"error\")) {\n this.toolRawInputCache.delete(event.id);\n }\n const meta = event.meta as Record<string, unknown> | undefined;\n const metadata: Record<string, unknown> = {\n id: event.id,\n name: event.name,\n kind: event.kind,\n status: event.status,\n content: event.content,\n rawInput: effectiveRawInput,\n displaySummary: meta?.displaySummary,\n displayTitle: meta?.displayTitle,\n displayKind: meta?.displayKind,\n };\n // Use a synthetic event with merged rawInput for enrichWithViewerLinks\n const enrichEvent = { ...event, rawInput: effectiveRawInput };\n this.enrichWithViewerLinks(enrichEvent as typeof event, metadata, sessionContext);\n // Viewer link caching: intermediate updates have raw content (preferred),\n // completion events have formatted content (with line numbers) — always prefer cached\n if (event.id) {\n const cached = this.toolViewerCache.get(event.id);\n if (cached) {\n // Cache already has links from intermediate update (raw content) — always use those\n metadata.viewerLinks = cached.viewerLinks;\n metadata.viewerFilePath = cached.viewerFilePath;\n } else if (metadata.viewerLinks) {\n // First time generating links — cache them\n this.toolViewerCache.set(event.id, {\n viewerLinks: metadata.viewerLinks as Record<string, string>,\n viewerFilePath: metadata.viewerFilePath as string,\n });\n }\n // Clean up viewer cache on terminal status\n if (event.status === \"completed\" || event.status === \"done\" || event.status === \"failed\" || event.status === \"error\") {\n this.toolViewerCache.delete(event.id);\n }\n }\n return { type: \"tool_update\", text: \"\", metadata };\n }\n case \"plan\":\n return {\n type: \"plan\",\n text: \"\",\n metadata: { entries: event.entries },\n };\n case \"usage\":\n return {\n type: \"usage\",\n text: \"\",\n metadata: {\n tokensUsed: event.tokensUsed,\n contextSize: event.contextSize,\n cost: event.cost?.amount,\n },\n };\n case \"session_end\":\n return { type: \"session_end\", text: `Done (${event.reason})` };\n case \"error\":\n return { type: \"error\", text: event.message };\n case \"system_message\":\n return { type: \"system_message\", text: event.message };\n case \"session_info_update\":\n return {\n type: \"system_message\",\n text: `Session updated: ${event.title ?? \"\"}`.trim(),\n metadata: { title: event.title, updatedAt: event.updatedAt },\n };\n case \"config_option_update\":\n return {\n type: \"config_update\",\n text: \"Config updated\",\n metadata: { options: event.options },\n };\n case \"user_message_chunk\":\n return {\n type: \"user_replay\",\n text: event.content,\n };\n case \"resource_content\":\n return {\n type: \"resource\",\n text: event.name,\n metadata: { uri: event.uri, text: event.text, blob: event.blob, mimeType: event.mimeType },\n };\n case \"resource_link\":\n return {\n type: \"resource_link\",\n text: event.name,\n metadata: { uri: event.uri, mimeType: event.mimeType, title: event.title, description: event.description, size: event.size },\n };\n default:\n return { type: \"text\", text: \"\" };\n }\n }\n\n /** Clear cached entries whose key starts with the given prefix. */\n clearSessionCaches(prefix: string): void {\n for (const key of this.toolRawInputCache.keys()) {\n if (key.startsWith(prefix)) this.toolRawInputCache.delete(key);\n }\n for (const key of this.toolViewerCache.keys()) {\n if (key.startsWith(prefix)) this.toolViewerCache.delete(key);\n }\n }\n\n /** Clear all caches. */\n clearCaches(): void {\n this.toolRawInputCache.clear();\n this.toolViewerCache.clear();\n }\n\n /** Check if rawInput is a non-empty object (not null, not {}) */\n private isNonEmptyInput(input: unknown): input is Record<string, unknown> {\n return input !== null && input !== undefined && typeof input === \"object\" && !Array.isArray(input) && Object.keys(input as object).length > 0;\n }\n\n private enrichWithViewerLinks(\n event: AgentEvent & { type: \"tool_call\" | \"tool_update\" },\n metadata: Record<string, unknown>,\n sessionContext?: { id: string; workingDirectory: string },\n ): void {\n const name = \"name\" in event ? event.name || \"\" : \"\";\n // Compute diffStats from tool payload even without tunnelService.\n // Includes edit/write rawInput and apply_patch rawOutput compatibility.\n const kind = \"kind\" in event ? event.kind : undefined;\n if (!metadata.diffStats) {\n const stats = extractDiffStatsFromToolPayload(name, kind, event.rawInput, event.rawOutput);\n if (stats) metadata.diffStats = stats;\n }\n\n if (!this.tunnelService || !sessionContext) {\n log.debug(\n { hasTunnel: !!this.tunnelService, hasCtx: !!sessionContext, kind },\n \"enrichWithViewerLinks: skipping (no tunnel or session context)\",\n );\n return;\n }\n\n log.debug(\n { name, kind, status: event.status, hasContent: !!event.content, hasRawInput: !!event.rawInput },\n \"enrichWithViewerLinks: inspecting event\",\n );\n\n const fileInfo = extractFileInfo(\n name,\n kind,\n event.content,\n event.rawInput,\n event.meta,\n event.rawOutput,\n );\n if (!fileInfo) {\n log.debug(\n { name, kind, hasContent: !!event.content, hasRawInput: !!event.rawInput, hasMeta: !!event.meta },\n \"enrichWithViewerLinks: extractFileInfo returned null\",\n );\n return;\n }\n\n // Skip binary/non-text files — they cannot be displayed in the code viewer\n const fileExt = path.extname(fileInfo.filePath).toLowerCase();\n if (BINARY_VIEWER_EXTENSIONS.has(fileExt)) {\n log.debug({ kind, filePath: fileInfo.filePath }, \"enrichWithViewerLinks: skipping binary file\");\n return;\n }\n\n // Skip viewer link generation if the tunnel only has a localhost URL —\n // Telegram strips <a href=\"localhost:...\"> tags, rendering plain unclickable text.\n const publicUrl = this.tunnelService.getPublicUrl();\n if (publicUrl.startsWith(\"http://localhost\") || publicUrl.startsWith(\"http://127.0.0.1\")) {\n log.debug({ kind, filePath: fileInfo.filePath }, \"enrichWithViewerLinks: skipping (no public tunnel URL)\");\n return;\n }\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 sessionContext.id,\n fileInfo.filePath,\n fileInfo.oldContent,\n fileInfo.content,\n sessionContext.workingDirectory,\n );\n if (id) viewerLinks.diff = this.tunnelService.diffUrl(id);\n\n // Compute diff stats from full file content only if not already set from rawInput\n if (!metadata.diffStats) {\n const stats = computeLineDiff(fileInfo.oldContent, fileInfo.content);\n if (stats.added > 0 || stats.removed > 0) {\n metadata.diffStats = stats;\n }\n }\n }\n\n // Always store as file view (new file creation or read)\n const id = store.storeFile(\n sessionContext.id,\n fileInfo.filePath,\n fileInfo.content,\n sessionContext.workingDirectory,\n );\n if (id) viewerLinks.file = this.tunnelService.fileUrl(id);\n\n if (Object.keys(viewerLinks).length > 0) {\n metadata.viewerLinks = viewerLinks;\n metadata.viewerFilePath = fileInfo.filePath;\n }\n }\n}\n","import { isApplyPatchOtherTool } from './apply-patch-detection.js'\n\n/**\n * Extracted file content from an agent tool call.\n *\n * Used by the file-service plugin to provide live file previews —\n * when an agent reads, edits, or writes a file, this structure\n * captures what changed so adapters can show inline diffs or previews.\n */\nexport interface FileInfo {\n filePath: string\n content: string\n /** Previous content before an edit — enables diff rendering in adapters. */\n oldContent?: string\n}\n\n/**\n * Extract file path and content from ACP tool_call/tool_update payloads.\n *\n * Different agents (Claude Code, OpenCode, etc.) encode file operations in\n * varying formats. This function normalizes them all into a single FileInfo\n * structure by trying multiple extraction strategies in priority order:\n *\n * 1. Agent-specific _meta extensions (highest fidelity — raw file content)\n * 2. rawInput fields (file_path + content)\n * 3. Known ACP content patterns (diff blocks, text blocks, content wrappers)\n *\n * Returns null if the tool call doesn't involve a file operation.\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 * - rawOutput (OpenCode apply_patch): { metadata: { files: [{ filePath, before, after }] } }\n */\nexport function extractFileInfo(\n name: string,\n kind: string | undefined,\n content: unknown,\n rawInput?: unknown,\n meta?: unknown,\n rawOutput?: unknown,\n): FileInfo | null {\n // apply_patch is a special \"other\" tool that modifies files via patch text\n if (isApplyPatchOtherTool(kind, name, rawInput)) {\n return parseApplyPatchRawOutput(rawOutput)\n }\n if (kind && !['read', 'edit', 'write'].includes(kind)) return null\n\n let info: Partial<FileInfo> | null = null\n\n // 1. Try agent-specific _meta extensions (e.g. _meta.claudeCode.toolResponse)\n // This is the highest-fidelity source — raw file content without formatting.\n // Falls through to generic paths 2-3 for agents that don't use this namespace.\n if (meta) {\n const m = meta as Record<string, unknown>\n const toolResponse = resolveToolResponse(m)\n\n if (toolResponse) {\n // Read: toolResponse.file.filePath + file.content\n const file = toolResponse.file as Record<string, unknown> | undefined\n if (typeof file?.filePath === 'string' && typeof file?.content === 'string') {\n info = { filePath: file.filePath, content: file.content }\n }\n // Edit: toolResponse.originalFile (full before-edit content) + oldString/newString\n if (!info && typeof toolResponse.filePath === 'string' && typeof toolResponse.originalFile === 'string') {\n const originalFile = toolResponse.originalFile as string\n const oldString = typeof toolResponse.oldString === 'string' ? toolResponse.oldString : undefined\n const newString = typeof toolResponse.newString === 'string' ? toolResponse.newString : undefined\n const newContent = oldString && newString\n ? originalFile.replace(oldString, newString)\n : originalFile\n info = { filePath: toolResponse.filePath as string, content: newContent, oldContent: originalFile }\n }\n // Write: toolResponse.filePath + toolResponse.content\n if (!info && typeof toolResponse.filePath === 'string' && typeof toolResponse.content === 'string') {\n info = { filePath: toolResponse.filePath, content: toolResponse.content }\n }\n }\n }\n\n // 2. Try rawInput for file path + content\n if (!info && rawInput && typeof rawInput === 'object') {\n const ri = rawInput as Record<string, unknown>\n const filePath = ri?.file_path || ri?.filePath || ri?.path\n if (typeof filePath === 'string') {\n const parsed = content ? parseContent(content) : null\n const riContent = typeof ri?.content === 'string' ? ri.content : undefined\n\n if (kind === 'edit') {\n // Edit tool: use old_string/new_string from rawInput for diff viewer\n const oldStr = typeof ri.old_string === 'string' ? ri.old_string : typeof ri.oldText === 'string' ? ri.oldText : undefined\n const newStr = typeof ri.new_string === 'string' ? ri.new_string : typeof ri.newText === 'string' ? ri.newText : undefined\n if (newStr) {\n info = { filePath, content: newStr, oldContent: oldStr }\n } else {\n // Fallback to riContent, then parsed content (e.g. ACP diff blocks)\n info = { filePath, content: riContent || parsed?.content, oldContent: parsed?.oldContent }\n }\n } else if (kind === 'write') {\n // Write tool: prefer rawInput.content (the actual file content) over tool result message\n info = { filePath, content: riContent || parsed?.content, oldContent: parsed?.oldContent }\n } else {\n // Read and other kinds: tool_update content IS the file content\n info = { filePath, content: parsed?.content || riContent, oldContent: parsed?.oldContent }\n }\n }\n }\n\n // 3. Try to extract from known ACP content patterns\n if (!info && content) {\n info = parseContent(content)\n }\n\n if (!info) return null\n\n // Infer file path from tool name if not in content (e.g., \"Read src/index.ts\")\n if (!info.filePath) {\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\n/**\n * Extract file info from apply_patch rawOutput format.\n *\n * Picks the file with the most changes (additions + deletions) as\n * the primary file to preview — patches can touch multiple files.\n */\nfunction parseApplyPatchRawOutput(rawOutput: unknown): FileInfo | null {\n if (!rawOutput || typeof rawOutput !== 'object' || Array.isArray(rawOutput)) return null\n\n const output = rawOutput as Record<string, unknown>\n const metadata = output.metadata\n if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) return null\n\n const files = (metadata as Record<string, unknown>).files\n if (!Array.isArray(files)) return null\n\n // Sort by change size (descending) to pick the most-changed file for preview\n const sortedFiles = [...files].sort((a, b) => getApplyPatchFileScore(b) - getApplyPatchFileScore(a))\n\n for (const file of sortedFiles) {\n if (!file || typeof file !== 'object' || Array.isArray(file)) continue\n const f = file as Record<string, unknown>\n\n const filePath = typeof f.filePath === 'string'\n ? f.filePath\n : typeof f.relativePath === 'string'\n ? f.relativePath\n : null\n if (!filePath) continue\n\n const after = typeof f.after === 'string' ? f.after : null\n if (!after) continue\n\n const before = typeof f.before === 'string' ? f.before : undefined\n return {\n filePath,\n content: after,\n oldContent: before,\n }\n }\n\n return null\n}\n\n/** Score a file by total change volume (additions + deletions) for ranking. */\nfunction getApplyPatchFileScore(file: unknown): number {\n if (!file || typeof file !== 'object' || Array.isArray(file)) return 0\n const f = file as Record<string, unknown>\n const additions = typeof f.additions === 'number' && Number.isFinite(f.additions) && f.additions >= 0 ? f.additions : 0\n const deletions = typeof f.deletions === 'number' && Number.isFinite(f.deletions) && f.deletions >= 0 ? f.deletions : 0\n return additions + deletions\n}\n\n/**\n * Resolve toolResponse from agent-specific _meta namespaces.\n *\n * Currently supports: `_meta.claudeCode.toolResponse` (Claude Code).\n * Add new agent namespaces here as they adopt the pattern.\n */\nfunction resolveToolResponse(meta: Record<string, unknown>): Record<string, unknown> | undefined {\n // Claude Code namespace\n const claudeCode = meta.claudeCode as Record<string, unknown> | undefined\n if (claudeCode?.toolResponse && typeof claudeCode.toolResponse === 'object') {\n return claudeCode.toolResponse as Record<string, unknown>\n }\n // Generic: _meta.toolResponse (for agents that put it directly)\n if (meta.toolResponse && typeof meta.toolResponse === 'object') {\n return meta.toolResponse as Record<string, unknown>\n }\n return undefined\n}\n\n/**\n * Recursively parse ACP content structures into a partial FileInfo.\n *\n * Handles all known content shapes: plain strings, diff blocks,\n * content wrappers, text blocks, and nested input/output objects.\n */\nfunction parseContent(content: unknown): Partial<FileInfo> | null {\n if (typeof content === 'string') {\n return { content }\n }\n\n if (Array.isArray(content)) {\n // Return the first block that yields a result — order matters for priority\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 { AgentManager } from \"../agents/agent-manager.js\";\nimport { Session } from \"./session.js\";\nimport type { SessionStore } from \"./session-store.js\";\nimport type { EventBus } from \"../event-bus.js\";\nimport type { MiddlewareChain } from \"../plugin/middleware-chain.js\";\nimport type { SessionStatus, ConfigOption, AgentCapabilities } from \"../types.js\";\nimport { Hook, BusEvent } from \"../events.js\";\n\n/** Flattened view of a session for API consumers — merges live state with stored record. */\nexport interface SessionSummary {\n id: string;\n agent: string;\n status: SessionStatus;\n name: string | null;\n workspace: string;\n channelId: string;\n createdAt: string;\n lastActiveAt: string | null;\n dangerousMode: boolean;\n queueDepth: number;\n promptRunning: boolean;\n configOptions?: ConfigOption[];\n capabilities: AgentCapabilities | null;\n isLive: boolean;\n}\n\n/**\n * Registry for live Session instances. Provides lookup by session ID, channel+thread,\n * or agent session ID. Coordinates session lifecycle: creation, cancellation, persistence,\n * and graceful shutdown.\n *\n * Live sessions are kept in an in-memory Map. The optional SessionStore handles\n * disk persistence — the manager delegates save/patch/remove operations to the store.\n */\nexport class SessionManager {\n private sessions: Map<string, Session> = new Map();\n private store: SessionStore | null;\n private eventBus?: EventBus;\n middlewareChain?: MiddlewareChain;\n // Set to true after shutdownAll() flushes \"finished\" state to disk.\n // Prevents SessionBridge STATUS_CHANGE listeners from overwriting the flushed state\n // with transient error status from in-flight prompts that fail after shutdown.\n private _shutdownComplete = false;\n\n /**\n * Inject the EventBus after construction. Deferred because EventBus is created\n * after SessionManager during bootstrap, so it cannot be passed to the constructor.\n */\n setEventBus(eventBus: EventBus): void {\n this.eventBus = eventBus;\n }\n\n constructor(store: SessionStore | null = null) {\n this.store = store;\n }\n\n /** Create a new session by spawning an agent and persisting the initial record. */\n async createSession(\n channelId: string,\n agentName: string,\n workingDirectory: string,\n agentManager: AgentManager,\n options?: { autoApprovedCommands?: string[] },\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 autoApprovedCommands: options?.autoApprovedCommands ?? [],\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 clientOverrides: {},\n platform: {},\n });\n }\n\n return session;\n }\n\n /** Look up a live session by its OpenACP session ID. */\n getSession(sessionId: string): Session | undefined {\n return this.sessions.get(sessionId);\n }\n\n /** Look up a live session by adapter channel and thread ID (checks per-adapter threadIds map first, then legacy fields). */\n getSessionByThread(channelId: string, threadId: string): Session | undefined {\n for (const session of this.sessions.values()) {\n // New: check per-adapter threadIds map\n const adapterThread = session.threadIds.get(channelId);\n if (adapterThread === threadId) return session;\n // Backward compat: check legacy channelId + threadId\n if (session.channelId === channelId && session.threadId === threadId) {\n return session;\n }\n }\n return undefined;\n }\n\n /** Look up a live session by the agent's internal session ID (assigned by the ACP subprocess). */\n getSessionByAgentSessionId(agentSessionId: string): Session | undefined {\n for (const session of this.sessions.values()) {\n if (session.agentSessionId === agentSessionId) {\n return session;\n }\n }\n return undefined;\n }\n\n /** Look up the persisted SessionRecord by the agent's internal session ID. */\n getRecordByAgentSessionId(\n agentSessionId: string,\n ): import(\"../types.js\").SessionRecord | undefined {\n return this.store?.findByAgentSessionId(agentSessionId);\n }\n\n /** Look up the persisted SessionRecord by channel and thread ID. */\n getRecordByThread(\n channelId: string,\n threadId: string,\n ): import(\"../types.js\").SessionRecord | undefined {\n return this.store?.findByPlatform(\n channelId,\n (p) => String(p.topicId) === threadId || p.threadId === threadId,\n );\n }\n\n /** Register a session that was created externally (e.g. restored from store on startup). */\n registerSession(session: Session): void {\n this.sessions.set(session.id, session);\n }\n\n /**\n * Merge a partial update into the stored SessionRecord. If no record exists yet and\n * the patch includes `sessionId`, it is treated as an initial save.\n * Pass `{ immediate: true }` to flush the store to disk synchronously.\n */\n async patchRecord(\n sessionId: string,\n patch: Partial<import(\"../types.js\").SessionRecord>,\n options?: { immediate?: boolean },\n ): Promise<void> {\n if (!this.store) return;\n // After shutdown, the store was already flushed with terminal state — ignore further patches\n // to prevent in-flight prompt failures from overwriting \"finished\" with \"error\" on disk.\n if (this._shutdownComplete) return;\n const record = this.store.get(sessionId);\n if (record) {\n await this.store.save({ ...record, ...patch });\n } else if (patch.sessionId) {\n // Initial save — treat patch as full record\n await this.store.save(patch as import(\"../types.js\").SessionRecord);\n }\n if (options?.immediate) {\n this.store.flush();\n }\n }\n\n /** Retrieve the persisted SessionRecord for a given session ID. Returns undefined if no store or record not found. */\n getSessionRecord(\n sessionId: string,\n ): import(\"../types.js\").SessionRecord | undefined {\n return this.store?.get(sessionId);\n }\n\n /** Cancel a session: abort in-flight prompt, transition to cancelled, destroy agent, and persist. */\n async cancelSession(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (session) {\n try {\n await session.abortPrompt();\n } catch {\n // Agent may already be dead — continue with cleanup\n }\n // Skip markCancelled for sessions already in a terminal state (e.g. finished)\n // to avoid invalid state machine transition errors.\n if (session.status !== \"finished\" && session.status !== \"cancelled\") {\n session.markCancelled();\n }\n await session.destroy();\n this.sessions.delete(sessionId);\n }\n if (this.store) {\n const record = this.store.get(sessionId);\n if (record && record.status !== \"cancelled\" && record.status !== \"finished\") {\n await this.store.save({ ...record, status: \"cancelled\" });\n }\n }\n // Hook: session:afterDestroy — read-only, fire-and-forget\n if (this.middlewareChain) {\n this.middlewareChain.execute(Hook.SESSION_AFTER_DESTROY, { sessionId }, async (p) => p).catch(() => {});\n }\n }\n\n /** List live (in-memory) sessions, optionally filtered by channel. Excludes assistant sessions. */\n listSessions(channelId?: string): Session[] {\n const all = Array.from(this.sessions.values()).filter(s => !s.isAssistant);\n if (channelId) return all.filter((s) => s.channelId === channelId);\n return all;\n }\n\n /**\n * List all sessions (live + stored) as SessionSummary. Live sessions take precedence\n * over stored records — their real-time state (queueDepth, promptRunning) is used.\n */\n listAllSessions(channelId?: string): SessionSummary[] {\n if (this.store) {\n let records = this.store.list().filter(r => !r.isAssistant);\n if (channelId) records = records.filter((r) => r.channelId === channelId);\n return records.map((record) => {\n const live = this.sessions.get(record.sessionId);\n if (live) {\n return {\n id: live.id,\n agent: live.agentName,\n status: live.status,\n name: live.name ?? null,\n workspace: live.workingDirectory,\n channelId: live.channelId,\n createdAt: live.createdAt.toISOString(),\n lastActiveAt: record.lastActiveAt ?? null,\n dangerousMode: live.clientOverrides.bypassPermissions ?? false,\n queueDepth: live.queueDepth,\n promptRunning: live.promptRunning,\n configOptions: live.configOptions?.length ? live.configOptions : undefined,\n capabilities: live.agentCapabilities ?? null,\n isLive: true,\n };\n }\n return {\n id: record.sessionId,\n agent: record.agentName,\n status: record.status,\n name: record.name ?? null,\n workspace: record.workingDir,\n channelId: record.channelId,\n createdAt: record.createdAt,\n lastActiveAt: record.lastActiveAt ?? null,\n dangerousMode: record.clientOverrides?.bypassPermissions ?? false,\n queueDepth: 0,\n promptRunning: false,\n configOptions: record.acpState?.configOptions,\n capabilities: record.acpState?.agentCapabilities ?? null,\n isLive: false,\n };\n });\n }\n\n // Fallback: no store — return live sessions only\n let live = Array.from(this.sessions.values()).filter(s => !s.isAssistant);\n if (channelId) live = live.filter((s) => s.channelId === channelId);\n return live.map((s) => ({\n id: s.id,\n agent: s.agentName,\n status: s.status,\n name: s.name ?? null,\n workspace: s.workingDirectory,\n channelId: s.channelId,\n createdAt: s.createdAt.toISOString(),\n lastActiveAt: null,\n dangerousMode: s.clientOverrides.bypassPermissions ?? false,\n queueDepth: s.queueDepth,\n promptRunning: s.promptRunning,\n configOptions: s.configOptions?.length ? s.configOptions : undefined,\n capabilities: s.agentCapabilities ?? null,\n isLive: true,\n }));\n }\n\n /** List all stored SessionRecords, optionally filtered by status. Excludes assistant sessions. */\n listRecords(filter?: {\n statuses?: string[];\n }): import(\"../types.js\").SessionRecord[] {\n if (!this.store) return [];\n let records = this.store.list().filter(r => !r.isAssistant);\n if (filter?.statuses?.length) {\n records = records.filter((r) => filter.statuses!.includes(r.status));\n }\n return records;\n }\n\n /** Remove a session's stored record and emit a SESSION_DELETED event. */\n async removeRecord(sessionId: string): Promise<void> {\n if (!this.store) return;\n await this.store.remove(sessionId);\n this.eventBus?.emit(BusEvent.SESSION_DELETED, { sessionId });\n }\n\n /**\n * Graceful shutdown: persist session state without killing agent subprocesses.\n * Agent processes will exit naturally when the parent process terminates.\n */\n async shutdownAll(): 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({\n ...record,\n status: \"finished\",\n acpState: session.toAcpStateSnapshot(),\n clientOverrides: session.clientOverrides,\n currentPromptCount: session.promptCount,\n agentSwitchHistory: session.agentSwitchHistory,\n });\n }\n }\n this.store.flush();\n }\n this._shutdownComplete = true;\n this.sessions.clear();\n }\n\n /**\n * Forcefully destroy all sessions (kill agent subprocesses).\n * Use only when sessions must be fully torn down (e.g. archive).\n * Unlike shutdownAll(), this does NOT snapshot live session state (acpState, etc.)\n * because destroyed sessions are terminal and will not be resumed.\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 this.store.flush();\n }\n const sessionIds = [...this.sessions.keys()];\n for (const session of this.sessions.values()) {\n await session.destroy();\n }\n this.sessions.clear();\n // Hook: session:afterDestroy — read-only, fire-and-forget\n if (this.middlewareChain) {\n for (const sessionId of sessionIds) {\n this.middlewareChain.execute(Hook.SESSION_AFTER_DESTROY, { sessionId }, async (p) => p).catch(() => {});\n }\n }\n }\n}\n","import type { Session } from \"./session.js\";\nimport type { IChannelAdapter } from \"../channel.js\";\nimport type { MessageTransformer } from \"../message-transformer.js\";\nimport type { NotificationManager } from \"../../plugins/notifications/notification.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport type { AgentEvent, PermissionRequest, SessionStatus } from \"../types.js\";\nimport type { EventBus } from \"../event-bus.js\";\nimport type { FileServiceInterface } from \"../plugin/types.js\";\nimport type { MiddlewareChain } from \"../plugin/middleware-chain.js\";\nimport type { DebugTracer } from \"../utils/debug-tracer.js\";\nimport { createChildLogger } from \"../utils/log.js\";\nimport { isPermissionBypass } from \"../utils/bypass-detection.js\";\n// micromatch is a CJS module — must use default import and destructure\nimport micromatch from \"micromatch\";\nconst { isMatch } = micromatch;\nimport { isSystemEvent, getEffectiveTarget, extractSender, type TurnContext, type TurnRouting } from \"./turn-context.js\";\nimport { Hook, BusEvent, SessionEv } from \"../events.js\";\n\nconst log = createChildLogger({ module: \"session-bridge\" });\n\n/** Services required by SessionBridge for message transformation, persistence, and middleware. */\nexport interface BridgeDeps {\n messageTransformer: MessageTransformer;\n notificationManager: NotificationManager;\n sessionManager: SessionManager;\n eventBus?: EventBus;\n fileService?: FileServiceInterface;\n middlewareChain?: MiddlewareChain;\n}\n\n/**\n * Connects a Session to a channel adapter, forwarding agent events to the adapter's\n * stream interface and wiring up permission handling, lifecycle persistence, and middleware.\n *\n * Each adapter attached to a session gets its own bridge. The bridge subscribes to\n * Session events (agent_event, permission_request, status_change, etc.) and translates\n * them into adapter-specific calls (sendMessage, sendPermissionRequest, renameSessionThread).\n *\n * Multi-adapter routing: when a TurnContext is active, turn events (text, tool_call, etc.)\n * are forwarded only to the adapter that originated the prompt. System events (commands_update,\n * session_end, etc.) are always broadcast to all bridges.\n */\nexport class SessionBridge {\n private connected = false;\n private cleanupFns: Array<() => void> = [];\n readonly adapterId: string;\n\n constructor(\n private session: Session,\n private adapter: IChannelAdapter,\n private deps: BridgeDeps,\n adapterId?: string,\n ) {\n this.adapterId = adapterId ?? adapter.name;\n }\n\n private get tracer(): DebugTracer | null {\n return this.session.agentInstance.debugTracer ?? null;\n }\n\n /** Register a listener and track it for cleanup */\n private listen(emitter: any, event: string, handler: (...args: any[]) => void): void {\n emitter.on(event, handler);\n this.cleanupFns.push(() => emitter.off(event, handler));\n }\n\n /** Send message to adapter, optionally running through message:outgoing middleware */\n private async sendMessage(sessionId: string, message: ReturnType<MessageTransformer[\"transform\"]>): Promise<void> {\n try {\n const mw = this.deps.middlewareChain;\n if (mw) {\n const result = await mw.execute(Hook.MESSAGE_OUTGOING, { sessionId, message }, async (m) => m);\n this.tracer?.log(\"core\", { step: \"middleware:outgoing\", sessionId, hook: \"message:outgoing\", blocked: !result });\n if (!result) return;\n this.tracer?.log(\"core\", { step: \"dispatch\", sessionId, message: result.message });\n this.adapter.sendMessage(sessionId, result.message).catch((err) => {\n log.error({ err, sessionId }, \"Failed to send message to adapter\");\n });\n } else {\n this.tracer?.log(\"core\", { step: \"dispatch\", sessionId, message });\n this.adapter.sendMessage(sessionId, message).catch((err) => {\n log.error({ err, sessionId }, \"Failed to send message to adapter\");\n });\n }\n } catch (err) {\n log.error({ err, sessionId }, \"Error in sendMessage middleware\");\n }\n }\n\n /**\n * Determine if this bridge should forward the given event based on turn routing.\n * System events are always forwarded; turn events are routed only to the target adapter.\n */\n shouldForward(event: AgentEvent): boolean {\n // System events → always forward to all bridges\n if (isSystemEvent(event)) return true;\n\n // No active turn context → forward (backward compat)\n const ctx = this.session.activeTurnContext;\n if (!ctx) return true;\n\n // Get effective target (null = silent, string = target adapterId)\n const target = getEffectiveTarget(ctx);\n\n // Silent turn → suppress all turn events\n if (target === null) return false;\n\n // Turn events → only forward to target adapter\n return this.adapterId === target;\n }\n\n /**\n * Subscribe to session events and start forwarding them to the adapter.\n *\n * Wires: agent events → adapter dispatch, permission UI, lifecycle persistence\n * (status changes, naming, prompt count), and EventBus notifications.\n * Also replays any commands or config options that arrived before the bridge connected.\n */\n connect(): void {\n if (this.connected) return;\n this.connected = true;\n\n // Wire session events to adapter (session → adapter dispatch)\n // The agent→session relay is owned by the Session itself (wireAgentRelay),\n // so session.on(SessionEv.AGENT_EVENT) fires for all sessions including headless ones.\n this.listen(this.session, SessionEv.AGENT_EVENT, (event: AgentEvent) => {\n if (this.shouldForward(event)) {\n this.dispatchAgentEvent(event);\n } else {\n // Event is not forwarded to this adapter's channel, but EventBus observers\n // (e.g. /events SSE stream) still need to see it for cross-adapter visibility.\n this.deps.eventBus?.emit(BusEvent.AGENT_EVENT, { sessionId: this.session.id, turnId: '', event });\n }\n });\n\n // Wire permissions\n // Only register the onPermissionRequest handler for the primary adapter (first bridge to connect).\n // Secondary bridges must not overwrite it — each bridge receives the permission_request session\n // event and sends UI to its own adapter via the listener below.\n if (!this.session.agentInstance.onPermissionRequest ||\n (this.session.agentInstance.onPermissionRequest as any).__bridgeId === undefined) {\n const handler = async (request: PermissionRequest) => {\n return this.resolvePermission(request);\n };\n (handler as any).__bridgeId = this.adapterId;\n this.session.agentInstance.onPermissionRequest = handler;\n }\n\n // Wire permission UI for secondary bridges — when the primary bridge emits\n // \"permission_request\" (after setPending), secondary bridges forward it to their adapter.\n // The primary bridge sends its UI directly in resolvePermission (awaited, preserving\n // ordering guarantees). Secondary bridges use this fire-and-forget listener.\n this.listen(this.session, SessionEv.PERMISSION_REQUEST, async (request: PermissionRequest) => {\n // Skip if this is the primary bridge — it handles UI directly in resolvePermission.\n const current = this.session.agentInstance.onPermissionRequest as any;\n if (current?.__bridgeId === this.adapterId) return;\n // Only send UI when the gate is pending (guard against informational-only emits\n // from auto-approve paths).\n if (!this.session.permissionGate.isPending) return;\n try {\n await this.adapter.sendPermissionRequest(this.session.id, request);\n } catch (err) {\n log.error({ err, sessionId: this.session.id, adapterId: this.adapterId }, \"Failed to send permission request to adapter\");\n }\n });\n\n // Wire lifecycle: persist status changes and auto-disconnect on terminal states\n this.listen(this.session, SessionEv.STATUS_CHANGE, (from: SessionStatus, to: SessionStatus) => {\n this.deps.sessionManager.patchRecord(this.session.id, {\n status: to,\n lastActiveAt: new Date().toISOString(),\n });\n if (!this.session.isAssistant) {\n this.deps.eventBus?.emit(BusEvent.SESSION_UPDATED, {\n sessionId: this.session.id,\n status: to,\n });\n }\n\n // Auto-disconnect on terminal states (finished only — cancelled sessions can resume)\n if (to === \"finished\") {\n // Disconnect on next tick so current event handlers can complete\n queueMicrotask(() => this.disconnect());\n }\n });\n\n // Wire lifecycle: persist and relay name changes to all adapters.\n this.listen(this.session, SessionEv.NAMED, async (name: string) => {\n await this.deps.sessionManager.patchRecord(this.session.id, { name });\n if (!this.session.isAssistant) {\n this.deps.eventBus?.emit(BusEvent.SESSION_UPDATED, {\n sessionId: this.session.id,\n name,\n });\n }\n await this.adapter.renameSessionThread(this.session.id, name);\n });\n\n // Wire lifecycle: persist prompt count after each prompt for resume decisions\n this.listen(this.session, SessionEv.PROMPT_COUNT_CHANGED, (count: number) => {\n this.deps.sessionManager.patchRecord(this.session.id, { currentPromptCount: count });\n });\n\n // Wire turn_started: emit message:processing on EventBus so SSE clients\n // (including other connected App windows) can show the streaming assistant stub.\n this.listen(this.session, SessionEv.TURN_STARTED, (ctx: TurnContext) => {\n this.deps.eventBus?.emit(BusEvent.MESSAGE_PROCESSING, {\n sessionId: this.session.id,\n turnId: ctx.turnId,\n sourceAdapterId: ctx.sourceAdapterId,\n userPrompt: ctx.userPrompt,\n finalPrompt: ctx.finalPrompt,\n attachments: ctx.attachments,\n sender: extractSender(ctx.meta),\n timestamp: new Date().toISOString(),\n });\n });\n\n // Wire prompt_queued → emit prompt:waiting on EventBus for adapters to show queue notifications.\n // This event fires synchronously from inside PromptQueue.enqueue() when an item is placed\n // behind a running prompt, so sourceAdapterId and queueDepth are accurate (no race condition).\n this.listen(this.session, SessionEv.PROMPT_QUEUED, (data: { turnId: string | undefined; position: number; routing: TurnRouting | undefined }) => {\n this.deps.eventBus?.emit(BusEvent.PROMPT_WAITING, {\n sessionId: this.session.id,\n turnId: data.turnId ?? '',\n sourceAdapterId: data.routing?.sourceAdapterId ?? this.session.channelId,\n queueDepth: data.position,\n });\n });\n\n // Replay any commands_update that arrived before the bridge connected\n if (this.session.latestCommands !== null) {\n this.session.emit(SessionEv.AGENT_EVENT, { type: \"commands_update\", commands: this.session.latestCommands });\n }\n\n // Replay configOptions so the adapter reflects the current agent's options\n if (this.session.configOptions.length > 0) {\n this.session.emit(SessionEv.AGENT_EVENT, { type: \"config_option_update\", options: this.session.configOptions });\n }\n }\n\n /** Unsubscribe all session event listeners and clean up adapter state. */\n disconnect(): void {\n if (!this.connected) return;\n this.connected = false;\n this.cleanupFns.forEach(fn => fn());\n this.cleanupFns = [];\n // Only clear onPermissionRequest if this bridge currently owns it.\n // This prevents a disconnecting secondary bridge from killing permission\n // handling for all surviving bridges.\n const current = this.session.agentInstance.onPermissionRequest as any;\n if (current?.__bridgeId === this.adapterId) {\n this.session.agentInstance.onPermissionRequest = async () => \"\";\n }\n // Clean up transformer caches for this session\n this.deps.messageTransformer.clearSessionCaches?.(this.session.id);\n }\n\n /** Dispatch an agent event through middleware and to the adapter */\n private async dispatchAgentEvent(event: AgentEvent): Promise<void> {\n this.tracer?.log(\"core\", { step: \"agent_event\", sessionId: this.session.id, event });\n const mw = this.deps.middlewareChain;\n if (mw) {\n try {\n const result = await mw.execute(Hook.AGENT_BEFORE_EVENT, { sessionId: this.session.id, event }, async (e) => e);\n this.tracer?.log(\"core\", { step: \"middleware:before\", sessionId: this.session.id, hook: \"agent:beforeEvent\", blocked: !result });\n if (!result) return; // blocked by middleware\n const transformedEvent = result.event;\n this.handleAgentEvent(transformedEvent);\n } catch {\n // Middleware error — proceed with original event\n try {\n this.handleAgentEvent(event);\n } catch (err) {\n log.error({ err, sessionId: this.session.id }, \"Error handling agent event (middleware fallback)\");\n }\n }\n } else {\n try {\n this.handleAgentEvent(event);\n } catch (err) {\n log.error({ err, sessionId: this.session.id }, \"Error handling agent event\");\n }\n }\n }\n\n private handleAgentEvent(event: AgentEvent): import('../types.js').OutgoingMessage | undefined {\n const session = this.session;\n const ctx = {\n get id() {\n return session.id;\n },\n get workingDirectory() {\n return session.workingDirectory;\n },\n };\n\n let outgoing: import('../types.js').OutgoingMessage | undefined;\n\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 outgoing = this.deps.messageTransformer.transform(event, ctx);\n this.tracer?.log(\"core\", { step: \"transform\", sessionId: this.session.id, input: event, output: outgoing });\n this.sendMessage(this.session.id, outgoing);\n break;\n\n case \"session_end\":\n this.session.finish(event.reason);\n this.adapter.cleanupSkillCommands?.(this.session.id);\n outgoing = this.deps.messageTransformer.transform(event);\n this.sendMessage(this.session.id, outgoing);\n this.deps.notificationManager.notify(this.session.channelId, {\n sessionId: this.session.id,\n sessionName: this.session.name,\n type: \"completed\",\n summary: `Session \"${this.session.name || this.session.id}\" completed\\n⏱ ${Math.round((Date.now() - this.session.createdAt.getTime()) / 60000)} min · 💬 ${this.session.promptCount} prompts`,\n });\n break;\n\n case \"error\":\n this.session.fail(event.message);\n this.adapter.cleanupSkillCommands?.(this.session.id);\n outgoing = this.deps.messageTransformer.transform(event);\n this.sendMessage(this.session.id, outgoing);\n this.deps.notificationManager.notify(this.session.channelId, {\n sessionId: this.session.id,\n sessionName: this.session.name,\n type: \"error\",\n summary: event.message,\n });\n break;\n\n case \"image_content\": {\n if (this.deps.fileService) {\n const fs = this.deps.fileService;\n const sid = this.session.id;\n const { data, mimeType } = event;\n const buffer = Buffer.from(data, \"base64\");\n const ext = fs.extensionFromMime(mimeType);\n fs.saveFile(sid, `agent-image${ext}`, buffer, mimeType)\n .then((att) => {\n this.sendMessage(sid, {\n type: \"attachment\",\n text: \"\",\n attachment: att,\n });\n })\n .catch((err) => log.error({ err }, \"Failed to save agent image\"));\n }\n break;\n }\n case \"audio_content\": {\n if (this.deps.fileService) {\n const fs = this.deps.fileService;\n const sid = this.session.id;\n const { data, mimeType } = event;\n const buffer = Buffer.from(data, \"base64\");\n const ext = fs.extensionFromMime(mimeType);\n fs.saveFile(sid, `agent-audio${ext}`, buffer, mimeType)\n .then((att) => {\n this.sendMessage(sid, {\n type: \"attachment\",\n text: \"\",\n attachment: att,\n });\n })\n .catch((err) => log.error({ err }, \"Failed to save agent audio\"));\n }\n break;\n }\n\n case \"commands_update\":\n log.debug({ commands: event.commands }, \"Commands available\");\n this.adapter.sendSkillCommands?.(this.session.id, event.commands);\n break;\n\n case \"system_message\":\n outgoing = this.deps.messageTransformer.transform(event);\n this.sendMessage(this.session.id, outgoing);\n break;\n\n case \"session_info_update\":\n if (event.title) {\n this.session.setName(event.title);\n outgoing = this.deps.messageTransformer.transform(event);\n this.sendMessage(this.session.id, outgoing);\n }\n // title-less updates (e.g. updatedAt-only) carry no user-visible content\n break;\n\n case \"config_option_update\":\n this.session.updateConfigOptions(event.options).then(() => {\n this.persistAcpState();\n }).catch(() => { /* middleware blocked or error — skip persist */ });\n outgoing = this.deps.messageTransformer.transform(event);\n this.sendMessage(this.session.id, outgoing);\n break;\n\n case \"user_message_chunk\":\n outgoing = this.deps.messageTransformer.transform(event);\n this.sendMessage(this.session.id, outgoing);\n break;\n\n case \"resource_content\":\n case \"resource_link\":\n outgoing = this.deps.messageTransformer.transform(event);\n this.sendMessage(this.session.id, outgoing);\n break;\n\n case \"tts_strip\":\n this.adapter.stripTTSBlock?.(this.session.id);\n break;\n }\n\n this.deps.eventBus?.emit(BusEvent.AGENT_EVENT, {\n sessionId: this.session.id,\n turnId: this.session.activeTurnContext?.turnId ?? '',\n event,\n });\n\n return outgoing;\n }\n\n /** Persist current ACP state (configOptions, agentCapabilities) to session store as cache */\n private persistAcpState(): void {\n this.deps.sessionManager.patchRecord(this.session.id, {\n acpState: this.session.toAcpStateSnapshot(),\n });\n }\n\n /** Resolve a permission request through the full pipeline: middleware -> auto-approve -> ask user */\n private async resolvePermission(request: PermissionRequest): Promise<string> {\n const startTime = Date.now();\n const mw = this.deps.middlewareChain;\n\n // Step 1: Middleware\n let permReq = request;\n if (mw) {\n const payload = { sessionId: this.session.id, request, autoResolve: undefined as string | undefined };\n const result = await mw.execute(Hook.PERMISSION_BEFORE_REQUEST, payload, async (r) => r);\n if (!result) return \"\"; // blocked by middleware\n permReq = result.request;\n // If middleware set autoResolve, skip UI and return directly\n if (result.autoResolve) {\n this.emitAfterResolve(mw, permReq.id, result.autoResolve, 'middleware', startTime);\n return result.autoResolve;\n }\n }\n\n this.deps.eventBus?.emit(BusEvent.PERMISSION_REQUEST, {\n sessionId: this.session.id,\n permission: permReq,\n });\n\n // Step 2: Auto-approve\n const autoDecision = this.checkAutoApprove(permReq);\n if (autoDecision) {\n // Emit informational event even on auto-approve (for SSE / monitoring consumers)\n this.session.emit(SessionEv.PERMISSION_REQUEST, permReq);\n this.emitAfterResolve(mw, permReq.id, autoDecision, 'system', startTime);\n return autoDecision;\n }\n\n // Step 3: Ask user\n // Set pending BEFORE emitting \"permission_request\" so that secondary bridge listeners\n // can guard on isPending. This also prevents a race where the user resolves before we\n // start waiting.\n const promise = this.session.permissionGate.setPending(permReq);\n\n // Emit the session event AFTER setPending — secondary bridges listen to this and forward\n // the permission UI to their own adapters (fire-and-forget).\n this.session.emit(SessionEv.PERMISSION_REQUEST, permReq);\n\n // Send permission UI to this bridge's own adapter (primary bridge path, awaited to\n // preserve the ordering guarantee: setPending → sendPermissionRequest).\n await this.adapter.sendPermissionRequest(this.session.id, permReq);\n\n // Wait for user response — adapter resolves this promise\n const optionId = await promise;\n\n // Broadcast permission:resolved so other adapters can dismiss their UI\n this.deps.eventBus?.emit(BusEvent.PERMISSION_RESOLVED, {\n sessionId: this.session.id,\n requestId: permReq.id,\n decision: optionId,\n optionId,\n resolvedBy: this.adapterId,\n });\n\n this.emitAfterResolve(mw, permReq.id, optionId, 'user', startTime);\n return optionId;\n }\n\n /** Check if a permission request should be auto-approved (bypass mode only) */\n private checkAutoApprove(request: PermissionRequest): string | null {\n // Bypass mode: auto-approve all permissions (agent-side or client-side)\n const modeOption = this.session.getConfigByCategory(\"mode\");\n const isAgentBypass = modeOption && isPermissionBypass(\n typeof modeOption.currentValue === \"string\" ? modeOption.currentValue : \"\"\n );\n const isClientBypass = this.session.clientOverrides.bypassPermissions;\n if (isAgentBypass || isClientBypass) {\n const allowOption = request.options.find((o) => o.isAllow);\n if (allowOption) {\n log.info(\n { sessionId: this.session.id, requestId: request.id, optionId: allowOption.id, agentBypass: !!isAgentBypass, clientBypass: !!isClientBypass },\n \"Bypass mode: auto-approving permission\",\n );\n return allowOption.id;\n }\n }\n\n // Plugin-declared auto-approved command patterns (micromatch glob matching).\n // Multi-line shell commands (heredoc bodies, `\\`-line continuations) contain\n // real newlines which micromatch globs do not cross — normalize runs of\n // whitespace to a single space so the whole command becomes a single line\n // for matching purposes.\n const patterns = this.session.autoApprovedCommands;\n if (patterns.length > 0 && request.description) {\n const normalized = request.description.replace(/\\s+/g, ' ').trim();\n if (isMatch(normalized, patterns, { dot: true })) {\n const allowOption = request.options.find((o) => o.isAllow);\n if (allowOption) {\n log.info(\n { sessionId: this.session.id, requestId: request.id, command: request.description },\n \"autoApprovedCommands: auto-approving matching command\",\n );\n return allowOption.id;\n }\n }\n }\n\n return null;\n }\n\n /** Emit permission:afterResolve middleware hook (fire-and-forget) */\n private emitAfterResolve(mw: MiddlewareChain | undefined, requestId: string, decision: string, userId: string, startTime: number): void {\n if (mw) {\n mw.execute(Hook.PERMISSION_AFTER_RESOLVE, {\n sessionId: this.session.id, requestId, decision, userId, durationMs: Date.now() - startTime,\n }, async (p) => p).catch(() => {});\n }\n }\n}\n","import type { AgentManager } from \"../agents/agent-manager.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport type { SpeechService } from \"../../plugins/speech/exports.js\";\nimport type { EventBus } from \"../event-bus.js\";\nimport type { NotificationManager } from \"../../plugins/notifications/notification.js\";\nimport type { TunnelService } from \"../../plugins/tunnel/tunnel-service.js\";\nimport type { AgentEvent, ConfigOption, SetConfigOptionValue } from \"../types.js\";\nimport type { MiddlewareChain } from \"../plugin/middleware-chain.js\";\nimport type { SessionStore } from \"./session-store.js\";\nimport type { IChannelAdapter } from \"../channel.js\";\nimport type { ConfigManager } from \"../config/config.js\";\nimport type { AgentCatalog } from \"../agents/agent-catalog.js\";\nimport type { ContextManager } from \"../../plugins/context/context-manager.js\";\nimport type { ContextQuery, ContextOptions, ContextResult } from \"../../plugins/context/context-provider.js\";\nimport { Session } from \"./session.js\";\nimport { createChildLogger } from \"../utils/log.js\";\nimport { Hook, BusEvent, SessionEv } from \"../events.js\";\n\nconst log = createChildLogger({ module: \"session-factory\" });\n\nfunction toSetConfigOptionValue(option: ConfigOption): SetConfigOptionValue {\n return option.type === \"boolean\"\n ? { type: \"boolean\", value: option.currentValue }\n : { type: \"select\", value: option.currentValue };\n}\n\n/** Parameters for creating a new session — used by SessionFactory.create() and Core.createFullSession(). */\nexport interface SessionCreateParams {\n channelId: string;\n agentName: string;\n workingDirectory: string;\n resumeAgentSessionId?: string;\n existingSessionId?: string;\n initialName?: string;\n isAssistant?: boolean;\n /** User ID from identity system — who is creating this session. */\n userId?: string;\n /** Glob patterns matched against bash command descriptions to auto-approve without user prompts. */\n autoApprovedCommands?: string[];\n}\n\nexport interface SideEffectDeps {\n eventBus: EventBus;\n notificationManager: NotificationManager;\n tunnelService?: TunnelService;\n}\n\n/**\n * Constructs new Sessions with the right agent, working directory, and initial state.\n *\n * Handles agent spawning (or resuming from a previous ACP session), middleware integration,\n * ACP state hydration, and side-effect wiring (usage tracking, tunnel cleanup).\n * Also provides lazy resume: when a message arrives for a stored (not live) session,\n * the factory transparently resumes it by re-spawning the agent with the stored session ID.\n */\nexport class SessionFactory {\n middlewareChain?: MiddlewareChain;\n private resumeLocks: Map<string, Promise<Session | null>> = new Map();\n\n /** Injected by Core after construction — needed for lazy resume error feedback */\n adapters?: Map<string, IChannelAdapter>;\n /** Injected by Core after construction — needed for lazy resume store lookup */\n sessionStore?: SessionStore | null;\n /** Injected by Core — creates full session with thread + bridge + persist */\n createFullSession?: (params: SessionCreateParams & { threadId?: string; createThread?: boolean }) => Promise<Session>;\n /** Injected by Core — needed for resolving default agent and workspace */\n configManager?: ConfigManager;\n /** Injected by Core — needed for resolving agent definitions */\n agentCatalog?: AgentCatalog;\n /** Injected by Core — needed for context-aware session creation */\n getContextManager?: () => ContextManager | undefined;\n /** Injected by Core — returns extra filesystem paths the agent is allowed to read.\n * Used to whitelist the file-service upload directory so agents can read attachments\n * saved outside the workspace (e.g. ~/.openacp/instances/main/files/). */\n getAgentAllowedPaths?: () => string[];\n\n constructor(\n private agentManager: AgentManager,\n private sessionManager: SessionManager,\n private speechServiceAccessor: SpeechService | (() => SpeechService),\n private eventBus: EventBus,\n private instanceRoot?: string,\n ) {}\n\n private get speechService(): SpeechService {\n return typeof this.speechServiceAccessor === 'function'\n ? this.speechServiceAccessor()\n : this.speechServiceAccessor;\n }\n\n /**\n * Create a new Session: spawn agent → create Session instance → hydrate ACP state → register.\n * Runs session:beforeCreate middleware (which can modify params or block creation).\n */\n async create(params: SessionCreateParams): Promise<Session> {\n // Hook: session:beforeCreate — modifiable, can block\n let createParams = params;\n if (this.middlewareChain) {\n const payload = {\n agentName: params.agentName,\n workingDir: params.workingDirectory,\n userId: params.userId ?? '',\n channelId: params.channelId,\n threadId: '', // threadId is assigned after session creation\n };\n const result = await this.middlewareChain.execute(Hook.SESSION_BEFORE_CREATE, payload, async (p) => p);\n if (!result) throw new Error(\"Session creation blocked by middleware\");\n // Apply any middleware modifications back to create params\n createParams = {\n ...params,\n agentName: result.agentName,\n workingDirectory: result.workingDir,\n channelId: result.channelId,\n };\n }\n\n // 1. Spawn or resume agent\n // Include config-level allowedPaths so agents can read whitelisted directories from startup\n const configAllowedPaths = this.configManager?.get().workspace?.security?.allowedPaths ?? [];\n\n let agentInstance;\n try {\n if (createParams.resumeAgentSessionId) {\n try {\n agentInstance = await this.agentManager.resume(\n createParams.agentName,\n createParams.workingDirectory,\n createParams.resumeAgentSessionId,\n configAllowedPaths,\n );\n } catch (resumeErr) {\n // Resume failed (session expired after restart) — fall back to fresh spawn\n log.warn(\n { agentName: createParams.agentName, resumeErr },\n \"Agent session resume failed, falling back to fresh spawn\",\n );\n agentInstance = await this.agentManager.spawn(\n createParams.agentName,\n createParams.workingDirectory,\n configAllowedPaths,\n );\n }\n } else {\n agentInstance = await this.agentManager.spawn(\n createParams.agentName,\n createParams.workingDirectory,\n configAllowedPaths,\n );\n }\n } catch (err) {\n const message =\n err instanceof Error ? err.message : String(err);\n\n // Emit a structured guidance error so adapters (Telegram, SSE UI) can show clear next steps.\n // We intentionally avoid leaking internal paths — instead, we point users at the openacp wrapper.\n const guidanceLines = [\n `❌ Failed to start agent \"${createParams.agentName}\": ${message}`,\n \"\",\n \"Run the agent CLI once in a terminal for this OpenACP instance to complete login or setup.\",\n ];\n if (this.instanceRoot) {\n guidanceLines.push(\n \"\",\n \"Copy and run this command in your terminal:\",\n ` cd \"${this.instanceRoot}\" && openacp agents run ${createParams.agentName}`,\n );\n } else {\n guidanceLines.push(\n \"\",\n \"Copy and run this command in your terminal (same project where you started OpenACP):\",\n ` openacp agents run ${createParams.agentName}`,\n );\n }\n guidanceLines.push(\n \"\",\n \"After setup completes, retry creating the session here.\",\n );\n\n const guidance: AgentEvent = {\n type: \"system_message\",\n message: guidanceLines.join(\"\\n\"),\n };\n\n // Emit the error event directly on the event bus so UIs (SSE, adapters) can\n // display it. We intentionally do NOT register a session — the dummy session\n // would never be cleaned up, leaking memory in SessionManager.\n const failedSessionId = createParams.existingSessionId ?? `failed-${Date.now()}`;\n this.eventBus.emit(BusEvent.AGENT_EVENT, {\n sessionId: failedSessionId,\n turnId: '',\n event: guidance,\n });\n\n // Re-throw so callers still see the failure\n throw err;\n }\n\n // Whitelist extra paths (e.g. file-service upload dir) so agents can read attachments\n // saved outside the workspace boundary without lifting the workspace guard entirely.\n const extraPaths = this.getAgentAllowedPaths?.() ?? [];\n for (const p of extraPaths) {\n agentInstance.addAllowedPath(p);\n }\n\n // Wire middleware chain to agent instance for FS/terminal hooks\n agentInstance.middlewareChain = this.middlewareChain;\n\n // 2. Create Session instance\n const session = new Session({\n id: createParams.existingSessionId,\n channelId: createParams.channelId,\n agentName: createParams.agentName,\n workingDirectory: createParams.workingDirectory,\n agentInstance,\n speechService: this.speechService,\n autoApprovedCommands: createParams.autoApprovedCommands,\n });\n session.agentSessionId = agentInstance.sessionId;\n session.middlewareChain = this.middlewareChain;\n if (createParams.initialName) {\n session.name = createParams.initialName;\n }\n\n // 3. Propagate ACP state from agent session response\n session.applySpawnResponse(agentInstance.initialSessionResponse, agentInstance.agentCapabilities);\n\n // 4. Register in SessionManager\n this.sessionManager.registerSession(session);\n if (!session.isAssistant) {\n this.eventBus.emit(BusEvent.SESSION_CREATED, {\n sessionId: session.id,\n name: session.name,\n agent: session.agentName,\n status: session.status,\n userId: createParams.userId,\n });\n }\n\n return session;\n }\n\n /**\n * Get active session by thread, or attempt lazy resume from store.\n * Used by adapter command handlers and handleMessage().\n */\n async getOrResume(channelId: string, threadId: string): Promise<Session | null> {\n const session = this.sessionManager.getSessionByThread(channelId, threadId);\n if (session) return session;\n return this.lazyResume(channelId, threadId);\n }\n\n async getOrResumeById(sessionId: string): Promise<Session | null> {\n const live = this.sessionManager.getSession(sessionId);\n if (live) return live;\n\n if (!this.sessionStore || !this.createFullSession) return null;\n const record = this.sessionStore.get(sessionId);\n if (!record) return null;\n if (record.isAssistant) return null;\n if (record.status === \"error\" || record.status === \"cancelled\") return null;\n\n // Deduplicate concurrent resumes for the same session\n const existing = this.resumeLocks.get(sessionId);\n if (existing) return existing;\n\n const resumePromise = (async (): Promise<Session | null> => {\n try {\n const p = record.platform as { topicId?: number; threadId?: string } | undefined;\n const existingThreadId = p?.topicId ? String(p.topicId) : p?.threadId;\n const session = await this.createFullSession!({\n channelId: record.channelId,\n agentName: record.agentName,\n workingDirectory: record.workingDir,\n resumeAgentSessionId: record.agentSessionId,\n existingSessionId: record.sessionId,\n initialName: record.name,\n threadId: existingThreadId,\n });\n session.activate();\n if (record.clientOverrides) {\n session.clientOverrides = record.clientOverrides;\n } else if (record.dangerousMode) {\n session.clientOverrides = { bypassPermissions: true };\n }\n if (record.firstAgent) session.firstAgent = record.firstAgent;\n if (record.agentSwitchHistory) session.agentSwitchHistory = record.agentSwitchHistory;\n if (record.currentPromptCount != null) session.promptCount = record.currentPromptCount;\n if (record.attachedAdapters) session.attachedAdapters = record.attachedAdapters;\n if (record.platforms) {\n for (const [adapterId, platformData] of Object.entries(record.platforms)) {\n const data = platformData as Record<string, unknown>;\n const tid = adapterId === \"telegram\"\n ? String(data.topicId ?? \"\")\n : String(data.threadId ?? \"\");\n if (tid) session.threadIds.set(adapterId, tid);\n }\n }\n if (record.acpState) {\n if (record.acpState.configOptions && session.configOptions.length === 0) {\n session.setInitialConfigOptions(record.acpState.configOptions);\n }\n if (record.acpState.agentCapabilities && !session.agentCapabilities) {\n session.setAgentCapabilities(record.acpState.agentCapabilities);\n }\n }\n\n // Re-apply persisted configOptions that differ from what the live agent reports.\n // Agents spawn with default values; without this step the persisted user preference\n // (e.g. mode=bypassPermissions) silently diverges from the agent's actual state.\n if (record.acpState?.configOptions) {\n for (const persisted of record.acpState.configOptions) {\n const live = session.getConfigOption(persisted.id);\n if (live && live.currentValue !== persisted.currentValue) {\n try {\n await session.setConfigOption(persisted.id, toSetConfigOptionValue(persisted));\n } catch { /* best-effort — don't fail resume if one option sync fails */ }\n }\n }\n }\n\n log.info({ sessionId }, \"Lazy resume by ID successful\");\n return session;\n } catch (err) {\n log.error({ err, sessionId }, \"Lazy resume by ID failed\");\n return null;\n } finally {\n this.resumeLocks.delete(sessionId);\n }\n })();\n\n this.resumeLocks.set(sessionId, resumePromise);\n return resumePromise;\n }\n\n /**\n * Attempt to resume a session from disk when a message arrives on a thread with\n * no live session. Deduplicates concurrent resume attempts for the same thread\n * via resumeLocks to avoid spawning multiple agents.\n */\n private async lazyResume(channelId: string, threadId: string): Promise<Session | null> {\n const store = this.sessionStore;\n if (!store || !this.createFullSession) return null;\n\n const lockKey = `${channelId}:${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 channelId,\n (p) => String(p.topicId) === threadId || String(p.threadId ?? \"\") === threadId,\n );\n if (!record) {\n log.debug({ threadId, channelId }, \"No session record found for thread\");\n return null;\n }\n if (record.isAssistant) return null;\n\n // Don't resume errored or cancelled sessions\n if (record.status === \"error\" || record.status === \"cancelled\") {\n log.warn(\n { threadId, sessionId: record.sessionId, status: record.status },\n \"Session record found but skipped (status: %s) — use /new to start a fresh session\",\n record.status,\n );\n return null;\n }\n\n log.info({ threadId, sessionId: record.sessionId, status: record.status }, \"Lazy resume: found record, attempting resume\");\n\n const resumePromise = (async (): Promise<Session | null> => {\n try {\n const session = await this.createFullSession!({\n channelId: record.channelId,\n agentName: record.agentName,\n workingDirectory: record.workingDir,\n resumeAgentSessionId: record.agentSessionId,\n existingSessionId: record.sessionId,\n initialName: record.name,\n threadId,\n });\n session.activate();\n // MIGRATION: old records with dangerousMode but no clientOverrides\n if (record.clientOverrides) {\n session.clientOverrides = record.clientOverrides;\n } else if (record.dangerousMode) {\n session.clientOverrides = { bypassPermissions: true };\n }\n if (record.firstAgent) session.firstAgent = record.firstAgent;\n if (record.agentSwitchHistory) session.agentSwitchHistory = record.agentSwitchHistory;\n if (record.currentPromptCount != null) session.promptCount = record.currentPromptCount;\n\n // Restore multi-adapter state\n if (record.attachedAdapters) {\n session.attachedAdapters = record.attachedAdapters;\n }\n if (record.platforms) {\n for (const [adapterId, platformData] of Object.entries(record.platforms)) {\n const data = platformData as Record<string, unknown>;\n const tid = adapterId === \"telegram\"\n ? String(data.topicId ?? \"\")\n : String(data.threadId ?? \"\");\n if (tid) session.threadIds.set(adapterId, tid);\n }\n }\n\n // Hydrate cached ACP state only as fallback — fresh agent data takes precedence\n if (record.acpState) {\n if (record.acpState.configOptions && session.configOptions.length === 0) {\n session.setInitialConfigOptions(record.acpState.configOptions);\n }\n if (record.acpState.agentCapabilities && !session.agentCapabilities) {\n session.setAgentCapabilities(record.acpState.agentCapabilities);\n }\n }\n\n // Re-apply persisted configOptions that differ from what the live agent reports.\n // Agents spawn with default values; without this step the persisted user preference\n // (e.g. mode=bypassPermissions) silently diverges from the agent's actual state.\n if (record.acpState?.configOptions) {\n for (const persisted of record.acpState.configOptions) {\n const live = session.getConfigOption(persisted.id);\n if (live && live.currentValue !== persisted.currentValue) {\n try {\n await session.setConfigOption(persisted.id, toSetConfigOptionValue(persisted));\n } catch { /* best-effort — don't fail resume if one option sync fails */ }\n }\n }\n }\n\n // If resume fell back to a fresh spawn (session.agentSessionId differs from record),\n // inject conversation history so the new agent has context from previous sessions.\n const resumeFalledBack = record.agentSessionId && session.agentSessionId !== record.agentSessionId;\n if (resumeFalledBack) {\n log.info({ sessionId: session.id }, \"Resume fell back to fresh spawn — injecting conversation history\");\n const contextManager = this.getContextManager?.();\n if (contextManager) {\n try {\n const config = this.configManager?.get();\n const labelAgent = config?.agentSwitch?.labelHistory ?? true;\n const contextResult = await contextManager.buildContext(\n { type: 'session', value: record.sessionId, repoPath: record.workingDir },\n { labelAgent, noCache: true },\n );\n if (contextResult?.markdown) {\n session.setContext(contextResult.markdown);\n }\n } catch { /* context injection is best-effort */ }\n }\n }\n\n log.info({ sessionId: session.id, threadId }, \"Lazy resume successful\");\n return session;\n } catch (err) {\n log.error({ err, record }, \"Lazy resume failed\");\n // Send error feedback to user\n const adapter = this.adapters?.get(channelId);\n if (adapter) {\n try {\n await adapter.sendMessage(threadId, {\n type: \"error\",\n text: `⚠️ Failed to resume session: ${err instanceof Error ? err.message : String(err)}`,\n });\n } catch { /* best effort */ }\n }\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 /** Create a brand-new session, resolving agent name and workspace from config if not provided. */\n async handleNewSession(\n channelId: string,\n agentName?: string,\n workspacePath?: string,\n options?: { createThread?: boolean; threadId?: string },\n ): Promise<Session> {\n if (!this.configManager || !this.agentCatalog || !this.createFullSession) {\n throw new Error(\"SessionFactory not fully initialized\");\n }\n const config = this.configManager.get();\n const resolvedAgent = agentName || config.defaultAgent;\n log.info({ channelId, agentName: resolvedAgent }, \"New session request\");\n const agentDef = this.agentCatalog.resolve(resolvedAgent);\n const resolvedWorkspace = this.configManager.resolveWorkspace(\n workspacePath || agentDef?.workingDirectory,\n );\n\n return this.createFullSession({\n channelId,\n agentName: resolvedAgent,\n workingDirectory: resolvedWorkspace,\n ...options,\n });\n }\n\n /** NOTE: handleNewChat is currently dead code — never called outside core.ts itself.\n * Moving it anyway for completeness; can be removed in a future cleanup. */\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\n if (currentSession) {\n return this.handleNewSession(\n channelId,\n currentSession.agentName,\n currentSession.workingDirectory,\n );\n }\n\n // Fallback: look up from store\n const record = this.sessionManager.getRecordByThread(\n channelId,\n currentThreadId,\n );\n if (!record || record.status === \"cancelled\" || record.status === \"error\")\n return null;\n\n return this.handleNewSession(\n channelId,\n record.agentName,\n record.workingDir,\n );\n }\n\n /** Create a session and inject conversation context from a ContextProvider (e.g., history from a previous session). */\n async createSessionWithContext(params: {\n channelId: string;\n agentName: string;\n workingDirectory: string;\n contextQuery: ContextQuery;\n contextOptions?: ContextOptions;\n createThread?: boolean;\n threadId?: string;\n }): Promise<{ session: Session; contextResult: ContextResult | null }> {\n if (!this.createFullSession) throw new Error(\"SessionFactory not fully initialized\");\n\n let contextResult: ContextResult | null = null;\n const contextManager = this.getContextManager?.();\n if (contextManager) {\n try {\n contextResult = await contextManager.buildContext(\n params.contextQuery,\n params.contextOptions,\n );\n } catch (err) {\n log.warn({ err }, \"Context building failed, proceeding without context\");\n }\n }\n\n const session = await this.createFullSession({\n channelId: params.channelId,\n agentName: params.agentName,\n workingDirectory: params.workingDirectory,\n createThread: params.createThread,\n threadId: params.threadId,\n });\n\n if (contextResult) {\n session.setContext(contextResult.markdown);\n }\n\n return { session, contextResult };\n }\n\n /** Wire session-level side effects: usage tracking (via EventBus) and tunnel cleanup on session end. */\n wireSideEffects(session: Session, deps: SideEffectDeps): void {\n // Wire usage tracking via event bus (consumed by usage plugin)\n session.on(SessionEv.AGENT_EVENT, (event: AgentEvent) => {\n if (event.type !== \"usage\") return;\n deps.eventBus.emit(BusEvent.USAGE_RECORDED, {\n sessionId: session.id,\n agentName: session.agentName,\n timestamp: new Date().toISOString(),\n tokensUsed: event.tokensUsed ?? 0,\n contextSize: event.contextSize ?? 0,\n cost: event.cost,\n });\n });\n\n // Clean up user tunnels when session ends\n session.on(SessionEv.STATUS_CHANGE, (_from, to) => {\n if ((to === \"finished\" || to === \"cancelled\") && deps.tunnelService) {\n deps.tunnelService\n .stopBySession(session.id)\n .then((stopped) => {\n for (const entry of stopped) {\n deps.notificationManager\n .notifyAll({\n sessionId: session.id,\n sessionName: session.name,\n type: \"completed\",\n summary: `Tunnel stopped: port ${entry.port}${entry.label ? ` (${entry.label})` : \"\"} — session ended`,\n })\n .catch(() => {});\n }\n })\n .catch(() => {});\n }\n });\n }\n}\n","import path from \"node:path\";\nimport { nanoid } from \"nanoid\";\nimport type { SettingsManager } from \"./plugin/settings-manager.js\";\nimport { ConfigManager } from \"./config/config.js\";\nimport { AgentManager } from \"./agents/agent-manager.js\";\nimport { SessionManager } from \"./sessions/session-manager.js\";\nimport { SessionBridge } from \"./sessions/session-bridge.js\";\nimport type { NotificationManager } from \"../plugins/notifications/notification.js\";\nimport type { IChannelAdapter } from \"./channel.js\";\nimport { Session } from \"./sessions/session.js\";\nimport { MessageTransformer } from \"./message-transformer.js\";\nimport type { FileServiceInterface } from \"./plugin/types.js\";\nimport { JsonFileSessionStore, type SessionStore } from \"./sessions/session-store.js\";\nimport type { SecurityGuard } from \"../plugins/security/security-guard.js\";\nimport { SessionFactory } from \"./sessions/session-factory.js\";\nimport type { IncomingMessage, AgentEvent, SessionStatus, TurnMeta, Attachment } from \"./types.js\";\nimport type { TunnelService } from \"../plugins/tunnel/tunnel-service.js\";\nimport { getAgentCapabilities } from \"./agents/agent-registry.js\";\nimport { AgentSwitchHandler } from \"./agent-switch-handler.js\";\nimport { AgentCatalog } from \"./agents/agent-catalog.js\";\nimport { AgentStore } from \"./agents/agent-store.js\";\nimport { EventBus } from \"./event-bus.js\";\nimport { LifecycleManager } from \"./plugin/lifecycle-manager.js\";\nimport { MenuRegistry } from './menu-registry.js';\nimport { AssistantRegistry, AssistantManager } from './assistant/index.js';\nimport { registerCoreMenuItems } from './menu/core-items.js';\nimport { createSessionsSection, createAgentsSection, createConfigSection, createSystemSection, createRemoteSection } from './assistant/index.js';\nimport { ServiceRegistry } from \"./plugin/service-registry.js\";\nimport { MiddlewareChain } from \"./plugin/middleware-chain.js\";\nimport { ErrorTracker } from \"./plugin/error-tracker.js\";\nimport { createChildLogger } from \"./utils/log.js\";\nimport type { SpeechService } from \"../plugins/speech/exports.js\";\nimport type { ContextManager } from \"../plugins/context/context-manager.js\";\nimport type { InstanceContext } from \"./instance/instance-context.js\";\nimport { Hook, BusEvent, SessionEv } from \"./events.js\";\nimport { extractSender, type TurnContext, type TurnRouting } from \"./sessions/turn-context.js\";\nconst log = createChildLogger({ module: \"core\" });\n\n/**\n * Top-level orchestrator that wires all OpenACP modules together.\n *\n * Responsibilities:\n * - Registers messaging adapters (Telegram, Slack, SSE, etc.)\n * - Routes incoming messages to the correct Session (by channel + thread)\n * - Manages the full session lifecycle: creation, agent switch, archive, shutdown\n * - Connects agent events to adapter callbacks via SessionBridge\n * - Accesses plugin-provided services (security, notifications, speech, etc.)\n * through ServiceRegistry rather than direct references, since plugins\n * register their services asynchronously during boot\n */\nexport class OpenACPCore {\n configManager: ConfigManager;\n agentCatalog: AgentCatalog;\n agentManager: AgentManager;\n sessionManager: SessionManager;\n messageTransformer: MessageTransformer;\n adapters: Map<string, IChannelAdapter> = new Map();\n /** \"adapterId:sessionId\" → SessionBridge — tracks active bridges for disconnect/reconnect */\n private bridges: Map<string, SessionBridge> = new Map();\n /** Set by main.ts — triggers graceful shutdown with restart exit code */\n requestRestart: (() => Promise<void>) | null = null;\n private _tunnelService?: TunnelService;\n sessionStore: SessionStore | null = null;\n eventBus: EventBus;\n sessionFactory: SessionFactory;\n readonly lifecycleManager: LifecycleManager;\n private agentSwitchHandler: AgentSwitchHandler;\n public readonly instanceContext: InstanceContext;\n readonly menuRegistry = new MenuRegistry();\n readonly assistantRegistry = new AssistantRegistry();\n assistantManager!: AssistantManager;\n\n // Services (security, notifications, speech, etc.) are provided by plugins that\n // register during boot. Core accesses them lazily via ServiceRegistry so it doesn't\n // need compile-time dependencies on plugin implementations.\n\n /** @throws if the service hasn't been registered by its plugin yet */\n private getService<T>(name: string): T {\n const svc = this.lifecycleManager.serviceRegistry.get<T>(name);\n if (!svc) throw new Error(`Service '${name}' not registered — is the ${name} plugin loaded?`);\n return svc;\n }\n\n /** Access control and rate-limiting guard (provided by security plugin). */\n get securityGuard(): SecurityGuard {\n return this.getService<SecurityGuard>('security');\n }\n\n /** Cross-session notification delivery (provided by notifications plugin). */\n get notificationManager(): NotificationManager {\n return this.getService<NotificationManager>('notifications');\n }\n\n /** File I/O service for agent attachment storage (provided by file-service plugin). */\n get fileService(): FileServiceInterface {\n return this.getService<FileServiceInterface>('file-service');\n }\n\n /** Text-to-speech / speech-to-text engine (provided by speech plugin). */\n get speechService(): SpeechService {\n return this.getService<SpeechService>('speech');\n }\n\n /** Conversation history builder for context injection (provided by context plugin). */\n get contextManager(): ContextManager {\n return this.getService<ContextManager>('context');\n }\n\n /** Per-plugin persistent settings (e.g. API keys). */\n get settingsManager(): SettingsManager | undefined {\n return this.lifecycleManager.settingsManager;\n }\n\n /**\n * Bootstrap all core subsystems. The boot order matters:\n * 1. AgentCatalog + AgentManager (agent definitions)\n * 2. SessionStore + SessionManager (session persistence and lookup)\n * 3. EventBus (inter-module communication)\n * 4. SessionFactory (session creation pipeline)\n * 5. LifecycleManager (plugin infrastructure)\n * 6. Wire middleware chain into factory + manager\n * 7. AgentSwitchHandler, config listeners, menu/assistant registries\n */\n constructor(configManager: ConfigManager, ctx: InstanceContext) {\n this.configManager = configManager;\n this.instanceContext = ctx;\n const config = configManager.get();\n this.agentCatalog = new AgentCatalog(\n new AgentStore(ctx.paths.agents),\n ctx.paths.registryCache,\n ctx.paths.agentsDir,\n );\n this.agentCatalog.load();\n\n this.agentManager = new AgentManager(this.agentCatalog);\n const storePath = ctx.paths.sessions;\n this.sessionStore = new JsonFileSessionStore(\n storePath,\n config.sessionStore.ttlDays,\n );\n this.sessionManager = new SessionManager(this.sessionStore);\n\n this.messageTransformer = new MessageTransformer();\n this.eventBus = new EventBus();\n this.sessionManager.setEventBus(this.eventBus);\n\n // SessionFactory uses a lazy accessor for speechService (resolved from ServiceRegistry after plugin boot)\n this.sessionFactory = new SessionFactory(\n this.agentManager,\n this.sessionManager,\n () => this.speechService,\n this.eventBus,\n ctx.root,\n );\n\n // Initialize plugin lifecycle manager (before setting middlewareChain on factory)\n this.lifecycleManager = new LifecycleManager({\n serviceRegistry: new ServiceRegistry(),\n middlewareChain: new MiddlewareChain(),\n errorTracker: new ErrorTracker(),\n eventBus: this.eventBus as any,\n sessions: this.sessionManager,\n config: this.configManager,\n core: this,\n storagePath: ctx.paths.pluginsData,\n instanceRoot: ctx.root,\n log: createChildLogger({ module: \"plugin\" }),\n });\n\n // Wire middleware chain to session factory and session manager\n this.sessionFactory.middlewareChain = this.lifecycleManager.middlewareChain;\n this.sessionManager.middlewareChain = this.lifecycleManager.middlewareChain;\n\n // Wire lazy resume dependencies\n this.sessionFactory.sessionStore = this.sessionStore;\n this.sessionFactory.adapters = this.adapters;\n this.sessionFactory.createFullSession = (params) => this.createSession(params);\n this.sessionFactory.configManager = this.configManager;\n this.sessionFactory.agentCatalog = this.agentCatalog;\n this.sessionFactory.getContextManager = () => this.lifecycleManager.serviceRegistry.get<ContextManager>('context');\n // Whitelist the file-service upload directory so agents can read attachments\n // (images, voice notes) that Telegram/Slack save outside the workspace in ~/.openacp/.\n this.sessionFactory.getAgentAllowedPaths = () => {\n const fileService = this.lifecycleManager.serviceRegistry.get<import('../plugins/file-service/file-service.js').FileService>('file-service');\n return fileService ? [fileService.baseDir] : [];\n };\n\n this.agentSwitchHandler = new AgentSwitchHandler({\n sessionManager: this.sessionManager,\n agentManager: this.agentManager,\n configManager: this.configManager,\n eventBus: this.eventBus,\n adapters: this.adapters,\n bridges: this.bridges,\n createBridge: (session, adapter, adapterId) => this.createBridge(session, adapter, adapterId),\n getSessionBridgeKeys: (sessionId: string) => this.getSessionBridgeKeys(sessionId),\n getMiddlewareChain: () => this.lifecycleManager?.middlewareChain,\n getService: <T>(name: string) => this.lifecycleManager.serviceRegistry.get<T>(name),\n });\n\n // Hot-reload: handle config changes that need side effects\n this.configManager.on(\n \"config:changed\",\n async ({ path: configPath, value }: { path: string; value: unknown }) => {\n if (configPath === \"logging.level\" && typeof value === \"string\") {\n const { setLogLevel } = await import(\"./utils/log.js\");\n setLogLevel(value);\n log.info({ level: value }, \"Log level changed at runtime\");\n }\n if (configPath.startsWith(\"speech.\")) {\n const speechSvc = this.lifecycleManager.serviceRegistry.get<SpeechService>('speech');\n if (speechSvc) {\n const settingsMgr = this.settingsManager;\n if (settingsMgr) {\n const pluginCfg = await settingsMgr.loadSettings('@openacp/speech');\n const groqApiKey = pluginCfg.groqApiKey as string | undefined;\n const sttProviders: Record<string, { apiKey: string }> = {};\n if (groqApiKey) {\n sttProviders.groq = { apiKey: groqApiKey };\n }\n const newSpeechConfig = {\n stt: {\n provider: groqApiKey ? 'groq' : null,\n providers: sttProviders,\n },\n tts: {\n provider: (pluginCfg.ttsProvider as string) ?? null,\n providers: {} as Record<string, never>,\n },\n };\n speechSvc.refreshProviders(newSpeechConfig);\n log.info(\"Speech service config updated at runtime (from plugin settings)\");\n }\n }\n }\n },\n );\n\n // Register core menu items\n registerCoreMenuItems(this.menuRegistry);\n\n // Set instance root for assistant CLI guidelines\n this.assistantRegistry.setInstanceRoot(path.dirname(ctx.root));\n\n // Register core assistant sections\n this.assistantRegistry.register(createSessionsSection(this));\n this.assistantRegistry.register(createAgentsSection(this as any));\n this.assistantRegistry.register(createConfigSection(this as any));\n this.assistantRegistry.register(createSystemSection())\n this.assistantRegistry.register(createRemoteSection());\n\n // Create assistant manager\n this.assistantManager = new AssistantManager(this as any, this.assistantRegistry);\n\n // Register registries as services for plugin access\n this.lifecycleManager.serviceRegistry.register('menu-registry', this.menuRegistry, 'core');\n this.lifecycleManager.serviceRegistry.register('assistant-registry', this.assistantRegistry, 'core');\n }\n\n /** Optional tunnel for generating public URLs (code viewer links, etc.). */\n get tunnelService(): TunnelService | undefined {\n return this._tunnelService;\n }\n\n /** Propagate tunnel service to MessageTransformer so it can generate viewer links. */\n set tunnelService(service: TunnelService | undefined) {\n this._tunnelService = service;\n this.messageTransformer.tunnelService = service;\n }\n\n /**\n * Register a messaging adapter (e.g. Telegram, Slack, SSE).\n *\n * Adapters must be registered before `start()`. The adapter name serves as its\n * channel ID throughout the system — used in session records, bridge keys, and routing.\n */\n registerAdapter(name: string, adapter: IChannelAdapter): void {\n this.adapters.set(name, adapter);\n }\n\n /**\n * Start all registered adapters. Adapters that fail are logged but do not\n * prevent others from starting. Throws only if ALL adapters fail.\n */\n async start(): Promise<void> {\n this.agentCatalog.refreshRegistryIfStale().catch((err) => {\n log.warn({ err }, \"Background registry refresh failed\");\n });\n const failures: Array<{ name: string; error: unknown }> = [];\n for (const [name, adapter] of this.adapters.entries()) {\n try {\n await adapter.start();\n } catch (err) {\n log.error({ err, adapter: name }, `Adapter \"${name}\" failed to start`);\n failures.push({ name, error: err });\n }\n }\n if (failures.length > 0 && failures.length === this.adapters.size) {\n throw new Error(\n `All adapters failed to start: ${failures.map((f) => f.name).join(\", \")}`,\n );\n }\n }\n\n /**\n * Graceful shutdown: notify users, persist session state, stop adapters.\n * Agent subprocesses are not explicitly killed — they exit with the parent process.\n */\n async stop(): Promise<void> {\n // 1. Notify users (best effort — service may not be available)\n try {\n const nm = this.lifecycleManager.serviceRegistry.get<NotificationManager>('notifications');\n if (nm) {\n await nm.notifyAll({\n sessionId: \"system\",\n type: \"error\",\n summary: \"OpenACP is shutting down\",\n });\n }\n } catch {\n /* best effort */\n }\n\n // 2. Persist session state (don't kill agents — they exit with parent)\n await this.sessionManager.shutdownAll();\n\n // 3. Stop adapters\n for (const adapter of this.adapters.values()) {\n await adapter.stop();\n }\n }\n\n // --- Archive ---\n\n /**\n * Archive a session: delete its adapter topic/thread and cancel the session.\n *\n * Sessions in any state except 'initializing' can be archived.\n * Initializing sessions are excluded because the agent subprocess may not be fully started yet.\n * The adapter handles platform-side cleanup (e.g. deleting a Telegram topic).\n */\n async archiveSession(sessionId: string): Promise<{ ok: true } | { ok: false; error: string }> {\n const session = this.sessionManager.getSession(sessionId);\n\n // Determine channelId and status — prefer live session, fall back to stored record\n let channelId: string;\n let status: string;\n if (session) {\n channelId = session.channelId;\n status = session.status;\n } else {\n const record = this.sessionManager.getSessionRecord(sessionId);\n if (!record) return { ok: false, error: \"Session not found\" };\n channelId = record.channelId;\n status = record.status;\n }\n\n // Only archivable from non-initializing states — initializing sessions are excluded\n // because the agent subprocess may not be fully started yet.\n if (status === \"initializing\") {\n return { ok: false, error: `Cannot archive session in '${status}' state` };\n }\n\n const adapter = this.adapters.get(channelId);\n if (!adapter) return { ok: false, error: \"Adapter not found for session\" };\n if (!adapter.archiveSessionTopic) return { ok: false, error: \"Adapter does not support topic archiving\" };\n\n try {\n // archiveSessionTopic handles: cleanup trackers → delete topic\n await adapter.archiveSessionTopic(sessionId);\n\n // Cancel session — stops agent, removes from active sessions, marks record as cancelled\n await this.sessionManager.cancelSession(sessionId);\n\n return { ok: true };\n } catch (err) {\n // Clear archiving flag on error (only relevant for live sessions)\n if (session) session.archiving = false;\n return { ok: false, error: (err as Error).message };\n }\n }\n\n // --- Message Routing ---\n\n /**\n * Route an incoming platform message to the appropriate session.\n *\n * Flow:\n * 1. Run `message:incoming` middleware (plugins can modify or block)\n * 2. SecurityGuard checks user access and per-user session limits\n * 3. Find session by channel+thread (in-memory lookup, then lazy resume from disk)\n * 4. For assistant sessions, prepend any deferred system prompt\n * 5. Emit `message:queued` for SSE clients, then enqueue the prompt on the session\n *\n * If no session is found, the user is told to start one with /new.\n */\n async handleMessage(message: IncomingMessage, initialMeta?: Record<string, unknown>): Promise<void> {\n log.debug(\n {\n channelId: message.channelId,\n threadId: message.threadId,\n userId: message.userId,\n },\n \"Incoming message\",\n );\n\n // Generate turnId + meta early so message:incoming middleware can read/enrich them.\n // Adapters may inject channel-specific context (e.g. display name, username) via initialMeta\n // so plugins can read it without needing adapter-specific fields on IncomingMessage.\n const turnId = nanoid(8);\n const meta: TurnMeta = { turnId, ...initialMeta };\n\n // Hook: message:incoming — modifiable, can block\n if (this.lifecycleManager?.middlewareChain) {\n const result = await this.lifecycleManager.middlewareChain.execute(\n Hook.MESSAGE_INCOMING,\n { ...message, meta },\n async (msg) => msg,\n );\n if (!result) return; // blocked by middleware\n message = result;\n }\n\n // Security: check user access and session limits\n const access = await this.securityGuard.checkAccess(message);\n if (!access.allowed) {\n log.warn({ userId: message.userId, reason: access.reason }, \"Access denied\");\n if (access.reason.includes(\"Session limit\")) {\n const adapter = this.adapters.get(message.channelId);\n if (adapter) {\n await adapter.sendMessage(message.threadId, {\n type: \"error\",\n text: `⚠️ ${access.reason}. Please cancel existing sessions with /cancel before starting new ones.`,\n });\n }\n }\n return;\n }\n\n // Find session by thread or lazy resume\n let session = await this.sessionFactory.getOrResume(message.channelId, message.threadId);\n\n if (!session) {\n log.warn(\n { channelId: message.channelId, threadId: message.threadId },\n \"No session found for thread (in-memory miss + lazy resume returned null)\",\n );\n const adapter = this.adapters.get(message.channelId);\n if (adapter) {\n await adapter.sendMessage(message.threadId, {\n type: \"error\",\n text: \"⚠️ No active session in this topic. Use /new to start one.\",\n });\n }\n return;\n }\n\n // For assistant sessions, prepend deferred system prompt on first real message\n let text = message.text;\n if (this.assistantManager?.isAssistant(session.id)) {\n const pending = this.assistantManager.consumePendingSystemPrompt(message.channelId);\n if (pending) {\n text = `${pending}\\n\\n---\\n\\nUser message:\\n${text}`;\n }\n }\n\n // Merge sourceAdapterId into routing so middleware hooks (e.g. history recorder)\n // can identify the originating adapter even when the message is processed by a\n // different adapter (e.g. an API-sourced message routed through a Telegram session).\n // Carry enriched meta from middleware result (plugins may have attached sender info etc.)\n const enrichedMeta = (message as any).meta as TurnMeta | undefined ?? meta;\n\n await this._dispatchToSession(session, text, message.attachments, {\n sourceAdapterId: message.routing?.sourceAdapterId ?? message.channelId,\n responseAdapterId: message.routing?.responseAdapterId,\n }, turnId, enrichedMeta);\n }\n\n /**\n * Shared dispatch path for sending a prompt to a session.\n * Called by both handleMessage (Telegram) and handleMessageInSession (SSE/API)\n * after their respective middleware/enrichment steps.\n */\n private async _dispatchToSession(\n session: Session,\n text: string,\n attachments: Attachment[] | undefined,\n routing: TurnRouting,\n turnId: string,\n meta: TurnMeta,\n ): Promise<void> {\n // Update activity timestamp for all sources\n this.sessionManager.patchRecord(session.id, {\n lastActiveAt: new Date().toISOString(),\n });\n\n // Emit MESSAGE_QUEUED — always, for all sources, no adapter-specific conditions\n this.eventBus.emit(BusEvent.MESSAGE_QUEUED, {\n sessionId: session.id,\n turnId,\n text,\n sourceAdapterId: routing.sourceAdapterId,\n attachments,\n timestamp: new Date().toISOString(),\n queueDepth: session.queueDepth + 1,\n sender: extractSender(meta),\n });\n\n // Fire-and-forget: return immediately after enqueueing so callers (API route, adapters)\n // get a fast response without waiting for the agent to finish processing.\n // Errors (e.g. blocked by middleware) surface via MESSAGE_FAILED on the event bus.\n session.enqueuePrompt(text, attachments, routing, turnId, meta).catch(err => {\n const reason = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionId: session.id, turnId, reason }, 'enqueuePrompt failed — emitting message:failed');\n this.eventBus.emit(BusEvent.MESSAGE_FAILED, {\n sessionId: session.id,\n turnId,\n reason,\n });\n });\n }\n\n /**\n * Send a message to a known session, running the full message:incoming → agent:beforePrompt\n * middleware chain (same as handleMessage) but without the threadId-based session lookup.\n *\n * Used by channels that already hold a direct session reference (e.g. SSE adapter, api-server),\n * where looking up by channelId+threadId is unreliable (API sessions may have no threadId).\n *\n * @param session The target session — caller is responsible for validating its status.\n * @param message Sender context and message content.\n * @param initialMeta Optional adapter-specific context to seed the TurnMeta bag\n * (e.g. channelUser with display name/username).\n * @param options Optional turnId override and response routing.\n */\n async handleMessageInSession(\n session: Session,\n message: { channelId: string; userId: string; text: string; attachments?: Attachment[] },\n initialMeta?: Record<string, unknown>,\n options?: { externalTurnId?: string; responseAdapterId?: string | null },\n ): Promise<{ turnId: string; queueDepth: number }> {\n const turnId = options?.externalTurnId ?? nanoid(8);\n const meta: TurnMeta = { turnId, ...initialMeta };\n\n // Run message:incoming middleware so plugins can enrich meta (sender identity, @mentions, etc.)\n let text = message.text;\n let { attachments } = message;\n let enrichedMeta: TurnMeta = meta;\n if (this.lifecycleManager?.middlewareChain) {\n const payload = {\n channelId: message.channelId,\n threadId: session.id,\n userId: message.userId,\n text,\n attachments,\n meta,\n };\n const result = await this.lifecycleManager.middlewareChain.execute(\n Hook.MESSAGE_INCOMING,\n payload,\n async (p) => p,\n );\n if (!result) return { turnId, queueDepth: session.queueDepth };\n text = result.text;\n attachments = result.attachments;\n enrichedMeta = (result as any).meta as TurnMeta ?? meta;\n }\n\n const routing: TurnRouting = {\n sourceAdapterId: message.channelId,\n responseAdapterId: options?.responseAdapterId,\n };\n await this._dispatchToSession(session, text, attachments, routing, turnId, enrichedMeta);\n\n return { turnId, queueDepth: session.queueDepth };\n }\n\n // --- Unified Session Creation Pipeline ---\n\n /**\n * Create (or resume) a session with full wiring: agent, adapter thread, bridge, persistence.\n *\n * This is the single entry point for session creation. The pipeline:\n * 1. SessionFactory spawns/resumes the agent process\n * 2. Adapter creates a thread/topic if requested\n * 3. Initial session record is persisted (so lazy resume can find it by threadId)\n * 4. SessionBridge connects agent events to the adapter\n * 5. For headless sessions (no adapter), fallback event handlers are wired inline\n * 6. Side effects (usage tracking, tunnel cleanup) are attached\n */\n async createSession(params: {\n channelId: string;\n agentName: string;\n workingDirectory: string;\n resumeAgentSessionId?: string;\n existingSessionId?: string;\n createThread?: boolean;\n initialName?: string;\n // threadTitle sets the adapter thread display name without populating session.name.\n // Use this when you want a placeholder thread title (e.g. \"Adopted session\") but\n // still need auto-naming to run after the first prompt.\n threadTitle?: string;\n threadId?: string;\n isAssistant?: boolean;\n /** Glob patterns matched against bash command descriptions to auto-approve without user prompts. */\n autoApprovedCommands?: string[];\n }): Promise<Session> {\n const adapter = this.adapters.get(params.channelId);\n\n // Create thread FIRST (before spawning the agent) so the user sees the topic\n // appear immediately. Session ID is not yet known at this point — the adapter\n // logs it as empty and skips tracing; this is updated in the persisted record below.\n let preCreatedThreadId: string | undefined;\n if (params.createThread && adapter) {\n const name = params.threadTitle ?? params.initialName ?? `🔄 ${params.agentName} — New Session`;\n preCreatedThreadId = await adapter.createSessionThread(\"\", name);\n }\n\n // 1-3. Spawn/resume agent, create Session, register in SessionManager\n const session = await this.sessionFactory.create(params);\n\n // Apply thread ID: pre-created thread takes priority over params.threadId\n const resolvedThreadId = preCreatedThreadId ?? params.threadId;\n if (resolvedThreadId) {\n session.threadId = resolvedThreadId;\n }\n\n // 5. Persist initial record BEFORE bridge.connect() so that:\n // - Lazy resume can find the record by threadId\n // - sendSkillCommands/renameSessionThread have threadId available\n const existingRecord = this.sessionStore?.get(session.id);\n const platform: Record<string, unknown> = {\n ...(existingRecord?.platform ?? {}),\n };\n if (session.threadId) {\n if (params.channelId === \"telegram\") {\n platform.topicId = Number(session.threadId);\n } else {\n platform.threadId = session.threadId;\n }\n }\n\n // Also persist the multi-adapter platforms map so lazy resume can find sessions\n // by threadId for non-Telegram adapters (which use threadId instead of topicId).\n const platforms: Record<string, Record<string, unknown>> = {\n ...(existingRecord?.platforms ?? {}),\n };\n if (session.threadId) {\n platforms[params.channelId] = params.channelId === \"telegram\"\n ? { topicId: Number(session.threadId) || session.threadId }\n : { threadId: session.threadId };\n }\n\n await this.sessionManager.patchRecord(session.id, {\n sessionId: session.id,\n agentSessionId: session.agentSessionId,\n agentName: params.agentName,\n workingDir: params.workingDirectory,\n channelId: params.channelId,\n status: session.status,\n createdAt: session.createdAt.toISOString(),\n lastActiveAt: new Date().toISOString(),\n name: session.name,\n isAssistant: params.isAssistant,\n platform,\n platforms,\n firstAgent: session.firstAgent,\n currentPromptCount: session.promptCount,\n agentSwitchHistory: session.agentSwitchHistory,\n // Cache ACP state for display before agent reconnects on lazy resume\n acpState: session.toAcpStateSnapshot(),\n }, { immediate: true });\n\n // 6. Connect SessionBridge — agent events can now fire with threadId available\n if (adapter) {\n const bridge = this.createBridge(session, adapter, session.channelId);\n bridge.connect();\n // Flush any skill commands that arrived before threadId was set (safety net)\n adapter.flushPendingSkillCommands?.(session.id).catch((err) => {\n log.warn({ err, sessionId: session.id }, \"Failed to flush pending skill commands\");\n });\n // Signal that thread is ready — all listeners (adapters, plugins, etc.) can react\n if (params.createThread && session.threadId) {\n this.eventBus.emit(BusEvent.SESSION_THREAD_READY, {\n sessionId: session.id,\n channelId: params.channelId,\n threadId: session.threadId,\n });\n }\n }\n\n // 6b. Headless sessions (no adapter): wire fallbacks normally handled by SessionBridge.\n if (!adapter) {\n // Auto-approve safe permissions so agents don't hang.\n // Permissions without an explicit allow option are NOT auto-approved — they will time out.\n session.agentInstance.onPermissionRequest = async (permRequest) => {\n const allowOption = permRequest.options.find((o) => o.isAllow);\n if (!allowOption) {\n log.warn(\n { sessionId: session.id, permissionId: permRequest.id, description: permRequest.description },\n \"Headless session has no allow option for permission request — skipping auto-approve, will time out\",\n );\n return new Promise<string>(() => {}); // never resolves; gate will time out\n }\n log.warn(\n { sessionId: session.id, permissionId: permRequest.id, option: allowOption.id },\n `Auto-approving permission \"${permRequest.description}\" for headless session — no adapter connected`,\n );\n return allowOption.id;\n };\n\n // Persist session name and notify SSE clients when autoName fires.\n // For bridged sessions this is handled by SessionBridge's \"named\" listener;\n // headless sessions have no bridge so we wire it here instead.\n session.on(SessionEv.NAMED, async (name: string) => {\n await this.sessionManager.patchRecord(session.id, { name });\n this.eventBus.emit(BusEvent.SESSION_UPDATED, { sessionId: session.id, name });\n });\n\n // Forward agent events to EventBus so SSE clients can observe the session.\n // Also handles session lifecycle transitions (session_end → finish, error → fail)\n // and fires agent:beforeEvent middleware — all normally handled by SessionBridge.\n const mw = () => this.lifecycleManager?.middlewareChain;\n session.on(SessionEv.AGENT_EVENT, async (event: AgentEvent) => {\n let processedEvent = event;\n const chain = mw();\n if (chain) {\n const result = await chain.execute(Hook.AGENT_BEFORE_EVENT, { sessionId: session.id, event }, async (e) => e);\n if (!result) return; // blocked by middleware\n processedEvent = result.event;\n }\n if (processedEvent.type === \"session_end\") {\n session.finish((processedEvent as { reason?: string }).reason);\n } else if (processedEvent.type === \"error\") {\n session.fail((processedEvent as { message: string }).message);\n }\n this.eventBus.emit(BusEvent.AGENT_EVENT, { sessionId: session.id, turnId: session.activeTurnContext?.turnId ?? '', event: processedEvent });\n });\n\n // Persist status changes and notify SSE clients — normally wired by SessionBridge.\n session.on(SessionEv.STATUS_CHANGE, (_from: SessionStatus, to: SessionStatus) => {\n this.sessionManager.patchRecord(session.id, {\n status: to,\n lastActiveAt: new Date().toISOString(),\n });\n this.eventBus.emit(BusEvent.SESSION_UPDATED, { sessionId: session.id, status: to });\n });\n\n // Persist prompt count after each prompt — normally wired by SessionBridge.\n session.on(SessionEv.PROMPT_COUNT_CHANGED, (count: number) => {\n this.sessionManager.patchRecord(session.id, { currentPromptCount: count });\n });\n\n // Emit message:processing so SSE clients (App) can transition pending → conversation.\n // Normally handled by SessionBridge; headless sessions have no bridge.\n session.on(SessionEv.TURN_STARTED, (ctx: TurnContext) => {\n this.eventBus.emit(BusEvent.MESSAGE_PROCESSING, {\n sessionId: session.id,\n turnId: ctx.turnId,\n sourceAdapterId: ctx.sourceAdapterId,\n userPrompt: ctx.userPrompt,\n finalPrompt: ctx.finalPrompt,\n attachments: ctx.attachments,\n sender: extractSender(ctx.meta),\n timestamp: new Date().toISOString(),\n });\n });\n }\n\n // 6c. Wire usage tracking and tunnel cleanup\n this.sessionFactory.wireSideEffects(session, {\n eventBus: this.eventBus,\n notificationManager: this.notificationManager,\n tunnelService: this._tunnelService,\n });\n\n log.info(\n { sessionId: session.id, agentName: params.agentName },\n \"Session created via pipeline\",\n );\n return session;\n }\n\n /** Convenience wrapper: create a new session with default agent/workspace resolution. */\n async handleNewSession(\n channelId: string,\n agentName?: string,\n workspacePath?: string,\n options?: { createThread?: boolean; threadId?: string },\n ): Promise<Session> {\n return this.sessionFactory.handleNewSession(channelId, agentName, workspacePath, options);\n }\n\n /**\n * Adopt an externally-started agent session (e.g. from a CLI `openacp adopt` command).\n *\n * Validates that the agent supports resume, checks session limits, avoids duplicates,\n * then creates a full session via the unified pipeline with resume semantics.\n */\n async adoptSession(\n agentName: string,\n agentSessionId: string,\n cwd: string,\n channelId?: string,\n ): Promise<\n | {\n ok: true;\n sessionId: string;\n threadId: string;\n status: \"adopted\" | \"existing\";\n }\n | { ok: false; error: string; message: string }\n > {\n // 1. Validate agent supports resume\n const caps = getAgentCapabilities(agentName);\n if (!caps.supportsResume) {\n return {\n ok: false,\n error: \"agent_not_supported\",\n message: `Agent '${agentName}' does not support session resume`,\n };\n }\n\n const agentDef = this.agentManager.getAgent(agentName);\n if (!agentDef) {\n return {\n ok: false,\n error: \"agent_not_supported\",\n message: `Agent '${agentName}' not found`,\n };\n }\n\n // 2. Validate cwd\n const { existsSync } = await import(\"node:fs\");\n if (!existsSync(cwd)) {\n return {\n ok: false,\n error: \"invalid_cwd\",\n message: `Directory does not exist: ${cwd}`,\n };\n }\n\n // 3. Hard cap for the adoptSession path (CLI-initiated session adoption).\n // This is separate from the per-user limits enforced by securityGuard.checkAccess\n // during handleMessage. The security plugin cannot override this value.\n const maxSessions = 20;\n if (this.sessionManager.listSessions().length >= maxSessions) {\n return {\n ok: false,\n error: \"session_limit\",\n message: \"Maximum concurrent sessions reached\",\n };\n }\n\n // 4. Check if session already exists on the same channel\n const existingRecord =\n this.sessionManager.getRecordByAgentSessionId(agentSessionId);\n if (existingRecord) {\n const sameChannel = !channelId || existingRecord.channelId === channelId;\n const platform = existingRecord.platform as { topicId?: number; threadId?: string } | undefined;\n const existingThreadId = platform?.topicId ? String(platform.topicId) : platform?.threadId;\n if (existingThreadId && sameChannel) {\n const adapter = this.adapters.get(existingRecord.channelId) ?? this.adapters.values().next().value;\n if (adapter) {\n try {\n await adapter.sendMessage(existingRecord.sessionId, {\n type: \"text\",\n text: \"Session resumed from CLI.\",\n });\n } catch { /* Topic/thread may be deleted */ }\n }\n return {\n ok: true,\n sessionId: existingRecord.sessionId,\n threadId: existingThreadId,\n status: \"existing\",\n };\n }\n }\n\n // 5. Find adapter (explicit channel or default first)\n let adapterChannelId: string;\n if (channelId) {\n if (!this.adapters.has(channelId)) {\n const available = Array.from(this.adapters.keys()).join(\", \") || \"none\";\n return { ok: false, error: \"adapter_not_found\", message: `Adapter '${channelId}' is not connected. Available: ${available}` };\n }\n adapterChannelId = channelId;\n } else {\n if (this.adapters.size === 0) {\n return { ok: false, error: \"no_adapter\", message: \"No channel adapter registered\" };\n }\n // If multiple adapters are registered, require explicit --channel so the caller\n // intentionally picks the target platform rather than silently landing in the wrong one.\n if (this.adapters.size > 1) {\n const available = Array.from(this.adapters.keys());\n // Map internal adapter names to human-readable display names for the prompt\n const displayName: Record<string, string> = { sse: \"Desktop App\" };\n return {\n ok: false,\n error: \"channel_required\",\n message: `Multiple channels available. Specify one with --channel:\\n${available.map(c => ` - ${c} (${displayName[c] ?? c})`).join(\"\\n\")}`,\n };\n }\n adapterChannelId = this.adapters.keys().next().value!;\n }\n\n // 6. Create session via unified pipeline\n let session: Session;\n try {\n session = await this.createSession({\n channelId: adapterChannelId,\n agentName,\n workingDirectory: cwd,\n resumeAgentSessionId: agentSessionId,\n createThread: true,\n // initialName shows a placeholder in the app immediately after adopt.\n // auto-naming checks for this placeholder value and overwrites it on first\n // user message, so the name is still updated automatically.\n initialName: \"Adopted session\",\n threadTitle: \"Adopted session\",\n });\n } catch (err) {\n return {\n ok: false,\n error: \"resume_failed\",\n message: `Failed to resume session: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n // 7. Update store with adopt-specific fields\n const adoptPlatform: Record<string, unknown> = {};\n if (adapterChannelId === 'telegram') {\n adoptPlatform.topicId = Number(session.threadId);\n } else {\n adoptPlatform.threadId = session.threadId;\n }\n const adoptPlatforms: Record<string, Record<string, unknown>> = {};\n if (session.threadId) {\n adoptPlatforms[adapterChannelId] = adapterChannelId === 'telegram'\n ? { topicId: Number(session.threadId) || session.threadId }\n : { threadId: session.threadId };\n }\n await this.sessionManager.patchRecord(session.id, {\n originalAgentSessionId: agentSessionId,\n platform: adoptPlatform,\n platforms: adoptPlatforms,\n });\n\n return {\n ok: true,\n sessionId: session.id,\n threadId: session.threadId,\n status: \"adopted\",\n };\n }\n\n /** Start a new chat within the same agent and workspace as the current session's thread. */\n async handleNewChat(channelId: string, currentThreadId: string): Promise<Session | null> {\n return this.sessionFactory.handleNewChat(channelId, currentThreadId);\n }\n\n /** Create a session and inject conversation context from a prior session or repo. */\n async createSessionWithContext(params: {\n channelId: string;\n agentName: string;\n workingDirectory: string;\n contextQuery: import(\"../plugins/context/context-provider.js\").ContextQuery;\n contextOptions?: import(\"../plugins/context/context-provider.js\").ContextOptions;\n createThread?: boolean;\n threadId?: string;\n }): Promise<{ session: Session; contextResult: import(\"../plugins/context/context-provider.js\").ContextResult | null }> {\n return this.sessionFactory.createSessionWithContext(params);\n }\n\n // --- Agent Switch ---\n\n /** Switch a session's active agent. Delegates to AgentSwitchHandler for state coordination. */\n async switchSessionAgent(sessionId: string, toAgent: string): Promise<{ resumed: boolean }> {\n return this.agentSwitchHandler.switch(sessionId, toAgent);\n }\n\n /** Find a session by channel+thread, resuming from disk if not in memory. */\n async getOrResumeSession(channelId: string, threadId: string): Promise<Session | null> {\n return this.sessionFactory.getOrResume(channelId, threadId);\n }\n\n /** Find a session by ID, resuming from disk if not in memory. */\n async getOrResumeSessionById(sessionId: string): Promise<Session | null> {\n return this.sessionFactory.getOrResumeById(sessionId);\n }\n\n /**\n * Attach an additional adapter to an existing session (multi-adapter support).\n *\n * Creates a thread on the target adapter and connects a SessionBridge so the\n * session's agent events are forwarded to both the primary and attached adapters.\n */\n async attachAdapter(sessionId: string, adapterId: string): Promise<{ threadId: string }> {\n const session = this.sessionManager.getSession(sessionId);\n if (!session) throw new Error(`Session ${sessionId} not found`);\n\n const adapter = this.adapters.get(adapterId);\n if (!adapter) throw new Error(`Adapter \"${adapterId}\" not found or not running`);\n\n // Already attached — return existing threadId\n if (session.attachedAdapters.includes(adapterId)) {\n const existingThread = session.threadIds.get(adapterId) ?? session.id;\n return { threadId: existingThread };\n }\n\n // Create thread on target adapter\n const threadId = await adapter.createSessionThread(\n session.id,\n session.name ?? `Session ${session.id.slice(0, 6)}`,\n );\n session.threadIds.set(adapterId, threadId);\n session.attachedAdapters.push(adapterId);\n\n // Create and connect bridge\n const bridge = this.createBridge(session, adapter, adapterId);\n bridge.connect();\n\n // Persist\n await this.sessionManager.patchRecord(session.id, {\n attachedAdapters: session.attachedAdapters,\n platforms: this.buildPlatformsFromSession(session),\n });\n\n return { threadId };\n }\n\n /**\n * Detach a secondary adapter from a session. The primary adapter (channelId) cannot\n * be detached. Disconnects the bridge and cleans up thread mappings.\n */\n async detachAdapter(sessionId: string, adapterId: string): Promise<void> {\n const session = this.sessionManager.getSession(sessionId);\n if (!session) throw new Error(`Session ${sessionId} not found`);\n\n if (adapterId === session.channelId) {\n throw new Error(\"Cannot detach primary adapter (channelId)\");\n }\n\n if (!session.attachedAdapters.includes(adapterId)) {\n return; // Already detached, idempotent\n }\n\n // Send detach notice before disconnecting\n const adapter = this.adapters.get(adapterId);\n if (adapter) {\n try {\n await adapter.sendMessage(session.id, {\n type: \"system_message\",\n text: \"Session detached from this adapter.\",\n });\n } catch { /* best effort */ }\n }\n\n // Disconnect bridge\n const key = this.bridgeKey(adapterId, session.id);\n const bridge = this.bridges.get(key);\n if (bridge) {\n bridge.disconnect();\n this.bridges.delete(key);\n }\n\n // Update session state\n session.attachedAdapters = session.attachedAdapters.filter(a => a !== adapterId);\n session.threadIds.delete(adapterId);\n\n // Persist\n await this.sessionManager.patchRecord(session.id, {\n attachedAdapters: session.attachedAdapters,\n platforms: this.buildPlatformsFromSession(session),\n });\n }\n\n /** Build the platforms map (adapter → thread/topic IDs) for persistence. */\n private buildPlatformsFromSession(session: Session): Record<string, Record<string, unknown>> {\n const platforms: Record<string, Record<string, unknown>> = {};\n for (const [adapterId, threadId] of session.threadIds) {\n if (adapterId === \"telegram\") {\n platforms.telegram = { topicId: Number(threadId) || threadId };\n } else {\n platforms[adapterId] = { threadId };\n }\n }\n return platforms;\n }\n\n // --- Event Wiring ---\n\n /** Composite bridge key: \"adapterId:sessionId\" */\n private bridgeKey(adapterId: string, sessionId: string): string {\n return `${adapterId}:${sessionId}`;\n }\n\n /** Get all bridge keys for a session (regardless of adapter) */\n private getSessionBridgeKeys(sessionId: string): string[] {\n const keys: string[] = [];\n for (const key of this.bridges.keys()) {\n if (key.endsWith(`:${sessionId}`)) keys.push(key);\n }\n return keys;\n }\n\n /** Connect a session bridge for the given session (used by AssistantManager) */\n connectSessionBridge(session: Session): void {\n const adapter = this.adapters.get(session.channelId);\n if (!adapter) return;\n const bridge = this.createBridge(session, adapter, session.channelId);\n bridge.connect();\n }\n\n /**\n * Create a SessionBridge for the given session and adapter.\n *\n * The bridge subscribes to Session events (agent output, status changes, naming)\n * and forwards them to the adapter for platform delivery. Disconnects any existing\n * bridge for the same adapter+session first to avoid duplicate event handlers.\n */\n createBridge(session: Session, adapter: IChannelAdapter, adapterId?: string): SessionBridge {\n const id = adapterId ?? adapter.name;\n const key = this.bridgeKey(id, session.id);\n const existing = this.bridges.get(key);\n if (existing) {\n existing.disconnect();\n }\n const bridge = new SessionBridge(session, adapter, {\n messageTransformer: this.messageTransformer,\n notificationManager: this.notificationManager,\n sessionManager: this.sessionManager,\n eventBus: this.eventBus,\n fileService: this.fileService,\n middlewareChain: this.lifecycleManager?.middlewareChain,\n }, id);\n this.bridges.set(key, bridge);\n return bridge;\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { SessionRecord } from \"../types.js\";\nimport { createChildLogger } from \"../utils/log.js\";\n\nconst log = createChildLogger({ module: \"session-store\" });\n\n/** Persistence interface for session records. Implementations handle serialization format and storage. */\nexport interface SessionStore {\n save(record: SessionRecord): Promise<void>;\n /** Immediately flush pending writes to disk (no debounce). */\n flush(): void;\n get(sessionId: string): SessionRecord | undefined;\n findByPlatform(\n channelId: string,\n predicate: (platform: Record<string, unknown>) => boolean,\n ): SessionRecord | undefined;\n findByAgentSessionId(agentSessionId: string): SessionRecord | undefined;\n findAssistant(channelId: string): SessionRecord | undefined;\n list(channelId?: string): SessionRecord[];\n remove(sessionId: string): Promise<void>;\n}\n\n/** On-disk JSON format: versioned envelope wrapping a session ID → record map. */\ninterface StoreFile {\n version: number;\n sessions: Record<string, SessionRecord>;\n}\n\n/** Writes are debounced to avoid excessive disk I/O during rapid state changes. */\nconst DEBOUNCE_MS = 2000;\n\n/**\n * JSON file-backed session store.\n *\n * Reads the entire store into memory on startup, applies all mutations in-memory,\n * and debounces writes to disk. Expired records (past ttlDays) are cleaned up\n * periodically. On shutdown, pending writes are flushed synchronously.\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 // Check new platforms format first\n if (record.platforms?.[channelId]) {\n if (predicate(record.platforms[channelId])) return record;\n }\n // Fallback to legacy platform field\n if (record.channelId === channelId && predicate(record.platform as Record<string, unknown>)) {\n return record;\n }\n }\n return undefined;\n }\n\n /**\n * Find a session by its ACP agent session ID.\n * Checks current, original, and historical agent session IDs (from agent switches)\n * since the agent session ID changes on each switch.\n */\n findByAgentSessionId(agentSessionId: string): SessionRecord | undefined {\n for (const record of this.records.values()) {\n if (\n record.agentSessionId === agentSessionId ||\n record.originalAgentSessionId === agentSessionId\n ) {\n return record;\n }\n if (record.agentSwitchHistory?.some((e) => e.agentSessionId === agentSessionId)) {\n return record;\n }\n }\n return undefined;\n }\n\n findAssistant(channelId: string): SessionRecord | undefined {\n for (const record of this.records.values()) {\n if (record.isAssistant === true && record.channelId === channelId) {\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 flush(): void {\n this.flushSync();\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 // Write to a temp file first, then atomically rename into place.\n // This prevents sessions.json from being left in a corrupt state if the\n // process is killed (SIGKILL, OOM) while the write is in progress.\n const tmpPath = `${this.filePath}.tmp`;\n fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2));\n fs.renameSync(tmpPath, this.filePath);\n }\n\n /** Clean up timers and process listeners. Call on shutdown to prevent leaks. */\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 // Remove any orphaned temp file left by a previous interrupted write.\n // If the crash happened before the rename, sessions.json is still the last\n // good state and the .tmp file is stale — safe to delete.\n try { fs.unlinkSync(`${this.filePath}.tmp`); } catch { /* not present */ }\n\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, this.migrateRecord(record));\n }\n log.debug({ count: this.records.size }, \"Loaded session records\");\n } catch (err) {\n log.error({ err }, \"Failed to load session store, backing up corrupt file\");\n try {\n fs.renameSync(this.filePath, `${this.filePath}.bak`);\n } catch { /* best effort */ }\n }\n }\n\n /**\n * Migrate old SessionRecord format to new multi-adapter format.\n * Converts single-adapter `platform` field to per-adapter `platforms` map,\n * and initializes `attachedAdapters` for records created before multi-adapter support.\n */\n private migrateRecord(record: SessionRecord): SessionRecord {\n // Migrate platform → platforms\n if (!record.platforms && record.platform && typeof record.platform === \"object\") {\n const platformData = record.platform as Record<string, unknown>;\n if (Object.keys(platformData).length > 0) {\n record.platforms = { [record.channelId]: platformData };\n }\n }\n // Default attachedAdapters\n if (!record.attachedAdapters) {\n record.attachedAdapters = [record.channelId];\n }\n return record;\n }\n\n /** Remove expired session records (past TTL). Active and assistant sessions are preserved. */\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 if (record.isAssistant === true)\n continue;\n const raw = record.lastActiveAt;\n if (!raw) continue;\n const lastActive = new Date(raw).getTime();\n if (isNaN(lastActive)) continue;\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","import type { AgentManager } from \"./agents/agent-manager.js\";\nimport type { SessionManager } from \"./sessions/session-manager.js\";\nimport type { ConfigManager } from \"./config/config.js\";\nimport type { SessionBridge } from \"./sessions/session-bridge.js\";\nimport type { Session } from \"./sessions/session.js\";\nimport type { IChannelAdapter } from \"./channel.js\";\nimport type { EventBus } from \"./event-bus.js\";\nimport type { MiddlewareChain } from \"./plugin/middleware-chain.js\";\nimport type { AgentEvent } from \"./types.js\";\nimport type { ContextManager } from \"../plugins/context/context-manager.js\";\nimport { getAgentCapabilities } from \"./agents/agent-registry.js\";\nimport { createChildLogger } from \"./utils/log.js\";\nimport { Hook, BusEvent, SessionEv } from \"./events.js\";\n\nconst log = createChildLogger({ module: \"agent-switch\" });\n\n/** Dependencies injected from OpenACPCore to avoid circular imports. */\nexport interface AgentSwitchDeps {\n sessionManager: SessionManager;\n agentManager: AgentManager;\n configManager: ConfigManager;\n eventBus: EventBus;\n adapters: Map<string, IChannelAdapter>;\n bridges: Map<string, SessionBridge>;\n createBridge: (session: Session, adapter: IChannelAdapter, adapterId?: string) => SessionBridge;\n getSessionBridgeKeys: (sessionId: string) => string[];\n getMiddlewareChain: () => MiddlewareChain | undefined;\n getService: <T>(name: string) => T | undefined;\n}\n\n/**\n * Coordinates the state transitions required when switching agents mid-session.\n *\n * Switching agents is a multi-step process with rollback support:\n * 1. Run `agent:beforeSwitch` middleware (blocking — plugins can veto)\n * 2. Determine whether to resume a previous agent session or spawn fresh\n * 3. Disconnect all SessionBridges and clean up adapter state\n * 4. Replace the agent instance on the session (with rollback on failure)\n * 5. Reconnect all bridges to the new agent\n * 6. Persist updated session record\n * 7. Fire `agent:afterSwitch` middleware (non-blocking)\n *\n * A per-session lock prevents concurrent switches on the same session.\n */\nexport class AgentSwitchHandler {\n /** Prevents concurrent switch operations on the same session */\n private switchingLocks = new Set<string>();\n\n constructor(private deps: AgentSwitchDeps) {}\n\n /**\n * Switch a session to a different agent. Returns whether the previous\n * agent session was resumed or a new one was spawned.\n */\n async switch(sessionId: string, toAgent: string): Promise<{ resumed: boolean }> {\n if (this.switchingLocks.has(sessionId)) {\n throw new Error('Switch already in progress');\n }\n this.switchingLocks.add(sessionId);\n try {\n return await this.doSwitch(sessionId, toAgent);\n } finally {\n this.switchingLocks.delete(sessionId);\n }\n }\n\n private async doSwitch(sessionId: string, toAgent: string): Promise<{ resumed: boolean }> {\n const { sessionManager, agentManager, configManager, eventBus, adapters, bridges, createBridge } = this.deps;\n\n const session = sessionManager.getSession(sessionId);\n if (!session) throw new Error(`Session ${sessionId} not found`);\n\n const agentDef = agentManager.getAgent(toAgent);\n if (!agentDef) throw new Error(`Agent \"${toAgent}\" is not installed`);\n\n const fromAgent = session.agentName;\n\n // 1. Middleware: agent:beforeSwitch (blocking)\n const middlewareChain = this.deps.getMiddlewareChain();\n const result = await middlewareChain?.execute(Hook.AGENT_BEFORE_SWITCH, {\n sessionId,\n fromAgent,\n toAgent,\n }, async (payload) => payload);\n if (middlewareChain && !result) throw new Error('Agent switch blocked by middleware');\n\n // 2. Determine resume vs new — if the agent was used before in this session\n // and supports resume, reconnect to its previous subprocess instead of spawning fresh\n const lastEntry = session.findLastSwitchEntry(toAgent);\n const caps = getAgentCapabilities(toAgent);\n const canResume = !!(lastEntry && caps.supportsResume);\n let resumed = false;\n\n // Emit \"starting\" events so UI can reflect long-running switches\n const startEvent: AgentEvent = {\n type: \"system_message\",\n message: `Switching from ${fromAgent} to ${toAgent}...`,\n };\n session.emit(SessionEv.AGENT_EVENT, startEvent);\n eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, turnId: '', event: startEvent });\n eventBus.emit(BusEvent.SESSION_AGENT_SWITCH, {\n sessionId,\n fromAgent,\n toAgent,\n status: \"starting\",\n });\n\n // 3. Disconnect ALL bridges for this session\n const sessionBridgeKeys = this.deps.getSessionBridgeKeys(sessionId);\n const hadBridges = sessionBridgeKeys.length > 0;\n for (const key of sessionBridgeKeys) {\n const bridge = bridges.get(key);\n if (bridge) {\n bridges.delete(key);\n bridge.disconnect();\n }\n }\n\n const switchAdapter = adapters.get(session.channelId);\n if (switchAdapter?.sendSkillCommands) {\n await switchAdapter.sendSkillCommands(session.id, []);\n }\n if (switchAdapter?.cleanupSessionState) {\n await switchAdapter.cleanupSessionState(session.id);\n }\n\n const fromAgentSessionId = session.agentSessionId;\n\n // 4. Switch agent on session (with rollback on failure).\n // switchAgent() replaces the session's agent instance atomically — if the\n // factory callback throws, the session state is unchanged.\n const fileService = this.deps.getService<import('../plugins/file-service/file-service.js').FileService>('file-service');\n const configAllowedPaths = configManager.get().workspace?.security?.allowedPaths ?? [];\n try {\n await session.switchAgent(toAgent, async () => {\n if (canResume) {\n try {\n const instance = await agentManager.resume(toAgent, session.workingDirectory, lastEntry!.agentSessionId, configAllowedPaths);\n if (fileService) instance.addAllowedPath(fileService.baseDir);\n resumed = true;\n return instance;\n } catch {\n // Resume failed (session expired or unavailable) — fall through to spawn with context\n log.warn({ sessionId, toAgent }, \"Resume failed, falling back to new agent with context injection\");\n }\n }\n\n // Fresh spawn: inject conversation history from the context service so the\n // new agent has awareness of what was discussed with the previous agent\n const instance = await agentManager.spawn(toAgent, session.workingDirectory, configAllowedPaths);\n if (fileService) instance.addAllowedPath(fileService.baseDir);\n try {\n const contextService = this.deps.getService<ContextManager>('context');\n if (contextService) {\n const config = configManager.get();\n const labelAgent = config.agentSwitch?.labelHistory ?? true;\n await contextService.flushSession(sessionId);\n const contextResult = await contextService.buildContext(\n { type: 'session', value: sessionId, repoPath: session.workingDirectory },\n { labelAgent, noCache: true },\n );\n if (contextResult?.markdown) {\n session.setContext(contextResult.markdown);\n }\n }\n } catch {\n // Context injection is best-effort\n }\n return instance;\n });\n\n const successEvent: AgentEvent = {\n type: \"system_message\",\n message: resumed\n ? `Switched to ${toAgent} (resumed previous session).`\n : `Switched to ${toAgent} (new session).`,\n };\n session.emit(SessionEv.AGENT_EVENT, successEvent);\n eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, turnId: '', event: successEvent });\n eventBus.emit(BusEvent.SESSION_AGENT_SWITCH, {\n sessionId,\n fromAgent,\n toAgent,\n status: \"succeeded\",\n resumed,\n });\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n\n const failedEvent: AgentEvent = {\n type: \"system_message\",\n message: `Failed to switch to ${toAgent}: ${errorMessage}`,\n };\n session.emit(SessionEv.AGENT_EVENT, failedEvent);\n eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, turnId: '', event: failedEvent });\n eventBus.emit(BusEvent.SESSION_AGENT_SWITCH, {\n sessionId,\n fromAgent,\n toAgent,\n status: \"failed\",\n error: errorMessage,\n });\n\n // Rollback\n try {\n let rollbackInstance;\n try {\n rollbackInstance = await agentManager.resume(fromAgent, session.workingDirectory, fromAgentSessionId);\n } catch {\n rollbackInstance = await agentManager.spawn(fromAgent, session.workingDirectory);\n }\n const oldInstance = rollbackInstance;\n session.agentSwitchHistory.pop();\n session.agentInstance = oldInstance;\n session.agentName = fromAgent;\n session.agentSessionId = oldInstance.sessionId;\n // Reconnect all bridges on rollback\n for (const adapterId of session.attachedAdapters) {\n const adapter = adapters.get(adapterId);\n if (adapter) {\n createBridge(session, adapter, adapterId).connect();\n }\n }\n log.warn({ sessionId, fromAgent, toAgent, err }, \"Agent switch failed, rolled back to previous agent\");\n } catch (rollbackErr) {\n session.fail(`Switch failed and rollback failed: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)}`);\n log.error({ sessionId, fromAgent, toAgent, err, rollbackErr }, \"Agent switch failed and rollback also failed\");\n }\n throw err;\n }\n\n // 5. Reconnect bridges for ALL attached adapters\n if (hadBridges) {\n for (const adapterId of session.attachedAdapters) {\n const adapter = adapters.get(adapterId);\n if (adapter) {\n createBridge(session, adapter, adapterId).connect();\n } else {\n log.warn({ sessionId, adapterId }, \"Adapter not available during switch reconnect, skipping bridge\");\n }\n }\n }\n\n // 6. Persist\n await sessionManager.patchRecord(sessionId, {\n agentName: toAgent,\n agentSessionId: session.agentSessionId,\n firstAgent: session.firstAgent,\n currentPromptCount: 0,\n agentSwitchHistory: session.agentSwitchHistory,\n });\n\n // 7. Middleware: agent:afterSwitch (fire-and-forget)\n middlewareChain?.execute(Hook.AGENT_AFTER_SWITCH, {\n sessionId,\n fromAgent,\n toAgent,\n resumed,\n }, async (p) => p).catch(() => {});\n\n return { resumed };\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { AgentStore } from \"./agent-store.js\";\nimport { installAgent, uninstallAgent, resolveDistribution } from \"./agent-installer.js\";\nimport { getAgentAlias, checkDependencies } from \"./agent-dependencies.js\";\nimport type {\n AgentDefinition,\n RegistryAgent,\n AgentListItem,\n AvailabilityResult,\n InstallProgress,\n InstallResult,\n InstalledAgent,\n} from \"../types.js\";\nimport { createChildLogger } from \"../utils/log.js\";\n\nconst log = createChildLogger({ module: \"agent-catalog\" });\n\nconst REGISTRY_URL = \"https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json\";\nconst DEFAULT_TTL_HOURS = 24;\n\ninterface RegistryCache {\n fetchedAt: string;\n ttlHours: number;\n data: { agents: RegistryAgent[] };\n}\n\n/**\n * Central catalog of available and installed agents.\n *\n * Combines two data sources:\n * 1. **Registry** — the remote ACP agent registry (CDN-hosted JSON), cached\n * locally with a 24-hour TTL and a bundled snapshot as fallback.\n * 2. **Store** — locally installed agents persisted in `agents.json`.\n *\n * Provides discovery (list all agents), installation, uninstallation,\n * and resolution (name → AgentDefinition for spawning).\n */\nexport class AgentCatalog {\n private store: AgentStore;\n /** Agents available in the remote registry (cached in memory after load). */\n private registryAgents: RegistryAgent[] = [];\n private cachePath: string;\n /** Directory where binary agent archives are extracted to. */\n private agentsDir: string | undefined;\n\n constructor(store: AgentStore, cachePath: string, agentsDir?: string) {\n this.store = store;\n this.cachePath = cachePath;\n this.agentsDir = agentsDir;\n }\n\n /**\n * Load installed agents from disk and hydrate the registry from cache/snapshot.\n *\n * Also enriches installed agents with registry metadata — fixes agents that\n * were migrated from older config formats with incomplete data.\n */\n load(): void {\n this.store.load();\n this.loadRegistryFromCacheOrSnapshot();\n this.enrichInstalledFromRegistry();\n }\n\n // --- Registry ---\n\n /** Fetch the latest agent registry from the CDN and update the local cache. */\n async fetchRegistry(): Promise<void> {\n try {\n log.info(\"Fetching agent registry from CDN...\");\n const response = await fetch(REGISTRY_URL);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const data = await response.json() as { agents: RegistryAgent[] };\n this.registryAgents = data.agents ?? [];\n\n const cache: RegistryCache = {\n fetchedAt: new Date().toISOString(),\n ttlHours: DEFAULT_TTL_HOURS,\n data,\n };\n fs.mkdirSync(path.dirname(this.cachePath), { recursive: true });\n fs.writeFileSync(this.cachePath, JSON.stringify(cache, null, 2), { mode: 0o600 });\n this.enrichInstalledFromRegistry();\n log.info({ count: this.registryAgents.length }, \"Registry updated\");\n } catch (err) {\n log.warn({ err }, \"Failed to fetch registry, using cached data\");\n }\n }\n\n /** Re-fetch registry only if the local cache has expired (24-hour TTL). */\n async refreshRegistryIfStale(): Promise<void> {\n if (this.isCacheStale()) {\n await this.fetchRegistry();\n }\n }\n\n getRegistryAgents(): RegistryAgent[] {\n return this.registryAgents;\n }\n\n getRegistryAgent(registryId: string): RegistryAgent | undefined {\n return this.registryAgents.find((a) => a.id === registryId);\n }\n\n /** Find a registry agent by registry ID or by its short alias (e.g., \"claude\"). */\n findRegistryAgent(keyOrId: string): RegistryAgent | undefined {\n const byId = this.registryAgents.find((a) => a.id === keyOrId);\n if (byId) return byId;\n return this.registryAgents.find((a) => getAgentAlias(a.id) === keyOrId);\n }\n\n // --- Installed ---\n\n getInstalled(): InstalledAgent[] {\n return Object.values(this.store.getInstalled());\n }\n\n getInstalledEntries(): Record<string, InstalledAgent> {\n return this.store.getInstalled();\n }\n\n getInstalledAgent(key: string): InstalledAgent | undefined {\n return this.store.getAgent(key);\n }\n\n // --- Discovery ---\n\n /**\n * Build the unified list of all agents (installed + registry-only).\n *\n * Installed agents appear first with their live availability status.\n * Registry agents that aren't installed yet show whether a distribution\n * exists for the current platform. Missing external dependencies\n * (e.g., claude CLI) are surfaced as `missingDeps` for UI display\n * but do NOT block installation.\n */\n getAvailable(): AgentListItem[] {\n const installed = this.getInstalledEntries();\n const items: AgentListItem[] = [];\n const seenKeys = new Set<string>();\n\n for (const [key, agent] of Object.entries(installed)) {\n seenKeys.add(key);\n const availability = agent.registryId\n ? checkDependencies(agent.registryId)\n : { available: true };\n const registryEntry = agent.registryId\n ? this.registryAgents.find((a) => a.id === agent.registryId)\n : undefined;\n items.push({\n key,\n registryId: agent.registryId ?? key,\n name: agent.name,\n version: agent.version,\n description: registryEntry?.description,\n distribution: agent.distribution,\n installed: true,\n available: availability.available,\n missingDeps: availability.missing?.map((m) => m.label),\n });\n }\n\n for (const agent of this.registryAgents) {\n const alias = getAgentAlias(agent.id);\n if (seenKeys.has(alias)) continue;\n seenKeys.add(alias);\n\n const dist = resolveDistribution(agent);\n const availability = checkDependencies(agent.id);\n\n // An uninstalled agent is \"available\" (shows Install button) as long as a\n // distribution exists for this platform. Missing external dependencies\n // (e.g. claude CLI, codex CLI) are surfaced via missingDeps so the UI can\n // show post-install setup instructions — they should NOT block installation.\n items.push({\n key: alias,\n registryId: agent.id,\n name: agent.name,\n version: agent.version,\n description: agent.description,\n distribution: dist?.type ?? \"binary\",\n installed: false,\n available: dist !== null,\n missingDeps: availability.missing?.map((m) => m.label),\n });\n }\n\n return items;\n }\n\n /** Check if an agent can be installed on this system (platform + dependencies). */\n checkAvailability(keyOrId: string): AvailabilityResult {\n const agent = this.findRegistryAgent(keyOrId);\n if (!agent) return { available: false, reason: \"Not found in the agent registry.\" };\n\n const dist = resolveDistribution(agent);\n if (!dist) {\n return { available: false, reason: `Not available for your system. Check ${agent.website ?? agent.repository ?? \"their website\"} for other options.` };\n }\n\n return checkDependencies(agent.id);\n }\n\n // --- Install/Uninstall ---\n\n /**\n * Install an agent from the registry.\n *\n * Resolves the distribution (npx/uvx/binary), downloads binary archives\n * if needed, and persists the agent definition in the store.\n */\n async install(keyOrId: string, progress?: InstallProgress, force?: boolean): Promise<InstallResult> {\n const agent = this.findRegistryAgent(keyOrId);\n if (!agent) {\n const msg = `\"${keyOrId}\" was not found in the agent registry. Run \"openacp agents\" to see what's available.`;\n await progress?.onError(msg);\n return { ok: false, agentKey: keyOrId, error: msg };\n }\n\n const agentKey = getAgentAlias(agent.id);\n if (this.store.hasAgent(agentKey) && !force) {\n const existing = this.store.getAgent(agentKey)!;\n const msg = `${agent.name} is already installed (v${existing.version}). Use --force to reinstall.`;\n await progress?.onError(msg);\n return { ok: false, agentKey, error: msg };\n }\n\n return installAgent(agent, this.store, progress, this.agentsDir);\n }\n\n /**\n * Register an agent directly into the catalog store without going through\n * the registry installer. Used to pre-register bundled agents (e.g. Claude)\n * when their CLI dependency is not yet installed.\n */\n registerFallbackAgent(key: string, data: InstalledAgent): void {\n this.store.addAgent(key, data);\n }\n\n /** Remove an installed agent and delete its binary directory if applicable. */\n async uninstall(key: string): Promise<{ ok: boolean; error?: string }> {\n if (this.store.hasAgent(key)) {\n await uninstallAgent(key, this.store);\n return { ok: true };\n }\n return { ok: false, error: `\"${key}\" is not installed.` };\n }\n\n // --- Resolution (for AgentManager) ---\n\n /** Convert an installed agent's short key to an AgentDefinition for spawning. */\n resolve(key: string): AgentDefinition | undefined {\n const agent = this.store.getAgent(key);\n if (!agent) return undefined;\n return {\n name: key,\n command: agent.command,\n args: agent.args,\n workingDirectory: agent.workingDirectory,\n env: agent.env,\n initTimeoutMs: agent.initTimeoutMs,\n };\n }\n\n // --- Internal ---\n\n /**\n * Enrich installed agents (especially migrated ones) with registry data.\n * Fixes agents that were migrated with version:\"unknown\", distribution:\"custom\",\n * or generic names by matching them to registry entries.\n */\n private enrichInstalledFromRegistry(): void {\n const installed = this.store.getInstalled();\n let changed = false;\n\n for (const [key, agent] of Object.entries(installed)) {\n const regAgent = agent.registryId\n ? this.registryAgents.find((a) => a.id === agent.registryId)\n : this.registryAgents.find((a) => getAgentAlias(a.id) === key);\n\n if (!regAgent) continue;\n\n let updated = false;\n\n // Enrich name if it's a generic capitalized key (e.g. \"Claude\" from migration)\n if (agent.name !== regAgent.name) {\n agent.name = regAgent.name;\n updated = true;\n }\n\n // Keep the installed metadata aligned with the registry. Package runners\n // are intentionally unpinned, so this records the adapter version whose\n // command definition is currently in use.\n if (agent.version !== regAgent.version) {\n agent.version = regAgent.version;\n updated = true;\n }\n\n // Enrich registryId if missing\n if (!agent.registryId) {\n agent.registryId = regAgent.id;\n updated = true;\n }\n\n // Enrich distribution from \"custom\" to actual type.\n // Also fix command/args — agents.json created by `openacp setup` uses the\n // agent name as the command placeholder rather than the real ACP binary.\n // Binary agents can't be enriched here (the archive hasn't been downloaded),\n // so we only fix npx/uvx which need no download.\n if (agent.distribution === \"custom\") {\n const dist = resolveDistribution(regAgent);\n if (dist) {\n agent.distribution = dist.type;\n if (dist.type === \"npx\") {\n agent.command = \"npx\";\n agent.args = [stripNpmVersion(dist.package), ...dist.args];\n updated = true;\n } else if (dist.type === \"uvx\") {\n agent.command = \"uvx\";\n agent.args = [stripPythonVersion(dist.package), ...dist.args];\n updated = true;\n }\n // binary: skip — binary must be downloaded via `openacp agents install`\n }\n }\n\n // Registry entries can move to a new adapter package while retaining the\n // same ID. Refresh existing package-runner definitions automatically so\n // users do not remain stuck on a retired adapter (for example, Codex moved\n // from @zed-industries/codex-acp to @agentclientprotocol/codex-acp).\n const currentDist = resolveDistribution(regAgent);\n if (agent.distribution === \"npx\" && currentDist?.type === \"npx\") {\n const args = [stripNpmVersion(currentDist.package), ...currentDist.args];\n if (agent.command !== \"npx\" || !arraysEqual(agent.args, args)) {\n agent.command = \"npx\";\n agent.args = args;\n agent.env = { ...currentDist.env, ...agent.env };\n updated = true;\n }\n } else if (agent.distribution === \"uvx\" && currentDist?.type === \"uvx\") {\n const args = [stripPythonVersion(currentDist.package), ...currentDist.args];\n if (agent.command !== \"uvx\" || !arraysEqual(agent.args, args)) {\n agent.command = \"uvx\";\n agent.args = args;\n agent.env = { ...currentDist.env, ...agent.env };\n updated = true;\n }\n }\n\n if (updated) {\n this.store.addAgent(key, agent);\n changed = true;\n }\n }\n\n if (changed) {\n log.info(\"Enriched installed agents with registry data\");\n }\n }\n\n private isCacheStale(): boolean {\n if (!fs.existsSync(this.cachePath)) return true;\n try {\n const raw = JSON.parse(fs.readFileSync(this.cachePath, \"utf-8\") as string) as RegistryCache;\n const fetchedAt = new Date(raw.fetchedAt).getTime();\n const ttlMs = (raw.ttlHours ?? DEFAULT_TTL_HOURS) * 60 * 60 * 1000;\n return Date.now() - fetchedAt > ttlMs;\n } catch {\n return true;\n }\n }\n\n private loadRegistryFromCacheOrSnapshot(): void {\n // Try cache first\n if (fs.existsSync(this.cachePath)) {\n try {\n const raw = JSON.parse(fs.readFileSync(this.cachePath, \"utf-8\") as string) as RegistryCache;\n if (raw.data?.agents) {\n this.registryAgents = raw.data.agents;\n log.debug({ count: this.registryAgents.length }, \"Loaded registry from cache\");\n return;\n }\n } catch {\n log.warn(\"Failed to load registry cache\");\n }\n }\n\n // Fallback: bundled snapshot\n try {\n // Try multiple paths for tsc and tsup builds\n const candidates = [\n path.join(import.meta.dirname, \"data\", \"registry-snapshot.json\"),\n path.join(import.meta.dirname, \"..\", \"data\", \"registry-snapshot.json\"),\n path.join(import.meta.dirname, \"..\", \"..\", \"data\", \"registry-snapshot.json\"),\n ];\n\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n const raw = JSON.parse(fs.readFileSync(candidate, \"utf-8\") as string);\n this.registryAgents = raw.agents ?? [];\n log.debug({ count: this.registryAgents.length }, \"Loaded registry from bundled snapshot\");\n return;\n }\n }\n\n log.warn(\"No registry data available (no cache, no snapshot)\");\n } catch {\n log.warn(\"Failed to load bundled registry snapshot\");\n }\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Strip pinned npm version so npx always resolves to latest.\n * Mirrors the logic in agent-installer.ts `stripPackageVersion`.\n * e.g. \"@agentclientprotocol/claude-agent-acp@0.26.0\" → \"@agentclientprotocol/claude-agent-acp\"\n * \"cline@2.9.0\" → \"cline\"\n * \"@scope/pkg\" → \"@scope/pkg\"\n */\nfunction stripNpmVersion(pkg: string): string {\n if (pkg.startsWith(\"@\")) {\n const slashIdx = pkg.indexOf(\"/\");\n if (slashIdx === -1) return pkg;\n const versionAt = pkg.indexOf(\"@\", slashIdx + 1);\n return versionAt === -1 ? pkg : pkg.slice(0, versionAt);\n }\n const at = pkg.indexOf(\"@\");\n return at === -1 ? pkg : pkg.slice(0, at);\n}\n\nfunction arraysEqual(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((value, index) => value === b[index]);\n}\n\n/**\n * Strip pinned Python version so uvx always resolves to latest.\n * Mirrors the logic in agent-installer.ts `stripPythonPackageVersion`.\n * e.g. \"fast-agent-acp==0.6.6\" → \"fast-agent-acp\"\n * \"minion-code@0.1.44\" → \"minion-code\"\n */\nfunction stripPythonVersion(pkg: string): string {\n const pyMatch = pkg.match(/^([^=@><!]+)/);\n if (pyMatch && pkg.includes(\"==\")) return pyMatch[1]!;\n const at = pkg.indexOf(\"@\");\n return at === -1 ? pkg : pkg.slice(0, at);\n}\n","/**\n * Persistent storage for installed agent definitions.\n *\n * Agents are stored in `agents.json` (typically `~/.openacp/agents.json`).\n * The file is validated with Zod on load; corrupted or invalid data is\n * discarded gracefully with a warning. Writes use atomic rename to\n * prevent partial writes from corrupting the file.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { z } from \"zod\";\nimport type { InstalledAgent } from \"../types.js\";\nimport { createChildLogger } from \"../utils/log.js\";\n\nconst log = createChildLogger({ module: \"agent-store\" });\n\nconst InstalledAgentSchema = z.object({\n registryId: z.string().nullable(),\n name: z.string(),\n version: z.string(),\n distribution: z.enum([\"npx\", \"uvx\", \"binary\", \"custom\"]),\n command: z.string(),\n args: z.array(z.string()).default([]),\n env: z.record(z.string(), z.string()).default({}),\n workingDirectory: z.string().optional(),\n installedAt: z.string(),\n binaryPath: z.string().nullable().default(null),\n initTimeoutMs: z.number().positive().optional(),\n});\n\nconst AgentStoreSchema = z.object({\n version: z.number().default(1),\n installed: z.record(z.string(), InstalledAgentSchema).default({}),\n});\n\ntype AgentStoreData = z.infer<typeof AgentStoreSchema>;\n\n/** JSON-backed store for installed agent definitions (`agents.json`). */\nexport class AgentStore {\n private data: AgentStoreData = { version: 1, installed: {} };\n readonly filePath: string;\n\n constructor(filePath: string) {\n this.filePath = filePath;\n }\n\n /** Load and validate the store from disk. Starts fresh if file is missing or invalid. */\n load(): void {\n if (!fs.existsSync(this.filePath)) {\n this.data = { version: 1, installed: {} };\n return;\n }\n\n try {\n const raw = JSON.parse(fs.readFileSync(this.filePath, \"utf-8\") as string);\n const result = AgentStoreSchema.safeParse(raw);\n if (result.success) {\n this.data = result.data;\n } else {\n log.warn({ errors: result.error.issues }, \"Invalid agents.json, starting fresh\");\n this.data = { version: 1, installed: {} };\n }\n } catch (err) {\n log.warn({ err }, \"Failed to read agents.json, starting fresh\");\n this.data = { version: 1, installed: {} };\n }\n }\n\n exists(): boolean {\n return fs.existsSync(this.filePath);\n }\n\n getInstalled(): Record<string, InstalledAgent> {\n return this.data.installed;\n }\n\n getAgent(key: string): InstalledAgent | undefined {\n return this.data.installed[key];\n }\n\n addAgent(key: string, agent: InstalledAgent): void {\n this.data.installed[key] = agent;\n this.save();\n }\n\n removeAgent(key: string): void {\n delete this.data.installed[key];\n this.save();\n }\n\n hasAgent(key: string): boolean {\n return key in this.data.installed;\n }\n\n /**\n * Persist the store to disk using atomic write (write to .tmp, then rename).\n * File permissions are restricted to owner-only (0o600) since the store\n * may contain agent binary paths and environment variables.\n */\n private save(): void {\n fs.mkdirSync(path.dirname(this.filePath), { recursive: true });\n const tmpPath = this.filePath + \".tmp\";\n fs.writeFileSync(tmpPath, JSON.stringify(this.data, null, 2), { mode: 0o600 });\n fs.renameSync(tmpPath, this.filePath);\n }\n}\n","import { TypedEmitter } from \"./utils/typed-emitter.js\";\nimport type { AgentEvent, PermissionRequest, SessionStatus, UsageRecordEvent } from \"./types.js\";\nimport type { TurnSender } from \"./sessions/turn-context.js\";\n\n/**\n * Event map for the global EventBus.\n *\n * Defines all cross-cutting events that flow between core, plugins, and adapters.\n * Plugins subscribe via `eventBus.on(...)` without needing direct references\n * to the components that emit these events.\n */\nexport interface EventBusEvents {\n \"session:created\": (data: {\n sessionId: string;\n name?: string;\n agent: string;\n status: SessionStatus;\n userId?: string;\n }) => void;\n \"session:updated\": (data: {\n sessionId: string;\n status?: SessionStatus;\n name?: string;\n clientOverrides?: { bypassPermissions?: boolean };\n }) => void;\n \"session:deleted\": (data: { sessionId: string }) => void;\n \"agent:event\": (data: { sessionId: string; turnId: string; event: AgentEvent }) => void;\n \"permission:request\": (data: {\n sessionId: string;\n permission: PermissionRequest;\n }) => void;\n \"permission:resolved\": (data: {\n sessionId: string;\n requestId: string;\n decision: string;\n optionId?: string;\n resolvedBy?: string;\n }) => void;\n\n // System lifecycle\n \"kernel:booted\": () => void;\n \"system:ready\": () => void;\n \"system:shutdown\": () => void;\n \"system:commands-ready\": (data: {\n commands: Array<{ name: string; description: string; category?: string; usage?: string }>;\n }) => void;\n\n // Plugin lifecycle\n \"plugin:loaded\": (data: { name: string; version: string }) => void;\n \"plugin:failed\": (data: { name: string; error: string }) => void;\n \"plugin:disabled\": (data: { name: string; reason: string }) => void;\n \"plugin:unloaded\": (data: { name: string }) => void;\n\n // Session (additional)\n \"session:ended\": (data: { sessionId: string; reason: string }) => void;\n \"session:named\": (data: { sessionId: string; name: string }) => void;\n\n // Agent (additional)\n \"agent:prompt\": (data: {\n sessionId: string;\n text: string;\n attachments?: unknown[];\n }) => void;\n\n // Usage tracking (consumed by usage plugin)\n \"usage:recorded\": (data: UsageRecordEvent) => void;\n\n // Emitted after a new session thread is created and bridge connected\n \"session:threadReady\": (data: { sessionId: string; channelId: string; threadId: string }) => void;\n\n // Config changed (used by adapters to update control messages)\n \"session:configChanged\": (data: { sessionId: string }) => void;\n\n // Cross-adapter input visibility (SSE clients see messages from other adapters)\n \"message:queued\": (data: {\n sessionId: string;\n turnId: string;\n text: string;\n sourceAdapterId: string;\n attachments?: unknown[];\n timestamp: string;\n queueDepth: number;\n sender?: TurnSender | null;\n }) => void;\n \"message:processing\": (data: {\n sessionId: string;\n turnId: string;\n sourceAdapterId: string;\n userPrompt: string;\n finalPrompt: string;\n attachments?: unknown[];\n sender?: TurnSender | null;\n timestamp: string;\n }) => void;\n\n /** Fired when a queued message is rejected before processing (e.g. blocked by middleware). */\n \"message:failed\": (data: {\n sessionId: string;\n turnId: string;\n reason: string;\n }) => void;\n\n /**\n * Fired when a user message is actually placed in the pending queue behind a running prompt.\n *\n * Unlike MESSAGE_QUEUED (which fires before enqueuePrompt is called and can race), this event\n * fires synchronously from inside PromptQueue.enqueue() so queueDepth and sourceAdapterId are\n * always accurate.\n */\n \"prompt:waiting\": (data: {\n sessionId: string;\n turnId: string;\n sourceAdapterId: string;\n queueDepth: number;\n }) => void;\n\n // Agent switch lifecycle (used by UI & dashboards)\n \"session:agentSwitch\": (data: {\n sessionId: string;\n fromAgent: string;\n toAgent: string;\n status: \"starting\" | \"succeeded\" | \"failed\";\n resumed?: boolean;\n error?: string;\n }) => void;\n\n // Identity lifecycle (emitted by @openacp/identity built-in plugin)\n \"identity:created\": (data: { userId: string; identityId: string; source: string; displayName: string }) => void;\n \"identity:updated\": (data: { userId: string; changes: string[] }) => void;\n \"identity:linked\": (data: { userId: string; identityId: string; linkedFrom?: string }) => void;\n \"identity:unlinked\": (data: { userId: string; identityId: string; newUserId: string }) => void;\n \"identity:userMerged\": (data: { keptUserId: string; mergedUserId: string; movedIdentities: string[] }) => void;\n \"identity:roleChanged\": (data: { userId: string; oldRole: string; newRole: string; changedBy?: string }) => void;\n \"identity:seen\": (data: { userId: string; identityId: string; sessionId: string }) => void;\n\n // User-targeted notifications (delivered to app via SSE)\n \"user:notification\": (data: { userId: string; text: string; sessionId?: string }) => void;\n}\n\n/**\n * Global event bus for cross-cutting communication.\n *\n * Decouples plugins from direct session/core references — plugins and adapters\n * subscribe to bus events without knowing which component emits them. The core\n * and sessions emit events here; plugins consume them for features like usage\n * tracking, notifications, and UI updates.\n */\nexport class EventBus extends TypedEmitter<EventBusEvents> {}\n","import { createHash } from 'node:crypto'\nimport { readFileSync } from 'node:fs'\nimport type { OpenACPPlugin } from './types.js'\n\n/**\n * Resolve plugin load order via topological sort (DFS-based).\n *\n * Plugins must boot in dependency order so that services and middleware from\n * dependencies are available when a plugin's setup() runs. This function:\n *\n * 1. Removes overridden plugins (a plugin with `overrides: 'X'` replaces X)\n * 2. Cascade-skips plugins whose required dependencies are missing\n * 3. Detects circular dependencies (throws)\n * 4. Returns plugins in safe boot order (dependencies before dependents)\n */\nexport function resolveLoadOrder(plugins: OpenACPPlugin[]): OpenACPPlugin[] {\n // Phase 1: Apply overrides — remove overridden plugins\n const overrideTargets = new Set<string>()\n for (const p of plugins) {\n if (p.overrides) {\n overrideTargets.add(p.overrides)\n }\n }\n let remaining = plugins.filter((p) => !overrideTargets.has(p.name))\n\n // Phase 2: Build name→plugin map\n const byName = new Map<string, OpenACPPlugin>()\n for (const p of remaining) {\n byName.set(p.name, p)\n }\n\n // Phase 3: Find missing deps and cascade-skip\n const skipped = new Set<string>()\n\n function cascadeSkip(name: string): void {\n if (skipped.has(name)) return\n skipped.add(name)\n // Skip all plugins that depend on this one\n for (const p of remaining) {\n if (p.pluginDependencies && name in p.pluginDependencies) {\n cascadeSkip(p.name)\n }\n }\n }\n\n for (const p of remaining) {\n if (p.pluginDependencies) {\n for (const dep of Object.keys(p.pluginDependencies)) {\n if (!byName.has(dep)) {\n cascadeSkip(p.name)\n }\n }\n }\n }\n\n remaining = remaining.filter((p) => !skipped.has(p.name))\n\n // Rebuild map after skipping\n byName.clear()\n for (const p of remaining) {\n byName.set(p.name, p)\n }\n\n // Phase 4: Topological sort with cycle detection (DFS)\n const visited = new Set<string>()\n const inStack = new Set<string>()\n const order: OpenACPPlugin[] = []\n\n function visit(name: string): void {\n if (visited.has(name)) return\n if (inStack.has(name)) {\n throw new Error(`Circular dependency detected: ${name}`)\n }\n\n inStack.add(name)\n const plugin = byName.get(name)!\n if (plugin.pluginDependencies) {\n for (const dep of Object.keys(plugin.pluginDependencies)) {\n visit(dep)\n }\n }\n inStack.delete(name)\n visited.add(name)\n order.push(plugin)\n }\n\n for (const p of remaining) {\n visit(p.name)\n }\n\n return order\n}\n\n/**\n * Compute SHA-256 checksum of a file, returned as 64-char hex string.\n */\nexport function computeChecksum(filePath: string): string {\n const data = readFileSync(filePath)\n return createHash('sha256').update(data).digest('hex')\n}\n\n/**\n * Verify that actual checksum matches expected.\n */\nexport function verifyChecksum(_name: string, expected: string, actual: string): boolean {\n return expected === actual\n}\n","/**\n * Central service discovery for the plugin system.\n *\n * Plugins register service implementations by string key (e.g., 'security', 'speech').\n * Core and other plugins retrieve them via typed accessors:\n * `registry.get<SecurityService>('security')`\n *\n * Each service key is unique — registering a duplicate throws unless `registerOverride` is used.\n * Services are tracked by the owning plugin name so they can be bulk-removed on plugin unload.\n */\nexport class ServiceRegistry {\n private services = new Map<string, { implementation: unknown; pluginName: string }>()\n\n /**\n * Register a service. Throws if the service name is already taken.\n * Use `registerOverride` to intentionally replace an existing service.\n */\n register<T>(name: string, implementation: T, pluginName: string): void {\n if (this.services.has(name)) {\n const existing = this.services.get(name)!\n throw new Error(`Service '${name}' already registered by ${existing.pluginName}. Plugin ${pluginName} cannot register it without override.`)\n }\n this.services.set(name, { implementation, pluginName })\n }\n\n /** Register a service, replacing any existing registration (used by override plugins). */\n registerOverride<T>(name: string, implementation: T, pluginName: string): void {\n this.services.set(name, { implementation, pluginName })\n }\n\n /** Retrieve a service by name. Returns undefined if not registered. */\n get<T>(name: string): T | undefined {\n return this.services.get(name)?.implementation as T | undefined\n }\n\n /** Check whether a service is registered. */\n has(name: string): boolean {\n return this.services.has(name)\n }\n\n /** List all registered services with their owning plugin names. */\n list(): Array<{ name: string; pluginName: string }> {\n return [...this.services.entries()].map(([name, { pluginName }]) => ({ name, pluginName }))\n }\n\n /** Remove a single service by name. */\n unregister(name: string): void {\n this.services.delete(name)\n }\n\n /** Remove all services owned by a specific plugin (called during plugin unload). */\n unregisterByPlugin(pluginName: string): void {\n for (const [name, entry] of this.services) {\n if (entry.pluginName === pluginName) {\n this.services.delete(name)\n }\n }\n }\n}\n","import type { ErrorTracker } from './error-tracker.js'\n\n/** Per-handler timeout — prevents a single middleware from blocking the entire chain. */\nconst MIDDLEWARE_TIMEOUT_MS = 5000\n\ntype HandlerEntry = {\n pluginName: string\n priority: number\n handler: Function\n}\n\n/**\n * Manages ordered middleware chains for each hook point.\n *\n * Execution model:\n * - Handlers run in priority order (lower number = earlier). Default priority: 100.\n * - Each handler receives the current payload and a `next()` function.\n * - Calling `next()` passes control to the next handler in the chain.\n * - Returning `null` short-circuits: the operation is blocked and no further handlers run.\n * - If a handler throws or times out, it is skipped and the error is tracked.\n * After enough errors (see ErrorTracker), the plugin's middleware is auto-disabled.\n */\nexport class MiddlewareChain {\n private chains = new Map<string, Array<HandlerEntry>>()\n private errorHandler?: (pluginName: string, error: Error) => void\n private errorTracker?: ErrorTracker\n\n /** Register a middleware handler for a hook. Handlers are kept sorted by priority. */\n add(\n hook: string,\n pluginName: string,\n opts: { priority?: number; handler: Function },\n ): void {\n const entry: HandlerEntry = {\n pluginName,\n priority: opts.priority ?? 100,\n handler: opts.handler,\n }\n const existing = this.chains.get(hook)\n if (existing) {\n existing.push(entry)\n existing.sort((a, b) => a.priority - b.priority)\n } else {\n this.chains.set(hook, [entry])\n }\n }\n\n /**\n * Execute the middleware chain for a hook, ending with the core handler.\n *\n * The chain is built recursively: each handler calls `next()` to invoke the\n * next handler, with the core handler at the end. If no middleware is registered,\n * the core handler runs directly.\n *\n * @returns The final payload, or `null` if any handler short-circuited.\n */\n async execute<T>(\n hook: string,\n payload: T,\n coreHandler: (p: T) => T | Promise<T>,\n ): Promise<T | null> {\n const handlers = this.chains.get(hook)\n if (!handlers || handlers.length === 0) {\n return coreHandler(payload)\n }\n\n // Handlers are pre-sorted by priority at registration time\n const sorted = handlers\n\n // Build the chain as a recursive series of `next()` closures.\n // cachedResult prevents double-execution if a handler calls next() more than once.\n let cachedResult: { value: T | null } | undefined = undefined\n\n const buildNext = (index: number, currentPayload: T): (() => Promise<T | null>) => {\n return async () => {\n // Return cached result on double-call\n if (cachedResult !== undefined) {\n return cachedResult.value\n }\n\n if (index >= sorted.length) {\n // End of middleware chain — call core handler\n const result = await coreHandler(currentPayload)\n cachedResult = { value: result }\n return result\n }\n\n const entry = sorted[index]\n\n // Skip disabled plugins\n if (this.errorTracker?.isDisabled(entry.pluginName)) {\n const skipFn = buildNext(index + 1, currentPayload)\n return skipFn()\n }\n\n const nextFn = buildNext(index + 1, currentPayload)\n\n // Wrap next to detect when it has been called and cache\n let nextCalled = false\n let nextResult: T | null = null\n const wrappedNext = async (newPayload?: T): Promise<T | null> => {\n if (!nextCalled) {\n nextCalled = true\n const payloadForNext = newPayload !== undefined ? newPayload : currentPayload\n // Rebuild chain from next index with potentially updated payload\n const newNextFn = buildNext(index + 1, payloadForNext)\n nextResult = await newNextFn()\n }\n return nextResult\n }\n\n let handlerResult: T | null\n let timeoutTimer: ReturnType<typeof setTimeout> | undefined\n try {\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutTimer = setTimeout(\n () => reject(new Error(`Middleware timeout: ${entry.pluginName} on hook ${hook}`)),\n MIDDLEWARE_TIMEOUT_MS,\n )\n if (typeof timeoutTimer === 'object' && timeoutTimer !== null && 'unref' in timeoutTimer) {\n ;(timeoutTimer as NodeJS.Timeout).unref()\n }\n })\n\n handlerResult = await Promise.race([\n entry.handler(currentPayload, wrappedNext),\n timeoutPromise,\n ])\n } catch (err) {\n // Report error\n if (this.errorHandler) {\n this.errorHandler(entry.pluginName, err instanceof Error ? err : new Error(String(err)))\n }\n // Track error for circuit-breaking\n this.errorTracker?.increment(entry.pluginName)\n // Skip this handler — pass ORIGINAL payload to next\n return nextFn()\n } finally {\n clearTimeout(timeoutTimer!)\n }\n\n // Handler returned null — block\n if (handlerResult === null) {\n return null\n }\n\n return handlerResult\n }\n }\n\n const start = buildNext(0, payload)\n return start()\n }\n\n /** Remove all middleware handlers registered by a specific plugin. */\n removeAll(pluginName: string): void {\n for (const [hook, handlers] of this.chains.entries()) {\n const filtered = handlers.filter((h) => h.pluginName !== pluginName)\n if (filtered.length === 0) {\n this.chains.delete(hook)\n } else {\n this.chains.set(hook, filtered)\n }\n }\n }\n\n /** Set a callback for middleware errors (e.g., logging). */\n setErrorHandler(fn: (pluginName: string, error: Error) => void): void {\n this.errorHandler = fn\n }\n\n /** Attach an ErrorTracker for circuit-breaking misbehaving plugins. */\n setErrorTracker(tracker: ErrorTracker): void {\n this.errorTracker = tracker\n }\n}\n","/** Configuration for the sliding-window error budget. */\nexport interface ErrorBudgetConfig {\n /** Maximum errors allowed within the window before disabling the plugin. Default: 10. */\n maxErrors: number\n /** Sliding window duration in milliseconds. Default: 3600000 (1 hour). */\n windowMs: number\n}\n\n/**\n * Circuit breaker for misbehaving plugins.\n *\n * Tracks errors per plugin within a sliding time window. When a plugin exceeds\n * its error budget (default: 10 errors in 1 hour), it is auto-disabled —\n * its middleware handlers are skipped by MiddlewareChain. This prevents a\n * single broken plugin from degrading the entire system.\n *\n * Essential plugins can be marked exempt via `setExempt()`.\n */\nexport class ErrorTracker {\n private errors = new Map<string, { count: number; windowStart: number }>()\n private disabled = new Set<string>()\n private exempt = new Set<string>()\n private config: ErrorBudgetConfig\n\n /** Callback fired when a plugin is auto-disabled due to error budget exhaustion. */\n onDisabled?: (pluginName: string, reason: string) => void\n\n constructor(config?: Partial<ErrorBudgetConfig>) {\n this.config = { maxErrors: config?.maxErrors ?? 10, windowMs: config?.windowMs ?? 3600000 }\n }\n\n /**\n * Record an error for a plugin. If the error budget is exceeded,\n * the plugin is disabled and the `onDisabled` callback fires.\n */\n increment(pluginName: string): void {\n if (this.exempt.has(pluginName)) return\n\n const now = Date.now()\n const entry = this.errors.get(pluginName)\n\n if (!entry || now - entry.windowStart >= this.config.windowMs) {\n this.errors.set(pluginName, { count: 1, windowStart: now })\n } else {\n entry.count += 1\n }\n\n const current = this.errors.get(pluginName)!\n if (current.count >= this.config.maxErrors && !this.disabled.has(pluginName)) {\n this.disabled.add(pluginName)\n const reason = `Error budget exceeded: ${current.count} errors within ${this.config.windowMs}ms window`\n this.onDisabled?.(pluginName, reason)\n }\n }\n\n /** Check if a plugin has been disabled due to errors. */\n isDisabled(pluginName: string): boolean {\n return this.disabled.has(pluginName)\n }\n\n /** Re-enable a plugin and clear its error history. */\n reset(pluginName: string): void {\n this.disabled.delete(pluginName)\n this.errors.delete(pluginName)\n }\n\n /** Mark a plugin as exempt from circuit-breaking (e.g., essential plugins). */\n setExempt(pluginName: string): void {\n this.exempt.add(pluginName)\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { PluginStorage } from './types.js'\n\n/**\n * File-backed key-value store for a single plugin.\n *\n * Data is stored at `~/.openacp/plugins/<name>/kv.json`. Each plugin gets its\n * own instance, providing namespace isolation.\n *\n * Write operations are serialized through a promise chain (`writeChain`) to\n * prevent concurrent writes from corrupting the JSON file.\n */\nexport class PluginStorageImpl implements PluginStorage {\n private readonly kvPath: string\n private readonly dataDir: string\n /** Serializes writes to prevent concurrent file corruption */\n private writeChain: Promise<void> = Promise.resolve()\n\n constructor(baseDir: string) {\n this.dataDir = path.join(baseDir, 'data')\n this.kvPath = path.join(baseDir, 'kv.json')\n fs.mkdirSync(baseDir, { recursive: true })\n }\n\n private readKv(): Record<string, unknown> {\n try {\n const raw = fs.readFileSync(this.kvPath, 'utf-8')\n return JSON.parse(raw) as Record<string, unknown>\n } catch {\n return {}\n }\n }\n\n private writeKv(data: Record<string, unknown>): void {\n fs.writeFileSync(this.kvPath, JSON.stringify(data), 'utf-8')\n }\n\n async get<T>(key: string): Promise<T | undefined> {\n const data = this.readKv()\n return key in data ? (data[key] as T) : undefined\n }\n\n async set<T>(key: string, value: T): Promise<void> {\n this.writeChain = this.writeChain.then(() => {\n const data = this.readKv()\n data[key] = value\n this.writeKv(data)\n })\n return this.writeChain\n }\n\n async delete(key: string): Promise<void> {\n this.writeChain = this.writeChain.then(() => {\n const data = this.readKv()\n delete data[key]\n this.writeKv(data)\n })\n return this.writeChain\n }\n\n async list(): Promise<string[]> {\n return Object.keys(this.readKv())\n }\n\n async keys(prefix?: string): Promise<string[]> {\n const all = Object.keys(this.readKv())\n return prefix ? all.filter((k) => k.startsWith(prefix)) : all\n }\n\n async clear(): Promise<void> {\n this.writeChain = this.writeChain.then(() => {\n this.writeKv({})\n })\n return this.writeChain\n }\n\n /** Returns the plugin's data directory, creating it lazily on first access. */\n getDataDir(): string {\n fs.mkdirSync(this.dataDir, { recursive: true })\n return this.dataDir\n }\n\n /**\n * Creates a namespaced storage instance scoped to a session.\n * Keys are prefixed with `session:{sessionId}:` to isolate session data\n * from global plugin storage in the same backing file.\n */\n forSession(sessionId: string): PluginStorage {\n const prefix = `session:${sessionId}:`\n // Proxy that transparently prepends the session prefix to all key operations\n return {\n get: <T>(key: string) => this.get<T>(`${prefix}${key}`),\n set: <T>(key: string, value: T) => this.set(`${prefix}${key}`, value),\n delete: (key: string) => this.delete(`${prefix}${key}`),\n list: async () => {\n const all = await this.keys(prefix)\n return all.map((k) => k.slice(prefix.length))\n },\n keys: async (p?: string) => {\n const full = p ? `${prefix}${p}` : prefix\n const all = await this.keys(full)\n return all.map((k) => k.slice(prefix.length))\n },\n clear: async () => {\n this.writeChain = this.writeChain.then(() => {\n const data = this.readKv()\n for (const key of Object.keys(data)) {\n if (key.startsWith(prefix)) delete data[key]\n }\n this.writeKv(data)\n })\n return this.writeChain\n },\n getDataDir: () => this.getDataDir(),\n forSession: (nestedId: string) => this.forSession(`${sessionId}:${nestedId}`),\n }\n }\n}\n","import type {\n PluginContext,\n PluginPermission,\n CommandDef,\n MiddlewareHook,\n MiddlewareOptions,\n MiddlewarePayloadMap,\n PluginStorage,\n EventBus,\n Logger,\n OutgoingMessage,\n MenuItem,\n} from './types.js'\nimport type { MenuRegistry } from '../menu-registry.js'\nimport type { AssistantSection } from '../assistant/assistant-registry.js'\nimport type { AssistantRegistry } from '../assistant/assistant-registry.js'\nimport { ServiceRegistry } from './service-registry.js'\nimport { MiddlewareChain } from './middleware-chain.js'\nimport { ErrorTracker } from './error-tracker.js'\nimport { PluginStorageImpl } from './plugin-storage.js'\n\n/** Internal options for creating a PluginContext. Passed from LifecycleManager. */\ninterface CreatePluginContextOpts {\n pluginName: string\n pluginConfig: Record<string, unknown>\n permissions: PluginPermission[]\n serviceRegistry: ServiceRegistry\n middlewareChain: MiddlewareChain\n errorTracker: ErrorTracker\n eventBus: EventBus & {\n on(event: string, handler: (...args: unknown[]) => void): void\n off(event: string, handler: (...args: unknown[]) => void): void\n emit(event: string, payload: unknown): void\n }\n storagePath: string\n sessions: unknown\n config: unknown\n core?: unknown\n log?: Logger\n instanceRoot?: string\n}\n\n/** Gate that throws if the plugin's declared permissions don't include the required one. */\nfunction requirePermission(permissions: PluginPermission[], required: PluginPermission, action: string): void {\n if (!permissions.includes(required)) {\n throw new Error(`Plugin does not have '${required}' permission required for ${action}`)\n }\n}\n\n/**\n * Factory that creates a scoped, permission-gated PluginContext for a single plugin.\n *\n * Every call to a context method checks the plugin's declared permissions first.\n * The returned object also includes a `cleanup()` method used by LifecycleManager\n * to remove all registrations (listeners, middleware, services, commands) when\n * the plugin is unloaded — ensuring no dangling references.\n */\nexport function createPluginContext(opts: CreatePluginContextOpts): PluginContext & { cleanup(): void } {\n const {\n pluginName,\n pluginConfig,\n permissions,\n serviceRegistry,\n middlewareChain,\n eventBus,\n storagePath,\n sessions,\n config,\n core,\n } = opts\n const instanceRoot = opts.instanceRoot!\n\n // Track all registrations so cleanup() can remove them on plugin unload.\n // Without this, unloaded plugins would leave orphaned listeners and middleware.\n const registeredListeners: Array<{ event: string; handler: (...args: unknown[]) => void }> = []\n const registeredCommands: CommandDef[] = []\n const registeredMenuItemIds: string[] = []\n const registeredAssistantSectionIds: string[] = []\n\n const noopLog: Logger = {\n trace() {},\n debug() {},\n info() {},\n warn() {},\n error() {},\n fatal() {},\n child() { return noopLog },\n }\n const baseLog: Logger = opts.log ?? noopLog\n const log: Logger = typeof baseLog.child === 'function'\n ? baseLog.child({ plugin: pluginName })\n : baseLog\n\n const storageImpl = new PluginStorageImpl(storagePath)\n\n // Wrap storage operations with permission checks — plugins without\n // storage:read or storage:write permissions cannot access the filesystem.\n const storage: PluginStorage = {\n async get<T>(key: string): Promise<T | undefined> {\n requirePermission(permissions, 'storage:read', 'storage.get')\n return storageImpl.get<T>(key)\n },\n async set<T>(key: string, value: T): Promise<void> {\n requirePermission(permissions, 'storage:write', 'storage.set')\n return storageImpl.set(key, value)\n },\n async delete(key: string): Promise<void> {\n requirePermission(permissions, 'storage:write', 'storage.delete')\n return storageImpl.delete(key)\n },\n async list(): Promise<string[]> {\n requirePermission(permissions, 'storage:read', 'storage.list')\n return storageImpl.list()\n },\n async keys(prefix?: string): Promise<string[]> {\n requirePermission(permissions, 'storage:read', 'storage.keys')\n return storageImpl.keys(prefix)\n },\n async clear(): Promise<void> {\n requirePermission(permissions, 'storage:write', 'storage.clear')\n return storageImpl.clear()\n },\n getDataDir(): string {\n requirePermission(permissions, 'storage:read', 'storage.getDataDir')\n return storageImpl.getDataDir()\n },\n forSession(sessionId: string): PluginStorage {\n requirePermission(permissions, 'storage:read', 'storage.forSession')\n // Return a permission-wrapped session-scoped storage\n const scoped = storageImpl.forSession(sessionId)\n return {\n get: <T>(key: string) => { requirePermission(permissions, 'storage:read', 'storage.get'); return scoped.get<T>(key) },\n set: <T>(key: string, value: T) => { requirePermission(permissions, 'storage:write', 'storage.set'); return scoped.set(key, value) },\n delete: (key: string) => { requirePermission(permissions, 'storage:write', 'storage.delete'); return scoped.delete(key) },\n list: () => { requirePermission(permissions, 'storage:read', 'storage.list'); return scoped.list() },\n keys: (prefix?: string) => { requirePermission(permissions, 'storage:read', 'storage.keys'); return scoped.keys(prefix) },\n clear: () => { requirePermission(permissions, 'storage:write', 'storage.clear'); return scoped.clear() },\n getDataDir: () => { requirePermission(permissions, 'storage:read', 'storage.getDataDir'); return scoped.getDataDir() },\n forSession: (nestedId: string) => storage.forSession(`${sessionId}:${nestedId}`),\n }\n },\n }\n\n const ctx: PluginContext & { cleanup(): void } = {\n pluginName,\n pluginConfig,\n log,\n storage,\n\n on(event: string, handler: (...args: unknown[]) => void): void {\n requirePermission(permissions, 'events:read', 'on()')\n eventBus.on(event, handler)\n registeredListeners.push({ event, handler })\n },\n\n off(event: string, handler: (...args: unknown[]) => void): void {\n requirePermission(permissions, 'events:read', 'off()')\n eventBus.off(event, handler)\n const idx = registeredListeners.findIndex((l) => l.event === event && l.handler === handler)\n if (idx >= 0) registeredListeners.splice(idx, 1)\n },\n\n emit(event: string, payload: unknown): void {\n requirePermission(permissions, 'events:emit', 'emit()')\n eventBus.emit(event, payload)\n },\n\n registerMiddleware<H extends MiddlewareHook>(hook: H, middlewareOpts: MiddlewareOptions<MiddlewarePayloadMap[H]>): void {\n requirePermission(permissions, 'middleware:register', 'registerMiddleware()')\n middlewareChain.add(hook, pluginName, middlewareOpts as { priority?: number; handler: Function })\n },\n\n registerService<T>(name: string, implementation: T): void {\n requirePermission(permissions, 'services:register', 'registerService()')\n serviceRegistry.register(name, implementation, pluginName)\n },\n\n getService<T>(name: string): T | undefined {\n requirePermission(permissions, 'services:use', 'getService()')\n return serviceRegistry.get<T>(name)\n },\n\n registerCommand(def: CommandDef): void {\n requirePermission(permissions, 'commands:register', 'registerCommand()')\n registeredCommands.push(def)\n const registry = serviceRegistry.get<{ register(def: CommandDef, pluginName: string): void }>('command-registry')\n if (registry && typeof registry.register === 'function') {\n registry.register(def, pluginName)\n log.debug(`Command '/${def.name}' registered`)\n }\n },\n\n async sendMessage(_sessionId: string, _content: OutgoingMessage): Promise<void> {\n requirePermission(permissions, 'services:use', 'sendMessage()')\n // Delegate to message routing service\n const router = serviceRegistry.get<{ send(sessionId: string, content: OutgoingMessage): Promise<void> }>('message-router')\n if (router) {\n await router.send(_sessionId, _content)\n }\n },\n\n notify(\n target: { identityId: string } | { userId: string } | { channelId: string; platformId: string },\n message: { type: 'text'; text: string },\n options?: any,\n ): void {\n requirePermission(permissions, 'notifications:send', 'notify()')\n const svc = serviceRegistry.get<{ notifyUser(t: any, m: any, o: any): Promise<void> }>('notifications')\n if (svc?.notifyUser) {\n // Fire-and-forget — don't await, swallow errors\n svc.notifyUser(target, message, options).catch(() => {})\n }\n },\n\n defineHook(_name: string): void {\n // MiddlewareChain creates hook entries lazily on first add(), so no pre-registration is needed.\n // This method exists to let plugins declare intent — documentation/discoverability only.\n },\n\n async emitHook<T extends Record<string, unknown>>(name: string, payload: T): Promise<T | null> {\n // Auto-prefix prevents hook name collisions between plugins.\n const qualifiedName = `plugin:${pluginName}:${name}`\n // Pass identity as core handler — plugin hooks have no core behavior, only middleware side effects.\n return middlewareChain.execute(qualifiedName, payload, (p) => p) as Promise<T | null>\n },\n\n async getSessionInfo(sessionId: string) {\n requirePermission(permissions, 'sessions:read', 'getSessionInfo()')\n const sessionMgr = serviceRegistry.get<{\n getSessionInfo(id: string): Promise<{\n id: string\n status: import('../types.js').SessionStatus\n name?: string\n promptCount: number\n channelId: string\n agentName: string\n } | undefined>\n }>('session-info')\n if (!sessionMgr) return undefined\n return sessionMgr.getSessionInfo(sessionId)\n },\n\n registerMenuItem(item: MenuItem): void {\n requirePermission(permissions, 'commands:register', 'registerMenuItem()')\n const menuRegistry = serviceRegistry.get('menu-registry') as MenuRegistry | undefined\n if (!menuRegistry) return\n // Namespace menu item IDs with plugin name to prevent collisions\n const qualifiedId = `${pluginName}:${item.id}`\n menuRegistry.register({ ...item, id: qualifiedId })\n registeredMenuItemIds.push(qualifiedId)\n },\n\n unregisterMenuItem(id: string): void {\n requirePermission(permissions, 'commands:register', 'unregisterMenuItem()')\n const menuRegistry = serviceRegistry.get('menu-registry') as MenuRegistry | undefined\n if (!menuRegistry) return\n menuRegistry.unregister(`${pluginName}:${id}`)\n },\n\n registerAssistantSection(section: AssistantSection): void {\n requirePermission(permissions, 'commands:register', 'registerAssistantSection()')\n const assistantRegistry = serviceRegistry.get('assistant-registry') as AssistantRegistry | undefined\n if (!assistantRegistry) return\n // Namespace section IDs with plugin name to prevent collisions\n const qualifiedId = `${pluginName}:${section.id}`\n assistantRegistry.register({ ...section, id: qualifiedId })\n registeredAssistantSectionIds.push(qualifiedId)\n },\n\n unregisterAssistantSection(id: string): void {\n requirePermission(permissions, 'commands:register', 'unregisterAssistantSection()')\n const assistantRegistry = serviceRegistry.get('assistant-registry') as AssistantRegistry | undefined\n if (!assistantRegistry) return\n assistantRegistry.unregister(`${pluginName}:${id}`)\n },\n\n registerEditableFields(fields: import('./types.js').FieldDef[]): void {\n requirePermission(permissions, 'commands:register', 'registerEditableFields()')\n const registry = serviceRegistry.get<{ register(pluginName: string, fields: import('./types.js').FieldDef[]): void }>('field-registry')\n if (registry && typeof registry.register === 'function') {\n registry.register(pluginName, fields)\n log.debug(`Registered ${fields.length} editable field(s) for ${pluginName}`)\n }\n },\n\n get sessions() {\n requirePermission(permissions, 'kernel:access', 'sessions')\n return sessions as any\n },\n\n get config() {\n requirePermission(permissions, 'kernel:access', 'config')\n return config as any\n },\n\n get eventBus() {\n requirePermission(permissions, 'kernel:access', 'eventBus')\n return eventBus as any\n },\n\n get core() {\n requirePermission(permissions, 'kernel:access', 'core')\n return core\n },\n\n instanceRoot,\n\n /**\n * Called by LifecycleManager during plugin teardown.\n * Unregisters all event handlers, middleware, commands, and services\n * registered by this plugin, preventing leaks across reloads.\n */\n cleanup(): void {\n // Remove all event listeners registered by this plugin\n for (const { event, handler } of registeredListeners) {\n eventBus.off(event, handler)\n }\n registeredListeners.length = 0\n\n // Remove all middleware registered by this plugin\n middlewareChain.removeAll(pluginName)\n\n // Unregister services owned by this plugin\n serviceRegistry.unregisterByPlugin(pluginName)\n\n // Unregister commands from CommandRegistry\n const cmdRegistry = serviceRegistry.get<{ unregisterByPlugin(name: string): void }>('command-registry')\n if (cmdRegistry && typeof cmdRegistry.unregisterByPlugin === 'function') {\n cmdRegistry.unregisterByPlugin(pluginName)\n }\n\n // Unregister menu items\n const menuRegistry = serviceRegistry.get('menu-registry') as MenuRegistry | undefined\n if (menuRegistry) {\n for (const id of registeredMenuItemIds) {\n menuRegistry.unregister(id)\n }\n }\n registeredMenuItemIds.length = 0\n\n // Unregister assistant sections\n const assistantRegistry = serviceRegistry.get('assistant-registry') as AssistantRegistry | undefined\n if (assistantRegistry) {\n for (const id of registeredAssistantSectionIds) {\n assistantRegistry.unregister(id)\n }\n }\n registeredAssistantSectionIds.length = 0\n\n // Clear commands\n registeredCommands.length = 0\n },\n }\n\n return ctx\n}\n","import { resolveLoadOrder } from './plugin-loader.js'\nimport { ServiceRegistry } from './service-registry.js'\nimport { MiddlewareChain } from './middleware-chain.js'\nimport { ErrorTracker } from './error-tracker.js'\nimport { createPluginContext } from './plugin-context.js'\nimport type { OpenACPPlugin, EventBus, Logger, MigrateContext } from './types.js'\nimport type { SettingsManager } from './settings-manager.js'\nimport type { PluginRegistry } from './plugin-registry.js'\nimport { BusEvent } from '../events.js'\n\nconst SETUP_TIMEOUT_MS = 30_000 // plugin.setup() must complete within 30s\nconst TEARDOWN_TIMEOUT_MS = 10_000 // plugin.teardown() must complete within 10s\n\n/** Wraps a promise with a timeout; rejects if the promise doesn't settle in `ms` milliseconds. */\nfunction withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`Timeout: ${label} exceeded ${ms}ms`)), ms)\n if (typeof timer === 'object' && timer !== null && 'unref' in timer) {\n ;(timer as NodeJS.Timeout).unref()\n }\n promise.then(resolve, reject).finally(() => clearTimeout(timer))\n })\n}\n\n/**\n * Extracts a plugin's config from the global ConfigManager.\n * Tries the new `plugins.builtin['<name>'].config` format first,\n * then falls back to legacy config paths for backward compatibility.\n */\nfunction resolvePluginConfig(pluginName: string, configManager: unknown): Record<string, unknown> {\n try {\n const allConfig = (configManager as any)?.get?.() ?? {}\n // Try new format: plugins.builtin['@openacp/speech'].config\n const pluginEntry = allConfig.plugins?.builtin?.[pluginName]\n if (pluginEntry?.config && Object.keys(pluginEntry.config).length > 0) {\n return pluginEntry.config\n }\n // @deprecated Legacy config path mapping — kept for backward compat with pre-plugin configs.\n // New plugins should use plugins.builtin['<name>'].config format. Remove when all users have migrated.\n const legacyMap: Record<string, string> = {\n '@openacp/security': 'security',\n '@openacp/speech': 'speech',\n '@openacp/tunnel': 'tunnel',\n '@openacp/usage': 'usage',\n '@openacp/file-service': 'files',\n '@openacp/api-server': 'api',\n '@openacp/telegram': 'channels.telegram',\n '@openacp/discord-adapter': 'channels.discord',\n '@openacp/slack-adapter': 'channels.slack',\n }\n const legacyKey = legacyMap[pluginName]\n if (legacyKey) {\n const parts = legacyKey.split('.')\n let obj: any = allConfig\n for (const p of parts) obj = obj?.[p]\n if (obj && typeof obj === 'object') return { ...obj }\n }\n } catch {\n // Gracefully degrade — return empty config\n }\n return {}\n}\n\n/** Options for constructing a LifecycleManager. All fields are optional with sensible defaults. */\nexport interface LifecycleManagerOpts {\n serviceRegistry?: ServiceRegistry\n middlewareChain?: MiddlewareChain\n errorTracker?: ErrorTracker\n eventBus?: EventBus & {\n on(event: string, handler: (...args: unknown[]) => void): void\n off(event: string, handler: (...args: unknown[]) => void): void\n emit(event: string, payload: unknown): void\n }\n storagePath?: string\n sessions?: unknown\n config?: unknown\n core?: unknown\n log?: Logger\n settingsManager?: SettingsManager\n pluginRegistry?: PluginRegistry\n /** Root directory for this OpenACP instance (default: ~/.openacp) */\n instanceRoot?: string\n}\n\n/**\n * Orchestrates plugin boot, teardown, and hot-reload.\n *\n * Boot sequence:\n * 1. Topological sort by `pluginDependencies` — ensures a plugin's deps are ready first\n * 2. Version migration if registry version != plugin version\n * 3. Settings validation against Zod schema\n * 4. Create scoped PluginContext, call `plugin.setup(ctx)` with timeout\n *\n * Error isolation: if a plugin's setup() fails, it is marked as failed and skipped.\n * Any plugin that depends on a failed plugin is also skipped (cascade failure).\n * Other independent plugins continue booting normally.\n *\n * Shutdown calls teardown() in reverse boot order — dependencies are torn down last.\n */\nexport class LifecycleManager {\n readonly serviceRegistry: ServiceRegistry\n readonly middlewareChain: MiddlewareChain\n readonly errorTracker: ErrorTracker\n\n private eventBus: LifecycleManagerOpts['eventBus']\n private storagePath: string\n private sessions: unknown\n private config: unknown\n private core: unknown\n private log: Logger | undefined\n settingsManager: SettingsManager | undefined\n private pluginRegistry: PluginRegistry | undefined\n private _instanceRoot: string | undefined\n\n private contexts = new Map<string, ReturnType<typeof createPluginContext>>()\n private loadOrder: OpenACPPlugin[] = []\n private _loaded = new Set<string>()\n private _failed = new Set<string>()\n\n /** Names of plugins that successfully completed setup(). */\n get loadedPlugins(): string[] {\n return [...this._loaded]\n }\n\n /** Names of plugins whose setup() threw an error. These plugins are skipped but don't crash the system. */\n get failedPlugins(): string[] {\n return [...this._failed]\n }\n\n /** The PluginRegistry tracking installed and enabled plugin state. */\n get registry(): PluginRegistry | undefined {\n return this.pluginRegistry\n }\n\n /** Plugin definitions currently in load order (loaded + failed). */\n get plugins(): OpenACPPlugin[] {\n return [...this.loadOrder]\n }\n\n /** Root directory of this OpenACP instance (e.g. ~/.openacp). */\n get instanceRoot(): string | undefined {\n return this._instanceRoot\n }\n\n constructor(opts?: LifecycleManagerOpts) {\n this.serviceRegistry = opts?.serviceRegistry ?? new ServiceRegistry()\n this.middlewareChain = opts?.middlewareChain ?? new MiddlewareChain()\n this.errorTracker = opts?.errorTracker ?? new ErrorTracker()\n this.eventBus = opts?.eventBus ?? {\n on() {},\n off() {},\n emit() {},\n }\n this.storagePath = opts?.storagePath ?? '/tmp/openacp-plugins'\n this.sessions = opts?.sessions ?? {}\n this.config = opts?.config ?? {}\n this.core = opts?.core\n this.log = opts?.log\n this.settingsManager = opts?.settingsManager\n this.pluginRegistry = opts?.pluginRegistry\n this._instanceRoot = opts?.instanceRoot\n }\n\n private getPluginLogger(pluginName: string): Logger {\n if (this.log && typeof (this.log as any).child === 'function') {\n return (this.log as any).child({ plugin: pluginName })\n }\n return this.log ?? { trace() {}, debug() {}, info() {}, warn() {}, error() {}, fatal() {}, child() { return this } } as Logger\n }\n\n /**\n * Boot a set of plugins in dependency order.\n *\n * Can be called multiple times (e.g., core plugins first, then dev plugins later).\n * Already-loaded plugins are included in dependency resolution but not re-booted.\n */\n async boot(plugins: OpenACPPlugin[]): Promise<void> {\n // Resolve load order via topological sort.\n // resolveLoadOrder will skip plugins whose dependencies are missing entirely\n // (not present in the input list). But we also need to handle runtime setup failures.\n // Include already-loaded plugins so dependency checks pass for late-booted plugins\n // (e.g., dev plugins booted after core plugins).\n const newNames = new Set(plugins.map(p => p.name))\n const allForResolution = [...this.loadOrder.filter(p => !newNames.has(p.name)), ...plugins]\n\n let sorted: OpenACPPlugin[]\n try {\n sorted = resolveLoadOrder(allForResolution)\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n this.log?.error(`Plugin dependency resolution failed: ${error.message}`)\n for (const p of plugins) {\n this._failed.add(p.name)\n }\n return\n }\n\n // Only boot new plugins (already-loaded ones were included just for dependency resolution)\n sorted = sorted.filter(p => newNames.has(p.name))\n\n // Append to existing loadOrder (don't overwrite — hot-reload boots single plugins)\n for (const p of sorted) {\n if (!this.loadOrder.some(existing => existing.name === p.name)) {\n this.loadOrder.push(p)\n } else {\n // Replace in-place (hot-reload case: plugin was unloaded then re-booted)\n const idx = this.loadOrder.findIndex(existing => existing.name === p.name)\n this.loadOrder[idx] = p\n }\n }\n\n for (const plugin of sorted) {\n // Check if any required dependency failed at runtime\n if (plugin.pluginDependencies) {\n const depFailed = Object.keys(plugin.pluginDependencies).some(\n (dep) => this._failed.has(dep),\n )\n if (depFailed) {\n this._failed.add(plugin.name)\n continue\n }\n }\n\n // Check if disabled in registry\n const registryEntry = this.pluginRegistry?.get(plugin.name)\n if (registryEntry && registryEntry.enabled === false) {\n this.eventBus?.emit(BusEvent.PLUGIN_DISABLED, { name: plugin.name })\n continue\n }\n\n // Check version mismatch → migrate\n if (registryEntry && plugin.migrate && registryEntry.version !== plugin.version && this.settingsManager) {\n try {\n const oldSettings = await this.settingsManager.loadSettings(plugin.name)\n const pluginLog = this.getPluginLogger(plugin.name)\n const migrateCtx: MigrateContext = {\n pluginName: plugin.name,\n settings: this.settingsManager.createAPI(plugin.name),\n log: pluginLog,\n }\n const newSettings = await withTimeout(\n plugin.migrate(migrateCtx, oldSettings, registryEntry.version),\n SETUP_TIMEOUT_MS,\n `${plugin.name}.migrate()`,\n )\n if (newSettings && typeof newSettings === 'object') {\n await migrateCtx.settings.setAll(newSettings as Record<string, unknown>)\n }\n this.pluginRegistry!.updateVersion(plugin.name, plugin.version)\n await this.pluginRegistry!.save()\n } catch (err) {\n this.getPluginLogger(plugin.name).warn(`Migration failed, continuing with old settings: ${err}`)\n }\n }\n\n // Resolve config: prefer settings.json, fallback to legacy\n let pluginConfig: Record<string, unknown>\n if (this.settingsManager) {\n pluginConfig = await this.settingsManager.loadSettings(plugin.name)\n const settingsPath = this.settingsManager.getSettingsPath(plugin.name)\n this.getPluginLogger(plugin.name).debug(`Settings loaded from ${settingsPath}: ${Object.keys(pluginConfig).length} keys`)\n if (Object.keys(pluginConfig).length === 0) {\n pluginConfig = resolvePluginConfig(plugin.name, this.config)\n }\n } else {\n pluginConfig = resolvePluginConfig(plugin.name, this.config)\n this.getPluginLogger(plugin.name).debug('No settingsManager, using legacy config')\n }\n\n // Validate settings against schema if plugin provides one\n if (plugin.settingsSchema && this.settingsManager) {\n const validation = this.settingsManager.validateSettings(plugin.name, pluginConfig, plugin.settingsSchema)\n if (!validation.valid) {\n this._failed.add(plugin.name)\n this.getPluginLogger(plugin.name).error(`Settings validation failed: ${validation.errors?.join('; ')}`)\n this.eventBus?.emit(BusEvent.PLUGIN_FAILED, { name: plugin.name, error: `Settings validation failed: ${validation.errors?.join('; ')}` })\n continue\n }\n }\n\n // Create context for this plugin\n const ctx = createPluginContext({\n pluginName: plugin.name,\n pluginConfig,\n permissions: plugin.permissions ?? [],\n serviceRegistry: this.serviceRegistry,\n middlewareChain: this.middlewareChain,\n errorTracker: this.errorTracker,\n eventBus: this.eventBus!,\n storagePath: `${this.storagePath}/${plugin.name}`,\n sessions: this.sessions,\n config: this.config,\n core: this.core,\n log: this.log,\n instanceRoot: this._instanceRoot,\n })\n\n try {\n await withTimeout(plugin.setup(ctx), SETUP_TIMEOUT_MS, `${plugin.name}.setup()`)\n this.contexts.set(plugin.name, ctx)\n this._loaded.add(plugin.name)\n this.eventBus?.emit(BusEvent.PLUGIN_LOADED, { name: plugin.name, version: plugin.version })\n } catch (err) {\n this._failed.add(plugin.name)\n ctx.cleanup()\n console.error(`[lifecycle] Plugin ${plugin.name} setup() FAILED:`, err)\n this.getPluginLogger(plugin.name).error(`setup() failed: ${err}`)\n this.eventBus?.emit(BusEvent.PLUGIN_FAILED, { name: plugin.name, error: String(err) })\n }\n }\n }\n\n /**\n * Unload a single plugin: call teardown(), clean up its context\n * (listeners, middleware, services), and remove from tracked state.\n * Used for hot-reload: unload → rebuild → re-boot.\n */\n async unloadPlugin(name: string): Promise<void> {\n if (!this._loaded.has(name)) return\n\n const plugin = this.loadOrder.find(p => p.name === name)\n\n if (plugin?.teardown) {\n try {\n await withTimeout(plugin.teardown(), TEARDOWN_TIMEOUT_MS, `${name}.teardown()`)\n } catch {\n // Swallow teardown errors\n }\n }\n\n const ctx = this.contexts.get(name)\n if (ctx) {\n ctx.cleanup()\n this.contexts.delete(name)\n }\n\n this._loaded.delete(name)\n this._failed.delete(name)\n this.loadOrder = this.loadOrder.filter(p => p.name !== name)\n\n this.eventBus?.emit(BusEvent.PLUGIN_UNLOADED, { name })\n }\n\n /**\n * Gracefully shut down all loaded plugins.\n * Teardown runs in reverse boot order so that dependencies outlive their dependents.\n */\n async shutdown(): Promise<void> {\n // Teardown in reverse load order\n const reversed = [...this.loadOrder].reverse()\n\n for (const plugin of reversed) {\n if (!this._loaded.has(plugin.name)) continue\n\n if (plugin.teardown) {\n try {\n await withTimeout(plugin.teardown(), TEARDOWN_TIMEOUT_MS, `${plugin.name}.teardown()`)\n } catch {\n // Swallow teardown errors — graceful shutdown\n }\n }\n\n // Clean up the context\n const ctx = this.contexts.get(plugin.name)\n if (ctx) {\n ctx.cleanup()\n this.contexts.delete(plugin.name)\n }\n\n this.eventBus?.emit(BusEvent.PLUGIN_UNLOADED, { name: plugin.name })\n }\n\n this._loaded.clear()\n this.loadOrder = []\n }\n}\n","import type { MenuItem } from './plugin/types.js'\nimport { createChildLogger } from './utils/log.js'\n\nconst log = createChildLogger({ module: 'menu-registry' })\n\nexport { type MenuItem }\n\n/**\n * Registry for interactive menu items displayed to users (e.g. Telegram inline keyboards).\n *\n * Core registers default items (new session, agents, help, etc.) during construction.\n * Plugins can add their own items. Items are sorted by priority (lower = higher position)\n * and filtered by an optional `visible()` predicate at render time.\n */\nexport class MenuRegistry {\n private items = new Map<string, MenuItem>()\n\n /** Register or replace a menu item by its unique ID. */\n register(item: MenuItem): void {\n this.items.set(item.id, item)\n }\n\n /** Remove a menu item by ID. */\n unregister(id: string): void {\n this.items.delete(id)\n }\n\n /** Look up a single menu item by ID. */\n getItem(id: string): MenuItem | undefined {\n return this.items.get(id)\n }\n\n /** Get all visible items sorted by priority (lower number = shown first). */\n getItems(): MenuItem[] {\n return [...this.items.values()]\n .filter((item) => {\n if (!item.visible) return true\n try {\n return item.visible()\n } catch (err) {\n log.warn({ err, id: item.id }, 'MenuItem visible() threw, hiding item')\n return false\n }\n })\n .sort((a, b) => a.priority - b.priority)\n }\n}\n","import { createChildLogger } from '../utils/log.js'\nimport { ASSISTANT_PREAMBLE, buildAssistantGuidelines } from './prompt-constants.js'\n\nconst log = createChildLogger({ module: 'assistant-registry' })\n\n/** A CLI command the assistant can run, shown as a code block in the system prompt. */\nexport interface AssistantCommand {\n command: string\n description: string\n}\n\n/**\n * A section that injects live system state into the assistant's system prompt.\n *\n * Each section provides a `buildContext()` callback that is called at prompt\n * composition time. Sections are sorted by priority (lower = earlier in prompt)\n * so the most important context appears first.\n */\nexport interface AssistantSection {\n id: string\n title: string\n /** Lower priority = appears earlier in the system prompt. */\n priority: number\n /** Returns the section's context string, or null to skip this section entirely. */\n buildContext: () => string | null\n commands?: AssistantCommand[]\n}\n\n/**\n * Registry that collects assistant prompt sections and composes them into\n * a single system prompt.\n *\n * Core modules and plugins register sections (e.g. sessions, agents, config)\n * that each contribute a fragment of live system state. At prompt build time,\n * sections are sorted by priority, their `buildContext()` is called, and the\n * results are assembled with the preamble and CLI guidelines.\n */\nexport class AssistantRegistry {\n private sections = new Map<string, AssistantSection>()\n private _instanceRoot: string = ''\n\n /** Set the instance root path used in assistant guidelines. */\n setInstanceRoot(root: string): void {\n this._instanceRoot = root\n }\n\n /** Register a prompt section. Overwrites any existing section with the same id. */\n register(section: AssistantSection): void {\n if (this.sections.has(section.id)) {\n log.warn({ id: section.id }, 'Assistant section overwritten')\n }\n this.sections.set(section.id, section)\n }\n\n /** Remove a previously registered section by id. */\n unregister(id: string): void {\n this.sections.delete(id)\n }\n\n /**\n * Compose the full system prompt from all registered sections.\n *\n * Sections are sorted by priority (ascending), each contributing a titled\n * markdown block. If a section's `buildContext()` throws, it is skipped\n * gracefully so one broken section doesn't break the entire prompt.\n *\n * If `channelId` is provided, a \"Current Channel\" block is injected at the\n * top of the prompt so the assistant can adapt its behavior to the platform.\n */\n buildSystemPrompt(channelId?: string): string {\n const sorted = [...this.sections.values()].sort((a, b) => a.priority - b.priority)\n const parts: string[] = [ASSISTANT_PREAMBLE]\n\n if (channelId) {\n parts.push(`## Current Channel\\nYou are responding on the **${channelId}** channel. Adapt your formatting and behavior to this platform.`)\n }\n\n for (const section of sorted) {\n try {\n const context = section.buildContext()\n if (!context) continue\n parts.push(`## ${section.title}\\n${context}`)\n if (section.commands?.length) {\n const cmds = section.commands.map((c) => `${c.command} # ${c.description}`).join('\\n')\n parts.push('```bash\\n' + cmds + '\\n```')\n }\n } catch (err) {\n log.warn({ err, sectionId: section.id }, 'Assistant section buildContext() failed, skipping')\n }\n }\n\n parts.push(buildAssistantGuidelines(this._instanceRoot))\n return parts.join('\\n\\n')\n }\n}\n","/**\n * Sets the assistant's persona and basic formatting rules.\n * This is always the first block in the composed system prompt.\n */\nexport const ASSISTANT_PREAMBLE = `You are the OpenACP Assistant — a helpful guide for managing AI coding sessions.\n\nRespond in the same language the user uses.\nFormat responses for chat platforms: use **bold** and \\`code\\` (markdown), keep it concise.\nTalk to users like a helpful assistant, not a CLI manual.`\n\n/**\n * Resolve the CLI command prefix.\n * - Production (installed globally): `openacp`\n * - Dev (running from local dist/cli.js): `node /path/to/dist/cli.js`\n */\nexport function resolveCliCommand(): string {\n const entryScript = process.argv[1] ?? ''\n if (entryScript.endsWith('dist/cli.js')) {\n return `node \"${entryScript}\"`\n }\n return 'openacp'\n}\n\n/**\n * Builds the CLI guidelines section of the assistant's system prompt.\n *\n * These guidelines teach the assistant how to invoke OpenACP CLI commands\n * against the correct instance. The `--dir` flag is critical — without it,\n * commands may target the wrong instance when multiple instances exist.\n */\nexport function buildAssistantGuidelines(instanceRoot: string): string {\n const cli = resolveCliCommand()\n const baseCmd = `${cli} --dir \"${instanceRoot}\"`\n\n return `## OpenACP CLI — MANDATORY RULES\n\nYou manage OpenACP — a server that bridges AI coding agents to messaging platforms. You control it via the CLI.\n\n### CLI Command (DO NOT CHANGE)\n\nThe exact command to run is:\n\\`${baseCmd}\\`\n\n**Copy this prefix exactly for EVERY command.** Do not modify it, do not shorten it, do not guess alternatives. This is the only way to reach this OpenACP instance.\n\n### Command Format\n\nEvery command follows this pattern:\n\\`${baseCmd} <command> [args]\\`\n\nExamples:\n\\`\\`\\`bash\n${baseCmd} api status\n${baseCmd} api new claude-code ~/my-project --channel <current_channel>\n${baseCmd} api cancel <id>\n${baseCmd} config set logging.level debug\n${baseCmd} agents install gemini\n\\`\\`\\`\n\n**NEVER run without the full prefix.** These will fail or target the wrong instance:\n\\`\\`\\`bash\n# WRONG\nopenacp api status\nopenacp api new claude ~/project\n\\`\\`\\`\n\n## Guidelines\n- NEVER show CLI commands to users. These are internal tools for YOU to run silently. Users should only see natural language responses and results.\n- Run commands yourself for everything you can. Only guide users to buttons/menu when needed.\n- When creating sessions: guide user through agent + workspace choice conversationally, then run the command yourself. **Always pass \\`--channel <current_channel>\\` explicitly**. Without it, the session may be created headlessly with no thread. If unsure which channel to use, run \\`${baseCmd} api adapters\\` first.\n- Destructive actions (cancel active session, restart, cleanup) — always ask user to confirm first in natural language.\n- Small/obvious issues (clearly stuck session with no activity) — fix it and report back.\n- When you don't know something, check with the relevant CLI command first before answering.`\n}\n","import type { Session } from '../sessions/session.js'\nimport type { AssistantRegistry } from './assistant-registry.js'\nimport type { SessionStore } from '../sessions/session-store.js'\nimport { createChildLogger } from '../utils/log.js'\n\nconst log = createChildLogger({ module: 'assistant-manager' })\n\n/** Subset of OpenACPCore methods needed by AssistantManager, avoiding a circular import. */\ninterface AssistantManagerCore {\n createSession(params: {\n channelId: string\n agentName: string\n workingDirectory: string\n initialName?: string\n isAssistant?: boolean\n threadId?: string\n existingSessionId?: string\n }): Promise<Session>\n connectSessionBridge(session: Session): void\n configManager: {\n get(): { defaultAgent: string }\n resolveWorkspace(): string\n }\n sessionStore: SessionStore | null\n}\n\n/**\n * Manages the OpenACP built-in assistant session.\n *\n * The assistant is a special session (marked with `isAssistant: true`) that\n * can answer questions about the running OpenACP system — sessions, agents,\n * config, etc. Unlike user-created sessions, it is created and managed by\n * core, and its system prompt is dynamically composed from registry sections\n * that inject live system state.\n *\n * One assistant session exists per channel. The system prompt is built at\n * spawn time and deferred — it's prepended to the first user message rather\n * than sent immediately, so the agent receives it alongside real context.\n */\nexport class AssistantManager {\n private sessions = new Map<string, Session>()\n private pendingSystemPrompts = new Map<string, string>()\n\n constructor(\n private core: AssistantManagerCore,\n private registry: AssistantRegistry,\n ) {}\n\n /**\n * Returns the assistant session for a channel, creating one if needed.\n *\n * If a persisted assistant session exists in the store, it is reused\n * (same session ID) to preserve conversation history. The system prompt\n * is always rebuilt fresh and deferred until the first user message.\n */\n async getOrSpawn(channelId: string, threadId: string): Promise<Session> {\n const existing = this.core.sessionStore?.findAssistant(channelId)\n const session = await this.core.createSession({\n channelId,\n agentName: this.core.configManager.get().defaultAgent,\n workingDirectory: this.core.configManager.resolveWorkspace(),\n initialName: 'Assistant',\n isAssistant: true,\n threadId,\n existingSessionId: existing?.sessionId,\n })\n this.sessions.set(channelId, session)\n\n // Build the system prompt now but don't send it yet — it will be\n // prepended to the first real user message via consumePendingSystemPrompt()\n const systemPrompt = this.registry.buildSystemPrompt(channelId)\n this.pendingSystemPrompts.set(channelId, systemPrompt)\n log.info(\n { sessionId: session.id, channelId, reused: !!existing },\n existing ? 'Assistant session reused (system prompt deferred)' : 'Assistant spawned (system prompt deferred)',\n )\n\n return session\n }\n\n /** Returns the active assistant session for a channel, or null if none exists. */\n get(channelId: string): Session | null {\n return this.sessions.get(channelId) ?? null\n }\n\n /**\n * Consume and return any pending system prompt for a channel.\n * Should be prepended to the first real user message.\n */\n consumePendingSystemPrompt(channelId: string): string | undefined {\n const prompt = this.pendingSystemPrompts.get(channelId)\n if (prompt) this.pendingSystemPrompts.delete(channelId)\n return prompt\n }\n\n /** Checks whether a given session ID belongs to the built-in assistant. */\n isAssistant(sessionId: string): boolean {\n for (const s of this.sessions.values()) {\n if (s.id === sessionId) return true\n }\n return false\n }\n}\n","import type { AssistantSection } from '../assistant-registry.js'\n\n/**\n * Creates the \"Session Management\" section for the assistant's system prompt.\n *\n * Injects live session counts (active vs total) and instructions for creating\n * new sessions. This is the highest-priority section (priority 10) because\n * session management is the assistant's primary purpose.\n */\nexport function createSessionsSection(core: { sessionManager: { listRecords(): Array<{ status: string }> } }): AssistantSection {\n return {\n id: 'core:sessions',\n title: 'Session Management',\n priority: 10,\n buildContext: () => {\n const records = core.sessionManager.listRecords()\n const active = records.filter((r) => r.status === 'active' || r.status === 'initializing').length\n return (\n `Active sessions: ${active} / ${records.length} total\\n\\n` +\n `To create a session, ask which agent to use and which project directory (workspace) to work in.\\n` +\n `The workspace is the project folder where the agent will read, write, and execute code.\\n\\n` +\n `IMPORTANT: Always pass --channel <current_channel> when creating sessions.\\n` +\n `Omitting --channel creates a headless session with no Telegram thread — the user won't see it.\\n` +\n `If unsure which channels are available, run \"openacp api adapters\" first.`\n )\n },\n commands: [\n { command: 'openacp api status', description: 'List active sessions' },\n { command: 'openacp api adapters', description: 'List registered channel adapters' },\n { command: 'openacp api new <agent> <workspace> --channel <current_channel>', description: 'Create new session (always pass --channel)' },\n { command: 'openacp api cancel <id>', description: 'Cancel session' },\n { command: 'openacp api send <id> \"prompt\"', description: 'Send prompt to session' },\n { command: 'openacp api bypass <id> on|off', description: 'Toggle bypass permissions' },\n ],\n }\n}\n","import type { AssistantSection } from '../assistant-registry.js'\n\n/**\n * Creates the \"Agent Management\" section for the assistant's system prompt.\n *\n * Injects a live snapshot of installed agents, the default agent, and how\n * many more are available in the ACP Registry — so the assistant can answer\n * questions about agents and help users install new ones.\n */\nexport function createAgentsSection(core: {\n agentCatalog: { getInstalledEntries(): Record<string, { name: string }>; getAvailable(): Array<{ installed: boolean }> }\n configManager: { get(): { defaultAgent: string } }\n}): AssistantSection {\n return {\n id: 'core:agents',\n title: 'Agent Management',\n priority: 20,\n buildContext: () => {\n const installed = Object.keys(core.agentCatalog.getInstalledEntries())\n const available = core.agentCatalog.getAvailable().filter((i) => !i.installed).length\n const defaultAgent = core.configManager.get().defaultAgent\n return (\n `Installed agents: ${installed.join(', ')}\\n` +\n `Default agent: ${defaultAgent}\\n` +\n `Available in ACP Registry: ${available} more agents`\n )\n },\n commands: [\n { command: 'openacp agents', description: 'List all agents' },\n { command: 'openacp agents install <name>', description: 'Install agent' },\n { command: 'openacp agents info <name>', description: 'Show agent details' },\n { command: 'openacp agents run <name> -- <args>', description: 'Run agent CLI (for login etc.)' },\n ],\n }\n}\n","import type { AssistantSection } from '../assistant-registry.js'\n\n/**\n * Creates the \"Configuration\" section for the assistant's system prompt.\n *\n * Injects the workspace base path and speech-to-text availability, so the\n * assistant knows the current environment and can guide config changes.\n */\nexport function createConfigSection(core: {\n configManager: { resolveWorkspace(): string }\n lifecycleManager?: { serviceRegistry: { get<T>(name: string): T | undefined } }\n}): AssistantSection {\n return {\n id: 'core:config',\n title: 'Configuration',\n priority: 30,\n buildContext: () => {\n const workspace = core.configManager.resolveWorkspace()\n const speechSvc = core.lifecycleManager?.serviceRegistry.get<{ isSTTAvailable(): boolean }>('speech')\n const sttActive = speechSvc ? speechSvc.isSTTAvailable() : false\n return (\n `Workspace base: ${workspace}\\n` +\n `STT: ${sttActive ? 'configured ✅' : 'Not configured'}`\n )\n },\n commands: [\n { command: 'openacp config', description: 'View config' },\n { command: 'openacp config set <key> <value>', description: 'Update config value' },\n ],\n }\n}\n","import type { AssistantSection } from '../assistant-registry.js'\n\n/**\n * Creates the \"System\" section for the assistant's system prompt.\n *\n * Provides system-level commands (health check, restart, version) and\n * instructs the assistant to always confirm before disruptive actions.\n */\nexport function createSystemSection(): AssistantSection {\n return {\n id: 'core:system',\n title: 'System',\n priority: 40,\n buildContext: () => {\n return 'Always ask for confirmation before restart or update — these are disruptive actions.'\n },\n commands: [\n { command: 'openacp api health', description: 'System health check' },\n { command: 'openacp api restart', description: 'Restart daemon' },\n { command: 'openacp api version', description: 'Show version' },\n { command: 'openacp api topics', description: 'List all topics' },\n { command: 'openacp api cleanup', description: 'Cleanup finished topics' },\n ],\n }\n}\n","import type { AssistantSection } from '../assistant-registry.js'\n\n/**\n * Creates the \"Remote Access\" section for the assistant's system prompt.\n *\n * Describes how to generate one-time remote access links so users can\n * connect to this OpenACP instance from the app or a browser. The section\n * is static (no live state) — it teaches the assistant about available\n * roles, link expiry, and tunnel behavior.\n */\nexport function createRemoteSection(): AssistantSection {\n return {\n id: 'core:remote',\n title: 'Remote Access',\n priority: 35,\n buildContext: () => {\n return (\n `Generate a one-time remote access link so the user can connect to this OpenACP instance from the app or a browser.\\n\\n` +\n `The link contains a short-lived code (expires in 30 minutes, single-use) that exchanges for a long-lived token.\\n\\n` +\n `Roles:\\n` +\n ` admin — full control (default)\\n` +\n ` viewer — read-only access\\n\\n` +\n `The command automatically includes the tunnel URL if a tunnel is active. ` +\n `Without a tunnel, the local link only works on the same machine.\\n\\n` +\n `Always show both the link and QR code when available so the user can choose how to open it.`\n )\n },\n commands: [\n { command: 'openacp remote', description: 'Generate remote access link (admin role, 24h expiry)' },\n { command: 'openacp remote --role viewer', description: 'Generate read-only access link' },\n { command: 'openacp remote --expire 48h', description: 'Generate link with custom expiry' },\n { command: 'openacp remote --no-qr', description: 'Skip QR code output' },\n ],\n }\n}\n","import type { MenuRegistry } from '../menu-registry.js'\n\n/**\n * Register the default menu items that appear in the interactive menu.\n *\n * Items are grouped by function (session, info, config, system, help) and\n * sorted by priority (lower = higher position). Each item maps to either\n * a command (`/sessions`), a delegate prompt (assistant-guided new session),\n * or a callback data string (settings panel).\n */\nexport function registerCoreMenuItems(registry: MenuRegistry): void {\n registry.register({\n id: 'core:new',\n label: '🆕 New Session',\n priority: 10,\n group: 'session',\n action: { type: 'delegate', prompt: 'User wants new session. Guide them through agent and workspace selection.' },\n })\n registry.register({\n id: 'core:sessions',\n label: '📋 Sessions',\n priority: 11,\n group: 'session',\n action: { type: 'command', command: '/sessions' },\n })\n registry.register({\n id: 'core:status',\n label: '📊 Status',\n priority: 20,\n group: 'info',\n action: { type: 'command', command: '/status' },\n })\n registry.register({\n id: 'core:agents',\n label: '🤖 Agents',\n priority: 21,\n group: 'info',\n action: { type: 'command', command: '/agents' },\n })\n registry.register({\n id: 'core:settings',\n label: '⚙️ Settings',\n priority: 30,\n group: 'config',\n action: { type: 'callback', callbackData: 's:settings' },\n })\n registry.register({\n id: 'core:integrate',\n label: '🔗 Integrate',\n priority: 31,\n group: 'config',\n action: { type: 'command', command: '/integrate' },\n })\n registry.register({\n id: 'core:restart',\n label: '🔄 Restart',\n priority: 40,\n group: 'system',\n action: { type: 'command', command: '/restart' },\n })\n registry.register({\n id: 'core:update',\n label: '⬆️ Update',\n priority: 41,\n group: 'system',\n action: { type: 'command', command: '/update' },\n })\n registry.register({\n id: 'core:help',\n label: '❓ Help',\n priority: 50,\n group: 'help',\n action: { type: 'command', command: '/help' },\n })\n registry.register({\n id: 'core:doctor',\n label: '🩺 Doctor',\n priority: 51,\n group: 'help',\n action: { type: 'command', command: '/doctor' },\n })\n}\n","import type { CommandDef, CommandArgs, CommandResponse } from './plugin/types.js'\n\n/**\n * Internal representation of a registered command, extending CommandDef with\n * a `scope` derived from the plugin name for namespace-qualified lookups.\n */\ninterface RegisteredCommand extends CommandDef {\n /** Scope extracted from pluginName, e.g. '@openacp/speech' → 'speech' */\n scope?: string\n}\n\n/**\n * Central command registry with namespace resolution and adapter-specific overrides.\n *\n * Namespace rules:\n * - System commands always own the short name.\n * - Among plugins, the first to register wins the short name.\n * - Every plugin command also gets a qualified name: `scope:name`.\n * - Adapter plugins (@openacp/telegram, @openacp/discord)\n * registering a command that already exists → stored as an override\n * keyed by `channelId:commandName`, used when channelId matches.\n *\n * Note: this registry only handles slash commands (e.g. /new, /session).\n * Callback button routing (inline keyboard buttons) is handled separately\n * at the adapter level using a `c/` prefix — it is not part of command dispatch\n * and does not go through this registry.\n */\nexport class CommandRegistry {\n /** Main registry: short names + qualified names → RegisteredCommand */\n private commands = new Map<string, RegisteredCommand>()\n\n /** Adapter-specific overrides: `channelId:commandName` → RegisteredCommand */\n private overrides = new Map<string, RegisteredCommand>()\n\n private static ADAPTER_SCOPES = new Set(['telegram', 'discord'])\n\n /**\n * Register a command definition.\n * @param def - Command definition\n * @param pluginName - Plugin that owns the command (set automatically by PluginContext)\n */\n register(def: CommandDef, pluginName?: string): void {\n const cmd: RegisteredCommand = {\n ...def,\n pluginName: pluginName ?? def.pluginName,\n }\n\n if (pluginName) {\n cmd.scope = CommandRegistry.extractScope(pluginName)\n }\n\n const qualifiedName = cmd.scope ? `${cmd.scope}:${cmd.name}` : undefined\n\n // Check if this is an adapter plugin overriding an existing command\n if (cmd.scope && CommandRegistry.ADAPTER_SCOPES.has(cmd.scope) && this.commands.has(cmd.name)) {\n // Store as adapter override\n this.overrides.set(`${cmd.scope}:${cmd.name}`, cmd)\n return\n }\n\n // Always register qualified name if available\n if (qualifiedName) {\n this.commands.set(qualifiedName, cmd)\n }\n\n // Short name logic\n if (this.commands.has(cmd.name)) {\n const existing = this.commands.get(cmd.name)!\n // System commands always win the short name\n if (existing.category === 'system') {\n // Plugin gets qualified name only (already registered above)\n return\n }\n // First plugin wins short name; later plugins get qualified only\n return\n }\n\n // No conflict — register short name\n this.commands.set(cmd.name, cmd)\n }\n\n /** Retrieve a command by name (short or qualified). */\n get(name: string): RegisteredCommand | undefined {\n return this.commands.get(name)\n }\n\n /** Remove a command by name (short or qualified). Also removes its qualified name entry. */\n unregister(name: string): void {\n const cmd = this.commands.get(name)\n if (!cmd) return\n this.commands.delete(name)\n if (cmd.scope) {\n // If we deleted by short name, also delete qualified name\n this.commands.delete(`${cmd.scope}:${cmd.name}`)\n // If we deleted by qualified name, only delete short name if it points to the same command\n if (this.commands.get(cmd.name) === cmd) {\n this.commands.delete(cmd.name)\n }\n }\n }\n\n /** Remove all commands registered by a given plugin. */\n unregisterByPlugin(pluginName: string): void {\n const toDelete: string[] = []\n\n for (const [key, cmd] of this.commands) {\n if (cmd.pluginName === pluginName) {\n toDelete.push(key)\n }\n }\n\n for (const key of toDelete) {\n this.commands.delete(key)\n }\n\n // Also remove adapter overrides\n for (const [key, cmd] of this.overrides) {\n if (cmd.pluginName === pluginName) {\n this.overrides.delete(key)\n }\n }\n }\n\n /** Return all unique commands (deduplicated — each command appears once). */\n getAll(): RegisteredCommand[] {\n const seen = new Set<RegisteredCommand>()\n for (const cmd of this.commands.values()) {\n seen.add(cmd)\n }\n return [...seen]\n }\n\n /** Filter commands by category. */\n getByCategory(category: 'system' | 'plugin'): RegisteredCommand[] {\n return this.getAll().filter((cmd) => cmd.category === category)\n }\n\n /**\n * Parse and execute a command string (e.g. \"/greet hello world\").\n *\n * Resolution order:\n * 1. Adapter-specific override (e.g. Telegram's version of /new)\n * 2. Short name or qualified name in the main registry\n *\n * Strips Telegram-style bot mentions (e.g. \"/help@MyBot\" → \"help\").\n * Returns `{ type: 'delegated' }` if the handler returns null/undefined\n * (meaning it handled the response itself, e.g. via assistant).\n */\n async execute(commandString: string, baseArgs: CommandArgs): Promise<CommandResponse> {\n const trimmed = commandString.trim()\n const spaceIdx = trimmed.indexOf(' ')\n const rawCmd = spaceIdx === -1 ? trimmed.slice(1) : trimmed.slice(1, spaceIdx)\n // Strip bot mention suffix: \"/help@MyBot\" → \"help\"\n const cmdName = rawCmd.split(\"@\")[0]\n const rawArgs = spaceIdx === -1 ? '' : trimmed.slice(spaceIdx + 1)\n\n // Adapter-specific overrides take precedence (e.g. Telegram's /new with topic creation)\n const overrideKey = `${baseArgs.channelId}:${cmdName}`\n const override = this.overrides.get(overrideKey)\n\n const cmd = override ?? this.commands.get(cmdName)\n\n if (!cmd) {\n return { type: 'error', message: `Unknown command: /${cmdName}` }\n }\n\n const args: CommandArgs = {\n ...baseArgs,\n raw: rawArgs,\n }\n\n try {\n const result = await cmd.handler(args)\n if (result === undefined || result === null) {\n return { type: 'delegated' }\n }\n return result\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n return { type: 'error', message: `Command /${cmdName} failed: ${message}` }\n }\n }\n\n /** Extract scope from plugin name: '@openacp/speech' → 'speech', 'my-plugin' → 'my-plugin' */\n static extractScope(pluginName: string): string {\n const slashIdx = pluginName.lastIndexOf('/')\n if (slashIdx !== -1) {\n return pluginName.slice(slashIdx + 1)\n }\n return pluginName\n }\n}\n","/**\n * Interactive CLI config editor (`openacp config`).\n *\n * Provides a menu-driven interface for editing global config and per-plugin\n * settings. Operates in two modes:\n * - **file mode**: collects all changes in a `updates` object as the user\n * navigates menus, then flushes the batch to config.json on exit. This\n * accumulate-then-flush approach avoids partial writes if the user cancels\n * midway through editing multiple fields.\n * - **api mode**: sends each section's changes to the running daemon via REST\n * API immediately, enabling hot-reload without restart.\n *\n * Global settings (logging, runMode, defaultAgent) are stored in config.json.\n * Plugin-specific settings (bot tokens, ports) are stored in per-plugin\n * settings files via SettingsManager.\n */\nimport * as path from 'node:path'\nimport * as clack from '@clack/prompts'\nimport type { Config, ConfigManager } from './config.js'\nimport type { SettingsManager } from '../plugin/settings-manager.js'\n\n// Compatibility wrappers — convert @inquirer/prompts API to @clack/prompts\nasync function select<T extends string>(opts: { message: string; choices: Array<{ name: string; value: T; description?: string }>; default?: T }): Promise<T> {\n const result = await clack.select({\n message: opts.message,\n options: opts.choices.map(ch => ({ label: ch.name, value: ch.value, hint: ch.description })) as any,\n initialValue: opts.default,\n })\n if (clack.isCancel(result)) { clack.cancel('Cancelled.'); process.exit(0) }\n return result as T\n}\n\nasync function input(opts: { message: string; default?: string; validate?: (val: string) => string | boolean }): Promise<string> {\n const result = await clack.text({\n message: opts.message,\n initialValue: opts.default,\n validate: opts.validate ? (val) => {\n const r = opts.validate!((val ?? \"\") as string)\n if (r === true || r === undefined) return undefined\n if (typeof r === 'string') return r\n return undefined\n } : undefined,\n })\n if (clack.isCancel(result)) { clack.cancel('Cancelled.'); process.exit(0) }\n return result as string\n}\nimport { installAutoStart, uninstallAutoStart, isAutoStartInstalled, isAutoStartSupported } from '../../cli/autostart.js'\nimport { resolveInstanceId } from '../../cli/resolve-instance-id.js'\nimport { expandHome } from './config.js'\n\n// ANSI color helpers\nconst c = {\n reset: '\\x1b[0m',\n bold: '\\x1b[1m',\n dim: '\\x1b[2m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n}\n\nconst ok = (msg: string) => `${c.green}${c.bold}✓${c.reset} ${c.green}${msg}${c.reset}`\nconst warn = (msg: string) => `${c.yellow}⚠ ${msg}${c.reset}`\nconst dim = (msg: string) => `${c.dim}${msg}${c.reset}`\nconst header = (title: string) => `\\n${c.cyan}${c.bold}[${title}]${c.reset}\\n`\n\ntype ConfigUpdates = Record<string, unknown>\n\n// --- Edit: Telegram ---\n\nasync function editTelegram(config: Config, updates: ConfigUpdates, settingsManager?: SettingsManager): Promise<void> {\n const ps = settingsManager ? await settingsManager.loadSettings('@openacp/telegram') : {}\n const currentToken = (ps.botToken as string) ?? ''\n const currentChatId = (ps.chatId as number) ?? 0\n const currentEnabled = (ps.enabled as boolean) ?? false\n\n console.log(header('Telegram'))\n const tokenDisplay = currentToken.length > 12\n ? currentToken.slice(0, 6) + '...' + currentToken.slice(-6)\n : currentToken || dim('(not set)')\n console.log(` Enabled : ${currentEnabled ? ok('yes') : dim('no')}`)\n console.log(` Bot Token : ${tokenDisplay}`)\n console.log(` Chat ID : ${currentChatId || dim('(not set)')}`)\n console.log('')\n\n while (true) {\n const freshSettings = settingsManager ? await settingsManager.loadSettings('@openacp/telegram') : ps\n const isEnabled = (freshSettings.enabled as boolean) ?? currentEnabled\n\n const choice = await select({\n message: 'Telegram settings:',\n choices: [\n { name: isEnabled ? 'Disable Telegram' : 'Enable Telegram', value: 'toggle' },\n { name: 'Change Bot Token', value: 'token' },\n { name: 'Change Chat ID', value: 'chatid' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (choice === 'toggle') {\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/telegram', { enabled: !isEnabled })\n console.log(!isEnabled ? ok('Telegram enabled') : ok('Telegram disabled'))\n }\n }\n\n if (choice === 'token') {\n const token = await input({\n message: 'New bot token:',\n validate: (val) => val.trim().length > 0 || 'Token cannot be empty',\n })\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/telegram', { botToken: token.trim() })\n console.log(ok('Bot token updated'))\n }\n }\n\n if (choice === 'chatid') {\n const chatId = await input({\n message: 'New chat ID:',\n validate: (val) => !isNaN(Number(val.trim())) || 'Must be a number',\n })\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/telegram', { chatId: Number(chatId.trim()) })\n console.log(ok(`Chat ID set to ${chatId.trim()}`))\n }\n }\n }\n}\n\n// --- Edit: Discord (delegates to plugin's configure()) ---\n\nconst DISCORD_PACKAGE = '@openacp/discord-adapter'\n\nasync function ensureDiscordPlugin(): Promise<any | null> {\n try {\n return await import(DISCORD_PACKAGE)\n } catch {\n const shouldInstall = await select({\n message: `${DISCORD_PACKAGE} is not installed. Install it now?`,\n choices: [\n { name: 'Yes, install now', value: 'yes' },\n { name: 'No, skip', value: 'no' },\n ],\n })\n if (shouldInstall === 'no') {\n console.log(warn(`Install later with: openacp plugin add ${DISCORD_PACKAGE}`))\n return null\n }\n try {\n console.log(dim(`Installing ${DISCORD_PACKAGE}...`))\n const { installNpmPlugin } = await import('../plugin/plugin-installer.js')\n const mod = await installNpmPlugin(DISCORD_PACKAGE)\n console.log(ok(`${DISCORD_PACKAGE} installed`))\n return mod\n } catch (err) {\n console.log(warn(`Failed to install: ${(err as Error).message}`))\n return null\n }\n }\n}\n\nasync function editDiscord(_config: Config, _updates: ConfigUpdates, settingsManager?: SettingsManager, instanceRoot?: string): Promise<void> {\n const pluginModule = await ensureDiscordPlugin()\n if (!pluginModule) return\n\n const plugin = pluginModule.default\n if (plugin?.configure) {\n if (!settingsManager || !instanceRoot) {\n console.log(warn('Cannot configure Discord — instance context not available.'))\n return\n }\n const { createInstallContext } = await import('../plugin/install-context.js')\n const ctx = createInstallContext({\n pluginName: plugin.name,\n settingsManager,\n basePath: settingsManager.getBasePath(),\n instanceRoot,\n })\n await plugin.configure(ctx)\n } else {\n console.log(warn('This plugin does not have a configure() method yet.'))\n }\n}\n\n// --- Edit: Channels (parent menu) ---\n\nasync function editChannels(config: Config, updates: ConfigUpdates, settingsManager?: SettingsManager, instanceRoot?: string): Promise<void> {\n // channels migrated out of config.json — read from plugin settings only\n let tgConfigured = false\n let dcConfigured = false\n\n if (settingsManager) {\n const tgPs = await settingsManager.loadSettings('@openacp/telegram')\n if (tgPs.botToken && tgPs.chatId) tgConfigured = true\n\n const dcPs = await settingsManager.loadSettings('@openacp/discord-adapter')\n if (dcPs.guildId || dcPs.token) dcConfigured = true\n }\n\n console.log(header('Channels'))\n console.log(` Telegram : ${tgConfigured ? ok('configured') : dim('not configured')}`)\n console.log(` Discord : ${dcConfigured ? ok('configured') : dim('not configured')}`)\n console.log('')\n\n while (true) {\n const choice = await select({\n message: 'Channel settings:',\n choices: [\n { name: 'Telegram', value: 'telegram' },\n { name: 'Discord', value: 'discord' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (choice === 'telegram') await editTelegram(config, updates, settingsManager)\n if (choice === 'discord') await editDiscord(config, updates, settingsManager, instanceRoot)\n }\n}\n\n// --- Edit: Agent ---\n\nasync function editAgent(config: Config, updates: ConfigUpdates): Promise<void> {\n // agents migrated out of config.json — read from agents.json\n const agentNames: string[] = []\n const currentDefault = config.defaultAgent\n\n console.log(header('Agent'))\n console.log(` Default agent : ${c.bold}${currentDefault}${c.reset}`)\n console.log(` Available : ${agentNames.join(', ') || dim('(none)')}`)\n console.log('')\n\n while (true) {\n const choice = await select({\n message: 'Agent settings:',\n choices: [\n { name: 'Change default agent', value: 'default' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (choice === 'default') {\n if (agentNames.length === 0) {\n console.log(warn('No agents configured.'))\n continue\n }\n\n const chosen = await select({\n message: 'Select default agent:',\n choices: agentNames.map((name) => ({ name, value: name })),\n })\n\n updates.defaultAgent = chosen\n console.log(ok(`Default agent set to ${chosen}`))\n }\n }\n}\n\n// --- Edit: Security ---\n\nasync function editSecurity(config: Config, updates: ConfigUpdates, settingsManager?: SettingsManager): Promise<void> {\n const ps = settingsManager ? await settingsManager.loadSettings('@openacp/security') : {}\n const sec = {\n allowedUserIds: (ps.allowedUserIds as string[]) ?? [],\n maxConcurrentSessions: (ps.maxConcurrentSessions as number) ?? 20,\n sessionTimeoutMinutes: (ps.sessionTimeoutMinutes as number) ?? 60,\n }\n\n console.log(header('Security'))\n console.log(` Allowed user IDs : ${sec.allowedUserIds?.length ? sec.allowedUserIds.join(', ') : dim('(all users allowed)')}`)\n console.log(` Max concurrent sessions : ${sec.maxConcurrentSessions}`)\n console.log(` Session timeout (min) : ${sec.sessionTimeoutMinutes}`)\n console.log('')\n\n while (true) {\n const choice = await select({\n message: 'Security settings:',\n choices: [\n { name: 'Max concurrent sessions', value: 'maxSessions' },\n { name: 'Session timeout (minutes)', value: 'timeout' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (choice === 'maxSessions') {\n const val = await input({\n message: 'Max concurrent sessions:',\n default: String(sec.maxConcurrentSessions),\n validate: (v) => {\n const n = Number(v.trim())\n if (!Number.isInteger(n) || n < 1) return 'Must be a positive integer'\n return true\n },\n })\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/security', { maxConcurrentSessions: Number(val.trim()) })\n }\n console.log(ok(`Max concurrent sessions set to ${val.trim()}`))\n }\n\n if (choice === 'timeout') {\n const val = await input({\n message: 'Session timeout in minutes:',\n default: String(sec.sessionTimeoutMinutes),\n validate: (v) => {\n const n = Number(v.trim())\n if (!Number.isInteger(n) || n < 1) return 'Must be a positive integer'\n return true\n },\n })\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/security', { sessionTimeoutMinutes: Number(val.trim()) })\n }\n console.log(ok(`Session timeout set to ${val.trim()} minutes`))\n }\n }\n}\n\n// --- Edit: Logging ---\n\nasync function editLogging(config: Config, updates: ConfigUpdates): Promise<void> {\n const logging = config.logging ?? { level: 'info', logDir: '~/.openacp/logs', maxFileSize: '10m', maxFiles: 7, sessionLogRetentionDays: 30 }\n\n console.log(header('Logging'))\n console.log(` Log level : ${logging.level}`)\n console.log(` Log dir : ${logging.logDir}`)\n console.log('')\n\n while (true) {\n const choice = await select({\n message: 'Logging settings:',\n choices: [\n { name: 'Log level', value: 'level' },\n { name: 'Log directory', value: 'logDir' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (choice === 'level') {\n const level = await select({\n message: 'Select log level:',\n choices: [\n { name: 'silent', value: 'silent' },\n { name: 'debug', value: 'debug' },\n { name: 'info', value: 'info' },\n { name: 'warn', value: 'warn' },\n { name: 'error', value: 'error' },\n { name: 'fatal', value: 'fatal' },\n ],\n })\n\n if (!updates.logging) updates.logging = {}\n ;(updates.logging as Record<string, unknown>).level = level\n console.log(ok(`Log level set to ${level}`))\n }\n\n if (choice === 'logDir') {\n const dir = await input({\n message: 'Log directory:',\n default: logging.logDir,\n validate: (val) => val.trim().length > 0 || 'Path cannot be empty',\n })\n\n if (!updates.logging) updates.logging = {}\n ;(updates.logging as Record<string, unknown>).logDir = dir.trim()\n console.log(ok(`Log directory set to ${dir.trim()}`))\n }\n }\n}\n\n// --- Edit: Run Mode ---\n\nasync function editRunMode(config: Config, updates: ConfigUpdates, instanceRoot?: string): Promise<void> {\n const currentMode = config.runMode ?? 'foreground'\n const currentAutoStart = config.autoStart ?? false\n const instanceId = instanceRoot ? resolveInstanceId(instanceRoot) : 'default'\n const autoStartInstalled = isAutoStartInstalled(instanceId)\n const autoStartSupported = isAutoStartSupported()\n\n console.log(header('Run Mode'))\n console.log(` Current mode : ${c.bold}${currentMode}${c.reset}`)\n console.log(` Auto-start : ${currentAutoStart ? ok('enabled') : dim('disabled')}${autoStartInstalled ? ` ${dim('(installed)')}` : ''}`)\n console.log('')\n\n while (true) {\n const isDaemon = (() => {\n if ('runMode' in updates) return updates.runMode === 'daemon'\n return currentMode === 'daemon'\n })()\n\n const choices = [\n isDaemon\n ? { name: 'Switch to foreground mode', value: 'foreground' }\n : { name: 'Switch to daemon mode', value: 'daemon' },\n ]\n\n if (autoStartSupported) {\n const autoStartCurrent = (() => {\n if ('autoStart' in updates) return updates.autoStart as boolean\n return currentAutoStart\n })()\n choices.push({\n name: autoStartCurrent ? 'Disable auto-start' : 'Enable auto-start',\n value: 'toggleAutoStart',\n })\n }\n\n choices.push({ name: 'Back', value: 'back' })\n\n const choice = await select({\n message: 'Run mode settings:',\n choices,\n })\n\n if (choice === 'back') break\n\n if (choice === 'daemon') {\n updates.runMode = 'daemon'\n const logDir = (config.logging?.logDir) ?? '~/.openacp/logs'\n const result = installAutoStart(expandHome(logDir), instanceRoot!, instanceId)\n if (result.success) {\n updates.autoStart = true\n console.log(ok('Switched to daemon mode with auto-start'))\n } else {\n console.log(warn(`Switched to daemon mode (auto-start failed: ${result.error})`))\n }\n }\n\n if (choice === 'foreground') {\n updates.runMode = 'foreground'\n updates.autoStart = false\n uninstallAutoStart(instanceId)\n console.log(ok('Switched to foreground mode'))\n }\n\n if (choice === 'toggleAutoStart') {\n const autoStartCurrent = (() => {\n if ('autoStart' in updates) return updates.autoStart as boolean\n return currentAutoStart\n })()\n\n if (autoStartCurrent) {\n const result = uninstallAutoStart(instanceId)\n updates.autoStart = false\n if (result.success) {\n console.log(ok('Auto-start disabled'))\n } else {\n console.log(warn(`Auto-start uninstall failed: ${result.error}`))\n }\n } else {\n const logDir = (config.logging?.logDir) ?? '~/.openacp/logs'\n const result = installAutoStart(expandHome(logDir), instanceRoot!, instanceId)\n updates.autoStart = result.success\n if (result.success) {\n console.log(ok('Auto-start enabled'))\n } else {\n console.log(warn(`Auto-start install failed: ${result.error}`))\n }\n }\n }\n }\n}\n\n// --- Edit: API ---\n\nasync function editApi(config: Config, updates: ConfigUpdates, settingsManager?: SettingsManager): Promise<void> {\n const ps = settingsManager ? await settingsManager.loadSettings('@openacp/api-server') : {}\n const currentPort = (ps.port as number) ?? 21420\n const currentHost = (ps.host as string) ?? '127.0.0.1'\n\n console.log(header('API'))\n console.log(` Port : ${currentPort}`)\n console.log(` Host : ${currentHost} ${dim('(localhost only)')}`)\n console.log('')\n\n const newPort = await input({\n message: 'API port:',\n default: String(currentPort),\n validate: (v) => {\n const n = Number(v.trim())\n if (!Number.isInteger(n) || n < 1 || n > 65535) return 'Must be a valid port (1-65535)'\n return true\n },\n })\n\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/api-server', { port: Number(newPort.trim()) })\n }\n console.log(ok(`API port set to ${newPort.trim()}`))\n}\n\n// --- Edit: Tunnel ---\n\nasync function editTunnel(config: Config, updates: ConfigUpdates, settingsManager?: SettingsManager): Promise<void> {\n const ps = settingsManager ? await settingsManager.loadSettings('@openacp/tunnel') : {}\n const tunnel = {\n enabled: (ps.enabled as boolean) ?? false,\n port: (ps.port as number) ?? 3100,\n provider: (ps.provider as string) ?? 'openacp',\n options: (ps.options as Record<string, unknown>) ?? {},\n storeTtlMinutes: (ps.storeTtlMinutes as number) ?? 60,\n auth: (ps.auth as { enabled: boolean; token?: string }) ?? { enabled: false },\n }\n\n // Local display state (not persisted to config.json — tunnel removed from schema)\n const tun: Record<string, unknown> = { ...tunnel }\n\n const getVal = <T>(key: string, fallback: T): T =>\n (key in tun ? tun[key] : (tunnel as Record<string, unknown>)[key] ?? fallback) as T\n\n console.log(header('Tunnel'))\n console.log(` Enabled : ${getVal('enabled', false) ? ok('yes') : dim('no')}`)\n console.log(` Provider : ${getVal('provider', 'openacp')}`)\n console.log(` Port : ${getVal('port', 3100)}`)\n const authEnabled = (getVal('auth', { enabled: false }) as { enabled: boolean }).enabled\n console.log(` Auth : ${authEnabled ? ok('enabled') : dim('disabled')}`)\n console.log('')\n\n while (true) {\n const choice = await select({\n message: 'Tunnel settings:',\n choices: [\n { name: getVal('enabled', false) ? 'Disable tunnel' : 'Enable tunnel', value: 'toggle' },\n { name: 'Change provider', value: 'provider' },\n { name: 'Change port', value: 'port' },\n { name: 'Provider options', value: 'options' },\n { name: authEnabled ? 'Disable auth' : 'Enable auth', value: 'auth' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (choice === 'toggle') {\n const current = getVal('enabled', false)\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/tunnel', { enabled: !current })\n }\n tun.enabled = !current\n console.log(!current ? ok('Tunnel enabled') : ok('Tunnel disabled'))\n }\n\n if (choice === 'provider') {\n const provider = await select({\n message: 'Select tunnel provider:',\n choices: [\n { name: 'OpenACP (managed)', value: 'openacp' },\n { name: 'Cloudflare', value: 'cloudflare' },\n { name: 'ngrok', value: 'ngrok' },\n { name: 'bore', value: 'bore' },\n { name: 'Tailscale Funnel', value: 'tailscale' },\n ],\n })\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/tunnel', { provider, options: {} })\n }\n tun.provider = provider\n tun.options = {}\n console.log(ok(`Provider set to ${provider}`))\n }\n\n if (choice === 'port') {\n const val = await input({\n message: 'Tunnel port:',\n default: String(getVal('port', 3100)),\n validate: (v) => {\n const n = Number(v.trim())\n if (!Number.isInteger(n) || n < 1 || n > 65535) return 'Must be a valid port (1-65535)'\n return true\n },\n })\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/tunnel', { port: Number(val.trim()) })\n }\n tun.port = Number(val.trim())\n console.log(ok(`Tunnel port set to ${val.trim()}`))\n }\n\n if (choice === 'options') {\n const provider = getVal('provider', 'openacp')\n const currentOptions = getVal('options', {}) as Record<string, unknown>\n await editProviderOptions(provider, currentOptions, tun)\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/tunnel', { options: tun.options })\n }\n }\n\n if (choice === 'auth') {\n const currentAuth = getVal('auth', { enabled: false }) as { enabled: boolean; token?: string }\n if (currentAuth.enabled) {\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/tunnel', { auth: { enabled: false } })\n }\n tun.auth = { enabled: false }\n console.log(ok('Tunnel auth disabled'))\n } else {\n const token = await input({\n message: 'Auth token (leave empty to auto-generate):',\n default: '',\n })\n const newAuth = token.trim() ? { enabled: true, token: token.trim() } : { enabled: true }\n if (settingsManager) {\n await settingsManager.updatePluginSettings('@openacp/tunnel', { auth: newAuth })\n }\n tun.auth = newAuth\n console.log(ok('Tunnel auth enabled'))\n }\n }\n }\n}\n\nasync function editProviderOptions(\n provider: string,\n currentOptions: Record<string, unknown>,\n tun: Record<string, unknown>,\n): Promise<void> {\n if (provider === 'cloudflare') {\n const domain = await input({\n message: 'Custom domain (leave empty for random):',\n default: (currentOptions.domain as string) ?? '',\n })\n tun.options = domain.trim() ? { domain: domain.trim() } : {}\n if (domain.trim()) console.log(ok(`Domain set to ${domain.trim()}`))\n else console.log(dim('Using random cloudflare domain'))\n } else if (provider === 'ngrok') {\n const authtoken = await input({\n message: 'ngrok authtoken (leave empty to skip):',\n default: (currentOptions.authtoken as string) ?? '',\n })\n const domain = await input({\n message: 'ngrok domain (leave empty for random):',\n default: (currentOptions.domain as string) ?? '',\n })\n const region = await input({\n message: 'ngrok region (us, eu, ap — leave empty for default):',\n default: (currentOptions.region as string) ?? '',\n })\n const opts: Record<string, string> = {}\n if (authtoken.trim()) opts.authtoken = authtoken.trim()\n if (domain.trim()) opts.domain = domain.trim()\n if (region.trim()) opts.region = region.trim()\n tun.options = opts\n console.log(ok('ngrok options saved'))\n } else if (provider === 'bore') {\n const server = await input({\n message: 'bore server:',\n default: (currentOptions.server as string) ?? 'bore.pub',\n })\n const port = await input({\n message: 'bore port (leave empty for auto):',\n default: currentOptions.port ? String(currentOptions.port) : '',\n })\n const secret = await input({\n message: 'bore secret (leave empty to skip):',\n default: (currentOptions.secret as string) ?? '',\n })\n const opts: Record<string, unknown> = { server: server.trim() }\n if (port.trim()) opts.port = Number(port.trim())\n if (secret.trim()) opts.secret = secret.trim()\n tun.options = opts\n console.log(ok('bore options saved'))\n } else if (provider === 'tailscale') {\n const bg = await select({\n message: 'Run Tailscale Funnel in background?',\n choices: [\n { name: 'No', value: 'no' },\n { name: 'Yes', value: 'yes' },\n ],\n })\n tun.options = bg === 'yes' ? { bg: true } : {}\n console.log(ok('Tailscale options saved'))\n } else {\n console.log(dim(`No configurable options for provider \"${provider}\"`))\n }\n}\n\n// --- Main Config Editor ---\n\n/**\n * Launches the interactive config editor.\n *\n * In `file` mode, changes accumulate and are written to disk on exit.\n * In `api` mode, each section's changes are sent to the running daemon\n * via the REST API, enabling hot-reload without restart.\n */\nexport async function runConfigEditor(\n configManager: ConfigManager,\n mode: 'file' | 'api' = 'file',\n apiPort?: number,\n settingsManager?: SettingsManager,\n): Promise<void> {\n await configManager.load()\n const config = configManager.get()\n const updates: ConfigUpdates = {}\n\n // Derive instance root from config path: /x/y/.openacp/config.json → /x/y/.openacp\n const instanceRoot = path.dirname(configManager.getConfigPath())\n\n console.log(`\\n${c.cyan}${c.bold}OpenACP Config Editor${c.reset}`)\n console.log(dim(`Config: ${configManager.getConfigPath()}`))\n console.log('')\n\n try {\n while (true) {\n const hasChanges = mode === 'file' ? Object.keys(updates).length > 0 : false\n const choice = await select({\n message: `What would you like to edit?${hasChanges ? ` ${c.yellow}(unsaved changes)${c.reset}` : ''}`,\n choices: [\n { name: 'Channels', value: 'channels' },\n { name: 'Agent', value: 'agent' },\n { name: 'Security', value: 'security' },\n { name: 'Logging', value: 'logging' },\n { name: 'Run Mode', value: 'runMode' },\n { name: 'API', value: 'api' },\n { name: 'Tunnel', value: 'tunnel' },\n { name: hasChanges ? 'Save & Exit' : 'Exit', value: 'exit' },\n ],\n })\n\n if (choice === 'exit') {\n if (mode === 'file' && hasChanges) {\n await configManager.save(updates)\n console.log(ok(`Config saved to ${configManager.getConfigPath()}`))\n } else if (mode === 'file') {\n console.log(dim('No changes made.'))\n }\n break\n }\n\n const sectionUpdates: ConfigUpdates = {}\n\n if (choice === 'channels') await editChannels(config, sectionUpdates, settingsManager, instanceRoot)\n else if (choice === 'agent') await editAgent(config, sectionUpdates)\n else if (choice === 'security') await editSecurity(config, sectionUpdates, settingsManager)\n else if (choice === 'logging') await editLogging(config, sectionUpdates)\n else if (choice === 'runMode') await editRunMode(config, sectionUpdates, instanceRoot)\n else if (choice === 'api') await editApi(config, sectionUpdates, settingsManager)\n else if (choice === 'tunnel') await editTunnel(config, sectionUpdates, settingsManager)\n\n if (mode === 'api' && Object.keys(sectionUpdates).length > 0) {\n await sendConfigViaApi(apiPort!, sectionUpdates)\n // Refresh in-memory config\n await configManager.load()\n Object.assign(config, configManager.get())\n } else {\n // Accumulate for file mode\n Object.assign(updates, sectionUpdates)\n }\n }\n } catch (err) {\n if ((err as Error).name === 'ExitPromptError') {\n console.log(dim('\\nConfig editor cancelled. Changes discarded.'))\n return\n }\n throw err\n }\n}\n\n/** Sends config updates to a running daemon via the REST API, one field path at a time. */\nasync function sendConfigViaApi(port: number, updates: ConfigUpdates): Promise<void> {\n const { apiCall: call } = await import('../../cli/api-client.js')\n\n const paths = flattenToPaths(updates)\n for (const { path, value } of paths) {\n const res = await call(port, '/api/config', {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ path, value }),\n })\n const data = await res.json() as Record<string, unknown>\n if (!res.ok) {\n console.log(warn(`Failed to update ${path}: ${data.error}`))\n } else if (data.needsRestart) {\n console.log(warn(`${path} updated — restart required`))\n }\n }\n}\n\n/** Flattens a nested object into dot-path/value pairs for the config PATCH API. */\nfunction flattenToPaths(obj: Record<string, unknown>, prefix = ''): Array<{ path: string; value: unknown }> {\n const result: Array<{ path: string; value: unknown }> = []\n for (const [key, val] of Object.entries(obj)) {\n const fullPath = prefix ? `${prefix}.${key}` : key\n if (val && typeof val === 'object' && !Array.isArray(val)) {\n result.push(...flattenToPaths(val as Record<string, unknown>, fullPath))\n } else {\n result.push({ path: fullPath, value: val })\n }\n }\n return result\n}\n","import { execFileSync } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\nimport { createChildLogger } from '../core/utils/log.js'\n\n// Autostart integrates with the OS process supervisor so the daemon restarts after login.\n// macOS uses launchd (LaunchAgents), Linux uses systemd user units.\n// Each instance gets its own service, identified by instanceId, so multiple instances\n// can coexist without conflicting plist/unit names.\n\nconst log = createChildLogger({ module: 'autostart' })\n\n// Legacy paths — no instanceId, used for migration only\nconst LEGACY_LAUNCHD_PLIST_PATH = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.openacp.daemon.plist')\nconst LEGACY_SYSTEMD_SERVICE_PATH = path.join(os.homedir(), '.config', 'systemd', 'user', 'openacp.service')\n\nfunction getLaunchdLabel(instanceId: string): string {\n return `com.openacp.daemon.${instanceId}`\n}\n\nfunction getLaunchdPlistPath(instanceId: string): string {\n return path.join(os.homedir(), 'Library', 'LaunchAgents', `${getLaunchdLabel(instanceId)}.plist`)\n}\n\nfunction getSystemdServiceName(instanceId: string): string {\n return `openacp-${instanceId}`\n}\n\nfunction getSystemdServicePath(instanceId: string): string {\n return path.join(os.homedir(), '.config', 'systemd', 'user', `${getSystemdServiceName(instanceId)}.service`)\n}\n\n/** Returns true if the current platform supports auto-start management. */\nexport function isAutoStartSupported(): boolean {\n return process.platform === 'darwin' || process.platform === 'linux'\n}\n\n/**\n * Escape a string for safe embedding in an XML plist value.\n * launchd plists are XML — unescaped special chars cause parse failures.\n */\nexport function escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n}\n\n/**\n * Escape a string for safe use as a quoted value in a systemd unit file.\n * Systemd uses its own escaping rules: backslash, quotes, `$` (env expand), and `%` (specifiers).\n */\nexport function escapeSystemdValue(str: string): string {\n const escaped = str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\$/g, '$$$$')\n .replace(/%/g, '%%')\n return `\"${escaped}\"`\n}\n\n/**\n * Generate a launchd plist for the given instance.\n *\n * The plist starts the daemon via `node <cli> --daemon-child`, with OPENACP_INSTANCE_ROOT\n * set so the child knows which instance to load. KeepAlive.SuccessfulExit=false means\n * launchd restarts on crash but not on clean exit (e.g. `openacp stop`).\n */\nexport function generateLaunchdPlist(nodePath: string, cliPath: string, logDir: string, instanceRoot: string, instanceId: string): string {\n const label = getLaunchdLabel(instanceId)\n const logFile = path.join(logDir, 'openacp.log')\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${label}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${escapeXml(nodePath)}</string>\n <string>${escapeXml(cliPath)}</string>\n <string>--daemon-child</string>\n </array>\n <key>EnvironmentVariables</key>\n <dict>\n <key>OPENACP_INSTANCE_ROOT</key>\n <string>${escapeXml(instanceRoot)}</string>\n </dict>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n <key>StandardOutPath</key>\n <string>${escapeXml(logFile)}</string>\n <key>StandardErrorPath</key>\n <string>${escapeXml(logFile)}</string>\n</dict>\n</plist>\n`\n}\n\n/**\n * Generate a systemd user unit for the given instance.\n *\n * `Restart=on-failure` mirrors launchd's KeepAlive.SuccessfulExit=false behavior —\n * the daemon is restarted on crash but not after a clean SIGTERM from `openacp stop`.\n * Uses `WantedBy=default.target` so it activates on user login (not system boot).\n */\nexport function generateSystemdUnit(nodePath: string, cliPath: string, instanceRoot: string, instanceId: string): string {\n const serviceName = getSystemdServiceName(instanceId)\n return `[Unit]\nDescription=OpenACP Daemon (${instanceId})\n\n[Service]\nExecStart=${escapeSystemdValue(nodePath)} ${escapeSystemdValue(cliPath)} --daemon-child\nEnvironment=OPENACP_INSTANCE_ROOT=${escapeSystemdValue(instanceRoot)}\nRestart=on-failure\n\n[Install]\nWantedBy=default.target\n# Service name: ${serviceName}\n`\n}\n\n/** Remove legacy single-instance plist/service if it exists (one-time migration). */\nfunction migrateLegacy(): void {\n if (process.platform === 'darwin' && fs.existsSync(LEGACY_LAUNCHD_PLIST_PATH)) {\n try {\n const uid = process.getuid!()\n execFileSync('launchctl', ['bootout', `gui/${uid}`, 'com.openacp.daemon'], { stdio: 'pipe' })\n } catch { /* already unloaded */ }\n try { fs.unlinkSync(LEGACY_LAUNCHD_PLIST_PATH) } catch { /* already gone */ }\n log.info('Removed legacy single-instance LaunchAgent')\n }\n if (process.platform === 'linux' && fs.existsSync(LEGACY_SYSTEMD_SERVICE_PATH)) {\n try { execFileSync('systemctl', ['--user', 'disable', 'openacp'], { stdio: 'pipe' }) } catch { /* ignore */ }\n try { fs.unlinkSync(LEGACY_SYSTEMD_SERVICE_PATH) } catch { /* already gone */ }\n try { execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'pipe' }) } catch { /* ignore */ }\n log.info('Removed legacy single-instance systemd service')\n }\n}\n\n/**\n * Register the daemon as a login-time service for the given instance.\n *\n * macOS: writes a LaunchAgent plist and bootstraps it into the user's GUI session.\n * Linux: writes a systemd user unit, reloads the daemon, and enables the service.\n * Runs `migrateLegacy()` first to remove the old single-instance service if present.\n */\nexport function installAutoStart(logDir: string, instanceRoot: string, instanceId: string): { success: boolean; error?: string } {\n if (!isAutoStartSupported()) {\n return { success: false, error: 'Auto-start not supported on this platform' }\n }\n\n const nodePath = process.execPath\n const cliPath = path.resolve(process.argv[1])\n const resolvedLogDir = logDir.startsWith('~')\n ? path.join(os.homedir(), logDir.slice(1))\n : logDir\n\n try {\n migrateLegacy()\n\n if (process.platform === 'darwin') {\n const plistPath = getLaunchdPlistPath(instanceId)\n const plist = generateLaunchdPlist(nodePath, cliPath, resolvedLogDir, instanceRoot, instanceId)\n const dir = path.dirname(plistPath)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(plistPath, plist)\n const uid = process.getuid!()\n const domain = `gui/${uid}`\n // Bootout first in case it's already loaded (e.g. restart scenario); ignore error if not loaded\n try { execFileSync('launchctl', ['bootout', domain, plistPath], { stdio: 'pipe' }) } catch { /* not yet loaded */ }\n execFileSync('launchctl', ['bootstrap', domain, plistPath], { stdio: 'pipe' })\n log.info({ instanceId }, 'LaunchAgent installed')\n return { success: true }\n }\n\n if (process.platform === 'linux') {\n const servicePath = getSystemdServicePath(instanceId)\n const serviceName = getSystemdServiceName(instanceId)\n const unit = generateSystemdUnit(nodePath, cliPath, instanceRoot, instanceId)\n const dir = path.dirname(servicePath)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(servicePath, unit)\n execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'pipe' })\n execFileSync('systemctl', ['--user', 'enable', serviceName], { stdio: 'pipe' })\n log.info({ instanceId }, 'systemd user service installed')\n return { success: true }\n }\n\n return { success: false, error: 'Unsupported platform' }\n } catch (e) {\n const msg = (e as Error).message\n log.error({ err: msg }, 'Failed to install auto-start')\n return { success: false, error: msg }\n }\n}\n\n/**\n * Remove the login-time service registration for the given instance.\n * No-op if the service is not installed.\n */\nexport function uninstallAutoStart(instanceId: string): { success: boolean; error?: string } {\n if (!isAutoStartSupported()) {\n return { success: false, error: 'Auto-start not supported on this platform' }\n }\n\n try {\n if (process.platform === 'darwin') {\n const plistPath = getLaunchdPlistPath(instanceId)\n if (fs.existsSync(plistPath)) {\n const uid = process.getuid!()\n try { execFileSync('launchctl', ['bootout', `gui/${uid}`, plistPath], { stdio: 'pipe' }) } catch { /* already unloaded */ }\n fs.unlinkSync(plistPath)\n log.info({ instanceId }, 'LaunchAgent removed')\n }\n return { success: true }\n }\n\n if (process.platform === 'linux') {\n const servicePath = getSystemdServicePath(instanceId)\n const serviceName = getSystemdServiceName(instanceId)\n if (fs.existsSync(servicePath)) {\n try { execFileSync('systemctl', ['--user', 'disable', serviceName], { stdio: 'pipe' }) } catch { /* already disabled */ }\n fs.unlinkSync(servicePath)\n execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'pipe' })\n log.info({ instanceId }, 'systemd user service removed')\n }\n return { success: true }\n }\n\n return { success: false, error: 'Unsupported platform' }\n } catch (e) {\n const msg = (e as Error).message\n log.error({ err: msg }, 'Failed to uninstall auto-start')\n return { success: false, error: msg }\n }\n}\n\n/** Returns true if the login-time service is currently registered for this instance. */\nexport function isAutoStartInstalled(instanceId: string): boolean {\n if (process.platform === 'darwin') {\n return fs.existsSync(getLaunchdPlistPath(instanceId))\n }\n if (process.platform === 'linux') {\n return fs.existsSync(getSystemdServicePath(instanceId))\n }\n return false\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { getGlobalRoot } from '../core/instance/instance-context.js'\nimport { InstanceRegistry } from '../core/instance/instance-registry.js'\nimport { createChildLogger } from '../core/utils/log.js'\n\nconst log = createChildLogger({ module: 'resolve-instance-id' })\n\n/**\n * Resolve the stable instance ID for a given instance root.\n *\n * 1. Read from config.json (preferred — instance knows its own UUID)\n * 2. Fall back to registry for backward compatibility\n * 3. Last resort: sanitized parent directory name\n */\nexport function resolveInstanceId(instanceRoot: string): string {\n // 1. Read id from config.json (preferred — instance knows its own UUID)\n try {\n const configPath = path.join(instanceRoot, 'config.json')\n const raw = JSON.parse(fs.readFileSync(configPath, 'utf-8'))\n if (raw.id && typeof raw.id === 'string') return raw.id\n } catch { /* fall through */ }\n\n // 2. Fall back to registry (backward compat for instances that haven't migrated yet)\n try {\n const reg = new InstanceRegistry(path.join(getGlobalRoot(), 'instances.json'))\n reg.load()\n const entry = reg.getByRoot(instanceRoot)\n if (entry?.id) return entry.id\n } catch (err) {\n log.debug({ err: (err as Error).message, instanceRoot }, 'Could not read instance registry, using fallback id')\n }\n\n // 3. Last resort: sanitized parent dir name\n return path.basename(path.dirname(instanceRoot)).replace(/[^a-zA-Z0-9-]/g, '-') || 'default'\n}\n","import { spawn } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { expandHome } from '../core/config/config.js'\n\n// Daemon lifecycle utilities.\n// The daemon model runs the server as a detached child process (PID file tracking).\n// This allows `openacp start` to return immediately while the server keeps running\n// independently of the terminal session.\n\n/** Returns the path to the PID file for the given instance root. */\nexport function getPidPath(root: string): string {\n return path.join(root, 'openacp.pid')\n}\n\n/** Returns the default log directory for the given instance root. */\nexport function getLogDir(root: string): string {\n return path.join(root, 'logs')\n}\n\n/**\n * Returns the path to the \"running\" marker file.\n * The marker exists while the daemon is intentionally running, so launchd/systemd\n * knows whether to restart it after a crash vs. leaving it stopped after `openacp stop`.\n */\nexport function getRunningMarker(root: string): string {\n return path.join(root, 'running')\n}\n\n/** Write a PID file, creating the parent directory if needed. */\nexport function writePidFile(pidPath: string, pid: number): void {\n const dir = path.dirname(pidPath)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(pidPath, String(pid))\n}\n\n/** Read a PID from a file. Returns null if the file is missing or malformed. */\nexport function readPidFile(pidPath: string): number | null {\n try {\n const content = fs.readFileSync(pidPath, 'utf-8').trim()\n const pid = parseInt(content, 10)\n return isNaN(pid) ? null : pid\n } catch {\n return null\n }\n}\n\n/** Remove the PID file. Silently ignores ENOENT (already gone). */\nexport function removePidFile(pidPath: string): void {\n try {\n fs.unlinkSync(pidPath)\n } catch {\n // ignore if already gone\n }\n}\n\n/**\n * Check whether the process recorded in the PID file is alive.\n *\n * Uses `process.kill(pid, 0)` as a non-destructive liveness probe — it throws\n * ESRCH if the PID doesn't exist, or EPERM if owned by another user.\n * Cleans up stale PID files for dead processes.\n */\nexport function isProcessRunning(pidPath: string): boolean {\n const pid = readPidFile(pidPath)\n if (pid === null) return false\n try {\n process.kill(pid, 0)\n return true\n } catch {\n // Process not running, clean up stale PID file\n removePidFile(pidPath)\n return false\n }\n}\n\n/**\n * Return the running status and PID of the daemon.\n * Cleans up stale PID files if the recorded process is no longer alive.\n */\nexport function getStatus(pidPath: string): { running: boolean; pid?: number } {\n const pid = readPidFile(pidPath)\n if (pid === null) return { running: false }\n try {\n process.kill(pid, 0)\n return { running: true, pid }\n } catch {\n removePidFile(pidPath)\n return { running: false }\n }\n}\n\n/**\n * Fork the daemon as a detached background process.\n *\n * Spawn mechanics:\n * - The child runs `node <cli> --daemon-child` with OPENACP_INSTANCE_ROOT in its env.\n * - `detached: true` creates a new process group so the child survives terminal close.\n * - stdout/stderr are redirected to the log file (append mode); the parent closes its\n * copies immediately after spawn so the child holds the only references.\n * - `child.unref()` removes the child from the parent's event loop reference count,\n * allowing the parent (`openacp start`) to exit while the child keeps running.\n * - The PID file is written by both parent (early report) and child (authoritative write\n * in main.ts startServer) to handle race conditions with launchd/systemd starts.\n */\nexport function startDaemon(pidPath: string, logDir: string | undefined, instanceRoot: string): { pid: number } | { error: string } {\n // Mark as running so auto-start works on next boot\n markRunning(instanceRoot)\n\n // Check if already running\n if (isProcessRunning(pidPath)) {\n const pid = readPidFile(pidPath)!\n return { error: `Already running (PID ${pid})` }\n }\n\n const resolvedLogDir = logDir ? expandHome(logDir) : getLogDir(instanceRoot)\n fs.mkdirSync(resolvedLogDir, { recursive: true })\n const logFile = path.join(resolvedLogDir, 'openacp.log')\n\n // Find the CLI entry point — must be the same binary that invoked this function\n const cliPath = path.resolve(process.argv[1])\n const nodePath = process.execPath\n\n const out = fs.openSync(logFile, 'a')\n const err = fs.openSync(logFile, 'a')\n\n const child = spawn(nodePath, [cliPath, '--daemon-child'], {\n detached: true,\n stdio: ['ignore', out, err],\n env: {\n ...process.env,\n ...(instanceRoot ? { OPENACP_INSTANCE_ROOT: instanceRoot } : {}),\n },\n })\n\n // Close file descriptors in parent — child has its own copies\n fs.closeSync(out)\n fs.closeSync(err)\n\n if (!child.pid) {\n return { error: 'Failed to spawn daemon process' }\n }\n\n // PID file is written by the child process itself (in main.ts startServer)\n // to avoid race conditions and ensure consistency with LaunchAgent/systemd starts.\n // We still write it here as a fallback in case the child hasn't written it yet\n // when the parent needs to report the PID.\n writePidFile(pidPath, child.pid)\n child.unref()\n\n return { pid: child.pid }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\n/**\n * Three-way liveness probe: alive, dead (ESRCH), or EPERM.\n * EPERM means the PID exists but belongs to a different user — the PID file was\n * likely reused by the OS for an unrelated process after the daemon exited.\n */\nfunction isProcessAlive(pid: number): 'alive' | 'dead' | 'eperm' {\n try {\n process.kill(pid, 0)\n return 'alive'\n } catch (e) {\n const err = e as NodeJS.ErrnoException\n if (err.code === 'EPERM') return 'eperm'\n return 'dead'\n }\n}\n\n/**\n * Gracefully stop the daemon, escalating to SIGKILL if it doesn't exit.\n *\n * Stop sequence:\n * 1. Send SIGTERM; poll every 100ms for up to 5 seconds.\n * 2. If still alive after 5s, send SIGKILL; poll for up to 1 more second.\n * 3. If still alive after SIGKILL, the process is in uninterruptible I/O — very rare.\n *\n * Also clears the \"running\" marker so the daemon is not restarted on next login.\n */\nexport async function stopDaemon(pidPath: string, instanceRoot: string): Promise<{ stopped: boolean; pid?: number; error?: string }> {\n const pid = readPidFile(pidPath)\n if (pid === null) return { stopped: false, error: 'Not running (no PID file)' }\n\n const status = isProcessAlive(pid)\n if (status === 'dead') {\n removePidFile(pidPath)\n return { stopped: false, error: 'Not running (stale PID file removed)' }\n }\n if (status === 'eperm') {\n removePidFile(pidPath)\n return { stopped: false, error: 'PID belongs to another process (stale PID file removed)' }\n }\n\n try {\n process.kill(pid, 'SIGTERM')\n } catch (e) {\n return { stopped: false, error: `Failed to stop: ${(e as Error).message}` }\n }\n\n clearRunning(instanceRoot)\n\n const POLL_INTERVAL = 100\n const TIMEOUT = 5000\n const start = Date.now()\n\n while (Date.now() - start < TIMEOUT) {\n await sleep(POLL_INTERVAL)\n const s = isProcessAlive(pid)\n if (s === 'dead' || s === 'eperm') {\n removePidFile(pidPath)\n return { stopped: true, pid }\n }\n }\n\n try {\n process.kill(pid, 'SIGKILL')\n } catch (e) {\n const err = e as NodeJS.ErrnoException\n if (err.code === 'EPERM') {\n return { stopped: false, pid, error: 'PID may have been reused by another process. Run `openacp status` to verify, or manually delete the PID file.' }\n }\n }\n\n const killStart = Date.now()\n while (Date.now() - killStart < 1000) {\n await sleep(POLL_INTERVAL)\n const s = isProcessAlive(pid)\n if (s === 'dead' || s === 'eperm') {\n removePidFile(pidPath)\n return { stopped: true, pid }\n }\n }\n\n // SIGKILL sent but process still alive after 1s — extremely rare (uninterruptible I/O).\n return { stopped: false, pid, error: 'Process did not exit after SIGKILL (possible uninterruptible I/O). PID file retained.' }\n}\n\n/** Mark that the daemon should auto-start on boot */\nexport function markRunning(root: string): void {\n const marker = getRunningMarker(root)\n fs.mkdirSync(path.dirname(marker), { recursive: true })\n fs.writeFileSync(marker, '')\n}\n\n/** Remove running marker — daemon won't auto-start on boot */\nexport function clearRunning(root: string): void {\n try { fs.unlinkSync(getRunningMarker(root)) } catch { /* ignore */ }\n}\n\n/** Check if the daemon was running before (should auto-start on boot) */\nexport function shouldAutoStart(root: string): boolean {\n return fs.existsSync(getRunningMarker(root))\n}\n","import type { SessionManager } from '../../core/sessions/session-manager.js'\nimport type { IChannelAdapter } from '../../core/channel.js'\nimport type { SessionRecord } from '../../core/types.js'\nimport { createChildLogger } from '../../core/utils/log.js'\n\nconst log = createChildLogger({ module: 'topic-manager' })\n\n/** Flat view of a session record, enriched with its Telegram topic ID. */\nexport interface TopicInfo {\n sessionId: string\n topicId: number | null\n name: string | null\n status: string\n agentName: string\n lastActiveAt: string\n}\n\n/** Result of a single-topic deletion operation. */\nexport interface DeleteTopicResult {\n ok: boolean\n /** True when deletion requires explicit confirmation (session is still active). */\n needsConfirmation?: boolean\n topicId?: number | null\n session?: { id: string; name: string | null; status: string }\n error?: string\n}\n\n/** Aggregate result for a bulk cleanup operation. */\nexport interface CleanupResult {\n deleted: string[]\n failed: { sessionId: string; error: string }[]\n}\n\n// IDs for the two system-managed topics that must never be deleted by user-facing commands.\ninterface SystemTopicIds {\n notificationTopicId: number | null\n assistantTopicId: number | null\n}\n\n/**\n * High-level topic management for the Telegram adapter.\n *\n * Sits above the raw Telegram API (`topics.ts`) and adds session-level concerns:\n * guarding system topics, requiring confirmation for active sessions, and\n * coordinating with the SessionManager to remove session records after deletion.\n */\nexport class TopicManager {\n constructor(\n private sessionManager: SessionManager,\n private adapter: IChannelAdapter | null,\n private systemTopicIds: SystemTopicIds,\n ) {}\n\n /**\n * List user-facing session topics, excluding system topics.\n * Optionally filtered to specific status values.\n */\n listTopics(filter?: { statuses?: string[] }): TopicInfo[] {\n const records = this.sessionManager.listRecords(filter)\n return records\n .filter(r => !this.isSystemTopic(r))\n .filter(r => !filter?.statuses?.length || filter.statuses.includes(r.status))\n .map(r => ({\n sessionId: r.sessionId,\n topicId: (r.platform as Record<string, unknown>)?.topicId as number ?? null,\n name: r.name ?? null,\n status: r.status,\n agentName: r.agentName,\n lastActiveAt: r.lastActiveAt,\n }))\n }\n\n /**\n * Delete a session topic and its session record.\n *\n * Returns `needsConfirmation: true` when the session is still active and\n * `options.confirmed` was not set — callers must ask the user before proceeding.\n */\n async deleteTopic(sessionId: string, options?: { confirmed?: boolean }): Promise<DeleteTopicResult> {\n const records = this.sessionManager.listRecords()\n const record = records.find(r => r.sessionId === sessionId)\n if (!record) return { ok: false, error: 'Session not found' }\n\n if (this.isSystemTopic(record)) return { ok: false, error: 'Cannot delete system topic' }\n\n const isActive = record.status === 'active' || record.status === 'initializing'\n if (isActive && !options?.confirmed) {\n return {\n ok: false,\n needsConfirmation: true,\n session: { id: record.sessionId, name: record.name ?? null, status: record.status },\n }\n }\n\n if (isActive) {\n await this.sessionManager.cancelSession(sessionId)\n }\n\n const topicId = (record.platform as Record<string, unknown>)?.topicId as number ?? null\n if (this.adapter && topicId) {\n try {\n await this.adapter.deleteSessionThread?.(sessionId)\n } catch (err) {\n log.warn({ err, sessionId, topicId }, 'Failed to delete platform thread, removing record anyway')\n }\n }\n\n await this.sessionManager.removeRecord(sessionId)\n return { ok: true, topicId }\n }\n\n /**\n * Bulk-delete topics by status (default: finished, error, cancelled).\n * Active/initializing sessions are cancelled before deletion to prevent orphaned processes.\n */\n async cleanup(statuses?: string[]): Promise<CleanupResult> {\n const targetStatuses = statuses?.length ? statuses : ['finished', 'error', 'cancelled']\n const records = this.sessionManager.listRecords({ statuses: targetStatuses })\n const targets = records\n .filter(r => !this.isSystemTopic(r))\n .filter(r => targetStatuses.includes(r.status))\n\n const deleted: string[] = []\n const failed: { sessionId: string; error: string }[] = []\n\n for (const record of targets) {\n try {\n // Cancel active/initializing sessions to prevent orphaned agent processes\n const isActive = record.status === 'active' || record.status === 'initializing'\n if (isActive) {\n await this.sessionManager.cancelSession(record.sessionId)\n }\n\n const topicId = (record.platform as Record<string, unknown>)?.topicId as number | undefined\n if (this.adapter && topicId) {\n try {\n await this.adapter.deleteSessionThread?.(record.sessionId)\n } catch (err) {\n log.warn({ err, sessionId: record.sessionId }, 'Failed to delete platform thread during cleanup')\n }\n }\n await this.sessionManager.removeRecord(record.sessionId)\n deleted.push(record.sessionId)\n } catch (err) {\n failed.push({ sessionId: record.sessionId, error: err instanceof Error ? err.message : String(err) })\n }\n }\n\n return { deleted, failed }\n }\n\n private isSystemTopic(record: SessionRecord): boolean {\n const topicId = (record.platform as Record<string, unknown>)?.topicId as number | undefined\n if (!topicId) return false\n return topicId === this.systemTopicIds.notificationTopicId\n || topicId === this.systemTopicIds.assistantTopicId\n }\n}\n","export { MessagingAdapter, type AdapterContext, type MessagingAdapterConfig, type SentMessage } from './messaging-adapter.js'\nexport { StreamAdapter, type StreamEvent } from './stream-adapter.js'\nexport { type IRenderer, BaseRenderer, type RenderedMessage, type RenderedPermission, type RenderedAction } from './rendering/index.js'\nexport { SendQueue, DraftManager, Draft, ToolCallTracker, ActivityTracker } from './primitives/index.js'\nexport type { SendQueueConfig, DraftConfig, TrackedToolCall, ActivityConfig, ActivityCallbacks } from './primitives/index.js'\n\nexport type { DisplayVerbosity, ToolCallMeta, ToolUpdateMeta, FormattedMessage, MessageMetadata, ViewerLinks } from './format-types.js'\nexport { STATUS_ICONS, KIND_ICONS } from './format-types.js'\nexport { progressBar, formatTokens, truncateContent, stripCodeFences, splitMessage } from './format-utils.js'\nexport { extractContentText, formatToolSummary, formatToolTitle, resolveToolIcon } from './message-formatter.js'\n\nexport { ToolStateMap, ThoughtBuffer } from './stream-accumulator.js'\nexport type { ToolEntry } from './stream-accumulator.js'\nexport { DisplaySpecBuilder } from './display-spec-builder.js'\nexport type { ToolDisplaySpec, ThoughtDisplaySpec } from './display-spec-builder.js'\nexport { OutputModeResolver } from './output-mode-resolver.js'\nexport type { OutputMode } from './format-types.js'\nexport { ToolCardState } from './primitives/tool-card-state.js'\nexport type { ToolCardSnapshot, ToolCardStateConfig } from './primitives/tool-card-state.js'\n\n// Note: runAdapterConformanceTests is exported from the separate './testing' entry\n// point to avoid pulling vitest into the main runtime bundle.\n","import type {\n IChannelAdapter,\n AdapterCapabilities,\n} from '../channel.js'\nimport type {\n OutgoingMessage,\n PermissionRequest,\n NotificationMessage,\n} from '../types.js'\n\n/** A structured event emitted over the stream (SSE, WebSocket, etc.). */\nexport interface StreamEvent {\n type: string\n sessionId?: string\n payload: unknown\n timestamp: number\n}\n\n/**\n * Base class for stream-based adapters (SSE, WebSocket) that push events\n * directly to connected clients rather than rendering messages on a platform.\n *\n * Unlike MessagingAdapter (which renders and sends formatted messages),\n * StreamAdapter wraps each outgoing message as a StreamEvent and emits it\n * to all connections watching that session. The client is responsible for\n * rendering.\n */\nexport abstract class StreamAdapter implements IChannelAdapter {\n abstract readonly name: string\n\n capabilities: AdapterCapabilities\n\n constructor(config?: Partial<AdapterCapabilities>) {\n this.capabilities = {\n streaming: true,\n richFormatting: false,\n threads: false,\n reactions: false,\n fileUpload: false,\n voice: false,\n ...config,\n }\n }\n\n /** Wraps the outgoing message as a StreamEvent and emits it to the session's listeners. */\n async sendMessage(sessionId: string, content: OutgoingMessage): Promise<void> {\n await this.emit(sessionId, {\n type: content.type,\n sessionId,\n payload: content,\n timestamp: Date.now(),\n })\n }\n\n /** Emits a permission request event so the client can render approve/deny UI. */\n async sendPermissionRequest(sessionId: string, request: PermissionRequest): Promise<void> {\n await this.emit(sessionId, {\n type: 'permission_request',\n sessionId,\n payload: request,\n timestamp: Date.now(),\n })\n }\n\n /** Broadcasts a notification to all connected clients (not scoped to a session). */\n async sendNotification(notification: NotificationMessage): Promise<void> {\n await this.broadcast({\n type: 'notification',\n payload: notification,\n timestamp: Date.now(),\n })\n }\n\n /**\n * No-op for stream adapters — threads are a platform concept (Telegram topics, Slack threads).\n * Stream clients manage their own session UI.\n */\n async createSessionThread(_sessionId: string, _name: string): Promise<string> {\n return ''\n }\n\n /** Emits a rename event so connected clients can update their session title. */\n async renameSessionThread(sessionId: string, name: string): Promise<void> {\n await this.emit(sessionId, {\n type: 'session_rename',\n sessionId,\n payload: { name },\n timestamp: Date.now(),\n })\n }\n\n /** Sends an event to all connections watching a specific session. */\n protected abstract emit(sessionId: string, event: StreamEvent): Promise<void>\n /** Sends an event to all connected clients regardless of session. */\n protected abstract broadcast(event: StreamEvent): Promise<void>\n abstract start(): Promise<void>\n abstract stop(): Promise<void>\n}\n","export {\n type IRenderer,\n BaseRenderer,\n type RenderedMessage,\n type RenderedPermission,\n type RenderedAction,\n type RenderedAttachment,\n} from './renderer.js'\n","export {\n SendQueue,\n type SendQueueConfig,\n type EnqueueOptions,\n type QueueItemType,\n} from \"./send-queue.js\";\nexport { DraftManager, Draft, type DraftConfig } from \"./draft-manager.js\";\nexport { ToolCallTracker, type TrackedToolCall } from \"./tool-call-tracker.js\";\nexport {\n ActivityTracker,\n type ActivityConfig,\n type ActivityCallbacks,\n} from \"./activity-tracker.js\";\nexport {\n ToolCardState,\n type ToolCardSnapshot,\n type ToolDisplaySpec,\n type UsageData,\n type ToolCardStateConfig,\n} from \"./tool-card-state.js\";\n","/** Configuration for draft message flushing behavior. */\nexport interface DraftConfig {\n /** How often (ms) to flush buffered text to the platform. */\n flushInterval: number\n maxLength: number\n /**\n * Called to send or update a message on the platform.\n * Returns the platform message ID on first send (isEdit=false);\n * subsequent calls are edits (isEdit=true).\n */\n onFlush: (sessionId: string, text: string, isEdit: boolean) => Promise<string | undefined>\n onError?: (sessionId: string, error: Error) => void\n}\n\n/**\n * Manages a single in-progress (draft) message for streaming text output.\n *\n * As text chunks arrive from the agent, they are appended to an internal buffer.\n * The buffer is flushed to the platform on a timer. The first flush creates the\n * message; subsequent flushes edit it in place. This avoids sending many small\n * messages and instead shows a live-updating message.\n */\nexport class Draft {\n private buffer = ''\n private _messageId?: string\n /** Guards against concurrent first-flush — ensures only one sendMessage creates the draft. */\n private firstFlushPending = false\n private flushTimer?: ReturnType<typeof setTimeout>\n private flushPromise: Promise<void> = Promise.resolve()\n\n constructor(\n private sessionId: string,\n private config: DraftConfig,\n ) {}\n\n get isEmpty(): boolean { return !this.buffer }\n /** Platform message ID, set after the first successful flush. */\n get messageId(): string | undefined { return this._messageId }\n\n /** Appends streaming text to the buffer and schedules a flush. */\n append(text: string): void {\n if (!text) return\n this.buffer += text\n this.scheduleFlush()\n }\n\n /**\n * Flushes any remaining buffered text and returns the platform message ID.\n * Called when the streaming response completes.\n */\n async finalize(): Promise<string | undefined> {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer)\n this.flushTimer = undefined\n }\n await this.flushPromise\n if (this.buffer) {\n await this.flush()\n }\n return this._messageId\n }\n\n /** Discards buffered text and cancels any pending flush. */\n destroy(): void {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer)\n this.flushTimer = undefined\n }\n this.buffer = ''\n }\n\n private scheduleFlush(): void {\n if (this.flushTimer) return\n this.flushTimer = setTimeout(() => {\n this.flushTimer = undefined\n this.flushPromise = this.flushPromise\n .then(() => this.flush())\n .catch(() => {})\n }, this.config.flushInterval)\n }\n\n private async flush(): Promise<void> {\n if (!this.buffer || this.firstFlushPending) return\n\n // Snapshot the buffer before awaiting — append() can be called\n // concurrently during the async flush operation.\n const snapshot = this.buffer\n const isEdit = !!this._messageId\n\n if (!this._messageId) {\n this.firstFlushPending = true\n }\n\n try {\n const result = await this.config.onFlush(this.sessionId, snapshot, isEdit)\n if (!isEdit && result) {\n this._messageId = result\n }\n } catch (err) {\n this.config.onError?.(this.sessionId, err instanceof Error ? err : new Error(String(err)))\n } finally {\n this.firstFlushPending = false\n }\n\n // Re-flush if buffer changed during the async operation\n if (this.buffer !== snapshot) {\n return this.flush()\n }\n }\n}\n\n/**\n * Manages draft messages across multiple sessions.\n *\n * Each session gets at most one active draft. When a session's streaming\n * response completes, the draft is finalized (final flush + cleanup).\n */\nexport class DraftManager {\n private drafts = new Map<string, Draft>()\n\n constructor(private config: DraftConfig) {}\n\n /** Returns the existing draft for a session, or creates a new one. */\n getOrCreate(sessionId: string): Draft {\n let draft = this.drafts.get(sessionId)\n if (!draft) {\n draft = new Draft(sessionId, this.config)\n this.drafts.set(sessionId, draft)\n }\n return draft\n }\n\n /** Finalizes and removes the draft for a session. */\n async finalize(sessionId: string): Promise<void> {\n const draft = this.drafts.get(sessionId)\n if (!draft) return\n await draft.finalize()\n this.drafts.delete(sessionId)\n }\n\n /** Finalizes all active drafts (e.g., during adapter shutdown). */\n async finalizeAll(): Promise<void> {\n await Promise.all([...this.drafts.values()].map(d => d.finalize()))\n }\n\n /** Destroys a draft without flushing (e.g., on session error). */\n destroy(sessionId: string): void {\n const draft = this.drafts.get(sessionId)\n if (draft) {\n draft.destroy()\n this.drafts.delete(sessionId)\n }\n }\n\n /** Destroys all drafts without flushing. */\n destroyAll(): void {\n for (const draft of this.drafts.values()) {\n draft.destroy()\n }\n this.drafts.clear()\n }\n}\n","import type { ToolCallMeta } from '../format-types.js'\n\n/** A tool call associated with the platform message ID where it is displayed. */\nexport interface TrackedToolCall extends ToolCallMeta {\n /** Platform message ID of the tool card message, used for in-place updates. */\n messageId: string\n}\n\n/**\n * Tracks active tool calls per session, associating each with the platform\n * message that displays it. Used by adapters that render individual tool\n * cards and need to update them in-place when tool status changes.\n */\nexport class ToolCallTracker {\n private sessions = new Map<string, Map<string, TrackedToolCall>>()\n\n /** Registers a new tool call and associates it with its platform message ID. */\n track(sessionId: string, meta: ToolCallMeta, messageId: string): void {\n if (!this.sessions.has(sessionId)) {\n this.sessions.set(sessionId, new Map())\n }\n this.sessions.get(sessionId)!.set(meta.id, { ...meta, messageId })\n }\n\n /** Updates a tracked tool call's status and optional metadata. Returns null if not found. */\n update(\n sessionId: string,\n toolId: string,\n status: string,\n patch?: Partial<Pick<ToolCallMeta, 'viewerLinks' | 'viewerFilePath' | 'name' | 'kind'>>,\n ): TrackedToolCall | null {\n const tool = this.sessions.get(sessionId)?.get(toolId)\n if (!tool) return null\n\n tool.status = status\n if (patch?.viewerLinks) tool.viewerLinks = patch.viewerLinks\n if (patch?.viewerFilePath) tool.viewerFilePath = patch.viewerFilePath\n if (patch?.name) tool.name = patch.name\n if (patch?.kind) tool.kind = patch.kind\n\n return tool\n }\n\n /** Returns all tracked tool calls for a session (regardless of status). */\n getActive(sessionId: string): TrackedToolCall[] {\n const session = this.sessions.get(sessionId)\n return session ? [...session.values()] : []\n }\n\n /** Removes all tracked tool calls for a session (called at turn end). */\n clear(sessionId: string): void {\n this.sessions.delete(sessionId)\n }\n\n clearAll(): void {\n this.sessions.clear()\n }\n}\n","/** Timing configuration for the thinking indicator lifecycle. */\nexport interface ActivityConfig {\n /** How often (ms) to refresh the typing indicator (e.g., re-send \"typing...\" action). */\n thinkingRefreshInterval: number\n /** Maximum duration (ms) before auto-dismissing the indicator to avoid stale UI. */\n maxThinkingDuration: number\n}\n\n/** Platform-specific callbacks for showing/updating/removing typing indicators. */\nexport interface ActivityCallbacks {\n sendThinkingIndicator(): Promise<void>\n updateThinkingIndicator(): Promise<void>\n removeThinkingIndicator(): Promise<void>\n}\n\ninterface SessionState {\n callbacks: ActivityCallbacks\n refreshTimer?: ReturnType<typeof setInterval>\n startTime: number\n dismissed: boolean\n}\n\n/**\n * Manages typing/thinking indicators across sessions.\n *\n * When the agent starts processing, a typing indicator is shown. It is\n * periodically refreshed (platforms like Telegram expire typing status after\n * ~5 seconds) and auto-dismissed either when text output begins or when\n * the max thinking duration is reached.\n */\nexport class ActivityTracker {\n private sessions = new Map<string, SessionState>()\n\n constructor(private config: ActivityConfig) {}\n\n /** Shows the typing indicator and starts the periodic refresh timer. */\n onThinkingStart(sessionId: string, callbacks: ActivityCallbacks): void {\n this.cleanup(sessionId)\n\n const state: SessionState = {\n callbacks,\n startTime: Date.now(),\n dismissed: false,\n }\n this.sessions.set(sessionId, state)\n\n setTimeout(() => {\n if (state.dismissed) return\n callbacks.sendThinkingIndicator().catch(() => {})\n this.startRefresh(sessionId, state)\n }, 0)\n }\n\n /** Dismisses the typing indicator when the agent starts producing text output. */\n onTextStart(sessionId: string): void {\n const state = this.sessions.get(sessionId)\n if (!state || state.dismissed) return\n state.dismissed = true\n this.stopRefresh(state)\n state.callbacks.removeThinkingIndicator().catch(() => {})\n }\n\n /** Cleans up the typing indicator when the session ends. */\n onSessionEnd(sessionId: string): void {\n this.cleanup(sessionId)\n }\n\n /** Cleans up all sessions (e.g., during adapter shutdown). */\n destroy(): void {\n for (const [id] of this.sessions) {\n this.cleanup(id)\n }\n }\n\n private cleanup(sessionId: string): void {\n const state = this.sessions.get(sessionId)\n if (!state) return\n state.dismissed = true\n this.stopRefresh(state)\n state.callbacks.removeThinkingIndicator().catch(() => {})\n this.sessions.delete(sessionId)\n }\n\n private startRefresh(sessionId: string, state: SessionState): void {\n state.refreshTimer = setInterval(() => {\n if (state.dismissed) {\n this.stopRefresh(state)\n return\n }\n if (Date.now() - state.startTime >= this.config.maxThinkingDuration) {\n state.dismissed = true\n this.stopRefresh(state)\n state.callbacks.removeThinkingIndicator().catch(() => {})\n return\n }\n state.callbacks.updateThinkingIndicator().catch(() => {})\n }, this.config.thinkingRefreshInterval)\n }\n\n private stopRefresh(state: SessionState): void {\n if (state.refreshTimer) {\n clearInterval(state.refreshTimer)\n state.refreshTimer = undefined\n }\n }\n}\n","/**\n * OpenACP Product Guide — comprehensive reference text injected into the AI assistant's system prompt.\n *\n * The assistant (in the Assistant topic/thread) reads this at startup to answer user questions\n * about features, CLI commands, configuration, and troubleshooting without hitting the network.\n *\n * **How it's used:** The assistant plugin reads `PRODUCT_GUIDE` and prepends it to the system\n * prompt so the AI has full product knowledge baked in, not fetched per-session.\n *\n * **How to update:** Edit the Markdown content below. Keep it accurate with the current feature set —\n * outdated entries cause the assistant to give wrong answers. Sections use `---` dividers and\n * `##` headings so the assistant can navigate the content efficiently.\n */\nexport const PRODUCT_GUIDE = `\n# OpenACP — Product Guide\n\nOpenACP lets you chat with AI coding agents (like Claude Code) through messaging platforms (Telegram, Discord).\nYou type messages in your chat platform, the agent reads/writes/runs code in your project folder, and results stream back in real time.\n\n---\n\n## Quick Start\n\n1. Start OpenACP: \\`openacp\\` (or \\`openacp start\\` for background daemon)\n2. Open your messaging platform (Telegram group or Discord server) — you'll see the Assistant topic/thread\n3. Tap/click 🆕 New Session or type /new\n4. Pick an agent and a project folder\n5. Chat in the session topic/thread — the agent works on your code\n\n---\n\n## Core Concepts\n\n### Sessions\nA session = one conversation with one AI agent working in one project folder.\nEach session gets its own topic (Telegram) or forum thread (Discord). Chat there to give instructions to the agent.\n\n### Agents\nAn agent is an AI coding tool (e.g., Claude Code, Gemini, Cursor, Codex, etc.).\nOpenACP supports 28+ agents from the official ACP Registry (agentclientprotocol.com).\nYou can install multiple agents and choose which one to use per session.\nThe default agent is used when you don't specify one.\n\n### Agent Management\n- Browse agents: \\`/agents\\` in your chat platform or \\`openacp agents\\` in CLI\n- Install: tap the install button in /agents, or \\`openacp agents install <name>\\`\n- Uninstall: \\`openacp agents uninstall <name>\\`\n- Setup/login: \\`openacp agents run <name> -- <args>\\` (e.g., \\`openacp agents run gemini -- auth login\\`)\n- Details: \\`openacp agents info <name>\\` shows version, dependencies, and setup steps\n\nSome agents need additional setup before they can be used:\n- Claude: requires \\`claude login\\`\n- Gemini: requires \\`openacp agents run gemini -- auth login\\`\n- Codex: requires setting \\`OPENAI_API_KEY\\` environment variable\n- GitHub Copilot: requires \\`openacp agents run copilot -- auth login\\`\n\nAgents are installed in three ways depending on the agent:\n- **npx** — Node.js agents, downloaded automatically on first use\n- **uvx** — Python agents, downloaded automatically on first use\n- **binary** — Platform-specific binaries, downloaded to \\`~/.openacp/agents/\\`\n\n### Project Folder (Workspace)\nThe directory where the agent reads, writes, and runs code.\nWhen creating a session, you choose which folder the agent works in.\nYou can type a full path like \\`~/code/my-project\\` or just a name like \\`my-project\\` (it becomes \\`<base-dir>/my-project\\`).\n\n### System Topics\n- **Assistant** — Always-on helper that can answer questions, create sessions, check status, troubleshoot\n- **Notifications** — System alerts (permission requests, session errors, completions)\n\n---\n\n## Creating Sessions\n\n### From menu\nTap 🆕 New Session → choose agent (if multiple) → choose project folder → confirm\n\n### From command\n- \\`/new\\` — Interactive flow (asks agent + folder)\n- \\`/new claude ~/code/my-project\\` — Create directly with specific agent and folder\n\n### From Assistant topic\nJust ask: \"Create a session for my-project with claude\" — the assistant handles it\n\n### Quick new chat\n\\`/newchat\\` in a session topic — creates new session with same agent and folder as current one\n\n---\n\n## Working with Sessions\n\n### Chat\nType messages in the session topic. The agent responds with code changes, explanations, tool outputs.\n\n### What you see while the agent works\n- **💭 Thinking indicator** — Shows when the agent is reasoning, with elapsed time\n- **Text responses** — Streamed in real time, updated every few seconds\n- **Tool calls** — When the agent runs commands or edits files, you see tool name, input, status, and output\n- **📋 Plan card** — Visual task progress with completed/in-progress/pending items and progress bar\n- **\"View File\" / \"View Diff\" buttons** — Opens in browser with Monaco editor (requires tunnel)\n\n### Session lifecycle\n1. **Creating** — Topic created, agent spawning\n2. **Warming up** — Agent primes its cache (happens automatically, invisible to you)\n3. **Active** — Ready for your messages\n4. **Auto-naming** — After your first message, the session gets a descriptive name (agent summarizes in ~5 words). The topic title updates automatically.\n5. **Finished/Error** — Session completed or hit an error\n\n### Agent skills\nSome agents provide slash commands (e.g., /compact, /review). Available skills are pinned in the session topic.\n\n### Permission requests\nWhen the agent wants to run a command, it asks for permission.\nYou see buttons: ✅ Allow, ❌ Reject (and sometimes \"Always Allow\").\nA notification also appears in the Notifications topic with a link to the request.\n\n### Bypass permissions\nAuto-approves ALL permission requests — the agent runs any command without asking.\n- Toggle: \\`/bypass_permissions on\\` or tap the ☠️ button in the session\n- Disable: \\`/bypass_permissions off\\` or tap the 🔐 button\n- ⚠️ Use with caution — the agent can execute anything\n\n### Session timeout\nIdle sessions are automatically cancelled after a configurable timeout (default: 60 minutes).\nConfigure via \\`security.sessionTimeoutMinutes\\` in config.\n\n---\n\n## Session Transfer (Handoff)\n\n### Chat → Terminal\n1. Type \\`/handoff\\` in a session topic/thread\n2. You get a command like \\`claude --resume <SESSION_ID>\\`\n3. Copy and run it in your terminal — the session continues there with full conversation history\n\n### Terminal → Chat\n1. First time: run \\`openacp integrate <agent>\\` to install handoff integration (one-time setup)\n2. In supported agents (for example Claude Code or OpenCode), use /openacp:handoff\n3. The session appears as a new topic/thread and you can continue chatting there\n\n### How it works\n- The agent session ID is shared between platforms\n- Conversation history is preserved — pick up where you left off\n- The agent that supports resume (e.g., Claude with \\`--resume\\`) handles the actual transfer\n\n---\n\n## Managing Sessions\n\n### Status\n- \\`/status\\` — Shows active sessions count and details\n- Ask the Assistant: \"What sessions are running?\"\n\n### List all sessions\n- \\`/sessions\\` — Shows all sessions with status (active, finished, error)\n\n### Cancel\n- \\`/cancel\\` in a session topic — cancels that session\n- Ask the Assistant: \"Cancel the stuck session\"\n\n### Cleanup\n- From \\`/sessions\\` → tap cleanup buttons (finished, errors, all)\n- Ask the Assistant: \"Clean up old sessions\"\n\n---\n\n## Assistant Topic\n\nThe Assistant is an always-on AI helper in its own topic. It can:\n- Answer questions about OpenACP\n- Create sessions for you\n- Check status and health\n- Cancel sessions\n- Clean up old sessions\n- Troubleshoot issues\n- Manage configuration\n\nJust chat naturally: \"How do I create a session?\", \"What's the status?\", \"Something is stuck\"\n\n### Clear history\n\\`/clear\\` in the Assistant topic — resets the conversation\n\n---\n\n## System Commands\n\n| Command | Where | What it does |\n|---------|-------|-------------|\n| \\`/new [agent] [path]\\` | Anywhere | Create new session |\n| \\`/newchat\\` | Session topic | New session, same agent + folder |\n| \\`/cancel\\` | Session topic | Cancel current session |\n| \\`/status\\` | Anywhere | Show status |\n| \\`/sessions\\` | Anywhere | List all sessions |\n| \\`/agents\\` | Anywhere | Browse & install agents from ACP Registry |\n| \\`/install <name>\\` | Anywhere | Install an agent |\n| \\`/bypass_permissions\\` | Session topic | Toggle bypass permissions (on/off) |\n| \\`/handoff\\` | Session topic | Transfer session to terminal |\n| \\`/clear\\` | Assistant topic | Clear assistant history |\n| \\`/menu\\` | Anywhere | Show action menu |\n| \\`/help\\` | Anywhere | Show help |\n| \\`/restart\\` | Anywhere | Restart OpenACP |\n| \\`/update\\` | Anywhere | Update to latest version |\n| \\`/integrate\\` | Anywhere | Manage agent integrations |\n\n---\n\n## Menu Buttons\n\n| Button | Action |\n|--------|--------|\n| 🆕 New Session | Create new session (interactive) |\n| 📋 Sessions | List all sessions with cleanup options |\n| 📊 Status | Show active/total session count |\n| 🤖 Agents | List available agents |\n| 🔗 Integrate | Manage agent integrations |\n| ❓ Help | Show help text |\n| 🔄 Restart | Restart OpenACP |\n| ⬆️ Update | Check and install updates |\n\n---\n\n## CLI Commands\n\n### Server\n- \\`openacp\\` — Start (uses configured mode: foreground or daemon)\n- \\`openacp start\\` — Start as background daemon\n- \\`openacp stop\\` — Stop daemon\n- \\`openacp status\\` — Show daemon status\n- \\`openacp logs\\` — Tail daemon logs\n- \\`openacp --foreground\\` — Force foreground mode (useful for debugging or containers)\n\n### Auto-start (run on boot)\n- macOS: installs a LaunchAgent in \\`~/Library/LaunchAgents/\\`\n- Linux: installs a systemd user service in \\`~/.config/systemd/user/\\`\n- Enabled automatically when you start the daemon. Remove with \\`openacp stop\\`.\n\n### Configuration\n- \\`openacp config\\` — Interactive config editor\n- \\`openacp reset\\` — Delete all data and start fresh\n\n### Agent Management (CLI)\n- \\`openacp agents\\` — List all agents (installed + available from ACP Registry)\n- \\`openacp agents install <name>\\` — Install an agent\n- \\`openacp agents uninstall <name>\\` — Remove an agent\n- \\`openacp agents info <name>\\` — Show details, dependencies, and setup guide\n- \\`openacp agents run <name> [-- args]\\` — Run agent CLI directly (for login, config, etc.)\n- \\`openacp agents refresh\\` — Force-refresh registry cache\n\n### Plugins\n- \\`openacp install <package>\\` — Install adapter plugin\n- \\`openacp uninstall <package>\\` — Remove adapter plugin\n- \\`openacp plugins\\` — List installed plugins\n\n### Integration\n- \\`openacp integrate <agent>\\` — Install agent integration (e.g., Claude handoff skill)\n- \\`openacp integrate <agent> --uninstall\\` — Remove integration\n\n### API (requires running daemon)\n\\`openacp api <command>\\` — Interact with running daemon:\n\n| Command | Description |\n|---------|-------------|\n| \\`status\\` | List active sessions |\n| \\`session <id>\\` | Session details |\n| \\`new <agent> <path>\\` | Create session |\n| \\`send <id> \"text\"\\` | Send prompt |\n| \\`cancel <id>\\` | Cancel session |\n| \\`bypass <id> on/off\\` | Toggle bypass permissions |\n| \\`topics [--status x,y]\\` | List topics |\n| \\`delete-topic <id> [--force]\\` | Delete topic |\n| \\`cleanup [--status x,y]\\` | Cleanup old topics |\n| \\`agents\\` | List agents |\n| \\`health\\` | System health |\n| \\`config\\` | Show config |\n| \\`config set <key> <value>\\` | Update config |\n| \\`adapters\\` | List adapters |\n| \\`tunnel\\` | Tunnel status |\n| \\`notify \"message\"\\` | Send notification |\n| \\`version\\` | Daemon version |\n| \\`restart\\` | Restart daemon |\n\n---\n\n## File Viewer (Tunnel)\n\nWhen tunnel is enabled, file edits and diffs get \"View\" buttons that open in your browser:\n- **Monaco Editor** — Full VS Code editor with syntax highlighting\n- **Diff viewer** — Side-by-side or inline comparison\n- **Line highlighting** — Click lines to highlight\n- Dark/light theme toggle\n\n### Setup\nEnable in config: set \\`tunnel.enabled\\` to \\`true\\`.\nProviders: Cloudflare (default, free), ngrok, bore, Tailscale Funnel.\n\n### Port Tunneling\n\nExpose any local port (dev servers, APIs, etc.) to the internet:\n\n**CLI commands** (agent can call these directly):\n- \\`openacp tunnel add <port> --label <name>\\` — Create tunnel to a local port\n- \\`openacp tunnel list\\` — List active tunnels\n- \\`openacp tunnel stop <port>\\` — Stop a tunnel\n- \\`openacp tunnel stop-all\\` — Stop all user tunnels\n\n**Telegram commands**:\n- \\`/tunnel <port> [label]\\` — Create tunnel\n- \\`/tunnels\\` — List active tunnels\n- \\`/tunnel stop <port>\\` — Stop tunnel\n\nExample: after starting a dev server on port 3000, run \\`openacp tunnel add 3000 --label my-app\\` to get a public URL.\n\n---\n\n## Configuration\n\nConfig file: \\`<workspace>/.openacp/config.json\\` (default: \\`~/openacp-workspace/.openacp/config.json\\`)\n\n### Channels\n- **channels.telegram.botToken** — Your Telegram bot token\n- **channels.telegram.chatId** — Your Telegram supergroup ID\n- **channels.discord.botToken** — Your Discord bot token\n- **channels.discord.guildId** — Your Discord server (guild) ID\n\n### Agents\n- **defaultAgent** — Which agent to use by default\n- Agents are managed via \\`/agents\\` (Telegram) or \\`openacp agents\\` (CLI)\n- Installed agent definitions are stored in \\`<instance-root>/agents.json\\`; downloaded agent binaries live in \\`~/.openacp/agents/\\`\n- Agent list is fetched from the ACP Registry CDN and cached locally (24h)\n\n### Workspace\n- Workspace directory is the parent of \\`.openacp/\\` (where you ran \\`openacp\\` setup)\n\n### Security\n- **security.allowedUserIds** — Restrict who can use the bot (empty = everyone)\n- **security.maxConcurrentSessions** — Max parallel sessions (default: 5)\n- **security.sessionTimeoutMinutes** — Auto-cancel idle sessions (default: 60)\n\n### Tunnel / File Viewer\n- **tunnel.enabled** — Enable file viewer tunnel\n- **tunnel.provider** — Tunnel provider: cloudflare (default, free), ngrok, bore, tailscale\n- **tunnel.port** — Local port for tunnel server (default: 3100)\n- **tunnel.auth.enabled** — Enable authentication for tunnel URLs\n- **tunnel.auth.token** — Auth token for tunnel access\n- **tunnel.storeTtlMinutes** — How long viewer links stay cached (default: 60)\n\n### Logging\n- **logging.level** — Log level: silent, debug, info, warn, error, fatal (default: info)\n- **logging.logDir** — Log directory (default: \\`<instance-root>/logs\\`)\n- **logging.maxFileSize** — Max log file size before rotation\n- **logging.maxFiles** — Max number of rotated log files\n- **logging.sessionLogRetentionDays** — Auto-delete old session logs (default: 30)\n\n### Data Retention\n- **sessionStore.ttlDays** — How long session records persist (default: 30). Old records are cleaned up automatically.\n\n### Environment variables\nOverride config with env vars:\n- \\`OPENACP_TELEGRAM_BOT_TOKEN\\`\n- \\`OPENACP_TELEGRAM_CHAT_ID\\`\n- \\`OPENACP_DISCORD_BOT_TOKEN\\`\n- \\`OPENACP_DISCORD_GUILD_ID\\`\n- \\`OPENACP_DEFAULT_AGENT\\`\n- \\`OPENACP_RUN_MODE\\` — foreground or daemon\n- \\`OPENACP_API_PORT\\` — API server port (default: 21420)\n- \\`OPENACP_TUNNEL_ENABLED\\`\n- \\`OPENACP_TUNNEL_PORT\\`\n- \\`OPENACP_TUNNEL_PROVIDER\\`\n- \\`OPENACP_LOG_LEVEL\\`\n- \\`OPENACP_LOG_DIR\\`\n- \\`OPENACP_DEBUG\\` — Sets log level to debug\n\n---\n\n## Troubleshooting\n\n### Session stuck / not responding\n- Check status: ask Assistant \"Is anything stuck?\"\n- Cancel and create new: \\`/cancel\\` then \\`/new\\`\n- Check system health: Assistant can run health check\n\n### Agent not found\n- Check available agents: \\`/agents\\` or \\`openacp agents\\`\n- Install missing agent: \\`openacp agents install <name>\\`\n- Some agents need login first: \\`openacp agents info <name>\\` to see setup steps\n- Run agent CLI for setup: \\`openacp agents run <name> -- <args>\\`\n\n### Permission request not showing\n- Check Notifications topic for the alert\n- Try \\`/bypass_permissions on\\` to auto-approve (if you trust the agent)\n\n### Session disappeared after restart\n- Sessions persist across restarts\n- Send a message in the old topic — it auto-resumes\n- If topic was deleted, the session record may still exist in status\n\n### Bot not responding at all\n- Check daemon: \\`openacp status\\`\n- Check logs: \\`openacp logs\\`\n- Restart: \\`openacp start\\` or \\`/restart\\`\n\n### Messages going to wrong topic\n- Each session is bound to a specific topic/thread\n- If you see messages appearing in the Assistant topic instead of the session topic, try creating a new session\n\n### Viewing logs\n- Session-specific logs: \\`<instance-root>/logs/sessions/\\`\n- System logs: \\`openacp logs\\` to tail live\n- Set \\`OPENACP_DEBUG=true\\` for verbose output\n\n---\n\n## Data & Storage\n\nOpenACP stores per-instance state in \\`<workspace>/.openacp/\\` (the instance root), with shared downloads and caches under \\`~/.openacp/\\`:\n- \\`<instance-root>/config.json\\` — Configuration\n- \\`<instance-root>/agents.json\\` — Installed agent definitions (managed by AgentCatalog)\n- \\`<instance-root>/sessions.json\\` — Session records and state\n- \\`<instance-root>/usage.json\\` — Token and cost tracking\n- \\`<instance-root>/history/\\` — Per-session conversation history\n- \\`<instance-root>/logs/\\` — System and session logs\n- \\`<instance-root>/plugins/\\` — Installed adapter plugins and plugin data\n- \\`<instance-root>/openacp.pid\\` — Daemon PID file\n- \\`~/.openacp/cache/registry-cache.json\\` — Cached ACP Registry data (refreshes every 24h)\n- \\`~/.openacp/agents/\\` — Downloaded binary agents\n\nSession records auto-cleanup: 30 days (configurable via \\`sessionStore.ttlDays\\`).\nSession logs auto-cleanup: 30 days (configurable via \\`logging.sessionLogRetentionDays\\`).\n`;\n","// Public API surface for the `@n1creator/openacp-cli` package.\n//\n// Most exports come from `core/index.ts` which provides the full set of\n// core modules, plugin types, and adapter primitives for plugin authors.\n// The exports below supplement that with top-level package-specific symbols.\n\nexport * from './core/index.js'\n\n/** Full product guide text injected into the assistant's system prompt at runtime. */\nexport { PRODUCT_GUIDE } from './data/product-guide.js'\n\n/**\n * Telegram adapter implementation.\n *\n * Re-exported here for consumers who need to reference the class directly\n * (e.g., in tests or custom adapter setups) without importing from the deep plugin path.\n */\nexport { TelegramAdapter } from './plugins/telegram/adapter.js'\n\n/**\n * Manages the local catalog of installed agents (read/write to agents.json).\n *\n * Plugin authors building agent-aware features should use this to query\n * which agents are installed and their configurations.\n */\nexport { AgentCatalog } from \"./core/agents/agent-catalog.js\";\n\n/**\n * Low-level persistent store for agent records.\n *\n * AgentCatalog is the preferred higher-level interface; AgentStore is\n * exported for advanced use cases that require direct record access.\n */\nexport { AgentStore } from \"./core/agents/agent-store.js\";\n\n/** Agent-related types shared across the public API. */\nexport type { InstalledAgent, RegistryAgent, AgentListItem } from \"./core/types.js\";\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACI7B;","names":["resolve","fs","path","os","log","path","fs","path","os","expandHome","log","getConfigValue","input","fs","input","execFileSync","fs","path","fs","path","os","platform","log","execFileSync","fs","init_config","execFileSync","fs","path","commandExists","fs","path","path","SettingsManager","fs","fs","path","fs","path","fs","resolve","fs","path","os","execFileSync","resolve","platform","log","fs","path","os","execFileSync","SettingsManager","ensureCloudflared","fs","path","init_config","expandHome","path","fs","os","fs","path","randomUUID","fs","path","path","fs","path","fs","path","readTextFileWithRange","log","text","fs","path","text","fs","path","crypto","execFileSync","text","text","c","path","text","text","resolve","DEBOUNCE_MS","asRecord","input","text","splitMessage","fileURLToPath","dirname","join","resolve","existsSync","readFileSync","spawn","clack","ok","toastText","log","c","getCurrentVersion","getLatestVersion","compareVersions","runUpdate","ok","InlineKeyboard","log","input","id","shortenPath","text","buildDeepLink","InlineKeyboard","platform","header","log","existsSync","mkdirSync","readFileSync","writeFileSync","unlinkSync","join","dirname","homedir","InlineKeyboard","text","getAgentCapabilities","installIntegration","init_agents","InlineKeyboard","c","log","InlineKeyboard","header","c","InlineKeyboard","log","buildMenuKeyboard","InlineKeyboard","escapeHtml","text","log","init_doctor","InlineKeyboard","log","init_tunnel","InlineKeyboard","log","init_agents","init_doctor","InlineKeyboard","listIntegrations","keyboard","agentName","getIntegration","integration","init_integrate","log","InlineKeyboard","redirectToAssistant","init_agents","init_doctor","init_tunnel","init_integrate","InlineKeyboard","nanoid","log","log","ActivityTracker","text","text","splitMessage","DraftManager","text","log","platform","text","init_renderer","text","log","InlineKeyboard","input","log","init_renderer","platform","ActivityTracker","DraftManager","text","checkTopicsPrerequisites","c","delay","resolve","ok","spawn","fs","path","fs","path","resolve","fs","path","log","path","fs","spawn","resolve","c","text","log","nanoid","text","resolve","resolve","fs","direct","nanoid","text","path","c","log","asRecord","added","removed","input","id","live","log","fs","log","live","path","nanoid","fs","path","log","log","instance","fs","path","log","fs","path","z","log","readFileSync","fs","path","log","resolve","log","log","log","c","log","log","setLogLevel","path","nanoid","access","text","platform","existsSync","c","path","clack","execFileSync","fs","path","os","log","logDir","fs","path","log","select","installNpmPlugin","createInstallContext","logDir","expandHome","path","spawn","fs","path","logDir","expandHome","sleep","resolve","isProcessAlive","log","text"]}
|